pax_global_header00006660000000000000000000000064132037622570014521gustar00rootroot0000000000000052 comment=005bb9a06aaa053eda2db22b65e2dc2e00d6a0b7 ocaml-rope-0.6/000077500000000000000000000000001320376225700134245ustar00rootroot00000000000000ocaml-rope-0.6/.gitignore000066400000000000000000000000711320376225700154120ustar00rootroot00000000000000_build .merlin *.install bench/*.dat TODO *.tar.gz *.svg ocaml-rope-0.6/CHANGES.md000066400000000000000000000006131320376225700150160ustar00rootroot000000000000000.6 2017-11-17 -------------- - Enrich the library to have all functions of the `String` module. - Add `rope.top` findlib library so one can just do `#require "rope.top";;` in the toplevel (REPL). - Compatible with `-safe-string` and code improvements based on string immutability. - Port to `jbuilder`. - Fix all warnings. - Clarify the license for files in `bench/` and fix FSF address. ocaml-rope-0.6/LICENSE.md000066400000000000000000000656531320376225700150470ustar00rootroot00000000000000This license applies to all the files in this repository except to bench/tinyRope.ml which was copied only to easily enable some benchmarks (as it is no longer available on the Internet). This Library is distributed under the terms of the GNU Lesser General Public License version 2.1 (included below) or, at your choice, any later version. As a special exception to the GNU Lesser General Public License, you may link, statically or dynamically, a "work that uses the Library" with a publicly distributed version of the Library to produce an executable file containing portions of the Library, and distribute that executable file under terms of your choice, without any of the additional requirements listed in clause 6 of the GNU Lesser General Public License. By "a publicly distributed version of the Library", we mean either the unmodified Library as distributed, or a modified version of the Library that is distributed under the conditions defined in clause 3 of the GNU Library General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Lesser General Public License. # GNU LESSER GENERAL PUBLIC LICENSE — Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] ## Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. ## GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. ## NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS ## How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! ocaml-rope-0.6/Makefile000066400000000000000000000017671320376225700150770ustar00rootroot00000000000000PKGVERSION = $(shell git describe --always --dirty) WEB = rope.forge.ocamlcore.org:/home/groups/rope/htdocs SRC_WEB = web all build: jbuilder build @install --dev jbuilder build bench/bm_ropes.exe bench/bench_rope.exe test runtest: # Force the tests to be run $(RM) -rf _build/default/tests/ jbuilder runtest install uninstall: jbuilder $@ doc: jbuilder build @doc # Benchmarks bench: jbuilder build @bench \ && cd _build/default/bench/ && gnuplot -persist bm_ropes.plot @echo "Bench results: SGV in _build/default/bench/" # Release a Sourceforge tarball and publish the HTML doc .PHONY: web upload web-doc: doc @ if [ -d API.docdir/ ] ; then \ scp -r API.docdir/ $(WEB)/ \ && echo "*** Published documentation" ; \ fi web: @ if [ -d $(SRC_WEB)/ ] ; then \ scp $(addprefix $(SRC_WEB)/, *.html *.css *.png) LICENSE $(WEB) \ && echo "*** Published web site ($(SRC_WEB)/)" ; \ fi clean:: jbuilder clean $(RM) $(wildcard bench/*.dat) .PHONY: all build test runtest doc bench clean ocaml-rope-0.6/README.md000066400000000000000000000016361320376225700147110ustar00rootroot00000000000000Rope ==== Ropes are a scalable string implementation: they are designed for efficient operation that involve the string as a whole such as concatenation and substring. This library implements ropes for OCaml (it is rich enough to replace strings). Version %%VERSION%% Installation ------------ The easier way to install this library is to use [opam][]: opam install rope To compile the development version, you will need to install [jbuilder][] and then issue jbuilder build @install Install with: jbuilder install To run the tests, install the module [Benchmark][] and do jbuilder runtest [opam]: http://opam.ocaml.org/ [jbuilder]: https://github.com/janestreet/jbuilder [benchmark]: https://github.com/Chris00/ocaml-benchmark Documentation ------------- You can read the interface `rope.mli` [in this repository](src/rope.mli) or [as HTML](http://chris00.github.io/ocaml-rope/doc/rope/Rope/). ocaml-rope-0.6/bench/000077500000000000000000000000001320376225700145035ustar00rootroot00000000000000ocaml-rope-0.6/bench/bench_rope.ml000066400000000000000000000031531320376225700171430ustar00rootroot00000000000000open Benchmark let empty = Rope.empty let ( ^^ ) = Rope.concat2 let rec random_string len = let s = Bytes.create len in for i = 0 to len - 1 do Bytes.set s i (Char.chr(Random.int 255)) done; Bytes.unsafe_to_string s (* Creates the list [[f 0;...; f(len - 1)]] *) let list_init len f = let rec build acc i = if i >= 0 then build (f i :: acc) (i - 1) else acc in build [] (len - 1) (* ---------------------------------------- *) let string r sub = let r = List.fold_left ( ^ ) "" r in let r = List.fold_left (fun sum (i,l) -> sum ^ String.sub r i l) "" sub in for i = 0 to String.length r - 1 do ignore(String.get r i) done let rope r sub = let r = List.fold_left ( ^^ ) empty r in let r = List.fold_left (fun sum (i,l) -> sum ^^ Rope.sub r i l) empty sub in for i = 0 to Rope.length r - 1 do ignore(Rope.get r i) done let iter r sub = let r = List.fold_left ( ^^ ) empty r in let r = List.fold_left (fun sum (i,l) -> sum ^^ Rope.sub r i l) empty sub in let i = Rope.Iterator.make r 0 in try while true do ignore(Rope.Iterator.get i); Rope.Iterator.incr i done with Rope.Out_of_bounds _ -> () let () = Random.self_init(); let init_len = 300 (* size of initial strings *) in let init_num = 10 (* howmany initial strings *) in let sub = list_init 0 (fun _ -> let i = Random.int init_len in (i, Random.int (init_len - i))) in let s = list_init init_num (fun _ -> random_string init_len) in let r = List.map Rope.of_string s in let res = throughputN 5 ~repeat:3 [("String", string s, sub); ("Rope", rope r, sub); ("Iter", iter r, sub) ] in tabulate res ocaml-rope-0.6/bench/bm_ropes.ml000066400000000000000000000235711320376225700166530ustar00rootroot00000000000000(* File: bm_ropes.ml Copyright (C) 2007 Christophe Troestler WWW: http://math.umh.ac.be/an/software/ This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2.1 or later as published by the Free Software Foundation, with the special exception on linking described in the file LICENSE. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file LICENSE for more details. *) open Printf let () = Random.self_init() let nsamples = 30 let n_ops = 1000 (* let n_ops = 10000 *) let max_nconcat = 8 (** how many concatenations of ropes (in the map) with lower length are accepted. [1] means: pick the laegest and complete appending chars. *) let prepend_size = 128 (** if [size land prepend_size = 0] prepend chars instead of appending. Set to [max_int]: always append (never prepend). Set to [0]: always preprend. *) let min_pow10 = 2 let max_pox10 = 7 let rec pow n = function 0 -> 1 | i -> n * pow n (i - 1) let list_init n f = let rec make acc n = if n < 0 then acc else make (f n :: acc) (n - 1) in make [] (n-1) let datapoints = let basis = [1; 2; 3; 5; 17; 37; 53; 91; 201] in (* let basis = List.sort compare (list_init 15 (fun _ -> 1 + Random.int 200)) in *) let d = max_pox10 - min_pow10 in let pow10_of j = Array.init d (fun i -> j * pow 10 (i + min_pow10)) in Array.concat (List.map pow10_of basis (*@ [ [|max_int / 2 |] } *) ) (* FIXME: for max_int (32 bits), TinyRope segfaults!!! *) let datapoints2 = Array.concat [ (Array.init 20 (fun _ -> 10000 + Random.int 10_000_000)); (Array.init 20 (fun _ -> 10_000_000 + Random.int 50_000_000)); ] (* ---------------------------------------------------------------------- *) let datapoints_ordered = let d = Array.copy datapoints in Array.sort compare d; d (* just for laughs *) let basic_loop_overhead = let t1 = Unix.gettimeofday () in for j = 0 to 100 do for i = 0 to n_ops do ignore () done done; (Unix.gettimeofday () -. t1) /. 100.0 let random_loop_overhead = let t1 = Unix.gettimeofday () in for j = 0 to 100 do for i = 0 to n_ops do ignore (Random.int 10000) done; done; (Unix.gettimeofday () -. t1) /. 100.0 let () = printf "Random loop overhead: %12.10f\n" random_loop_overhead; printf "Basic loop overhead: %12.10f\n" basic_loop_overhead let time ~msg f x = let t0 = Sys.time () in let r = f x in printf "%s needed %8.5fs\n%!" msg (Sys.time () -. t0); r let sample msg f x = print_string msg; let samples = Array.init nsamples (fun i -> printf "\r%2d/%4d%!" (i + 1) nsamples; f x) in printf "\r \r%!"; let min_sample (tmin,_) (t,d) = (min tmin t, d) in Array.fold_left min_sample (max_float, max_int) samples (* let sum_sample (tsum, _) (t,d) = (tsum +. t, d) in let t, d = Array.fold_left sum_sample (0.0, 0) samples in t /. float_of_int nsamples, d *) module IMap = Map.Make(struct type t = int let compare = compare end) module Benchmark(R : sig type t val name : string val balanced : bool val empty : t val append : char -> t -> t val prepend : char -> t -> t val concat : t -> t -> t val length : t -> int val height : t -> int val balance : t -> t val get : t -> int -> char val sub : t -> int -> int -> t val of_string : string -> t val to_string : t -> string end) = struct (** [make_rope size] returns a rope of length [size]. We concatenate small ropes as it is more reprensentative than only appending repeatedly chars. *) let make_rope = let rope_tbl = ref IMap.empty in let rec add_chars r c size = if size <= 0 then r else let op = if size land prepend_size = 0 then R.prepend else R.append in add_chars (op c r) c (size - 1) in let rec build nconcat r size = let largest = IMap.fold (fun _ v s -> let len = R.length v in if len > R.length s && len <= size then v else s ) !rope_tbl R.empty in if R.length largest = 0 || nconcat > max_nconcat then (* no piece to add to [r] *) add_chars r 'x' (size - R.length largest) else let r' = if Random.bool() then R.concat r largest else R.concat largest r in build (nconcat + 1) r' (size - R.length largest) in fun size -> let r = build 0 R.empty size in rope_tbl := IMap.add size r !rope_tbl; if R.balanced then R.balance r else r ;; let append_time size = let v = ref (make_rope size) in let t0 = Unix.gettimeofday () in for i = 0 to n_ops - 1 do v := R.append 'z' !v; (* ignore (append_f I !v); *) done; let dt = (Unix.gettimeofday () -. t0) in (dt -. basic_loop_overhead) /. (float_of_int n_ops), R.height !v let measure_append_time size = let msg = sprintf "Append time for %s of size %d\n%!" R.name size in sample msg append_time size let random_get_time size = let r = make_rope size in (* Gc.full_major (); *) let t0 = Unix.gettimeofday () in (* let sum = ref 0 in *) for i = 0 to n_ops - 1 do ignore(R.get r (Random.int size)); done; let dt = (Unix.gettimeofday () -. t0) in (dt -. random_loop_overhead) /. float n_ops, R.height r (* float !sum /. float n_ops, R.height r *) let measure_random_get_time size = let msg = sprintf "Random get time for %s of size %d\n%!" R.name size in sample msg random_get_time size let sub_time size = let r = make_rope size in let t0 = Unix.gettimeofday () in let h = ref 0 in for i = 0 to n_ops - 1 do h := !h + R.height(R.sub r 0 (Random.int size)); done; let dt = (Unix.gettimeofday () -. t0) in (dt -. random_loop_overhead) /. float n_ops, truncate(0.5 +. float !h /. float n_ops) (* round *) let measure_sub_time size = let msg = sprintf "Sub time for %s of size %d\n%!" R.name size in sample msg sub_time size (* Test inspired by http://www.rubyquiz.com/quiz137.html *) let size = 512 * 1024 let size8 = 8 + size (* [text] is make of [nchunks] chunks of text, each of [size] bytes long. Each chunck starts with an 8 byte number. Initially the chuncks are shuffled the this function sorts them into ascending order. *) let rec qsort text = if R.length text = 0 then text else begin let pivot = int_of_string(R.to_string(R.sub text 0 8)) in let less = ref R.empty and more = ref R.empty in let offset = ref size8 in while !offset < R.length text do let i = int_of_string(R.to_string(R.sub text !offset 8)) in if i < pivot then less := R.concat !less (R.sub text !offset size8) else more := R.concat !more (R.sub text !offset size8); offset := !offset + 8 + size; done; R.concat (qsort !less) (R.concat (R.sub text 0 size8) (qsort !more)) end let bulk_string = make_rope size let do_qsort size = let nchunks = size / 100_000 in let data = ref R.empty in for i = 1 to nchunks do data := R.concat !data (R.concat (R.of_string(sprintf "%08i" (Random.int nchunks))) bulk_string) done; let t0 = Unix.gettimeofday () in let sorted = qsort !data in let dt = (Unix.gettimeofday () -. t0) in (dt -. random_loop_overhead) /. float n_ops, R.height sorted let measure_qsort size = let msg = sprintf "Qsort time for %s of size %d\n%!" R.name size in sample msg do_qsort size end module TinyBM = struct let name = "TinyRope" include TinyRope let append = TinyRope.append_char let prepend = TinyRope.prepend_char let get r i = TinyRope.get i r let sub r start len = TinyRope.sub start len r end module FullBM = struct let name = "Rope" include Rope let append c r = Rope.concat2 r (Rope.of_char c) let prepend c r = Rope.concat2 (Rope.of_char c) r let concat = Rope.concat2 end module BalancedFullBM = Benchmark(struct include FullBM let balanced = true end) module UnbalancedFullBM = Benchmark(struct include FullBM let balanced = false end) module BalancedTinyBM = Benchmark(struct include TinyBM let balanced = true end) module UnbalancedTinyBM = Benchmark(struct include TinyBM let balanced = false end) let benchmark dst measl = let gather_times f = Array.fold_left (fun bm size -> IMap.add size (f size) bm) IMap.empty datapoints in let times = List.map gather_times measl in let ch = open_out dst in Array.iter (fun size -> fprintf ch "%d" size; List.iter (fun tbl -> let t, sz = IMap.find size tbl in fprintf ch "\t%12.10e\t%i" t sz ) times; fprintf ch "\n" ) datapoints_ordered; close_out ch let () = benchmark "append.dat" [UnbalancedTinyBM.measure_append_time; UnbalancedFullBM.measure_append_time ]; Gc.full_major (); benchmark "get.dat" [UnbalancedTinyBM.measure_random_get_time; UnbalancedFullBM.measure_random_get_time ]; Gc.full_major (); benchmark "append-balanced.dat" [BalancedTinyBM.measure_append_time; BalancedFullBM.measure_append_time]; Gc.full_major (); benchmark "get-balanced.dat" [BalancedTinyBM.measure_random_get_time; BalancedFullBM.measure_random_get_time]; Gc.full_major (); benchmark "sub.dat" [UnbalancedTinyBM.measure_sub_time; UnbalancedFullBM.measure_sub_time; BalancedTinyBM.measure_sub_time; BalancedFullBM.measure_sub_time ]; Gc.full_major (); benchmark "qsort.dat" [UnbalancedTinyBM.measure_qsort; UnbalancedFullBM.measure_qsort; BalancedTinyBM.measure_qsort; BalancedFullBM.measure_qsort ]; () ocaml-rope-0.6/bench/bm_ropes.plot000066400000000000000000000074231320376225700172170ustar00rootroot00000000000000hardcopy = 1 if (hardcopy) set terminal svg size 650,800 background rgb 'white' set grid set logscale x set format y "%g" if (hardcopy) set output "append.svg"; else \ set terminal wxt 1 enhanced raise set multiplot layout 2,1 downwards set key right set title "Min. unitary append time" plot [:] [0.5e-7:4.7e-7] \ "append.dat" using 1:2 with linespoints title "Tiny (unbalanced)", \ "append.dat" using 1:4 with linespoints title "FullFeatured (unbalanced)", \ "append-balanced.dat" using 1:2 with lines title "Tiny (balanced)", \ "append-balanced.dat" using 1:4 with lines title "FullFeatured (balanced)" set key bottom right set title "" set ylabel "depth (after appends)" plot \ "append.dat" using 1:3 with linespoints title "Tiny (unbalanced)", \ "append.dat" using 1:5 with linespoints title "FullFeatured (unbalanced)", \ "append-balanced.dat" using 1:3 with lines title "Tiny (balanced)", \ "append-balanced.dat" using 1:5 with lines title "FullFeatured (balanced)" set ylabel "" unset grid set origin 0.1, 0.68 set size 0.45,0.25 plot [4e6:] \ "append.dat" using 1:2 with lines notitle, \ "append.dat" using 1:4 with lines lt 2 notitle, \ "append-balanced.dat" using 1:4 with lines lt 4 notitle unset multiplot unset ylabel set grid if (hardcopy) set output "get.svg"; else set terminal wxt 2 raise set multiplot layout 2,1 downwards set key top left set title "Min. unitary random get time" plot \ "get.dat" using 1:2 with linespoints title "Tiny (unbalanced)", \ "get.dat" using 1:4 with linespoints title "FullFeatured (unbalanced)", \ "get-balanced.dat" using 1:2 with lines title "Tiny (balanced)", \ "get-balanced.dat" using 1:4 with lines title "FullFeatured (balanced)" set key bottom right set title "" set ylabel "depth" plot \ "get.dat" using 1:3 with linespoints title "Tiny (unbalanced)", \ "get.dat" using 1:5 with linespoints title "FullFeatured (unbalanced)", \ "get-balanced.dat" using 1:3 with lines title "Tiny (balanced)", \ "get-balanced.dat" using 1:5 with lines title "FullFeatured (balanced)", \ log(x / 32.)/log(2.) with lines title "Optimal (|leaf|=32)" unset multiplot unset ylabel if (hardcopy) set output "sub.svg"; else set terminal wxt 3 raise set multiplot layout 2,1 downwards set key top left set title "Min. unitary random sub time" plot \ "sub.dat" using 1:2 with linespoints title "Tiny (unbalanced)", \ "sub.dat" using 1:4 with linespoints title "FullFeatured (unbalanced)", \ "sub.dat" using 1:6 with lines title "Tiny (balanced)", \ "sub.dat" using 1:8 with lines title "FullFeatured (balanced)" set title "" set ylabel "depth (average after sub)" plot \ "sub.dat" using 1:3 with linespoints title "Tiny (unbalanced)", \ "sub.dat" using 1:5 with linespoints title "FullFeatured (unbalanced)", \ "sub.dat" using 1:7 with lines title "Tiny (balanced)", \ "sub.dat" using 1:9 with lines title "FullFeatured (balanced)" unset multiplot unset logscale x if (hardcopy) set output "qsort.svg"; else set terminal wxt 4 raise set multiplot layout 2,1 downwards set key top left set title "Min. unitary random qsort time" plot \ "qsort.dat" using 1:2 with linespoints title "Tiny (unbalanced)", \ "qsort.dat" using 1:4 with linespoints title "FullFeatured (unbalanced)", \ "qsort.dat" using 1:6 with lines title "Tiny (balanced)", \ "qsort.dat" using 1:8 with lines title "FullFeatured (balanced)" set title "" set ylabel "depth (average after sub)" plot \ "qsort.dat" using 1:3 with linespoints title "Tiny (unbalanced)", \ "qsort.dat" using 1:5 with linespoints title "FullFeatured (unbalanced)", \ "qsort.dat" using 1:7 with lines title "Tiny (balanced)", \ "qsort.dat" using 1:9 with lines title "FullFeatured (balanced)" unset multiplot unset logscale x # Local Variables: # mode: gnuplot # End: ocaml-rope-0.6/bench/jbuild000066400000000000000000000004101320376225700156720ustar00rootroot00000000000000(jbuild_version 1) (executables ((names (bench_rope bm_ropes)) (libraries (rope benchmark)))) (alias ((name runtest) (deps (bench_rope.exe)) (action (run ${<})))) (alias ((name bench) (deps (bm_ropes.exe bm_ropes.plot)) (action (run ${<})))) ocaml-rope-0.6/bench/tinyRope.ml000066400000000000000000000262071320376225700166550ustar00rootroot00000000000000(* Rope: an implementation of the data structure described in Boehm, H., Atkinson, R., and Plass, M. 1995. Ropes: an alternative to strings. Softw. Pract. Exper. 25, 12 (Dec. 1995), 1315-1330. Motivated by Luca de Alfaro's extensible array implementation Vec. Copyright (C) 2007 Mauricio Fernandez http://eigenclass.org This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version, with the following special exception: You may link, statically or dynamically, a "work that uses the Library" with a publicly distributed version of the Library to produce an executable file containing portions of the Library, and distribute that executable file under terms of your choice, without any of the additional requirements listed in clause 6 of the GNU Library General Public License. By "a publicly distributed version of the Library", we mean either the unmodified Library as distributed by the author, or a modified version of the Library that is distributed under the conditions defined in clause 2 of the GNU Library General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Library General Public License. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. The GNU Lesser General Public License is available at http://www.gnu.org/copyleft/lgpl.html; to obtain it, you can also write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *) (* =begin ignore *) type t = Empty (* left, left size, right, right size, height *) | Concat of t * int * t * int * int | Leaf of string let height = function Empty | Leaf _ -> 0 | Concat(_,_,_,_,h) -> h (* For debugging and judging balancing *) let print = let rec print prefix = function | Empty -> print_string "\n" | Leaf s -> print_string prefix; print_string s; print_string "\n" | Concat(l,_, r,_, _) -> let prefixl = prefix ^ if height l = 0 then "/" else " " in let prefixr = prefix ^ if height r = 0 then "\\" else " " in print prefixl l; print prefixr r; in print "" type forest_element = { mutable c : t; mutable len : int } let str_append = (^) let empty_str = "" let string_of_string_list l = String.concat "" l module STRING = String (* 48 limits max rope size to 220GB on 64 bit, * ~ 700MB on 32bit (length fields overflow after that) *) let max_height = 48 (* actual size will be that plus 1 word header; * the code assumes it's an even num. * 256 gives up to a 50% overhead in the worst case (all leaf nodes near * half-filled *) let leaf_size = 256 (* let leaf_size = 128 *) (* let leaf_size = 64 *) (* let leaf_size = 32 *) (* =end *) (* =begin code *) exception Out_of_bounds let empty = Empty (* by construction, there cannot be Empty or Leaf "" leaves *) let is_empty = function Empty -> true | _ -> false let rec length = function Empty -> 0 | Leaf s -> STRING.length s | Concat(_,cl,_,cr,_) -> cl + cr let make_concat l r = let hl = height l and hr = height r in let cl = length l and cr = length r in Concat(l, cl, r, cr, if hl >= hr then hl + 1 else hr + 1) let min_len = let fib_tbl = Array.make max_height 0 in let rec fib n = match fib_tbl.(n) with 0 -> let last = fib (n - 1) and prev = fib (n - 2) in let r = last + prev in let r = if r > last then r else last in (* check overflow *) fib_tbl.(n) <- r; r | n -> n in fib_tbl.(0) <- leaf_size + 1; fib_tbl.(1) <- 3 * leaf_size / 2 + 1; Array.init max_height (fun i -> if i = 0 then 1 else fib (i - 1)) let max_length = min_len.(Array.length min_len - 1) let concat_fast l r = match l with Empty -> r | Leaf _ | Concat(_,_,_,_,_) -> match r with Empty -> l | Leaf _ | Concat(_,_,_,_,_) -> make_concat l r (* based on Hans-J. Boehm's *) let add_forest forest rope len = let i = ref 0 in let sum = ref empty in while len > min_len.(!i+1) do if forest.(!i).c <> Empty then begin sum := concat_fast forest.(!i).c !sum; forest.(!i).c <- Empty end; incr i done; sum := concat_fast !sum rope; let sum_len = ref (length !sum) in while !sum_len >= min_len.(!i) do if forest.(!i).c <> Empty then begin sum := concat_fast forest.(!i).c !sum; sum_len := !sum_len + forest.(!i).len; forest.(!i).c <- Empty; end; incr i done; decr i; forest.(!i).c <- !sum; forest.(!i).len <- !sum_len let concat_forest forest = Array.fold_left (fun s x -> concat_fast x.c s) Empty forest let rec balance_insert rope len forest = match rope with Empty -> () | Leaf _ -> add_forest forest rope len | Concat(l,cl,r,cr,h) when h >= max_height || len < min_len.(h) -> balance_insert l cl forest; balance_insert r cr forest | x -> add_forest forest x len (* function or balanced *) let balance r = match r with Empty -> Empty | Leaf _ -> r | _ -> let forest = Array.init max_height (fun _ -> {c = Empty; len = 0}) in balance_insert r (length r) forest; concat_forest forest let bal_if_needed l r = let r = make_concat l r in if height r < max_height then r else balance r let concat_str l = function Empty | Concat(_,_,_,_,_) -> invalid_arg "concat_str" | Leaf rs as r -> let lenr = STRING.length rs in match l with | Empty -> r | Leaf ls -> let slen = lenr + STRING.length ls in if slen <= leaf_size then Leaf (str_append ls rs) else make_concat l r (* height = 1 *) | Concat(ll, cll, Leaf lrs, clr, h) -> let slen = clr + lenr in if clr + lenr <= leaf_size then Concat(ll, cll, Leaf (str_append lrs rs), slen, h) else bal_if_needed l r | _ -> bal_if_needed l r let append_char c r = concat_str r (Leaf (STRING.make 1 c)) let concat l = function Empty -> l | Leaf _ as r -> concat_str l r | Concat(Leaf rls,rlc,rr,rc,h) as r -> (match l with Empty -> r | Concat(_,_,_,_,_) -> bal_if_needed l r | Leaf ls -> let slen = rlc + STRING.length ls in if slen <= leaf_size then Concat(Leaf(str_append ls rls), slen, rr, rc, h) else bal_if_needed l r) | r -> (match l with Empty -> r | _ -> bal_if_needed l r) let prepend_char c r = concat (Leaf (STRING.make 1 c)) r let rec get i = function Empty -> raise Out_of_bounds | Leaf s -> if i >= 0 && i < STRING.length s then STRING.unsafe_get s i else raise Out_of_bounds | Concat (l, cl, r, cr, _) -> if i < cl then get i l else get (i - cl) r let rec getn i = function Empty -> raise Out_of_bounds | Leaf s -> 0 | Concat (l, cl, r, cr, _) -> if i < cl then 1 + getn i l else 1 + getn (i - cl) r let rec set i v = function Empty -> raise Out_of_bounds | Leaf s -> if i >= 0 && i < STRING.length s then let s = Bytes.of_string s in Bytes.unsafe_set s i v; Leaf (Bytes.unsafe_to_string s) else raise Out_of_bounds | Concat(l, cl, r, cr, _) -> if i < cl then concat (set i v l) r else concat l (set (i - cl) v r) let of_string = function s when STRING.length s = 0 -> Empty | s -> let min (x:int) (y:int) = if x <= y then x else y in let rec loop r s len i = if i < len then (* len - i > 0, thus Leaf "" can't happen *) loop (concat r (Leaf (STRING.sub s i (min (len - i) leaf_size)))) s len (i + leaf_size) else r in loop Empty s (STRING.length s) 0 let rec make len c = let rec concatloop len i r = if i <= len then concatloop len (i * 2) (concat r r) else r in if len = 0 then Empty else if len <= leaf_size then Leaf (STRING.make len c) else let rope = concatloop len 2 (of_string (STRING.make 1 c)) in concat rope (make (len - length rope) c) let rec sub start len = function Empty -> if start <> 0 || len <> 0 then raise Out_of_bounds else Empty | Leaf s -> if len > 0 then (* Leaf "" cannot happen *) (try Leaf (STRING.sub s start len) with _ -> raise Out_of_bounds) else if len < 0 || start < 0 || start > STRING.length s then raise Out_of_bounds else Empty | Concat(l,cl,r,cr,_) -> if start < 0 || len < 0 || start + len > cl + cr then raise Out_of_bounds; let left = if start = 0 then if len >= cl then l else sub 0 len l else if start > cl then Empty else if start + len >= cl then sub start (cl - start) l else sub start len l in let right = if start <= cl then let upto = start + len in if upto = cl + cr then r else if upto < cl then Empty else sub 0 (upto - cl) r else sub (start - cl) len r in concat left right let insert start rope r = concat (concat (sub 0 start r) rope) (sub start (length r - start) r) let remove start len r = concat (sub 0 start r) (sub (start + len) (length r - start - len) r) let to_string r = let rec strings l = function Empty -> l | Leaf s -> s :: l | Concat(left,_,right,_,_) -> strings (strings l right) left in string_of_string_list (strings [] r) let rec iter f = function Empty -> () | Leaf s -> STRING.iter f s | Concat(l,_,r,_,_) -> iter f l; iter f r let iteri f r = let rec aux f i = function Empty -> () | Leaf s -> for j = 0 to STRING.length s - 1 do f (i + j) (STRING.unsafe_get s j) done | Concat(l,cl,r,_,_) -> aux f i l; aux f (i + cl) r in aux f 0 r let rec rangeiter f start len = function Empty -> if start <> 0 || len <> 0 then raise Out_of_bounds | Leaf s -> let n = start + len in let lens = STRING.length s in if start >= 0 && len >= 0 && n <= lens then for i = start to n - 1 do f (STRING.unsafe_get s i) done else raise Out_of_bounds | Concat(l,cl,r,cr,_) -> if start < 0 || len < 0 || start + len > cl + cr then raise Out_of_bounds; if start < cl then begin let upto = start + len in if upto <= cl then rangeiter f start len l else begin rangeiter f start (cl - start) l; rangeiter f 0 (upto - cl) r end end else begin rangeiter f (start - cl) len r end let rec fold f a = function Empty -> a | Leaf s -> let acc = ref a in for i = 0 to STRING.length s - 1 do acc := f !acc (STRING.unsafe_get s i) done; !acc | Concat(l,_,r,_,_) -> fold f (fold f a l) r (* =end *) ocaml-rope-0.6/bench/tinyRope.mli000066400000000000000000000166651320376225700170350ustar00rootroot00000000000000(* Rope: a simple implementation of ropes as described in Boehm, H., Atkinson, R., and Plass, M. 1995. Ropes: an alternative to strings. Softw. Pract. Exper. 25, 12 (Dec. 1995), 1315-1330. Motivated by Luca de Alfaro's extensible array implementation Vec. Copyright (C) 2007 Mauricio Fernandez http://eigenclass.org This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version, with the following special exception: You may link, statically or dynamically, a "work that uses the Library" with a publicly distributed version of the Library to produce an executable file containing portions of the Library, and distribute that executable file under terms of your choice, without any of the additional requirements listed in clause 6 of the GNU Library General Public License. By "a publicly distributed version of the Library", we mean either the unmodified Library as distributed by the author, or a modified version of the Library that is distributed under the conditions defined in clause 2 of the GNU Library General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Library General Public License. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. The GNU Library General Public License is available at http://www.gnu.org/copyleft/lgpl.html; to obtain it, you can also write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) (** Heavyweight strings ("ropes") This module implements ropes as described in Boehm, H., Atkinson, R., and Plass, M. 1995. Ropes: an alternative to strings. Softw. Pract. Exper. 25, 12 (Dec. 1995), 1315-1330. Ropes are an alternative to strings which support efficient operations: - determining the length of a rope in constant time - appending or prepending a small rope to an arbitrarily large one in amortized constant time - concat, substring, insert, remove operations in amortized logarithmic time - access to and modification of ropes in logarithmic time {8 Functional nature and persistence} All operations are non-destructive: the original rope is never modified. When a new rope is returned as the result of an operation, it will share as much data as possible with its "parent". For instance, if a rope of length [n] undergoes [m] operations (assume [n >> m]) like set, append or prepend, the modified rope will only require [O(m)] space in addition to that taken by the original one. However, Rope is an amortized data structure, and its use in a persistent setting can easily degrade its amortized time bounds. It is thus mainly intended to be used ephemerally. In some cases, it is possible to use Rope persistently with the same amortized bounds by explicitly rebalancing ropes to be reused using [balance]. Special care must be taken to avoid calling [balance] too frequently; in the limit, calling [balance] after each modification would defeat the purpose of amortization. @author Mauricio Fernandez *) type t (** The type of the ropes. *) exception Out_of_bounds (** Raised when an operation violates the bounds of the rope. *) val max_length : int (** Maximum length of the rope. *) (** {6 Creation and conversions} *) val empty : t (** The empty rope. *) val of_string : string -> t (** [of_string s] returns a rope corresponding to the string [s]. Operates in [O(n)] time. *) val to_string : t -> string (** [to_string r] returns the string corresponding to the rope [r]. *) val make : int -> char -> t (** [make i c] returns a rope of length [i] consisting of [c] chars; it is similar to String.make *) (** {6 Properties } *) val is_empty : t -> bool (** Returns whether the rope is empty or not. *) val length : t -> int (** Returns the length of the rope ([O(1)]). *) val height : t -> int (** Returns the height (depth) of the rope. *) val balance : t -> t (** [balance r] returns a balanced copy of the [r] rope. Note that ropes are automatically rebalanced when their height exceeds a given threshold, but [balance] allows to invoke that operation explicity. *) (** {6 Operations } *) val concat : t -> t -> t (** [concat r u] concatenates the [r] and [u] ropes. In general, it operates in [O(log(min n1 n2))] amortized time. Small ropes are treated specially and can be appended/prepended in amortized [O(1)] time. *) val append_char : char -> t -> t (** [append_char c r] returns a new rope with the [c] character at the end in amortized [O(1)] time. *) val prepend_char : char -> t -> t (** [prepend_char c r] returns a new rope with the [c] character at the beginning in amortized [O(1)] time. *) val get : int -> t -> char (** [get n r] returns the (n+1)th character from the rope [r]; i.e. [get 0 r] returns the first character. Operates in worst-case [O(log size)] time. Raises Out_of_bounds if a character out of bounds is requested. *) val set : int -> char -> t -> t (** [set n c r] returns a copy of the [r] rope where the (n+1)th character (see also [get]) has been set to [c]. Operates in worst-case [O(log size)] time. *) val sub : int -> int -> t -> t (** [sub m n r] returns a sub-rope of [r] containing all characters whose indexes range from [m] to [m + n - 1] (included). Raises Out_of_bounds in the same cases as String.sub. Operates in worst-case [O(log size)] time. *) val insert : int -> t -> t -> t (** [insert n r u] returns a copy of the [u] rope where [r] has been inserted between the characters with index [n] and [n + 1] in the original string. The length of the new rope is [length u + length r]. Operates in amortized [O(log(size r) + log(size u))] time. *) val remove : int -> int -> t -> t (** [remove m n r] returns the rope resulting from deleting the characters with indexes ranging from [m] to [m + n - 1] (included) from the original rope [r]. The length of the new rope is [length r - n]. Operates in amortized [O(log(size r))] time. *) (** {6 Iteration} *) val iter : (char -> unit) -> t -> unit (** [iter f r] applies [f] to all the characters in the [r] rope, in order. *) val iteri : (int -> char -> unit) -> t -> unit (** Operates like iter, but also passes the index of the character to the given function. *) val rangeiter : (char -> unit) -> int -> int -> t -> unit (** [rangeiter f m n r] applies [f] to all the characters whose indices [k] satisfy [m] <= [k] < [m + n]. It is thus equivalent to [iter f (sub m n r)], but does not create an intermediary rope. [rangeiter] operates in worst-case [O(n + log m)] time, which improves on the [O(n log m)] bound from an explicit loop using [get]. Raises Out_of_bounds in the same cases as [sub]. *) val fold : ('a -> char -> 'a ) -> 'a -> t -> 'a (** [Rope.fold f a r] computes [ f (... (f (f a r0) r1)...) rN-1 ] where [rn = Rope.get n r ] and [N = length r]. *) (**/**) val print : t -> unit val getn : int -> t -> int ocaml-rope-0.6/pkg/000077500000000000000000000000001320376225700142055ustar00rootroot00000000000000ocaml-rope-0.6/pkg/pkg.ml000066400000000000000000000000561320376225700153210ustar00rootroot00000000000000#use "topfind" #require "topkg-jbuilder.auto" ocaml-rope-0.6/rope.descr000066400000000000000000000006301320376225700154120ustar00rootroot00000000000000Ropes ("heavyweight strings") Ropes ("heavyweight strings") are a scalable string implementation: they are designed for efficient operation that involve the string as a whole. Operations such as concatenation, and substring take time that is nearly independent of the length of the string. Unlike strings, ropes are a reasonable representation for very long strings such as edit buffers or mail messages. ocaml-rope-0.6/rope.opam000066400000000000000000000012361320376225700152510ustar00rootroot00000000000000opam-version: "1.2" maintainer: "Christophe Troestler " authors: [ "Christophe Troestler" ] license: "LGPL-2.1 with OCaml linking exception" homepage: "https://github.com/Chris00/ocaml-rope" dev-repo: "https://github.com/Chris00/ocaml-rope.git" bug-reports: "https://github.com/Chris00/ocaml-rope/issues" doc: "https://Chris00.github.io/ocaml-rope/doc" tags: [ "datastructure" ] build: [ [ "jbuilder" "subst" ] {pinned} [ "jbuilder" "build" "-p" name "-j" jobs ] ] build-test: [["jbuilder" "runtest" "-p" name "-j" jobs]] depends: [ "base-bytes" "jbuilder" {build} "benchmark" {test} ] available: [ocaml-version >= "4.03.0"] ocaml-rope-0.6/src/000077500000000000000000000000001320376225700142135ustar00rootroot00000000000000ocaml-rope-0.6/src/jbuild000066400000000000000000000002651320376225700154120ustar00rootroot00000000000000(jbuild_version 1) (library ((name rope) (public_name rope) (flags (:standard -safe-string)) (libraries (bytes)) (synopsis "Ropes (heavyweight strings)"))) ocaml-rope-0.6/src/rope.ml000066400000000000000000001340331320376225700155160ustar00rootroot00000000000000(* File: rope.ml Copyright (C) 2007 Christophe Troestler email: Christophe.Troestler@umh.ac.be WWW: http://math.umh.ac.be/an/software/ This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2.1 or later as published by the Free Software Foundation, with the special exception on linking described in the file LICENSE. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file LICENSE for more details. *) (** Rope implementation inspired from : Hans Boehm, Russ Atkinson, Michael Plass, "Ropes: an alternative to strings", Software Practice and Experience 25, vol. 12 (1995), pp. 1315-1330. http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf *) (* TODO: - Regexp (maybe using Jérôme Vouillon regexp lib ? http://www.pps.jussieu.fr/~vouillon/) - Camomille interop. (with phantom types for encoding ??) See also the OSR http://cocan.org/osr/unicode_library *) let min i j = if (i:int) < j then i else j let max i j = if (i:int) > j then i else j exception Out_of_bounds of string (* One assumes throughout that the length is a representable integer. Public functions that allow to construct larger ropes must check this. *) type t = | Sub of string * int * int (* (s, i0, len) where only s.[i0 .. i0+len-1] is used by the rope. [len = 0] is forbidden, unless the rope has 0 length (i.e. it is empty). Experiments show that this is faster than [Sub of string] and does not really use more memory -- because splices share all nodes. *) | Concat of int * int * t * int * t (* [(height, length, left, left_length, right)]. This asymmetry between left and right was chosen because the full length and the left length are more often needed that the right length. *) type rope = t let small_rope_length = 32 (** Use this as leaf when creating fresh leaves. Also sub-ropes of length [<= small_rope_length] may be flattened by [concat2]. This value must be quite small, typically comparable to the size of a [Concat] node. *) let make_length_pow2 = 10 let make_length = 1 lsl make_length_pow2 let max_flatten_length = 1024 (** When deciding whether to flatten a rope, only those with length [<= max_flatten_length] will be. *) let extract_sub_length = small_rope_length / 2 (** When balancing, copy the substrings with this length or less (=> release the original string). *) let level_flatten = 12 (** When balancing, flatten the rope at level [level_flatten]. The sum of [min_length.(n)], [0 <= n <= level_flatten] must be of te same order as [max_flatten_length]. *) (* Fibonacci numbers $F_{n+2}$. By definition, a NON-EMPTY rope [r] is balanced iff [length r >= min_length.(height r)]. [max_height] is the first height at which the fib. number overflow the integer range. *) let min_length, max_height = (* Since F_{n+2} >= ((1 + sqrt 5)/2)^n, we know F_{d+2} will overflow: *) let d = (3 * Sys.word_size) / 2 in let m = Array.make d max_int in (* See [add_nonempty_to_forest] for the reason for [max_int] *) let prev = ref 0 and last = ref 1 and i = ref 0 in try while !i < d - 1 do let curr = !last + !prev in if curr < !last (* overflow *) then raise Exit; m.(!i) <- curr; prev := !last; last := curr; incr i done; assert false with Exit -> m, !i let rebalancing_height = min (max_height - 1) 60 (** Beyond this height, implicit balance will be done. This value allows gross inefficiencies while not being too time consuming. For example, explicit rebalancing did not really improve the running time on the ICFP 2007 task. *) (* 32 bits: max_height - 1 = 42 *) let empty = Sub("", 0, 0) let length = function | Sub(_, _, len) -> len | Concat(_,len,_,_,_) -> len let height = function | Sub(_,_,_) -> 0 | Concat(h,_,_,_,_) -> h let is_empty = function | Sub(_, _, len) -> len = 0 | _ -> false let is_not_empty = function | Sub(_, _, len) -> len <> 0 | _ -> true (* For debugging purposes and judging the balancing *) let print = let rec map_left = function | [] -> [] | [x] -> ["/" ^ x] | x :: tl -> (" " ^ x) :: map_left tl in let map_right = function | [] -> [] | x :: tl -> ("\\" ^ x) :: List.map (fun r -> " " ^ r) tl in let rec leaves_list = function | Sub(s, i0, len) -> [String.sub s i0 len] | Concat(_,_, l,_, r) -> map_left(leaves_list l) @ map_right(leaves_list r) in fun r -> List.iter print_endline (leaves_list r) ;; let of_string s = Sub(s, 0, String.length s) (* safe: string is now immutable *) (* Since we will need to copy the string anyway, let us take this opportunity to split it in small chunks for easier further sharing. In order to minimize the height, we use a simple bisection scheme. *) let rec unsafe_of_substring s i len = if len <= small_rope_length then Sub(String.sub s i len, 0, len) else let len' = len / 2 in let i' = i + len' in let left = unsafe_of_substring s i len' and right = unsafe_of_substring s i' (len - len') in let h = 1 + max (height left) (height right) in let ll = length left in Concat(h, ll + length right, left, ll, right) let of_substring s i len = let len_s = String.length s in if i < 0 || len < 0 || i > len_s - len then invalid_arg "Rope.of_substring"; (* If only a small percentage of the string is not in the rope, do not cut the string in small pieces. The case of small lengths is managed by [unsafe_of_substring]. *) if len >= len_s - (len / 10) then Sub(s, i, len) else unsafe_of_substring s i len let of_char c = Sub(String.make 1 c, 0, 1) (* Construct a rope from [n-1] copies of a call to [gen ofs len] of length [len = make_length] and a last call with the remainder length. So the tree has [n] leaves [Sub]. The strings returned by [gen ofs len] may be longer than [len] of only the first [len] chars will be used. *) let rec make_of_gen gen ofs len ~n = if n <= 1 then if len > 0 then Sub(gen ofs len, 0, len) else empty else let nl = n / 2 in let ll = nl * max_flatten_length in let l = make_of_gen gen ofs ll ~n:nl in let r = make_of_gen gen (ofs + ll) (len - ll) ~n:(n - nl) in Concat(1 + max (height l) (height r), len, l, ll, r) let make_length_mask = make_length - 1 let make_n_chunks len = if len land make_length_mask = 0 then len lsr make_length_pow2 else len lsr make_length_pow2 + 1 let make len c = if len < 0 then failwith "Rope.make: len must be >= 0"; if len <= make_length then Sub(String.make len c, 0, len) else let base = String.make make_length c in make_of_gen (fun _ _ -> base) 0 len ~n:(make_n_chunks len) let init len f = if len < 0 then failwith "Rope.init: len must be >= 0"; if len <= make_length then Sub(String.init len f, 0, len) else (* Do not use String.init to avoid creating a closure. *) let gen ofs len = let b = Bytes.create len in for i = 0 to len - 1 do Bytes.set b i (f (ofs + i)) done; Bytes.unsafe_to_string b in make_of_gen gen 0 len ~n:(make_n_chunks len) (* [copy_to_subbytes t ofs r] copy the rope [r] to the byte range [t.[ofs .. ofs+(length r)-1]]. It is assumed that [t] is long enough. (This function could be a one liner with [iteri] but we want to use [Bytes.blit_string] for efficiency.) *) let rec copy_to_subbytes t ofs = function | Sub(s, i0, len) -> Bytes.blit_string s i0 t ofs len | Concat(_, _, l,ll, r) -> copy_to_subbytes t ofs l; copy_to_subbytes t (ofs + ll) r let to_string = function | Sub(s, i0, len) -> (* Optimize when the rope hold a single string. *) if i0 = 0 && len = String.length s then s else String.sub s i0 len | r -> let len = length r in if len > Sys.max_string_length then failwith "Rope.to_string: rope length > Sys.max_string_length"; let t = Bytes.create len in copy_to_subbytes t 0 r; Bytes.unsafe_to_string t (* Similar to [copy_to_subbytes] do more work to allow specifying a range of [src]. *) let rec unsafe_blit src srcofs dst dstofs len = match src with | Sub(s, i0, _) -> String.blit s (i0 + srcofs) dst dstofs len | Concat(_, _, l, ll, r) -> let rofs = srcofs - ll in if rofs >= 0 then unsafe_blit r rofs dst dstofs len else let llen = - rofs in (* # of chars after [srcofs] in the left rope *) if len <= llen then unsafe_blit l srcofs dst dstofs len else (* len > llen *) ( unsafe_blit l srcofs dst dstofs llen; unsafe_blit r 0 dst (dstofs + llen) (len - llen); ) let blit src srcofs dst dstofs len = if len < 0 then failwith "Rope.blit: len >= 0 required"; if srcofs < 0 || srcofs > length src - len then failwith "Rope.blit: not a valid range of src"; if dstofs < 0 || dstofs > Bytes.length dst - len then failwith "Rope.blit: not a valid range of dst"; unsafe_blit src srcofs dst dstofs len (* Flatten a rope (avoids unecessary copying). *) let flatten = function | Sub(_,_,_) as r -> r | r -> let len = length r in assert(len <= Sys.max_string_length); let t = Bytes.create len in copy_to_subbytes t 0 r; Sub(Bytes.unsafe_to_string t, 0, len) let rec get rope i = match rope with | Sub(s, i0, len) -> if i < 0 || i >= len then raise(Out_of_bounds "Rope.get") else s.[i0 + i] | Concat(_,_, l, left_len, r) -> if i < left_len then get l i else get r (i - left_len) let rec iter f = function | Sub(s, i0, len) -> for i = i0 to i0 + len - 1 do f s.[i] done | Concat(_, _, l,_, r) -> iter f l; iter f r let rec iteri_rec f init = function | Sub(s, i0, len) -> let offset = init - i0 in for i = i0 to i0 + len - 1 do f (i + offset) s.[i] done | Concat(_, _, l,ll, r) -> iteri_rec f init l; iteri_rec f (init + ll) r let iteri f r = ignore(iteri_rec f 0 r) let rec map ~f = function | Sub(s, i0, len) -> let b = Bytes.create len in for i = 0 to len - 1 do Bytes.set b i (f (String.unsafe_get s (i0 + i))) done; Sub(Bytes.unsafe_to_string b, 0, len) | Concat(h, len, l, ll, r) -> let l = map ~f l in let r = map ~f r in Concat(h, len, l, ll, r) let rec mapi_rec ~f idx0 = function | Sub(s, i0, len) -> let b = Bytes.create len in for i = 0 to len - 1 do Bytes.set b i (f (idx0 + i) s.[i0 + i]) done; Sub(Bytes.unsafe_to_string b, 0, len) | Concat(h, len, l, ll, r) -> let l = mapi_rec ~f idx0 l in let r = mapi_rec ~f (idx0 + ll) r in Concat(h, len, l, ll, r) let mapi ~f r = mapi_rec ~f 0 r (** Balancing ***********************************************************************) (* Fast, no fuss, concatenation. *) let balance_concat rope1 rope2 = let len1 = length rope1 and len2 = length rope2 in if len1 = 0 then rope2 else if len2 = 0 then rope1 else let h = 1 + max (height rope1) (height rope2) in Concat(h, len1 + len2, rope1, len1, rope2) (* Invariants for [forest]: 1) The concatenation of the forest (in decreasing order) with the unscanned part of the rope is equal to the rope being balanced. 2) All trees in the forest are balanced, i.e. [forest.(n)] is empty or [length forest.(n) >= min_length.(n)]. 3) [height forest.(n) <= n] *) (* Add the rope [r] (usually a leaf) to the appropriate slot of [forest] (according to [length r]) gathering ropes from lower levels if necessary. Assume [r] is not empty. *) let add_nonempty_to_forest forest r = let len = length r in let n = ref 0 in let sum = ref empty in (* forest.(n-1) ^ ... ^ (forest.(2) ^ (forest.(1) ^ forest.(0))) with [n] s.t. [min_length.(n) < len <= min_length.(n+1)]. [n] is at most [max_height-1] because [min_length.(max_height) = max_int] *) while len > min_length.(!n + 1) do if is_not_empty forest.(!n) then ( sum := balance_concat forest.(!n) !sum; forest.(!n) <- empty; ); if !n = level_flatten then sum := flatten !sum; incr n done; (* Height of [sum] at most 1 greater than what would be required for balance. *) sum := balance_concat !sum r; (* If [height r <= !n - 1] (e.g. if [r] is a leaf), then [!sum] is now balanced -- distinguish whether forest.(!n - 1) is empty or not (see the cited paper pp. 1319-1320). We now continue concatenating ropes until the result fits into an empty slot of the [forest]. *) let sum_len = ref(length !sum) in while !n < max_height && !sum_len >= min_length.(!n) do if is_not_empty forest.(!n) then ( sum := balance_concat forest.(!n) !sum; sum_len := length forest.(!n) + !sum_len; forest.(!n) <- empty; ); if !n = level_flatten then sum := flatten !sum; incr n done; decr n; forest.(!n) <- !sum let add_to_forest forest r = if is_not_empty r then add_nonempty_to_forest forest r (* Add a NON-EMPTY rope [r] to the forest *) let rec balance_insert forest rope = match rope with | Sub(s, i0, len) -> (* If the length of the leaf is small w.r.t. the length of [s], extract it to avoid keeping a ref the larger [s]. *) if 25 * len <= String.length s then add_nonempty_to_forest forest (Sub(String.sub s i0 len, 0, len)) else add_nonempty_to_forest forest rope | Concat(h, len, l,_, r) -> (* FIXME: when to rebalance subtrees *) if h >= max_height || len < min_length.(h) then ( (* sub-rope needs rebalancing *) balance_insert forest l; balance_insert forest r; ) else add_nonempty_to_forest forest rope ;; let concat_forest forest = let concat (n, sum) r = let sum = balance_concat r sum in (n+1, if n = level_flatten then flatten sum else sum) in snd(Array.fold_left concat (0,empty) forest) let balance = function | Sub(s, i0, len) as r -> if 0 < len && len <= extract_sub_length then Sub(String.sub s i0 len, 0, len) else r | r -> let forest = Array.make max_height empty in balance_insert forest r; concat_forest forest (* Only rebalance on the height. Also doing it when [length r < min_length.(height r)] ask for too many balancing and thus is slower. *) let balance_if_needed r = if height r >= rebalancing_height then balance r else r (** "Fast" concat for ropes. ********************************************************************** * Since concat is one of the few ways a rope can be constructed, it * must be fast. Also, this means it is this concat which is * responsible for the height of small ropes (until balance kicks in * but the later the better). *) exception Relocation_failure (* Internal exception *) (* Try to relocate the [leaf] at a position that will not increase the height. [length(relocate_topright rope leaf _)= length rope + length leaf] [height(relocate_topright rope leaf _) = height rope] *) let rec relocate_topright rope leaf len_leaf = match rope with | Sub(_,_,_) -> raise Relocation_failure | Concat(h, len, l,ll, r) -> let hr = height r + 1 in if hr < h then (* Success, we can insert the leaf here without increasing the height *) let lr = length r in Concat(h, len + len_leaf, l,ll, Concat(hr, lr + len_leaf, r, lr, leaf)) else (* Try at the next level *) Concat(h, len + len_leaf, l,ll, relocate_topright r leaf len_leaf) let rec relocate_topleft leaf len_leaf rope = match rope with | Sub(_,_,_) -> raise Relocation_failure | Concat(h, len, l,ll, r) -> let hl = height l + 1 in if hl < h then (* Success, we can insert the leaf here without increasing the height *) let len_left = len_leaf + ll in let left = Concat(hl, len_left, leaf, len_leaf, l) in Concat(h, len_leaf + len, left, len_left, r) else (* Try at the next level *) let left = relocate_topleft leaf len_leaf l in Concat(h, len_leaf + len, left, len_leaf + ll, r) (* We avoid copying too much -- as this may slow down access, even if height is lower. *) let concat2_nonempty rope1 rope2 = match rope1, rope2 with | Sub(s1,i1,len1), Sub(s2,i2,len2) -> let len = len1 + len2 in if len <= small_rope_length then let s = Bytes.create len in Bytes.blit_string s1 i1 s 0 len1; Bytes.blit_string s2 i2 s len1 len2; Sub(Bytes.unsafe_to_string s, 0, len) else Concat(1, len, rope1, len1, rope2) | Concat(h1, len1, l1,ll1, (Sub(s1, i1, lens1) as leaf1)), _ when h1 > height rope2 -> let len2 = length rope2 in let len = len1 + len2 and lens = lens1 + len2 in if lens <= small_rope_length then let s = Bytes.create lens in Bytes.blit_string s1 i1 s 0 lens1; copy_to_subbytes s lens1 rope2; Concat(h1, len, l1,ll1, Sub(Bytes.unsafe_to_string s, 0, lens)) else begin try let left = relocate_topright l1 leaf1 lens1 in (* [h1 = height l1 + 1] since the right branch is a leaf and [height l1 = height left]. *) Concat(max h1 (1 + height rope2), len, left, len1, rope2) with Relocation_failure -> let h2plus1 = height rope2 + 1 in (* if replacing [leaf1] will increase the height or if further concat will have an opportunity to add to a (small) leaf *) if (h1 = h2plus1 && len2 <= max_flatten_length) || len2 < small_rope_length then Concat(h1 + 1, len, rope1, len1, flatten rope2) else (* [h1 > h2 + 1] *) let right = Concat(h2plus1, lens, leaf1, lens1, rope2) in Concat(h1, len, l1, ll1, right) end | _, Concat(h2, len2, (Sub(s2, i2, lens2) as leaf2),_, r2) when height rope1 < h2 -> let len1 = length rope1 in let len = len1 + len2 and lens = len1 + lens2 in if lens <= small_rope_length then let s = Bytes.create lens in copy_to_subbytes s 0 rope1; Bytes.blit_string s2 i2 s len1 lens2; Concat(h2, len, Sub(Bytes.unsafe_to_string s, 0, lens), lens, r2) else begin try let right = relocate_topleft leaf2 lens2 r2 in (* [h2 = height r2 + 1] since the left branch is a leaf and [height r2 = height right]. *) Concat(max (1 + height rope1) h2, len, rope1, len1, right) with Relocation_failure -> let h1plus1 = height rope1 + 1 in (* if replacing [leaf2] will increase the height or if further concat will have an opportunity to add to a (small) leaf *) if (h1plus1 = h2 && len1 <= max_flatten_length) || len1 < small_rope_length then Concat(h2 + 1, len, flatten rope1, len1, rope2) else (* [h1 + 1 < h2] *) let left = Concat(h1plus1, lens, rope1, len1, leaf2) in Concat(h2, len, left, lens, r2) end | _, _ -> let len1 = length rope1 and len2 = length rope2 in let len = len1 + len2 in (* Small unbalanced ropes may happen if one concat left, then right, then left,... This costs a bit of time but is a good defense. *) if len <= small_rope_length then let s = Bytes.create len in copy_to_subbytes s 0 rope1; copy_to_subbytes s len1 rope2; Sub(Bytes.unsafe_to_string s, 0, len) else begin let rope1 = if len1 <= small_rope_length then flatten rope1 else rope1 and rope2 = if len2 <= small_rope_length then flatten rope2 else rope2 in let h = 1 + max (height rope1) (height rope2) in Concat(h, len1 + len2, rope1, len1, rope2) end ;; let concat2 rope1 rope2 = let len1 = length rope1 and len2 = length rope2 in let len = len1 + len2 in if len1 = 0 then rope2 else if len2 = 0 then rope1 else begin if len < len1 (* overflow *) then failwith "Rope.concat2: the length of the resulting rope exceeds max_int"; let h = 1 + max (height rope1) (height rope2) in if h >= rebalancing_height then (* We will need to rebalance anyway, so do a simple concat *) balance (Concat(h, len, rope1, len1, rope2)) else (* No automatic rebalancing -- experimentally lead to faster exec *) concat2_nonempty rope1 rope2 end ;; (** Subrope ***********************************************************************) (** [sub_to_substring flat j i len r] copies the subrope of [r] starting at character [i] and of length [len] to [flat.[j ..]]. *) let rec sub_to_substring flat j i len = function | Sub(s, i0, _) -> Bytes.blit_string s (i0 + i) flat j len | Concat(_, _, l, ll, r) -> let ri = i - ll in if ri >= 0 then (* only right branch *) sub_to_substring flat j ri len r else (* ri < 0 *) let lenr = ri + len in if lenr <= 0 then (* only left branch *) sub_to_substring flat j i len l else ( (* at least one char from the left and right branches *) sub_to_substring flat j i (-ri) l; sub_to_substring flat (j - ri) 0 lenr r; ) let flatten_subrope rope i len = assert(len <= Sys.max_string_length); let flat = Bytes.create len in sub_to_substring flat 0 i len rope; Sub(Bytes.unsafe_to_string flat, 0, len) ;; (* Are lazy sub-rope nodes really needed? *) (* This function assumes that [i], [len] define a valid sub-rope of the last arg. *) let rec sub_rec i len = function | Sub(s, i0, lens) -> assert(i >= 0 && i <= lens - len); Sub(s, i0 + i, len) | Concat(_, rope_len, l, ll, r) -> let rl = rope_len - ll in let ri = i - ll in if ri >= 0 then if len = rl then r (* => ri = 0 -- full right sub-rope *) else sub_rec ri len r else let rlen = ri + len (* = i + len - ll *) in if rlen <= 0 then (* right sub-rope empty *) if len = ll then l (* => i = 0 -- full left sub-rope *) else sub_rec i len l else (* at least one char from the left and right sub-ropes *) let l' = if i = 0 then l else sub_rec i (-ri) l and r' = if rlen = rl then r else sub_rec 0 rlen r in let h = 1 + max (height l') (height r') in (* FIXME: do we have to use this opportunity to flatten some subtrees? In any case, the height of tree we get is no worse than the initial tree (but the length may be much smaller). *) Concat(h, len, l', -ri, r') let sub rope i len = let len_rope = length rope in if i < 0 || len < 0 || i > len_rope - len then invalid_arg "Rope.sub" else if len = 0 then empty else if len <= max_flatten_length && len_rope >= 32768 then (* The benefit of flattening such subropes (and constants) has been seen experimentally. It is not clear what the "exact" rule should be. *) flatten_subrope rope i len else sub_rec i len rope (** String alike functions ***********************************************************************) let is_space = function | ' ' | '\012' | '\n' | '\r' | '\t' -> true | _ -> false let rec trim_left = function | Sub(s, i0, len) -> let i = ref i0 in let i_max = i0 + len in while !i < i_max && is_space (String.unsafe_get s !i) do incr i done; if !i = i_max then empty else Sub(s, !i, i_max - !i) | Concat(_, _, l, _, r) -> let l = trim_left l in if is_empty l then trim_left r else let ll = length l in Concat(1 + max (height l) (height r), ll + length r, l, ll, r) let rec trim_right = function | Sub(s, i0, len) -> let i = ref (i0 + len - 1) in while !i >= i0 && is_space (String.unsafe_get s !i) do decr i done; if !i < i0 then empty else Sub(s, i0, !i - i0 + 1) | Concat(_, _, l, ll, r) -> let r = trim_right r in if is_empty r then trim_right l else let lr = length r in Concat(1 + max (height l) (height r), ll + lr, l, ll, r) let trim r = trim_right(trim_left r) (* Escape the range s.[i0 .. i0+len-1]. Modeled after Bytes.escaped *) let escaped_sub s i0 len = let n = ref 0 in let i1 = i0 + len - 1 in for i = i0 to i1 do n := !n + (match String.unsafe_get s i with | '\"' | '\\' | '\n' | '\t' | '\r' | '\b' -> 2 | ' ' .. '~' -> 1 | _ -> 4) done; if !n = len then Sub(s, i0, len) else ( let s' = Bytes.create !n in n := 0; for i = i0 to i1 do (match String.unsafe_get s i with | ('\"' | '\\') as c -> Bytes.unsafe_set s' !n '\\'; incr n; Bytes.unsafe_set s' !n c | '\n' -> Bytes.unsafe_set s' !n '\\'; incr n; Bytes.unsafe_set s' !n 'n' | '\t' -> Bytes.unsafe_set s' !n '\\'; incr n; Bytes.unsafe_set s' !n 't' | '\r' -> Bytes.unsafe_set s' !n '\\'; incr n; Bytes.unsafe_set s' !n 'r' | '\b' -> Bytes.unsafe_set s' !n '\\'; incr n; Bytes.unsafe_set s' !n 'b' | (' ' .. '~') as c -> Bytes.unsafe_set s' !n c | c -> let a = Char.code c in Bytes.unsafe_set s' !n '\\'; incr n; Bytes.unsafe_set s' !n (Char.chr (48 + a / 100)); incr n; Bytes.unsafe_set s' !n (Char.chr (48 + (a / 10) mod 10)); incr n; Bytes.unsafe_set s' !n (Char.chr (48 + a mod 10)); ); incr n done; Sub(Bytes.unsafe_to_string s', 0, !n) ) let rec escaped = function | Sub(s, i0, len) -> escaped_sub s i0 len | Concat(h, _, l, _, r) -> let l = escaped l in let ll = length l in let r = escaped r in Concat(h, ll + length r, l, ll, r) (* Return the index of [c] in [s.[i .. i1-1]] plus the [offset] or [-1] if not found. *) let rec index_string offset s i i1 c = if i >= i1 then -1 else if s.[i] = c then offset + i else index_string offset s (i+1) i1 c;; (* Return the index of [c] from position [i] in the rope or a negative value if not found *) let rec unsafe_index offset i c = function | Sub(s, i0, len) -> index_string (offset - i0) s (i0 + i) (i0 + len) c | Concat(_, _, l,ll, r) -> if i >= ll then unsafe_index (offset + ll) (i - ll) c r else let li = unsafe_index offset i c l in if li >= 0 then li else unsafe_index (offset + ll) 0 c r let index_from r i c = if i < 0 || i >= length r then invalid_arg "Rope.index_from" else let j = unsafe_index 0 i c r in if j >= 0 then j else raise Not_found let index_from_opt r i c = if i < 0 || i >= length r then invalid_arg "Rope.index_from_opt"; let j = unsafe_index 0 i c r in if j >= 0 then Some j else None let index r c = let j = unsafe_index 0 0 c r in if j >= 0 then j else raise Not_found let index_opt r c = let j = unsafe_index 0 0 c r in if j >= 0 then Some j else None let contains_from r i c = if i < 0 || i >= length r then invalid_arg "Rope.contains_from" else unsafe_index 0 i c r >= 0 let contains r c = unsafe_index 0 0 c r >= 0 (* Return the index of [c] in [s.[i0 .. i]] (starting from the right) plus the [offset] or [-1] if not found. *) let rec rindex_string offset s i0 i c = if i < i0 then -1 else if s.[i] = c then offset + i else rindex_string offset s i0 (i - 1) c let rec unsafe_rindex offset i c = function | Sub(s, i0, _) -> rindex_string (offset - i0) s i0 (i0 + i) c | Concat(_, _, l,ll, r) -> if i < ll then unsafe_rindex offset i c l else let ri = unsafe_rindex (offset + ll) (i - ll) c r in if ri >= 0 then ri else unsafe_rindex offset (ll - 1) c l let rindex_from r i c = if i < 0 || i > length r then invalid_arg "Rope.rindex_from" else let j = unsafe_rindex 0 i c r in if j >= 0 then j else raise Not_found let rindex_from_opt r i c = if i < 0 || i > length r then invalid_arg "Rope.rindex_from_opt"; let j = unsafe_rindex 0 i c r in if j >= 0 then Some j else None let rindex r c = let j = unsafe_rindex 0 (length r - 1) c r in if j >= 0 then j else raise Not_found let rindex_opt r c = let j = unsafe_rindex 0 (length r - 1) c r in if j >= 0 then Some j else None let rcontains_from r i c = if i < 0 || i >= length r then invalid_arg "Rope.rcontains_from" else unsafe_rindex 0 i c r >= 0 let lowercase_ascii r = map ~f:Char.lowercase_ascii r let uppercase_ascii r = map ~f:Char.uppercase_ascii r let lowercase = lowercase_ascii let uppercase = uppercase_ascii let rec map1 f = function | Concat(h, len, l, ll, r) -> Concat(h, len, map1 f l, ll, r) | Sub(s, i0, len) -> if len = 0 then empty else begin let s' = Bytes.create len in Bytes.set s' 0 (f (String.unsafe_get s i0)); Bytes.blit_string s (i0 + 1) s' 1 (len - 1); Sub(Bytes.unsafe_to_string s', 0, len) end let capitalize_ascii r = map1 Char.uppercase_ascii r let uncapitalize_ascii r = map1 Char.lowercase_ascii r let capitalize = capitalize_ascii let uncapitalize = uncapitalize_ascii (** Iterator ***********************************************************************) module Iterator = struct type t = { rope: rope; len: int; (* = length rope; avoids to recompute it again and again for bound checks *) mutable i: int; (* current position in the rope; it is always a valid position of the rope or [-1]. *) mutable path: (rope * int) list; (* path to the current leaf with global range. First elements are closer to the leaf, last element is the full rope. *) mutable current: string; (* local cache of current leaf *) mutable current_g0: int; (* global index of the beginning of current string. i0 = current_g0 + offset *) mutable current_g1: int; (* global index of the char past the current string. len = current_g1 - current_g0 *) mutable current_offset: int; (* = i0 - current_g0 *) } (* [g0] is the global index (of [itr.rope]) of the beginning of the node we are examining. [i] is the _local_ index (of the current node) that we seek the leaf for *) let rec set_current_for_index_rec itr g0 i = function | Sub(s, i0, len) -> assert(0 <= i && i < len); itr.current <- s; itr.current_g0 <- g0; itr.current_g1 <- g0 + len; itr.current_offset <- i0 - g0 | Concat(_, _, l,ll, r) -> if i < ll then set_current_for_index_rec itr g0 i l else set_current_for_index_rec itr (g0 + ll) (i - ll) r let set_current_for_index itr = set_current_for_index_rec itr 0 itr.i itr.rope let rope itr = itr.rope let make r i0 = let len = length r in let itr = { rope = balance_if_needed r; len = len; i = i0; path = [(r, 0)]; (* the whole rope *) current = ""; current_offset = 0; current_g0 = 0; current_g1 = 0; (* empty range, important if [current] not set! *) } in if i0 >= 0 && i0 < len then set_current_for_index itr; (* force [current] to be set *) itr let peek itr i = if i < 0 || i >= itr.len then raise(Out_of_bounds "Rope.Iterator.peek") else ( if itr.current_g0 <= i && i < itr.current_g1 then itr.current.[i + itr.current_offset] else get itr.rope i (* rope get *) ) let get itr = let i = itr.i in if i < 0 || i >= itr.len then raise(Out_of_bounds "Rope.Iterator.get") else ( if i < itr.current_g0 || i >= itr.current_g1 then set_current_for_index itr; (* out of local bounds *) itr.current.[i + itr.current_offset] ) let pos itr = itr.i let incr itr = itr.i <- itr.i + 1 let decr itr = itr.i <- itr.i - 1 let goto itr j = itr.i <- j let move itr k = itr.i <- itr.i + k end (** (In)equality ***********************************************************************) exception Less exception Greater let compare r1 r2 = let len1 = length r1 and len2 = length r2 in let i1 = Iterator.make r1 0 and i2 = Iterator.make r2 0 in try for _i = 1 to min len1 len2 do (* on the common portion of [r1] and [r2] *) let c1 = Iterator.get i1 and c2 = Iterator.get i2 in if c1 < c2 then raise Less; if c1 > c2 then raise Greater; Iterator.incr i1; Iterator.incr i2; done; (* The strings are equal on their common portion, the shorter one is the smaller. *) compare (len1: int) len2 with | Less -> -1 | Greater -> 1 ;; (* Semantically equivalent to [compare r1 r2 = 0] but specialized implementation for speed. *) let equal r1 r2 = let len1 = length r1 and len2 = length r2 in if len1 <> len2 then false else ( let i1 = Iterator.make r1 0 and i2 = Iterator.make r2 0 in try for _i = 1 to len1 do (* len1 times *) if Iterator.get i1 <> Iterator.get i2 then raise Exit; Iterator.incr i1; Iterator.incr i2; done; true with Exit -> false ) (** KMP search algo ***********************************************************************) let init_next p = let m = String.length p in let next = Array.make m 0 in let i = ref 1 and j = ref 0 in while !i < m - 1 do if p.[!i] = p.[!j] then begin incr i; incr j; next.(!i) <- !j end else if !j = 0 then begin incr i; next.(!i) <- 0 end else j := next.(!j) done; next let search_forward_string p = if String.length p > Sys.max_array_length then failwith "Rope.search_forward: string to search too long"; let next = init_next p and m = String.length p in fun rope i0 -> let i = Iterator.make rope i0 and j = ref 0 in (try (* The iterator will raise an exception of we go beyond the length of the rope. *) while !j < m do if p.[!j] = Iterator.get i then begin Iterator.incr i; incr j end else if !j = 0 then Iterator.incr i else j := next.(!j) done; with Out_of_bounds _ -> ()); if !j >= m then Iterator.pos i - m else raise Not_found (** Buffer ***********************************************************************) module Buffer = struct (* The content of the buffer consists of the forest concatenated in decreasing order plus (at the end) the part stored in [buf]: [forest.(max_height-1) ^ ... ^ forest.(1) ^ forest.(0) ^ String.sub buf 0 pos] *) type t = { mutable buf: Bytes.t; buf_len: int; (* = String.length buf; must be > 0 *) mutable pos: int; mutable length: int; (* the length of the rope contained in this buffer -- including the part in the forest *) forest: rope array; (* keeping the partial rope in a forest will ensure it is balanced at the end. *) } (* We will not allocate big buffers, if we exceed the buffer length, we will cut into small chunks and add it directly to the forest. *) let create n = let n = if n < 1 then small_rope_length else if n > Sys.max_string_length then Sys.max_string_length else n in { buf = Bytes.create n; buf_len = n; pos = 0; length = 0; forest = Array.make max_height empty; } let clear b = b.pos <- 0; b.length <- 0; Array.fill b.forest 0 max_height empty (* [reset] is no different from [clear] because we do not grow the buffer. *) let reset b = clear b let add_char b c = if b.length = max_int then failwith "Rope.Buffer.add_char: \ buffer length will exceed the int range"; if b.pos >= b.buf_len then ( (* Buffer full, add it to the forest and allocate a new one: *) add_nonempty_to_forest b.forest (Sub(Bytes.unsafe_to_string b.buf, 0, b.buf_len)); b.buf <- Bytes.create b.buf_len; Bytes.set b.buf 0 c; b.pos <- 1; ) else ( Bytes.set b.buf b.pos c; b.pos <- b.pos + 1; ); b.length <- b.length + 1 let unsafe_add_substring b s ofs len = (* Beware of int overflow *) if b.length > max_int - len then failwith "Rope.Buffer.add_substring: \ buffer length will exceed the int range"; let buf_left = b.buf_len - b.pos in if len <= buf_left then ( (* Enough space in [buf] to hold the substring of [s]. *) String.blit s ofs b.buf b.pos len; b.pos <- b.pos + len; ) else ( (* Complete [buf] and add it to the forest: *) Bytes.blit_string s ofs b.buf b.pos buf_left; add_nonempty_to_forest b.forest (Sub(Bytes.unsafe_to_string b.buf, 0, b.buf_len)); b.buf <- Bytes.create b.buf_len; b.pos <- 0; (* Add the remaining of [s] to to forest (it is already balanced by of_substring, so we add is as such): *) let s = unsafe_of_substring s (ofs + buf_left) (len - buf_left) in add_nonempty_to_forest b.forest s ); b.length <- b.length + len let add_substring b s ofs len = if ofs < 0 || len < 0 || ofs > String.length s - len then invalid_arg "Rope.Buffer.add_substring"; unsafe_add_substring b s ofs len let add_string b s = unsafe_add_substring b s 0 (String.length s) let add_rope b (r: rope) = if is_not_empty r then ( let len = length r in if b.length > max_int - len then failwith "Rope.Buffer.add_rope: \ buffer length will exceed the int range"; (* First add the part hold by [buf]: *) add_to_forest b.forest (Sub(Bytes.sub_string b.buf 0 b.pos, 0, b.pos)); b.pos <- 0; (* I thought [balance_insert b.forest r] was going to rebalance [r] taking into account the content already in the buffer but it does not seem faster. We take the decision to possibly rebalance when the content is asked. *) add_nonempty_to_forest b.forest r; (* [r] not empty *) b.length <- b.length + len ) ;; let add_buffer b b2 = if b.length > max_int - b2.length then failwith "Rope.Buffer.add_buffer: \ buffer length will exceed the int range"; add_to_forest b.forest (Sub(Bytes.sub_string b.buf 0 b.pos, 0, b.pos)); b.pos <- 0; let forest = b.forest in let forest2 = b2.forest in for i = Array.length b2.forest - 1 to 0 do add_to_forest forest forest2.(i) done; b.length <- b.length + b2.length ;; let add_channel b ic len = if b.length > max_int - len then failwith "Rope.Buffer.add_channel: \ buffer length will exceed the int range"; let buf_left = b.buf_len - b.pos in if len <= buf_left then ( (* Enough space in [buf] to hold the input from the channel. *) really_input ic b.buf b.pos len; b.pos <- b.pos + len; ) else ( (* [len > buf_left]. Complete [buf] and add it to the forest: *) really_input ic b.buf b.pos buf_left; add_nonempty_to_forest b.forest (Sub(Bytes.unsafe_to_string b.buf, 0, b.buf_len)); (* Read the remaining from the channel *) let len = ref(len - buf_left) in while !len >= b.buf_len do let s = Bytes.create b.buf_len in really_input ic s 0 b.buf_len; add_nonempty_to_forest b.forest (Sub(Bytes.unsafe_to_string s, 0, b.buf_len)); len := !len - b.buf_len; done; (* [!len < b.buf_len] to read, put them into a new [buf]: *) let s = Bytes.create b.buf_len in really_input ic s 0 !len; b.buf <- s; b.pos <- !len; ); b.length <- b.length + len ;; (* Search for the nth element in [forest.(i ..)] of total length [len] *) let rec nth_forest forest k i len = assert(k <= Array.length forest); let r = forest.(k) in (* possibly empty *) let ofs = len - length r in (* offset of [r] in the full rope *) if i >= ofs then get r (i - ofs) else nth_forest forest (k + 1) i ofs let nth b i = if i < 0 || i >= b.length then raise(Out_of_bounds "Rope.Buffer.nth"); let forest_len = b.length - b.pos in if i >= forest_len then Bytes.get b.buf (i - forest_len) else nth_forest b.forest 0 i forest_len ;; (* Return a rope, [buf] must be duplicated as it becomes part of the rope, thus we duplicate it as ropes are immutable. What we do is very close to [add_nonempty_to_forest] followed by [concat_forest] except that we do not modify the forest and we select a sub-rope. Assume [len > 0] -- and [i0 >= 0]. *) let unsafe_sub (b: t) i0 len = let i1 = i0 + len in (* 1 char past subrope *) let forest_len = b.length - b.pos in let buf_i1 = i1 - forest_len in if buf_i1 >= len then (* The subrope is entirely in [buf] *) Sub(Bytes.sub_string b.buf (i0 - forest_len) len, 0, len) else begin let n = ref 0 in let sum = ref empty in if buf_i1 > 0 then ( (* At least one char in [buf] and at least one in the forest. Concat the ropes of inferior length and append the part of [buf] *) let rem_len = len - buf_i1 in while buf_i1 > min_length.(!n + 1) && length !sum < rem_len do sum := balance_concat b.forest.(!n) !sum; if !n = level_flatten then sum := flatten !sum; incr n done; sum := balance_concat !sum (Sub(Bytes.sub_string b.buf 0 buf_i1, 0, buf_i1)) ) else ( (* Subrope in the forest. Skip the forest elements until the last chunk of the sub-rope is found. Since [0 < len <= forest_len], there exists a nonempty rope in the forest. *) let j = ref buf_i1 in (* <= 0 *) while !j <= 0 do j := !j + length b.forest.(!n); incr n done; sum := sub b.forest.(!n - 1) 0 !j (* init. with proper subrope *) ); (* Add more forest elements until we get at least the desired length *) while length !sum < len do assert(!n < max_height); sum := balance_concat b.forest.(!n) !sum; (* FIXME: Check how this line may generate a 1Mb leaf: *) (* if !n = level_flatten then sum := flatten !sum; *) incr n done; let extra = length !sum - len in if extra = 0 then !sum else sub !sum extra len end let sub b i len = if i < 0 || len < 0 || i > b.length - len then invalid_arg "Rope.Buffer.sub"; if len = 0 then empty else (unsafe_sub b i len) let contents b = if b.length = 0 then empty else (unsafe_sub b 0 b.length) let length b = b.length end (* Using the Buffer module should be more efficient than sucessive concatenations and ensures that the final rope is balanced. *) let concat sep = function | [] -> empty | r0 :: tl -> let b = Buffer.create 1 in (* [buf] will not be used as we add ropes *) Buffer.add_rope b r0; List.iter (fun r -> Buffer.add_rope b sep; Buffer.add_rope b r) tl; Buffer.contents b (** Input/output -- modeled on Pervasive ***********************************************************************) (* Imported from pervasives.ml: *) external input_scan_line : in_channel -> int = "caml_ml_input_scan_line" let input_line ?(leaf_length=128) chan = let b = Buffer.create leaf_length in let rec scan () = let n = input_scan_line chan in if n = 0 then (* n = 0: we are at EOF *) if Buffer.length b = 0 then raise End_of_file else Buffer.contents b else if n > 0 then ( (* n > 0: newline found in buffer *) Buffer.add_channel b chan (n-1); ignore (input_char chan); (* skip the newline *) Buffer.contents b ) else ( (* n < 0: newline not found *) Buffer.add_channel b chan (-n); scan () ) in scan() ;; let read_line () = flush stdout; input_line stdin let rec output_string fh = function | Sub(s, i0, len) -> output fh (Bytes.unsafe_of_string s) i0 len | Concat(_, _, l,_, r) -> output_string fh l; output_string fh r ;; let output_rope = output_string let print_string rope = output_string stdout rope let print_endline rope = output_string stdout rope; print_newline() let prerr_string rope = output_string stderr rope let prerr_endline rope = output_string stderr rope; prerr_newline() (**/**) let rec number_leaves = function | Sub(_,_,_) -> 1 | Concat(_,_, l,_, r) -> number_leaves l + number_leaves r let rec number_concat = function | Sub(_,_,_) -> 0 | Concat(_,_, l,_, r) -> 1 + number_concat l + number_concat r let rec length_leaves = function | Sub(_,_, len) -> (len, len) | Concat(_,_, l,_, r) -> let (min1,max1) = length_leaves l and (min2,max2) = length_leaves r in (min min1 min2, max max1 max2) module IMap = Map.Make(struct type t = int let compare = Pervasives.compare end) let distrib_leaves = let rec add_leaves m = function | Sub(_,_,len) -> (try incr(IMap.find len !m) with _ -> m := IMap.add len (ref 1) !m) | Concat(_,_, l,_, r) -> add_leaves m l; add_leaves m r in fun r -> let m = ref(IMap.empty) in add_leaves m r; !m (**/**) (** Toplevel ***********************************************************************) module Rope_toploop = struct open Format let max_display_length = ref 400 (* When displaying, truncate strings that are longer than this. *) let ellipsis = ref "..." (* ellipsis for ropes longer than max_display_length. User changeable. *) (* Return [max_len - length r]. *) let rec printer_lim max_len (fm:formatter) r = if max_len > 0 then match r with | Concat(_,_, l,_, r) -> let to_be_printed = printer_lim max_len fm l in printer_lim to_be_printed fm r | Sub(s, i0, len) -> let l = if len < max_len then len else max_len in (match escaped_sub s i0 l with | Sub (s, i0, len) -> if i0 = 0 && len = String.length s then pp_print_string fm s else for i = i0 to i0 + len - 1 do pp_print_char fm (String.unsafe_get s i) done | Concat _ -> assert false); max_len - len else max_len let printer fm r = pp_print_string fm "\""; let to_be_printed = printer_lim !max_display_length fm r in pp_print_string fm "\""; if to_be_printed < 0 then pp_print_string fm !ellipsis end (** Regexp ***********************************************************************) module Regexp = struct (* FIXME: See also http://www.pps.jussieu.fr/~vouillon/ who is writing a DFA-based regular expression library. Would be nice to cooperate. *) end ;; (* Local Variables: *) (* compile-command: "make -k -C.." *) (* End: *) ocaml-rope-0.6/src/rope.mli000066400000000000000000000414121320376225700156650ustar00rootroot00000000000000(* File: rope.mli Copyright (C) 2007 Christophe Troestler email: Christophe.Troestler@umh.ac.be WWW: http://math.umh.ac.be/an/software/ This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2.1 or later as published by the Free Software Foundation, with the special exception on linking described in the file LICENSE. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file LICENSE for more details. *) (** Ropes ("heavyweight strings") are a scalable string implementation. Ropes are designed for efficient operation that involve the string as a whole. Operations such as concatenation, and substring take time that is nearly independent of the length of the string. Unlike strings, ropes are a reasonable representation for very long strings such as edit buffers or mail messages. Features: - No length limitation (contrarily to strings); - Immutability (use {!Rope.Buffer} instead); - Efficient concatenation ({!Rope.concat2}) and splice ({!Rope.sub}); - Share memory whenever possible. Disadvantages: - [get] is not O(1) — but it is amortized O(1) if you use an iterator (see the {!Rope.Iterator} module). {!Rope.get} is 2 to 3 times slower than for a string so it should not be your main operation. However, as soon as in addition you have a few concatenations (especially with sharing) or splice operations, ropes will usually outperform strings. You can say [module String = Rope] to use ropes instead of strings and, in particular, so that [r.[i]] gets the [i]th char of the rope [r]. This module has all non-deprecated operations of [String]. It additionally features {!Rope.Buffer} and {!Rope.Iterator} modules. To use this library in the toploop (REPL), issue [#require "rope.top";;]. @version %%VERSION%% @author Christophe Troestler *) type t (** Immutable rope. *) type rope = t (** Alias for type {!Rope.t} *) exception Out_of_bounds of string (** Raised by various functions to indicate out-of-bounds access. The string argument is the name of the function that raised it. *) val empty : t (** The empty rope. *) val of_string : string -> t (** [of_string s] creates a rope from the string [s]. *) val of_substring : string -> int -> int -> t (** [of_substring s start len] create a rope from the substring [s.[start .. start+len-1]]. @raise Invalid_argument if [start] and [len] do not designate a valid sbstring of [s]. *) val of_char : char -> t (** [of_char c] returns a rope consisting of the unique character [c]. It may be useful to append a char to a given rope. *) val make : int -> char -> t (** [make len c] returns a rope of length [len] filled with [c]. *) val init : int -> (int -> char) -> t (** [init len f] returns a rope of length [len] with entry of index [i] filled with [f i]. *) val to_string : t -> string (** [to_string r] return a string with the same content as the rope. @raise Failure if the rope is too long to fit into a string. *) val is_empty : t -> bool (** [is_empty r] tells whether the rope [r] is empty. *) val length : t -> int (** [length r] returns the length of the rope. O(1) time. *) val get : t -> int -> char (** [get r i] returns the [i]th char of the rope. Takes O(log(length r)). *) val sub : t -> int -> int -> t (** [sub r i start len] returns the sub-rope consisting of characters from position [start] to [start+len-1] (included) of the rope [r]. O(log(length r)) time. @raise Invalid_argument if [i < 0], [len < 0] or [i + len > Rope.length r]. *) val blit : t -> int -> Bytes.t -> int -> int -> unit (** [blit src srcoff dst dstoff len] copies [len] bytes from the rope [src] starting at index [srcoff], to sequence [dst], starting at index [dstoff]. *) val concat2 : t -> t -> t (** [concat2 r1 r2] concatenates the ropes [r1] and [r2]. *) val concat : t -> t list -> t (** [concat sep rl] concatenates the list of ropes [rl], inserting the separator string [sep] between each. *) val iter : (char -> unit) -> t -> unit (** [iter f r] execute [f c] for [c] going through every character of rope [r] from left to right. *) val iteri : (int -> char -> unit) -> t -> unit (** [iter f r] execute [f i c] for [c] going through every character of rope [r] from left to right and [i] the index of [c]. *) val map : f:(char -> char) -> t -> t (** [map f r] applies function [f] in turn to all the characters of [r] (in increasing index order) and stores the results in a new string that is returned. *) val mapi : f:(int -> char -> char) -> t -> t (** Same as [map] but the function [f] is passed the index [i] of the char. *) val trim : t -> t (** Return a copy of the argument, without leading and trailing whitespace. The characters regarded as whitespace are: [' '], ['\012'], ['\n'], ['\r'], and ['\t']. *) val escaped : t -> t (** Return a copy of the argument, with special characters represented by escape sequences, following the lexical conventions of Objective Caml. *) val index : t -> char -> int (** [index r c] returns the position of the leftmost occurrence of character [c] in rope [r]. @raise Not_found if [c] does not occur in [r]. *) val index_opt : t -> char -> int option (** [index r c] returns [Some i] where [i] is the position of the leftmost occurrence of character [c] in rope [r] and [None] if [c] does not occur in [r]. *) val rindex : t -> char -> int (** [rindex r c] returns the position of the rightmost occurrence of character [c] in rope [r]. @raise Not_found if [c] does not occur in [r]. *) val rindex_opt : t -> char -> int option (** [rindex_opt r c] returns [Some i] where [i] is the position of the rightmost occurrence of character [c] in rope [r] or [None] if [c] does not occur in [r]. *) val index_from : t -> int -> char -> int (** Same as {!Rope.index}, but start searching at the character position given as second argument. [Rope.index r c] is equivalent to [Rope.index_from r 0 c]. *) val index_from_opt : t -> int -> char -> int option (** Same as {!Rope.index_opt}, but start searching at the character position given as second argument. [Rope.index_opt r c] is equivalent to [Rope.index_from_opt r 0 c]. *) val rindex_from : t -> int -> char -> int (** Same as {!Rope.rindex}, but start searching at the character position given as second argument. [Rope.rindex r c] is equivalent to [Rope.rindex_from s (Rope.length r - 1) c]. *) val rindex_from_opt : t -> int -> char -> int option (** Same as {!Rope.rindex_opt}, but start searching at the character position given as second argument. [Rope.rindex_opt r c] is equivalent to [Rope.rindex_from_opt s (Rope.length r - 1) c]. *) val contains : t -> char -> bool (** [contains r c] tests if character [c] appears in the rope [r]. *) val contains_from : t -> int -> char -> bool (** [contains_from r start c] tests if character [c] appears in the subrope of [r] starting from [start] to the end of [s]. @raise Invalid_argument if [start] is not a valid index of [r]. *) val rcontains_from : t -> int -> char -> bool (** [rcontains_from r stop c] tests if character [c] appears in the subrope of [r] starting from the beginning of [r] to index [stop]. @raise Invalid_argument if [stop] is not a valid index of [r]. *) val uppercase_ascii : t -> t (** Return the argument with all lowercase letters translated to uppercase, including accented letters of the ISO Latin-1 (8859-1) character set. *) val lowercase_ascii : t -> t (** Return the argument with all uppercase letters translated to lowercase, including accented letters of the ISO Latin-1 (8859-1) character set. *) val capitalize_ascii : t -> t (** Return the argument, with the first character set to uppercase. *) val uncapitalize_ascii : t -> t (** Return the argument with the first character set to lowercase. *) val compare: t -> t -> int (** The comparison function for ropes, with the same specification as [Pervasives.compare]. Along with the type [t], this function [compare] allows the module {!Rope} to be passed as argument to the functors [Set.Make] and [Map.Make]. *) val equal : t -> t -> bool (** [equal r1 r2] tells whether the two ropes [r1] and [r2] are equal. (It is equivalent to [compare r1 r2 = 0], just slightly faster.) *) val uppercase : t -> t [@@ocaml.deprecated "Use Rope.uppercase_ascii"] (** @deprecated Use {!Rope.uppercase_ascii}. *) val lowercase : t -> t [@@ocaml.deprecated "Use Rope.lowercase_ascii"] (** @deprecated Use {!Rope.lowercase_ascii}. *) val capitalize : t -> t [@@ocaml.deprecated "Use Rope.capitalize_ascii"] (** @deprecated Use {!Rope.capitalize_ascii}. *) val uncapitalize : t -> t [@@ocaml.deprecated "Use Rope.uncapitalize_ascii"] (** @deprecated Use {!Rope.uncapitalize_ascii}. *) (** {2 Search} *) val search_forward_string : string -> t -> int -> int (** [search_forward_string p] is a search function that, given a rope [r] and a start index [i0], will return the position of [p] in [r] or raise [Not_found] if no occurrence of [p] in [r] exists. [let search = search_forward_string p] takes O(length p) and [search r i0] takes O(length r - i0). *) (** {2 Input/output} Input and output functions for ropes modelled on the standard library [Pervasives]. *) val input_line : ?leaf_length:int -> in_channel -> t (** Read characters from the given input channel, until a newline character is encountered. Return the rope of all characters read, without the newline character at the end. @raise End_of_file if the end of the file is reached at the beginning of line. *) val read_line : unit -> t (** Flush standard output, then read characters from standard input until a newline character is encountered. Return the rope of all characters read, without the newline character at the end. *) val print_string : t -> unit (** Print a rope on standard output. *) val print_endline : t -> unit (** Print a rope, followed by a newline character, on standard output and flush standard output. *) val prerr_string : t -> unit (** Print a rope on standard error. *) val prerr_endline : t -> unit (** Print a rope, followed by a newline character on standard error and flush standard error. *) val output_rope : out_channel -> t -> unit (** [output_rope oc r] outputs the rope [r] to the output channel [oc]. May also be used with a [%a] directive of printf. *) val output_string : out_channel -> t -> unit (** Alias for {!Rope.output_rope} to be a drop in replacement for strings. *) (** {2 Balancing} *) val balance : t -> t (** [balance r] return a balanced copy of the rope [r]. Implicit rebalancing is done by some of the above functions to avoid gross inefficiencies but you may want to call this function explicitely to try to improve your running times. *) val height : t -> int (** [depth r] returns the depth of the rope [r]. This information may be useful to decide whether you want to re-balance. *) val rebalancing_height : int (** The rope will be rebalanced by some functions it its height is greater or equal to [rebalancing_height]. *) (** {2 Submodules} *) (** Iterators for ropes. It is more efficient to use an iterator to perform small steps and get the characters than to use {!Rope.get} repeatedly. *) module Iterator : sig type t (** Mutable iterator on a rope. Iterators are less efficient than {!Rope.get} on small ropes (of length [<= 1024] chars). *) val make : rope -> int -> t (** [make r i0] returns a new iterator for the rope [r]. It is initially at position [i0]. *) val get : t -> char (** [get itr] returns the character of the rope at the current position. O(1) time. This does not change the current position. @raise Out_of_bounds if the position is outside the rope. *) val peek : t -> int -> char (** [peek itr i] returns the character [i] of the rope. If [i] is close to the current position of the iterator, this will in general be more efficient than [get rope i]. *) val pos : t -> int (** [pos itr] returns the current position. It may not be a valid position of the rope. O(1) time. *) val incr : t -> unit (** [incr itr] moves to the next character. O(1) time. *) val decr : t -> unit (** [decr itr] moves to the previous character. O(1) time. *) val goto : t -> int -> unit (** [goto itr i] move to position [i]. O(1) time but the next call to [get] may be slower. *) val move : t -> int -> unit (** [mode itr i] move the current position by [i] chars ([i] may be negative or null). O(1) time but the next call to [get] may be slower. *) val rope : t -> rope (** [rope itr] returns the rope from which the iterator was constructed. *) end (** This is similar to the [Buffer] module in the standard library except that it constructs ropes. It is recommended to use this module instead of repeatedly concatenating chars. *) module Buffer : sig type t (** Mutable buffer to construct ropes. *) val create : int -> t (** [create n] returns a fresh buffer, initially empty. The [n] parameter is the initial size of the internal rope that holds the buffer contents. The buffer will grow dynamically to accomodate new inputs. *) val clear : t -> unit (** Empty the buffer. *) val reset : t -> unit (** Empty the buffer. *) val length : t -> int (** Return the number of characters currently contained in the buffer. *) val add_char : t -> char -> unit (** [add_char b c] appends the character [c] at the end of the buffer [b]. @raise Failure if the length if the buffer exceeds [max_int]. *) val add_string : t -> string -> unit (** [add_string b s] appends the string [s] at the end of the buffer [b]. @raise Failure if the length if the buffer exceeds [max_int]. *) val add_substring : t -> string -> int -> int -> unit (** [add_substring b s ofs len] takes [len] characters from offset [ofs] in string [s] and appends them at the end of the buffer [b]. @raise Invalid_argument if [ofs] and [len] do not designate a valid substring of [s]. @raise Failure if the length if the buffer exceeds [max_int]. *) val add_rope : t -> rope -> unit (** [add_rope b r] add the rope [r] to the buffer [b]. *) val add_channel : t -> in_channel -> int -> unit (** [add_channel b ic n] reads exactly [n] characters from the input channel [ic] and stores them at the end of buffer [b]. @raise End_of_file if the channel contains fewer than [n] characters. *) val add_buffer : t -> t -> unit (** [add_buffer b1 b2] appends the current contents of buffer [b2] at the end of buffer [b1]. [b2] is not modified. *) val contents : t -> rope (** Return a copy of the current contents of the buffer. The buffer itself is unchanged. *) val sub : t -> int -> int -> rope (** [sub b off len] returns a rope of the current contents of the buffer [b] starting at offset [off] of length [len] bytes. The buffer itself is unaffected. @raise Invalid_argument if out of bounds request. *) val nth : t -> int -> char (** [nth b i] returns the [i]th character if the buffer. @raise Out_of_bounds if [i < 0] or [i >= length b]. Time: O(log(length b)). *) end (** TBD: Regular expressions for ropes. *) module Regexp : sig end (** Toploop printer and its configuration. *) module Rope_toploop : sig val printer : Format.formatter -> rope -> unit (** Toploop printer for rope values. The value will be printed alike a standard string except that one will display at most [!max_display_length] characters from the rope in order to allow convenient interactive manipulations of long ropes. In case the rope display is truncated, [!ellipsis] is appended after the closing quote. *) val max_display_length : int ref (** Maximum number of characters displayed. Default: [400]. *) val ellipsis : string ref (** String used a ellipsis for truncated ropes. Default: ["..."]. *) end (**/**) (** {2 For system use} *) val number_leaves : t -> int val number_concat : t -> int val print : t -> unit val length_leaves : t -> int * int module IMap : Map.S with type key = int val distrib_leaves : t -> int ref IMap.t (**/**) ocaml-rope-0.6/tests/000077500000000000000000000000001320376225700145665ustar00rootroot00000000000000ocaml-rope-0.6/tests/rope_test.ml000066400000000000000000000017761320376225700171370ustar00rootroot00000000000000(* Simple tests to be performed in the toploop. *) #use "rope_top.ml";; let ( ^ ) = Rope.concat2 let rope = Rope.of_string let pow r i = let rec pow_rec m r i = if i = 0 then m else if i mod 2 = 0 then pow_rec m (r ^ r) (i/2) else (* odd i *) pow_rec (m ^ r) (r ^ r) (i/2) in pow_rec Rope.empty r i ;; let r = rope "Hello" ^ rope " " ^ rope "world!";; r ^ Rope.empty;; Rope.length r = 12;; let r = rope "Hello world! " let n = 50_000_000 let rn = pow r n;; Rope.length rn = n * Rope.length r;; Rope.get rn (10 * Rope.length r) = 'H';; let r = "Hello world! Hola amigos!" in let n = String.length r in Rope.sub (rope r) (n-2) 1;; Rope.index Rope.empty 'c';; Rope.index (rope "abcde") 'c' = 2;; Rope.equal (Rope.sub (rope "Hlicodal") 5 3) (rope "od");; Rope.compare (rope "Hello") (rope "Hello ");; Rope.compare (rope "") (rope "Hello");; let b = Rope.Buffer.create 1;; Rope.Buffer.contents b;; Rope.Buffer.add_string b "Hello ";; Rope.Buffer.add_rope b (rope "world!");; Rope.Buffer.contents b;; ocaml-rope-0.6/top/000077500000000000000000000000001320376225700142265ustar00rootroot00000000000000ocaml-rope-0.6/top/jbuild000066400000000000000000000004561320376225700154270ustar00rootroot00000000000000(jbuild_version 1) (library ((name rope_top) (public_name rope.top) (modes (byte)) (synopsis "Install toplevel (REPL) printers for Rope") (flags ( :standard -w -9 -safe-string -strict-sequence -principal -short-paths )) (libraries (compiler-libs.toplevel rope)))) ocaml-rope-0.6/top/rope_top.ml000066400000000000000000000007121320376225700164070ustar00rootroot00000000000000 let eval_string ?(print_outcome = false) ?(err_formatter = Format.err_formatter) str = let lexbuf = Lexing.from_string str in let phrase = !Toploop.parse_toplevel_phrase lexbuf in Toploop.execute_phrase print_outcome err_formatter phrase let install_printer p = eval_string (Printf.sprintf "#install_printer %s;;" p) let () = if not (install_printer "Rope.Rope_toploop.printer") then Format.eprintf "Problem installing Rope-printer@." ocaml-rope-0.6/web/000077500000000000000000000000001320376225700142015ustar00rootroot00000000000000ocaml-rope-0.6/web/documentation.png000066400000000000000000000150421320376225700175620ustar00rootroot00000000000000PNG  IHDRlsRGBbKGD pHYs.#.#x?vtIME 4)ZIDATxweEU'w@74(4("AE/ZA 3'*r WŀWIAE@&% MCw{Ūz z yc�j97 f0`3 f0`3 f0qYoYZmѰ %nUi}ҲIS'ֺ1C}|j:dIJjYޥ%PHbhqT8p 3L2m$;r?96־?CWU[gLMcGERYs$z+H<߄ƶŞ0cȧ,aa~v$yoMebm-rf{:Z9`3j3L'5|{&i3b,A?pR,a1+olGDr* Y()Z^u$h1 ʾ0yǚ\jC6ı']iYsouϨMf34bN'Rwk5b +r_}a A_Ξ%>72KC&A 0ȃI'#SIi )2ZUx|wK ́?;eGOor_m s/~ZOk*mvp> hxK]87C ^MӖ5}fm97).*Y",yW0K* va~37߹`7oЖ_oyi&4l=VN`<\N:RIb> X(uc=K:E]Ht,'^uyS/nYоW'i 'tgYD$ER:vO̮\)?vX#!2L?;rWW;Gv+g"+dL0׸RÅܗݲ϶(|ԸjΌ\TqZ#BC% q .X~ sMK=l_tu.,T]Oezh=3+HhN9*E>92){nOݠqDZW۷dhO=Kf3}X⦃%窨ѽ56Zw;P?qp&&89Ώzl2Ѯ(rFqY_#!RfVL/|YӇz $wW Tּ$ŀ~߷ Vf} 8n3W]#U RۘTu^W-??`yuױukjydS[UiXi8䨃[ Zį>Y ,N٨,LVNۉheJOi_\5O/tuV7Xr56H{tXVb0-7T}990F0+Ou54$ pWRœ9]<셤$$R7yZ{_T[I[kL3p-*J[΅asO4%iXhX/tζ\sTT<ںA>Ez{Ax|̺U( #q]~7g3sn8cUXޑwݳW<-Xێ8DNPWy(֘E\㢁S6veX$tMjK: Xm8T^Vpn~Oqx&N'թ۪uB ,!Q`y(Wn[HCֆ!RpM5[ؗz,!Z8u~/Uõʛ4S/F/:&p%}ya/, ,u[f\ب dNbIG[z3R+ySk~ÈTZ="g[zJʳX)bcBaDD@ZM)3@&o8lNb-ana Mt+.E#IXfYNގiox0 ~V}|__-^i,licǓϬ"$oo$^5& ',ROh~ @^))ig>WcrH45i\lsDXo4me<{J]-Yޚ&,JyνhS˞d6S/M/\7t\pc){E)&3mT,Ϲ;ʿxNBԽ " (ɰaϞ]/JnaxX",CJ1, 3o&ˋ}3g?U{(aWJ7 u]"o $tW: 2{qH}{]jPNh7(`0KJYRVyAGm-> =)iZKӤeqPϒo5K;c>ʁ,Eo^WVìn"uNGeOp 0At̮%僅6}}xRYR;NtG|GfLg<w-AmHlq%㌊2MZZp]wv^L2!v\}mnv %|adU5,@(bj{ чeWWϙo|" {),yC@iYK>c!$$=j tr$x8F]65^݉o=P<ae}B|e;1 +Hdt9q5&X, d/<6s7Ѫq Z|.\w{=IIYຄr|Tyi \M~[?|Vo?'L\'?mq?vy]6I8e %)nTh;]/9/\I3rSj kׄZXXV/^E :ct?9kMH0',I=?,Vv={t&$1R]wxf Ƿ{^2kGKIyJD]esg!&2?˴YZBJq+7ǒiK G׸T|ﭾ||2}YӳvU؈$ f>gv~2/3/i{)*w3B+a\hC=d= ēpuc;:ԑh0` K Vd>4{8HL)2n?r9 dRorO\s;xRe{Xd_M?{ޅ`"ڣhvg+2mdȂ44d1NXÔݺ7aKhYi3gI$n€;k|4e͢\a&#4# "g$8{N: 8ZP0W< &L%Yh8ה+oy+έ(VR^JQ{dZ6zmF}A4euz{͵הz8TXl"Q!RAif--ҚJ3֝3ϏVĴ#x2&,v,^05,[]\aUQPꄝ}%>aqFbrg5#tcQl-~jmP404!۫LK59dmo̯ Y60F*׶ce}G;)JT]`.:c%8@)5.GOi5m!X)Qo0{ 0JnJO x$}I)4F*9|%$ntMf+KIm_wO6?+7*!H>]o[+X^̈́s2n*O~zlգCrx4!mK԰/D] s[=-wxBnEr录iZ#%q]cD)R!|ͺ(QR#8i׻Pfbe,95;/ Lgy?6(auN}"]vyQoFGwUK\{LOnXϊUmI$v iB)%0Q/vs͙%*y f]8z\ &D؉=^J G8k"/`@9r[ 鬰ӞaOJ+ x(I2sž77SahYSЇ'P6}e<+h7S;C ;T*&gjȕTY+H}[r-yfYOݣbo(e>ӗ<\CBQg}W%1OY6f@ؚP*8qhE Ǩ-Oq. ÏNx"d?eU,|O7T)wdjj_%}r2"ιvD[$jD8h{ػtߗ/ ||Q*cW([i|wVn&%xhxlV|ve|r-7 t|`Łɾ^epmr3;GƼv{B1]R/qrѥ=[sW#5C3) x{{S ǺyŇg+rdx0lP=~;Р#Yt=h&za2v"iHnv+(HAqv1/nN`364<պݲY7EQlgXlba|I֭Jɒ Jmkkg0ĴҐߛ"Kύt#EWۑRw2Ɇ$vIzodX-P K>?5hC(vu}1-bsY:!wO@ysw?wܠƶYYmߏZ%tNj+FZe}+ζ AT֝8nix.xj0ʇ pIv$m>48jZ68 Zٻqyl1c:U/9~|)Ɗ\j#AJ0;N6,oҷK|`b/Vz<'o^o.9K9=4GsAu"6a.VXP c[6G̓B(&ݲ>ZaENZ6wɓzdX!!/oG %aFʚ9X.u\TieS m6O/c21H]q|Oc<4-5ĉLJAّүc$b+q o\-kkD ͒|>~(f\~qFuI6qۋy˓&>u: =T'5|6QxWB;6Urq3 f0`3 f0`3/R luIENDB`ocaml-rope-0.6/web/download.png000066400000000000000000000110111320376225700165100ustar00rootroot00000000000000PNG  IHDRz(` sRGBbKGD pHYs.#.#x?vtIME VGIDAThZi]Ev9;޻oH{ F EADcDTd4RAQc2B@(B3H1D$%L !stp3թ<{EV]0)La S0)L7www4wot(si;GQ56\RBԊ #oKOΎ&x~ ,uVzo[k/ YӤmXK|k@@ ?I&~ o\Յ vEx@h4UG[u(?LgHtb@5XDGb&@FN m,m)9dxێh 9!߲s FP!kxS=0K8Uxl1K3o[Hrwg;z%E'f &Bі@֜kCY!(c{yFA,vXkTp8:Xs" ͯp)C71o)&z[M^أm9$R^mnJ[-?;6 )$n˵]nh;%m_x S`xj@ e^{qu&NרV$Slo E*a\L\?XĂ1|)BqbyW4+zÅ2`A}f2tA!  F?ƹC#Txp?p3Jy~XzU4X?ļd q<-K::.aTвW*?h}Y9PrrJ_m, @,2aȌ7x0ћ7'.7VRp |$/Nx96|:^rNYsfԺw` ߯֋6y[L-]P~tIU FQ$9K<E(+^0@fkp炫/IA,̈́X~Ȗe'.ynLDjwtrY-h@BJ,`fB4ؠS\Obm՟o!tbqa+onܮ*dyD2%V}|:]k#`HBG 81 揿4v !6RR "\ӱb ه,DqxWVv[^ `vщ_̴θ2h$ݓMHL]nId'lk "92Mfԣg #߁Ua]wIk^^eas6 `gi$kpԳ>PsC>"*ilm`AM=mŞqķuK8X:?ӑ9Eu4pOٵ' Xal{F0l8aLl۞%T mQ$-Dj(//+B3'=&Z2ǵL"  uwe [U|7dfWcI`7× q8:0; 6 jd:4$@FSpȱ/ViJg]{&nwjQHaDK7=;9}֚;FGoXys >% X6Dq)~@Yix7'Q˦`*ckuBe FHJNCYM،4_<֬>ƼL~m]EXxN%EKdѦ'Qߧ!$?iB,OQk'Nm|!4 2d0x(t "2P@]2^=KhG཰i cMaEx:gd(YIgFR~sLⅦ X8mv"yĜL:y+F5'2p,TvғY}Y`Rf zsI(( Be##kkOm}ˎ}o m7ð/4+uӰBVc◔Q>'L‚GNͲy B\&gA&D'JVku!#AP7u/p EXyrE'4A@`$8vN*sE8 Ì(a(݂va1 (wuvӛ'ivX L?([hZ%1M[8?r&nA_!$t9P tĆJuUߨdihِ86l:u=4gkvXP6)tJp&3g%vƭZfӌ?UށJP1Z lCc٘txyvW  GKNb u[?fb&d`0;Uhm˛ﺻvq\/>L{Yr82!}R;屔D cG>z1B@dlm^wR:鼯d3#3@ gצ5s #"_}4(/d; v!R$_ 2&b L&:lqN$+u{iVow #payKJ -}1P0N~Z] UO|2#:$3UI c:_L2|h#7O:/XD[ёL:mYVֱmn׺|} q"ghh֏I% =vw.2+3sZDaF2HBJ~4*0 `3)La S0)La S.5-ZبIENDB`ocaml-rope-0.6/web/index.html000066400000000000000000000070711320376225700162030ustar00rootroot00000000000000 OCaml Rope module

As its name implies, the OCaml Rope library implements the immutable rope datastructure [1] (with some small variations). All meaningful String module functions are implemented; regular expressions are planned (help appreciated).

Here is an example use of Rope in the interactive toploop (notice the special printer to conveniently display ropes):

# #load "rope.cma";;
# #install_printer Rope.Rope_toploop.printer;;

# let ( ^ ) = Rope.concat2;;
val ( ^ ) : Rope.t -> Rope.t -> Rope.t = <fun>
# let r = Rope.of_string "Hello" ^ Rope.of_string " " ^ Rope.of_string "world!";;
val r : Rope.t = "Hello world!"
# Rope.length r;;
- : int = 12 
For a complete description of the functions, see the interface Rope.mli. If you have questions, suggestions, bugs,... you can contact me by email.

The code is released under the GNU Lesser General Public License (LGPL) with the same special exception as for the OCaml standard library (see the file LICENSE for more details).



[1] Hans Boehm, Russ Atkinson, Michael Plass, "Ropes: an alternative to strings", Software Practice and Experience 25, vol. 12 (1995), pp. 1315-1330.

ocaml-rope-0.6/web/license.png000066400000000000000000000064401320376225700163350ustar00rootroot00000000000000PNG  IHDR\j#sRGBbKGD pHYs.#.#x?vtIME qP# IDAThy}?}̻>c ,#L vLc1pL06  L"IY!( c$1H€ cޜcDBؕRo;&dI&dIqgOT7 Vvւ8axq9^@{{kB/6'=8?8pC&5W Ǥ0fO a~xCϤRGMî`,;ℚ(kQRNaaRd=]q|+{z%d _(%Q``-kn>{:j㗔?˙ >>oYTa@ZO[y;=@R򧇆+{YV&Ijy1.x2w#ԭm{gL ;Z>z33<'RB&Ets&r#'V}y} W HStD:/.jGR#Ӧr}M2lt^m薵]{vZ@ h˧LL糥Ѳ4b{"C2+5vUrIqh1D/[esE$%$q\UbI~"WJ1^ 0Ym(k" cwZFN*>ok&;HIdZ7b~ 3L* :;AO|̞䙖;doޕCoYHq^ӳ9I[ORg q s:?,=>T-w\ 9^A๸i|0(<jUSCFIj&Q_u!c }e.Av 1%G rm$/F8vZ3/[r“z.˜¦=gzr¦" >'+BY0}8dN)X;:2mX~qkaO@U8)@"VG:jOcyƯY.S|黍z?{vy%oܿR'([tn?6L0f {SgK)x$4K9q6fgww I,HI|yw얁d"J쫣i֝c"ݒT2ưUlK# 89 e!2uQ]RTRQx eGOV9[o-IqN4+L62$:ŤLjo*SX:6B!TSx;6ZSJ62;t?;G 9?8oQ7q f漿^Vd qL;i/OrlLt58)CB:TcT JR>1vP$,,ߺr xWeWYR-忑$- ߴ-n/ 񭩃_{ogK6l\ ޤ|{wo3,{.RX &c#4\T:XV x}3}uk$=m[ɾV"1SlS'OUHj ~sM.l_wa# M5:LoRh9v3fvOaǹRY׈%sZǻ|" F`Cas o&q{DǮnW_;zz6;Es'negFm. xܙj^Z*=-fX8fY)LnY†b_Yz50+ brYq2.CAp$1Qw;lFmLj{Rۈ}OXoR\__,l^d- 3!ْmGiaiZn!7#5t:Wa3ډ5ül^h=wYTbWsk$D^X2X-H(ҹx7na_^.= LYȸTV݋+ n E|ߍ`67m vF+"Ђة?~|[/J•x)1M473%JJ?|fY^[fh!pf$1>sԶm|Pv{nI&dI&dw/`F=YPc}IENDB`ocaml-rope-0.6/web/rope.css000066400000000000000000000024511320376225700156620ustar00rootroot00000000000000 A:active { color: #ff6600; } A:visited { color: #999999; } /*** * Code */ code.ocaml { display: block; /* font-size: large; */ background-color: rgb(240,245,254); padding: 5px; } .prompt { color: rgb(128,128,128); } .let { font-weight: bold; } .module { color: rgb(126,27,29); } span.input:hover { background-color: rgb(200,200,200); } .answer { font-style: italic; } span.answer:hover { background-color: rgb(200,200,200); } /* Banner ---------------------------------------- */ #banner { text-align: center; } #banner img { padding-top: 1ex; clear: both; } /* Menu ---------------------------------------- */ #menu { Float: left; width: 18%; /* border: 1px dashed black; */ font-family: sans-serif; font-weight: bolde; font-size: x-large; color: #981419; margin-top: 2ex; background: #d5dbe4; /* padding: 2px; */ } #menu ul, #menu li { display : inline; list-style-type : none; margin : 0; padding : 0; } #menu li { padding-top: 0px; padding-bottom: 0px; display: block; } #menu li:hover { background-color: #fdf9b9; } #menu a { text-decoration: none; width: 120px; } #main { Float: right; width: 80%; } /* General */ body { color: black; background-color: rgb(255,255,255); } A:link { color: rgb(126,27,29); } ocaml-rope-0.6/web/rope.png000066400000000000000000007031141320376225700156620ustar00rootroot00000000000000PNG  IHDR oLsRGBbKGD pHYs.#.#x?vtIME8J$/ IDATxtɳeWvۧ׿ P*VN&%% [aK#OAxj%[`X"$ * d̗nߞtݽ^{o}wi$I q!B |GQ ܽ{0Mj躎pqq,K^{ϓ9I(U=#|-'c)Ir.P%PJISj:pyy{E@Jl62M nC%EQ)$I}J(&1VY$qHElll"`1af)a, Z׽>8C^Y ;J*fYpЧR0޽ABJ"@JLƴ;kH)fcE!#|ףPLz$ 0Q5 C7p kLà @R)YQ2N%Y*bA`6nwi5d$%BQ,Ȳh[;(P ) e C+ֻMTUZkRHl:՘FM4 lPy^.}ZN Ȃo"(!Jb $cڭ,IMӸ@QFZJh;7XE NNO*w@h K3 Xؠ($a#˔7błf@$DQHQHdrDQ LFA2OtRJ`iDS4GU<'#dYr&f!T A' TMCik-M7( 4$CQ"oN$s$ C12N)[[t6nP\uň,MOٹqRD[0Lulb6o$()LF!˒$+)~< ~R" tM73 SuZ0(˒J`:E Gì٥RJZ4\ br=T`4-! c\1Eⲱu !IS$Af9*ƻR!NŖcZ Isn߾]됗AU$Qn /(2V#)Ѯ)8F!Z0ô%|6 V1E PT!odH$4,S5}-B*$i|>EըQ"Ng,ǧ8N[H2IؖAV0 9BA(OQ*T q!eQ`[|?k!3|wN0+Gx(@E!aگi,K) HcZFJpƽn2$"RX SBoFع(:e)($ipo%L$IVD qZ w1X&[,c4 BE MlNV XỶ7&%%PmpkU3T@4dʂ¿uêruݣjTҐPJ( L]!3*F L䪦Np5+Q-<EEJIڛ.Њ8m$fص.bF7fd:Ro9B1c(e"߼h $~,phzZ͡(J:NB@^QӍU%Ucmm_R%eY$'ipwKt@S!%QRP!Pf>a&eJE/"](3vna5sbIC*i2DIC ,CF.k QpPK$4 < 0{sAA!qw{Կ(9U "ʲDQ.a$1q`U, 2|,i @RAUJB`:h4HA@ZE4!aN4jp,p*qa:NY X|d6_Zj$I9;;EUtX IZFYyN@ AFض b5 }ܥ{aQ'T ĩV) ۶MdYIc&f s],#"gп, jJF|㐦)1h4jEh*Q&11M Ӫ $b&1LCD!r4Cf9wjBJ0@UT8!M3&qT,R'A!e!vDQWYȂ0,j*r*Nz!VbWPh8aX bP$]WAiqJT,ǩpK$85U#LS!KDqiYdm}M7MƴU`5s*~*NiEF!K,%MR 8&<,zY IX.f05A! D*Bv%Q dSoԉ]7HC[5EE՘MDq4(J|$aP* /N']8!PAYl2qLl,3(#/$i9zRRku 0Ø|@"tU Mțj:XEF!ʢY|:bWIs@hNe.4EuZ@>2s].EgE:NOQEh8Fb>]zsR4+y K4IP(h֫Rd9Z/g,&|j.YiEHЈЩT%^o1Fma&QiVH uE!Pߛ-gT*ID |o8NhA;5\!KɸxtiZil|EHua5(˲HibjB)劀KEnXCQS?K^匪̆gYS[a\jqFEc"% },B5B5TU'3lK\g61Mkd\!SkPeaרUT`5TMA4$\: ECJPL&>I"G f <$A3i4T*f `V0 N' .N,+Wl$I,qU[)k(XMp9Bi/ ,eXbRir>&Ib".HۮRqArVkһ>'Kb:",/OD.m IQr1' Uwrvv$”,+,4lPMDQ$KMΫd< Iը+04OXv:eA*E3*@\п:Zk`5 >i`:rv}$H] \!J X eBQ)bUyK4Er?KLBhr1Ǯ(U3bUĪؤYFUEBEL4EEbFy("r1MUYƨiVPE:EZq|:"VΩJ%qAh4T;Ƕ(ѐR9E" ˆlLhI!3Z(B&\_Rqh H)HôPU'!dINQ tUA ))qjo hf9ń4rql{Wd`gbf8&2K(PbJT,d>"W9RQ5WDŽ4- j(XFfg UVQ)qQʕF 0VJY))ս/qs4)R!rJT,J(Ӳ Ne2'Pmm8 fj6ISpn)YKL$  r$@$}B bLY$)ftBꍕr4푕`*"(JtW5ƽ#"g994.ȂP_:Bf$᪮Z-$!V.J\#Ob4[Q.nP)ꊀ* t)$bh%E $Ha BQ)乄""cRA2y*yc+>q\D$B?W8P%sr|Le 2TU% C,`:\, ©VL&u>n8CƍFʒbm CUZu0 ) 2ƣ1kڭ''h6B!sk66Bp~~Nb[0Ð/U\z<|Ӵ,hD`2Y5%s %:i`U*{TUz+ɔ/8:ibYuΪ*Ҍw Aq%Sa:;7q*ŜT*+z+4es{A!i2*WIJ^.I M8qjU docY* Pl,gc<ôhHS..Ω {Ϟs$I&Ȳ\)xWDZ~d<`[躁R0ɳlźO?AdaZ,Sd:cW*(dYYV0Mx+J!Ji Kld<"I󕽮M_hEQrwdL|NwmUx_?mڝ 4FUS0-XeYضCN)sL(eh'>l鷜#Tdpzr:`Wʮ!M] x2]34zx2fG/ng9Ya<&1c^秇E4\yH)!TMǴ<耣=B%tA }W}VhHB7zhWҔOpk$#x)FӓNE^|勧,svAѿ:_YșL\TU(gbWXDԛm1\.Fص:O48ufjR+Rh!t6qZxKt]CQ P4|>ySmnp~~JĔel2Ʈ%EQpt&b({覍D`WL=,KؽnZ2|:"JG{Bt: #>;wH]r ]]`UFK4"/J) Y}u'b)NǴZ]4;[(S(\\z̧#0Wjn5VNAY10 6B0)qb5,f6s)}ȹ8! <s yڸl6E 9QחxK&\`MJYR%&eKcފݶ,QdB˧f2aZ6'/lH[i G{dyQj;%+%Y\2S_CyN!U|ottMыo<k0 HTB%A2MŤ e c~iBNh3vww^0W֧8 IDATQ?C~.y3 Xqr|%EQ>{Ϟr||­[7Iӕ~~z}}rt:vg Ęnw Y;;ųgO/ſIJ*\:kkkh;| O>(VbIf\2 hlll{},'#677۷_# #f/|'Ϳd6Sʒm gg|?et6e2q]<~B& կ/Fy0->hbvv.޽78=9rz]>mlxFIӏ?b{gxO>.FV)|g"el6O?bS5~џpqq誎-ܦ,K8&z]f9+"_0*6V,|sZ?? N`0@UU wp= ~0 [w__oCAK/S5;^u^&qjupK0dsk^1o[dsk=RJ_6oPX,ܼuSUŶWvC4Ylb[;5P5LJ}b?b;=9Fb' ?{p?w(`8z.ac\/`}5\0I]?ͷꊃOc>#N_ b!'G;]|}>ʽ$>- Uiʣrn}Zɗ>ۯy;  JOu .j#ArA$KNOΏY#.t9=9[rrlNgO#k7{}Lp]'YƗ_=d<{Cvnp~rhF01՚Dw}aL#>eG;?f>/I|JѦV_GΏ_Pw]^<( y{7n3Nhdmc R_rC)z ϽE?}xMj8u\2_c`0٧D(I,' #CLƒ?-v^VI ">C:u&Oug%l}d"u](ͣϹ{-NOp}q?AE|Ww5z,.zILKNc *:*ϙfض?!auJ5Z -\17Rp~~HE39<|-0჏ٽ>[7W`;68u}O,ϸRҿ>g4sgFEe?e2.x w9~ʝM) 5$XCUU>O˝o;4g>fsowy Id/i$IbWbI蹜^0^w]m1̸=M7~iz]7VdUTFr9ßf^g m-;޵곾pyqư >tfG$it2yOx{~wPî( nhsO89|w#bl6Zkqӟr?:`6{G_wL˳ i_~1UǢYQb($.G/ȳG~)t*ٜ[c]y |Dg|@*%Qa\`U^<!1 'ϑҧM'/~Senܸq~q?c!6^jko8;9DQuS0_}˓4ݷ9y>wDQ碚%IyqpL>?&MeW'8&j>>>dGV(GS6vɳ˲]htwSomb46ӿ>kr#$$p>S׋xuP577{ujDބ]_̸w^#Z]fEAN #EVRO>aww)%''#<;;z=,pȿ˓'O0 ̦3!V4Mł !F^˗_\tx|7GLg3͹fkgO' }~YNű9:|IXt󈣈۷_<~-sL}~ Ϩj>Ϟ>ao)<{bsEUv;|/)(\R1y!XY0x3fӕ2| QKqzvJ^C=C55g=9p}u{IF%Mӕ +Mx\.bA%>pxxvwo`0oWwd~vo=~8:<"#\o傃{Z)5טVM78TiZ, ,B7 ..ι8g2#e zk8!/J9WWl6/?>EQϦ\\]"N$Yid4DLJlnmO训r$Mc./OE5]b[Fe)Pt$ݮcU$gƛ%goYQrAT3ٔVgC8lIKi!$vY9s~Jh`+W.qtG{\*R]w7/5PAн~H$Nd7T M&gO@h6-4 24[x}^* Mޠms{h4+|F$IT5TU֕yZǩC**;[0śx73L̬n[x:_`fvA409= tit:n@4)dh&щI^|iZ c:cooS,d\cs: ACdGo7SLM!JF2$nҩ##$ٝ|$>BE DLVervG46EG2I!LJihǩ-4Md{s߅*3^_11O-vJl;8>:?и3ԫeGVnLf.'cDBbz՛׈'fNq"$+TJſt4Ueo{ ,F3\pN뵩ߺ]X "l]#888`4Ш"\,6ZJZk 4_k/^VP8jv>FFl47X>s<-^?Ց#:= M,݅jШWQ}z!| $,޸( \z ~_oR$(•+~:/^ի8c}U" CRhF$'s3 t F#Fv;K$&X,׿I(˔JETUu^ynW h;L&]Jbj `mKSB4cwwfj&vv)|~`OL277`׮rޢ^S(4pH\8/|뤭RK0l6/5 ɜdtJףP_1-iɬUj|~^~Ez.ƷEQu& G|ߠT*" x}~l6@׽MlSոr2NEZarrJ7Y;x<Jwv|+_VD>_`0RZcXNs:6^@ IuVB>bfscMӍK F.3sF*lmnz|.{R,X,u (bX1%.7oz[0ME_̷oh6=l6;˜> PmD@gbh4v:Ȳk.#IF*\*i4jw$?]SK(.ng hg)>a^K4٬399ͻ$^N0`8lt]Vu|#D4 TGjg:7_>KTb8 l!Dxd2uaTMerrQ‹ѨWxc)hvC$E"@PqMMp(J_x*qJJNZ ժ*kU&#`χd6r}_um4v0B8E@х"1}-z.^_fF0E4Xm]Ԭ76W\T,K,V+$)K{}$B!>0ZTuOUecZ݁?bYvwX9}j F` oe |J^n\*pybn__UM$ݎ«/?7i4zi"QQ^~|0)IL( Isidg&&>bt~G4vw6 UJ\})Z&06u3h7y)]ulwPeESٓc~cOҨWp=(JKrt $q{A|554U.t: v_X" b0G>_~Uawb9\>@Ãlsrr|Wycrr4Il6c"1CRa0豹kTZbtst  ;VN;"럾B`r?X,V\.XtzƵҳ^Z)X9{/rcwyo!:G8]OuV#\t  1B8yu?RVUL[Q0M(JzɿQ1D݁A8{#l]dp˔YJUFi5q'+\=p\$p7R>:Z-31-t b?Pá;En]K?VNYG $b>'>.Р^+3u;$ Qi6j4U?;mwE&/}ȝfxo#"A݉"pX9s/vp^C[_"J"i b4"a&Sw[ =dpsWr$Jb!@8ۤRĝg5fwFMXdVo\^jv`(J&uA223H'w)fIQ5ƭk'n\n//R.`99k|}:/?mZ*{;ceyNѭ?j>$ɨLJ?&<`@0Ƞ#>>`vvd*??y@XdaqL6CwQ.y~:./"^>vi`mmJJ2yćCX,Vlv 059 ~?4>GyD"OG#2 x|lTTx"׮`fv}BVPiwP5 󈂀("13_BPzgsk л\L'~vC'u$&8N9>_$c00MaX`}uh,ƻ^ʥ;u.&d^ox[A0Lа5\2:{JY_狺Te'i%{ͺqhb"r'F>6nu= J#2^~xBp8[Q>_  %( rxGx䵏""VhB4F!@׮'rE/{.[[j5AՋd2-M&&f;<cggL:Ebr}DQ$prRd6sU>i' BvX]A>?ޟZ`fsR\~xkei,$Μ"d}[PM_0L I=~S{>VNV-1 MTns,.-3;OVNIlv''GoO|g0̈́q& M=X|w|~ ]w3x`0Lc0Ϟzyŗ_\׼@0""~^K.!Կ|g"M`e.pŗY9s.+GpOox<I IDATD4 łf8y-Ν? >>biĢqr!iav=FXEݭu^z9VFTAq͍5n]'Ҩ_ × 1v{Ԫenh4O0;ïyvfp9͖4ܞF ţq!nM0XW/u_ gϜcfn,#" KgӀn\|?`0$Z)`wyQZ5Fǿ?ַQ8N%uvKD@(-n]e{k YpHrtK8~3gϣj*6F8\>*[M33s G C]_X,FO09.Z*,#w5{H|LAUV,,3 ^7&0,YE7F>?QӨՈ4 L@$̵˗9h+-AϣXlF!nO'}^}Cbj^C[Q7 N%ɔ!#"Jxb0-Nҩ$=v6H'ނdF9ѐ׿F'?٨QM6t:]{0Fq5(r޸DT$&gx>7zy*;(䎱v*׮\Zpu9]o~Q1[d&@?ql^`4SMV+ן+]LfO9wq& $'-6?8:ڣV=Iq+x}~fyԹIn39ʥY9}pl@#2i֊Xl.RG{/8}.LFO Ҫi4tz0/<Czhl1M1d dҳ49SdA6Taloܤ4eR|gބd҇6hԪ|7m lۏFvxI#ǐmN:Jۏh9| mߎa4EIm~)Nk/`TW^`8b0H\y 4~XM Ci>g΢j*zy'i2 t+Qm6:6Ҧl aN'ǃ(m6 0Ol2333S nZhP7| "++4{ .>.pHߣT("PVe`0"8Np8V\Hdxh4A\i;{#C}gw;cI{{ﻟzJpőe+Ν? ՊClv;~݆lq{t:rp\9}?sffzVlt&}|6A"yXV\.7~C&p8u;4uBVH,΋/i:}U׾H$ 4B.G^f3&ۃ$IN>C,ڵk|q X|L%N!I3LMb۝ܺymt0RGj._z[]4q:]HfYaZV|~{NӳXeyL״ ˿`}KG C0sKTJ%:G^QdssHtIl~;ۿC^ezvťSMgh5\~N(LL`wzX6N7ƭ8<095QPP2H$ N)dժSz,,`003׮^ I|A6ncʄQTUh4Q.HLx1[,F%0 菇n@0trp8lRxBJ @0bhԑm 8K{!,+&v TJ[7Eh| É=M& <^_㗘%pPU1Mffvc۱26\&(5 &r ddjfd\mHD">&u|n 9>ڧ뱸tpH8@4łl#33xGy~MKI&!"bOz>4m7(@vdMp~X|T@(L.f2]E TfR, gEHt^h0E;Nb]VS8\ +gէxI={ '8wCdj#u`&61dB jV.h03ӱF*f\ySgB;EndbZB8e$SD(FI'8fDQƧ$#g:B(!1=Ocrvl\*OQmbfq0$?Oa[ Dݪx^ 'C]#ۉD^&19kLN/"$^I'uۄ#1@t埢)hRLN݅?215,,r |C>yj)/43 G#"S̟nK,`(N6Z cS },V\'۝;./NZGSE$>G6EIX,2.OMSYae\n/bdDz6 bq}x}!.Ӌ CZj|v E#LbL4 ._!7^}ygq:=HF3FH|Ah@B.i#"͎35dfn@\.P-qXe'&l7Jz|AJU&1A2 MEsij%d`4( }S֨1<mUUQUs$bF}bgt:$_ Inb6IHÃ}ztdrvbw:1Z #?(?T$Ab P)Ȥ]`~ g uT)$2 ?4$"R pf F toabh>!Ka89Zz73@h6U G"a3j +Zxq&VMOc1 B02Y-No~!880~[fVWrMvv錥kVˆCl6bpHgm}bq%RIT  V eSKtng"^hq\(md2O?M^R|>/ZhD/.|h4bn~q xz=WV)TUfc;8sfD`@ z\Uji.DH&AS5,S[{\TF\6k%ILM i5[X,VF\Fi #:=HN(AÏL]VZvfd6ҨbA'8ݺ|,8$S*{x~441Q-WP㕚P$lIeۍl&ۈa1^-]3 E@F%Lf3|n*8nLinT\fV(J [7>Ñ>^T< #I'lv\f"gϏA;^"zC`8QV$b"SW9<:fw3\45&'9166-Ea~q@(flԉ3ѴUUӺ. ÁG4InQglv"\ʓh) VL4@7@dF sxEiHBhF\/>|:$^+_!ϑJR.tQ*z6{w&NC[zT*EJ4EQژVZƝ ņ*۸+cCNCcu%A@2yrgpHlq88:أlFX|r!}ܔiHq%6/h4b4m&=L&@RզWm& #U3eAg8`sNTUSZ IV+&e4fCX6.Aq:CdNpK =>R!"pIQo:6@O!Ai5'lT&g1x|F 4۝6;nф?%Qw]A tQA`dҌF;a45 ?pe0319s uڴU&gPU=tpNqjdh4U^m$Tͦ1@GiqE6Ŋbbz@$@>WCD,1 3c<ԫenf@hUA'b2Y^XD3щiVαQ2E40-L.x^:ji:=iQvFBlbt*E!mh)r%l7JЀvNP*;89Ijԙ^Dm5&?ClrKfwS-ƙU-RG{UdSǔ b3dOR? `pbS CfB$NRlorPU9N""vQp| i&&h) V7v<(XwiA"XdUS)3Ss45FLp Fb޼rgZ>wh13ljâAV NIR&IlupV l688GMSi8=AbKxqr ^rIG &a^JN#uQ c]$QZ 4" *JB.6;.M,;Aҷ]99dR|+2Ilj;(8N h&ij)q"pzpxNڝ1ɣ=z ;\L̜iFt;-VHY`6 N09NZ9Ҭ35jJj9wØLf&If5i]KfDɊjNӇ$?K% vǃi377ǃ>D|~].]fTU;h9*2KK d~OpGV%76AOV*D1.^ȍkרUL&SSȲ#<$(R<؛;+t/h4v9$˲x2~Z((-q2I0mv=p8ި}D# }Lf36 nfs~SΞɩ)ZsIJ;M|^fǣo "&Fd0u^Z8\:K/>U2<^/hv{5a4Ed(tIi)t:vNelP&}?{}2ׯ_# Dɀn0M*~LfDdd{`n^GƝ9{N`>V1BlP*щVUψZ-,..Sטs3zvAfsP-*޺#1ffhd;.UR.>Nn+S|~:ǂlptm,f+)F! 0L nۃ&J" 6nPZ Hwtul(pyS#1oS xA^mVMfguJhh6&P'!%67VnA[iE4ha4R1͔JE.jH|x% R`Q0[,,VjQYdM_~YdW/5ֱ_d( 4M%uO , h4kaES{& R+zS&q:,./[i+q)fH&>1ԴU5mvVY#z0`0`zvѠ>?7] LLHLSXcL&3dU'19lfwzQ4P*e]⤴$ɠ}f\U0LnTM`8RWuh% SVH&3vS=^j:7]l`0G,h|!۷Q,dBHpeCu; 6&n`8Iv3.gl( :NB>Cg56Vq=,,E \*{sY*"w4[ B J4z4{0=~#.7ӋsY0nHtN[ߒ6=_*#lo1e-gY8  XEaj &nC'Bi5\%M(CQfL^1=Bv7}Pfq Ibz&dfbzpll#K`4fuLWG\lv'&5mpzqsdm[p9].s}2'G}f)&~ǧ02E46g bt9贚?jX"YVo\b"na6[z[g01#`fNQrprk'1e^Ӵ ;i 9< v 6يh2q?~2 Q*d!._4e6pBȲ׸vZe6NJFV"D*rI7F>g Vz3Qlg1젪CTu"Nq[uX}Lϟ*1"6Y,hBi޸I$ITS&Y<Պg0rZ&d$ 6n2h˴;,tMDw@Ñ+tc Eb(v/YvMRi6"8^h@ 2#u"ӌ4hbekdD2p$X:^KVvMR+g@پE!$9`G&15FCGQ˽UB(nwJ1MB; b4[ Ŋls!;<$fpm$f2MF ͡ibK,`4[ &wXmN(H{4 DP$AxPl?B0:;";H% H EvK!j򜜤ɍNyGz Œp Mlryl@4 f+Azl%4MbѩY~nnv| >1܃ܸq*Ŭ' smoR-)0<vL(mZJJ޼ IDAT^H#bݤra;|27`4SKzx=CQ.\x+/ W_0s==wfE,Pj5|4"ug,p452'i0J:enw(!"V67ScC\zRD.cqqy@#k^Zp|lrx͛#A\.11 }:.+N|P$r'u\* ӳLNMvuͦlVᐝmw"Fdk6 ph4g1DQ$HL[J3gq inNjX,qtHQz$QTX\\&SW%ruڝ.v Ѩ'g3i6jVI.fw wvh$' C:6z݉( wkt.](j>U`l&Coml6 T.fH4`M:,c9pSgvy8>N)`Z`1Aw FѠ':KDrM8&pb%SgR.sxA8-ElbjhBE$I=ыfɈ`jf۫t;t]$h4o 8G'&!p 08L`?K/|ٷ;Xm6,6b}fSggdyZ?}ͼޛU]~1 %@ 00!6bCVb%! P4=3T>ɛ<Q::dYlu0Dv9y"ZNAv#|~? JD"I\cl$YFƙ=kgX;"5fŠgd|'19{b>+dJ)OpX[O*~S8IlP`4qvz=8ߥQ?-<?GOMs٬lԐ&XGgF:X,fl6;f{tm4ZrL?i4dRAGx bt,\̟~Cr*3穗 Oai+ s'!MaDphzSf3RAF&8(Ш0 VwɦxuWr<>J$yy'^$`0p^|E'&DP`FÙgŲiy 242L%HPVf3v IdnWh"IkkiHx۷osi7nL&@pR4^O}aFuizHSY-;],f3\B!ɓz= g lmobT% NJ'R6Gm|R(J-gZmD8yr,5U+ڠ^t:W JY-V+߻0O_%4}*hB3;(ll32:bT,h\.$Ix<$Q$  `j>}mGd3ElPV)$1Ve9j188MI^FN&[}:^o^O@~5$SI{Dd5G& B\b!fgkހbA7Rژ6 z^jUdIlQ*h)n/:uzjdgYJx>FHd QODZV. o`xKQ/IF3hfO@Q49Μ;` fw#VNE%'(LL)6X4uF0Ll]\dQDQZLr潷G2cpxÉAoTtiX[t$Ixhj5sݧQqLN/5 F=V N,05;V ,rh0V9nR&VCxԪE*V("P$HƣHbA MQfxA.VG} %'@Vd jR)#]ȥ%G>GQt:z4pHTLZЬW6`R(nc80 VF I% LJ"6hZ7TCӒ$:\,߻A>Ble{srD2'q{?vJ`hxYz$5$>Fv$1<>??x7"IBvZTb(FF>z HTL` O27iNш( 1'NRc1"햂^'8f xZJRD2:2F:fQU^QOv:G#lB.KT xR@ /BEzYv6Y~pE}" $ XCLNͱEK9>RmE^EM&ڝfwe0&DWɤHg/<ȘdHdFև9XYi) uòNG݆L.Y\ KW?z\CVGQd䖌`og1 F)w\R;/-t::N.c=zJQ ?"MpWwC6P( K"Bz?bowtCA2.RЬW3 rn˥ZWΥZզ]4tD~eug(VFgepx ?D:'4K@cTL͞`yOpU-IbEiaېeTYXypNO^'KI?S*Q_3 i+E384,Kt; NlJ1KVZ-J?PJ%(B!RgXǸr)fzR.PZ26T2/8lˣl:A!Vl6'k˸<~, kAo`{sEh$hwAf= 4%4ZTl&dV7} vp|AjHb\^*+dxˇ``0L#7t*-YuhiuzzɊFY?q^dBj /p{ɤUs*⻫B.s ZSV(s Fw\eok;.2J M.q}BCqZ*6ՍL!M|(NRRTP7iu[M^OXrܣQdL:UZԉ9]QJ&n^odx$ i+-} :C.fN"KUfN&f0ERP(K<lR.( llK>gww@ NũS8<8dgwSNsUJ dY"UoǪLd/a(J5_X8z̵kWy'h4cZ4 ^/JSZ2_@g"(b4H2tZ<یxcfdӧOJM90Re~?B%N>͇?rA<&''q\Dv ^D-.-.2;3ǭIRGGx}>4Z-7o0:::Ln˭[79v-vv)c<}G@N}9u~?m NVNG.]bn|$qZ-| .099#*٫^;FG?S`o4-N:Nl63vXmV>c$ID0YYZbI,Odt*ESTEl}l6ÕO.3pR*D*r8]jq:\./#{F4XbJ[Ae40 &$~df,Xwxv[DїA\1?Q`y[ IY`0"v|ӳF8##,=$x]:>.=K[Q(bmff)rM& &D"$I Lh0MԪUL&!lwI)x6s'Ba6Vkno27*W?"ztH!HfFr:%2R9 ;ۛ4:^>F0q~JD M?='NbXYZFEruAc8Nn,F :{X4 H$ݷހ`_6u4Z-W?dtt$dkc!v|6C%/P*$u,V??jNOp`HK5j q>h4ޤ\̱Fk%KDZ 9^?J"J$]5LA ^_r Jfbwk鹓^߹fvS4:\y@ޠgw{-7W2{nc\6?<a(T2FQGl 8:`4bY[l;ԫ:-cܹsÍծv`dIDiwY$Y{NjK'I$mE-:]{h4*<8ʃKN4zFJEyҩ2RN:3(K‰s.215Al6Dv?EUh6M r;@&X, 9yYݾfRI2$̟ey6TNR)v{q)Vc.a{tl&~|HFiR#~ϓEǓmBxpJ@&@%$t_M.S'}=G&%h232qun^}::?Z;DpoN_h4^"=./Zӏ#6*=&Ov?fB6N)QȦbhdВ̝"U/Cc3N $I1 zl[ޢT*av bwX_7lvvt:oP) X~NCZj144LTB% (LZQ)S!L@K tm6+vQ677lTq(+rY8JijgX?οd2q8_fS-7 Ea}um*J\# SV Rx`eiQ}!bQZz1bKZȭZ/ law8%= |岘-f2؟VvzFwz7oRTX|DvjqTGEVeioIu!t=L@0DՕZZ!}~GyܽuCE=ܫDO>WnѨ)a48+@8ʝ7_8IX :uv.?$1 fF%HPhjD&=$]!b#`@q\6Cxln!jyfv_{YP)}*k.X,$z]*묬'e,+4MLv{hu::@t}(,p:}&|j|%#6 Nÿ_h`$_wO.nwb4]&BEJeUuy(>jVceR4 LiSW~IZ6VpcyxNժt:m*(r J˨Wc 2wo`0}OL`6G{&5շp)D<^㓏!͐LĈEH=(r|>сH%qcjoFxhեl/( ZfoԿxlwV~b!G4zwsA_ >_jL0~(3 n\VT+eVTs2~L{od8$@R6Rn]#`VIb%Gpy}t:=JE4.w v|I0sl/cZj4~̵baVo F#?~nGC'EtLNoIĎ#DVKBl1T?ج2=f4Z` l'z5YsKZՒh~W?xJHRa4^Z-6+w?) z]KFReӼݿGQ.Ϯ$vkK>z;$؏.sѨqƇh4ZnWh*a(meuGkwyU=5aqfu֖Rgt:mʥT.Y#v rW-Y_ej$n}HQQժ[owF"fdYj#-*qOlpuco8~L% F^KܿNZ,zn66KtzaAaz4G|huhu] TK&~RUYU&+;9^Kjew8>ef,:;WV҉#uڷ`Ғ1:mRr)Տ^'N"u.ՋxhzpYY[]SH fffd2 SV0o&/"NS)W*`8w__'244,H$~_ /`q8]zp=jnw0[̼gϢ陞Fף( pXu`cZ/?Q7ŎZ,f JZ?LOO22Áb!<~+__`ppH}(cXVv@y睷AT``f>}O 8^z鯸tQa n_+~_j,&n޼NVCԇK<'h6V+.^"˱Âk(F̘}g/lB!^oR)c'k'|d" 0 fRN!>Ul6cAdIB%.j^?kksI_hhpLMOc20MOLr#_"{N ~PُD88ޝ;RL٠hɃNqW_$IH `pṕ t7HZT+dRӟ,LDB-`o-OY&fת?jt:ɍkWxK8]n\nv`FCV%3qΞ@Qh]RS%CsY^/O?_@y=|=6֨ի656M&QWPts/l6Tt;V+Lp`y_g_[ *58R*24:F2tVղaz#/}Ox`1L4Un/D來-c?E04L\ XbL&ŭpO`4l) NK/g_a{} bōki+-&&AYvu{@&:,3 32:bfs֍O?# ^K~g VX[c٨V(ng%( ?YR n2TSS3|?牧>էg@!48F&_*O}y*h+mj2op;qFF'ڝ F<S%A0w-j:Jb!$ɤ*Wӓqz:`FTJO=ƒWXlL͝&Gj6ڝt; `hdM ~._z=$͉(.<6fKOt2NjqzCh4:dIo%.=EWapcok ŋg?˳>K0h4h0V.^|F˅jxH&(J$I$Ij244DZd2dWxv> ϟGMxƆ:E#- zAP5<;[.M^瞣lIq]pG  6{w`2dR/:NB>`2393lQE t{LpQnmXt޻o}\&VK\o/=ŢNA({\x"w_s9 9˟ϥg?yh5ZcDYDstxw^{>KkFǧ$qvxȲww|_jwr@e|>?drjSg.t0 %";f+ʃ{?$tjV@Ix\| F'fٝbC߽-/0.G^d^כ:w~,!ݗt-]ݭV/Rܲ4xQ{f21$/AI C fAdf۲c˲%ٲޗګn}p9wXD~UFSD2VZL,au}^# c1&L nM?8xmdI Xf~Y\^w~KQfw2iTK(N'_x\PL/0}/?X;MCuGhDb)v|ooPHv봚5V+dmO-¡8hٹ%HIg:M}n ftY$ T=Xdgowp(ȗ6_}VʙP<ϟ_hHnV#6 3?3jt-@Guy"rFbK_˃apauXE*Cto(qCWm `@2*P-XY]Cަ8T7hyGnk4<@E@Ӻ͔=o}7:6[op~v0e;Oд"ʗ vw`wziԪk5~7%ψgvq7{>|{aڍ*P,L-3 HH/!rL%` ?{_&5i%d}4 ɲ>ۿKc`e]&N!{DZu~W11sSM,b[/]f#g]Vs),--py $I/lP*f2+_ѐXr }<~ɏ8>xk_~/MC`0iQ]*Qk)6.O#f:MO| [`g.6p|? $ɆF 4/CdMN*H|v~Q+2@;ߐIlvdنzЧWpMu.w_yVfWǗ|Ez%N/[YYFS R*˞rm$2ͦ0q"X"{o6݋ha?OGDѼrzdYWhZj)K0xyba0Jrr|XYY! r~~N8 0_X@$7j/~VGi6F#"^WoP񔦓BRpdb>3y\d{z.NM42}(9@q889>beyw_X1;;|޴Z2>Aq8Xj1>s^wrY2q+1ZUF!J7 ;[oikḱŊ|nNGD4MevE1.W61[[Ӕ%>~+$Il^DNfe">ZziZd3e}Mc5ިӨ7Lt aLh6E$.Ma4R/|坯LU+dX2N^woIf.e:7n x7R9rթJBeH$g9?;A LCdB8%O$QR1|>._y'o2\B!O8% ~L>dKEkװX{T,"^3|r?b|QVuet]p+.Ȍ)pkWDuyœaXLz Hsvr t]'9;b9};G_!J2b]3 $ŵ%*" g{̦$? t}+e1(L&:bẋ7zLknZ)?koqtǍnEfT"4Uny~O@5`JT*9px$g6mJCbYlv%<7l6Nxշ)r oTh>A`8NѠ\ DQ0.Mt*~I0lvFTjJT;4UwDo.f (SbYt}hƳ{[L&Y@+*LJ8]>WZ >`ZQ4 0xxlKokM,L68!S@xl0&(,|P̣:L0!"($hTv<^TՃ$JXV6a;(IFC7G'l|mVo6*< o NEp5Nwq]pW9T@z~ vF1 p\*`4ׯ﶑m 6s*)tr\?NwǸ!&Ƅ\$zlvzhC .I"9$OS~W(N7Wo21 =(Ji~RF8UPguܼuLM kU%^+t:>nbdz dsB1a0 `4i7Dj~_ 0ux8]V um6^^!Tb$3 +WP,E?3sW#CJn5 C8.^{nT1 # &4QlTXZAx՚%Z;ۯàߥU 9K/!ll=G?&9anqPc4Tl61㏲`]F#F$5pl2v~O"nzbx<ܿP ]~?}$IJnq* SkfR钅EK:6NURi4!$ITUE%N>)سXwb1R lub\.GZl d Q8<<03z~1^ Dd<12%Vf77rg}Bs6ߡQfp*@2) //4s#~TUz>w.vws EV@.^" "*/tcv;]q<94Z-.l$Tx<FѴݤX(QF׮s+f^ިl69>8`~~+Wli XJ"hu  thZ"e `n~f3˿^?>_vMX''G?Q;hb`0dnaV&9Gul\'łn@ӐeOc> J $ \*~dz$Z`vvBX6} ~.N9;9z]Dx|P0$)>$8]"`a-,Kfi g1| 8N*KdY&O E<~􀝭THD*e>^EY^nQy>|3*/}[V-2E%XI!y2љYXC(%ǫHa@z~DP+ 4dNM P^>y@>wJ#3):w:LtM*hȍ[wV׮q-&UqޔuR4DeOOUH`2uZ-3ڧMgl\3%24Z=3[nGl6\pthH$'5;O8pv9][d6=PӨ #X5fx}~̋8W$D4M }(aSF!7Jy ۍ`P]X}8ns2slH.KCͯGS|k8,LU+4UZ9lcٹ}un}>h ŭo1  ^XR.lzJ&sFR*pv|@zqfU ,nj5Ѵ.n]fx<ѽOT.,XX\"l4)+gY$Qn*%| gK&Ai)Zh@ݤqE  cLK01MVQ`vn0g/@(11#_0}^v;&7 \d&9/i7D$V_n|QgP]Reh}43y0&TrGd.NvZ j2udn@ M3F$fX߸I?EĀ5>WdaqX,7Frh4sx 5QSȞ1(G;gZK8UqXbwYzkگ՗3I-Q-wtUT'f јLie " Kkhph׳kU<LB.p0K/Arn[%M0|R+7]q0|\_ #$8vڀFD&S7~Pc2ɜB<6$H!Ee\1Q~tMcT߼mNv;bYv& L eqVKsߊDG#yvt{]dI ϝQ.((qJrnFl3/|NO눢ȫfL抢Ft:vE}zPY 9PEqd:%Ibi}d  9ɘSa( qzzdrLۡ͛I2^c67M7'L ./Kf:}S6?H(baq ߏ@Q&vOX$X-uMx㍷88ChM*d/.Hf<-dv6llJ8= NO6\%S),/j 4v~z+Ȓ \.!ϙ1N&Q¥h$S j(Lj^].2o,O1D U@4|>?$Fh J ĥ$ΖpOcl6G=})JEa1u0 GcZ6fk >*XT(fFγE"fpD0F0ʕ DId004O8??c<nk ,45s@j2u\NsP tm$YFdZL EQhۈ :66jޔ!</,.㟙i "  @Y},b"]DAdu},1 eX<l 0өu'Dq, nL {;vy1ngUSVHΑ˞_prrr>Q.?He8!"~Ϟ0hĢfDNUJx>`2Q_dbQmvb3-eA^ح8)A`m:%IZ*fv2Ȳ ?py hV}.v7QvjK"Åb@0BZj1 4d3/A~n'5;!^?AVc4ҬW4 n@ OP)8=5 s ˬo@dQbk6;"2Wݟt`Ѓ NVA`nnѼZI.L{,U&(pZ$fssY=jaP)QN4ZJYiO}ITYa t: r 6">3KBNیhuL~V XDc DQfaibֻ }0(R.4U zZBݢլ2z=>18hw^mtl?ݬbmsqe:{:/r{Q)Πߡm;uN7_a48Bmx=463O 4nUSfH-H-Eh4*5$I"Hf/$Tynhe1C 4 5Z "ʥN $Q, GcZr{z\ '1w) fy5V7n8U$Qpzӛ!a„x fO#TF("#6|ՆFq/+DNOxo0J `u02}ͦ`WL!YF5e%tN#A'ªQLA)>{B82e!C"HSTo"F<*~wحOKFCy}ާB-6/lSDI"ptLՊU P=A[/{ϭZ)G!Ac񋿫׮OH3d$OZV?&pE4>K˃ba2k=!(v;N rv IȊ˯ Ath0p{C {[ȜbS\lJr%Ɇtu[\ojHWDUU0VnC >H4JT]#1v0ظzRŋjϷdܻw0X60kGc:6. ǃbWED"iFf:9C;ѧ4.H]D..ېdcΘ_Xd,Nb)rUl6 qJDr9C"t""Z F=n߾ Xv:d3$Y& 1;/> `O?O?az} 9b8Xwy[QllQ8?=Ey!㱎REhH.{At x}^v;/ݼ:јfB0E~1bPhZ`Q4p*3V=veѻ}N0\z@0 `u}%*acparURp` pjp\ G^\'_nY3j>Ti6L 3j'ERfoo'`>H1!O;D"qvw\$,n`X DH$gy}&D(`< IRV8=:Xțӎ#r H c9?eK/((,ly3?{]Q(Ġ$J/2;bҫ$A9;cwgB >Rv JqN ̾2NՅQ>bDrFD>5=/'n{ZzsfLөt9 -<^^Yh,5c,VbϞ<#A2<.OQ1l>}@"JhH6sFQUJgL<cL l6Ŋ?b(Eg }1aF!ڠSFZ FƵBfxbpWHͯ_TKyFhTKZ:G{ "*IS,ftB.G_LqUj:NIﶰX àVP]N8~u~f&1&O~jŦ8ͨfQTSyc ,Yܪ ~ポ7F`4E4>Ѭ8?9`n m?8dvva봑e3T9t]GD~Hl& 8\^TiqtK%KR.f8e"2,nU_!zn$`PV6%I6dha}&\8(-a Ύvz/>Ȟsv$ِ%;JZ,1-6+WniV jM롺 da Dg84;7\m0 Τu5+vҺ?0Kڵ"p ł0{]}4b4n'J:=kR&50[B! <>1n331}ézjrgB!c21z-,I$p"S+xX`U@uд)BzH I*9DB IDAT㡆 ܸzA={AQfR˄#Q>A^bSll>^fn~If2MVW(Jz " zv{ܾ2q&OXY^%1uVVi4nCpv㥯 &fiiͧGk7Ol\Ee"(\`0|U^W^{|{hZߴ?ߒH24Y9Nt]QH&Vnܼ XpMVe%ZB񒵕lrQz^nOL6hltoa .KFbS{}{vE0n";ۛ\ɘ%j*Pm`q?*$ɜpT7$[O[Xb{<|pB>^^VI$1$HťU=gSCaNb!bWH-`DA ːLX\\au &''mL1;;6lRCws<~zXx< >秴ۦkaooH4FFeNz.{|c @/>+W8MD (=:6nӇhF^ӨWظv,ʲqu$Ibe @LL&lm=cwwZ6G)$IⲘX,h%gjJ[^g:5jU3*2p8t;nCﳷr {|里FC&@GQhZG/GuTeb!ohj^\YG&Ʋťzh4U1^]a}}P$AZŧ~8N>ߠn6fu&ϟ|5bj"34Vͮ0k  Sv6MRCe&0Ś$On7q{/]`g'{ }TO~@t?AuNp,1 !/Pp$P@_k`uSzxz q:U~3ΎDӯbSQNHΑ_g!JfS-XZAV¦x*JAzfgÉ).3>?Klo=_cd/N)1s֗(2dSDQ$0 hv$ypc* #s[잧iq{^SV 9@)rDI:n_!M*X-VavkVA03PT-~ o5J,d4q|[T AdWxsUJ9,KX%;O3yo4qN= &9?ǘ@T`iv`4I !53ꥪ:2,Ib"~K!ƆZE|&LbY/P݈~MbdGt[u}7-j9;9pQȟ~xEq"Ij}^I6K{Ϙ[XGT˗ } v̤h6x|dc8UȲG(ۮ)/6f07/,dchRMos~O٦j5)NDgҸ=~w1ޙDfSg1&:#cʣ1ͮCeTjHgcO R^k=I&fE1]%:nl&EDjj d2)i4M&ŀH$p0$13jD" o  #KM] x}^:TbH$B&cׯdq,,.}J&1b\ӓRY<~!|GTtXY^>d8ZT*qqqA2$ p$<`B0egfkk C`k9kW89|9|kX,sh6I$h''<X,k%2 .v Ri*+kȒL F"rf3h.;[[,,.F0 6$q|.[2|eud*r䙛5o|}*>D*6;:<$ a1{&FfS88EQXvTJT+eL8994KZbtB(ˢiBc( |⒛ݝ-^yMNV*rY&ƴpO 4-_}Di])3$V@Ze<fl0Qgee ba42߻GժȒ'mui6fI/,|uJY/hUΎå>㲘קIIk6PNIY]`o=*2d{W(ת8*gF#*|@(L#& <`X%b>GRjP.I2UQV#nw0l"}jxDܢX|g$g}}UUwU<===9lծ!!D`2`c8$8l6`|8`l16cA$I(ar9wC5{v?K" vfiN!`"ZEz.guv(nm1Vl[=.u2>AlxȤSsZ&cSsCa}aB Iߦ#,ʥ" [}R>KmB=gnUUCD'.LE9jYYb} HUՍZC}cyz߶ Yzo3:1M>O(tTrfOciZ LSgxxu2Sa4p{7u}]:rҵ$v7G?M:cE U#P%Yvo288D\ Q)A^p.@c?CT$bj NN쓖 lB:e|b|6ρ4U#;<XDKL:=O`Ed7lfz}]Fh6{d{ OPTLT\.Ë(uPOQ;ɏQ57O3v ИawoT >2 (2:v(!fGjohLZ.rҳEr~3$b06U(^Tb7l6n6Q>QZ]E$ZmBLl$l28=Y qE.+9kpiä;;FauY$I$>2-_BS4j% ^\& {;hi9rF(O1qelz癘Ta( 4-첂8=Iv)T+WOp-DCӍhj0LG1Ypk 6AZ%D$Kco(00 Օ* jmd2Z-^jF:ӿ?wj5:}o˺& N9~?qLLLP*WHQ5E&׿~.Zf6U* #`bb&CjsEqݜ}VWVH&+_RLø> \x0v+a3O?Eɓlo#J1ykkFAx 2D@uVV%`h gVqu‘!RUm$&Ic./zN'lK/n9u^bdYFS5Lbfnfv: Ɔd>B:vI8 K%L]VclbзE24`Y\''T*V&Sx}'m~IvwDIIR`Mc!7铔+%ֿ\fI:!ϓHWCݦluZϱ!>ʑ#G]W7n-{>r >S3+{az=\-D_G.Xgy#GH#vg_[l6}6WiZD"Q<.Ro}k_P)tf|_# 0S3hƣ?@XjQe{r9]" RTPjNC󱶲&tOgq{|MSI2v Q ,vlݨ6nڭuiaV~,H^#e :$ rqRݎppQ7t2=@H4Z8.nCV& ѭW)H+?y|gQry<+KXZDQg)s|k_?-:u W>A%y^OJz͆$IVAZXY@VeaV6IrcWXboDz.j8x8|N:`]ޏ(O?$aN vGӦlctW9z4{`+x|| DI%(H6;6I"!M{_Xr|>s}=kUӉ(BRp?t C;<@Q=Lf"^.+8! Cghhná8x_cT+n7l Gnoq'BSʕ_E6b{kjٺ9'>l\N7ãe#cV72'T̓$F4v ZK܇nx9qsAT?@$rkx5z+>Qvwtڸ=~z;L.]fW1uӋ&rX(pǰ*2ۛ;yivhhD5R8x Cb72(pF.}+jx|dCuR{[TE>Cd F$:,s;xTbh,Nvx1.n#+`;)4ekxd2k=n$H֡ٴGÍdZ_pEJ*nzU 8lv }?9;q=lY"@0B2!`gsi]ղ8HYdHt|.MV푸,`l6@xAR(\4ZoGJ, Y_;G(c9\K/AQX*qCUuJ,?7xֻ(KR{q,գ\yllŁ$x, u@IUeX7Pt0j:MCt6M*v$.ZєHdCٮ[ae[εAMӴ ~ڬIa4t2 hAEo=y/yTU%So԰d*% ~?RCa\/j$kO` Y21fV+M366(J_͆aRH8'N MӬ8GW9>ft00 l6&鲢S7JwE|S$I"LM7;p} /(RILDUU}A|j*4 ,<]/~9o} /^$ڷQNP(n **)5d"h1!)#7{MX 9/O̳>E؉O 9#<J O`e|xx/ˋWz7jBml%kU_ce?Tߺf3! ^s?x?8K.#f*W/٧̭}68yы|LNXPj'?obumn뱷MVAlt7؄eYVqvYae*G8$;A!. {P*k:=/ppZf}oFaz@4]OIA IDAToD; e0M>AFF9s yǽ/(N7 lX|zJh 5KT+e|0\CSUtCg{sqYvr8z8NɩÄ.¡$vޢP:`0tˤO|:r뫋!^> q>?Z&$ ?AdՋHZ ]ڭJ./ZD@Yv`۸og= (yBd)l6vȤ ,x.P8ր2:8L|d}c+ .Zݺ)kҬ6Snp_;>Xu.,Ŕqt:2Vz/[kNVuv6Wϲ]v088L,>]VU''c tzVbA279kt5`+P{*>¯~# ,k+t;UU5uOeq(23eZ 6!2D/[vQd?ȨuDFK l,խnh}[w 7'k(V1\-?Dt$70=])x^^NM46D p[r<bi"fhٰN L`hՇ@DQvW~DV^ڭcs\>8n8C.OC$L]'24<Ω3/_`s}:|\:A0!F:KtЂ}o`*(הu;ӡQj։f4=]Oq$I^op83gwua[EZ\ Ο?dY썍 `0Y..K,r{hʠFkeDl&H@omiS@s2i0>8y :tr V.w@J u+gscafk P|럿_]2F*NJh?gppnzHX#ٿe;>QZ&*xd;|\b :M0E o8Z5t,%z8NdŋF Sx<@G$O5V2W+TU B|x~,,dll ˃!Yb6>7_4M<^/E8t;x<ffes, Q Wr讻naO4M5z)O#[OVXx%>< Wy>da&'gp8d4MGläSv;;WVln}EQ|;rWn<ԧ[q8MSx/.^ӧz4M&STLAVoܩx288 `~u* .XDMr&fwiAKx^wAOsE.]D6gyq}Q,uuiM B7̛no}k|3M^p8,$vh6q&dl|B.oML#A!#${^nd3iՒEQj6x>ӳ 8NzjIR)*?7`R)|0TGSD%B#;nm PV0<>e4MYlvIV.kZm4uE!'+|_z Gvo -éN>Xv%64B>"NlcE H[@0Fzx>%~==| IJ"pðhRw|? `jJ/5t|z .u/fSNw},]=/}C~dccz4>_Q80wc}m+xP3smƇ?vʥs͚*(j6Ȥu+K(f``A8|r{AD~owPx+DѴ_yޭq[D/h|!T2thln鶤~%n^3flNj= U'U+%+zi‘TxCÈ]Nqؐuc'kDADvxx[_beh?fSeYZ\[DQ<&$|vۺ9r,\/~=]\g\$k6㺉.~vsi (>Gɵ+ x/'btrphh,` es!>8q>vvm[A6Wŋa*9bu;$vVq:|;_/ lTA`mfv']4LӀ 2'n[_r.{5o"$hZPQP.|?'pFe .""p^F-9 ɽmWQ)$x]Yp+EI!񦗝 7W.Z=4UE9ڭ&LljSރF]~  |]"լR-g1E ۸ "J26 <0 R-e?pY?b`p _`J)MSgypcwOML/Gǭ319auz.>st]N:M  P.xs{ /~Kn z%)=$3vb2mMCt :&:*USlj71л^A$$J+e6ٽ-n9#lom"Iv$Q̞TU|>bN }#DQ. $;nMh[vct;\.7e Ỵ̄?' ^/~Ey*4]n&t 4j5LI [ISiu: h=k+1;AZ:I[b7MfH&=t0 ӢxhA^cksq @YNAqXӘmFj344l*J(p(Jt{"0jdbt:M8f06I&Ćd!})0`jBX$wd3V!#fbjnáCG8rĢ*9'g}s T/0(iȲLgM2)&- 5fqE)gv:(Q |^^7>T2Et:Wz{._:ϋQl55MjPF-*R4E^EVxof ?|4%M'`8EeVUNjjٹ8Cakk*RU0t:.cY nG{loB7fNץXs1,ADwv[&'}v"+Nkqop15D4A6^׳ch")be3Ȥ(1p O7#~ (AQKz`0vE\,13;j6[;6W&<0(Ym/@Zhjwp{iDBDH% [k Dd3C >x=>$ q{I'ݮJ,6( 1<h4[BQtSnSQvu$ڨt:sgko8tϹNF /ƣÄ"12r`8re9Qgh6 DctyF2]M.Nt*٧YSnb -ՋR{ Jۍ딪mR7DC7$3c:ˋQTZdSzu4tRg(3,>a<~֖pčG&-zrw9 6`7$tWD& !"8Z^C-^gh6,nYgb0rC( H'~gsh5k #K2cgrƗ>$*6b#3!$h?w^(ZhQհ L?}iz˽]LLSd*J45Rm%O_{;$u %\ "\G)  nx<7jKt{ȊAQdlmwVEqZ!*f+mNv0 &f#?ϲNwץZq !Z /4uXn]#gvV l-1@ۮBt`j9#ۈDGȥw9xV?c}*ϒ&Bai ]K059I 4hy7A-AQQVvhZ&NV ez@>_7 o ?4uNx_b@E.j\JB6E]R~ H O02BYhƗ>]/y% ҪW mNӳMը\un/N66s u MR-eƧ;W|(f>xC4M&ptd@zͮh4Vo0L$c0ceyb|^*K,^CCC8NBӳ~3}qc5KYTD\Lh1D:*8m=t 4SPHR40uro=._F3ox=Fn>._drrӄ>'N$bssyVo<BR"Ł?\,rY]dll|" qT~~Da}j1?YAgW55Mh:^A#Ȇ] vQchLHvOSxy:#:0m Ο4M;AczfIh ( nrT rtnNCRNAH tiJ% bY|.A!M(N<6˜y-m<&_2CÜ= ldbÄB,^~{{bp=8+U=Q}!fɚPpdip|>zCYWKdYs-rX jUtC'exlvU*9rk+x}~dYfvvAJ;{iBA?.Ӓ )LS@$LD]RHӦltbi2b~,dlt(brY8oMU7jnor1Ӵ(QVe?Kbft:8hEqI'i7ȊN5FG 2s0P;h 2c? ډDQu 0X[]٨ QoTW4 C'>!tjQmQ뷜R 0?fs*S3+|M^0=sLzO(2H^e?n*nan`ZN D ݭU;Cq344ì,_q}k>B|dzLOfgk0h7"1<n* z][An/JV?! l}r]|Q\;hmHԊi۵Y^zӄr ^M뱾LgYʼnkiYQg@Zk5cdl B0ruvwwe:z`8Jrg$t@Qv~Bhtfu[v)Ԫeڭ:.@G ci2`CC$gh B{6=!G֣Ѩ 1dAR#4 v[ -t$43Y3X^8x Z۷ Ǐ`l/YiMF$dY=yTVPli74a CCt}^4bA"$v4X@qi4j,]%:0iJlm,2>uEqt4UZ]'Qd'.O1m*2P7H02L밷N8Ti5h7C1vݰ`I.Ocxd0#"s'ae &n fn@?EQ$ J6~gt;-ڭ:OH266MX, ;Lߧ(N$ARL[XkEqSհ.\.'3(U5Vx! ݠ\*PX^L4GU{=A4]ekѨY]FVfdx$ ClhZpԺeCYB(RM\M/ B86L8\H˦(3NVc`Hc?K|d Kx=.*n-+' H /dF:M( ]WQN\`14JeA[Ľnvp:*rO(fksA4D"8ywɤc}}t:Ñ#G vt~܇b Ǝ$TK隐P5\" v dU$SGDAĔ$tA@5a\KsvuW'?l6~ =Y Gn' ΩS 17751??@4JRfum av'NހMcgAwk ;IBQ5F0p"l %B" ]1ٔ0E&]du5nZN}Ki#J l4dҔEl6;BؠIt \nBAVW`S.7FLh`bj*vcQU 曹p"O?=1FFǙ>(LMPVQd0X\0BGi=Ν}vcuwCqZdT IDATVCaŜoY_[enn'N&=8fDb7t9N܅lV^W7 ffgiϻvw6I6bݏai&;}G6Np1z. 1>92YFA@TAX$Z'bea8Jl2"Ntz=D0 *H*4 j Kטt(  DI5Z:.` ̃ q vH'wIRsq._GR\H?HFWDȩ2{56km P 7vu< h,H@[˳JZY4UGO_ǼoӶn4Yv#e`0.c3Tet]Hp H&dR>|,nH4ƺMW084 x^vwiw: :\aFF'M'%=%9qrތEX"6m*ڥ/.WURɔe "MIEM bNى7зs>}B1Fv{ǽݔewvpT#B<F"o_eo{$(yvTlHg?"` n5dGX"E(q1G@T:O,`oݭ gMjrx" ʲ:0-.^L4tuv]Ѣ-_JX"Iv*J'RX<~i6j"%4MerzݭUOcn`@&sxKxC±g 6/>x :Fl.G~l5| X[0MFRD&^ LNMa]`I G8efv@GvpF$`8J@0L"bh4F:ɳ4uFt&'&)W,?7r؈|k,ӓ}6*pJOPԩuʎ19{D" H:,vW?sKxufPNeescfJA25{OШWi<0-M;X8\lCq S& 0(ً H4N e]T-J Qt-mᗂ8C8e'P:-v69sE`X6(xtc21qj#8ip4N('^YȎ́dD*ϠG% bt]AU+(P:uD6i̟`c m!Jn^Ьqek1HӲzqZ"Xڅpe:'.jC*]&fNK)34tZ3' RHɳϰNA,0tBJ&ry^|?&9u.=Ex}4GGIq=? l R,?_215')JԎq'NJu?RulǾ$qS4 BPH >$Ǧ -( 8@`Ӱ!#`!" 6~Q 4toߡWF }~?^YQsT,+fow5CSO#IOnj83dY"H[!X__gnl|Ze稄l84CǢX!]IJlD! EL1PE\!"&%``Ţk61aN>yLWmTj6 4Z TUeksIffxw&K 2t0al|j/ PcNtmY<^TUTrˏ?u]``H8gQ*Pkt(W}>{/ Gzd< NRI$n@TkU~z 2#D6O=piX@Qczn9<>&ݥnd j5pH 4 ^HTF)LM)ln!I2VA|>S9~뛻C!UĒ)"/\Pahh-Ct]ggkq$dQ57Tx<[$Q^ SRib lC]DdE{Fx+ p}j((mu.xznp)v8' d._U$S 2zt}D ^G`8P.agcǏ->!֖>!`rfÃ]~?4QlJG{qc1>Mۼ"rnR막 K2Q,qS:j۶u;mtD+D$3.Y3 -N~rE߽(䀺F0e9DG{E"c;-Dg;nO! ` ܋3@$d*쮓DL0Ѩ 9u1M"^~t:CpGē|~/>cHkA:L]c x qbE}U+5x!vVgr_WO-3'>[+w? xvwvHg2bqDQ$ t;ԪU*=beuǁJ޿7T~iS^7|S^OɴPq#g~|H룞![@ϱc;s91xcALSWa0'rJ$Kx>Wn-n߾'nnxwZ z  BD"2,Hx<.`+_3odYaC -skȶQ4tp8ǎ'{ ѳYx f 1>/%kȞew@rd|D(aDB84l%kȏ n<|#H<`:ss LMO M C4 c5{n4gjz^P70-@ @6'?6A:cwgK"^MU]P<*!;DT<^/ N$΂I&HTetVeY #; -FF{4 &'q.6S:{,#BnI:UGrCuՠkt}K8/`Q88cscBQ0M]i6tl[D ݾ'Qrf'R>`wVqΞgv;OiJ`gnnEbl3aYزl$\64E۶Pe >A'J AQ"@H !$|KMۤd)&Q#Kpu~?ʗ)ܽ@Q>+KA Õ~bhFA٠T,!{|>C`0H*frbTƥ|> bLnp8mh}T ouwF^]0PC-ET*%&gEQPU<~?mp 킈e; NpG,SUVJ|;0tdY"a[j_cĒq:>S9cC! >GضJG.G ۛk8("?6m|h]|>Z)"{nzy}7]V6TMbo.(a t;Mw0H"樰O$ñmJG#zaT^XИ?:@٧/NF4crrx=5ڝ6Nad9h *Eר*m#2p}Qah Mp,l6F< E="(ӨW5^E t]ݛ}$t1F Q%vAh<>mE ,H;xMLl*\[r}{4U2C]ghRRava 6s:JzJmh>7>g"'ksrNi,Iڀ~COO&x} -[Cx@X<]D)v pԲX< cD9,Jdhg.%2*>\uJG{Ơv4z`WbSǓL2TJ;8Ľ[WFKK@!I rً/~$\BO`J@~|6{jm: ^_qʌL90$"e?-'sShjIjTl- z2>˱9ߢXAW{;uʇZu`wߏ zH3! jT(p2( Fh,IR&'3>3r@%;>C"麭LCgz8(Q[&76cwx]HC0'M1B_Qpo@ e w(×egoox,gΜf{kmw>5Ktɉs.ѕ=(JRFؔG d08qB(D Y&jZ4E/uɠ4hY&>Rk3%3>j2M1un߾+,#C>2-Ji~lmmrYfgx뭟%t Opȅ(5dthCD#{' e C7(HR+r2q_RstG^:aA$X8蒈8Ё[[[9{x,΍9~87}>_/( ӨDw,L6>fׯHx /bƈA@cI%SX' P8B\ppiQUykug+nMXYzSg|;/";ۄa^LlTes=Ai^~3||2F@ )+~l7n!y9ȥgsMڀ[72H.7ƕ>`wӲ$.ODl>UchY  AB LayO?"}N<7ɤ3NpдyWpb[\ȡt 0<iPT!}$^Oy}loy}MLcY&JKp]˛9q +<|^bY&Z9"Hs;8ͅ` HQ-&^G_`MwaHômd2J qxPD,65T:COichrj#&'&I:*/*joQ)I.D+ï+[ƮVLNA}*Zp{$Y(h=QiՉƓh${z]^~\|~obYCR< Tstg_DtD2Q "s1 sGm`%Q g<& {CaڭgM̱:K67<2#έkl25= ry c±S M%Ƕ?)LӤR.rF:#OkGxO ۶1͖s>_kkB~nEEcnpt:lo0\|En^[xw\_U#De 8^(\Gf}S!3I$S.ZR) Q>{DyH 1192)v$SıE,kٴmŲQqjk$<>I^x{$ck5O>.!-DAmd%rxCQd84]8ab)JGMh֋TGĢ*Cr;+  B$90K 'n*K\{3ht gf$ !{eJ %'Uhg^toAE (i*y! IDAT\xScy3n!y|6J"`L27cl= '̻Ƒ&̌f?G,dat~cwk8~ hf{o~^ ? cch*3'2v&Ƞ]$&rI"%vo'8C.CC.n}og@>CctøYbL9Ah;pI~?$eگll U«[3ϸhZE0K=K6dstxdP.Tk`g,RL z҉z<\ɉ"}`0S_Ƨ_<,LVsRǏp4BVE>CUUr<=ر?U^_.CA.E!MVCZ@4c8q>P$B8"Kp <סk?t]g-/-?!u;nJr@ "` "}Q~h 8*q*jY,Rh6|gy7F$ ^|] tZQA|?n}8t)vk]t]ĉS%i^mt$ (t:mw3͎aMfgg!H4>'NvlnݼFR\)ժhjЇ6<pnK>ia6;ۛBa&&&Qz=Z&~s|;#$nh,/-q9{hJ\"uK19,5t-!La {oi0t !lb$N[a4f &&TnS|+|~ V{|x=y Z&s ߶yz/vyV`;h|u ee RvoC5u$ a/ruA7 = GpMT8Ć&A $f; ))" Z #dž+S8< J3;@\T:2M6Cbgk-nGsۭ)G[8u^ ,=ygfq#B+ C7uzJKBQX,xxM?jҥp,;[t~oC`v2vݝ- TRix%vlmeA8iɍa $H ?edsyg/>|(I:jבeiwɤȒ2._둑=:G損eQ"ʳ&mnryVߥR.rP], ,I"mDIZ>R>²,>sfF\jb&n@cɓM:}d*CQCU"ݎZ6Z^{ZlC2,voh7!bXP8Ɏ ţ^7[Ezl -rE2ML9?takcJ$GZ,ݝ j"ˏb[m(I7t8QZuOc0US9ZstZ+~r'8{ MraK 9Db4; FH KxuAQCx{_#p3kx|kcib1v7P@YcWG[g5l`|^.GGnݺEfO&E$Ib{{l6K`R:-4.QQ B(;aa1&XXXݻky\B4n(~J$1+ ?Oвq!^QMٶhzJdaX,N6l߻wx2Pi*c[-j5]^˷no"*mt)q(Y"{Nٲ(vە2XiJVr$Ux}21Cʕ@P0:c:`(4E A#Ht-cW*?DviY ժWB&}e;8Ģ́_u0ϸseR)8yjtu<6iҧ^ʕ<OVCTZ6vM+K,=yD6G 0=5tpf9: pN=ϻoGё%[7?&:`N?vߥQn(_EΜwY]~Lg!#XIC',hF<2iaZn|n849ţ2~ӲA(x^~BD4A'?"P2S!=K6% n+|0QP8Fam I}cբt)tv*>,l{hJ2O_ohx"E8!86VnUM='nr{$iLӤQwWܾ1¡;C* hFe</l{n^'wߥ(TE7SWP6zJY8[cl|}3<~x?xT@$zxޑ~8:ܣR>±m`Q I!r#QuC H369A?l iD(Z *-mZ`n?C("?6C"(H$/?^-cY&{;.!r7h6i.r-c3v|xn܎(p> 2 MC]w͠>[^)] ]#qU:-$>㐕dLv8w8:ܣWxGߦi&&L!K"^_W_Z͕(:D`OUljFh%n6]selbZO^6wo_V)mP>t۲Hglm,T*M?$H1=`0DVNVWx'ߣV)~'j?W]c[Fp"qJm^?|h, y">dMԫew7%s/|kf^#0115 (,?y6{ !fCS&ɲ6i ax<> àV)$S u3z=*n]{:.{ NX"$S[4tGnL麑UO:;N4X+@?y+]a0豳kDq Z /‰sl.1p M(6>pttD<02=B]icYtLD@bG&bym~?J)X,rpp [ܡ( ~$y0kF$e& 1@24+26TD/9%Ə#c~3,^ö-kz$|.O6lhԞNxQ5y>=U*t56 .Kԇed>fH$ ccL"(BtilcRNq`owgɕ+1C4Mj먪(DQ:>.i*z/#K2HY~c'׫?wK_EB'BUn\/7^gkcO3K4u>,mQh[$ r /P::"H(R,_fllmaNXM.ҳϒLȏgbr_Ut H2.[*_Ul BR⥗_K\zgyZ ǎ೯}2ILկ;$z} 8,1ZT%>`>g$YTMfc1? 8LO26>ɽ{ \zYڭǰ,חưOg !ڭ&OWY_ ?>/O3g/b6tD"c7599IP@$lác mXH% 3>>jh,$Qg}9]䑄4F"d#^p .k{ºIF  ʣ霪r:U'ώ>G>Ԫϩ}<_sjc:8h< i eL϶PD#â `Tv~h,]EQTo p]>?>Ň?mduL-p]<,Tn>!~R@VQz]Om~_?ށ@8w2$q|?ʿ?I"/ʤ9Sr7xp6??MمSt-Z*e)6H=o&#}160Lk3|]RQ+1u]efnE>ʷ/LiC|wwCݿCǝ[o+/kO눢8-0u nx]J󋧉'_'t gg?Cy_? {]~~[6 ~^z%~^Ǿ#2=?bY&D??C|;aP(Qٳ'x^0,8h0qɴѨͯ~fp\9q,zDȠE%8 v4h~")g.㰾W~WiXOqf$E6+? \zi--&mT7xrR!G4f~~SO4ч]2O?NZf8nǬò, ;[klro.4ٙYO'.M ykCA Cg0' '|RDqDסլj1c"ms?1 cHyjb!ÏOe1?u7 H$>TX, %spxm޸5@¢@JQVL"|{{] kkkloﰰ((4-4U⅋|k_E "Ϝex|p$"8^ &$MU #d2Y `^opдm,w,-?OMl6effߤb\y^z{u;;l"r-Ojri-' d1SӄCa4_}o/{R UJVI$I^U~?*=-Ç$)TMeY@;oW pStm&XK| `=ǜXZ&/r-N<9Z {LMMWvI0}?!nݸ΅W&yIQB   bY7}+Qdh6 oI"N6|tm{=HH8Oqr{pp8\F_P|Uk&D .Hb vwH$$SiffHēȊSO@:aoo}8ߋeT)?TլӨUu NZ/dz[]T01s,Xb[X$'}XQF!_)!e~Qb&qñ"a:3dy[(/h.s|oD"Fլ3mzvP+$Ez AT?d;,HVCӂHhCdr9҂˯|FP(­~Ji'IK- WPd0@4{ޖg}G_xX\OD&Z8Ÿo-¶,Y^Dr8X\:)ls̠7A@V$"sF1tQ9u2`'k(&Q!, + H3sɔC{1Sg.i~"3  {Pcاn֛HœdEt}, 4˶2pb4r+YI cEoiZe)abylH/  U(f "$ڭPLaY\a]Oa۞`Q%N…KOS[fwAϝxXX4ƹKWG:cCjveyo p= | ШUIŒ4CZeTmD  H:WfP,zDaFGIPf0.]> +jOKt%b~*~<-"[de$EZ=/C]GEQtU G2OaGz:r>"a[`Ebs JT+;)'i),MH=M~Ƞ&M'Na&Q{@gD<\|@8[<&_~qlbE%c8 )wAQdѦ89#N(7Ig iO3ϑ'U|Ls?]o:??#+1xʼn?Br[&t,L&3), +\Y'PR'U)Y$3330B\:I8h1kkkQupD.CQ< ezYfffsI,ezjR|y(uàf+(87p71uN9Û4 TM#Roɤ3޾i(B8BzpڃCs`lEpƒ|KeU|>O6њ,f<6?@$'P0 YR\3Oȉ$SIOAG8,3s:wٳH ȊBT ar1+=qp񄀢$ :cS*Oc[z 0lH$Be,B uY\4#xC| G0bna 0L*!A4CxL.,~z!,3 i.i#msY ãC,8E$ ɱhTiN`f M&TBi QX_B ǴSލ9.:i1{;TG1i5/d8Q(Y:yGq qiH}<&_,4cLt-]UDh%\b8Btlkr2r'Ŗ>6ǕCZ6ڮv|>?DW&H!KǷ,d6rrb4qW!>r eϜ ܽʣlmNe >M4MG}:6!"8ccL,(mb- clƲ]"h۱7 h tfXXX:MTOۡi0ȲL,@dsHb1K,70W&D>_$ac*[ڣ;ffa+W_P(#I 1$SKpI Evv &n7'1AQdX\a۶fog`(f8O$uI$l1D,km#J"1i5E.q\,{SH,ιWI2-du: J'=ldn$\\.`slY&(k2h5nO,h߻ "3 ghv:| V{L~~}檏D*D,DU}gNaHL"8`14Ue4cὛG3EVL'!!ʡm#) SX@ TS g(Μzt@ݠibG\4Ml@(?$iԏxt&i)L-c#/rKy `ýu3$Ydk}BiٓDj=4Vh@*c{kBa@<odQfJ2;4 SrEY B-s&lTADhr³P}aTD)@ex2(L/EчdEeUZ}|jer?"f Id˲IeHL,uldGz@z#|koHȗ2hi/GBHJI =,$C)2,L&v '6D<իW9wo[8v~Mɫ>F64kOVi4e8 hD0$ FchXƈbʜpt:M2" l)JLMOS*Mq%>,ဏ Ř8$d;oƵkT*t:2 cy61zxѡMt]?"|msHǵ9 j;YRZ^E:ހf->dD"I8adY P$'Obv3b/X"_(xE旖x[90I;0ǓvLcnߺeY^'ug'XXXDQ5ry II3T*Ix۷b1*}:W>C8a{gd*s 콎1P(Nghlmaٖ'o,vdsKSئEju676%D"$IĒ $XSul"O뙧4DP8,#Pkv;d8M"CϠjY[ħj8C6_򕧐eX LMb&BG+8::zwXF|y33lnnМ[*@@t6獠 [a(ѐÃ}s '˲eK $űm,F%~?T q쩏d:Mf}1 5=f4x=0L E4[-|7X'037OXkDD`>/zN-$ŵ]ctئX[I]Hu,@VgTv~U#whF0ޕin5FV5rP=_%<~h4gFtZ v7 Ȋyp.ހni`;6HfJP&HP$`ļ{'ajc"`\:l}5zVr-Fdyߍx/ "d GH3Twi5$RӴLTG"4(̓ǏuIO4@ 9ݠrà#HhT=el1] ̡ ,yY2M*P&/$!E–v]fj qorh|k_^;`Y&lP=thܾE(f)Ɔfcڎ< P?01?@:r(DQ7zG([H }Oñ2)9ٳG\, ?NGbc:XmM; 33_LG[*$?IFƖ$h6xbcHAױ Eb1z.+`z ۱'n'OsT96@A'5J*$iFPIˆȝ:nqݞ.Lv.Oָp)/78 "7zZiN,>_LZͷ;d~Gp\\Az#/ z@U8* #N,-= (Ycqp>#XKDrAPQ1n|K,Xz|4f~:otTEGF>RVi2yJiR PMUI"ͯ;( ;"¨AРeypH+_ }cSϼ$IBUAѨ:.NiB@x"H|. mb[nX"x>xulroV4;VU3t \QDfby%ƣ!vX"û7ߵ'S9,ˢn6g/?M|޿M0A ?a?${*pavmCc+u2ܵITj3hDffBqw$1LO2irUP5E,u'rns&$"+>TEh7eF}EEe?DTf.\BU!D݊FH;"N4˂wpǵQoY^dEcC'^$'LdP5qEFK_9lH4E,#K(nn#2j>U2L `?ē:sp=.hӲ5z.lUQ׎&`(L*%yMRi\Fqnj2$ d,IdM#-""~םCo|cgn2};v?.RUe*21f8h ''˾"'+P9ew{YQUU$3y\W@Q4"@?A3%}lcnxc"^V0v\%)W+bz4@G ŞL\ k+w@Tg01O8n'X6{h&W z_%'sgOcG ]69y9xp$ʃ{ԏɕƶ5!_H=T*,⛯`0`%8hktM;AC" .9ΰQ4 4t:ضM?`ggX-.izG^(I&>fErv1ygq'EFT|x'vI* %sT03?ןH?uC$pn\bggeC׮38}c<\$\+2MBe 13Qo毂rwD,^w;k IDATFEbvs/n(O`ﱺp8̙sQPY8%QD ߺASRwqְO2AH-.GfO%7k__g4"̛:$aK2J֯9O!tvwx3=66xtC*›ܽoPw$,A!-6#QaΗ*HHK9ޢ]^#rp`0  qffzQj*f@0@4V;f{C%ICW]dEEp O80ƈX/;`*E4;Lx-=qN"K .?MkMt۔q\lN?azq)͇#4<^b;cBDP*Igs ĽQdjj@rpKijvEP@Q5ۿKffY촩?>` ئh (eB2!~\.&!$Mfit;-r._EV ȡDcq^zp?koC9/̒/,tlz8/?b "[Uӈ%RWx>@*I0%QDQ2YΜƓLj(& V8 z3iz:Le֎~Ɵ;9a|;`p(d ٱ1E7K{dm}ի;m7o\P8. nX"msԎL6`1IgK } k|>qMuG%Ee75fBRcwD׿HC\>O(W"l;İeo|nw>jZ#2)~*I2Ǔ(DȪFX=,e\Kl2t;ҕp\rH$ܹYLsLo{->O8l."x;7DU5o*Lq"% :^ 5xH̛8!I2(pJ$a8*^7blԘ=qvrE‘ $ڭ4Isv,$IayYpVY&gbh-]x{iK 6EŠK|/3DnǶҹ7 Kf69zijzQK$KRn_ hSvN_xN>bh4>i5aC4mi1HXʹc#Rs`*Sp}v>3QeE@,J.[qce6+$ӌmHfD+ip]*SYq|!ڛ_CQ}5Ȕ{p툋 7NHDE#HY.$#qi6C!6760 vbq>=GRx!cӦaYg(p 8}>B0ԻԀ@ ab1R i4Ȓe[LOO,;od(ܽ{0xN8mcb_Xߤ7Lr]tAA @Vg<64~G(h6P9yt7m,(,- !^BDrm[t{=!TAd"*hr'kklooUUc4ֽA8wvCx^˅K:$ *G"\'Nm$I~?GX,olr T ]3ƴmka8 `%$@D T{'g)KHg|*[24Mcmk\H@V<398ܛmip|[`?c?ș4Zt:C.Ba3F_ ˾mBw-\\|?!ϒO=-;2߻'ǛcUYRI-²l`c Ecm`&vO4 `X0!ƶ-aYJV9ߓgYZS}kwn޼AoO/x t5|dY|%CSh4^G8R)&q5rSS83K}PjpI*]Vs;@߉iX$Rin$_(PWSw&IO/}n.aKhfoT\t[a*˗i5 W.MOHbC9g/}&;?Kdw{B@2t$TL&BiR,ϾF,f>a>sӔJ=T;j5JFc L ;ؚL+ւ/$h$Au){afߤG+0eϞ1]iZxvK^լQ,Y^Z3,/-m!VWWY_[CJ?n\d~ #ch6[JJ7:=HXxkW΢ dJt7^&k]뒀vȠQO8T*3QiJ-W{_W>S5 );4b},*:V B:-+/s敂RT|i<~xZT!@dGszXH|VFKovEgy*lRب7)ժ&ǿ7^d < ~F4~@a'xqÛH6etd?*sP^EVY<#zso@=[d]G1 !nDϓЊa~Zu;L޼P{_Ÿz|Pdht/G7,4CL]gggryC$S2"F"aೢk8cэg7Vs( ĢXr0t* ix^mfٳw/kxwϐ|$O?t45?Gil4uFGGF#2 A!dppD"VMٻg/QQ 'accMD VٳgdՕ666ܦS`MICBaHER7A)PYsm0DJI2dee|@OOzSNTD䝟K/"m|k&PB@a1t]LäX,I08tJPnkt:67i5M8}$J|#ܹ{RCdr8z#]D>4MDCNq,p]Rilj$ _vBGDl =L]᫐X3#|4mT|)ȕ}1ztU;]nߙd&o0 "FL&ǦRHzԨAV5 JV]h`_ƍkx27x1"u*3ؔVUCGxDŤe*$JHDyAC?`geuq:cc{H_C\?t<ߊݮeZݨ^kp2,v<2BD4 5 J.ⅳ=DW;b!I@\b,FЉr/QGҶ%Fp5fp]G>^~Yvw;q-Rbi&L0?;RWiKk 4ρ t:b/p)'3O14<4cv;zw-=$i"DhA\shd3yVld*.tL^Rf~n s) R C] >gpl/?ǁC'0 m6#1OuhCђKQi!};7!O0{2(EԋiX]^$bcu? _i*\e|VRxM%  Ȑub(d(PR# " !RqOS"?V[T+]ɍӨ Njֹr n\(`g{+T4I}rqQTfvq~t:*B)uf^=Gz}lR4t4%ЅNt & )imkTl2O1ybd`'ؼz}ϦRen(ٔ!aIuB<)BI4JC Q"J0=Aetu/?=>V!/1y;|J,Fkԙ;dt+&pl$wHD$"\nb'0$//MSoY~WhqtG>2PRj-SX&./)0b@hP5I|+,yTvC4[GPEFQ...T;H(auygCcE>!XCa)gMP$jCP <q{./^dee5>4##?Uݪ3~:2)RPVI&<;4MF4BI:׮]g=z7gΛoIR=_ C<#kۜ⑻t0C*Gi"z|'Lta_KLOOfY]]A>oR]⡠|i"4A:MqlfiyfB%WȳIdcc9;"t )CF#/pe6Je}}.nkkŅȼ=2a2?T+2*k4 2;3m`E(RH,1qVL{A [/$CWIE \_ l Kf  `{{{V6*F&!BuckKKIJℊI҇ƑsԷу&JgEJ˾K(%anVdQQzR:b/Zl405MA>cS(>B__cll (`fzq]T*:WΝ#bxǀi5 B]ebێqg|D2ɞ=8N03s?(\J%X^Z | I@ 4D`  >|w?"͓Jh?8qY[["n0cϟ3l8 RUikA XV|;3u;[./204E\p577pmNW)bfkTP_%|Q%u8(BF0$/4DɅ~QgC h!Z|%ry+aa~Ԫfl4)̥g9t873:\rDn7ypuPLlo38XLgu= Cml *PБ9Ws gӼ$ӑY pW߻)^:5N^#_h0=u\DRayiܾqmڎk+ \zGgDuKm^}x A@mcKI\h <%t@4B!AE6(&wОIn߸ęw> F IDATҼvvE[Tp]١<G(PxB12>!6.*_jС"%]ze.d$]\xKgcll;A"s]t$gA賶4G6W`ksd"A}n teeӗ,=,+<?ެvZ8t_m@ƆrzNբVR~xݏ344 Ox-.9wz8kd66q? T˝'& %4t1m謠&ڨ ԻT*8>rzF\ݏo] mM.1+FQ֢ T2cI}jU [[) }'Nt;(H)E__dYFFGybř;\p:(y!дH G4[XRai:>ѴBwT!"o.Ȑ~$ t >軰q[hBv=91]6!$[!"8Jd+€à6jAbbxe7n4w1$,P[1hBacvwX'ÐU#$ub:McT:2&'S8qW/'RThuWKh"L3[-3Ѕ4i)CRY&+ez{P"eH< /Hı,+ўGfs@El&K$Ï1g3g099\px<qxꩧ8z$j]T*~>|z#lc,ptEӢLrBV"A.Bݤ. b1:?01ۦrx?PӨs<94#FK(z5ANXׄFUl:?puNwCh@7L`q~)fprWwػ@IN B: AF3`Z6L4 I0ߙRwD%}2CgI ʻQalq5= %F 1- Mhҙ(d?ܑۜkI (&1JXqd+&=Ч`0LBE3 U@GIRh'wzLݹ|!2* Lw"h\<L~^;g_[|,ÔRVhC,eˮǚDx.R~G2uO.dItD"'R3ǁCǨWT*ݩxa&<?OƓa^y8vq]HN{.+ͤ5}W/B ,|Q bmu:r,ރ#c{<4?i3* M# oGjn!B4a5ŐA"iH4RhH($B n "'q]3I^we`Da"N|_߿L QmrY4K0"~>8[}oRٕ:EN'&)]C.O OA n ;ȱVt& <4ɍ^r,Q ѭ#"s. Dly"m J ȃҗt!CLMIiJh ch~c?4MO =Iߌn5hܸ#c{)He %~ř<O>RH4{bdv||OVa!tH*bhP A[ItV,8-qol24Qd^?I_9_;~'黷%x6kKGa*bŽ8r,ℤh3$vR3gя~{>,LaEwjldZ^A)Ax DԥRd 4y)˴Z->78qtӧO3>>N:&0<<ʯ wfd2}Ο?ooR(QH"Ss:v?TvBb!FR@VJqYYYɓ  \uQsc=QRt#5ܦX,pAnp\кNZ䋟 V9^ysm S(zhSl68r8\n\8N8K%Zp)\;npg?n\g;8Gh`(WY]Vˌeiƺ|Y܎BMCS_d12"(T'L$R) " YX}<<| N2nP Y kZ '3;Ei$A,leR4b$)%drd{Qr9gm2BB! ?6*?2Н[ +Xŝ U4ei]hc6}=%3y2 mTNtC3 щjk@hУ~Ms_qzz y㵗hkR(z#F}hgu6`jаBփǎ hQClMD/W ["T'14 *%zI8J<<g\V(1ۦ^7RV3duy?AS*{-1SRC# LrNQAsi;fc{ :m @kul펫.v{{__Î Zpy~?mI$ӘVjL.'Kϳ{>>Neeg{C*"NS,7&DR J< 'ɗImEL108޲;@*'?B Ȑ+ů|WQ(tM5!Ȥ3#=O|;Wh7k|~}Ĭ{(X_[a+ xU3 C ^KLib>\rZ,>[2AnTQbJpDgviB6,0$CɷQWˏ7evlz}u DtdE'EͶ"XV([_H83[ۢt}sKK)"zo!QM Bɗ0ȤsyxCXMO0A^jc'2а@$Ҏ!SrEZ{ q=neawf :A!4<ДWo^}Y|b?6?suξ-:N'!V,ܾ'94."S%9 ܸJPRMqWIλ֔;$Cn0h !%ˡGGFC7?˘~ /ϰ)-!UR9m f,4y=?8hT*eߍ3=4Uo>d:;Ai;]y瘱IEgn S$mp-!K}tZ5޿"4ޚZv Mh]BH>Ur!.<^j9C! ,ir!lf޽ mY ~)\?jٕ nRb:L}8ms՗?}?DdwN4%C0Mju2$C*E2 )c69Pk0a6GG{}pMrrĉ޿FތSEBFҴ|Ya% iIS)#qq`!f}cNGDz8bа)AE9xd(P0\jJ|1h v['Hjt]Ja+H>3rR24R .;ljSYZ4-!"NZM2,(E*u] R1,]V:04]7X_]vwSyy0 [nhCo ~&/?8y8k144DK?$"BlEzfwwI:Mp3LbD/nW>n4]X*a@u:ŕg1 ˶u 8|>ʁI$S_bwB@8Wcn&}I$SkGj6X\#+0?7MC230%B'cIAZ,ˢnnu .1;fd*D^Hܾu!);,NGT&IE'MDq(4 R;k\<RIfhDѶ`uul.GyzH RJGq!%dAi`ol3 .V0B Uv+Pd $GҚxL7L.Ml5h6ԫ|qsW[U6C5H(bR)^wL>AFPMz%ph n0+bZqzIwBߑZ(Q4Acp^ײMeܹӮCmw 5-G\z@*;ux&N35=M"#įs$7,>Rbv+blx\aV)Jq]@*MZƞ$ #BhA[.qʻ/2025|L&KQqFFX'.JH| <6 -~sNm𚑠E4gIբ?GKi[jL^{u Eҩ 1fsCHiULJ}'lqwϦ8bycD(39"b}2BȸTuEg "@$@|e>P ,NMP F>J;<%QTD& 2 %$lrMUJcޯZ[[bL@L[_yvDh _K~4W)Z@4J.\?!\s6Knd"/c!P0| DBM)`8LcuXZ锇0" IDAT7LC&1'xg?TJۿ+T5 9u]rmo1+\*kTDDCqBvP˚MXc<袨 :IP:Az.du._'QqN?q͉"A$ *rt>0=FbN<kPl9c%"HPT4@.s%tx_j8ů?xGJ,Ty.tL.v14ţ#Fǒ q)ݏRvYv;7 !%H+W d`Dn!N`jǻ_R,Q.4%}\#qM9uFL\)l]1fX&UP > R1}NB/X\^sz@F&W"0a14uXP}ìy| I!.2pxLEV2$éc Ur2A& Ӷ̦ $Z9 Wh<=13$l pOl|08=JMUt#<z۲9鱾wA'9NNW=u0O3puWk<)L :~՛o7 0#gS4l6.-3f5Í<4I&:8^l`weǙ|_0h4wȯo@Q"αT5~A,)h5Ϩ댆CJ U* =6G,,,{!28@f3wP5Oy~:[[N V0- ӜN ̥JNp21 BX$S_`3Ӛ^u݇~e4`8.i RF>@DL/J/W@OT]PTa&dT7- |0`d$$P1 t!\UH-28;f~p@f6g.#)hCP"=b+WS(h*Y^݈iUmg~@yp\r2_&ZOFAWVH$O&xds{qirz]|TQ:OrХ S)xy<zy|"U>=^~-G,nlS[D!ϟ  ;->xߗi&a>3q+/K^wĥL B]OJb)Uf1>" 2x{(4ώJ2fR.nvL\M*@ ]G ]/Pi6p\᠇d٘xC:#H@!A*%H^M.-/P…a]YbJ&6kstQA9Le89g214'\EWHuUEM#YcC|NʴHqm#g9Ɉӓ^zg %ld2IuF^SVHS2f-f)>k_mf3PGBX.w>U׸u%E U70YܤnJ0ΏBC*ncߥwNHɖDK+U]R) xO\Omc ɢbPWUT n$d29fxc]/r|tdE "xzL"y|)2 >^I+[/1<UӸ+XV^OF-NO8ko~㣃nR&c4O4i>GEE Ub6"yj GLv:s)Pl#(~ jK~;H|huAV,!NKyR@YJrvz:eΩ]d2E6gl_Qm7\Wt8 ˓0.^^@򫯳t6e4svzĠۡ:#0 dry Ef)կ}0 0}dO~A1M6!YZ٤<<9ayD~S/&??=g' %NO)WBr)_yܻ6Gr|Ϗ)9"r"]c"b̺*B0Qry7<#o5]#tc46 ;P0NXX(a&aEQ CͳP,Qdp-VR Ե"H yADal:mUKbsc4by<ݽG2aX&.K$0 C|e8B(:A.(ۯI\GQu>'G/BuF>dEӈYY 9]d)DHfx m&1g!dHw|?d<rz|2WTqzÐy0Tt ,TI2D4A+HHEw]0#: Gu6cl"+?AS5T*U7,G?s]dnɰO&c<9$}5ڝ&lJC82AɄ@#"'E|#Hg!NǦDaȖa͍-FD-gt^~Jt28 |yvl>{M!_qtH:%H_Neе/ @!DDth8d2vRRp%z.W^'ΠjŜB_|1}AjƇ , v܀;bll]۲bR8<|TTg':ré0DE86!j:9c01 ƌ'&ɄfgH|nS,.&>рm(5^$" W,IgGC1m[\r6i7BHwZt|xDׯ y%Ņx x!Ɉ7^' &1ldsƓcF}U(Hu5-%d93N^%H"|ySt=BmU:#ĝPv=(j@&E~LLWiN'ؖI" ƛ$J N RQOF(d:Jgmn!ok2.yE QL#lIJ/?WX_T)60dǴq#.$DK+DD$S)6.`[6Q_~/9;9´l޸lk[xvn!C.T^AKTkEM"bP.PSRqtH((0H"q}]>لn v }Ku-<=XYalNPX;J (̄B(%P"4rѥ+14M(=n]˯/^};(BRT"?-&6a$m6U<35 ¸<vi<%/'{dAc6P[]?}UV"_r߱89Z*JLTx>/rrtt l35k-o Z}(\Lc40 8>:`iy\sڝ6!G G̈KR'FLc.]e#dLW۴x* >xln-ES}&c{p9i2ޝO^IP$Si""=7jx` IDATO})D:]H%#̄d*"J~ ϑn,d Z/`;iYV,ju[ۗre&!Cuƕk/QչGd9< s>ewi70f#H:0HtgFv+7^"A4T{w>aem( v2Ͳq|'bH&reڭSu(W/uHer zm02g05 N^`<lDZC7檮saq).)UnfAjd3ַF4t:4tJRAJI} JYPWV5UD̄`H%%N&4c"\~?7Sjl~YFU5V{=VVV{zys^x("@9"D>oD0P$c־|gO?ˀPd0'!Qa$xklqTbqq{$OT:m9dW}Z}Ӳy|"Q.RZ(.#'?kj:eS2,Α\?fs>v7NǻDQDq`q ;;n#VA=d:M:!ͲQl&CPҥ+<}p}m'3'A&e2eY(RRh|9O?f9;j5l:\GȐ1$ AF.g̳ٔJz~21tP#U?b6d0gح*u}i89(EAS:Wgux>{{O$S)~|TkH)o6\gOqWyp.d|tJQQ5 (*Uʕc*ۗh1v .bN!=~S|2ߡ*(uYXJ:<4NY]]g>a[i~޽͓2ǻ;O&{. "Φ[M'Ի]rc|ʨB'yT TM;c3qb _RgU M%[  O=ί?aE e"$)$F2Tv]x21tt*wzX(UPU5rtϧ  9W<XkChEsB+n/,P-BR.:|<}CuhQ/5ͳ޻>e2ijJeDL$@!D!@zр~i2Y2Py?mGc;-Ue/dLȑ 6LƼxo+ 8ds!ϧ@oKE0vryNN9l~F6в1 ӪHTˢy\\ϧT)G(J̧32v넋1F OQS u@Jm v ><{C~.-uupAKgY}-.?x!!$:" Q"AJjs~SN>FCJN/hS\(SX(cNONpT*ţ_pMǦ!RѰh<( z/c(+dy>G}рsߧPaWx$<@ A䓗1}MӸtkS\X u~;x>Bbڤ3)Ɠ1/_.lͳcvܥV;aL uv2YZYV_ɣd ,&ؚ1 CQ$qX\1~U^dy,spg>S/Ox:O>`Cz,Kɢ#dDFJ EJ\ǶH22{;dryJUZg'\Nu `:psko~{?ѐikDg6a._"t2Bu$0Tk)U=N&H!p_&F"Mmq Z좉Nc"W,9r6}BOY]XO_6=EN/37R,UI3$)XYGQ$lJR+_PZw>N:vE.hlj^'`AMHD֔sDH3֜xHJELt:m^tZ)n `Lu2˳ e#BuW6mEon'giq1vam]aRc2yk_p>f_xs*&FQp:F[(s]JU/ Mhxxԧ)+Ğ9`\e:) 5TR$˵EB-|2dm}߳Vw3>]lb:r-"ݍ,b6q=>Svwwc!=%=:ysNZVel0C00f3 !2 ð0^ac# YeIVVwɱToy[sꜪy0 VI5&(jYM;f_x[ފn褒I?/|i##O+EQ,ɓ}B-1C'Y>R8M!hm$SI `fopfiibQOrildHXr8j=&Ǐ Cޘt*)%l5qlZNƱcqc2!d2fggIx^@\̙tR$?ܸ~/_bc}`a7X04;ۄaSIL+_?EĶl7 |VES)">KfD"IfyJITk5t62o@ C>H9Jg/ؗ&xB'N9D_Ea= tMCr ebDK/گp]uv0JNݥR"4AĴoLo=Q/"C abF!m"^8vhZK&S0Mu{L>gEG2`4i\jzqaIx2 Vj be\8W`g8hԱ,ͩNXbjzôx׻W7_ϩ֦h5wfK@a[6} ӳ<~u۔$BJQ@F10lg"51-T:C.N3x!>W.}A񐖦fؼa ؋B /tv |<ѐ M茆"b%M. r{s ;ᘑ RT2bq(S31ѿ4}wpUWo#(+,-/č?qt:nT\Y`EZ_{LS&3K,.enn>8^0bWȑ'xǷ»cf:$ "~ $RfPJIǑGԡ.\ӈpmǂAIQirV(2d7R5aaJP%(q4Xud~7ݡVc%#JNdN@g}oF~,rL;\V ]7lD"MPbjv_{O_v!B#  hI[XVITL#P%mj)~?g>a8賿C3y&y#e#~zc|D2BJq1:𷼏_|?/<鿠. ZWxPэ$bӌ/ɦe=a1H3L@Jx&W< ,9$aLˢޘA ItLw7"ţG87eH7uGSnĞMòlzØ$8`,ES%t|s7+2# ] HJ abZ6RgdcdobfnHhHRgog q[:~`繌GC2J%M9@S!kqa2BFӲI$4>}/|w첵vopaxsnkahFEl0M(N2&t&g w;x7HXdqL]>oC?w BNrAȶ?$J)8N Iiw=@P05&o~o?|QOͫ7W2bxuC8H$)mIa Z{ e\wdr*B?-" p9ffgI$hLQo \|OQ)W(I5J8 ~~/6W)pNq%իW,..2 9{ 'O%p_X䙟';.pۼe^N'evn} (R()Kx1gA[_eC41Kg &Fjd2&,b6tzbn͏+~0pIN<РnJ+I' &zeuA2X(;^++ϑ9},WbR) C/!n2*bH@^3B'LRT7O,䃓Tk ,0l ?͸}׸o3X瘪7X:r^yOErJTX*3;7ω>k>}BD:a<SԱpHZgscON8rTeD*-NңG1]d2޸z Y:z|g򶷿ۋG~O=E*BMN)ǦVO*WȦ3qdXbq8s{=Z7cgt\cck V]4dLm#OI!l.(W0$/roejj3/p=jKO}pb ^i4g]0dww7pc;m# ,/}F'u2.ǏP,33wlw4`&ƴ50(&~[\Ryۿñ[e'S#5=*ӞL&tx۷Sdh CBT*~@;~ܧI&~Hi[d94]Iį?"r[K3ʀi`: ߇aZd2r"؉ss L$u};hyd:+7I$ӌFwF:ĩӔK5t'Oxߊi9i,駞򫯱מ~_2c'X"X*Bǎ->jX&~?{qr"$ {wy/O=e2?Bcz01Mb>w~b pئ\@N3 8v% j$ \Eh:SD\"s""n\} I2O"eL6|D._BJooa| wE;ppHXk/S^n3r=\{{M\wİaog#{C\^]N"Ŵp~j|E[$#C !*Pʕ]$L/tZ }qe%ҙ`n~:jOwHa]<ʶi>Rlm[dre<3I4fiﰰt~~>{1-dUa7y׷~;yߣBw@(@H=mZEZ' } `C\Ǵln[/\D"EP;fᶺpk䢄F0h:zHau>=?>xILvlJ|׻ xL.Ŷm7nի100=3LMO!F"OsΜ9K2"aZ| r<[[1|}Mj Rh ӠOhoNuvX"D7 r9g$EqБH&SXg+mr>+:\96xv IDAT:{]\t o!STX\XbeuD"A"`kk]bsqdI7VXK/ V~,/;=S*j:kd9+cYQ EQHXիܼqd4 }8dR=o{;aKd(% Ǔ|c]m&JJ <33G(# dyB\gjXRœ4:SA08,Fu Y,k+yff~b/NR*c4Z"JiF}zal+z &8.xL,eSv]R-.Y&R@F5B'mX]Igyߧޘ\+*]z"oA2l5ĵ|5Z&TӴkz].b0 49Zu_a€P< 2`ʏ7w9ݱs-1(="Bb]!!%̣Q6mnkp+X 0,IR5gn?~0F゚ZcӴۼ,:y>Cƿ#yUE*$dZ0:~< |Jwv[,,>A^{8JG u KHhH55a\'+qBĶL,Eh?C\#|/NUҙiHx+g: s8j1̃Rf|(I4B!!g(#t|) teooGK{>BJ旎1 ODNdq1Z 392<ͽ-RN$BP,ױJNI"1=G{.h9Qݴ>^FIb$QJARG`YӋԦ+$)o/_ŲlY۸I<qR*paH!-WQP?c'hǤ[0}r"i%X>u/t:eXREuN8>v PBBnoNDI`0јܧZRV,jJ&azfpV<#w2m6r?~8"6dooT2RUfgX[[% wI?e]~z=Tp }5eaxDݰx0WB ҏ4BnÓSӰtBm[R1/ݶmXcnaL&Gb)'R9ho^lmmb۲Q(O/b&ɪYuN:ؤY;xKR%W(PJexoB+BC4Ŭa $ss^@16rkSBv9}S(X'L׈%-nJiHW 1lQ L6]筄a],M0t)8ʲtP gnE|8)"(T!~aIpp$W(Q( $ɘdXv JcOGNcsc0S4 C9u\|1`H6I=1Œabi>( #"M1R13~hx{* NNӰAǔggh A>9~kW/L&t)Qqz=1 a@j}wsG*(dPa<3QL&8ߍީL1;HQ m翆7H%hBP(U0-\hB2hHOIQO.Y}3dE>2l5ҙ<{ EQ@/ 1[(h"&ISXp0IJL䘞]D OplfsuJLmBI(HFB")'ce܈{sXBz!o.JӈD-ȋI![CR tjAZgixO/6fHq WW9q${1kԫ\9 mB' 8FQė[="_0pp@&W>5ORE~D1BH9hbr۶L -1|R}vu:&,JF(b@dd2m;Da4\_TnOPy4C{.ʥ:aP4H l^Mm9膉HjJ(Wg2" O*2*E+4 d1v0I5@b['?ŗH! R9qS_)"% uIfbKoI0 T)t:Î?uC*IRaMh(a'ӄaĠ _XrR8C>G}jh5w( t2y6Wtn#dsyFܼʃo7|vkp+2JG mܤ܈a>iQչq%"JFql{> U h@YX:N:W"Hk 'R, L:M$?)$ F8tN0zUl<")wbjz.\rh R,KHjB΍H _)H1Qk$BOh6 ]B#mXVJ3st}=l'{|@a*R2 X^:iLŇ$ˡP(VعvV`+AFQ#4xo&1rΒXXZ`~f٘K/㎆DQK/1y%2eza5D,- V9^"^ٹi| bJ2esY>~vL6:'\:BswK^$HrU},c5|u1!1AH U g7{T:>'Occ6fsro]L$G3<}>ls.R}*h*0u$:w"`&9^yo 2~x4mE@=5FbA0Hh#boso⹱ߏŭ8B:ME*uFN|a]!FF7O/ *LV AK4hm͗C8z鰿d×WDRa`:7FnB&4ɦ@A6EYK';BNw H~RATA7t]~,VWp[^y0/v#]aJQB)>*d3!uB䘮X!51yN#hL̢ ݝM:YھG2c`ŗ!$IM=oi13{qO0TTj$Si,;IX$Wկ|T&ϙކe%}re0)<+=p4fqwvH$ 9l6ɖ"A~K>C}zcvn4[MV( uO Hf2r!ӳKaZSN oqVN#s$iA@fXE 8Kd(D ĩ~G&X,Ŷ!_ BtQtsdH$c Bk:zť%#x%ffyo{3Z-VW_S`f\d<<\68q${qkklnn`ZV, 7֩+}Ͻf4H&(b_a/1x2IMrI)RN7eK[x B>f$W8WZ e0&x}g\.>1j{kvMX_;dnnFc#2IdIO@-`$#L@!dBOc\S1t]h[{}R*N>Wz:I(,]TxXƥE 0M @NDxE7o\P(rpB"Jɐ t]I$,H "$@`*AFl`KhNTW <벵ʑǨVj};v]7ucWDH $i`Jc eaA qE>(hP*VgueB1\}_?3Ğݝqoen*bJJG Q5X R I޴P(~$1d@54X&Q".4J$IMT;FצTu3E4(tHDt{cU{~.~B*,LgY[ų_{RhHafbh%bx,N"I"%/Ĕ/Bg*B*"c肸$㾠a3TH =sGv54 }i:&:'-PoLa 13tp$dJE08hǘ("J{GvJB/aӐKJ78j FCdD[& ˦(4Orpp@gks\>Oavf!%7e,!P..H @) $cjzq=*#=&I`a*GHrpPH2M ]lrdnut{=vCg*?*~s9z(GJ댎Bpƍ\t1ln4%ܾuEe&i燴|@^g|bGkr:묯P֨ 쫗8/ǞAG0"s G<$Z |*M(%T 䔤TGG)T^Al6>VL6ˑ#dt*C"MR(ep#5^QD\>O&#_ȇn(gΞrX& ~?󰔇' *$ M"ԥؘ6:SӳR08ui4]<wn"Q=Z࠰$4.׺>,2L&SI:6Ǐ"/gϟmsX_G}WLà^Rz6o?_ `>277G^'FPc>XfMy  pQR!"K[hB aXOWEϔ`9n2A2Z)sO Wz!o&4u$A*Z9YoiƐ161ŭMR*` 04; ҙ,s ІBH6#O 5͛wim dsu}b8RJ#` < ,!*p6n|tFFf}2 ?*Ї)iBO 0  }XcH&)C$iqX>m"LF5g_)~CJaD4+DB:O8~ `OpPU>U@5H i(E;P,wmD&*2h*JJBPɖ$4(>x]8j(()`H<Xf4D¼ ,+"fvA -vs,~CIeBw v|iG@"wn\$-M2̵kW;2Ǖ+W8<[{'?i*0?xzaӴxĥ8+ņ4z6E)%^\ExRPfttR 1U|\B^fhp{-px+aQv 0)hR)|_! 4)ժ;cfX<~Gk8C`zvMض#~W1šRD@j:m!p]KK%''La vX,Lb0P(HR[^BӳgMD !lq@H$!Io *eXZv2SvDxQ%h"e}M\RfjzǶ>P<ժTk!~V4N:C&a2X`tl*g̅BZRēR9<`lbbq=HEVE$N7TJ/P!~7RO® %1$Da;$iD4&B@`^DGONؓP(hkxG C0 gcdl\H:aovDXUaoJn$BqDSh$F:E '1MaS'Z!}R1!Zji25;\x gRK&FƓ=:j:P0gF1 ڭ6\,T&[llMHV~BT!9n5WL&Kqds={;8~7y/΄^yqr<'Px"ASgcp㢂3 - lf9v6u}\/hZл " d GW@AJ"R``4j~h]Ӆ '{Cth4eFzL醈h8nKܺqc"uŭWYA/f8M6_ `CC%>cv<'LqיHhtA sSHR{pBjGƈ%RD" =:}@n.]Ph3 CR'2";'bivm+Ea͕hmC;E]01Ii"}L ϭ } ,3G\~Uj*T6̺|\ 2^MqECFe R#H#t:KiBӹ٨R,j6趛%|f8~Ho`5$M@W-j~+$iBpip=E,$#8ВK7Q&nb D\P(2t)wxG%MpWY)oI#<@@4b$>#STpZyF'9<ءQcxG|tӊ 3O|B+16>t;-ݽŧ??N>Ʊ2͆ W$TD~sɤH)\D2|3#Oٵ],!*F&)^lvvH!h;8 [oy駉O{u *_~~1fKwo111Շt;vww_o gj5z W1Ȱڷ vdFVj@[h$n,JM)t-\P>,sqDJWH]>l 'OegkZ½˼`ۃ{ M ]4<al~9c,ݽCXn!|G#HR($PhY__Z3qיrt\"S~񟰺B|ڝ/}oB7)nox &eqEv2Ł-P)ް-Z I8bX%$oc|Gm"phI 0)&%Z 2YXXlLH^y8|ϬL6O,'H05=!yc+a{zT*McKamvHͤEGӌ٪@H DZds] DxW@5.iߣzRic Ao7}ࣔ2>8`Wq^x48<h|V,zv,b~ZuK@>'PhVI$ZM[ _Xu]b8#\~ww9,j6\ﳿC<`Mvw(?΄;@J \MgM8u[D#q|<\]!LSU{UnL ?y|H*ݬ76fM΀^#׫aԸ|+C4qMgcO0ǡr>Rei~4TLY:UHF((0}H AL"dPh*!ìOy2EJ!UM_q8(M7CcLLR]c{~ZbH84m&fyG/qW׫ln'ʽیMnAP(Ngu"jwQwCw>VxD""ΒvEOeb|_a*sǐ<ׯpRcSf=R#xJaX&GS簼G,suaQ$sOͯ?GhEt@{^m D-fN1 <+Wn22:M*ŊD"*fsCt$tPB|Aabr0{%;a."0]5Q*xG 6=02.1*{u҅167VUW.Z%춊'If l>\氼N_e\ttC % SӉ^^J3:7* pQEc0AGHG8 ͇ _Oiw9HMò[? LYn oDZz?!tLâ?';uH8z &P(tdoG:BH"7KX_Zcq-~ S\zGsW]*þTxR*{ēilGVyʷXFLpE&Sxxӌ25{ݭ5.(j3$Si"NI6VnÇD":V|; ^~eQo4_C"MݦԝaPu|7?T nߺE^'X}˗/#n߾m .]HQ+T:K.86/{|wappQ8B s])J#i,?I*Rvvd A$2j ;oy.cchBggw#4]ʥKll#{{Aخ{%  \.{!z/ŵwe`T2bwgK..R^K:ai6G̿7E W_RO rfkJ#0tM$A4ʚmӉh‹j޿=:tݰaK <E\ Gl 2~ ,ˤ06 e&~0;墔ŷ}ip5";;[|o,3uϲy=޽r o2sԗ- /(g}} LMͰB R4dT2%~#$TiZ\xU8BѰR>`ipz77(EMJb*eU!Rf*jh+u :nݼ^>$.mZFc[M aۿ&tݝm ߻FZ-~7o\X,E.]23{ qU.RUVn7lnkciERR4 $RJEf| }bëj`#M W)MC&}{@]+guD h<rs2k?x'|@coo Ljt;*~<ĵw/_&_(u'qU#0|*C>DRi+}v;W2!i5\t9,;* $nn\cll!-'H D}EYx ^LO> F0KHPE4L"H!!"dwwjL,?sjn^k$]~o~pS8&% ımibY~ig3p•MPhT}\'U*v=vo\$^04LMGx -i K^HtLA)aBKf g–YWxrw*LsװBh4Ʒ' {0Ym2_/{WQ,Y}R ixCbmcI!>'(:A^il_@x5,;CX]Y'Ëz^{# ؃^w1Mz?+my{5Ђ@ GOu9<Lt:KG8M27 ,҅Wtk5^pMtxO(ߧnOЇj< W鶛؎_})fƭW9,tnIo; 8t^ 8.yRH4xߪxk|O7r#L=7>Z]_ Y4C7C&JG_Q|'=Fˊ(Gm5tnu:զ}+ IDAT\{4.$q)t~4)bOʲpvh4)5"Ew0nbJ(C}^Pyt³-|ߧY;İ", O`&p^7h*{[,DRXnM 3H Pp[/3;zzB;7i`|*pA,ݾJUr`c~Hkֱ"ٵK*|MJ3^`RfS ' q$8PKf~ t0-<}kxqwC ʃ˽>C!\EÊDX[aZ AGa=#@Ph4 i#sssT*B+3ۇܬ,).zp+v^\q\b(ByzeE"LMN155ɧ>}~׸xbc888 G4:_җBhdh4`03?YKw=8/iBv=Q^yG?8M23$Ǭ9}zGW)H&1Ɵz(_y4cp8^avo}uZX P:-Mؠi}~l&K?ʙ3p]l6j# ͫxkm?T'a܊IeVH ۽i$ r,߽ͅ7_V };>n褒iG'`"̻Dd||v/2o*3dsffffaaT*E:n?O>B:6kWyzT^j̖P=+ydq?I,'>Gϕ׾0t!(зzOLyhL2Kwy^J#I4>@{!$nSΠT@PeZКTeMned~?SVq 2Z@$;\[IJ"d9Z&mwZÆl]g*j.I4>}&(HT[pL-JT b)bO2,R!T\vDc,tO\<Գ]<3}|._/ O-vwVnw\$!u~6g_5|[I"RIt&GeMb(\d:ǎ"0to_p›T 6o_w\'̕8iq|xN%_(rY'yױ/p]3/]n=|l:FKb&\I&fgk^a]ۡ3:(7VAlglB%|}+H:yl261İ=<`o\)\E{Ef? vVq]}t`0Шˊ1>>[}H177Oi9;@A&%~[acyWaWal =4MJgdrL: sd6VioWzB2!!_(/ 5t&O.g"G>f/w~wdh?<(Nv% #cJc3.>ssXX17So~MAݷw"KRO:F',T*h7k4k<rd # .X1 \?( bg'xfAg?>eԧ>>###+d2iRĢQ:/2_bllncLjF"H))< ^;@:q QG#2R~a FFBB4GJDY1NsO %$jx)qB&(JQ,/3lmmh6?q <3fhFXbc}){=[OFI&|%+P>%ؚ!+bE(?F`|ytvl&''LsdsE-`lr|6OzJ |,!h&MͧahSӜsx~H$BQ#i, O:wo{K1C0BY~?!:.Ba_ӈfp\X,ƹOˌOS/|)2,=`rz{wYsœ f<}$T\ȝL9H6GiB K§2,*IRl.쑣ONj8x" BGSSIdj|T:'hZGF8},Qԙ3F~g>dEw!?k<\O"̰x<;eC0ih4 ml((Ka2W{]!B ȱq$wo_#RVRrט:>!֔0nZQN|,l|qd*=3}gHega,v l>ϯ]Y_$2E%Pp2b?%#aHywӡ|zBju]jz~̑c,,ĩ'wkYL]~KW>RذM09=!3I"iFƧ0tt6O.7믾_}ygJdEc38.#3U||?OvH%z~J2(<0?Oݤݪ2yX\ πpT/PN/+12nG8u)F&=+oG_dqn ?qM3U _海T@ʻ4}51PԈNz"^|aѐ\LS4UR~h,g>"!W(a2ϊ $F'hN>bl|!sGO/ro8z$'w=4Q^b 8Ӳ X,N"t>'ĒYmQ/5= 'DhNZqu.#KHH8U$29scgR3L]j芀 $N$F 0=~#K;;8 If&{eUZZ-[=Ȇ$X[\Ɔ/lذaǃxf`inuR]^;;d0z'T sq| aWX[_ǝ1M6׷n1;wk:ɘ\.F\f8LH5ڶoQ(5NP5gD(0GQysӤde]0 x74 'Sn* uk?51ue4}&T!ݫ6REUQU5a 65#~O]!6'c.[ptp@6X,>; ϟA7O}Ɇ 1(y4g0S|'R_Zf>wImL&/|m}}?YZZbc=|M?C %c2%s>RvUSBp|t阖A`{ S[ohR*Ues38Z[h4`xAZU"KJ(HxlUISQ-%0P*Xd2䖡`KJB'# qXi*BGL EPT^0 ظF._ R[Zܹ|@JF:t<|#zxt60Lh,&>T*l>3&iKe "e%( xAh IðH99lX8i B$՗٩jDB C[MjGԖW{fl\m|oZeu\Ha]sz @Ejt*Һf)6ع}l@e2;eÿ3TUyO*/V<+l^W/x/HIAQ10 <'`f3IOE9GDDCO2BY3.P!caejK TEem}+E2KN;drV׮'Sϝ3N8?;ٓ/ obrN0c PRleR~ wqȟ1Ic^HpDD]pu/Qof& Nh]ưg<EB|ٺMP뛌}4ro7jXƔ_R.aTIĂ\t:zW{-nzL6O}sلN;|ٯ4[$=0D0bKҨAB;H{Ba8TK =OXjݫ|B@i4H&B%)0x1E* *  ]\ð EbҺm+|Ry o6vrUr1~ qtƄFTruhH&['[׮` nBôP5RuR;"t#>lx< ׷1q'xru~3SeӚNp$ =m0-./aoGQ4$1s @K9dsEVt B :X)k7_4-RNdİwF)'DQHGZ-9ɔoZrCDB.[PÅ8UQ"rƵͥt=ׯqzrWWWDQDd4R,$Ԇj̵k,-e:B'i:dR \Tq!U.R&qBɄr_|l:G1bk! sڗqmT*Uf)K6=vw_&Z7Mם3M)J3KƓ1T+ ﱲ繼|^TgccUSg&#! e´6DRҕʷOrrw1tdBɓ\/\ 5.mة4| ͠>f|9Xƪa7M D5LhH.( R+sTEeemB > @J2NfqM3<0L !\]z"Vp|J6# Mc(1PU dmp]qzR*/*o5z]5tFo;IXk׹staji%cjلlF6W $f[hZ8N\HESl96LP؋}~ٟϓABi[H2rL&cE!r(-?R J2mRwX~ #JP O>耓C:锓lآ>y.P*װ,~/Sd{-]ZeSl"g+"R։NtLy=Ԥnu\]vnxhSNU(Wꘖ$lʠߣu$ Cr/U)I'dv<JetN'ˎ==R T&s./ =Z %wD_C$D8JCR*WI;0֖w>Zo0)W4=&9~Wol0uQH Űg94) Ɂ\T6vP+n" 3t|?q ]]!>BULD], ЇlµN򳂀Uѻ@Uu&!+[ 5fVtu@XU E7Pu-!$f29AS'eX,!HB@Q\JXt q,txK:hW'̦xjd=WBlf"R-(k/ m6Xٓ/xdzǟ%a/8 9{V xoR/V)60 ayHFǫK,6M7Lh@0Ҿ8ecr*QLfR;\^^ba!rms7oJTXvbLRܸqui6[[[L3fFc u$.6Ǭrپb< &}ΧKQض-vi BQX 0L쳁GIae/w_p+>b<`'9edUXP nC&aV8==sI0!rjd%ZnMUE#kDatܾ{Ӳ)J+Unnosmsu^wuzj4E'~I \$f:eb&3 `>J GhɄɄnzsN3g|oUn1KB=O\P9f38&&Qt#("/F-jBBҍ"€Wޔ ёiET*t:F48?;aum HQcXJF^>@L,c֯m!N6Bx~r3i]4l䓏>d4Hߐ& N* xNx6K΋L6G.')8I*Z]Rߺ0V7gG  d[3qW/:XMR*k:l9_}9{V6A( `ewEA|B ;c8"MSfr`u}֍ۨ Mub9M{E/[ҽj%^V(ZM)Bvhe2*8̫KT* ANa6zr6Ϸ) I/!OGUu(H6*"x:   zyѠNNN(W(Jg{=TUe2ܙbĜ)2J5D3}Q"UL[Vo)10"ɤE!a_??p@|rL.#ةQLX䵇cZN۷GX"ct>SL6 0te_>~{W€PI`%]K ?u]>C|g4L&HIԗ8€*V/Rʀ@[("&:Y!x^{.&ͰAKEbpL[ d5>->{Qa[)NO\컿g}cWяJ N&ǫW/ ܻ(ټ~O!bN KI@UTTE2u@diYl\u!e<X!J NI$GsI3YbȘ @1M uA޹OC2N~WzWC dd(cB"$He% 3O\-lJd2VoynXƴ.88xoo~s N&WdYt#J2Rt2b<qw׿emE#=H2Cҗ v dH/>7o&}:mf)jJp8z} 2ӵ ?n4$LM*ir5!ac9{}=rA@Rb{{ 4y +pMNJ'$=1{>'U(:nvPDO;i^=c};=n`&iƲS=y(~ۑKqx6sDD_BAV &TUAQHF֠Zf|%/i(>Ztf"OLHlEqLe0d8psZ}Ӷq֮.Aӓd[o?yqiy!Sb:qeHqq[S6O?rd2.RuBJsiaaf ۷ѽIJlf ɄRD>_}(+Q*d2"-rtho1LH;Y H4i3-0/v`9/&'/Igip"!IhN窍I(vg,-1\׿d2kr PdU9p:;'XI"MU]c&%B ݴזͦQH?Dܸ0y}/hDz= XXXRҽlYVX\_%].;'=iIDDGbK]7L'vj}NRhDW50TmpCaM e"T5&T̈́,%.Npȭ22g'a׿E9:|B +;c=տ»:u(L4\ҋ%´TvI`63=E"4*9iŠ+cfaH(c EAiI"![Ĵ, uryv_<%Xx #qT6{.B?wx$=qCT, Bg02~MFF}6BQh#!=(bnv:A{.v* \wr nY2J")ei 5Gb{; dSq:#%e$'"XH!S`ghŝwiBJ'(N\]\ )a>jPpy5{^!۲[l\=3M0r4H[HR2"\ K BtsaEzKeҎiV U7/oICчvHF'RdL*o醋up*aC׉0ܾC'\.Ǡe:Jl@ۗ,7V(%=ʊ D,ƒ "5qUUt|˲5VѴ%ʕ:cd~cӓDQD7J?DQ{|tڤ* hEA R0X?3Nd29^|(Adr<ZT?DTƒAeǒxqBm)!Iͧc6v(ktZt;ll@Io0,)1lNc^I'X!=މU~S]i+ 2OXYۢ:]t8Xq5W lu6ݫ6MҚN\O|3UyO&[]^_|>G$<2sJt°%b٩aBbeUv t3K2"\E3y^OR{G0-f.n&~Ս<*?~K.|.(N gS܏]T@F(D tbxAN;9TUg8&(ΎiePߝ#?R,-0 h}>}F{{rӳSV۾Ŋbi@(B#$"w{JE|ϣuZ`ڮsxpiZXr=nܸέdm=: Aɲ,Z G$~rK<67)W*;ԪU}]s{T`Y(T ݴłdjKR`me۲NeHxo}aU3QbdJTbIVIA:yj 2H?v*ERIQ!O D1MY^^kdR)2 |J84t:rya,9E޻OG 򪊩= eQՙ3TExZέ;Eei\tBX$ <^FDY)mWU~f)A7 nݾ<{NXN g8@I ʚm˶(DqLeeu'[| םqrrWd2Qr|t =tFFp._L|G_}9 %Yyː,Ih:D9͡ϟ=p_Dqh<&cnݹGeM/NW/A._oggÿb4.p/_LJ}'X^a IDAT>>ɘW/c*d+T _ gK 0 /28;9e#c8ә|qluG&SgHnݾmۄ hR@dtMeY*VBp}?o-RFcF a# fҎCLsd8:ܧ׽Z ,NswNX7ٹk׮A&ݗO?u|6^gOdie|ě79R  ,+(*Eyy<iC=C`8dҴ]3=Lˢ\ cI>"fp 8=rppHrDsIL:?%(Wt`.(f?B(1dvrKSvgTu^xLa:;?r|7aK7XRT\Ӎ#(`6%yR|0y/}g \[ܺ:~}2bc͓};g h aYU0Χ\@*йP*D'<&H?N/X6+ƎniA92-DMz67֖fiV֯|oW+%=f-!DH, <HS/U)qonغqqK)Rozm2f"(j,#AvuwulPTT`8'[J'[D( ϟ~aAPDR?U,9B |< 0$_( i'֍ yterpJX,j3F yDɱq4V7qLڠyruV[a!iLCxy< jÚuתUvzG{:, /_I"L]04G;d3/b ø{\ik,/-1=32:a!-fO$ssaݬayuxH$ڵ&K{ܼ8 qOV%_(p E߾ålyn+OL|cH3H.e'H(N]=6oZ?a@xk:tx"I  tdT!O&oPqIVxKtJϽ ͭuvÇ{^Cד!Pх "ȴmdYT[d99Ƌ/<[\ h=:{ =& ׯqGS-3,TFMH9`Hw}t{?Kg>Q$Ņk\r1(4W#i:/$K>A䩻1RD:MOaYJKM|s/[Gnh6A2Een޸N2fmmiZC%0A .ZjLLZ>:;ۛ,\BĈ ǎ˜{ Ѩ8xG<7y4 JUtAݤuTqL4g F'H3Mvwv|!BoDD"d5$ٖM*atlv$P.u ^cjfJ.ۡR޿ Yp]JoV(U; &͵{< ϝs]ZG܇V.!IȊzm*#8uulב$wPKqlCGNp TJ% Qyc'Z)m[hNiñ-&O 0{=vֱ]_±×=Ƕi+8vÈQUd3~D<X}jyh4,ZW/uǐe#BP,Eݲ} p'|axd?f#:z]MסBb<&32!&&gҺ#)y[_s]HP!~{/՟S@ue8F&[ JBQyq#8k7Y]Yd>^/cJ*uǡ2 US)\r].%!'bEN8t4}T&K^A MV uA<]vl,gISV노 8~8kkk|3ӟ4t~~fO`]HyI0$%eYÊDk vGB4>33<<bumPj㹯9o,#iDe9.UJIǵ b-]@Jی4;zeJ i Q\=N>gjz۱q~Ķ鰺zKgycUUY[]E>K{NѹX,y6Na"hƓt4?izCL&z3{Zt _ <)H$QW4QzN08a=D"$ {ct:!4}A$E羗_Zy%*I@j=pqcs}wxɩi~#vnlOB( !v:3YA!OQl#b܎ ]X )msTuz*Sd2Y K/CVaN#/e_84M:ݮj n,^ }7ll:|ף$%b>H=YAtrf6nq̞IVebr*|XVc[T*e|?  j+HǤhhE-p1v&Mi@ZRT%`:>ۛ;ht9p_œ.!`TѰeH,Bz.EQ}j%%GQ|L#2)yܯu,|tReY'H*o~clmG UAVJ{Z[Ml>i |T`DQȰ,:6ۿ=ڭ54MawgEQD("cSbq` VPT1>#*l|dpфq}X㓜} #$i\ q{I& N4QU8B$D۾Guh =] "?UUU$W{;GQup 8!V]QPK!eK t=:hUQLjX}h,\:4M ;b׮\`|r U 8z$aDo_.V3>LbpK`&'d*͙gO>wm=uץ:p mIIjYQ UHer,ݸ <8粼tL67r)MP%Ė [,#+ cU.[&3k$/qX*2l( \%!_!-e)5f;pf $sH9\zH~5eBqU?Q(y.B=ǫfH)2FIB`atYVBaD= =FGG|༖eYVn%Yn.8BB>~2S3Lmmq-Œm"  -Icr8zl |EVУQfalW.ɻ4M*e:DD"͵Wo~[Y吮!|_VR2Mh$<x2Ko?1.N#'z.ϸ]23ƲnIBN^A*!+`Db8=<Gq&g9x(l dwRlHx&^A{.e1A;> ˇ"6b)tZ^0Uj pп}}?Ͻ˻.y"EzL2豣dYPU1m`<~c|?>ALO*R*fkk ~Μ9C", ,nX,"Z[nq]wqo|q -:B(Ą@tb#3f۶cXxqcccO%xG1{&sss<!I =dD}5p%9(>Q=@ofu]Οo[$I1554sǝ'Oﰽ\Yv1}ff?c2=CCC8EM.}q O>I~1 Cj37{?  pI+ Rat.P3Y\\@tnSݢoٔ%>_ߐJ9z8¹vK`(DBeuQd`tP|v0G?2:ƑG!3>6t]n3iBB %7<́cy8uL$O22:A<0l_ş0{{HL\bggSL tS@$4 6ww|{NKgk_2161I&ȑ8["oǻud*~1=;:{Ob 0_%y>ȣ?@>W`zv#bxG4jS^er< Q$@Ggx8'NރGdM^x;lompRTqz.>e"e'%IEmD#( 騪ƅsgHҤRY4шNjjq&A_[ yEm@LKuv?{Fh6j2^'&$34UZ*n{%ݣd@J2I;WB1ז=8ʭHZ+E4bFtjS.crzIdEȱll ըQ.261CVcrz/}(kUƧH熨.\Az/C#ɣhNmúCS {6Fi^eq~o⣿q 1 #,N<x;E@U"e͕K*$8>$I"p @d) Y|0 Oq+?'>f(FP5b [ozs{QutfVYCe>L2Б;UCqiRncE^yd g8|Y"΄ $dW>ǏE&fd|4XiRM`_+NI/e؉H&S;qHG|GM0%74F"…"vNK˹ X}'@ o^Gd^wC115G0̱;NL%y 4k[ނ 0}ˮLiR,dNiLL +O}d:O.W.__|s%ۣYU#nۘZeRJea4`jHؽyg?‰P+mH z.VW^zssv]U9A2P ( 3LM#HrF+S_hB>짿D^ɫtEb8#,\>é{0"Q#1{mzV"GÑc(v:m2<~?V?M1>uze]ShFskyw8PcykyqI\Aq->3JVQ)*9r2LP.V,d)thA6?2{;D b\qzLG4vH4˗tLxϓw݅yjuf'?e&%CLs%hK٣nBhU^8Xn\ho;~'O111A,웨qU_ Fg IDAT/o4AMϡB(SiqJD#톥Gb$1Th5[rkqB^DBPIE& 971{@hy7MvwNC#K2at͠Qrk^G6$) =b+e|ieoGYa#Vh ¸,yAlz4RTB I`;~~?>TtUS1H%!^yt*kcjj]ױw_y<^EC~@("qlnݺD"V0N, ΐH&XXNVgdlJJAH2iYf0ә.ATe:TM;'^#}{DFj$SiΟ{D4@$HDÈD$AbrnoSU]CSO/qmaH$L)yBKx d :VFϩ{GQb7> ŋ(|=UзjU"MӐel6w6tƍk>rt6GBTe{Zb"feҒخCb٧)BQ9s(T{O͒A;),Vxl-=ڕ 4r^,dsE_ [1v)zX}K_οI" ;,+%I&(cTy׽|D6'ŘebrH\AD%b\rQ TM9p0cS GG$R,ZL&Gf-P|ZMT?`,c>@1Y%80I:hZ])'fN< z>BQ4NO0pM# HBq\cN!VS 8t\Gۃ4|T2AN"Dz?@0Bbk_@;)|0 ICȲBU^7vq6VoMY]j<㴚 |stT:Gew3S tZ ^^ZcsXA"|/w߇羆 Ȩ=MÒCR4{ɝ+wB,dhdD*C6?$24-B^FKPfzB2>k7(J8H4|]ǧ(FÈJg +toh,M% 6snthGAyoGq]\>$9;lo!x"40;7FQ"E\&xTa{.&>N~l\='zXv6hmYDq|Yjk!l YRTsw;(jݞI:7LYiQ$OxTAeo^'Llt >u/"e > Uc|rH4F Qj0HfhQ*B#Lbďps{7Axt"B""@(+ c{.|:N"Moyg&-L̠j16WE^q9$wh O E>NL W z$Nf4>asƛF8I*;Hd>hՕ'2d2EtŸ}]# !z$`z.۩4Mñ4= Ҭ0B΋*`F2;.C׉F"x/G(؎>g^xёaq" sB |ZC!fץgvEcνO3<2ŋ`ww,j5a4]CWU?g VLlx.뎋,e]C%HMGG;~٧ii1=lfwg! FFF{ȇ w\ I "l/>k\z\.KGe<.uv7dsFǶ-;A:!cmk+4M)87?$ʐ [}|%q 67\=qIfHQ*-3Q.nl.L=c~~u2 fR Mי9qSϼx>C+ }AMt6in Kk,^B*1'O=T-U.K^zB S Ҩ7pr-q` Bi6:R4|{c\FzYRBd:<}G< Nod"ޠ'cjj'K*@! ‚dO~q}z0vkW%r"Oo7v CøK^Q1:6ڨwn;bNqf8|(zᡰ7<2N\-<<%ҷGd*뱵xry4MR-8QN{Or?,IdVp)#*ЬW#u-ga~o{GFh}E@^SߠW/oAgϿKku6>-%*A5:e4M $3xK._to6c"ؽr #c…nDӴ *1>1MZݪSexwW}!K%xf6P4'3G&Cfj59|&&f8p 7훴p 5:JOC.mҙjAضI&7Do*60"q `lb챷Kyy^L3ȒL,O)LrcP(b[}4]}''O0s(XT&Ljձ}ڭF4IZHD:!!zܾSNQea|'j6p\Vylm%y /r-T=:;j\|f ޠ p8uD.C$DC Pq;C&{[?ishnX,i T٦Xv8 en^X}JxK$bp+4%\!ɓ 6kKX*714cqTy2OcgL %ֹa"kd29|&[#@ahdX"Qڟ6oi՗OH4Y 6 d >fNP$IPT:CRJJQDH2=˗}$}b$fkz+cD'bD"+{8!2q31fy|zZZ-ī}gJXSs GH& +Br[^I2EG$zK 625wMӉF4kU烀fYQY]dvEU"W`H$9> "S.dQ챽*|1=JG%tА $vιlZϐ˅ zd2ڠ۶q]@h4b,X w~gծїa ' &$Ba|ZAd ; W33T*lazzCq2t]:$‘0L(q]]~vEX޹s'gұX_\nSPBH*Fe9&R)=dFQ,H.}| ߳]@RÓ6m B%j5R47ذ3B܎DH1XZB`ZT%b8Mzilln Uf4 l֧0>\$$qkaVqtMd6?rh$ʍ83pE|| h<Vl4l I5 !(lQg44b|!| O_q,^Hr ϝX,*^˶x*+rHe2=l If|b4{?7X"ۢ/or艰LNM#+2S3s3ِ"ﳵAZMA) N*r$vwH$)n,qϽ1?Jbﳻt E4E467ֈloor%o z1>{;; ҙ [롼1UI$Db1v7ٲ{^qݘHfmp=B7"llXz[K[ $)ҨUx9HLep=9=/{, )'c8A$+녹H$c[,8.RhNSDqv埍d&6mc[&vBqX"'NMdsN ݈uU'Oj'`o>A$M_¶lbFMCۯ"S1plVi["*ч񷽛V r#!bKgQ55fVds9ҙ,fJhױX[*b{/=czͳ/T+-%rw~U&fi6li9q=T{l6>d*\FES5U1dNe䇩BDZ;fUv]SضjQu<@$T$BPx*`D䆆vMZcKU5ڍ2# ;fDZȲD1%tɛQ!Q@Z#mv=h<ڨRة3d ÀKNd.rs/4}N+l)@H5aW<}D23@ V40"Q~rݧt}.`! F4;-:ﺟI雸yĈ<Z2PqHJy/ FGɪ"Bj֘?r'{rh 51CQ?879PsT LInLZcw{z50y.Ž׮avZlobg{ku [|&RiZZ-^_3Ȳ<|29}tx@"%br"6DB!Q  Aڀ?]yw2s;Mꊪ{3O~ϫ5G#nݺSx|\z`j)^B'R!&il,$t^y[>($gjz0 k8q  e LWQ;xqy |ȉ)\=a$)&m?A9v-8!yRDkd1,KYGZl&,#Iz.mFo*qS@c w 9ģV!m'N###Rv;x^Lcll>6+oڛߺAb@'\<6)4I1( IJwS8y~x(I IZZ8XHG3Hp~&ս=G 9_{SC)-kA@CkF-J3sBνN>Rdà?`jrI^UޗV1c N!$R Tlbkk 21౳{ClݻKX~G;1Ш3xO8‡>MӺc&+y>{;lns)T1`5M 1[ذǎ,+N"*GKk{~~(Ҳ<|dVݝa\".K1&J/]r2'Ԫd2Y*#&Q,lm.7 7=lǡHWb< vI2C%qMr` AB À?xG,ts ac/1?kqG/y9W_=Cco ˡ" |-,‰ )ZKrXv Q(8yc'CǘH$NSksN3ZʒCa2YvV057Ǐ"dL417o7uHL0 7d96}12r9v8w~z.hҙ sLWߤq DCcQ`s %.-,G' .~jRIzs~߳nHq؍4^Ci%J#!Jtm*Xn؎C \a1)LjɩY,۪ۡ36>C.W_Kj5^h2=$~X<$aw iFGVV9HD$6װ}gsW0/zJ 99E;e=B!R]䋤e\ܦBE&_D 9F*erb Bd4R[P8{}ҙ,D 5<0s:NÒBq l)⼛R#@ġw3i[,( qF2ؔJe CW^ѨwЊt};3dX6m!X\vLL[L)1s!J4;-5QQ(E>ѱ_dm2Ԥ'21oRJ012YgzfBIL&ƀ82]O}yoH$ln |/b)H smLΰysY:sNq2U;tMo^M?T#((7̹C( 8w,&m{Xw>Dݢ顝}飌Z @H,T:%S`T!Өqn.S,( Zm;?u^QWUbD裴zz IDATc/mUqEO^$(䧙{5 } P"-dϊ[ QqridL.O*!Sbfs.cɏpĹP LmfbZU|@ 8b桇jsG>qWpc-5E͞2,-͍UN^NSs;u: Bo@pmg)/kTwH|%/XR5[ Hʖ-x`@>sۊ/OX2 LbOVrxxDVnatLGV8yn2::fK#RO(R{PlMB\iS{EeTQVi4|yrO2hlJp|#!++GLLMsEj2?`ܳF>ֱlJXHmh>7s*<8BHv&X{g)mHɉ\!!+/bWX㧩,#m^y|c? ;ݾYM}w!!DrBHIed h|)%'hZ$R)n^63%H%G2?Șru,kBp0q!S!SwI%J"D#v&s=VU!Y2,!XԈc1ǎsgiL& HR{Z^ TrܾqC2<-\]C`bIg tV7^2 Qym\4\:ɠY]r[< Ypu@J{#$!lV"$b)c6_i5YYMPd9&ij`ttJ{&јLV[8A4իvP*|0XbO{$;xCh #3~ƒ0m)^ 5-) Ji$ 5Rd"Bjz#He  H)ta7C a DY(=L΀#lpGI&S[OLfs hij bsXH!V KEL6(N"Ogugp|8LwbY˱ISD!Zg1kZkҩ4kk13 1J<~?30eaTH"a4Yc`Ynv]%VJ/|s) MwM|ۣc(νp$#$IJHlb}k0 ֭|}]k5J/uQZX$IOEW!SNqT!Tč` cb*b'Xr{,1rʡTs C2m A# `@Oi56(˴-67֙_Xm>QdSH:F#!I H&t)%c(8u KwpO=A_3W(tw㋋x7nuV+$%EpXheb0 9owbDHҤ - #awxBydeedžXc `(LM͠L64Aolz1řQ20XB HJk0$Jl237Ǚ~2V!^qpԢRR,d}g|T*CH1sy~{jLKsfv`9Z}BLm8!  BTU8}&"l6a:viRHwdIm|o,( 8l( 8q\?SXv pi7r4 ڭGB~h*4-MV+@RWin_yo222e6p>)m>rb@"m5XrL|sq{=&!S3 cRI#o2_LƓ$3! Zs; p0`oS41H64'M "Mmp%ZF(b[ڲɜ9ȓ[9fbMӌO23wA%?~YV(ǹwskQ? tWu<2Vт,m7b3nbLv*ҠBFS3ei &x8zOO\&12:k@"`v~ɩO/9~ D;sT*lnbscnYN2G 8BiE"]`gk/1=3*^7_{M}'1VطE 1IE¶͑ 4"G"2 (J2 C䤤C^ lKj*ƀmɝ}l&zg/$vs(H4H-PB!sM`w9y"s '4UVn_ay.<.w޾! C1(Y6b^ᘓRWgy ɤs<xP_ پP!+X|ˆe۶|%1YAt6K.W: Fƙ_<Q (eЁ&-hK3@j͡ '"hmPJa kG E:V_ulʓ|5Hc;yMy}A.(/tZUF'vا8wt@Q3V.iźlz]:&s F^ w)^2А{h j6cvW)A@Ѳ(3*Xq/HҤSIχqu.}`ov42C`hHL:iKQejz>ɥH[ Bvwy>Sd{c5ݦ2 7$r{-zRH޾܁" ~pd % )89~]cHc 4`(9Rx*}fH3ԫDQ$p]0l1:9DZœܸ a13w,!W'" =fNdii !}߸Jsr{t[ogE>;fssW_C2/}6z0 yBWfS C< FBUnz^tߥhBRTC,.HLٜ"7`ʨFhE'O077>vZ֚&'&x'8:{{xZSVcu"A*){ܺy}vpB M$]{ڧZ/iE6?lt{reځV ϶CR唕ĕQ EBx"Hd J%4ӆGM18Ll4Bx]&u6 M*<"B -Ab =Z:1-ͬԄ5$e'H8F9V^{!#lTX`YT"s?d|bDaK/< JeVV; ԕe9Rѵ;؎MP`nX<M`wg|0gsh88o]3fxriCMEH3$GeiT2I2"0;7ǣ rf3e[ <S+w]vgXOva-iL!$D1P( p۬n5o5THNͤeUv$pksc hZ$I2QsgS`'ҸRyT:CX[45z=]4Sӳ\z˲qd:CL&(O:ݡZl/W$YFήRS0E*!e'bA)$J+6VKds1nAN3Ou?F&#ͳ^I>_Z9"T/PW^c`e,*0DЏlXXI$8$r#%#?ɹ:~A`IH֬>[YgCA 2.Dl% QExR0b!j$΃fqg9s>Vת>Qrxpfgs'Klv;E]8l;$TKưG DFw7_%o}AB[XH(T&% 7>A! I)w$"0tDP.՚Jra?vF` dlhFiM**DQH=[}ksR ŲSw!$MJ 2fO4bNЉ,uǦꅌ\>ɦ|?UqhUe*"k$)N&KoEזJ\zQ66,3.!Eg~BVZBQE[: rx 耾hnb>ZkH]掝vs2'>fE  a; BkJ|o2Ј|¦0 8 |:Ơ,G>i&)!H<#qmHGy~~MªDR0TUȑH+I%ӌMr%K7[nƍ\~ `VgLb5[ٷ1Oh4G.<:M?>FXs^bhC~ $3~Trn1Tڗaw7I$:-F'b(C:W82M'|_Bָ4, "g/=O nc'Sujd )vZZ ^iN*C co><47o`skёQyOh?0X[[۬ĉ2^vg?hƐ aZ$ ha1 4UDG+gy.<<3Z=?Ē 4Sfȵ% *?4:BHQM/A,{ ,~"̀ 7Zi9 u]|&5Ғ`6SS3ndv[cvUaDp s,W&(h؈d#tx5Q#m &AXxQ2Rl3軸nFAŶlXR;[y՗Tᄎ?KQXX p҃aH&瓟Y677 PJ]67v:trEjG# °$TiAWh6Rx42A鴩UH)Hlu[$I67_y~~?&[-pmv__&Jpɩ2,v~ ϱ̳|BZ&#yǡUsi8R),mFGGs~S(V+\~5qo.׾ ,@< /sV~q`FvBP:g׾ٸsHZC2w۲zt_|K̓[ \haHhdLoX5w -!$wuЄ1d_ YIl}t"| "ʽmvn!|裟`-^; <Kf"U8h'*g]}\sD9J/f|,Z?C `K ʦ+-TĭOWEA@)I&-Y) tf?~A%P=ڏ3 ns?6QakTPhCxx>}FF(nQH0B L3wYgogcH\-eMƈdLn\{ ǣ< IDATIШJgyq$#ﳼt=׸.vBh&tlgA rшBB ER2$MC):Z)D:<E&dD+F2f''l-ۏqdt&_wz=e3pI1Hah-Zz'"V^i4Ƌ52R@P>q.>7Mt?LNNrY**hFGr4MrO?4n/K/7nmAct8DZS"ZFU0j̦RL[6(b'b7!9 0@ض|s% #GE<'HgS; Nl pZlW~埡ӧSm w?IobMj 0keP6$)pESćwW+,`.])XA(IDBaKؙ(gK<_K.1<i##GX8*F.WY' рCQ"8O$L;H1- Dthmp,¶$ )Kh"MF"l##4ȃ?8?zcN$zxt@;3H A_AWfiK}]k IӸ}5,BU+4 G,_>)?f˲5$A:G Nq۔E{ -gjjIʥQ_^=wgS>b\Vakw\2X:FPQV@~7>׿ L&e;8_/qG@R܌BC#Bz~$O=+W0==,\)FFFAs[<(Ke>e)JXͿ|cqdsad2Y>7~J ^yyRXŋ?K7R4=ΉdLI( "*R$+25=C"btd)%]1݌MLQ,&[MR4;;|>//!l#< c"Pu B0$S.=}m17wT:1 oy6WrW*$UDڀc !Aα!2Wi*ÎkZ&l6`p;mIIgZ Μ/NXS?%fq,3ۯr"`Դy܊<=80-ƴFq[EH-IHz0ou(davSg/?KalDry>FlhT[nJ&d r<.>Hզ4R:U<2ʝ[Wmv[Ae;{]!_+( m2@e鎔e lG uMDcش xd$0aGOW0NʡGFǙ] ??=zaR St:fOzɩy7?ӟb}mVI*[y1C|?]'tyJL|g<[ - ii,T0 i7,޹ ьY fRIFE: i-qzTD Tt Xq;lk'lڰ֏Y_M.öRptM($J鈪鐎8Bgl|?|Wۡ٬ax<8 \|Rxkͩi/E;IPŀd%[0&-<'&/n$(=4IMwֹ'fXي4=)9=j*ۏ (!_م>0'N_G>I6_"ɣTD>M$BDz291h:M8B\13{9n_{zLL-Lü!˶?$I(a'NN|C>I2#/J2AUAkE耿ēc~a\g_ıGQLo:;Ibyl.O6_B(׸x#$3!09$"SO?'콞$KxygVfUev3zpwf A1d!.t$ "]bݝ1;}YiOfW{_ɕo+VP`ss`(kWDQLx29>>Fy@ť?ywn݈caN<,b,$Cvt ļ$ %8cg3 U*H.(dY>^L&DN>,VVV%vvwǴ i@ciy/|3LSTUemuQ9=;ʕ[蚎( \~:z&Kyu~S# T K‚Re :BBi@'0d%E.ɈDDxSHHG"Iggdxq!dG&E^z5,bDR孷ϿfQȥ~J1,m>bgd8 0Dr*J*aP(prydEM/ I6Sqf 8N~CE쓩S$ٱ:IY0ʕYoO1o /rڳhB:*Q"( iTcvOڔ0Ea8N.(4/ (Ks) /kk_@$ â63OȊBXor+IBSutdBt |泟ҕn;Ta6'';4U?58`󋫔J.3YG)x"9&7nM'ǷSɘL#b#Y= h$!8!AMtEA1Mt;KHk,,' |'TX:K\&Q2ۘZOq=7$׆(  @\%!ﳱ5裏] L7H(LHh d, $$,BZ+3H\#8,l 0un eV/swXZ@5*:יGȒ̃DI]Za<99JAbo:K+lqL.1bEL731cY8BDirJ+ g`Y0@("q\v 'W0MFkW0-rNufC4MR {~ fR,d󜟷K,]B ,;a zʵY˿˯%Ǐ!+sF [$ u223s|[pވv8ETU2\ 7"+Q_"_le#+ f&MW.3L=;JN7;3KU D7LVKc8/||1 "R˸n}r o3e02ɗK-#f7D G$vK7 uID\I}YfLdم5CTY1t@H-drE8 C$Ĵ3:-\]\~Ѡ-( LLhQ#IdH #Eb$3uOOX8::VX[_OO8?aY11Q#iT|2,An`f2?W4%xp,-K DODd^ DQDXDRIs Qe哗FC7P4-fno\g~_F% Nz8"+誆籠o2auuB!\ȭǶ3a;\8FD4ER_T+%@N/n $I?Ej ;%cgYX^bO 4 ᐣ.s<#?1>Md$q\s'G.g'79=>$ bL\asb%S\6B0ҍMizIvw ds = ²mvh5Qt ˶tYc$YPrMNdqevwݦE%q0e4vui#IEII?mlo":e3H"]԰3\4`0s~FQUNN/@#GT ӦRޝsHl\{.Mv I,lxOğ 8ΔV%|+U(dO3%>W7OFaDqSAl"4ԋKegIAvѕT lg3S5<4 d$%pMB~A7 !X^FGO\Z%ɥg( HF!025勩F/ؗo >='ϑ3d,Nq 0r6p,q "SnΰLEDzc w)KX?xc^}u'GXLѱ!owZD(Ip[$q[G|@^|]]M 5MDZaqivE&&xJ}**`mdyB`|޽ͫP99~~.R*}9*2xIL0^fIDVcii)u'GG|$IVk(r,snLTf4rRjSYZNc벷d q*a09>EP͟WO)+矻Ly hv:D)'qLCeS5\|)B\g8. N9}0 S*o|(N>i6 ,^F7G=N2Q۷ާX,k3~뺩M1(YOSǿB2@"SWE{4I ;$^c +}EUQ;?X,' 2rȊ{d9dEex1KqHH`u w,^$N IDAT0"YD DLPKH+EeL})g DA@c~M&E 4UC N~@XTs7y7- 1IwDzmV߹(,^:SG \B:_3 Sx ͅeNe'G\a뱼zrN>w!a0D!(k3RY04=m(egq4]gv?K3H Id2pb`C MC#TEd0-d4j:n'{[Hn=A7L$I`e ~6t0LUt&bs|J 0LI#*?ݷW>CmfM1T7IH:v\y ]& CR5Lw6)頨J'CtCG%i4BUTwM O99ܻ3dq UscA7(Tꜟ< jS,UxO}!Gd'A@T$,+]~?b4:i^`wׯ +טN=ڝ.c,ûHre\q49z7#8!_d<$}2NUDACGiS1>qvvL$hZV(:7&=J,qAq!K2KWxpxcj} rRrub,,l0x#&QJTl'{oAT!>nM8fP,bn͇ tۇhf"qPEM30,n!aLL 1N80DaBU "lIV@!NNMȲ+[ $EFQ qvO )$ S)٬|/N MUyW0 ]XXXd<Q*yo5g<'1յƥKlonp/<ͭM*Ɉ^Im ÀCE}J)gg, ۶X^Y,DIb2svzGSr(mx֭M]F>|AX˼;,,-p0@4~'ͯSTP5Ql#w\~8q'!, K=Zgg|82%IFCH J؉hhJP@UTTMRMWz#ŗn2K_DӓSD!="QmI7gc2#Iy="W}ٹY K|ᇜHT4%1T0$A.I")t;=\w3;7at=%F3ܽsTbtT*U2Y^}1\ZSNOyO|iO_e1n;:kk]llUaMMy[Ǖ+Qh(Pd0 y!z[~s7eye硩2oG%_An,,l\Dw,Ir'c.n17d8k(AFx~!9=9\hAӲ$ͧY\Z&8/uHc /2lomC&^nC{ضZ{= Hy^dlEL?xdqaZ)|8 =,AE>|ͧO(ILaem/lbWQ AʾATDF.aS+4 fQHP2;;JΞf!?v""mqBY(Zxވ\Mk.bZNda" FcQ(g7z}ʠ,p8@ (d{x bnQ,X^L2CkF$DQdW8?o/y5RjK5 NE| y˜{DQxRs!m>2_밼z~CLIuTgP,hZ4fgux9%V1aptx!'J%$?1upe(pkW^2 ڭc+yӟ'"<ݷM\c:;$f8ô!Q3  ( E IQ% p<~sK82a/1KѤ23i(if8;=wƕ|篾2A0%WPo^b2곿C` [OurdSIj*k>b9<ܧTX@Q!zzzyj(FBͲmx<€ˬ^▚OvK2A0%FYUL?qJdsy$P˗ۈtnJ*R bedsy<`vnCLD,3i)rMAdK<~x3gg1G/ߣ?pxÃ1a}T5BK3FCv~F>+} DdfEn K)9 4SL ^&zFs-F.$Bb:~HW0-\o1;;$S]q]Ӷ_T*yn8N qkj˫k6f 8r !`l&$P*>~CunK/B1ud pnчq3)/]&cLcMS334)WjU0X,_/t:ܸ70L onBB$D7 Ad;h#+2ՙ:nD%&z 䋥r/hQ8M#egf}KMGtEqzrbV=DA4mXXXi3$JdQ3h")*N$If9??CUuV.?YV0 1P03@%3~fWuÃ=$Y 2t|Ս42ROYөΣXm /)\)^ 8>:f5vw!v%].<,EסP,ŶM%tSbٙs]C5(Xs;ĴlsJaa&=d33m3rH(AH%x1v6bTg-"b#ul݋*Da d0ɗ{.Q0Ŵr7IUlHK7PFh{B%\upw$vl>຃/8<}Un&ө$˔kSa>_ƽ;TUc&^( צs~Nh,pAT:weK׮i~̠wNXVsޢ\k8s~(iprʓ9='cIvU!'2Xpg$~{'!h$1("VEՀS gMf8cw7I7NJ\*fN >#?:8Nu{bnnZJR?DtmZg.ik霓14-ZifuHW?KWAJ'IOX0Bu}TUӳ3tà2 9o G&a'8J2$cl48990MVV, QyhdsDQ~t tOi :6~4N)E)4`x4foB<|إK+kjiI}B7 66HHhS.WS͙ "# B<|He0B*DYQ o2!J"MZ3^~U)_O/ "EanITw$N䵺LbAUT“ޘg8P*yK "=`wY9J||s}h!8_:7|u}?1߹ !vUzÇXt\Dݢj1iZ_~ ]P\~"+T*fgw9?yv'^5vwz|[`מZA׳ضIۆ$ag{rHFV/>,sxO}X^YTnTZ98 $!I>EXf<6.?Cc~mdIfo<5{=ʥ ' DQiqvv°guFU(0 \7!`|O~gi,>[O4¦&C'HĂ)N{7icU 0/]L&cJ [VT&$"2!2{;)h~a8膎4:y ÐxLiULBU5NOyfvI2р`1X KhU7C>B}EBE4tӢS,Te|LLQ4 o9i4999\FuƓ /!L&X6f]:ð,2zc۷uS..Q0d4qr%w9wuα3a^bB2C>_""q "iy2,qИ\P*\X"Evv6YYD&G%ItZiWhiӰѵf꤉#DQBJ$1xUQҭ?aO<ڭ3\wHwd<"2Y %ӲN}4դ~y!lL& _$IOzie)+=tFQT|on2\e1 zn? >3M|zBn01]|/It}:," !__N1 s+}zD>l˫0AH8V֯DׯȐK W(c1."E=$Ip&{[JU 3$*|+}TB*ӽ}s0(reR8"EKZ^jkkٲ@a;sݷoP{s x}>b y kG OoA ڭ6BǞx/k cOju PJL2buyk_[AS,M0 IDAT;X^^$OA\6#Fk+ Y%& У'>iG}(!ūLVchh|.K\hBuSصx Gb7"I6H8}ExQ77Ua66h5UݮI/srJ&gDt:yr7TʌO/4fgOdckBDXp+T+UdI6HZR.1>9 }R.pXףQ>:"H 6kU2GiϞq 5cֆAHp([AUUʥ"\|*h/lX_#"088BV YMC@\6XZ@Nh`*WxE2)r=F1tP:ãc֛8u.hAt{X_eqÏAfv;j 54Lgxt(i6noSi4>wb>"0>1':4M:&g EjZBR2 6;n39=@<X%+\O2C^y:f~10VwP6/n6V[ʛ(^ R@0Qo[ |\/`]"8-U%>"#d #Oܝ@0?wn^!14L(<`^&&Gd\DaX,ѻ:@㧸w04r/`J$ pc'JP8!\셋tCcH6;7o'st`E6זwt:HVeZe&q;nxѧ0ʬ./ji[ԉ~t,̧~ Q|.cvVޜ$3?EZM~ũ*".?_Fu6VZ*ŊwY?gJ-rvY{ `&'2dog?4e0pIik.13{`htb5.j7M('%/ӊ&Z[63ri,eB.{w}i6( NéN$Sc؝.2'\C}ZDR86&BMWon3<63m126M`pak+s+{Qz7lwb)]NƧOD7X[O~1nMY]*!ZrMb_k1hP5bhBE5t‘8S'֪8.0HG^!{tGN r\z[!ޥP)ɤ4u+D4F&K0^_CUUi /͛7ᥗ^ɓ+\vAZDn߾ESUq9]f ]@`wo1 ̈NAX,<%=~_|FI*_8 vv4 ţ f`~~Um|t:MKmv7?7urM `oo}iVbVX/6UN'+,/6|[֭ @U67L0HH$ׯt999F=^)SUy;]nӃusTU\yE: E0*YMôoe?[@6kn6Lˋs6|3ϲ*OiʥBJ[7Ѵ׮_eppU60zw_jXUmj8NsÉVT*( |˿H$178?k>-JC)?cVI1BUUDA`ss|LNͰڏ+ݸvմ q$7o\%2:6N>cddjr"++KLMM#GxG/R7X^ZbeeQwnQUD~,v:B,`:r,}H_B[ ]G7tV?=:Zdjfk܊b}}j ذoj(󬯯V[lom219 d3V<,/ߧn@]׹jվ7^z,_`05=FG͍`"Z,t1 fAoam(wqFC Et]GD6YC^c}uEɤ ܛcN\3j;&Ifo@0W"q f?@բGIK \Sg.H1A7`og& G7bwk7xb>[WXC׻hZ`J|ooW2bu>` }ϿZ%Z@QVyr,|[7vs߾K!"VNGC2@BD VhŇp2G,˸=,-I br8ˋݎcr8 whFF'P C7X[YDllmsttHDVei.Pׯ/L,@UU&fYYXCmv|k_fqn%ˀ 0 Zjx"L'C\Μ{M2(MV!52F"1 `lb/>aw242>BnsǸwN&VbΕ?/H3/U^h,N(>va.>cZx,4v 7Mc(F(auob!_׏rccu U8:ZMa:Vv[!Z,MPo@0'N_ IshR)]xCQq=LΜ[ )Udn6L4awrO~3:xr16+q+l0d;rjri6זL _Y[YnwMhB7 2{sbMnGvE 66p<Ԫw?O[U:fg si~~OZ$s:a Zw\k lV&Lj$#nvX_pIVS.,tyyu ,'<@ʫ/0L|jb>/~uffϠZܻ{,nk/[l. dǦ>~} [ˈ鷨 h6k+Puʅ#l?~ET+%}Kc]-H6IcDlv'VD\_FmЍ@B4T#n_auqVK`;h4j6s@Ygi.4Hbp|YC\yEfJN6{%si5V+cӧo=g_kxEL8iwx;_~iʖEQGT68?&TF&YVD`[PޜK2*rr`Ec-+/~ U&;w^Zwy,޻g~qaUV`T?nC^gmm9Jo6o6.\41})KKKLOO A^x3(WKzÃ]ʣiI&St4 5wg4{AV}ZY'9%Gco@ 2cCT\6C:}sI"Y:}p$B(>I0 :\7*vqgDn' 2~]ru<㌍MP*fAz~_30@ȜTJ&Gu7Y[]'N2Gŋ񱷻շԙs $޵] eRBxWSOpj8 ʥ׮̱8NjÉhp=ls[<}ne^٬yG/2;{UUT~l~cI~M6{bZ%`]Μxx㵗 HQ&RL|&6Q. +w4=M]_RKB!,V$1<2u5g]4gEz_o`lTՌFz?lo={M͆T:+mNOIĀwd+nzÏÃ}+r5'p8]1lG78 -A8}S#Z,(@vb6㵗_7 Z&VfIY<4yI񸆉m&g9e}>~ĝ:ajvZBRFwQՊ$YM7~hpQNk?D׻QUi39u wn\a #8*aA9}aM٧>GX\.RU,;[A٤\.r'K<^Cc҃+Ct00t6Mvo ͦJgL30t&q:jSESa|8o'O0 ٨jUG'f|b`lDbIn\}ǩV+i:Zz(dZ-^o1slۃӨQ[*w\~)$Ɋ㧭6@0rrxx!ddP$AWkv\[`ht?_,rCVj<yS?F@׏,˦ &_s?!j:mA\ѨѨUh4U<W^>gΞXvKl ;O}TE.7BEQJ/SOqMy9(HT*e}97i6[keTE6X2Ox&|5ڭ3Z lw2w-SCaN?#0s<;_#YDт7rR)fHxj1 ]>t*(d1|UzZ#&N`؝nڭ&`no#N rlwSQ,GI"$͎"94J`%!~q>Wӣ͡ ڢgAk't9vDPp4<&rtx=H^ 2g/nRUd2IGB!.\?q{1Ѩ=Klf',>>)t칳,-.Q+#"=y|>/b˗d2~Rǫ`e5%iVܻ"nŋ&@O:r`p$bNWT*rYzG{b[oɯoSp$BW BuVWm8 GP@ 2`   8<8ʕUN'LPi$llmnt:yϰE0(Wtu]j۷os5ڭ69t8 G9v8ׯc?IZe ` tjEe&&>aKK ܼyDK%FG#\Cη8y'loj*Z=FFG`2=&Ξ`~N8QJ"J1ntW_Kp8̸馩6#!Z,iD,~o ZD?" T~1`G}H8L "6`/b` 71*>E7_OGX@JΊH^)ׯw .\zA fcm@0L0~x4uRca!@E,ǎpE!wlxXK ,-ͳʧ9ƧNP9:<$8&XVCT+%D*A~5.nwMDI Er9yaR/bMRˁí}?A0㕃#T+%"+?:|jtq3 HjR*PnHTjQtIgϒaڐ}v'Kܸ~_  =g` w-8,ݟCia*}0nrK/~@""A9w4je=XlNW6:vy2G< D'Ho4~JN!_q96HZDդT*S.Il!D>/LJKRoԱZDlvB?a9ZT%S'?$ɨjFA:O)e~+(x`?9Fbp EfBTppG0eei$CkkZ ,vCӤZH4䩳x}A /lTHNpܯ";x|!, =@D?X*׿S|r@86&M#ZEdL`,޻׫{etG;*9<_ݮ`jAQڭKngLzcd{m/@7)Hdj@riQTd%r4nh,F $IB IDAT񲿗EQćjUv(;+QDBaxdjH(:H (S̥ ̾/%9I7Y0vr!&H߇Z. I6b!8]bY&yg;FMӰXl Mo`*$.ŌBn.Y]mN>0lGd۴5X<_%0:qz55nШ׈ShmщELB{[ ?$˛PAR$)fN>Qz?$S#aeˋ({8'ĵ_g>$ `{.zW5hԊ M/S.qY"S,YXet]ܹyE?a`pNM6\}Eb>cV0 ,DTTR[iе:Ҩ2{ݻrtyM8G߉ &^pz9jۇ# r2I68| NmXVOy@D|ags9Aٳ(K 6Z%2$\uUpo&65Q NL&Cj0EZr/3gltfN"(}HG|#ݮ9Jv,ns8N( H% uA'Od8uAFX__' %ZDS\0͖$Iܻ7a͍ ҇yFFFq:]t:-½y8ceeT*{zP d( s #?J:}dna, yxiZ˗LNwH $Y^YboIp]vct*1>1( x~j2hl|]TAQN9OVCx #zN`:jAǓX,>?n0{44u.^_/316F8Ň8P)O'02:QRۭ099 Cܹuxbf#Ϫn.t0wD".ZsB!fff糌N"ZK$:@\B`jHtm<^pf,b )6W' ۝X% A6AEj2ze;(H qK$:'apQ 0woaD)Z&Bg/38ܹKh *eDl+MbpŊ"b}m [AJ SZ*eFƧ-7f^@UgHR\.7}ݝM06D2)FƦTJTJE;LN =x}>첝?gyq![d2d@2!JV&5<1'jyfahdAX'}Kjxxbb-|^(!52"Q6h5a/VՔ̂1:Ynԫ4U\>5lBł",@@ M."IjI$'Gv&I(ȈYn4H'q:z=@ L,>H4$;X?g|ZMZjlc|rSĉs&x&P.V|bnUcw8C $RH`8g 8uۨי:~ ݮ*EK % "$N \g.>JoX$6q]$S\P Yr> L126{sLN$(K(jXzHl!"fOͿ2ljDxY+Gc&[k-t]4@Gd E\|K,Z$|@8"I6:z7h3Gx BQ*#J%NM= zӥP)m2 j Mv ? |lUm N SRMҬVGY]]c΍64:m,+o"XYV"Zq< =zj-n??&JGA{?Dx|LN"Z.q bX"I Q ahk`PxJH&Oid!6חds9Nշ^RE(3X`H|Vo|)NZV-M Y$,V-wG>di $vl,۱;\x|aD`g E0C7z;ys $i*L^"k/}hjqnM֖qKy.7~28dl8㋠dr lj xÌMѨJdw8va\n"nq ԫe=b1V HNu;J2́B;M}72i0P)VC[ul nf&,Zl"x. ,t[$X0hGtӍnp8jAdyX-y@ סݬtft[ԫyC܊]R)Npy!80OFDzh4p8.RW_ennX)K zvu|P(ng0bhxÛoW`l=wX,bPTUk(VB\~LO(;P(bb4gghhH4y$,yЃjԔit9) Z-,f93*>RѴEP ?Y Y]]>ٳbg|lٓĢ1Ovc<pY?]v@>awpxB"3!ǧt;b$l6x"]!\JjJ=FGhZ<f>fcz8PUUDt%,+rÃVVTʄQZjoUh5:}.'p8E4_N'f 9`ۘ9~ÉjG4*"yʛz=Ϟ{C!z=fKnBX`kc|.[&@7 " J"jjQvIZ啗~H?Ii6r9$"Y<sLc.%Ĺv+ܹu˫ǝx$C4[MGGv|~?v WdVHp8iM/gscP8$Yh5ZjVKeoor?"5DYb),.EMۺlwhG pxG.{zWTh-Q$;B" "n "` 8nxr+<;[[t.{{MC7 ҇d.>ܧѨa $A3ΜD a)$D͍MO Eq. CP!}bq&@k XI/ E0leSj{=CG5\.^ӎhHF6>݉˭#>(O8=z˛:\*d;SaTp82Q& +cUzs,޻d-j)B$@Y6oK"#B>B0rn(sl"I6F'CCA0&`Mbbb&P)2nopt/%s<؝.fO]k?I1ƥ(ēôZ*VR.tre 13lzF(&nwr%RC'IOa],;h6$'vytC@; ѹŅvgBԤRӻ疥ѬS}7'M"L}.FXs=(Z1_3VKrYZ6ݮKRw AޓYY=vggH"AGBq q;Dg]bx!H,.]ozzڻ.o|VfeCF>7}n|^WZ)vY.S,|Gpat1b@!| Upعxi,V ۏU1Mn]{NI^c.%$I76S)4xpF lⶲ~\'GdWSd9{2`%nL8 Ew9q98^]Өq: d`OűZmc:1Ē"ZUbszX$sE6hjX0Vu۴%4}v/g?DFTO"';V:Ӫw= <(2TP_0BRpS)Lt7dE%f+t~H.5dc:e,S*&gkcudp{tfs>(cw;}4 vr1OdKhjHxX("S! 1/L&ɁYP=>^ J0%%BxHT@dU:<ڸMtD_ipyȮ<;Ho&>nS*|,fq8(j5@&R|h믙7Ys Cn@ 3i6$X}A.]Gemnt:&qG9-2i[mDQ@h)fhR899A4&YBj5Ο?!jLzXqe0>}2ag{FA ` 3g믳!D7 FLsI$򷩔\GGf~p8ĘLt泋BY@17$1NyKM3jT` H${vZJAPa` ;ܸA01&h(\|&#vAf}%g.ݐ[D12Y99 ǟKgBTJ!/ ǨK ic 9Z:ۏQt6xLQgqq@0.XERܼ.cU5$crLJLyjjg4 2+Ԫ%QDipܸ\,Vf d=Y*M$^_$gJIb0HgSgPcLJ 7`.ZIj5.,19' + >>F!?dVb $ :BXDQr6- F> 01 #oLMH߼U Ƴw d0!$l\pȕϘ`$?a:5_\!Oa0TgVnj@ S_ZrHd896}bm#*&3AUu ^GPAG(n3B:Z! DhQUHtp$*T]( ;&HWV@h phbS"^D?~~D`LL@0F9VkQz}XUOk :fU*{ {75nI!̞d48=͑,r Gb!&NVaX1?R9ɔ$l' 05vҢmTkZ`%}J"J;붘foNi5u} CQ|n Jtx)M:mBx;@h _fj::fwd{m]WXe;`38ۤS;\Xm.:Z(Ud;XFX!pk35bzpScJQ `EM1'*>NXV$N2LjA#8]^^vUi*mmܾ^Ѡ?|q\`Lq\&0HϜdM3 7L9WXSU A\0J!KҬa`٘+gVpr7n`00 xy}\nlv;$)tE$Ϝ2 b* \S8l|^Ӟ i5]_g84Eu''9AvϬ#FrʣGF#:ŔZ%YZ^zg*67)JlmoqtO"0wJE0`kk2h<̄vF͆6֨ժlorm#n߻Fj޾ Z-t{f4u P*q8;ϠprK\)lD`Xp=6/I%ŤrD+M񲸴/15> ڹ $):ncjG'XDszʗD"I,$ p8ʅKOlF*$:&ivvZl4u4f3EgOrgD"_nJHYCKȒD1lvg_ @&f/tl@^0p4ZB hK YY]+ fϝ&ڄ=]q|p@("07=wof.2L(^O$99:tHeHœu<o~3NsgVneݾyWMȲ)x>ݹM؜>U&O>QVs:y.`d*ó~P8 4U.\| s#0e"np̥f{T+E(YUi5UK32~n IDAT? $|',-2g.T*7sDcIDQ$."Y-q<^?A7{Rvܻw/0 Y^= vD3kp<.^~?f8Z^6Xe4zlNԱIS?("nI| ϲX,x=>v=U17" ijEQDLt3hd0t:q-|ɹy4uat9I^/."6D"OTB'!n^ݭ ‘8nVNץ\*R)gٵ#qA>"f3v;Va' zVh4C5n@ v;ѐ{w789!ZD ,qaaiKgp*ϡO&T+60 (xz|H-x|^&HU97lc4ܸ0D1 ˤ|xIg<'GoΛ5{wk*+eu4`e'Db BVIUj*UZ)]Xri:b@(N(  c.VR!khb*Dz@0ϾB y[B1a⊉d\ny)&z}d2f00wpd>f`w)NQ:-S.W>hDa(p{\SѨ`إV>Qi+!w8{4>jҙ,Vz@ץ$N<O.#uBL*Ӯfx|hp$NnM8:+h*urGaCbni.^^c^X`86P"p-V.BbiXw|,.K͓YXrB4X&pCg=W/tm;=}!dN8 F*NH,A4$Y&Ips]"O\kcJ{vVI Nv~U pz2tJ!weRI*djDh5dNռw6hyww06&-k4>5LPdb5^E&LJlPZU:T8f:ųQ:J 0PMߠj Ie\.GbR:.[?X8Ee |>~~a ׯ"D67zi1V.P:=ѨXKd(-'|p N09{{,-2tv6Xeݬa%0;snxAݣt:EQdW Ȓ8>e9ۢZ)"dnxZHUp:ܾ"MpdWAyÁ(-1 ~[=i5^?_ }.\\fg' <ڼdb?/ݷ^Gdh4j45|@Y]XZ.PwI=촑IE cu@*e3o5]coDb~M<1Ǡ#ШEA3 qo^?DGQ*[R.NNwHb'w/%57ϠcQvYQ#}p,.:#/|Ҥ#hLJ[n"Lq{YXҪ7X%FDIz9ߚ{X,&W5Z*poPMЂ(k*N _ x<&=̙5 hN!6J,e=,G$3+DfRAQh4<O0epy|HV~3-x[NװZ%JN~:U$D(MZͪ ID@D3tc>ͷ8ۂ=4Mۮs³$s  AE0vw[tm|^15x A9\h ;x! JÎajcpݛo X ,yRo(IӢV+0d73 $w`1{fnʙutaIb9n/PdqQUQ1JB@,:WT05 $+J^Vn%; Xp8"i\X>lY-0w,".Kk12pTf"jٸ?z CJn{ׯ3 P*XnbX,'|ܽs]X^Y&H )Jx<^|E>STmD13g6j4 t2RU.]15x!R YV* JGQ7p>͇G /rb%NSU%@p)ZuS*p:4Mt4M8ă6Fo"*vwwY_;GZ%Ni5Dc1^t:2,kW+._fii Ϥ+E/֌;XqmNt2JE"qcsݔJenh!Ί&29+;ݝnܸ'?d!bqimS‘(t;]y94M0RGC!ϝX8dYT,no]$S)2 fQ{0jVT8~tfR_Vp8h0 f99>舳k `A+sx~nܧV5#N6tD"Qja=S_| 8N\0~E25GZA~O4*>o7KSUyF# y GXg mmn088"Z8wk:`+O=g6Emfj%55QK9G|#*ak'$(Yfba8S*d)Ov}zu46a ^?8n|9.4ORV(&rH%.F0BZT, ajp|7ltMQ>rxG0r( ~@04[j]Nz./ΦHflR*H/P*2VU:7fný7IgظO>XU* ^_#N'`Nx]v8ѐO̧q:TeG^`~<9~}jSzLYL ,+6fu( *G!cN7Tcb~QZD"qnߺjQ [uAGƌ#v6z y:J"adwn |%.7'c4}iQ(U` K(QSLp}<4bjD v @F:=h9{j ]QG|/Q*䙥,Q1v~3=oF1gDg~Au4b`J%YղyX2Q0(x8>ئ^+38nDт\S.zSYe(JXŜxm3`CmDYZ: #ēivrjD#Y$4Q<9f.hRʬ_xD1 LL*QZd4W*tmtM7߻0N?R* |/>E,9O*J\^^0QRILh1Sg99ܤL/0 z]ά] t9(mܞ6{L( nK0R.q&0tm#6ns|aLLF*v;Al71VUW16bwp#^*Izp8-TU},h6k5FBGZs-:MN RTzR񔽝M"8cM3v7VC#2TJ9vn2 mlv7''ZLYeFKyv$_ըUZ躆AQ g2@ 3wm:l_2;e.mf `4G8"Kr{r(R }j H$ZƝ7ih:Y*KH̏xtJ.(,-nR*8::D(\.^RH4O25G00not!I2}B(yXI>1 /^nwr.^FxZh4,ܸW>ˣ >I3 Y,$9Cnbi45:ݎ?埧nTv;:B4>^C6;]NrrMV2/0 o) c-Tei1`izʿ'JD*eUd2|[`*xzd"08͛QR8͡:nZJۡި|@HEUi44[ _i[7oJXp ic lsv{df^4v9<=:ΣSC^R.G_<'S,X"Iv~,N&MhjL4яizj5EHWVEUU8cL!DcGdg{`izݜfhONp(6w~'h>C<^トT+0k.M6;X}s '@^eohAtp\M\.]XBDv~i6N(-$YF D$IbEwH20 "(p8(z}VVh!Jn"MC߻I0h"8Na~?wϑ;ڡi,$rT%Rsnv;S:HUGd,V >FAק^; DUpJ_o}CA9>إQ/(Vi{P.PUP!nbbwY^Yj689cȲ?uz.-,s(&"Aa9޻EUcܹ㱊EPV F樖LtzNj.Jms*J%9&x2ptO$`pWwPZ ?H0ʃ{7p}<{|gN ܻA't K+ZMk>hПM[bo{FwxTENsG& &g0{5fX6eg.R*P{cloS+wh^n/hcDj`;4ZD$$^ ]R:%^YoQ,䨖Nyq{ތuڌ* +kdVYXYG)% w0fs8x1vfw07HgٜTJyрvhKfxJ.oJNQtz` ^'?`MpL8@X4m={UsM6L`$FVe<rjTœ,V0Nl5XXZADx jًW.ESMJ%/]w>9>jnXlm=lb(x<& Ia ØJP 0'vl?daa^2|n50u_X@4]zf IDAT;4[M^Kܸ~)l6Qm4MY4MJVC8G|W~~}~;hN(n?kq=: VIB@0 c̦mX,O*jJVEƚ6Dי<O= irk5ˍk<3[$q)H5oDg9_d2vt:>ﳸD"aZ'Ƅ|.d2a03)NI36$YDӤ>C~?/|=aZ-j'uY_?ϗ?_|ߘE|>HD.#c16#? ;Dc1~v2 b=Sʥ"Ԝ)5k͆OfRbH4jxV,+/]Fd$O5?#BaygQ^"v"j۟#C:J3kan._oF HBa`d}~~Dœ4Itݴ[%?7~|.G٠h̀#9>pfuO_?v{tNKN18?Vh8$ bXE Nd0)(JیCȲd2Au6<~K$)$I6z\:C$bp:_ۿ0-ƣf!FfA6t .فcm>|k_ӟ,ssic#3z&񇉮{;Cs_*ɄJ<@쩎TJS2Qld22.@ 0Xn!"N:E/^UܿC/S.uz1[sFbd2 YZ%S8nTuh8V-forY¡ *O{ =_kwJ/4ʑ<*JZw[=x6xl;,6N\ IHb2>1 [ ,HN\ۀ1cx}VvKu?<ɗr9]]]}}"gF0V|^v. OvN=ŹYr+a'C<9ƞ}7'y#07s0PnfD]/^͏lS{XxB!_٨10 TŮ׿4?4K1"6,JOp9ph,p{ME*cW|_Wn6s9h:S:n4X%CX+NlXv~}7ތs8˦9  6vY[fnYn/kKl(VJH Q46Uu:3CuF?᳜8.RqJ?y Np;X,L_p# 27{T: gpUn7g3ct=:mSuz0XZ)bp8=c` I~"I,`+x~a`]1c ?ÃORFD?rx;yh\JOX}EuHę ɎO?(t:WӈǓJEDA$j6xt2`F~<ͯ oyM0mScfH``&ۿOO2tR. t$#o>d`( ˶{ip"I[u|γ&kOmVWW1Z4h&ʗ?Y^ v;fðl[f*+ s`@f,[0p5N'})yt6fe_S)EX"IhD)r+v4[z'"MJR66z |b`m8%DANWi:_MF h~u_~6hdjnfjZ6׋wC:jDCDJo}X6T4c}n'?n3]ণlv'?A x-S.ct,jEo>x#wbgj htzB/s06:rQpu/`X)Nʅ ν<k\ 17s}oe/򨪊7A%ZMs_7KC $U6xE槯0?C)Z.ſ8VY&0yQN[q1 DX#I\?I9y NkXHVgϜO>c{xc;.q.Zxq~D*Cn͵E秘JRlu?B:NA򦛑e+{ayijuS-)\"o&36ۣaW] +g|"1 ZmXEj9ʲY<M6;ɛU*i(}鱝ëm2$c~t("ZDDz(*7_ 9r&66AhZ8Ny!nxd@(ZO&X\Z+|cr8U0/xjBVEJ8.Ƴ4++d2ҙqtnf|6]BVinZ 슊ΌHEK.ӧ)soǮ8p:.vŁ1i^Zm%( Q kkDA @noJ]oRh^/Z׃$YT[y\*t{4SSLgع{/a @^6]p]wc)x^>?P6űlLɩStb,'\p  ݶ^]Qc Es;,&TH4CH`zEŅ9o|wHej?A^ t;] ϴIlOnDRcrb!L!`mlv, ݴTnz@쬮pڝ6Ji#ɲQ#T*EXC)NjNCEed nx"Q>vϏy:F\ Ev:[N܆ǫ?|>oKX]Y>L%Z%ӱjC@ feec0XlY\Gu8DlmsÁ#HAu[-Qtԫ5$L87t E1 'C Zkf1'vE e:_RďL.38^\g_:=z3Gߎ"2yTUV>M9CuM$l0?gv4zj%=4-dIfmeh,13mjM "-Q*P*R^-MncKf|,yM CGom  )Ξydz}S$ [$RY67ayilv9)E̋m|w(Zz@5.BL@tyrQٱs9ntZ$+jAE? LR.t"f蟦i`@<"Eq5DтpOy7><̿R'zdzP3|V]wa67֘FQqZ Mvʭ B[( 7 :wjaوF)'7쎽4uS';~ï9]q?D1+/u4bAm4[m6VKhRV?njS= ]z}~] P)Y^R.q1pH<ɧä)>"Y ` _lnVCu!ji\n͍5Ѱ \^z]3 'h6jxaA I..-DSll(7dDcrk4ke(B\9>SEDc7Ze>.ź?̠!NPըUci&S'3 ^0dJA%AQnsr0 DH tz]~_-o{Tf;aPvZXj䙧H㎻E?8 )+ k>Y0"t;M‘Nu\.׮\AhԉwЫTksh0뫫U榱|8NN-wĜN VwUxlvٮR-mh" `Hj,cXYǮ:zi{.hwE:6׮ DNf0lb% {M@0 'y$0MFYCDT%6֖Lǁ#bS\ ztըQU f.p/<<|yns͞gyaFS* 'Q)mph5\U4]TM6;N]d7(7v(v315j&˦TdNǴApz<oH"Cv6-"`O?`H~s@ #O$emʥ2R~WX7EW%;338]N&&&[D0B@Z3%7kfʊ4"wi X[]5=P8̎]mBan$O( / ,.sPVVl۾N:TkUj:R?@*F|x5EV 0v $ah^N2LkS`we AUT$mn|f>tmjHcx<|>pӁ zc8(zbqxlv\P(D0cx<]N\v͆NZϣ:ZY11tS^nT*!2a`g Ao\|z^.f{ey P(Lդ뱲 H-k+\zkj%H8 c@Sq:XDEQdw`Xb 3CHZB$0w"qv;^ Eb{H4I**8N0j`0JCdi5X,LRBW b־٠鰼4O>#v ~z4dZnv"HJ[ 0?c 7t|>?J)<ϼ֡eCdƷp0h5umwDZ!NZ*$3YNv7ozp{=lv;~CGHg-{!@>EncU E0(6[䷶8ڻV6R2srN͙~46|; 0;u,-ph6x~Ba*Z. uf.vbBqRٝēf,^B!?a GرXf.Ջ9)7~? cqB8nBnli ,PF(DEnA20$wm'\\fW4Û]X^Z5UDV`lW^9KZ6͑&Stg,XbVT,pi6tiU$Q.nP봚5Rq4_n4-]<{[9{rD+N lkՅL S(/ˋSlͱjEQ]j P1 t:r9N>SO=K/4t1bĈ#F1bĈՊgrrx'Nȫ]rłn|f}=#F1bĈ#FTHvcH V)z(1bĈ#F1bĈW &&&Ȳ|\UUtصkVt1bĈ#F1bĈA~RiX5,@Ä`@mfCtcĈ#F1bĈm, Vq]%Yq=DՀ#F1bĈ#Fj$i+{2bĈ#F1bĈeIPYrIENDB`ocaml-rope-0.6/web/summary.png000066400000000000000000000101671320376225700164110ustar00rootroot00000000000000PNG  IHDRuksRGBbKGD pHYs.#.#x?vtIME  9k_IDAThieUug^ ( FP\b,ąJdX!mP56h$F24( htWuWo9'B OUOι}}b,f1Ybk_\gq|κB0yV L*kDJ9go^7#Y+Z:7FgNѹԖ}w5sGo5 t]^jPjҲk7e.@"e`mΙ?62ӟ[޺}U۷i ޗd?ZOrwjzDMۅuqV.w+Vh5 ʕ˷mX]y5L. R\3>&_~KYCEE-n'-U} Rw)m?$=u֦?C[;vbھ2^xT8L*`+ JDSyLl0&᪉VŎk-18n3"(FưADchPT{N;sϡE I}kJv2ӘݝE|kbIJc =)M3úce|bNXיi-?n'mv{6ŀ2]g`8MyF$/ۖ㗿ỷZ ͽ"=yKQ`jλƨKo[[J.NzeoѺS?UWV*6H(U=?rL-ֈѥ/{9|M{QC8|@ /ԿwUo;.p-"=y$I:];;)-9ǶeQe5`ltC'pucS-Rd&%B0-Ia!< twJ'64&i~@Nؓa먦P3c>`ujyΨW}8~"U!R&&`q2.?X:'2LE38;GBL^ןW(j\%ҫ n}2N^ҡx ??ϝSRxBG6WVW{4bqהBd^<ս\9kW'>:>JfG,HxfxFkNL^ =Ozm5k 7 ~1+g3YH¤ۓ+>IENDB`