bench/0000755000176200001440000000000014742305307011336 5ustar liggesusersbench/tests/0000755000176200001440000000000014424465045012503 5ustar liggesusersbench/tests/testthat/0000755000176200001440000000000014742305307014340 5ustar liggesusersbench/tests/testthat/test-workout.R0000644000176200001440000000133014742240574017153 0ustar liggesusersdescribe("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-press.R0000644000176200001440000000507614742270407016606 0ustar liggesusersdescribe("press", { it("Adds parameters to output", { res <- press( x = 1, mark(1, max_iterations = 10), .quiet = TRUE ) 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), .quiet = TRUE ) 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_snapshot({ 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) messages <- character() withCallingHandlers( res <- press( x = 1:3, mark(rep(1, x), max_iterations = 10) ), message = function(cnd) { messages <<- append(messages, conditionMessage(cnd)) if (!is.null(findRestart("muffleMessage"))) { invokeRestart("muffleMessage") } } ) expect_snapshot(messages) expect_equal(colnames(res), c("expression", "x", summary_cols, data_cols)) expect_equal(nrow(res), 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), .quiet = TRUE ) 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), .quiet = TRUE ) 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) }, .quiet = TRUE ) expect_equal(res$result[[1]], 2) }) it("`.grid` subsets tibbles and data.frames the same way (#142)", { x <- data.frame(a = 1, b = 2) out <- press(mark(c(a, b)), .grid = x, .quiet = TRUE) expect_identical(out$result[[1L]], c(1, 2)) x <- tibble::tibble(a = 1, b = 2) out <- press(mark(c(a, b)), .grid = x, .quiet = TRUE) expect_identical(out$result[[1L]], c(1, 2)) }) }) bench/tests/testthat/test-bench_time.R0000644000176200001440000000163314742240574017544 0ustar liggesusersdescribe("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]]) < 5) }) 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]]) > 20) }) }) 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-autoplot.R0000644000176200001440000000041314742231022017275 0ustar liggesuserstest_that("autoplot works", { skip_on_cran() skip_if_not_installed("ggplot2") skip_if_not_installed("tidyr") skip_if_not_installed("ggbeeswarm") y <- mark(x = 1:1000) expect_s3_class(ggplot2::autoplot(y), "ggplot") expect_s3_class(plot(y), "ggplot") }) bench/tests/testthat/test-mark.R0000644000176200001440000001773014742261636016410 0ustar liggesusersdescribe("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), FALSE ) expect_length(res, 10) res <- .Call( mark_, quote(1), new.env(), Inf, as.integer(0), as.integer(20), FALSE ) expect_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), FALSE ) expect_length(res, 1) res <- .Call( mark_, quote(1), new.env(), 0, as.integer(5), as.integer(10), FALSE ) expect_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), FALSE ) 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), FALSE ) 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_type(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_type(res$result, "list") expect_equal(res$result[[1]], 2) expect_equal(res$result[[2]], 3) }) it("works with capabilities('profmem')", { skip_if_not(capabilities("profmem")) res <- mark(1, 2, check = FALSE, iterations = 1) expect_length(res$memory, 2) expect_s3_class(res$memory[[1]], "Rprofmem") expect_equal(ncol(res$memory[[1]]), 3) expect_gte(nrow(res$memory[[1]]), 0) }) it("works without capabilities('profmem')", { res <- mark(1, 2, check = FALSE, iterations = 1, memory = FALSE) 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_snapshot(error = TRUE, { mark(1, 1, 3, max_iterations = 10) }) }) it("Works when calls are different lengths", { expect_snapshot(error = TRUE, { # 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_s3_class(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_s3_class(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_s3_class(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, ) }) it("truncates long expressions when printing (#94)", { local_reproducible_output(width = 30) name <- paste0(rep("a", 100), collapse = "") exprs <- list(as.name(name)) assign(name, 1, envir = environment()) out <- mark(exprs = exprs) # Only snapshot static columns out <- out[c("expression", "result")] expect_snapshot(out) }) }) 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_no_warning(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", "1.0.0") bnch <- mark(1 + 1, 2 + 0) res <- tidyr::unnest(bnch, c(time, gc)) 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-hires_time.R0000644000176200001440000000050214424465045017570 0ustar liggesusersdescribe("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_type(start, "double") expect_true(end > start) }) }) bench/tests/testthat/test-bytes.R0000644000176200001440000001037314742240574016576 0ustar liggesusersdescribe("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-time.R0000644000176200001440000001147314742240574016410 0ustar liggesusersdescribe("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/_snaps/0000755000176200001440000000000014424724326015626 5ustar liggesusersbench/tests/testthat/_snaps/press.md0000644000176200001440000000056114424724327017307 0ustar liggesusers# press: Outputs status message before evaluating each parameter Code res <- press(x = 1, mark(rep(1, x), max_iterations = 10)) Message Running with: x 1 1 --- Code messages Output [1] "Running with:\n x\n" "1 1\n" [3] "2 2\n" "3 3\n" bench/tests/testthat/_snaps/mark.md0000644000176200001440000000122614424724326017103 0ustar liggesusers# mark: Can errors with the deparsed expressions Code mark(1, 1, 3, max_iterations = 10) Condition Error: ! Each result must equal the first result: `1` does not equal `3` # mark: Works when calls are different lengths Code mark(if (TRUE) 2, if (TRUE) 1 else 3) Condition Error: ! Each result must equal the first result: `if (TRUE) 2` does not equal `if (TRUE) 1 else 3` # mark: truncates long expressions when printing (#94) Code out Output # A tibble: 1 x 2 expression result 1 aaaaaaaaaaaaaaaaaaaa~ bench/tests/testthat/test-expression.R0000644000176200001440000000115314742240574017643 0ustar liggesuserstest_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-bench_process_memory.R0000644000176200001440000000113014742240574021644 0ustar liggesusersdescribe("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.R0000644000176200001440000000056214424465045014471 0ustar liggesusers# This file is part of the standard setup for testthat. # It is recommended that you do not modify it. # # Where should you do additional test configuration? # Learn more about the roles of various files in: # * https://r-pkgs.org/tests.html # * https://testthat.r-lib.org/reference/test_package.html#special-files library(testthat) library(bench) test_check("bench") bench/MD50000644000176200001440000000644214742305307011654 0ustar liggesusersbde679a5a5e8d39611380ab0a8d54341 *DESCRIPTION 1dda47ff18f938acd56950ad33f52ce2 *LICENSE 620cb9cc31a7b4ef0a833115923501cb *NAMESPACE a990b314c46a30b2d9ffc9ea6ddb84e9 *NEWS.md ddbe4a578a856da0f0a570ffba1bdcfb *R/autoplot.R 60410c54dd2234b4f96d89d09d645ccb *R/bench-package.R cd9f2a6098ebc356020778349ae318e2 *R/bench_process_memory.R 17aa8136f9802f314f8ff1dbb53f5be6 *R/bench_time.R 07dd4a0df66d177cadd82b3f23b7933d *R/bytes.R 53c180bd091df78f75e97fccf4166f33 *R/expression.R 77e68ec28bbdde8f4bedaff981f6309f *R/hires_time.R 7fa3b622ec697c432cf194cb810d91c6 *R/import-standalone-s3-register.R 5081488c5f47905e1e90fd54d54ce7e9 *R/load.R bbe9c94e48e4fc6cc36528bcd06f0646 *R/mark.R 2fa9baef95f674762efc0ceb41a8172a *R/press.R 4e170b169889a9122f20edf68f20ac5f *R/time.R 3ed7c5cac0dd8a8cfe03c73b8885e80c *R/utils.R 0fd012fbabefb7e5a985595829dc63ab *R/workout.R e204e52ccf14d3d121ea192680fcaf2a *R/zzz.R 2ab4c3985e0420cda290ceaace3f0efc *README.md 1fe3413a86a0c4c2906e7165a90f50c6 *inst/examples/exprs.R 7caab55fd54d91cb18f632e305601d2a *man/as_bench_mark.Rd 30db463e739be59bd8ab5f860369ba18 *man/as_bench_time.Rd 3a40169ebc2513616ba352a7e2ece4a2 *man/autoplot.bench_mark.Rd 860fe02b8996a0796f2214b85a31f5af *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 7caad6578f5a4540ea5df724ed7dace4 *man/bench_process_memory.Rd 2e18e19606c9c1c1d5ea5ccba1642005 *man/bench_time.Rd a8137bcbacc4e74c922c29ed701499cc *man/bench_time_trans.Rd 2ffa360ca5ef05477039e9eae9f318e0 *man/figures/README-autoplot-1.png 8643c59be2df01f7082be204b3ef7afb *man/figures/README-custom-plot-1.png fdac20c9c4139609fea0f9e64981956c *man/hires_time.Rd a73a679ddea597dac7a172d843483a23 *man/knit_print.bench_mark.Rd 26eefb1ea43c365ef599f42eaeabbdef *man/mark.Rd f71c5d7dd9987a72e86184821d54cbc8 *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 23950326a3c36819758c500d11cb753f *src/load.c f2c720fbda25db33899c8ee386978707 *src/mark.c da4293a8454c4cf6d04e6085cb030a71 *src/nanotime.c a4ddcbf152c7cc443c7fc4d419b7ba02 *src/nanotime.h 3292c9d23affdceeb30aca948ae0609c *src/os.h e9c07bac16328c31e7dfbeb94920d2e4 *src/process_memory.c 6fac3fba3b03a971f611b6de40e077ab *tests/testthat.R e2fdb651b601e9ac62412338db079d61 *tests/testthat/_snaps/mark.md 7914e7f96609801fee4cde25abe1dd09 *tests/testthat/_snaps/press.md c74cebc287412a0bfaef8107cbfa6293 *tests/testthat/test-autoplot.R a72d5e9d76338148f3b86bab1abd6d2d *tests/testthat/test-bench_process_memory.R 14bd9b77496582292dc45264424090dc *tests/testthat/test-bench_time.R d44e7e5d1ca5f804d8c6dc0ba5873e84 *tests/testthat/test-bytes.R d0cf1afffd6e03f2cfbefdba981d8628 *tests/testthat/test-expression.R 4a6a82facc351c8d376ae071da009c26 *tests/testthat/test-hires_time.R 0d4dfc37d0fb68477b9689d879a4afc3 *tests/testthat/test-mark.R 9b704b4e11dc6fce89b6297f3db32b77 *tests/testthat/test-press.R d24fadab87a28d67db35753bed63fe45 *tests/testthat/test-time.R e2c71d701d4b1de0dcba9e0401f934e4 *tests/testthat/test-workout.R bench/R/0000755000176200001440000000000014742271112011533 5ustar liggesusersbench/R/bench_time.R0000644000176200001440000000327614742240574013773 0ustar liggesusers#' 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/bench_process_memory.R0000644000176200001440000000227114424725472016077 0ustar liggesusers#' 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://learn.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-process_memory_counters) #' - [PROCESS_MEMORY_COUNTERS.PeakWorkingSetSize](https://learn.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) #' @export bench_process_memory <- function() { stats::setNames( as_bench_bytes(.Call(bench_process_memory_)), c("current", "max") ) } bench/R/zzz.R0000644000176200001440000000103714742225614012522 0ustar liggesusers#nocov start .onLoad <- function(...) { s3_register("tidyr::unnest", "bench_mark") s3_register("dplyr::filter", "bench_mark") s3_register("dplyr::group_by", "bench_mark") s3_register("ggplot2::autoplot", "bench_mark") s3_register("ggplot2::scale_type", "bench_expr") s3_register("ggplot2::scale_type", "bench_time") s3_register("ggplot2::scale_type", "bench_bytes") s3_register("knitr::knit_print", "bench_mark") s3_register("vctrs::vec_proxy", "bench_expr") s3_register("vctrs::vec_restore", "bench_expr") } #nocov end bench/R/import-standalone-s3-register.R0000644000176200001440000001432414742240574017477 0ustar liggesusers# Standalone file: do not edit by hand # Source: https://github.com/r-lib/rlang/blob/HEAD/R/standalone-s3-register.R # Generated by: usethis::use_standalone("r-lib/rlang", "s3-register") # ---------------------------------------------------------------------- # # --- # repo: r-lib/rlang # file: standalone-s3-register.R # last-updated: 2024-05-14 # license: https://unlicense.org # --- # # ## Changelog # # 2024-05-14: # # * Mentioned `usethis::use_standalone()`. # # nocov start #' Register a method for a suggested dependency #' #' Generally, the recommended way to register an S3 method is to use the #' `S3Method()` namespace directive (often generated automatically by the #' `@export` roxygen2 tag). However, this technique requires that the generic #' be in an imported package, and sometimes you want to suggest a package, #' and only provide a method when that package is loaded. `s3_register()` #' can be called from your package's `.onLoad()` to dynamically register #' a method only if the generic's package is loaded. #' #' For R 3.5.0 and later, `s3_register()` is also useful when demonstrating #' class creation in a vignette, since method lookup no longer always involves #' the lexical scope. For R 3.6.0 and later, you can achieve a similar effect #' by using "delayed method registration", i.e. placing the following in your #' `NAMESPACE` file: #' #' ``` #' if (getRversion() >= "3.6.0") { #' S3method(package::generic, class) #' } #' ``` #' #' @section Usage in other packages: #' To avoid taking a dependency on rlang, you copy the source of #' [`s3_register()`](https://github.com/r-lib/rlang/blob/main/R/standalone-s3-register.R) #' into your own package or with #' `usethis::use_standalone("r-lib/rlang", "s3-register")`. It is licensed under #' the permissive [unlicense](https://choosealicense.com/licenses/unlicense/) to #' make it crystal clear that we're happy for you to do this. There's no need to #' include the license or even credit us when using this function. #' #' @param generic Name of the generic in the form `"pkg::generic"`. #' @param class Name of the class #' @param method Optionally, the implementation of the method. By default, #' this will be found by looking for a function called `generic.class` #' in the package environment. #' @examples #' # A typical use case is to dynamically register tibble/pillar methods #' # for your class. That way you avoid creating a hard dependency on packages #' # that are not essential, while still providing finer control over #' # printing when they are used. #' #' .onLoad <- function(...) { #' s3_register("pillar::pillar_shaft", "vctrs_vctr") #' s3_register("tibble::type_sum", "vctrs_vctr") #' } #' @keywords internal #' @noRd s3_register <- function(generic, class, method = NULL) { stopifnot(is.character(generic), length(generic) == 1) stopifnot(is.character(class), length(class) == 1) pieces <- strsplit(generic, "::")[[1]] stopifnot(length(pieces) == 2) package <- pieces[[1]] generic <- pieces[[2]] caller <- parent.frame() get_method_env <- function() { top <- topenv(caller) if (isNamespace(top)) { asNamespace(environmentName(top)) } else { caller } } get_method <- function(method) { if (is.null(method)) { get(paste0(generic, ".", class), envir = get_method_env()) } else { method } } register <- function(...) { envir <- asNamespace(package) # Refresh the method each time, it might have been updated by # `devtools::load_all()` method_fn <- get_method(method) stopifnot(is.function(method_fn)) # Only register if generic can be accessed if (exists(generic, envir)) { registerS3method(generic, class, method_fn, envir = envir) } else if (identical(Sys.getenv("NOT_CRAN"), "true")) { warn <- .rlang_s3_register_compat("warn") warn( c( sprintf( "Can't find generic `%s` in package %s to register S3 method.", generic, package ), "i" = "This message is only shown to developers using devtools.", "i" = sprintf( "Do you need to update %s to the latest version?", package ) ) ) } } # Always register hook in case package is later unloaded & reloaded setHook(packageEvent(package, "onLoad"), function(...) { register() }) # For compatibility with R < 4.1.0 where base isn't locked is_sealed <- function(pkg) { identical(pkg, "base") || environmentIsLocked(asNamespace(pkg)) } # Avoid registration failures during loading (pkgload or regular). # Check that environment is locked because the registering package # might be a dependency of the package that exports the generic. In # that case, the exports (and the generic) might not be populated # yet (#1225). if (isNamespaceLoaded(package) && is_sealed(package)) { register() } invisible() } .rlang_s3_register_compat <- function(fn, try_rlang = TRUE) { # Compats that behave the same independently of rlang's presence out <- switch( fn, is_installed = return(function(pkg) requireNamespace(pkg, quietly = TRUE)) ) # Only use rlang if it is fully loaded (#1482) if ( try_rlang && requireNamespace("rlang", quietly = TRUE) && environmentIsLocked(asNamespace("rlang")) ) { switch( fn, is_interactive = return(rlang::is_interactive) ) # Make sure rlang knows about "x" and "i" bullets if (utils::packageVersion("rlang") >= "0.4.2") { switch( fn, abort = return(rlang::abort), warn = return((rlang::warn)), inform = return(rlang::inform) ) } } # Fall back to base compats is_interactive_compat <- function() { opt <- getOption("rlang_interactive") if (!is.null(opt)) { opt } else { interactive() } } format_msg <- function(x) paste(x, collapse = "\n") switch( fn, is_interactive = return(is_interactive_compat), abort = return(function(msg) stop(format_msg(msg), call. = FALSE)), warn = return(function(msg) warning(format_msg(msg), call. = FALSE)), inform = return(function(msg) message(format_msg(msg))) ) stop(sprintf("Internal error in rlang shims: Unknown function `%s()`.", fn)) } # nocov end bench/R/bytes.R0000644000176200001440000001253514742240574013022 0ustar liggesusers# 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")) } 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) } #' @export pillar_shaft.bench_bytes <- function(x, ...) { pillar::new_pillar_shaft_simple(format.bench_bytes(x), align = "right", ...) } #' @export 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( name = "bch:byt", transform = as.numeric, inverse = as_bench_bytes, breaks = 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( name = paste0("bch:byt-", format(base)), transform = trans, inverse = inv, breaks = scales::log_breaks(base = base), domain = c(1e-100, Inf) ) } # Lazily registered in `.onLoad()` 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(..., transform = bench_bytes_trans(base = base)) } #' @rdname scale_bench_time #' @keywords internal #' @export scale_y_bench_bytes <- function(base = 10, ...) { ggplot2::scale_y_continuous(..., transform = bench_bytes_trans(base = base)) } bench/R/hires_time.R0000644000176200001440000000137114424264614014016 0ustar liggesusers#' 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/autoplot.R0000644000176200001440000000724314742276064013546 0ustar liggesusers#' 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-package]. #' #' 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) && require(ggbeeswarm)) { #' #' # 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") #' } #' } # Lazily registered in `.onLoad()` autoplot.bench_mark <- function( object, type = c("beeswarm", "jitter", "ridge", "boxplot", "violin"), ... ) { rlang::check_installed(c("ggplot2", "tidyr (>= 1.0.0)"), "for `autoplot()`.") type <- match.arg(type) if (type == "beeswarm") { rlang::check_installed("ggbeeswarm", "to use `type = \"beeswarm\".") } # Just convert bench_expr to characters if (inherits(object$expression, "bench_expr")) { object$expression <- as.character(object$expression) } res <- tidyr::unnest(object, c(time, gc)) p <- ggplot2::ggplot(res) switch( type, beeswarm = p <- p + ggplot2::aes(.data$time, .data$expression, color = .data$gc) + ggbeeswarm::geom_quasirandom(..., orientation = "y"), jitter = p <- p + ggplot2::aes(.data$time, .data$expression, color = .data$gc) + ggplot2::geom_jitter(...), ridge = p <- p + ggplot2::aes(.data$time, .data$expression) + ggridges::geom_density_ridges(...), boxplot = p <- p + ggplot2::aes(.data$time, .data$expression) + ggplot2::geom_boxplot(...), violin = p <- p + ggplot2::aes(.data$time, .data$expression) + ggplot2::geom_violin(...) ) 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/R/utils.R0000644000176200001440000000517214742261636013035 0ustar liggesusersviapply <- 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 } 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"))() } lengths <- function(x, use.names = TRUE) { viapply(x, length, USE.NAMES = use.names) } dots <- function(...) { dots <- as.list(substitute(...())) n <- length(dots) if (n && rlang::is_missing(dots[[n]])) { dots <- dots[-n] } dots } bench/R/workout.R0000644000176200001440000000334014742240574013400 0ustar liggesusers#' 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/mark.R0000644000176200001440000003254214742261636012630 0ustar liggesusers#' @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 [utils::Rprofmem()]. 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), TRUE ) ) }) 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`. #' #' @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, ] #' ) #' ``` #' #' @inheritParams knitr::knit_print #' #' @param options A list of knitr chunk options set in the currently evaluated #' chunk. # Lazily registered in `.onLoad()` knit_print.bench_mark <- function(x, ..., options) { if (!isTRUE(options$bench.all_columns)) { x <- x[!colnames(x) %in% data_cols] } NextMethod() } #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")) # Lazily registered in `.onLoad()` 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 data <- suppressWarnings(NextMethod(.Generic, data, ...)) # 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 } # Lazily registered in `.onLoad()` filter.bench_mark <- function(.data, ...) { dots <- rlang::quos(...) idx <- Reduce(`&`, lapply(dots, rlang::eval_tidy, data = .data)) .data[idx, ] } # Lazily registered in `.onLoad()` group_by.bench_mark <- function(.data, ...) { bench_mark(NextMethod()) } bench/R/press.R0000644000176200001440000000643214742271637013033 0ustar liggesusers#' 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::tibble()]. This is useful if you only want to benchmark a subset #' of all possible combinations. #' @param .quiet If `TRUE`, progress messages will not be emitted. #' @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, .quiet = FALSE) { args <- rlang::quos(...) assert( "`.quiet` must be `TRUE` or `FALSE`", isTRUE(.quiet) || isFALSE(.quiet) ) 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 ) } # For consistent `[` methods parameters <- tibble::as_tibble(parameters) if (!.quiet) { status <- format(parameters, n = Inf) message(glue::glue("Running with:\n{status[[2]]}")) } eval_one <- function(row) { env <- rlang::new_data_mask(new.env(parent = emptyenv())) names <- names(parameters) for (i in seq_along(parameters)) { name <- names[[i]] column <- parameters[[i]] value <- column[row] assign(name, value, envir = env) } if (!.quiet) { message(status[[row + 3L]]) } rlang::eval_tidy(args[[which(unnamed)]], data = env) } 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]]), ] bench_mark(tibble::as_tibble(cbind(res[1], parameters, res[-1]))) } bench/R/load.R0000644000176200001440000000041314742240574012603 0ustar liggesusers#' 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/time.R0000644000176200001440000001404214742240574012625 0ustar liggesuserstime_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)) } #' @export pillar_shaft.bench_time <- function(x, ...) { pillar::new_pillar_shaft_simple(format.bench_time(x), align = "right", ...) } #' @export 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( name = "bch:tm", transform = as.numeric, inverse = as_bench_time, breaks = 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( name = paste0("bch:tm-", format(base)), transform = trans, inverse = inv, breaks = scales::log_breaks(base = base), domain = c(1e-100, Inf) ) } # Lazily registered in `.onLoad()` 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(..., transform = bench_time_trans(base = base)) } #' @rdname scale_bench_time #' @keywords internal #' @export scale_y_bench_time <- function(base = 10, ...) { ggplot2::scale_y_continuous(..., transform = bench_time_trans(base = base)) } bench/R/bench-package.R0000644000176200001440000000040514742271112014325 0ustar liggesusers## usethis namespace: start #' @importFrom methods setOldClass #' @importFrom pillar pillar_shaft #' @importFrom pillar type_sum #' @importFrom rlang .data ## usethis namespace: end NULL #' @keywords internal #' @inherit summary.bench_mark examples "_PACKAGE" bench/R/expression.R0000644000176200001440000000461214742240574014070 0ustar liggesusersnew_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() } #' @export type_sum.bench_expr <- function(x) { "bch:expr" } #' @export `[.bench_expr` <- function(x, i, ...) { new_x <- NextMethod("[") new_bench_expr(new_x) } # Lazily registered in `.onLoad()` 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)) } # Lazily registered in `.onLoad()` vec_restore.bench_expr <- function(x, to, ...) { new_bench_expr(x$x, x$desc) } #' @export pillar_shaft.bench_expr <- function(x, ...) { # We format bench expressions exactly like character vectors. This ensures # they are truncated as needed, which is useful for long unnamed expressions # (#94). This is the same logic as `pillar:::pillar_shaft.factor()`. pillar_shaft(as.character(x), ...) } # Lazily registered in `.onLoad()` 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/src/0000755000176200001440000000000014742301456012126 5ustar liggesusersbench/src/mark.c0000644000176200001440000000707214424506065013232 0ustar liggesusers#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, SEXP gcinfo) { 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]; Rboolean gcinfo_ = LOGICAL(gcinfo)[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); if (gcinfo_) { // We don't emit the separator during low level testing of `mark_()` // 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_(void) { 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_(void); extern SEXP bench_load_average_(void); static const R_CallMethodDef CallEntries[] = { {"mark_", (DL_FUNC) &mark_, 6}, {"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.win0000644000176200001440000000002314424264614014412 0ustar liggesusersPKG_LIBS = -lpsapi bench/src/nanotime.c0000644000176200001440000000666114424465045014117 0ustar liggesusers#include "nanotime.h" #include "os.h" #if OS_WINDOWS #include #elif OS_MACOS #include #include #include #include #else #define __EXTENSIONS__ #include #include #define NSEC_PER_SEC 1000000000 /* nanoseconds per second */ #endif #if OS_WINDOWS long double real_time(void) { // 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 OS_MACOS long double real_time(void) { // 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 OS_SOLARIS long double real_time(void) { 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(void) { 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(void) { #if OS_WINDOWS 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 OS_SOLARIS 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) // Modern macOS and Linux 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 // macOS before 10.12 didn't define `CLOCK_PROCESS_CPUTIME_ID` // https://github.com/r-lib/bench/commit/cfd4e2392f980e29d833f4df42a43ea2ba131aaf 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.c0000644000176200001440000000713114424465045015344 0ustar liggesusers#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_(void) { 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/os.h0000644000176200001440000000047314424465045012726 0ustar liggesusers#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 #ifdef __sun #define OS_SOLARIS 1 #else #define OS_SOLARIS 0 #endif #endif bench/src/load.c0000644000176200001440000000123414424465045013213 0ustar liggesusers#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_(void) { 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/nanotime.h0000644000176200001440000000027514424465045014117 0ustar liggesusers#ifndef NANOTIME_H #define NANOTIME_H #include "Rinternals.h" long double real_time(void); long double process_cpu_time(void); long double expr_elapsed_time(SEXP expr, SEXP env); #endif bench/NAMESPACE0000644000176200001440000000361514742271112012556 0ustar liggesusers# 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(pillar_shaft,bench_bytes) S3method(pillar_shaft,bench_expr) S3method(pillar_shaft,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) S3method(type_sum,bench_bytes) S3method(type_sum,bench_expr) S3method(type_sum,bench_time) 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) importFrom(pillar,pillar_shaft) importFrom(pillar,type_sum) importFrom(rlang,.data) useDynLib(bench, .registration = TRUE) bench/LICENSE0000644000176200001440000000005314742271506012344 0ustar liggesusersYEAR: 2025 COPYRIGHT HOLDER: bench authors bench/NEWS.md0000644000176200001440000001032414742272603012436 0ustar liggesusers# bench 1.1.4 * `press()` gains a new `.quiet` argument to silence progress messages (#145). * The `.grid` argument of `press()` now subsets data.frames and tibbles consistently (#142). * `bench_time_trans()` and `bench_bytes_trans()` once again apply pretty breaks correctly (#140, @plietar, @simonpcouch). * R >=4.0.0 is now required, which is aligned with tidyverse standards. * Switched to modern ggplot2 conventions internally (#141, @olivroy). # bench 1.1.3 * Long unnamed `bench_expr` expressions are now truncated correctly when used as columns of a tibble (#94). * `bench_mark` tibbles now respect the knitr paged df option (#103). * Fixed an issue where macOS specific C code paths were accidentally being used on GNU Hurd (#118). * Fixed `-Wstrict-prototypes` warnings, as requested by CRAN (#124). * R >=3.5.0 is now required, which is aligned with tidyverse standards. * bench now uses testthat 3e (#129). * bench no longer Suggests mockery. # 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/inst/0000755000176200001440000000000014424264614012315 5ustar liggesusersbench/inst/examples/0000755000176200001440000000000014424264614014133 5ustar liggesusersbench/inst/examples/exprs.R0000644000176200001440000000013114424264614015412 0ustar liggesusersx <- 1:1000 evens <- x %% 2 == 0 y <- x[evens] length(y) length(which(evens)) sum(evens) bench/README.md0000644000176200001440000001741714742272227012633 0ustar liggesusers # bench [![CRAN status](https://www.r-pkg.org/badges/version/bench)](https://cran.r-project.org/package=bench) [![R-CMD-check](https://github.com/r-lib/bench/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/r-lib/bench/actions/workflows/R-CMD-check.yaml) [![Codecov test coverage](https://codecov.io/gh/r-lib/bench/graph/badge.svg)](https://app.codecov.io/gh/r-lib/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("pak") pak::pak("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](https://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, ] 151µs 202µs 4994. 377KB 36.8 #> 2 dat[which(dat$x > 500), ] 124µs 168µs 6169. 260KB 33.2 #> 3 subset(dat, x > 500) 184µs 232µs 3911. 510KB 40.3 ``` 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.22 1.20 1.28 1.45 1.11 #> 2 dat[which(dat$x > 500), ] 1 1 1.58 1 1 #> 3 subset(dat, x > 500) 1.48 1.38 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) { out <- replicate(cols, runif(rows, 1, 100), simplify = FALSE) out <- setNames(out, rep_len(c("x", letters), cols)) as.data.frame(out) } 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 27µs 34µs 27964. 15.84KB 19.6 #> 2 which 1000 2 25.7µs 33.4µs 29553. 7.91KB 17.7 #> 3 subset 1000 2 45.9µs 58.2µs 16793. 27.7KB 17.1 #> 4 bracket 10000 2 64.1µs 70.8µs 13447. 156.46KB 40.5 #> 5 which 10000 2 46.7µs 54.7µs 17586. 78.23KB 23.3 #> 6 subset 10000 2 116.2µs 132.1µs 7228. 273.79KB 40.9 #> 7 bracket 1000 10 77.2µs 85.4µs 11335. 47.52KB 19.9 #> 8 which 1000 10 67.8µs 75.2µs 13073. 7.91KB 23.2 #> 9 subset 1000 10 84.7µs 107.5µs 9281. 59.38KB 18.8 #> 10 bracket 10000 10 130.2µs 169.1µs 5799. 469.4KB 52.2 #> 11 which 10000 10 75.1µs 96µs 10187. 78.23KB 17.4 #> 12 subset 10000 10 222.7µs 253µs 3810. 586.73KB 43.3 ``` ## 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. ``` r library(tidyverse) results %>% unnest(c(time, gc)) %>% filter(gc == "none") %>% mutate(expression = as.character(expression)) %>% ggplot(aes(x = mem_alloc, y = time, color = expression)) + geom_point() + scale_color_bench_expr(scales::brewer_pal(type = "qual", palette = 3)) ``` ## `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 #> 1.73s 1.7s bench::system_time(Sys.sleep(.5)) #> process real #> 58µs 497ms ``` ## 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/0000755000176200001440000000000014742263770012120 5ustar liggesusersbench/man/as_bench_mark.Rd0000644000176200001440000000061114424465045015155 0ustar liggesusers% 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/bench_bytes_trans.Rd0000644000176200001440000000057614424264614016106 0ustar liggesusers% 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/summary.bench_mark.Rd0000644000176200001440000000725414424465045016200 0ustar liggesusers% 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/as_bench_time.Rd0000644000176200001440000000132014424264614015156 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/time.R \name{as_bench_time} \alias{as_bench_time} \title{Human readable times} \usage{ as_bench_time(x) } \arguments{ \item{x}{A numeric or character vector. Character representations can use shorthand sizes (see examples).} } \description{ 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'. } \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"))) } bench/man/workout.Rd0000644000176200001440000000226314424264614014117 0ustar liggesusers% 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/bench_memory.Rd0000644000176200001440000000112014424264614015043 0ustar liggesusers% 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/bench_bytes.Rd0000644000176200001440000000146414424264614014674 0ustar liggesusers% 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/autoplot.bench_mark.Rd0000644000176200001440000000462114742231022016332 0ustar liggesusers% 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-package]{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) && require(ggbeeswarm)) { # 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/figures/0000755000176200001440000000000014742271112013551 5ustar liggesusersbench/man/figures/README-autoplot-1.png0000644000176200001440000060175214424465045017241 0ustar liggesusersPNG  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|@IDATx dU}(S=ݳo 젬TĨ HѼ Qy5D\Q(4 %Ȏ0{{PEuOV{}kֽ;ýuN'IA"@ @ @ @T@CES @ @ @ @ @ @ @ @@ h!  @ @ @ @@@ @ @ @ @4Tܐh @ @ @  @ @ @ Pq*nH4 @ @ @w @ @ @8 7$D @ @ @h;@ @ @ @T "@ @ @ @4 @ @ @ @*N@@C  @ @ @ @ @ @ @ @' D @ @ @ @ ~ @ @ @ @PqCA @ @ @ @ @ @ @ @@ h!  @ @ @ @@@ @ @ @ @4Tܐh @ @ @  @ @ @ PqM" "Pc+V+W^ @CCWK. mmmaƌa---C:_& @&)w @Eַn3<3-zŨ`Ŋ]wZ[[{UА><±7 @UVߴiSKWU hhoo7tSwb9iO~øq&@ P*~4 7}/ttt|0xp饗W / ӧO7 @W+\y啹Ӛׇ m۶$>O)S> @) "E @ ? s&L~j7,Y_c38|/  @#?τ;c؍ھ}{? fw\:C~ýޛB?i?t)aW @ePtU @F*򗿜;裏v}sM=Pg; @XfMg?5$.SO6773/9wYgiC ~_‡>$ @R 0 o1%nhjj ܐk ===ٷ^  @(@.;3l޼9G? .3̐=p!O}Sٷoq  @@5hQF @UWWaٷk.[, @x_>υxӘ1c¼y՘'!κܹs)2tP8㝝!.c' @KNT(i#7?aʕ+a=qԸp°{8jS\vi Uҥb]{k"466vʀ׮][7fz^6`;vښzӇ^  @ E|{??7Z>򑏄aɒ%٬>>O?=Ӹqrg#q嗇k:::“O>>N;-jFԀ ĉ '0;ehѢa/3 @@ /Ǚ^׆xgIz֭[s%0av5{[n(*A @ ʀJ P/wC DȦɓ'SO=5~aڴia~no|R'|r?aeeS<7?~|Xgjeկ~5gb?oSi`;]"=Cu]kҌ3%3r;l @@0g^0{Q˦XYoweA @JP#} @Jñ.>t]z6 8??8KCL_Yb AL/B׾ns>ٝ1o{1 Gy$HgOk!(d'?ɝo=Ûp{,//yhnnN{l;o @p-XC fKJ*0iҤ4ǼSL_Fn  @&0+ k @/o;ebK%|&Lة|#i@B{{{ؼys{qCOӡ5=/NO}*)Zwwzkz8BrCM+ȕg{ @p-C\K=Kܗ ?>v~}@y'@ P  m @jK rp]w:?w0وKH%NⷠbPCL˖- >h32${nI6mJgi?>v7VXV^=)- W^yekM7ݴ>XH6_r%9r饗VLeE @H| \̖-[r50 @&`J!@5 {ȟNWqىK2dv'ǩQO>A˙3gNï4o`8i}Mgt, \pAp1nܸ^EŵjWKtD@ /0%?$ @ L2bj @(b*ԩ@P=RӧOϾo0CͦP͝;7{Z2r;w' 1_>]"I>p1DŽ 좔z'‡?q\.׽. @] ߕN;vl1q龡EoG @-| P1 fΜ9@9fϞ=k!"{}{ߛ 3\2wPfyfί3tDP^, 1âE=%.gqpW~bglG?\0C|O~R0C)A117oNJQ6Edy%@ Pifh P3fbÆ n䗳nݺ!f͚\ɓ'-v c㵯}m?˖- {o/vm!&qW)S e~_ 7pCv- fXpan  @Cȿw-?T_f+EVJ\f @$ L%@,{g2iH?[`-D{ϛ~pw[n%dx}x_?X r<>0`lg}e'<̊x%@kን'1cƤ'˗/4!l۶-8 m @@ XrRGF @@ )Û sN袋—p~9/Q]tiYf嶇?˞q\sMkro> fx+^> R4 @@ _mll ŠR~Lٳ;q @eP! P{s [6|_qӇz(1o޼\7_WmM|+Czҗ>p駇뮻nN=\'|2]̍o~[oUŸøqrl @kO<1Wo@qFl:C^  @T#@)g183rC[[[}ߍ8C |e/{Y8ꨣrmY ٔ/oWӧOk׮M~}Wsyc3g2o!>s{^N8!|@;h @0\_WjqYR>묳j? @PQá1 @vwɓ'k^z!β7e>OŠY>^r%ˊqtgy気`8餓rƥ+/ܾ?inW1.K\ޑngƌooRuօl߾}U; @N\B_gwvZxq}g7/^.\!:a @*Z[q @@ L81wys\ڇw .~âEBsssxgM7ݔ ~hk𖷼%7+>wSN9%-'.SO8j,/gpguܹi}7pCzt7!0aB4p-??ݩG}4dvmh],i\r#.ɑMk֬ {n^I\RC"@ 0k2zwK 8+^ $\~x;vla1{˖-iU&M 0jO @dJF" P1 >Dn%|;ݎ W_}u..Qq7/#.&FbDᮻJO3CĺJQԧҾ)DY @ +ҩcSN tF8\GGGAoG_~y7o^C @X ;4FjC5yMSƥ ;u,ζ L&7>:|K_ ?pz7O>b/Τ},7 ^{mxw:7&1pǦ3씧;⺷ @R /5kVꪫWpm͛7*0OYngu @.IR7RT+ʕ+ N ˎ^x!]"NF 'r q8uj||HqfUV?a…i0CSbx+u4ċ)Sƍ+@0iҤ^ٷnZ1+:2AԩS)oC!AlMtoX ~0mڴT`˖-O꘣]O'mڴ)k׮]+LcvĉFX){j\dA۶m RkMXs 4;vlj9mds _(0Okٷ^  @ PMS{FT< @ @ P!)и?a1l @T\BOOaS @ Я~Y$P[?lk\ @hغ`KgÓ+OA @ @q< @*u&@ @%PRn @ @%(xu L @ @; h"9\ @ @> =\OgWYr @ P/e~ .> l|5s @Fm/S @ @v6@m tCO=^[NzC @@M 4-]R~5lX2H @, ag{Ԕ@-JPX @ PPL{{Aˋ5l3CCQH @]jIaҢtqㆢP @T@Wg7Q  @ @@-hQh\RUwQC @}"-WpzH @' ax^r:ˋLQJU( @*_P发 @ @@mhq  dn @ @ @T :2E@ @ @ @N@@CD @ @ @ Q@@d#PE]۵ZY @ @ @ Py*U hki @B@ @T*빳  @ @@mhq  d,wv3XPmT @+ik-J{bMME)[ @ @K^EC @@}Y/ ޴qXL @ _@@͜Ajjڪ @(@wwظW @ @HX ٸ  @(@1kX,}R) @' 4Zw EZ/6 >ȾJ @hXn @ @\%^5"yn @ @` += @ @4Id{Ov+ @F }{UG  @ @@4K^J PbozW @ԍ@n @ @\%^5"ٶFz @ @ @TJ m!P Y4 @r twbޯrө @TJ @ @ &ټ`e) @(򸫕 @(@f"h @ @J @ @ JZ @ @ h( ԟ@WWY  @h֊n @ @ hH)SvgZ2/,+I]*!@ @P.jV @ @ PYF-IcnT @ Q asF @TJ"0Z BByxu> @ *ٴ) @(қ@I_Rzb% =%KE @@f۶dqtMF|  @ @4 n$l^h @B Ybv]Ʋe>( @ 0* s2h|5.]%V @ b U+o @ @4Ή*[aժ6?+i}*#@ @@2ח '@ @5- WYavGJZ @ Pn @ @Pës,ٶϴ> @ @K;3^~ @ Pj WR RO[ @4l>H  @ @hs T M#@ @6kzA @*T@@Cf @ @ @ @zPϣ -m[KT @/P%@g @ @C0%yT .qÃ擁 @Ehm +  @ @@4XGWOg @(IҠ[KRJ @ @@= hQ(ӷׯ}[=$@ @ydml|ђե" @ Pom.|,4kYUJ @@k)ۦ-^J&@ @u. t66;W @@f!*L  @ @; h؉/ubխf @$ 7Ρtu @ԙ:pݭL[[Y:hԭR @d֯n{E'V @Ա:|]]r>Pkڅ3 @C 1NZI @0 Oa͚ 7 @ ,(p#@ @! $PYu @ HZ[+uE @[@@Cu @ @}JhS}Z- @ P PTJ쬤h  @j^;j:H @! $PD̆ F*bwM @h\lŷQ  @ @@5 hQfh8 @ZE* @H45 Pc?V!2/,+kTN @ @ @@ h1?{ @L -~ykɺ" @ P/MQ\`ҥaiӧ{ɱ,p}ժUiiӦr#kItwM:tzۡ @ԡ@OOI]꠆:d.L:]&@ @" h(l5__?ASO=5\z饣 7OLrJF]fS JЮ?ƭ[m @.Ғ3N0`Lѹ* @ @. GXI`ܧ?:82Lg a̘2F @ԋ@C2`R 0?O<\B[uCs~\â^ @b Ud6oNhS<(hnчɓ/ @V@@hKN8!̜93ͳ{2hR+>w,>۴14>PhI~J~/f/{~<#}扯}Ѽ&'wJcS|w#tpruhJٖ<1h3cp3Zt@HG'4=phش)}CήO( y{uyocW~gBS ֔Ĉu$vI&nifN?L;'wj&{M=&70e잽Ca (0e&I8u.e5|'ciur754@)cKhܲ%YǾG>{ԫTx,0?O\eIFY{!eɲ [t˘l$1I{Orғ[d{Ć IF{t߉:hnCA'/e$GØ'֮ =qIz93q!$JZҶ:<ª͏CWwr-\w'_ASu}τZꄹygCf7%Iާ\l5c~iDžgi;刂ӵwnMxo֥?a|na)f۫u* @4TޘT<I>,w,/_3!8$?~Sq" lMyWcy8$3>\lHJ$?]Z>'y{b'@_>ɰnמzZo;oKI93&Aɇ(@C2[4zb׾5? Dh\"Ɍ! l IFӒB/~:/{>I1=d֋df$ŏmc%]c~`2[vkBXZ@IDATq'옝/?Wccχ& F'+$py>\M}Eܶ2߾x"w+6?^pIᵋ9̘ wG{׶ps_M:鱕? ތøk"1P> Tڭτopϒe0} K6o0Cs]w{{NOfreCza{~_0C~aO&3W4m @0CChWljHg)H=ɷ>nL_U/{zOV&kv|bܹ+;񑶽o9>by7QX 2o,E|I"@v~%d39| ywӥ(JV @nsh&y0&yFqđIpC8,dIqf~:Yf <]iZZaWmE? [YNg:[~w8+|%0 䲛 @4T]tEO[xg /p~+_ w1h'?I8qb׬Yby15%E?&L7}>φ~- }{ӱ/| nHߞr).G ¯dSY̱w]vY5>?+Vp|?z7;Pe)i0C;.8ӳ @ "7gv0Cgͷ"]^ T@b WY@Z_ -M}m ̐_}9qQA/6 @@|X-8dӽޛ5x7_y]wq32oA3|/ 8뮻.= կ f>ooR})1ɃB*vWio:W}!ֹ%g֙mo5}% PH̚Յ,NYU*gkzQ7̆ *J-Ȋ~_jwokpm۾lnL 'wUc @U*` ?>|;/]4 }~Ogv?xwޞx≹X]bʔ)ᨣJgHزeK:kD6"FO_uU!FE yvW;Bb_}X|ǒ W_}us=ë_9l{~nrV:sݡ oSYu@m 1w  ͍ u @@ 5khƥɨAd'? ?ȯɯYO}tˎ_v( |wMoD"@h.^80n܎oo<&, 1餅 s:'?q{}. @5" Jc ٥޴`8_\O qժUYn"WP7Ν!8Yb?򖷄w?;F/o"E(@}Bۙ>|,* X`!mKfL7!XjEԁ@Odw]ZГq>9{=s*F ~l9l'.|߈ݢiqc @jO@@C)SrA 1HOb>ҫ'36s=!;dzT7{h{l~\2|O?>[@?2u&M7#ܤ4LrtصHIz鞜| 2!ޑdIڑWNO J;;6$Hi=54&Kd;-;Mf,J iJ4qC,#̲ٙ3a|螾[Z0t+$@'CgCd撆3iRҾI!QV2K\6ӑ3}3oc'nX\h|ٺ%9gl^^o/Aq|ҟ <^pXڱ!4%^u%<–ɿaaC\".#{)ĖaG-J  @`sl^ I;o+Jɞ<}>Wř{>&-{)ySxߒܫdړ/&/#9ӜzE2[BE =cDžx߲rEh|ټ)֐I{tݳdC$2iX$d6mmɽJ/H ;{KrWi0-'ِt%Lޗ{5!0}d':a†χ͡k{r}<H'?6\$#==̟qLXv}gt=ÕW^ w^8S~ť4yp7kצy1!Q-еh4 Ҿ{욳GJ~|87'ZOl^?>431ٴuְmS@o) S3(ݍS~=Ѓ7<0lDfbARއ:Of @(=uZ-ҠƦ0\{:1;g\?J,MN @T0?m~&&팯{5\;4!Xv5yMwސ2҇r f̮h @U&н~eiq꘿0l\ MY @Ԋ@&Y+m/Jeҥ&Z2ϟ.1.NYis2U-Z%J.8;Rew^PWo5-9i$KNT/Aŵ<.%˗W\AӧOOۋ ׯ7oVpɉjt 7fʔ)H4N1+0{dt6k%W Z)ɲ[+W,TdIm{hz;cƌdؖа1]JW6oJ(Иd1%'VXrW5mڴ^_lV[L`w,9gQLM'0y^3"W $${ݒ'g'[y:ʰIr Xr\P ~X2wgL (=abhZm @/ond_SX_  @ P(rPP/ tۻA^~ @ @ɬ  @ PX T txpin% @ԋ@  @ @ h( Ug}Z  @ PoS[ @D@@CIUBt=SŖF5 @ @:< @I@@C`KlL٪V1 @Q X @j^@@C(cK6 @v(}I9;7 @ @`FMzZZ&@ @@J8s] @ԁ:d]?R)_k>om @ @KF @ Px 7U" 3 @ @:'No'@ @5" FR7 Lw`rTg0yF#i"Ȉ񂍱_lî˲ I65J@(sI;Ftt?3Ւ:'c:"82NC   U<(~'vf3܉P'   $@@C. MY]iT  (P^b{J%@@@@ !b"%;0E@@ld{NU    )@@C9tdclY a   AuM9+SM   E'@@Cѝr\nwV0)+(   )24cVPwb@@@]b0~l{Q   @IIS(zJ@@@N;etO&C3Tn +@@Ҭ,×3#    r"9!⟔8t@@@@@@8 @@@2/g#!   s48gKdUkYq@@(F@@@Ț Ya0՚LPdR@@@ չ    @ Pٯ_F Cf=C@@ d":0.^8   i А& UZEd=C@@-`d    @A PЧ/s7O㊙#  @ 1CCv   A2MSdRds"h @@'rGnpp @@@p>RR0kkS+BLMB(  ,8?r۠@@@X"> Celk@@@DZ;} y@@@@ ER9/PY%9sr"  @q g :ZYc   t @ I)   ٻsS3   dD0+`MZ]]dd   t@@@> c @@@@P%2Ɓ   @ P#   PN(12@@@D"9 @@@b0։^"   +@@C;z@Np0'N@@@(F}B@@@ !=E 7<B@@p@@@h(Ȧ@np6m@@@ +AN   %@@CqoF[dX?iR2\@@@@ W    00#PI'   M اw}@@@@{hד-X3@hT  䢀o̸\}B@@@h(`,,+ÉW.q:@@@ }GcO5    ЃDaYݧ   @ 97:   @P(gq E?)P @@Ee#F"D@@@ k4dp^ 0r   s=+pnjF@@@Kٿst=ZG{N   P riM    ` B'ҲBcl   ځrqK\*E@@@ B 6@ 9@ @@Ƞ@pԨ FS    ` vRE$`+2T@@7`_@@@"h`T~2#  0+2m!   4 Ju@pb.cE@@|η_@@@ !MHR,dG@@2'`'f暣%@@@YAbn z2\Ɖ       !4@x)ⴇ      v`"u ˽o       @T0+ g0@@@@@@F9 X}7   K9J   t  ;(,! xR)  Cyp"   @APA [8(4O)  Cy|:   @> АOg"@`J\$8``ϙȁ  dY\Niy@@@" HN4,^woG>Ñz@@S Xꬺ̒RB@@@.@@Cw PXn8DRprb4   Pl٫:@@@ ݄= Ѓ0kjza@@@ މ@~I    ]&A@@@0{}:Ӂe, a    ` rR   䔀aspuR!   t {@:z 0  䐀^67 @@@bd v#PH [9 @@pR 0bm[^c9*B@@@h(p&;hSڧo.4B@@!v55vW5!   @\py5*O#{Q   PL搡]ͱ/`cE@@@ RQ &`׾3δ*A@@2)vΜf;´@@@HLĜȅ@ ﯥDy~S*@@@|KKJRjZ0NB@@@АgZA fHcjT}+wd}t@@HI+@o}=4YRB@@@hH͍R@kĘ6=w-~R#.K@@@ W>}IKz7Tiː@@@ GjA o* q}򆄟J2tM.Yft@@%`/P'M{]HuǪ    @$8 F ^v;NJ_|N1#J̪A@@ `к1b   E%@@CQn@ wųrn4 V)~ڙb   u6XZƵc ' tu-SSqM@@L 5o|C\.&O @(h( (\5R3jh:Hױ)Y@oiv"nwԼD@@2%K_xN\Ǣ6:\e櫀E~Y~\Db'  kf[_m@b X4D ޖҿ)XԳ6,^$Mw~@@@Q)%صL ow}^ѾQ9   $@@C.  xV,2̐lr'\ Qs{%[   @ZߓR̐ll eOQZo8E5@@ .,Gݻwˮ]=\fҤIr 7Q3{p @ A"䎀q萔=;mW]rD@@02e}&b+ſ}23 @@g]&TݻW{1?ZJG?v@@x<@O,B`AG MhHkk,C@@l0ᥓ^xND-CAB@MCͦUWW's53-[䮻+@ hD@@OO;Aר'hճ4@@@L}>5ݔ/v^v=T  _ndͲm۶k6@ !1'r!@[z"@@@ @;ԱҗT~2# dG`RRRnp W P$4ɉf@œevU-aAB@@-ۚp=b[]T  ʕ+]ͰJ:  y%V[[NGZ'!  QNeu-_*E)  ACIYYb^d„ l@0{J{gg8ZN^|@@(t ڳ|.8i@@.93߶T ]hKMH<+,"  @H p@hӶm[g[mT  bbQ^(| 3B B#pr^*E@@BU+Cvc[]T  ƍeǎsNٵkP#FȰad2x`qX=9Yr#hnALZ@@@իl0l @@ ~Xщ'ʷm+d@)@hXL @1@1 1"  @6?36  Pdi?._ f~@@ y=ʕ+}[<&?wr]w.ӻ+z- @R4$EfȊ@ fi@@H[ֆϡ,4  !gimmg]'$::_) Ν;-J>@4V@PO4TI   @ :堃u;i*F@H`ގ7.رcٷmf@ 1s"dYt}+Pѡک@@] Sx;#  46~|︼<***Xr"L  S%`l !ֹ֝]@@ ] ۹i=:s.tCƇ dK`ׯ_NfcݺuF o  9 (>t][79Z?#  @ slc M  "C 3,\Pjyd]N'%#FH8y@ @ >p !  䥀ys%wCݹP3  Pz)SXۼyHj:-[2#Gʓ@@ MbSS;  @n:X7U# h+RVXaaG; R9  -~gv@@r&ѫW/с?O2! 'PRRb;0}tF زe;v7i&?sABH\ĭȉ   $,`:0K%|RȈ 8$PUU%#G&9!mEb 6cE@@@ f-=˕h @@@.]j>x`Lbp'@@C<!@V$SYYbVO:#  P@f@]cd Clf5@@WE/_bغuk\N8AMfL<&jq.7w]؋9 Y* !޴  @ >(c#t\..X{4 U k֬@ sC SZ w>}b @b4$D.Ȃ{j45YV6i @@ X` hȘ6 ! @1 |k_9Z5C!Ġ_@ ^OjC\X[Uy/ /9#9@@@DDrْu-P   @|_/3f̐CQ@hH @)TQuu?;@@@L3b) f.x"Q@(˓O>)Wٳg˩*2: wN02,ڿ/-  _cp  @1%'.\(~kllyY?.K&L v0bĈb`a @h5 !@ s@@IXC>" D+D455{g7'LYV~q4h̚5 n8䓥$JBHTDȇhiL >_fHk   P hOƞb .lSF dIR̙cԽe˖Y -p+/SVV&ӧOfoKSԄ  9 2,`>?֜͸9 "  P@Fٰ^|4dԜ@@8^̜9;eڵVp^bǎaVG^{Mnw8 = г9@ ۲*M"  6 A+*Ϛw=g$  ma㭟/| }ppt !'@@Cz~F\9T3"  ,p{240A@%0|p?СC zD @ y7d@ySZ @@@AtUmmw@@ k555rWZ?MMM,73A 4٣Tch   P%  ;w~[oנLaKQ=:PYY5?;@ ߇ %WssvZOP$@@@@@Rx/g^Р<2oCl# 9rx<XŋjkkNzzq^ ,@@CF@ x֭p7g(R.}7o8[   @'Ȟ,z?E搵N0  #:!oڵk=;z}t駇64M+7M٣fiԨQr\ @ AHl 9Ϧ4DR7zMjSF  }{6`  ]a e]&555Kcdz ._\t0Cd"_ ` B 2'ڷ?s%ؒwJP36,A6@@(fC|O}x6L_@@\"BKHW;v?se„ ]K䬳 g@ q"'dHh.{);t1{s #y@@@ !=?JÇGW^yE~_Аcʱ5Sqf& p@@B0ͼ13MסHѣ 0Jjagu0Z[^ٯFDAVj I}.7 (   4I |3cǎ%]p56f!֤||en{n.nbiAuCO%nrG4c[HД ]gzbUUS0~oC=~}Q84UfeHAc2--긪Cg :#onב^cPHO_u'Gie}hj #aV]j][.[^jOoZqQML_8rX\j}ST76Z1Q7Jb*=C+.5^Z{K(g=&!@Fߘcmտ`(wEVi 4Y<2H器]%R'];," Vkf񪟞R:I3u^KZ]>S k&}ݠyԵR ֟* T[]Sk☸1u&fW~r5u#z7~N/UzhmQ1zoZ6DqQV:u֪ͥ}WSOk5R-ǻ/4zݣ M]UZ/ѠuMKLQۮzWO@l%%ʫk_+N~S3+*uc/a{=aw2ܢ:Cb{zuQg@*g{5ns~{!V4QeJj*1տ ~VgRLZX^u[zH_/'-#uY!~#  tKh)!yys]C7۬Co 9~bI!ǷԍXI@C7#Cc`fc4}RߤVFT7F~J& w ucI`H 0ĉ~9b֨M$pD`Gޓ@`ʒ2~2v%[rekJ4Ɇ+e[R)Ӫ+ewпBN>/j2蛡Kw^Ev7^GyCV!7@A=Uq@bP_G~-wou\9JV­O:*L (v+0\e[9;>D_rnjߩ%%%]x>Z-];;T c-],O$z.A:gYF\:x3e/;Aǎ/oKųnV=&8pUPWQLGb [+U *?u`V$V{.^$P3~,u}Ӧ=\zGω *;{%`e*j<*G%롍A\L ^PJd2;+k,{ta2n5BrpFal<4O7oU﫠P}LNzRKled:B9J5 *xJ%pK]$uqLS%BE  G@}>~E' [_|䥗^*wuWAV߫v\%'&LxlDQ'uh'N|ꂿ$Y"g}h:qJ_{ESIIJJE$ԈGxcz]w+ZU0^6=CݯB|5G;bw+?9hϿNNin%UccwCuCScWq5kjbz*g i(ry;s՗<.!nB=*Ds)Sˑ;W>;/+kTPgP}!o_`rz\۶J#kw~zÍQ!)x&B~9u칿gG ?q^Y",JxUEtGeWX:G)眯~KlX_X?@]=|N<+Wt $Vүli*_cNFA꫚8zD2`Py۽{"*p#Ѥ0(SflG$Q#MjF8=TRmZ b̭'j~P0CuFTpA*uE~of,?n 3D^>Z:B3Cƚ}S=|7r  ] /S駟hӑAG-sUE孷޲O~Rθ~ꩧGL2E~A= ғO>) S^{r7_(] wQyeݖpQN[1vMoh+_JxWUԧ>~F\ '~ ?iS'E!KS{o꠆YM%/耆Qf-EqQDOUi.{~QooI<\w5YrP @[7Z) }xW$h ;Z֭oϋ1\ǃ0퇞kz./{)3<ؕK5߸򴔫Գ=d~yN+OOI//v*UXf?I.1/@ ;zYPJv]6|~#$.@@CVi3$u"Zah}'.oN|}Y]_5Xup^&md~ʏ~#k-vBۺw!7t|wȇ t}ex# `7R,~Rm}A֔w6˖ҋdt+1+oh%%$zZvb֑ʁ)uKSz Ą]kYB?Noȣ34Ji5[DmoW^B"R30 D4rA)7t]w!!.7%URϊ f31\|r)HMM\¶mV/g-gqFRYZbVN =;Nc-eXEP& zKQB0_N?3VcQ Sx%zmob t }Y /4N^̕>Iw&KlzcVhueT@{њϿyZ,ɣFg5ůj#o(՗^nGUW-aKT%zY;ƅ2ut|wZ=CD$DZoT?6G'@@C7VKC ꂞA//ڵdijj;Og?Yپ}'|R.N;9^@qP]o?fevDo龵 :=_2=M}nJdӦM]Y^֧kB _u@\;RgԮ="GpR:#Рb0)@z?z捶k%KߚeyooW:eoԖ@($ɵ?BӲkB8!5sGğZ 2~J:~4W]Go7m q=oշl9S8и/։3n?ވ P,ʕ@7gٿVǏ/><3+dv.؇y4w,sƙC>繘R<`>SIrhfy9TYW??n̾#"/  8cǎ *T ~_jA4hPVBm>Z}}?i2e̚5KfϞ-F@o71lذ˗/^dx#-WW~"ۊ;w6m}gXˁ$R6KaBFY4ڲ`W5]w[9uc *S7F.1:p򶻥F_Hi7;qu^j Rb_9)P56f)U^O7rM]8L412[/;?BR!G$DrC.w=4kb9ȬuAۏ"^vBݯi#~51>cqI7j\G?7$sd3 %@@Cwi-%ܢ_MtRGșg)]tL81/:P!4ppN;Т >ߑu$\(?OՑرcrpn=?lϺ+} CN*U/M6_aH1}7 j`˹#s|߫Ȁ=k%;mtd큗V\C= ܇ Rf^NBgU6Xʂu17uT.?MFU묘mvOWd@=Ԣ?'t$/[UU)\kjj \Sg3jp _$vX9K7S1twJF ڏV1Rav>kevcė¹v~K>LAgXZuFz_~Eccc=4xPg$dH @^DN~JFw㣶~knM^ۢ6cg>}b;251={t6{M $!!ऀ_i';Kz6^ک#:ڋIVN^)sR.OA@ms\L0mF"on2U@񝪃JOA5;J@4&I0NfJm/=5~5+]ubWuIcK&:r[>BL" E# >՗\r=s+5CC)z-ٺu<6UhO%:6XM+3޾}q̝;7: T 7.HQ`l2d{cIXO6E4Tv mǭ[Ny[<#S_<ٳ4Y&%ng׆qPd@Ⱦ~?AV>W#RkYG"z6nHkS$8?oDpK.5=j;e,- .;S/#GJ`Bvfk:F߬Rxi3Wתz} w=de)Es mqTS@(htz*뮳_|,\P^{5kzZݭM6ɂ c HBb >۷/\GO)6,\g~Fa ^jJyv ]~oR$pͤ^Ym谂▅UZ6_@~rğJ4j\0{)k@N@59hVX#:Ͽ lqoX'J& ‹w |gb&?`WA7}N*m|x8šk\+Y'S:x`OYwHl |C Hw zFcnvY++&ON>1G/gZ?&}MߘSO@ =Qi,(m3T~Wx:p-rZ㏤o`SZt2y8u\9;Na:)82#*OZ(oK3t5;[oEqfLe@1clY"A3|fk,K^_?iU3Y2L-7/+*S~2'K5uڗ uz7)dVUI-s)x^VP~$8IK \U"n$"Vn9WMr&e䆩OZ 5ȕ)j/(^! E/ xk/?q[K\zkD][ȥ;#6˖-sQ."K^Z֭[u≝k W^y%f@ zwyrnY/CY[pL/-k _fH B-ȓ! HA`d;/1)NHkgc ߾r^7Åz OOLzS<3rݔ_Hui],Q뛩_rK*jv"@pT(iS2%Lumo̤)`=B\umtbjm]`]cE54h>"Z&Nz-٤r]qOlQ+^v~F͐f%)]_WDFA* brB?v45 P=ۣfhS^{mON>;$ޤ*(WO|P.w2yX5[h;^$w34Duf9z"Fn{]jo[ nWr 1SQ֡Y͛'7|ʫ=`nݏܷtRٶmBEnE&O=| _rcׯiӦԩSï]۷\ve/[?r;B=ڵڵg2{Tii.=8YXLՇğrxIaB־㯺ȺT V(7|jP7oS׊gJl$#TfuF įouS+<6@`PWy+kJrQK=fH8YepMij?#{-UzT]rQ^rjY$[HϐˤkeOI:7OpX3GV}VE׿+~3+Lj9}gw9Kxrɀ~?EAM(1rT:t#w`POE:^kO 3EhkTMd'LT_07w^F\3)]Pz u)bYђY[+^%G]{O &H$N2ԬeT}.b:'M1e}}~rũT}=/yKpxVk.1Ѩuݪ{dk^D(bH.~_D o}3DQf1uQL8nTSfbLqիfCWyťʗWWMcw)F7t;Wʸ>^w֬,D1(E)T˪`w{{ZkkWm{*.ZmmVVkUTDEEYȦ@$@u0$䜙33:,yaΙyqIS-V'؀ktV=SB9jXRU>O'jI=R6AT>Fף7t;+AqSMx(?#Pzy~ ` 0ihZ"aYujjVxRb4PA20w{K[PW4Fd9?u-{_ȀNԘ2cu6꧂2?>E=52@"BjEQK̈CY"q_=AuZ۸·#ѣm3-}_R/uB K~}o w)&GEsPOy>ł Ev/~YB*_P}/)8|^ iP}7$z~*lDTay,b=pxыNFy˘6"TĹ]*X__'uϪx5xы~栺.FßU+56|jrwꗌ%8Ӣ }mg}/tPSE:Z[3ue%~x@>[y~`E@I>DE'lD/Ng筑E&5XA-` o֦'t>}Q00,CAw>"S%   'tAQOz)z= Ĝ9sD+T7{͚5K}nUfϞmGGp'A e]֗%8q~HSN?0zʋZ?__q֭[7ߔ-[mlargؾ} # x@IDATG4yY|y)[n%RRR"Jz4_]tg͚%W%)hBǶ}M7 )m]'4L)//o_g 'LLY`+&!/T  mjtMzjtHt  @1:GOQ_j%!$&XVr)M͉x1G5|KGؼy)1$ÇN8Ac#@|'z^οhfuty۶m3հoғׁ z9O*vHҠANzŸ^gfܫRb69G[!N@_}]tH]C"ZZZ ƣ*GڧJaj=eVg3=UcY Qtp)nNVWAIJ~~峃W Q`= n7ˁr,Qos:?PѠtl~mJNzFueA=hou4WNq36H/n롯 ){nR'']ƍ7mS*/m:餓Q9R#4 2D2q>@W7B_*0\3^^_xt^z(SMd`SJ_9=Hk@@@ z4 h0wp^uoQ#SJ  @: :xݺu/\] @$@^7Բ!wJo` C[@@@PnKfH G{@@z~Qmh E\ ځao)Ei @@@ @ph裏Wj E[Ye# Y-ᑶdѢEƈ YB@hH, \S[at d4]#5*ÿ3*C'v#  @gwtOHl: d@oct>K@ft^  Exbh`1"yy},@@@ c@~4"j  @g7waeΝF ˖-f}(S@ !s-=C m`J;}fJr@@Hguu:VZ*-[!۱>:Mt%;{@@{Rw}W.r;c. GQtj ~~=@@\Tj^{Zq\&Ί=hm+aGIxt @@@4rTH ɳ&HT@=d#!  ,Tإ"c2{*qԉ dg 6,)P  n4 `@岴| G@@!'%R)  7|sv~!i!LVH@ IAT   @F 姤{QSR/"   ` VR&@iv  6IIcz@@@+hB2@ m'}!mN@@@    14d̥#d@0E  JQT  M N=op6   6 F @UOR4,lrzH-   Ke)D@@H )bJ vuغcN,ZK      $.@~VD$ inJbT  ,pL.8vq!+" X"dϞ=k.illfcqρ/R\\,%%%@z&@@CϼȍI/޽Ij@@@ toA >^@@***db ٶm|ց &L)S/K<2!.@@Cl*8Xm:  $ V]5:F@@7555#/ܛӍ>#ŋereIA$ZSJ 4bDۛ`5*D@@TI5p\iR2@@ [*++媫+U bKHM;*mmmR__f!~xeڵrKQQQbه hl)xm.  U T<ԮM]  @ 蠄 F3i&'OÇˠAt_إ"v%k֬+W:-[k~Y;@, !>PlUGIbmT  /z-/)O%.*A@I͛7]Qϐof>}zzjDbL6Mϟ/,ZA:,]TΝpdDI vX6 W@@p@,@@@ԐIMa9 @@\7|(#5$!VKHg},X <Ò%K$b} hUM `D*f=JpHP  dy  R__ot`֬Y2uT:#f̘aWWW'UUUMA @& АIW a$]: ;   RW˕ @[{:eʔY+&M  !t {@&$` @@oNҺ,@pWsssf'Skkk*?@"* `'%bH*8!  aÒCI@@l5jTWvpQC  pD#!OLnTr;Fm   Cs"'n)$@@J`p8>/[LonZdժUFy2fʦ @ hȤI_0PP %OF=nwj@@G nSN$ŚJ@@ (f2: 7paÆ rM73:묳$''yӕ$QoMU    EY'yuQ  E͓zKڤZϟo9̘1C&O,:!tѣ2VX>T6+ 4D{   "rؔº($Q! E`ذar]w-" W_5].bСҿz Q[[+ҩz;C:c !M@[ [Fnٺ4@@ ǚMk T@@C@p}g$;v[Mׂ @ 1k$5_@@@jPF#   @ .@^9a7)gq   /Xd~  @ ={Ȯ]QpHaaKAAKII$@ 3/r#@ %@@x"2A@@EE,_\VX!۶mϗPt`Ä dʔ)rK~:L @ А@@@Wͣx@IFyy{l=G}d,/ٳge]&yy+PNB7k.5E@@@Li]?  *++媫+UbK(VikkzۑIOMOڵko"F݊a ! @@@@ }'Oci)  :(a…Q riɓe2H:Θ"v%k֬+W:-[k~Y;@, !>   @hm<-G@@͛7-ɑoYOp #F0iӦ_E#8@Kܹs. @6 &Y/=Ws  f x@ h_@@@@2ײu!5O3 @@ 7 5kL:մt`Č3ꤪʴ)$2j2T pX35 @@D Ǻ=\?SNXr(@P[n ;L2%nʤIE'H hld N{@@ ~vF YLB@F1\pnnnxݬ=D{jmmm_' @;YE$bdB#   8R  @QF^:nڵkE 2$  X6 eA|P@@b)u@HsѣG˖-۷֣2YjQ^~~3ƴ)$2j2T 0ڀРA*G@@@ N+8~@@ (f2 7p6lnI|>QYg%999}.@L5L *Ye fY  -_*w޶!tYR." 7o[&2|#aƌ2ydAAbŊ)rWYA !ڃ-@pXK[wS8  dK |; rJ  Æ Kn`0(|zCJK(2 tDmmTVVJKKKdƺf;N؁ pH~@zjâ ,*b@@@ \F[Fl|5@@4#1r},D6Ivu-3gΔ+B m~2 ,@@C6_}@3ht   naU)0X\@@%%%h"ٰa<쳲n:鐫b Γ3s@`@,j&   b -)O/wnj噜  ['^tڷolܸQ/zFcrss@F-zz#GԇI  А @ |K!>U#  @Te)h F@^`ذa uYgK     Bo9>   А"   @ T#FfZ  V@@#v2hnn8),,|SNKII?:Hc@`@@@@@ 4zt/@@˗ˊ+d۶m~P6L0AL"ׯ?@ !   }PP  @jjjG_~Whll>X/^,gϖ.LzU'!"@@C\i1Bn Ƅa'  'pp0$@@TVVUW]%:!Vrz k,PHZ[[Mŵ֋ގLzZ~Z֮]+~Ef@ Bo"0XE  h AѾ!  ! NjS{1< C@-.\PZZ*vLpYgIzMB,@@Cg  ^߱ljv%C@@ p "^^nj/|ND@y'VWW宻+VHMMM8p@x Yp\s5l[ZZ*W^ye6B_2D 08i2]g94Wb/Xfr!  & 4CwΛdQk,*PuѢ9rA)Ĵ HTDȇ#ae(^@@$TBB@HaÆ^H  f-%#       @/h%!       u4XgK        Kz i        ` R2       R^q       X'@@u        p        `-%#       @/h%!       u4XgK        Kz i        ` R2       R^q       X'@@u        p        `-%#       @/h%!       u4XgK/wh=      ;m[N@\Vyyq]#6q.=~ /oֿ\Ki   @ ڱ]ܟl]hiPN@ M!kh7   ) !嗀 zgχA -ҁ }5QKsw,#  Y'Z_&?'}WA{"_+Yg ؃   @4tQ2[@M)⪫K:!ϋӭ&t@@@P2OQ2tMRNEd@@@@% w&Τy6m%iO@@@ kĂ"D[H"    НPOQ!gZq%FRه  @γ;{о}R$@@@H\ĭȉ@ĻzuGjT'#  "ਪ?M}+@@@"bU1s3b  d@3OrkD@@@0[E)4V<Ĵ(@@'rsj?Gm-I@@@ v"B57< C@@&ھ͔thyVڭ@@@[ `BN~uS2   O6+捦Ga   d zeqoHv뷋w's   n5Ce   6 & L'^wL/@@@8mJkQ   *@@C^X@\z{ukoO<@@@ "mtGa   d zeIpߟڨ @@H@ R̮24DC@@@ hȸKJH9R#   `@?g @@@HR.`J) @@@PyVcβ)@@@L !S$@ Dr@@zOZkhl_'    ! HJ@@@|/4D["XE@@@X4Ra*mu=+@@@$ 췴FrK˧p@@@2ALP;V$ !  6,o:@@@t !ݯ GzgY4L   `WGܶ29+l@@@2BtueE.58S@@@ x}~E   d Yxr 8,k.ʦ`@@@ ΪdVG]    G80F #B!˺hnl F@@&-wꇅAQ   @ Аf#`7痵[i  dco$Fؓ @@@h0 b@@@@ +IZ'\nMZ]T   @: АW6#`W`Ю-]   @BgF&gMP   +@@C^Z:@r!Y[[[)!  & $3Q}S   %@@Cf]Oz@J)@@@> B}."UUf%   Y)@@CV^v:5޳`JE@@!'p:+    Y&A 3ԃ9cZ {ں)@@V"{"7YG@@@4taLp46X5gcuP   `s^\@@@ @Rf[=@@jIr%N*C@@@ hHE[胀s>ͩ   @ v}N:^'"   .4˕QU%p:   ڤw0&N*D@@@ ]hH+E;裀c>zTMg$   `CߟV ^'"   .4˕Qy`KHt+/'\   BYU:@@@t !]Dκ>=H.@@@N)fw]m'ڂ   h1X( ZXxD)zV@@@ 8+F/ɹgW>@@@FT4 $Ծ@@@d 8fgEEH=    iwh0H֛FF=>_Y   @j3M_9[[;b@@@ /b~  X(ٺһ(:Yv!   `Wzehi,Y#  @6 pW>#  ]_!ڇ@ 7 [M@@@ 4ן#  S{^ZuSkϞnrp@@^i-A@@@4@8*%ᜣ7׊."  1UK0@@@ ` C0SN3,lꖈ    `#)krԤ~*F@@@46LpmjRI  dgvʹO   U^څ>54B@@2K SN**2    ` &AR vp45ڹy @@H1LܳS9   hši%**rjKN@@@ g诣.bU@@@h ] `CykiQ   ν{3{ @@@^ k:NDr~K{+#!  7А6@@@(ch{D\=@@@p/%)@@~ܹSv!2zhcdFccݻeذar1HAAIS /  ] tSCzE@@pNH#  JP($/̜9bЁf͒N;MJJJ㏗;SZZZf\RN?-WTTw!{ĉsΑN:>|̛7OoߞpdD Sh+K8,tKJ6Z   ڣmV   :=3gXB1۴ixF5kHuu;E)ttĭjHtMgccڶyfcГT[[+gϖnM$'o`(@@C6^uUl%,@@@^ vf=78A@@( zZ={D}!Aʠ}Xd|Czct D݆B_6f/)˖-)瞓SF{gg?Y>6wt~"N ClnV~  6pڢk7l  @蠄pw=,ZH>2vX7)]t|[ߒ.ByǤ({РAkF~_yrXA !2}^kO@@@ !P0lgrV*@@@#6lz?S0Cd?_VX!.+rw@@~D ߣ"3 t`g[/^ @@C6\ew߶,l   `7Gm-Hp][4F   ?`t'/o9i$wpo޽;_+Ox;ފ햟Qx≨m6ȆLV lw=sZ ~[F   κD$@@^矏D`Dҭ*_K.3w\SٴiS/Nferꩧ[oMNQyEmw:U~!L9W>!{3݄j|@@@ ]  %e˖F!^7j_wcǎ.q|QJJJ@hڇ۫zTa/Z  >   $A`޽Q3&j;>l [62 %@@Cd{6[uF)Em.  EU.M  d@ǀ=DOS"AQź\.ѣA@ q"'i%_[rI"MMvkA@@R/ H   `@nnnTu^Qźnq:z6 pws :Т[%4xeS   #VS0n_ИںYPn>ߗKu   Y`Q;wF"i)tJJHA%@@d5#WC|-sI]/B@@ SlfToC-O/*@IDATk**fuwL,l#  @w\T?欮ŏ@@7n\pZJݓcǎ{Qt@COң>*W_}s=sɞ={zr:yH{bi])NC@@; 8E0xG;#0|}?pH pphjRG  <*y„ QGmwQVV&~WY:4\2|,F:eÆ F2Ft# lٚ~0WĩFm !  V AkowN3<}d Yqd 8wuuPCCi止4@@ 耀tOJA5DUG[x> yO=!w.7_Oĸ_TH  dQt Tx饗{|_~?>NxvW+s]@6 АMWfr_jӾt@@+hloLlS=0}%ӣjD_7Z( @@p\p¨F{rEImmm~TSvm2{NǺڡ瞨,/qgz(pȼy۬ -lEGj4x`)))1.oRYYiHƎIƷ]c5jL={tC@@&МN=EpMM2mک6"wZ[Dz{Z%@@ܹsEO|p.]*w~2m4ׯZJ}]ٲeK80Ht qgkfߢ#7rWˤI#/ up]zg?|+_ ŝj6t}|'7 Wj}iw3<#zIqfIqq >1 ÌZp zڦH   y+1}]K'H$~/l8twd\0Q|3ϔQwOݕ@@2K@%tZhO]=B^b믿^6m$/BpwSA̙#|IG}TcQڵKW9#ȡ}d t!!xwȀLz^*('Hd%#šGJq(E)\N&u   Z\mׁMEphϡ:dG-~%\>⨯ DN>.0j+R ,Zqn&F| "  @2/ozr}lTXXhLo|:(t1G}$7xQ;ׯ_tLk  !yK/zp6! M=r}"$~q<]2R_rDrs$XqC⬯EZEL=<E~UѿCPPt8>qa^.8mQu|pp !`}>qTOcݫ?4bCjȬzT;T~gUd՛W|UuIPO%n?=^Uo`X/Ί -.5FH(aGuhA;U[5Qv:D]4Cꇨפ|a(}-G%AW?%!ML/փRR!ZuY)]5JC~S S%30o*?_QrTt:woj$-:ep89dq:\Oeoz+ɐ2D$.3ԤݱGM{J)9 '#@z RgD\ ^;ҾGfˌ=` +*qԨu}<ŢTu]C!Ǝ1%Ŝ< )ԯ*qi13yr>= c,)tyOb|fu}^uP%Fu_ÿ!~ m&)^)?آa)=B_\6KZZnw ?F7-+רgRUH `B_2P?@ :h{h9Ssϕ3N>="2KskC)$h;XN3N9Xه@А5 440hEv%/ qhJӿJF~zu }{{xu/b ߔmY"*" ɵt0aj3rtι=k?߻U[#/XP(_3gy*@ C)WY{z˖/vwd0h1=pZ<ywYm! #]=$+ٽD[+:s;s6 /{ɣ./RL˖[DOW헥Ʃ@ /"9deD +>'fC;~A7AwU4n\z/+oo<%~uӭ"z*oBaj`ǬduT~- QƲnbןŧD՟?)2jYIu^N CeeثPYߺOVlWݗ-N2nr?'*}dbZQ u4T;[dF˽=??EAR~&R/%k >SYnl޼U㏗N8AT. TL; %77Jiͪs9*5obqo=hj[9In5ns[llPZ?%~l%p@QFBܧvTP+D0|S#i[ᵭ+˶ޡNJO]j?"'?h9v|]Ct{m~ ZZ]r122!: Yd@Ka]3 _CؙS$ `k6<9=fI}D}6{U0Cd-j:/5۟#wwnLnA/n>z'[dzOA/7T_`|\mS"v<ߎ=,ڄ@X@n@!8abxW˹8W*"8X|~}WO%SҤ K9<$]BQ[CՐ]5T R^̪/^9_;_\*C 3?~ !u9^9>ISŻfG!,l(ǩ~H~c57Ƒg6w}y; gȼ/>/ŅG1[1M dPX9zݞ_ ҁŅԒu}dM=SyR񏠌c7У"-[{ $7tSD{o38ﶢN` !a3?Ɨ쑹 &]t\r%]1뮻䥗^2Nկ~e|Qg}.NI\}ry7{9yESԛűy晱GO=XB7 SL6iҤzG 7 mjOt}3BWrJiPoaG&9CٿkrmEb{ /7ye6vgj}ixO;䔄UEHJomg{="C`<7i7 e跦L;߶CkrԿ&|N:fQUp.Yr.?d@R#TLi+પ;+7̴ G;=}.6>oEuʓҦJ"Su:Q#o鍕ا?#\۴_aJay/-UszU~?n"}8&OU>(wtxǍ@w"7YGrL@xN x=C=$7x'T5c0>wH zD 7=rUWo3Afvo.'?I8A$hX#IF?_}N ~#~ m;H>64O/:得ыj9.M&zds#-ا~j|7v嗇G\^x~"`et4̙3Xֱkߖ?Шw1FWxG]OOD̠Shmm5haAG렇9ztgu?:(@_QQ!:w|i:dȐN~߅*=\c!=Ԏ~BDI3rH5kVDV֭[g̠gѢEҿNkߟF}:ޓkJ mr뭷{3g;d mIKD8,&Dn}>ƥFfpvυF.['̉#^.jľvۺ9x{!ޡ޿2f㿪kL+kߊR p둶Rf굇SiW;}.0A`ʗe;8]ZУnjcL^SScOII<m&\@  qi?KOWG-p: |1uT#"KGr/u>+W.+(+;|饗}С/-S/)&trF@ c⋍=NzZOv)=XxXp.SN1su:=cqx<}Y~iĉrwKa}IUf{g]RtPQD4bF k4XbD,ILycPQ5`T,ĈPwm3½Yfwgw3-swv}NqO>io߿eB=2ds: ưu Ú~s΢-zkDze5СCEč7)T|'St@4CAkwU}:zW>#ixrȺ;Kس[ZB mښƗ`ի$0M56Ҥjڙ jB\糘bd@V[?5vY ηƎ7~/si<m5eU+$aM#vnzC"/?,=A(Ga~'ojLE|IJL̆}w{}70#jeMp16ޡS {Ѝ#)p1ǘזuJǣSz-$v#p{pڊVF%\sĸKe̘1{o¡ꪫt7fU ̚5>DS f*wqfk}ɒ%֢0.i6.. f*)SHJ: foY4PAK~@  _A4CA:uanprDaG}`ó27s6x;vj;޳uۻHO`Fp4 Mm,;= W3<ǸÞ@C<66PC 4P.ebVɮ5U۾aǮ5S!lF &Jhk馘8ezmiMo IIף42̞=`'H A;_&MTkKP33x :NSSV4MME,hȯ~5U ~{.h{*\p5 U94CfPC ۷֭[ zҶm[Ѵ5 -?c+V0ɖ-[f7"EV4*fYx@aN hh;0~} P%#]VT5#w^}Fz׆O֌hՃ?Q}ơR3=X|_ۘN?T7Lzդw_%8I+'tR0AT6!=UYE}<^ {;vo_l47etD]{yG%!'Pݹ>P۸ݧe7?v8x{ϟBNg4щh7v 0W vH4xA/ڵcv|J˖-Lac=똪[HҥK]3肳@ f/kpCV =,Z۹X 07x"h ء,3/goICg-%vH=Hq474.n>Jlz;0ZdEsB94.>> ;B h=d_Y53B}F?8?:( ] Ni4?Inxzw+8~33A8.5Nʄ7z[zѩ\Fv+ons%z%h3p+A @ ָQ5 8uY hL=qM7o!/>"ASݻ 0ƦwX1;N8:N9Q]k%%%w~inǎ~:H̜9Ӝz_-ZKnΝ4j',wyhNנE37hg~vVtY&M^m5s>}%/9O<6SJweRյ)6a۶m[6KnB߾}宻2>cd̘12g4 6 pb.oޤ:Enw"4 E [sr/ӟ'}-C12~ۭ`,U}]uh{:+SH-pKswWWڏ%)` `ʉ [.V\ҷ~+?z53;v[>3B $K_9뮓{nBAA`m͌SO<~p4hv =uIIpe42Tt3zLD(] FӏP[wVN>E\[xoq _f˾#79sHXq%z@f}Q5ީ TxWO?^[dҥv.] Yw2lR4 BmEth Jc.wyǹg}&uBh6cf]red1eF*TIm#'=ZD s%>bxMrCq?mR)ȗyvGȪRqKFBtNOrdDn3Ѡ  $h}=Cڮs77|) {vK†Maӎ/iV*;^^(ĥ@~fYb^do&^r0WYR!on=x=x/xO(]e-ffPHYo>ĝ.w7>/瀥RnC?dv.'I^ﶝ..`~6ٲoU$NF'J q,٩R\M.M=ߥ)Rw<٩Br: Y]t؋SGh 74[x%|XUU%ӟdv\hhl֭Vݼydȑ6_yם ΍_~d{nsʉo]{zf:uڵܤ"jo6;7.&ZSO<3fpUW]%}۷Oʌ r9b矛 "==>&p~5y4jrscdfrfe\ 3#1I]T,wX&.Hb\:xwލj\qg^deK^q1\l4P1~<51V#ZHZh!$`|Y2x߅{)5+F(J`3Poݭ𻟍İսXwy[{zzO裶`,tl>|T{15bC uz~s"  X4e|Q_?xrs@o[v~22iptUReB2Rnv)!UT:uhvJC˦MC#4ƢA^3A(ǫ{d@ zZf}ԧtnd:^Cꬫh> ͌l_V8+ (7M,93@h !D3N_ x'E/2D:w,˗/7 -[f2n8ڵЅ; 4 >i-4ˁ^$c\]jy_-U4 ?<&Bo8S @۷u47d4`/~ `} cfiGyܴzjsSO5Զm[ *?3m~΅:YC3MkxI'E]rWKe.:j~ 5oqW<>֢*ڏ8.@@"#]FEẠӨUhd3.I%PnQ}ܘ,f#( aqi(L?xgyq#B@wk^zɧ4cBO#E{Cxt}Ω't*\.t#:]ƿ1.kP.+g}ݟ;2QTډmEObyO?1*C@@$ӎ%lРcAĥ76$S]ݥ] ӭ>(   AFAh(,w}\r%N d}Q3!S;XcӧˠA6S+s1f戻K; u5_BC@hЂ2zh1E}4(#H1E-~3hQ^W3I+]K^}Uu- h߾ϮkJ"頁]!7  4aEM>5Y3e} fmslϥRa z:U@@@P C/'mߛwkLPsL ׯ7Ys BcJݧ?6l0(++"3KDl۶͜Rcmڴ=_>EPh f94SpԧjF}V%2"3a:pqgQuϜSX{$Jd41mE9 vC 8_ Ȣ彃D] JkDz5{ћgfW2<-[5 4 E5XvH7;v+ڬ~ǦEo)k)y333롯 %%r'V`K,Vyvϱ" [L9K:itg61`v.ڵkg>p#+((}4^z5;kwIW}Tٵ[fu@@@ 4]i8ZgP -Sʘϸ!#+@@@/@@C ipw& e\1H+Ε!  @F8`c5o\Iq$#K7u0C.F#    "NXSp0e~ ze\RyؠM   @uwǢbfkL_ :hq.    ДhhJ&!o'2[\ukB@@8pw<K1.%%_=N@@ ڵqpb+D>iQ~g~ ׶1t!P3.   x'rr$J\J}IN٩3ěs@@@b@x"MI@Gʱ5\@@@ i3Jdhбf#s]Lv6o   @ D]@Ö-[dʕsN$~@b] ,`'`  HLlz`-F=O˴   @ DE@׸`Μ9SOo-#c0KA ^ƭ=Ԙӵ[Ԍ   $Ȉ0@@@".0vXY`A1MI$?s攼 3Dͫ@@@@ ܝD   HXχ:>묳f@MRҘ!rx 3'  Kݭ{@@@@D34kG,={<Ζn S=RhcA@@ \"Q0eWs?   8"0k,Xe|0uT/)>XA Tv()?!8jJ   @I⪪l\#::33X-   MB +Q2+rw`@zyTL;wy[lp4   oFtP#S@@@|"аgYr=N8A?|{@VV @@@ xZ2*^   ] ho)*eԨQ" Dr7={@@@73:jWa@@@C b >/ G]4ԈV*>Gs(   :O|fҳ:oN@@@Q4mևl֭> @p ř\   @ 0D8 ` vo oW`   D7''b#j?b'N    @@CHTA RRz Gg   @c<-[51cX@@@I SN4@< t\@@@ G\.#UB8s:G@@ TVk"q-Z(DJK4iB) _U  Tu=   P?wn$Q  CIv:(~YL/{xN?C$3zl@ 2ڵkeҥ?˖-DiѢl| 6L?xIH`x|[S%@@@jUO>5   M[햄YHy.:뿟kj֭< 4e hxk1/,,s=WnСC PB(`&hMK a4  _Ӯm EOjj  ,h0]۷ISDrsb~YonOo\w9KKK?+VH޽o߾Qs.//aᅲ2ӧK1IM|رQGDMjM6_,G8AKJJ'믿/F ^!)!'@@7Yp 5ovN5  (˜f" ͵g$>?߮vuIRRx7s?)f͚ȑ#e2tP13/_s:%رc[n?pBs޽{eҤI~;P|n|oݺ\tEfzXf{:SO=e.>S^ToW׃5W^yE4cU4ŕW^i^7˼ỳ_V^-% SO|P| ӧ):%\"K,͛q WOV$!╘F@@XЩSuT@@kֵrHiH z?,_9 K~x'M\/ଳrJ[M'tЪU+{۸qD3W2t,=\~v= fuZ^xAڵkgozhΊ&@@@H&ZXXhf,ڴic.++uA [ErZ5>gawaot,4v˖-rѣG5-uѮ[T?իW˞={샜?4z.hb/O>*ĵ7;'ϟG@@feU%u>ofAm@@@ z-t[PH1rLzrzӼjqqMPB׮]6썎ƎqvkYYY駟-8g'pQ1Mq_lgG=Vu&bJ]GS- dIi/g  a)$7ͻH  Dg/eK=2oТeۉڷo_O:jh\-S/]Zk1:'Ǐtv=g8Y8СCGkng2 Ľ@Fe7J   @=zx1(   tq1g8Fq)))֭[ܖNOax#;5kf}78ϳzNe…|!c/kAb͛[<#@z˿0]   @ x-4<\a8T@@C|=^\?n{3jtom>M6u-lܸѮהY AZpOGzSUkX@PxM?ot@7|3oJ}pF =m!  Sr  M[sZmmЉz.HM/еkW{G}2!838Ƃ۶mrgTĦPڞ=_|d޽}?}YA x['#u@@@ iFw} YS .r5Hyy݌FL8^gB/`fh}7  TݳWHw6)d   ăo?\sTp'_)*#}oq"FJ=D}aر~u@I_כ۴icjA3,߿_N;4Yh}]sРAqАdADꫯiӦ… !ٳG4zFçSGIjj]V֬Y#v}B3#_sX,#tp8MHi@@7;[BЙh윰!  @ t.6?Dbn8zM7$̱."2dd y7oOCYN*^{ŋͱ]ve2x`)..6o뭷xo%f̘ʡE}Ꟙ(eQ(_~`)j*qjts9WJ*"s(N!< $!  fek߾7@@ |Pڵk'vycMG9>}yC}Cg}޶m9_Wчnݺ7":儥ܲeK7o\yv:k_ FヒtXQ'RRB.i@@@쎾@@@)22kSNdffmqĈ2sL3fw?~yM[G^o~F`(9k}a4ZfΜ9|Gb\ԩ{2eСCuف@$+qgO'ģa}5_e1ǓKGhsz3IܡDN@_}]Dn l 4o':Lv܉NCA֭lٲEnwGCK{4YVZIBBl߾]***";8=˸^SJZk{彃~V;8<BuM%;C7e5V3̥W@35Z(Z,QЛ~QfDVر^g!2f[n5}VEfj \C_JdD^-JbXE?-YZ!Я_?*k. jͭeolzy…ג5A6n(:ď?hf D?S D|ʉ/HOۂzm!@ ^=lF@@bWle>}'Ub   N۵CgKč'77nΕE@@pw. .zf=@@@&(@@C|Q9%jU_N@@@ L& ÄM7   Tٔ钙iwa/[ XyF t(fuP;rtPۣ1@@@ E*y:hJօAk@@@hpw,mXނEC   4ehʯ.熀䣏M   5 iWk@@@ n#j ФRSz>䠶Gc   @ vNRշ_@@@ MA Y ;꿹l   @ x33œ{SR])(m   M] n*۷oo@'\%1}|4  D@ 2NIc@#   "u'۶mqVI&IvvlR $??_ YreDZ $ >ZƖAEr  1 \,U]7-rƵ   đ@4̞=[.yyy} l"#Fӧ>Xeǎ2m4ٳ̘13"5*K')I_KB@@IWYnN*-]w\ ;@@@C4x<eܸqKUUL ϗ2UVVʄ ^;@@B);o|po40!  @| SEn׾^*h!<& :   Ļ@ny}^SGL2E>#zV^p N[8!U Px23e] !  MG 1QJA{xSSk?/#]H#Y{]"    hXz<G *%%gfqxg|u pسg|2`{Ν;eԩ:  PKWw߃RZ5x/N?C}@A@@Yj1eJ6sݢT()OERǝi2xy4V߿̜9Swnm2?)))[oI޽mÇO>D=XYhm֬Y#𥁏+ N12$_'I_}) [5jՀ/`6#  +X$e_q(    p~;w=ۛA ~~7zp' ֎ {37mذA+4hUgKkӾT        IM9}vYx}vwyǮ gyϺ,dkg@@@@@@@!4lܸчIW/_.+W5vXuk%HئMkU6ol/       #,;vtճ3I MwﶗY@@@@@@@@X@Qr>ʜ9sEy̘1>W6l`o*,,Y@@@@@@@@X@CqqRIIϺ˼y|4݄VڿlٲŮߡC{@@@@@@@4kNRSSmo^{=)--V%))IFeW_x|64O%V@@@@@@@@ "А ݺuAqqNCSPL2ޯ Gmݻeԩ֪IQQ       Ď@hɶg}&&Lkʚ5kꫯ,]vs^^d}Ѳa{e       #рI&Im3fY:u$ӦMB۶m嬳/åO>l2{_rrz:         [ h̔~ZRRRTlN#8|EpnMڷo2       Đ@DΓsJnn_6Ͷgq=zlKJJzJ>        @l $EpG! .ٳg˼y?:t\wuҥKô4ᤓNo]F.@@@@@@@@ v"At|׿6m}ْaC@@@@@@@(8iɓ'7PA@@@@@@@(H14@@@@@@@Shغul߾=N_:N@@@@@@@+u m۶(j*4idggK˖-Eϗ+BV\Yq@@@@@@@@ٳg%//O}Y[l#FEc6mSf̘am@@@@@@@bT )x<2ay˴PUU%_ޮW}lv˥^Z}7        #p-3)SG}T'ni+l         Q4^Zx#RRR|igyg[N={ȇ~( ܹSNj       %р'|Rt_~ywM| %%%>z-9$;;[.|Ǯ3k,l @@@@@@@=4̝;k߾н{w{oX'({ٖ!smÆ W_,        @$Ej۷oŋvmi;gl83eݞ(nܶd4h@@@@@@@ 5-[h׮Gn0!G֭['˗/Ri۶K#|6>b6nsF:m/ʕ+}v;gZIJJ6mXyf{@@@@@@@ L2EFi>&½Ǣ\?J˖-HN>d,[J3 ~ h ұcG窽\=;8(((e2        Ěf۷W\#O?tI~:)'^s4>9sl3fz 6؛ e@@@@@@@%O~Yl9l3ΐaÆIbb52Y`1BΝ3A,oF,\(%%%3>M1ox{W tyWOrG}dnۻw|wr-K/d׉先4kNRSSE|7ҫW/{OJKKmm2j({ /೩i,|*@VDCq\fH1EKƠ***=wD{w$2J N8u"l7KII  ? Ej<%>%Z;~&r*$"IKK@y?Ht`X{T{~DG{ koF|5ywT]VvX/"~9]{3 ܹS9m4`k, =YOjʪ hHHHnݺɢEL\."?veʔ)>Gmݻeԩ֪Ң"{"%ojyc)}E ?#~GE@^)R9SFh_=AJZt|<-L8%iݭct׿f]ТzzMeСr3h3<#wygMcf{B$G:yd>L&L k׮5kW_-Y.2窽d9eÆ 6m;++^g@@@@@@@ q(]#?=G(qneo7s +Sfm(Touq }{g!4 VyE3>zC|v&M͛۽̘1̪ЩS'tҶm[9묳_MѧOYlOSz:        @Y?+ Ƶ|튛m۷o7o3#g2dΖA'|RkfN jMR{=FH4vltnӠ ؛N(       @V 9P+D}{?8|:z뭒i_SR ZpP[@|_YYiկ_?0`TfwPm]j8"      4\ )!W;2mUk:V-[&Fm/״#*K,K.Ğ~/\pN71qD. |:q83Mh6@s9㣭^R 9;v퇦{ĚCk>}8c@@@@@@@~f'u`]P<((D{#zm*YYY駟Z>qaǍ'?M/ 4m>qd0a fg+n/++ug] u %%%<'ohBr 7 ~5JN:K.^u@@@@@@@ G1nv`=q |ڵ~;v6m$z^W4kU.rk~N Ν;9ςz`24X|^xrzTf֬Y{n?)j       @#2:ʂ5f2khr>}{] ӟT~ߙ_z%3f<|YzܪU+9Sh"c;3c&JeehF6mM5>m˱)'6l`@_|).. O78*3g@@@@@@@ 'SVlPvX^aI78/o۶M>;)&Nh4Mz9--MM\z饒tePkSSSEg:Т hغuٳnccu!SN\%`eժU?j9sHB35,Z؁      Z =%W.=%NmUNyisVA%%%yNg޽MУG:t}dܹRUU%?~M7;C=6{4D~\ӂfرcMUcf{DDVi֬|#HN͵>r)/z:ɔ)Su@@@@@@@H*)j~Bz]cHOΕ M]j߽{wq\y=rM n[hfZܹZzey|:4V9{ӛoi/״3bĈ4SSybW,/O_]v6@@@@@@@ dK. בS4$#$7Eef^-Z3Cľn:k{NVX!zj6lzFQ>##\}뭷>U[jx,X@t5^f̘aYBX@@93ql {}fiyD(       @4*पƏ?S]zY)*EEE(5ƿ-%֨۫&M""b׈ Ă*ei,e23;;;uws˹|g{sy׬ :u$|4hpϤ [( z^͛7E       @Qt~˓*yܹ!C]o@~7H6noaƨU[Əo:;#Æ r K.^-],W$@@@@@@@>}lذ"##o|PcƌF5C>}3P_}K.kF I?_x„ czݦ8<Gzj=zU%K< ?.lܸQFam޲e >>2m#KXB{zD=C[Lrw^\њwyIDZ@-h39P}së:rymc%¿֨?疙7bJD#߈gPd#ozb#(n~oCR}!gL;b~&-)W}Hd]D=-HtF6>Bdo}t}7Mj-Q?ٳF z)me"Р#DF%֭3q1c y o:D8Su@uuEzzc^X t:| ߓh] FtԨ31777:Վ[ҟ[f=)rBA=#\H{kر$%%Y16ݻ:${ )r)))l5 A=N? o5˥Zg!zQn݌GYf:??++0ozv\*-        ^4x㍒jxܹ2ydζ5#袋d֬Y^YNuV@@@@@@@@ vu6agΜ)vUŋeȑr饗I'$>sA1ZJkyg xiԩS"       @ D4A[z뭲vZ׿e /`X\8w޷o_Y`       Ğ@D0fϞ-s$$$|m,A'H<        Q'=lڴI.fKΝ+_~ 2s       D@ħ0`,ZH>33glݺ9pg6:^SP|͒╇@@@@@@@] h0'M$LF`C^^ 8fp:fyE@@@@@@@VDtʉիWԩSe…RUU`7dر2yd//O/_;       ȑ0cSOUܹsYA@@@@@@@)rI'YO?\}RZZjmc@@@@@@@ڟ@DO?[oU/ȑ#뮓zJ}Znw~I@@@@@@@hHvm۶M3f,_X޵k n !       @ D4wߕe˖Ş-F@@@@@@@ Dtʉ@@@@@@@@ fhٷ#       v":Ē%Kv]]z       -h@[A        z15        @3hh&@@@@@@@@ 4ޘ@@@@@@@@f[r/d۶m~pH=gϞ2p@9%!!!lm"@@@@@@@@T4n?F0Ceee )))r9ȥ^*~yى       #5SN,_\N>dK䣏>4qii2m4@z~+.]}s  DD ^Tߺco8^$'U_*#FF@@@ڳ@D***`*=p8r˽+Æ [ظq<ƨ f`CYY1B͛%--alC˕[MMu'O<&U_(5'd~2   !p%gđPvuΓ?SzC&@@@ڋ=}Vh 6l9s4̠>|̛7O֯_/ ͕_|ZgbKP03S;$OM"  ahN08|fRWS@@@@@Dy%;СCm.}o.L6Q"I#39EO?-8C@@@ /V#௦%-;6@@@ښ@[n<y9.s1OXe +WY@Hxf+:   ZPE1N$RhI   Đ@&Oޒs=0π  @t Vhul5~yˡ@@@ P/hHs |7   @X@CnnީSM5aBff4a`RfUP.%|8*H@@@m 8:(3E!   ĺ@uf"rYŤX, @ }4h, B@@4%;KF@@@ڲ@'vk0˖-k={$''*g2  ܽAk~Ig @@@MMFmUUT   @,D,!##C&Md=sv,azt>Zg_Bb3mLmb) @@-{k    4Fr-V{=뮻.|^x㍒bȏmH{w ]A@@hggGkh   1-р)S_o>rmɡCm,|rJՏC2s9r(y@ Z:D]MCCB@@B-ܸ>UP>   R^?jZf4e֬Y/ 7 W\qO,--W_}eucֶ(6[oF!?[-eA  mRV\~W)D:v j  DMFS233eРAЬ!W7O9唈#֏em>e}ެ ʌ%%%#?Cz%4V8VvxuǎҧOm.!@v6{ωWD   ЮgW\4@@@ JH7Z?Ay(iYZ|ѳ"߿_O.4=\t,[Rd6^@ s9[MMˤ@@@@W. ;ًB)@@@W^yf:#v?؄]#+u/@@@[qϖ֯J_f[E     rdƌoGCsBچN9t:%...p j&I}@@@P j^PG}z#)@@-P]|_ K˥V6铘(f˔.$S-hH ''G~ˇ~P6= K,iStZ(l-l!  9#{w@f@@~w'w}Z-*v_&7+q[l[{KrKqF l)'aIVX( j@@@ &E1N  M Ԫ-Kزo0g uj}kr],c,4i\}V0Ccm^6A_o$-D@@)i,|B@@#l! TBsҞ*n,Sr'˶mۚ|3<3۷O~ӟ_/XFX3O('(2@@@ 8wfʉvJ@@h ԍ`$=`e =z Jfff n?Hc9"pHMZy=!`ͪ"t:b c=HzwyS>z}  ]rQ/R~GśK#@@h{CK.4$eS(  G&x*JJJd֬Y?Y%=ErreժUƔ kSc z:=ceqjU ~"РO~0p饗CvKzUW]%ӦM3̣̙#:؁/`ӁaHbcGVj @@hOҒu׾͓2L F@@  ?NXV02l"UUUVgyBZZмӦM?7o9Č3m^RO{zCO>&z3gZ.W^yEk?3}re /^xAwnf\VS-[*u#F>*B@@v r:5pPʧ`@@@ qs5MLl1s֭[ ,$$$X{=07vEN*-26ǎk6^q|󍱬=iYn0Y"РcWDzn]#c<tt'Z~ay衇gH]s='ӧOC @T 87m[v.*B@@!`+. YGl F@@ LHO []Qס4fO|=CРsts=Wtg,7meoN9ٔ?^&W^yf믿^233OS`.H6nآ_fXAssV.luQ  GNv:ؾ-deS0  ^`rNrvyύGܹՍܵk2L"={4۷O>S+=j.rsz e۬JX'<[,g^,5 k׮OCѣW>sodXA_O~@@@J 9@@.pfNce px^81L1'U ÍHGڞa!== (++L>ǎd@CZZ?7s=ï~+XB6",4,_\/Z3il2pX9Ϸ=ZOs#Ldeeɘ1cfgC>}3PoxcDGhtSIңGM.uQW_ÎG!A<  q[ȴj@@ږgPΦs%!  mI`޼yIݺuػw1ݻ%33S(@5_;-4tBTCO)Se]&۷ooiQEf1=.@@hcⰌ8g+-icrt@@h@^DDcEWs?Pb c4Ǐ7+v"wisK   vaq^\v     >l3t={{Mg&DTT۷JuD{N   [AaX@@@Q #4dڴi,@ εkPJ (*nA  pl|d%K_BK   Q.ϗ[Z?3gJ׮]C5jTSEPV s(ԠI   KXj.mE@@@ &.!77WnyD,o hXfL8Q8 ۧN*v{t—ub_>ݑZ@@@JVV&G%   D@G}T "?3ܹ_b⢋.jl#@ Fkh   ШmFN@@@ڪ@Tа~z# ar6̃.\(EEE[oI2O.,"0=4 @@@ %R9xh9ɂ   @ 999r9爿`Ν; 'Рxeee}|\~OpZƧ'*B@@6)عMN!   Д@ >2tb||tM%yyy+4؇{L>c9֭[絍>[md0ݛ} @@bC.ԹSX;Ie   h@ŋEcǎj*yꩧdF_>lYtL>vY@|XG@@(֤u@@@ho hx-o& >B\\̛7k7|S a(!hugz  Apn!(4fe'3   mA b 555^3\~2uv( ۷oY@<aʳv@@@ l%%am>}[*uR    ;vH'㬳Βx,k@S n:U@@@;w7X[(A@@@ f"а~/?k%+zZ`Q>qkHD|  4.`?x !k߳'R$   - /.]xtųܪq %IyTlyy  mZVSJ^'"   i4tֵͫ~ٵkUn׮]e@I6@@@ *lnwTF    N4۫+VZow}uXff:+ EQ}a0gVV5m!   DC@@@ڢ@ FLqϷ֛ۭz:  ]}}ds$D jv   mۢ\&h   X@ygcr 7X]{sڊKFFFs!?KUǮڔLsaǐ@@ڗ@ܷ_[@@@"(рoQRSSϝ;W&O,ֶ+]t̚5+w D;%%ck ˿?'F~!!  -?xmY V!     hN̜9a2rHdΜ9_ >@^6l,X+i&SN D4{e jWl ǧHV@@@ ت#ڀ ]@@@@ pgYC[okʿ/"yk 'pn[/DsiKC ݒ#R~P6#  @{(/=RBRܷHGzE@@@ ٳ{sS3>޽{dzCt4VOEj"'@@@ ؋"E[MHmmD@   S *䡇M6\KΝ+_~ 2s_VaZ[jkG40#  @pl!'mr@@@))'<;;`Yh|g2gٺusl t^%%%++ nO~)5?곕U@@hO#݄>OU qo @@@@ Q`vvҤIT\\l6`3*n6WhBV;#4OB-\jND؍  mU7']&ɏ<(e4@@@ Q1DSLKKcɓeB0CSbGPY(L@@{4g(}.Fh&   -w#@ تbkT@@@p©Fp^5!   BPR&4.jäZmkͦ   Fm=[    s떰 }+XEQ   *tK*@@@fݡmQdž131v  -ʑ kZ1D@@@ Q6mT-vGhյwn!  @cM}zs_ ]FjQ;lEb+*qŝ&A   [`pyrA:u~7999r ߿W@(GVU"UJiHJJj^@@-?/V0JL4@Vfw|%5cƉgOy˿+^SU[順ʳHݘ"*LĮIJV     @hm`y뭷}r} .ĉou E6¤Y.L\}ŝ.@@@ ؋\QY)/o;u`}VS?M%]@IoI;IP{nqdNJ>v#8ޯ:­k0!  M7uм5mjfCjWq!&.5,W;|:uQ%VR*6zJ= RO7(%6w$qr\zXWcíOO9}>kjD#Mw'Q" smUIxƱ@@Z&`ϋZ֣=\[$NMjT 2~ϟHHjz =Ņ^34%Tun5R\J]υHz4l/(0y j[J p:6"  Ħ]Fp Ym%tpC9XROCI_;rֽvIUѣO;#ցLUA.5G¹k8- 2*0=z v"A߽p}@8Ov>M2;Sw\.oRIP$Q:|8Gz&^?ڧ?A礣s)CVT}aYS[nMzb ub@@ x_lg0atzs~B_!5D@܊\lP>6d={my}3cE&}QaߵC]ۡn|sn}IRFj>+@('ٻ@}֪'>/}fӡn4ȵoȗ۟]oڙ|ޡ1<9X|1Bo~ɖ?IrໍAHo{DzI =g; :⌣s7  @hh64G`رH<>.άxgI)Z[#1#C,Z{  Íߕ_wcrM]/jkanykb˲x`9ūdΊrBɤ.\! ^ ]f}o0T>}SeG`eU0gQ=/lvu*2@OˑSJTWu_:8ceߐt0؆1,`d}h|IJj87mK#u!B`w彍#5}.Ʋh_q&+%]#/C0Fh 2  ~&@ xB 6U{oK6@ u W^|ҡ~U' \P0g9E+ f̯pcYyq ǖW3xrBkeZܕh0CR>9#:'gMd7Ĕzʿ11=GWfcֈ@ }LӰBңXh*mD@(`(}ctԼfymj>ZإT19\%ϲʫ)))͛7KNNdddȀo߾UalnGSH͕%7 z(333+:wK[A?tJ6$)ujC+ @QezZf%Tʛk&}jn2^!5jeþwdh wG-鍏!yP~D65!hfajZ+./q s ;c;t w%e7&Q:.'|hofzw#>6  M ДPz뭲b \pr- ̛7د>III?//Oty:9Ny%99o^|IٳgOC뮻NF]og 'NUuÆ ҥKύo]{9rdcmhmɶ TSz8քߑnVS7.P$9%oHE˪= d° =T]WLOxL .U=2U}(] h*ci2eC'D@I@lc`I9 N^yb1} c$mF@ )~uOpdsa 7Xƌ7A̠ o4'[0|k/̠]j\s5ZFN/皆/2  @ WRkӒ0燿59nk닥sl{jd#˨T.V̕ʚF~65݄#T?L[q+,Mk@ ;~#67 "Yf9ɂ x 0BGTr)3mΖ{~7ȶmۼ6<"\tZur-^ /Pf$i-tN _ f0O;=ofTRR" yhLBB Pwq`ACG>@ Z+:Ji%àlȹV5=_l+H6 @ u4->(} < GU+Lk+w@ڤw?>lcjΝ;WkC0G-IIIr{h1(< o2G\۷i*/ &zhaN#FxG=cq#= :78M|7׎5$yՒ1&FY}eoiP@?R>N IYCjmIR'jIHq ao9#/x>xw>CYK,Yfn 4;Р~Y:G6p􍆃Jvv< t ]^jj~ iBQ$ F G{pS#Q|`2z^čė;W( ޜ! (1R\ٞC7~rnWW:IW<)-{ v:Cxs7|C=w/i5/hAurT@CIJtt)y T~rh^in#-93Q_\7SlO֍'ސƪC8H`S+zBG7r\ 5DQhn?mSߍ7Ҋ_ y;HER !['wSTVإdk Q>V3iHQ'OKyyѮ>믿>~[ʼy:n =HPocƌDW'=T9CMMY>dW`&QHf-}7ͤ V GۃVN[o jt0C^-<@.cՍ<;2NZo }3KdQtHlpޱ%d~Wx=C q!?½0 T>QsFaf:bZWH{jWڱ2ˤm? GZ+Vq;^ Ys3x&ȆY)R[^6p/t}Y9sKS޶m +wN} /+V^nTVVcǎz`տׯ7!esjݨ:B ="@B/TY잠^@-LptLlqWv ~b/<z1w9{VaW#o8!,'uh~vvK&'6!@, TNͦQ*P׫TYU]̤?b=rjnA :HaӦMƏȑ#%!!מ#6|wbꐔ$#8x敞={CEj>\lUczjo0;W?BZ֐kCyZݕ&UgM[Wf1 @ tN$WX& WR7؏#]~q*X!|zǰnu|%'$>Y= %:V;s}rIS(^G/dqrQ[}%w_}CWO\uI{=uhqo?GX~ݮ^i|Dk'-A]N佑GeݺJG?tPêH]χSG :ed$A(v mT@FЈu{H_I5׋[@ Q/~4:(~+rRk=hN{h;MݧI &S$מTe @{(hK _]L"z;C͛'/G}dt3WP/kh*փArնђ=u~Gz.8czډ5k]n+t@BEsJII<~x5atDqv\xV;ϟ/?g W#8|檜vi2 #P{q\r}RϋXfd3|-q'8oGN*+KTj`mb$]}Ru&nVT$6(dE # :Lj9V\=z-8wdpiiR;][h+zcn5/=/ODmtT_WA f7#J@';RV}H`7tfjI?r Qe*&UbgtM*z goе59PY/8{A:!iO~t9#{^`=˥jFq!}Gn;5W}-:fTivU;V<|_\WGt*u4]M-1Bt:UXتߙt>̞LUTJiK5=E]k)() $%dv2h)V F jtD}^IƘf |Hy;9E{§u[E؜qIE^fvQoB|6w@yQ4J,/}NkjyVY|PlxWJԩs5w4bG 1L|dm[WNkaӬN3)K6H^V+SR箑l)1c!-$Ʃk>JY#\"Ι$7trdo|\ըi]Ht=t au'W)6q&~+nḙjuOW_}UԊ>}H߾}eVuAeСֺ>|[d8 Tꪫ'0ک?` 9쳍~a`o.o:A{ ~ZuCD62usc5^{@@@ \E}B^Ocf:Sǹ:5R\УU:SUwՅVR:JjV%F9:VL9@@ e 6j=BC{HzYfY]7ʠTO?ذj*aj=]go&cSc z:5J{}PͨΖɓ'S^ umc;ZC ̑uW=rg@>JVO7D[:%Q SO4g{uo]>h,#6}Aψ ak!  @* ?>W9Jꆏ4F=]MG7puj5k cw\}_5~Σs.5OR}dq)NʨR'G.|,@@h„Y[;eR3Z߸A.]jG+ hK{:XaͲb ;vY9Č3z!Tm󪤙+Yj;={GQ+^|E#N~%/g@&}*6݄0tpƓO>)+WJƲkEOm s=!!&6 ٻ8zi[id;$t(^^}-> *U&FHٔMO9yNsΜ3{sSg̜  @dL[ZV"mVѪND=RyCzVK~ȍ_Af@@,(>SjVuCZ~}lj%KPGS|cg͚%?qРʽy5hq9^3֮v.]*_~yBJ@#/Kם2eJ,\GϨ`H=Uo6L^g̙=뭷z$(+պ8j PSSc,Q\TV=>'=[6Y.e><  /3FhQ34} D.65@@p@ń^ȸRT 2{ e' O?u] 8A _}o'_~Yijj2?񏝂0yI8K@EUWWYge0n8.Y7O9-lK"!  @ ̛,?՟˯H& tZ@@@ @"?Iד'i˖-t֭ PK3 <8{ny뭷b4M5kC4}k_nƞ[$6~ꪫb jɍ9sd0`$(Ȃ Dƍ7nƯjn vUN   @ /3fP   `@a 8M.:B#2T6jNy,j9DI|Ř1^'WE͛զY/c;[|;]mo]X*yDܟ\ ' MnvCYZ0   I}vi@@REPsuK2!6wرꌙbLW hxE$~/wu7p8,_ge:u+r9D._]Ɔ D3*C@@ K$:%vDrS  `h@y$jg5Kg 4k.ٳgORnGHg˩j sΕP($?|DMV-ցj\]w]`.L,X "] ^riW8  y %uB +tBW  $!P<0"DF-+Ȩ6ʐg(̸qL?)zg5{iӌF)կ̫N\* T:餓dĉv-wa,WfK|L@C\IƁ4^~dN'L"  Ǧ>5TK|T@@ PPɄo7Ș|lP}z {/8p@@5\+цʾulSMjT]]-/^C)++RD@k/Gj]A+$8lWN"  uڀUD[iӓI@@pXydJL/[n1_rxruɌ3dԨQƬ jv\iGgPTC2I-;TRKVtWJ޽ώE~gd]پ}e/RIjvo="K@. B ?"b>eOӭ`+K'@@@:H2*OfPe%1,   *x@=Ikk}vG.6miӦ%̓G!k|СCO51+mP$  @vJ$`L:bx~_"  XPNK}Z_E0. A@@A.޲^n8O    'ai: D)4GHwoQ   qZ߾q{l"   ,9a.u#@%]Lh0Ck,]C@@@@@@ /hȋ @+m)qNEW}8  䯀Fv^|F   y|:,$30v@@HC@ Q2"~/LI@@@\*ui6 @xh20  +U}ZUm     !W@ +I."  C>MB@@@ hȷ+xpždUY.M#  #M-B$\    9t1 ndZaa[i@@p@x[o&!   p. d{4  !'ٜq.7   = г9@"糨U73q@@p t@@@n= C @@@K]   ,@@C._]ƆӯC@@{zA@@@_i,9mg@@@ ]-Hh"R.C@@@rA\ D**s//}فU@@rG R9h[ڢ@@@&zN"<׌h5Ȋ;"  K), *G@@@4870] R]mzT       `)5"S8wt @@@@@@4D%xFІ ϋq2H@@GXª,^ªnS/   f `$ @Z׾!uQ+HB@@ &KR@@@"@@[D W,5wF@@L 4*A@@@4t6@h cb8    n@@@rR w h~nF׸ "  {-YFώ܃LO@@@U| jj,鍺t7EJJ,J@@@~[g*F@@@dL:H] 4 %YB7ɜdC@@$喍]A@@ȒO pL 2[   b|"   r'9,Yl14@@qHEE!   г =\*t@@     y/@@C 3c~Ԉ  tG2ߍ y%Ԁ   hp@_'5"  G2zf^ 5    r\~>"Ԙ>:@@(|*     yw0b*g^!  @N D**L֧uR!   n mW"5L4N*D@@: h&SkzZ^a@@@ !.9F~vfp34I]   X 4fl?#@@@w/#crxt @@"6)]    9u9 . $cR}@@pf|s+M'@@@Zט&//qI@@L]B/R^~_(   @ m_]LN;Ӵ!՘V!  = h{%i3KF@@@rY\  'N2WMm]]@@p@i}xEYgR    v~?$vz4+zKdxM.0@@p@3D2eh(@@@rA\h="kߔvy "  i rŕi 뮏   @ А ~jeȈ  -HیE($:,"C@@@ /y1J*/ ̛+-p8M)z,M   ZB&Kߞi8e1c _|@@@@ _h+ϸp/.yozJ<ģE$CG@@@@@@@+C@@@@@@*p饗&~}}ܹSv!mmm2'OZ~|q6@ !9'r!     @Dh&wzH,X +WF/gr 0G%'q@@@@@@<x<2x`O~b̠86o,?\# А@@@@@@R.۴ilݺ5 @r4$D.@@@@@@0`'m6@ !9'r!      +-_8@R !5/r#      УW^^.'N @ ﲇ       r;cTU\\iGN ^ 5`x AC$4eQŸF    Mii]*\z @ lذAjkkecٷo 8P.CaÆɐ!C b袋#& `$ n,|G ~4CgU+D  ̳D|E@@l%rm"=@[%Mr@GHA6v@@ _T/|A&M0zL@.h蒆 @ BR}۱Ay&+k_2d@@@L _~Q D"9_ ,x['*W\) ;  #s='O,{sժUo|x̚5K<5 @ UR#? 6vo~/^+/MvJ   @J?'UGSbK5/%%I#  K/$ KGꪤ_^^.*w'] { W[O(dYT  -` *@sh}7.G@S oZ,X ݻWâj<.]tq̔Ʃc3t=ZOFB@@,X3?: |%]R@O35֠z=?"5X-ԏ)bgk  ")+7xx}@@tL'B@ &@@C @@@@lX۶-áM@@ &{nyop8; @r,9@WhLkD'@@@w׎LkRMT $'iӗZd,ZH6oޜ\Ar!$ !! @ kYmDJJjz@@@ O/dVPH-0K@ 6Yl,^dؿ?2 & i$HA moYGK @@f&il"?a5 X&P__/KKKKmUUU U.~@IDAT]qJd8Q5<^7g@@p@su=YW?5# @l߾`P31ZJ"]̘[ZZ*SNO<d@ "EE9<:  4jzYR7" @~R۶mK8h@9n81c0a|y9  yp>տ;Cn@@@/5>iٴKEE ޚpؕ jN8A  @f4dGi@1ŷfN=vh@@G{uz" *뮓[# @ G<Z>m`y34  y$=xѪY?I'[# @~ KLL:UM&&M@ _IiZh77ﱍ  d.g^G75xn!!N! @wr|rill4/|x'P;81c EMMMwUr@ R"+ dHqφȴ  @~ hطsS9 W^)ꡂ֯_o7VZ%`|kkC~ ӧO7fpӧqCH]oR7 H͆C:rt @@+۱<r@pkVdʕFG}d;D"su`ԨQFÍ7(Z  Jp@eP-h׃#!  f x ͬS]fR) AB@(((0f_=ӆYlY,6ƍE=n- Ib0l IАY@7l*nطV“*,m@@@ 4WTUUTzwHUu~2Z@UW^RSS# D8 4UeL &۲Yhly;4  #߰޲`^(  `>C㹮˚z' @ uR7 <6BimG@@|mjY[-  @. 4KY-_<İYsG!'p2e McCh@2& @@0SԬv4Q y+0`={̘1Cց#v xjv@/_f]q5{l "I6@@@ B!KG mP9 ޽{?O2o<9rH "Y`,$ `KA-J}   K˖I翐KCb,  `ZrbѢEGI@}+'NSO=UN9>|M=@ ?hȏ(@ _kHaQC    h aa_>xo [j@E/\ԣQ.]j7禦&cHDV\i<~a4h|Fp/˜@h@ A +?Vշmnj=v-@@@ \d {eJ E@oR9sGPLJܰxb9p@ gK/"6m1{I'$c@ 9s" ho[mĽ9QB@@xmJ *[ڢ@m@ 'x[e͚5Fp p xPA*b@Yȁ8[@DilKlk@@@ B8=t00" -xd„ _lgU H  А@ xD-aW*?Of-OFB@@2>iɕW \~r! i 6Lk34D @ uR7 ,}6;G X替mg  (6 ڳ _+2zt.J2&@p@~d֬Yƣ&x8_E @@ ö_M|7. "  @n Vu9=h@@V۷˓O>)w_F͵kʺu$wﶴ2@Z  W~4;YiF@@@ 7.u ޵cm+8! #*Hᡇ{y[ni7:\\驤D.b馛; @4$oEN@y--_IV۱]U0+(  _wek{4 %xywcٿ455 у_l?frGO'pBl_?@R !5/r#X戾,^~ @@pw=F@ٳ'nuuul;~#>aR\\Zۏd@[$ \CI:!  ?mVۤA@p@[ܒ e>n4xskkklo߾m6@ !9'r!hmsD<&} @@@ 45Bw8mm  S`ԨQ']aŊsI'Ŷ۶mncl $'@@CrNB'tF@mNG  8[#d R2]M@p@MMG;uzɒ%c2bĈؾP3<Ʊ@ nw@hو 3K )#   |{v'S4wyMY|;o\}l#$)@@CPdC%.s^)=3   Х('ZKxhirIG@ ̞=^P3-/!?>[&N;qi/X_HP[ >  8\=T_-v%>##. @@A |P^{5Y`۷Oƍ'3f$ ^O^Wy)--?6 @4F@ Zq7O}o =3   IpDg l(Kij .fWh@(>dG0C2JAz`ɉȁ8N@+tf@ ,^8/:  448Cq)Wϑ@@@.@@C/@R؟z!J?Y%ލsަi@@&|rO8l G9@@@"j@+۷[Y}FuiZK~Ш#\Y))K3EJJ2   ;Zyhp'&O((%-M_8я>!  f 0CYԃ(۲oʷo[7W,M"J"  @N xw:6! ;p@}$3   @! 4d*<--R_milVç<  \ |+F߰^ ~\Zfjp /pw*4i\}ձ6}YYbE,OSzq @ @"  8;_ -2 ,|}   @! ?/]K&Z?JːţvijxW%/m!~}Rs4[)+(Łį@c@>u3q3Cw{@@Dymml",uq%8qYF,c"U7Aƨ]+pil?4ҿbrTWH1]KLXJ KR*O0ܤ/~)?5m[(}Jj(ˢ׽n>s2 y/@@C @CCC 眀q*ҹ@^} 歹]տ7XG/jhBtu"M+?=oƪ 1RâU./'Ļsx>GD"}L#ֶާF*q!=-`ܸ iCtaϖOlW;  VQh|?xzWW=lPV.#EU[V>ld`0Q'$Z9ExF)o˻[(Hk衾dedryGVUPz_###!kkΑG7y (PaZwHs`͊{!W=z+n5?D4"r^$j(/bN )uA,"Mյvjh9MA#Na٨5%=ErfEj hHQ@< I棧j}\uJkΜNc I?')W»# pPi2rrJ@i' 7kԌ\[ANj-l3TYo t0ia<:U7[կ>~u3?evO2ï hY씱qy~3wK4$. }6 :u |7g'u53) +PϾƣ+m3ϖ.`ֆZD~z ܵ?oJO{7m궮fvtY[jGGe8o[9;i x4ygdΚȤAe u]ޫWU.[w"g/-CAij~8x|>\ǟ͸u@X|].ΪW?wyc폻 fHw\*KgA9@Lc${<2ޔ?>,Ҫf0"!@6lGI3d*0AͲiZ%}u fHT#+|^>\Ӯ:֨涍[׵;3pgNufiH7 @ А+#o.KpBc_O&|az=%'?6u믿.-M?=sMݻeikHkko K2,dM@%SƟNþ}{)ھz^קĿfu=쩛EOE[ F>籅_;^vcO78hw6-v>;̲I]3.@dLUI]a?DeR Ϳw66*rlD ?lNFѾ3/1JZx|GjI  pL%'YXtWg]:;\PP FkF>l}iKg?y?/zk}'4wqr=c=&5p@ꪫkΘ5h}{,YD:|SvBcz뭷DuL;t7>8UInP3%gv+*(~/xiA!pǵ f~q}:+dgr}ٌGOYx~ſʍ'3x @޺:>t0{WH/3n{{4yqNGJ a9ع[;xټφFo.Nr@ΝMZ͐pw=ܓ0AuMF|F_WԻ`x_N ={?ȑ#]6mx{=7̠:[n|[ߒ~XB/I@S|4'RD/ U7@ yK@Qp@S!8[ig_I8&ٮaVˋv@3Z[n?fGOX|Fƌ#F ZcٲeFȟg'?I7U+FN<9zg+~ثsJSz!}:dr7Ĵ}n9p_YjȅT߲].x^\dp>?dm yGRBf8Ot\Q>ެnTc,>oAWYwou-%#z˷;n|t@l`=W_twʴibjO+X2aʕ9`w@C4A-czN1c >'_TM6ܹsK.i73,w_Sʏc0`@؉'h,PKcD9rJ>h,h.'JvC͠L:S3L&y'j9rۣ}Es`hv]*P? Sq}ePS4gDuo6E߈_,+I kC |\;=[;˾ 3'*Ȕ:$=H|+N7f\M^h)+>}֋'μw_c^xo%ڲmkˏc;Y?̈/))vcsnv v~ϟ BA764MlذAw:3DMԗjɄn8Kccvs=WnM~_m۶믿n .fWP_Էo_/~a`?&RE.QРZGeex矗ӧ\|I+kߕ]css; x7ӰY;n_M?i.Vщ8餃kqpWky1NrNbJ߸)fdDfw !Fᘍg:οVEի>Y9^ԝ{~%~6< ٕg+xR8yϟWO0;COD o^.Jx.;o Gd~?ߌA>H$ٌsB.uzsYXvv {g}V̙c8E 7- I-]J#ZȬ ~:Z衖.]{_­_^.]]v!> ux:lZtYA 3늫MȂ@c,j]pr3`݃Un@|К#13@>\{nޫKwv h;66@$oX!:kC|TZbРAqj|Vm i)Sb]ؼysl[m5Z oI-{SRQhںuktgrCe`rc0"oVѬzskl[wF4Rhp@=;H͚ÚR+%Y<+Z/IXVS->T ^ک ;TjVP(T^3(~n{P"~4&5{CCY8p} Ҵ4XdKtG01KݍA{HK꿑*)e?o)ri%꿏ecd]:SR-Mm:ѿtik}:3tVHOS^=WКׄh%D33No"ϞM/q?]9k߾Fww>:.DS#GȾ}zfB۷SdHee;)PZkDBQr$RGW0e\+MmT;^Ah~j*hr␋r*alKeg*=^$X(y]T+Eʞ@/=L}qiRY/h|vkݯ_?#)}2mu_קE_~>/>;AԙF,.ӧOlPk5SzIʮ`=E%/SͻU'?)*̃Oq̽(Կ魭e?I]tP36NDbgWbGQ~a2dCzG7@ MHE/Ʉ w^co~ӟTԍS޽j2>_C_1]v%՗2޽ӜCug>ǖi d>bpzt"-o;ԥȔ񕗤]G: \:( @xh\Y-R %8irȎ|Ϥn+;R?ea4Lv8o | Tw͌~ 34|!/bQ5s{'K.5fh0o<ٲe{「~}UJf T"lɤDzuIxU~48nnj $=Z4Ww~^@۶)%mgL<@y*&ȉ#(K7=ΥأC"e.k!-b g/+(3~5@H[ )iWB:[DU+ ( T)þ! 6c_.n#^y+d~Oz>kitqn(g^+s5fOVyr嗛5"9(@@C.juu>9c:=آE7ސhPƍe…reud͠UN}H&5R::thZկ3.F^ y{3 8xMtW>'av[pU#,[];cdی-K_yu7/=|f_?:eHHYd@ Zo-}de $?sF nKj;Wk7Hz *^[ꇡ*嗏X顇SN9Ee$@Xr{S655ztLj=3fKR>_UUU27>A](R;3j9oz2k2况6@G@+c{/}Uo qkT0.nmC.ġ˩5t(&/F A ~kd<)7n?{|Q!4 {G*袋'H%vL-/q% ^fM\t0)K?m%)-[w%K$ /j*Yvmh} dxTs̉o&v8믿^=\={G T"]P>Uw\+ҧ SCpŸw'qaɒlYm6`q`\І_Qڦ?~W[I]$ДpN `-)K|=Ϙ].{W53!~y :@Y4ǞUrGp@*-#@sGl_~tWb9Dx؏(ɟ#3]Xϝ|X|@*D\B>U$')jqY<|yRw~W9C>]~fԴZ}ݢ_rIOIb k@_gO j8( |k_;:Ä ޶uV9xL:!>?qA O~_7!,N.gɒ%K^GeRY9ۇ:ȑ#v& =aw@%c5Ĉ/Ӂ7R mycVh}vXVuq?x|C<+.5M8YmyKK$xe"\&?@ J 'WR4^/9pu1yM,#SˮrͳvJb0x}E WʴdkaƸOrn;9tNsYu]xؠϺ5W쉟 nG2@y0ɎgW1HRu_2[Wrbd;+ ZR;xݒ3׺r ni#eMA*/}6.?|<(`3%(gΦ_gpfW,M9SFt1J/Եץ߬SsM*jeVAdKSd͹P~|\="Գ略 )ŕ99?ZQUU:t^yڵw'%%% @l ! gr…ĮIOo}K7_&ҪO|5\#+sXmξf;B?#U^E'c(`; @L9ɵu{<}Fp%bbtkhKuAiҿz vkT9'z[*S/MjSXgVܭ[ţu9U7ګ:/a@ uKz)%{ &Sjqޣ*ZW4uG--Q#/g/: p0T~/ʺ̟OC}z2P ?/'SOɄ_fe؎VL @ F=CIS'znq@Oq 77FEj6m۶BzҥKuUpeAwy.+ٳg_Gz7oۃj򢭭M~wyR__/ׯ{Fm9sfsx%գ4,_\.~g=-X`%QI8D҂j5quȺ~8JBH?*am5,I-2՗WH 5U ,Wنza V-T M"fuMlAHY@ t0AM"jtpokE&I+G,i4cMpQtd0\*ɟ(OpmkF0腄Ph4 6uOs]~GgБv:566ʏ~ak4i=C0wqu]a套^Bϟo/9zzm5kACBe˖Sg WP"''vB[Q){᪶ew+:pat7%}Gm*p^~".zlH:8AO3G*HA} -jS#|fI/#5͂ 7B 􈼙 @@@,_^̀k >~&x_@'p5;(L 4$鬹ԗlz =} ܄<5 ar7놏֜6m=ĪUW^nFrTZA 7t;v,jWsLPs/>vAEn7M{~&^{=5=y뭷C? /ry@6e),|I;$U~" iʳ7T7t*.9{F"  @V &?4Ϊә*K_3   fw}w %Fcۑ-Ёz2C̓8}t&} {Q/1[RUU%]tioQ:tH{={=Caa/:555Ur1Utp_g%)Ҧ:qÂGBjfRYfzck57jtpɓ'|DR'AߕDLk +D9PNO/#I"ɜԽ"_}MlFNFG'ӄCuU?c=A544ԔR+#gEJEwNݻw?3JK2* @Z yZv#je٣nrAO0 Q& @IDAT?F7}$2MRsfr2ǍO`L  d@^H>[*X:T]#fKcL'!  @Z А֧!yّ _j7"  sse;\*_!+ǯ5íx@@@ 7hȍL/@ jլ   5d%S݇(PVA&M2_@@Hg6@`L h\Z'O}f^@@rF F0L.O}ZBӦ4>ELB@@pTGy)pH҃fN\./]9   X*8pADG9   q '@dƟo=$kp_xX҂F   @_)޷7!giΒ]  PHUfCCtuuIOOjz1cHaaIFv @|X|}"7  "J~#zǗĜ8Q-U"jt   -`N}uۺ"^otYG@I6l 6m}I IE6̚5K뮓☎# 4oG\Z'eͱc%xҲm4 @@ChO`-OI  @XUyyÛGpزe+ۈh@@x衇dϞ=vc|>}2Go4i\~|rYn\A:h١Vћιxꫯ[oU2z9Ȁ-@@Cnz.R-)EKRV7#  @7\5S]%! @ʕ+e׮]zjٱcճ eɒ%RVVױdFrU\=B?LI_kn*E@@,pEۜX!Y&>*B@={EcǎݻĉH ]]]HEO/1ydq\~@ 6bs" )b)WgGg&!  #04v֖qqb!I  @"*++E/$@sΖ@..) g@@@ .3_X*(   94dι Fs ^Zd  9 :oFR{ @@@`t7:?FR.`ؒ,UYQsϒ@@@f֊)"c͋/+?@@ B!ill꒞{1Գ1ꥰB)** QTyz6O!C 0 @L2H֥pfXI6NmE@@Ir0aN}dhC@N 455Ɇ dӦMo> 1YfI]]\wuR\\qdBr]\   jp&/+'*A@@s  0BVyG_Q z-[?.K.[nE FT!"@@CiY-`x]o4f^|EԀ  9#`@#  )9cJG@@ rj*y<{ +z,Kksqzj~Zo.1T @ #*jKVJJU   @.2HpvR@lA w_`ZꪫdܹRUU%Rh 耈ٶmλw^;!:m @ АC`@@@lh/ff] x衇dϞ=v1>On??iy &M/\/_.֭+W#8@g}Vn6@>.+O+B\Z/  dKt Cq㜮@@ ֯_o*OMGj'a(=ŋ{OiCeg 4 fդl}@@@\PsL;̊ |@B&{h"7o^z#,X`.ǏOXd t6 䬀NN=Ԃ  9#`ykpŎA  }NE2gΙz  `8:)mR   @Xw6xl@>H#ZOS___x QgRFmd@ ,_/N>f!  #Κ5cc=P8@@xL9d֭Dl߾=R#  k @ Ί\g.-G@@t^tfeeBNM  jd\0]VߟܹS6olWN:5aeS M4d٤/ VI7VFS  x>qb*@@ +JKKeѢEv:;;;v풻K]… \ @QO @. ^"69uWw^  @ X%c.T5:(@W`ٲeqF"˗/,X sK:yQtY6y[[[+v[,EIriFīNPE; @@H=A8v/'RP&S @eeXB^4e͚5t&L5˲&;HMO3KUH~@,8\YE7@@@ ] ߱`Ist*@@ #1ZJ|A{ntwwl6xꫯ[oUƎuvxE^@ hHdzB@Z[FpTN>f-dG@@p:h?9Z># @nʕ+e׮]zjٱc eɒ%RVVױdFrU\=Nr;'Sunt@@R+`8=E++@@ fϞ-zرc{n9qEϷ"5Vuu%&O,.K&!(_16l 0r2kg9Y 1YYfI]]\wuRbj@4 !OCé7JnS%S.  䰀فp+" k#ȋ/8wuuɖ-[K-"#*@\ !W4Dp8P@@*`-Q#)$:YXjg  0fE5 NOKOݬ#D * %i: {P@@r]wSc̲r5焞x L@%w}jkk媫sJ)//z6;T "d۶mo:޽{;~x!.@@C.=/@&!  $"  L2%y֭Dl߾=R#  k @ O87Ϛ>t@@SX%V0B@Vy~kڵ{ΝyfB9uԄMA @6 АMg Tՙd9S0"  J[pR  9$PZZ*-{)wyy4$Fh k,sEP   03ܮm7ǩhoG½GB@#1ZJ|A{twwˁ7sW_-z;ɀ |;d@[rf$L D@@"nWd5+g&@@lYrڵKV^-;vָt***믗%KHYYY\ǒUroZOwz118„Ga   @UطCs/.u@@ !gt1ٽ{8qBH ]]])|RFխ=ɓJld |v䦀-7U $jpU=lr^#  $zȟd$Dr@@ *++E/$@sΖ@ ^$}Y"VZ`Π%H   S3%x )B@@@R'@@CpT p͵?uP~nv  #?1jgG]   ^)'Rh @ooߺ#C?E|A   @<O\#yooW{[<J1'O鷍  PHA^ ؘ1cPBjjjDo'!'@@C|^F2N@:~BWu0Cߧ>-K:   |ҳ/P_ěB5=  @MMMaٴi۷O@LYfI]]\wuR\\qdBr]\  7" ]1LBa\կKhE9C'@@@ }̪I}7?~"Θ=Wz;cȈ **<⋱/a˖-ҥK[n~ _|BVOKcܯSwNzDz<}K?/H   @*ɓ)_7^òX5EoƎ/   @"o0Tx<^|U/v/O?l߾]~)++: @ Q".`ޯpXG+,4~@@2D@9wTxwu}?c9ZWXjNj  J3UW]%sΕ*)//W X0 ۶m7x^ݻwqہNr@L !mGF*`5Nq   @R%xe@x衇dϞ='w}̟??*W &M/\/_.֭+W#8@g}Vn馳^@ GQ       @X`j^^=RC< 2/^,sOdT'xBLӌ: G4      &谷.ZH͛7 ? jj- @@@@@@ #uuuD̙3'Rz 0X&lA@@@@@q@~~~d=Q+zp @ J_ʨ"      SLt֭Dl߾=R#  k      bvZٿdv)7o+,,S&l BIl:@@@@@@ !h"N;.{׮]r]wI Zp|QK @6 xS @@@@@@ ,[L6n(~_ZZZdv dܹbI'O=*.kӦMCjkkn|f@ =      -PYY)+V{W:::4MYf z &HIIx^{,Ammm,D4< @4      0z$UVɃ>h[8Gj[eرO@ hO@@@@@@)PSS#+W]vիeǎz3TTT_/K,]# #@@00lF@@@@@@ Z`٢;&w'N^|{)**jKL1[:MIhf8:@@@@@@ Sh3G@ aG%珊 1j}gOoЬ#      $ZW-Jy Qy*~WC3 숻C x[   @ po{#  I`$bSf=A2 ?\Yُ  d~tw^$yϗU}3    !uԌ)p5Q0Cɾ$t ]PO@@@ k\_} }^x^\/+ @@@`ʉ8Ȏ!Pq0羧1́  dg[R:a@O _ZH(4`/@@@OȍY RCFwWu9  w$òjb;;SqCf@@@ 0P IX ?V!  i%#Gn*ӪK4@@@ hȬEk@`"WKhKo)'m3+   @?{Ѥ~Fȱ   @ Аt\p>4ⷋnxE{lG@@2O@2x0Şw3@@@ -hH@#@ Y^Jw /@@@T xmCp{yM" @@@ !O:]F ܇9}϶-K   @*t pЏ   @Վ3׉U   @L4D&E4]vgSS.   nc>ʦ`@@@ !{-=CF}G@@@ |k8H)@@@ ;hJ@`CN9_]c   0rqP?c8Ϗ!Y@@@/@@C>!@ 1۝łt @@Q}`lz{-@@@Rx:@@@ hȾsJ@`# o   @z 690w!@@@ !)=Ba̼a$nFI   @\I1:;@@@6V4}ف  fuP   @ Аu!pv%lח(@@H8^UZxT   }4d9G 0q0{ٲXE!  IH @@@6^ o _9{@@@p*>]G@@F,82MH FIB=fO{@@@  N-@@@S< Ξp" =6!  #HFPvoQ   9-@@CN~:@n n[   @X`&   d vh/\2G~,G"  (.O8!F=    S<䖀~hV-  dHRn9z@@@ hȮIo@ i     ŐiYB    9'@@CΝr:@ IFU뺚sn#  @ ij@@@ hȮIo@`W7u10   fSə Yi   @&А g6"Qs@@H@wwR   Lo>2@C  d }~|$:q @@@.-|4ZH   !@@CXdE0zzxH^]Ԅ  Bo(P}d~9@@@P4k!`Xæ&@@hs;1   4䌀 L_(   ﷿d/ԑb    @ А"@B} )&BAWX@@HQMnɌHnϨ @@@!XH//y E?`   J 7`yXpX xޜƭi   ۓmO^F@@@ !OGt#)Itǡ}   `Pq'NjD@@h2x'ĉLi.D@@,pi|MzT   4d ޞuQm3,K|/<U@@@ 5ƔTlVT^*E@@\2rQt$ƜfVB   9<#3g T@@@ !kO-CHz4!]=oo7   @J\GJ)AR@@@АF AJ~{|]P(mEC@@@ w\)g떔K   +@@C;Z1 c|6=J'^-}%p@@ ᵤIj}T   =@!p92nw7G-y?)nL]C@@pXp    mАmg Wsm! ҭi@@MRӣ`05R+   @ А#@ޞX6}RjG@@ /C@@L,;tHGC4\>W7+@@@ bHB@@@ 4$ߜ@ L{X{9P(Jԅ  "`dR   0 C F|,/y2M@@@ 62FK-#  h=@p3#}ifdi4  1eۮRV?#  d wh1!ȝ>Y> Z  Y!`x-‘N   $G8S B@ eLE K )B@@@^MO=)J@@@2S|XN8a=n8aIyؑ#G\YYL6-;;JRiٮ6JAe=j$Ϟbxb+iϳ^@@P(#F [&Oi9>_}|B@@JFxZ|Iy???<엿EkVᄡΝ;BҮm4htFG7 }Hq]"m*[q%0bqowwL,OW_#ş\G4.ĜR- 45MA@@GԽO uSevpw\ u>vL B5S%Pw/LuJG;=چ    q`5;ZZZ䡇w8Y ~ L+;G:$O=,$@IDATqcԈVa'٣MX%%G(-?o[` K?T*.\'TYz ZYz+\*hĥ~D/X%joR~p۩FUH\zzd> I@ IW=dYޣҮ~WiA/@}/wZIQ^9 fސ*Wx~vU~GQ[)Ru5>|IM yC;=UYJ G祵B7VJeW`gq9ģcytWtpc2aTmnb*L  Y)|*uT`g1⣖-:ykikwWg:}=oKj=u]K8d@P]6ﰯ-+$E 2)+ucKuOU_p9KŘ J}ݮ3qE˔k|פE],2aEU}oR\G]c7L 5vvOv=ҧ-Debl՞YRQ4ӮQޣw[ 1  %@@C\\dN%!)kGov =?s|aZp>,ȋ/3oCnj0k$RCFbd#tЀ~P̊ ҭXmo- [.e~=9Os_8%pzY4N=%rjTT Ss2Iup&5: (( 8T f.Ո I_G.o<]$mu/'7|(TF^~p_C?fm)sT{u99[wLpv ^3/PC3IH)Yr]R[iqCaW< %?)@7ŮM]Fߓ-*` #>{к~ܮڽk>AN<$@ k<:jYus@o%d7sc\ }K?g}%n0([^W젆?wz:8&;$GTAžwZFp {1n|~QFƒ-o=Pv6=c/Ugg=F;8%'}wCmm?[M0.WcIup񟔥/biW8OÍSCaTϪ}zdSS-($@^1솷ad>[M3M"?9b>ռ?ް 9l‡n:!?\`z_w:zsڬ7++~{EL э_O* 9`]s稇u!r?8N{F; #l?{˝eG 0@gwTw_n , @2_IF5q R.O'?+k3 =Sۗosi7OA==+cfw:u _ORu~6 kc[a;uUSp@t.Ӫsmz {8:Gm+ODOQABX>{x[bumȌ^O;EߧēNtgC}@xLuޖPgnt;{so~_>퍫|oˑ8 U @pH |gG~VVVʍ7(_0Mοb ;Aw#3f̐o}[F+..뿖%KD_WvЁ}+3gΔ~r5Dor}׮]SOAAuQ50ɭ*s+[l;S}'w}ڴiʸq+VZ%otvvVKjۿ/>_//#J, PC<v2:s뙿%1I?#7wc^zQ!*Ɉ`?w\#Hzhvom:q#(!pz4}ko߾aj({G~SVYғO>ٻ07s㯶]k{/``l1%@LHBhBrS ~Fn7 $!jlpq%}󎙱V+JhS~sFIΑ.L~1A06h0S݂4z+;i{.[Y칿KQW>דOxyC=&n&5*}~ń l޳6S9^;ZV˫Lk&zL۞:L&w$ @J}ZBI~LI8+ڻR8E-+${5\:N~uy껥}[No7dmw # ?0 t̟?_Ə/VPA+{「= 43Cyy=BEEÂA_is1r衇A% Dt g.~466:G__r}}x2i$߿4=Qh:iasIe_{fpo6vp jㄖO^xyD 5c4X⢋.ߙN;4!&4y̍@k&crE(yu O/flu<8-@#Աgy#ieȫ9h;{D%"6qZ$(}yI^J_\"Id*_ڶ3׾t쐕!S{o憇U댴ü[K؀#[1 R" @%@@:pkAQQ|!Yg%?/u?E'N:'ٿ7x~VKAlٲEm:1SIICAO' A'uwr}48; 8]7k,<\s@nV;ݯ7|С2t2er-RSS;ӧO{Pg={=ch::̇3رcEt::SNɓ';Ixgt-#:ԈDhyC2)Ct6iLX2(pTNn拾;1АwHڂsԫrg>ک>L}5 qN4^NQ+m;zyiJkYPO]9|RU j51mh~I}5e+XD(c>v`Y/s^vzjP~i;WU7A{О'onn?Fń io-[&/bf*q NPGD {1R3i) _J%4V_n*֗9f8LX@ ;ITs*m6ӊ9y,1jZm]>i;:ͽKI܆C"v[3! @Wlkfi̙2r˫5@iŊv@@ !C$lG^pI';0N: Ğ={U:Coӷ-7H}.]j̠A :va_dn&Qbĉ ٱj*3gNo=B z*Q|LJs54R*'{i5\Ne%UR*1ֳ@.˼XهXZ$W,P Ԗ4VƔҪ*kHiBݩU/z­~h*:! ~ 1bDJ < h/{ vW~ɶips,_\^}Uy#qQk3x:.voDoi9.'O1Aӌ5*)v<ҵk{/ZHк1ڮݠiza?F/}m&r"`sGkj͵C;'>}T5 kkkcjfJJ_mS u_7vg?Oѡ'կ~;y뭷3SL3f8u{/ݝILfc7o8A9ӛ 6d\>(4H ?:;WI]Ő>ρ @6Vb Yxrvw&霙~@uX&f[l[/8{*ʴgˋ{u\ΓNw6>*珛MW|1\l mYVۏxuc@+|lwܙR6nXCJgA]w̠__uUr1C =5l0͛7&fM&Pa& .'xNZ#G& ,G3IM7J?E$vt9oTu1ӝ#FҎ 8|~Q>tzZ{U Ӧޞq<7Ɋ˚mt9s17w)wȽ/~B/<~ni@(7%^ d --s^W@hOΐpsڅā? iW*ONOӥ='cug =cꏤ YΓӢ}eyu^ˍGd xdYZn~})믿.=Cr-rI'% f3ci*+?fUb"i`$+;soA2J=jgg6~]3z\1?u=eb# @ jW ` 7 .H×:e` X@_j^!{ӢPd{Lҷ!4Iei0&4/O~XaV8F7+iǝuط|7Ecc֐Towu4K'E@ }ߖKwAd2k Itg@iȐ!Rl<9g6%KD{вlٽI8 r;cl@LLZ/cEs7=gFo؝)>Yc d>K֞{!<,i#cdWJ_/?B_.Z/KRF|;_k)d}>}rᡏȀq"W-|FMLB:iz\62oURZa\~U.|S3ꇯ_>rApmySi/yi#΃},_w䰑T H$@_ dYaevi O(_L< Z0 w7Pˇ[*K7QxIvmIm`+äflkYi m 7vYY[.=0lzuuo#qʊ]>GRQRgmܾ͚JճC{ڝʟ}W6zӮXQRo8vץz䷩zqH9i]}g#Ñurʀ~rx]~A@ W4o~-{9tISn6yC4d:M8Mbg>];D~ƮrЏ]yA;wwp(nFٱcͬq`W_m2z{+/^,>>,hLuK/d[;jpD+*++ &L+6{5q%-)7~Hg=uV߄A?shSjzu V ohkő,U > 4(HDBVbTeeK=h>_?:h5 NǤzN'PGmf:XIT6Ȭ៷PW5JpCUzꇯs|Z[Y<&:0n][n+8 pҨTDG jo4%٤A(6b%'A-tFa%zL7GB%d` CEBm{Z3*gXCZ)^v۬&ãF[ u._'NX'ٿ9=~&d Z锖JȄ'AЫ^MtZ]P}yNHv inz?_d10|w]IVqY.:Yyh}k>l%I4os@ Щ}tކK=3%t5h @{&7+Yhh;OӬ_A-a갆8[ݓZibNY4teiU5Jgn~$_wL)4pk Wq1AC@@@@4SA3$P/%{۬@NDžj M*Qei!>xI@@@ 9^oc_;j /YfΜ68g{!^~ni :y'olYfٽFHР5av+bc?]A{YdR ʸ @ӟ{=4bhPF qgKu]yHC?~a?K_m]GVϾYXz`J!  9X~{t24#6   x)Ø ^Zy@_cH8ړڵke=7N b5۹|ZǺu&Z[[ev,i˖-7n:t]_#I{@BRbH' äÆ#aW:TGPyP^SJ>Wܴo|,Y@ 6[W^}-gסָL:5눾0N@̊^)WѦL zv.+zS~w(`~r+h !>ˏ K.+;j3s3\G3EF)w9o|yaʭ>_Sę=ػ,#5f @CCNul,dg'_ߌ3f_\ p??Me: ɓ3M}&>4XK0!  F4O$a"QG -~!k 3e%   hp .:0!  jm1t4ȢsAvƙ+K#   @^ Аק#@2{I$"@@@ƒqVL8Dy:njJxtZ0!   /Pe@ mKC> @@|&:%j;,idtq$ 8#@@    @hɤ* ] _eHt@c @@@ yuLp<   @ P@'"P3geۏ?!/M@@@Ɓ9-`tРO   %@@C~/Ji DI.fqݣV GۗC9@@Qsgwc(ɓ:   @h)B M _|nU`   `B Z[k">o}&!   % !_" ~-Zrq'N궞   ҲbgG j0rI@@( DSM Y <)??wzz~R"  @~ v(C3,2F@@Jp@/P󧺊 )y>kȮ]?T@@ T虧   @|-_Ł +Dy/Bl Rz{R<ʁ  =xI@@@ S2x@yo IYgKCd`S4웓CA@@(v"2$C@@@>R77H:9TV'@@&E   4ɣ @dv^Q+󠃲 #  = C'LR$   TzfƁ|.gڏ>V?_҂@@@ wӦ{yʱK   -@@C~?J)D~x)2Qڏ:&E o@@@Q/v@@@ !-.vF()i|E"ΐA   ` D!'rw/g@@@hH@/])Q @@"2+YY   @rR@ @Jڎ=N:k}jSm"#!  @:.(ھ @@@ !!5@)en_L:E09RRB@@gL-%^   |,@@M C^t,hݷI   `J 2I:'`*҉ ~   #@@##Z@t0z  FOH4>=Ɠ@@@ 84\RA ZYV6!  'i,f{Wr"'@@@@H%@7ȠAv@@@ ZٽOF4ѧq   D 21sÃK@@@ F9TB{fNL   `XsS\甩W@@@^h @:aٝ}@@@ Dz Yσ @@@'@@C)5Bdeɶ[_Rb.-RB@@< \zUUV q@@@` J@ 'A%șU   "ÆK:k Y)oZ$   \{n [c~1?]/5)" ?>>IDAT @A IY%(ڰ.8   @0hyV H2ZcՈA LB   ', c   @4M њe   o~Oeؑ~f5\F@@,7o>T **D{Q`B@@.9mrDC3Xcf7p`)p(   @ Pgz#qhq4I@@RcQ [L   +@@Cb$&j@@@ NhIG"f#5@@@ N3DW({O{ᑣlA@@Z`tyA@@@r#orSrEzVV=pE@@|#0)*)M(   @А?犒"H~R鞄~@1   y(`"#  Q>q@xPSV ilJ$  x-1XњZci   @a PX"Paób>K   @.&˶c4ci   @a PX"P4n--CfO@@@\ ǎhII| O8@@@(L Sk V j 4/)Ԕ)   `#g\'g   y@ĉ #!@@@/G+>'bfv֢>ρ   @@hyzih   P r-.NPHZ/D55i   848<"@TTJ׷s( q:$  U 2|4_$R]r-_*1cS>@@@H$@@C"!@>T\V;f̒W/p !  (EA`_*~Hzg%48@@WhmԽH,wޖ?ЮVuzc#J!_H@@@|(@@O EB.S|[R?͛R4ZZ&-V0Cxg'@@@ HѺzszQ@@@ B@+Ji)Y?G)jiIs4NԶNG X      Yz@Qt>W:_Y-%ޗ[D"VUKxp<`HUU>֎2#      4i Qn1!      @nr=#       hn@@@@@@@ȱ 9>d       hn@@@@@@@ȱ 9>d       hn@@@@@@@ȱ 9>d       hn@@@@@@@ȱ 9>d       hn@@@@@@@ȱ 9>d       hn@@@@@@@ȱ 9>d       hn@@@@@@@ȱ 9>d       hn@@@@@@@ȱ 9>d       hn@@@@@@@ȱ 9>d       hn@@@@@@@ȱ 9>d       hn@@@@@@@ȱ 9>d       hn@@@@@@@ȱ 9>d       hn@@@@@@@ȱ 9>d       hn@@@@@@@ȱ@I'{/P\\,g(VF)/`rTzK:::23 ׈{|rinnv TUU%MMM23M ^Zv\.36{.>x-9prݴi{΢}Nf͚.vg7}޻+L,}f]Rb98]"⏅{~fr'n:ٹs[>|Ln{~>ryfYt[='uuu23 YrDQj Drs=WngG ^ .u?}23׿uy] &c=.3@ G?;eW^y]f/N9 Mg:묳?#/p%ȳ>:~r P9ѯ~+aȑSO P_[] V|ef@!q        !P         q        !P         q        !P         q        !P         q        !P         PԚQj/mmmn  CuAVX!{qdԨQ23ڵke.CEEL0]fBشilܸe(..ɓ' eˤͪ_~2|pw ]`ժUk.Fƌ.3@ _^n2ĉef(t͛7G}2ɔ)Sef@!@@C0#@@@@@@@@ P 9Ie@@@@@@@ 8@@@@@@@@ Ie@@@@@@@ 8@@@@@@@@ Ie@@@@@@@ 8@@@@@@@@ Ie@@@@@@@@I0A-@-[&+W&6mZ;'DdӦMvZimmѣ<޽2^9^H hdzvs-Lۦvd:|;7X^_Ҡ6$`zPH-&8ucZ N& @4+ wv풫Zo.sLZ ~3tb*9/Oe2^*yfHl dy="l s-eL_Mtzw6(q2fA} K ӶAa omk)H } ony"妛n-@hš5k,Yt}lYY|ߕsi}S^{M\\R.}bgL6x-uyS&>x)Ƕϵe o^Mtzd UF1&>lA>R[CR(J}WD/Fa >9aέ^%0}tۜEygd۶m3c:=']Zu>&ix)׶ϵe+g^&ڦvd:=SB0q}u AFk׶ϵG@hHϋ@ׯ+RnfinnK+=:R?#v zЈ#OzԩS裏wvvʣ>m_uˀx `dQmIJ,ϵMMtz>? /P lj~n\KlpT @!'2PF󟗶6Pp 7{{HwC/3/3QTsQGeΠy9 άf_75k׮7ʀCv[X,חh4u+ oʆ DAD>Yb4|p5jVKLOºxm%5zo9ϵm506M#9F1̣ 4=?ܧL0&`~=õ\Oϵm# ! 8 i&_~w)i1 z3kfΜ'ѡ'iӦMvȑ#Us`s9Y'xBD3'|sŏc_۵NjqɹkLћg,節˩.k/tM. ӟ۰-~V?|p;;K뜙SN9žkkkU]i]8XHATA.%zJ.~m\KGyܼ2*iNߑ+חS\ާpS0zkIRmE_>yEQjȦ %mT@N~;L8 +/5aܹa<X f .貋s A5:̄N-ŋKii;K/uJ!,bh.3i j~=ZR>4Q׶ϵ/LMtzz;[IHëx"}֥"`gkI͸Ri9_>uQ0-Pd:ACY 5@)0 V"gڸNA丞r%|M}Mkɛ3bN/dX@rW۰żWKZ\O9^۾vNvF(CNY @qq?Omz7]'vM&7)4i`F*fg8h!bI_JFl{qpAqϫ/ ߼L}Ak`N/dX@WWz䝀WHψ(^M}䇳@@ hG@ +Gu>e3c:='e˖.v_tiu+nv93e…_"vSy^⤓Nr׿{<3U63SX Q'/o:\is-J~_ۑpϟHuyusq=yM믗_|]vf>CW"p^3L:>NM8fEnپ}{Z͛77MٱcG o֬Y#ztvvf/k >-6b] >hqx= `aN/W\Kߗvd:D:'Rax^o\x=v\O^(+mtzZʕ<"@JV!Xx'\;UVV=Kۿ50o'&ΗʤI /x@ nc ~ ||=J+`cN/\KPvd:r<x^MZzUmV>۾r͵ uD _FV)dC@{,.^e2rHHNΤ@TUUCJ,_\o̘1v5їi˖-qF]ƍgRA lv:O6zʦnp6M y\wt;$=={%y5]1ϖ@&*Sr=+*`N/gk).@@CAʏ)͂Ġ  P{T   @         А!)        a        d.@@C憤       h0 Jr        @(jM'C  ^ ɫf9{l)..vA@@?G@@0#@@GRA@@@@@@@ 9a@@@@@@@@ fI@@@@@@@ `@@@@@@@@ fI@@@@@@@ `@@@@@@@@ fI@@@@@@@ `@@@@@@@@ fI@@@@@@@ `@@@@@@@@ fI@@@@@@@ L@@@ȊܹsMg:w@@@@ jQ#@@@|xG7g?뱸555w^w[Jef@@@@';@@~(z~b ߗ"   x/ޛ#   Po̞=[Z[[ ځ#   ,@@C>lE@@0,}. PL"---~<3     !Z!  XdICe@@@@z(}@@@@@@@@ [orC@@@@@@@r"$vA@@@[[^Z֬Y#2b6lij*8@jkkl޼Y֭['6mJijj1cHiiI@@@WPԚ@@@ ?}袋ڵK|MuuurAǏ]֙ HKK=.~:Et:__{On6կ~%{9*/+Bڵko˳u./| v]A wq<#],4XNK/~t    t+   #W_}UfΜR|k]^LYYttt؋\pw}M>OR4M2Ex ?_mavo ?%(nVw#wi7w6{n    ÒW!  [,\`UxN{ f /P".&4B$暔4'|R̙#oVҴـ   oBӌ3Ψ`Oh"teiע%QJUPP7HHED D QfC"(qpgpZgsyf|]АbUD @^&5۷O?ofI^Æ 쨐Ouӓ&NO7oV>jܹ]GCqꯊ͛7Ǯ]*k7yXtid;GdU|'Ĺs e(d ֍926mӧOG+C)͕x{c @ 44  @ dG@̟??j޼yc!Nnz11|AX}*={lx3oV+  @ @ eGN\] @ @@63d4# :cںukt̐7n\|G~¸z//^w{53d϶#kh: W @ @@jR| @ @ x'*L,_ &ĺuXpad8_mmmqҥaW_Ş={qCCC)Sڵk%ّ[ln @ @) hhHr#@ @@x碦KGy$ PX59TPxuvtEǝV\G~Q. @ @@_,G @Q9st+{n.u8υٳgd1"_G"@ @.Pz#@ @+0f̘n%_WW[:64 :4~>W9.^Oe9k֬| @ @/6)f(' @ =Gs|r?0Rd'j!@ @&ȉUL @ m!Ct{mšaO8gz] @ @Z9q @ @}jkk\||P @ @@/pD/( @ @ MBbCÇz2>E @H]@CC @<0G]]]ܾ}åK>Fb @ @8r,' @N kfKw}W<L @xvhx꾓 @IyKl޼9 ƍL6-ޝG!@ @V@CCiK'p @S_~ܹS69sfݻ7Owމ5kDccc>_|ovaѣG54D  @ @9bUD @^,0hРBtW^-Slذ!Fuʕx饗qW7n݊_d1uœ @ @ E )VUN @Ξ=[Ki0xؾ}{!?86n7n(WXjU>|z:֯__ @ @RАjeE @^*VPW ׯ_yEo۶m륑<+V cǎxꩧ?!3gNdײeˢz= @ @ Yl#@ @$0px'o:uTܹ2ׯ_꫅|ao{XhQ9s&رcdɒxرq|Mͳ>wM @ @i +H @z~#F4[nʼn':}̓'NqN>?] 555Ǟ={n_ @ K,A @>)Scڴi~ȑ#oO?4ΝL3kdȎ+GOs7 @ @) +D @Ȏ?xǣ([[[+;0455EÆ q @ @@y44v"'@ @ @ @ 8r"J @ @ @W@CCyk'r @ @ @ dK+1 @ @ @ P^ 孝  @ @ @ @@- @ @ @ @@y44v"'@ @ @ @ hhH#@ @ @ @Pډ @ @ @$+!J @ @ @W@CCyk'r @ @ @ dK+1 @ @ @ P^ 孝  @ @ @ @@- @ @ @ @@y44v"'@ @ @ @ hhH#@ @ @ @Pډ @ @ @$+!J @ @ @W@CCyk'r @ @ @ dK+1 @ @ @ P^ 孝  @ @ @ @@- @ @ @ @@y44v"'@ @ @ @ hhH#@ @ @ @Pډ @ @ @$+!J @ @ @W@CCyk'r @ @ @ dK+1 @ @ @ P^ 孝  @ @ @ @@- @ @ @ @@y44v"'@ @ @ @ BqIENDB`bench/man/figures/README-custom-plot-1.png0000644000176200001440000044726614424465045017670 0ustar liggesusersPNG  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|@IDATx ]u}/߼$!@"ȣ`@Tm%R+rջk[. .*,Px]+Ղ-RZyV c&Lqz3{g5ٯ934 @ @ @ @@B բ @ @ @ @@E@@ @ @ @$' Аܔ( @ @ @hp  @ @ @ @ 4$7% "@ @ @ @\ @ @ @ @@r M @ @ @ @ @ @ @ @CrS  @ @ @ @@5@ @ @ @$' Аܔ( @ @ @hp  @ @ @ @ 4$7% "@ @ @ @\ @ @ @ @@r M @ @ @ @ @ @ @ @CrS  @ @ @ @@5@ @ @ @$' Аܔ( @ @ @hp  @ @ @ @ 4$7% "@ @ @ @\ @ @ @ @@rU  tvv&PR6mZttt sXn*0{hmm7FwwwYn1cFNcڵחf@# Ĝ9s*C_zu 4"1 dBCGOOM^(@5 skN50{7oNCߟY&K9E;wn @@MwhIc @ @ @ P@CQ%@ @ @ @j 4Ԥ @ @ @(J@(y @ @ @ @5jA @ @ @% P~  @ @ @ @ 5i @ @ @ @h(J^ @ @ @ @@M4v @ @ @ @E 4%_ @ @ @ @@CM; @ @ @ @/ @ @ @ PS@& @ @ @ @@Q E @ @ @) P @ @ @ @(K @ @ @hIc @ @ @ P@CQ%@ @ @ @j 4Ԥ @ @ @(J@(y @ @ @ @5jA @ @ @% P~  @ @ @ @ 5i @ @ @ @h(J^ @ @ @ @@M4v @ @ @ @E 4%_ @ @ @ @@CM; @ @ @ @/ @ @ @ PS@& @ @ @ @@Q E @ @ @) P @ @ @ @(K @ @ @hIc @ @ @ P@CQ%@ @ @ @j 4Ԥ @ @ @(J@(y @ @ @ @5jA @ @ @% P~  @ @ @ @ 5i @ @ @ @h(J^ @ @ @ @@M4v @ @ @ @E 4%_ @ @ @ @@CM; @ @ @ @/ @ @ @ PS@& @ @ @ @@Q E @ @ @) P @ @ @ @(K @ @ @hIc @ @ @ P@CQ%@ @ @ @j c @Ezov}1%׾|QY @ @eh(, @H 0\,]ՇψO8eS?V @ @q/ 0  @ OIߕeLJ[\3Mg*!@ @31IG @ ۟ma( ?}*C7yM @  2цI @"77|zJ/V?}:I @ @h(ܩ @u#bKd @ @r 4kTK @ ձfӎս7~+wi  @ @%h(ل) @e7Q:SZA @ 0 o @4OorKlW4B @ @CsB @V`Sº-=P8 @ @ 4읗  @ @ @ @1hd] @ @QƤ -u)֖ @ @hH{~TG @ x𴺌ySሺ @ @Qh5Z  @ @@&cgߛUv4B @ Pr̓*  @ PZp@o£+/QN&@ @(@CK @(Ķ1?yaߤd @ @r 4kTK @R 9qS:y^UΰOxN"@ @Xĵ+ @J$7_5=+#g?s( @ @q% 0` @ O:|F\O'6,v#{< @ @- 0 @ |XeGܻt},[]{c҄X0grz1wz{ru+ @ @`l[o @ ;YS';Nǃ @ @n @ @ @ P@C&@ @ @ @hV @ @ @ @@uM @ @ @ / 0 @ @ @ @@  @ @ @^@ax[  @ @ @ @ 5 @ @ @ 0@. @ @ @ @ 4k @ @ @ @`x]l%@ @ @ @ h-o]Y7V\˗/'Ϗ:9 @ @ @ 0 ;&g!믿>9w8Y!@ @ @ @ xD3u|~0&:;;v\pOa#@ @ @ @ CC#ܙȌ3sΉ Ķm7M,^8cŊq饗ƍ7fͪ; @ @ @HR@!i}QW<5yM\ve1umx;]zW\r%uX~}\{/}i/ @ @ @ @@9AMٝ+GΜ93/"f6qGǧ>jdwuXn @ @ @ @ Egej;:sc뻾87Œ%K׽ @ @ @ @) xDzzzb…1mڴXlYdxr-[T=Ty?= =\s5tmsN\tE>z̎;?q|{߫'۫߳1~/8Eu @ @ @ 0|;ŋnz-o~o1ΝguV~qUWU0 ~86[[_|ew~pWewx*ǽmo~PS @ @ @_].w#$0~;%d~;߉G}rrJ/˗mVyD+͛7u]W 3|1x'*ْ Vև_?f𖷼%N:&;7<3(;#^W"1z=:#ړţ-D1 @ @ @]=0]h},-[+W~T≮ˆGNTG>>l\p1aqF[[7>GHdAܟΐqa۶mc+.\;&[Yoo^t\ W_}u|ߨ;+  @ @ @@v>n0CKKK|3˿60\M{Ѯ. c&.sL|̅ӧOvvvFu' 3e/6̐ݹOOկ~]]]^ @ @ @ @hGlc/Qv_עEp饗FSSp裏mP#Z0"ːUCxꩧ*NxGcӦMqM7<W]uU ԲGC̜9s,X[k`˖-q MV/{G?Ѹ袋NYhbQB @ @ @B@a\LĉC^{~^{me%K+/|]_ /[om&{Ŝ9s;lGsssoG6lWUxEdc8S{vvMozS̛7owUgA,b!;I&UR=.֭[()S"m۶عsgiǡp@e׀͛c]HhT,0~Uo^ƽ@uԩ{콾@Gyfa}n-,}@:]]v۷oNCߟZ)m? @ _\g@ {w;/_rK{'.]Gu԰#ه^#Y.,xQ]S׿|}_&6uYqV'pBd_{d؝@~)T]d(@ٝQEeǯ@vת5]eNjj!y/WW ܮd" d쳨@dK;"^Q`lh_i4XǮO= pR?kVa,,M>13lk׮!Eal$@ @ @ @p3/91 Ϗgyrʕ+_"v}ّ}X"~_/v]Oxk "fJ @ @ @}7bժUe˖Xp1s@î!=j`:蠃"?ʳz衸bŕfe,[,8sƨ* @ @ @ @X xXjשr'??Oӱu=j9Au9#/=nܵ'\y$M7|`], @ @ @ 0>J8'xb455U*퍟gEfx;#__j-~%K/ַu%K  @ @ @Ɨ@C 3@¢Ebǎ5GW^ySƱ;^1֭pwFOOKS=6;hΜ9/y @ @ @ P^݇?ʳ/\uUÆ/GydK/4PC 2/_W_}udwnpm :_{A @ @ @Ku| qFsi{޸+^xq<裕7vaO>d~vA>;<_̟??J)wqG'NLt5 @ @ @ @( 4 RۚcM6O?* 6gΜʨW^>6` d;BCGBg3ϭeEdeKWWWl޼;R }~Ptmsi,z dLO8W,YfEuѽ>  @ @ @ @ jP @ @ @ @ ' АB @ @ @ А, @ @ @ 48 @ @ @ @)40 j @ @ @ @r 9+ @ @ @ @@  )̂ @ @ @ @@C  @ @ @ @C  @ @ @ @ ' АB @ @ @ А, @ @ @ 48 @ @ @ @)40 j @ @ @ @r 9+ @ @ @ @@  )̂ @ @ @ @@C  @ @ @ @C  @ @ @ @ ' АB @ @ @ А, @ @ @ 48 @ @ @ @)40 j @ @ @ @r 9+ @ @ @ @@  )̂ @ @ @ @@C  @ @ @ @C  @ @ @ @ ' АB @ @ @ А, @ @ @ 48 @ @ @ @)40 j @ @ @ @r 9+ @ @ @ @@  )̂ @ @ @ @@C  @ @ @ @C  @ @ @ @ ' АB @ @ @ А, @ @ @ 48 @ @ @ @)40 j @ @ @ @r 9+ @ @ @ @@  )̂ @ @ @ @@C  @ @ @ @C  @ @ @ @ ' АB @ @ @ А, @ @ @ 48 @ @ @ @)40 j @ @ @ @r 9+ @ @ @ @@  )̂ @ @ @ @@C  @ @ @ @C  @ @ @ @ ' АB @ @ @ А, @ @ @ 48 @ @ @ @)40 j @ @ @ @r 9+ @ @ @ @@  )̂ @ @ @ @@C  @ @ @ @C  @ @ @ @ ' АB @ @ @ А, @ @ @ 48 @ @ @ @)40 j @ @ @ @r 9+ @ @ @ @@  )̂ @ @ @ @@C  @ @ @ @C  @ @ @ @ ' АB @ @ @ А, @ @ @ 48 @ @ @ @)40 j @ @ @ @r 9+ @ @ @ @@  )̂ @ @ @ @@C  @ @ @ @C  @ @ @ @ ' АB @ @ @ А, @ @ @ 48 @ @ @ @)40 j @ @ @ @r 9+ @ @ @ @@  )̂ @ @ @ @@C  @ @ @ @C  @ @ @ @ ' АB @ @ @ А, @ @ @ 48 @ @ @ @)40 j @ @ @ @r 9+ @ @ @ @@  )̂ @ @ @ @@C  @ @ @ @C  @ @ @ @ ' АB @ @ @ А, @ @ @ 48 @ @ @ @)40 j @ @@ 4 @ @*к @ @`:7n<:Y> fOӎ?>8dVh} @ @ @ @` l_2zweر?Rw=-}DLiu]C @% @ @mGB [_:ÿwƃn|qI;~ @ @q&յ8 @ @U@#@ @Zin/fO; @ @\ / @ @t]j>3.mi @ @ }H @([O<.Sb4B @ 0 N @4 Oӎ]_~̬Ɔ4z @ @@ 44؄. @OO]Oio/}:I @ @h(ܩ @m{U-qCgwy&@ @(@C @ P {~连'2m=jNG; @ @`|   @,0wz{|,[?rC?Xr[KS9<0|ܜhjj @ @@c 44|- @xѳ"+];zcBK̛ZQ @ @ 4w @4@[ks:  @ @`x7J @ @ @(N@8{= @ @ @ @5jL @ @ @' P  @ @ @ @ 5`l&@ @ @ @h(^ @ @ @ @@ 06 @ @ @ @ 4gg @ @ @ @@C   @ @ @ @3 @ @ @ PC@ @ @ @ @@qug L<9T@kkGhsssv@ eĉQ}=JP]VgL4) KRhjj,#{S/bغ7:&铢E~prSG[[ۘ֡3^>[V{E }Ր;fCz]wtt^@ zij @H茛]w>f>%8vnkٯ_; @ @@ 44zzzv#^%U V [ٿ Ȯ캶(@v-WEbu;_@Ą * cn mѯ%kw;\03>q)=!07v_HW dwfϭN(PZm// fe:eYHA@!YPCrդMߪKuUW}'PZٳgW~ٹq.8N n>cƌڵk3'bQ`9s*_zέE{ _q!u{ ?f.ׯ k>fa}nv G&]]]y? }fdDiGb!p @ @`oF˱ @ OWx6t @ @ l @(RFh.YE @ @` @ w,N?>ؗC @ P@k1 @M'ϋmqm=} !/b9 @ 0 @R8f-OѵIZ͎ ֞PE @ 0 y @ ̜2!>so="|fS,[]{# 2,39^yȴ֒? @ @@ 44ܔ0 @hmiQJ"U @ @$МR1j!@ @ @ @d  @ @ @ @ 9DA @ @ @ @@k @ @ @HN@!)Q @ @ @  @ @ @ @hHnJD @ @ @4 @ @ @ @ @ @ @ @  @ @ @ @ 9DA @ @ @ @@k @ @ @HN@!)Q @ @ @  @ @ @ @hHnJD @ @ @4 @ @ @ @ @ @ @ @  @ @ @ @ 9DA @ @ @ @@k @ @ @HN@!)Q @ @ @  @ @ @ @hHnJD @ @ @4 @ @ @ @ @ @ @ @  @ @ @ @ 9DA @ @ @ @@k @ @ @HN@!)Q @ @ @  @ @ @ @hHnJD @ @ @4 @ @ @ @ @ @ @ @  @ @ @ @ 9DA @ @ @ @@k @ @ @HN@!)Q @ @ @  @ @ @ @hHnJD @ @ @4 @ @ @ @ @ @ @ @  @ @ @ @ 9DA @ @ @ @@k @ @ @HN@!)Q @ @ @  @ @ @ @hHnJD @ @ @4 @ @ @ @ @ @ @ @  @ @ @ @ 9DA @ @ @ @@k @ @ @HN@!)Q @ @ @  @ @ @ @hHnJD @ @ @4 @ @ @ @ @ @ @ @  @ @ @ @ 9DA @ @ @ @@k @ @ @HN@!)Q @ @ @  @ @ @ @hHnJD @ @ @4 @ @ @ @ @ @ @ @  @ @ @ @ 9DA @ @ @ @@k @ @ @HN@!)Q @ @ @  @ @ @ @hHnJD @ @ @4 @ @ @ @ @ @ @ @  @ @ @ @ 9DA @ @ @ @@+ @ Pm;kGotLh'Y  @ @hHh2B @FxMq;ޥc֞aOsgn @ @o΍ @ < [æ޸5W>#;{ @ @[y| @ @ oa][f]wY'@ @hdC$@ @@팋=U)v%p޴wU'&@ @HR@!iQ @ƗU?~ԥ @ @@è @-pu100P4B @ @CsB @V`go]l[{ҖF @ @hHTH @ tk];^] @ @@]ʩ1 @{'gQ7%{%Ztib/bA+;""X^|A@@@z\Y=vr|}yfؽ CD~9;ZWӽ&@ @ 3U:J @ێIGq2iK# @ @/ H  @ P<.L @ @@ehyK @T{Odۉ\:N @ y 7S @:!Iؽ&vF4&at r @ @>;F @q;vwCFg^KD @ P*w @#0lPm|{!5q:UOa @ @Psh @\XS}][?NQ @ @ohh @(k?7?~u./5tN% @ @@% hw @T* ߾~vz?w  @ @*K@@Ce͗ @ @׼X{i+r[VH @ @# zH  @ +7=8H6d @ @@m zM9sĉc=~1 @+e˖<6u+ɤ= @ @.nʕ|$-[Gy.KH @@wlh 7wim .sB @ 7l9絡!.|0C! @ PAe٬˶wZ#@ @R@@Ce͛K_Rr-e] @ @кlH vR'@ @T>4Ջ-s=7n>4*C!@ @aT&CcQ7 L: @ @У[-[W_o~;:7u @tPeM`ɶ J @ Ч4Tt>qgWXvm~4߾G @W9!ԭ: ^n2 @ @@e VVwX ַ56lИ;ӟsio`͑K/mNK6m|0͛ 1a„}cСM3{xꩧlMlv1vئZ=OHAs΍ 1M2%߯V+@ @@ 􏏝S|gwt>vF kzj @ @*S_*s{]fO<1E]]]tPN(lrr|\rH~.,.]ZRzqio}+f͚՘W88>|x!chZ :;xK_ڡ15m9 @'pq+w Nm㘽&v  @ @- /R 1_!bUO?Zlnݺu0&OG}t~5EZVx"Y[)}oxG^WŹkņV\ @ Pm20GccCK5xs@zE @ У?]iypA @wgc0zگN^SȨ+UJ~hKt\+azC_V-7z袋+ȷqG?67ѸDx^8?>3w#Ҋ SN38# И|xݸD t/x=Ҋ k׮m+wߒ2$n H _k-_z|orI~ 馛J @ ǬǪuc1c8|[A"@ @n_CouvXmokv=(NkuY% QA͋͛7NO6@`ĉmR[&L0 hZпx_W^yeҥKϏt=388cm;.<^t}lٲU uuu rs*U`ĈO}}}C kaÆhX6X%Z\|(^=J @37*N)0?8(d5M_6q{ 0L!_>֭[W8H"_{ZSnGn @ L7&JC𿻫%}7 oo`{~C=4>9sfcm2nVݴD=`T62@K*]`!lܸs'S[ذaC 4@ Oa4T3@qA$x ,hxTw:],4,.r\M_5J @@ 4h!mY7)ST-6p*gqFwyޠ$ @ @ @}G }g.+~$sLs]ww}w~bΜ9o}q܊ @ @ @ @@' 6Dyk_i?ny={vzqqǕC @ @ @2DƠڵk#4M -).vmU$ @ @ @V̨.8㕯|eh#Gc=̣>x @ @ @571cŋ1ʦB&Lh @ @ @ P*wDϏ.Vo8eʔJ j  @ @ @ P*{D\pA|ӟ4{f0`@L>=>8ӢY @ @ @ wmɥ3# / cwv!jk^ͼy1ȑ#cȐ!wٸqcDƏ|Xn]G\ 4zUa NT@MMML0!?燷U0Z8-Y${S@% =<^,^ä2f͚Xre#(~}>yN.0y/B lO[ Jƍg̙ @ @ @ @@( @ @ @ P1*ft @ @ @Tk#%@ @ @ @# bJG  @ @ @ @@h6R @ @ @ P1*ft @ @ @Tk#%@ @ @ @# bJG  @ @ @ @@h6R @ @ @ P1*ft @ @ @Tk#%@ @ @ @# bJG  @ @ @ @@h6R @ @ @ P1*ft @ @ @Tk#%@ @ @ @# bJG  @ @ @ @@h6R @ @ @ P1*ft @ @ @Tk#%@ @ @ @# bJG  @ @ @ @@h6R @ @ @ P1*ft @ @ @Tk#%@ @ @ @# bJG  @ @ @ @@h6R @ @ @ P1*ft @ @ @Tk#%@ @ @ @# bJG  @ @ @ @@h6R @ @ @ P1*ft @ @ @Tk#%@ @ @ @# bJG  @ @ @ @@h6R @ @ @ P1*ft @ @ @Tk#%@ @ @ @# bJG  @ @ @ @@h6R @ @ @ P1*ft @ @ @Tk#%@ @ @ @# bJG  @ @ @ @@h6R @ @ @ P1*ft @ @ @Tk#%@ @ @ @#P[1=Q @3k6l[]Zչ!kbC㐝Kw#35 @ @ h輙 @ E͛o?ӱbfu1d`M>_|]l7%= @ @+ rN  @ P1' /:3j;6kv=uoQ @ @F% @ 9kߘ:zP*>0╶萗B @ @&Pd< @ PNW|?|<>Mn?~H8mbPh֨  @ @PqS @lm'RBYb}^&֬o!' ɭ0 @ @hȄQ# @ #E @ @449 @ @ @ @4 ? @ @ @ L@@C3 @ @ @ @@o hp @ @ @ @f$2 @ @ @ @3 @ @ @ @@3 Hd @ @ @ @- g  @ @ @ @f @ @ @ @z[@@Coπ @ @ @ @44#A @ @ @ޞ'@ @ @ @ hhF" @ @ @m =O @ @ @4ЌD @ @ @ z{ܟ @ @ @h&   @ @ @ @4 ? @ @ @ L@@C3 @ @ @ @@o hp @ @ @ @f$2 @ @ @ @3 @ @ @ @@3 Hd @ @ @ @- g  @ @ @ @f @ @ @ @z[@@Coπ @ @ @ @44#A @ @ @ޞ'@ @ @ @ hhF" @ @ @m @ @@ _pլJD" +j @ @@yvqNc:UPxi#ouI @eNw\8T @< @ @綌cέ0yd]|{Zo_{|܀ @@<*.SmhT<ʶ JP @ 3?}#Ʌk;r{T  @^̾cZ$@>- OO @ @<`AgԙsnD @@w? 3Ƃݎ @z4T\) @zE`}}C\xݺ%}2ح6T&@ @k=k[ucK[ȕEZв\ @HO͏ūښ 񻻟V* @ 5ZjY][-4/>6C @rEt)v2F @TܪkY, @ h訔r @ i-[Ϭt*̚:eԖ! @Cjgv1g蠚.TQ@@C5κ1 @ @`+ o_߸9-Yݽ+2눆 @T C3e֖ @ hsl @5~znL @@F4&"l+NiV@@CN @ @ҴCYR6G>Q @@ ^ D^ HmI @:* @tI`Gu^J3G4m9 @m uߍRݳڱ훸Jhh @8r4U;tF# @LEٯ|QGȵ! @h茖 @ icSF t # ?81 @[YMLw]÷TW"@Y1  @ @Sj9T?x̎1lPml @le3_1=ߩ3c9IemW! @kPW!@ @N ˸HOv^*Lt= @W>!yl\{}Ϭ 7oV7H[Ϥ4g:U  U1I @HKӎ: |cѰyK/7zX痴mq @@ TxS?[lU7_z1/ @ 4d  @/m?*R?Yb`C3r 3& XJ @ k)av7&@+ έ @ @,7$.xrƸ1{X6א~\0Ø=xrp:E @ (5D @Hz;SNY@IDATEY @D m3q%GŽsVĢU=?.~dtqcm?Q&T :sM @ @Y_>wH?W;?q;>m$ ] @ @ @@ ?`_/H @+ @ @ @A|Ǣavc2miC@@Cu̳Q @ @ @GyjY|O 4b5qŞFG)v2Y9m-\RwSj#=פ6>T-'d  @ @=%pƙ?`|ٕݿ%WgֳeVf#@ @ @@ ~":M㳿{8nu2SȲ:!([ e;5:F @ @x*<֥Nnɭ^+ū6tJl=Aj2٠> SC*!;]G߿wmmmxT/?oPÆ C4*ׯ_ю3Y .0nܸj'0~ 5*O$,PxM}kObw{r͆ ⋧Q6$Pcǎ-.~yĝs3';j&!@@u˽)02J @ @h"0x9W5頁5}zj:sC?yM&7K'gҖF !֭klG1A xػ^˗M%4ohhUVSe/VfH4Y&6nXCm 0 ZRbŊi["Z7 ogq7(wQzش)= z$5C ?sZ"wCG@ n_/ @ @(+U[fz+ &>2V]٤u @u_=h+ @ @ @ /P7 J@ S2=?kd֨ 3gTjN7U5# Aj @ @$0m̆e[uJC4 ,XYݾO$p]w(cBhxF @ @ }z :41̛7/}3fLNy6ƛ7osٳ_~1vؘ2eJ}l\O=gAń bАL.^{#Fh[M@CO @ @rͭ+[b7b<۞/~f̘4Ų2  @8rq̞t3o>dZ1mdDX~Sf7[!47ַꫯ| G}t?6+$@{%7c/k$}@%6mzG~Ie;_wuo|#E8͘1#>x;nBG!x},V\y{7Mc555׿>={ UJ6.i/SOE]_~y,X$;ƾ{sVx_1/|Gz@ EI9rd 2ƍcōT㣶6/_^"Rǣ-G]"SD8ҷR?~l<">b< ? nW@z ^ ,*N wMaooq:nkZ@% >OT5೷?=sn&8mfdV%5//=;~0Z(NZxGb+[g0{[N%;|^7tScƍ?@cf ^ktI'~?_|q>򗿌KmO /0awb5\ZO䷿m>bҥ٭6RPWT6lX>p>0vZJ[wg#ёt!XN%:f )ӗo;+jٵazk p,N=i\O 8Ӣ ^WZo}k{oJfB h0Cjn:(rٗ^zi~3n8cWZq7" @4fJK힮|Oow  @ :E^'79FlF)L}xnVHe)0|Pv;][eS]H2?-;qH Yfx׻Usƫ_hi[B{c-Ӷ<@I^gRB )%m1r7*"i\_I+3i tI7X rL;(`ˉB)Vi^(^ҶwxeH/xxl4@[N"OrOLc b-̒xU}C0m [wvg@莞*P= y5{R&mUR#h}zH($oAj)m𖷼%䅔>N={g,ZZڊ⮻* me>?^Ro/bx}cq`=38SKږ#-ݳP3 @p_ v2F @6ۏ/>:atf mEzLfvme) G>`>:g(t4>4 m!V'M? M׾V|$a_K*69IJV{iˌTHi two~3̐ Eq0Ci%V,hm\ZX*MSO^?rZ$&Rz>]}%/H+yt4}_j ׮Hc$/̢1 @ @nlI&񥑾) @ ur1J,ʬS5/vm'ڪ_O/)>N骫ju>E/zQINRD{}Im7g%筝l-9Z^ZMزisL~ۄ4/i ؓm7ޤ8Rˆ#k.16$myvoO~rЇxٳۭW 4T,# @*T`ƆXrC&_qs,ZM[tH# @D֌mU%}ziKti6lSq{qJ[Q3fh,nݺ|`D%xPws(;c+|ӟ_ok׶{WUm Ph`k7pcZ$? Ҷ {n4m'ie-믿$siK+f͚O?tI-@\{i[-[b_QCq @(W Ag!  @ uZu-:˶?^ATr;PҥK#VC=C!=XhQ!+vexޙw].V[(BA jh/eUvk|%H{r!>ذ{_NɶK:ܹsK!IR+g)MG.)Z___r^'/K7 @5j-1 @@HK/]9k_Ǥ5)=owc5-\4}ԨQwj;c,YV86^g>>X'h<4hPWTSSiΤTEב: 2>묳K.)ٖᮻ;.G^"m`aO]G[=P崵ԩSK:r2}{>yƌnr[J[-2+_*m @$P[?OO,- * @@ @W5ģK7?a51d@u,|mv]vc=-VX .,iN(9IGRԧ뮋;mEF}WҔ)SJVHM ϟnlM`q饗FKS-4ώN:x^VFKɶ[_Ӽ’tmڴiij Yx,+޺8ҏ>KO @ 8|}4:#@mշP1bD 60EӶ0>#R-;9vW7o^򗿼يMדm7Wy ~)Nuuuŧ>N_s@^( @tZ`1wtBcL) p# @ ܻ>V֗~6SO_MY.Jf\>}zIMᄏ$+'F{;N0hz[sv3N[^~M?uj}c'@ @V8gWƿ?$>vN @,\wcNВ8hܮlT=\H崙 ,_<S|i傴AqJAg>o/ߧr7 Nh|ϖ*7 J+VկYf7xcX1`G%d1cFi4 L(Iz&LhK.XG @uo#vd2caw Na @ <|c˶VpͱbCǷhrb زlЦH Mk  we{8 m~YlY^SA4uTcE{Ӹ%;s}UzHA+(>qOvf͚XhQyG4tTRg @ CK߱w.VM\v~1ioe @@s+,^M ¼nlVU:xv*];4ù?qK.mV(|ɓ'%\R?6-[ro~(h6lX/--ԍaV__|quYqw3gΌ'%eS0Hq@O]r6NsiEڲ3˯Q\C->u܊# @ d8qʁS-kb͆M1d`Ml?~h8z 1e;E @Er*d’ w^gȮxK^;pE{ޘ:ujc^[7pC|+_))Ę1cJ '-8<|O>9N=]~zp%74^l mpGr4Ϗ-[^MtF]]]inm 㫮*Tf76{fK=v#iB:c0c<_P=R!xu q @d*0nx]vȶq;|и_?yX}3dʭ1 @VozCVt8{Cn.x(+IW}(LXuժU :6ngJy䑱{N6mo~#mPHicZe`yiw]9[[*WG7%N;l (BEZy#6#)eرU{ƛt =O X򩧞m<k_((D@@C  @ @ @ZvoSd0uLv W׾Vҕ96l(/>ټys>8.Ύ>$p]w5f6>Ǎ:`kmE򖷔ܯiqi,Vu8餓 Oy|p ^ 'X;P(OǧrJ5)NA>hI^['?|qqƳ>Xtܹq饗ǐ_H)K.)f5?񏗬c̙3c-^# ,qW(^!mp6{Ofmd ӧ7HA7gqFMo+l)֑WVbΜ9uAZ!HHעY)UEͫjWFYP[xqWS@`˗/u֕Mt@W\.\)J\"P5551a„.yj]q͓Ǔ+6Z ? @m~1a 2ilNzSfd֖3"mt~M҇>/M[3D|qĉqENi}s%y-|qǖ\zJVj(\L.7tS r}IBܶW-9|yw-'+~ rfoE1geׂRÑ !h(~}^[NG_d2eXgV%5bˉֲeiMAZwܱ6Cv5& mep 7䷠H_1wW2ȮWii덴HI[MVR`E?tfیdqZunEرc#m{GGP ѸK @ @ |Y] fHuWN%TCU:̐Vrۏ't&@7 Vo%r:r;VK&u+3뭲o֫w-p$hb6'xb#N>.j~OړmwӦM7 |GP @zVsWƍ/MmVvv3* @ 6ē]oo ع:=E  @疯?xr]l:.W)u1~ptr+!mܔ][? MH @Eo~Y?v5  y/|[rǏ,[EM;$@&5^<.6k㓴Nj1~0CbJ6YښʪO!@^]΍ @ @OX6++ܖkgKn'0 XasO! KϯiMDm&uGj @o LR'hcp>OC5xt֡콕'@Pys @r-[IlhūcLzZ`A.!>D[!&, z$@@O}7*v @F ٠G ɮ:!([ e;5:F @2&EDv6ȷ=-[& @@o 2yXfeJvme)  @@ h(۩1 @T@mMnܐL2jȀ;̷2[E6ÿ0:lM @/ CxKB- jK,2  @ @ +vs=yll󟶶f݋ ؼ/tٵͮN 3zC;!@n& @t@}&/n{&ZIM!@@LR//Ŏr @ِؐfVfn L:-N@k &# @2a8a#kQG#Z#ǨOw6j–UT#䢛oʰf @@;> cX @ }Q lJ>v@L`NP Њ)LiJOͰ] 4*m @,Vjdn7<-o~"4CQ[CmtIqm !@ @%m-"?)߽v;$׀[@ Qݥf) / $nd  40 @ @,P\J)7Cekٽ] @@R 8=\v1={$["B@r !9_7 @ 0HSOG+Cqdԅ @I+&(Zh}..ٝ)4@ +}i1@ @@dMv6?V @KDDIT;.csyřځ @@C6 @ @ "6OMD6c= @Ѝ n()A-Aٖ@H`4$A @"{"3vl @zH7 CH7㒋 $&@ @4Kߋ'4  ($((\GG!@ i5i^*t @ \j^!"ۊన @Ud4PiI1GfiGHg @@'h a@ @rM# J!m@ S-Xm .hD ?uS@ @@ &zi P& @ ri'&䚣Urh5# @C ahl @ $@i e42[sh $@Ru R8!@ @@&N4Αim*v @$Ru'.;D[[Q!@Z C`  @ @ Mic,yQ틝 @@ losi.@ D?)n1@pyC{=%lr*0?f9  @-PaCFPK 4ۙd4TA8^@><|E:"Kl&>Rh Y !@ ,/X&c`p?;JwLDXiL84  @+02r#ojfȢc|>?  UMn^gH>*@ "BU@@<ҷ5 /šC`}{M4JJ !@6,ӅfN6A&2S1 Lc)O z0p'  @C rKA܍C@@ !e^j Q+efzZ}!Rz l2_x@iooꑢҭ&WAM)Y)0<, <2إ(LạL ( $tEXؔ>!@@ !_At-q0K;4%[G@@BlA/&~ Td'MEם1N,Tp.jwo!)!^4z,nJ@ gIܔ"ҳ9@D h. @`^tm.m4/:!@ ߞb b{}7 njΜHKf)0ay`Bv֤ @@j p0'"5$3ht+DW]up بq!@ $I Nzm@(K5 wQ[SSsc)y іݮvw@JO..79ELo  Nz^d (0uT2qơ!@BDn[P)ځ m=tk FN,9e\GO:jF '%ݣH{])Ux @v f]@TjNI b4eZhLZ`V@ G24C"D mA~,dKiGHg&w5ySN!`h xO yhH7:2n骚nC hp f`g\::-}gKT 1@@C< @ 4DÏhe> @$ްC@KD;wO_2 {G@ [0$S@IDATݪc`@V:i(>~X  ;@b/`R`hZ&~ ]G:5)bTA[]ICC :[j:3+Nn[c9iޡE44+u00҄ȱhȵk+A$gPYsRS觢 K:VVV_Myyy4w\*)) ٘U2LIoݺV\Icƌc9233xhݴm6ڳgeddPQQqTVVt@Zhr[n&L@'N@#^7l4ÚǶ~zjmm3f)--m #|,^%cА@)+Pe7xvP @=ݨKs,+A D$P)eSztq98ޖ>fW;=.wH]D)FʱȒ.RfY ,XB lnzbn:gRt /  PF~Fz?/xb"9C6lP*((~.\^z'_;v8+No袋.'VGy9<'L{dhp}tR__\}ڴi駟x_=-Zl6̙3_?~6~ke]6h{衇oVezy%1@НlZ eZ  63eG 3eY&wD2r1# @).цZڀ 4_J.r }U9b=

7mz̃UhwOW_M7pà`\.}g#/rKlڴi`otw7392wqo f_|q0}7`p,];<%*Ē%Kq+ 9{ĺuh޼ytwW DUluG [`θ/R>3Gp8W8{]w%?Lq5(g5#*b  [t.%k+ؠ~#J+p`6@@0S-pm@i"չ7k(4NC͐h@ ԵѝDcqK9o}u3s\IOy}cp Qe6Z^k'gxCQ Ph{OԐ.MUz!r-?c&\@C}}=-\PN)6>h׿SdÆ 5?OƘ!@INH9,X贊4*H 3!PCX@,`6s'I_Σo&Q z$+=vyP}v@H~'-بy o%49 -_NF%/'< C K$pv!=>B f׿#N3(\q\pP JGGoQ~8pVA%%%tPp`B[[۠:WSWWN333N2}ᇕ՜邯+¶9p8GSZB mB2<|>/@ YcGQKj{<^ _ |)ge"tH|`cǧLth'O-[&۲eveeW_)σ-p}E݆o]== (Ar#Ṕ'UW]Eoz5!@ @ Q0w?I?=u|{ΥRgֽ-o2 M5zhߢh<[7@B_9x#֪AiS2(ׯ̽+߬a ڦ^F ,Xֲvat`.n {!?ђ%K`ԅ35lڴI  @ GUUtO]ۗ{MOLq V^6i^Zm@п@W[ ;zŵ%Sht!0:*M۫wF5bճ8P(O?f͚Egq=SrA IcO>yCR[[DYpҘ1cx m{8N5]׉k?Wrss7顇[˕ |GwMoVQ @b+xOH|XE'L)teHhoٽV/ q¿kx@ $FNCXMF:864DY߀u0@0+ǏמIqŊty:+?>uT9s&q~={6q#<"O=tBP^^<4 ]v gy\37pCE1cu]CjOU% J)-BM=h"k|q hصk+ro1O?8 ܹӷG@ @V!G޼n|)!|[!@ lR O! R⤏ѷMNPA }^斃کՎRbV8yl<)NR T%./Qq>C=Β7(ux:-eҥtq)-pg}lr2/78 Qp։/r@D Wlܸ?( SL!W㑧HKK#<P*h!n (GyfVҠ I @ M=7]b8dSCS(b͐w.4 >Nt3Fh vvs2)Ce.խuPu{6@L8SatNq=͔.! vw?-\!"Ņ?<@2dXL4$ỌKEӂC 肩4555;(~_Ȋe 5gϧfY jj:0EѣsT/؊'|rss*-b…d9B^ށ9===tgӦM6' ʘ3gNLeŋ+z][@onP/o{8mmmrZ_]zA @ ȡ'ԭr)\l(dŦ b i@r3,tά2Zp(2KiQ /N׽AvOd rPC qI'y: /I$ 馛3Π|x:ꫯYf)ϣ]/nVMw例kO0o!^/sfѣGcޕZ/7T t/WWWƍ9)':D~O?w^YƏOcǎ%5#h۶mwYnSVz˩AdWN/\8bxoD( @ _LITb.mOiF" @@'RL/O&=mk:+>8 -.eVUZ u<@ V|? g)//'9餓"&N(mXcc#G$Zrrr訣LB/.'Hxui"/İh"yf8 !@ @`b/[N^ZNh  @Jrl4udeXfO#7LSFfSAfdw%Kb#-QC; doYN/*O?,pI4s@ @>Ӧз{:4饚{Z!C<͔m*  j_HmsӣfP4 8<24l* @I" >{~ߐf1X0gfO'@ @,pe]ڦHGsϓpnr tFX[h-S fꙵ! 骧RCG>X@,44M;  @"b=C[n /0!"*-]&O @(~,f}5:=^ZӖ'So^]&4_s8*BjЈ΢ЇC @Hyn238"[^Ln֞x /Ͷ@`O9ϸqwߥ>^~eՈ ./7x#eee ' @[C$3 ,5vWlwSIf(˚1g>VE>%3JWĨgzkϢ j@@ ^\7 ]VEX[LL0x>lb>9.eÁ @ *~ꩧJggLǏ캯x @RL`{uc_?}!el[f SXҭLt3=atYî-F.oDH4@@v^Ft]tH2v͖h r h ICs 1l @ mN)?-Cx3_;rNPY^T7[ Lٮ^ղaʋj_@#O5/頭RCs79b.`HcV@D u& nB @%jw3FwqP+0_*zuݭʕY2$!@з=~w"06h)4\Pl @@HM@ NMcvy9Uy:% }qAr>T@Z0&[%ǀK@@}ix O]LKdĂIeZp%AH)'i׮]dk{1Dc} @ %!egriϮM -ZSQ?:,ӡ*=.:RϴU#PaMmQ{Gɐ v 4ui TIvȳ,!@c$#uj!@ $ K/=T]]Mmmm?h~'!@ 2~Յ(LOhBƤno멥[^)(յtMcOt[P0QHHAqvn6P/q?U(` d|)8 q?[i&ꪫhڵbG @ XO܉4|]WA@Cp6^6rk?u}C懬 @]N/UhgHA #L41Bi2 ]ʷ *Y6.D(n17G9_T k@ жmۂ[!@ $S+*| ߁a_M<}E<mrkn{'y^2]G_0ꎡJԪݴKsh&YR N]Tٖ> A (͉ex0J@H)e3dddPyy9eeefK @G+0Ad[$#~BUHCLkCNo,CR]-A2p&)S0JS tهЉH}HwA}X-ߢ@ V  =܀/Lz*Lf@ @ ' __ 0csB9';u h@oe  gY ;A KJcvT)[ Q!-A@ "DŽ $@Nq1b\Ǝ @ D0KW]$eI\D2˴)S[E f(ʲ&/t4@j=Tmm.iœ_ W[\[1*Ա[ <UXK)!IFĽ̆%:dhuxW߆^)gG܅dC?_-} z뵇 "oA$X@,O @ $@ J43gRy#G&?Fl CI'" _ kӖ 舧B@<8 @z[@Ø1cPݰa@ }䮢}ϷNvogbu @q(JwXd[qzHH7YӅϚH6s`#I ⌬,BzvkK㰰;Y4sy)=B | sԀ OzfZp|,cH 6O {,mr,JRgmq|D+}J ;E @(P!&!b4ghƱ 8'2}TuXY4>6f،h<]ۋc").Oab2X4[4qI|=Э5@F@_(~ 7PAAe駟i} }kڽ5RMRpbWt7 @HxQזR"Պ-%tsM'-ް?}Sm'W=0^@@ Z9ŨQ^ҎpM7у>HN3ư D8-ڽMv*B =47:Owg}ٺ=^z}]\E=w ȞF5h-)0c(ko2*M˅Gk A NP gqkFtқoIW^y%mF< -Xƍ'*AfڴiTG]@@:fhNh @`Xz]`o{)S䭼"A>@moq{k+;h2 4MQE 2MťA( !рp 6v4~Fb*`wkȶS@@ $UNj.֬YC~]S\j AxAzO z[z /†-6C@Yb)O[19fq)uM'Aӵfg?C NDIn {hwmm-qJ0C>*v!o3 [ >]]ȻasM'9\ Sgq; Fm[`Z5캁*M}u]ZvzLe:OM@o q @@ 5ϟO֭Kt'vgXZ6>`@ rqrCzb8 _S3M ;U&g3,̈́z{_e<}Cf /mb%J6B SN9cі-[0--lk +!GAm}THcȄ1A |!(BU8VCBj3f i@D,.(YSb !6N!"%4"Nq3N @^E xywhǍGr -X0_<@Ž`K7(Sh @ [ Bt+תϓ6\YRmPMFRYsdLCv pP`4mD~S]vif8 Y`tA*@D ۊh @б@jkkN-))˗Syy  S)KbR GrLԅ @J;uIH3,ڴO{-_4IN`/Lyպ7ֶ"x2p!5,,T@&*Hj*~*N7H~Eq8zR=.!=8Fj Ȑ~*fD4D; E n S: 7t WjQo5 ~x_=  x J: wSu;qe@0Cwzq4eK5 42gȭh0Jʳ򏶖7/phydY;TzbI&m:\ $.3=nW\b΄@&?v9=gΜ7Qvę'8XfQii)=Z%Ojjj͊iرr[t{$@LXD%Sh @ *}nqm ꒰fBڪ=RH:kNKE="x4F?v 'A@i~D4SD@&Я.{G71+W30#3ϤreI.K905+[viu^ccR pgVpj쿁4!@"xOp,HG[wSz}i0.@\Iڲ30Φ^oӈϟ]``9 Dz+**(++Kkǎr2-t38矧Z[[-\Pfz{{ߧn:::B)D;F2T90BsCh*-%qӾ.7<葱"J8w^uzHMvwǷ{ֽQS=R4!|W{Shc66;E\ .&uU.lKXPJt <xcJa X v K/g}V /@W_}u+e˖AFsΡqA}-YDJ&ݑ$Mq-s=GEEE]=ѭ*O[M&wqSrFZn^8֭[=C)A ] ̢:f(h&x16a @v7핂z=m1Ӹ\tG%R GB Hiu9kY(0mȎc4I2bڹhѳߚ{߽*,s4 3}U\!MɷPEI4g,LܻZAb3Rg)¿Q @+M7Do<Š+^16߿sҝwIʺ .nFzV/p6۷˫V\|/mF{n9n/1N?Rvf蛾7Cf鶳)x@ : DCk,ҿO[\T- =Ji  #Wd!Ԉl$uj(M{iZ{m{[3!]R{JJYv2[>! B)򪲈> +D% (T+m0uT(==]އY v/ g^.tw f_8Css\E?U~AYYf#:<z=!@@@.\Ti 5LTwׅ)'*mAGQd[u!GNi* ߅e5v"ߢ#`LFq3gu>:*"~/"U1N@@h4pN8zw4 ?я/ŋ͛:::, ܟÏ_||R^^ᤓNRVy<ڶm\+AGN;Myi޼y:M׿l'tЭ@JF k4I*2ӭx HʾmtF|!3ipPˇcC) '.Iƣi WH~0o[X[h, w2xҾpO*Ʃz{{*O=qf@pӏT__? O0.R93 2 !FZ?:څ P+ja2CB@0* ;Xc[fEaB[cY"{:5 ]VP m|num.4h{)w:.v2Y h :'4CRV x e;p+4c Sq޽T@EEEvݻw+D4? fCrV z?q&@z~ⓍjŴwo@4%7j ؠLw-2r\c9!CJ4 tB".nqsnMwSb_dBk70%櫬^ HuM+c ⚡l6ŢߓQ|բE駟V^/X~BY^W8@iiiGg1|+\Rn31̟?̙#O7x|!+qfO1'-VܛUA B| %4HDfpt@Ы@\KݹVWWSmm-۷/^@O9p===l`pJ^^rne) ^ +> O1w\ 믧͛7U9!^x~ma>rgy&hl L/:[~@@@M^9x2;{oߣL!}@#@iWzEȦ*tB!ji_w1{SV\HE앙y o[Jm֑th鱤~AsssISKy[~ɘfI9/ [NAɵ]AjiTqnU|i_ŖoB@4ގfi&GzWAIe@H 𮚧QL (_6d= v],=a UUU{byy`x]w]{5k1 mʹc_sA;;;u9$QsM89b}}6rV$sd/!ë8cWCјO|DNdNnG&}gm\NvHڝ]t. ½@ !/N)+\0Ms{kmH sӣ~/6!'8"cq c+0$T2є)S|Dnrڗؿsv'w۪+DРn[t{%h.zjllC9[o%g7a@ C炴?@@IDAT!I5@ )`5U1˫1C<-Jp+fmYa dZKi,@9N"4L.հ]L5E<@x?wTԂ `=͛7OUն+Vݻu5&A@t:+{s>k/KjootTHuˢaA+&OL륫>c%k0<>#pǰZlR641'?2md~@@lfʣtKԍO-ͦz-ْ ~*R6 ;mjl aliuОaD@ \q/7AങBj6C˄F @3}T!@誫N8!ߩ%JI3+\qڹs%\Bo6=cm۶<@wRx7_q8ŐD+C#wwꩧһ+7JSzΜ9裏k:i / aJ)%SrPzꗔ`c"PmNvs^!q!$AgVd8'ϣ[(teYV:bDfDERն;eyiY2x]i,w~KNòk{WTb.w(F8 svh>ODO/+փX#Pe#'ҚM ?l$-;"A@4%fjfpo՟CR{kNՓgMFFFt6z{&dVk#3ݦ; Ý:u*5R1 h$P>: ⟶?\|Ŵo>z-_o>`2uB( u]u@#\ g+YnSOQII tg'DZ,Zު ܂g^Io}H v aIt#Y#Ƚ`!f`Ip͛G~BS/lڴIy g}6\ *3,fƇn=/0@ qҥ'3y4i EV*ϊױCsǤ%+t ء.Om\ӈ+D%0%B!D6#UHAp(HTsSӟ:o2*[Q`a>c*Qux!p)s&ϫKfy+b  0l[W} =z2Ze8Z>ܴ" J;ʲU P, pgy^{5c g9T" ] L+diJ~\,@)npRtnvi6ozZ]}^l$Q}lvהEڧAßѷ!VG؈EZҥو3@ .= "-c2Ye*P#nM3  4w:O|E 06Qn_!06#Φ0{Z)0#98ե,2< Znkk0סݞҰj!T1v 8j(⟋.Hذacɒ%7\]]Ms 6BHvt5L)g@ i~Z^gnW;kmks v_:ĝ f%R =ce/c68 ')PF,M-")746jvFͽТJD/v(,/lJT 4uwEd26*IYc#7*s;wRUU|-sĈǏ2< 庾iun8Ֆęhܹug̘AzjZ`'?Q2+ '餫Z gG倂vsc򜉁m:&_?^nqwܡd9 )!W ꀄ_|MQ5+3m4SOVn*O[_,~PYges@_|AK)X:%%%b 4zhi:N{._TRƓ@@L<~.MlqnSD4;rz&l|FEX[h ΰZt -]mն Z7oߡh} >ӣ@dc=٥)DB-| GǙ?#zGi„ J-Z<s='㎣zHF駟tߧO[`&!ZI͝k/3%\"pfuegN$ʃ? o (_[$jF󏚸%QcTbML(|bE4 Kd`齺=5TMuOuwUݧtխssީ<!o~3fs1wu|K/Tl mtM3ڦWsԟ[ ?|7 3Zߞ>kw㦟0v?g wן|;ޛ;wsUR墋.&+,fhs6l biT2Wm۶~=Naa0 Yg% Ccll,I0ZͮL+a1N>dyGp 3\ &իo&, 0ޘZ[[~?p8a^{_݆ ^m\*Ihx:zF/7@&Ac1ux+5iE;d(w"%hBXO!kTք17-UJ;+%\/#P]JT$o\9ǥ]od<\neFa(06iFj8sfbUl-Ŗ|#k7xcO}S򖷼E5Jh$O8pte˖ɏ~#Y LiI0#4+b3OPrfN;9bFg{e<(/2gy3 ÂxY܉5dsܾXZyz7q7UuV ou}ث#Y9$*Z\Sv7jt^sPA{SF/V6Cz}zDxiZ7V+nS\b3S3̷_UvdscތnnB7V_dng?[4!m볉 暸I17'P?fG}4cF$l\2wRk1#(?p ED(fn3ZdZhYt|_͎0Qlj O3ԋ^hюdoXQ455eG0ëR)|}cS'|fNH$"'xo}[|axxN`ߘ]ux! jRZ~89tt:z,hv5ٮ!#`rS2;TIgtpD\ؤ xs-FzmM zUA38C[$|GǏ\4#8k`hF7[u /0_L[fGŽrmF0疧z*eu2YR̈ zuxb[׿.###s?M\0Jk֬))avL 4X,dbY㙺M"bSo*a\LBYS&y3⋳?חb1qs1My@&k_isнnN@J"A!XZS/m̩2@jH$2 ,_N\"7^)ɴ,mp~_|h=iݏM"( lZ*I.eF+so|c~M]tQv3RZe%5aݺur]we˚ܢ;vBJkkw}=(ٽ0|L$4;s0r1[~z Gkюg lْh3?[nG @SK32i2ws'n@UΆйnbyqYҦ{A|SO4_yLX:CzE+o ["^QS"PNէC3|h;2.qǎDG7=}Mtv:_W07~3]իꫯ-ryIT(e1m- fF_ HoxrՕ0FQP`,}&fޕ{,nk_ZyV9h055sζz)56@ͬbxjzQ/ PS-!yܠs⦚cUFUZRV( [] Dq/jTk^O; j*Kײ(+^~rOTV_{α 9dc3rYgeGjo~s\ۗ}nȎ`Fonn>\5W,Y̨F_(v3?Kg 7Sfɔ-3g^0#%SB8  PSa;M޼鷦@bmѠ,i akp;ˆ}5,mI{612CRv=ψw0as6F!!iaA8}uWik:TrJ#d[/@vK7ʗoyJ7.mk^UNoV/~!۷o'O>dvŋx-ВHwcB!i3rˆ rg„SO=U>OΈ]ʓZ.PJPg^UW]%?Okjaۿ[M2@4;uk5ӄE Y@#uqT?% e/^s8 =0Й#7׍ii vG@@kDsf6;o^$akҚ6SW T,wV\/"@m rAف+nN~^,^O0ݴi\wu3įk[ }QO~"x;:0<<\t۹V G?0}o'pB>ik_sTW@1OxCP%zCp8宻>{ 5}`4*En.s4"QgXռ\59BÑ^:p"T3{Ӥ~#n3TwAF!q֗p3{G@ '`LR9VJ%Tm^Ѩ\zrˎ;d˖-"f%9vNdzzi8PKߴ3Kh„CI___yzị/VLg:}{,+(|EVX!MM̧Z@/=,ϏٻҜ\V? @5C_Xv6z"Uo$5FV0')\4أ^Y)zK tYIo-(  {.k t5ǠBO ޑ/\,NI`AA-M׺ԇ 5E_uvȍwH-c.:mn1[̔rg2>>.Mp˃>{kOG#7ƍ7(؃T*%gq#VZ%z׻qEMܒ{okJZ r)Eio?69^-Jg7/| 444@@_˺:qi.tUVk~w@ Iʑ3w 'eekXH^] $*ߩtF&jQr'REwf\H@*ecJӥ P(QwɶK* DO P]t+O7F~y$fY(D^r6ї-[Mf0M(d~̷vA۷O?=cD&)b|S* 5'>K.D^җkuɒ%]odcMo|/<7);w>۳g\tEdHEywϘyVBD5a@K;+x`.p_elJQ@g%Gǘ;z}_JO id`EA6%FCCeOխCj%DJKfnȌv'W2?J'/V[<9L?y!_ si Ͳvڹ ɤ%1Ya$dCp@R}д1LIf8oYdwN iɔ$*!o⤬YL͛'iZ^#,HRvя~)\f`~D 3*ſfUnnl2H:3B]w%{Ncʉ#s]732W``22]{\#P}Enγ׺F$]ֈ 'YI /_(vGIf.@ +0a&|VZbeģTet5FnS,%J)I(E `%5l%)\f}f7IM=-ֈ /_D2,:S!wz)mo3IO~"?3(J`7S%֭[WI'$fjw~\+VdLD4Zލ HD(&ŒP>vi_J>y^l쫉7Ax yӛ$]]]Ū̮7w7}MH {Cnyꩧr};`ezzC8tPվY\7;q_Y]>IN^fcQV06C.f"!L6 0 ?hmK 䤙j 466<~%-=NR]+ 'g5WmkRcԺw'^VJQߖ}"tliܦ g Yp1wUITJ 9c*R|{˽ >,s tvv޽{?SSSD[I((g1=P\sϝ񷻜x>3FV^Mhkk]dZ}Ud♄j*1Yr7L<ƌtaXߌ[祽I_P8xdSmo)HlnxB+ h'3Z/@ VGZtZWyT:`Z%)[f> &vk\+pV7kahRJpEO Qyg_朵iTcш\rr6#\s9vBxn[J]GLd4r$@p@5֢KMqƧa1zGyP]0RSu_1YhYUWNUgtZ@'N /|$;~u}$FW@@[ ul\ jBC2;dI6fQTw$XUo !5O˸aAmOWk{Gbro˩  PD 0gΠs@rFF@@ύ$U5X"-͊64L!HZݤuя6-ŵZ{$!&R2eMe5$2 Wvw$Wk!:R$PYi>^Ea@j- >*0"p"}ġY  NBiվ1Xm8`ֽ\ERM&0~X<;_g%3<1۬2=w}ּ=䪲'M8vr?i n`9ڍ@JN4D/;_Cp@Ȕlܷ;2)qlY&/-X"arr}ռI32<KI*a5)؜E$Tۘ0?֨ ,q؃qF9yqM(@]8"Z`F%NRQP Q@PL2(p@HNhr'F(O`|*)_}7$9i;!7?+;}/ W (@4O~]^\ZTfqj<-0^w\O2IΕߵD_vS ~9-4Ռar^m`FwAq@[exkZq:NI ͻ2ԋUNEKcJd\a/gh&7ԏ":}Fpݴh+UޝnApU'4 ;< 9X@Jv.!0_߻"<{h||Z\& ʦRk@J'++aXpx\IczE _( & +: @=Hh:u"@`Zj M]}T1o"+Wh^Zꅪ ,,s DbСsW+ PE23 N;iN ܛD@|/@B8*(FbQP/Z (NXB1yԆP@72:4h< 34*K5mR1Z (]MpަE@Xq.TvR Rʶ@kp@NŌP.w^AG]D@$4Ԝ p@Zj K)Rk@'p=dn.ن$@I&<:2ط؆-uUK e80#cn("Ҩ7TW+J!fU Ak@ }N(*aN|5f% L^Zld晁y@ Ҋ9qu{p0G֍8=#@ҒPXfx:-1@'F $  F8;?\۽@## C@$4yv4mchZ=C(@mMkN:7V="!y7WX3FbD/lJ"7Xa\PRHtV! ÓzhO]8>xML%pd6 @p;h  DT8^%A@/pK彗m(f~/4jaXڲ@U1w=K6i޹i 5.)yXjD i*Z"PhUI`w-|,"]G@z -p@0em+Z@?ox|'KkOl].zYiYcZ"zIQo~=klI̯Y^RHe&J^,gj*7 ;"c mf/E e;G@ۑt ʹ*W)NT5BH@Glt@ P3j*B@lmx<:5vS"i-h[6B@9"]1 78)dZڭ:,8M`,pp"%ZMIJEN; <۞`{ǣgws@R*Yܰֆ)7wG0e (UY3@@>%D|$^/',Kbv}P6ȮJ7! Z@֛p@OYXr[ۃdu%   ͻNUQHy3׶T23sNmxC RZxS$ʮFp:b5@ N"X<@5ҙTvʉԳ%MC?Dfm( xA@sGc4#qpĚG U^BR؇ZGLhNռi^,F@@|.8Kr$|ٍ cS?tFu a@@ !;ol|iJuS >oQ&B\-@ azn nFM]*@zgLy@(E@[U)Q-0/W~9dc+fC@@N˚Cj5orR֨js:U`Ia VTc^FX5f: O$Tzg.(OL&A4 N"X<}CI(   nViͣ#4DG0+:U @PvxS 87Q4ĿIo%սuྜྷGbrHh(MKOh*Bp n{Eڎd8}v  nh_hkI9:هQMTAC-HP޳ƙrNJ[;Q?_/Z J%֋UZ) fg{ڎy#}v   k걗mXcprw=նμB3)WA:Qf7(|v7\2+ Z.-` .?M/V Q@$4||&3#Zd2KQB@$7ih<-2ne$3vgp"{* D Ux_@sH`{ĸSIFo&EՌ(!8V@m2wHZ1Vm @(O mM׮tOE5jxהL\#`_9ыegD@@<99 ;22*uaѓWMVP&#KzS/N krA@f LeJaC)I*γ>u~Um6@2Ýs  ,iVRK8WEj,E39 TrJ߁t}(ibuhV#P1!C޼e튱|+2 H/3ʕe0^\ . &~)3)C$!Dξ-jJSX!P=)ſ gG** P 7nKK腒fV11X@(Gr(z7SE @@,k^pYSDouӯ0'@k{`",0l"a?7Gu Sno7u\x74e6͑  9һsFcj#Cj 8Y x})P jk㲼I5& $d EiwٟepR6vrdx R@wL'W: xSxu1[r&w== E+ܺ]qZy@RHh(E2@ 9˩Lj@p@\̻u˓KlJ7otZN;у҉Դɳ ΤGbx:- ك!@xb:uB*6 e`4mKw{T^S7ԭmT / R 7N e 韐v2L[CRmrEf mɂ4pIa,?4>Cm <\M.bPF% e ąO]ZVG@4ND)e%Ej"wJO.#t45VJ Lbû)яBe*Pzh=u"عs8p إKʖ-[l5|׮]l9餓lśkcvU`}xĥݰ$3 \ ~HZ'GLf(N/ky@@{4L6cDͬz˺%-z4SNË$`c-E h~zisTߪE//Y^eov)xsn\~~>%ëF`:5 jB.O_OS*ږмoJm~팆u$/ -k2;Bó?MQ "J+Ur GqV' 荪yE@TOWqD*#oφh$sitN)dR)0Cj<-`[W(M@hF+B`!pU xSxuW1OJ,5" ߃{|Խ#*@/;` Gn^K=wD~ܰ^ Fcq ЇbMмa$sƺTF7m$&NX MZIW&Ұ!F9s#Bͮ9]uFwa@͵:YprG;;׼FV\eȄr N+ :4d8}@@J xW J˯P'Р"Iz x2}@yw(PMHfuQ^h dT)a- ^=Nܯ +*C@f $3qybWљ/̌@ݟؕ9y*,/Zضmp?3D`AS,SjE5 OvDkXW/Sa4_c5=狕dxx 7X= 4bT«NkTz\id2o;rh/j( 5Ħ*, $Ԛ4" 7Mul`,@S间7nVGҢaZxo L(^3wN1A&IhpQ@@w)jpZl) ©Ա`PV#H:}sh(4共*x@v%===uV9s/].~ӏAy矗d2)7nM6姮XhٯW1x^{8B #>_wEg5@@k / ###s?tD"lfyWlٲE-Z$_~ɟuˮ;;s??˷o~ XB^ɵ^+|;WZ7Fv]ǥ p&t+J"iDfRE @G (ޱuTh  ɡҰu.?vFcƧqIâ1 6&WhSx_" G{[;Nꫯ7n?.e~};?g?r%H{{"fćOb+y6|AKFK_ /P~H<^|۷\ w7oZ{ކENpe E fbme 6HGSXF&_#h8M@3`_1^@@3ue㲾la_jyߟmǎ;dϞ=~/f5y͌u'?Osk?$)-]vYv3K0 fm۶ɺu>9G>eFg83eҥ'lM~i(hŖZX~_ ~??/4:,zE @G yɖn6]r8AiѼU?i%CF (TD-xX@3U-h ̂  P@|Wb4'S"kI8%WLq!I8(LOOKn4ps ~O?Cc3/~ b1^g}V>O_tE#??țlIx'{9GrE 3K3B$(FBkb@׾dXy 3B-C6'`QyأF'3i3}?ydנVL `4sZ1B@*O V]&zůպ„DPl)LtXlY?.>w!s,_2+L_W%9sMrO}S255ݬKnYrqa455e_;rzǕ˭vs{Gy>hHg7r 8+݌ >XEu7-=#`W Q fcۏ)L-SxyPqa˖-b0`ŲKn6I _%+^|ǥ<0}1 ';E/} WƬvVʬ޷*@@yb ?*lGt;m!-i|͋^:q!d̮.0WIFX#\[ lwȵ5G羨QP - 4\ѹ^r3ٳ'̷av <صkWDCCȯ,Uݳ1ob\,9z,/n9P 'r:w>9ODz;oy?9:=q/Јx\ۼIViX$MH냉#UnT^)ѠSU0n5L q%͛eժU2;vN7qWsT^ںn3f۝o4=t\xV t5\%k"gXM/M'Yl|Dn.E uNX>oNZD))O"9kό)' Lpf;8"|6yZ`kaOZs@BNU"Iũ ,n´+^m۶Ikkk< u]gq._ve4&4,4݄ `$ }Pvmo.}CB\"aYKk-#A4   Ztr7cI|͹ҍ&iI/P@-aY^wӺ#^)֔q@?;ށ.n}{\C`lj:D5Smpf:+"۬t:-?}|=Dn"Kh0I{os©!*ې%/$) )cͫ+q<1sIg @AOAnrTQ`<7A:WuKc flY8v+_ !o PT$%(w93,:\7ʻS@S;O'~fXH`C9YPQ9jON]8/~ls IaBC zRGo0w(#J\iӦ|©1+y:Sɮ; F"2cB K;1_֩@,K,xқ*Xqc荦f4LK$uVɋIB ɦ):<'`F>YYSVTgNK-_W姀۲޾}{wcc\tE,[LN9}Ɏ;&0ajjJJ[nu%\":BW"@BC%jlGb!H>&=oφ XY꜠?{C詧fpSHQlkME荥Ҧ2מJ4Ia88$US950SOL8V.VkkZVxb U$x cV\pb i'̈f˶m %9}o\-?qۻ`y VzSO2,ٜm@|"`dm'Dnz]&KxW=Q*@ 5ZkXU!mމd_'a>0%cq>w  L5RÉ-˺%'4D:$ .6durp^Ǜ%̎(pډO|H$ UW/|A/]yܼߦ{_ɹT4#8|ϗk_g @23-Sl>T8@/Y#!3,ktVو8G޿cvK%edՠ+*( PR6zICӒ(E SFY)(h ?+r+\}rZە֨ V&4?G,L^ȭݻ7Zc'<0٦W_-5mllLÇ^Z4, Y P7#} :w} [?@=pP^lb *7W&sze# 7#/4g GKQBp+Ik(e;=]}qi0lUhmr$DKB ha 7¿=wtRyDn߬~{uME8L {A@@ =F{@H1 *$"ikPMDGV"v6K1G&tS/!Ҋ I#l   =WS57&&)_NdFv~ !nVZ  >9#0WҒTZjB*-KTU#1KUm@"c*PU   y<  DzCE.z@)@ %k$`sW9mP<?81N ᔁ/v4t@F3;5   wN ̄,8ˢgI$@+7W="41'1UmNO[D5 Qyn^A.M|&"  KHhpɎT[`4ӫVŤ" LBik+_^eh[6B@K *T A8]- :ZER¢7[)Q_̫   Gb"@^BJVOy(C c2X<-tF"]mѠt4p F:DL9pމG/:dЌ`^x}| k5Qα]RUB@@@PUQ %qnsSP⩌NȾLYg/Mာm ˦ΰ l,A[wϏTlZ"[WU6CEZ&VVz26Α gku$eVUQ Pah )  8W[9oh yCղ_ELdFJd L ܿ%ĞeG۳! !T-o&4ēi | y*?H 'LLxh."  qzgߎ @xn4)%Qi=}!^fP<T3d+#`G m .\WAغ?J^7S/F TD-xX@>P  )@BÜ4[\"p8.Js Od*-~fPC۟PC*O]\{vgc9lэ7gE a3EBv{   @mHh3 xhIA' 0w>l%3Tz,mmٞ;Y " Ꟑ)QDy~X}DTx04$T];ة֚&Vh"̈́Fj渡   pLB,xTB}/w,% enC(+Zac<2WNd CUg~84̿yUy%6ϫ$cL-ǂ xI$j/O@`VEzo5< P:w̧Cw1R~Y0uꅪ6i ZEfG괌լ.*B4# N@@@szW0=GC@T:b]5KWL7103x5B NXޠ֤%E *PګIXsu퍡53Ң\9#(O'f0   #*ک!Ъ6!G)k [q+/"\~<=-Qi4  0C`t21'Sm4})z@-lFW&R   .@B:)p@$ФpQ- g8f#g;/޲X1NVC@(3:LT=+'4@@@_pvN"Pk䪵8yO 8aNFm   @g 0xҢw3~.Oe)k; Iy(Xp@8]ۭM:qFDs@`psа<.fa  8TLuP pԇ]Ѥ؝&|J;TnҧñX?ˁ#aU4?gd2|֬Ү",TA@+ }%$   P JL\( [MJ4  Bm<}"FbӪ;SG0@-$4eON@@ ASXX -:wɺ#(5MΘS:qDc\/0>7rf,({@wQ $@3ea%  x\`@)J5ZLg?8$ )Eь(!@^9;D@y |CyPD@wU    y_E|*o`pfN9~mg̿)z#DB]]( Bj{@6.o떴yxe{2㗢Lz s_]Hs|XR)$y 3C96nj>ulЭ-5uߵ<&`l>Bˑ#G$1uH3;,[먴 R0TLX,&eDQ'Pq<%֝"xv}J]@HZV NXѨ֜zE  LuGh7EPU%ZV(  SzS7h&" 7-iΝK%A@Zzc%;xA ?I/F  e P&@J2d8~NAq,9/:`vj@@.@BCf5!PT*#UIk TZ)JX\odZ-@@_`YGj&RR%F@y"  HhA8@ ;:ZrhhGƧj=IhQk@B$U[`$Dzmw G@}C@0\8@@Ij@X/v<:&FaAo3Y PtJ5@@-ݤyߚ@}3ӗ"".Wa"~оԫϯ~ t(TXK"!PW߫ & LLR4  ִ1+}B ٗ[` 70./kuZ"j#E *jBn M@n @L@\/w!B:@* dEB*`>k-K h58 G#{n xU@LW>MB’OYeeJ',T L" {dRDD$ eur#["xu瞣 ~ /{~"@FR e*Wmn%PTrVt"M6g(PGBE^A&zFz%5B O5]&a,4t@zkl*C@]!Yb﫰IhGz?7Bs;h&⺉IEExU`Jqޝzz/VAB 8G+@VKhXcs Ǖ".lv8E<[zSh%_d6j,fRCo)@G B@@x;:5"0@&]HxtW\ -@ϙZ'kwבܾi2JۼBo@D>)tQ@8&{fX\!ޝKvڻnhdeVe1D Gt3`Dy!vhH2N=eAeFzͽ}޺|y_FTݼ|~7LV+X%[/餳c~ Z7s.v܂IPB d !Ş#]1ܪ5pb&0Y~ LHΥ֘oSo=>ןa OX PdyN PL)εC|lLd!5GY PpQttT9&*&hkkGzR-xO @` MBUL\! $M Gׯ?<ە9Z|M hzEf)I}j54l!TR 5vE 0E+W =5ˍ SԦ @@|[V-=:)s{n $5k~v7,qӑmr@X, p|" @ g) .pr3tt~C'O9Jp . @d́4WQ N iJ0\ DD% %0jҽon 09NVd%yv!@)73pϹNN|}Y$dmfyFGdO&'ar"(OF 6`-\uk/\Tyy8<Ь8r"%p~ݩ,$@(SIdKj",&⌢L  @ >-(J'54,e.7 "Tj;}ឳ!1?=G&#`J@| rC`r$71k}A TF@CCeZ P4k-q3n'S_OKsur&pŜe$S ݹ˚ @ hhثr.C4ٙ+n: S9JwFXY %'=FQE$@g  @(b  @)kjuGZi!a] |s4/lgE46cȥ@o =uJ @|_5%@xV::?UK5DT^WL|6@@>n>f 0k;Sn`g  @ P8o 7d&@* 4jtkae;ob.ȣsqc}r"@QKGqlZ VqP. @Pܱ9T@`?szvJvοw-nRL.Ir@H:: @F0" @4>}z:cЇ{346xc 5(@79(^ȁLz ŭW-0ls @0 @\ύ5/n榝/x}tְd483r&A4̄F  @ @m,mSB%5#{ɾW37X 4KsN_12 LA3S؊MLB|d0 W1  @ @|:1%  @T9? [3,[q$)p5a D`V`S s8 @ mCf&~tf=f66wnGq#2ʶC/c| ?Y @ @hhX $эm@hw TT+!p#hE ;&^ H!N @H@CÌm\N r fc,C@ iBLSrbJ-p`) @ @`pLWh 0@؝zad 'pdi.IɈ @ @>I.0I4N#W P kݐB^ &j @"fE U& @ @@pR SX*ɺ^C8Kݞiʺe34uM`h\z+ @ @44%_+ȓ@ۀ6ICW.}<ƁYH$@ @N* b 8D1MhcMY Z٩]С6< Mf @ @ lɮ@@'m @ GgɞQIN,$e6J,D$@ @ Id>"+Bzwo)fevI kxƍG+SB  @ @b44zFvx^BoG6H[I3847$X}9?"l @ @`&,ͤ%@!":^U4m/m2@<" ~6Cٵ @ @PV(I`'\f" %rbX-/i0"0zdO0 @ @ 7c#3@VKW. +)p݁o^w@CC1rg}t`̼cR r7 @ @TC@CC5Y P@"t_DQgBY״SFh5{wB @ @ TPN @@;c㌝.8N'O_th~s}Adϯ؊' @ @@~44wldF깘F{T|8, /_9K c϶2?V,?  P$iJ @@BQ @@tg7b>M^L>&p~K z)9-~D$@?Y @` @9X5 dp7\~h'P!v );sp @hhP+$zt"a5e^"@`Ar @ @ r9,"@ ,6d:K38V34`(I 3T%@ @ P@CC P @ J +o.~#@ @TY@CCG_ [Nֆwi;)RW. oC\넆 @ @ r<8R#@ 4qgYH\*w! S̺o^*Y \h:'@`ZY\ @ @"a5(@#l UIXM7"6LPཡgYD`qNC%(n @ @H@C@L"P~F ,҇ցB @]/e]"@R+Kot  @ @44 dU'SWaXF\cPUWEY`ehGn"N_ ' @ @@|\V* tR7eʿ\#Pff!Iq2)ŭm@" XM(WE9y @@gp!+sG2LN`}r" @ ,Jny5jl @ KX @@Ճ0oh(>P;PG:(@-k"@TP@CC] z1OW.5_ +' +,pf+\`~s9w!@ pWE͐T[)$!AM'0cC<;B @X{880 @F)84s6*NluU]U@*#ЩL %@|X[ % @@y44g,UB%vHEq֡HABNlF)xNUP ?ʰ<^qUa"@(J @dw5"|LLcDx3cmf<6O`jݩmɆ @i |3Is 5j鹏b @@44`HXFWZW ^P '$vsB @|3ۮOzDkJ! @) hh @aւU`  @x?!-Fr+ŏ)>  @&"0+͉#( 'T虡3o_lDC @`4#Kisz4jqk @^@CCX PD1ܲk;wq:]  @xڍGL7&d33tl#@(2Z @4ا@+dfgV3۶  @(0a"V'2<_oKz5r=Ty ~ثP @&(*Пu3bPNL( P)S.ϥyӏ?>}C鞇Wۯ.g=h;},=A! @@r,J)p~#v:voȋՍ" @@!/0Ik[۞~?( @8XXi IJJ X @ @ @@944s\UEXlv 7bW9ʃ9B  @8c%S @44YDbO8Rۧac#PC7bB @ @PڡU!ayhq @ @,!ϣ#7@)"+/F^S @ @D፜ 4^7eg?2 @ό bVJNN^ʁU @ @44b E,{d_6ncr[6!]C( @ @L\@Cĉm /}pA. @B @ @hh  @`ZAh:f?2@`es~!@ @ @ hhX @ |GcjH34r E @ @R hh(* P75D\j^D1 MK0 @ @ȑ T @Nb_MCb+Ȃ @ @r+!C#1@M~ک*p`м @ @ $e$Av<4;VY,̇#Y @ @ @@n44vh$FU8܀ ʃPSڡ#@ @ @ hh(ߘJ 0WV) " @ @ @>J.+)pdPOAD @ @ @R hh(** ~bg|(#mO?ddB @ @>\%E ' MNJ \lUnE@CUT3 @ @;*'@ DNjʒ=8v @ @**lȷ|#Yg|xg*X TS f&@ @[፱  @z-E4kjE@״!Uv5WTġχ @ @ r<8R#@j tNޭ)Ъ{ϑxk.=qd14` @ @Ir~Ff Pav\BޙrZ\#IhАϺHh< @ @+!c#3ٍq-j cݠ)Hb Z [' @ @@~M} _Hwyg:qD[G*'O++2|i~~F(+ @}j34 eigmȅVh7\{ #@ @hӧO7MԩSoa}}=OX;/KKK%/yIz_\ M @ss(5jѓ& qbBȧ\-PY @ @ ~^1v:ma &Y_򮫟?>}H wqG:|礍kRn$@PV3!t|_`iPG@zΌ RImlH @ @=ZR'fvJ__NGGJɟfF^Mox479h?n~﹭x{n *PK?$+U;#~sHo # @ @@18Nw__#G~sq~xߝ>OJYwޙw-ogl  @`r81y4vwE@1cZJv\yh>}dzy}+ @ @O c6O׼ff3gΤ??o~ [w|߷Dۊ'L^ v~k 8vX3(܈@j߹o?pk O @ 0!.^No|wܑΟF 7|ŋ>YoduԧE~e]7 @Vڱ- x+\,5*SB xD 7ކ6k-'$@ @p$U gr~~ }S^4kd&7`g0w @~[u0(SK-pSJٿNtnbrZj'@ @l hhؒg6K{tW{7dqO8d}K_\$kbغDۊ;x;^җg=Y+H Y}9CD}C/xANB \CW];x<5&)px5b P V#rb@K @ @`ZƔao ;wn;fs9rvCm'|%OOqm? E`lvf?2r$@I vdcYv6 6e_b:f뮻feu؏Xzk_yoָ<7N>=V̽~v݁JTIMǠH ,>E f]!~yέ@ @@Y[lUYyheǤ xZ֑n]^TwUN -4[9¨ +++[8.ӯ~ɓ)!a˓=]{߂n7% PZh)zl=îh @ @ %p|CjHwN-6v.zt?Ûg3#p?YvNGC3N (JZF74`X(n @ @ P\34d3Y[[۾ߕGkyx1tp/~>`_io>7vm鱏}쨛\/kbd ;O1JD ٩PZvYSΙRpέޫO`OٹGQ;{!pp1ڗNuQfv͟Fs /O"axiii34;ZĜ@-5~Z߅@F}Lfft:i)KP"ukݍsҭ$P$Co- c8eI l}mh;do7M~A>y߸yea;Pb+[I`>&:{}N3jYk5~wھۋ"pA>[/Jm|D`1& 0SN n5%8?0q+?z; modǕAfdY3?3IOzҎ-zq.c} @ @9n뮻ns|o+_ʾ N<ֶ'o]M︲ 7"XWy?c8eWdMҷ}۷]6;  PT1py-G|tV7?uՠdP) @ @ @XN9Ώ=n; ~^ѣꫯ=:vW.wɚ2d$]|iZ~))~~/xۋd8 @^ 8Q=u}{:,"@ @ @@44h4^ng>"Oӷo]w?;o/}iz߽Kgx_}> @lgh|˶ά @ @ @dL6ےG۷+g?y.//o[Wm75Kɓ'ӻEyرtoWWi}}}e;ͮ_uUR`+)z;C)5U39T7jc @ @ P(أ%*=^{xvboy['>߷ۿ[[wokWVVV[tԩ Ϧzwegŗkknhfd?}(ot%Xh>EkJkN4( @ @&#МLXQGx^ofI`mm-Otog?;ϧO~ә3g67q?{n.:N+ O~ӧ?Tfs, 7ܐ^W}?mx+_y߁R6ç>x#[ۿ/N @@A. 4  @ @ @`Vf%v=yllF]|Ko{M7t]ۿGܿJe%N , .2 @ @`  YnAS l@Zj5 S6BֆXڢ 0 Yl6  @ @ަpK >_+Mέ @ tS'J @(Cea @@!ڽ |R-()a*/] @ i5 ʎ @(  @؃~S < ,y vi܍m @ @| @ @\~c%@8{ʉsN &oi < Ӳ,Pad;+ @ @!B!@؆u#bH3c&+POn@t @ @>r7B3vcAE[ ; 0Yn@t @ @>on 0 fZ-6^hrURs[(@g"\  @ @@44qTDb`Qx_^ ggɀ%8{U) @("\  @2FlCC3~ez`)*E)\(Jt* @ PF78j"@ AU՝r"HS(f8 p@=9&~'@ @+a޶FAa-HԻ$@ ,>  @ @PW<U`Kkz`yWn7 @@ɏ @_@CCX P@nl5cAE@hn@t @ @>r7F)';0eM;ٔ @`Dڝ)F* @ @ @a44 eY%hW4XΨx2&@ @ @ 'pC&a%ޞF>D44cdA.8IX.  @i52׈-.3 @ 09 ,POV|k" @}vx>.ֈX @SNh6H |(+b1a,T%@ Pck]c2j @ @`84 4REW4kgh @dW-bCT~0E%@ @@b 4\ؖkɟ0L*+pjZNh< @L):\v֊;lc"@ @@u,ـ*sQWdW\b Y e_m6H\vc$@ @`[|=z|l޶oz>5bgpN @\@CCHz%0W[ T=ai DPFlCC)P;d  @J%í+kjh{t|Q*  @ @`0 [ lVÊvX,TU`>Xs>ꮤn @<ڈ#>3]0fp!@ @@44TxN`j_ǺIcoeRJNEq" @M mE/+sj'{֟aYPO.7ұɣ [ @ hh(ۈim5w[-vݶ6en?h¢#@ 0)Z{Rcߤ!. @"},. P9^7+`nhug  @ @ZW}6oC` @ @ @`GKf\ @?Cյ4eP @ @@ᒪLVF)'&;b @ @ @E#PPZ砠C' nhXikh(鮢, @ @ PZG0K; #0@#[LT^ ?06= @ @ @hh )0 FX,TU`.:An @ @ @`zчK- *PE6!:8UR 1'tƉJG&@ @ @EpԱȣ'wq_߮'ZBFJ jкq"S0 @ @ @` :N&A #`'GϨo5I @ @)a*,P5)TEڝQ9'*') @ @W@CCqNBz)rz௖V*bzfN)˒ @ @аM j l:K UbZ>TuXM @ @LQ@Cm@:bg{KK >Ew{: H @ @HGK PXn  Hs{}J~|\leE.,l>EEu޵jY;=\/:{B<"@`]n-MzXKU4WG -4Ӄ+q˫z 'ȳ0HmVED1bE`[:v1tSom}Pcz**XjŖE?{B Yr>{B[[Hq_ ؊'i |}4姷l/^@Cf֨ӧ+P%7U0̦2А}0T/s6)v;sÝέM:aq;2` Z{͚.nh8{fAP-Szg̙3}J?Ѣb+쀂d[~U ۯYsNE3y_^iܛ_k a,OG9I6 gUp!@ ?bpHɤ7XM @ @S0%h!@݈ma-;o|a[iv6 D @ @0g[!P 0HR>ڮR pKԈg\]H @ @C 89$ U9(ت3*sd=+uP1{v @ @(#<hpA_ H(Eh?C5& *W<.7_# UR# @ @*'|r~ &PڑZZai DPڝB/ي,~{6rǖ['  @ @ @ =>#05Íij (@;x'Oo0b3{VZ4Ȱt'@ @ @@a44f$J`^{d@:3*U`8E>Zhgt8&ZB @ @ PL>5.fm&@`;6җ_|z }]bV|n&so9>KJ;?:sZzG7ŝ&邍 @ @r%!W!XM6M )Pe§P X١Qzoqz'}JZowӡVBkܺ @ @ 7 y@7ŝrbF%,R DlG1Xkǝ8PJH @ @{ D/k;n'@zK. @ @ @B hh(pIj)r@m*6j$@ @ @>\R*?p\`ࡹx @ @ @&-椅'P34dY feJ @ @C@CC9Q覍c@@@ވ ֏TB F @ @I OFb:S` @ @ @`nߦ  @^g׻{5hY @ @ 0# 3Y p9V/w< 1| @ @ @ %a(.  @os#@ @ @LX@CÄ'@y8o<  @@jW @ @@44mDCvhr @8#@ @L\@Cĉm^`3A @`7=`K @(eQ @]jfEM @|4Vb D @E@C(j!@hw t  @nn>{.^S޸fٻx9zq'H <@QDEOA)"* "XEEE@B !BH۽eݽ|>a9sw3w~s @ PW uv0 ;oaw|=iAֹG @XY@@& @AC˝R"@ ysmgTrKG @ʘ4@l(  й[umyOҎ` @ @ h @^CC;(5-}EH _~ @ @j%/h  t2I` =ONW}RFޖb  @ @Q44ʙP PC~5[ @@ o\eGeޗ-{>-MKR7*귱@=JF @Vi WyXUki$@ 7"mo @ Р}\ETQ`Q @ @ @ @ @ @ @@E*⒘ @ @ @z(; @ @ @ P$&@ @ @ @! ʎA @ @ @T$ ".  @ @ @ @zhc @ @ @ @ hKb @ @ @ @ @ @ @ @@E*⒘ @ @ @z(; @ @ @ P$&@ @ @ @! ʎA @ @ @T$ ".  @ @ @ @zhc @ @ @ @ hKb @ @ @ @ @ @ @ @@E*⒘ @ @ @z(; @ @ @ P$&@ @ @ @! ʎA @ @ @T$ ".  @ @ @ @zhc @ @ @ @ hKb @ @ @ @ @ @ @ @@E*⒘ @ @ @z(; @ @ @ P$&@ @ @ @! ʎA @ @ @T$ ".  @ @ @ @zhc @ @ @ @ hKb @ @ @ @ @ @ @ @@E*⒘ @ @ @z(; @ @ @ P$&@ @ @ @! ʎA @ @ @T$ ".  @ @ @ @zhc @ @ @ @ hKb @ @ @ @ @ @ @ @@E*⒘ @ @ @z(; @ @ @ P$&@ @ @ @! ʎA @ @ @T$ ".  @ @ @ @zhc @ @ @ @ hKb @ @ @ @ @ @ @ @@E*⒘ @ @ @z(; @ @ @ P$&@ @ @ @! ʎA @ @ @T$ ".  @ @ @ @zhc @ @ @ @ hKb @ @ @ @ @ @ @ @@E*⒘ @ @ @z(; @ @ @ P$&@ @ @ @! ʎA @ @ @T$ ".  @ @ @ @zhc @ @ @ @ hKb @ @ @ @ @ @ @ @@E*⒘ @ @ @z(; @ @ @ P$&@ @ @ @! ʎA @ @ @T$ ".  @ @ @ @zhc @ @ @ @ hKb @ @ @ @ @ @ @ @@E*⒘ @ @ @z(; @ @ @ P$&@ @ @ @! ʎA @ @ @T$ ".  @ @ @ @zhc @ @ @ @ hKb @ @ @ @ @ @ @ @@E*⒘ @ @ @z(; @ @ @ P$&@ @ @ @! ʎA @ @ @T$ ".  @ @ @ @zhc @ @ @ @ hKb @ @ @ @ @ @ @ @@E*⒘ @ @ @z(; @ @ @ P$&@ @ @ @! ʎA @ @ @T$ ".  @ @ @ @zhc @ @ @ @ hKb @ @ @ @ @ @ @ @@E*⒘ @ @ @z(; @ @ @ P$&@ @ @ @! ʎA @ @ @T$ ".  @ @ @ @zhc @^X @ @ @J4T*&=@*c*g(; @ @ @@4X @;#tg;! @ @ @@54TCQ @T=vhurueG @ @XIӍH @/v0lРޯ @ @ @*_AZI{A`ҥi֬Yi̙iЛƌƏ^˗/O/Bzgŋѣӆn sH@_~l @ @ jhhA _|q2eJZlYI)]w4qtalhaɒ%׿uO~͛WlСi}IsLy47c^UQ:g薛 @ @ @0D{+2}CJwqJ ٳe]&Mv(^9gΜtGfm"… ӵ^N84w] @@/ }UR& @ @ @@]PW?) <(\s56h,N'ON1tD qI'iԨQ]J>-ZN>lȊ]imM}MwߝSc=6}0- @@Mbx͇M]츖+gG @ @ @W4*{tg+wytgW_=_wA}3Lz/.tYgig'iӦet!?r))OTqM ^~}azqqe`X4~&* @ @ h2D Tȑ#iVdi7O|a1E/R\?~k 鳟l;C_3o} +/!@uдň.;3ÿΐ[{G'@ @ @VUʞJumݖ=yO1bDvf=W-[,=#rak$Əg¦>z{f.]XA'Z{7 k!Áo[_ CJ @ @4!'-Y$i'H1DgСCb艶}ݗa&<~%$w"JDDaׯq+M|Ϧgy&=iJna;vJe)gu~iE[\B@5Zhe+זEo~ֿOZgi@>< @ @ @* W,F?/B0C=ztI+VS"X)(L/BTVezhCdD`Egӻp,%\'_=]骫Jg^)Il&`֓D7Ꙫ+Z/V%~WȫrXAW5DjO @ @4@7yZLP fbO^{-_Wl33jԨ4x|ZD/~z fcG򕯤T]oP W3jyɈ @ @ @US@ Mvޣn!]zy9l|ś31\Ca ƌ)s1cFkEcӔ)SCEO |;ӎ; 7=7Dy~_e=DDwiwO1z>xުdހL @ @ @Xu44'ҬY̙3Ӎ7ޘD{}lZ,X _ꫧ;#F/!Ϭ3;Ch;/cwNtP?z,i5TpM7?Ot9|t1t57OCH ڣMӦkVaA"xׯ_@ 8E@m^7_mn;ŽQkkƪΡ2/ $Z"f(]f>^,ж߮&S,dD@yOi@ жz'8ݲ4 44,~pxTu?.\9nܸ rwq騣Vs=)X*gz/ruY'|emDyóQ]k߲KԀ;CC6`@*~t";J6{>?J׻C3nI@nS`)V9AP o>s+!z6a'׆I&Hq <*hXhJǭ#gϞ^xᅴk{-"}LcǎMGNqV-0fia;^['9h\j=W'@ @ @ @@4t~;-Y$}#IlABztgC/̝;7]q{bT(PUUa{Gi;J&LȺ,_<}φ{mVz=v8+ ӢYs_yc'kVVoOv& @ @ @/ |^I+qwyt衇:]tEGy$}k_K{nK_=_LPrs(gtuw#׿vWҗc;n)-m>gȑe=Dk$cMiڼ;߬~c7dO˗HӪcw0E ҥK > 4@DE@3 w0ftu@qSM,жr߆֩{v߰~9]kRZp ᳙ܟ7+m3K*Є߁9C&~d5;Ҵifm-UUuv?yO:{(A)`-w%dPD _j a9_S Oi-cߞ^^6=-M1}S44aih5+/ʑ >'޾K @ ?~C'F$AkfI̙씬@`[F@*%P(Qr6E Nanm3] =Lp/Nj7 4@/%>[+ܺ۽EU\@@C>6_4 6,Y<7mf5aW/$o!\SԩSW^ȶSO͆X)UgHj,%+9" Zܷs_ՂȌ @ @ @UN@@C?~|1cFVYf嵉)zRFUX?okVJ_3]Ri'NLgI_wߝ-ܒOofoo3_;}r&@ @ @ @@"ޞ]`Azg.wa3r<]<# @ @ @h- r>a}q@ِ˗/O_үiʗcfʗN ېh3SO?|G3W^yeꨗ^x!}{h|} K/e˷v[ZdIBضk: @ @ @h t<ȼ4ynP ҹ瞛|i顇ʆdxS<nHBa8qb{ %G}t6|E M=>zi}M;S4hP7ߜϟAQJxYĖ[nTz骫.}omذaY]g( @ @ @ @@ hhvQGeӬYҷvKٯ_,bҤInkf6C=)_i+_J` njw]zWӊ+"{#F?_XO}*E SLV= W''  @ @ @h CN4y K.$M07m~Φ1cƤ/8tA+ K'L?ϳ|;˫x>( կ~5EY"MR}yi6K8]ek; @ @ @4@7߮_oϝ;7M>=͘1# \'_.:bX [//zrjbxCb :4R"ˆmQxݝ^zg,Y6d,ߞYiYfϞ].үÇ(T=z@﯉@ =:Eꫯf5{}]aVm_Ƿ^;cx粞Vu'@q/T<)~4@&~Oc@IDATgQً&e ӂ Ҽy > 4@y}|MYXBԹ\m@n4l՚>oyy5*ſڪɃ @ @ @PRL @ @ @4>JO @ @ @ZR@@CKV"@ @ @ @- ϟ @ @ @ @ВU @ @ @ @@s o+}=Zkty4hP>o @ @ @TS@@C55[<`xT= @ @ @hCN4YP @ @ @ @D@@C  @ @ @ @44YP @ @ @ @D@@C  @ @ @ @44YP @ @ @ @D@@C  @ @ @ @44YP @ @ @ @D@@C  @ @ @ @44YP @ @ @ @D@@C  @ @ @ @44YP @ @ @ @D@@C  @ @ @ @44YP @ @ @ @D@@C  @ @ @ @44YP @ @ @ @D@@C  @ @ @ @44YP @ @ @ @D@@C  @ @ @ @44YP @ @ @ @D@@C  @ @ @ @44YP @ @ @ @D@@C  @ @ @ @44YP @ @ @ @D@@C  @ @ @ @44YP @ @ @ @D@@C  @ @ @ @44YP @ @ @ @D@@C  @ @ @ @44YP @ @ @ @D@@C  @ @ @ @44YP @ @ @ @D@@C  @ @ @ @7B!@ 0ъ< (O%4hP;vll@ ,Y$-]4+YϢr͛b9~]BbaӧOZxqV4\EF 瞒뮻n+ޜZ:jB <3iΜ9 6t| yҌ3J f@B`+hҰaJY @6mZZdI^QFu]7_6C :Z\  @ @ @hCN̩T @ @ @ :Z\  @ @ @h -s*U @ @ @9jB @ @ @ZF@@C˜J!@ @ @ @# uΥ @ @ @ @2RE @ @ @ @@o "ϧ'x" 2$7.=:ӧY_rΝ;7=y^[lE.]'6lXz[ޒ/=FL߾}ꫯXc4bĈԿKC"VI^z)͜93-X ?>k{>FY 4(3&OםmiweVw$c=荶uzWң>{iȑ=>\wl4Ȁ@K 4=y-XlkTh{nHz @x4gΜn[i޼yT96!zU &loEe2au]uD &Z_?A~y6wSO=5.SNIkV{Iun +^l 3;`+/})mś6_Z[]S$#V(7xc:Ӓ%KکmJѻBk74|vEw5ҕWkQrX5Qr}*ԹVmzwCA'  *?W򕼔n햶zlSo=:gIG}tp~7鍙SkC7&^auI (믿kprfH\p+W^ .1;FC3f;3Dg?ѥi{Soӟ eZs5. .L?pr!J_੧ʆঘvy}nG%'a*Vsϥ#8"{Pct9|&kYk!VzrmiSI{]i6 P jݵ޷v[:]=O}7F9+H\rI.{jYip})> @3ܓעj^jq}uNr] @ +uPre$@bƒx)fj=zt&T_Ӵiҵ^w?6[E C!.ySuN]tQ/~roG y f_GFDa@L@?^f^ cXg}rph/6y!U))vJ m7OU⏃mT/l3"= T{踮E/fH{lI0Cl;찴{[o5/Ew5ҕ[kQrZ5Qy}*ԷmzO @4YT (cr-y:N(']+k0oƛNNȆ(,WYC8`vֽ`+'}t{Ic[D@k yŎ3/Y$ٿÇgCgBCTxUX7kmugIz:譶;S{o9j Vfnt)s9'aE[wytvVbh{Z[רZ\zMgIG4@F-r ӧO+M34k߼y}ᄡT{?izgl㏖'xbQܫ:/S׿5ñz(z@>!jg}bpOU|o\y}6=Lne Ll)+&` Rݾڛyb{?y>򑏤誼S5kC73<^[رx4{t뭷?Oܭ хwqĉW*w# *C3gL7xc1cF^;}c˗˝U{. HG@5z=tEnҥ.ˆX|yaSNkIrJs=3ȳ8yחb ' 唱רj^jզ }[#@ZU@@CY"ƍކ*tK~UWO>R,\(~K`ر=~`_"jݨ^ .H_WS>}Y3,{~裏V+{' hi3=3~raO?==Ӆ"f'%;k=u@oոneh׿|]2pj]G uM&Mfjg!§KA' 4=ye쬞}mzP-1O@V ;O~_{)";bLdsP)-!(tC۝cU+K.1>eaF@H=7Ѣ -s<묳4ZL,ΨPa'EVwH%e Ի=t{+nl?]ro4RBg}v:1zsMW\qE:ӫa}7ԺVk'@luݞOqAm?蠃E]&L3o:/;/%8ZY̻L@@3ܓwul^רj]jݦ }cl#@ZMvFՇ@ D()O_tݽFa)AFm1ߖNKlTѫg?Էo}.7ؿv߈4]M1Eؓmӿ)F?&Om^2<7ov?BQSO=4hP:#bC΍[1E׾t麚U{Jvj%P& (^L1ܱ_l˜1c־dkׅ-2O^≮H@j{[kT-Ojr= @ V8@wGѣKJ9k֬SO x`K/v wqG~8>;3/ Aa3K|p^x!li""d7 j y%=0>eA4R߶#!RF>mڴI]Z[)* ԣ=t۪:dmMgR<" / ូe,ԵZ_}}6=,ne; Ll)+&a^?-B!C[t9"s^n\0Jz2U3-" }E믿~^[n%8_Ywiws:uj&V _|ab91\C LwT躶Dit4_H:?-f@jCWQߝh9S!@jdMJO,wP|;|dȳ],td3h{zSZkT=O:Fp| @( Ϛ2h>஻zd%KdGN묳NWUo#e+a1\GqG{Eu[?%[M-|l[o5G5D{\ߞ&N>яO<1ke[jnԻVu%"@ z#Cf;|gkv>3ȳe,$ &h{jSVkT=Ouw '@M@@C1%D'tR'>N=twvY뮻.OҪ9=yvmQ>ݙjU9bN;-ǾkM o(b7/,$@~o9U/71o .]۰ s7O0!Lzj{G@ԣ=t;`aַ$E%(c $I4@3ܓW垲Z_q}*k8 g< l)/&y?(WăiӦekGqD֞FOee?wyO.z<6Fo|#=y=㭯jZ[]3 *E]\jC{ |ſ SQz}٧Ho;O4)/E;RtKkQr8FUԝo\w8  Ь?jZ)7 !vۥ:(]s5Yy=ܓߵZ+ L l!`muTfvXSOv~e+g>X/b|ԩK/MsL{챝nhc3T厎a=#p-dmpѮxL'aVKo[>1M<9k/>,mѢEO7pCz2{|Z3iҗ Ԣ..C=mfԿ/.>Sqwʾ[\ZYr2\_ʄ@4=yXn^kTOuMc]q @( Ϛ2h";.l#t ~l_7 807._L ?'ݎ;U]飻xw[ z|[01,h!cn1F@t6n;U")] Բ=t=3{Y +YFV @ @ @ @4IT @ @ @ jZ팪 @ @ @h -pU @ @ @V;C @ @ @Z@@@C DU @ @ @ @& Ψ @ @ @ @'Q @ @ @ @@ hh3> @ @ @ @4IT @ @ @ jZ팪 @ @ @h -pU @ @ @V;C @ @ @Z@@@C DU @ @ @ @& Ψ @ @ @ @'Q @ @ @ @@ o  @ оyҞ{o>A^.ax뿲c 4xSo~3]{\)}K~DRvV4K)zWw u~)ۛyvKӡe`K{yT{]^s9;L?~!Xc>I&=%]={vO:묓&NN?,cV @ @M!7=V4EI @o#qكyu*V[ɓ'g=6D믿>}#IJEo /6liƯ_J ~F`C}z/vish&oaNC뮻.}Nf}a`Q^Yk.?!$LaGS L81M:5Oܷo|]a@rEpAS|WaÆ/S~zڨ2Z$&jKɈ!cloYc, ZB~P,N!K*_サ[{w_:q?zy0u*D_= 1!\xɖ,1C~4&MJVZbB @@(q% @JF ,.!t=y1\=Sm0C{1DŽ_|1uYܪr7F^z$c@M׌3&y!2K2 d,oq>nՐТE3۳g$KCKM665uLb߸F_n3gN裏r{o1yP]0C<1'B @Y+#%@ @bDmeUNwqK*'rFbL߾}{/sSw!<p aĉ.q{iӦek_};wnvw^HT1bDh۶mO?7Te֬Y, 1cuz\,q#ۗjOCD)1Xaƌj1($?HM j @ @@ hhR~'@ @@\pAkzu9i&%9fFHJYYY!\Z"grC=X-q+B{]Y} ~m 1wQ{l \ySw8SmU$qrʰqL57n\0`@OMI$N\uU 3g {쩩v @ @Zx  @rS# 6lv=w-]zzTi_rWVV&]m_Nj^d̰Я_@nۺuewNy*ߍ+?=jaРAaɒ%bPCw޼ys`رzm!;G @Z!@ @@J裏NkϽG8?<,_A . ˖- k֬Ib ֭[1|Lƍ bJ}gl ^1CC_2:>  @ @/ _c3$@ @ڷo@o֬]Q|%?CuhW7h[hQ9sfx뭷¾}c962%?,uQ>  @ @\t 1= @@|۔ nM=Ԑm{ ǏOx7&N͛ߐCSKQݳgO꒸ʁdHD @(f  @/PQQzhyyy&L^t㿵mۆ~O<18p`٩SliӦeK=jƍz]+u%{idK-Ur@ @\"@ @@aW_o߾=kNz^YfٳgzLJ>rЪUԹJ\ܒ m>}n:YK%`6mڄ=zM6eyf Y  @ @pp @ @ 1KêU/4d5W^I3s ۷OTٶm[Tc4+ ؽ{w;w&r3,UCxI0xV K, 18'~ [I  @(H9*"@ @۷oj,y^ʭχu_|Ejzv[֬Y~KRVV {޼y᫯J>|x='SN ]tQXlY* @ @@ h(ܵ12 @)?躖> ~gk;Xn]A䖋/8Ā`m(bPM]Jnq燖-[R} @ @H/A  @ @=k%)T{n%+¢EräIRRׯK_bE/wY? IDAT ={ ӦMK:<өJbcԨQl;v rK~GI%Ƽ[ozlkfUW]; @ @ _@@Cᯑ @ @.0bĈ8KO>9_͞ mС!>K.$3&[jb֊c%1A~}Ĭ >5TW*++knPQQw r0aBBN#D̔r0sd[ 6d1C Yg=37\s5/LFlڴ)\wu!~rߞlw @(l=<#@ @B={vRo sNRիWh۶m[ Tg^z*[ doVe۳ <@x' 'm̺/_ݛ7x0vp]we/^=n֭[^x!cǎ̙_-B߾}͛Cuql \pA2LeEc<#7&o!n @ @% CCq @ @l!37&bqƅ~;lJU va1 f9xW{bI&e˖a?O2"DLywL?㏯={$A3t5';ĭ*n馐;x] mbDB @z- @T# ,aBYYY/c:z]v5u"$V^Z]cõ^l裏&A"]t  2IIb0ҥK#Wc8.D{M6a֬Y? C I2tM~.Z("5uN @@gh @ P|I֭[!_wرG_C 1a͚5a!, 0 ?tСp3M6>,lٲ%l۶-~{R%fF1hfʕ[n!Km:|C= @ @@ hhZO'@ @ @ @D5( @ @ @ @дN @ @ @T# M @ @ @ @@ hhZO'@ @ @ @hQM& @ PSL ,hŋf͚5ʳ< @ PLi @E`ڵaɒ%,!@ @^ջh%@ @ @ @P?&|G @ @ vjqu޽Q! @ @bPl+f @ @ @ @l9Ql @ @ @ @4ۊ/ @ @ @( %ȦH @ @ @M@@C @ @ @ @Pl @ @ @ @4ۊ/ @ @ @( %ȦH @ @ @M@@C @ @ @ @Pl @ @ @ @4ۊ/ @ @ @( %ȦH @ @ @M@@C @ @ @ @Pl @ @ @ @4ۊ/ @ @ @( %ȦH @ @ @M@@C @ @ @ @Pl @ @ @ @4ۊ/ @ @ @( %ȦH @ @ @M@@C @ @ @ @Pl @ @ @ @`;LlnxIENDB`bench/man/bench_process_memory.Rd0000644000176200001440000000253614424725507016620 0ustar liggesusers% 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://learn.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-process_memory_counters}{PROCESS_MEMORY_COUNTERS.WorkingSetSize} \item \href{https://learn.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} } } } bench/man/bench_load_average.Rd0000644000176200001440000000045314424264614016154 0ustar liggesusers% 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/scale_bench_expr.Rd0000644000176200001440000000130614424264614015666 0ustar liggesusers% 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/mark.Rd0000644000176200001440000001122414424466001013326 0ustar liggesusers% 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 \code{\link[utils:Rprofmem]{utils::Rprofmem()}}. 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/press.Rd0000644000176200001440000000331014742271646013541 0ustar liggesusers% 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, .quiet = FALSE) } \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 \code{\link[=data.frame]{data.frame()}} or \code{\link[tibble:tibble]{tibble::tibble()}}. This is useful if you only want to benchmark a subset of all possible combinations.} \item{.quiet}{If \code{TRUE}, progress messages will not be emitted.} } \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/hires_time.Rd0000644000176200001440000000146714424264614014542 0ustar liggesusers% 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_time.Rd0000644000176200001440000000150414424264614015646 0ustar liggesusers% 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_time.Rd0000644000176200001440000000176514424264614014510 0ustar liggesusers% 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/knit_print.bench_mark.Rd0000644000176200001440000000212314742225323016646 0ustar liggesusers% 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 \code{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 (\code{result}, \code{memory}, \code{time}, \code{gc}) are omitted when printing in knitr. If you would like to include these columns, set the knitr chunk option \code{bench.all_columns = TRUE}. } \details{ You can set \code{bench.all_columns = TRUE} to show all columns of the bench mark object. \if{html}{\out{

}}\preformatted{```\{r, bench.all_columns = TRUE\} bench::mark( subset(mtcars, cyl == 3), mtcars[mtcars$cyl == 3, ] ) ``` }\if{html}{\out{
}} } bench/man/bench-package.Rd0000644000176200001440000000225614424465045015060 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/bench-package.R \docType{package} \name{bench-package} \alias{bench} \alias{bench-package} \title{bench: High Precision Timing of R Expressions} \description{ Tools to accurately benchmark and analyze execution times for R expressions. } \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) } \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@posit.co} Authors: \itemize{ \item Jim Hester } Other contributors: \itemize{ \item Drew Schmidt (read_proc_file implementation) [contributor] \item Posit Software, PBC [copyright holder, funder] } } \keyword{internal} bench/man/bench_time_trans.Rd0000644000176200001440000000057314424264614015713 0ustar liggesusers% 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/DESCRIPTION0000644000176200001440000000254614742305307013053 0ustar liggesusersPackage: bench Title: High Precision Timing of R Expressions Version: 1.1.4 Authors@R: c( person("Jim", "Hester", role = "aut"), person("Davis", "Vaughan", , "davis@posit.co", role = c("aut", "cre")), person("Drew", "Schmidt", role = "ctb", comment = "read_proc_file implementation"), person("Posit Software, PBC", role = c("cph", "fnd")) ) 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 (>= 4.0.0) Imports: glue (>= 1.8.0), methods, pillar (>= 1.10.1), profmem (>= 0.6.0), rlang (>= 1.1.4), stats, tibble (>= 3.2.1), utils Suggests: covr, dplyr, forcats, ggbeeswarm, ggplot2 (>= 3.5.1), ggridges, parallel, scales, testthat (>= 3.2.3), tidyr (>= 1.3.1), vctrs (>= 0.6.5), withr Config/Needs/website: tidyverse/tidytemplate Config/testthat/edition: 3 Config/usethis/last-upkeep: 2025-01-16 Encoding: UTF-8 RoxygenNote: 7.3.2 NeedsCompilation: yes Packaged: 2025-01-16 22:07:42 UTC; davis Author: Jim Hester [aut], Davis Vaughan [aut, cre], Drew Schmidt [ctb] (read_proc_file implementation), Posit Software, PBC [cph, fnd] Maintainer: Davis Vaughan Repository: CRAN Date/Publication: 2025-01-16 22:40:07 UTC