prettyunits/0000755000176200001440000000000014504122652012665 5ustar liggesusersprettyunits/NAMESPACE0000644000176200001440000000060714504004735014110 0ustar liggesusers# Generated by roxygen2: do not edit by hand export(compute_bytes) export(compute_num) export(pretty_bytes) export(pretty_color) export(pretty_colour) export(pretty_dt) export(pretty_ms) export(pretty_num) export(pretty_p_value) export(pretty_round) export(pretty_sec) export(pretty_signif) export(time_ago) export(vague_dt) importFrom(grDevices,col2rgb) importFrom(grDevices,convertColor) prettyunits/LICENSE0000644000176200001440000000005214253066262013674 0ustar liggesusersYEAR: 2014 COPYRIGHT HOLDER: Gabor Csardi prettyunits/README.md0000644000176200001440000001713614504004233014146 0ustar liggesusers [![R-CMD-check](https://github.com/r-lib/prettyunits/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/r-lib/prettyunits/actions/workflows/R-CMD-check.yaml) [![Codecov test coverage](https://codecov.io/gh/r-lib/prettyunits/branch/main/graph/badge.svg)](https://app.codecov.io/gh/r-lib/prettyunits?branch=main) [![CRAN RStudio mirror downloads](http://cranlogs.r-pkg.org/badges/prettyunits)](https://CRAN.R-project.org/package=prettyunits) # prettyunits The `prettyunits` package formats quantities in human readable form. * Time intervals: '1337000' -> '15d 11h 23m 20s'. * Vague time intervals: '2674000' -> 'about a month ago'. * Bytes: '1337' -> '1.34 kB'. * Rounding: '99' with 3 significant digits -> '99.0' * p-values: '0.00001' -> '<0.0001'. * Colors: '#FF0000' -> 'red'. * Quantities: '1239437' -> '1.24 M'. ## Installation You can install the package from CRAN: ```r install.packages("prettyunits") ``` ## Bytes `pretty_bytes` formats number of bytes in a human readable way: ```r pretty_bytes(1337) ``` ``` ##> [1] "1.34 kB" ``` ```r pretty_bytes(133337) ``` ``` ##> [1] "133.34 kB" ``` ```r pretty_bytes(13333337) ``` ``` ##> [1] "13.33 MB" ``` ```r pretty_bytes(1333333337) ``` ``` ##> [1] "1.33 GB" ``` ```r pretty_bytes(133333333337) ``` ``` ##> [1] "133.33 GB" ``` Here is a simple function that emulates the Unix `ls` command, with nicely formatted file sizes: ```r uls <- function(path = ".") { files <- dir(path) info <- files %>% lapply(file.info) %>% do.call(what = rbind) info$size <- pretty_bytes(info$size) df <- data.frame(d = ifelse(info$isdir, "d", " "), mode = as.character(info$mode), user = info$uname, group = info$grname, size = ifelse(info$isdir, "", info$size), modified = info$mtime, name = files) print(df, row.names = FALSE) } uls() ``` ``` ##> d mode user group size modified name ##> 644 gaborcsardi staff 232 B 2023-09-24 10:37:41 codecov.yml ##> d 755 gaborcsardi staff 2023-09-24 10:37:41 data-raw ##> 644 gaborcsardi staff 1.06 kB 2023-09-24 10:40:32 DESCRIPTION ##> 644 gaborcsardi staff 42 B 2022-06-17 13:59:46 LICENSE ##> 644 gaborcsardi staff 111 B 2023-09-23 16:44:21 Makefile ##> d 755 gaborcsardi staff 2023-09-24 10:37:59 man ##> 644 gaborcsardi staff 523 B 2023-09-24 10:39:58 NAMESPACE ##> 644 gaborcsardi staff 1.46 kB 2023-09-24 10:42:01 NEWS.md ##> d 755 gaborcsardi staff 2023-09-24 11:25:00 R ##> 644 gaborcsardi staff 7.90 kB 2023-09-24 11:27:42 README.md ##> 644 gaborcsardi staff 4.31 kB 2023-09-24 11:28:23 README.Rmd ##> d 755 gaborcsardi staff 2022-06-17 13:59:46 tests ``` ## Quantities `pretty_num` formats number related to linear quantities in a human readable way: ```r pretty_num(1337) ``` ``` ##> [1] "1.34 k" ``` ```r pretty_num(-133337) ``` ``` ##> [1] "-133.34 k" ``` ```r pretty_num(1333.37e-9) ``` ``` ##> [1] "1.33 u" ``` Be aware that the result is wrong in case of surface or volumes, and for any non-linear quantity. Here is a simple example of how to prettify a entire tibble ```r library(tidyverse) ``` ``` ##> ── Attaching core tidyverse packages ─────────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ── ##> ✔ dplyr 1.1.2 ✔ readr 2.1.4 ##> ✔ forcats 1.0.0 ✔ stringr 1.5.0 ##> ✔ ggplot2 3.4.2 ✔ tibble 3.2.1 ##> ✔ lubridate 1.9.2 ✔ tidyr 1.3.0 ##> ✔ purrr 1.0.1 ##> ── Conflicts ───────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ── ##> ✖ tidyr::extract() masks magrittr::extract() ##> ✖ dplyr::filter() masks stats::filter() ##> ✖ dplyr::lag() masks stats::lag() ##> ✖ purrr::set_names() masks magrittr::set_names() ##> ℹ Use the conflicted package () to force all conflicts to become errors ``` ```r tdf <- tribble( ~name, ~`size in m`, ~`speed in m/s`, "land snail", 0.075, 0.001, "photon", NA, 299792458, "African plate", 10546330, 0.000000000681) tdf %>% mutate(across(where(is.numeric), pretty_num)) ``` ``` ##> # A tibble: 3 × 3 ##> name `size in m` `speed in m/s` ##> ##> 1 land snail " 75 m" " 1 m" ##> 2 photon " NA " "299.79 M" ##> 3 African plate "10.55 M" " 681 p" ``` ## Time intervals `pretty_ms` formats a time interval given in milliseconds. `pretty_sec` does the same for seconds, and `pretty_dt` for `difftime` objects. The optional `compact` argument turns on a compact, approximate format. ```r pretty_ms(c(1337, 13370, 133700, 1337000, 1337000000)) ``` ``` ##> [1] "1.3s" "13.4s" "2m 13.7s" "22m 17s" ##> [5] "15d 11h 23m 20s" ``` ```r pretty_ms(c(1337, 13370, 133700, 1337000, 1337000000), compact = TRUE) ``` ``` ##> [1] "~1.3s" "~13.4s" "~2m" "~22m" "~15d" ``` ```r pretty_sec(c(1337, 13370, 133700, 1337000, 13370000)) ``` ``` ##> [1] "22m 17s" "3h 42m 50s" "1d 13h 8m 20s" "15d 11h 23m 20s" ##> [5] "154d 17h 53m 20s" ``` ```r pretty_sec(c(1337, 13370, 133700, 1337000, 13370000), compact = TRUE) ``` ``` ##> [1] "~22m" "~3h" "~1d" "~15d" "~154d" ``` ## Vague time intervals `vague_dt` and `time_ago` formats time intervals using a vague format, omitting smaller units. They both have three formats: `default`, `short` and `terse`. `vague_dt` takes a `difftime` object, and `time_ago` works relatively to the specified date. ```r vague_dt(format = "short", as.difftime(30, units = "secs")) ``` ``` ##> [1] "<1 min" ``` ```r vague_dt(format = "short", as.difftime(14, units = "mins")) ``` ``` ##> [1] "14 min" ``` ```r vague_dt(format = "short", as.difftime(5, units = "hours")) ``` ``` ##> [1] "5 hours" ``` ```r vague_dt(format = "short", as.difftime(25, units = "hours")) ``` ``` ##> [1] "1 day" ``` ```r vague_dt(format = "short", as.difftime(5, units = "days")) ``` ``` ##> [1] "5 day" ``` ```r now <- Sys.time() time_ago(now) ``` ``` ##> [1] "moments ago" ``` ```r time_ago(now - as.difftime(30, units = "secs")) ``` ``` ##> [1] "less than a minute ago" ``` ```r time_ago(now - as.difftime(14, units = "mins")) ``` ``` ##> [1] "14 minutes ago" ``` ```r time_ago(now - as.difftime(5, units = "hours")) ``` ``` ##> [1] "5 hours ago" ``` ```r time_ago(now - as.difftime(25, units = "hours")) ``` ``` ##> [1] "a day ago" ``` ## Rounding `pretty_round()` and `pretty_signif()` preserve trailing zeros. ```r pretty_round(1, digits=6) ``` ``` ##> [1] "1.000000" ``` ```r pretty_signif(c(99, 0.9999), digits=3) ``` ``` ##> [1] "99.0" "1.00" ``` ## p-values `pretty_p_value()` rounds small p-values to indicate less than significance level for small values. ```r pretty_p_value(c(0.05, 0.0000001, NA)) ``` ``` ##> [1] "0.0500" "<0.0001" NA ``` ## Colors `pretty_color` converts colors from other representations to human-readable names. ```r pretty_color("black") ``` ``` ##> [1] "black" ##> attr(,"alt") ##> [1] "black" "gray0" "grey0" "Black" ``` ```r pretty_color("#123456") ``` ``` ##> [1] "Prussian Blue" ##> attr(,"alt") ##> [1] "Prussian Blue" ``` prettyunits/man/0000755000176200001440000000000014504004233013432 5ustar liggesusersprettyunits/man/time_ago.Rd0000644000176200001440000000432714503574705015532 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/xtime-ago.R \name{time_ago} \alias{time_ago} \title{Human readable format of the time interval since a time point} \usage{ time_ago(date, format = c("default", "short", "terse")) } \arguments{ \item{date}{Date(s), \code{as.POSIXct} will be called on them.} \item{format}{Format, currently available formats are: \sQuote{default}, \sQuote{short}, \sQuote{terse}. See examples below.} } \value{ Character vector of the formatted time intervals. } \description{ It calls \code{\link{vague_dt}} to do the actual formatting. } \examples{ now <- Sys.time() time_ago(now) time_ago(now - as.difftime(30, units = "secs")) time_ago(now - as.difftime(14, units = "mins")) time_ago(now - as.difftime(5, units = "hours")) time_ago(now - as.difftime(25, units = "hours")) time_ago(now - as.difftime(5, units = "days")) time_ago(now - as.difftime(30, units = "days")) time_ago(now - as.difftime(365, units = "days")) time_ago(now - as.difftime(365 * 10, units = "days")) ## Short format time_ago(format = "short", now) time_ago(format = "short", now - as.difftime(30, units = "secs")) time_ago(format = "short", now - as.difftime(14, units = "mins")) time_ago(format = "short", now - as.difftime(5, units = "hours")) time_ago(format = "short", now - as.difftime(25, units = "hours")) time_ago(format = "short", now - as.difftime(5, units = "days")) time_ago(format = "short", now - as.difftime(30, units = "days")) time_ago(format = "short", now - as.difftime(365, units = "days")) time_ago(format = "short", now - as.difftime(365 * 10, units = "days")) ## Even shorter, terse format, (almost always) exactly 3 characters wide time_ago(format = "terse", now) time_ago(format = "terse", now - as.difftime(30, units = "secs")) time_ago(format = "terse", now - as.difftime(14, units = "mins")) time_ago(format = "terse", now - as.difftime(5, units = "hours")) time_ago(format = "terse", now - as.difftime(25, units = "hours")) time_ago(format = "terse", now - as.difftime(5, units = "days")) time_ago(format = "terse", now - as.difftime(30, units = "days")) time_ago(format = "terse", now - as.difftime(365, units = "days")) time_ago(format = "terse", now - as.difftime(365 * 10, units = "days")) } prettyunits/man/pretty_p_value.Rd0000644000176200001440000000111614504001530016757 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/p-value.R \name{pretty_p_value} \alias{pretty_p_value} \title{p-values in a human-readable string} \usage{ pretty_p_value(x, minval = 1e-04) } \arguments{ \item{x}{A numeric vector.} \item{minval}{The minimum p-value to show (lower values will show as \code{paste0("<", minval)}).} } \value{ A character vector of p-value representations. } \description{ p-values in a human-readable string } \examples{ pretty_p_value(c(1, 0, NA, 0.01, 0.0000001)) pretty_p_value(c(1, 0, NA, 0.01, 0.0000001), minval = 0.05) } prettyunits/man/vague_dt.Rd0000644000176200001440000000366614503574705015551 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/xtime-ago.R \name{vague_dt} \alias{vague_dt} \title{Human readable format of a time interval} \usage{ vague_dt(dt, format = c("default", "short", "terse")) } \arguments{ \item{dt}{A \code{difftime} object, the time interval(s).} \item{format}{Format, currently available formats are: \sQuote{default}, \sQuote{short}, \sQuote{terse}. See examples below.} } \value{ Character vector of the formatted time intervals. } \description{ Human readable format of a time interval } \examples{ vague_dt(as.difftime(30, units = "secs")) vague_dt(as.difftime(14, units = "mins")) vague_dt(as.difftime(5, units = "hours")) vague_dt(as.difftime(25, units = "hours")) vague_dt(as.difftime(5, units = "days")) vague_dt(as.difftime(30, units = "days")) vague_dt(as.difftime(365, units = "days")) vague_dt(as.difftime(365 * 10, units = "days")) ## Short format vague_dt(format = "short", as.difftime(30, units = "secs")) vague_dt(format = "short", as.difftime(14, units = "mins")) vague_dt(format = "short", as.difftime(5, units = "hours")) vague_dt(format = "short", as.difftime(25, units = "hours")) vague_dt(format = "short", as.difftime(5, units = "days")) vague_dt(format = "short", as.difftime(30, units = "days")) vague_dt(format = "short", as.difftime(365, units = "days")) vague_dt(format = "short", as.difftime(365 * 10, units = "days")) ## Even shorter, terse format, (almost always) exactly 3 characters wide vague_dt(format = "terse", as.difftime(30, units = "secs")) vague_dt(format = "terse", as.difftime(14, units = "mins")) vague_dt(format = "terse", as.difftime(5, units = "hours")) vague_dt(format = "terse", as.difftime(25, units = "hours")) vague_dt(format = "terse", as.difftime(5, units = "days")) vague_dt(format = "terse", as.difftime(30, units = "days")) vague_dt(format = "terse", as.difftime(365, units = "days")) vague_dt(format = "terse", as.difftime(365 * 10, units = "days")) } prettyunits/man/pretty_dt.Rd0000644000176200001440000000131414503574705015755 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/xtime.R \name{pretty_dt} \alias{pretty_dt} \title{Pretty formatting of time intervals (difftime objects)} \usage{ pretty_dt(dt, compact = FALSE) } \arguments{ \item{dt}{A \code{difftime} object, a vector of time differences.} \item{compact}{If true, then only the first non-zero unit is used. See examples below.} } \value{ Character vector of formatted time intervals. } \description{ Pretty formatting of time intervals (difftime objects) } \examples{ pretty_dt(as.difftime(1000, units = "secs")) pretty_dt(as.difftime(0, units = "secs")) } \seealso{ Other time: \code{\link{pretty_ms}()}, \code{\link{pretty_sec}()} } \concept{time} prettyunits/man/pretty_round.Rd0000644000176200001440000000216014504004735016465 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/rounding.R \name{pretty_round} \alias{pretty_round} \title{Round a value to a defined number of digits printing out trailing zeros, if applicable} \usage{ pretty_round(x, digits = 0, sci_range = Inf, sci_sep = "e") } \arguments{ \item{x}{The number to round.} \item{digits}{integer indicating the number of decimal places.} \item{sci_range}{See help for \code{\link[=pretty_signif]{pretty_signif()}} (and you likely want to round with \code{\link[=pretty_signif]{pretty_signif()}} if you want to use this argument).} \item{sci_sep}{The separator to use for scientific notation strings (typically this will be either "e" or "x10^" for computer- or human-readable output).} } \value{ A string with the value. } \description{ Round a value to a defined number of digits printing out trailing zeros, if applicable } \details{ Values that are not standard numbers like \code{Inf}, \code{NA}, and \code{NaN} are returned as \code{"Inf"}, \code{"NA"}, and \code{"NaN"}. } \seealso{ \code{\link[=round]{round()}}, \code{\link[=pretty_signif]{pretty_signif()}}. } prettyunits/man/pretty_signif.Rd0000644000176200001440000000235314504004735016621 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/rounding.R \name{pretty_signif} \alias{pretty_signif} \title{Round a value to a defined number of significant digits printing out trailing zeros, if applicable} \usage{ pretty_signif(x, digits = 6, sci_range = 6, sci_sep = "e") } \arguments{ \item{x}{The number to round.} \item{digits}{integer indicating the number of significant digits.} \item{sci_range}{integer (or \code{Inf}) indicating when to switch to scientific notation instead of floating point. Zero indicates always use scientific; \code{Inf} indicates to never use scientific notation; otherwise, scientific notation is used when \code{abs(log10(x)) > sci_range}.} \item{sci_sep}{The separator to use for scientific notation strings (typically this will be either "e" or "x10^" for computer- or human-readable output).} } \value{ A string with the value. } \description{ Round a value to a defined number of significant digits printing out trailing zeros, if applicable } \details{ Values that are not standard numbers like \code{Inf}, \code{NA}, and \code{NaN} are returned as \code{"Inf"}, \code{"NA"}, and \code{NaN}. } \seealso{ \code{\link[=signif]{signif()}}, \code{\link[=pretty_round]{pretty_round()}}. } prettyunits/man/pretty_ms.Rd0000644000176200001440000000127314503574705015771 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/xtime.R \name{pretty_ms} \alias{pretty_ms} \title{Pretty formatting of milliseconds} \usage{ pretty_ms(ms, compact = FALSE) } \arguments{ \item{ms}{Numeric vector of milliseconds} \item{compact}{If true, then only the first non-zero unit is used. See examples below.} } \value{ Character vector of formatted time intervals. } \description{ Pretty formatting of milliseconds } \examples{ pretty_ms(c(1337, 13370, 133700, 1337000, 1337000000)) pretty_ms(c(1337, 13370, 133700, 1337000, 1337000000), compact = TRUE) } \seealso{ Other time: \code{\link{pretty_dt}()}, \code{\link{pretty_sec}()} } \concept{time} prettyunits/man/pretty_bytes.Rd0000644000176200001440000000252114504004233016456 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/xsizes-docs.R \name{pretty_bytes} \alias{pretty_bytes} \alias{compute_bytes} \title{Bytes in a human readable string} \usage{ pretty_bytes(bytes, style = c("default", "nopad", "6")) compute_bytes(bytes, smallest_unit = "B") } \arguments{ \item{bytes}{Numeric vector, number of bytes.} \item{style}{Formatting style: \itemize{ \item \code{"default"} is the original \code{pretty_bytes} formatting, and it always pads the output, so that all vector elements are of the same width, \item \code{"nopad"} is similar, but does not pad the output, \item \code{"6"} always uses 6 characters, The \code{"6"} style is useful if it is important that the output always has the same width (number of characters), e.g. in progress bars. See some examples below. }} \item{smallest_unit}{A character scalar, the smallest unit to use.} } \value{ Character vector, the formatted sizes. For \code{compute_bytes}, a data frame with columns \code{amount}, \code{unit}, \code{negative}. } \description{ Use \code{pretty_bytes()} to format bytes. \code{compute_bytes()} is the underlying engine that may be useful for custom formatting. } \examples{ bytes <- c(1337, 133337, 13333337, 1333333337, 133333333337) pretty_bytes(bytes) pretty_bytes(bytes, style = "nopad") pretty_bytes(bytes, style = "6") } prettyunits/man/color_reference.Rd0000644000176200001440000000123614504001530017054 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/color.R \name{color_reference} \alias{color_reference} \title{Color names, hexadecimal, and CIE Lab colorspace representations} \source{ {https://github.com/colorjs/color-namer} and R \code{colors()} } \description{ \describe{ \item{hex}{hexadecimal color representation (without the # at the beginning)} \item{L,a,b}{CIE Lab colorspace representation of \code{hex}} \item{name}{Preferred human-readable name of the color} \item{name_alt}{All available human-readable names of the color} \item{roygbiv,basic,html,R,pantone,x11,ntc}{Source dataset containing the color} } } \keyword{internal} prettyunits/man/pretty_sec.Rd0000644000176200001440000000126014503574705016120 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/xtime.R \name{pretty_sec} \alias{pretty_sec} \title{Pretty formatting of seconds} \usage{ pretty_sec(sec, compact = FALSE) } \arguments{ \item{sec}{Numeric vector of seconds.} \item{compact}{If true, then only the first non-zero unit is used. See examples below.} } \value{ Character vector of formatted time intervals. } \description{ Pretty formatting of seconds } \examples{ pretty_sec(c(1337, 13370, 133700, 1337000, 13370000)) pretty_sec(c(1337, 13370, 133700, 1337000, 13370000), compact = TRUE) } \seealso{ Other time: \code{\link{pretty_dt}()}, \code{\link{pretty_ms}()} } \concept{time} prettyunits/man/pretty_color.Rd0000644000176200001440000000111514504001530016441 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/color.R \name{pretty_color} \alias{pretty_color} \alias{pretty_colour} \title{Color definition (like RGB) to a name} \usage{ pretty_color(color) pretty_colour(color) } \arguments{ \item{color}{A scalar color that is usable as an input to \code{col2rgb()} (assumed to be in the sRGB color space).} } \value{ A character string that is the closest named colors to the input color. The output will have an attribute of alternate color names (named "alt"). } \description{ Color definition (like RGB) to a name } prettyunits/man/pretty_num.Rd0000644000176200001440000000256214504004233016134 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/xnumbers-docs.R \name{pretty_num} \alias{pretty_num} \alias{compute_num} \title{Linear quantities in a human readable string} \usage{ pretty_num(number, style = c("default", "nopad", "6")) compute_num(number, smallest_prefix = "y") } \arguments{ \item{number}{Numeric vector, number related to a linear quantity.} \item{style}{Formatting style: \itemize{ \item \code{"default"} is the original \code{pretty_num} formatting, and it always pads the output, so that all vector elements are of the same width, \item \code{"nopad"} is similar, but does not pad the output, \item \code{"6"} always uses 6 characters, The \code{"6"} style is useful if it is important that the output always has the same width (number of characters), e.g. in progress bars. See some examples below. }} \item{smallest_prefix}{A character scalar, the smallest prefix to use.} } \value{ Character vector, the formatted sizes. For \code{compute_num}, a data frame with columns \code{amount}, \code{prefix}, \code{negative}. } \description{ Use \code{pretty_num()} to format numbers \code{compute_num()} is the underlying engine that may be useful for custom formatting. } \examples{ numbers <- c(1337, 1.3333e-5, 13333337, 1333333337, 133333333337) pretty_num(numbers) pretty_num(numbers, style = "nopad") pretty_num(numbers, style = "6") } prettyunits/man/prettyunits.Rd0000644000176200001440000000203114504005772016340 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/pretty-package.R \docType{package} \name{prettyunits} \alias{prettyunits} \alias{prettyunits-package} \title{Prettier formatting of quantities} \description{ Render quantities with a pretty, human-readable formatting. \itemize{ \item Time intervals: '1337000' -> '15d 11h 23m 20s'. \item Vague time intervals: '2674000' -> 'about a month ago'. \item Bytes: '1337' -> '1.34 kB'. \item p-values: '0.00001' -> '<0.0001'. \item Colors: '#FF0000' -> 'red'. \item Quantities: '1239437' -> '1.24 M'. } } \seealso{ Useful links: \itemize{ \item \url{https://github.com/r-lib/prettyunits} \item Report bugs at \url{https://github.com/r-lib/prettyunits/issues} } } \author{ \strong{Maintainer}: Gabor Csardi \email{csardi.gabor@gmail.com} Other contributors: \itemize{ \item Bill Denney \email{wdenney@humanpredictions.com} (\href{https://orcid.org/0000-0002-5759-428X}{ORCID}) [contributor] \item Christophe Regouby \email{christophe.regouby@free.fr} [contributor] } } prettyunits/DESCRIPTION0000644000176200001440000000240514504122652014374 0ustar liggesusersPackage: prettyunits Title: Pretty, Human Readable Formatting of Quantities Version: 1.2.0 Authors@R: c( person("Gabor", "Csardi", email="csardi.gabor@gmail.com", role=c("aut", "cre")), person("Bill", "Denney", email="wdenney@humanpredictions.com", role=c("ctb"), comment=c(ORCID="0000-0002-5759-428X")), person("Christophe", "Regouby", email="christophe.regouby@free.fr", role=c("ctb")) ) Description: Pretty, human readable formatting of quantities. Time intervals: '1337000' -> '15d 11h 23m 20s'. Vague time intervals: '2674000' -> 'about a month ago'. Bytes: '1337' -> '1.34 kB'. Rounding: '99' with 3 significant digits -> '99.0' p-values: '0.00001' -> '<0.0001'. Colors: '#FF0000' -> 'red'. Quantities: '1239437' -> '1.24 M'. License: MIT + file LICENSE URL: https://github.com/r-lib/prettyunits BugReports: https://github.com/r-lib/prettyunits/issues Depends: R(>= 2.10) Suggests: codetools, covr, testthat RoxygenNote: 7.2.3 Encoding: UTF-8 NeedsCompilation: no Packaged: 2023-09-24 10:53:19 UTC; gaborcsardi Author: Gabor Csardi [aut, cre], Bill Denney [ctb] (), Christophe Regouby [ctb] Maintainer: Gabor Csardi Repository: CRAN Date/Publication: 2023-09-24 21:10:02 UTC prettyunits/tests/0000755000176200001440000000000014253066262014034 5ustar liggesusersprettyunits/tests/testthat/0000755000176200001440000000000014504122652015667 5ustar liggesusersprettyunits/tests/testthat/test-vague-dt.r0000644000176200001440000000420514503574705020555 0ustar liggesusers context("Vague time intervals") ## Amount, unit, default result, short, terse all_tests <- list( list( 1, "secs", "moments ago", "<1 min", " 1s"), list( 30, "secs", "less than a minute ago", "<1 min", "30s"), list( 50, "secs", "about a minute ago", "1 min", " 1m"), list( 14, "mins", "14 minutes ago", "14 min", "14m"), list( 70, "mins", "about an hour ago", "1 hour", " 1h"), list( 5, "hours", "5 hours ago", "5 hours", " 5h"), list( 25, "hours", "a day ago", "1 day", " 1d"), list( 5, "days", "5 days ago", "5 day", " 5d"), list( 30, "days", "about a month ago", "1 mon", " 1M"), list( 30 * 3, "days", "3 months ago", "3 mon", " 3M"), list(365, "days", "about a year ago", "1 year", " 1y"), list(365 * 10, "days", "10 years ago", "10 years", "10y") ) test_that("vague_dt works", { sapply(all_tests, function(case) { dt <- as.difftime(case[[1]], units = case[[2]]) default <- vague_dt(dt) short <- vague_dt(dt, format = "short") terse <- vague_dt(dt, format = "terse") expect_equal(default, case[[3]], info = paste(case[[1]], case[[2]], "default")) expect_equal(short, case[[4]], info = paste(case[[1]], case[[2]], "short")) expect_equal(terse, case[[5]], info = paste(case[[1]], case[[2]], "terse")) }) expect_equal( vague_dt(as.difftime(numeric(), units = "secs")), character() ) }) test_that("time_ago works", { sapply(all_tests, function(case) { t <- Sys.time() - as.difftime(case[[1]], units = case[[2]]) default <- time_ago(t) short <- time_ago(t, format = "short") terse <- time_ago(t, format = "terse") expect_equal(default, case[[3]], info = paste(case[[1]], case[[2]], "default")) expect_equal(short, case[[4]], info = paste(case[[1]], case[[2]], "short")) expect_equal(terse, case[[5]], info = paste(case[[1]], case[[2]], "terse")) }) expect_equal( time_ago(c(Sys.time() - 60, Sys.time() - 3600)), c("about a minute ago", "about an hour ago") ) }) prettyunits/tests/testthat/test-numbers.r0000644000176200001440000001116214504004233020475 0ustar liggesusers context("Pretty numbers") test_that("sizes.R is standalone", { stenv <- environment(format_num$pretty_num) objs <- ls(stenv, all.names = TRUE) funs <- Filter(function(x) is.function(stenv[[x]]), objs) funobjs <- mget(funs, stenv) for (f in funobjs) expect_identical(environmentName(topenv(f)), "base") expect_message( mapply(codetools::checkUsage, funobjs, funs, MoreArgs = list(report = message)), NA) }) test_that("pretty_num gives errors on invalid input", { expect_error(pretty_num(''), 'is.numeric.*is not TRUE') expect_error(pretty_num('1'), 'is.numeric.*is not TRUE') expect_error(pretty_num(TRUE), 'is.numeric.*is not TRUE') expect_error(pretty_num(list(1,2,3)), 'is.numeric.*is not TRUE') }) test_that("pretty_num converts properly", { expect_equal(pretty_num(1e-24), '1 y') expect_equal(pretty_num(-1e-4), '-100.00 u') expect_equal(pretty_num(-0.01), '-10 m') expect_equal(pretty_num(0), '0 ') expect_equal(pretty_num(10), '10 ') expect_equal(pretty_num(999), '999 ') expect_equal(pretty_num(1001), '1.00 k') expect_equal(pretty_num(1000 * 1000 - 1), '1.00 M') expect_equal(pretty_num(1e16), '10 P') expect_equal(pretty_num(1e30), '1000000 Y') }) test_that("pretty_num handles NA and NaN", { expect_equal(pretty_num(NA_real_), "NA ") expect_equal(pretty_num(NA_integer_), "NA ") expect_error(pretty_num(NA_character_), 'is.numeric.*is not TRUE') expect_error(pretty_num(NA), 'is.numeric.*is not TRUE') expect_equal(pretty_num(NaN), "NaN ") }) test_that("pretty_num handles vectors", { expect_equal(pretty_num(1:10), paste(format(1:10), "")) v <- c(NA, -1e-7, 1, 1e4, 1e6, NaN, 1e5) expect_equal(pretty_num(v), c(" NA ", "-100.00 n"," 1 ", " 10 k", " 1 M", " NaN ", " 100 k")) expect_equal(pretty_num(numeric()), character()) }) test_that("pretty_num nopad style", { v <- c(NA, 1, 1e4, 1e6, NaN, 1e5) expect_equal(pretty_num(v, style = "nopad"), c("NA ", "1 ", "10 k", "1 M", "NaN ", "100 k")) expect_equal(pretty_num(numeric(), style = "nopad"), character()) }) test_that("pretty_num handles negative values", { v <- c(NA, -1, 1e4, 1e6, NaN, -1e5) expect_equal(pretty_num(v), c(" NA ", " -1 ", " 10 k", " 1 M", " NaN ", "-100 k")) }) test_that("always two fraction digits", { expect_equal( pretty_num(c(5.6, 5, NA) * 1000 * 1000), c("5.60 M", " 5 M", " NA ") ) }) test_that("6 width style", { cases <- c( " -10 k" = -1e4, # 1 "-111 " = -111.33333, # 2 "-100 " = -100, # 3 " -10 " = -10.33333, # 4 " -10 " = -9.99999, # 5 "-9.0 " = -9, # 6 "-1.0 " = -1, # 7 "0.00 " = 0, # 8 "1.00 " = 1, # 9 "9.00 " = 9, # 10 "10.0 " = 9.99999, # 11 "10.3 " = 10.33333, # 12 " 100 " = 100, # 13 " 111 " = 111.33333, # 14 "1.00 k" = 1e3, # 15 "1.05 k" = 1049, # 16 "1.05 k" = 1051, # 17 "1.10 k" = 1100, # 18 "10.0 k" = 1e4, # 19 " 100 k" = 1e5, # 20 "1.00 M" = 1e6, # 21 " NaN " = NaN, # 22 " NA " = NA # 23 ) expect_equal(pretty_num(unname(cases), style = "6"), names(cases)) }) test_that("No fractional bytes (#23)", { cases <- c( " -1 " = -1, # 1 " 1 " = 1, # 2 " 16 " = 16, # 3 " 128 " = 128, # 4 " 1.02 k" = 1024, # 5 "16.38 k" = 16384, # 6 " 1.05 M" = 1048576, # 7 "-1.05 M" = -1048576, # 8 " NA " = NA # 9 ) expect_equal(pretty_num(unname(cases)), names(cases)) }) test_that("compute_num handles `smallest_prefix` properly", { expect_equal(compute_num(1e-24, smallest_prefix = "m"), data.frame(amount = 1e-21, prefix = "m", negative = FALSE, stringsAsFactors = FALSE)) expect_equal(compute_num(-1e-4, smallest_prefix = "m"), data.frame(amount = 0.1, prefix = "m", negative = TRUE, stringsAsFactors = FALSE)) expect_equal(compute_num(-0.01, smallest_prefix = "m"), data.frame(amount = 10, prefix = "m", negative = TRUE, stringsAsFactors = FALSE)) expect_equal(compute_num(0, smallest_prefix = "m"), data.frame(amount = 0, prefix = "", negative = FALSE, stringsAsFactors = FALSE)) }) prettyunits/tests/testthat/test-p-value.r0000644000176200001440000000116214504001530020367 0ustar liggesuserscontext("p-values") test_that("p-values work", { expect_equal( pretty_p_value(c(1, 0, NA, 0.01, 0.0000001)), c("1.0000", "<0.0001", NA_character_, "0.0100", "<0.0001") ) expect_equal( pretty_p_value(c(1, 0, NA, 0.01, 0.0000001), minval=0.05), c("1.00", "<0.05", NA_character_, "<0.05", "<0.05") ) expect_equal(pretty_p_value(NA_real_), NA_character_) expect_error(pretty_p_value(1, minval="A")) expect_error(pretty_p_value("A")) expect_error(pretty_p_value(1.1)) expect_error(pretty_p_value(-1)) expect_error(pretty_p_value(0.5, minval=0)) expect_error(pretty_p_value(0.5, minval=1)) }) prettyunits/tests/testthat/test-color.r0000644000176200001440000000060214504001530020132 0ustar liggesuserscontext("Pretty color") test_that("pretty_color works", { expect_equal( pretty_color("black"), structure("black", alt = c("black", "gray0", "grey0", "Black")) ) expect_equal( pretty_color("#123456"), structure("Prussian Blue", alt = c("Prussian Blue")) ) expect_equal( pretty_color(NA_character_), structure(NA_character_, alt = NA_character_) ) }) prettyunits/tests/testthat/test-bytes.r0000644000176200001440000000740214504004233020152 0ustar liggesusers context("Pretty bytes") test_that("sizes.R is standalone", { stenv <- environment(format_bytes$pretty_bytes) objs <- ls(stenv, all.names = TRUE) funs <- Filter(function(x) is.function(stenv[[x]]), objs) funobjs <- mget(funs, stenv) for (f in funobjs) expect_identical(environmentName(topenv(f)), "base") expect_message( mapply(codetools::checkUsage, funobjs, funs, MoreArgs = list(report = message)), NA) }) test_that("pretty_bytes gives errors on invalid input", { expect_error(pretty_bytes(''), 'is.numeric.*is not TRUE') expect_error(pretty_bytes('1'), 'is.numeric.*is not TRUE') expect_error(pretty_bytes(TRUE), 'is.numeric.*is not TRUE') expect_error(pretty_bytes(list(1,2,3)), 'is.numeric.*is not TRUE') }) test_that("pretty_bytes converts properly", { expect_equal(pretty_bytes(0), '0 B') expect_equal(pretty_bytes(10), '10 B') expect_equal(pretty_bytes(999), '999 B') expect_equal(pretty_bytes(1001), '1.00 kB') expect_equal(pretty_bytes(1000 * 1000 - 1), '1.00 MB') expect_equal(pretty_bytes(1e16), '10 PB') expect_equal(pretty_bytes(1e30), '1000000 YB') }) test_that("pretty_bytes handles NA and NaN", { expect_equal(pretty_bytes(NA_real_), "NA B") expect_equal(pretty_bytes(NA_integer_), "NA B") expect_error(pretty_bytes(NA_character_), 'is.numeric.*is not TRUE') expect_error(pretty_bytes(NA), 'is.numeric.*is not TRUE') expect_equal(pretty_bytes(NaN), "NaN B") }) test_that("pretty_bytes handles vectors", { expect_equal(pretty_bytes(1:10), paste(format(1:10), "B")) v <- c(NA, 1, 1e4, 1e6, NaN, 1e5) expect_equal(pretty_bytes(v), c(" NA B", " 1 B", " 10 kB", " 1 MB", " NaN B", "100 kB")) expect_equal(pretty_bytes(numeric()), character()) }) test_that("pretty_bytes nopad style", { v <- c(NA, 1, 1e4, 1e6, NaN, 1e5) expect_equal(pretty_bytes(v, style = "nopad"), c("NA B", "1 B", "10 kB", "1 MB", "NaN B", "100 kB")) expect_equal(pretty_bytes(numeric(), style = "nopad"), character()) }) test_that("pretty_bytes handles negative values", { v <- c(NA, -1, 1e4, 1e6, NaN, -1e5) expect_equal(pretty_bytes(v), c(" NA B", " -1 B", " 10 kB", " 1 MB", " NaN B", "-100 kB")) }) test_that("always two fraction digits", { expect_equal( pretty_bytes(c(5.6, 5, NA) * 1000 * 1000), c("5.60 MB", " 5 MB", " NA B") ) }) test_that("6 width style", { cases <- c( "< 0 kB" = -1e4, # 1 "< 0 kB" = -100, # 2 "< 0 kB" = -1, # 3 "0.0 kB" = 0, # 4 "0.0 kB" = 1, # 5 "0.0 kB" = 9, # 6 "0.0 kB" = 9.99999, # 7 "0.0 kB" = 10.33333, # 8 "0.1 kB" = 100, # 9 "0.1 kB" = 111.33333, # 10 "1.0 kB" = 1e3, # 11 "1.0 kB" = 1049, # 12 "1.1 kB" = 1051, # 13 "1.1 kB" = 1100, # 14 " 10 kB" = 1e4, # 15 "100 kB" = 1e5, # 16 "1.0 MB" = 1e6, # 17 "NaN kB" = NaN, # 18 " NA kB" = NA # 19 ) expect_equal(pretty_bytes(unname(cases), style = "6"), names(cases)) }) test_that("No fractional bytes (#23)", { cases <- c( " -1 B" = -1, # 1 " 1 B" = 1, # 2 " 16 B" = 16, # 3 " 128 B" = 128, # 4 " 1.02 kB" = 1024, # 5 "16.38 kB" = 16384, # 6 " 1.05 MB" = 1048576, # 7 "-1.05 MB" = -1048576, # 8 " NA B" = NA # 9 ) expect_equal(pretty_bytes(unname(cases)), names(cases)) }) prettyunits/tests/testthat/test-ms.r0000644000176200001440000001060614503574705017462 0ustar liggesusers context("Pretty milliseconds") test_that("pretty_ms works", { expect_equal(pretty_ms(0), '0ms') expect_equal(pretty_ms(0.1), '1ms') expect_equal(pretty_ms(1), '1ms') expect_equal(pretty_ms(1000 + 400), '1.4s') expect_equal(pretty_ms(1000 * 2 + 400), '2.4s') expect_equal(pretty_ms(1000 * 55), '55s') expect_equal(pretty_ms(1000 * 67), '1m 7s') expect_equal(pretty_ms(1000 * 60 * 5), '5m') expect_equal(pretty_ms(1000 * 60 * 67), '1h 7m') expect_equal(pretty_ms(1000 * 60 * 60 * 12), '12h') expect_equal(pretty_ms(1000 * 60 * 60 * 40), '1d 16h') expect_equal(pretty_ms(1000 * 60 * 60 * 999), '41d 15h') }) test_that("compact pretty_ms works", { expect_equal(pretty_ms(1000 + 4, compact = TRUE), '~1s') expect_equal(pretty_ms(1000 * 60 * 60 * 999, compact = TRUE), '~41d') }) test_that("pretty_ms handles vectors", { v <- c(0, 0.1, 1, 1400, 2400, 1000 * 55, 1000 * 67, 1000 * 60 * 5, 1000 * 60 * 67, 1000 * 60 * 60 * 12, 1000 * 60 * 60 * 40, 1000 * 60 * 60 * 999) v2 <- c("0ms", "1ms", "1ms", "1.4s", "2.4s", "55s", "1m 7s", "5m", "1h 7m", "12h", "1d 16h", "41d 15h") expect_equal(pretty_ms(v), v2) }) context("Pretty seconds") test_that("pretty_sec works", { expect_equal(pretty_sec(0 / 1000), '0ms') expect_equal(pretty_sec(0.1 / 1000), '1ms') expect_equal(pretty_sec(1 / 1000), '1ms') expect_equal(pretty_sec((1000 + 400) / 1000), '1.4s') expect_equal(pretty_sec((1000 * 2 + 400) / 1000), '2.4s') expect_equal(pretty_sec(1000 * 55 / 1000), '55s') expect_equal(pretty_sec(1000 * 67 / 1000), '1m 7s') expect_equal(pretty_sec(1000 * 60 * 5 / 1000), '5m') expect_equal(pretty_sec(1000 * 60 * 67 / 1000), '1h 7m') expect_equal(pretty_sec(1000 * 60 * 60 * 12 / 1000), '12h') expect_equal(pretty_sec(1000 * 60 * 60 * 40 / 1000), '1d 16h') expect_equal(pretty_sec(1000 * 60 * 60 * 999 / 1000), '41d 15h') }) test_that("compact pretty_sec works", { expect_equal(pretty_sec((1000 + 4) / 1000, compact = TRUE), '~1s') expect_equal(pretty_sec(1000 * 60 * 60 * 999 / 1000, compact = TRUE), '~41d') }) test_that("pretty_sec handles vectors", { v <- c(0, 0.1, 1, 1400, 2400, 1000 * 55, 1000 * 67, 1000 * 60 * 5, 1000 * 60 * 67, 1000 * 60 * 60 * 12, 1000 * 60 * 60 * 40, 1000 * 60 * 60 * 999) / 1000 v2 <- c("0ms", "1ms", "1ms", "1.4s", "2.4s", "55s", "1m 7s", "5m", "1h 7m", "12h", "1d 16h", "41d 15h") expect_equal(pretty_sec(v), v2) }) context("Pretty dt") test_that("pretty_dt works", { expect_equal(pretty_dt(as.difftime(units = "secs", 0 / 1000)), '0ms') expect_equal(pretty_dt(as.difftime(units = "secs", 0.1 / 1000)), '1ms') expect_equal(pretty_dt(as.difftime(units = "secs", 1 / 1000)), '1ms') expect_equal(pretty_dt(as.difftime(units = "secs", (1000 + 400) / 1000)), '1.4s') expect_equal(pretty_dt(as.difftime(units = "secs", (1000 * 2 + 400) / 1000)), '2.4s') expect_equal(pretty_dt(as.difftime(units = "secs", 1000 * 55 / 1000)), '55s') expect_equal(pretty_dt(as.difftime(units = "secs", 1000 * 67 / 1000)), '1m 7s') expect_equal(pretty_dt(as.difftime(units = "secs", 1000 * 60 * 5 / 1000)), '5m') expect_equal(pretty_dt(as.difftime(units = "secs", 1000 * 60 * 67 / 1000)), '1h 7m') expect_equal(pretty_dt(as.difftime(units = "secs", 1000 * 60 * 60 * 12 / 1000)), '12h') expect_equal(pretty_dt(as.difftime(units = "secs", 1000 * 60 * 60 * 40 / 1000)), '1d 16h') expect_equal(pretty_dt(as.difftime(units = "secs", 1000 * 60 * 60 * 999 / 1000)), '41d 15h') }) test_that("compact pretty_dt works", { expect_equal(pretty_dt(as.difftime(units = "secs", (1000 + 4) / 1000), compact = TRUE), '~1s') expect_equal(pretty_dt(as.difftime(units = "secs", 1000 * 60 * 60 * 999 / 1000), compact = TRUE), '~41d') }) test_that("pretty_dt handles vectors", { v <- c(0, 0.1, 1, 1400, 2400, 1000 * 55, 1000 * 67, 1000 * 60 * 5, 1000 * 60 * 67, 1000 * 60 * 60 * 12, 1000 * 60 * 60 * 40, 1000 * 60 * 60 * 999) / 1000 v2 <- c("0ms", "1ms", "1ms", "1.4s", "2.4s", "55s", "1m 7s", "5m", "1h 7m", "12h", "1d 16h", "41d 15h") expect_equal(pretty_dt(as.difftime(units = "secs", v)), v2) }) test_that("pretty_dt works with NAs", { stime <- Sys.time() v <- .difftime(c(difftime(NA, NA), difftime(stime + 1, stime)), "secs") v2 <- c(NA_character_, "~1s") v3 <- c(NA_character_, "1s") expect_equal(pretty_dt(v, compact = TRUE), v2) expect_equal(pretty_dt(v, compact = FALSE), v3) }) prettyunits/tests/testthat/test-rounding.r0000644000176200001440000001035014504005011020640 0ustar liggesuserscontext("Rounding to string values") test_that("Rounding", { expect_error(pretty_round(1, c(2, 3)), regexp="digits must either be a scalar or the same length as x") expect_equal(pretty_round(11), "11") expect_equal(pretty_round(5), "5") expect_equal(pretty_round(0.05), "0") expect_equal(pretty_round(NA), "NA") expect_equal(pretty_round(NaN), "NaN") expect_equal(pretty_round(Inf), "Inf") expect_equal(pretty_round(-Inf), "-Inf") ## Respecting the digits expect_equal(pretty_round(0.05, 3), "0.050") expect_equal(pretty_round(123.05, 3), "123.050") expect_equal(pretty_round(c(100, 0.1), 3), c("100.000", "0.100"), info="Vectors work with different orders of magnitude work") expect_equal(pretty_round(c(100, 0.1), c(0, 3)), c("100", "0.100"), info="Vectors of digits work") expect_equal(pretty_round(c(0.1, NA), digits=3), c("0.100", "NA"), info="Mixed inputs (NA, NaN, Inf or numeric), NA") expect_equal(pretty_round(c(0.1, NA, NaN, Inf, -Inf), digits=3), c("0.100", "NA", "NaN", "Inf", "-Inf"), info="Mixed inputs (NA, NaN, Inf or numeric)") ## All zeros expect_equal(pretty_round(0, digits=3), "0.000") expect_equal(pretty_round(c(0, NA), digits=3), c("0.000", "NA")) # scientific notation expect_equal(pretty_round(1234567, digits=3, sci_range=5), "1.234567000e6", info="sci_range works with pretty_round (even if it looks odd)") expect_equal(pretty_round(1234567, digits=3, sci_range=5), pretty_round(1234567, digits=3, sci_range=5), info="sci_range works with pretty_round (even if it looks odd)") expect_equal(pretty_round(1234567, digits=3, sci_range=5, sci_sep="x10^"), "1.234567000x10^6", info="sci_sep is respected.") expect_equal(pretty_round(c(1e7, 1e10), digits=c(-3, -9), sci_range=5), c("1.0000e7", "1.0e10"), info="Different numbers of digits for rounding work with pretty_round") }) test_that("Significance", { expect_equal(pretty_signif(11), "11.0000") expect_equal(pretty_signif(5), "5.00000") expect_equal(pretty_signif(0.05), "0.0500000") expect_equal(pretty_signif(NA), "NA") expect_equal(pretty_signif(NaN), "NaN") expect_equal(pretty_signif(Inf), "Inf") expect_equal(pretty_signif(-Inf), "-Inf") ## Respecting the digits expect_equal(pretty_signif(0.05, 3), "0.0500") expect_equal(pretty_signif(123.05, 3), "123") expect_equal(pretty_signif(123456.05, 3), "123000") expect_equal(pretty_signif(123456.05, 3, sci_range=6), "123000") expect_equal(pretty_signif(123456.05, 3, sci_range=5), "1.23e5") expect_equal(pretty_signif(-123000.05, 3, sci_range=5), "-1.23e5") expect_equal(pretty_signif(999999, 3, sci_range=6), "1.00e6", info="Rounding around the edge of the sci_range works correctly (going up)") expect_equal(pretty_signif(999999, 7, sci_range=6), "999999.0", info="Rounding around the edge of the sci_range works correctly (going staying the same)") expect_equal(pretty_signif(-.05, 3), "-0.0500") ## Exact orders of magnitude work on both sides of 0 expect_equal(pretty_signif(0.01, 3), "0.0100") expect_equal(pretty_signif(1, 3), "1.00") expect_equal(pretty_signif(100, 3), "100") ## Vectors work with different orders of magnitude work expect_equal(pretty_signif(c(100, 0.1), 3), c("100", "0.100")) ## Rounding to a higher number of significant digits works correctly expect_equal(pretty_signif(0.9999999, 3), "1.00") ## Mixed inputs (NA, NaN, Inf or numeric) expect_equal(pretty_signif(NA), "NA") expect_equal(pretty_signif(c(0.1, NA), digits=3), c("0.100", "NA")) expect_equal(pretty_signif(c(0.1, NA, NaN, Inf, -Inf), digits=3), c("0.100", "NA", "NaN", "Inf", "-Inf")) ## All zeros expect_equal(pretty_signif(0, digits=3), "0.000") expect_equal(pretty_signif(c(0, NA), digits=3), c("0.000", "NA")) expect_equal(pretty_signif(1234567, digits=3, sci_range=5, sci_sep="x10^"), "1.23x10^6", info="sci_sep is respected.") expect_equal(pretty_signif(c(1e7, 1e10), digits=3), c("1.00e7", "1.00e10"), info="Different numbers of digits for rounding work with pretty_signif") }) prettyunits/tests/testthat.R0000644000176200001440000000010214253066262016010 0ustar liggesuserslibrary(testthat) library(prettyunits) test_check("prettyunits") prettyunits/R/0000755000176200001440000000000014504004731013063 5ustar liggesusersprettyunits/R/sysdata.rda0000644000176200001440000022510214504001530015217 0ustar liggesusersBZh91AY&SY;<>$@zrnwMq\ͻuVZ4(UUHmg/3j$'AIRa 6Ŷ^vh-wP$FŰ fw8$g 9 &@n;( 1UH(A@@> A#ACX$PXDB!`$ .*Po>VR4+@Q9'c }/yp2zQzNä6UW@6Xl=apSmPz4=]-wuv{4` ( ;_y-,:>潽o`q I$f`:qܷi}}w@PQ@(PB (@(E( %TJ*?0 yPL0 &L#L F&`O 2MLɑbd&4S& L@M2b S)h4&LhB)H ii  =6@(dA➣bPz4hh =@P2=O(C 5?HDh@S2OIOSlzz 'z&SF#!6 ih !   A2`<&i 4 jm#jiꍊi )=4ђlSiOQѨzGX4VzQ!, ;̆A&Lr2&@ 6-hT"SGAT gM' V8})z>N>U^YזG@7yGї /q:1dHU|`c:. 7*tpjna(7U^7``(pJҴ-{=: ` zȿ"B"C0c%Sۄ^Q?njR_d=3Nlr,ޛ[\l &g8 􁨩g.<,!ttSm'Gao <0^>DTDKn"% Ȋ,[V]BJ cJIZ7`+C83K0YC=a+szC;#,z:}7B%H5{Ta+#f%b"=ɋïZr`%)[]i)hژOzZlz3M_Ρն"!uN;ֱ2Mͮ/tM8QǙYOHT(:- Ki5w[|wDFU3-p9vE-®W_1 }ä)@(2;h`T蹂 mՁN:R$=~~gDF¢!!*599qɓt[%х&;% ,TяDzhfC|<,9=}'=U/SsP1x'聹a!̲F~] ys?u?9fffffffffffb"""""ffffffffffffff"h=N=z<SI!Ϛ/Og]zsDVD.ɭ>d?x=YTTW}]EUU pxv˿O^^e|Tg7+y *?*J(@@B/BgQ I'bX $ȡxut>zg p'B ])owe4(0`hdhr42&7'C&8 853`l][Fe e(G7rML˒dr28dsdr.Ado<|o_ -0]GZ$?">!_#]mo@r|I''{{y.;c۷-e6|OU|ya?)m+pl+ לr(ZiΖ1Kltib5s^nEX3R 5;Ĩo* ؜d؍^|+nlB+ c6Ƞ'x1g8ВE7R:~$W:]*3(ociޭɹJC m'$d2nɩKcYF@P2!WE @.rZ d`p16+9o'.qTЀk Ƥ[%kZ d/u3=5ffTЌfEwX^v)rRųlrj S(IbVRzv:8me ]hG!&Z`'5[)-)HߑMQ2&ʁoq[lf/Ϋ5:&Ar gJɚc]J&W3RE#aI ˝56tn[K4o{#3i@ԙր0z僞O+x{l)]!]SxäSƉLQc2={G!~ P{'xp ?Dp2=ќ'ģA "Ҙj\홒h32M.\A&KM>ns>N&xH(hljldlj`g!j{`3qA;@Iٓ3079 [gn\9.f3ccqRvhWc7&c8lZ0MB\Kmg.đ.vyF疺Zm9:1uO2VZ#֌kZVdh§UTP0l_tRί1AJz䰶Wh_D{1ȞBwꣴՁ)g.9#پFF!"v4B<ՆևSنܹ\Qs Q3B|bHim، WF IC\ѕUs3Syj0wUVƎY=n#hĻSAJ ^Q"6aҴep-g#{̬9* o{)-я9B E_lx,LC+dF5TqRDEDDN77 [ӨnG2܁B;mrOUoM!,Zo}QfgC&hW4@Iy $`FE CКRn&OH%T.xWܦ@a Vi8DsRb֋(w%ep9Õ[!*5.9V*qEƒmZγ`]?:Ԙ*i3ASb"国tl#q:4444444444444444;z=SCsSSSSS3sCCCCCCCCCCCCCCBb}`(gP滋|g3SR $#(t a͍rIGh :d6ȓ$̣$@2Kȹ|hfx7: Q=bgfr2FIEa%79#CcS2ND8qt8jcFF89a32NhdD fzOQ%V[SY'b0I?QEr"(L_~sIF : Qs2Fup.,a`q 4̣bę s3Mf0g(CME= J30IS n/@`jr45 ny˫,tld΅ n{&ddt279 O仙ˈW#zAnCgu$Imq@yG~>B@3RpN;70@h\Ԡ5 (/A1C~١\#;`A$MQ2: 5$BI (=#7 F\3Q0I38 sdnh\dIEp3.wjd3й29yGyºi=Qw' w0b|_AB'f!wtp$f= SȣC@ADqH!(MCC䙚dl#GsD'2>b283rLIDFFǨ lH6.{f``ifr.lh-5ACx7;3(\[Zh!rDK`onÃf `؂deůAs([ћoz2=G"I$\|/KaQJ d ;L̊3#Pѳ3BOQE"p9$X(B:ez0wQyL?'fQDI$I%˚rs.\n+˹dAS#4:s~"+' R7(2:IԢ$FFI$G =Fr$pZ}qtfd)B]-u:ND\Br.Adt9eHYŗg(7u^ڤuNJQMҩUE;*)UHRIEUQ]L*T(RJtR[}#&\N1 3QX4.hh `=6E .PAH8Q+RBJ2ZOj bp0+ՈAhI|*wȻeuxM<;IډHD2.@BHbDyچ``Nr)!ypOCx ; D ^o6&y&R> Q,{>@B!V)hnd !#s HWc`ܰV|abC ț\ذrLUR)JVP|YJU? )HBд0JT4|E\{`)9- wtMBN!.?O?8="+y^v9 qB /j Bm w=xHD@`, f*AfauMעslw)_v‡INLAU64y./e ozwԔO}ʸ[%52TU%u6qjEEXUsQ ɎĒ!]=wk!@w/Ϙgʌ隷EG|\fk~$#Oa`+,؋OC[a"O1-)E^{o:{j(gtG_$ zKi7K'Oս4˫(8:VFaӃg\׊Foy ╦-K^:z9~g5[v{wM*Tw}E@ G4+wIagIYȃ F<.<nOIɤ×ֹЅfO#o|,AVloK/ Fbf!0h("#UGʪ)U_pA $!4.;}@  $TR+S"" ^?_//x v"J2=똖 j~} /%,hS8Ӛ2z-D_nY5P Yp@/P(Kyn_2A>7zzwcC?6>Pw$ R@@4)O7P?(/Z+aL2X A9}`-9Sn3%EO4XqɬQ 0ichIԹh" r~ÓQ 0<( ͽovU,"ךT7}O0tX$a0 H3yFJ #R%tg ao!'AcTSn ʃћ:&m]ӿ\;7y'ֽfn/˾ť \c6A0mB)S{پ]]F\nюSly{dZhpzbPAWOX0[ `"LAHo`AG` o2~m-ຳ/^U4e?ǫx {DۖWkݮL˦§L?}2Xr'pF6瓀D(DS`~ŗ 290q`p9 Ou^w<dA9,a*[bi8iu M:h^te莨1xx+[BT"Ķ7!SOv[QYZ~` 5>Q:,|J4J~J$8Sܗ^z):ČrllxNПQPO=FFb׏Η "W4F$SUpç68'J>P I}cb^>e\,?Z4>[˷/bgVf)ٹ3~y@Huگ&LBq1] 0OkۈR5*XG'/Pt%ޒW|ˡU,`A#aǺ@.= E}@CsW|;#=Րc׵⪄MJًpdK"1U9Q@(i9$v(k9X`/ą,St1.^DԽz5Hh"w%\, Lh|eE} ؾ%z&&d;@a-0wo7EH}(K$^F~*˞2ΰNqy2 3xŽHkև:X7PCϐ'@t|g5Mnʎ:[ gthHG7埤7\ l6P\%Ʉ A71L J XQ| .5%g+ uj6P@$蟳a+m$UV̨!+?L^/|er w版2­)ݔ6@ ]vKXbF8Ҙ*-{?qP'O |Ng2uH\0#Rmگ.-g@lcc~M+]_tɼlr*j6,g5i%e/WM#T&$-ee k Ca,As寠[ĥAQzoxpJ%!=1i'Uۢ= h v;MS$A&>,:pSQG pu$?An 8ѧeI¶0 ^CU$^sȇV:=j_~Â1nd`+h05m͟I?AC*+h vfJ,&=L$p:0LQ| gh}BJՉW_~A]4 aAU)dϜ#'qPZZŶ9>Nѭe`PB..fK rmAk@+:qw:qABFDH1XLdK橆G@xrz0HyYY$ Db 3tT| gVh AU_<ʔ^1VZ?3;l`-HK_JPtU4,iGXd [@wGo(^U&P\}/@~W f,DP8Ǚt}=&)\^Eg?Ўe`re* 4[F0] Դ&,^Hz*]:A񲸭ݔΪ{(w8(iɎǰv@ !?jG<-Wx+`0H'+ߒrb]؇eDurZ7cr!l:{(~\_@9ʱrlCbQp8KzRAC8 \^cEVd)ߕ"T.͢l@Tഡ{6MT[D8/(žDf=w_y}#oL̆LA\-KzUu†YPB?7:Rq@Ii~BW,9 2L"lch[Y"GU#$@  A~"XJ|!*1QXaR\i녹)'m,qf½L6&aӌZ`|sm$ TpK }E dђiwC'FZ_j'2n7fd& Ҧy,*F%HY/^."A /\DDDD0x 㴕ށ(0>PY- AҦŨPT'b1U8P{$Jt6\&G/(.b<ܟ"B"g& 2:LvbI* 6)M/g(ͷ:euWVuXj\;831=QJJrZַpѥ5wK_M˸0\'' ^m],mEI%\"AmGG byUTI ԋx g*dFg2Ӎ1UbML1hnf*6=MפBT VhPpjˆjWn*B-;w#w1mgYgb\ujVxn*i23 `Q0N˻l0B^2ytUe Pb"0.v[]ػlBJ2 ࿴#8#d!v lpSׯ8KUt5Av+B\q7{FBz-⢯Ă-9`@r⢱8"0|x:XRfeeÉP996ۃh^CPA;-܋Dn_]vɫqq՘V:NLclf4 Ƿȅs6mq3q="[\fGt]o_^J1p#\_}]9ٓFO ih˱L8D0%:ک5v!g`R@m|<, *sK9R \g^ v՞p)˂bo3gRSUK+Dg cM%@K ۰)=77foZIr!-Z3󦨫XT2LR'լ{VZ'Wy(:Ngʲ[)m3 ;$]Puؒ}I'(7e0] 4\&5ILaKYl^{C 5,8&Ef)$ѡ)b+a]Cp!I@cwA?>r쌯zVE&idC|-`j}Ki^i9ɶ#ζD'b}Z:mIuW~0~i;5e{kl_3>(p,G//F#2ȿV?"|FR5"wld?Jm]~~rp!fP(Z$+y=O>M}[wU5R*zBUEQ)0:4s'1*T̓@d,gՅ nX<yšl&_W9.#蛞 4ʳi uwgWIɵ(2tuU,(<m@ѶXPMn2Ւ'Dc"An'[DUկ"6@,RdD~yMo[m '7xKNvԘUS,Kv:hë$M t<D& uzIE&w{s9FlQƿD`S/d / YmSɝ[M9t_UMD?Ş:A' _7VdZ kSys8u/x%v~cH_h6e-x    ɱN{ߵʓTE;X6sE{c[ߺ_=*6PsbjT9[LZH?>xݐKY'n=d,@70<!>.e%[sIKk,0B+BU"jcբ~]V{cűX(iv/J{;a$$OKRXdzT&?'xWrlߞ̳LR{ԻZ$l&gIro{#l e;nq.:dN;?"4g[س&2UAh0-eaa~ßV9b (œ2)6_yϗ;1r$kn 6N\ݘSR(d/@M>o@ۥ>aAf{vtg}JjOcKbO"gO79?&@a i7 e!t50*˚= 4d'sO@s?1LǷ>Ͼ~o aʆ)leԯOw\#;̊BUtGA,1I\wDo }/e7hQ|&)ݜ RLH#R?fܢ1 ='@#FOd QDAڅ HMbD& 6Β>ޑ.JǼl +U8)lUNFf \>(7Gx*h2afmrq͈4A o+mA#WOxlOezgUn6z"Y;k CV y蕢`!Y_hVi[ݡ ٿ*u~QXR& ]@7!zv6 -Αx?㣐..lYe4Yeb_b4BɪoLDm>P@MiHGYhJ=)H~. x)ޣ cYD%4:,f|@X8mx~xc1q[/"G0o?|Ux n }~wK , @,͘lar\]Xti)!4;n8¶sƄ+U]*Ląj4i% gwl^U,7"U0-'tJڝ.U@]߭xWdس/ 4JY@.hwN1EVVƂVDZvQ尫o#Q3.yrA:9Eg!2'6A#|8aJi4^mO*A6kÈDnL]:js_A 5Mg,E ɘ|X\Mus;]GWܢ |nT y2jy\"H- +bCA =ڰXj WqY:qQujiqu:#B9 Do}C]cfont.U|nuJr 8mp,L{WV$kJ^6s:%R#/Qu i@΋ik8w}DG圮]JEP}eBCbh}tT^B  XBĬ -=w81W?]WRvu{H%io _z2z! " /N׾Δb6 n*@^hc蝽g^泺k'-͖ ,X2`x0+ X @^T2v+js*Cw҄c#Tyv0!y$Dž8P 3Zg/A"WTS "ܛly+\T?uJdt/Be?.04lGNupP'uL4kǣ4̤- '" 4by|cyffffgZ)$>[ K@OD]{?iOqU>qS!DO.O@>GEƈJaACTspvAa@t4G]f}`ρi|ckf2` "A[YbDA@MD(E2% r'` o(g74:8H]I3o~_>ta0`rX 6QBb;[c-r~{ߺ~|>g\8NLR0@O?/Ã]=YTQ\.|t(1) QۏFz`%aH!ԨBKUҡa#rZ>snypZ8I>FD:h4  .FH.@q/.͔d$BC3cI?uFLx5d.a7GK_hK"9t]` =!/_nZ7W6zz%\VA\p7J;J 0!T"Z5hm&:|یk| Hs 0QrcIqcwsz˥Z -%ł M%կCŠ;PH%hNL 23zdHX1qN@JPՙ\ZY$h}.KTujK vvj}HO 4{c 9Ɖ#-+zWB,qkȲLE``?oA4m!ZP?\PBo}];s\{;r919~冀JfeТma`IvF7Ak%=fݰ 6)@0@`sq{DDDD_>$^@,s XMŦ)Bʼi3Ġ7/Ua"*14$V8TE $c RWOD_aCTMyb BVrˈ;R!O{cM&f3,b?Y?g;K/50|o>CH a׿־^Mo+˨q"Qk%@yseX\@` `cAy*|qևѶJ(ZѾ+V.xj[m(VYì"0KV{e#&[ h-Z&)yPu!vE3W-tH{+HBmDs" 4rt# 4rz0WiNm0s/tFkOTY?w^| W#0ȁhQr)fL)g&‚@e$c3/y\ g*ϕF penhV$ 1ہw!ﮐ]sjvfb`:* ^TS2Qy@1z8iDFdTf, +3kJK :0b~k12[pxeJTiWъr 9ٍY3m-υ6^M4*`GFУڜ h鮤RS>ؐeǤ*3XWpQaARdSQ5QEu'_}J)#GdʠO@c{_@ `m'yeo+O3kpl,;  !B a+eл j :"3Ȓ B;2f6jT2 QA6xLd=m3"%jAIkq~Q`:7Ħ}a\x3zÏtcY;d*j04 f'|䘍Sruܔx(TᮻzH g%,{MfQ_o'asdhw)5wl\4ECТFƱC>A 3(eI eݻBn "#&byrq&AUYX4 @:(9 c<ĞErv(StpUX)"sG Z\I?Ok>>qvbe'MmuA36֐uZnk4Tӧ4=9׌0)_1O gj{J`F9<3Ϫmۀ>:1llj $[G j&B?ASnD2!~A /ԮZN>ȀywSy07yF[v0ww҈z?O~?cw=Gf4Чe Ťo5 W|N],IG&E Ql\qGr(Yȁ nV dx.z?.ZC"q< vω:) o+:3:Ǯfɾ_' X=?:Ec}PS{"bo r\"JBmA-d$K ZX6ç >`J@祚+G0N/ Q""i3T7WeId -2g t:G$ 8O$@U+n̮Vbg_ QPZo!xݕA(2Ho:Z)9$,wK;2ҽۓļHLߥ}ā; i| % /ؒh&@nh"݋ij|¿©pAYfi8/"<ŀFor^25%$`].5ԭbmg&P yTy=B~E=0FQe(O$ig| :Z k -I 9b :D|n9H ʚu/O2f߇Fd܎\ѹԕ 2Wk"i{3&u*{ᣏgN{ (d6d5w.mML*O~k*Ze|F,;cpBwMr0X?F~|/a6uLGGU2KYUW^Y PbjqH֕쐽Շ,Y,:0˹=dN%eH-F@!|ن; J91]:`_@8j0Ȅ OO%kq ~I[Y\]L?gJO>n7kTM͌1~Ov]ku,'owʝ珜^l y .a#Q. ơÄ ׺AGwTry}5!߇}W*7cMM:axvqƛOJMԷj~8!ӐTZg~MrEı؃ Zӆ A\}hve88ω\YˎB?o?~bu<$D `8$ Vz ƛ<%7 .Yc~䧷ĄE7jﲤK C Kl 0(LzY7l`0J1;OQ<[)mYwP8uHOu \].~N ,obT籔u2O:GnqqV~-u3|?Eֶ ŻPW?0@>=;yw?A naGQ;ȵX|{)cZ PVR97w\_\/C&..Bzڑe{o "4-jP, [-.Ӌ-uR-/=ո^ѣԋ9vǝW{zVs(a.dYCXO8Q(-%vȸmLvVXY#qu7%WOڳ(duf(m5l XNg$;}*iu.9D:I=iU%<'"rY'֫Z.C [ߓMo_ޫJKIRѦfdOW!3C=+/YήyAӤyl(VYz.8KX#)CĻovꕫ砼F`<]a}A>pi"U0d߭ǿO80~욙"||-ǃKpS[hcAÒ]Զ@w}ULA.@SFnXm;>Ly@oM+>,/.K\]2%͌ b Ux_9u V76XɍI&/\95 o$B*˝/O⠂X9[[S {_ O/ffNj1WZ*P`_;&pv7" ɮdUDyc9¤7;FǾTeA'h0p-X(P^@0 `b6x !L:4"Wj;N0#c)<$MG(sj^H|_AuJ_ 4,yd~xSx瓺]ղ4meN!uA #(:q/  >sl{$'g$a K1%OÜi\SS(0wZu{!ƛdSCqS@*f[{@PgX6X%j Q>gjЉmPI1C_R^+< |RȎ sjIGKK @Z%.ѣ\ ܥCц`ݿ3g,̟ldi y8)/cke3Io <`@ʡ8$ԅTjA߇G\pȄw>r+`ʹjm R\NJɢ:P~T~dN ڪ&r͉#쌨]ܗsy* -*ny YSxT0'~Ep"Uu]ʡ2B[]P(֡M $e0o b>qW<>/a jc[aBٿ|Yfy*zޖfe{8xk\Ruv׏5B̲!ȿMme-B%33Y̱ \]%4 sZ9Fni/Yl;ulb޻./vd%s3zOI5v6(i#Nhc2~I7@ И-RcD?+ܘ` ~,&:SMRCt24< vm,]p31FoҖPSDl*G## 5֍b fW bAgj5O S-OE`{JW.Eb!cf^8KMWz/ɇƫY۾/L#Y# .;'ϫg{n c}$3l`wrl^J2 @UYcUZ>.E[@g\"|1M bZځ߁$6.sE<)+Q1u؆ ֊ $X4~R;!oJimCFΚ dr„Ch|h!aUfmӴqD.,Y*18#(#ӊRB4!d_Z}tϣ"\\6ߚOMepc;'vxCV[sCbV\]'g 'AT,\jn 4l,4r@.Ү] ;ݒ)K++-nH^Ja=/,i•o["z6֮QmC>Ms3iy;KNNWUrE$'5g͈fc#U}V 8(I̳]Hi`}ʸoQ!nOV /eMZR.Ư-s$#&w5r< J<[ 1gsGh69ٯ0a-":rH :x#M<[%9s'h#V2Dh/mgDX}:,UˑPބ} DE0|uDo"{Ii CN͍>eR.蹭#t<k}2<,ฦVdoK"R)8_<1&Hn_JD,ݜf7HnߧNyOBk%.0]H{vBOq\|+uvp4/Z-%9?^3!(DCbIXtA3'` ˈ_7z*rfm,ϹrO"I("oKPP"S⥂kphX?.NvyI,=/,[ʹ GNl360,jFK>:iZOj)"i2M9 1HIύ6h{dmAHM5r 6PnTZ?d{H$0g< ίQ q޲~^yw#=)DYW:Oq]6?4 )߇UM߼'88_"Ez <|Rm~kNcڒ %HbHdVk4;iUfA@C'/eWw(x_kV.g;oVcڛA!-TtIC lؗv /o՜drEHS-HnHߐjZP\ƻc~ l*]YNDzHrf Dԯ@ KZ8n;|CVU'R3-/ZvVqL90 ܵ/ HiwȭF@%Q!枧8q#$W bG:%9Dd__c j  sFQOU Q~-{ :⯷_ Ѱȱl775T,Y CCCJ+Q2 Ӄ_`P=˳d0&v{Kp$gmTTD؜{GͬК0ɣ)d,ѳE!cF 5dY/qGe*35"56Yu{6T{ $o53F ]Ҍ,F6aXoYml2hS ,}cfIY.T!RTr55kdد59\84r%8jRdn +Q)ߋalD=%G);EgITf#1[wd5Fs&2v {%.E/^%}ǜ}X~jȉ$dsA fƾsC eWx=Z|&>vREwN-kk <ɀ{icAYd,N H-(Sal˶30ҴXYkj4dG.ͩ'>4jm2hΙOF؋Y$n[rFjs450#ػjۙ74Ew+c&\ζ2I6FMk,ѭ,|ͬjj50ֺ͍MG}L53oa+)SuY#kͩحIQJ~EUIXpCѴڍF?MejFa+G4hNFU oow[w=UݷĞ!q@%H*Z {m.[$uo*\:|yV@\lyp՟f^nuxڂ@< ?']ǬYVmdZ![_|SZ  g:0hR^W༬9pP}S>P(L)C'^j.DЃ *~{B 1apAbav}hͣ[sj~0zɱDin3[rIߓ6G[ѭz]?Ke$WXc%24و bzofs!_%W G)[G/^tί1x*E5A4UЯs !CT&<K4o~.l! ,P2BG6zM:EסWD<9xɁǶA镏KBNź@n!!/-wmggTد'$P|bSvw;3ClAЇgC闪v |ɩ&M$iWQͺ޿֣D'a nj&nVV`ɓ a2N>nUBqݜYlKem[°aӣ6inp^D*$1*&Y#j/ޜ!%lu8V+Q#{SZ7uvrS&- f3d fهzNR'(ICMgܼv]w&!@cC 8y_y-Krpu|zKRCYrkVuk7a_YcIyaz)ZY $dKtŗ׾U2XEt9•GH2 Խ#Z\\7ސ}LJHb氊1"p^n#Ad\lUF7""%Q$ rGj0b0nv2."gwoVXC=æRYrD)W pɟʫQ^o%V)JuQ(o >{$@8[*rڦu^ k^"ku¸x>htŀ#ˮJ{-R"g+%]T_K]1xwwzsWLGWw\PG:Q+0 pՈ[Q(Q@P  \C'ӟwc͋-g蹚>7HLF]0~#8R~׽Y ΅sתP6y*$Hz$aGPӋulD#H4qt,D6~iY8]1(NZ}t:Jꐅ]NEk":)m vt+\3b72F4|we\7>444Kq[x/7- 1SpZ宥LS9ыbHोHlΑ2 :24Ɋx L C@BP2 yJ|7TbfU (\`2z{PÕ'c mu~wʠo=%^,ʼn5, B?6ũVDVbVK9OޛE5;7oyޭqB0*txC2.cfglcJfR?QE~NFSD9"ߧ$qe@0%HsGGzP3& mMRdEI[_bj_9Y{ns556ͬY.ɧLͪ=ĝO`_dK[t$ 䦦DUU3[W1) ?ku4c!q #ײ>F<$(el Yo,%U͎_qHX|biLMLB+p]2{h_;#kH$hqMen{5GÂ*d/ŕ,>g?ʽjOv# 3{wY"c24F4|Zr'ֳjdFk파xS=#s[ajܭ9wy*7.'C7 ³fZ#[Fjh9fȍjS NOŭ_GQxl,2 3@ rXj^.b8g *M gmbCv+.W{Ȩ< )V>OU֔@AD-92xk D0W\O37.@eFUx`: įJ6N^#\rC<"XD<(GXaޤ4$D-ċ5 7ZHSs5L6J喴l)+ 3bb~M0wӈ#G4k&#H @ [CC8[ [駥,ΐ˖̨bg4N 8 y# m4Tˇ7 <[ZPQA1mH%-4ϟ-w)r0Ƿ`l2sХJA2a$"ZB}.fIff>Ř 1BxMe7}faxYbֈu B $b M?<ƴ"-@M!|֮%,kku4B!RSQ! hF$ ?Fnl/b"ͨiڜ^tK\UH [r2$2l!~} \ץș% 5vRY!P 3ѪAr3:X \E$t tl~K*Ĉ%~|yI89FbD#3 75̵)5;XʔYRacHػ DT YP{NFİk)TImLO u"lnhx.{QuXhzc9U][E(MaP2%E{ ] Uf쮺e$E,w]YEٺXpf6ǩlQr7&fa#S7=+SC[Z.бȂ[bc4pYl#vHݩi|4E0mMrdS&2+&ht6=Y'42VH\qb3ag[[Fjkklaٽwoz9'p#}TFHOȣ P$"?c?%GR)ɽl/:[okttZpJH?λJ4/ mM3g6 @>o2| jTO% "IQP 0}熑t]w/ ª6 dcW .62ܒ?lD(-T[YŸzYIZL0͆qS=kkcl,ྐྵ6#koqjY}QڛMu~z8886YQcu+'S&qqdɹ"[D5tfgYv~W~~}5(RG =t&GtH6$!ȇ|Kչ5mmV31dQ N(㢌M.re(Yח:n}:j]HqB%Nt|ӦQ\:ƛ`kkR4= cW?[> "AՋJ殈p 䀙*WXVb|NTȤ{*d9sF/2O^?0IG E!{|/XJ4s)lA ШfAHfJC)"h9[i.ϗܲy5w|KV+%Ћ (ĕPGȷ y+?fcZgz.j4{ĢO`{3FL9ofo6{Ooor?O3( Fg hυ:h`Dr8c$5DžbxΉ}a@e4[d1_4!~$r.f*x*!hBGZ6y/F!(ՕȆ3@hĹPX77S ~#B]4i8fnQD__GT:ʸ׻*Kfb:Fr>[i'vqzRu#\P+uM kB1ZJ8.$DtU^{ӧD#p6\/jqw6 u:7>A'L*aKM7w޲]4Ifp<+BpgRNPj(Jkt kv^"ɛ!PAqB_;̤o$:WCOWލ+/6~r-\[Y DZuJo1rN7d\ AںHwuNõs;#JYyiϟLK6k;i +Vm"?<3 af#&m!J!}z>:i9ncf -`r=d !QaHGAޘZefمLEN0QR|j!N8f)"i3ͤ% ;%i_嫭٫ۺ-2"TxQdVu(_wiH_㨥ifΒDGSPSrzx/6jF22e-<E sDq1?Ca,R)z).ɪ] 'u/P/5pR3IKt{4mgDTKZ# 'okCĞ>/ZX*?3Yݠ7!kǢu \+*Fc֌$a%÷H]u# YD/6NPyOG"G)GC)0ߐeD]1]V?+:̇on ++dc6J5SrJhhڿ' h/:'?Яt(o n>-!L |B}Fk˾|[s_O?/ͳdGk Jg.D\@g3{Ƅ9UtŹ96 NapB^{o˝TF@|gXIuFQdv؈ FvwگG* .8ɘ m˝z[w.}s,t;.n0,aDGs&ܯ(f4Gt @<>[ z3Ÿ”$I,52 ٛerLҁݷYPYjĪJD] _԰bn垧{w7vn;~'K飶25./Z HYz+af/OHUҜYwkZ+X-eoؐ {Jr$v=rq/tOwש^֥.Ye}u)+ /k[kh^/ƭN 5suVOf2Ol*@''Iec9o?^ #47Lk`LBk{kSu/'y=Sk.SU݆ov:h3yrWUDQK VkUy/~LĀZr dtEHVƧ]ŵ;V#rÐZ1\fd՟r/-f',p=:NJ4U6L`YlKhڮejKJ7$HH&nsa8SϺM.|6{0д,5&k7KP-k._O,A{!5vy+w[uk֓&gRŎVĥ,oV{]r؉(:{&O:EyͲv]JAt0ә5:on<'?i%f9oeW./zHu6՗Y 3_?奄'+?gxU eO Hr$¥d3%ˈ9$+.Du]A qV5y1ch\ڭM0 ^f?YdW/5Է+,=WB2Nj怅o?sP:9caA [ {mM"A!{Ba}I rLRڧEy|h_+. ƒIQ| y'#D6HWULsleǗDDDDjZ ;<?st:p J gEA!/tVWUdKyu2ZXIY#D*0A'.WeΉHர DPGdncdcgN0դ@kbaOY90fVHUvD;MK>7/朻P~!AdAfSA YEG^j9fDj V/,xH9zt~|㮗DD?% Ξ֭ ~phPI;Vпy^xaܥŖ+51Js]ܑ7>VJG!ű_:_vڦ3gnlWm "Hrߝ'}^pH IW@ ̈viQ> ȍYquEm]%")9^f1-Tb18mDV&J.(FBdmL"rn\L]޲sV_o=?Ah5n<" d 4'B%F~<)|^8:@Nr"ִas""!u!EK0=o|\LDBa Qdꘛ;6H(Ƭ=,ه$%f(C=S_ȋgp "9;lzN̴|msw]@D"o;{x(y2ߝ2Gh@Byb}6p "U]xhLMqш7ill zǎ6`b" \XR㻂9U5)04QR)Qd|<8:ڸRyAJ VE4V^0NFyMfBD(\DX.\r" n3J&Aɻdfn\+K~o8&vt=L|;aͽ0*N]#s7c{Sj]&ԃ FԖ.o9.%D'ˁ&9 BD,Vyo{ď~ޏ~>gj+1- Tp/\iLMxTJȕwS""zVW{]?!c8 A~`v&)DسT EO)bI ,'+zO4`C߇S%զlkL[B%&",Nk[o/|請Vo=??+0%BU,Uy#svbgi'5&^<=Ny˵d]jf]4D&<:ͬl4lHG/+VV;UV_tCb՜*~eFLu.t0ѕnk HL P ! E'ٻe꯵~%>cWy+lq~ QUq#Aʼn5NbB(/LPX&GjDx|20WQyg7;~Iݼ{`ӏO?1{(D)G$o=ȷs >PHPUe[&F00f׵+U/Tva+[MC@U x%n͕ ΂}.k_C1M bcDn/+F`Ɨos4c-(b,A!.d%Ga &s  [Qƭ6k ճb7Hq".TN 55AY_?9܈u9d,2S93:/f+JRdR«kQTF&^<"q"BJ%aOd.]ZFUSG;vh³xF A=Xw$7-tm""cT(tS(ʌvfp$э}Q/9f WOXLc$ :O,1C-wo'wtwTE;E5=B$%FE|B&I, /PˮryB8FPEH i %/L EnCm.!ppmWʲ-ڱf˅-7Tumkɖ ҢY0tGS~)\GCJPAp"eo$(, ~?8i> +?9QT&qkzɾ+^g]G,+R"ECf#bAIU~LU8ͮ0 N~ϏcW;jyUiBV^+ Q>Kw) G-P]ɹEa$':"b ϻԍ]X/Ziavetbڎ8E:ҍ.Lk+)]p?>e3_ ˢK5KG^]lL/{+{S^ޯ-#]~ΏMJ<]ȁFv=^RKTB >ͪܘm߽xݖ򭰞%`'ecuUR*ļJܣw0Ɂ6XwDj8g!¼-:uSkKHIK׌l>(0+LOJ`IpfVex@9KvA˫ pv*--#o,VrتRD R`giwB#Ysn878ڙ:rEu;&+y~ݕh*L^giJZ 8*BIXnu.J:XqfTphۜI7UVarx*QׯLu ]qu+fR2v0WbDuG%.ZdAF30q) -E0`ꏤ'")$RTm[ T, Eƒx;󥽨֕Vn<ѵS%v]3&7®ЍkQߍJlNg+jIg]/ǯp+"λb$ȕ_]Z-{T֝sa~1zKbb)uإ4 M~ͼJg.n츭Lc*%~ ([{4%8cʘwJӲ,v9A1m ftRiPETe~|cY@\epJ8qdT+NaDBD$R .nH3DgD;`Fh͂KE%@[f鬇I";ۢL@2#*I6jA58x' BHJb[߇vaoයo>ϑ~L)1>@VWJiii]̐w+ܶ F 2\BnSۮ3ejÊo+۪ 'vF@8,H5 ODɤ~ɯN`FbA#[h@**tQFx9xg1*4y(j:}b! ܗE"2`&BP`'m+G}qb7zZq ;/7"psm{m̞Ot|NORx0i$vI ( e/!_(5hԽjbkPlj5*…a]3lUD/Ddo1[5Unnom*-qj*;mQhȘ^U K\1"(ى`bBD,K,iz#Ki{UHDFi74TPr#^~I8s}A߾HEv+`M=uSḚ)Nŵ-|+F:9+Y &nY\dD[V+)TW>LUr=mlpc;SS]يbWIfo =wNsO܅~S~6I,NG'mPHQJX499a\=-Q-<סg7m\0] ^[}Rw3:_S~- :hEV4D7 Fo= ?};iۇl`h@@H3"A@kT}~bYvgW,Ff ȱ[8{>,fΊ(Aq3!_6ɡ佗o4^%Fiiyl[XDKTx412RE?~xQq4m,=50Y֒ˈ جzQooKmnKZK-h[YGn<=Z// (H,R,UB D)JK&gkc7-Â8"n!DD`ԳvVݍHHHH+'t]cOklmA.R;hSn&|)&a1)A2UN2ǠAÁҼv&UKT@0A5 $ d&"4Ac?US~'Ws)$%DI%@DDlj^ v?3i Wd^-i|)KKbpشzd旱&wBAJ /BԔ|#}=²>Ya,*a}jT$Rޫ. U>,;T+Se[E[wZf0dֈ/KʣҠJDHIKŢE% W,Cj-F\^N/h6,7F¨@F:a}Z+]AuF&ݸKrx- 0X.dc||X8't䨷tN~ηkk_W_?v@(O\@QJ&ݐ Wթ`l߹Ob[ 7@RTj<@lOe{9F9b 'U6{MuUNiyxHMsúESNQkPTkEl+}eHmyIi&>f .]c싻i-^ؖ40|&ܲjt+ٟR~^%6,iRR7국Lޭ_xZՅԯu-taan).Yzmk[ *;'uVh>Ү ; 6N^&.!f0UJN떋N2Yve_!z*wQv.ؕYUmpv,U)nvPC|XryŰ) +vݻ6m]RW.n䓬,0ɱ+nNNy]۔snkx jY]ȋlMiWF-/5^5)4Һi5^z3kUvIe%2HH$2Eq^{Zx6|Y27Z^xJ6#kZ~"N"*Dt|\lbZ)Գ{kQwklҨZ;lwp']ga;V&h3a.+k3$uQms?~6HKZҤ"X,et:[t<軡e!ujʕأwHw["-Ih:+)Mܲ;\F槁$UG"UY=W5+336a:0$, #dw;{e!V;Ma#UN:P淹h63Y9L3ł@gt5 5Qv -/݃s֐A/P0v] "3BCCfub#|\J-6nU!S DeX$.$ *RN%ĻB1!"eP,H,D̤ Dᩄ.g>5V~@=.E-'߇lkvUZZQ 3f*[x\͇>p߫IS|&dӈ+c`*D!CC2 D}CW+"s;Gi˒T$HIv$JVg̖JEKIEB)5x#ՕJ`Ykb 60b7h0<XFp"S4d]r얫4$.g$H"Dròns= \mje٪DhݶNW]^rg/ )x^^SZ)QȹL+4g!GeeֵYd".)AvJϺZ 1UڜΘzu.g4H;;'QTR]? sT!QY%2ԊD5 XmM5 JP!LZ*ҕEh$05 䐇>NmfְZ^J)ey2YVYWL$^xUWVڊ UmC}RI"1X~ qHJI*$TBTIU%TH*H)ygf [RM"ִYjV%IEٱ(0Rgb%gpema#V\O֤~UMU{+M^r9):V-ޞ$.LđF*f.K".65N-*2RRfQLx޺ɂݶ?V;t< A@ȂD~.ihc=Q+]ޕk-O/#W}2|mRRgJ2I$ ܌!Z#Fgynn[o~ƆOLi-d\.kZ.+ڽ=c?C mq,v:$Zͣjzhmtl.ȵM+ >j_}݇%n\V-=MBQ @URZ,͹cV:gavkW5BVιyz߉/|`sʟ`]~j9ܝv\^B"i8s62N1I@t,u8P$;%B`̖0$Td8b@)eTa 8,^fEx%y$C$)^PvԂ1g-+I5BvJ]MPZ*en)H'ӆHͻ@eȚR&@q8F D('q50X&ZzJ̌CDxqB H#0YJHW#1韇;|{0R(>X Jj!8 M)M@ Jq-BM T(M!8MRQN˙Bj&u*T&)Ji0kO%lu:ƫQ8n!tSH@CDD cD p"!م@DMv16['rWete[EqiF 8$I$$<SϾɇ!ŭ?s]լqlbK[7=7/G:RVyT^`xlvKIV^KIR Y!XEJS_E JKJ1-X%aeԲŢK,R"DȔ^Z۱lj Ak7K WQi%j\DY%+"DbVZUUW^.DB,YV^J%jEGE>ד+$uݕ2\VT|ԬeJ[+I›$i)!VUR$*D,t%+)=SU{)ت75bK2kDd*PBUaj@XM@m5#%] ŕ(+"ʳ0F앛$xj vb֒:9!d묭fGu)ZkyoT1Wlw0sE`- G*?Lljhζ\;7wjSmF֙+i0L,u# VR*:, R*D^| C@|h@ȱ7$P`K(Īr`šƋR*E'$Ȃh8c/Ǝ黳;@ y"yy E? ĕiT?xqE:۰B3R%ͳժD1k[pII) 04z|NO?S.M6?5g3|3Uug^ky++z;ކ`L@E TB{venG lwGJAG޺κ'CGҼ8JiiIB-ER%*1QU]^OʪTWc񬵭\] t6ừ;q4y$Q%aMvq;X\P/eN QZT>tj't`%u5eۤleQ kz.ߙjDvYr27nHءjdii ciwz.ZҒ\pf+ye+T_k% h]eλ#ѩ՚;һ;_;?$TQy5#jHJJEq.$k\99{eYeCJAQI$yO*ywC4k<6mm}{\U񬭄v;.<)]I*B .au 7<_- U5<7GȩZPTl7.qxM+MY~'i))MCSe"܎-M-nV;F\NEy" 'yV/g13w\Wc"˻&jMU:ƙ:хhI'QgOɢ&2╔E11$}Q~ޞό"ydF>][S|O{gMrUnp;8-W.vnj3eLmSkAfFa>ZIiiac3xz\YbdJIPkhqu?E$YcZ{=jSSUz>oL7f0Ek,ѓS$I$]{ܫy⼷2~mG[g^Kl[FE-ZXRSD ̅.Z{X 9T(,Pz*N6#tGGQj 8ս6|Jeeܽ*K.^I8UBTW辧QRu?\ 9G'bm H@8֒ ƙɑ0tP}ݔp#9M^> ~\-ymƋg]5,耫E'T0D2Ib"HRRR$UP"P@ D V ^^*D~AoXH@$2 @ 9Tb)ge9RVw;rVjmZJʙ%I~(9*r*A1['af[6W;ѽfIB#sfl3W䄒(# jpqjxl9m&$bʺ禔Hx*UҒZUҤ +ScYʑ$DAV.%6m6BiBBi_=51Sdr\Gp4!8,+)_ޑm "q "!7":BJktRUHII*$JJ(*"RBT*].W4JY;Cifěe *tLΕJÂ9c .*M9 dҕӷ$$ॎUsܑ4l4lawJ\II$I#4)\ lsκ=2گ'aeo^$(5%UBP9IU["2BJ2JRT"QDBR{_˾YM챱-7;[,x7PhΆf i*ԧaԊ$4nTw'~5U9QQ*$!]e6Lo~GBF+?Yh¼*ViTb.ul/{u鮺Xe gY:]Ͱ܎]uyÃۼIxp^ @I @P.뇗/Kw+uKZ7o'rI$I&_ekdjEfL2JTַ9R榤22ZG!P0 tKevtK kEu>oVqXnN;_+RU:TY% z5 0%(,.nQ 9E5bc{0n@Yj]НҦV̪)=")-i;.sO#;zXW?g_l.ne53*!5ڻ;ÁDBvPM6em0 Lڌ"/i3ac5мȯ@O11 Ʊ͊y#n!?rH~kNKsYyi8zXYd$eDY A@ JE.WhY5:$Y5ԹcZJ{4%TI$IO~Nz蚙%Tg7BI) BБRb$fYU$H!#b KQPfl.R̲Ù00 ,&|3CUi#,S;Z{TVػ=5+hJ-" B谡RM]qIh\Abܾp~#VMֵawlmX*֧odNfk-ny{Zͬ6G7*I$I$~'f:57 !TtY8]c3qfW40VE,.˿~ lYyz#}96_]}*FfC4> .qKk罯<[LgkMDm5Ok9{X:ъ3t6RuT)]ur і݉X!C@r'8}oΊWA9s}.ք̻w-T0 .\\\v߬}'<_".ھObOf'έ$?~^N3^LQJZ^*EIUQPJҕ%RE$IT%Lla)wZOsF봺GޣDGHz#.W v9W +SI~kJִEҮ]_`૴'Cm8;2 xH+N{6Rve"GB<^ne4^Ga+6W3QUV؈a9r@\qqp"H@$ `n~2~Ӝ̹H|ݻN#[Iw:]V$c( (2},'p:.<NvFvX lޥյ^DF8UxՋo*Z+0@U@n4#IkuٗU@sf縑HR -CɓuRQbN!;/kVL#hdzEA2JvIAd#[ApTԯlx{p@&ZT"M1U,4gu,.(c+n& 8ij%{ռ #p@FQFGDtd;NMGӏMmtwn`3o͠I5Xj9&j6Mv+CcUmn .n,kZYhicI @1111 @bbbb @ 1c  @ @H"D?^$H"D$H"D$H!  DB1N3ukQ52Ս,4Wo|JUԫt4+ɣyGפI$}5r>^Ou=n .#h!έ@v9Ϟchjc錌O2bȂ$VNh {'+(_Gz ŵwKay"c0fDf9KV^n)6eׁoi2tqdnTC{2B)mQ0w?ϣ $hGc@BBH~+J-R~"QQkh^>.광*>̤D @#`eo:+=)ESsknfk}iwHm\ߥZd{ȹr _t뽢,>v [{c"׀n;.:,S'Ԭ /XNoLt;|!)\Jϯ p+=\=;"na ]mA,~>ߊ(DZ<'3]xi.i%l,H2KI8i3Dq%Hcggt(؆rm$׳jUA y7Vd? F@VgVf8}Fn/TRXM B&"3%xW! | Kk4a %sr[??2ӇSz}"a<\gZ漲'pˡ8,v4L m)7% $,ɑ)HF6luI>ų(B r Tm_ 1$nudkJ Iɱiģ%n69\#X)ych VŹK} `u*Q$l6ֹFP="&ͿL:Ƌ€H'Y4DQ %g}eW")u2pgP ЭnΓ`ّ× pi%Cjb Қ5=3C"iJQ}hk܂ŽM-AdI>ח6 %LTbɵ1GRԙ G o PgJɕe5#, `Ζ`U}hǪmӻ %Ϸ6Ks-)CPk3DI{'4XxCVoJ),uq<8JYm %q[v-"Oh))%ӳʱc}XԠ+`9l](;li"?}|xFatbFAYzũDxye+dȷO <_w]:2K-*RsO̾cZ" ,OzټfUI˫EIdE#R+M<:!⩷֑ݟ`;i ☤hѱSYdݕ,%nS?m=hSŻè-8#{DDOÙOutDAg#[`.7[д%\84!.dW T.JWC Z"МmIrxвUеMG?fj,ubh@LLTMbäگS $YAhbD#':d؜mcA-#ꘜo)޶K::oVMf]H<38C-ч|)^\jɼ`*o*FX2.52ӿuzpiڮ'̘Е\ @dyRi.DKz -pQP'z㤼od,@R6WG${-nenxZw&"nWmƯ#c Xqx%}l;T1ZRE%W)dU=ˠ]D{a[!qX)<Zú?P9=fL޻{ ل!!%R`.5g]PfLtw߮ 5A]^VLxrc)&3"ZgxZK mIVwRVsP8$/MOf <&.|X?z]-:Pն6,c>~cbqpcJ|̎g:*Ng"ԉÇ%'_ QYOi8NZ9Ғ7sX}oF ͮc:g&NUv*MoŹFa%iPD*N^A0t-iTMpM::&."ױg s;v]W4ZKYbO"96>;D*IZejľbצ\2,\ɫH ?a:Gԡs5̔|HSDi-mKuήZj@zkn+>*|I\X~֒+;Srs &$ M-+hIqp%(hRuPcWcbadf ܫS+Bn[)$>Io Oi$5Τ^Ϝ7BnI[S"N: _x]4#5_ΐ=qsqFNz^5"q`L]c 4Q+4#T}? ǓЬ$h;{)ïT:MK)XCi+ܬEl՟}dxD]vVRYn:K1x?GSXR yY{T`7X,58Ewx Eʪ?!>yr351He<@Ґ,ߋ,!qm)m즩J̸GiG$4-<ˤ?Gi?wZbPeEn3-6ܘwe05G?} -e;~F(٢ܿaGK޺=VY mJ%~(ӮE,ꙐHweeO'0!HX$H۩\PSS4pGo$~bowE`\2O1V".9-Z΁rDKNZ ß[0 AQO@Fda}qHWTt!ϝ#e&mВӒ9A>T9$D%׸f{cC6g|A@K@GOW֋p)TI tJrV2»|`:s8 |5 _"ﵯEE( e$ y 'mQg|ŕA}qi$LM0.iT/]L/ս]LGɔɛ>$0LM0rqAB( lG{ژa`A12 V*>Z}M8[ i;8~0/W=. xyi2;?x8zx1 QCUaP̫b΍ c a=p>MFle1LQ$N?dU֋V'H&R-dlRcWLG׮QG`&5d &5|Rd jBvnܦ}}Cj=DVj"Ħl 24u%Siļ|$8W.x^7 Ͱ{x꺘 ]u-`7ɤ Ko)P8V P]apTn-M9v&On*sbM`ȝ!cͳ3mo_9o+p[Wys>SdD"s=E'! yߖoaVcadC閺#JZ>T 'w# {Sij6Hw^.`Fm7LcyuRQY=޾(}2Ғbx5b?ovj,BIl|_FےƫZԲp~"郜@N<[f ‰ƍLV&Ҷ0'fpΧJU01 XZdJZ>ZsG*q1ٵ],+ƹ/76;k%k'ž pq20&*⾚娇KE$L>Rb\;.T0kQ^@;t??Ͼ랜XEv #@>MfɝƺhƼE ފ9r0VQ#5praۑTw\w1(pnԞܷ ږֹj%+i %>]g,pksW'.Y8cB5̛6[~"a,L:A1.@]RMʝ%YS!V_bM 2eީVy/#rꖷr5 '?5Dt-tgKHo:HȒL Gzsgh2ѩmM ƒPf2EZN  mAs`2U9=/3;'(x$N DVL!TIUS:K|+8 q ~0SyZ: pga8ܚUjDr3.t>uV5ܰLBt3/`D}]S>6öĵ_%:WVHߖxx d@h48Qc6 6g\8r~gxӊ& )${Ek(`: ^ALl%eGI2 6h0Z4iO~a6[ LllwG”ւ&+?U4mPY.Q_شԛ:؎/=DO) [ObSC5|ҩq0tr7'x ~ /Blu Rӭr rcB4fBӌfHi+PYzpcaSpi4"“X][[d|a&"u%f>Gx,zaVpWtɱ~*uE5hwBz巴NiLg&JjC^OW3Wi%9\71^[l1Ov&YCѷU0BU޴aF Qt$Wb #2̶.ـBǸ_QAGv{z@ބFg8~8 L|Oc$o'ü(?27[JP迣vjqIȰLXݛ!d9J&sjV WtS >Q|bzntJC'H5X 3*ڵ%*lu{(W? ~?& Gq~qt)HE]Sol Nzki$RS?&($?וTB<04\]@W=lu@kM]K]k݃s[]:] ,"ft5SGiS.Xj<6z} 9QJ.5?Lv,?d=SP!,Jrm2};.1k@Ϧ> 7u|ߕT,Z+'[VQAbMβ1Nz lQC̽#J&/EuDi 5vU C#qj)h9GdwJxo2(kL)!cٻˋΗ]}}h' e"ϽWlД[q} Z3N;ZZ+ 1,?K[y)LjSPtgrg j'މ/mMt2 Չ1S5.bYMׄj Dxoze|b *x:E o"+kzr*-0S^4TBa;/Uay4jU%T9o%FH<20' 1wtIOs[/'e̢uyJ~7O+b<+C8)Z! mWE ^t&WsJ#lt&`wZ 5/ JQs4:e*:)V"wYHsO?ૻ1 R,ᨷ׊K;"sjZ髾;X^8nzxD]hD]VKm?Ű2U ݄aG/V&n[>Y?r%ao[\G=(4BW!ו"W\ZK1ldQbB×;qP 0X@ٲoکaiz[ɼlT/T sD*IZ#f: $xQ&CC{:RMq ;75Vr[Y4ntwQK B[z|W1IPRMJe@N{%-`Á EJ mFPۓhs>=vcbf*eZd5O7Y+y5Z dV T*\E Ŵ,rѤTgb36(Oھ}^u}HX>Y.{J̴u.fL,dIuSRۥ̣Ym@U 5}|-4^MU~+vӽ9b7SPpOW\4 [c̄ye^nAhGiUgٴdu]i7Z~jDC 7]GesˡO2hu;̋W3ˤFͤrWxIEcK^}_$͗ѝv<=Ljܬ&zʳ=O3P-E0XFw2%5[sNHʌ. lثJ]6LQxh TuCpk_ A›*PqF)v ᄒaE?+k%AKG*SЖ>iF{ chBu~x2Dk*09c pQ\B4{iaKBE5YljT6%TX&dBC $}\XWdP6I.`Oߺ d D#PaxnxÊA܍~h*W}J[DŽ\b/C7*XE@K19˚+g[K-I;$soGVBxTQb1+3 ^~ٜ%t1$袅3 >lނ" kUOw' H̩G 4s #C h"$qʕ;%b ?hG!+PLeov[Q&?b>AǾP ?Jsu8᫏:Eâ>LZ`)4SMu4 ^Bb:)7.G& F2<pl`av/K_eGhՕRۇ)LEz+?Kƨw1ۺ]< q.Jyw!CJj)߾0%fNfbTTcoa)hH*G0ld.7Do5LBWQ{đ2_+lYCjX8t!uqtүޑ;< &4`ehAM&SB&߭ bwN1F3O#'BYr & %0Q )?&nw]a!h< ߧסzo8{= +őlgo]"A4 5K(K>KH+gnXB~%tD?lF\J2* ="w%p#~~߭uN41@m-א 6y6u5=#B!ö4K*< v&Ol[jDWxΡX5ax A^O[1[>ו-+k{TX&Au?gknďMdeK5ags"DMGvQ?ϛHEgGQ?rZJ.aG&=g,%,ܶeQD6Ұ=D.u;ja Uw0;Ql!hfشqd'REV171Q%"6 HM"lp<:p5Qm hG!r+QJ@(eܜR@Χ%0p3뫳 A+5|KqƐ}'hmI nWbpx>OsVLǣu9uKiqL}Xn=ĒቤgYh4~?&" Ʀ jRLXQn6&yoo /soTMX>2)FT' TRl>ӷl7"ҫ3!pLOHn;a߾Z; t9]qu嗼#Sڿߨㆳ&pQ;&Kр|xoAiULQc`Þ~oPn\#ظ9t(p7?KύE(ʄyz<R9RuZ%!3Gf<~gl$.Aܷ⒡ÆntՂ^^Yc~AW-J2*_U Gˈ y v$s\>!gKc@S 慟aU̹8ؔe]-gq~pFH {כ<}=4dU"׬[a'kfIXERxiqa_wA}UX}Fs#(.v]qUt#)g#>@K'y z'"P!ivSf碓G~4 [ AsSZV\! dI> cLZT72gy)hcTH59HF"R 0|aT80v=̢N)"N#qb+[)bp"L/{P36u)ŲCzLQd@sM*=ݡ%o]Y6Ј0e1xCk: "G;~=/h 8e[J6]35o7`Xp ȔA"L 3WzP9 [@ƑI_{#IA4a6W&F2|g *&B6[Í&0@ã3")T['}3?~Nam}Q`>9֜ l[0k8UqyOIO,R *wz \nm3o\x1u/$ y@B bcri~ (F%#:5" 6jZnʱKTm-M<0s҂@WPR䯙 MyW-^ԝP` "/RD! 'zsW{/&Duӎ-< Jx4 Lʻۘf:m=ܮUFSt h^_* E0\ ykE_r1K/g16AR[*Z6EW^FȂR9r = rNSlt!n:(fe)d$޴G"_] p-,_ _Jo;J_!3F=H=h#Cpb~Ti@ SEsS%= &k rFymvX^Y-fYDi$e/n\#AxKit3Av˷SX4 OOh951 t]4 ;鉿B4E+mYW Is^3RBxV'CUGg|MjLJΧ+ϥa G#ku]cBf+mO٪_etτ C)YH=o\Orl8.d[%崻`;d.aGIxARO+!{%߅~ .BP("ָa4l >&'JQ3+QBP3wHr :@Rc.uY,]+a K\S xŐDLn}Xi"(O~򞺝V;H (9mpX17+H7F!'wA iwy%.2LjZ[zp6$iMVYE#`_`ƧJ uCšLa.>{>2pM$ZfwɆg`|MLJK?V'فR YrK`,lyQ zVV.ptM)u fnRVe5Dܟ7/2lo$TL!'1ny۠/6B}:Юb~l O[R).oA{-aeڌgAgfQp@XkZz@-ƎѦA)Y1O5kH -օʐN:5w|KLkC L 3yL&MDB=)OիGnk[řS|v C9x Rsw .-GJ/g2 )_uNkGAV^4>>뗕m.IbGI4RjHs{7փT򨥬3kXPN%T @xlpLﺺYX7V Om yqwFvls~A/ͬ' +и_@;pkiYGpw6> $';2m40?1AK^ FڰE]ÝE~oS0/@͗>% cL|SN|Q Yb F_I5-|qB#-)!'rvQIti\*d]w*q3(KI rPm A側cʜPՄxhȠ\:p؆ $5wޓ6  3=?M(;:.anLD[1^/8kCft:*g2:|6 4ah<17\$;) XrAe_?~ٛ=&YfK*恗 }d'H:TPB%iuC`'keV ~yH;w@|Cڟxh3<'7FF&UDV:NV 7ٽG-[%H,ȺJqT\J}I (1x@e3w~oMxK A+L8i()MxV`ڰg(RY/BSh;`O9"$bׂCw{Kݴf|/`î.F˩N$,|^!Slc= g4 ejEF"P۞'Cv  Cf@ ~ +I})xZUN,@#':0jcݶΈ!NA.PT.jt5=w:E轑S(]aZ j+ CE0X4 (2!(57sj}CBaBE-s.[Z22Qک*qG1玅SY3rDcs0qAJuBu/[(8o N^ ڒH~?福Xk'ҋ/ҿg;>s'yD]ae0 i%yCk"$ӑ.A_{L!;'QK{qu||mݕdvĎ[!~9":ҴT, |~펊Qj yiz#T  `>Ox`gnzeD6}uW? !LY ʎV#:ؑIFOHzAeLiA8 oĨAP~LWQyI>Ż8Qktja"˧C!߀U45;r" ,~z #qK]jPɽ®kO9.#5gx|kDD?8Tσg|6 )WU4C_at˱jDHecM+|WS-BN\~T,m}fg[A}Tlh[@6FPC2o[[ *hxbt?kqpԽ\pel_}:/ПDC@u?_(TdFǠ}B֣'13чSPQok lxCy!EEbL>x DbS FMܑxͬ+AI#5 p2u==>B?2q>0,ur dF ZVS@QJ„;R0h(i7_%3lfGG &$۪'pėY`=_~!}I!Fx]N1 1NPڼޡ8[\ڦ3b!s񫋈,nH:cR,ECqe$FDwⅯnUCDhy!uUos:j_D( +PWP}hCP}P}`XRf)\~d$+dycp;=Bc_ Mba.˵ޱIz0hkoI]$լNykv4ca'Kzxn{2L/6Dߤ;7[:'ݕLԃKd͸dP`UmM3⒂]tM]2Znò"&:1DŽ(g5+OURSgX7 lNC[z YU`{쉑Dސ'~)uu؝xT 8C$^w򗅅bW~a$s"TLb Zz6Dv$# 4%wڧKXrFxҷ3.P "NsD.-%شA(\,-$^C yz:5l1$~ 1˸FFN1y3|CDkf 72DnKҀw1JJ5Wꅥ?]D\\J(;wi!|B뫭52`sE]|&D9鏚L Hj ހp!fw^:$;lWe#5{Y|M,9goOWTjEb_ՀXD!a^6J 8鼷m)U\ׇ` sܧlau+nXs( sdp[FjWL҄U pMBt_ pi.l8H2oioT nb3Sړ Olj fxhon5Hwx/8؟C@ e%'U(n4\9\-[CE!qQAǴˈӸor%;4cδAЇom#fz/O${$[[:١!dPnF("_9-ϯi9 lo&ᙟ6O5+º)𴢃RTnƀ Zq(0^W,)*|GWVb;P?/6g<V981d|ҘSCcK"3 EQ bQsVGB\`?`d`s$X1{e+n=s#,+Gk:`}!$TgƂ_oXd[\6Xl%SB`&8u+e<g$2{jppTu+&{X(dC7 !KӁ.?3GzBsZΌx)aiM 2_"R^ڿ72[m ^{;W8@/N`qIZvD} h<M_ot7HyL()ݹzqU1 OSw^j0VA'GUA⊑T$+|vYBh4鍥E~ WnH/sIUFG 0QEy`vĜQW.4@Km'"=5ʝ jL(y^(~,7V>v7Šro Q|3F8:F3);:{!_Y|'p8$ÿULD"<!̓'-;\z8PsgI'^L/Q/>ef8ZwpTg\ͲktUN0 $m cXHWU6 xV\]35PL207i$Pg { u-hFGȉɇuuy]MK&* 7hnQr!D4UhK4zL{>kaɜ)emμ}DY`N?r0]Ū@ЎJ{j9vn,fz f2# Y^4`HLd{2WQd|bŻ5[DdNXx/Sr鮑L1Ts0EZlb%AmEumZVlYaO97S/(o'DfŒ%\l~ ebZF‹W2+Vwj ہCR߷7ĔgRIe 5c0HF84r'%! rޑ% w4k xFam3Fpȁ*YBJ&Occ(![/UZ c ; `JZ9}"lАi`}iAƂ_c>b&< u.6P)urѪ^5Y*9Ԧ> &L-c3#þ#Τ{E/xB@ϬVķE5ӠG꿱QkǸ@ҜoFObGI YkF[)K;~Pb@jOְ>>$oO`0%"auB7E qH-6F  Ukz;Z gs#\$ bd(mQPǷ{ j36f{}<?V~'c:%ҫQl3%b((STXKnC%AWba)b7D~6Crl_Tk[J[)U֢x@CyF )xH"g fJzs^:ҌJ}: ¼RkTɑW ]ې@*DlZp~$%uK[_B8nBQR|yN"QUUlE38 g=۟;gGSܙӺnioXT08HH:0lAqiڌPRbzytG2ƹ!egPr2z<6Or\_.ƙŚ'_L>cW7nᓪٵxf῟.LA{^G%/XY_(©zCZū:. 96ڻ!%^'Ќv4M:1qEN[Y݅v 8~n>Gި$yb];~%p|;x? Vq7\Z \\\/e50+u4[R'=X_u|֮&9UUFMʸ auL0~@io<~4߶|jkED \ Q?Rq)ʼa6*B7!U&.1Cw{*]쟘b_L^2d%^,a)h nik4?O`:i(z A~EMؓQ1X }~eo/_dڊ O3NYa}zԨ2vlm7&S!\ʱ(A,.Tkp[by}(<1se˸6b~`mXu$7U`HTTs-% ^yƮ]dIE2{(vt>tJ|YCN~տШ m9g@^Rk{ /6qxҕM%ʾGqBﶫzx@`CgUʂ_PYӋʶw"EN#[qIȒt?,W.RtLnRVW*-<(Ȗ?ut @.HcTL,Y{PqMf^ϔoF*'G$ZGzvOn.@_ q#gA'"qQlZ`w x%8#sWduJ)bJ3OTirE8Pprettyunits/R/color.R0000644000176200001440000000330214504001530014314 0ustar liggesusers#' Color definition (like RGB) to a name #' #' @param color A scalar color that is usable as an input to `col2rgb()` #' (assumed to be in the sRGB color space). #' @return A character string that is the closest named colors to the input #' color. The output will have an attribute of alternate color names (named #' "alt"). #' @export #' @importFrom grDevices col2rgb convertColor pretty_color <- function(color) { stopifnot(length(color) == 1) if (is.na(color)) { structure(NA_character_, alt = NA_character_) } else { if (is.factor(color)) color <- as.character(color) stopifnot(is.character(color)) color_rgb <- col2rgb(color) color_lab <- convertColor(t(color_rgb), from = "sRGB", to = "Lab", scale.in = 256) dist <- color_diff_cie76( color_lab, as.matrix(color_reference[, c("L", "a", "b")]) ) ret <- color_reference$name[dist == min(dist)][1] attr(ret, "alt") <- color_reference$name_alt[dist == min(dist)][[1]] ret } } #' @rdname pretty_color #' @export pretty_colour <- pretty_color #' Color names, hexadecimal, and CIE Lab colorspace representations #' #' \describe{ #' \item{hex}{hexadecimal color representation (without the # at the beginning)} #' \item{L,a,b}{CIE Lab colorspace representation of `hex`} #' \item{name}{Preferred human-readable name of the color} #' \item{name_alt}{All available human-readable names of the color} #' \item{roygbiv,basic,html,R,pantone,x11,ntc}{Source dataset containing the color} #' } #' @source {https://github.com/colorjs/color-namer} and R `colors()` #' @keywords internal #' @name color_reference NULL color_diff_cie76 <- function(color, refs) { d <- t(refs) - c(color) sqrt(colSums(d * d)) } prettyunits/R/rounding.R0000644000176200001440000001113614504004731015035 0ustar liggesusers#' Round a value to a defined number of digits printing out trailing zeros, if #' applicable #' #' @details Values that are not standard numbers like `Inf`, `NA`, and #' `NaN` are returned as `"Inf"`, `"NA"`, and `"NaN"`. #' #' @param x The number to round. #' @param digits integer indicating the number of decimal places. #' @param sci_range See help for [pretty_signif()] (and you likely want #' to round with [pretty_signif()] if you want to use this argument). #' @param sci_sep The separator to use for scientific notation strings #' (typically this will be either "e" or "x10^" for computer- or #' human-readable output). #' @return A string with the value. #' @seealso [round()], [pretty_signif()]. #' @export pretty_round <- function(x, digits = 0, sci_range = Inf, sci_sep = "e") { if (length(digits) == 1) { mask_na <- is.na(x) mask_aschar <- is.nan(x) | is.infinite(x) mask_manip <- !(mask_na | mask_aschar) ret <- rep(NA, length(x)) ## Put in the special values if (any(mask_na)) { ret[mask_na] <- "NA" } if (any(mask_aschar)) { ret[mask_aschar] <- as.character(x[mask_aschar]) } if (any(mask_manip)) { xtmp <- round(x[mask_manip], digits) mask_sci <- xtmp != 0 & abs(log10(abs(xtmp))) >= sci_range mask_no_sci <- !mask_sci if (any(mask_sci)) { logval <- floor(log10(abs(xtmp[mask_sci]))) ret[mask_manip][mask_sci] <- paste0( formatC(xtmp[mask_sci] / 10^logval, format = "f", digits = digits + logval), sci_sep, formatC(logval, format = "d") ) } if (any(mask_no_sci)) { if (digits < 0) { ret[mask_manip][mask_no_sci] <- formatC(xtmp[mask_no_sci], format = "f", digits = 0) } else { ret[mask_manip][mask_no_sci] <- formatC(xtmp[mask_no_sci], format = "f", digits = digits) } } } ret } else if (length(x) == length(digits)) { mapply(pretty_round, x, digits = digits, sci_range = sci_range, sci_sep = sci_sep) } else { stop("digits must either be a scalar or the same length as x") } } #' Round a value to a defined number of significant digits printing out trailing #' zeros, if applicable #' #' @details Values that are not standard numbers like `Inf`, `NA`, and #' `NaN` are returned as `"Inf"`, `"NA"`, and `NaN`. #' #' @param x The number to round. #' @param digits integer indicating the number of significant digits. #' @param sci_range integer (or `Inf`) indicating when to switch to #' scientific notation instead of floating point. Zero indicates always use #' scientific; `Inf` indicates to never use scientific notation; #' otherwise, scientific notation is used when `abs(log10(x)) > sci_range`. #' @param sci_sep The separator to use for scientific notation strings #' (typically this will be either "e" or "x10^" for computer- or #' human-readable output). #' @return A string with the value. #' @seealso [signif()], [pretty_round()]. #' @export pretty_signif <- function(x, digits = 6, sci_range = 6, sci_sep = "e") { mask_na <- is.na(x) mask_aschar <- is.nan(x) | is.infinite(x) mask_manip <- !(mask_na | mask_aschar) ret <- rep(NA, length(x)) ## Put in the special values if (any(mask_na)) { ret[mask_na] <- "NA" } if (any(mask_aschar)) { ret[mask_aschar] <- as.character(x[mask_aschar]) } if (any(mask_manip)) { xtmp <- x[mask_manip] toplog <- bottomlog <- rep(NA, length(xtmp)) ## When 0 give the digits as the output bottomlog[xtmp %in% 0] <- digits ## Otherwise set it to digits orders of magnitude lower than the ## current value toplog <- log10(abs(xtmp)) ## When the order of magnitude is an exact log 10, move up one so ## that the math works for determing the lower log. mask.exact.log <- (toplog %% 1) %in% 0 toplog[mask.exact.log] <- toplog[mask.exact.log] + 1 toplog <- ceiling(toplog) bottomlog[is.na(bottomlog)] <- digits - toplog[is.na(bottomlog)] ## Find times when rounding increases the toplog and shift up the ## bottomlog to a corresponding degree. e.g. x=0.9999 and digits=2 ## should be 1.0 not 1.00. newtoplog <- log10(abs(round(xtmp, digits = bottomlog))) mask.exact.log <- (newtoplog %% 1) %in% 0 newtoplog[mask.exact.log] <- newtoplog[mask.exact.log] + 1 newtoplog <- ceiling(newtoplog) mask.move.up <- toplog < newtoplog bottomlog[mask.move.up] <- bottomlog[mask.move.up] - 1 ## Do the rounding ret[mask_manip] <- pretty_round(xtmp, digits = bottomlog, sci_range = sci_range, sci_sep = sci_sep ) } ret } prettyunits/R/xtime-ago.R0000644000176200001440000000774014503574705015124 0ustar liggesusers #' Human readable format of the time interval since a time point #' #' It calls \code{\link{vague_dt}} to do the actual formatting. #' #' @param date Date(s), \code{as.POSIXct} will be called on them. #' @param format Format, currently available formats are: #' \sQuote{default}, \sQuote{short}, \sQuote{terse}. See examples below. #' @return Character vector of the formatted time intervals. #' #' @export #' @examples #' now <- Sys.time() #' #' time_ago(now) #' time_ago(now - as.difftime(30, units = "secs")) #' time_ago(now - as.difftime(14, units = "mins")) #' time_ago(now - as.difftime(5, units = "hours")) #' time_ago(now - as.difftime(25, units = "hours")) #' time_ago(now - as.difftime(5, units = "days")) #' time_ago(now - as.difftime(30, units = "days")) #' time_ago(now - as.difftime(365, units = "days")) #' time_ago(now - as.difftime(365 * 10, units = "days")) #' #' ## Short format #' time_ago(format = "short", now) #' time_ago(format = "short", now - as.difftime(30, units = "secs")) #' time_ago(format = "short", now - as.difftime(14, units = "mins")) #' time_ago(format = "short", now - as.difftime(5, units = "hours")) #' time_ago(format = "short", now - as.difftime(25, units = "hours")) #' time_ago(format = "short", now - as.difftime(5, units = "days")) #' time_ago(format = "short", now - as.difftime(30, units = "days")) #' time_ago(format = "short", now - as.difftime(365, units = "days")) #' time_ago(format = "short", now - as.difftime(365 * 10, units = "days")) #' #' ## Even shorter, terse format, (almost always) exactly 3 characters wide #' time_ago(format = "terse", now) #' time_ago(format = "terse", now - as.difftime(30, units = "secs")) #' time_ago(format = "terse", now - as.difftime(14, units = "mins")) #' time_ago(format = "terse", now - as.difftime(5, units = "hours")) #' time_ago(format = "terse", now - as.difftime(25, units = "hours")) #' time_ago(format = "terse", now - as.difftime(5, units = "days")) #' time_ago(format = "terse", now - as.difftime(30, units = "days")) #' time_ago(format = "terse", now - as.difftime(365, units = "days")) #' time_ago(format = "terse", now - as.difftime(365 * 10, units = "days")) time_ago <- format_time_ago$time_ago #' Human readable format of a time interval #' #' @param dt A \code{difftime} object, the time interval(s). #' @param format Format, currently available formats are: #' \sQuote{default}, \sQuote{short}, \sQuote{terse}. See examples below. #' @return Character vector of the formatted time intervals. #' #' @export #' @examples #' vague_dt(as.difftime(30, units = "secs")) #' vague_dt(as.difftime(14, units = "mins")) #' vague_dt(as.difftime(5, units = "hours")) #' vague_dt(as.difftime(25, units = "hours")) #' vague_dt(as.difftime(5, units = "days")) #' vague_dt(as.difftime(30, units = "days")) #' vague_dt(as.difftime(365, units = "days")) #' vague_dt(as.difftime(365 * 10, units = "days")) #' #' ## Short format #' vague_dt(format = "short", as.difftime(30, units = "secs")) #' vague_dt(format = "short", as.difftime(14, units = "mins")) #' vague_dt(format = "short", as.difftime(5, units = "hours")) #' vague_dt(format = "short", as.difftime(25, units = "hours")) #' vague_dt(format = "short", as.difftime(5, units = "days")) #' vague_dt(format = "short", as.difftime(30, units = "days")) #' vague_dt(format = "short", as.difftime(365, units = "days")) #' vague_dt(format = "short", as.difftime(365 * 10, units = "days")) #' #' ## Even shorter, terse format, (almost always) exactly 3 characters wide #' vague_dt(format = "terse", as.difftime(30, units = "secs")) #' vague_dt(format = "terse", as.difftime(14, units = "mins")) #' vague_dt(format = "terse", as.difftime(5, units = "hours")) #' vague_dt(format = "terse", as.difftime(25, units = "hours")) #' vague_dt(format = "terse", as.difftime(5, units = "days")) #' vague_dt(format = "terse", as.difftime(30, units = "days")) #' vague_dt(format = "terse", as.difftime(365, units = "days")) #' vague_dt(format = "terse", as.difftime(365 * 10, units = "days")) vague_dt <- format_time_ago$vague_dt prettyunits/R/xnumbers-docs.R0000644000176200001440000000230614504004233015775 0ustar liggesusers #' Linear quantities in a human readable string #' #' Use `pretty_num()` to format numbers `compute_num()` is the underlying #' engine that may be useful for custom formatting. #' #' @param number Numeric vector, number related to a linear quantity. #' @param style Formatting style: #' * `"default"` is the original `pretty_num` formatting, and it always #' pads the output, so that all vector elements are of the same width, #' * `"nopad"` is similar, but does not pad the output, #' * `"6"` always uses 6 characters, #' The `"6"` style is useful if it is important that the output always #' has the same width (number of characters), e.g. in progress bars. #' See some examples below. #' @return Character vector, the formatted sizes. #' For `compute_num`, a data frame with columns `amount`, `prefix`, #' `negative`. #' #' @export #' @examples #' numbers <- c(1337, 1.3333e-5, 13333337, 1333333337, 133333333337) #' pretty_num(numbers) #' pretty_num(numbers, style = "nopad") #' pretty_num(numbers, style = "6") pretty_num <- format_num$pretty_num #' @rdname pretty_num #' @param smallest_prefix A character scalar, the smallest prefix to use. #' @export compute_num <- format_num$compute_num prettyunits/R/xsizes-docs.R0000644000176200001440000000225714504004233015464 0ustar liggesusers #' Bytes in a human readable string #' #' Use `pretty_bytes()` to format bytes. `compute_bytes()` is the underlying #' engine that may be useful for custom formatting. #' #' @param bytes Numeric vector, number of bytes. #' @param style Formatting style: #' * `"default"` is the original `pretty_bytes` formatting, and it always #' pads the output, so that all vector elements are of the same width, #' * `"nopad"` is similar, but does not pad the output, #' * `"6"` always uses 6 characters, #' The `"6"` style is useful if it is important that the output always #' has the same width (number of characters), e.g. in progress bars. #' See some examples below. #' @return Character vector, the formatted sizes. #' For `compute_bytes`, a data frame with columns `amount`, `unit`, #' `negative`. #' #' @export #' @examples #' bytes <- c(1337, 133337, 13333337, 1333333337, 133333333337) #' pretty_bytes(bytes) #' pretty_bytes(bytes, style = "nopad") #' pretty_bytes(bytes, style = "6") pretty_bytes <- format_bytes$pretty_bytes #' @rdname pretty_bytes #' @param smallest_unit A character scalar, the smallest unit to use. #' @export compute_bytes <- format_bytes$compute_bytes prettyunits/R/numbers.R0000644000176200001440000000711414504004233014661 0ustar liggesusers format_num <- local({ pretty_num <- function(number, style = c("default", "nopad", "6")) { style <- switch( match.arg(style), "default" = pretty_num_default, "nopad" = pretty_num_nopad, "6" = pretty_num_6 ) style(number) } compute_num <- function(number, smallest_prefix = "y") { prefixes0 <- c("y","z","a","f","p","n","u","m","", "k", "M", "G", "T", "P", "E", "Z", "Y") zeroshif0 <- 9L stopifnot( is.numeric(number), is.character(smallest_prefix), length(smallest_prefix) == 1, !is.na(smallest_prefix), smallest_prefix %in% prefixes0 ) limits <- c( 999950 * 1000 ^ (seq_len(length(prefixes0) ) - (zeroshif0+1L))) nrow <- length(limits) low <- match(smallest_prefix, prefixes0) zeroshift <- zeroshif0 +1L - low prefixes <- prefixes0[low:length(prefixes0)] limits <- limits[low:nrow] nrow <- nrow - low + 1 neg <- number < 0 & !is.na(number) number <- abs(number) mat <- matrix( rep(number, each = nrow), nrow = nrow, ncol = length(number) ) mat2 <- matrix(mat < limits, nrow = nrow, ncol = length(number)) exponent <- nrow - colSums(mat2) - (zeroshift -1L) in_range <- function(exponent) { max(min(exponent,nrow-zeroshift, na.rm = FALSE),1L-zeroshift, na.rm = TRUE) } if (length(exponent)) { exponent <- sapply(exponent, in_range) } res <- number / 1000 ^ exponent prefix <- prefixes[exponent + zeroshift] ## Zero number res[number == 0] <- 0 prefix[number == 0] <- prefixes[zeroshift] ## NA and NaN number res[is.na(number)] <- NA_real_ res[is.nan(number)] <- NaN prefix[is.na(number)] <- "" # prefixes0[low] is meaningless # Includes NaN as well data.frame( stringsAsFactors = FALSE, amount = res, prefix = prefix, negative = neg ) } pretty_num_default <- function(number) { szs <- compute_num(number) amt <- szs$amount sep <- " " ## String. For fractions we always show two fraction digits res <- character(length(amt)) int <- is.na(amt) | abs(amt - as.integer(amt)) <= .Machine$double.eps res[int] <- format( ifelse(szs$negative[int], -1, 1) * amt[int], scientific = FALSE ) res[!int] <- sprintf("%.2f", ifelse(szs$negative[!int], -1, 1) * amt[!int]) format(paste(res, szs$prefix,sep = sep), justify = "right") } pretty_num_nopad <- function(number) { sub("^\\s+", "", pretty_num_default(number)) } pretty_num_6 <- function(number) { szs <- compute_num(number, smallest_prefix = "y") amt <- round(szs$amount,2) sep <- " " na <- is.na(amt) nan <- is.nan(amt) neg <- !na & !nan & szs$negative l10p <- !na & !nan & !neg & amt < 10 l100p <- !na & !nan & !neg & amt >= 10 & amt < 100 b100p <- !na & !nan & !neg & amt >= 100 l10n <- !na & !nan & neg & amt < 10 l100n <- !na & !nan & neg & amt >= 10 & amt < 100 b100n <- !na & !nan & neg & amt >= 100 famt <- character(length(amt)) famt[na] <- " NA" famt[nan] <- " NaN" famt[l10p] <- sprintf("%.2f", amt[l10p]) famt[l100p] <- sprintf("%.1f", amt[l100p]) famt[b100p] <- sprintf(" %.0f", amt[b100p]) famt[l10n] <- sprintf("-%.1f", amt[l10n]) famt[l100n] <- sprintf(" -%.0f", amt[l100n]) famt[b100n] <- sprintf("-%.0f", amt[b100n]) sub(" $"," ",paste0(famt, sep, szs$prefix)) } structure( list( .internal = environment(), pretty_num = pretty_num, compute_num = compute_num ), class = c("standalone_num", "standalone") ) }) prettyunits/R/time-ago.R0000644000176200001440000000627414503574705014735 0ustar liggesusers format_time_ago <- local({ e <- expression `%s%` <- function(lhs, rhs) { assert_string(lhs) do.call( sprintf, c(list(lhs), as.list(rhs)) ) } assert_string <- function(x) { stopifnot(is.character(x), length(x) == 1L) } assert_diff_time <- function(x) { stopifnot(inherits(x, "difftime")) } vague_dt_default <- list( list(c = e(seconds < 10), s = "moments ago"), list(c = e(seconds < 45), s = "less than a minute ago"), list(c = e(seconds < 90), s = "about a minute ago"), list(c = e(minutes < 45), s = e("%d minutes ago" %s% round(minutes))), list(c = e(minutes < 90), s = "about an hour ago"), list(c = e(hours < 24), s = e("%d hours ago" %s% round(hours))), list(c = e(hours < 42), s = "a day ago"), list(c = e(days < 30), s = e("%d days ago" %s% round(days))), list(c = e(days < 45), s = "about a month ago"), list(c = e(days < 335), s = e("%d months ago" %s% round(days / 30))), list(c = e(years < 1.5), s = "about a year ago"), list(c = TRUE, s = e("%d years ago" %s% round(years))) ) vague_dt_short <- list( list(c = e(seconds < 50), s = "<1 min"), list(c = e(minutes < 50), s = e("%d min" %s% round(minutes))), list(c = e(hours < 1.5), s = "1 hour"), list(c = e(hours < 18), s = e("%d hours" %s% round(hours))), list(c = e(hours < 42), s = "1 day"), list(c = e(days < 30), s = e("%d day" %s% round(days))), list(c = e(days < 45), s = "1 mon"), list(c = e(days < 335), s = e("%d mon" %s% round(days / 30))), list(c = e(years < 1.5), s = "1 year"), list(c = TRUE, s = e("%d years" %s% round(years))) ) vague_dt_terse <- list( list(c = e(seconds < 50), s = e("%2ds" %s% round(seconds))), list(c = e(minutes < 50), s = e("%2dm" %s% round(minutes))), list(c = e(hours < 18), s = e("%2dh" %s% round(hours))), list(c = e(days < 30), s = e("%2dd" %s% round(days))), list(c = e(days < 335), s = e("%2dM" %s% round(days / 30))), list(c = TRUE, s = e("%2dy" %s% round(years))) ) vague_dt_formats <- list( "default" = vague_dt_default, "short" = vague_dt_short, "terse" = vague_dt_terse ) time_ago <- function(date, format = c("default", "short", "terse")) { date <- as.POSIXct(date) if (length(date) > 1) return(sapply(date, time_ago, format = format)) seconds <- difftime(Sys.time(), date, units = "secs") vague_dt(seconds, format = format) } vague_dt <- function(dt, format = c("default", "short", "terse")) { assert_diff_time(dt) units(dt) <- "secs" seconds <- as.vector(dt) ## Simplest to quit here for empty input if (!length(seconds)) return(character()) pieces <- list( minutes = seconds / 60, hours = seconds / 60 / 60, days = seconds / 60 / 60 / 24, years = seconds / 60 / 60 / 24 / 365.25 ) format <- match.arg(format) for (p in vague_dt_formats[[format]]) { if (eval(p$c, pieces)) return(eval(p$s, pieces)) } } structure( list( .internal = environment(), time_ago = time_ago, vague_dt = vague_dt ), class = c("standalone_time_ago", "standalone") ) }) prettyunits/R/pretty-package.R0000644000176200001440000000064514504001530016125 0ustar liggesusers #' Prettier formatting of quantities #' #' Render quantities with a pretty, human-readable formatting. #' - Time intervals: '1337000' -> '15d 11h 23m 20s'. #' - Vague time intervals: '2674000' -> 'about a month ago'. #' - Bytes: '1337' -> '1.34 kB'. #' - p-values: '0.00001' -> '<0.0001'. #' - Colors: '#FF0000' -> 'red'. #' - Quantities: '1239437' -> '1.24 M'. #' @docType package #' @name prettyunits "_PACKAGE" prettyunits/R/p-value.R0000644000176200001440000000204214504001530014547 0ustar liggesusers#' p-values in a human-readable string #' #' @param x A numeric vector. #' @param minval The minimum p-value to show (lower values will show as #' `paste0("<", minval)`). #' @return A character vector of p-value representations. #' @examples #' pretty_p_value(c(1, 0, NA, 0.01, 0.0000001)) #' pretty_p_value(c(1, 0, NA, 0.01, 0.0000001), minval = 0.05) #' @export pretty_p_value <- function(x, minval = 0.0001) { stopifnot(is.numeric(minval) & !is.factor(minval) & !is.na(minval)) stopifnot(minval < 1 & minval > 0) ret <- rep(NA_character_, length(x)) if (!all(is.na(x))) { # The input check on x class and value is here to allow for inputs of all NA # values to be of any class. stopifnot(is.numeric(x) & !is.factor(x)) stopifnot(is.na(x) | (x <= 1 & x >= 0)) ndigits <- -floor(log10(minval)) mask_min <- !is.na(x) & x < minval mask_over <- !is.na(x) & x >= minval ret[mask_min] <- paste0("<", pretty_round(minval, digits = ndigits)) ret[mask_over] <- pretty_round(x[mask_over], digits = ndigits) } ret } prettyunits/R/time.R0000644000176200001440000000377514503574705014174 0ustar liggesusers format_time <- local({ assert_diff_time <- function(x) { stopifnot(inherits(x, "difftime")) } parse_ms <- function(ms) { stopifnot(is.numeric(ms)) data.frame( days = floor(ms / 86400000), hours = floor((ms / 3600000) %% 24), minutes = floor((ms / 60000) %% 60), seconds = round((ms / 1000) %% 60, 1) ) } first_positive <- function(x) which(x > 0)[1] trim <- function (x) gsub("^\\s+|\\s+$", "", x) pretty_ms <- function(ms, compact = FALSE) { stopifnot(is.numeric(ms)) parsed <- t(parse_ms(ms)) if (compact) { units <- c("d", "h", "m", "s") parsed2 <- parsed parsed2[] <- paste0(parsed, units) idx <- cbind( apply(parsed, 2, first_positive), seq_len(length(ms)) ) tmp <- paste0("~", parsed2[idx]) # handle NAs tmp[is.na(parsed2[idx])] <- NA_character_ tmp } else { ## Exact for small ones exact <- paste0(ceiling(ms), "ms") exact[is.na(ms)] <- NA_character_ ## Approximate for others, in seconds merge_pieces <- function(pieces) { ## handle NAs if (all(is.na(pieces))) { return(NA_character_) } ## handle non-NAs paste0( if (pieces[1]) paste0(pieces[1], "d "), if (pieces[2]) paste0(pieces[2], "h "), if (pieces[3]) paste0(pieces[3], "m "), if (pieces[4]) paste0(pieces[4], "s ") ) } approx <- trim(apply(parsed, 2, merge_pieces)) ifelse(ms < 1000, exact, approx) } } pretty_sec <- function(sec, compact = FALSE) { pretty_ms(sec * 1000, compact = compact) } pretty_dt <- function(dt, compact = FALSE) { assert_diff_time(dt) units(dt) <- "secs" pretty_sec(as.vector(dt), compact = compact) } structure( list( .internal = environment(), pretty_ms = pretty_ms, pretty_sec = pretty_sec, pretty_dt = pretty_dt ), class = c("standalone_time", "standalone") ) }) prettyunits/R/sizes.R0000644000176200001440000000557614504004233014355 0ustar liggesusers format_bytes <- local({ pretty_bytes <- function(bytes, style = c("default", "nopad", "6")) { style <- switch( match.arg(style), "default" = pretty_bytes_default, "nopad" = pretty_bytes_nopad, "6" = pretty_bytes_6 ) style(bytes) } compute_bytes <- function(bytes, smallest_unit = "B") { units0 <- c("B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB") stopifnot( is.numeric(bytes), is.character(smallest_unit), length(smallest_unit) == 1, !is.na(smallest_unit), smallest_unit %in% units0 ) limits <- c(1000, 999950 * 1000 ^ (seq_len(length(units0) - 2) - 1)) low <- match(smallest_unit, units0) units <- units0[low:length(units0)] limits <- limits[low:length(limits)] neg <- bytes < 0 & !is.na(bytes) bytes <- abs(bytes) mat <- matrix( rep(bytes, each = length(limits)), nrow = length(limits), ncol = length(bytes) ) mat2 <- matrix(mat < limits, nrow = length(limits), ncol = length(bytes)) exponent <- length(limits) - colSums(mat2) + low - 1L res <- bytes / 1000 ^ exponent unit <- units[exponent - low + 2L] ## Zero bytes res[bytes == 0] <- 0 unit[bytes == 0] <- units[1] ## NA and NaN bytes res[is.na(bytes)] <- NA_real_ res[is.nan(bytes)] <- NaN unit[is.na(bytes)] <- units0[low] # Includes NaN as well data.frame( stringsAsFactors = FALSE, amount = res, unit = unit, negative = neg ) } pretty_bytes_default <- function(bytes) { szs <- compute_bytes(bytes) amt <- szs$amount ## String. For fractions we always show two fraction digits res <- character(length(amt)) int <- is.na(amt) | amt == as.integer(amt) res[int] <- format( ifelse(szs$negative[int], -1, 1) * amt[int], scientific = FALSE ) res[!int] <- sprintf("%.2f", ifelse(szs$negative[!int], -1, 1) * amt[!int]) format(paste(res, szs$unit), justify = "right") } pretty_bytes_nopad <- function(bytes) { sub("^\\s+", "", pretty_bytes_default(bytes)) } pretty_bytes_6 <- function(bytes) { szs <- compute_bytes(bytes, smallest_unit = "kB") amt <- szs$amount na <- is.na(amt) nan <- is.nan(amt) neg <- !na & !nan & szs$negative l10 <- !na & !nan & !neg & amt < 10 l100 <- !na & !nan & !neg & amt >= 10 & amt < 100 b100 <- !na & !nan & !neg & amt >= 100 szs$unit[neg] <- "kB" famt <- character(length(amt)) famt[na] <- " NA" famt[nan] <- "NaN" famt[neg] <- "< 0" famt[l10] <- sprintf("%.1f", amt[l10]) famt[l100] <- sprintf(" %.0f", amt[l100]) famt[b100] <- sprintf("%.0f", amt[b100]) paste0(famt, " ", szs$unit) } structure( list( .internal = environment(), pretty_bytes = pretty_bytes, compute_bytes = compute_bytes ), class = c("standalone_bytes", "standalone") ) }) prettyunits/R/xtime.R0000644000176200001440000000233714503574705014355 0ustar liggesusers #' Pretty formatting of milliseconds #' #' @param ms Numeric vector of milliseconds #' @param compact If true, then only the first non-zero #' unit is used. See examples below. #' @return Character vector of formatted time intervals. #' #' @family time #' @export #' @examples #' pretty_ms(c(1337, 13370, 133700, 1337000, 1337000000)) #' #' pretty_ms(c(1337, 13370, 133700, 1337000, 1337000000), #' compact = TRUE) pretty_ms <- format_time$pretty_ms #' Pretty formatting of seconds #' #' @param sec Numeric vector of seconds. #' @return Character vector of formatted time intervals. #' #' @inheritParams pretty_ms #' @family time #' @export #' @examples #' pretty_sec(c(1337, 13370, 133700, 1337000, 13370000)) #' #' pretty_sec(c(1337, 13370, 133700, 1337000, 13370000), #' compact = TRUE) pretty_sec <- format_time$pretty_sec #' Pretty formatting of time intervals (difftime objects) #' #' @param dt A \code{difftime} object, a vector of time #' differences. #' @return Character vector of formatted time intervals. #' #' @inheritParams pretty_ms #' @family time #' @export #' @examples #' pretty_dt(as.difftime(1000, units = "secs")) #' pretty_dt(as.difftime(0, units = "secs")) pretty_dt <- format_time$pretty_dt prettyunits/NEWS.md0000644000176200001440000000275414504012346013771 0ustar liggesusers# prettyunits 1.2.0 * New `pretty_num()` function with all the [BIPM](https://www.bipm.org) agreed unit prefix (#26, @cregouby). * New `pretty_round()` and `pretty_signif()` functions preserve the requested number of digits as character strings (#14, @billdenney). * New `pretty_p_value()` function to convert p-values to character strings where small values are shown like "<0.0001" (#13, @billdenney). * New `pretty_color()` functionm converts a color to a named color (#12, @billdenney). # prettyunits 1.1.1 * Fix spurious zero fractions in `pretty_bytes()` when formatting vectors of sizes (#23). # prettyunits 1.1.0 * `pretty_dt()`, `pretty_ms()` and `pretty_sec()` now handle `NA` values properly, and return `NA_character_` for them (#10, @petermeissner). * `pretty_bytes()` now formats quantities just below the units better. E.g. 1MB - 1B is formatted as `"1 MB"` instead of `""1000 kB"` (#18). * `pretty_bytes()` now has multiple styles. In particular, a fixed width style is useful for progress bars. Another style avoids the left-padding with spaces. * The new low level `compute_bytes()` function can be used to create custom formatters for bytes. # prettyunits 1.0.2 * `pretty_bytes()` always uses two fraction digits for non-integers. This looks nicer in a progress bar, as the width of string does not change so much. # prettyunits 1.0.1 First version with a NEWS file. * Get rid of `R CMD check` notes. # prettyunits 1.0.0 Last version without a NEWS file. prettyunits/MD50000644000176200001440000000373314504122652013203 0ustar liggesusers80ec5fff04a58adf365bde9fd8939ab4 *DESCRIPTION 3d55ef9a10c04c95a867277c02d32bca *LICENSE 3fc42c77c184ee3ff3931e693ee5e3a4 *NAMESPACE 23fb85680175e310a4cdef794f1f8780 *NEWS.md 28351811e6926ae1185112862806e5aa *R/color.R 9b4ad374e6aeeb023b136d2f8313227b *R/numbers.R c7585789de7aa6b3d1ec869d194ba66f *R/p-value.R 58d7dafbbc0e83d9b346cdab4b431faa *R/pretty-package.R 7ebcf717feb9a838c4d83e6741d4a983 *R/rounding.R fe5d0e7464286dd1e07746d3da593ad9 *R/sizes.R 2a981e4c6df72a41950d9faf1d715502 *R/sysdata.rda dd7f5ddd6a39f483fe1606a1f80b3e0c *R/time-ago.R 3420e6b0bfe0ddd1338ee059086e6bfa *R/time.R b1c3ea8ce883cbe8e7683fcbb942a836 *R/xnumbers-docs.R 3846445cefe55584e4a70d4c2e2a938d *R/xsizes-docs.R b9ea0da597f739a8867ef68746dae253 *R/xtime-ago.R 382c94cb658af748092a462549b4bef5 *R/xtime.R a86547028affad9b08dfe4a3e0537f60 *README.md bfdad679e44f260bb412861e693c8c00 *man/color_reference.Rd a76805eb8e488f0daffe1c81c3c39c53 *man/pretty_bytes.Rd 41ebba243ef4e7aa1bce7ab6f26d0904 *man/pretty_color.Rd cde3e9552d29c03a5caf4ef522e8e8b0 *man/pretty_dt.Rd f6abc740a7f442982d07e27e895edaf5 *man/pretty_ms.Rd 6cf8431a326feb002699871255d03809 *man/pretty_num.Rd fa607831293f2ecc4d8cc7edbc31e30c *man/pretty_p_value.Rd 205c0c49e772eeb84bdb42cf71f77a50 *man/pretty_round.Rd 0fb33ae2869bc2ed5fa363aea76691e3 *man/pretty_sec.Rd 9023a8195c0d7c044d34293b6d44459e *man/pretty_signif.Rd 928feb58a3022d29151290fc51dcfbfb *man/prettyunits.Rd ad419c2fbcbb7dd732128ee878dd26c0 *man/time_ago.Rd 754db288929f5e9cacb9ebbd155f5d65 *man/vague_dt.Rd d4df5b35b4ce6e070b4980c38847cad3 *tests/testthat.R 675e661fec46a342af7362d194bb6765 *tests/testthat/test-bytes.r d102cc01256bf517457c0ea392029e00 *tests/testthat/test-color.r c7ce99540a9d95b591172192553b4511 *tests/testthat/test-ms.r e905086215f3aff260d78954d52f2a74 *tests/testthat/test-numbers.r cf6450e061c082a63ec9fd71e07d034c *tests/testthat/test-p-value.r 67197e0a2e7f97fd84d6c71b80c7dc63 *tests/testthat/test-rounding.r d9cfa1952b67c36ed99eeaf771c00ed0 *tests/testthat/test-vague-dt.r