bench/0000755000175000017500000000000014151353665011474 5ustar nileshnileshbench/MD50000644000175000017500000000614014151353665012005 0ustar nileshnilesh2be19b38ed96231960dca40e48f5a651 *DESCRIPTION 37b3d1fa5ae9f97de7ac49d6af126d1d *LICENSE 038a94ec64f0685546ff6c03bf130771 *NAMESPACE 664306ba2ddc051b00a7bbe83387121b *NEWS.md 7be32a97ba64d1a18348854e6205b2bd *R/autoplot.R ea7992cf90bb4cb5f36512829f62c1d5 *R/bench-package.R 8d1adc5463ea824fcccd2441d3fb17da *R/bench_process_memory.R 1ee5f554612a36963a0ee6d2a65578cf *R/bench_time.R ba194d26b0344d7492987996d1dc2c49 *R/bytes.R 09e662a6a328315c2607353b70703426 *R/expression.R 77e68ec28bbdde8f4bedaff981f6309f *R/hires_time.R 51a94b8001e3229a6950b42643c9feb2 *R/load.R b9651048df9c3462f24817c7a06287c0 *R/mark.R e2dc4876f5d1fcc1181ed46736da219f *R/press.R baf1716216309e457a706920e387fb90 *R/time.R 0e4a58c2627bc70eb652525cae2ab66f *R/utils.R e1bbe29b95d0d57ed776b04826483acc *R/workout.R 7bb570528f275ab419ce0108456b63d8 *R/zzz.R 83380b1d3607784c0e6e3b9aed3a7150 *README.md 1fe3413a86a0c4c2906e7165a90f50c6 *inst/examples/exprs.R 7caab55fd54d91cb18f632e305601d2a *man/as_bench_mark.Rd 30db463e739be59bd8ab5f860369ba18 *man/as_bench_time.Rd 4691c983194c470647c4b23310bb6e04 *man/autoplot.bench_mark.Rd 553a1eb20c03497b65aba21ae05b62e8 *man/bench-package.Rd e00da89923ce7cb5acfefc0aebbee819 *man/bench_bytes.Rd b00acbed01dc7a2c5fa82573a44f2b5d *man/bench_bytes_trans.Rd 2de6749ef7f5d6c1c68734bbb4d81aae *man/bench_load_average.Rd c18918dfaaa633fea6f00f17b5b21053 *man/bench_memory.Rd eb5d32051b5c89408d76e90a81057f78 *man/bench_process_memory.Rd 2e18e19606c9c1c1d5ea5ccba1642005 *man/bench_time.Rd a8137bcbacc4e74c922c29ed701499cc *man/bench_time_trans.Rd b1a915af152c37bc96447534a48e94bf *man/figures/README-autoplot-1.png 14a106c98d808ee78b32ab21e6a2b699 *man/figures/README-custom-plot-1.png e8ffa8ffebc73b316e9cf2e1d1245939 *man/figures/README-pressure-1.png fdac20c9c4139609fea0f9e64981956c *man/hires_time.Rd b089ea2b51a2a9046b8c9e2a57a894ad *man/knit_print.bench_mark.Rd f760a978aed96e0d1e9d5e0dba3fa758 *man/mark.Rd caa5b8ea96c93c2db3c412122018741f *man/press.Rd 082d65d3d1143fa4b2cf8dbdf685301a *man/scale_bench_expr.Rd 2a72e434c9dc761c8c989a2a06c1ecba *man/scale_bench_time.Rd 3eb90d05fc5b5e05b18edf3756ad9174 *man/summary.bench_mark.Rd f025b1dfc97f6c46ad65f26e0f7f05b4 *man/workout.Rd aca489119fcc0bf9b67ba6d99e10f7f2 *src/Makevars.win ea08595d2801262b68e67e830f629ef7 *src/load.c fe7486b920834d6e38e0928fa213f19f *src/mark.c b38c6c7a859e6b2cc4db88256ace6785 *src/nanotime.c 2a199e99c4051ee86d32d73561f58d22 *src/nanotime.h bea51994f8a5ce146b207a8ee1339bd8 *src/os.h 6516b3bad34f1af7733fe49e2334d6a8 *src/process_memory.c d8db634775f53a1a9e93912e01b798db *tests/testthat.R a0a974655bfb7e72da63bce70ae07d96 *tests/testthat/test-bench_process_memory.R 11868a9ec8d55f835eeb90752c85d5e3 *tests/testthat/test-bench_time.R a56252f27fa9e1a6962845380a098e7c *tests/testthat/test-bytes.R 0650fd0059de42081c725b3678ab6223 *tests/testthat/test-expression.R 0650ba599bd979b856d197b87eeb791a *tests/testthat/test-hires_time.R 8af2acddab88e9df85b0a54b48f73c60 *tests/testthat/test-mark.R c3ea6e9b56c6f9681754d8581fb47089 *tests/testthat/test-press.R 471464fac8c966eb4addd79450802e19 *tests/testthat/test-time.R a75b5a6a77f0e3121feaa6fc195964a8 *tests/testthat/test-workout.R bench/NEWS.md0000644000175000017500000000640414151175672012576 0ustar nileshnilesh# bench 1.1.2 * Davis Vaughan is now the maintainer. * `autoplot.bench_mark()` again supports factor levels for `expression`, as intended (#82) * `bench::mark()` and `bench::workout()` no longer support unquote and splice operators. This fixes inconsistencies in performance results with functions like `rlang::list2()` (#61). * bench has been re-licensed as MIT (#101). # bench 1.1.1 * `mark()` columns memory, result and mem_alloc columns are now always included, even if they are unused. # bench 1.1.0 ## New features * New `bench_process_memory()` function, to return the current and maximum memory used by the current process. This uses system functions to track memory, so can measure memory outside of R's GC heap. * New `workout_expressions()` function, a low-level function to workout a list of expressions, like those obtained via `parse()` from a file. * `mark()` gains a `memory` argument to control if it records memory allocations, set `memory = FALSE` to disable recording memory allocations, which can be helpful when trying to benchmark long pieces of code with many allocations (#62). ## Minor improvements and fixes * `mark()` now permits empty arguments, e.g. accidental trailing commas (#61). * `mark()` now errors correctly when the expressions deparsed length is different. * `bench_expr` objects now work better with the upcoming versions of tibble and vctrs (@romainfrancois, #64) * `autoplot.bench_mark()` provides a more informative error if the `ggbeeswarm` package is not installed (@coatless, #69). * Update documentation of `bench_mark` columns (@jdblischak, #67). # bench 1.0.4 * `bench_memory()` examples no longer fail if they are run with R that does not have memory profiling capability (#56). * `bench_expr` now has a class of `c("bench_expr", "list")` rather than `c("bench_expr", "expression")`, as it is really a list of calls rather than a true expression object. (https://github.com/r-lib/vctrs/issues/559) # bench 1.0.3 * `summary.bench_mark()` gains a `time_unit` argument, so you can report all times in a consistent scale if desired (#18, #26). * `bench_mark()` now checks for user interrupts, to allow you to stop benchmarking if it takes longer than you were expecting (#49). * New `bench_memory()` to capture just the memory allocated by an expression. * `bench_time()` is now an alias for `system_time()`. * `unnest.bench_mark()` is now compatible with the upcoming tidyr 1.0.0 (#48, #51) * New `hires_time()` allows you to explicitly capture high resolution time points. # bench 1.0.2 * `workout()` a new function which makes timing multiple expressions in turn simpler. * `mark()` now internally uses a tempfile rather than a textConnection, as the latter has a 100,000 character limit on some platforms (#27) * `mark()` no longer returns the mean or max values and the column order has been tweaked to try and put the most interesting columns first (#37) * Errors during evaluation now halt execution (#28, #43) * `scale_bench_time()` and `scale_bench_bytes()` now allow you to use a non-logarithmic scale. # bench 1.0.1 * Add support for macOS versions prior to 10.12 * Disable load sensitive tests on CRAN, to avoid failures # bench 1.0.0 * Added a `NEWS.md` file to track changes to the package. bench/DESCRIPTION0000644000175000017500000000216014151353665013201 0ustar nileshnileshPackage: bench Title: High Precision Timing of R Expressions Version: 1.1.2 Authors@R: c( person("Jim", "Hester", role = "aut"), person("Davis", "Vaughan", , "davis@rstudio.com", role = c("aut", "cre")), person("Drew", "Schmidt", role = "ctb", comment = "read_proc_file implementation") ) Description: Tools to accurately benchmark and analyze execution times for R expressions. License: MIT + file LICENSE URL: https://bench.r-lib.org/, https://github.com/r-lib/bench BugReports: https://github.com/r-lib/bench/issues Depends: R (>= 3.1) Imports: glue, methods, pillar, profmem, rlang (>= 0.2.0), stats, tibble (>= 3.0.1), utils Suggests: covr, dplyr, forcats, ggbeeswarm, ggplot2, ggridges, jsonlite, mockery, parallel, scales, testthat, tidyr (>= 0.8.1), vctrs, withr Encoding: UTF-8 RoxygenNote: 7.1.2 NeedsCompilation: yes Packaged: 2021-11-29 16:12:02 UTC; jhester Author: Jim Hester [aut], Davis Vaughan [aut, cre], Drew Schmidt [ctb] (read_proc_file implementation) Maintainer: Davis Vaughan Repository: CRAN Date/Publication: 2021-11-30 07:50:13 UTC bench/README.md0000644000175000017500000001662714151175656012771 0ustar nileshnilesh # bench [![R build status](https://github.com/r-lib/bench/workflows/R-CMD-check/badge.svg)](https://github.com/r-lib/bench) [![Coverage status](https://codecov.io/gh/r-lib/bench/branch/master/graph/badge.svg)](https://codecov.io/github/r-lib/bench?branch=master) [![CRAN status](https://www.r-pkg.org/badges/version/bench)](https://cran.r-project.org/package=bench) The goal of bench is to benchmark code, tracking execution time, memory allocations and garbage collections. ## Installation You can install the release version from [CRAN](https://cran.r-project.org/) with: ``` r install.packages("bench") ``` Or you can install the development version from [GitHub](https://github.com/) with: ``` r # install.packages("remotes") remotes::install_github("r-lib/bench") ``` ## Features `bench::mark()` is used to benchmark one or a series of expressions, we feel it has a number of advantages over [alternatives](#alternatives). - Always uses the highest precision APIs available for each operating system (often nanoseconds). - Tracks memory allocations for each expression. - Tracks the number and type of R garbage collections per expression iteration. - Verifies equality of expression results by default, to avoid accidentally benchmarking inequivalent code. - Has `bench::press()`, which allows you to easily perform and combine benchmarks across a large grid of values. - Uses adaptive stopping by default, running each expression for a set amount of time rather than for a specific number of iterations. - Expressions are run in batches and summary statistics are calculated after filtering out iterations with garbage collections. This allows you to isolate the performance and effects of garbage collection on running time (for more details see [Neal 2014](https://radfordneal.wordpress.com/2014/02/02/inaccurate-results-from-microbenchmark/)). The times and memory usage are returned as custom objects which have human readable formatting for display (e.g. `104ns`) and comparisons (e.g. `x$mem_alloc > "10MB"`). There is also full support for plotting with [ggplot2](http://ggplot2.tidyverse.org/) including custom scales and formatting. ## Usage ### `bench::mark()` Benchmarks can be run with `bench::mark()`, which takes one or more expressions to benchmark against each other. ``` r library(bench) set.seed(42) dat <- data.frame(x = runif(10000, 1, 1000), y=runif(10000, 1, 1000)) ``` `bench::mark()` will throw an error if the results are not equivalent, so you don’t accidentally benchmark inequivalent code. ``` r bench::mark( dat[dat$x > 500, ], dat[which(dat$x > 499), ], subset(dat, x > 500)) #> Error: Each result must equal the first result: #> `dat[dat$x > 500, ]` does not equal `dat[which(dat$x > 499), ]` ``` Results are easy to interpret, with human readable units. ``` r bnch <- bench::mark( dat[dat$x > 500, ], dat[which(dat$x > 500), ], subset(dat, x > 500)) bnch #> # A tibble: 3 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> #> 1 dat[dat$x > 500, ] 258µs 383µs 2543. 377KB 17.1 #> 2 dat[which(dat$x > 500), ] 204µs 260µs 3803. 260KB 17.7 #> 3 subset(dat, x > 500) 332µs 426µs 2318. 510KB 20.7 ``` By default the summary uses absolute measures, however relative results can be obtained by using `relative = TRUE` in your call to `bench::mark()` or calling `summary(relative = TRUE)` on the results. ``` r summary(bnch, relative = TRUE) #> # A tibble: 3 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> #> 1 dat[dat$x > 500, ] 1.26 1.47 1.10 1.45 1 #> 2 dat[which(dat$x > 500), ] 1 1 1.64 1 1.03 #> 3 subset(dat, x > 500) 1.63 1.64 1 1.96 1.21 ``` ### `bench::press()` `bench::press()` is used to run benchmarks against a grid of parameters. Provide setup and benchmarking code as a single unnamed argument then define sets of values as named arguments. The full combination of values will be expanded and the benchmarks are then *pressed* together in the result. This allows you to benchmark a set of expressions across a wide variety of input sizes, perform replications and other useful tasks. ``` r set.seed(42) create_df <- function(rows, cols) { as.data.frame(setNames( replicate(cols, runif(rows, 1, 100), simplify = FALSE), rep_len(c("x", letters), cols))) } results <- bench::press( rows = c(1000, 10000), cols = c(2, 10), { dat <- create_df(rows, cols) bench::mark( min_iterations = 100, bracket = dat[dat$x > 500, ], which = dat[which(dat$x > 500), ], subset = subset(dat, x > 500) ) } ) #> Running with: #> rows cols #> 1 1000 2 #> 2 10000 2 #> 3 1000 10 #> 4 10000 10 results #> # A tibble: 12 × 8 #> expression rows cols min median `itr/sec` mem_alloc `gc/sec` #> #> 1 bracket 1000 2 25.3µs 33µs 29008. 15.84KB 11.6 #> 2 which 1000 2 25.5µs 32.5µs 29813. 7.91KB 8.95 #> 3 subset 1000 2 45.8µs 56.7µs 17164. 27.7KB 8.66 #> 4 bracket 10000 2 45.6µs 60µs 16286. 156.46KB 56.1 #> 5 which 10000 2 40.4µs 42.9µs 20583. 78.23KB 31.7 #> 6 subset 10000 2 94.6µs 117.5µs 8158. 273.79KB 49.1 #> 7 bracket 1000 10 64.4µs 77µs 12601. 47.52KB 12.8 #> 8 which 1000 10 58.9µs 63.3µs 14338. 7.91KB 12.4 #> 9 subset 1000 10 85.1µs 104.7µs 8755. 59.38KB 10.7 #> 10 bracket 10000 10 128.9µs 144.6µs 6752. 469.4KB 70.3 #> 11 which 10000 10 89.8µs 97.3µs 9699. 78.23KB 14.8 #> 12 subset 10000 10 189.5µs 232.9µs 4180. 586.73KB 56.8 ``` ## Plotting `ggplot2::autoplot()` can be used to generate an informative default plot. This plot is colored by gc level (0, 1, or 2) and faceted by parameters (if any). By default it generates a [beeswarm](https://github.com/eclarke/ggbeeswarm#geom_quasirandom) plot, however you can also specify other plot types (`jitter`, `ridge`, `boxplot`, `violin`). See `?autoplot.bench_mark` for full details. ``` r ggplot2::autoplot(results) ``` You can also produce fully custom plots by un-nesting the results and working with the data directly. ## `system_time()` **bench** also includes `system_time()`, a higher precision alternative to [system.time()](https://www.rdocumentation.org/packages/base/versions/3.5.0/topics/system.time). ``` r bench::system_time({ i <- 1; while(i < 1e7) i <- i + 1 }) #> process real #> 2.37s 2.37s bench::system_time(Sys.sleep(.5)) #> process real #> 91µs 500ms ``` ## Alternatives - [rbenchmark](https://cran.r-project.org/package=rbenchmark) - [microbenchmark](https://cran.r-project.org/package=microbenchmark) - [tictoc](https://cran.r-project.org/package=tictoc) - [system.time()](https://www.rdocumentation.org/packages/base/versions/3.5.0/topics/system.time) bench/man/0000755000175000017500000000000014151175656012251 5ustar nileshnileshbench/man/mark.Rd0000644000175000017500000001114413620533713013463 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/mark.R \name{mark} \alias{mark} \alias{bench_mark} \title{Benchmark a series of functions} \usage{ mark( ..., min_time = 0.5, iterations = NULL, min_iterations = 1, max_iterations = 10000, check = TRUE, memory = capabilities("profmem"), filter_gc = TRUE, relative = FALSE, time_unit = NULL, exprs = NULL, env = parent.frame() ) } \arguments{ \item{...}{Expressions to benchmark, if named the \code{expression} column will be the name, otherwise it will be the deparsed expression.} \item{min_time}{The minimum number of seconds to run each expression, set to \code{Inf} to always run \code{max_iterations} times instead.} \item{iterations}{If not \code{NULL}, the default, run each expression for exactly this number of iterations. This overrides both \code{min_iterations} and \code{max_iterations}.} \item{min_iterations}{Each expression will be evaluated a minimum of \code{min_iterations} times.} \item{max_iterations}{Each expression will be evaluated a maximum of \code{max_iterations} times.} \item{check}{Check if results are consistent. If \code{TRUE}, checking is done with \code{\link[=all.equal]{all.equal()}}, if \code{FALSE} checking is disabled and results are not stored. If \code{check} is a function that function will be called with each pair of results to determine consistency.} \item{memory}{If \code{TRUE} (the default when R is compiled with memory profiling), track memory allocations using. If \code{FALSE} disable memory tracking.} \item{filter_gc}{If \code{TRUE} remove iterations that contained at least one garbage collection before summarizing. If \code{TRUE} but an expression had a garbage collection in every iteration, filtering is disabled, with a warning.} \item{relative}{If \code{TRUE} all summaries are computed relative to the minimum execution time rather than absolute time.} \item{time_unit}{If \code{NULL} the times are reported in a human readable fashion depending on each value. If one of 'ns', 'us', 'ms', 's', 'm', 'h', 'd', 'w' the time units are instead expressed as nanoseconds, microseconds, milliseconds, seconds, hours, minutes, days or weeks respectively.} \item{exprs}{A list of quoted expressions. If supplied overrides expressions defined in \code{...}.} \item{env}{The environment which to evaluate the expressions} } \value{ A \link[tibble:tibble]{tibble} with the additional summary columns. The following summary columns are computed \itemize{ \item \code{expression} - \code{bench_expr} The deparsed expression that was evaluated (or its name if one was provided). \item \code{min} - \code{bench_time} The minimum execution time. \item \code{median} - \code{bench_time} The sample median of execution time. \item \code{itr/sec} - \code{double} The estimated number of executions performed per second. \item \code{mem_alloc} - \code{bench_bytes} Total amount of memory allocated by R while running the expression. Memory allocated \emph{outside} the R heap, e.g. by \code{malloc()} or \code{new} directly is \emph{not} tracked, take care to avoid misinterpreting the results if running code that may do this. \item \code{gc/sec} - \code{double} The number of garbage collections per second. \item \code{n_itr} - \code{integer} Total number of iterations after filtering garbage collections (if \code{filter_gc == TRUE}). \item \code{n_gc} - \code{double} Total number of garbage collections performed over all iterations. This is a psudo-measure of the pressure on the garbage collector, if it varies greatly between to alternatives generally the one with fewer collections will cause fewer allocation in real usage. \item \code{total_time} - \code{bench_time} The total time to perform the benchmarks. \item \code{result} - \code{list} A list column of the object(s) returned by the evaluated expression(s). \item \code{memory} - \code{list} A list column with results from \code{\link[=Rprofmem]{Rprofmem()}}. \item \code{time} - \code{list} A list column of \code{bench_time} vectors for each evaluated expression. \item \code{gc} - \code{list} A list column with tibbles containing the level of garbage collection (0-2, columns) for each iteration (rows). } } \description{ Benchmark a list of quoted expressions. Each expression will always run at least twice, once to measure the memory allocation and store results and one or more times to measure timing. } \examples{ dat <- data.frame(x = runif(100, 1, 1000), y=runif(10, 1, 1000)) mark( min_time = .1, dat[dat$x > 500, ], dat[which(dat$x > 500), ], subset(dat, x > 500)) } \seealso{ \code{\link[=press]{press()}} to run benchmarks across a grid of parameters. } bench/man/bench_process_memory.Rd0000644000175000017500000000277014151172025016736 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/bench_process_memory.R \name{bench_process_memory} \alias{bench_process_memory} \title{Retrieve the current and maximum memory from the R process} \usage{ bench_process_memory() } \description{ The memory reported here will likely differ from that reported by \code{gc()}, as this includes all memory from the R process, including any child processes and memory allocated outside R's garbage collector heap. } \details{ The OS APIs used are as follows \subsection{Windows}{ \itemize{ \item \href{https://docs.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-process_memory_counters}{PROCESS_MEMORY_COUNTERS.WorkingSetSize} \item \href{https://docs.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-process_memory_counters}{PROCESS_MEMORY_COUNTERS.PeakWorkingSetSize} } } \subsection{macOS}{ \itemize{ \item \href{https://developer.apple.com/documentation/kernel/1537934-task_info?language=occ}{task_info(TASK_BASIC_INFO)} \item \href{https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getrusage.2.html}{rusage.ru_maxrss} } } \subsection{linux}{ \itemize{ \item \href{https://man7.org/linux/man-pages/man5/proc.5.html}{/proc/pid/status VmSize} \item \href{https://man7.org/linux/man-pages/man5/proc.5.html}{/proc/pid/status VmPeak} and on Windows \href{https://docs.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-process_memory_counters}{PROCESS_MEMORY_COUNTERS.PeakWorkingSetSize} } } } bench/man/bench_time.Rd0000644000175000017500000000176513620533713014636 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/bench_time.R \name{bench_time} \alias{bench_time} \alias{system_time} \title{Measure Process CPU and real time that an expression used.} \usage{ bench_time(expr) } \arguments{ \item{expr}{A expression to be timed.} } \value{ A \code{bench_time} object with two values. \itemize{ \item \code{process} - The process CPU usage of the expression evaluation. \item \code{real} - The wallclock time of the expression evaluation. } } \description{ Measure Process CPU and real time that an expression used. } \details{ On some systems (such as macOS) the process clock has lower precision than the realtime clock, as a result there may be cases where the process time is larger than the real time for fast expressions. } \examples{ # This will use ~.5 seconds of real time, but very little process time. bench_time(Sys.sleep(.5)) } \seealso{ \code{\link[=bench_memory]{bench_memory()}} To measure memory allocations for a given expression. } bench/man/autoplot.bench_mark.Rd0000644000175000017500000000456213700627654016505 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/autoplot.R \name{autoplot.bench_mark} \alias{autoplot.bench_mark} \alias{plot.bench_mark} \title{Autoplot method for bench_mark objects} \usage{ autoplot.bench_mark( object, type = c("beeswarm", "jitter", "ridge", "boxplot", "violin"), ... ) \method{plot}{bench_mark}(x, ..., type = c("beeswarm", "jitter", "ridge", "boxplot", "violin"), y) } \arguments{ \item{object}{A \code{bench_mark} object.} \item{type}{The type of plot. Plotting geoms used for each type are \itemize{ \item beeswarm - \code{\link[ggbeeswarm:geom_quasirandom]{ggbeeswarm::geom_quasirandom()}} \item jitter - \code{\link[ggplot2:geom_jitter]{ggplot2::geom_jitter()}} \item ridge - \code{\link[ggridges:geom_density_ridges]{ggridges::geom_density_ridges()}} \item boxplot - \code{\link[ggplot2:geom_boxplot]{ggplot2::geom_boxplot()}} \item violin - \code{\link[ggplot2:geom_violin]{ggplot2::geom_violin()}} }} \item{...}{Additional arguments passed to the plotting geom.} \item{x}{A \code{bench_mark} object.} \item{y}{Ignored, required for compatibility with the \code{plot()} generic.} } \description{ Autoplot method for bench_mark objects } \details{ This function requires some optional dependencies. \link[ggplot2:ggplot2-package]{ggplot2}, \link[tidyr:tidyr-package]{tidyr}, and depending on the plot type \link[ggbeeswarm:ggbeeswarm]{ggbeeswarm}, \link[ggridges:ggridges]{ggridges}. For \code{type} of \code{beeswarm} and \code{jitter} the points are colored by the highest level garbage collection performed during each iteration. For plots with 2 parameters \code{ggplot2::facet_grid()} is used to construct a 2d facet. For other numbers of parameters \code{ggplot2::facet_wrap()} is used instead. } \examples{ dat <- data.frame(x = runif(10000, 1, 1000), y=runif(10000, 1, 1000)) res <- bench::mark( dat[dat$x > 500, ], dat[which(dat$x > 500), ], subset(dat, x > 500)) if (require(ggplot2) && require(tidyr)) { # Beeswarm plot autoplot(res) # ridge (joyplot) autoplot(res, "ridge") # If you want to have the plots ordered by execution time you can do so by # ordering factor levels in the expressions. if (require(dplyr) && require(forcats)) { res \%>\% mutate(expression = forcats::fct_reorder(as.character(expression), min, .desc = TRUE)) \%>\% as_bench_mark() \%>\% autoplot("violin") } } } bench/man/scale_bench_time.Rd0000644000175000017500000000150413620533713015774 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/bytes.R, R/time.R \name{scale_bench_time} \alias{scale_bench_time} \alias{scale_x_bench_bytes} \alias{scale_y_bench_bytes} \alias{scale_x_bench_time} \alias{scale_y_bench_time} \title{Position scales for bench_time data} \usage{ scale_x_bench_bytes(base = 10, ...) scale_y_bench_bytes(base = 10, ...) scale_x_bench_time(base = 10, ...) scale_y_bench_time(base = 10, ...) } \arguments{ \item{base}{The base of the logarithm, if \code{NULL} instead use a non-logarithmic scale.} } \description{ Default scales for the \code{bench_time} class, these are added to plots using \code{bench_time} objects automatically. Default scales for the \code{bench_time} class, these are added to plots using \code{bench_time} objects automatically. } \keyword{internal} bench/man/bench_bytes_trans.Rd0000644000175000017500000000057613620533713016234 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/bytes.R \name{bench_bytes_trans} \alias{bench_bytes_trans} \title{Benchmark time transformation} \usage{ bench_bytes_trans(base = 2) } \arguments{ \item{base}{base of logarithm} } \description{ This both log transforms the times and formats the labels as a \code{bench_time} object. } \keyword{internal} bench/man/bench_time_trans.Rd0000644000175000017500000000057313620533713016041 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/time.R \name{bench_time_trans} \alias{bench_time_trans} \title{Benchmark time transformation} \usage{ bench_time_trans(base = 10) } \arguments{ \item{base}{base of logarithm} } \description{ This both log transforms the times and formats the labels as a \code{bench_time} object. } \keyword{internal} bench/man/bench_bytes.Rd0000644000175000017500000000146413620533713015022 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/bytes.R \name{bench_bytes} \alias{bench_bytes} \alias{as_bench_bytes} \title{Human readable memory sizes} \usage{ as_bench_bytes(x) bench_bytes(x) } \arguments{ \item{x}{A numeric or character vector. Character representations can use shorthand sizes (see examples).} } \description{ Construct, manipulate and display vectors of byte sizes. These are numeric vectors, so you can compare them numerically, but they can also be compared to human readable values such as '10MB'. } \details{ These memory sizes are always assumed to be base 1024, rather than 1000. } \examples{ bench_bytes("1") bench_bytes("1K") bench_bytes("1Kb") bench_bytes("1KiB") bench_bytes("1MB") bench_bytes("1KB") < "1MB" sum(bench_bytes(c("1MB", "5MB", "500KB"))) } bench/man/as_bench_mark.Rd0000644000175000017500000000061114151167337015307 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/mark.R \name{as_bench_mark} \alias{as_bench_mark} \title{Coerce to a bench mark object Bench mark objects} \usage{ as_bench_mark(x) } \arguments{ \item{x}{Object to be coerced} } \description{ This is typically needed only if you are performing additional manipulations after calling \code{\link[=mark]{mark()}}. } bench/man/press.Rd0000644000175000017500000000307314151167337013674 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/press.R \name{press} \alias{press} \title{Run setup code and benchmarks across a grid of parameters} \usage{ press(..., .grid = NULL) } \arguments{ \item{...}{If named, parameters to define, if unnamed the expression to run. Only one unnamed expression is permitted.} \item{.grid}{A pre-built grid of values to use, typically a \link{data.frame} or \link{tibble}. This is useful if you only want to benchmark a subset of all possible combinations.} } \description{ \code{press()} is used to run \code{\link[=mark]{mark()}} across a grid of parameters and then \emph{press} the results together. The parameters you want to set are given as named arguments and a grid of all possible combinations is automatically created. The code to setup and benchmark is given by one unnamed expression (often delimited by \verb{\\\{}). If replicates are desired a dummy variable can be used, e.g. \code{rep = 1:5} for replicates. } \examples{ # Helper function to create a simple data.frame of the specified dimensions create_df <- function(rows, cols) { as.data.frame(setNames( replicate(cols, runif(rows, 1, 1000), simplify = FALSE), rep_len(c("x", letters), cols))) } # Run 4 data sizes across 3 samples with 2 replicates (24 total benchmarks) press( rows = c(1000, 10000), cols = c(10, 100), rep = 1:2, { dat <- create_df(rows, cols) bench::mark( min_time = .05, bracket = dat[dat$x > 500, ], which = dat[which(dat$x > 500), ], subset = subset(dat, x > 500) ) } ) } bench/man/workout.Rd0000644000175000017500000000226313620533713014245 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/workout.R \name{workout} \alias{workout} \alias{workout_expressions} \title{Workout a group of expressions individually} \usage{ workout(expr, description = NULL) workout_expressions(exprs, env = parent.frame(), description = NULL) } \arguments{ \item{expr}{one or more expressions to workout, use \code{{}} to pass multiple expressions.} \item{description}{A name to label each expression, if not supplied the deparsed expression will be used.} \item{exprs}{A list of calls to measure.} \item{env}{The environment in which the expressions should be evaluated.} } \description{ Given an block of expressions in \code{{}} \code{\link[=workout]{workout()}} individually times each expression in the group. \code{\link[=workout_expressions]{workout_expressions()}} is a lower level function most useful when reading lists of calls from a file. } \examples{ workout({ x <- 1:1000 evens <- x \%\% 2 == 0 y <- x[evens] length(y) length(which(evens)) sum(evens) }) # The equivalent to the above, reading the code from a file workout_expressions(as.list(parse(system.file("examples/exprs.R", package = "bench")))) } bench/man/summary.bench_mark.Rd0000644000175000017500000000725414151167337016332 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/mark.R \name{summary.bench_mark} \alias{summary.bench_mark} \title{Summarize \link{mark} results.} \usage{ \method{summary}{bench_mark}(object, filter_gc = TRUE, relative = FALSE, time_unit = NULL, ...) } \arguments{ \item{object}{\link{bench_mark} object to summarize.} \item{filter_gc}{If \code{TRUE} remove iterations that contained at least one garbage collection before summarizing. If \code{TRUE} but an expression had a garbage collection in every iteration, filtering is disabled, with a warning.} \item{relative}{If \code{TRUE} all summaries are computed relative to the minimum execution time rather than absolute time.} \item{time_unit}{If \code{NULL} the times are reported in a human readable fashion depending on each value. If one of 'ns', 'us', 'ms', 's', 'm', 'h', 'd', 'w' the time units are instead expressed as nanoseconds, microseconds, milliseconds, seconds, hours, minutes, days or weeks respectively.} \item{...}{Additional arguments ignored.} } \value{ A \link[tibble:tibble]{tibble} with the additional summary columns. The following summary columns are computed \itemize{ \item \code{expression} - \code{bench_expr} The deparsed expression that was evaluated (or its name if one was provided). \item \code{min} - \code{bench_time} The minimum execution time. \item \code{median} - \code{bench_time} The sample median of execution time. \item \code{itr/sec} - \code{double} The estimated number of executions performed per second. \item \code{mem_alloc} - \code{bench_bytes} Total amount of memory allocated by R while running the expression. Memory allocated \emph{outside} the R heap, e.g. by \code{malloc()} or \code{new} directly is \emph{not} tracked, take care to avoid misinterpreting the results if running code that may do this. \item \code{gc/sec} - \code{double} The number of garbage collections per second. \item \code{n_itr} - \code{integer} Total number of iterations after filtering garbage collections (if \code{filter_gc == TRUE}). \item \code{n_gc} - \code{double} Total number of garbage collections performed over all iterations. This is a psudo-measure of the pressure on the garbage collector, if it varies greatly between to alternatives generally the one with fewer collections will cause fewer allocation in real usage. \item \code{total_time} - \code{bench_time} The total time to perform the benchmarks. \item \code{result} - \code{list} A list column of the object(s) returned by the evaluated expression(s). \item \code{memory} - \code{list} A list column with results from \code{\link[=Rprofmem]{Rprofmem()}}. \item \code{time} - \code{list} A list column of \code{bench_time} vectors for each evaluated expression. \item \code{gc} - \code{list} A list column with tibbles containing the level of garbage collection (0-2, columns) for each iteration (rows). } } \description{ Summarize \link{mark} results. } \details{ If \code{filter_gc == TRUE} (the default) runs that contain a garbage collection will be removed before summarizing. This is most useful for fast expressions when the majority of runs do not contain a gc. Call \code{summary(filter_gc = FALSE)} if you would like to compute summaries \emph{with} these times, such as expressions with lots of allocations when all or most runs contain a gc. } \examples{ dat <- data.frame(x = runif(10000, 1, 1000), y=runif(10000, 1, 1000)) # `bench::mark()` implicitly calls summary() automatically results <- bench::mark( dat[dat$x > 500, ], dat[which(dat$x > 500), ], subset(dat, x > 500)) # However you can also do so explicitly to filter gc differently. summary(results, filter_gc = FALSE) # Or output relative times summary(results, relative = TRUE) } bench/man/hires_time.Rd0000644000175000017500000000146713620533713014670 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/hires_time.R \name{hires_time} \alias{hires_time} \title{Return the current high-resolution real time.} \usage{ hires_time() } \description{ Time is expressed as seconds since some arbitrary time in the past; it is not correlated in any way to the time of day, and thus is not subject to resetting or drifting. The hi-res timer is ideally suited to performance measurement tasks, where cheap, accurate interval timing is required. } \examples{ hires_time() # R rounds doubles to 7 digits by default, see greater precision by setting # the digits argument when printing print(hires_time(), digits = 20) # Generally used by recording two times and then subtracting them start <- hires_time() end <- hires_time() elapsed <- end - start elapsed } bench/man/scale_bench_expr.Rd0000644000175000017500000000130613620533713016014 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/expression.R \name{scale_bench_expr} \alias{scale_bench_expr} \alias{scale_x_bench_expr} \alias{scale_y_bench_expr} \alias{scale_colour_bench_expr} \alias{scale_color_bench_expr} \title{Position and color scales for bench_expr data} \usage{ scale_x_bench_expr(...) scale_y_bench_expr(...) scale_colour_bench_expr( palette = scales::hue_pal(...), ..., aesthetics = "colour" ) scale_color_bench_expr( palette = scales::hue_pal(...), ..., aesthetics = "colour" ) } \description{ Default scales for the \code{bench_expr} class, these are added to plots using \code{bench_expr} objects automatically. } \keyword{internal} bench/man/bench_load_average.Rd0000644000175000017500000000045313700627654016311 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/load.R \name{bench_load_average} \alias{bench_load_average} \title{Get system load averages} \usage{ bench_load_average() } \description{ Uses OS system APIs to return the load average for the past 1, 5 and 15 minutes. } bench/man/knit_print.bench_mark.Rd0000644000175000017500000000175513620533713017011 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/mark.R \name{knit_print.bench_mark} \alias{knit_print.bench_mark} \title{Custom printing function for bench_mark objects in knitr documents} \usage{ knit_print.bench_mark(x, ..., options) } \arguments{ \item{x}{An R object to be printed} \item{...}{Additional arguments passed to the S3 method. Currently ignored, except two optional arguments \code{options} and \code{inline}; see the references below.} \item{options}{A list of knitr chunk options set in the currently evaluated chunk.} } \description{ By default data columns ('result', 'memory', 'time', 'gc') are omitted when printing in knitr. If you would like to include these columns set the knitr chunk option 'bench.all_columns = TRUE'. } \details{ You can set \code{bench.all_columns = TRUE} to show all columns of the bench mark object.\preformatted{```\{r bench.all_columns = TRUE\} bench::mark( subset(mtcars, cyl == 3), mtcars[mtcars$cyl == 3, ]) ``` } } bench/man/bench_memory.Rd0000644000175000017500000000112013620533713015171 0ustar nileshnilesh% Generated by roxygen2: do not edit by hand % Please edit documentation in R/bench_time.R \name{bench_memory} \alias{bench_memory} \title{Measure memory that an expression used.} \usage{ bench_memory(expr) } \arguments{ \item{expr}{A expression to be measured.} } \value{ A tibble with two columns \itemize{ \item The total amount of memory allocated \item The raw memory allocations as parsed by \code{\link[profmem:readRprofmem]{profmem::readRprofmem()}} } } \description{ Measure memory that an expression used. } \examples{ if (capabilities("profmem")) { bench_memory(1 + 1:10000) } } bench/man/figures/0000755000175000017500000000000014151175656013715 5ustar nileshnileshbench/man/figures/README-autoplot-1.png0000644000175000017500000057564114151175656017405 0ustar nileshnileshPNG  IHDR4הLiCCPkCGColorSpaceGenericRGB8U]hU>+$΃Ԧ5lRфem,lAݝi&3i)>A['!j-P(G 3k~s ,[%,-:t} }-+*&¿ gPG݅ج8"eŲ]A b ;l õWϙ2_E,(ۈ#Zsێ<5)"E6N#ӽEkۃO0}*rUt.iei #]r >cU{t7+ԙg߃xuWB_-%=^ t0uvW9 %/VBW'_tMۓP\>@y0`D i|[` hh)Tj0B#ЪhU# ~yhu fp#1I/I"0! 'Sdd:J5ǖ"sdy#R7wAgdJ7kʕn^:}nWFVst$gj-tԝr_װ_7Z ~V54V }o[G=Nd>-UlaY5V}xg[?k&>srq߀].r_r_qsGjy4k iQܟBZ-<(d=dKO a/zv7]ǰod}sn?TF'|3Nn#I?"mzv~K=گsl<b|_|4>?pߋQrib 2* (Ѧh{28oIyes8';Z9h6g>xRx'b8ՃWOϫ[xn%|^z}%x c8eXIfMM*i4!O|@IDATxde}8{e K[JS: Xǟ[,1h1QTb j#JG)"l0{޽mf|.syd)$ @ @ @ @@ 4P[4 @ @ @ @ @ @ @Ԝ "@ @ @ @4  @ @ @ @5' D @ @ @ @ > @ @ @ @@ h!  @ @ @ @@@ @ @ @ PsjnH4 @ @ @3@ @ @ @Ԝ "@ @ @ @4  @ @ @ @5' D @ @ @ @ > @ @ @ @@ h!  @ @ @ @@@ @ @ @ PsjnH4 @ @ @3@ @ @ @Ԝ "@ @ @ @4  @ @ @ @5' D @ @ @ @ > @ @ @ @@ \4@ ^:֬Y` @%0}XdI}wTl @ 0i⠃8 PWfhX @ @ @ 064qK @ @ @ PWj4 @ @ @  cc @ @ @ԕ.%@ @ @ @cC@@g$@ @ @ @u% Kc  @ @ @ @06Y/  @ @ @ @@] hX @ @ @ 064qK @ @ @ PWj4 @ @ @  cc @ @ @ԕ.%@ @ @ @cC@@g$@ @ @ @u% Kc  @ @ @ @06Y/  @ @ @ @@] hX @ @ @ 064qK @ @ @ PWj4 @ @ @  cc @ @ @ԕ.%@ @ @ @cCeltS/  @O <裱ys~:Z~}X"Z[[c…>ԫl @~Z>`>Ο??:Cuˣ-̙- & | @Ԛܵ6"CT\F󲗽,7UJTzW7xcڵk*./8Sv;  @zhkn^xa>R@C{{{\}o;R9ig?;7ĤIJ&@ Pj~4 __~yttty0ۓlٲ袋Om1{>I @ tuu'>LrCiͦM2uގ;G?Q_5f̘W6 @Ԥ"@/8o3L2%ŋqc 754Ç>?i @Hrvy{ zSOo[w_>h~)!W @ aUI@ZO _BN:)w1矟z(>qqW+^6 @]'?ɸ曇Րߟ?wJ;;=t?/9w>' @U[\} @\uU֖/a}?k)x_Pݥ^===^  @%t]0.a3l۶-~{+ f(8c#HmWLD @4(i# @IB~?#3쌴D @,9Q @Alz_wQ('F{e?FZz֭[>  @I 59,EG+|+{ ;W򕥇_"W@Dʔ~x׻/~z4iRl#Ǎ7iq}>xiځoa<0u8liֺ==8b֬Ylٲw[??K9<.HeR:7ʙ K)O ԧ9PV__җ i&㎋0}*M?E:?M {iEe]VlҜ9sKfw @ Џk~`F;ͼp%GTRZR+kWX"EH @j]@@C @:H҃B:ꨣ¦?R HS?cYRg? r򗿜NIDv@˿ R?O' 5ΐ ӷtI(w|TW_ G}t?>38\xn(f @ Џk'`*u-?`R ӦM=1cF2;m @1]X5}o~󛑖eH)-}(LG)۶m[n%N=H>яƮ]X?DwJo~?N43`C=;vgOmj|)ޚwOtU5_>`4ğ @ Пk'dFZq)_gI ԥ.[/ @jA  @@c Ynb҃/6iɆ3gFT jHiŊqw^WP̐mo'mݺ5?KCK zXn],]4>Oĥ^W_}^o!8|SE]T3@I P&ep1۷o/0a„@r @Pk#= @rӗ>WiىK2vM'QgXξ_M3 %zR0Dwww~׿I&VTZvX7idY񶷽-Ғ @\$TK}`PL @TR@@C%uMƨ@z! fϞ]xk`1[Hi>|[pa(FjG H3.iӦ2iMpI'3Xd^JC{o=-[= @ 7{өc'N,6&-7Two < @TZ@@COƠ@ (s:pNtvvfi(Ҡ¾^7gW~{+_J~zq9GQ7Z_}|C|2|;9yh5I @u(Z>mņ)wQW@C?Hv @Ԕ@SMFc @(]u̙Ӛ5kfB: KG 5Ґ!.])iI4?صkW3}o1!=?, @4k`m۶LQ!Ey%@ Pkfh s)bnqA~bӧg[(sH?+V[n%n ~_FMD̘1c5,kZ7K_R\yłg|0ATg @`J]VJtT:vhڵC_l @( h%x @FoK3 7>+aJ#`җ4:::⮻n!~El˖- ԼO-, )>LD @ UPF'qOR VZ5`@C PٱcGO{ӊ6 @Ԫ%'judԱ@!ux {xW2rV\9iT/_^,~޼yl} 禇|~Iqgp(Fwww|c-|f|ӟ!fh @(w-_RiL:q @. aԇ@ @@ ムOfiI4wQ 8l^{m})!G'\?x+^/~.9is=+nWr/*ҲcҤI}6 @ GpF38Xo~viFB:c ^  @Դ#@)f1xK^RlW\mmm7 K'vi,rHx}mY Toogώ 6We/MΝ׼8裏_^,Ow] @ 0DCguV\/Ҡ; @) 55CGUzUL>=ߡVE]i)-я~x,Ϊ7.^X萖(}HlK0ybgt#=4\WD >xޗ?X;܍̙3':qΝ;[ @ƈk',X Mzk}acٲew}1đGY8 @@M t4[S\}osΉK8A6{ݖN8/yqVo=^g/'-pGb5WH|p-… ]ys2/| #2eJ~i;3~ұ{swGajצŒ%7Ғ~8 oLҒ @\?!SkG5yM~ɽDfK㎋'FKA۷oW5mڴV| @UP5j @'C}s%|^;ҥҥ%*=ve7RpR H3xRfG>|_S,e @؛kα3gƿgK3uttD N?S?zx`C @Y 5;4FC9yN)MR iv]vѱ4B "HK(r\/N:)>]wg9Eg?b/ͤ| ~_ǥ^>$/^rJ~ƃ &쑧;Һ @j 7o^|S/~_2m۶[~*ڐfw?nǼ!@ P,z#@= ^:֬YS]vM ]v\24D5}3(-)JJYi/-^+k׮AfhiZ oe @ӧO%KT|KU @`\ˏ"NJ<@~tL] -H Xk<4h @0\  @Ƙ16Kh3=Q @ @ @ @n4Pi( @ @ @;X) @ @ @ u3TJ @ @ @Ǝ3zJ @ @ @F@@C  @ @ @ @# a쌵 @ @ @ @P7C @ @ @ @`h;c @ @ @ @n4Pi( @ @ @;X) @ @ @ u3TJ @ @ @Ǝ3zJ @ @ @F@@C  @ @ @ @# a쌵 @ @ @ @P7C @ @ @ @`h;c @ @ @ @n4Pi( @ @ @;X) @ @ @\OꦵJvvÖhii3=7n\45=~mW.)>{f"?~TB 3>RuƏ\X;El}]d„ QImԩW76m*gqVkѡ/\sjw_k5gu 9 $ZwYB-k{+yYW-ռOoذŋW& @-/R F hH'Ommmm۶nڮĉcҤIR0֭[mTV`ƌ;b항LE)SDzPH)8F_'7ʿaWy ӧOPJjmm_kI7o̽ ޅ*fΜ[ OƩJWj5oo# לSkF_ӿs֥5$?rߣ~ Uvp͟s߰ʊiӦ,T(5A:g [ly|֐]I+ @@c < @ @ @ @u, O  @ @ @ @@ hhԑ/ @ @ @ Px4 @ @ @4FY"@ @ @ @u, O  @ @ @ @@ hhԑ/ @ @ @ Px4 @ @ @4FY"@ @ @ @u, O  @ @ @ @@ hhԑ/ @ @ @ Px4 @ @ @4FY"@ @ @ @u, O  @ @ @ @@ hhԑ/ @ @ @ Px4 @ @ @4FY"@ @ @ @u, O  @ @ @ @@ hhԑ/ @ @ @ Px4 @ @ @4FY"@ @ @ @u, O  @ @ @ @@ hhԑ/ @ @ @ Px4 @ @ @4FY"@ @ @ @u, O  @ @ @ @@ hhԑ/ @ @ @ Px4 @ @ @4@KvL-~wŤ#ƍy䓣gP @@Ƹ_2?' ki S#GV  @ @hB 4@U1oEZOvc1gh?SNk  @u)ДݫLףug -YC˕Eߍ]< l8a| @ @*P䨶BԬ,&'xbuo}[Ą 0 @*#0~z̀14kä˿mَ~*f @ @{ 4&-61ߌi=!@ @u, O TJ`CB&¦W @TV5r;vlƹq9 @ @F. aJ p}Yܺe)K! @؛K,٩?ުr @*A@4jҸ\E) @  @hd 0̽MݳfGDZFϊ2e%: @::z Ǐ g @ @#0CÈLP6m 6~r-C9U^ @+[.ʽLD[ @ @@u4TY-vIW\zM^ @@ .wjr< @ @`"] }s#ySDwOL]GFkMFϬYq3Q @Ertxr Dg @ @eP6Jm?i4^gC'O,! wUt6vI @V(ː7[?s@ @WTH;\t%3F7ڪ?׫B @G m{;۶e* @yMv/~>Z=[/kG @@ 4mRe @ 0F_**0oE󚾗hC,<-s|}C@۷׼z @4?9&@ @*) &0 =8 ʎ2 @\i @ @`MD)мzUm6lPT. @ P jT @ @ Mnvۦk8 @,ys|Nhʖ @ @0zj&P])W @E܎kVu+[ @ @6@] ꪽK @ *h#.`|Dcd @ 0BaN'@`xjuxq @*+kܲ{=]]m  @ @ h+ @ @@- 4=PŚX &@ @0 @ @@ ;*23UV @ @`P$H sըJ @4@  @ @z h-=+> @hzj @ @0x+9 @nݺ*Ԣ  @EyF~ @ @4h^|tv @K @ @4 JN!}k5jQ @ "l @ [@@Co Գ@ww=>u:@ @@rEWO[M @ @6mP›vV5 @ 0n @ Pf eUQصsT/K @cEce @Ơ18S}皶o> @le=.W: @k u=|O@#nU[ @&PeRٹMVc @ @@4TW-ծSMH @rQr+W4 @ @Pu$дv͐bJ~M @Զ@ʕ]]H#@ @ *Ҡ-cR uL[  @ P)I߈'=kV?|)N @ @@J0l{ @ @lr͛6Ť]S=裵Lm"@ @ % SgƼc @*-ko_\x}R> @| 4e @ @iƆn4_+S @vc v5f @5(&W#::juD @_@@C( 人6 @ @*_ @ @0]X1:-01R/ @ qw16: @ @ :h@4ekL '@ @@91˗He @ @jOmWI @  Wo/G"@ @# a8j!P[Ar5B7 @1$cJ @# :j!@`+L:.Y  @]h;zN @п#@nQY @ԕ@ww]5Wc  @ @ hTC @zhnC)"esF3 @ @@chhq  %۱3 @TH`Zr" uT @ @`l hjZyӆn @ Pj!Ob7T{4 @ԻzA'PhoezCW @jL`jPT{4 @ԻzA'@T# s  @ػ@OG3ThSWWkT @hl =z7r[4Los] !@ @r{s W2 @ 0F}4@yz&M*OA5QJOMB# @m\n @ @#0">'GNc @Uɹ:;sU @ @! al^֞m @hڼ\vjӃg @ @ h؍+8 !``h9 @Uhڹ* ;Jvy  @ @h CI ZOfhH @c\ }{ 4?P͵I @ @@ kõ˗/u ={v,ZhĕX"֮]/g֬YxWh{_\GcryLm @@gvk޲yUTO @G@@C{|'9s㢋.qW^ye>x72*m﫞Zޗ mputWG|' @ 0jpfzc^  @ @@54TCY!SZVGzؼmkLg#̳󤓣gΜGU @eZnۖQ  @ Pv4H g/"LFOzӦE>YĤI='Dѵ`A4?p4?h)j{[{uvFӦ;w{&O}DסEyY\{{zsG;Zg\V{e{<׭i1#?z_])(cWpє3ujLfuYU+v6Gѝ} -q]Ѵ}[L;=gO "@hptPi7+]d$${tдiS4c:25{(7>'gSFyѝf۹#Z|(r;{~;7kaLSD;H]rϏȖ1l~|rhc&Nc?wnt-98z=GjCVV)JuL=3{ϔv fmIEYRSJR @ wcbG~zzhѢkH\ӬV|0,s[Lك0Oiw~n8rםeH"{'+±oWa瓯f_x~z 3wٳ'GUۢ{|ak ~r}}r=&Geۓ-K$) # iJ!'{4[6ZflTOެWN=1ioqs}*l[ƭ-[cٮؑ;x8k8i~J 0J5okCHr S;ڳ'9 ^- hzh~zUh?㬈A㨧֘kb7exK߱+IYcG׼/dI_>s?v<m1nxY@É9!u}}\Gܣmmkb{KCߓp; '㋫V웫m[;6Ă-3w  @@ u_׾;@SмjUDSWtnܮ,{۶'o>?`. tvoXm/D=)_wOo<}?~{L?p+ @(Oh<,.[KSN51?[G1M#S>,`=zy횘__Yۋ3c-ZHKv ⎕lл˷z^ܶ~?o~aVw,eq_<oؙ`G-3UG=J3&@V)[vp7DW=ZغkU|ڣ0P{{zw+._+8 @0CCmn*)M;iC{Ke7-F½+VYfŒ%K9HJO(mٲ%~XfMEob…ť+:ᶽw9)D` 4gN-A1 iI_hJl !MhpFW`}qm]wt5' %/ځM;o/bgG6uR "MCag=kWvm=7m|_S?ܸ)vfd?h^^ @@- ^'Lm#ή]93[B,ֶ:y+g\sdJ @X@@C -|^o{m?~l=#RKFØ2eJ_Y[oO~q|ҥ7);=v|+2ώ}C{}qu]-WYl׽uqQG=s/[#m^CM\4@5֯߹,vjVaa7/r0CZ8[Z@ tugCJ0CigRP~ӏӎ(]鎫zÐ \’'!s.뿯\=`B?߼%.Y.߾  @@%eJ(o!_ǟXي>&떗-ۿs_NY4M(J @|uq|;s-(=fjʖw7}32.|E_ /ַ's.7q̐~7\r^kvژ*Mث?=<>ntt^Un]~I%L ?C~+cMHR*;fwie[{|# HI5c͑\%kgk~QJpk!nCEi#q_H %@ P-34TKzvi|&cʕziӦxfv( (=/=3ۥ),Ō3Oİ}ħ>H ,(-b7K_R1~ fϞ_~+UV|+_?99l{iݴqhVn5#0׿R䧚amͫWGE!!+,pc5m.ַ>M{=Txt dSA9w[5HҎ,8 |4ѹث@S$D`4nfi8a4:=XWEh '@ PI a)@z(_R "xK^Gii鷿m]QFH; z(3/EZƢ/2Ҿ |+ YcS 9R̝;X 'x;3Oۿ[y1nܸbF5o|#veӛ&y䑃^&c0'}[_#0| Ǘiinl{.3'=pڰ:6:ܜo_زy,۶m_@F}.dLJdzꩃ:z}$-ϳnC`W֪4t[ۺԣ @0CC%TXҥKc} 6Dkkk~Fc9XòeҎ4?o8쳋yFig۱7tP̘1toqlȡ+A,⢋.sƈjc{)kMZA`O۸Փk1(}ZT;HMܴgYmTSǕ,5Hn @`lzz34 K 3>V4:Ҁ׼RqӲuf7֭˗ڵkc޼y<]n"eNT:.SXzQ3ҙ.jkZce^GT 4@̙6?4[ɓ#eKr'ŔlSn͂6͍0)-%ZHv͛7z@Z)ifRJ?rK)R}t.Y͎C_ª7SG iSƭ#_V椩SԖt=T0#2Y} d~[woJ--tHiozR+N2%Oْ9iZNLj;/-5ijgώbӦMcu9}eMGq]ZOkڠ@znR2kLu^ }V#RI#4:ժu(|LtsOvztw:ÊAiB*a( Pp_KoMTR5^'?e+OAT󨧖x@Σq5=ٷ;N]O[hFqeyX}f #a&34dɃuVͿzP<׭@v-(@MoO|q9ײt?T,P @@ R#iى;#ߚ}kՕ.]bpW~[ߊm۶O>76nfR;;b;/?=3 }ڴiS(3<=7?H,Eu{4?hztP<&*[OGΧ_VqiљrםѴ}n}ɦO=;N;=wnǼ!POCG-xYzgu?ݺ8šs۱IS:qã_{0\K,uJtYsv;V7ʦ3QO\6~aSlܣy5K̎7nL) # @@ t7:yJsrdΨ~hxm #4.v8h⌃i7 @@h< _BŅқ yӟ = 3>-zJӟ4lْ_r /1sݪ۱cG|͛7ϛ7/2=O:`ޟӋ|ҷCҠll&uev':;ɼ}'[~{y12-\f\Kt0;lɞ66 L75^{5G;˂ؼ3R?ybEb=5nj'u uP @( a`<0{b3NK.-/l%8∸뮻 %{_kAӲ?PO|vzqĹ瞛WZJ㡇*6lؐϓfvH ғ6r*<0KYK^"בRҝ8cxCqwЪ{ѓ8:I>uԞ=,Pk '}Y`C!E{_BMǟwʦ`:޻|<3}6{~$.4>yRܩEf8rhE P龥#O| }ܳ v?7woO?>,Nnu"ic:,H{μ|rW~\6>MY`w/R|e@ 9|?}4;& @ W4¥^ZYa&⎒4sCi@ͷ$&fԧ>}!5֭[[Kw3ݠA`WKo^ @H [F%k;{/6\vQqb6YK!h? cen( @ lsҲ& {& L{_zzc<9o}+?{Ca̖h}?D>*qY @L =G"@ @W ד힞գ@HX"Įlŋ(rm۶ؾ}{ś<=MٚZu}O/^ ]rm}[.-qcǎزeK#tgN(ď?s'drgr@nBrr 7%suaUu׿ +r(!@HBd9wJ3Lu{~ktU?91߯9Z[[F8Yֳ'e0# ֪zGСCtX6kRf >׿N$]uuU[u6448Vw&+ֳQS'Y&][w}[]PZwVK6%H3gP {Fa;0td9=Gag~}J'eF`ֲȽ}G!eFg~'5WoߞX+'|r"Xr[.74 f\ҥ膺/y2$7%  dM ܯx~w s-ӝ @@@HE%'RQ .;YV%*+4  @ p]oCӦOt@@@\ !WF@`¤.{r꜄  &`Wv8+ǂSf]E@@@ hdzʘ SOaQ#  @ aW .s_    IАYpps   `ᵿNjD@@@SAGHO,*J6 E+   V/3չ/@@@ E@fU zaSJ   @hȼ9-"@ k@@@Ȃ' m$8$>qC5gZ}3  E \-]    wp> ee9?<(@@@  #A@@@ hȱFwM8|ùq<7I/@@@ 䊋~ @@@ 3CHE Nʘ,t3  ]4sD@@@HUT(Kثb:פȌ  @ ~+_   4eh'`VU5%j."{O@@@ y؝i2C@@@ Whȕ3E?(@F0@@@R[@@@p ;'Sg   H@@@@ Whȕ3E?(а2R  v;@@@p .<)t B0¡B>cG@@$B'Y    +4ʙY/2L@@C&G@@@@4' T.++3l@@HIMX"kD2@@@ V*E@@@LFD3   Y ! 4o?t@@@@ 4d<n $] uKH1߾ @@pX Vˑ@@@@ 6O 6lRvQ       q% NjM@s΍   @+9ZY䷭.*B@@@hHݎ@IEEMO0ɖ@@M{;ԱK*?@@@@qVrZSe1"9mA@@@ 7§̰: ̞1;7M/@@@s3<RM;ETF蛀퟽. @@@iOKbVT]    @4oH @W)ڟ+J5C߾yià@@@sϗPuuʝ HW\    `@jVjC7 I7AL7Y _ s*Gf@@@|CeIWe|EԵ @@@!@@;@fMi>_<;cZ@ :$0$fK̳@@@&g[Żz<Mg2i?\ U   j] Za"_C**+!9 zIJJ\g:  -0Q8|H׉g3,fj Ixb   \  ٧F@@@>>@@@@w x=z       (@@C!uƌ       hp {        x3       . '!       P4Yg       \        @! Pg1#       r\~       (@@C!uƌ       hp {        x3       . '!       P4Yg       \        @! Pg1#       r\~       (@@C!uƌ       hp {       ͘@@?wf1%JKk:ڠ^@@ T{53aZN;L2J   |jF<*F4]:uzxK~뮻y  (0=/D2$@@ \|NmڴIn} @b4$D.Ȓ%'Қm!ծ{aiT  䩀1%!}~ۥA@@WZf@ 1s"dI fP7, m۲4m"  @ .p_S7  @}t<H  А@  Ż{W[<=   @%[U=s   @B*赤ۧO8qbBȄ pLwl-@]zٽ{444ȁmmmZ⦛nT7  А@  }sSVjVq؋  `l`0C  #;vfgT>x` sx#kLUUUr]wϗ^zw.ӢE?  -]VD dv<  t0g} '庞# 444H[[Uu!h:.SX-V__hQ! _@ w898  ,پ&;:K/@@ lڴ)qE;vl4֭[l $&@@CbNBlVg   O8x ^Ǐvh@($pKKKlED @ a"#q`_q#  @f鱛 dw$^ϻr#uS)  Pѡ[.ڵkGf@ 1s"q`#  @<<^nܐvh@(aÆE` ltxs]2dȐ۵k<#Gvmm6@ !q+r"@ ^  t/`v {PrS g{4 9#_߶x 3g%)(d vZ.ӧ4 m}h8xPB`B=   Q;A/@@<7! qFkdpXy睄F9yd[K&@!~KiiiȈ Y:{@@@HJs`RlzNUM  @ ~kzK}]ٳgT3:tHtíW]]̝;Wp i А& @~J@@pHPUk 7  @fjA/A0)zҘ@kHdA ;3 !  nHF(n@@z fصk<3o~SB|^+  0CCԀ6 x\#!     ༀin:Yp,X@6o| y|r9+r֏#    dLC.]*ؘiw 3>rPhi^e@@@@@B8x,Z `Xx8C)"-Mca .@@C0|(9pݢO   "" 9(P__o0V\)f.//S̙3@h2 qb:(f.]A@@: kώz @@RЁ $mm=aȸqdƌk„ zN@hH΋ PMEh(Գϸ@@Y@즤g C@qw;X/YM&}6;@ !=?J#.bi#N@@q3#&+t@Gpqx p ;%tcC0=N =B@@ F(N4c߱  `ŋeɒ%SNӧˤI斨@@ NUO71]D@@@ fyE;Ӄp퀘wl" +pwX ˖-f j]jzǥXL"3f̰9rdR@ *  xI6dA@@B0 wdD`  W\qbX~ܠghXr.ܮ37N555V`)bPUUl'c24OuϰBjB@@Yq-PB渪Ot@+Ǐ^]wtttȊ+? v]NQ^}U>f+[nepn }~$ٲ٦@@IٽU3  @ ~kӧ[CljjKFoƍEn&с$@hH܊ P=CG@@e〡;&! d^BF)Tu~f^@hHߐ@NӴ6rMJFM   @25\R̙#f͒J;@nh] EYl@@@$tc0E+  @ %',X ~Ak MMMoX/#'NSO= ps@4% YxO!#  @ B뱱w/ ;+t@rUK.jnn{ n?[.=V3vXz=2h ={p'աo@44 x"D@@\/`}JM5\d& "P^^.s tR+wޑ}EܵkSUU%- K??J@͠KYz"* 34$D.Ȁw˦ zF[{)  'ڒcbD  Q{ʆ ~3رC֮]-s뭷Z zԩS .ڵK  yl`6Tmm*@@@ o]9CKN! ]s%t;ŋy|>~zcڴi:  y`Wo @@@ŝ -["'  @w6lXt;v#6aRZZ{Xjjjc$;@U^y8 Lj  - ]S>ZTԋ @GGGtF e˖E̙3ۑcKWWWGv@ A"8/`:-    Y_d  cƌ6 }E̚5+ضm[dSl $&@@CbNB Vl"N#   9-  y.0rH|(/?~ F  Fzg}WTT$t7 hoD@0#H47g.QkDB  A  䴀f)kf͚x<(z_$viM1{:7 ihh=Z<"$(p$,dC ]_KіbtCBO9sn1~@@R f-d׎O5}е}c  t% ׾5袋F^}Uijj΋n/[ fP\sM[@ !A(!ZicS5?+HYQm^v"  @ xmUaMoE ޒY縷 @ ^3-.!>[&Nuiyg= @4$nEN@ i=|Hpe [    {ޭ[\i/$fY&$u)yvPooZ1ZZDt V3NtY_@@ YO}z%R`Dȃ !Qx'矷yʝwޙc\|mst]˪ Az>^%}ï[ӯ@@@5#CItD_*<դ˗pPKPGPQ$T7RB&[?{ VZj> O/%!   yzbV|P^z%~F@CT=ٓ hz)7$׫-.p>V3F R7*nVTYYiY9f($}b5p 5٧o?@@@ 3y' D&roy#K\@Wvuմ:hz%Uik֍1:Tj5 |zz}ͪmU-jYVc8pjODA!z=;]dե͊$]f>-jƫY|e+ڃpnfHE@)鿎ɧ@Mڂ[*%>W`CPC6iVRs^ Zo֜\FBw@z\4k!,`] "u46nlZ?cT"Z2&MP@rS]TAeR`C_}]g2D}$W-@ 緮a@u8tHhb@u:̺ftųe-|G|XtЇJ b9Q:8S]`u=E7):z44X ~Ĭ5?8(>O9X;B-ruϙxUuٛ-kSM'Fo&9ԶSjKztm[;ɾzikvc[T @g쌤@~5\.'ku2͹x{뗡oEof%\*I@w]YOSY5Ec!8Iݔ*U+ķzx:頇>CddeO^]ώh< -S$pD!!P`_ڏ_=Mk:~`$?B9eJiQ{O}-ȘIQ5gD;o.z "ŵ'AIHN4P>~MVzV66E=wOP;6:P]dB@d;@h9}hUŸN#FV`qIE5.gCNpDw K q*BU{N*H?odstuN1r~ӻj}֮# 9KB#GEv3I-[e'Dۼ.ZZF]sLDZdŮ߫u˘sdէGvm7<%k"ޗ@X"T쫔y2iރvtÒ=/Há坚WZ'|RfA`4ӮMGP[jgYW;7 @@^.{矷.B;Z3G'Nttz6| POoTW*;SvM]%pX)KҦfG8u:-xK_{E=vɭ ڮҺS\_SS&PmMj f3)#Ujv?׍yɫ)Փ]muse7=_[};멫2쨫:M.#*kƽzkGz ̪YM.k4m \Mq82eeߧfԿjF!eF`РAF?fSwI1S_ӑ>7^' 6GR˄: ISOo떸T|UWr3NO|T: ??VQ3@t`åӚkU+4uHПUOPAݥW~OZҟ猼UuEd![5K)p_W7Aw=xH^Zsz bZ||qtߑG_*Scv'^7A2;d}y5pۼmyt 3Sq.@^1`FiURv??J.`_ϾF)y儂t~C=PGkm%MR} 3Y90> Jq@zƅ>a[t766!|RilةWyn Ё֭/Ȼ[֩/gW|9n0Ϋ?/SZ.ԾG~Ԭ*:N0weI wyG"5k:kl@gax8 pLc /wߕ{N~mޟHBjZDJ?'?6"ь/,X (tK]v[o%/zƀRAld-c˲m@6Ju$&$S|^My$ͥ,y}~RȌ@ y4Ozط7g O-Iv7J}cɥ7*Sfī뾧'7Id$ٯqZyz-H.Z8Ar]hBfgKO-M/k =c\Ѣ9 xK#Y)MG3f\{rgwz5_v}ӟ>nz?eyuG=kl8p\yruؿH~OOv}Bu^ײz]ӟ'n]ӗo]>F߳Yjͬ7 OC~ ^ĻvyL-yiiV~j &m"4Ց1M=W4n[rì?[jock?~YMةCڲ O'TIr`챩8S2 @. \8OGy1w\(f6~#btݦ'ܰ>ׯ  F>󗔔"&z3Yk=ɣ8X2vx_J* )d2nw'58?;b @@C~6l ---VKC=.!^2[ov-^XZV&ӹ+ ?yٶmG}T.N;9^@w^-}3?5+C׾!ommm +/A]ˤ^&TŴi)PȤ]8OT鐀ZȯRشlZǎ=Z h;%C W\|p!2-oDn]Y 46oLsEB.C˓jX>nZgZlO*5={ΝkN'Y'k'gu?vs!_p4‰xvfu'>0Au gKڽ )onٯPSc^sSL9жE**NXNI '`b"JdO/kmN0A>yꩧ䥗^x0iuM7:~8A/YJ#RȮ 'x"R街-]wu{[vE@%YնjV$t~d-b3 ؕvU=qE+h fS s s A\Nv^uNt?H _{?RvDx=]C@,@@Cرc [nZ!2kClt^bРAYyU=bĈ.tI7on ƻ}Ne/%=n?n䧋<}t-MƩȍ5_iQ[Jzd$+)U$YH}c))b,)ꓒ@0͜*E m3ݯ+Igdq9l%>?f|)6X3Kmp) dM%'2@g38ھ}__)L"gϖ9sѣ3Лޛ>|x˖-dxcj\zŶvZeI{駟n-HET/)2B}c(b{Y6VOJA Fئ~էT#:j r_O e+[i@ű l!]>]_vblUimVE }&޿,?84A]*lzةwg~_;;욦=~55y6"@w=3ҺMS4!CHSrmڔzGrdmE/[w=BH⺼K% ̟"\3GHQ-6|닙~:ZT_0,Yz=R[[+'䓟L4)/:P!4phN;ЪQw>៱u$\(?u$a5RYYi}e wo Ǝvw(}nLoxQ_/T8$&PUUe4[*!֏Rv7imu4i>.Ҟto:IZ`D|:U6{ksI,GtG,_}aw^'dSNɥWeLq ]Huv:͉;%cI#sO+H_|yUTgGJ}̑ND#%v9=ΔfSb6k3'w7{ MPE7]?;Nkp37c @Xr"Ⱥ }sT]wɄ muϞ=,v}Y/'cuÎ644t]v%P3p5S:O&9!i2UԷiCIx9;.:GTy /"OLq7?@@\` y~-Z$5CCI_uٲew}ҷo{i&vg2SE'Ӑ!C]7$M7yOT K x‰RO؝],e"jMO/ry "fsNG | M'-o,˔Ѕ^ry]p[eɎǤ5U-(G/w>$Hy3 }J+F(@,+áD\0<2 *K 5sJ&S2ٜcmk8}-۰>6爩fs"/P毖Sn77msjݗdx%צ\!9{?H,oxJڃpމwGE]w'qa@89iU=f?  P -9l0ѯJz˖- +mƍo_cc+t7BOLpݻM/2ÇG:3@2.Ч}泎47^:NRm ΰRB +'ycI)bHu(9Gl;Hv^t=žJbOø=[#WN_~o4CIcϊ~˩w#/S7w%̐>S_K@\%svL_ڮH NHc5GBj|ImWV~zx''4x_|1Jg]-]l?^:Xvd'tՆ5yo=y')*@O> e6^tƌ֒dĈ^Y&z7t0DwxYǷom#6СC͚xc$n8SOȡ^Q-f1{1{1']/SST5K$ԧI9fY>1j~?o˴Y劼rɿT701_+ _~BwW`Tr{k;>={¼qԔVN#E-CiWOU-' z| XZ?w ~IW"+݁`S/LzV?Izy/Cչ ,ɧ\tidrIFg)A }*fY$f}1Zɥ#f zmx=lU0| @@| !g{+RΝ+o{mQ//qFY&(.>vCxO$ҥKcwpB+WڵkYN8 ~Р{饗m os=Wjyǎte"KvW;Oȴ@B7LһEǂZkMߤ,`XY^<ۑP)n8k*uOIWZ'L}TNwUʔQ?8,aRW}jz}3iO[XīKȼ`ʓ}8iDla*SgS}^vR@hĤX.i@h0i˝fI imb& .&ꔩIϻ@>72)fZh| L:)nESKw8#9A/=SWf ^(OTUvoX G}{"PYQ\u D4eg>q+qOzrՎ^M//g!Y q@@ %7ސ믿^|yٻ8);{canTOk kf1GDfWW׈#qc4Ơh4$xAENE9s``{?OA7=WLUj]PUMIOV\)۶mѣGgF0CF'^}#+ӦM㎋NVK՗ug}V}O>yRōzعsZ9H[ : Zmkk ogBgL2O~bzW_mfqU ԥ]oLGMQݴQ?Q{#yBK%0i/9V4=hL,'`QH H$:$^Z[D~/Y[vM[Sr便LMtIK7De]gN909УMU#5zʺ7I`ĀiֽUv0rwY_._*mO%9k2uEG}m6gU*gS`SQ˲nzfqߢ}dOo8@@'Ǒ]| uk_DOn᪫.g'p%D6L:U~_?O_*O//mwu/f@IDATK1ۮjc?v=c=&'Nɗ8pkll;vr-2~:g?ᨬG}]??2}>/W\qE< 'LURR" 3+yk$ݑ-l#mޖ販^azUl[WLom>1:׭4I`ҁTBpίQrQȨSy$8f s b橇e".]JHQ_qFk9 _n1C*je0ʩ2 UAK~qhP/Y뀳z"O^Sڞ|&V՛BnH Ӂhn- so?xoBn:5P/-0fYAAa7Yo}555I}}}Ӯ^_Q)ȩ% Zkؐt#8"-G;St`NUNH*8=iw#:}C\z/qlbA,Z}.9 ܊1WͭV/R7QC,5_{)}ϡ́%>RgVU/xnubwjjտAul/ wSYCdjCO'S 79zVe={Uݣކ TϬuR[IZv#FשQw/:_gmfiը݆/j(HHI'42E?VKKs Y؞D|QX닊OJtٮǏ8cXDK@qR-Nz;O>%qMMZJ⃑z߬Y5O/}I :}GVY_Wdɢ"N8ەŝo~#|_q*ӦM[oƍ#3쯾3gs=V[l:`aĈݻ+ovmo9PQQ2^Fo̙3vtV(Po85A?Lof G9H}~YTp^aoMq!}.<4hH=x4AOC4{!M"I?;/뇌 L=ZB)8A`h81uvphY3U;"`{2;b^NQpSmvh#m@*kyGKr}RġCӪFP*ZAw`g#1c%xİ_,L!Pc%"A*PڣFiTmOCzdU=j4]uQo߫oL۵Y`'tve`[bԧrYAۇF o!}h{A =Bt̠I^јIhǓG|))(P}"  f\Sv+".]j5o~衇:mpu}w[oGvksۭM':I'$\sMTk׮ŋ[ABK/Ԛ:#z2{EBW- #J?_:J^xP޷{nџgy7 )*۟zȳyСC/KY>= !~_iӦYShB#a|=LjgI{gcSy2'`M=SVG )k#  @RjEvjCO]x(,Jvv]gd/  }ktn#<"V @b#%Vbt:@ԐǏcFP͊>z^ 6M6l}IX'X)@zCk7:  i{n#C@$(2Ҷdֈ n2 @o7j}_WP(hP󚫩2Bm$@@pM z| [ ??_n&ꪫ| wyp zxZdP S5 anX6EH   Lx^h dߟF6LonKKK\wu_MeLBh/@@C{ @TPjO^5" @@@lݛH@@xRΥKe]&wqL0!uQ8 '^fp"!  ] 8v| @@ʶmR & vh/0 D5@@pFp3Lkr:u+X1vmA  @6 ~iivyyyZ@ hȶ3F{pY:H=Sږ!  |<6-;֖Q  @ tMdڋ8J)'u: -?`J {Z  @BĪu8 @@@, !O GI#m3O}EB@@ F@@@R(@@C q)z(07Y/R  Z 8[    4d)8G,-khTv)0zM[F@@@ #3Qmuzx=9@@@A;l8K[-g~"  @f J3??R#   ) !sh  Fa)?&d@w @@@'!@ EART2"  @6 -lO0-O B@ AٵkTUUISSX0DXʤB@z&@@Cϼȍ.0U͡.=F@@~rB ;j.@@ P]]-o,YD6o,~?Ru`äIdڴir9H~:L hpoGrsKN@@pFqv{.8r   nZyGeѢEZb OˬY{+PBSMG;> 9Z  ix^e"MH$y@@+RtPCG)''ǚb"//O4M|& zqzj{NV^-vFf@ J( @ FUs$@@@^ $@@蠄[o5&RN>d:u 6L<UUUj*Yl[qFk!bJA%@@'A 5o Y  C!t" nxeÆ V妛nN:)a=zíeΜ9ot… e IF@MMg"@9``:+   Bݒ9`ݚD{@@x뭷Z#5$Niݺu2w\v;3$߮[ @&ӷ9po9DM@@z(JȎ d^*󎴵ɁdΜ9VÌ3dԩI=*.kɒ%C*++?qd@Xc=XC:w   nP0l:ꝭh   @yyyr7KCCB!Yxѥ"*ua^^1M AuuuRSS#*L~RXp؀ _@ |hȂ  n a.m [@@z$O^kbe֭ћ]Q#virˀO@4wH\ıȉ  {̲21Uw t9x0f  ˺udfQ'Ե+g}X2#n g~#@Bz   `VO;V1  &O,Ӟ={O?}}֧XMg [>@N79.   @̢┕ݛ͜1   R)'lyZhc8YPB@@@C_?Ӵy!573 @@ =`Pv%UUU$---0 )))")..2 3zEnH/ܜB?TWe&   !}bک@p@uu۲dټyMA&M$ӦMs9GX @4toDH@q}C@@@@@mm<裲hѢ^5Lb OˬY{2:p@9\#@@kN5E {[=      +RtPCG)''ǚb"//O4M|& zqzj{NV^-vg$@h؅ As N     A zkL0Cee|2uT6l 4H<Od@@t@DUUZJ-[f-7nkV~a+؈\}(]!R;66!    H 6X=ϗnIN:餄 Ç>ӧO9so!ϷFpЁ .ٳg'\&@7 t.&گM@@Dz;f@@lx뭷.Z#5$z_ 4՟b5j%F!G&!$(@@CPdC s@@PqIִ" N(//!!N0R2ASSE@@ )/ =C@@p#4i/`qjL'P,,t3@@, a6@@AٵkTUUYSM!%%%RTTdM9QVV&4B@ Kf a^^6Sv@@HMCԐjhG@2/P]]-o,YD6o,~?F&M$ӦMs9Gg$@nh薈  `Q$@@@l5m_%  u裏ʢEz&Ybye֬Y}O {U!n -g~"eD[e   @3T1" +RtPCG)''ǚb"O?i瓶65q}瞓իWm&ѻYF ! E@![s:h   t'93LUM  tP­P_|2uT6l 4H|>}̙3Gx ?5tXp̞=;2ɈIp17 W@PEf:)~@@B 8, L@p[oeu-77' /O@()6E 4|XA hPP3x*@@@ 'oD?@%mD'  d#GFrrV^)jȐ!e@ p؂%@ك!T }IxR"  f^'dzD]ٓCȋ  QFqh_]l҃κvZY|HFE\*@@KOR|{Qklnn[Fov9''GN;4ew{@ Op"cZaw'a*P1$Pn  C@L7_1Tp+M" &ʣD%jt:S}H  ˺udfQԈ{}RZZڣcɌUyԃcO@@@ 3!rJf*V@@ Lz_AA)VF=Ĉ#lLw}H @4$*E>@@@@@@ @R'@Xl)@@@@@@@z)@@C/8 @@@@@@@R'@@Cl)@@@@@@@z)@@C/8 @@@@@@@R'@@Cl)@@@@@@@z)@@C/8 @@@@@@@R'@@Cl)@@@@@@@z)@@C/8 @@@@@@@R'@@Cl)@@@@@@@z)@@C/8 @@@@@@@R'@@Cl)@@@@@@@z)@@C/8 @@@@@@@R'@@Cl)@@@@@@@z)@@C/8 @@@@@@@R'@@Cl)@@@@@@@z)@@C/8 @@@@@@@R'@@Cl)@@@@@@@z)@@C/8 @@@@@@@R')*`65J5b'~%8H ./@@@@@@H i:Z`iܶU ;1sr?mοH$?/D#  37H⿉zH :eE?i7D 59@@@4 c %XB?4-@rW_&N@@6ώmRijjtCfl䩏wwD<-   $&@@CbNB>kɩJPO<"͖>   @5VPvOĨW-bHpD L*qr#$F y֬?k).I(    @ "#@@Q0C``#$T1:   -Z)ūk,ޚ5»:TGMlx;i"f7    0a l.kVE{ =PO#?   RO$`^3HW   = XdEm{o ~䨷H   6J^GM-hj7CʗsTQdG@@@@Z[n-xAv@@@ j*svO["^\ȏ   hpt,= EB+b+   &ߣ,=`TdMY   d zh7)]Ajo#Y(@@豀nϞ}QW(@@@WL'蹀8"gٲ. @@H@I(tlQ{Gr    ^#vΪI   @X gb~z>_6"  ] B]O^%M5Q     ^ TﶭI-@@@L @@@@4pwƤwϚ‚J   н@g"   N۝@jؖ@@pQ{u}   8A'E@ I.`qںK   @ M]f'   S{Z@F<3R(Q,*G@@ xg6ZS5&   $Y$RRӍԔK   @:b-e   D 2X[C)ܕR)@@ZΐƽFSck*@@@[>ٝU   ӴOGA    `sl~hN|N}A@@z,੮1   [hp뙧dBNoEeԉ  f*>    S@@@蛀op4   . E'"@(P62!  \kA   Khp鉧t*Nv   Y&mu2 "   92gOSգ>{*@@uc;Z   @h z&ٶnM=   @Z[` "   6v/xo&@@@0WcM;a/Z   @ "#r9 Z_JG@@##t@@@ɚPH *+'f^ Q?u2WO\I.Q́q{XE@@R#MM   ) !TM԰9gbIgg6USK   #ݹ3f@@@\Rz-`'{B5{:,CU#6>2@mu6"  JE   i@ xm`T%]\@@h/#!   @v АV#RbB OG&履@@@,PFȃA   $ @@CHdA [f*'蹌woM@@@^ì;475S*    a' Y\< UliO[h   8^S}   8UYzS{Q   LghZ@"@@@ p؂%#۳?>>LB@@d,xP}ܑQ    Q gۢ16ŽX @@.`^FKNr   3hpyW.{wm e[(  0䔓RYe!   X{j阛}G@@"vΰcED@@@#@@}-A@@@7L@@@ @@C)@@@HTo_Yɇ   hpĎC:ҚN!  ދ8@@@ sN.{H@@@0@@@4 А&hA ]ލU   B{q    >w)rUWIKKK؈l#IPfCB>P/aa&J?s`i6t6"  ꁣ]w ٵy @@|}MvڕP7l`ӟ4Luuu~W^yn~yF{91l:^!}`>q( ]U_H>)t{ Fl(6D$PÎִ @@7 $T|[:6Sԋ  @X@f  zڇgyF~F q O=D|0ѣ_)0@IDAT^N8!^~lc0B[4t1JRܐ}n,-^'tԁ}{hwm   @;j׶k.@@@ }֭z({]0Cts9G,Y޿L`0(\sMt1V‹/AO-+N=OǬhpY0jܝU}ocG@@@ Lu[$%"  |zJtA8s1 ~N2E~tsvr n#INr*jA@@ xZ[l61Cm-C@@/RL/u6H$r-^| Lsp˜"gϞ-SNYg%_eذa2i$@ :؁[mw˙0O}?gI@@@ 9Fccr @@pg}&ׯX ,5JNf=nFg#Q"    .!]UQ  D L81jMd1뉬$r0瓽{W АY@6:ֆb4 @@l&imFNs@@<;vĬ'm۶n F*fZ}؋6бU+m2  pɔ|!  dqFZ\WW''uDɄ b4Znyd׮]1层hp8T o:gt @@"요SSH0r)@@:͕I&GbֻZYv]ehXlYd_" <ruE]$֭K0 s*hI   Щ/f;C\x?a=@@p_Kbur-|L1:@6f[g+퓕+X=p@9;v)@@#O+r@ssv}ʓC@@@ Yq[14%⟥g~/9)@@%\3J7o^ 7.'F˯~zW 9sE.ABM9n,}EFCarPBJ#E :F@@pVtlIGHП_/ׯ_~9 h zO>$r?.oQTUUɁ^`=묳 ȁ, "\tA>(_巿s?r$LB-{/#7Dǎ/SXI +e%=zh~(_@@:ϾYDlD3 u/ǒ>SFC@" &CC%8BĨ; MXB/` O<5{&1_BcHpp   7|Sn{%|y'QJ*?^VX!7xw}1;ԵG}pcM3_R2 7 Ƴ>ߕ)xT;OP8!-_F4 o>˺rˈoթ4K䩇-Jҁ^=nHB*z(i*o/}jf}!e-c4<{v<@sPUfT:*o_=ѽ{`wZ!zb~8?v9<.>xxFQkHH?pb/Д@4|"5h\o . +PZOf6EH#MQB-${Kl&EdXf7$H "dhQҿ#N%ӝ@I]0뢂=GD'YI)+:F_{Tˆ_Qctp.CxUFԽOmuX7:O.KU*=4D].#%4Po <6]FPidd NTZ:]]nc37}!kmϑ#qƍ~u$_2$)΋iƯ}uMm!GvzM]۲CտF_ȓd%[ɩk=zzc؉ bum yW_۷[ӫ;VN |8;6\#{nQ7 4J< Hy~0]ggۣt]6]y_ :Rm(/p޺%i;{-:HapDu Ko{z87^ 3į>I 4P@sV~(y˖WB*#p̱vLZ~Ȑ]J}v(-)G\ 'v3l;U;ږKʔaʌ1WIanյ5UJ K'&˴ߑiÿarB psJc1=̍?:F.O||KwlY2~k*J:{ 5_F79kW>LGl;T =9f+8E-,l{TVC9#ΓSu55}UjL-e$'Zv.Vn=!dHErڸeK$wm\W&~ú!3(kw=o]t Tri۸!9_0:"vdb@1z=t 8")k >3YflذU>ZJKKkp 8t zxQ=x+@22GGOۉ' Ѩ_'؇|q٬@U+W]ހ)V3\`_?@|wl"+7:kRz8sԝCfr{UJY<8zjܣVbn 3Oo]hO:Y~,1#>  5 X@?F-ڗ0r>ƍo_F>#[Rgumضdeʪ1Dlu_>/{~W=ÊĊ9پ7 D]Z=/ɧ5ȩc0~b?$ jY~9Ge]ǖ [m#GƬ'U#3WVVZD7 qH ..!&eR#t`Aw _)ƾoٹSs_ KޢW7X_H^.բԲ[ڸw2!zJ?\>Ȣnq0Cty?'e@L}N z#Gt_~(ͽjJ}g&Y ik*.?"CT.Q.7Hl/ϬD.Ӂ}v'!8C/ f͚%?O%>@^_^v:݀"SQtu@snرc[N RQ.:"+n*j&Of, j3pܽ{GWs?&M݆:5͛eϞ=ְ7<:¬Co}G ݜҺo6m=G_iU6mS"o֖n߰M[i#H/YZg_|?*P7i~>S4 1;Vz)+"z~U@ L9L@~_ f z!AQHң3ߩ[Wy: f ͛'=^4=rWʛoa0>pժU&]~H0HІ$[\xq`]t\sHGAmg6lh&mt@E"VI`r5< J$)*rVH$ky6ޝGQޏn.rpC@FQF E +gh֊xh=עU*Z@UDъ\7>\&${3<{CܝO?t;@ ~"-Ӂﶿ^>}ʶ_v ߥ돎@hpA5Z-ΏF@Øb@4 ll9fUw=dɶWj6@(0`hfr=7ʹꫯ?n:W_mg\}Ǎ'\p  `24>pi4cǎ|͋-2+̘1C<53gzef)$ :=XshÆ Az???_4/k\srr:kA=֪9]Ř1c94ՎN?(_-'77WFa²e zNZ4O۶m[sX{>mڴ^x!89-@@C돗NWY 'ׯ\z_i@ydرUy/\{ӦME+6zAВ`T̀͆Eh2³lٲE^z%J3xiҤ]׷o_sҾ;vLy^zY&*7` Yi>':t Ez:,֭Օ߯ßv?Mkզ/ڝ>;qשwnzs,ۼIlIuA1?GVVֆ PB+S]Oʖ/ H}+s2AbZ@M'[dޒ3[i:NϮ?^y}G ^أo<7_q2zh_nӂF]f X`~fϞml|3X N=T3Mj*k~q5*?{3X 5[ԩSE/pii04 ?Eqb}ٳQ՗h1m43n:Qg_3cb8(p/ǟqコ={k@Airp[@$F`ҁA鰆蹄U w Umf ncz1 p!p|j%ȦZWwª>3Ģ[dd# QI]̙C0C,BpN'@%aCrt l*+eAM~^Ϯ].|gѣGz]4U.*5jdoMA Օ~Anv;A3X<#4'f͚eo몍74֮]+/SN9#GY,qNpסqSЯRVW3( z;]!cW72'73b;Ħ nwI~F Ǐ\j g=ۺ+ ͚5G}T~w}g8hfJׇ[li 4Ȝ"2F(4}ӧySNFhٸqc եԌ &M2< hgi 4}Ut*}3#V^mffҼj zH4CeE#QZ hΝ;5e͛c>ѸQu,lG FZP#R) pޑ!n?}n`~؎5Ho ɉRTZ}P?='YҤ;`4Ǩm_i rj+!-SF> FSkF91ЧN.3b,5v,SР!@@Cއ֭[KY_UQۢ [l1#֭[gFYN3Ex`DRhf ׿WDnݪ f~=t*bM۶&շzwJq`q(@7^{UϫB)j^!őJUwԟH1gbF])yr6u5 ޳YAqVHMM<߻ڌ&7߻6z퓙+9T)pNJ}2MZfЎ*2ogPmd6vF<ki,?G?h|~!F/u@Y\;\ &3~/\mW (I%٨FέϜ֙xN4 J{,?F@ ?)cxOt5VYoyjs寿Z.\(7 o6m뢢 :E)7:vX~2]}i7amt:~oih}\ ؐtIō)WƍJB.T׾μrL8 zQI^ox=5w?؁$y#'>;fȀa\6B Pd|oqV]#CC$lIy)ײSgqvZU@r1'aޝaR:z@w_@uY~! 4E (>? 896SeX)2|u"G}g@@fJݵk}N)aӂQ7k6lh>_Osss]c/bᤓN{''|=ZΝkv  2.uv}m40oj/6RDզ-ۮɹsljى=!٧4j22A$koV@ }[BNV]n[g ~a@sV~~<$)qG7x__;|muM! @U@4 l9ף4{B\j6evشaWsCUF@@ 0D,nW/<ڹ\e˖gf{LƎ[i0~;g!O/B.Bw3[̌SO<> h|9(JP A\[xt()6F}/fZ(;vS?=K Gu+ͥ׋ܼvY]$}r'sŽ^U59}ryQIƕOozn9@@ dHWO` d@gL LjRG}r:5'P}2㳻f  F8嗢7ѫ*^nұcQ\tWӦME TU4A{x-ZHGJ?s対J4넎`(##&au~zy嗭U3A+Na1|p0akvւ|`! CTrX\ƿҾD+;VҷvZ\/NV\>{V5_,zVlRb5^yUSps;v.'HuoSfƿ Wv8;wn0n~V)еٙҥWve$Ƨ; f{zHR|tlܿHΥx+͸'a"8Ov%%Ș!',m|O6Wb-ET[,{Y$) 4qAxPs{$90@ 9 L 6jk}<+ۿB@NJWc&c41;\`|).OLOq$;;0(:&mkt0mYr-eyyj|)[ɨ3$a IXZ4m-eݻq=A c(dysl=3JS϶yCCsCKv)+Ha>^ۮiҥcԷ˚F%rTLi%./{vdþvfo<ݤ$#aG2}~rͬnY)Iލ-Kfg!z~fPE4㳻q~ ꯉxu   @@op@+._|Wweœ'xB/^lkvsv{ СC:υ{N~{^j놾g 28t9o~!bڴirAJ3D 8wafw8z= /`WX\pB9r7X.sLyƅ믿6z "%%h'`E8߽+&N%'Ǚ%T—^қ# J14R㢔;tPccx5~ln,N゗;8,!zqLo63X*Gt%Njʜ m@1̛zq=Y8*Ӱ腻'?i3~BV gL?^^ČY4CjZR2g>cdMw= Onƴ9\=@_6#üo|'Ьf7WcY8 .޸߃g3X88[:=v6+{M; cwbew$Oq4@ڰt IGfL3Z៮u?9hv:e+)D^hda#S]mrUM?=EjR4p\?@@7:&33S4gA޿С] 4Xf}s9G:udvSO5 |PtZ rĸ [PP 6l0XS46nx[SCv HH/3ST gq9A3aLܐjiqW?:ޗ_~՗fi1=fƍcǚc͕ݻw>Sk7ЦMA3kh }G!^zgӨZ.38h03낝lbE7gŴZ݊WFfž4*vD   _;fh+9Tqߗ͚0=y<A V1-E)   $@@C~t˗E| +,Vz{|\i"W=zvm2a9l<14"ꫯӼ~7n$''9A?뾺4iƄ]Oxt+<Щ0 ut#:]ƿJ'+4(C|?>n߹sST:g"-at|95SO=ebhPFbMq"oֵ/B?njU}˝a|^g*X4 ^硄Fg`^kx>3XFh% -@oTMwD? O޽}l}Dj}/S/8O]^֭[]7 @@@: 8RytP2x@0%kTOA@@@  ) 5Nx5υ#  @-ZA߁9= !   ^@rOvC@@ 6J veݴY}x;8G@@@j%bc'^+yF8@@@ cZ>cG   V=`-+++FpcY rnIJ1wsC@@^h6@@@* r    Ap7Zt   @,DܔwˁDJKKk|Ez_vDpe512@@@OW۶~   S "n̝;W}Yr~)'w\8\Θ?qc9A@@}c$9C@@@ 0fYxqN]@@\ $ 14LյkL#'  t^\:_:E@@@ V}"w ~8~ڵsD\i +D=   -AkfNɸ_    @Efhx뭷d…F(]'5j$ FT K:f_uD@@@ 4ñ.   @x0{lw82m4-)))^XA[T([23E63`   Jwr#!Gɱ"y2m@@@@X,Ybfex墋.X@ jC63vG"  @-B@@@ r6YcAA_ޖ8 f5X@v,1aERoC@@ 8Crlcc`   D@24,[Lo>>NZgj!<ƥ$\.?\Pi`='323x2   +#r\9~@@@a hr߳g:+ P #P&jqɑ_^/r$~P֮Ǒ"qn"e{KYn"I;m@ @@*PDiWA=Fm;wv\]@@@z#VZIfd׮]&ʕ+墋.7(Yuod^(5;  GmdjGq!   N)'.Rg͚%%%%:  {r  DKU@@@Рø[$==њ5k'k .I!  iBcj,:U4`|k   @ 5}SO${RXXhױ50/lv4 19   ?2bK$-b   @}H O4Io6x衇\{m۶vIVVlذaO#@9 pgge@@@_K(@@@j)րSի͡kºu[}WSy7;Uy,!wbHii JJ   @EW㜊    @5a޼yhѢbD;#Sdވf !b   @, ~o K0   |7ӫ?i"=b   <=c\s@IDATgK   1% ) 0g6"˗?:B@@ ;@@@Xk@ bՕB le'tv|   P;9lE@@@S)'E@ rr9bc8@@@ V\-cT8@@@*@@CPy08a8(D@@WC'@@@z-)'<'   ApGoGϗ  D@i8VNJضUHB51woH=B 4"|w|8#   .3\e,I4"-»"N>p   YA@@ 9Ǿ_y,MQ:A{_-֭ݻI'$5>b)**=zHn$99ٯB1F>tRٿ9O<1u"1(bRܹS.29r ꟟/`_] ''NFIvΓZ/X?XoGD@,]t7x /P~ZĒ@iy:HH1  upG4 q㺜"  @Ji űliOnokNc/¼_s~-[LF%7t=z|J5ƹs皙^u9x`/Y {%W^mFES[:t ۷7tMV MY>0`e@DF|@@@ II"%%F@@z*~8|ܴ)OݳWMwO?|'XuJ1cHΝ}(~Çɓ}|y=޼ysKͬ@xtg}\OsʋzS4cU4u]g6Ȃ ̀zH6n(PKO+xc#<"={}vyeƌvtgr˪U1OcǎY< ~O=V+}Ӈ~Xa}-=b^ 4㷿ci7ր^z]#o4HMNK[oI ]u MrjP<""fP @@bVZ1i8O !pl@@T~0p}jLGoOxfJ~~h[S Ig-{=r]Ǩe}Սn WT37JAA/]YCUZz-(Gs) ƜM|KcY1Ջuk)pr[I,? 󉤠@@@ۈ| @@@ sÏ ;'6n۶aL{^3)U,tه]P1]aÆ_U-xNGUⶰ4޽s 5Xܹh(yMSꇀYs)1~d>OSP@@@ TN'yBuL  k7564s?AkJ*nc 4󂿥}vի~:F`#&Lg+~~C|FӁic<1QǢje߾}2  @~6y3?1AҢb@@eYN  ^ݳ=n>}kkTm=poxϢSXE)\.kk]ǸiӦ }ִ}HrrtA}ja h~'5mo}u-7[vm^ qa i  D}c  Y 3SZW.q?ƝD\g}O^zѣֿEWU?HYYD .yNiqUWL:UnjZ ֭[gƍeРAՋ޹}<37|o߾:! 7 ӧO˗C(((ө#ڵk' T7oM6Iaas!ӈnz%!!9,@@@@q6o   "!/ȋEyhK/T/7Ȼ+˖-RO׃YM&7ty+Wc+_~g>;#nu3gshwNjFhʷ~[L#e6l`TXB9GP_ioY4@A_k@]WHΝ}m7uarRnڴ,X@:;͟T#NQtXp SIN   `   @l 83f`)'*oȐ!2k,3fu0ayO[G4j^}C}٦>U=CݰaCy6eΝ+k׮6WxMJJ)"SN6mThCD@6   Am\r3ִ@@@ <쳢?.LǟұcG)4 `rA^f:hݺ?ݘ ak;vN?e3ÄHЮ];{`!&=m7p߾}?:=vIڶmk[YyI@m  N YXXő ӫ&@@@@hƆ<'-[P*ϡ<'CA e?3SC@@@:wD) L9Qv@@@l8{@'WjjP,Y @@IE4$&    4 E3꓀;IPNլYPS@@@*;a   +)' 񒙙Q4n8O$q$R:s@@@/gv"ՔF    AA h:t|wx≲b {]@ą8  Dqq\   %|7a"J@A@@bF 1q{@@@ڔ)))fc^ݘtn]rt@@@|q5iZN@@@ -aѢEՎfնĆ;++6N@@@ph) ;:vWa=>G@@@ r"1Ƌ@=kY!9A@@* 0NxRŁQ    P) ed@@@[IN.mrO:C@@@ hwC%cϬw wYq5   @ 1)lY\M$&l9   / {}E -vUOs2d1chU/ӧO]̙3j^@F)QKO!̬:@@@ 8R4j)>RƲ?٨?| +K0>I%   (o.K.29s>2.m۶UXZZ*'NW_}6l@˱n.#_4E@@B `21F1ŜݠAkp襤W m0$Vkv@@@!.Uwu^5uԩSe…^|n[eرtP 5n&#i"!OK;@@@ Pncˢ 8/$A{aCqA))/\mIq286iкlG@@@JšaƍOWZRRWfqxڷoo8ȧ~*{ādڴi^YA vG;vMZ,8^@@@@X6>3DXW^2k,ҥUe~'UKͺ.rX¬={<͇9+ PA%r۝rw8FGKv ?cN~A2TУ@@bF 1QJofJܮ(.wdqd@@@@X͛gE֭͠4ZxwEuذav0!55U~ ͪ۷˒%Ko߾V^@ He`̙.@@@(0ҹtj    E lSN۷OV\iԔ)SW06v0n8ukE4VYj+       D@vŤF*k׮{m3f׺`<ѲeKkUve/       #Yڶmj/Ю];ҥBNN]u!{@@@@@@@4n/%n̝;Z4_G^~evU-e@@@@@@@@ zА祔ﵮ+Ų`ʦFGݻw۴ic/       #VZI lKG}$֪$$$ WUeXx5b@@@@@@@"N l qqqҹsg䩧i(t SuaȑUg:tHMfJrrk^g@@@@@@@G l Jt5R_}L8Q6o,6mnAγ\y啞Ud}vNnذ       D@X&O,7fΜifUh߾L>ݮׅ\9x 3)<˔)Su֞U,#       @ 5A.B7odeedl /{p ^u ʃ>U        D@B$ wȐ!|r3g,X@kɑ7,;v9L+AF!odС>R       # ʥSDp 揿|zӧ/F;@@@@@@@p hfqkj+        @ E       Shسg۷u6       +q G{V*a>lvuF:mo63kZIHH-[Zk.{@@@@@@@ L:Uj&$A4pަBIIߞ8;9vXNbbbc"XKѿ{g>ַ,[T`wU$Bj}Mhpi~-мZN|v \ݎz->++nfo,t[249T‘&}.!ru½0xys=gӽ 64viwٳgO K"  .ƙ   P@Æ 6&t+7[Q\n|~B8ui޹}hE.h{z?2p@ D4^{QSΑ^s5+8ql޼Y6m$7phg+=WUVɀdvl@@@@@@@ 3xm&x)8Y]>{'/9fGf{u@@@@@@@P ,6ۯ uxs/}m7u3oԨyY_}ҿIOO}^[o%:%5EOч4W|YA/ f/W0d{֭[eڵz.5!--M42$))Zo4 -Z$}|{}ʔ)ҺukϦ,#      !(uUx%ϩSѺܹs_u9x`(++%KСC{ mƍg?`| mW{ꪫ7@B*V^u]t)עj-D[~5 vʼyF5‹/("_N8/xygSV@@@@@@@ 5O^Ŋ1^Sƌ#ǧ43k裏 ʠIzChefBAA]b/Wl7h/PȜkDS}P@@@@@@@H(,=>mA s` >>,?=Gy{w}|wv?-xPU@>|_ZZjճgOݻUf܂gvᰊ       P{߹ܞ\f)..4rH{ , VYjh^~ ?|6&&MI56xxfl϶i"m@ٿ]X~vM7ңGXF@@@@@@*^14Nk~"7l ʗ_~iVڠ#}X;is9Gy^}ZW('Nݪج>˿ZY'J=jG h79yEʗ[ohXl >\ &ZqqQ=Fe@@@@@@Pc~:qj;NP~`#GȄ j;O.b))))ߌu@@@@@@@ '4v|ü{~mwu͛}>fiѢmΝnvE6X媫`>BVVvcAg= ,:\ HX~}z*K3{l9t_5       @~mś_uOɉ2(Zm;6ir=] |_B?_u=zh"ٸqܬY3;vl.9 3*4UJKKE0ZliUUe{[nn arb/Ķm*fggK^^,^6j0|ш7|Ӫ@@@@@@u{>otNzLRR9"͓2ywnB7{l~ZЌ={?3{6im۶54/X%33S;y'}Vugq3aq2uT{@@@@@@@ M;ɯW5>~#1$f}T4wE}^\ق>}:t >heMz}*Ȱ`A{0`tyX?~]{˕-x2dHe͢> m,T },$&&oᕩߖhM       :ri"y9%QqJƩehԻ ;ѤI̐oݺ_uIiilܸQ 䳝UʧyڦSRTVB1ֱZ/oe~3gڛuVX(a h_( y94..Nx{ҠP@@@@@@@HLjѫ嗧͑*W>k#a<᷒ C 4BVVyGʙg)+V9ˍ7ho۷o r߿_,r饗}ZSg5 K[W\)#F0:`Ma׊?!:ѹL2|pk֯F$)))1ذaׯ#       h i@wi&iӦM7dެ߿\ٻ) Ebh 4AQ%jDb[%bA@Tb( *UzGnaoonwo}d)#ӧ뮈]ٔ%%%5Zn]crIB@@@@@@,ڵki Zlz#<"Anf1CΝsq@]}-nA I?UWq#zݦ8:GrJ0`U… < ,[NN8kMgϞֺ‘#Gj|/xYXF@@@@@@&,ۄ{p[hpػw'v%Ԭ2m#K,[L{zD=C4M9ѩS\јO?8\ )$ZF j:KAUWWԯ͵s2srr`L~[WS"8樂]t4j"~kp|>5*0͍􏋋=b s|^9w:n7%nosM༛< NO"lu ccc%//Ϭw':8@ tQ+SۦЯBР#D/W6~i8qb :D<3u@yyz֛*)))FFNN_˦t$w3-ui޼$''KqqOâ%z3Z=0my 5mV/h{}H=᭟pr@kns@'$$8 Tٿ9~slNiu f cXs ?sٵt=LlRѣ#`oopzYk87%n^k7uRp\@;+ N"" PC^c-+cǎj_^.":v(3gΔoVz       %)'9u&sΕ Ȍ3dD@:xAOAqmIjjj<        @ U@8b/3 ݻ atɼ#       @#B:ʕ+e̘1KYYYHKKAȑ#gϞB0CT@@@@@@@@ *BSOɧ~*C+D*@@@@@@@@ ,D{=GLk@@@@@@@h! hشiTUUYzs=Zg@@@@@@@@rrrjhBtP @@@@@@@Y@C>}n?VÇeɒ%|"        (2Fd5j9sfuV@j@IDAT@@@@@@@i ,AsSO=Ւ믗Bk         BА&_}qcү_?馛gO?T6n(p8z5#       O?-[l10p@YdsNy4AB@@@@@@@i@,}b@@@@@@@@Hh(@@@@@@@"Vh8       +)'.\(#zu       $ҀؐV!       @r"Ԁ       ^ %@@@@@@@@ 4ޘ@@@@@@@@KX/-{qq|7e9p񊉉K{rgKBBBDE         hp82{ly`zRSSe2a N@@@@@@@ hXdr-?xWXX(:_=zx|<@ <lnl*<T&Uݤo?qn    ?E**DDR@@@Nh;w?^|V7oO̙#cƌD %a$vT֭Sw8Z*  D]MkݴQlEw jC>Y=:wt!   G  ,+V0]=)]ԗyUYY)vݻw[_/oV dnPs$/4غ5$v:)Rէoɀ  @i$1"k ^' DbN ?i> A@@@ ! h(Q72u0CYYe#]v<ҧOku#A   D@7o,q9䓭uoNc.q8m-v pс gϖ_]֭[;vEEߧE`~oGB@@]VP]  D{`bRݶq$߳+-[Jff7N&M$f͚ɠAᆱޓX㕔$FE ?{A $@@@ *ʚPo*  #PF]wس( /jaee,_ܸ/;Į /tc?g,jkGjZφ8kdƍK/h*) .N:I>3xoذA &Oriɖ-[rܿ7?ORRRձdF ک @@ ? y") S(  L`1T TT쏢" g۷_222j]\p}/s-Rk$P'hfFiΖ? 7mVE,)Ҁ [l1ʂ$}GƼ)jewV6 @pp?$@@@ bj^W_mx/KA@@ U!Pܛ:u1=}/(RRR,+V0p?i$kS} z:_=餓dqjU nBРկ~Uaa„ ~ʸq)+3fv !   p$&J@ HR@@@/S!C ޏe=*cӦMR49`UiiiCfכֻgoذAt'N%PmQ +@joܹ1dʔ)V=}tYx̞=[t~G+NMq/,ڵ3   E{DlV`H@@.%)A8ݜoL]TuSH7oj/6Գ`u.غuk3f̝;ؤ4hxƲ~sls,i@n]EMttqA1Æ gyFzyGJ!Uڴi#/?vقa!za1  "`߱=0.AS "  hB7Ή 54t~3! h{111V.Aɹ\͹k t UPt}OCra'N$&*ucYg@L9ܪm۶ :a?824EN@@Kukò]4 @@B'\G>6>Fܡ!Fiq|s}ž@ڧOzP͛'2w\XwM蝁njU@CUUӯ_?/k4ueÆ rwmDθg@@@@̾@@@ B'''SҚyjg{k3:y?2[ }ld7|{=B'I&Yu … EӐ!Co߾~`͹> M@êUdСrJIIIGO?zj馛jlQ6dPCvMH"  @lzJ<   Fm|8y^rj4uiof 7t$OFf0 ڴi##$<ò{ns7xBo.gq|Krr'ȬY]+muݔ<\L"r,[g&?-=jË/(o߾F>sjȰ@t <[^^tw!  @8~ 'H   J⡃d pxI8L; b8Y@];KGڔaEAQQ71~wg244ok#(z/]5ֶ@ͪK  K,CtČsٳ|JLLk^hm zgyFN>dYxq} @ 5@@w.Y;C@@j *጖-N:*GBxG-5k믗^{Mk#K.#GJYYW7sLSV4n8+cѶnCsmtM^{<쳒Xoݺ|2m4[EGiƍFnLGanS+ms @@"J #9(   pM76^}U/wUV_H^Wt͘<Յ{Φ-#4J fp>\RFV@ *lEe  D}O?,@@@ Ə/k׮5hٲu6l15\SW#@l%5ပM   WA$Ȍ   %!^;cjjL6M.M @Y]u(C@@+QY*3Jȿ4@@@"@ #4gܸq,@MKȋ  N &pYlL9ύ@@@V #44nTn޼zegg˔)SM6ơz={ ~}9jjJF@@(lG.!  4 v O> iV@?,Ç_ע3F= @3{ng}p>4C@@@ TVLVV)@@@peO>)z7|m0+;M|)&.R)//w:X_Z`+- X  x#`&y=   D@Xаf# a֭^ AɿoIII17!M   79A 4   D@Ghسg=Z3jJ R's'ϟ/W_}uǰ/`+ `AO)F@@p'`@/lC@@@ B0i$ʲ,[om۶Çe>ׅzJK:th]zիW N^Tm\ .%#      :4̛7OL͛7+Vȳ>+ݺu37~yɢEdV>!?X2(@@@@@@@H^}UW6uSߖ}Z<]Yf>#GxZ@ 40D?9F@@@@@@ t! h1:W_-cƌYnC=dGiغuDNDJ@@@AB@@@) ,aRPP`Y>Zusϕxm۶Y, @:=5#  @tU1׉GG   z' Y8PXeEн{wٰaq8 (r    @[EPթ];ͤM   M d#4D֭k\nYYp    vz={$o膪k# B@@@_! hh۶m>^ƺ/+G;wZiZf@@@@uuvWl_JyR]!   ! h褆GtN˖-s^i~q\FC/a@@@#PQ!: )V=ܑʋ@@@@ :OKNN킞^⮻3sA1XB/^,/:KƌSc+    @4 ${ <%H@@@@Hw!Vzˢ˓_~xYY2dnֻt"s̱Y@O!IE  L@/ qUW||[B   G SN]6m`n`=2]vu E;   BΝb\ >(RYVP5   8d>*ׯ.uQfΜ)~9(P]ЋzFDBb;@   @# g B{u   D@X4#F2S~~pa޽M7;"`;xVPqlyHoe b/ȫΘBI~w8ԫd$͡<@@ha6eKcD~2   =&4uѓ$`߻'ݵgeIuIu26BR~)dn@@@Қtt:st?z{(@@@h- LW$· tPCҟDb‹G   .[qu( 4z@@@ ><spTV>387*~ľs&@@@b;ZdF@@@ hO ;}H ɯ   kc߶%E@@@TOȇ> st\b- vԇ  Q"o*E@@@PȆ زs|;0G%7;R%  D#&&,}$HnnXF!   P@l};״СCFӥk׮سg&i  @"%ۮK>7P%:u='c8w M4=E}đ\EǪCjZ8532*   4Lǁ :N!6?ODEB;r%n>"Kadn_KEujjClI/{s @[R]UЅTA j}L:Z$&V[4H!⨺xZOKb߽[j:ޡKX.փ.XJٕ[.*.qs!26ѿo;wl^&=ZZbo/Sn.YcٚrIVIW$9>(U׹|ٞHm"+K%-CTY{ԟ?^o+mRzIfҽկT 2tQׁ5IOʔϖNiײȀx%`zuD3F}qmrtvWK?fUڮɫ[p$g爭`S 5U}`z@n [Ŗ.slQM*`f8eg=rdTTJYeqnlWDt֒|lsQk(95}~-[U.UV$IHjBe4T/v寐͇R@ήd\=>&UZ'ǵ!RRGl*.˦)Pחߒ r "1{H&@"EH"gqis$k׮ @\/6cGWn}%PZ*1褃*+$a8ԫR6hѢFlI?j\;/VSR|9R14(/&@W{_l~\+ku@(\+KwOWA *q1QY+ﲬ%֞ \#e(ۯ6=,;,Uޠf-G#%:>1xQҬ7UEy |zСM͖'U]_2mNZ$u_SNhw! n؈.|>lvt@KJU ۞,w{klh9_b>D>T* .[aRt4`( njq|Tx N>,"|* >Y5X{Yo!&ȘH˔Z5ʷ[ ߺ~'˯[Z&g*#P67?7yTWi'*Oi)=du zs:Q}ǰp'pVuNlsOgOdYXy* 57Y.8dO1}JcjG̼DK{比/]=7F /=>5j;v|y[I2Fr  !z?  Lm_uRȐ8Ibv$R>9/ @8 TUWȻ+q0}7\[./7>a0"q_*yso<(o7hw5\*c(`,#> jJsa荀`ljT7%1={X@ Yw&Q0s r-ۤR@@ hϯJݼ3_zȱo %o dɒ%ʷ~+vj cs צql.Hnv,+"f2j-jw^8ntO?TRҥK޽޽{M7$ /o>\zk׮w}W-Z$z^L_~.{ܯ6Ke7={N^<s]V6m 뤪O_  z=j )xu9-F:=|hs;|/v7jĈY3d` ҮmYFhA-JJ$~R!m#@YGjVt`e)ozX~>r` t^o.[֩үEqAg IKڤKñ  BFh!~CUqV=:B}y aʕuf}t̠x7 w 7ɓ @fϞ-7x|7ntzԆnAzzk vmLHM@ ~7!*@~5MhWrw&16Jm>34KY+@%v (l)SEFv`6fwO~1,J+;MyU,zӫVS3+z,wv: D#4u?o0++K+;v#GȖ-[jl#;8D8gyֲ,0h޼r)H jnJ]@xgEFtg̘!?QF"==]m&wtx^{M:u$#Fq^F.\(Lkws#eY術$!!fǪ[(s$T6ky111F}#MO0r,"KN>?w_sϷ6>:Z\-WI%+WF%n='NF݈-PX<&O̝ˍٴy9nuA'OFH¾Gf=j*= [-9Gּ-Зrzo =[_ƅs@sw %@@CFx=d֭F+u^Xzj״|rMzyy7`3;V'&Ls 2cd+Bzv%ӧO߿1-E6mm ݎ;ybԩrYg욂[ $v^-24>g@@ YI*PuF@L*S9}Zc0;M<GweZ @0L"}ٳgKQQQ}UV{vR [Zj%uOپ}OoS3d3fٲe2|p+^p 83ks^޽4oyS[}xaRXxl?*|vXG7@=\*[H@]BB29èeJgٗ6$mV-S5XQDMwQ%[=#WFux%"WErgU`Or`͓=옔(;3@'S! @ L6l^-.N8_O[~I+#9,XfN@C8:m:VG-K843蝧rtȧGtpN:a߾Ïv{0Ùɜ6\]b&=h(=Aϣ(<@ƙfs/Sý5<\\\ O6͡]4o!gz:=a, ^ 2s!}.ɗ9+C@5ta眇>rKۛmR{ˡ2aU9LFڐhHo#tH}NטSWs~P Լ糁Jrί{(+7AuءRst=ZCp[>wOEo}YbU$U% 7@IDATssݷV}TA~صiuW㞎O۱+ꔔd4#rk ק}sSP TW䮉#(+F*mbWwlZWI>>BZU&aɓ'[=z#ȣ>j>,\PN*wuul.s@`hZ8o0O:$YYYƉl۶m_ׁ6]FmjD$1 t5-鎌|m!@~M˲cdK*߃ yw)zZӧҥKFZ'O=Zm\m[{^ko{  LvR!P(x-pnJ5YcSJ|_(qiZHlpQEOGiOmHkD%IN34Uȸ֭|c@ ^u0ـ|lyis{SzΖ+Rh2n84i <@$yDOI_III$p qcǎu{HcPsΕ_|Q?{zyyY2"=!੧*T K,1Zm+oDЭكݻś&aՠqy2J%\"7|<,C]cvl"UU-ӥ;ѺTynoT{1]M)?r2?|ڌ9s{M H`AdE.l[SUu~*nIŎ1i;J.z󧣇vָkkē^@ :I| Go?I*Nq&#ǐ0bqro{']Y,1KN|Mv|:t0pỌƟ/ߙ6#`dž6b4AjdՋZB$P.+9?Dž+!/4;[ }HEwI^x䨽]}յ2m uТE 袋䦛n{ .YU]̤?"=!`s5|֯_ot' 5z6\eb XA[%7HV MA"̛a3[FQCk>pvԂ*CM1qGz] 7~+^7Jل 9&XMEJ(%^D]!! $ʸ~/KTϧMK$*DZ7^z(1As$E7zԶti9FZ;A6#C2oKNMk&#W.lB>8LPH @8䮍SSF4āEޏXoL}]5jitJn5k̛7O|I#ASI'Nԫ5zWXtpDCO?௶5_?Sa[둺PJ`[O;?׿e.@A4!Tt9sÆ 8us0n_lsVpV=_mYge-@ /zwwy^]^sG5Y1z8Xwùm vu2(mJU6b[aBSN Fa*p*DPyb+)6HVuV)RvTgt;j#_}.Q#t=}*JH\݅_ə򻓦IEUl=P6g/=F;g!9P&ٝD ߷6iA,o.S?a5}3g, eZFgݨ{O 9PVJ*r%Ξ(zʈOBrxfIie HLUNWT_kov-kOPGHMࢦ;;! ~/pͫ'ԙGXrbu؍[Eq"D7nA껄WcC;+8T {BuRٻT~T0CzIQ.<8V5X,#_z!]UeWrKvI\bTVb\3um)mO'm\tUY$tQ摒Regiy;KV29TQ9#Uw8\:tWER?Cs765͝:υEI]S*P7ߓLfpTIrf2 "#sO`kT$6γ00;*ϖ~[G?sؠGgV=Q=U z{پ}5ڽs>sYe& G7flZ";套^2ZlWGc0s'$=5L[uTOABժkGtq$@?bۤF2/9q3MnWP7j+;T}R)C[:)Z<<&gS.R hJq1IҧhPۤR~}!Uؠ_ %=8_uIujvJW^Gf@%PG=(?L=D@ausz~])))RyBPL DZϤOF GJ#Ckt"!.P˽6&%Ҭ?.{}xG0)5D>\dРr-tR5=ʉ'(V2У4 wIϭ]MmVE\У_veV);v{Z"ܹtEve:55Uzm zj}իMjذ h֬\{3Կ`&M$w/= ֭[>ntp#)uu-=wTk%?| z~#9Z띧U?qԥ6#  @ 8bV^;֬j5Zџ4mqI]<"!  ["/BңO:O<6ʠT=ذb aj=]soVcS} z: J't 0X T۬ |\ʒ#GJNNQ:g\z6 ұД(LS H :r>JQO*[ ((m/9ꫯZ j(J=nݠ=u8"'nJ?T5:zf@@@zv~S~@)zQ3K@@3q$5QH6mcsN7}P3 \ӄ 3GJݰa,[5<ĉk TjTʶmd[MɭB[#GKj"?qço5hܮӧ[\!atpwY|ju7sɆp_mswZ\*   Lj0K{NŐR6SޅY;i  (غZs3.)͛7[#/^ZoAn&2mcƌsf̘! 2w~XW^ye,~I.뤃M&ޖn hO_-Z@\0G~ˣU:'O,Ij׮WC0ɒk׮G{m̝;WlۿS_V6?f;a:̠x22Aq0CC TA 逜^$>@@pyXۦ߬s2!  A)?M<BcueЅ鱴wcV,[\??n 5g?YsxuG5\W\);wTfK[d?]mo]~󟇫 :T.]*jL-{Ҍ ,К[ly˙gKEEO_D/׋9@@Pq5d!  %HfnV6LW qƅYSScDa>pBO?----'M|3v|SŋM6M{9QI \Xr".cCz~Qk5w(M_H    7+>긘@@lP(9E~!RqENϞ&Fd@uu8p _z {6>30lhhe˖z*O&Ac aCk͛'Z'3( :B,(.>'?qs$1c;7@@@ NЊ8ϰgivB   @LF㏩ (c>(C.Ξ&B0ǏM;1'С. f̘aδ0zhe]unW T9d_MFl"6Nsy.lƒ%K5B2' r%cA"t#M>z4s4H6:ͼ|i=@@@Hsݴ4#  @6Bs[¶i@@@HhH@ n+@   fZ{fy-gD@@@$48(n%%=֣   `aa2@@@F  p۸Fm1,@@G 8l= *   Y*@BC^x@&hjMsfc@@@ K!   $4df|dʠi @@@d4 UWؓʫ{u.'!   I$4dd,-7$Սs+"yq    @nǟ37P@@@G BPd޿X5 @T`/sGTD@@0ky ӧĆɻ-VN;   'G@ -tf]ӭ_UK+98i;'@@@HDD80|] ޹C\uGEt]}{$`+F=    lum]i @@@,@BCg @n 4|$kC@@@@@@g Q!       @6 АW#       Pza @@@@@@@l !>cG@@@@@@@$48       ,@BC6_}Ǝ       CHhp!,@@@@@@@Yl@@@@@@@ CX       d'@@@@@@J{PK\.k7' @V А՗#     t%K/uu(_>@l ,?       `^BB@@@@@He]Suuuo>ٻwϙ  @l$4D-@@@@@@bXn  @|$4Em@@@@@@z<^QQL8s ^)@@@@@@@ Q/X:,yWͦm@ !.9FE;zTf ?b @@@@Q`۶mRYY)UUUw^9t 8PF!Æ Çː!CrJb3g4 $4det e];%gh>_xIbN?COt   2֭5ԛ_$0$/j#PKMx֯K(Vj@@@^ 46J3OIZsxzO@@R5ºu˷mxbvw!CAlʌ2Re,Y[oX M    x_}E4/yma  @uu_ SEͺJNUU_J0XO $4gCb355s/O@@H@{-H%z{6~`I[4 D ر#tx6ƍ{6  @ U(Ϳh6@@@qn,[R:  @\ x6 Yr"L  1SQ\#C@@ Zh @Pa-[ؼėFϩE0Hh@@\uGQ@@@'Xi,1@P"lrJl|>QrdĈF@"0DFxᬖZ !  = 腅=UJ}M|fF@ SN5رC|ωܹ5rHzFA8N #G,l@@@t qU ߞ<  @W\qEO?-s۷//z)U*V._aUL  )-}?RҚCD@,ӧ7:vD%B|@ 0CC{!!j7tR%  -="ǿhH\|orSD@[nEZy7ߔJ]];vLTðaLj#dΜ9pl J^q Z4c    `@lA,`w Ȁ̙"gkPKPHEu[XiYcq>34|h@@ͶA mF@ ;! Аg#)Ys-%t  p!E]@@~Znv n@l+ȸ@$ ӗFEZՎE   dXcYV|̱ F@zЍpl"o\Rv9T@Zm88V@,A R%GP@@@;\Uv6/n0v# d͚5j*3c#R^q JWlbT^MF@@ ;<۶:PF;|X2[q@@@ [oe&0;%СCSO1.@ >6 ^::  &`|kQn<۶9@2L 6l`0zaaL6Mf͚e&2 <8j=v",@BCF@'p\L  t%gy]s  =*q!Ğ={\4?~̜9|L0XebS88A دa薶Fc   @{WCC6=jkljf@@ [nC/3fRHYO.EEEQ@ 1l@ %,W5KA@@ГlQ@  ),X '|2" I ! tX-஬IC@@lЋmk;aW͡ȧl#  ;zjsiӦɌ3dҤIcy_4 BB @ hQ^6BF@@ kE-ugpZ J V Zk  zfڵkl<`ǢE$77WN*3g49rA $4dg N @@H?iА4o:B@ *QİuV3Aаa|[[[Eܠ z9Cqf2;? @ Аa @Y2P   Y3{BD@,pdO>dqK[[_Lpxd`0hW[[+f{@IDAT-3jǘ1cnI49,2 !  @N6   B&wé!! @V x^s3fohh5kք*++.۷oE%FP@b !v+j"8p@@Vu`-=HMc  }#GJ\7R GI1< pSTG@@R'Y|$4J3 %pasFիW?kjjp\] @$4tm@FF`   @;O4]o'ZkHSHA6 d@Ek׆vխ¨Qdcʔ),7ѭ@$4Dwa/ h   &w<8ťC`@ 7n@ eSCiӦ g@Hh͉Z &G`@@@ TBA%$伿da .}L 9@Z5@ )zn^R@@@ QuIMfPj--:x@ @@v2|9sTTT;@\7I v Ԛ@@@ <7$J) $MpBӟ$/'o:BQ3fH#@@@O8sy[|瞟@2G@-9rJykhh+V%'N38CN?t1bD  @x_8{@@@d RQ\񩃈_O@2E/hll~LnP?!AY~xeРA2{l3SNכ)HRN @bKA@@A@kkMIѫ zJJS@,B ̇5k֘ VÇߘjɒ%#//Of̘apiIiii  @g Ίh@@@hF2G;}֓Аi:A@ rrrd֬YnM6 ji0F1SJxPU^z%qbA8Ns\H  tP3$RU7$)  Ʉ ٳgO8A%:P@ !1?FR#`FA@@.W)@@ .quImm9;A-QAA_8Hf7T˕  dڒhޱ]Ǥ4:G@(--+|466Dv0jHPOt@ %'4+ڒ̐KI  d6S۹]P)# @dѢEr}uC͛e˖- l . [Q@@@@ W8jSսJܻwI`({:U@xCɓO>iUͼp뭷sˊ+رc求;w|N' @5@0s   @T׾2|pB| a ntwjkkh~ɒ%K ꠪SO]w%mmm@ 6bs (sGED0   @{ׁw𙫪25 믿.ofxrׇ˗O?26dȐsuҥK@]%'b& ͘  8Y@kq Hq2! }ptjnMrrr3<~^\\,oL֬Y#ַ3<\q,=bMbs (h~[9@@f]sm'W}}6_ Ǝ m۶g6 ݆(4φVi@@U@/#vw鍧.g{s*  @ 8p O֮]z*f o6Z#f-)) ' @$4E5@Iv' X@@@ <79jZ]!@p1cAF]aݺusiC{ mʈ#l &@BClNB2^_Ə"  @uCOZ0C-  'F)8oWVV&F ?Wjgyܗ#nw' = г5@Ќ(   eK ss- ! @vdPeӦ3P?}rg6Eu7 6=Z\.> #(p<,TC\ͽ̽ @@A@@P [nd׿uˤT-[& 3R]tE׮]+*!\{OFQdܔCd|   $hI+n\kbJ=@@ ,0|3yAʹDqĉû:.-1w\9s@]حdfet  $Qa}{+*R @5{TUu~=8CYY̙3eΜ92{l4{F M@$48 L   dfw?7:Vtch   AKk&Ǜ jI Y\.,^X #w z) @& }f  $YRqKru:\+bâ PI G]hƒN-Ku`SC$.@@@`j:gN ׄ#  fΞ}_^.^6]Z/B(͔ @@fhȞkH@n4oN9  t'֊wW14#5Io~)]@@@@ 0CC{!Y+Ihڋ@@@c$ W1SԐ\zzC@ mp&Mk6'xB֭[vww l 1U@' vGv;iĂ  i&޶5"0ܦ&i1[I@lXjU}wP7o,vo7! @ yה!@ 7V&i@@HuKڎ6XG7H @@Tfh+˸@ t[>\kK4  Y P/|EB=FNjD y8Ď  D. - f@ !.7ELܢRAkkƀ  )_Q> cy?m#w>BHqqCC@pezw!AA ˘3+++Oii >꥗^5ق2# _ uî7a˓yK`K1e{q'/t   @2A>t2zJI}R ϋVW'mW^@@fg؏9"^|,=Hav.U`L}n֩ni=)hq}Z1{;~7n=!ZCC(G9IۜKCy@@Ve3z]<>d,AQe|   cHhp̥ d |SduG?-Ho#Ƹϵ5}%0rFƁ\W<њX~c_F=1%hm> E/)UU)KwI mkx_}E|N?~ȽF|@K6Mܮ={#׭m&1*S.]EOk6s ԿZ(`5oc'j L]Jr>l,A##-AǓ#ߘAKwE/)4YCU729{@ YO Z.u-A}'%}}9XQvyS6lz}_|}ǜR?TGd` >kckeg/̵}JجV+&a5hg .".EQ[-{1]P@dr*Nh0MAqƇ7M޾Hc$yֽ/ݔ\?:~ycnLJjY./c_}rb}}ijawl1jƀEEf2ycsp:eH ur%r&&iP/*.>)4ɶJMi7Je˄yǸQoQ7;?l>9co޺]^dˍefR]qSHf(F*H}{2>ɕ:,si2H+ȃдIFLR`ܨ;"5O@8el* 5G;r_^n&<1>3ꫢKHzPf"E/#a?H>sA8԰EV_xm U9|0d̙YW>446HIh[z 7]o]s]l:k?HJ ʌO˾`ծhF#i#Mۍ#:+y٣j$8T;U꪿/ʁڵc_^LQ0r))8O@@5A,py|v̸J( \6'R- g@ 9ݻD%S: ̐fԚŽ#=FBiQHl .4f/>h_Yޯz|?rʝ1gW` jƴKm6YnryWUb}˺IɚkE%ZQ*/ ~S FF>@@ &bb `@[o j}#%IW̳,|FunV|Z2=Z!@ 4'߿I*Ԓ A|b#ƷF;< ؚv=Q7_+ud, ݯnN,yQ)oE)m\duWgh߸-5d3Iy @PQp-o&@<ۺi lPQ/KQ O1 i0PTB[w(.5\^g̰vas   [UUUoʒ%K_7U/`LWj➊k6+++_+Wʑ#GBxu~y矗M6Ikk"FDel#g{߄dD%c;_}s,٧~Zm[%)!ԮKcKS'd7ƔPӰM{Ӣ!Bh,)!tYuYsfs(cS9~ =il;$V_gLq  Ip`z3jm #XS"cͶA֨W{s:   I%P?sxbԳ1cu]'~"+{ߓ_~\nÝ-Z$<ԩrwqOO1c@삶P#G^ԯ%?*)E-x4~v)5@ ^3{Wqm#o]>qo9PWmi,Aww:foqթww99E5j{OtF]Su:@{\54L &a@ :X1Gt n/;A 34$RN/5A&7n4?xgSb(>O~ȯN ַ (Vޒ ;%3T[l|+ƺƷ)8Y wًOIf.?{"x_^.jޞy Z;L(-;j"k>P[iթF2yqWe]47c? `6tMr>g+~.<)@@ !IL͊j*75Ü9sSORsݻw7ٷoYgҥrYgY{wCV3?2eرcEŧx.TKPz#cr*;Eaa|N:Ig&2;֬Yc&69jn|b+R `> ! j>ӽ+3f?Wgϖ\>bkd'4 j9$3gΔkF򗿘3 z;ve˖ɥ^n,V}nڴi~Wf͚e.׿5ZbB-Z~bѢ 'm̛7O&Nn+ 5-rg9KU_#EEEש[6}".&}mR"BgZ#!Ѣyw.fhSu~d5XFW%777ZaEH;Ӓ8S<^\5G6<v]&4sfuаͰ*S nW|ouB{|ޜq; j(vπ:SN_]LS*3:OMr 6 n*ڄ 6C9Cp!$!oE) l};SJLX^gIyY~> 2,x۶mdbU1!4:5sZ2o6w;hϮf$Hf [ng>ٳg %\U 55,)) SoTl---fhPE-ޖbz)F2wܨuztwZf_<^o7&sՖ]ǜ}G,3S lHTMԃ{ ?9fQQ+ʡ-H;liZFn;q_IZF tYzPR#`=]#˴Ǽ7%v?JbKmԊ.ப,ykv^24e}%h3Zk/`罆X>"!8]e@ Z<&Yկ'^x!ox]Ƨā74B'Y5^uO=ݕ믿^T*[n&S;/F@7(ceH<%>i! ߂&n,\v"9-$M讽+}ZAw_9 k.u w1@G z$+4Q1$@S;u?l{ܸqՔG7tTCo  m>|xwUS}Ν[5Jc6cW^TԷT{&?pƷq-Oct9L8XR:v;?,$w6plgH-ݜ-1u0@t0פ$A{$((-y~eld dpl]te T9||7Ξ:u̞=[N?t=zt龋aÆu_ã*bZcxăbx#Ukoذ|Dm;rj쪪hUާ>;S;jV S]WRV=Tbj?3-ͮ~"F[w*5r:ݱJ6vljE +%-nWܚW 6%}r$US۴WEM tϥyްm(`N.rƱ*l r5m5S-W뢼$G oot v yo/RCwuϐbJ~qzBמvL( =9NgMގr' @$4tmcn ' nx^|wrCγ>[>ȤIb)C%2 ϊF&#P1l#by{^__/eTj}}3gJ!CON,tGĒTII"uu'e׆ : ` X4hT"Z[[S 8bs FcDIJ,>גg] >P2ĸHAMs{ZRYCo2a„Z N@j#QXGVL{@` i;B,@bؔd{2mpx oDrʐ HbJ`xg(z!9E-HA^%'Z*\jޙ3gKR!1cn3`rW-n(භdF}Q?|( 4P1ئ F)ܔ O2?\]֏~{R _>E9iEp,t,ڎYψn.^=>pfHV%rɫ}$~M`**g6PV aRe)Nف `yb0mmX3t%pR1tU%qwEyC:.#Ӈ~JFnܓ\lQYPvwc+c+%򓟈\ еw]# Hh+VȂ vjP^^ީ^իWݻeĈ>`:lmmEGN<}SO>]Mk s[\\,]v,]|…rOaK%=ݻ[]]-SNPC}~+Nف@R.\ګ5ьDL/XkWIbmxmv-W^-ٴQ_t㍊xsؠ1;O@݈'eˡf!TB؁i>-zE;>ٻsϞ/$ $XTEÿk{XN{~=SN+Z[k/RT@EE= _&qI2I&3{&sg̞zZ̞gSP% ?R<+[Q70T֔1۾h-2} jU__jp%mSMFˀ~ʸDj?z5)wyelכ216낫mCs&쉋2txK@/j.ǟ=>OGԥ,ItW#pJ`d$Ҍm`;YN0EVQtLp^MM"nQHƦ$c!Y;fꋚVKӨ7䚾OשfqZjpA g~ZC'34EtR3%7{D؝w87b=:m*fo?VF 9o:vgz4GNAV?S H(hh? ffh<(MҩfKXC~͔"5ˆVnԪ׶>W ׶Pm}C|(umحPNG ½s@kuy"wHǯyyb{):ZOGFnZP{Zrd9[;-o{H:C?%/TT<̛,<-;k_N8Y2vG̔Yrf8yz~iߖʿ,[QvTrP3}JfV/3?|L[oY3OG?3bǴa#I" p1~ayg=>D/޼ʦM_L/|G 3>OJUU3g{gg,MX?[MCFUt .@N?tٽ{k.NU?i=^C',[vo^:pNQQ5ʾ߂Ip3FΝ+_1hii27qNv3ySb&`46HzsPߜe:w,*x*39wC3MQNLNqrssxc$ȑ#{fgill&Nz$# ]TYT!R`ԨQִo6'VZ'1Ӂ z=ٳo쑥?sĉֲK.^{OF6I ϗcǎ Sgu]!׿50[tM>O ^~>;!3d?oWt,}O>QUUPw -0x5m}N'PH&2&5]CKI'  I'`/t}RZ:޻_'ԝW3/Ls@@@{wNq{@@  j&    @וW HfHH@@@ HhH+x@ rrcr92!  F@h`&v<:] h6@@@ @B8H?nw" >   @ tO;'I{./QR   @ STb\ @ Rxp @@b-9+M&s$3W&6Q@@@pEU )#`feX  _W^.aao<MKW͓EP*  #GHuuIGG0k|WnnIiiTVVOA Cl@ e‚C@@8>"oT,1.+fQD  555zjYvݻWRЉ SN3fȼy󤠀aq i+ *D.   @tW]-y~]"bLh@RRQ-[&+Vh|z 6X'|RZ馛$'ʈ@i#@BC\j ,`y` "  gRzp6dဧM@\V-Z$:!TqTKpn꒖σ^g͛7˽+F !M@ M ?F0 F@Hp=Hf2e\|2}t;v BxD'DTWW˦Mdݺuֶܵkz裏Zi@@!0,NER @IDATY3&@@@ N}ιySZ 3/?9 @xGdΝ֞,;墋Va={øqǬYdꫯʒ%Kt / 3"!%:], - p  "v-q +*3@@ M^ukL CIfEgr+䮻 SO/f w!!@Bp& @ע quG}=  i/9L`ΨS@OFZZZϝ;WfΜiN={Y?n[l!$@BC*]MƂD!g(BR@@U 3+]Gθ@@ v͌3vmL6-J/=AA+@BC_ i)3.-͠@@@ 6fAAlGU_MA@"8=;;;m׆^~_n ARd@  5+*lJ8@@HgijfffB  @ ?>ɍ7ؼys ԨQl  [ @  5m@@@~1"Y::TE@tP_3_/[j۷6[x2abH%Rj2@ /E@@z+sƙ=@@`0";wuZB.ڠǷo.wqtww[Ι3G  @: x`:^uƌ`(B@@=^f[ɷ#TD@ nfYftuuICC,XJr={L>]tC8^ :ڵkUL"rK9 =Hh3@ )L5͙a[r6`B@@ ᠧN @H.2Yx}">5ʕ+^.bRXX(Tnu~455Immtvv^f>؁ pR~@N \  @@@͖8v1Fb  z&K<`Ͳ .5K.D.\(#F|N@YtX@g7C)o+%/]G@@  }YotY! i.PYY)K,۷e˖-8$Rkꫯ!d@ ]HhH+ϸ@@@@ b}h6  #PUU%˱cdǎRWW'z6롏e#//O***D//Q^^.C )@BCP$@|)\   $ҚW.VTMO )$PVV&AAi`%2 ;@@0E22U 7"TF@@ǝV@ {dEz@@@^aƢNw_^)   @D\) ulL@{p:[`@@ZpU:N|#7v\թ  (Uh9"&P_HWUJ^^Jee@ 04/FBtbPt@@RN,.1EE># @ իeڵw^k:aԩ2c 7oU@t !?$yIy4  i'+3c sBŰ@@VFYlX"z 6X'|RZ馛$'''xTBEtҌRKԺ@@>IwS5-Ee SV-Z$:!Tq\i햮.iiiKS<쳲yf{$`@$4  Ñ   "TvbjjYUv$ NJ{z$3L2E.b>};VJJJ=[#:!Z6m$֭5][oG}JHC^ *@BàD$|3:F@@@^fVÐ]&-IA@xGdΝV,;.(zqYYfɂ W_%KX38D^xAϟvLNDIf :.@A@@@/^v ჆a " @RV?3ԬSz$3++0SO=%>/C^Qt;}F@@ <&˰c]E@k@sΕ3g681{l+^ss?~ܶBRIORj2H7}H@@@ <ω{MoK  Zw hƌm6M  {@g"  @B Ǝ{|E"jk   @[[[zvvv`ۮ n&?@N & @b F@@@ )22ŌsG.,- f|ڸqCܼys Ql  [ @F%M_(  桙7<𢸵GC  r٪Ud߾} v֭~z+^nnL0BRITF1"m@@@@ 'mfNO ! @ ܹs.z۷o;CXsQe\V԰@p  [S!  @Lϟ)۷Ŵ@&  @7|YFA,X`%9̞=[O.:!R__/zVkڵ*SL[n% @$4 Pt@@,_EP6PG@l(++ŋw----dʕC7=zJff0MJIMMMR[[+}$''1v  @ $5]F@@t0Eec!otef  #=ҥKfYn]km%\r,\PF^-@BCz_FI*+z7I{O@@@  mx$3hWOմte  JYdl߾]/_.[l!\ZZ*\s\}R\\< z7$OME&'<  @[K\k2{qiF@@ =D?t9vرCDf=l둧êDyy8}  aBq HޢD}A@@pQjkq:m  R^{@ vΖ @ ;f   .`1d@s䨐ى   А׍^#   #Ŷ1  ^W9"RaH~~^rT*++I?hY8 `eGb   qĥZ RFV^-k׮{JwwwXĆSʌ3d޼yR@"fXn 5ae@@VTJ41|xIh)0@HcFYlX""6ٰax'knIrrr"G%@ ]HhH+8@ ܝ5F  )/`)؎3+3 i)P[[+-\.kL4vKWWXσe)}Yټy{R\\|m@ 0DE&k7YD@@ ű}[8LˏY|# @z 褄{G2Ô)S/ӧرcDj6PNM6ɺum}][oG}JU} @ АRW.faSu,    K$# ېdyX^Cb# <ܹzVVyrEMgo7n5k,X@^}UYd5NtxdaD@ BcEPPYv%3$2  $2o   `믿nȰfjJ2C 뮻:@ HhH_@ ):;t@@W|1p_4;f  )PSS#---Ν+3gδ B'F̞}Kss?~ܶBRITFuaR3P@@RG0g-C  w1cF`ۮiӦB'( }Hhk@ ZǍ  Z9b:1iQǷcKP@X-0lɪz qM~" KM@P/tM;HBB@@c}?q[F~@L`7n l۵y@QF@8%@B) @032v @@+=oxNd{lG%  i.PQQ!Z6KUVɾ}lٺu_ފ+&L-6@T !&cA"03Ihj   @AF" C(**sZZ[[o 1Jӷo.wqtww[̙#YYY}Od / ^ {@@Chl OKy~D@e͚5% `+a2}tIzѳ2Xk׮ T2er-l  @p85gmKj((LqK@@H G {ǡ60ކ .PVV&/[ZZZʕ+^.b)P2La V:;;Pe&>s 'Hh7HFm`$q@@@ ,7=T$P@@X .]*<5Bp;]n\.Kd…2bĈA@ HhH@M   `oG@ l  @L*++eɒ%}vY|lٲEVii\s5rWKqqqLIP@ HhH+x@ }22DmW   +yP1Q|F  UUU;vLv!uuugjhkkXvv˓ KC-'LA_8H(ikΨd:b:@@@T|]e:1@@ &eee@ ;["#1u-=SlK,    t}v^zi    ''Xw1 ` xΪ_Qt]    @&wTiz%ȑQ!   @ D]z'ybWYݟ-1}@@@|S狨9ƙ"@@ m^9rDM:::a/'RYY)z?@`h$4 ͋@N9K>Z0Q\7zT@@@l((o,ea9V:0$q6 D)PSS#Wk޽{;:aԩ2c 7oU@t !?$@O)YK35tWM/~Y?I @@Hr3}7%ebtu5kfA< D+(˖-+VDJaOʵ^+7tDJ @IV\iƉ)-K;4zoڻ߱}g3|Q{@@@x N;]nwz/ڼidm:=:. @mm,ZHtRCR_KLd/釩frҥ5[ZZ>l޼Y^)..>6 @ Al",૨&xv| #P/͌ *Sq8y@@RT,,/(ƕWk6qH);axΞ&@NJ{z$3L2E.b>};VJJJm]=脈jٴi[ߵkz裏Z $@BC2]-!`n\NA@@M,)z:  -#Ν;ndeeɝw)]tQҳ77z̚5K,X ,YĚA': 2cr" NI"      B_fpL CIfΚ++0SO=%>/C^@@@@@@@MMXΝ+3g}JubٳrcQHeR26@@@@@@v7cƌ]ӦM KOP@ ׄ=      i.l۵pv8 @J KRb0      D.0~@7ؼys ԨQl  [      Xbj*ٷom2[n[rsse„ & J$4d,      ܹsXrˁ}v;ۊ5gʊ:.@Tp       |ͲfꒆY`0{l>}複pJ}}YtkL2Ens6@z Ӄg      XeeexbE|>\zrGB̴iZ : Ijkk^f>؁ pR~@@@@@@G@İtRyYOkootr%\" .#F z>' ,@BC:_}Ǝ      0@ee,YDo.˗/-[HccO(--kFj)..>6 @?$4n@@@@@@D?t9vرCDf=l둗'(//áS@ !L(NC@@@@@@/PVV&AAi`%2       D(@BCpTC@@@@@@@ ;["#       @$4DG5@@@@@@@ %2       D(@BCpTC@@@@@@@ ;["#       @$4DG5@@@@@@@+v/`k8xPjt8,.|?k@@@@@@ _i"0%땗ĵi'BkeKs;@@@tp)[ĵs"꽔~=mxM9N@@HBeMCp!/M2l,戢    Ç%'y&$C=|/U]s;@@@HG"t>  8?@r[6h2Cp}Ò7Zw3?8GQsD y;@@@dpm|OridwYtxb8@@@nfh+@ ЯQ_/9O<.Fg(ٿB:e+B{%*2=.x'MWox  $sV~!\ZLl_וW%   @ А!#,O^ /Z~sތ@ m?-+/xP34|t5SF@@OMrTRwtūyOvND@@@ ,9ka#@DƉ>QJ+_<|%V'!p. EH]   @<r~h :"wM     #@BC\ :>8,g}䈌wߖVw*TD@A@@R[G5GG8:@@@K$ 8-ms4uh>@@@X d|Ŗph;rF@@HO3j[1MfftY'\[47@@Hm닳m   @$4D+H}]hj:!Sm너Q]*!  p472<@@@6@?ׇw(o4C-]AA@@N뵭K'۶xB@@@ ѣ.D8x(&qFa!      ),@BC _\@ -9]cJ@@@ATZo/k8@@@xe@! 8_bGe#   {ur @@@B(0c   @ 99l'%    @$4DG5ӥƮDF@@W #ClO wrhx/*#  x'    l1Xd3    #@BC\ :    Mgs;6Np6   1 !FEn  $}vՒ`;*@@@" !"6*!@: <|Ǝ  $oJD@@@HhJ fVV:#  @ ƖDD@@@ "@ @@Jh<$"   U@ } 5t= @@@ G   @"АW> @R 8cp0,  $rsf|t@@@ HhHH<$ԴH<;z  'a#KmI@@@@" !5 @ӛz1?@@@%%C9=s wgXq    !G0Nԧܘ  $/$    u@ ffn^bYsĮPA@@0ZZr7;@@@ =PHba!  @B 8/    !@ 6FÉ1ec4B!  Q`6#1!U@@@X4 t'dztgx@@@`k8   @zp]`ػ0hԈ@@@ .Fww\ڡ@@@COC6@_~%J@@Hs?14We   "@BC\ 0<|*/D@@ MGj2D@@Lܨ1pQd]]#   %ڡ:Hh ^@@@ wrDL{Éq4@@^hnZp=.@@@ C|xL@@H)$[)n A@@TwIz6*`w$Ьo+'|?   @f3w>i@@@YD: H: @@K .A@@U[9 Q ddF"̜x4C   f,&Rp:l   KxI%-yy5D y h@@N v|"   Ma HV3''9֖   Ff3lI@@@@" !5 @ 'f gtuiW8    #"oEO/@@@H)Rr2_& 55IO:  --.}&c@@@HHhD: 3Yl;;:: G,@@@ "Q*"@q @@@\`? 01.51N *!@@@ 8Bg:;%-ܷW127\.P H   @$4DLGEwX=@@,`` uBQ[+zGdB̢b=y   ,9a&@ jGG[1`ɉx0   & vFDǝ> Hβ_tۿED   @ Аr!F},}=  [dG%?r{贇   $4Ef$s讃?q%  -`tv'%ˆt@@@ D.)#B  I333)I'@@@ 0} 1ĹusBN    s- )!. 8q  N%F29<'   ` #A@6!߸%@@_@g&0 W6%P    @ АW#x=."   8ꎋ`+`=;   ,@BC2_=@* 8ϒ@@.a]QJp@@@`hI fFfRtTggRN"  @ 6?X3F8j(8B@@ =PxBL$   ";qD@@HO3jV,,Lؾ.#  q#fZ !fh_3@@@ a !@i9?{P2|C4@@@N132"޽1LX@@@t !ݮ8E |I0M˟$硥" .K@@@ %\:|(%   @Hh9-"FsG޿XI+@@HM5[ўpc3^NA@@@ Z>*LҵV--l    0(@' 1'ajf@@@RIJa, ֤aq^Ǿi "NJGI=L\6i8  i,ڲ)aGܵK|+t @@@ 9HhHD/HwgR/۠gЏWCcѓv_)qHnnx  -7)YW.#  @B$rvכDt@@@ HhHGH)ZR:2@@lL|~q`5   )NY,:@ x<)6 G'6>^{uh9@@RhmMqmm ?:   bbСCRWWgunȑRYY^>|Xjkk֋eĉM&3'Wľ!kOk _y%17nx'ΏuG@@$p#=D7禍D"B@@Hp"@O?jFJjE?t{InݺUJKKeر ׷t퐙)xNސ4M1::ıg%<^.t`   KKj~GA@@@`$4 ӓ_Ayy饗䗿% tIΎMuŻUiSb.Gy`HwFR93D22@@lp?93m giȿ=hģgʲB!   |u[HoQZZZBc0 CH@ C`ݛRbtňb抣E-bD\J%R񕔈v?1[2uCTD-ѣS'Z:y2~qH^ƈq] ݆Bgѣ('|U {4c=3X4zE<^1TbOX==]]PA\.+, x}б_:idIQNfGζz^G5l>.3GJ2ϭ{H6o:iW:dDNV#J~hi<,mIC]2*LR(-uBKFdK+o>Qmgz%/s2-ݒ̓.Otxye^;1Pխ~C YuHZr}%fB}1+ԿM "x)L[0YW+9/,S=B޳X?{ZޫC**TKYbLMMCHg3eʞW5y{z?74{CD0TPKFyV. Fss,ꇩΡ t h6n교^w*[Fr]5y3G/w׫& g+Y}zuɉ*Vu0g+4x[*sVtϺPgٻ=wP2UNnEϹ3oM 6CCGu{S0]\'u1;ѾO|xE7S'>=Uʿ, R`J}Y1uщpU6O>1ں/JcǁPndQ*N5=SG_%T|#!Mzt' ts5yҲ#[=o& RX)IfJJ @T0?J J6hi>9Ѩ{oP5]J5k`~|#3RlMr"v!@@S6~RޯY.G[:͜b6AJ~kڗsFL,X>9a3pmӞ;^/56noť9_%}HY".i4D@ iHhHKGH-޽5$M7{ˡ"V{u\ϼ\x 䂌XJhг=`Fkd?kNu;*W^7JNe?H,֍7C8ًQs;նzv{*o{0d2P ̠il==L>WTQޫ-P7oxr{eτ:ڷ yu5+ME'SNkۥ\vɻ#V?W@=H@IDAT}yPj^,r7Ciqʉa}iUeSw f(K wW"sgӏ~Q2X#o%+K.Lv#@ ޡ^K =+Cտ9'NX_o{X֨&>5Z5dOk+ڪ%*Pu|C:5g5C~<~VP!JW Q]  =.]S κ0L@7(Uݻ$q緀#풡}x jۜ=n%6gb+[vQ'3ӹwQ73o2Cpǭ dGw_yQ{g @l4t;0%J.7%Rf^ #0@ɕC`J!`EN~'>$@ >Kyn?'L2C0NH+/lN[=o2\Oj&Hлr#WKR@@ i!СC}v)((>[Z./"uq5?.m۶ɘ1cJrrr=*lԩSeȡؤ֣ܫ91LWƍ'Ǐҁcӱv[P7dZr=ݻwKssL4IN;45{VUnwt ynHdS"CMa2iShs~k^oBoQ_^Pjڐr2 ]@/ܖoXk)fpykc3af_D/ 1mtG`G66o鵄Ck4@sEҶ<ސOL3؏ jC:c1K@AUMxmGWKOyW=c53~׷M?#NGGR[Sm~>e ?$B"?I_  `s='?!{pز2+_ʀ/^X^|Ec~gϞ@8$o}KNyeŊ)&3ΐ~re2yYvx7z3f… eڴ>۰a~uĉ|+.]*֭֞^|G9ݕ} WҗxF|2+9ߒjY/fZYx&Muq l~ێʄlJhv5ZzwNn~ho+Y<,=_~ȧaiFhz?[t͓G"RvH񙃝q#>}';"|V||d)#ꮕ?^Ҍ݃3G]JԌqK# jFc(rwknw;(s**$3s(r. $KNptNFx$3zv__ZZZjQ;AW]}M~[3)̠ .[~iYhk!tM6Nx'3{^ A'$hP3I;8W\'Av%WUO&@2mG3$uWr[kh몓w.*pGwic.kT}CtYLk*&j4G_b֣:5c* ȭDeLAke9M' s=3^KoJhԫ&u@@`UV>}\r%2ydkه7xC{=7|Ӛ]7M%%BuCr nZB?Â^FA'=x~=K9sd֬YVRFt2[oh|sQF7_ԍ{\UW]%SL+iBDꢓeܹaܲe53.:%KO}yyyV:ߖ͛7[J$ m0PG'K|_k6ϡ dlܠI _M=bȍ*F%l(cFʡV|Sڗ;:#*!OV'lwCdgYԿIa r^/w8˞פA},%݆vatEaH+PB8 ap)F[>S~lHfq'QׯR},m6܋rYSa I"".N,E/Wg-p8NN|1sL y{W^yE>֭[7xG-: NrssczĄ..JKA.7pРgCE/!|rkɈs<(=X`Nθ{4ϷsmY o%qd.(ХJ~H~~~wy,_.k9,^bB/_~OE͛'g}?T??-z=F*v-"ڱuU=x d%Ak{п]PGFa~\tbwcW{sr"FoL*6u_V|_ I"/p]&9ф YwG/uiJޝQ{}&Lf2WHa7@EV5l"d+W ?eQT@Q@\BXB$$!dgtOtLֳ|Ts?e.,u5BLZi^'tNz~+:9(̯SFWx V b=v>8، $voYC0V _3]\r>NQ5R5Oˡ-gI+Y"Ϭd]SYh{~˔}Y3unjF ~ `H#K/4ajsh+»[9;!#_W>` I{h`DD5H; 0ukzVGFҤ&Գys!P<_/V[}qG׭MːחlX/eF b.op]hUϻDv$gL{vvܑ]IrwƝ||}g .ݱS&Z r#=`Cr禶 `B (υ^8hJes_{Hv%\l˂>`ў>$٠.8N+;i 3~ Æ {eW J/SM/|Sr q"kx]}' fpwf7'|ҥO{73R\@{ɠ;5k<7䘰&w\ܹU )ߨ [HK& rC N^ `Dot9n:4d2-CE"@H!PX?OQY62nܸAxFl2; AިA~w^v Iرp!~ H%K4HAٳg˗%M:7R RMUشicŊ2gΜT=%!H լٶ͈U 8j*o%4uMC>&6~}m1@z˭b@`yZi )>\5N{8(2rMk+~˭1 uJT_`=O @'aرi4MGb2It _{N^{57h5™t#R?vzpKꪫL̘1c`Mkcjpj`gZj3ku…gJX#Fcº]ۆ[Q:)l}bHFIG]j׶VUuz5gYϝGH$ǜ{*m=yQB'jyopaC~޼+4l'gOv<%,'ӌ'M}uw&>׻*#e {0^zω{~?u ?mөiڟZF}9l^3wuߑkß* @4@tVw7J̘1cdH,нғƵ4h/Tĉam߄ҩQ!$[0j7AT7S0P>UDҀ}ɧ|Iv|$XOl13K}D%ۻd] lMImEYd*+U~8ǔ@b}*ckN%VL";}{ >XpZlBJ{AP/:e{n8޹t҇WUUKs **e(m. ~{oYC!Y_XLg(Y?˒i}]EYwըݝ}5YRS-eΨv+8b֭iX@ED~>kO& @SMMuC/՗}+?O{O}S_Z/^[0F?Μ9SoIy??yԡtȉ~}fMy_q.M7t 1wdf̔h}C;4d7D`jV@zAۜѶƞqSuy|@As}Gwu80y .2nIa 2Sev@=48)`۸q=^xA:;h'an?~L+Üq3bynsKCyI6O~iMZ)zi(n'FF D&r#H9dN *9ɿV/= Uw+LDi,Z}l|k=Y9j>VOjWZqZc++1?ϸ  А1{jULk׾ׅPO_(z7Aꪫ=o<ؘuLwv#=iiC/)'LIƇGvȟ,{J"Iz>̈́ 'ʕλ7 "0׾593h6mr`cKÓO>)Ї.;M;~fp_~vCO|_N87A{I+kGGxL%mx~U҆@|k\l>$0dMäcʄ0V6~V4##j'Vȑu{YD4p"rhF*+SPıMɇT%U??eϑ ( v@d=\.IǭH̰H%B4\fyDa̳]=@?XNRVRZϝx5-hkt9s|{f8h\C_8NYO e+-'O=kjr" @h0p2~>K/ɒ%K]t8l=|ȑ 6iLHF%V]ޙMSO:eI7@khh{p~7vWo@LLZ/# }ͯ3v8y ^ $1[.X JǢOJO@A3K~Њi(BVL @iI`ɱ{~zH7%M?m}oW|)=(nrkLN;M~<~P*+Gk?7 4ovx0oZZ[[|g4a]cY&a֩>T{` @t Ga'ǻNiggKG?QJ_M"8ǹV@Ί-l&wZ>jop/+ 2Rz7[z=LJV,?/KH{%kw?]_/fmѬ%0>^|Q^yIJHg/bGH{J}$fՁ r'p^mk?&l{v i'Fn=;QF5h!}$:ɳkWy\wʼg%"R7U4pas z~bj2e\g+NR{Ltm5Ttajj"~YʺGd֮H@RC_E;Y{vh "ղfۿeΞֈ4T .Ѳ{QS+S. ;VȶUsym_/zqfR_ f}`!k}{}Cg]Eĺg'!ާvG1k88ݿZX-bkٕ9#Z gH޳w;3 @"ߥ$ESDg+ ,٧tk)M7Jyso>VVޛO67%Fi塍 kW;:[㭩`X0If@# @>1oO?}@r}o]~i{>!M&~zy'#pyg/~ * }?2غu=_/7t;`gsگW_}ݻΝ;ݡ'.;IdѢEEv!]2M{s''zxꝺ:E}ɞn+Yyx2+Nҭ0&S^CJ!g}ML%I: `^@_k&Y1Cn,=1)S_#VCT }uө?kS똦E4Hlq[? G(,y49cbYAn 욷~ HZa}}ni@9uw`v3@P[[+mבּdZfB2OhO4W* ii{+em~j޽+yI{]o=Wǹ*{}g^EJKv=ﲟ{uJ1AˇNKk7o8m%ʬW3 畡  0@~}*h}1A7E ?蠃dҤItR;^s w'ԩSLg9; * :rbuݿm6y7 ) 5آy`D2."׾fKϟo=QmMڳ7d0K.nNK{weWXh/ ZW-[^>2=Z֭[ga>9w;3n8A{О&uQrtg r>f& X]slv A>+V6Yj[bNp>I?DQ];{ZzV=ySƴdG@ x+XkW N ˪eD$gȫHbY$B0CxE,`3m>gĽ@&MeC Ixt#憨s^;acc5xY /f1nk=Ğ=춱@WgjYT֑j! P48: / ?hOzS= {2Z/ok@DNa"/Ѵ`+e…}vGzJΝ;`O>Y9A{,~cӝUC~us.4/;d#:\C=4 SO=O;`CY$JuFIgh9o zPgnP _  d#C>WH@@@ +\g?Y9묳_{ :<wa4/ Ms̑r$LQV={sč7h$ou/W MChh/h:E{7G2*_AouS/}Z{Gʽ+$h\l@@Db;0yŊ@̽1i SHycKɷ/|fg)  D F6_2E@@( www7}*z-yE= d2Pˤ=)ZJ޶~ݮ=h(0ͤcP]]]2|6l`vZ{v:P&B#Gm( cǎvɛm#6m2^k|x>2[ۧa_ܝM@k<{`Fi*0Ksў4ˇ3"/>E c FNMk=)6k1gmt=ϯi͚5~%hV_{@Gxhτ7Tnur]?uװ9+Dk׶=gXwd}ov&}yfgW{~}fL!y֠w9S0~>kWV\L,}'JLq@r= cse ~%E}8f//IX̘1#d8ސ@Rd֗;NF@@@#[Bmn ԃ@@@ O;(]*o˞MWKGϗ^ @@HWo)tw|9'"   u> y-PFy]`e&@@@`(ѱㆲ{g}4O2C@@(<«5B|twk3*>yU3dA   qcuu$@@@8L ĪL& K.X}}I@@@ >߅$P0@@@h(H(y]hZ^!D{d67Kco}D"{"   QyZr   &t6( E.зtxoy W}Zb#FHReX]+ƼG@@p DvWt=_J*VJ$ XI{9yE@@@|a%QTo{Q\Jp @@ȹ@o/E{XA bٲE"kꕡQħ _*B   y-@@C^> @ l`=h=cB@@r,mjUr{}4d   PTĸN7E %KV [@@ [ Z[kCetI@@@! 0D0vGJ6n7/4I    O] 1DV!  B\'$(ٺ-0m578@@TyƇg`B@@@ 44PpJ.ao[df@@@\ itL   ^ܟJXeg)-%C@@(}JАXm]RN@@(`HO1D   s()E!ai2)   0d?zSh)z{\@@@@ EI #  @q  jH@@P*V Ru   Pu> =ҕ(   q d#7aR6s,q&@@H)?qr}Cv?    +"@L 츘]Vx2B@@65O$bfA @ίkh4 !  DOǖ'^Z@@@ !`pC]4xC;k@@@6F 3& "    q 'PW_چRi(%A@@UUg@f   @ P'*!zօ@@EdZUḐI   d @@Ch> #]]f"@@@ KիLa#}}d    9 !d-mh Pz@@(Ȏ7&)"    q@ @@@@ 4 e0&)"    q@mmP}@@@mDz{I   d"@@C&j-x=  hT    E}<d"L8@@! :|%%M@@@,@@Ð8Y ,f  !54/M>O@@@DL8@@@@x)'O6& "    q     R(ٸx$   @&4d1 ી=9dx$  ()ӟ".$!    q@HkN@@@ 1v cU)   @ P'*#zFaba-B@@bF:>x$   @&0p @̇1`Mk*)A@@r츬E    "i YZL$   *Bq8   fh0H* `P j05IOf6ARC@@&V*ʃ   P4I]ԨPQBY6   h"&" @@@ !kB@&NHzRxHz$  R0#B@@ 4QHK 6f ,o}@@@#OFE"   d+@@Cb b*A זZN3^WD@@66f{|̝g@@@\ А3@ `G%,as\@@@ }5VSEB   d+@@C=HڳB=2UzN>HyH@@0-ŝ?xbLwG@@@ 4䔟@ @İS=+O$]gTbC[(dd=   {Gc.Gt u:$   )LIwҴ]tn}{Ͳ6w] |~qDC6@@@ $-rI{cB@@@ Lea* eAݧ*e-۽Ӟ=0J)>Z:FJ_yYʟ-yE"vmnsDjk΋@@@ XA^$5w}O"C,3t} Y@@@ a: TTJ%I7R,Θ) ^gS_Hg'C"il@@@N&Xj~`5fXxeC^U%;@@@"`ȉ"9T| ni7>*øÎ)I-N"!-avB@@0 DL7,}'~>J6~ ɐX   szh) J V_/oJɪu!=G#ѶQd;   PyJ*//j4w ׀!   qE ` 7k_/mv{a&ڃCEE@PE@@@ M*;[o&@@@Q|      @ 9|+P=@@@@@@@ :@@@@@@@T       NН        4@@@@@@@@ t4P @@@6IDAT@@@@ 6        !t!        @@@@@@@ ;%@@@@@@@h        @h)@        @@m@@@@@@@B'@@CN B@@@@@@@h        :BwJ(       @@@@@@@@@ SB@@@@@@@@       NН        4@@@@@@@@ t4P @@@@@@@ 6        !t!        @@@@@@@ ;%@@@@@@@h        @h)@        P+PZZ*fPXL`]V,_\vQUU%FrW@;_D/YvwIKKȫ}}}a>I^wF>|ȫ=fCۓ>+^L1P ~iXb=ee>=DQH-KJgg{PMM+k/3ŋE;{~G"W6>s%ذaKY|uu/i( @"Ê DફGyݺ{˃>.3@! qsϹU?|pA8Yf[~r 7 P{ǀj]wur X po~zm.3 @*H%v@@@@@@@\@@@@@@@R АJ         !        !@@@@@@@@ p''C@@@@@@@H%@@C*!#       @4NN        JTBlG@@@@@@@h @@@@@@@@ @$fMvb; @V\)[lq PSS#'OvAx p(ƍsA,Y"===n՚eԨQ23ŋTۼ}&M`ժUyf2ew Q`ٲecj 2~xw Q^nj#Fvw Qॗ^h4VUF.3 @*R @@@@@@@`ȉ@@@@@@@R АJ         !        !@@@@@@@@ p''C@@@@@@@H%@@C*!#       @4NN        J,lGI`ҥ|rimmYf hT֭['V.iii &Heer#M'm^V k&9ٶgޯM V l#3&v}hݸFLa2-5c<@6ٶgޯXal\#2! @]m۶W_-7oC h_rjjj裏K/Tt>ɏ4͛HG k& iϼ_y+0gt)C5'Lf2ɏcS g3[{,r  @4+~ Pr뭷 C@hʕ+!=hm& #Mo#@6Lys<iϼ_>@3GgM кq>ägZ ktYHliϼ_g֞FLaC(ٚWv/Y?՞8qK"Lgg\uUo{ʼyO9sHyy;46l O>vaGIj2(CBGl3>H L#SA> jrxIڈ@6׌iϼ_<$eD lki%@А Pׯ~#G?%KVTT?y;wև?aykZ._\oM7?Gy@^3q!m{ڏB =s}dz&9]ZG?4i1UA@kg4L =s: 0@`ن@,83fؾ}׿v Ά}WnvgQx ٴi#Mo#k&Ӽ9&3צ e*8a>\#40q͘* ugM =s:ä JTBlGx+nM:::i &LR]z!=hرrG'=~#8'?p}H3aFD`!dɮ&`=~)"!=s} $oa>\#vB5E8c3N e!5ʼnP@! 0Đ]@#?яJww[ dwN>3QR2xnΠ 9眳1~ԝO63u>`j*Yv1 ioo߭nc}L^3ZSm3NzD"{^}^|EYfh9rh Pmm=7xC-[f%3FƏo-n:ُkhja,Na>N~\#C2 eT'׌JqOS%3ٞx{ WޞM_#\j a:pN:I.R%뀕@+kw>ٌ=L֭ƍ笲5~n>Ըˉf?xoo[Oj]{Rr@o&}2{vO ړ@&[o%;vp&+fJ-8?Ҍ+E5>9y'c|~ qucCRo0q$=#n씻KF%Ϸ{Qѡb( i šz{D{룸j:c{'>a>A]#s`oIVkiɰ/9kZA5Q8혚  |ڟmj\@{ x- Τi:~+VGM[r' `wQZ_P#<"vK|Se L]3J|}ŋMzm̙3GN*+W~)_l۶Mo tرceҥv{\~g1 K]0՞x(fvڞ>IcXr׈ ZL(OD5& 0՞x%Fan~_#\ц `JSy#N0v6شsNwvokpwcLSSAw#͸^_jI-?ٳSN+ZΝk fU f s2:̄N .EIyy-[;a hݺTX&s\4t؞>vsrߓN w0^Z lewV0?7q$94TX緀5ׇ-0k{(K-@S%"@7핕iW]{4p&g gُ4MG[0.sgL ̛7O=f;M>]>Of(4δfF΢n] f ~a脑̠۳# M ўHd5-Lٴ|#[GُG\",#+@ {9d!]!Ӏ%#X\_[NF0eYU.---c1!/p5׈vhs=巿I{qHiDpݺ4 #ALg%++ُ4e>EXFWSsB/}H3QY_766I//"y^ikt8 ` رcen#Fnĉ Eپ}Lu)1$5ׇE2)Lg%G"!G{#Muɴhq{vq|}\B~g?r}x5G @UU{Tww;j% X?t334SRR"Gu򗿴Sܴi| _25k̞=[̙#&M2#ӃB:jr& IS^^+v7Vd)5G'0ݞM"\TX燀ُ4Mם{'Ӣŝ}A_s}s ɯўHӫ`@o @H[L27>4jeya+}i׿oχz̟?_fΜ ^k-~:>h,g#5G6gc"`=N/Q]>?ڳiם{xs){v.; ޯ>9C;?ڳiz`@!'h @uuuQڵ} g[:cGnfΤӛD}4AdY֭{qXh|Օp?V@eee`" ޯ>)۳!q}$Ra~g?Ҍ{y?>;ūٙq\xY.h~=#\^ @衁6d 9J^hnnvV%}]vŝ?!"~.kO 1;cEVZ%O=>rO @jR Pwk.;#MoK..6dɒyWqrꩧʂ nm^8; A n.~J#(pc=N/Wz\W~g?q`Xx.3l=~6\AI;?ڳi8! АG!UxWaq3˖-?څ ?$aݺu߻nÇ 6?Ky}#G+BvruP\AI;tzȕ|=W{~"{v`uPJ:ўH3hEL8 v9]nI~iwٙy7+~{ΰ^{97Neղ~ze˖-/x?\RK___nۼQJf@vrfuP\AI;tzȕ|=W{~"{v`uPJ:ўH3hEL,8 @@-Z$ncd;bƌϋwA cǎO?]~ӟI>#'t趺:ў{9yG v'z< n.~#t{6^.>rr%|h~qHZlg~_S]p}޼h~ G ]!'n Vַ%rh S$ ȏc5kV~]~rgWxjgk/~QMMƝ///=C?|{dԨQ6fȅ6ő_Aq}LgB#ӏG=8_l5ޯR;yўH3HE L bJ7١ H@{,.^;::d̘12n8'D.48"™t{H_]oĉv5ɴaYv#'O4L1 Uv;T1OW tb? #tzPȅz8=g=8 ʭٹ/3} GFnG{# >&/@ =sb/@ /=Ph@@M{HX   P@+^@*        ?4Ϲ        Es(       #@@C+J       @P4"       ?5Oq)) Ps9蠃]f@@[{>@@@`pa+       @!'rN        0 @@@@@@@r @@C@@@@@@@ ap"       @h:Y"       4 V@@@@@@@ȁ 9@'K@@@@@@@\}؊       9 !d        0[@@@@@@@@ e9ȓ,@@@ܹs='w@@@@ĬE@@@AࡇrZܺ:ٹsƍe23    'wN   z7|SNnŊRWW'{AY~^Z֭['*'Nr#    5k)7   @~kr؅ݶmndvL"w}3stvvy#8B4@?^?ÞWo'c{O{{,ZH.2ijjrVۯV;.ƍl>yTVVؖjANy衇X8i@@@(&lSW@@@ G=i>??`_aΝ: .>|TTTHooxyɏ~#_*D)fΜ)>cV=\ٴi`ٽ5g?1_[n۷M~r70   }jx   @a   fP^zIN:$ю |A{>U0?Fu1餁 q5פ̠=c2gYxqҴـ   =4YN   @40w]%Kȗ%ӦMo]6lݣšɤSʊ+Njܹv@!/= źuYu]'7}t.'ph:\?.od͚5^4!٤u[kmmkV8 63<#^DN<z@@@@ 4 Q>@@@t;̭աj H0I@Cqw˙gr_W^-͓e˖뜙;/vV˗/}~zw݁h#+<3ӥ]|H4t9#[nuW"_     P 9Qg!  w݄ J3zh{DLr$ f}'L w߀xޅz@0d z'(O>h@3i=C8xE@@@ MB;@@@5k=̄"… w[;ew[]qQGL7o-[8'կ~.766~wy3f%\C^|3qA@@@ YB> @@(rN:I"Ƞ RUU5` r0` vmᬳΒ1cxt>>B`B@@@Ab8@@(RC9$ oĉ- 5`Y.]:`<`9ՂeĈn6mcB@@@ ]+H@@@W`رiUlI&u\eee_Nywɓ'ƍUٳef@@@@BĦkH@@@V192ںu[nOTeZݻ7n߾]iijj*^>}߾KϷ˗/W @ @ Y'/ @4@n;w~Ǐq̙͛111ѤFGGcpp ܹNOJZvn`` j폋/ƅ rL @H+-j @I`Ϟ=qxE7oݻw붶~z%иP{| Qyu|qsbrr2v [&~ @ @| {#@ @@S sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATxker:2$I&T@YQ'bQ0=󯀂 }"J%soewols3===魩6(B    1"`yp    SP@c'@L @)n @@@@ (    1%`dGm= e@ ^]A0TN4-(0ٳg5DD 9, PB/7   z&TwmRH h)$3(z;hBP@KM% @A@@@@Z o*.'Nŋ%eڳg[i{޼ym6tD@4ᢢ"?iҥdZ5?|~@ @KȆ[oњ5khРAo߾]j_$pJOO UŊ1dggӱcǨF&S`0Pjjj"?M>ʕ+vޣѣGSVoSϞ= 0a| :uJZ#U浬gfʗ/_g:У>* 2-[FիW&}F/i̙O :;^uTPP@&I*<-YLB*TVP>}Z믿LwC=D_~9M0RRRNTRn'Vt[l)+4N3|qz̙3S;v[%aT4k,;w矷YڴiC6m"~!gO5 9P@#5ŀ+Փz|md#338 VWKl։m 4|!.g4+ȡ7jݺLgdڵiʕn-^gyF*lda:V[^QdK @?op*U3/,]r%4||=*n|pϭ*Bn8l'@d@ G##Bk>n[BدGh"zCjuJ3hU9y+U  g'J*|EsT\UG0d;\y_j! %4ڢLk׮G{e}5kR~hrϒ*lQa.UnV*ӦMJ'~29uTԲ/@  bbEq[j8O.Uה#PsO<ޟ={VoRg ,P8>WM@K3n<[gW;80}aaaPom~ pxf͚ED8CL S8YxT |r>ϘjP>|B e@|*vmWf|}'6w=?A' @ O h Q@ YA@@@'Y,IנD- ó3=4G*VWS@5aVzuֽkx'Yz~3nG7p J,'^,/J+Hs&$&=1p8\N|W:u*3m,x Z ǡ!kԳ+F/>^יnS^^p9wCoEC$>08+YP@Dž@|@w@@@,(eA@@@ >Ƈ;    e2{q    P@g2K hp2wdڻ k峡z?(ѺGNzL{8Ϡ0r}:4l@Y#!(W^"P> ~_~Y~)JgD  ]+(e', jF֬&uk bpTtuQ8+HS2[K1 dY7<=JwR  Pj @s}I_ ǏSox()UP]ĂQ'kUeZ;)_Τd|f׌J[FCYiPXH|M)Wt$m1ϓAlC@@ gϐ#]x%n P @MT,D:KNZ[58NiS?rr=\L  kUMi)Nusp>R@ hobZZϜ'ir9噁Pñcpd$۷at AQpg~^lS,@9>}o|7=BJng1@ x?{'@KK֤U+fzo閹jT+r>Ww  1#`ZD}DIC h0I 09S"OPW9.Q  UEEdYحR=sa|T٣r(FƚG eހy@"I c(>]R092QP@#}mVM5fߔAv&gDW·  W kfȅ5!i"g~5%]7arl@(&2T>ɘ#8F#ˋZ[P1@!W)=+)H;/+y8v(,ݻҰ Q! ZYjՑx޲xϼ8 *(*H|:lAb'ڑk$Z!M;I1o' M hQԀDǛ>h™$> !rf@ i&2>.pfBO~ E,+T?&ӆue@@?2^yb<[?(r~L VJ J"ϔTr+r-l-ERH@(bMJR9jՎURNp YfITT+/j#Tp}?*H ֭nݺ aX,Ըq8_Y4$&-]!WlE(E٫bkӮLP]{Rjd 3;u@UdQrmbNAЄRK@t}W"m۶/ٙV*p:;-;*֡+PeClڵ+nK4^lgҚUœ~f$ iNzEdW(Rk*5zEZӔ%}{D!Mm1II+o$%'SUא"DwhOD+OZM_h@N a̔IDɢsb9-U7}QVVг^z 9.PZd=ʪǂ4:WP=G+컚8'-)$I{U#_ \ݏS*jչEhځt]שU֍j2>A "&['M+ %/]V/ĖR'QFǬSDRתP8fz:J2>ŬQ8$)'NМ9s(|+*O>$:tq5jJGp-[W_mҔ)SV{iڵ4rHYȑ#cQFFϲZf M0Aݥ:tܰ=뙔0R~r9toC,&,🢣PP/"+Vͽ61Ԁm:pf+Vm,]5)Oo7fmz_9|ØqU39̣^?>復Jb{SWj̊v_?KwwH-O=){dž$H#>DV5jԠ*U֭[Ւ}6W\)k[d ]r%r-$r?Ν+PӲsQΝiXxԷo_iEW!\y_}]wnVڞa{΋?ls%I6$B[vĮK~FNW3rGnm-(ΥC l`ݭͬTȕkDxJw+嚑ٔ*}=O;z<󇻯Q?í/iS8e#y +tuڕoWT8.]Jǎ#V@ٲC|#v [Hf*R1Pݮ˗/Olvm 2On9D/׫dMϾp~HKE$K|d%f6zҺR]i_dOv}Y1*^9hZC-r$8ldtٲeRd%rѢEKLiժ$򎷷#xhu~*ŜlΝS y\;r `5A_|goPl4$QYK/1ZZ-v'GkzsJ(م7ص#BgdDZ_(\I<}[_΂j@tХI&Lz0.^wN+V> xPy*@ۛ|PD83wlUPMSUo!5d<~됕<ŏO"!RX Z@LeV_sj Ҡ%d4sqRDk()ċ"6m#OXɗ_kKHPUίv }}jxa̤&ծLAA tE;v[2۵k'0.Ҡpѣiԩɓ\-AU'aC2'G9MS$kBb.\u:QTs۹!,y؈;ԴqoP oFtʷQ$ȾJ*U_t ߮/m4 P6 na?@ UwşߞAۤ{16ad3PmăVcT"(GЃ荿A}g6N)FLxe .>:(~bb-P}@r6ۨO{̚7o[z K hה5S QϿ_\O-|@ 1((OȦHyi4+<ܞp%RQD(t9 )R!>&s/z)ٛ .7h3F2#fd'6 ~(hčϿ\5ܹ^ !7_"lND}oM8 P@ <r*vu9O>CbUQ@|7g` ba PD5>.+f=-&Lp  0@9AXPbQS#t H[1tAb۴ @6(P)G9+ZD?Nύ&re-ő@ (cr?08φ78tY]Sg_N" $nkF>(UX܏YnIݕX FN7Tm *q, ȉG@M@(mEuԫS;12I h$o;kN%鴖u@~Vx&d$mX^54@ 4QIbuU)\z7  %`DWEH,~+C&M P*h-N!M^#KDg.ef'V3J{jDyYp@,&XD!ҳGo΂%Xp]D}HX04vl^p^ @'Ϭ8!/W΄W**:L$, yC㪵?YqͿNp@Dx`?r?(Ղfừ$+&9((Yd:tP\?1-DYr1=)J7(O.dךm5kzPz  KһZG@)..J-{zMx(VHrPAw=mO֮Cl&X|OM̆}D &L!d3sGEi69TUxDbN$"y  .( P(&P|@@@@bJ hLqd    w@@@ a [kۛ5k&B G3: h@h&N,Gӿo(N2ۀ{h${<~8 2l6[:vE>h @*UgbUXy\2?a[20tF5   pB:vhтMF\,DޔhA(c'Vج)OdCH@O>~:t zI"_~= 8,\~^#ui6QAXғWc.fR]Txj>A@ q di.7o"%# (--q-*J_xVPA롇"߿THT2.J.ʧ)%.>A@ 1 עQ#ɲw2;J];(1D%Q'PXXH*|”ϳ!%Tƭed(*rKsݱ,u  XkQ9z'_G f4'Qk322(==NJ'Nٳ}Bk֬V4 8I>Ygΐqyp@JT>]iB 7cƌk҃>H<޽^~ēKGiٔ|2\Whg3(\ ,q@@D?Ggv!;LyplR :p8Qb|$}2%rbyזdL6np~< nl::Ӱ z!+OӧJ4K#O>)'ԵkW @="ӶNvrf|5Ml!M=rV%Gμ=,..ߔK#_|A+We˖<]vԷo_W oڴ~OlN؟p%ؠބIz;]- @@ "|":uc&01$.UFnJ={[T7Gmʧ7өDtSUbSr'E<'$pʯC~`%3Ν;iӦԶm[7n5hk+Y:A<%e b"&BfC&-Oi$PbEz$K$$^ɠwг"ǔИd=CPZJ+IIҰXm$V˹@%`ްN[ q/E2E`駟J;vУ>J<+D0{lg:|oH?)aMZC, S~+2EDP*DKn!77ʭAH׃_`]}Ք%Aԯ_ɶH l]ڔIZEIuȴk'Y5  Sn:lV( G 6]Dy' &E뀻#f^ bIrŋ(ȼjS D:0ݷrr"v~T Ĩ߉'fJOO^ZBݛW[T7x`T[ȝ>O\J9L%9j~6i|(bZ% RΝcǨV oAJL{7}\j)MX5鴏\H.+RSSaÆrwyӔ)S(&!ȖY/H)?̉I+yY_PmxOD@ҒEA5E,h޲D2q@y5jH#VL!& %Lu"14"4Q &< uU. 68Gul{7!7^?٣ǒLްQrAp(v:Li\-:$PO3>%Ӷ4;B(+U||t`YŽ73H|Zwx6_*J7t5e˖n=2`GR}嗋| WyjӦ u-ܪS^^5<$Ob/i!/5kQސEK{£4c]p ԴjOqeaJpzTF< @ ˜/ Yw6V* EY!RU$vzr@ bfzqg8-;;۹_$q˰۷v:r䈳N|޽[Q.L>qiʧyīIevGB:M*|[ Dng!'j_dr\܆Y]V{:y.[npax%w ѣG4h@cƌuĉiҤe(whܸqB8/k.{讻ŊaÈ z,YB۷oJ,oh׮GSOW\܏FNɐM6P;!%%E*d\&Fxv^833SSRSݑ~tS!n%z^=[]QGֲ2j7+ cӵ4MGSyr"FS\ br+?(W^y̖$^u*dɅx% G'|Vn%pgEP2n8Y3*^WMTLj~g}#g_`]7F AEqб{)3%\=23#G hhԩāc9+Z|;/2y7XYd-[W]l9eYq,N PVޝYYe%U3E+,]Sy=?lUEXroa]fkGrQx7oX)TkG\)V<1:}0bGbeCOXdV)Nmiz#<{8*ԥ pGPpx'$˞ Wdz܈w+=8I<W16YO6S'^L}d2o<+آ#qʢ3gҐ!CtrXUw[~S]6cǎԷo_7tP#L-8ʧ#Y xګTAR%zL$h0+L8/ /DR"  gIXw#FjuBymO z2,~zmpNdӧ-8tyq8{>t͛GZMxU~g0`CwNVW=C&ҥ^Ju֕>Qjrmlӧdg*aTtQղ d޵C8UָIӠkCP$3h;ŐbO2E/2 KO_2:@f֡~1_Otiv3Et(RIݏ]#-j]ryI؎cE/#)lp?~<5mK$œ@PVTޯ_?l!T~;'mp'N8Y|ٳ"x7LIE[7xC;Y^6 gxEsAVcqJ}ArVW!^ϜgGTT4:O ʧ֖Nx2yԪMtA͛""N:y]MF'F&'8N=qSUor)Vn]-;:z}@vڻoHTz80{x5/W|@yxi)6y/_^~]CE+gY38D/3j_h)_C:"'$y C*B+Vs "QW$1y֓h;g5o<QGJjuAVPuW]TH#uMک3JLX@ϿHB+ (@F8JˉA KQDA@ ӊ+G8"v֫W/jD87'zP:JL۷Ea_Km/DAh>1A!F_ sI/c3q=w ЍT|,m|B,b  <ٳ8"v7iKW v50a U8꣏>J۶m]a;`=mnC"RރHC^BnQ!h"Ρs<}LySx^ I{"["|tlR݊5 ;  U<4smQFYyvVL+c]~ukA2|}zRDx@Š*@p@ jD,ςoOj:yny>z#S 8.8AP߫BR0A:uk5(Il)-J@SJ5#r)M0PDX/@&W TIPx^D8M bye ۏ@'+x& (+X8 Ӕ)S{J$8q"u҅ 5W^zن;2C@,HR3b? WJ#&rfZ|x>λQw2r礈ـy>.Hz4*vR2Mo$1T$v&ʿϡ)ۄeSP{pYN׫OCr-m{b2!EL& OWA0LO= 4Ə/# '-T?wKu<6=;8b:|XLm/$NJ__%㡃ɟcA@@@D?gԙ<1cڻl-Zz?2K0ՋdQcxe P:u,_8P!nx)28Tp"1!L $w9Ɂ'̂$P@>i':Ū"H4#8^}ٍUb`/lzf'7> r1[Tp Iikݖl;,  H:A@ gE+VT^`ACJP'~HٟN'ۙ"^+in(   KaAMfHM#E,YW`-GsA@@ )WKR~"q*aP@# ՁĎ0Ŏu$44Q@L SLqGdJT    P-@@@@ b01p Y~_@TX@ ڐIS}4 @-E_Nx" B˨d[)J\ @Y$8qLwC񭔯PATZngdp_Gf-HC@@  X@yR2RAD@-%__juS>ld}!" H<P@hQJ!L_"fE"@JiM'?Jq{ {&c@ @M5kp|r*#a@\ (II_TDގ|Q(wY-(5/%g{/(L>@ak}DJ))%/օ7R" A hb' 0Ŗ'#ąe+"Ĺ"*,z̼ryt$$(qRIM{\)A@@o 6ߘZoݣ vAC ~E-q,˝i@B@)o/fʂ :$:)M2mHE!; Z]@U,AӐ}̇{)/t%2"@bL C>E!CA'(IF)?י&v7q2mO*'O3&tH`Qoe &`k2Qs?XԦkAP(a4:ߺ)sd / @4VKêA ~zn/&L@ss=9~8+Իwo>|8X"MP@us+<k +rI+SƳPƈ(Q&^H#4 L1yPwΝK}!F]taÆI%sErsskӧSϞ=Ś 2߮]B?iNo~ 2c^XҟIU=iXȒqC Gz$c<@l޵^K-Zp6uʔ)tھ};Y,^$^7o:3 X@uzS ZD'ċ@|jdk8("pJرXTyT~}uVZE\rS}i(z\+1ٳbu:@ `!PXCQt'2,s$'Oʆ֩Sǭ+Vt3= sol]7\MLbToJ} أq/RwߐyΐO/)?%⅔:sJ~uEҞF򼺴_8 !zIrss5X\zwmXP7ߤ^zən\/f+N6-*<II$sT*=>]q X ǎRҒE2cx ^_klzoq@IDATW:"ل+x41`FI~5  ~JqX?tѢE[oOHbtرtYJg&!VZN߇<! U&СCC55k֔igϞ5jIn";ח*o8+_zu-wۣ:UkQ HlV11I;vE M۲Yn;3mX6EWkҮ =0P=.0h<"z'D %uk)D&pܴ~%_c l4KEjR*3N[~3<-wzniacH(}^|EE'ym۶F94b6ψ/*UDW_}5kF|ٳnJW]u ]0j(yCNFSN|=5lؐr[ovϟO'OoC'2ƍ%KșggΜJlvdB R 9ѩZO~C(ڛEH'#xԹV!կԅΫ|[:v@@ OSzJ}1+w"aN`mޫZoe2yT1 7=Hk΢}^P-8.?X`acڧ~4q]wEwy(g $Ǽ`wߥ/ae~o ~-~ԫW/MrR1uTL5oL<^jU洛󩧞"_wN?#]wu*jGyD8@,mXGX5y\QmRp֣uRR0v%ZcKm%jѨȈZz^G<@b4"x)jz8]vmJwzӖܚy`?:6k=~߳Αt¾Z>7;3O8UT xTʆg&p:vve#ޖ%ʊ'p`VV>u R 6}J Y?VЍ7ReؚʲaavF' +,/J ,VZ}&Aǚ6mJ{ߠdùOkb4R+x}Qa8TLiwTݡ.ceZb1dg$ʝ›2*N{I Ȁ^/<\TPoX>-݅"F\'xvL>ݹoa 쏊kVU'J쵻$c*YiJvK]RUqwX!;^$θXmvD>ᾏOw2eQxv̘1ԩS'϶oߞ~g_ENV\طSMY`ܹSZ(99/_^*j>~+r9rg '0cرTn2n7< k4:c <67ٳɱCIBQЉGyFU<Fk|-~M}<]}6V'V46R@éG_լEb`=%`O2?5lfiUD$^SR͕$y[\=ֳg/nƿm'.}c >3i$Gb4a?OVpnݺy=ڵk<;MۼyTrvJ|}7Y&MDf?_<>٣Gi5/@_M6%hT2d&Gڔ3bu-ZRQ)煱B 3 _KA]jkCRPj=iXuVP$CDJQi6+(g藢,-k@sv[E0կKFW­,vJ7~ч~b[C<Ó ;zE4ynF_ ς2dWaMVng̘A7tVM4hЀ!7xC*{V^9>;`\xudACraB"-*7ymߕVyOEnKxXD"~p/DZꟌFp4n}v_F*tmWzߌBڷջԢ4kMlcf1t^Kfլ,v)P"[UbqTjj>+P+3=ODA=Vz]-fmfԳ|b8;ch˱;iRV |o <苊B5XLKYϑ)"2p"Mx˄=6wG"[x#P?9/ C?Clkݼ >mgXJMHY?TO,sϳru}3xG}GK\,*@'cЛZ׬NOTk}Q'eVL79k!)i j(5$fuX}db|h)2/z0ŐJ5P2R>.%šTS9T%p$ZFݶ>@bHyXY'7"y$JK(ʀđftq8/[h֬Yqwj[^g{Y)Iydp<@H썚PLC2Gf9˳/BH}Ьoe2ʆ={ȕ#LB'Nz+P:Y]v-]uUzбcX={oR=PDkNrzbБB (-A[dڹiZ9ϏZ@@_X=OԐK$~^z"חyPu]\[o_Vu:xS].yK* hDC=$ͺ_f:p\6 C֦!#`YG!,/~ęA@ ]W~%<|9ώOD$ K.{%yWT%r/cvЎIx+x~РAoذaQYlHZ4`zpY !'xϟI806XJ̌ !Ys,P}P M"~@8x=w="9NVH,/QO+()i"4@ U@9Sd#8b=YOCX>.97HLAy믿^7*M+NTbKvX#97gP&sG!ed=Y5|YMIzxC@_tժUθO /OWGQc*2bg~uJpn<]ءDu㎻q?yؽd? wi;ˁ!% U@\,g<C.AI.\H}￿dH9k{5)erX+֘'HP.:u6\yR*Wd @ *yg_;vŋir2c=&\c;NlmQ@i"` b44Ѳq-O(9|f &ܹs^z4}t|~w/_|~ФkgggՐit 7&6lؐ?4    N@l 駴rJ5)O (K*2[DY8 B@@@@#Wt:06lH֭[iJ|ަM|^wuđ (0A@@@V8k{y7^ךZq.C[hƍ4''ǭcǎ9ٽrtYg+z7= ]IE$PB mTd2-ܕEx90bjٲ% 87nL[l~Jge˖Qn~X}3op m۶Q׮]ezݺu\r}v|hT0Ixljh,&8SsdddDz^#'=J*Q{vB8\NժU Ժ-(v2X*!OvjΘ1C4e8q"񼝇zH*5iӦQ~dƍe.r:r]znK۷OIb9\ۢE 5)OM (0@@@@'EX@L>o=EiJ>;7ߐbOxȜh9Rd"VX-Z$Rr)Ztf9&Zְ_R5)|BaR@@@#`6%S ޢWVeQniZwE!p4D￟C۷lfpYcÆ r㾳o(ȴÇS v47i҄vEcǎRff&     @KU{P]vGX[nVX!Ν;KM~M !URN=gNVTSVLy>@&}Tzu -BtӦM2=kw4j(:s }re$L |KsdYOr&F [3MDsfggS0y8-;Ej!xӋ/(ޫc,/9w\5    q$`Onj DJ4)|!F2O     &P!uUWav`a(/+$rj9a"رCƇ*p    L?HIPOӁqn+ sfJ֎(xp<&nM (_Gŏ>h[h֬Y%ґ    4+lhM5G wi?>YF.h/ 4+Wq@wM>=zN].L@yy-OFBѤ'ûbŊٺu+U^D:@ Iȴk' (bٲM?X}cp~'/fiOgڤ ?A fGҗ҉"kH=l0RaX?~('ML gC'xgi4|pZ`ksCzAC@@o%xD6!y聀iH mCІ(8.W{n ڜiƍ<] RR 5ׯgϪIn|ur2ہ0v4+? BꫯRݩW^ 8pTBYFsP"H 5deVvT2)  ^VQD;LR phV)a(ɱ)O ذ~2bY7ㅃl" W^ye˖y=эׯO?8իWON,wfg#;;NJO>硐5+G!^3&$|/P0:tZQcHXPSJn4=FoJBB XtH+,[5g̘A{졅 s=G'N={޽{iiӦQ~qdy͛ q/袈_%VJyN腀"5觹%XAKrA @ qu+5[Sńlka-?| Y,idR<7|h9+9E+<ԩS2‘܉? (7+Wvkr(?-; O1țW^a@$_KU8,U5R X9a}zvNb*fՠ!VgeFhРAju￟C۷YYY2&+<'4.ulذY6w)PwD߻wo3p%DmB&$yEA0MX!5=J>ժҌ ZPrtAF:<)P妛n4h@;wm۶oA1״iSiD|QURN=[5w>'^?([Dc%Oj۶-ٳg_Nڵ1C@@WؗB_.GڞIP"nJ_Q>|kZhA֭of͚%vȐ!\tCj+ 4 6@N@X)6łGz z%Y/$zW{5k@j>A@v>2o(ƷBփOy&4N1h! PZ hV@o/_|Dxϊ΄#  ]y9 X>@< ~ӦMt5Ç,)A5j(9!g)腀"%ܱEh ċ!,| s"CL(@i%ʡx)&YN;8)쏿Iyd$#H )OŬz1ww8fP sFDeI w':ggvgzfgWy]+BFH7HT>hWhla]?+D1h:EtMmU' #==="BH (Y|zg;PPP@r }–L0mVgXj$EH-ꓐrf؉jlGioTRFZ4H3@x&NC=0?T;wa?BvH|H-8w gzHj/ʇʶ' 9d_O\F) s7h!iOw|ʁGE_uzgӰv%+~n6X oڬn+h M% ~CM7HϮ>jeTc?Jg9*iƂ֌@@BR@YRvJah4vXiܸqt71wg-P؃z!!@7#+V R8>`%Sy34j+l.]J .70@_!q ]Yǩ)bi|_F&P% xǶ`}Um&I*z&/..3zhFA6P. <`_B{^ =scȐ  OjW AkZW)ͷ\N=2 m 1(,$@Szô`ExQԖ >]X "@-qo(={#۬ˍs%`ڻ[xOnK]ۂZЦ#}iմ&Ȃ bA .PޅiΜ9֯_T vbOau=Qm{~>p7;E2%TH':֐%GQysS]C+KIJUuWF) 4#ꫯ*z*jN4oH2e uޝnVbm-@7(Cm.ҋ/H,.իWwy}II;]떉X}Jݺu|y/ժ)B6FJmt7a0)Ki P.KvYFKvU1=ґ|ض[oϓϸ^ګGי #~y'XfT,EiZGo tVS̡2eUT'N1 VZT JWRn?:G`X\ (#R8YyAse=7u wMny[n;Jʝ?yQtO>T!ҥ{Xw ()'eͻ]vRY\rlŋiԨQGB)%V@+s1x/{:,j5kH~a:ݧ1mWS;a-`:KKtP1TURVBޤ|ʶQ8v;^s?ؤzmn\Z>Szk%<|9G V1b%4.`L,(ebf@a%"apښNǜ4.W;#~/^~ t:M{讻z޽ \_%KH(/'6[f߾}ۣ>JW]u{'LvTl3 S@1'xSNRdO#X9eCf8ށG1YhVyHERP٫5p£K'skߓ`A{N +<*G9dEϪ^AVV!vij|ڡ| Z]eTelxkrPL h!$Mb$aGC bHf"$n.1')~jaGAYW;w.رCL>K<]{RY]^GsWpu4}ty',c]y2[QTނu1vٜKG9gΜ)?x޳_~Rc)_w)D(?)ONg{Q>oZ~Y*FԼL= 1:6fwIw//$Vċ0M[o_PaF\H[.|]EV_'2iYv(ς=O$S=l*.'s)z ڝQJK:n Hg+;wgG̿T`>G EBm}rͻsYFL .r;LJ}| M:_-[F3f̠K/T*l.+<<X SO'}KmMp r{Wl?3N"L]B'w2x!`}z=p>5q[{ArZ:\19,dέiؚO˧gUӎwSeoxFRrNPVy/O]|£'N3<;\XX(,t7<Á2ح; &x^3U T@m~MN 4HNQp8>={-CoᔃqF|H>F& O~ZqV3VF&"!PT@A07<$^^SL }zzdu1:s{:>N(N9O ~kIbERù+ y"&RϞ={Rnn.NEDo%\B쪒ϟ#^(WTEDƲlԠ)x~cͥ^ۆ0'l@3G8ҫWÕLN\e;6ɜ֫}bTf͝'OG<\/M^ /߇'fXݾ|K;Ԗϣ|>3/G~y/@D&$4WϤV?^٭U1u֭[D:t+y{ /@[?wyT4 x}E]$dc{Q;&Æ S!845rQFC| j{v7 zvre6J@Ԝg2Vi$'8u k.ZS  )A\ ~OCym&_=GT;yN8A٣[N:c X{C14@v^6|(BHpoO?^u7lkG: %IW>kXHCE?шGt#C" V+#Jv%QA]U?fBHP#T @y&`|C z%`(<`XUFU#.9 plJKʧkG|M)lː@E؀Í&ۍqʧK겝gK@Z*F=Dp0w+pN1i0w/1 @bA@rUud6}T,Kd7Op-+$x;ow F.ѫ]G鼊h\*;IC-[4]}mFZh.;Q E@y `}UӐ@ h"EA@ɾxU06grT2ɕH$P@n-Doo!Wzo>@88~}*)1Ղ&jꁀ˥sQgvCAsΓE-=nL>izNi={6dmB۶mjr}hdLԗ_$CQ2)#P^nQ Q'luֈQm~PLiӦիk &PAԷ^XK Pb  ؎*ff[P˦yyy)&|>>$F@>A(0:(&B9  #Z SrP1ii4i׮]4w\Qe%<ۙ(c4Q$92 u\=( @4"\Hfc2-h_-ZDK.T(q} | iPMc AQ D@%\*S9ody9z\ptW҉'(g1)!4HrvK0(7Z)OϤMUNG,@VJ{ʨPTg{=R9so&\~'W4:, b@@8iv ;"G^bpIlf`;<~ToXBSJ6,N#]Fod79lGH‹ƌC[&>itiA_Y: ,ՔPrm@Z~O4$Ols6 .'!%@qm6ٳ' 8ON]tI]ꦉ(HV= "b2[U~@Nqx55О ,+7ІO.VjJ^2Lroəh[j.b4QWT&٥)z+hFW}-)@ ΈI$JsD;3tFϫ_}U#<"҈TB"N hđx&`,+ :7ۈXM J@(c'.oM_k9QoSX/ӊvUBud W0$ĊTWH=6TijCKUG2iy+h4.Hmڴ_N"F@} ] &jDdCFY2T 0H*gnx{X#2[]@ fivWw}#G#3.#@XHBy!w% LQcb=0Tj}y+}\s<<2b4rIDATY1ʥEI6ok3H&xKg' !vX<]i꬧EM7:мw[۫WNr'@%`ڼI(ti RITݰ8{G"ʹt%y+ yVkkvǟ% O?M<OҥK#Qʈs BeHm;;' yv.:G%9\uG~{mVѵB9,Ap1\I$#?-dg5eS1ߓoǴ 1ƳePk;]+qtŢ~CsԨQԽ{wyzbbH[FژO-PHίhrLbڡL/F ̉_Bn_ЫRYl>[g{Xl0O}, ڜ<,2iYv8TYhayyࠗ&Ǹ/H)֭U-(|pZ;SYk!RaM 4 Զ+r;;wGs&wԞ@P֭=rV8CJ?)B˖-S,モ m&;iڵ4vXO(GJ|M}X wkY~Ne2{=j) ʷC>X|J+w5|r^mPjjxT,OfUs.XO`/:<hpkzi^\Z 7E&gyr?$6P Fv(a&wtܝMۑ@ z@2^9V@СCٳDz/VCB3Fލ|9i&R+GN98S~G%Kx9?!JӄPh۝yC(Z2;`lM< rBI2}̾XI罋6QYaك29Z{y 3j9ʒ/3/2G Nr$X,izϊ'..0rܲjJc)ӄ{8j3)&bWIt3~h|uا0 $rٕн?e*:hS^מq?o]w"# Mߗ<݁m9۷oO6l(fFFr|՜gJ_^t62aJPq<6V)-rg.h0SjR>w\(Cف](*:'/; D@88;t Q]0}~x֜ZkLNq_+rd믿^4u$[SlyӐ!ChƍT\\Lڵ}x~__ɓ%?ctRYƍ˗˩z]G9;v(CIwe6f"}œm)͏zpCU"?Z{-[.|jK |57哫O%3XZYlX|r9zsk2!ĆS*6R>dhpLQ呃t~Qb CIx |sU>?vڨ&xHm.a斈A 6*=O cQa@pum hIax$W,βz5XX.y$k<_{@Y0XX_}]ѣt8w\::敌$P-A d)Oq\2 ^{{vdJ"3U 祉)nCRWֹy}){a/"]ver;v/BvçuQ2(\B 8tlm<r@ ٚRgD6~-$Rd+o*! #ۙoqwg>@Yj>Waf` D Ę@Hcji~uTNY<(ӫ*;*R\}jqfDx_->ٽ#J(~ @ad1 ` auKtvc@:T   b͉ C YoXO,̍fH t42%a3uTZ߾};=úN)Jkh0vAasjHĖV|r#4!ʻuTb#%aΓ,Kst}I7w ɠ0nȯ=(3F qDλ N1CV5"9q  '`ڼItFhyr5ʷ >?Kx~[CgII >|zַidn@mwm lگB4@I)tO:c)pDxSE]Y6l[oC q F#V0 x$`Y\ nq޲2@bIlX߀tC=2\P"aB4g3%PHr %Oj2 c !CV>Njs՘mTm aYh…TWWG6V\)f$TRň]Fd4FZL*{MY*cvy-B)o=ژh |Qy>piUضyjА@LRiJ)yװf hhp-4<:߼Bh J] EѻBS6x`W{bH)%RԻMaʺTk/ hˡoᴑɘXhŊtA۴iC]tN:0@>` ^R@SG@)%uD@y3z6|ZQ oyUWiϞ=rEwA3gԇ"F@ ؉x7s63= 'tu*մxK< ={;E׿lBݻw%M[KGv}.OXx 葀U'pLnwA "21ԪU+9_Q $tڕvោsGəF.Ktb)nD4IJNIԱcG1cYFNv<;ҥKi> p}z# v wsb!)IoBP%&/(ɔNNLZ~mz饗^rTO>R1M'Fs&}D+"U(@4%09:Pғ[Saqbc#FqF:c(%%%V"@ @<N ;ģ!kѼy?2:e C6m@J! KGf@ Jwv4gψOC |zᇩ hHD@EEظ.G<妦O÷oߞ(:!@@@ 4?<ڵ,YB~)=:+f =*h.{w*++[nٳgӥ^JĶG999ͭk@6@E   !Pv>.S눀ґ`@@@B%|zņpG    b @m 5TU"g@@@ r)r,QRH3Jd{#WzFMh)vYAk}Hfc2tؖ341(:9-6, H spPw%%8 z%`Z,$L놦GIQ de;ÿzro!7 @'_e\N2mG  [!oin2(c[Kvޜ57V~r@m9- 4 FhR HCѣdKQ.M4̎>yp DƢv :#`8tPQ"K(!@@PU)̄DIvlxvly&4Q l6JB;v|Xp|aCW̃H&(ѤtMPvZQFofMQLC$/T!~9y $4Q~ l>d(/_t/D%8]00cZ}=r.~jlG=8X SwxjUV>YZݡ;!xpzzM\t'   P@cAuOOIAtC׀iJS%N@ ƒ> GbǣZUi{6((蔀7T%[YX䓶>8hmO,r G%bczY@0ۧ(ۀojTZU  P@Au蟀b7֪b4;X !"ݭv'' MP@Iu@엤lj#hWLTcO ` -OZgӁr4{I h4i.0߸lb7$c1krĄ0#2A@N T4 sFQ)5 /"@ ZF45A$"-3葀UVdrRσ>9N@ F8% I.g^nH @Mg7!А(!L~\|.v +P@cE` }EtC)} @@/L?$*Υbx(ik_21j 4Q7ni >Ķ  7eKTEs-u8I7Q-WmVobႃ=֨IBx* Q%fEyU>vCwş.Y;;4Hȁ2C fhu|bgÆ/DUUοo>igyy9UWV(Ӟ={ēG>GO$<^3Ul;tr"vinམg,8W(\U?ί_.v;:2  d 2Ѫ(I{7#h>SAF1 txY6 " D}}/ERpS,J~(5ЇB׏\)[\qjrM?Mꙿ^3=X,e9Q ZʪM(vGr,2km} JǜlT~/z!>PGzq(#]+*uܙy9s&= ~>|tg!&OL+V\b哯Z4{lLkN?>4o<޽;vm;3֮]Kcǎ?N?t++'yX=O!lbǶ~Cd[x܌>d4 vP A<ѐ]G06HsFt=qr]729 !͙LI ;N o9Fq#wP |>(x#GJדּ΢K?/G*y*GAׯ_OC'֭[G<}.b|r<+|Aɋ{ׯmZ\2l0bLBgy̏Z6G.T}M ĴRs/*t;b4=0ڑFx1tP><8h\9TwI`|[&Q%~wDW7)]ܸ>bU, .J^Cyg`~$ (lɣlSvٸqe;vk~J{EW_}dEuܸqrԴgϞt믿N6l5+-Srԧpj~ϓit_S6ұgE_G. %92 iLi\.V-ښﰜDU֞쯕x""|P0 fL8NrRq*}"G(C/\;J吧y1 /BR &MzJz O4|FFs9R96^@tM7izw9g}TnΝK{zyQvv6u҅N9yN0}9>[x݇vUUEMOIg}?~@B!`ڸ^5۵+҃?b1}:B| (,?H)l~LpB `6i?vꠎ9"BPRyA? %"ۗ P^m]cq 748Pm@ݥX_z,7O~:[SۃQJU8 "{K hqq.Gz}s?cYꭴ;$/Yѱp]$"ˉI='DaNsvl,R|u|r~6W>9ԔONGH|0Vۆ(ɡ z!]]0/ *,4A@ #OT. Y/ͧ[#A h4(ݐo,]  MP]q, m4LTkv5f#D@Aʧ~Z A@ b7$_@ M/5:W@^D@@iԣk|wTFȃ z%d;u? ;/ Mzy@kԤc}dQ*!ۀNPR  M ʧzފ;${E hH]035UUFgFj@@@L~UcOaU^bH h jp& Udă[NOŘBfPOFjEWv6L<[ ԅ :ZGm6jOYM[s(:uI>h0՜RӶmB$M8RFK&cR@>D@4 @&mԥ{Q,@9E,01  UdildrD_ZUDe5~A.BhL ƀQ|]ߍ9 69 u]MH -(-pue$sZG / (hx"Px P;L2fЙQvI$}C #@$boVlF'}PJO_cĄcJA@g|Ȳdѹ 9uי@+)Ti2%*Y(٘LEw!NA F9jӶBF0uAwKgڏ@4Ó P@p D+'lcE4 6\Q* (*`     (pE    *A46j     W     B DhC 6\Q* K4DNjk^1|VZE;vKPb!Nzz6oLԾ}Ac|d2#ƒ4T{n=z4D 6dٚ,Innnk:4=Z?~ڵ/7$$''?rS,5:u %8ݬ{4m4 H:qD:_W+.ڸq#}q$uz=ի飏>JFiВz^Zv- /2s0`r&ĂN` ^'7b@K!i@@@tBt:b4BmڶmKC VZ5e'ӠAehrK.D?s1Z0eQ>} l@Yy@l@}p@@@@@kך0!GOӢEb/%Pci&ꫯ:::"ˆY}%^$NK믥 7ol ߍŋ7}}1hM ք($w޼ya\2y//tWΝ;[VˆY]y_L^,#}}v:u*mذ!E}y=fm Muu5]q4|z׈z*( m۶NBɣ[Iر3Zr֭[cqBD *-\{9;袋.s=zT~۷1da޽Rqx MaK7tdK/Q]]|Z/SL,bC}6ynd]Z'-l 9|pbO?-[&Ѷg¹%N˕wRgy@ݣ>*/y'Oyq5\C<]S׮]cn?xʉ56| b=W駲<"i _~dƫilsMz<{) (!#O;$<$eÃǏp~pqK|xE;O6l)In5hΚ5K8i{Ŧw*x6I~e/{g$TG*e#UhΝq^^ʩ?4}tDx 3)`zۏ;73㽑Y8 }O<= &27VYI³س9tP={9r$~[ Jx6թcn?5uH&؀FvV$Y(֩Jl?xM-~.cgdz"9+=mmu]'#/~Jij~/j~}sEJl @-F<# M8]v%SÆ i_r%rt.>;aVѫ1rG}$9 Z-Y P?\#gSN;uY MͨQ&DigNcDS1j/BbD<QS^\gSNaS ~N:R5uHњP | @՚0!N@@@@&Tk(@@@@P8КP |`Px+iӦ#^ ЎPآd '|R (o T(@4$TC(@ -[)gg @? @'裏RQQ͛7z!t5ƍ%W_ѬYhĉtӶm57x#p{N;4KoI @ @c=G0auYI|\@+V+++iәg)1bO   W  ^ή JsΥg} *y^a?sf@@ \"'#@GWPߌTG<펑HE9  N :h@S@E    A jE)    T     NjR=IENDB`bench/man/figures/README-custom-plot-1.png0000644000175000017500000012146313620533713020001 0ustar nileshnileshPNG  IHDR4JPLTE:f:::f:ffx333:::::::MMMMMnMMMnnMnMff:ffff:ffffffnMMnMMMnMȎ:f:ېnMȫߊf:ȎMȫnې:۶fېnfȎې۶ pHYs.#.#x?v IDATx}H^tQQkw'3zXNh~D}/88:7өѺI.uq!gFC>wWm]KH992!?D&b:x:;Zt1 tn5.hH/E!u_D\`q~!㏗[LfC&b:x:;Zt1 tn5.hw?I #% 0)J fN%Ύ]Lt1<=[ lpqq4\!~<>c!?nKbzH\_<;u4yx)⏧W(wǯΝy1J< bx:{Z8&?{~48|mvțTh]$LӺոpS.ga|i!o"SuA0] OgOVB0[G/ CFU(hxΎ]Lt1<=[ l )N7\iѥ./h!o"SuA0] OgOVB0[GC>15@0.7өѺI.uq!ͣaA裱ytv.bij\fh8}}P o%өѺI.uq!p|eb|_AHLOgG.&aΞ֭ƅ`6MhCDLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*w~9qu.bij\hGLi=tA0] OgOVB0DC8b:H; x.uq!!1J_^nW.bij\hGLa痗j]Lt1<=[  T"L48Ξ֭ƅ`pt*v&\ OgOVB0DC8b:; x.uq!!1J<Ӻո N%Dij\hGLagtn5.C4#S3bx:{Z!өDٙ;Ξ֭ƅ`pt*v [I.uq!!1J}<Ӻո N%7uKt1 tn5.C4#SuA0] OgOVB0DC8b:x:;Zt1 tn5.C4#SuA0] OgOVB0DC8b:x:;Zt1 tn5.C4#SuA0] OgOVB0DC8b:x:;Zt1 tn5.C4#SuA0] OgOVB0DC8b:x:;Zt1 tn5.C4#SuA0] OgOVB0DC8b:x:;Zt1 tn5.C4#SuA0] OgOVB0DC8b:x:;Zt1 tn5.C4#SuA0] OgOVB0DC8b:x:;Zt1 tn5.C4#SuA0] OgOVB0DC8b:x:;Zt1 tn5.C4#SuA0] OgOVB0DC8b:x:;Zt1 tn5.C4#SuA0] OgOVB0DC8b:x:;Zt1 tn5.C4#SuA0] OgOVB0DC8b:x:;Zt1 tn5.C4#SuA0] OgOVB0DC8b:x:;Zt1 tn5.C4#SuA0] OgOVB0DC8b:;sKt1 tn5.C4#Sˈ͞[I.uq!!1J}<Ӻո N%//sKt1 tn5.C4#SD5xn.&aΞ֭ƅ`pt*v&\ OgOVB0DC8b:; x.uq!!1J<Ӻո N%Dij\hGLagtn5.C4\t"b/ agtn5.C44b^Zgs<Ӻո phhm0 w2s1<=[  G>} v [I.uq!!S\54@ڙhs1<=[  AF2n.&aΞ֭ƅ` h]$LӺո ѐ!< bx:{Z!2DuA0] OgOVB0DChtv.bij\h Ύ]Lt1<=[  ѺI.uq!!C4x:;Zt1 tn5.C4dOgG.&aΞ֭ƅ` h]$LӺո ѐ!< bx:{Z!2DuA0] OgOVB0DChtv.bij\h Ύ]Lt1<=[  ѺI.uq!!C4x:;Zt1 tn5.C4dOgG.&aΞ֭ƅ` h]$LӺո p$ -]$LӺո p" -]$LӺո phhm8[I.uq!BL2;[I.uq!!1J< bx:{Z!өѺI.uq!!1Jĝ_N\蹥 bx:{Z!өDef-]$LӺո N%D>ij\hGLe痗๥ bx:{Z!өDeUCN< bx:{Z!2DuA0] OgOVB0DChtv.bij\h Ύ]Lt1<=[  ѺI.uq!!C4x:;Z4Dij\h Ύ&wmbx:{Z!2DuOgOVB0DÑfwz3+WxK?B0] OgOVB0DC8b:8F2Q [.&aΞ֭ƅ`pt*v&\ OgOVB0DC8b:; x.uq!!1J<Ӻո N%Dij\hGLagtn5.C4#S3bx:{Z!өDؙhps1<=[  T"L48Ξ֭ƅ`pt*Qvh-]$LӺո N%ηtKt1 tn5.C4#S3Ѡbx:{Z!өDF2n.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.bij\hGLOgG.&aΞ֭ƅ`pt*tv.brlƅ`pt*tv.b2+lƅ`pt*tv.bL4D  Th]$LCeҏ  Th]$LCeҏ  Th]$LCՙh!өѺI.37.C4#SuA0] Ug!n\hGLOgG.&aDCܸ N%Ύ]Lt1Tq!!1J< b: qB0DC8b:x:;Zt1 Pu&ƅ`pt*tv.b!ŝƅ`pt*tv.b< lDž`pt*tv.bL4D  Th]$LCv2!өѺI.뉛_uEvKB4tN%Ύ]LBu1^GDn ӹfSy-]OP] Apt<: I.|5(:CsK߃h!11D"]LBs1^_T]$]n 1<~.&D DC7 >:"Ih.  DC8b:D&]LBs1n  4bA4@4tѠI\ AptMb 2 @4#3@4h$4hh!1A.&!wrkn{ @4#3@4h$Tc,薾 DC8b:#RuCwK?DP] Apt<: I.L2xn{-= DC8b:cEbx:{Z!ӹb3  @4#SuAP] ih!1J< b " @4#SuA] Ӡ DC8b:x:;Zt1 Ř4IB.[.DC7 Th]$4hh!1J< b 2 @4#SuA\ Apt*tv.bA4@4tN%Ύ]LBs1n өѺIh.  DC8b:x:;Zt1 d hGLOgG.&!\Ipt*tv.b1 ^tK߁h!1J< b " @4#SuA]d[z_N  Th]$LӺ/#ƅ`pt*tv.bi]}WA!1J< bx:{Z%hGLOgG.&aΞ֕w|y vǤn!өѺI.Q-M4C4\tb OgG.&aη4Ѡpӈ{jtv.bL4  '㹥 b:8;D:DÑOª!Bg{< b:9;D:DCӧjiuAIDsp] Ughh Ύ&u1TA!C4x:;Z Puq4pq'u h]j\Cye!2DuAIDPu&Y h]@4CyGckC4dOgG;q!!C4;sK u9-ظ p$3Bg{'?=t]ҏ  '>Ct6GڙhxDAy'ƅ`DCkiDC;dK?6.C4\zw:ɓ} w)tq!!S\54@yTvK .:LV[q!!C4H; .,//~l\h S[hpAՙh!2D&\Pu&ƅ` L48 : qB0DChv.$*: B0DChv6.#t> B0DChv&] A#\I1hl N ƅ`at4PO\g :n-}DCkI$7tظ p!&g.-]$4hh!1J'@=tA\ hࣱƅ`pt*v&\ hBrK߅h!1Jĝ;:#Ih.  DC8b:x:;Zt1 d hGLOgG.&D DC7 Th]$DNshn{ @4#SuAP]f[DC7 Th]$Thh!1J< b$綗ҳ<UCΓxK3oGbiÒ!0|:c:FO'|/oq ՗DA4d>}gA4($LӺո1M8?ӁC{N_y0\ts7\uQyS DCf:] snbij]:$Χ —/ 7x.WJ ]DL5:"I.uqCO_<N~4޺pn3[&nz!jNpR6y ' <~.&aΞ֭ ˛ӑÏWO?\=p ?k'L<#U}!C4:"I.uq#_}o p|*õʍ.PVo*mA4dQYdct1 Ř|qBy-=w4)_?Hg>|?͗h t|D QYdct1 ŘyPB[aхOwvuÍhV~A7ǧ[ c3hr"Dgҏ$Thh8-1x|L|o\ #Ia {ѠI]=>|-}a"D7Ux(n%a}+tMb AHn 3 p3 +o晆GYo &]LBs1aډcJ]N;WeNb0s3({WL2HN<~: Ih. w4\o~ Ż"&.t'9>!Q|(wO '7C4($4h;ʫ)\/?]W1p9@_OOI\nNpy1kxԣIh. w4WY~t?溏C_?`xW$N!o"SuA\ Ah oϛm|S$i,5^/\Oes4hѺ`v//& 3nD÷ˇ> 7T8p ?Gۗs*~oW+6FCjA OgG낹ITbx:{Z3v<O]hS ŷc#3[EçOT((,aN4# PVwNw<1A4@t w = ғ =ܜ%Dtd[ 'R4_FDNlѰ̛: tv.ࣱ!|՗ғ3_6.c û'n?ߐYg Ύ]Lt1T߾?t`'vC4s9yɕ{h]$LCy:.c lxaE4x:;Zt1 Pu&ƅ`LԆ 5 ܳOgG.&aDCܸO4xyo5kwfwz3nJn黟 F4 bܳ"өYzkqH&ۻEý>#1VYT,kM4pg!$3N- \I!1J8wU#py2_(updѰazp퉆E?(8=ˑ~Ÿnxc|`|#DÑ{V$b:DÖ&~bw"d D鉀_<#8>XO7xQ3ޟ'osN{"+HtMZE.#M4@4܍%?V<(_/ߓ-nfH7!O)u# +HtMA4@4܋ٌ@xsZ!8 s /ݞ k"NO9;${V$b:#@UA4<;͠,斾p'ǫwJba U0nÆ[ݞ (>OVY錙yuAxK?DAx^u-}: }х3!oӷw~Hzrķs?o=.qW(VY!D'B4@4܉˫Chz78H0$ջ.·gGY.b76i=+1k^ܝCzK(L2hSk.g6.4~3-x"r1WZDCzb! ΀-bN%ΎDC;<=+h Aa8I:֫waO~l _o =$sߴ=+Ht*t .OgO; 7!_|2ć*?:V?7ukYTȕ{V$b:x:Zy) .#OgO{vrf.?ƉWo|'B%.'] h8mYk"SuX4LStktρ:f~ջ+Go}C/Y\t3-6U1JniOgOhp:܏O=p-kPpιqm{r(ϋwE!SaSvK[D>.tOgO]7a|ߒb^ a[.,Y޷өDڙhxF42wt>?]AyZP oIͽ}n_UwʆC4 }+1J[hpӺ{|fh8]'z8[,6q >9q|__1.#q/+iݵөDy)MaȆ%x:{ZW1x:<9 V;!K$S3hDv1ft#ѠNGc_RFF _"1JYx:ZDÞfP%өDؙh O4*>[|=+1JYx:kZ D5ʝpdN%D,\tK剆E8+Ht*vs4<{Byz5}h XgE"Syuʻ=@4B4#sg0 "dg[h XgE"3fZ91"#!{'tw#&3VY!!6ryAhR\I]+HtFV:0z@4L4 TDíd usK Pu&YGf & hpAՙh dhlt ]QghNDÙϪfѰ4CwO#24p$} bѰ4C4 0xh8>De+Ht#얎|hXýPtNEˁhY4|d+Ht|a1sK4=D݅t D,DC8b:DVEC#ᠻ b5'B)K$3` S"[hxDf n4|{wMVY F䣈. m4UD,w!}{V"Sw@4l o4D*˕{V<a+<8r'R5o{  A4 #;(ztgS h7f]rK DÈ=|ehs 6f ȽPtṊÖ&v UDs0.#M4BH:H g)_kUe4L DV DÉDOܳ s·]_܉HfvD >O4&v)Dsb1DvKFXTTA>2C4:'a1<Ӱ Dpb'XE4:'a!wA@4 Լr"DDѰa3ǩ+DЃhAa3a _A=VY  [A4` |` h]F:1a+,!߃b A4 gG h6]rK DQt^8D ѰDDf D+Ht\; K hhGLg6v|ia%wA@4  錘Vu7쉕 A4l _V}O DC8b:cf~e|ҁ\evDkן~ DC8b:c|&[hpAՙh n";ᚕ{V$b:LƄ'ҏ4Ughv4wZ[.GLqg'~Espq: Vpv%ET"<"!fP^Th ߈өD/wUgh=+1JDp4Icl:L8Ftq ?1`[p"d8b:;O>xnih3` o}E~>OF?{ nrϊDLaghxDþa8S厧'ME\&@4`N%DswODî!S)-0 '+[n| _?-YgE"S3h: ݞhU4 '8}>NGÇ/)))>Y4|)Jr+. VYT"L4 +}3Ѱh?P|ohC ?Ѱ(4@DѰh[w-M4o{?ufsW.Rܳqg?NLi hh|Z@:r['B{cZ)=ht=RhxDf:z =O0hށp|1h(Xg βDDf ˣ Ӌ Q4\O?˕!  VYDC5D #jK0n]ytn %q{?:gOȱr"Wז^ѐ!2⹥ A4lFDChu>B4 6h DChu>ssK Oh a7 Ay-]@4< a3@4dQYp{$>{)ǛN}|+!r AؙhxJ4DPu&lтhot9  F:r[wh D,T=Eßy_zw ?9ND|w ѐ!Y: { C~9 _/|GCQ!}ou` L4 BՙhS4|q.> _//Kjm A3/݃!2Dԃ&Dagp~#G k oa8r݃!2Dc|D Di/ ,1fօjVYD[4{hxA9A4DCŝ=?P8y9a>wG լܳe-M4pw!3DO4D\FVp̅/EDa +,AyQDvK " A4c#|υ&Wt^x+,AؙhxJ4'q4D|4v~zNj8>鐘N\=hv&xa!Df:?2|e_/W[(|$_mN IDATP=k7`y  A4qzv!=^30"DC+D/3@4,h a]4|?>gat%jVYD3@4,h a]4?xϟl=d4\>*cZP }Doh C4lѰ.r)Sjy?y`.aZ9A4 !6hX _N"faT 3MC4dQ@4,h ae4\>bMpΆٻC4dQ@4,h ae4@4dQ@4,h h0cE4:'a!Df D+,A9A4 !6h Xg hXѰD`=huN DB 3VYDsh B4l@4r"DDѰa3{  A4 fܳQ@4,h h0cE4:'a!Df D+,A9A4 !6h Xg hXѰD`=huN DB 3VYDsh B4l@4r"DDѰa3{  A4 fܳQ@4,h h0cE4:'a!Df D+,A9A4 !6h!C4:'a!Df h hXѰDn 2Dsh B4lѰ  A4!C4:'a!Df h hXѰDn 2Dsh B4lѰ  A4!C4:'a!Df h hXѰDn 2Dsh B4lѰ  A4!C4:'a!Df h hXѰDn 2Dsh B4lѰ  A4!C4:'a!Df h hXѰDn 2oh C4lѰ>0K-L4 !6h DChu& A4!hVu> Db vѐE4Z:'a!Df h hXѰDn 2Dsh B4lѰ  A4!C4:'a!Df h hXѰDn 2Dsh B4lѰ  A4!C4:'a!Df h hXѰDn 2Dsh B4lѰ  A4!C4:'a!Df h hXѰDn 2>{+ vѐG4f~ea1Df h DѰa3@4d sժhX ѰDn 2_kQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 +y)nSG>Oy]~ѐ#ߣ .}o{hX/O:'>\๥ xyi]AܺKni^DChuN DB vѐ!DDѰa3@4dQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 ]DaZ@4 !6h DCf3 [A4@4lѰ  A4!C4:'a!Df h hXѰDn 2Dsh B4lѰ  A4!C4:'a!Df h hXѰDn 2Dsh B4lѰ  A4!C4:'a!Df h hXѰDn 2Dsh B4lѰ  A4!C4:'a!Df h hXѰDn 2Dsh B4lFX4bvǤn!2Dsh B4lF\4BwvǤn!2Dsh B4lѰ   h hXѰHcR A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa3@4dQ@4,h a7 A9A4 !6h DChuN DB vѐ!DDѰa;DtcR A9A4 !6d~'h hXѰ!DN 2Dsh B4lN4tѐ!DDѰA  2 @4dQ@4,hh!C4:'a!D DC7 A9A4 !d h hX  DChuN DBn 2Dsh B4@4tѐ!DDѰA  2 @4dQ@4,hh!C4:'a!D DC7 A9A4 !d h hX  DChuN DBn 2Dsh B4@4tѐ!DDѰA  2 @4dQ@4,hh!C4:'a!D DC7 A9A4 !d h hX  DChuN DBn 2Dsh B4@4tѐ!DDѰA  2 @4dQ@4,hh!C4:'a!D DC7 A9A4 !d h hX  DChuN DBn 2Dsh B4@4tѐ!DDѰA  2 @4dQ@4,hh!C4:'a!D DC7 A9A4 !d h hX  DChuN DBn 2Dsh B4@4tѐ!DDѰA  2 @4dQ@4,hh!C4:'a!D DC7 A9A4 !d h hX  DChuN DBn 2Dsh B4@4tѐ!DDѰA  2 @4dQ@4,hh!C4:'a!D DC7 A9A4 !d h hX  DChuN DBn 2Dsh B4@4tѐ!DDѰA  2 @4dQ@4,hh!C4:'a!D DC7 A9A4 !d h hX  DChuN DBl0[.DC7 A9A4 Kn @4dQ@4,hh!C4:'a!0%]n 2Dsh b SHn @4dQ@4,hh!C4:' sgѯh8HHn @4dQĮa愸 ~-}  2 @4dQľa, ~-} {4< O"B4tѐ!DDS һ$[.DC7 A9A4< O"B4tѐ!DDS һ$[.DC7 A9A4< O"B4tѐ!DDS һ$[.DC7 A9A4< O"B4tѐ!DDS һ$[.DC7 A9A4< O"B4tѐ!DDS һ$[.DC7 A9A4< O"B4tѐ!DDS һ$[.DC7 A9A4< o#K~IFvSv n'V|_2n\E*Qd7JbS~f(QuWM"$I4LQD3.( i$9SiP"Jha>$'}0 JCDiP4,ҧ}OQAi( JCEO)J4( A4s@iҰHI>Ei!4f( ]P>Irҧ( Ӡ4D J"}'INa 94tAiXO$I4LQD3.( i$9SiP"Jha>$'}0 JCDiP4,ҧ}OQAi( JCEO)J4( A4s@iҰHI>Ei!4f( ]P>Irҧ( Ӡ4D J"}'INa 94tAiXO$I4LQD3.( i$9SiP"Jha>$'}0 JCDiP4,ҧ}OQAi( JCEO)J4( A4s@iҰHI>Ei!4f( ]P>Irҧ( Ӡ4D J"}'INa 94tAiXO$I4LQD3.( i$9SiP"Jha>$'}0 JCDiP4,ҧ}OQAi( JCEO)J4( A4s@iҰHI>Ei!4f( ]P>Irҧ( Ӡ4D J"}'INa 94tAiXO$I4LQD3.( i$9SiP"Jha>$'}0 JCDiP4,ҧ}OQAi( JCEO)J4( A4s@iҰHI>Ei!4f( ]P>Irҧ( Ӡ4D J"}'INa 94tAiXO$I4LQD3.( i$9SiP"Jha>$'}0 JCDiP4,ҧ}OQAi( JCEO)J4( A4s@iҰHI>Ei!4f( ]P>Irҧ( Ӡ4D J"}'INa 94tAiXO$I4LQD3.( i$9SiP"Jha>$'}0 JCDiP4,ҧ}OQAi( JCEO)J4( A4s@iҰHI>Ei!4f( ]P>Irҧ( Ӡ4D J"}'INa 9+ ]J J4( A4s`X=JC7iP"Jha>$'}0 JCDi8۴? Aa 9, ( PdPAi( JCEO)J4( A4s@iҰHI>Ei!4f( ]P>Irҧ( Ӡ4D J"}'INa 94tAiXO$I4LQD3.( i$9SiP"Jha>$'}0 JCDiP4,ҧ}OQAi( JCEO)J4( A4s@iҰHI>Ei!4f( ]P>Irҧ( Ӡ4D J"}'INa 94tAiXO$I4LQD3.( i$9SiP"Jha>$'}0 JCDiP4,ҧ}OQAi( JCEO)J4( A4s@iҰHI>Ei!4f( ]P>Irҧ( Ӡ4D J"}'INa 94tAiXO$I4LQD3.( i$9SiP"Jha>$'}0 JCDiP4,ҧ}OQAi( JCEO)J4( A4s@iҰHI>Ei!4f( ]P>Irҧ( Ӡ4D J"}'INa 94tAiXO$I4LQD3.( i$9SiP"Jha>$'}0 JCDiP4,ҧ}OQAi( JCEO)J4( A4s@iҰHI>Ei!4f( ]퓘tݺhWi׋ ?gA4s@iҰHILn]4֩4}ݔk=X_|x}̢4f( ]P>I׭EK눟s~',Jha>퓘tݺhKiZp S/OY J"}'1uXr+[)U3 94tAiXO$&].k_3~?ץeT^a<( JCEObu뢱!>M $W.\-|_n_!ćOY J"}'1uX~됸 W{"Y>yfQD3.( iĤEcKOTX9?p 3 94tAiXO$&].N{3^/<, wͫ,q￁~i/ / L)y ) ^xX_1.Y?@iBiS{Di IDATpxaix5w4T4`)yS&7( U( Xys@coZ}2- N<RO3gQ14^=qxC3 94tAiXO$&].3x5EiP4,ҧ}[=4|}~kCцOY J"}'1u#Jç+~s<( JCEObu뢱GWZǑOY J"}'1u#JC_{*D( CgA4s@iig0 ̞GR^oU^~[iJQ4 ;t3<3{.{HiީE?]cgA4s@iҠ|'1uCJC,?CGZ53^/EiPiV\73gQ뢱ǔ_yfQD33u!4f( <73gQ1JCDiPxngfԣEc 948 ̞G( A4s@ip=SZQ"Jh(t']g0 ̞G( A4Π;:SfxfL=j]4Fi( PyngfԣEc Aet)t3<3{.4DY3AngfԣEc 9Kvu8zԺhQD3gNa=SZQ"Jh,Iי L73gQ1JCDi͜%;:SfxfL=j]4Fi( d']g0 ̞G( A4sLq3u!4fΒt)t3<3{.4DY3AngfԣEc 9Kvu8zԺhQD3gNa=SZQ"Jh,Iי L73gQ1JCDi͜%;:SfxfL=j]4Fi( d']g0 ̞G( A4sLq3u!4fΒt)t3<3{.4DY3AngfԣEc 9Kvu8zԺhQD3gNa=SZQ"Jh,Iי L73gQ1JCDi͜%;:SfxfL=j]4Fi( d']g0 ̞G( A4sLq3u!4fΒt)t3<3{.4DY3AngfԣEc 9Kvu8zԺhQD3gNa=SZQ"Jh,Iי L73gQ1JCDi͜%;:SfxfL=j]4Fi( d']g0 ̞G( A4sLq3u!4fΒt)t3<3{.4DY3AngfԣEc 9Kvu8zԺhQD3gNa=SZQ"Jh,Iי L73gQ1JCDi͜%;:SfxfL=j]4Fi( d']g0 ̞G( A4sLq3u!4fΒt)t3<3{.4DY3AngfԣEc 9Kvu8zԺhQD3gNa=SZQ"Jh,Iי L73gQ1JCDi͜%;:SfxfL=j]4Fi( d']g0 ̞G( A4sLq3u!4fΒt)t3<3{.4DY3AngfԣEc 9Kvu8zԺhQD3gNa=SZQ"Jh,Iי L73gQ1JCDi͜%;:SfxfL=j]4Fi( d']g0 ̞G( A4sLq3u!4fΒt)t3<3{.4DY3AngfԣEc 9Kvu8zԺhQD3gNa=SZQ"Jh,Iי L73gQ1JCDi͜%;:SfxfL=j]4Fi( d']g0 ̞G( A4sLq3u!4fΒt)t3<3{.4DY3AngfԣEc 9Kvu8zԺhQD3gNa=SZQ"Jh,Iי L73gQ1JCDi͜%;:SfxfL=j]4Fi( d']g0 ̞G( A4sLq3u!4fΒt)t3<3{.4DY3AngfԣEc 9Kvu8zԺhQD3gNa=SZQ"Jh,Iי L73gQ1JCDi͜%;:SfxfL=j]4Fi( d']g0 ̞G( A4sLq3u!4fΒt)t3<3{.4DY3AngfԣEc 9Kvu8zԺhQD3gNa=SZQ"Jh,Iי L73gQ1JCDi͜%;:SfxfL=j]4Fi( d']g0 ̞G( A4sLq3u!4fΒt)t3<3{.4DY3AngfԣEc 9Kvu8zԺhQD3gNa=SZQ"Jh,Iי L73gQ1JCDi͜%;:SfxfL=j]4Fi( d']g0 ̞G( A4sLq3u!4fΒt)t3<3{.4DY3AngfԣEc 9Kvu8zԺhQD3gNa=SZQ"Jh,Iי L73gQ1JCDi͜%;:SfxfL=j]4Fi( d']g0 ̞G( A4sLq3u!4fΒt)t3<3{.4DY3AngfԣEc 9Kvu8zԺhQD3gNa=SZQ"Jh,Iי L73gQ1JCat{3AngfԣEc9JÒ |g0 ̞G( A6skLq3uMΠ}NT{4AngfԣEcfxNzg0 ̞G(  yNzg0 ̞G( Wm*a=SZQS3c)t3<3{.44'gf;SfxfL=j]4FihN,N!̎w8zԺhМXBSLq3u98<3;ޙ L73gQ1JCsbq yfvL3AngfԣEczg0 ̞G( ͉)1a=SZQS3c)t3<3{.44'gf;SfxfL=j]4FihN,N!̎w8zԺhМXBSLq3u98<3;ޙ L73gQ1JCsbq yfvL3AngfԣEczg0 ̞G( ͉)1a=SZQS3c)t3<3{.44'gf;SfxfL=j]4FihN,N!̎w8zԺhМXBSLq3u98<3;ޙ L73gQ1JCsbq yfvL3AngfԣEczg0 ̞G( ͉)1a=SZQS3c)t3<3{.44'gf;SfxfL=j]4FihN,N!̎w8zԺhМXBSLq3u98<3;ޙ L73gQ1JCsbq yfvL3AngfԣEczg0 ̞G( ͉)1a=SZQS3c)t3<3{.44'gf;SfxfL=j]4FihN,N!̎w8zԺhМXBSLq3u98<3;ޙ L73gQ1JCsbq yfvL3AngfԣEczg0 ̞G( ͉)1a=SZQS3c)t3<3{.44'gf;SfxfL=j]4FihN,N!̎w8zԺhМXBSLq3u98<3;ޙ L73gQ1JCsbq yfvL3AngfԣEczg0 ̞G( ͉)1a=SZQS3c)t3<3{.44'gf;SfxfL=j]4FihN,N!̎w8zԺhМXBSLq3u98<3;ޙ L73gQ뢱YJf< PM? f< PM? f< PM? f< PM? f< PM? f< PM? f< PM? f< PM? f< PM? f< PM? f< PPP@J(BiE( 4"PP@JCƿ׿Cp+/k??$MnI8+}BG~]FiHi>^Wk< mr;?̤?n?΂p۷D/ggޔ3G7խ_>p?=3ǃ!dǿi]'zp!R~~M>1stCiHxvz swu~t/z&a')ˏ.|})??|~Ґz͆VQo[#/'dZn}~/ozf~?cuv/&ztK~73G?cᆶ~5} .\J7o.P3 C_V39I8)Ӯ= ?*ܧg9:4lyѯWn[smo{'U􇝝SV$ޯsxK~73GGcv7ߛ_Nnwݫrdb۟.40:'><ÌwO9 ?̛}z#JñmvsˉuoG7_(|',L><Ň<~vrU~ZA>1stCi8}&Ԑ+n])IGz Ocmͫ/d_o*Jñm{^W /_ey_//IDvډzk/=n!V~Z>1stCi8~᫟ V5- 6+בrL#N<;otx!R~b940MѽV oxu7t_L^nک_;m.d_ngn/+'fn( oއ3nz=~,㙼@_?2L ~D ٗ3|7 !!<o\/GM޼+7O*zm L2><پjB]uG4tsN˭w}>Y0g$|e‡/gx3T3G7_?$^T42~묓϶| }_91IM;j^ޕBM]Cix\_~{ao"ɳ>zĤ?xکNK]B%*?Ï>:|PJ~,Vyvwx#nb=3ھ/A]Ci(q(EJニ|}'|ޓw9sbvw[/?j<u@c?^xuwo{{7o]=IDz?/d_jg_:x$JCºi [nW:?G4${=~^RSnpw=z z}dݿQ1JM;[_§ߗ33O:x(JCB5G7;'/WLGaqN.zI;ԴOw|^;qK~|QRVrݿ K kٞ I1MdyeŎl^gߗ33}v}Ґtn~}~{G<_qw}r?˴N}?XL>ǟy>=!Vբ4\|7ᝃog -bvqZޯtD |_gng^OO>sDiE( 4"PP@J(BiE( 4"PP@J(BiE( 4"˿@ xb5( 4Ai@ J( jP'FiP<1JQԠ4|ᅦo_}'\ix9. P0iPO׻/k_?ۗk;Hko9*6fFiw%y B_0A4ۏӠ4݅;K@owﭡ_4zɷp`"pC򿷏{跇~~ڷկ+Jo립<Bi{k^m^]{>)?KWo7j.K_m}'yoO(~o_OLH֝aSKbGS4ݭ~qio?{s~ 500, ], dat[which(dat$x > 500), ], subset(dat, x > 500)) # However you can also do so explicitly to filter gc differently. summary(results, filter_gc = FALSE) # Or output relative times summary(results, relative = TRUE) } \seealso{ Useful links: \itemize{ \item \url{https://bench.r-lib.org/} \item \url{https://github.com/r-lib/bench} \item Report bugs at \url{https://github.com/r-lib/bench/issues} } } \author{ \strong{Maintainer}: Davis Vaughan \email{davis@rstudio.com} Authors: \itemize{ \item Jim Hester } Other contributors: \itemize{ \item Drew Schmidt (read_proc_file implementation) [contributor] } } \keyword{internal} bench/src/0000755000175000017500000000000014151175722012257 5ustar nileshnileshbench/src/load.c0000644000175000017500000000123013630252670013335 0ustar nileshnilesh#include "os.h" #include #if OS_WINDOWS // Currently does nothing, there is an example of emulating linux style load on // Windows at https://github.com/giampaolo/psutil/pull/1485 #elif OS_MACOS || OS_LINUX #include #endif SEXP bench_load_average_() { SEXP out = PROTECT(Rf_allocVector(REALSXP, 3)); REAL(out)[0] = NA_REAL; REAL(out)[1] = NA_REAL; REAL(out)[2] = NA_REAL; #if OS_MACOS double loadavg[3]; int num_load = getloadavg(loadavg, 3); if (num_load <= 0) { Rf_error("getloadavg() failed"); } for (int i = 0; i < num_load; ++i) { REAL(out)[i] = loadavg[i]; } #endif UNPROTECT(1); return out; } bench/src/mark.c0000644000175000017500000000662114147256521013364 0ustar nileshnilesh#include #include #include #include #include "nanotime.h" #include #include #include double get_overhead(SEXP env) { long double overhead = 100.0; for (int i = 0; i < 10; ++i) { long double diff = expr_elapsed_time(R_NilValue, env); if (diff > 0 && diff < overhead) { overhead = diff; } } if (overhead == 100.0) { overhead = 0.0; } return overhead; } SEXP mark_(SEXP expr, SEXP env, SEXP min_time, SEXP min_itr, SEXP max_itr) { R_xlen_t min_itr_ = INTEGER(min_itr)[0]; R_xlen_t max_itr_ = INTEGER(max_itr)[0]; double min_time_ = REAL(min_time)[0]; SEXP out = PROTECT(Rf_allocVector(REALSXP, max_itr_)); long double total = 0; double overhead = get_overhead(env); R_xlen_t i = 0; for (; i < max_itr_ && ( (total < min_time_) || i < min_itr_); ++i) { long double elapsed = expr_elapsed_time(expr, env); // 1E is record separator  REprintf("\x1E"); REAL(out)[i] = elapsed - overhead; total+=elapsed; // We could do this less than every iteration, but even with 500,000 iterations // the overhead seems to be less than 200 ms, so it seems ok and simpler // to just do it unconditionally on every iteration. R_CheckUserInterrupt(); } out = Rf_xlengthgets(out, i); UNPROTECT(1); return out; } SEXP system_time_(SEXP expr, SEXP env) { double real_begin = real_time(); double process_begin = process_cpu_time(); Rf_eval(expr, env); double process_end = process_cpu_time(); double real_end = real_time(); SEXP out = PROTECT(Rf_allocVector(REALSXP, 2)); REAL(out)[0] = process_end - process_begin; REAL(out)[1] = real_end - real_begin; UNPROTECT(1); return out; } SEXP hires_time_() { double time = real_time(); SEXP out = PROTECT(Rf_allocVector(REALSXP, 1)); REAL(out)[0] = time; UNPROTECT(1); return out; } SEXP parse_gc_(SEXP x) { R_xlen_t n = Rf_xlength(x); const char *out_nms[] = {"level0", "level1", "level2", ""}; SEXP out = PROTECT(Rf_mkNamed(VECSXP, out_nms)); SET_VECTOR_ELT(out, 0, Rf_allocVector(INTSXP, n)); SET_VECTOR_ELT(out, 1, Rf_allocVector(INTSXP, n)); SET_VECTOR_ELT(out, 2, Rf_allocVector(INTSXP, n)); int* level0 = INTEGER(VECTOR_ELT(out, 0)); int* level1 = INTEGER(VECTOR_ELT(out, 1)); int* level2 = INTEGER(VECTOR_ELT(out, 2)); for (int i = 0; i < n; ++i) { level0[i] = 0; level1[i] = 0; level2[i] = 0; const char* str = CHAR(STRING_ELT(x, i)); while((str = strstr(str, " (level ")) != NULL) { if (strncmp(str, " (level 0) ...", 13) == 0) { level0[i]++; } else if (strncmp(str, " (level 1) ...", 13) == 0) { level1[i]++; } else if (strncmp(str, " (level 2) ...", 13) == 0) { level2[i]++; } str+=8; } } UNPROTECT(1); return out; } extern SEXP bench_process_memory_(); extern SEXP bench_load_average_(); static const R_CallMethodDef CallEntries[] = { {"mark_", (DL_FUNC) &mark_, 5}, {"system_time_", (DL_FUNC) &system_time_, 2}, {"bench_process_memory_", (DL_FUNC) &bench_process_memory_, 0}, {"bench_load_average_", (DL_FUNC) &bench_load_average_, 0}, {"hires_time_", (DL_FUNC) &hires_time_, 0}, {"parse_gc_", (DL_FUNC) &parse_gc_, 1}, {NULL, NULL, 0} }; void R_init_bench(DllInfo *dll) { R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); } bench/src/Makevars.win0000644000175000017500000000002313620533713014540 0ustar nileshnileshPKG_LIBS = -lpsapi bench/src/os.h0000644000175000017500000000036613620533713013054 0ustar nileshnilesh#ifndef __OS__ #define __OS__ #ifdef _WIN32 #define OS_WINDOWS 1 #else #define OS_WINDOWS 0 #endif #ifdef __APPLE__ #define OS_MACOS 1 #else #define OS_MACOS 0 #endif #ifdef __linux__ #define OS_LINUX 1 #else #define OS_LINUX 0 #endif #endif bench/src/nanotime.c0000644000175000017500000000644613620533713014245 0ustar nileshnilesh#include "nanotime.h" #ifdef __WIN32 #include #elif defined(__MACH__) #include #include #include #include #else #define __EXTENSIONS__ #include #include #define NSEC_PER_SEC 1000000000 /* nanoseconds per second */ #endif #if defined(_WIN32) || defined(_WIN64) long double real_time() { // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx static LARGE_INTEGER frequency; frequency.QuadPart = 0; if (frequency.QuadPart == 0) { if (QueryPerformanceFrequency(&frequency) == FALSE) { Rf_error("QueryPerformanceFrequency(...) failed"); } } LARGE_INTEGER count; if (QueryPerformanceCounter(&count) == FALSE) { Rf_error("QueryPerformanceCounter(...) failed"); } return (long double) count.QuadPart / frequency.QuadPart; } #elif defined(__MACH__) long double real_time() { // https://developer.apple.com/library/content/qa/qa1398/_index.html //static mach_timebase_info_data_t info; static uint64_t ratio = 0; if (ratio == 0) { mach_timebase_info_data_t info; if (mach_timebase_info(&info) != KERN_SUCCESS) { Rf_error("mach_timebase_info(...) failed"); } ratio = info.numer / info.denom; } uint64_t time = mach_absolute_time(); uint64_t nanos = time * ratio; return (long double)nanos / NSEC_PER_SEC; } #elif defined(__sun) long double real_time() { hrtime_t time = gethrtime(); // The man page doesn't mention any error return values return (long double)time / NSEC_PER_SEC; } #else long double real_time() { struct timespec ts; if (clock_gettime(CLOCK_REALTIME, &ts) != 0) { Rf_error("clock_gettime(CLOCK_REALTIME, ...) failed"); } return ts.tv_sec + (long double)ts.tv_nsec / NSEC_PER_SEC; } #endif long double process_cpu_time() { #if defined(_WIN32) || defined(_WIN64) HANDLE proc = GetCurrentProcess(); FILETIME creation_time; FILETIME exit_time; FILETIME kernel_time; FILETIME user_time; if (GetProcessTimes(proc, &creation_time, &exit_time, &kernel_time, &user_time) == FALSE) { Rf_error("GetProcessTimes(...) failed"); } ULARGE_INTEGER kernel; ULARGE_INTEGER user; kernel.HighPart = kernel_time.dwHighDateTime; kernel.LowPart = kernel_time.dwLowDateTime; user.HighPart = user_time.dwHighDateTime; user.LowPart = user_time.dwLowDateTime; return (((long double)kernel.QuadPart + (long double)user.QuadPart) * 1e-7); #elif defined(__sun) hrtime_t time = gethrvtime(); // The man page doesn't mention any error return values return (long double)time / NSEC_PER_SEC; #elif defined(CLOCK_PROCESS_CPUTIME_ID) struct timespec ts; if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) != 0) { Rf_error("clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) failed"); } return ts.tv_sec + (long double)ts.tv_nsec / NSEC_PER_SEC; #else struct rusage ru; if (getrusage(RUSAGE_SELF, &ru) != 0) { Rf_error("getrusage(RUSAGE_SELF, ...) failed"); } return ru.ru_utime.tv_sec + (long double) ru.ru_utime.tv_usec * 1e-6 + ru.ru_stime.tv_sec + (long double) ru.ru_stime.tv_usec * 1e-6; #endif } long double expr_elapsed_time(SEXP expr, SEXP env) { long double start = real_time(); // Actually evaluate the R code Rf_eval(expr, env); long double end = real_time(); return end - start; } bench/src/process_memory.c0000644000175000017500000000712513620533713015474 0ustar nileshnilesh#include "os.h" #include #if OS_WINDOWS #include #define PSAPI_VERSION 1 #include #elif OS_MACOS #include #include #include #include #elif OS_LINUX #include #include #include #endif #define FAILURE -1 #if OS_LINUX /* read_proc_file is derived from https://github.com/cran/memuse/blob/f2be8bc6f6af3771161c6e58ea5b6c1dd0eafcd7/src/meminfo/src/platform.c#L44 * Copyright (c) 2014-2017 Drew Schmidt All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ int read_proc_file(const char *file, uint64_t *val, char *field, int fieldlen) { size_t len = 0; char *tmp = NULL; uint64_t value = FAILURE; *val = 0L; FILE* fp = fopen(file, "r"); if (fp != NULL) { while (getline(&tmp, &len, fp) >= 0) { if (strncmp(tmp, field, fieldlen) == 0) { sscanf(tmp, "%*s%" SCNu64, &value); break; } } fclose(fp); free(tmp); if (value != (uint64_t)FAILURE) { *val = value; return 0; } } return FAILURE; } #endif SEXP bench_process_memory_() { SEXP out = PROTECT(Rf_allocVector(REALSXP, 2)); REAL(out)[0] = NA_REAL; REAL(out)[1] = NA_REAL; #if OS_LINUX uint64_t current_size = 0; uint64_t peak_size = 0; if(read_proc_file("/proc/self/status", ¤t_size, "VmSize:", 7) != 0) { Rf_error("read_proc_file(...) failed"); } if(read_proc_file("/proc/self/status", &peak_size, "VmPeak:", 7) != 0) { Rf_error("read_proc_file(...) failed"); } REAL(out)[0] = current_size * 1024; REAL(out)[1] = peak_size * 1024; #elif OS_WINDOWS PROCESS_MEMORY_COUNTERS pmc; if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) { Rf_error("GetProcessMemoryInfo(...) failed"); } REAL(out)[0] = pmc.WorkingSetSize; REAL(out)[1] = pmc.PeakWorkingSetSize; #elif OS_MACOS struct task_basic_info info; mach_msg_type_number_t info_count = TASK_BASIC_INFO_COUNT; if (task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &info_count) != 0) { Rf_error("task_info(TASK_BASIC_INFO, ...) failed"); } struct rusage ru; if (getrusage(RUSAGE_SELF, &ru) != 0) { Rf_error("getrusage(RUSAGE_SELF, ...) failed"); } REAL(out)[0] = info.resident_size; REAL(out)[1] = ru.ru_maxrss; #endif UNPROTECT(1); return out; } bench/src/nanotime.h0000644000175000017500000000026513620533713014243 0ustar nileshnilesh#ifndef NANOTIME_H #define NANOTIME_H #include "Rinternals.h" long double real_time(); long double process_cpu_time(); long double expr_elapsed_time(SEXP expr, SEXP env); #endif bench/tests/0000755000175000017500000000000013620533713012630 5ustar nileshnileshbench/tests/testthat/0000755000175000017500000000000014151353665014476 5ustar nileshnileshbench/tests/testthat/test-bench_time.R0000644000175000017500000000164013620533713017666 0ustar nileshnileshdescribe("bench_time", { skip_on_cran() res <- bench_time(1 + 1:1e7) it("returns process and real time", { expect_equal(names(res), c("process", "real")) }) it("returns times that are reasonable, system and real time are relatively close for process bound expressions", { epsilon <- abs(res[[1]] - res[[2]]) expect_true((epsilon / res[[1]]) < 1) }) it("returns times that are reasonable, system and real time are far apart for non-process bound expressions", { res <- bench_time(Sys.sleep(.5)) epsilon <- abs(res[[1]] - res[[2]]) expect_true((epsilon / res[[1]]) > 100) }) }) describe("bench_memory", { skip_on_cran() res <- bench_memory(1 + 1:1e7) it("returns memory allocation and the raw memory used", { expect_equal(names(res), c("mem_alloc", "memory")) }) it("returns reasonable memory allocation", { expect_true(res[["mem_alloc"]] > "10MB") }) }) bench/tests/testthat/test-mark.R0000644000175000017500000001625413620533713016532 0ustar nileshnileshcontext("test-mark.R") describe("mark_", { it("If min_time is Inf, runs for max_iterations", { res <- .Call(mark_, quote(1), new.env(), Inf, as.integer(0), as.integer(10)) expect_equal(length(res), 10) res <- .Call(mark_, quote(1), new.env(), Inf, as.integer(0), as.integer(20)) expect_equal(length(res), 20) }) it("If min_time is 0, runs for min_iterations", { res <- .Call(mark_, quote(1), new.env(), 0, as.integer(1), as.integer(10)) expect_equal(length(res), 1) res <- .Call(mark_, quote(1), new.env(), 0, as.integer(5), as.integer(10)) expect_equal(length(res), 5) }) it("If min_time is 0, runs for min_iterations", { res <- .Call(mark_, quote({i <- 1; while(i < 10000) i <- i + 1}), new.env(), .1, as.integer(1), as.integer(1000)) expect_gte(length(res), 1) expect_lte(length(res), 1000) }) it("Evaluates code in the environment", { e <- new.env(parent = baseenv()) res <- .Call(mark_, quote({a <- 42}), e, Inf, as.integer(1), as.integer(1)) expect_equal(e[["a"]], 42) }) }) describe("mark", { it("Uses all.equal to check results by default", { res <- mark(1 + 1, 1L + 1L, check = TRUE, iterations = 1) expect_is(res$result, "list") expect_true(all.equal(res$result[[1]], res$result[[2]])) }) it("Can use other functions to check results like identical to check results", { # numerics and integers not identical expect_error(regexp = "Each result must equal the first result", mark(1 + 1, 1L + 1L, check = identical, iterations = 1)) # Function that always returns false expect_error(regexp = "Each result must equal the first result", mark(1 + 1, 1 + 1, check = function(x, y) FALSE, iterations = 1)) # Function that always returns true res <- mark(1 + 1, 1 + 2, check = function(x, y) TRUE, iterations = 1) expect_is(res$result, "list") expect_equal(res$result[[1]], 2) expect_equal(res$result[[2]], 3) }) it("works with capabilities('profmem')", { skip_if_not(isTRUE(capabilities("profmem")[[1]])) res <- mark(1, 2, check = FALSE, iterations = 1) expect_equal(length(res$memory), 2) expect_is(res$memory[[1]], "Rprofmem") expect_equal(ncol(res$memory[[1]]), 3) expect_gte(nrow(res$memory[[1]]), 0) }) it("works without capabilities('profmem')", { mockery::stub(mark, "capabilities", FALSE) res <- mark(1, 2, check = FALSE, iterations = 1) expect_equal(res$memory, vector("list", 2)) expect_equal(res$mem_alloc, as_bench_bytes(c(NA, NA))) }) it("Can handle `NULL` results", { res <- mark(if (FALSE) 1, max_iterations = 10) expect_equal(res$result, list(NULL)) }) it("Can errors with the deparsed expressions", { expect_error(msg = "`1` does not equal `3`", mark(1, 1, 3, max_iterations = 10)) }) it("Works when calls are different lengths", { expect_error(msg = "does not equal", # Here the first call deparses to length 2, the second to length 4 mark(if (TRUE) 2, if (TRUE) 1 else 3) ) }) it("works with memory = FALSE", { res <- mark(1, memory = FALSE) expect_is(res, "bench_mark") expect_equal(res$memory, vector("list", 1)) expect_equal(res$mem_alloc, as_bench_bytes(NA)) }) it("works with check = FALSE", { res <- mark(1, check = FALSE) expect_is(res, "bench_mark") expect_equal(res$result, list(NULL)) }) it("works with memory = FALSE and check = FALSE", { res <- mark(1, memory = FALSE, check = FALSE) expect_is(res, "bench_mark") expect_equal(res$memory, list(NULL)) expect_equal(res$mem_alloc, as_bench_bytes(NA)) }) it("fails for memory profiling failures", { skip_on_os("windows") skip_on_cran() keep_busy <- function(n = 1e3) { r <- rnorm(n) p <- pnorm(r) q <- qnorm(p) o <- order(q) } expect_error( res <- mark(parallel::mclapply(seq_len(1e3), keep_busy, mc.cores = 2)), "Memory profiling failed" ) }) it("ignores trailing arguments", { bench::mark( 1 + 3, 2 + 2, ) }) }) describe("summary.bench_mark", { res <- bench_mark( tibble::tibble( expression = "1 + 1:1e+06", result = list(1:10), memory = list(NULL), time = list( c( 0.088492998, 0.109396977, 0.141906863, 0.005378346, 0.007563524, 0.002439451, 0.079715252, 0.003022223, 0.005948069, 0.002276121 ) ), gc = list( tibble::tibble( level0 = c(1, 0, 0, 0, 1, 0, 0, 0, 1, 0), level1 = c(0, 1, 0, 0, 0, 0, 0, 0, 0, 0), level2 = c(0, 0, 1, 0, 0, 0, 1, 0, 0, 0) ) ) ) ) it("computes relative summaries if called with relative = TRUE", { # remove memory column, as there likely are no allocations or gc in these # benchmarks res1 <- summary(res) for (col in setdiff(summary_cols, "mem_alloc")) { # Absolute values should always be positive expect_true(all(res1[[!!col]] >= 0)) } # Relative values should always be greater than or equal to 1 res2 <- summary(res, relative = TRUE) for (col in setdiff(summary_cols, c("mem_alloc", "n_gc"))) { expect_true(all(res2[[!!col]] >= 1)) } }) it("does not filter gc is `filter_gc` is FALSE", { res1 <- summary(res, filter_gc = TRUE) res2 <- summary(res, filter_gc = FALSE) expect_equal(res1$n_gc, 6) expect_equal(res1$n_gc, res2$n_gc) }) it("does not issue warnings if there are no garbage collections", { # This is artificial, but it avoids differences in gc on different # platforms / memory loads, so we can ensure the first has no gcs, and the # second has all gcs x <- bench_mark(tibble::tibble( expression = c(1, 2), result = list(1, 2), time = list( as_bench_time(c(0.166, 0.161, 0.162)), as_bench_time(c(0.276, 0.4)) ), memory = list(NULL, NULL), gc = list( tibble::tibble(level0 = integer(0), level1 = integer(0), level2 = integer(0)), tibble::tibble(level0 = c(1L, 1L), level1 = c(0L, 0L), level2 = c(0L, 0L)) ) )) expect_warning(regexp = "Some expressions had a GC in every iteration", res <- summary(x, filter_gc = TRUE)) expect_equal(res$min, as_bench_time(c(.161, .276))) expect_equal(res$median, as_bench_time(c(.162, .338))) expect_equal(res$`itr/sec`, c(6.134969, 2.958580), tolerance = 1e-5) expect_equal(res$mem_alloc, as_bench_bytes(c(NA, NA))) expect_equal(res$`gc/sec`, c(0, 2.958580), tolerance = 1e-5) expect_equal(res$n_gc, c(0, 2)) expect_equal(res$n_itr, c(3, 2)) expect_equal(res$total_time, as_bench_time(c(.489, .676))) expect_warning(regexp = NA, res2 <- summary(x, filter_gc = FALSE)) expect_identical(res, res2) }) }) describe("unnest.bench_mark", { it("does not contain result or memory columns", { skip_if_not_installed("tidyr") bnch <- mark(1+1, 2+0) if (tidyr_new_interface()) { res <- tidyr::unnest(bnch, c(time, gc)) } else { res <- tidyr::unnest(bnch) } gc_cols <- colnames(bnch$gc[[1]]) expect_equal(colnames(res), c(head(colnames(bnch), n = -1), c(gc_cols, "gc"))) expect_equal(nrow(res), length(bnch$time[[1]]) + length(bnch$time[[2]])) }) }) bench/tests/testthat/test-workout.R0000644000175000017500000000131513620533713017302 0ustar nileshnileshdescribe("workout", { it("times each expression and names them", { res <- workout( x <- 1:1000 ) expect_named(res, c("exprs", "process", "real")) expect_true(nrow(res) == 1) res2 <- workout({ x <- 1:1000 evens <- x %% 2 == 0 y <- x[evens] length(y) length(which(evens)) sum(evens) }) expect_named(res2, c("exprs", "process", "real")) expect_true(nrow(res2) == 6) }) }) describe("workout_expressions", { it("times given expressions", { res <- workout_expressions(as.list(parse(file = system.file("examples/exprs.R", package = "bench")))) expect_named(res, c("exprs", "process", "real")) expect_true(nrow(res) == 6) }) }) bench/tests/testthat/test-expression.R0000644000175000017500000000113713700627654020000 0ustar nileshnileshtest_that("`description` is sliced along with expressions", { x <- as.list(expression(x + y, z + b)) x <- new_bench_expr(x, c("a", "b")) expect_identical(attr(x[2], "description"), "b") expect_identical(attr(x[c(2, 2, 1)], "description"), c("b", "b", "a")) }) test_that("`vec_slice()` slices `description` attribute", { skip_if_not_installed("vctrs") x <- as.list(expression(x + y, z + b)) x <- new_bench_expr(x, c("a", "b")) expect_identical(attr(vctrs::vec_slice(x, 2), "description"), "b") expect_identical(attr(vctrs::vec_slice(x, c(2, 2, 1)), "description"), c("b", "b", "a")) }) bench/tests/testthat/test-time.R0000644000175000017500000001152013621521410016515 0ustar nileshnileshcontext("test-time.R") describe("as_bench_time", { it("accepts numeric input unchanged", { expect_equal(unclass(as_bench_time(123L)), 123L) expect_equal(unclass(as_bench_time(123)), 123) }) it("accepts bench_byte input unchanged", { x <- as_bench_time(123) expect_equal(as_bench_time(x), x) }) it("coerces character input", { expect_equal(unclass(as_bench_time("1")), 1) expect_equal(unclass(as_bench_time("1ns")), 1e-9) expect_equal(unclass(as_bench_time("1us")), 1e-6) expect_equal(unclass(as_bench_time("1ms")), 1e-3) expect_equal(unclass(as_bench_time("1s")), 1) expect_equal(unclass(as_bench_time("1m")), 60) expect_equal(unclass(as_bench_time("1h")), 60 * 60) expect_equal(unclass(as_bench_time("1d")), 60 * 60 * 24) expect_equal(unclass(as_bench_time("1w")), 60 * 60 * 24 * 7) }) }) describe("format.as_bench_time", { it("formats times under 60 as whole numbers", { expect_equal(format(as_bench_time(59)), "59s") expect_equal(format(as_bench_time(1)), "1s") }) it("formats times 60 and up as abbreviated minutes / hours / days", { withr::with_options(list("cli.unicode" = FALSE), { expect_equal(format(as_bench_time(.000000005)), "5ns") expect_equal(format(as_bench_time(.0000005)), "500ns") expect_equal(format(as_bench_time(.000005)), "5us") expect_equal(format(as_bench_time(.0005)), "500us") expect_equal(format(as_bench_time(.005)), "5ms") expect_equal(format(as_bench_time(.5)), "500ms") expect_equal(format(as_bench_time(30)), "30s") expect_equal(format(as_bench_time(60)), "1m") expect_equal(format(as_bench_time(90)), "1.5m") expect_equal(format(as_bench_time(90 * 60)), "1.5h") expect_equal(format(as_bench_time(60 * 60 * 60)), "2.5d") expect_equal(format(as_bench_time(10.5 * 24 * 60 * 60)), "1.5w") }) }) it("handles NA and NaN and Inf", { expect_equal(format(as_bench_time(NA)), "NA") expect_equal(format(as_bench_time(NaN)), "NaN") expect_equal(format(as_bench_time(Inf)), "Inf") expect_equal(format(as_bench_time(-Inf)), "-Inf") }) it("works with vectors", { v <- c(NA, .001, 60, 600, NaN, 6000) expect_equal( format(as_bench_time(v), trim = TRUE), c("NA", "1ms", "1m", "10m", "NaN", "1.67h")) expect_equal(format(as_bench_time(numeric())), character()) }) }) describe("sum.as_bench_time", { it("sums its input and returns a bench_byte", { expect_equal(sum(as_bench_time(0)), new_bench_time(0)) expect_equal(sum(as_bench_time(c(1, 2))), new_bench_time(3)) expect_equal(sum(as_bench_time(c(1, NA))), new_bench_time(NA_real_)) }) }) describe("min.as_bench_time", { it("finds minimum input and returns a bench_byte", { expect_equal(min(as_bench_time(0)), new_bench_time(0)) expect_equal(min(as_bench_time(c(1, 2))), new_bench_time(1)) expect_equal(min(as_bench_time(c(1, NA))), new_bench_time(NA_real_)) }) }) describe("max.as_bench_time", { it("finds maximum input and returns a bench_byte", { expect_equal(max(as_bench_time(0)), new_bench_time(0)) expect_equal(max(as_bench_time(c(1, 2))), new_bench_time(2)) expect_equal(max(as_bench_time(c(1, NA))), new_bench_time(NA_real_)) }) }) describe("[.as_bench_time", { it("retains the as_bench_time class", { x <- as_bench_time(c(100, 200, 300)) expect_equal(x[], x) expect_equal(x[1], new_bench_time(100)) expect_equal(x[1:2], new_bench_time(c(100, 200))) }) }) describe("Ops.as_bench_time", { it("errors for unary operators", { x <- as_bench_time(c(100, 200, 300)) expect_error(!x, "unary '!' not defined for \"bench_time\" objects") expect_error(+x, "unary '\\+' not defined for \"bench_time\" objects") expect_error(-x, "unary '-' not defined for \"bench_time\" objects") }) it("works with boolean comparison operators", { x <- as_bench_time(c(100, 200, 300)) expect_equal(x == 100, c(TRUE, FALSE, FALSE)) expect_equal(x != 100, c(FALSE, TRUE, TRUE)) expect_equal(x > 100, c(FALSE, TRUE, TRUE)) expect_equal(x >= 100, c(TRUE, TRUE, TRUE)) expect_equal(x < 200, c(TRUE, FALSE, FALSE)) expect_equal(x <= 200, c(TRUE, TRUE, FALSE)) }) it("works with arithmetic operators", { x <- as_bench_time(c(100, 200, 300)) expect_equal(x + 100, as_bench_time(c(200, 300, 400))) expect_equal(x - 100, as_bench_time(c(0, 100, 200))) expect_equal(x * 100, as_bench_time(c(10000, 20000, 30000))) expect_equal(x / 2, as_bench_time(c(50, 100, 150))) expect_equal(x ^ 2, as_bench_time(c(10000, 40000, 90000))) }) it("errors for other binary operators", { x <- as_bench_time(c(100, 200, 300)) expect_error(x %/% 2, "'%/%' not defined for \"bench_time\" objects") expect_error(x & TRUE, "'&' not defined for \"bench_time\" objects") expect_error(x | TRUE, "'|' not defined for \"bench_time\" objects") }) }) bench/tests/testthat/test-hires_time.R0000644000175000017500000000050113620533713017714 0ustar nileshnileshdescribe("hires_time", { skip_on_cran() it("returns hi resolution times", { # it is hard to test this, we will just sleep and verify the second time is # after the first. start <- hires_time() Sys.sleep(.1) end <- hires_time() expect_is(start, "numeric") expect_true(end > start) }) }) bench/tests/testthat/test-bench_process_memory.R0000644000175000017500000000114013620533713021771 0ustar nileshnileshdescribe("bench_process_memory", { it("has a current and max memory of bench bytes", { res <- bench_process_memory() expect_named(res, c("current", "max")) expect_s3_class(res[["current"]], "bench_bytes") expect_s3_class(res[["max"]], "bench_bytes") }) # This test is unreliable due to when gcs happen when run repeatedly, so it # is commented out. #it("current memory increases when you allocate a medium size vector", { #res1 <- bench_process_memory() #x <- rep(1, 1e8) #res2 <- bench_process_memory() #expect_true(res2[["current"]] > res1[["current"]]) #}) }) bench/tests/testthat/test-bytes.R0000644000175000017500000001042113620533713016714 0ustar nileshnileshcontext("test-bytes.R") describe("as_bench_bytes", { it("accepts numeric input unchanged", { expect_equal(unclass(as_bench_bytes(123L)), 123L) expect_equal(unclass(as_bench_bytes(123)), 123) }) it("accepts bench_byte input unchanged", { x <- as_bench_bytes(123) expect_equal(as_bench_bytes(x), x) }) it("coerces character input", { expect_equal(unclass(as_bench_bytes("1")), 1) expect_equal(unclass(as_bench_bytes("1K")), 1024) expect_equal(unclass(as_bench_bytes("1M")), 1024 * 1024) expect_equal(unclass(as_bench_bytes("10M")), 10 * 1024 * 1024) expect_equal(unclass(as_bench_bytes("1G")), 1024 * 1024 * 1024) }) }) describe("format.bench_bytes", { it("formats bytes under 1024 as whole numbers", { expect_equal(format(bench_bytes(0)), "0B") expect_equal(format(bench_bytes(1)), "1B") expect_equal(format(bench_bytes(1023)), "1023B") }) it("formats bytes 1024 and up as abbreviated numbers", { expect_equal(format(bench_bytes(1024)), "1KB") expect_equal(format(bench_bytes(1025)), "1KB") expect_equal(format(bench_bytes(2^16)), "64KB") expect_equal(format(bench_bytes(2^24)), "16MB") expect_equal(format(bench_bytes(2^24 + 555555)), "16.5MB") expect_equal(format(bench_bytes(2^32)), "4GB") expect_equal(format(bench_bytes(2^48)), "256TB") expect_equal(format(bench_bytes(2^64)), "16EB") }) it("handles NA and NaN", { expect_equal(format(bench_bytes(NA)), "NA") expect_equal(format(bench_bytes(NaN)), "NaN") }) it("works with vectors", { v <- c(NA, 1, 2^13, 2^20, NaN, 2^15) expect_equal( format(bench_bytes(v), trim = TRUE), c("NA", "1B", "8KB", "1MB", "NaN", "32KB")) expect_equal(format(bench_bytes(numeric())), character()) }) }) describe("sum.bench_bytes", { it("sums its input and returns a bench_byte", { expect_equal(sum(bench_bytes(0)), new_bench_bytes(0)) expect_equal(sum(bench_bytes(c(1, 2))), new_bench_bytes(3)) expect_equal(sum(bench_bytes(c(1, NA))), new_bench_bytes(NA_real_)) }) }) describe("min.bench_bytes", { it("finds minimum input and returns a bench_byte", { expect_equal(min(bench_bytes(0)), new_bench_bytes(0)) expect_equal(min(bench_bytes(c(1, 2))), new_bench_bytes(1)) expect_equal(min(bench_bytes(c(1, NA))), new_bench_bytes(NA_real_)) }) }) describe("max.bench_bytes", { it("finds maximum input and returns a bench_byte", { expect_equal(max(bench_bytes(0)), new_bench_bytes(0)) expect_equal(max(bench_bytes(c(1, 2))), new_bench_bytes(2)) expect_equal(max(bench_bytes(c(1, NA))), new_bench_bytes(NA_real_)) }) }) describe("[.bench_bytes", { it("retains the bench_bytes class", { x <- bench_bytes(c(100, 200, 300)) expect_equal(x[], x) expect_equal(x[1], new_bench_bytes(100)) expect_equal(x[1:2], new_bench_bytes(c(100, 200))) }) }) describe("Ops.bench_bytes", { it("errors for unary operators", { x <- bench_bytes(c(100, 200, 300)) expect_error(!x, "unary '!' not defined for \"bench_bytes\" objects") expect_error(+x, "unary '\\+' not defined for \"bench_bytes\" objects") expect_error(-x, "unary '-' not defined for \"bench_bytes\" objects") }) it("works with boolean comparison operators", { x <- bench_bytes(c(100, 200, 300)) expect_equal(x == 100, c(TRUE, FALSE, FALSE)) expect_equal(x != 100, c(FALSE, TRUE, TRUE)) expect_equal(x > 100, c(FALSE, TRUE, TRUE)) expect_equal(x >= 100, c(TRUE, TRUE, TRUE)) expect_equal(x < 200, c(TRUE, FALSE, FALSE)) expect_equal(x <= 200, c(TRUE, TRUE, FALSE)) }) it("works with arithmetic operators", { x <- bench_bytes(c(100, 200, 300)) expect_equal(x + 100, bench_bytes(c(200, 300, 400))) expect_equal(x - 100, bench_bytes(c(0, 100, 200))) expect_equal(x * 100, bench_bytes(c(10000, 20000, 30000))) expect_equal(x / 2, bench_bytes(c(50, 100, 150))) expect_equal(x ^ 2, bench_bytes(c(10000, 40000, 90000))) }) it("errors for other binary operators", { x <- bench_bytes(c(100, 200, 300)) expect_error(x %% 2, "'%%' not defined for \"bench_bytes\" objects") expect_error(x %/% 2, "'%/%' not defined for \"bench_bytes\" objects") expect_error(x & TRUE, "'&' not defined for \"bench_bytes\" objects") expect_error(x | TRUE, "'|' not defined for \"bench_bytes\" objects") }) }) bench/tests/testthat/test-press.R0000644000175000017500000000413613620533713016730 0ustar nileshnileshcontext("test-press.R") describe("press", { it("Adds parameters to output", { res <- press( x = 1, mark(1, max_iterations = 10) ) expect_equal(colnames(res), c("expression", "x", summary_cols, data_cols)) expect_equal(nrow(res), 1) res2 <- press( x = 1:3, mark(1, max_iterations = 10) ) expect_equal(colnames(res2), c("expression", "x", summary_cols, data_cols)) expect_equal(nrow(res2), 3) }) it("Outputs status message before evaluating each parameter", { expect_message(regexp = "Running with:\n.*x", res <- press( x = 1, mark(rep(1, x), max_iterations = 10) ) ) expect_equal(colnames(res), c("expression", "x", summary_cols, data_cols)) expect_equal(nrow(res), 1) msgs <- character() withCallingHandlers(message = function(e) msgs <<- append(msgs, conditionMessage(e)), res2 <- press( x = 1:3, mark(rep(1, x), max_iterations = 10) ) ) expect_true(grepl("Running with:\n.*x\n.*1\n.*2\n.*3\n", paste(msgs, collapse = ""))) expect_equal(colnames(res2), c("expression", "x", summary_cols, data_cols)) expect_equal(nrow(res2), 3) }) it("expands the grid if has named parameters", { res <- press( x = c(1, 2), y = c(1, 3), mark(list(x, y), max_iterations = 10) ) expect_equal(res$x, c(1, 2, 1, 2)) expect_equal(res$y, c(1, 1, 3, 3)) expect_equal(res$result[[1]], list(1, 1)) expect_equal(res$result[[2]], list(2, 1)) expect_equal(res$result[[3]], list(1, 3)) expect_equal(res$result[[4]], list(2, 3)) }) it("takes values as-is if given in .grid", { res <- press( .grid = data.frame(x = c(1, 2), y = c(1,3)), mark(list(x, y), max_iterations = 10) ) expect_equal(res$x, c(1, 2)) expect_equal(res$y, c(1, 3)) expect_equal(res$result[[1]], list(1, 1)) expect_equal(res$result[[2]], list(2, 3)) }) it("runs `setup` with the parameters evaluated", { x <- 1 res <- press( y = 2, { x <- y mark(x) }) expect_equal(res$result[[1]], 2) }) }) bench/tests/testthat.R0000644000175000017500000000006613620533713014615 0ustar nileshnileshlibrary(testthat) library(bench) test_check("bench") bench/R/0000755000175000017500000000000014151175656011677 5ustar nileshnileshbench/R/bytes.R0000644000175000017500000001213513620533713013142 0ustar nileshnilesh# This is mostly a copy of https://github.com/r-lib/fs/blob/0f5b6191935fe4c862d2e5003655e6c1669f4afd/R/fs_bytes.R # If I end up needing this in a third package it should probably live in a package somewhere, maybe prettyunits? byte_units <- c('B' = 1, 'K' = 1024, 'M' = 1024 ^ 2, 'G' = 1024 ^ 3, 'T' = 1024 ^ 4, 'P' = 1024 ^ 5, 'E' = 1024 ^ 6, 'Z' = 1024 ^ 7, 'Y' = 1024 ^ 8) #' Human readable memory sizes #' #' Construct, manipulate and display vectors of byte sizes. These are numeric #' vectors, so you can compare them numerically, but they can also be compared #' to human readable values such as '10MB'. #' #' These memory sizes are always assumed to be base 1024, rather than 1000. #' #' @param x A numeric or character vector. Character representations can use #' shorthand sizes (see examples). #' @examples #' bench_bytes("1") #' bench_bytes("1K") #' bench_bytes("1Kb") #' bench_bytes("1KiB") #' bench_bytes("1MB") #' #' bench_bytes("1KB") < "1MB" #' #' sum(bench_bytes(c("1MB", "5MB", "500KB"))) #' @name bench_bytes #' @export as_bench_bytes <- function(x) { UseMethod("as_bench_bytes") } #' @export #' @rdname bench_bytes bench_bytes <- as_bench_bytes new_bench_bytes <- function(x) { structure(x, class = c("bench_bytes", "numeric")) } #' @importFrom methods setOldClass setOldClass(c("bench_bytes", "numeric"), numeric()) #' @export as_bench_bytes.default <- function(x) { x <- as.character(x) m <- captures(x, regexpr("^(?[[:digit:].]+)\\s*(?[KMGTPEZY]?)i?[Bb]?$", x, perl = TRUE)) m$unit[m$unit == ""] <- "B" new_bench_bytes(unname(as.numeric(m$size) * byte_units[m$unit])) } #' @export as_bench_bytes.bench_bytes <- function(x) { return(x) } #' @export as_bench_bytes.numeric <- function(x) { new_bench_bytes(x) } # Adapted from https://github.com/gaborcsardi/prettyunits #' @export format.bench_bytes <- function(x, scientific = FALSE, digits = 3, drop0trailing = TRUE, ...) { nms <- names(x) bytes <- unclass(x) unit <- vcapply(x, find_unit, byte_units) res <- round(bytes / byte_units[unit], digits = digits) ## Zero bytes res[bytes == 0] <- 0 unit[bytes == 0] <- "B" ## NA and NaN bytes res[is.na(bytes)] <- NA_real_ res[is.nan(bytes)] <- NaN unit[is.na(bytes)] <- "" # Includes NaN as well # Append an extra B to each unit large_units <- unit %in% names(byte_units)[-1] unit[large_units] <- paste0(unit[large_units], "B") res <- format(res, scientific = scientific, digits = digits, drop0trailing = drop0trailing, ...) stats::setNames(paste0(res, unit), nms) } #' @export as.character.bench_bytes <- format.bench_bytes #' @export print.bench_bytes <- function(x, ...) { print(format.bench_bytes(x, ...), quote = FALSE) } #' @export sum.bench_bytes <- function(x, ...) { new_bench_bytes(NextMethod()) } #' @export min.bench_bytes <- function(x, ...) { new_bench_bytes(NextMethod()) } #' @export max.bench_bytes <- function(x, ...) { new_bench_bytes(NextMethod()) } #' @export `[.bench_bytes` <- function(x, i) { new_bench_bytes(NextMethod("[")) } #' @export `[[.bench_bytes` <- function(x, i) { new_bench_bytes(NextMethod("[[")) } #' @export # Adapted from Ops.numeric_version Ops.bench_bytes <- function (e1, e2) { if (nargs() == 1L) { stop(sprintf("unary '%s' not defined for \"bench_bytes\" objects", .Generic), call. = FALSE) } boolean <- switch(.Generic, `+` = TRUE, `-` = TRUE, `*` = TRUE, `/` = TRUE, `^` = TRUE, `<` = TRUE, `>` = TRUE, `==` = TRUE, `!=` = TRUE, `<=` = TRUE, `>=` = TRUE, FALSE) if (!boolean) { stop(sprintf("'%s' not defined for \"bench_bytes\" objects", .Generic), call. = FALSE) } e1 <- as_bench_bytes(e1) e2 <- as_bench_bytes(e2) NextMethod(.Generic) } pillar_shaft.bench_bytes <- function(x, ...) { pillar::new_pillar_shaft_simple(format.bench_bytes(x), align = "right", ...) } type_sum.bench_bytes <- function(x) { "bch:byt" } #' Benchmark time transformation #' #' This both log transforms the times and formats the labels as a `bench_time` #' object. #' @inheritParams bench_time_trans #' @keywords internal #' @export bench_bytes_trans <- function(base = 2) { if (is.null(base)) { return( scales::trans_new("bch:byt", as.numeric, as_bench_bytes, scales::pretty_breaks(), domain = c(1e-100, Inf) ) ) } trans <- function(x) log(as.numeric(x), base) inv <- function(x) as_bench_bytes(base ^ as.numeric(x)) scales::trans_new(paste0("bch:byt-", format(base)), trans, inv, scales::log_breaks(base = base), domain = c(1e-100, Inf)) } scale_type.bench_bytes <- function(x) "bench_bytes" #' Position scales for bench_time data #' #' Default scales for the `bench_time` class, these are added to plots using #' `bench_time` objects automatically. #' @name scale_bench_time #' @keywords internal #' @export scale_x_bench_bytes <- function(base = 10, ...) { ggplot2::scale_x_continuous(..., trans = bench_bytes_trans(base = base)) } #' @rdname scale_bench_time #' @keywords internal #' @export scale_y_bench_bytes <- function(base = 10, ...) { ggplot2::scale_y_continuous(..., trans = bench_bytes_trans(base = base)) } bench/R/expression.R0000644000175000017500000000413413700627654014222 0ustar nileshnileshnew_bench_expr <- function(x, description = names(x)) { if (is.null(description)) { description <- rep("", length(x)) } names(x) <- description structure(x, class = c("bench_expr", "list"), description = description) } #' @export format.bench_expr <- function(x, ...) { desc <- attr(x, "description") is_missing <- desc == "" desc[is_missing] <- vapply(x[is_missing], deparse_trunc, character(1)) desc } #' @export as.character.bench_expr <- format.bench_expr #' @export print.bench_expr <- function(x, ...) { x <- unclass(x) NextMethod() } type_sum.bench_expr <- function(x) { "bch:expr" } #' @export `[.bench_expr` <- function(x, i, ...) { new_x <- NextMethod("[") new_bench_expr(new_x) } # @export vec_proxy.bench_expr <- function(x, ...) { desc <- attr(x, "description") attributes(x) <- NULL out <- list(x = x, desc = desc) vctrs::new_data_frame(out, n = length(x)) } # @export vec_restore.bench_expr <- function(x, to, ...) { new_bench_expr(x$x, x$desc) } pillar_shaft.bench_expr <- function(x, ...) { pillar::new_pillar_shaft_simple(format.bench_expr(x), align = "left", ...) } scale_type.bench_expr <- function(x) { "bench_expr" } setOldClass(c("bench_expr", "list"), list()) #' Position and color scales for bench_expr data #' #' Default scales for the `bench_expr` class, these are added to plots using #' `bench_expr` objects automatically. #' @name scale_bench_expr #' @keywords internal #' @export scale_x_bench_expr <- function(...) { sc <- ggplot2::scale_x_discrete(...) sc$transform <- as.character sc } #' @rdname scale_bench_expr #' @keywords internal #' @export scale_y_bench_expr <- function(...) { sc <- ggplot2::scale_y_discrete(...) sc$transform <- as.character sc } #' @rdname scale_bench_expr #' @keywords internal #' @export scale_colour_bench_expr <- function(palette = scales::hue_pal(...), ..., aesthetics = "colour") { sc <- ggplot2::discrete_scale(aesthetics, "bench_expr", palette, ...) sc$transform <- as.character sc } #' @rdname scale_bench_expr #' @keywords internal #' @export scale_color_bench_expr <- scale_colour_bench_expr bench/R/zzz.R0000644000175000017500000000322313700627654012656 0ustar nileshnilesh#nocov start .onLoad <- function(...) { register_s3_method("tidyr", "unnest", "bench_mark") register_s3_method("dplyr", "filter", "bench_mark") register_s3_method("dplyr", "group_by", "bench_mark") register_s3_method("ggplot2", "autoplot", "bench_mark") register_s3_method("pillar", "pillar_shaft", "bench_expr") register_s3_method("pillar", "type_sum", "bench_expr") register_s3_method("ggplot2", "scale_type", "bench_expr") register_s3_method("pillar", "pillar_shaft", "bench_time") register_s3_method("pillar", "type_sum", "bench_time") register_s3_method("ggplot2", "scale_type", "bench_time") register_s3_method("pillar", "pillar_shaft", "bench_bytes") register_s3_method("pillar", "type_sum", "bench_bytes") register_s3_method("ggplot2", "scale_type", "bench_bytes") register_s3_method("knitr", "knit_print", "bench_mark") register_s3_method("vctrs", "vec_proxy", "bench_expr") register_s3_method("vctrs", "vec_restore", "bench_expr") } register_s3_method <- function(pkg, generic, class, fun = NULL) { stopifnot(is.character(pkg), length(pkg) == 1) stopifnot(is.character(generic), length(generic) == 1) stopifnot(is.character(class), length(class) == 1) if (is.null(fun)) { fun <- get(paste0(generic, ".", class), envir = parent.frame()) } else { stopifnot(is.function(fun)) } if (pkg %in% loadedNamespaces()) { registerS3method(generic, class, fun, envir = asNamespace(pkg)) } # Always register hook in case package is later unloaded & reloaded setHook( packageEvent(pkg, "onLoad"), function(...) { registerS3method(generic, class, fun, envir = asNamespace(pkg)) } ) } #nocov end bench/R/load.R0000644000175000017500000000041413630252670012731 0ustar nileshnilesh #' Get system load averages #' #' Uses OS system APIs to return the load average for the past 1, 5 and 15 minutes. #' @export bench_load_average <- function() { stats::setNames( .Call(bench_load_average_), c("load_1_min", "load_5_min", "load_15_min") ) } bench/R/hires_time.R0000644000175000017500000000137113620533713014144 0ustar nileshnilesh#' Return the current high-resolution real time. #' #' Time is expressed as seconds since some arbitrary time in the past; it #' is not correlated in any way to the time of day, and thus is not subject to #' resetting or drifting. The hi-res #' timer is ideally suited to performance measurement tasks, where cheap, #' accurate interval timing is required. #' @export #' @examples #' hires_time() #' #' # R rounds doubles to 7 digits by default, see greater precision by setting #' # the digits argument when printing #' print(hires_time(), digits = 20) #' #' # Generally used by recording two times and then subtracting them #' start <- hires_time() #' end <- hires_time() #' elapsed <- end - start #' elapsed hires_time <- function() { .Call(hires_time_) } bench/R/bench-package.R0000644000175000017500000000011113620533713014453 0ustar nileshnilesh#' @keywords internal #' @inherit summary.bench_mark examples "_PACKAGE" bench/R/workout.R0000644000175000017500000000333013700627654013532 0ustar nileshnilesh#' Workout a group of expressions individually #' #' Given an block of expressions in `{}` [workout()] individually times each #' expression in the group. [workout_expressions()] is a lower level function most #' useful when reading lists of calls from a file. #' #' @param expr one or more expressions to workout, use `{}` to pass multiple #' expressions. #' @param exprs A list of calls to measure. #' @param description A name to label each expression, if not supplied the #' deparsed expression will be used. #' @param env The environment in which the expressions should be evaluated. #' @export #' @examples #' workout({ #' x <- 1:1000 #' evens <- x %% 2 == 0 #' y <- x[evens] #' length(y) #' length(which(evens)) #' sum(evens) #' }) #' #' # The equivalent to the above, reading the code from a file #' workout_expressions(as.list(parse(system.file("examples/exprs.R", package = "bench")))) workout <- function(expr, description = NULL) { expr <- substitute(expr) env <- parent.frame() if (rlang::is_call(expr, "{")) { exprs <- as.list(expr[-1]) } else { exprs <- list(expr) } workout_expressions(exprs, env, description) } #' @rdname workout #' @export workout_expressions <- function(exprs, env = parent.frame(), description = NULL) { if (is.null(description)) { description <- names(exprs) } out <- list( exprs = new_bench_expr(exprs, description), process = numeric(length(exprs)), real = numeric(length(exprs)) ) for (i in seq_along(exprs)) { res <- as_bench_time(.Call(system_time_, exprs[[i]], env)) out[[2]][[i]] <- res[[1]] out[[3]][[i]] <- res[[2]] } out[[2]] <- new_bench_time(out[[2]]) out[[3]] <- new_bench_time(out[[3]]) tibble::as_tibble(out) } bench/R/bench_process_memory.R0000644000175000017500000000252414151172025016215 0ustar nileshnilesh#' Retrieve the current and maximum memory from the R process #' #' The memory reported here will likely differ from that reported by `gc()`, as #' this includes all memory from the R process, including any child processes #' and memory allocated outside R's garbage collector heap. #' #' The OS APIs used are as follows #' #' ## Windows #' - [PROCESS_MEMORY_COUNTERS.WorkingSetSize](https://docs.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-process_memory_counters) #' - [PROCESS_MEMORY_COUNTERS.PeakWorkingSetSize](https://docs.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-process_memory_counters) #' ## macOS #' - [task_info(TASK_BASIC_INFO)](https://developer.apple.com/documentation/kernel/1537934-task_info?language=occ) #' - [rusage.ru_maxrss](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getrusage.2.html) #' ## linux #' - [/proc/pid/status VmSize](https://man7.org/linux/man-pages/man5/proc.5.html) #' - [/proc/pid/status VmPeak](https://man7.org/linux/man-pages/man5/proc.5.html) #' and on Windows #' [PROCESS_MEMORY_COUNTERS.PeakWorkingSetSize](https://docs.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-process_memory_counters) #' @export bench_process_memory <- function() { stats::setNames( as_bench_bytes(.Call(bench_process_memory_)), c("current", "max") ) } bench/R/time.R0000644000175000017500000001341113621521410012740 0ustar nileshnileshtime_units <- function() { stats::setNames( c(1e-9, 1e-6, if (is_utf8_output()) 1e-6, 1e-3, 1, 60, 60 * 60, 60 * 60 * 24, 60 * 60 * 24 * 7), c("ns", "us", if (is_utf8_output()) "\U00B5s", "ms", "s", "m", "h", "d", "w") ) } #' Human readable times #' #' Construct, manipulate and display vectors of elapsed times in seconds. These #' are numeric vectors, so you can compare them numerically, but they can also #' be compared to human readable values such as '10ms'. #' #' @param x A numeric or character vector. Character representations can use #' shorthand sizes (see examples). #' @examples #' as_bench_time("1ns") #' as_bench_time("1") #' as_bench_time("1us") #' as_bench_time("1ms") #' as_bench_time("1s") #' #' as_bench_time("100ns") < "1ms" #' #' sum(as_bench_time(c("1MB", "5MB", "500KB"))) #' @export as_bench_time <- function(x) { UseMethod("as_bench_time") } new_bench_time <- function(x) { structure(x, class = c("bench_time", "numeric")) } setOldClass(c("bench_time", "numeric"), numeric()) #' @export as_bench_time.default <- function(x) { x <- as.character(x) re <- glue::glue(" ^(?[[:digit:].]+)\\s*(?{nms}?)$ ", nms = paste0(names(time_units()), collapse = "|")) m <- captures(x, regexpr(re, x, perl = TRUE)) m$unit[m$unit == ""] <- "s" new_bench_time(unname(as.numeric(m$size) * time_units()[m$unit])) } #' @export as_bench_time.bench_time <- function(x) { return(x) } #' @export as_bench_time.numeric <- function(x) { is_small <- x < 1e-9 & !is.infinite(x) & x != 0 x[is_small] <- 1e-9 new_bench_time(x) } tolerance <- sqrt(.Machine$double.eps) find_unit <- function(x, units) { if (is.na(x) || is.nan(x) || x <= 0 || is.infinite(x)) { return(NA_character_) } epsilon <- 1 - (x * (1 / units)) names( utils::tail(n = 1, which(epsilon < tolerance))) } # Adapted from https://github.com/gaborcsardi/prettyunits # Aims to be consistent with ls -lh, so uses 1024 KiB units, 3 or less digits etc. #' @export format.bench_time <- function(x, scientific = FALSE, digits = 3, drop0trailing = TRUE, ...) { nms <- names(x) # convert negative times to 1ns, this can happen if the minimum calculated # overhead is higher than the time. x[x < 1e-9 & !is.infinite(x) & x != 0] <- 1e-9 seconds <- unclass(x) unit <- vcapply(x, find_unit, time_units()) res <- round(seconds / time_units()[unit], digits = digits) ## Zero seconds res[seconds == 0] <- 0 unit[seconds == 0] <- "" ## NA, NaN, Inf, -Inf, seconds res[is.na(seconds)] <- NA_real_ res[is.nan(seconds)] <- NaN res[is.infinite(seconds)] <- Inf res[is.infinite(seconds) & seconds < 0] <- -Inf unit[is.na(seconds) | is.infinite(seconds)] <- "" res <- format(res, scientific = scientific, digits = digits, drop0trailing = drop0trailing, ...) stats::setNames(paste0(res, unit), nms) } #' @export as.character.bench_time <- format.bench_time #' @export print.bench_time <- function(x, ...) { print(format.bench_time(x, ...), quote = FALSE) } #' @export sum.bench_time <- function(x, ...) { new_bench_time(NextMethod()) } #' @export min.bench_time <- function(x, ...) { new_bench_time(NextMethod()) } #' @export max.bench_time <- function(x, ...) { new_bench_time(NextMethod()) } #' @export `[.bench_time` <- function(x, i, ...) { new_bench_time(NextMethod("[")) } #' @export `[[.bench_time` <- function(x, i, ...) { new_bench_time(NextMethod("[[")) } #' @export # Adapted from Ops.numeric_version Ops.bench_time <- function(e1, e2, ...) { if (nargs() == 1L) { stop(sprintf("unary '%s' not defined for \"bench_time\" objects", .Generic), call. = FALSE) } boolean <- switch(.Generic, `+` = TRUE, `-` = TRUE, `*` = TRUE, `/` = TRUE, `^` = TRUE, `<` = TRUE, `>` = TRUE, `==` = TRUE, `!=` = TRUE, `<=` = TRUE, `>=` = TRUE, `%%` = TRUE, FALSE) if (!boolean) { stop(sprintf("'%s' not defined for \"bench_time\" objects", .Generic), call. = FALSE) } e1 <- as_bench_time(e1) e2 <- as_bench_time(e2) NextMethod(.Generic) } #' @export Summary.bench_time <- function(..., na.rm = FALSE) { new_bench_time(NextMethod(.Generic)) } #' @export mean.bench_time <- function(x, ...) { new_bench_time(NextMethod(.Generic)) } pillar_shaft.bench_time <- function(x, ...) { pillar::new_pillar_shaft_simple(format.bench_time(x), align = "right", ...) } type_sum.bench_time <- function(x) { "bch:tm" } #' Benchmark time transformation #' #' This both log transforms the times and formats the labels as a `bench_time` #' object. #' @inheritParams scales::log_trans #' @keywords internal #' @export bench_time_trans <- function(base = 10) { if (is.null(base)) { return( scales::trans_new("bch:tm", as.numeric, as_bench_time, scales::pretty_breaks(), domain = c(1e-100, Inf) ) ) } trans <- function(x) log(as.numeric(x), base) inv <- function(x) as_bench_time(base ^ as.numeric(x)) scales::trans_new(paste0("bch:tm-", format(base)), trans, inv, scales::log_breaks(base = base), domain = c(1e-100, Inf)) } scale_type.bench_time <- function(x) "bench_time" #' Position scales for bench_time data #' #' Default scales for the `bench_time` class, these are added to plots using #' `bench_time` objects automatically. #' @name scale_bench_time #' @param base The base of the logarithm, if `NULL` instead use a #' non-logarithmic scale. #' @keywords internal #' @export scale_x_bench_time <- function(base = 10, ...) { ggplot2::scale_x_continuous(..., trans = bench_time_trans(base = base)) } #' @rdname scale_bench_time #' @keywords internal #' @export scale_y_bench_time <- function(base = 10, ...) { ggplot2::scale_y_continuous(..., trans = bench_time_trans(base = base)) } bench/R/mark.R0000644000175000017500000003206314151175656012760 0ustar nileshnilesh#' @useDynLib bench, .registration = TRUE NULL #' Benchmark a series of functions #' #' Benchmark a list of quoted expressions. Each expression will always run at #' least twice, once to measure the memory allocation and store results and one #' or more times to measure timing. #' #' @param ... Expressions to benchmark, if named the `expression` column will #' be the name, otherwise it will be the deparsed expression. #' @param min_time The minimum number of seconds to run each expression, set to #' `Inf` to always run `max_iterations` times instead. #' @param iterations If not `NULL`, the default, run each expression for #' exactly this number of iterations. This overrides both `min_iterations` #' and `max_iterations`. #' @param exprs A list of quoted expressions. If supplied overrides expressions #' defined in `...`. #' @param min_iterations Each expression will be evaluated a minimum of `min_iterations` times. #' @param max_iterations Each expression will be evaluated a maximum of `max_iterations` times. #' @param check Check if results are consistent. If `TRUE`, checking is done #' with [all.equal()], if `FALSE` checking is disabled and results are not #' stored. If `check` is a function that function will be called with each #' pair of results to determine consistency. #' @param memory If `TRUE` (the default when R is compiled with memory #' profiling), track memory allocations using. If `FALSE` disable memory #' tracking. #' @param env The environment which to evaluate the expressions #' @inheritParams summary.bench_mark #' @inherit summary.bench_mark return #' @aliases bench_mark #' @seealso [press()] to run benchmarks across a grid of parameters. #' @examples #' dat <- data.frame(x = runif(100, 1, 1000), y=runif(10, 1, 1000)) #' mark( #' min_time = .1, #' #' dat[dat$x > 500, ], #' dat[which(dat$x > 500), ], #' subset(dat, x > 500)) #' @export mark <- function(..., min_time = .5, iterations = NULL, min_iterations = 1, max_iterations = 10000, check = TRUE, memory = capabilities("profmem"), filter_gc = TRUE, relative = FALSE, time_unit = NULL, exprs = NULL, env = parent.frame()) { if (!is.null(iterations)) { min_iterations <- iterations max_iterations <- iterations } if (isTRUE(check)) { check_fun <- all.equal } else if (is.function(check)) { check_fun <- check check <- TRUE } else { check <- FALSE } if (is.null(exprs)) { exprs <- dots(...) } n_exprs <- length(exprs) results <- list(expression = new_bench_expr(exprs), time = vector("list", n_exprs), gc = vector("list", n_exprs), memory = vector("list", n_exprs), result = vector("list", n_exprs)) # Helper for evaluating with memory profiling eval_one <- function(e, profile_memory) { f <- tempfile() on.exit(unlink(f)) if (profile_memory) { utils::Rprofmem(f, threshold = 1) } res <- eval(e, env) if (profile_memory) { utils::Rprofmem(NULL) } list(result = res, memory = parse_allocations(f)) } # We only want to evaluate these first runs if we need to check memory or results. if (memory || check) { # Run allocation benchmark and check results for (i in seq_len(length(exprs))) { res <- eval_one(exprs[[i]], memory) if (check) { if (is.null(res$result)) { results$result[i] <- list(res$result) } else { results$result[[i]] <- res$result } } if (memory) { results$memory[[i]] <- res$memory } if (check && i > 1) { comp <- check_fun(results$result[[1]], results$result[[i]]) if (!isTRUE(comp)) { expressions <- as.character(results$expression) stop(glue::glue(" Each result must equal the first result: `{first}` does not equal `{current}` ", first = expressions[[1]], current = expressions[[i]] ), call. = FALSE ) } } } } for (i in seq_len(length(exprs))) { error <- NULL gc_msg <- with_gcinfo({ tryCatch(error = function(e) { e$call <- NULL; error <<- e}, res <- .Call(mark_, exprs[[i]], env, min_time, as.integer(min_iterations), as.integer(max_iterations)) ) }) if (!is.null(error)) { stop(error) } results$time[[i]] <- as_bench_time(res) results$gc[[i]] <- parse_gc(gc_msg) } out <- summary(bench_mark(tibble::as_tibble(results, .name_repair = "minimal")), filter_gc = filter_gc, relative = relative, time_unit = time_unit) out } bench_mark <- function(x) { class(x) <- unique(c("bench_mark", class(x))) x } #' Coerce to a bench mark object Bench mark objects #' #' This is typically needed only if you are performing additional manipulations #' after calling [bench::mark()]. #' @param x Object to be coerced #' @export as_bench_mark <- function(x) { bench_mark(tibble::as_tibble(x)) } summary_cols <- c("min", "median", "itr/sec", "mem_alloc", "gc/sec") data_cols <- c("n_itr", "n_gc", "total_time", "result", "memory", "time", "gc") time_cols <- c("min", "median", "total_time") #' Summarize [bench::mark] results. #' #' @param object [bench_mark] object to summarize. #' @param filter_gc If `TRUE` remove iterations that contained at least one #' garbage collection before summarizing. If `TRUE` but an expression had #' a garbage collection in every iteration, filtering is disabled, with a warning. #' @param relative If `TRUE` all summaries are computed relative to the minimum #' execution time rather than absolute time. #' @param time_unit If `NULL` the times are reported in a human readable #' fashion depending on each value. If one of 'ns', 'us', 'ms', 's', 'm', 'h', #' 'd', 'w' the time units are instead expressed as nanoseconds, microseconds, #' milliseconds, seconds, hours, minutes, days or weeks respectively. #' @param ... Additional arguments ignored. #' @details #' If `filter_gc == TRUE` (the default) runs that contain a garbage #' collection will be removed before summarizing. This is most useful for fast #' expressions when the majority of runs do not contain a gc. Call #' `summary(filter_gc = FALSE)` if you would like to compute summaries _with_ #' these times, such as expressions with lots of allocations when all or most #' runs contain a gc. #' @return A [tibble][tibble::tibble] with the additional summary columns. #' The following summary columns are computed #' - `expression` - `bench_expr` The deparsed expression that was evaluated #' (or its name if one was provided). #' - `min` - `bench_time` The minimum execution time. #' - `median` - `bench_time` The sample median of execution time. #' - `itr/sec` - `double` The estimated number of executions performed per #' second. #' - `mem_alloc` - `bench_bytes` Total amount of memory allocated by R while #' running the expression. Memory allocated *outside* the R heap, e.g. by #' `malloc()` or `new` directly is *not* tracked, take care to avoid #' misinterpreting the results if running code that may do this. #' - `gc/sec` - `double` The number of garbage collections per second. #' - `n_itr` - `integer` Total number of iterations after filtering #' garbage collections (if `filter_gc == TRUE`). #' - `n_gc` - `double` Total number of garbage collections performed over all #' iterations. This is a psudo-measure of the pressure on the garbage collector, if #' it varies greatly between to alternatives generally the one with fewer #' collections will cause fewer allocation in real usage. #' - `total_time` - `bench_time` The total time to perform the benchmarks. #' - `result` - `list` A list column of the object(s) returned by the #' evaluated expression(s). #' - `memory` - `list` A list column with results from [Rprofmem()]. #' - `time` - `list` A list column of `bench_time` vectors for each evaluated #' expression. #' - `gc` - `list` A list column with tibbles containing the level of #' garbage collection (0-2, columns) for each iteration (rows). #' @examples #' dat <- data.frame(x = runif(10000, 1, 1000), y=runif(10000, 1, 1000)) #' #' # `bench::mark()` implicitly calls summary() automatically #' results <- bench::mark( #' dat[dat$x > 500, ], #' dat[which(dat$x > 500), ], #' subset(dat, x > 500)) #' #' # However you can also do so explicitly to filter gc differently. #' summary(results, filter_gc = FALSE) #' #' # Or output relative times #' summary(results, relative = TRUE) #' @export summary.bench_mark <- function(object, filter_gc = TRUE, relative = FALSE, time_unit = NULL, ...) { nms <- colnames(object) parameters <- setdiff(nms, c("expression", summary_cols, data_cols)) num_gc <- lapply(object$gc, function(x) { res <- rowSums(x) if (length(res) == 0) { res <- rep(0, length(x)) } res } ) if (isTRUE(filter_gc)) { no_gc <- lapply(num_gc, `==`, 0) times <- Map(`[`, object$time, no_gc) } else { times <- object$time } if (filter_gc && any(lengths(times) == 0)) { times <- object$time warning(call. = FALSE, "Some expressions had a GC in every iteration; so filtering is disabled." ) } object$min <- new_bench_time(vdapply(times, min)) object$median <- new_bench_time(vdapply(times, stats::median)) object$max <- new_bench_time(vdapply(times, max)) object$total_time <- new_bench_time(vdapply(times, sum)) object$n_itr <- viapply(times, length) object$`itr/sec` <- as.numeric(object$n_itr / object$total_time) object$n_gc <- vdapply(num_gc, sum) object$`gc/sec` <- as.numeric(object$n_gc / object$total_time) object$mem_alloc <- bench_bytes( vdapply(object$memory, function(x) if (is.null(x)) NA else sum(x$bytes, na.rm = TRUE))) if (isTRUE(relative)) { object[summary_cols] <- lapply(object[summary_cols], function(x) as.numeric(x / min(x))) } if (!is.null(time_unit)) { time_unit <- match.arg(time_unit, names(time_units())) object[time_cols] <- lapply(object[time_cols], function(x) as.numeric(x / time_units()[time_unit])) } to_keep <- intersect(c("expression", parameters, summary_cols, data_cols), names(object)) bench_mark(object[to_keep]) } #' @export `[.bench_mark` <- function(x, i, j, ...) { bench_mark(NextMethod("[")) } parse_allocations <- function(filename) { if (!file.exists(filename)) { empty_Rprofmem <- structure( list(what = character(), bytes = integer(), trace = list()), class = c("Rprofmem", "data.frame")) return(empty_Rprofmem) } # TODO: remove this dependency / simplify parsing tryCatch( profmem::readRprofmem(filename), error = function(e) { stop("Memory profiling failed.\n If you are benchmarking parallel code you must set `memory = FALSE`.", call. = FALSE) } ) } #nocov start #' Custom printing function for bench_mark objects in knitr documents #' #' By default data columns ('result', 'memory', 'time', 'gc') are omitted when #' printing in knitr. If you would like to include these columns set the knitr #' chunk option 'bench.all_columns = TRUE'. #' @param options A list of knitr chunk options set in the currently evaluated chunk. #' @inheritParams knitr::knit_print #' @details #' You can set `bench.all_columns = TRUE` to show all columns of the bench mark #' object. #' #' ```{r bench.all_columns = TRUE} #' bench::mark( #' subset(mtcars, cyl == 3), #' mtcars[mtcars$cyl == 3, ]) #' ``` knit_print.bench_mark <- function(x, ..., options) { if (isTRUE(options$bench.all_columns)) { print(x) } else { print(x[!colnames(x) %in% data_cols]) } } #nocov end parse_gc <- function(x) { # \x1E is Record Separator  x <- strsplit(paste0(x, collapse = ""), "\x1E")[[1]] tibble::as_tibble(.Call(parse_gc_, x)) } utils::globalVariables(c("time", "gc")) unnest.bench_mark <- function(data, ...) { if (inherits(data[["expression"]], "bench_expr")) { data[["expression"]] <- as.character(data[["expression"]]) } # suppressWarnings to avoid 'elements may not preserve their attributes' # warnings from dplyr::collapse if (tidyr_new_interface()) { data <- suppressWarnings(NextMethod(.Generic, data, ...)) } else { data <- suppressWarnings(NextMethod(.Generic, data, time, gc, .drop = FALSE)) } # Add bench_time class back to the time column data$time <- as_bench_time(data$time) # Add a gc column, a factor with the highest gc performed for each expression. data$gc <- dplyr::case_when( data$level2 > 0 ~ "level2", data$level1 > 0 ~ "level1", data$level0 > 0 ~ "level0", TRUE ~ "none") data$gc <- factor(data$gc, c("none", "level0", "level1", "level2")) data } #' @export rbind.bench_mark <- function(..., deparse.level = 1) { args <- list(...) desc <- unlist(lapply(args, function(x) as.character(x$expression))) res <- rbind.data.frame(...) attr(res$expression, "description") <- desc res } filter.bench_mark <- function(.data, ...) { dots <- rlang::quos(...) idx <- Reduce(`&`, lapply(dots, rlang::eval_tidy, data = .data)) .data[idx, ] } group_by.bench_mark <- function(.data, ...) { bench_mark(NextMethod()) } bench/R/utils.R0000644000175000017500000000637713700627654013176 0ustar nileshnileshviapply <- function(x, f, ...) vapply(x, f, integer(1), ...) vdapply <- function(x, f, ...) vapply(x, f, double(1), ...) vcapply <- function(x, f, ...) vapply(x, f, character(1), ...) vlapply <- function(x, f, ...) vapply(x, f, logical(1), ...) captures <- function(x, m) { assert("`x` must be a character", is.character(x)) assert("`m` must be a match object from `regexpr()`", inherits(m, "integer") && all(c("match.length", "capture.start", "capture.length", "capture.names") %in% names(attributes(m)))) starts <- attr(m, "capture.start") strings <- substring(x, starts, starts + attr(m, "capture.length") - 1L) res <- data.frame(matrix(strings, ncol = NCOL(starts)), stringsAsFactors = FALSE) colnames(res) <- auto_name_vec(attr(m, "capture.names")) res[is.na(m) | m == -1, ] <- NA_character_ res } assert <- function(msg, ..., class = "invalid_argument") { tests <- unlist(list(...)) if (!all(tests)) { stop(bench_error(msg, class = class)) } } bench_error <- function(msg, class = "invalid_argument") { structure(class = c(class, "bench_error", "error", "condition"), list(message = msg)) } auto_name_vec <- function(names) { missing <- names == "" if (all(!missing)) { return(names) } names[missing] <- seq_along(names)[missing] names } utils::globalVariables("_rval_") with_gcinfo <- function(expr) { tf <- tempfile() con <- file(tf, "wb") sink(con, type = "message") { old <- gcinfo(TRUE) on.exit({ gcinfo(old) sink(NULL, type = "message") close(con) output <- readLines(tf, warn = FALSE) unlink(tf) return(output) }) force(expr) } } deparse_trunc <- function(x, width = getOption("width")) { text <- deparse(x, width.cutoff = width) if (length(text) == 1 && nchar(text) < width) return(text) # Remove any leading spaces text <- sub("^[[:space:]]*", "", text) # Collapse all together glue::glue_collapse(text, " ", width = width) } # inlined from https://github.com/r-lib/cli/blob/master/R/utf8.R is_utf8_output <- function() { opt <- getOption("cli.unicode", NULL) if (! is.null(opt)) { isTRUE(opt) } else { l10n_info()$`UTF-8` && !is_latex_output() } } is_latex_output <- function() { if (!("knitr" %in% loadedNamespaces())) return(FALSE) get("is_latex_output", asNamespace("knitr"))() } collapse <- function(x, sep) { paste0(x, collapse = sep) } lengths <- function(x, use.names = TRUE) { viapply(x, length, USE.NAMES = use.names) } # check if the new interface is being used tidyr_new_interface <- function() { utils::packageVersion("tidyr") > "0.8.99" } # Read lines as UTF-8 read_lines <- function (path, n = -1L, encoding = "UTF-8") { base::readLines(path, n = n, encoding = encoding, warn = FALSE) } has_description <- function(path) { file.exists(file.path(path, "DESCRIPTION")) } find_package_root <- function(path) { path <- normalizePath(path, mustWork = FALSE) while(!has_description(path)) { path <- dirname(path) if (is_root(path)) { return(NULL) } } sub("[/\\]$", "", path) } is_root <- function(path) { identical(path, dirname(path)) } dots <- function(...) { dots <- as.list(substitute(...())) n <- length(dots) if (n && rlang::is_missing(dots[[n]])) { dots <- dots[-n] } dots } bench/R/bench_time.R0000644000175000017500000000325713620533713014116 0ustar nileshnilesh#' Measure Process CPU and real time that an expression used. #' #' @param expr A expression to be timed. #' @return A `bench_time` object with two values. #' - `process` - The process CPU usage of the expression evaluation. #' - `real` - The wallclock time of the expression evaluation. #' @details On some systems (such as macOS) the process clock has lower #' precision than the realtime clock, as a result there may be cases where the #' process time is larger than the real time for fast expressions. #' @examples #' # This will use ~.5 seconds of real time, but very little process time. #' bench_time(Sys.sleep(.5)) #' @seealso [bench_memory()] To measure memory allocations for a given expression. #' @aliases system_time #' @export bench_time <- function(expr) { stats::setNames( as_bench_time(.Call(system_time_, substitute(expr), parent.frame())), c("process", "real")) } #' @export system_time <- bench_time #' Measure memory that an expression used. #' #' @param expr A expression to be measured. #' @return A tibble with two columns #' - The total amount of memory allocated #' - The raw memory allocations as parsed by [profmem::readRprofmem()] #' @examples #' if (capabilities("profmem")) { #' bench_memory(1 + 1:10000) #' } #' @export bench_memory <- function(expr) { can_profile_memory <- capabilities("profmem") if (!can_profile_memory) { stop("Memory profiling not available in this version of R", call. = FALSE) } f <- tempfile() on.exit(unlink(f)) utils::Rprofmem(f, threshold = 1) force(expr) utils::Rprofmem(NULL) memory <- parse_allocations(f) tibble::tibble(mem_alloc = bench_bytes(sum(memory$bytes, na.rm = TRUE)), memory = list(memory)) } bench/R/press.R0000644000175000017500000000565414147256522013165 0ustar nileshnilesh#' Run setup code and benchmarks across a grid of parameters #' #' @description #' `press()` is used to run [bench::mark()] across a grid of parameters and #' then _press_ the results together. #' #' The parameters you want to set are given as named arguments and a grid of #' all possible combinations is automatically created. #' #' The code to setup and benchmark is given by one unnamed expression (often #' delimited by `\{`). #' #' If replicates are desired a dummy variable can be used, e.g. `rep = 1:5` for #' replicates. #' #' @param ... If named, parameters to define, if unnamed the expression to run. #' Only one unnamed expression is permitted. #' @param .grid A pre-built grid of values to use, typically a [data.frame] or #' [tibble]. This is useful if you only want to benchmark a subset of all #' possible combinations. #' @export #' @examples #' # Helper function to create a simple data.frame of the specified dimensions #' create_df <- function(rows, cols) { #' as.data.frame(setNames( #' replicate(cols, runif(rows, 1, 1000), simplify = FALSE), #' rep_len(c("x", letters), cols))) #' } #' #' # Run 4 data sizes across 3 samples with 2 replicates (24 total benchmarks) #' press( #' rows = c(1000, 10000), #' cols = c(10, 100), #' rep = 1:2, #' { #' dat <- create_df(rows, cols) #' bench::mark( #' min_time = .05, #' bracket = dat[dat$x > 500, ], #' which = dat[which(dat$x > 500), ], #' subset = subset(dat, x > 500) #' ) #' } #' ) press <- function(..., .grid = NULL) { args <- rlang::quos(...) unnamed <- names(args) == "" if (sum(unnamed) < 1) { stop("Must supply one unnamed argument", call. = FALSE) } if (sum(unnamed) > 1) { stop("Must supply no more than one unnamed argument", call. = FALSE) } if (!is.null(.grid)) { if (any(!unnamed)) { stop("Must supply either `.grid` or named arguments, not both", call. = FALSE) } parameters <- .grid } else { parameters <- expand.grid(lapply(args[!unnamed], rlang::eval_tidy), stringsAsFactors = FALSE) } status <- format(tibble::as_tibble(parameters), n = Inf) message(glue::glue(" Running with: {status[[2]]}")) eval_one <- function(row) { e <- rlang::new_data_mask(new.env(parent = emptyenv())) for (col in seq_along(parameters)) { var <- names(parameters)[[col]] value <- parameters[row, col] assign(var, value, envir = e) } message(status[[row + 3L]]) rlang::eval_tidy(args[[which(unnamed)]], data = e) } res <- lapply(seq_len(nrow(parameters)), eval_one) rows <- vapply(res, NROW, integer(1)) if (!all(rows == rows[[1]])) { stop("Results must have equal rows", call. = FALSE) # TODO: print parameters / results that are unequal? } res <- do.call(rbind, res) parameters <- parameters[rep(seq_len(nrow(parameters)), each = rows[[1]]), , drop = FALSE] bench_mark(tibble::as_tibble(cbind(res[1], parameters, res[-1]))) } bench/R/autoplot.R0000644000175000017500000000755713661222732013700 0ustar nileshnilesh#' Autoplot method for bench_mark objects #' #' @param object A `bench_mark` object. #' @param type The type of plot. Plotting geoms used for each type are #' - beeswarm - [ggbeeswarm::geom_quasirandom()] #' - jitter - [ggplot2::geom_jitter()] #' - ridge - [ggridges::geom_density_ridges()] #' - boxplot - [ggplot2::geom_boxplot()] #' - violin - [ggplot2::geom_violin()] #' @param ... Additional arguments passed to the plotting geom. #' @details This function requires some optional dependencies. [ggplot2][ggplot2::ggplot2-package], #' [tidyr][tidyr::tidyr-package], and depending on the plot type #' [ggbeeswarm][ggbeeswarm::ggbeeswarm], [ggridges][ggridges::ggridges]. #' #' For `type` of `beeswarm` and `jitter` the points are colored by the highest #' level garbage collection performed during each iteration. #' #' For plots with 2 parameters `ggplot2::facet_grid()` is used to construct a #' 2d facet. For other numbers of parameters `ggplot2::facet_wrap()` is used #' instead. #' #' @examples #' dat <- data.frame(x = runif(10000, 1, 1000), y=runif(10000, 1, 1000)) #' #' res <- bench::mark( #' dat[dat$x > 500, ], #' dat[which(dat$x > 500), ], #' subset(dat, x > 500)) #' #' if (require(ggplot2) && require(tidyr)) { #' #' # Beeswarm plot #' autoplot(res) #' #' # ridge (joyplot) #' autoplot(res, "ridge") #' #' # If you want to have the plots ordered by execution time you can do so by #' # ordering factor levels in the expressions. #' if (require(dplyr) && require(forcats)) { #' #' res %>% #' mutate(expression = forcats::fct_reorder(as.character(expression), min, .desc = TRUE)) %>% #' as_bench_mark() %>% #' autoplot("violin") #' } #' } autoplot.bench_mark <- function(object, type = c("beeswarm", "jitter", "ridge", "boxplot", "violin"),...) { if (!(requireNamespace("ggplot2") && requireNamespace("tidyr"))) { stop("`ggplot2` and `tidyr` must be installed to use `autoplot`.", call. = FALSE) } type <- match.arg(type) if (type == "beeswarm" && !requireNamespace("ggbeeswarm", quietly = TRUE)) { stop("`ggbeeswarm` must be installed to use `type = \"beeswarm\"` option.", call. = FALSE) } # Just convert bench_expr to characters if (inherits(object$expression, "bench_expr")) { object$expression <- as.character(object$expression) } if (tidyr_new_interface()) { res <- tidyr::unnest(object, c(time, gc)) } else { res <- tidyr::unnest(object) } p <- ggplot2::ggplot(res) switch(type, beeswarm = p <- p + ggplot2::aes_string("expression", "time", color = "gc") + ggbeeswarm::geom_quasirandom(...) + ggplot2::coord_flip(), jitter = p <- p + ggplot2::aes_string("expression", "time", color = "gc") + ggplot2::geom_jitter(...) + ggplot2::coord_flip(), ridge = p <- p + ggplot2::aes_string("time", "expression") + ggridges::geom_density_ridges(...), boxplot = p <- p + ggplot2::aes_string("expression", "time") + ggplot2::geom_boxplot(...) + ggplot2::coord_flip(), violin = p <- p + ggplot2::aes_string("expression", "time") + ggplot2::geom_violin(...) + ggplot2::coord_flip()) parameters <- setdiff( colnames(object), c("expression", summary_cols, data_cols, c("level0", "level1", "level2"))) if (length(parameters) == 0) { return(p) } if (length(parameters) == 2) { return(p + ggplot2::facet_grid( paste0(parameters[[1]], "~", parameters[[2]]), labeller = ggplot2::label_both)) } p + ggplot2::facet_wrap(parameters, labeller = ggplot2::label_both) } #' @rdname autoplot.bench_mark #' @param x A `bench_mark` object. #' @param y Ignored, required for compatibility with the `plot()` generic. #' @export plot.bench_mark <- function(x, ..., type = c("beeswarm", "jitter", "ridge", "boxplot", "violin"), y) { type <- match.arg(type) ggplot2::autoplot(x, type = type, ...) } bench/LICENSE0000644000175000017500000000005314147256522012476 0ustar nileshnileshYEAR: 2020 COPYRIGHT HOLDER: bench authors bench/inst/0000755000175000017500000000000013620533713012443 5ustar nileshnileshbench/inst/examples/0000755000175000017500000000000013620533713014261 5ustar nileshnileshbench/inst/examples/exprs.R0000644000175000017500000000013113620533713015540 0ustar nileshnileshx <- 1:1000 evens <- x %% 2 == 0 y <- x[evens] length(y) length(which(evens)) sum(evens) bench/NAMESPACE0000644000175000017500000000316714151175656012724 0ustar nileshnilesh# Generated by roxygen2: do not edit by hand S3method("[",bench_bytes) S3method("[",bench_expr) S3method("[",bench_mark) S3method("[",bench_time) S3method("[[",bench_bytes) S3method("[[",bench_time) S3method(Ops,bench_bytes) S3method(Ops,bench_time) S3method(Summary,bench_time) S3method(as.character,bench_bytes) S3method(as.character,bench_expr) S3method(as.character,bench_time) S3method(as_bench_bytes,bench_bytes) S3method(as_bench_bytes,default) S3method(as_bench_bytes,numeric) S3method(as_bench_time,bench_time) S3method(as_bench_time,default) S3method(as_bench_time,numeric) S3method(format,bench_bytes) S3method(format,bench_expr) S3method(format,bench_time) S3method(max,bench_bytes) S3method(max,bench_time) S3method(mean,bench_time) S3method(min,bench_bytes) S3method(min,bench_time) S3method(plot,bench_mark) S3method(print,bench_bytes) S3method(print,bench_expr) S3method(print,bench_time) S3method(rbind,bench_mark) S3method(sum,bench_bytes) S3method(sum,bench_time) S3method(summary,bench_mark) export(as_bench_bytes) export(as_bench_mark) export(as_bench_time) export(bench_bytes) export(bench_bytes_trans) export(bench_load_average) export(bench_memory) export(bench_process_memory) export(bench_time) export(bench_time_trans) export(hires_time) export(mark) export(press) export(scale_color_bench_expr) export(scale_colour_bench_expr) export(scale_x_bench_bytes) export(scale_x_bench_expr) export(scale_x_bench_time) export(scale_y_bench_bytes) export(scale_y_bench_expr) export(scale_y_bench_time) export(system_time) export(workout) export(workout_expressions) importFrom(methods,setOldClass) useDynLib(bench, .registration = TRUE)