vroom/0000755000176200001440000000000014533733452011425 5ustar liggesusersvroom/NAMESPACE0000644000176200001440000000516114533651065012646 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method(as.col_spec,"NULL") S3method(as.col_spec,character) S3method(as.col_spec,col_spec) S3method(as.col_spec,default) S3method(as.col_spec,list) S3method(collector_value,collector_character) S3method(collector_value,collector_date) S3method(collector_value,collector_datetime) S3method(collector_value,collector_double) S3method(collector_value,collector_factor) S3method(collector_value,collector_guess) S3method(collector_value,collector_integer) S3method(collector_value,collector_logical) S3method(collector_value,collector_numeric) S3method(collector_value,collector_time) S3method(output_column,POSIXt) S3method(output_column,character) S3method(output_column,default) S3method(output_column,double) S3method(output_column,factor) S3method(print,vroom_altrep) S3method(summary,col_spec) S3method(vroom_str,data.frame) S3method(vroom_str,default) export(as.col_spec) export(col_big_integer) export(col_character) export(col_date) export(col_datetime) export(col_double) export(col_factor) export(col_guess) export(col_integer) export(col_logical) export(col_number) export(col_skip) export(col_time) export(cols) export(cols_condense) export(cols_only) export(contains) export(date_names) export(date_names_lang) export(date_names_langs) export(default_locale) export(ends_with) export(everything) export(fwf_cols) export(fwf_empty) export(fwf_positions) export(fwf_widths) export(gen_character) export(gen_date) export(gen_datetime) export(gen_double) export(gen_factor) export(gen_integer) export(gen_logical) export(gen_name) export(gen_number) export(gen_tbl) export(gen_time) export(guess_type) export(last_col) export(locale) export(matches) export(num_range) export(one_of) export(output_column) export(problems) export(spec) export(starts_with) export(vroom) export(vroom_altrep) export(vroom_altrep_opts) export(vroom_example) export(vroom_examples) export(vroom_format) export(vroom_fwf) export(vroom_lines) export(vroom_progress) export(vroom_str) export(vroom_write) export(vroom_write_lines) import(rlang) importFrom(bit64,integer64) importFrom(crayon,blue) importFrom(crayon,bold) importFrom(crayon,cyan) importFrom(crayon,green) importFrom(crayon,reset) importFrom(crayon,silver) importFrom(glue,glue) importFrom(lifecycle,deprecate_warn) importFrom(lifecycle,deprecated) importFrom(methods,setOldClass) importFrom(tidyselect,contains) importFrom(tidyselect,ends_with) importFrom(tidyselect,everything) importFrom(tidyselect,last_col) importFrom(tidyselect,matches) importFrom(tidyselect,num_range) importFrom(tidyselect,one_of) importFrom(tidyselect,starts_with) useDynLib(vroom, .registration = TRUE) vroom/LICENSE0000644000176200001440000000005314422654617012432 0ustar liggesusersYEAR: 2023 COPYRIGHT HOLDER: vroom authors vroom/README.md0000644000176200001440000002356514533532252012712 0ustar liggesusers # πŸŽπŸ’¨vroom [![R-CMD-check](https://github.com/tidyverse/vroom/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/tidyverse/vroom/actions/workflows/R-CMD-check.yaml) [![Codecov test coverage](https://codecov.io/gh/tidyverse/vroom/branch/main/graph/badge.svg)](https://app.codecov.io/gh/tidyverse/vroom?branch=main) [![CRAN status](https://www.r-pkg.org/badges/version/vroom)](https://cran.r-project.org/package=vroom) [![Lifecycle: stable](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html#stable) The fastest delimited reader for R, **1.23 GB/sec**. But that’s impossible! How can it be [so fast](https://vroom.r-lib.org/articles/benchmarks.html)? vroom doesn’t stop to actually *read* all of your data, it simply indexes where each record is located so it can be read later. The vectors returned use the [Altrep framework](https://svn.r-project.org/R/branches/ALTREP/ALTREP.html) to lazily load the data on-demand when it is accessed, so you only pay for what you use. This lazy access is done automatically, so no changes to your R data-manipulation code are needed. vroom also uses multiple threads for indexing, materializing non-character columns, and when writing to further improve performance. | package | version | time (sec) | speedup | throughput | |:-----------|--------:|-----------:|--------:|--------------:| | vroom | 1.5.1 | 1.36 | 53.30 | 1.23 GB/sec | | data.table | 1.14.0 | 5.83 | 12.40 | 281.65 MB/sec | | readr | 1.4.0 | 37.30 | 1.94 | 44.02 MB/sec | | read.delim | 4.1.0 | 72.31 | 1.00 | 22.71 MB/sec | ## Features vroom has nearly all of the parsing features of [readr](https://readr.tidyverse.org) for delimited and fixed width files, including - delimiter guessing\* - custom delimiters (including multi-byte\* and Unicode\* delimiters) - specification of column types (including type guessing) - numeric types (double, integer, big integer\*, number) - logical types - datetime types (datetime, date, time) - categorical types (characters, factors) - column selection, like `dplyr::select()`\* - skipping headers, comments and blank lines - quoted fields - double and backslashed escapes - whitespace trimming - windows newlines - [reading from multiple files or connections\*](#reading-multiple-files) - embedded newlines in headers and fields\*\* - writing delimited files with as-needed quoting. - robust to invalid inputs (vroom has been extensively tested with the [afl](https://lcamtuf.coredump.cx/afl/) fuzz tester)\*. \* *these are additional features not in readr.* \*\* *requires `num_threads = 1`.* ## Installation Install vroom from CRAN with: ``` r install.packages("vroom") ``` Alternatively, if you need the development version from [GitHub](https://github.com/) install it with: ``` r # install.packages("pak") pak::pak("tidyverse/vroom") ``` ## Usage See [getting started](https://vroom.r-lib.org/articles/vroom.html) to jump start your use of vroom! vroom uses the same interface as readr to specify column types. ``` r vroom::vroom("mtcars.tsv", col_types = list(cyl = "i", gear = "f",hp = "i", disp = "_", drat = "_", vs = "l", am = "l", carb = "i") ) #> # A tibble: 32 Γ— 10 #> model mpg cyl hp wt qsec vs am gear carb #> #> 1 Mazda RX4 21 6 110 2.62 16.5 FALSE TRUE 4 4 #> 2 Mazda RX4 Wag 21 6 110 2.88 17.0 FALSE TRUE 4 4 #> 3 Datsun 710 22.8 4 93 2.32 18.6 TRUE TRUE 4 1 #> # β„Ή 29 more rows ``` ## Reading multiple files vroom natively supports reading from multiple files (or even multiple connections!). First we generate some files to read by splitting the nycflights dataset by airline. For the sake of the example, we’ll just take the first 2 lines of each file. ``` r library(nycflights13) purrr::iwalk( split(flights, flights$carrier), ~ { .x$carrier[[1]]; vroom::vroom_write(head(.x, 2), glue::glue("flights_{.y}.tsv"), delim = "\t") } ) ``` Then we can efficiently read them into one tibble by passing the filenames directly to vroom. The `id` argument can be used to request a column that reveals the filename that each row originated from. ``` r files <- fs::dir_ls(glob = "flights*tsv") files #> flights_9E.tsv flights_AA.tsv flights_AS.tsv flights_B6.tsv flights_DL.tsv #> flights_EV.tsv flights_F9.tsv flights_FL.tsv flights_HA.tsv flights_MQ.tsv #> flights_OO.tsv flights_UA.tsv flights_US.tsv flights_VX.tsv flights_WN.tsv #> flights_YV.tsv vroom::vroom(files, id = "source") #> Rows: 32 Columns: 20 #> ── Column specification ──────────────────────────────────────────────────────── #> Delimiter: "\t" #> chr (4): carrier, tailnum, origin, dest #> dbl (14): year, month, day, dep_time, sched_dep_time, dep_delay, arr_time, ... #> dttm (1): time_hour #> #> β„Ή Use `spec()` to retrieve the full column specification for this data. #> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message. #> # A tibble: 32 Γ— 20 #> source year month day dep_time sched_dep_time dep_delay arr_time #> #> 1 flights_9E.tsv 2013 1 1 810 810 0 1048 #> 2 flights_9E.tsv 2013 1 1 1451 1500 -9 1634 #> 3 flights_AA.tsv 2013 1 1 542 540 2 923 #> # β„Ή 29 more rows #> # β„Ή 12 more variables: sched_arr_time , arr_delay , carrier , #> # flight , tailnum , origin , dest , air_time , #> # distance , hour , minute , time_hour ``` ## Learning more - [Getting started with vroom](https://vroom.r-lib.org/articles/vroom.html) - [πŸ“½ vroom: Because Life is too short to read slow](https://www.youtube.com/watch?v=RA9AjqZXxMU&t=10s) - Presentation at UseR!2019 ([slides](https://speakerdeck.com/jimhester/vroom)) - [πŸ“Ή vroom: Read and write rectangular data quickly](https://www.youtube.com/watch?v=ZP_y5eaAc60) - a video tour of the vroom features. ## Benchmarks The speed quoted above is from a real 1.53G dataset with 14,388,451 rows and 11 columns, see the [benchmark article](https://vroom.r-lib.org/articles/benchmarks.html) for full details of the dataset and [bench/](https://github.com/tidyverse/vroom/tree/main/inst/bench) for the code used to retrieve the data and perform the benchmarks. # Environment variables In addition to the arguments to the `vroom()` function, you can control the behavior of vroom with a few environment variables. Generally these will not need to be set by most users. - `VROOM_TEMP_PATH` - Path to the directory used to store temporary files when reading from a R connection. If unset defaults to the R session’s temporary directory (`tempdir()`). - `VROOM_THREADS` - The number of processor threads to use when indexing and parsing. If unset defaults to `parallel::detectCores()`. - `VROOM_SHOW_PROGRESS` - Whether to show the progress bar when indexing. Regardless of this setting the progress bar is disabled in non-interactive settings, R notebooks, when running tests with testthat and when knitting documents. - `VROOM_CONNECTION_SIZE` - The size (in bytes) of the connection buffer when reading from connections (default is 128 KiB). - `VROOM_WRITE_BUFFER_LINES` - The number of lines to use for each buffer when writing files (default: 1000). There are also a family of variables to control use of the Altrep framework. For versions of R where the Altrep framework is unavailable (R \< 3.5.0) they are automatically turned off and the variables have no effect. The variables can take one of `true`, `false`, `TRUE`, `FALSE`, `1`, or `0`. - `VROOM_USE_ALTREP_NUMERICS` - If set use Altrep for *all* numeric types (default `false`). There are also individual variables for each type. Currently only `VROOM_USE_ALTREP_CHR` defaults to `true`. - `VROOM_USE_ALTREP_CHR` - `VROOM_USE_ALTREP_FCT` - `VROOM_USE_ALTREP_INT` - `VROOM_USE_ALTREP_BIG_INT` - `VROOM_USE_ALTREP_DBL` - `VROOM_USE_ALTREP_NUM` - `VROOM_USE_ALTREP_LGL` - `VROOM_USE_ALTREP_DTTM` - `VROOM_USE_ALTREP_DATE` - `VROOM_USE_ALTREP_TIME` ## RStudio caveats RStudio’s environment pane calls `object.size()` when it refreshes the pane, which for Altrep objects can be extremely slow. RStudio 1.2.1335+ includes the fixes ([RStudio#4210](https://github.com/rstudio/rstudio/pull/4210), [RStudio#4292](https://github.com/rstudio/rstudio/pull/4292)) for this issue, so it is recommended you use at least that version. ## Thanks - [Gabe Becker](https://github.com/gmbecker), [Luke Tierney](https://homepage.divms.uiowa.edu/~luke/) and [Tomas Kalibera](https://github.com/kalibera) for conceiving, Implementing and maintaining the [Altrep framework](https://svn.r-project.org/R/branches/ALTREP/ALTREP.html) - [Romain FranΓ§ois](https://github.com/romainfrancois), whose [Altrepisode](https://web.archive.org/web/20200315075838/https://purrple.cat/blog/2018/10/14/altrep-and-cpp/) package and [related blog-posts](https://web.archive.org/web/20200315075838/https://purrple.cat/blog/2018/10/14/altrep-and-cpp/) were a great guide for creating new Altrep objects in C++. - [Matt Dowle](https://github.com/mattdowle) and the rest of the [Rdatatable](https://github.com/Rdatatable) team, `data.table::fread()` is blazing fast and great motivation to see how fast we could go faster! vroom/man/0000755000176200001440000000000014504643364012200 5ustar liggesusersvroom/man/cols.Rd0000644000176200001440000001041314362130126013413 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/col_types.R \name{cols} \alias{cols} \alias{col_types} \alias{cols_only} \alias{col_logical} \alias{col_integer} \alias{col_big_integer} \alias{col_double} \alias{col_character} \alias{col_skip} \alias{col_number} \alias{col_guess} \alias{col_factor} \alias{col_datetime} \alias{col_date} \alias{col_time} \title{Create column specification} \usage{ cols(..., .default = col_guess(), .delim = NULL) cols_only(...) col_logical(...) col_integer(...) col_big_integer(...) col_double(...) col_character(...) col_skip(...) col_number(...) col_guess(...) col_factor(levels = NULL, ordered = FALSE, include_na = FALSE, ...) col_datetime(format = "", ...) col_date(format = "", ...) col_time(format = "", ...) } \arguments{ \item{...}{Either column objects created by \verb{col_*()}, or their abbreviated character names (as described in the \code{col_types} argument of \code{\link[=vroom]{vroom()}}). If you're only overriding a few columns, it's best to refer to columns by name. If not named, the column types must match the column names exactly. In \verb{col_*()} functions these are stored in the object.} \item{.default}{Any named columns not explicitly overridden in \code{...} will be read with this column type.} \item{.delim}{The delimiter to use when parsing. If the \code{delim} argument used in the call to \code{vroom()} it takes precedence over the one specified in \code{col_types}.} \item{levels}{Character vector of the allowed levels. When \code{levels = NULL} (the default), \code{levels} are discovered from the unique values of \code{x}, in the order in which they appear in \code{x}.} \item{ordered}{Is it an ordered factor?} \item{include_na}{If \code{TRUE} and \code{x} contains at least one \code{NA}, then \code{NA} is included in the levels of the constructed factor.} \item{format}{A format specification, as described below. If set to "", date times are parsed as ISO8601, dates and times used the date and time formats specified in the \code{\link[readr:locale]{locale()}}. Unlike \code{\link[=strptime]{strptime()}}, the format specification must match the complete string.} } \description{ \code{cols()} includes all columns in the input data, guessing the column types as the default. \code{cols_only()} includes only the columns you explicitly specify, skipping the rest. } \details{ The available specifications are: (long names in quotes and string abbreviations in brackets)\tabular{llll}{ function \tab long name \tab short name \tab description \cr \code{col_logical()} \tab "logical" \tab "l" \tab Logical values containing only \code{T}, \code{F}, \code{TRUE} or \code{FALSE}. \cr \code{col_integer()} \tab "integer" \tab "i" \tab Integer numbers. \cr \code{col_big_integer()} \tab "big_integer" \tab "I" \tab Big Integers (64bit), requires the \code{bit64} package. \cr \code{col_double()} \tab "double", "numeric" \tab "d" \tab 64-bit double floating point numbers. \cr \code{col_character()} \tab "character" \tab "c" \tab Character string data. \cr \code{col_factor(levels, ordered)} \tab "factor" \tab "f" \tab A fixed set of values. \cr \code{col_date(format = "")} \tab "date" \tab "D" \tab Calendar dates formatted with the locale's \code{date_format}. \cr \code{col_time(format = "")} \tab "time" \tab "t" \tab Times formatted with the locale's \code{time_format}. \cr \code{col_datetime(format = "")} \tab "datetime", "POSIXct" \tab "T" \tab ISO8601 date times. \cr \code{col_number()} \tab "number" \tab "n" \tab Human readable numbers containing the \code{grouping_mark} \cr \code{col_skip()} \tab "skip", "NULL" \tab "_", "-" \tab Skip and don't import this column. \cr \code{col_guess()} \tab "guess", "NA" \tab "?" \tab Parse using the "best" guessed type based on the input. \cr } } \examples{ cols(a = col_integer()) cols_only(a = col_integer()) # You can also use the standard abbreviations cols(a = "i") cols(a = "i", b = "d", c = "_") # Or long names (like utils::read.csv) cols(a = "integer", b = "double", c = "skip") # You can also use multiple sets of column definitions by combining # them like so: t1 <- cols( column_one = col_integer(), column_two = col_number()) t2 <- cols( column_three = col_character()) t3 <- t1 t3$cols <- c(t1$cols, t2$cols) t3 } vroom/man/vroom_write_lines.Rd0000644000176200001440000000176114363145201016230 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom_write.R \name{vroom_write_lines} \alias{vroom_write_lines} \title{Write lines to a file} \usage{ vroom_write_lines( x, file, eol = "\\n", na = "NA", append = FALSE, num_threads = vroom_threads() ) } \arguments{ \item{x}{A character vector.} \item{file}{File or connection to write to.} \item{eol}{The end of line character to use. Most commonly either \code{"\n"} for Unix style newlines, or \code{"\r\n"} for Windows style newlines.} \item{na}{String used for missing values. Defaults to 'NA'.} \item{append}{If \code{FALSE}, will overwrite existing file. If \code{TRUE}, will append to existing file. In both cases, if the file does not exist a new file is created.} \item{num_threads}{Number of threads to use when reading and materializing vectors. If your data contains newlines within fields the parser will automatically be forced to use a single thread only.} } \description{ Write lines to a file } vroom/man/problems.Rd0000644000176200001440000000175514362130126014307 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/problems.R \name{problems} \alias{problems} \title{Retrieve parsing problems} \usage{ problems(x = .Last.value, lazy = FALSE) } \arguments{ \item{x}{A data frame from \code{vroom::vroom()}.} \item{lazy}{If \code{TRUE}, just the problems found so far are returned. If \code{FALSE} (the default) the lazy data is first read completely and all problems are returned.} } \value{ A data frame with one row for each problem and four columns: \itemize{ \item row,col - Row and column number that caused the problem, referencing the original input \item expected - What vroom expected to find \item actual - What it actually found \item file - The file with the problem } } \description{ vroom will only fail to parse a file if the file is invalid in a way that is unrecoverable. However there are a number of non-fatal problems that you might want to know about. You can retrieve a data frame of these problems with this function. } vroom/man/vroom_format.Rd0000644000176200001440000000416314362130126015172 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom_write.R \name{vroom_format} \alias{vroom_format} \title{Convert a data frame to a delimited string} \usage{ vroom_format( x, delim = "\\t", eol = "\\n", na = "NA", col_names = TRUE, escape = c("double", "backslash", "none"), quote = c("needed", "all", "none"), bom = FALSE, num_threads = vroom_threads() ) } \arguments{ \item{x}{A data frame or tibble to write to disk.} \item{delim}{Delimiter used to separate values. Defaults to \verb{\\t} to write tab separated value (TSV) files.} \item{eol}{The end of line character to use. Most commonly either \code{"\n"} for Unix style newlines, or \code{"\r\n"} for Windows style newlines.} \item{na}{String used for missing values. Defaults to 'NA'.} \item{col_names}{If \code{FALSE}, column names will not be included at the top of the file. If \code{TRUE}, column names will be included. If not specified, \code{col_names} will take the opposite value given to \code{append}.} \item{escape}{The type of escape to use when quotes are in the data. \itemize{ \item \code{double} - quotes are escaped by doubling them. \item \code{backslash} - quotes are escaped by a preceding backslash. \item \code{none} - quotes are not escaped. }} \item{quote}{How to handle fields which contain characters that need to be quoted. \itemize{ \item \code{needed} - Values are only quoted if needed: if they contain a delimiter, quote, or newline. \item \code{all} - Quote all fields. \item \code{none} - Never quote fields. }} \item{bom}{If \code{TRUE} add a UTF-8 BOM at the beginning of the file. This is recommended when saving data for consumption by excel, as it will force excel to read the data with the correct encoding (UTF-8)} \item{num_threads}{Number of threads to use when reading and materializing vectors. If your data contains newlines within fields the parser will automatically be forced to use a single thread only.} } \description{ This is equivalent to \code{\link[=vroom_write]{vroom_write()}}, but instead of writing to disk, it returns a string. It is primarily useful for examples and for testing. } vroom/man/date_names.Rd0000644000176200001440000000213014132374245014557 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/date.R \name{date_names} \alias{date_names} \alias{date_names_lang} \alias{date_names_langs} \title{Create or retrieve date names} \usage{ date_names(mon, mon_ab = mon, day, day_ab = day, am_pm = c("AM", "PM")) date_names_lang(language) date_names_langs() } \arguments{ \item{mon, mon_ab}{Full and abbreviated month names.} \item{day, day_ab}{Full and abbreviated week day names. Starts with Sunday.} \item{am_pm}{Names used for AM and PM.} \item{language}{A BCP 47 locale, made up of a language and a region, e.g. \code{"en_US"} for American English. See \code{date_names_langs()} for a complete list of available locales.} } \description{ When parsing dates, you often need to know how weekdays of the week and months are represented as text. This pair of functions allows you to either create your own, or retrieve from a standard list. The standard list is derived from ICU (\verb{https://site.icu-project.org}) via the \emph{stringi} package. } \examples{ date_names_lang("en") date_names_lang("ko") date_names_lang("fr") } vroom/man/vroom_fwf.Rd0000644000176200001440000002142714533532502014472 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom_fwf.R \name{vroom_fwf} \alias{vroom_fwf} \alias{fwf_empty} \alias{fwf_widths} \alias{fwf_positions} \alias{fwf_cols} \title{Read a fixed width file into a tibble} \usage{ vroom_fwf( file, col_positions = fwf_empty(file, skip, n = guess_max), col_types = NULL, col_select = NULL, id = NULL, locale = default_locale(), na = c("", "NA"), comment = "", skip_empty_rows = TRUE, trim_ws = TRUE, skip = 0, n_max = Inf, guess_max = 100, altrep = TRUE, altrep_opts = deprecated(), num_threads = vroom_threads(), progress = vroom_progress(), show_col_types = NULL, .name_repair = "unique" ) fwf_empty(file, skip = 0, col_names = NULL, comment = "", n = 100L) fwf_widths(widths, col_names = NULL) fwf_positions(start, end = NULL, col_names = NULL) fwf_cols(...) } \arguments{ \item{file}{Either a path to a file, a connection, or literal data (either a single string or a raw vector). Files ending in \code{.gz}, \code{.bz2}, \code{.xz}, or \code{.zip} will be automatically uncompressed. Files starting with \verb{http://}, \verb{https://}, \verb{ftp://}, or \verb{ftps://} will be automatically downloaded. Remote gz files can also be automatically downloaded and decompressed. Literal data is most useful for examples and tests. To be recognised as literal data, the input must be either wrapped with \code{I()}, be a string containing at least one new line, or be a vector containing at least one string with a new line. Using a value of \code{\link[readr:clipboard]{clipboard()}} will read from the system clipboard.} \item{col_positions}{Column positions, as created by \code{\link[readr:fwf_empty]{fwf_empty()}}, \code{\link[readr:fwf_widths]{fwf_widths()}} or \code{\link[readr:fwf_positions]{fwf_positions()}}. To read in only selected fields, use \code{\link[readr:fwf_positions]{fwf_positions()}}. If the width of the last column is variable (a ragged fwf file), supply the last end position as NA.} \item{col_types}{One of \code{NULL}, a \code{\link[readr:cols]{cols()}} specification, or a string. See \code{vignette("readr")} for more details. If \code{NULL}, all column types will be inferred from \code{guess_max} rows of the input, interspersed throughout the file. This is convenient (and fast), but not robust. If the guessed types are wrong, you'll need to increase \code{guess_max} or supply the correct types yourself. Column specifications created by \code{\link[=list]{list()}} or \code{\link[readr:cols]{cols()}} must contain one column specification for each column. If you only want to read a subset of the columns, use \code{\link[readr:cols_only]{cols_only()}}. Alternatively, you can use a compact string representation where each character represents one column: \itemize{ \item c = character \item i = integer \item n = number \item d = double \item l = logical \item f = factor \item D = date \item T = date time \item t = time \item ? = guess \item _ or - = skip } By default, reading a file without a column specification will print a message showing what \code{readr} guessed they were. To remove this message, set \code{show_col_types = FALSE} or set `options(readr.show_col_types = FALSE).} \item{col_select}{Columns to include in the results. You can use the same mini-language as \code{dplyr::select()} to refer to the columns by name. Use \code{c()} to use more than one selection expression. Although this usage is less common, \code{col_select} also accepts a numeric column index. See \code{\link[tidyselect:language]{?tidyselect::language}} for full details on the selection language.} \item{id}{The name of a column in which to store the file path. This is useful when reading multiple input files and there is data in the file paths, such as the data collection date. If \code{NULL} (the default) no extra column is created.} \item{locale}{The locale controls defaults that vary from place to place. The default locale is US-centric (like R), but you can use \code{\link[readr:locale]{locale()}} to create your own locale that controls things like the default time zone, encoding, decimal mark, big mark, and day/month names.} \item{na}{Character vector of strings to interpret as missing values. Set this option to \code{character()} to indicate no missing values.} \item{comment}{A string used to identify comments. Any text after the comment characters will be silently ignored.} \item{skip_empty_rows}{Should blank rows be ignored altogether? i.e. If this option is \code{TRUE} then blank rows will not be represented at all. If it is \code{FALSE} then they will be represented by \code{NA} values in all the columns.} \item{trim_ws}{Should leading and trailing whitespace (ASCII spaces and tabs) be trimmed from each field before parsing it?} \item{skip}{Number of lines to skip before reading data.} \item{n_max}{Maximum number of lines to read.} \item{guess_max}{Maximum number of lines to use for guessing column types. Will never use more than the number of lines read. See \code{vignette("column-types", package = "readr")} for more details.} \item{altrep}{Control which column types use Altrep representations, either a character vector of types, \code{TRUE} or \code{FALSE}. See \code{\link[=vroom_altrep]{vroom_altrep()}} for for full details.} \item{altrep_opts}{\Sexpr[results=rd, stage=render]{lifecycle::badge("deprecated")}} \item{num_threads}{The number of processing threads to use for initial parsing and lazy reading of data. If your data contains newlines within fields the parser should automatically detect this and fall back to using one thread only. However if you know your file has newlines within quoted fields it is safest to set \code{num_threads = 1} explicitly.} \item{progress}{Display a progress bar? By default it will only display in an interactive session and not while knitting a document. The automatic progress bar can be disabled by setting option \code{readr.show_progress} to \code{FALSE}.} \item{show_col_types}{If \code{FALSE}, do not show the guessed column types. If \code{TRUE} always show the column types, even if they are supplied. If \code{NULL} (the default) only show the column types if they are not explicitly supplied by the \code{col_types} argument.} \item{.name_repair}{Handling of column names. The default behaviour is to ensure column names are \code{"unique"}. Various repair strategies are supported: \itemize{ \item \code{"minimal"}: No name repair or checks, beyond basic existence of names. \item \code{"unique"} (default value): Make sure names are unique and not empty. \item \code{"check_unique"}: no name repair, but check they are \code{unique}. \item \code{"universal"}: Make the names \code{unique} and syntactic. \item A function: apply custom name repair (e.g., \code{name_repair = make.names} for names in the style of base R). \item A purrr-style anonymous function, see \code{\link[rlang:as_function]{rlang::as_function()}}. } This argument is passed on as \code{repair} to \code{\link[vctrs:vec_as_names]{vctrs::vec_as_names()}}. See there for more details on these terms and the strategies used to enforce them.} \item{col_names}{Either NULL, or a character vector column names.} \item{n}{Number of lines the tokenizer will read to determine file structure. By default it is set to 100.} \item{widths}{Width of each field. Use NA as width of last field when reading a ragged fwf file.} \item{start, end}{Starting and ending (inclusive) positions of each field. Use NA as last end field when reading a ragged fwf file.} \item{...}{If the first element is a data frame, then it must have all numeric columns and either one or two rows. The column names are the variable names. The column values are the variable widths if a length one vector, and if length two, variable start and end positions. The elements of \code{...} are used to construct a data frame with or or two rows as above.} } \description{ Read a fixed width file into a tibble } \details{ \emph{Note}: \code{fwf_empty()} cannot take a R connection such as a URL as input, as this would result in reading from the connection twice. In these cases it is better to download the file first before reading. } \examples{ fwf_sample <- vroom_example("fwf-sample.txt") writeLines(vroom_lines(fwf_sample)) # You can specify column positions in several ways: # 1. Guess based on position of empty columns vroom_fwf(fwf_sample, fwf_empty(fwf_sample, col_names = c("first", "last", "state", "ssn"))) # 2. A vector of field widths vroom_fwf(fwf_sample, fwf_widths(c(20, 10, 12), c("name", "state", "ssn"))) # 3. Paired vectors of start and end positions vroom_fwf(fwf_sample, fwf_positions(c(1, 30), c(20, 42), c("name", "ssn"))) # 4. Named arguments with start and end positions vroom_fwf(fwf_sample, fwf_cols(name = c(1, 20), ssn = c(30, 42))) # 5. Named arguments with column widths vroom_fwf(fwf_sample, fwf_cols(name = 20, state = 10, ssn = 12)) } vroom/man/vroom_str.Rd0000644000176200001440000000073114132374245014516 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/altrep.R \name{vroom_str} \alias{vroom_str} \title{Structure of objects} \usage{ vroom_str(x) } \arguments{ \item{x}{a vector} } \description{ Similar to \code{str()} but with more information for Altrep objects. } \examples{ # when used on non-altrep objects altrep will always be false vroom_str(mtcars) mt <- vroom(vroom_example("mtcars.csv"), ",", altrep = c("chr", "dbl")) vroom_str(mt) } vroom/man/generators.Rd0000644000176200001440000000400714132374245014635 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/generator.R \name{generators} \alias{generators} \alias{gen_character} \alias{gen_double} \alias{gen_number} \alias{gen_integer} \alias{gen_factor} \alias{gen_time} \alias{gen_date} \alias{gen_datetime} \alias{gen_logical} \alias{gen_name} \title{Generate individual vectors of the types supported by vroom} \usage{ gen_character(n, min = 5, max = 25, values = c(letters, LETTERS, 0:9), ...) gen_double(n, f = stats::rnorm, ...) gen_number(n, f = stats::rnorm, ...) gen_integer(n, min = 1L, max = .Machine$integer.max, prob = NULL, ...) gen_factor( n, levels = NULL, ordered = FALSE, num_levels = gen_integer(1L, 1L, 25L), ... ) gen_time(n, min = 0, max = hms::hms(days = 1), fractional = FALSE, ...) gen_date(n, min = as.Date("2001-01-01"), max = as.Date("2021-01-01"), ...) gen_datetime( n, min = as.POSIXct("2001-01-01"), max = as.POSIXct("2021-01-01"), tz = "UTC", ... ) gen_logical(n, ...) gen_name(n) } \arguments{ \item{n}{The size of the vector to generate} \item{min}{The minimum range for the vector} \item{max}{The maximum range for the vector} \item{values}{The explicit values to use.} \item{...}{Additional arguments passed to internal generation functions} \item{f}{The random function to use.} \item{prob}{a vector of probability weights for obtaining the elements of the vector being sampled.} \item{levels}{The explicit levels to use, if \code{NULL} random levels are generated using \code{\link[=gen_name]{gen_name()}}.} \item{ordered}{Should the factors be ordered factors?} \item{num_levels}{The number of factor levels to generate} \item{fractional}{Whether to generate times with fractional seconds} \item{tz}{The timezone to use for dates} } \description{ Generate individual vectors of the types supported by vroom } \examples{ # characters gen_character(4) # factors gen_factor(4) # logical gen_logical(4) # numbers gen_double(4) gen_integer(4) # temporal data gen_time(4) gen_date(4) gen_datetime(4) } vroom/man/vroom.Rd0000644000176200001440000002255514505325006013631 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom.R \name{vroom} \alias{vroom} \title{Read a delimited file into a tibble} \usage{ vroom( file, delim = NULL, col_names = TRUE, col_types = NULL, col_select = NULL, id = NULL, skip = 0, n_max = Inf, na = c("", "NA"), quote = "\\"", comment = "", skip_empty_rows = TRUE, trim_ws = TRUE, escape_double = TRUE, escape_backslash = FALSE, locale = default_locale(), guess_max = 100, altrep = TRUE, altrep_opts = deprecated(), num_threads = vroom_threads(), progress = vroom_progress(), show_col_types = NULL, .name_repair = "unique" ) } \arguments{ \item{file}{Either a path to a file, a connection, or literal data (either a single string or a raw vector). \code{file} can also be a character vector containing multiple filepaths or a list containing multiple connections. Files ending in \code{.gz}, \code{.bz2}, \code{.xz}, or \code{.zip} will be automatically uncompressed. Files starting with \verb{http://}, \verb{https://}, \verb{ftp://}, or \verb{ftps://} will be automatically downloaded. Remote gz files can also be automatically downloaded and decompressed. Literal data is most useful for examples and tests. To be recognised as literal data, wrap the input with \code{I()}.} \item{delim}{One or more characters used to delimit fields within a file. If \code{NULL} the delimiter is guessed from the set of \code{c(",", "\\t", " ", "|", ":", ";")}.} \item{col_names}{Either \code{TRUE}, \code{FALSE} or a character vector of column names. If \code{TRUE}, the first row of the input will be used as the column names, and will not be included in the data frame. If \code{FALSE}, column names will be generated automatically: X1, X2, X3 etc. If \code{col_names} is a character vector, the values will be used as the names of the columns, and the first row of the input will be read into the first row of the output data frame. Missing (\code{NA}) column names will generate a warning, and be filled in with dummy names \code{...1}, \code{...2} etc. Duplicate column names will generate a warning and be made unique, see \code{name_repair} to control how this is done.} \item{col_types}{One of \code{NULL}, a \code{\link[=cols]{cols()}} specification, or a string. If \code{NULL}, all column types will be imputed from \code{guess_max} rows on the input interspersed throughout the file. This is convenient (and fast), but not robust. If the imputation fails, you'll need to increase the \code{guess_max} or supply the correct types yourself. Column specifications created by \code{\link[=list]{list()}} or \code{\link[=cols]{cols()}} must contain one column specification for each column. If you only want to read a subset of the columns, use \code{\link[=cols_only]{cols_only()}}. Alternatively, you can use a compact string representation where each character represents one column: \itemize{ \item c = character \item i = integer \item n = number \item d = double \item l = logical \item f = factor \item D = date \item T = date time \item t = time \item ? = guess \item _ or - = skip By default, reading a file without a column specification will print a message showing what \code{readr} guessed they were. To remove this message, set \code{show_col_types = FALSE} or set \code{options(readr.show_col_types = FALSE)}. }} \item{col_select}{Columns to include in the results. You can use the same mini-language as \code{dplyr::select()} to refer to the columns by name. Use \code{c()} to use more than one selection expression. Although this usage is less common, \code{col_select} also accepts a numeric column index. See \code{\link[tidyselect:language]{?tidyselect::language}} for full details on the selection language.} \item{id}{Either a string or 'NULL'. If a string, the output will contain a variable with that name with the filename(s) as the value. If 'NULL', the default, no variable will be created.} \item{skip}{Number of lines to skip before reading data. If \code{comment} is supplied any commented lines are ignored \emph{after} skipping.} \item{n_max}{Maximum number of lines to read.} \item{na}{Character vector of strings to interpret as missing values. Set this option to \code{character()} to indicate no missing values.} \item{quote}{Single character used to quote strings.} \item{comment}{A string used to identify comments. Any text after the comment characters will be silently ignored.} \item{skip_empty_rows}{Should blank rows be ignored altogether? i.e. If this option is \code{TRUE} then blank rows will not be represented at all. If it is \code{FALSE} then they will be represented by \code{NA} values in all the columns.} \item{trim_ws}{Should leading and trailing whitespace (ASCII spaces and tabs) be trimmed from each field before parsing it?} \item{escape_double}{Does the file escape quotes by doubling them? i.e. If this option is \code{TRUE}, the value '""' represents a single quote, '"'.} \item{escape_backslash}{Does the file use backslashes to escape special characters? This is more general than \code{escape_double} as backslashes can be used to escape the delimiter character, the quote character, or to add special characters like \verb{\\\\n}.} \item{locale}{The locale controls defaults that vary from place to place. The default locale is US-centric (like R), but you can use \code{\link[=locale]{locale()}} to create your own locale that controls things like the default time zone, encoding, decimal mark, big mark, and day/month names.} \item{guess_max}{Maximum number of lines to use for guessing column types. See \code{vignette("column-types", package = "readr")} for more details.} \item{altrep}{Control which column types use Altrep representations, either a character vector of types, \code{TRUE} or \code{FALSE}. See \code{\link[=vroom_altrep]{vroom_altrep()}} for for full details.} \item{altrep_opts}{\Sexpr[results=rd, stage=render]{lifecycle::badge("deprecated")}} \item{num_threads}{Number of threads to use when reading and materializing vectors. If your data contains newlines within fields the parser will automatically be forced to use a single thread only.} \item{progress}{Display a progress bar? By default it will only display in an interactive session and not while knitting a document. The automatic progress bar can be disabled by setting option \code{readr.show_progress} to \code{FALSE}.} \item{show_col_types}{Control showing the column specifications. If \code{TRUE} column specifications are always show, if \code{FALSE} they are never shown. If \code{NULL} (the default) they are shown only if an explicit specification is not given to \code{col_types}.} \item{.name_repair}{Handling of column names. The default behaviour is to ensure column names are \code{"unique"}. Various repair strategies are supported: \itemize{ \item \code{"minimal"}: No name repair or checks, beyond basic existence of names. \item \code{"unique"} (default value): Make sure names are unique and not empty. \item \code{"check_unique"}: no name repair, but check they are \code{unique}. \item \code{"universal"}: Make the names \code{unique} and syntactic. \item A function: apply custom name repair (e.g., \code{name_repair = make.names} for names in the style of base R). \item A purrr-style anonymous function, see \code{\link[rlang:as_function]{rlang::as_function()}}. } This argument is passed on as \code{repair} to \code{\link[vctrs:vec_as_names]{vctrs::vec_as_names()}}. See there for more details on these terms and the strategies used to enforce them.} } \description{ Read a delimited file into a tibble } \examples{ # get path to example file input_file <- vroom_example("mtcars.csv") input_file # Read from a path # Input sources ------------------------------------------------------------- # Read from a path vroom(input_file) # You can also use paths directly # vroom("mtcars.csv") \dontrun{ # Including remote paths vroom("https://github.com/tidyverse/vroom/raw/main/inst/extdata/mtcars.csv") } # Or directly from a string with `I()` vroom(I("x,y\n1,2\n3,4\n")) # Column selection ---------------------------------------------------------- # Pass column names or indexes directly to select them vroom(input_file, col_select = c(model, cyl, gear)) vroom(input_file, col_select = c(1, 3, 11)) # Or use the selection helpers vroom(input_file, col_select = starts_with("d")) # You can also rename specific columns vroom(input_file, col_select = c(car = model, everything())) # Column types -------------------------------------------------------------- # By default, vroom guesses the columns types, looking at 1000 rows # throughout the dataset. # You can specify them explicitly with a compact specification: vroom(I("x,y\n1,2\n3,4\n"), col_types = "dc") # Or with a list of column types: vroom(I("x,y\n1,2\n3,4\n"), col_types = list(col_double(), col_character())) # File types ---------------------------------------------------------------- # csv vroom(I("a,b\n1.0,2.0\n"), delim = ",") # tsv vroom(I("a\tb\n1.0\t2.0\n")) # Other delimiters vroom(I("a|b\n1.0|2.0\n"), delim = "|") # Read datasets across multiple files --------------------------------------- mtcars_by_cyl <- vroom_example(vroom_examples("mtcars-")) mtcars_by_cyl # Pass the filenames directly to vroom, they are efficiently combined vroom(mtcars_by_cyl) # If you need to extract data from the filenames, use `id` to request a # column that reveals the underlying file path dat <- vroom(mtcars_by_cyl, id = "source") dat$source <- basename(dat$source) dat } vroom/man/spec.Rd0000644000176200001440000000132114132374245013412 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/col_types.R \name{cols_condense} \alias{cols_condense} \alias{spec} \title{Examine the column specifications for a data frame} \usage{ cols_condense(x) spec(x) } \arguments{ \item{x}{The data frame object to extract from} } \value{ A col_spec object. } \description{ \code{cols_condense()} takes a spec object and condenses its definition by setting the default column type to the most frequent type and only listing columns with a different type. \code{spec()} extracts the full column specification from a tibble created by readr. } \examples{ df <- vroom(vroom_example("mtcars.csv")) s <- spec(df) s cols_condense(s) } \concept{parsers} vroom/man/output_column.Rd0000644000176200001440000000111414132374245015375 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom_write.R \name{output_column} \alias{output_column} \title{Preprocess column for output} \usage{ output_column(x) } \arguments{ \item{x}{A vector} } \description{ This is a generic function that applied to each column before it is saved to disk. It provides a hook for S3 classes that need special handling. } \examples{ # Most types are returned unchanged output_column(1) output_column("x") # datetimes are formatted in ISO 8601 output_column(Sys.Date()) output_column(Sys.time()) } \keyword{internal} vroom/man/vroom_lines.Rd0000644000176200001440000000604014505325006015012 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom_lines.R \name{vroom_lines} \alias{vroom_lines} \title{Read lines from a file} \usage{ vroom_lines( file, n_max = Inf, skip = 0, na = character(), skip_empty_rows = FALSE, locale = default_locale(), altrep = TRUE, altrep_opts = deprecated(), num_threads = vroom_threads(), progress = vroom_progress() ) } \arguments{ \item{file}{Either a path to a file, a connection, or literal data (either a single string or a raw vector). \code{file} can also be a character vector containing multiple filepaths or a list containing multiple connections. Files ending in \code{.gz}, \code{.bz2}, \code{.xz}, or \code{.zip} will be automatically uncompressed. Files starting with \verb{http://}, \verb{https://}, \verb{ftp://}, or \verb{ftps://} will be automatically downloaded. Remote gz files can also be automatically downloaded and decompressed. Literal data is most useful for examples and tests. To be recognised as literal data, wrap the input with \code{I()}.} \item{n_max}{Maximum number of lines to read.} \item{skip}{Number of lines to skip before reading data. If \code{comment} is supplied any commented lines are ignored \emph{after} skipping.} \item{na}{Character vector of strings to interpret as missing values. Set this option to \code{character()} to indicate no missing values.} \item{skip_empty_rows}{Should blank rows be ignored altogether? i.e. If this option is \code{TRUE} then blank rows will not be represented at all. If it is \code{FALSE} then they will be represented by \code{NA} values in all the columns.} \item{locale}{The locale controls defaults that vary from place to place. The default locale is US-centric (like R), but you can use \code{\link[=locale]{locale()}} to create your own locale that controls things like the default time zone, encoding, decimal mark, big mark, and day/month names.} \item{altrep}{Control which column types use Altrep representations, either a character vector of types, \code{TRUE} or \code{FALSE}. See \code{\link[=vroom_altrep]{vroom_altrep()}} for for full details.} \item{altrep_opts}{\Sexpr[results=rd, stage=render]{lifecycle::badge("deprecated")}} \item{num_threads}{Number of threads to use when reading and materializing vectors. If your data contains newlines within fields the parser will automatically be forced to use a single thread only.} \item{progress}{Display a progress bar? By default it will only display in an interactive session and not while knitting a document. The automatic progress bar can be disabled by setting option \code{readr.show_progress} to \code{FALSE}.} } \description{ \code{vroom_lines()} is similar to \code{readLines()}, however it reads the lines lazily like \code{\link[=vroom]{vroom()}}, so operations like \code{length()}, \code{head()}, \code{tail()} and \code{sample()} can be done much more efficiently without reading all the data into R. } \examples{ lines <- vroom_lines(vroom_example("mtcars.csv")) length(lines) head(lines, n = 2) tail(lines, n = 2) sample(lines, size = 2) } vroom/man/vroom_example.Rd0000644000176200001440000000135014505365056015343 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/example.R \name{vroom_example} \alias{vroom_example} \alias{vroom_examples} \title{Get path to vroom examples} \usage{ vroom_example(path) vroom_examples(pattern = NULL) } \arguments{ \item{path}{Name of file.} \item{pattern}{A regular expression of filenames to match. If \code{NULL}, all available files are returned.} } \description{ vroom comes bundled with a number of sample files in its 'inst/extdata' directory. Use \code{vroom_examples()} to list all the available examples and \code{vroom_example()} to retrieve the path to one example. } \examples{ # List all available examples vroom_examples() # Get path to one example vroom_example("mtcars.csv") } vroom/man/vroom_altrep.Rd0000644000176200001440000000322614132374245015177 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom.R \name{vroom_altrep} \alias{vroom_altrep} \title{Show which column types are using Altrep} \usage{ vroom_altrep(which = NULL) } \arguments{ \item{which}{A character vector of column types to use Altrep for. Can also take \code{TRUE} or \code{FALSE} to use Altrep for all possible or none of the types} } \description{ \code{vroom_altrep()} can be used directly as input to the \code{altrep} argument of \code{\link[=vroom]{vroom()}}. } \details{ Alternatively there is also a family of environment variables to control use of the Altrep framework. These can then be set in your \code{.Renviron} file, e.g. with \code{usethis::edit_r_environ()}. For versions of R where the Altrep framework is unavailable (R < 3.5.0) they are automatically turned off and the variables have no effect. The variables can take one of \code{true}, \code{false}, \code{TRUE}, \code{FALSE}, \code{1}, or \code{0}. \itemize{ \item \code{VROOM_USE_ALTREP_NUMERICS} - If set use Altrep for \emph{all} numeric types (default \code{false}). } There are also individual variables for each type. Currently only \code{VROOM_USE_ALTREP_CHR} defaults to \code{true}. \itemize{ \item \code{VROOM_USE_ALTREP_CHR} \item \code{VROOM_USE_ALTREP_FCT} \item \code{VROOM_USE_ALTREP_INT} \item \code{VROOM_USE_ALTREP_BIG_INT} \item \code{VROOM_USE_ALTREP_DBL} \item \code{VROOM_USE_ALTREP_NUM} \item \code{VROOM_USE_ALTREP_LGL} \item \code{VROOM_USE_ALTREP_DTTM} \item \code{VROOM_USE_ALTREP_DATE} \item \code{VROOM_USE_ALTREP_TIME} } } \examples{ vroom_altrep() vroom_altrep(c("chr", "fct", "int")) vroom_altrep(TRUE) vroom_altrep(FALSE) } vroom/man/vroom_altrep_opts.Rd0000644000176200001440000000104414132374245016240 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom.R \name{vroom_altrep_opts} \alias{vroom_altrep_opts} \title{Show which column types are using Altrep} \usage{ vroom_altrep_opts(which = NULL) } \arguments{ \item{which}{A character vector of column types to use Altrep for. Can also take \code{TRUE} or \code{FALSE} to use Altrep for all possible or none of the types} } \description{ \Sexpr[results=rd, stage=render]{lifecycle::badge("deprecated")} This function is deprecated in favor of \code{vroom_altrep()}. } vroom/man/figures/0000755000176200001440000000000014504102737013636 5ustar liggesusersvroom/man/figures/lifecycle-defunct.svg0000644000176200001440000000242414504102737017746 0ustar liggesusers lifecycle: defunct lifecycle defunct vroom/man/figures/lifecycle-maturing.svg0000644000176200001440000000243014504102737020141 0ustar liggesusers lifecycle: maturing lifecycle maturing vroom/man/figures/logo.png0000644000176200001440000005427114132374245015317 0ustar liggesusers‰PNG  IHDRπ·ΊšOgAMA± όa cHRMz&€„ϊ€θu0κ`:˜pœΊQ<bKGD ½§“ pHYsgŸRtIMEδ 08£ΩŽγW¨IDATxΪνw|[ΥωΖΏη^I–δmΗ±γμ½χ ‹°WX J₯@)-ΠB)-έΏ–Q(£Πe”QfΒ„$d|αm˚χόώ8’,Ϋ²-Ϋ’-Ωχω|”ΘΥηήηœχ9ο8`Β„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ &L˜0aΒ„ IΡΥ'`"9qγz‰>2χŽ3•d„yWL4ΑMdθ­ _ά3Ϊ|\’ ζ1Fqu`6π ΈXϊ$rςΐΌ&"‰ 0ΈψΠ+ψΩNΰΰ `hC“Θ]στ`4"noΰΫΐ΅ΐπ(›K`9pπ P `χ™Dξ2˜-ίCA^'pp#0ΠZω©xΈψπƒ9wΜVοaˆ’soζŽ6ξͺxxSwΜΦξ!ˆAη6€ˆx2€€%μώ <Ž©;f+wsάΈAFήδΞύ0¬Ήίhjλ`Λ6dš † F€YΑhžΘQυ±4ΰή±ζ#–H˜­ΫΡHηž‰2—’+ψό°crεj8P‚Π449yτν£ώnaDφοχ`κγNΩ²έmΥΉB(R8ˆ\± vμBψύΤ?μv9 9q"?/ψqσDλckC›™DŽ?ΜνFh―Ξ-―€Υλ6#κκˆώTwγΗ ΗŒBd8[4«ΑΤΗ ‡Ω’έΝψscΉ7#W―C”WΏh퉐Šψ…½‘“Ηw@χšDξ0ΜLaάτ…„œπŸνΣΉ νO‚έƒϊ›ϊΈ+aΆ^Š’έ:·ΉbuΫ^tD Φ†Ά3‰ά>˜­–bH¨Ξ A_‚ΨžSwΜΦJtŠΞ · †ΐ‘ZΨzŒXMμ}6 ΈΟτΗ³•RρπηΚЈΪ ΘrΐΉ“ΰ;GA–ͺέπΕNψγ{PZKμOKP쏜bκγ„Βl‘$F{p-θ\CBI[tV 7~8Ζυ—ς‘(*κΰ†—ΰιe*σ¨M0υqΒaΆL"ŠΞύ>p ρΦΉΐ„bΈώh8q$xύΰ΄AΏπ°r/μ*ƒ’,ψΩk°t­η*5sS'f‹$β s)―ήΣVtnί\e*_4¬ΊΪΌ8Jͺ`Λa¨¬ƒ>Y0}$όψexδsΪΔMψ’δ€ νΧΗn7<4Ω|lC0["IΠY:7ΣηN„kfCa&xόPœ­Fί§Ύ†‡>ƒΓ5πό•pά0ψt;|²rpχΨ~„Ž=5¦>Ž+ΜVθbt–Ξ΅XΰΨaJηN(κ\'d€Α[αž%jΒΚ έοΞ‚ΡE`·ΐΩγ!Ϋ<o£νft44ΤΗδη©+hE?܏`-¦>Lw: sεκuН;ΎŸ«t/v«Ήϋ+α§―Α’΅ΰςPOL ½2ΰΏ—Γ1ΓΰσπίPγΧΦB₯;NΠ~}ό(πoL}lΈ³ΡˆΈ…ΤΧ‘ŠIηZ1κάβΈς(Έx*ΨtυqΏlpΨ`Λ!xgάω©iΊ/«wœ nŸς_: †υ‚3―wŸQ8ς|¨°Μ6θγΏ/ςΰΎVΏΊg]m#‚ΌιΤΧ‘ŠΏΞ΅ΓΒ‰pνl5εκάl»š€ϊr7X4˜ήnω4ΚH5Jωl8c,¬Ω~k¨—ΏΝ>₯`κγ6£η\i’‘Νƒ"n«:χ@‰"n[tξ1C•ΝΨWιάόt₯sίΫΛvΓ¨B˜3ηΓΖƒpοΗπο₯ΰ 4Ϊ_Ќ~π|ψj7,ί£f¬'υ…s {ΚHάΣΣ}άΣόΗέ »ΜεQ¨Έεψκ\”‰=¦Ύ4œΤΉfΐ7ϋΰžΰυ΅Κ~φ 8v(ΌΊVEW3 ξxήέ@“QXύsΰ²Κ?Rwοl€²Ί7^½>–γΗΐ˜‘ˆŒtS7Fχ½².DδΊB$Jη† !Χ OFτ†€‘ΜεƒΥΚ%τŸep° EN ³‡ΐ)£`φ ˜7Ljάτ2,^εXFΒΏ.†ΧΦ¨ΙγGͺYμ›_jO'4fϋτρ ”ΈΫλγξwE]ŒF:χLΰβ­sA~~όδxε―ύz\ω ¬ AϋΚHƒ7±EπΤrψf―2‹ούvDρρZ4θ›­ΆΉαεzΊxjY³;‘0υqTt―«ιBtŠΞ ’ρ67žΎLιΪέεπ·%°z/QΝβγΥ¨=\2U€=Λw7έf ‡.€Eλΰ‰/axo |‘FόNEϋυρB°¦»ιγξq]ˆNΡΉΑCδ₯ƒΓ %Υ4όMΊn>Ξ™ ˆω›·α7ouvΉ0 ^Ύ FφVΡW―­…ήπΖz¨ŠβγΝ²Clε?ώώ\ΫG™ηO|Uί§t*‚Ν κγ±±ιγ]¨ψκn₯Sϋμ»ΡΉ‚qΛ1ι\вα²ι0,Τyam <ϊl:ρ[™9>άn…gΎVa‘χο°Βus`ΛΘwͺd« ƒ₯DᏠ8>Ϊ  šiVεRκ2ΔKKΈoLjR!5ΟΊ‹EηήΜ Ξώάt;œ=›«t¨Η―2ƒrπόJΈκY…€ϊξ’i0΅œ< ~ς*<±”¨£pq6όύ<˜5^Y -Un§ΝG’˜ΕδAA:μ«„yCΥ¨όή&Ψ™HwR¬ λA}<1f}όJ/!…υ±₯«O •EηήœJK:Χ€ύΡtn QTΊs‡Γ σ`JE,G0ό±Ξ§FX‹ߞΜΣPR©Θ}ΞXΌTͺ\__j ΄ςƒŸͺNΰGΗ*Ώο%OBYγδ}»KU ΘΉαλέ°|/ψtyEΔ„”)‘€ TΖΤφ]ˆ=aδπz}¬D³σΣ€ΣQ±ηa}ΊΏ©DδΤ9Σ.Dϋ}ΰbbΠΉ«ΦΑΖ6θάΡ…jΔ=mŒŠv²[TbύšJwΦωΰΖcTšί—»ΰτ‡‘άEψαžΨfRD^0~ό |±¨£pQό`œ?QΝ\ίχ1l>εΝψx{₯«¨š:βJΩ ρ"ΪI‚Νη&ΓSF―κέτ/[OΏ² d»b x14 ~ΝJ@³βΆ¦γJΛΖeΝΒ§§‘žc“C'gΓρGγνέG΄R €φ…Ž}O ˜ΥΙ†]ˆ›ΦΛΘ ιάοC›ϋM{una– ˜ψφ4„aHe6‡?W³Ώ* ? ^ύ"νΚ}πΩvx{CύώJoΌΏ~Ή(¨S£θΪ‰ύTΌσγ_Αϊpιt5;}ΛkΚ\o‚6Ί·Ϊ! Ύ2|³g ‡%&}όwΰ%Rd}§δ=³.F‡tξͺ`½εXtnœ5^Žኲ”~{n%<π1¬+ Dνξ˜apυ,8} ΌΏEΉ€Βf²TιyN•„pς(ψσϋ°§ΌιΉθ 2ΰ΄Ρ𝙰·B₯.ί› Xη¨ ύJΧ³πλ?S\±‰άΪ2λŽ`χΥ Χ_RrΆπδ±ΗΚ7ΛiγΫ¬?|Όf΅©‘{`Ά}»t.jΦwΦψα10-BηfΒ[TςόΗΫΐο§A—! e5k°ši~uΖαΩc‹ΧΓm§ΐοΞPz΅ΉQ'€1EpώδΰρΆΒδώ0{°:v§@ΐψ½K8aΓMFΠxΕ‰Ÿ;%ϋaΔ0δ€–ύΗ!}<‡`|΅–ΔϊΨ$pΝθάK€όhΫGΡΉΤΥ!bͺ₯lΐ‚Iπϋ3Ԍ―!UrΑϊeΎΎΌjά(β6ο5ΨvΎχLξ??QEIέόrύΓo &¦~ΉHiΫΉCΰυuͺ“hΌ―₯;ας§ΤθΑΨY KχvbΓKΘvT&o‚Π¬ ©r<«Χ"vξFŽƒ;2Νtp9ΐ5ΐ)† ΗWο»iƒL*}άγ άhύάBΰ2”?·5+7l†˜un#μ(Su§FφVfτ½ΑŸίS3ΗZœ$ςϊ”‰{Λ πΩx{£Ν.Ÿϊ>`ΐί?VΩCO| φTΐ»›‚IϋΞΡε…‘…κ<^Z₯f²6AΥ œžΚ„i9pΩ²ΤΑƒTU!>ϋΆnGNž€lΕ<ψ °€>Tί΄A"%άΫΕDξ±ΎqDΤ_}ΫtξΈu…Ϊ3±£ΑΊJ£τ«šSίμUΉΊ1‘G¨,£+ŸQd½fΆAύΌώχšG RΡR――SίWΦ)wKγ}­= *P"c<~ νΣΐ―N‹ŸTΑ­B*½š>XιάZ―Ήa(sΑυ/Bfš2§w”*ίqrA’ΛΔε%4+^‹#LZ«ΟCAυ.ϊ•m€wΥ2έ₯θ†·5ƒΓ™ƒΨ•7V,/ΖζrΚxδ°ΨτρB‚ρΥBPuΣIž”x"wkί°^’Υ·a:p*?·eλSώά«‘$F# Ξ£ͺb ΜSΥ/rυώΨΥϋUdΤζCυ †Ÿξ€ΫOQΫ>ώ₯r%Υy[8^p)”F©πΗ>ΩπΨ*£hb_•©΄·Ό~Rͺς°_ξRqΜΙ™H[πZμ8=•Lέ±˜Ω›_`ΨΑe*šΛπ…c „F=“ψbΘB±|χB6κ#§N€ΎΕJG™*ΐ”>Ύ€`|΅#­sτqrΜ…'όΉs©[nџ{ΰ`Ϋςs5M%όpΜ¨ˆ›γ€l‡ς«.^―κP4RΉŠ.žYFΈϋΘHƒͺΤLpyόρέ(ΟIπr&υU‘”s‚‘G?W…λ~x dΨΰƒM%υϋιχDFQuΊαγ–7Ξε¨ν―ΗΝο‚Κ‹ωΟά?1wσ™Έϋ]l/Ρã룑ȼ΅pΟOΏ £N•ΣŠ0ctώAƒU+6/y+ΖWk«CuΆEδ$Ό₯CŠ[n»ΞmΑC /PTgŽS&–MWdΪxHΉ…^ZU΅π³ST΅Œ77υθ eς†φ5ΆόηREπϋ>†ίΌ1[$_Ώ\Έz6\8YUˆΌ{‰ςυ^;G™ί/¬„g–«I³}•1šαI‹αε–Eη0cΗq'0€OOΓcM'Λ]Φf?³TΪσY1x>ύެ£½J:ο;<η <…}„h©'PρΥa}ϊ0ήDξ6n”Ÿ[„ΉΧ‹ΞέΤΉ•±ιά^™ͺNςε3”Ύτ*nΉΌNειώki½ 0(.œ’π¦€sŸl%<ΩΤ7n=ήΫ¬Fδ*7ΌΎFœY8’Jp¨σ©PΗ•{α‚ΙpΞDˆρΐ'*vωΈαͺcx}IΰH„Lδφώ6x›ΤKjΖN•ϋ.»‘F’εŒ―‚ψΖWw 7Š[> εϝNY*οΆΕπβJαΤZHŒMWξžωcT–Ρ]οΐξˆjΊ€ώΉΚέtΙΤ`Ζ΅οkζΐQαΕoT5Θ‚ Uvo…J:¨t΅~όd…&άΌψζl})ε‚ά}Ι\«>ލǒƒϊ8₯ά™:7Σ‘Κ¦zύυŸ]2ž ΖΏ΅n_}]‘h¨j•€Τ@UP=œŒoΐόqπ“γT•Œε{ΰΌIκυε.₯sέ>8vΈάn}±;™‘Ι7ΏysΆΌ˜nοd½ BϋJOZˆ?#C΄YχΆΘzΜ[v1nΪ&o:pJO\δEΫ^•Š·eς£Ο`ύF„ΫέJ•‘’•ΞΏ;v•Γˆ"oe.ΘqͺΩίc†)ξςέΔ|Ηw–©ͺŽ7[¨ΕΕ<©‡{ΚΥLυΠ^pη|΅,ΚoίR³ΩSϊ+?οπΑζ`υΘ'/ΑK˜Ήν%–λν.P$°Φΰ+ў쨴CϋEήΗ‹EΖϊ•ψς‹πχRΧ‚q·MΠ˜ς#φ0fώΰΧ,}ΰȎ—τνΥΉϋK+VΑΞέ±εη `Ζ UΌ|Φ • Φ8΅΄¦ίPμ+W©|WΝT+XuθρNϋ₯ϊύΥU§Ž†­Φ–B°V‹ϊό_KΥ(?ΊΟ’&ΗήX‘s»yAZ>=­Σ+Pυ²Q€Υ Θΰv fŸΪt=€πzΘ_²Hd­ψLž‘lEΫP$žMPλ:«cΝ?NΪΗ ½:·¬ΉΊ:WC…?ήz’2ΉuMιΡƒUpμύ«0m ͺ†qΈf „ίΎK6ΣΊXςσŠ£T™ΨΆΒ/5ŒO¨ŽΑλ‡ }•ήύzwjλάΦpέϋΧpΚΪvͺ ­‘tW.M«} ΨDpΊΈ¨ΧΗWspα•xŠbΦΗφ-ΖW'έά(nΉHΚpάςζ~Σ.‚„a½Υβ]šPεl²μj„]Ή¦φ@ƒ{`ί5c\Z«όΐ­"Έδη9U¨₯?w,VΙ #z«0ˈΣΑ¬φΈf0Šͺ•όΰ”†P pp’FΰH„²*­¨Έ#£œμϋvŠAχάF―w_–ϋΎ}ƒ,=q!ώΜfυρε‡β«£9©4πMΒδMGε]ήMΌul”*&ατq*0c@·mƒ½•0s *9σβ*eή"”ΎOΤxUμςΔΎ°΅48₯pœEW·P­³ϋΜΧπ»wTΰΗoη«`Ι–:䀡‘βwΏΛ¨’₯ύ(–ε;‹:55³'„υρ†6ιγ©(}Ό[ˆ¦ϊ8)FΰF:χh”Ξ=…xκά qgV/­ͺ€_¬FΘO·«0Δ ΕJ{ξ­P«ςω"λkπς*ΐρΫωŠx/―Vu“€¨‘ό΄?œΗ‡·6ΒΟ_…άtψγYκχό\Mde;š/εΪΰΣνΝΉΟγ^fG’ϊL”ωœM󝧝ŽEqE;vS}|=αγZΗ§³κc‹΅‘>ξώ½‘ΞƒΉo¬9uΝlUτόχοͺ2­‘ΉoMS™Bύr”Ι\ηƒΎ FVνrn=Y%β?³\ωˆ?Ψ Uuj}²U•ΗoMSΩGw¨f¬―ž­ Ζ½²ZUΚΘuͺ(ͺe»”Ϋ¨ΣοFk“a2ΖΟ:xs6?ΗΙkiΰ΅8pΩ²pΫ2Θ―ήΓ€]oΓδڏ IC%ςζ’Lg½…Λ Ύ[h_'XS7w wίAςΐωW+qQq›υq—ψΗ›$†AΚLΎ†Φt+"?7F« ΅‚ΑηACͺίyΆ&<15sΌφέϊ ’GΎP$k6² eϊž4RΝZeΒEOΐΦC°p’ ΅”ξϋHΉ}NTI+φΒύ«ΥφσαεWN8"Θ* I†·ŠΒšύτ­ήMΆ§έπγΧ¬Α—…:k:5ΆLκ,NšΏΠ hͺr¨LΛνΠ©4Ή·2€Επ”šN@XΊΖ‰kεΊχnχ~%Š€N”ΛF‘8–QU Β¦ΆΖ°mδρ4Τ°i Ύ t΄Υˆ(Η‘BP3fJ(Ύffkώ㕨όγW»Τ„6 F ’QfΣ ƒqΛrϋ.XΩ†ΈεP‹e« *§U‘Ξ€ύUΏϊ_?Ϊ¦\<½3T€UKδ5JŸ6φWΒ­―AοtψΝΚ‡ϋΔ—*jjjxτ¨vΓ­―«ΥfV#τgΫUœvΥΤj IΊ Γ[EQΝ>UleXω†–mdhω&ϊVο"Ϋ]Μ‹ .U‚@ €¦γΥπι6Œΰί†°°=g8?;ρQφdŠΫhl―ήt:Ζα­&z n‹·PPY(βf’ΜfΩh›VΟ« ΗΥPD>ͺΓih‰"o-PŠΥ#/€$sέr1βφ«)σyΉος›dετc„am6Ύz2π0pEWkΰΎΐ4’<Βa{ ·£?·‚ΙμύrTF/ %TgjΕƒηV¨:ΚƒσΌψ Έν ΅ο+R!–οn‚_Όςp¦"κ??‡7Χ«σΈpŠšY~k#x|mΌ–X! ΈzGο~Α›PΉ•Ϋ(ήCΆ»›α gΧ4ΆΦDθ―ΰˆhΗέxΧΤ`Hω&φδ JhΦ„fθU½;ζνCύ e"ηίGf΅FŒΏ³‘δ\ššε!Σݎ2ߏ fΏ»§šθγΣ.ϋ/ύAkϊψ˜&p³σeεͺής¦XκP5‘ΑΉ)Χ—ΐ²=ͺφTc*έΚ-τπg*%otl(iω˜5^ψωI*4ςšͺ9ߝ­κ0ΏΆFΥ«ΚΆ«pΘ’jxϊk(―₯aΔ@pɚGΈρ«ίΗΣ†d5ˆ}d‘Q–Ψ‰q³^N]ύŽ_x« ™ΙιΤ›Ι6κGΏŽτ1±΄“UW'§•γ…ΜλήΑίμ"Έ$b”ν,Uε’ψΉ‡Ιϋδ-yΰ‚«εΑsΔ[Xu}§&pT¬ί„ρΥrD›όΉQZ"?C―­U‰ξCς‘"ΚΦhJϋώξ•w;o¨ZšsC΄5…"ΰς©βι†T.’‹‚SίyFΉ•Ž΄ϋβυj%ΐΠ± !%ύͺv’!ITύ:™ΐή'ΝοbαΧβμ%ΝWΫμ<š ‘™œAϋΜδζ`5NJςυ΄4“ Ή(]Ό—– ŸΆ—tοντzχeΉγζ?Κς9'ŠΖ&uΨ0`ΛvDE9’E/u Ϊ1Ο©&Œζ U~ΧΏ Gͺ£ΞΜννŸ?]Ύ9ιPαjώ8RͺYζΗ…_-V©}3©$ύΞ5:±œΕπ“ε©HΨώΒBΕ™˜ θ[Ύ‰“Χ<•Ό‘Q,LΆΣ13Ή9HZ£”¨Ξ#?Άέ5A/Τ$Y«jMJ²Φ-ΉŸΏg”Ο=±Ι¦IG`Ί‹ZρηŽ,TZ΅ͺ…e;/˜ OQ³ΌX½š%S0)Σ!8},όψXU_κ­u­΄°PEεωLΆΗPIο|YόΡ™sύΊαΗι―Mˆ<€WO£:-;aϊΧπb x|ςέf FΫ,Τθ3Ή9h¨Ρ5/βoP³Κ‡Q‘–Ή4υΫtΑ}WΗ°m¨HsϋIH•Ψ~ΓVΉ6c>‡!ΤDΣ…« ­ΞW“Y±–0κ3—Ί š4°‰)ˆ%·Εέ„uX‘ΩΪӁ 5ΒšŽ5³;„"SΘTξ,Θ(/Pn’4Ϊ?ϊ†φJ_μΘ5₯Φ\Sh}‰"―V±—¦‰hFΐΟN€t›2ϊa %g„šlΊσ4e~gYΔbc)M -”ξΡνxCιΑvIσ»Ι«;Bnέ2|5)©΅¦sΔYH©³ŸΕΦ²­${†§Š©ΏfxΐC }©ΙRηK’fΉC&|G#-΅Œšlzg“2‰χWΑ)£TTUδ ΆYaαEτgW¨bteΨφ/Q«ύ½ΌZύξGΗΒ½ΓαΦFο$*”ž˜…:CΨ«§!€Αˆ² ·c1³φ.apΕf²έeΨžΰv6Κν½ΨΤk8™žΒžμΑυΪ!Π§z/Ην\ΜY›ŸcRΙ—8#&°’…Έ‘° Lω#PθΪBKΛύ[τoSŽΐε˝?LP³ΛO.k8™e³ΐ+k  ]εξΊ}πψW±bιNψιρΚϋʚ຺)@^Dύœ¨ί«§QX»Ÿο/go|†>5{£Ξ;}΅δΊΛV±…SΆ½ΒŽœ<3ξjž{•Žτ€ŸGΦ1λ8mλK -ۈUbžθJθ¨Y䎀ς  –ΙφŒΎ@3 4Γ@“~4TG,€& ά֌¨ “’p©u~>ί‘ͺBN*ŽXQ^*οE“•½b/όφν–g«£cG™ͺœq€¦τS€φ%jκΈ6OβF`(¬έΟέο\ΖΈC+C‘δΝ&⇣t€dhω&~ριϘzΰsήq>σv½Γ±;ί’°φ@xίΙP+VdΡ±ŽF^}9wυK„!Ρ€$pMΒΔΥdaŒΧ‹q²ι1Sΐ(WΠύŸ¨ϊUΧΜV₯UΏΨ©¦ Έ~ΚγύΕ"΅ŽξΪmΫΐP±Μ'ŒPu˜?έ¦>K ΘfΝ­ŽοŠjφΡ§f_›GJ2ΐi[_β€ν―a3όmŠ K6t€…p ½˜Ζ~‡ŠŒ’ΦS— θ•}_)I`„εΑΖƒjιΞή*—Χ‘¦FζηVͺ<ά_ —=ΥφCXt΅^Ρ'Ϋ[Yl,ι  L ::‘$QΎκTmγΠlώOqΖζηΓ>^·ΕA­5“Zk:.[΅Φ κ,ιΤιЇQά€ρS“ΐ(]{λ"8qΝl•τΛE`Ρ”ώΫBpXΰΟΔΰ>Š_%€Z95ώ¦–w°§ΑVnγΪkς]8^]hΑΞ€fay―Ÿ°μ€_5yS–ΐ .ͺ|―_쫦ς~Ώ?ž]&6jvmΧΦ‘BΔ@oΌ¦₯ά©χ4HZΦόZP[‚Ωcvτ’Τ%°P 7Ύ€j5Ÿ?QΕ>WΦΑ%O(~wάσμU•μ ζά“wΪ0”ξύύι0JΜw£ ³|μ52§Z FG‘βf»ϊ,T*§ΠAXΤ=ιt&Rw*@γW‹Υ* 'Ž€{ΟUU2AΥ]φτ°ΩCθψ4kΗwΤF μ…`ΛέΊ-Hήΰα)ƒΚυ •γΨC‘ΪFΝ―?^€ŠΔέ³DΥ™:jd6\§·' τϊPΗN„fƒœq`Ν ~ΠΘ5"4¨Ι€‘jHi:_Ε;_τΈͺŠρΠ*ΑΎΪέα]§šή%K•ƒΘvΐ–Γpη[ͺ GudαΊChψτ΄ξλΘzσ 1aX³y-ΝD  Τ(mΪΡρCχ °Pλ ν«€“FAY­Z‚³'" t₯Q‘–Cπι6άΊ―%,OωCq;–f…΄^ΰμ§F]έό’3 Β›Ί $δ₯«QxΥ^΅”ggB Ι6*Ι7JI—΅$tBΗ‡·°γv|X   H‹kθ£Ÿ~ό»¨OΐχZψΑWΏη;«ξοp£%}qm9jΆ9tZ<7λΫDm[‹κ,„¦4Άα55uέ‡ΐ"˜Ψ|ŸHhd•φ38°ƒ‘ΎMŒχ―a΄o}pΚΊ 5 ‘αΗ‚[Ψq‰tκ„Ώ°ΰΕ†WΨψΚ6ƒ?fώœZ‘ήρ bVφgυoςΉΥπ΅»i„f˜ύΐΩ'h*·1ΫΎM.$Ά\pφ…΄|ΠƒΥλ€ό5PwκφC‚+ά&=Ί!aΔΝ5ΚιίΔ`†Ά3Ϊ·Qώτ μ%Χ(Η&ΫΊ t¦zWπ¬γbΦ[ΗΔο„›!TΊ/–j ‘₯½@M@Ω ”»(|Œ6ަFŒΦl5‰ΫΦ,exCΥFpνkϋΉtt/'ί­}”Ϋͺξ"CΦ’Ε3F€²&‘η/„δ΄-cφž%1Ÿ½%3ΒLΞFSuB₯t- r'Az?Z]ΔΘ–yS•φnΛ"Fέ&c@žQF–ŒηJ±υ Μί°^n|ŠŸv+½\‡ZδΠƒfkpΕI܊RΙ΄jh= ϋΗxL šU=F¨Χή„5cΒ$p Hd~­?8Α•˜ΆS~ρΙOΙs—5K^Νf‘Σϋ+½Ω3ΉΉσ0Όΰ­hX«q‰n{2›Ϋ)Ρ¬5 <₯=O›ξbΤ {\'°@@οΪdz«š’W€5Sωn}ΑšŒ[N„³XͺYδτAΰ―mXΈίπ‚»Dιγτj»6_*3ίQ 5ΫΪψΫ‡Iΰ.†K8q‰-U‚Z %²F–°@Z0*ΚQzθΠ έ*tΘωAπ°”­χA5ϊ·š²"jwτ,“Iΰ.FΘΐ-ίQ3πθv 4¬v{‘"nZ~p€ƒ›ψ Wθκ|όΥj«#•­ ¬ ·0EΑ$pΓ₯9ρЦιiCΊ¬Ε.UF†K8©Ρ2π·α–ι2@AZ)Ω£!³XΉ_f&·AβιφϊΤΓφ"œ{άƒ`Έ‹αFEg‡™ιύ‚yžOη[KQ „tY @•–Ε}0Km3ωΠ~k-γπ [Τ}¦ΛZfz—r‘λΏΜΧ“?&θ€M&βF €atΔ©PT:Ξ0 άΕ¨²dη»^ΰŠΪΗο[uc*Λ9―ξEUχζ=ϋ‰<q=_Ψf…gΙ NvΏΛΕg™γύŒ,#1―ΈB*Ϋ ι0ω|n o@a„]sBΦ»ι'w4~O Ÿ·r)Ϊ¦₯•šƒIΰ.F㏕_Εiuob‰qΊήΖ!.q=Γqžωsζ-|˜vσέ‹9ίυγόk±ΚΔ,n–( d μΰN$<έϋrž?u‘Z’TͺιÏnψ‚βόͺH\pυ ‹αW…ΤQΕγ,†]ЌVΓ‡ρ^—ποtΓ‡nαύkΑο,nψΓΩ-Αχκ7ώp±v‹τ‡‹Φ… ΉkΘπΉ)Γί t)ρX’»Mw1¦xW2•ενϊmŸΐώTω3*΄zβ—e”ŠΨ§χεή‚³6m\ϋFς‡F΅ς…"Τς' όΡ‘εP‚Ϋ 4)Γ„Arκ‘•ΒK©Β„EͺƒiRΦwF€‘c ή]VfθN\›ΐ&½=žΌ;-ƒ˜ΰ]ΝhίT« ꄃZ‘ŽK8©κ„·°γ6ΌΨπ +~,„N Ζ•Ž$’αbΫm™5kΕ.Ο2ͺθΨKΐ Œ#€I7Υd`u€‡Έέ&Mt Μς|Α,ψJf‚ω„/6<" O0­Σ%Έ„3Lπ-ƒ‘A΅Θ€F ώ/2¨Ρ2¨ιΤ g0›ΜN υΎˆŽΐ/,j&β4Z\‘TͺΒ‰i£^ϊΏŽ™‰v`―ޏίfέΞ<οΗό¨ϊn¬q2ŸL:Θ`ε*_Π`Γ‡-^Ι§!’Z\ P€i›ƒŽΩ§O9/₯D/β‘ςkβZ_-) vΙKUQΡ*|αΘ”™RW€Š6:ΕΩ¬|Ρy›­#jE:U" ·°skυ9Ν½Έc;m{£ˆ›Cύiiπ7ˆ©c“γ…Ϊ¨qΑ:ΣOι<㼘3λ^gšχλΈξ;ι,€!ΆŒΝZΛηFΩ7R`9ŒŸŠ†‘V|J­‘Ne ›ͺ@^iΓ+mxd{}ύX\5Ÿ##nη΄:™•ΦɍΞSβ­ι€ύ*Q#hŠ΄…@[‹₯bά.t< Θς€ Τέ΅@PόΫ$r\ρ₯ν(φκύωΏΊŸ Η³,1IH`]ψmΡνLK{›ζmσΓTppξΞρfΥi }­έoΧBd³ω(Q>ΚΗϊΎ-r;΄”g,ΗΝ† FyMΫΖΆε‰k·žˆmσ(μg²χ›Έο;ι ’l½›π†ώlZΕΦύ ?Λyޏƒ™/1]’"L:j€νƒ ;Z,=VςφΖ’Fέζλ-Τ›ο} oΒ―°±Ζ:ž1ώυδeqί€μΨΠi‰ O pΊϋ ξ­Έ‰~V*‰‡ΜδlΤhΫ₯uγT,-“X’&ΔΖ‘: ₯γ@‘=ΐ4§;ˆZ‘N‰^Δ$χ7q7Ÿ!I άQψdβ|+'Ίίγ‘ςk( ŒΎAˆ$i@/qσPfkθϋxx₯  4ψc’…ώΆΓ‰Ό‘ηoFU(}l’Έέπ³–1ϊB7$p@κT²ΆIΎUΡΙ2“3©7“³PG‰(&'PD Γ, TœtՁτjΗ±eπ:ϊ=tεψA5Ύ‘ ^°ΫΨ‡•J#;aϋor#$ͺsP£mΚ —™ά†ƒƒο#Γ-·;QIHg·½ƒϋ1C6›…OΨT½¬fΜc{°<π­ !ΗοvφΆ„Žΐ‘ΦΠL. ήLŽ,–ήΑ[–ΰ1#₯‘FΤ(Ϊ^H”ιν@%N˜ftΌβXΐcιWQ8Θ±žΘ’•H:SΌ+(μΓ)k)μg›e(~,1.Œݎΐ™F­Ρh­‘Θ\DωΌ §G€)b‘ˆ’IβΜδX £όν@‘―£S:υΪέDψ±π_ΗE,rœώ~%Ί …@Γ`’wwTέɩζ]Ξ³Ξ‹8¬Π'p ηΡνμ“V<2θX•c©`tΪΖ:Φ1ΨΆƒ|½]¨d²έ;„UuYλGy ·ωJpκ.Žr~Ιι}ήP€ν,3Ή­(βvd™­β°^ΐjΫ„phUι—Ω¦sYή“œSχ™²šz–Ω¦sVέkq=nI`·a§Ÿm/δ<Ο9Ω/1ΞΎ–l½2jVO΅‘Ι:χX^\Θsε²ΛΧ°@q‘ε 'fΎΗΕ9Οrtϊ'dYͺΒΏMZ‚θ¨Ι­Žz-ό‡„™n‰m–‘μΡϋ·ΈM™–Η£ιίE'€ΖsŽ 9Υύ6ΏFνv sFΦ"ΚŒ)Ž!›^2΅jf¦/e¦s)η<Λ]oγͺΣhΫΕ‚μW8?η&ΨWcΥ|ΙMΪHΤδVΘ΄oη>Άy‡ςœυBD–Δ‚VιΓ‚ͺWlΕ§Š”c`Α~/X4x―,€|―Λ@ψ½&ϊχΑοBϋΣ~ͺ§ŽŠ@—ανDψ;Q£Ήρ Έ‘Φ[ΖΔΌ,l¨dν›φΣψ$νhNpΏ·[ένάΧΊΏ”t½6vΒ·™δό†φΏšΟjη0ΦLξ€4“cAﬔ‚Ηkΰ.ΗmJ.4ƒzR„,xވ8‘Δ ‘0τyψ;)ΓΘpAΉΠv‘B'€x{ ώ0Ω­ψΒο#; MαŽG' :Ÿ`§d‘ώ†ί…:)κm—&=θxΞya›~/ΧrωCζ­Lπ¦ΐ8œ ·9ω&<€ O»«σηκεœ‘½¨ΕQ»'`―?ŸΤM_λΎπƒκ—Ό†OZρK•RiΘΰXΨZτ\, %2υj2΅jμB­9ε‘iT™T2;υΧV|˜vweέΖ+ŽCΦuxݎΐqA%m$zιGψWο„ ΰ•6܎ΛpRk€Sk€SΘ ΖΘ ΪΘ€ΖΘzΉ '.ΓIα N:pv<2 Ÿ΄β•6ό’j/‡C‘υφo‰sυZ&ΨWstΖ'Μp~Ε0ΫVς-₯ͺc^GY mž‘,uΝdIΝ±¬u£6žπΞΕ@γατkɐ5άZυ2dM‡φgΨDT85ClΫ›~ΡΪ1+λ—R“5”ςYg8št΅FΊκ™υB°3}ο2œΈ€·aΗ-νxŒ΄°Eΰ“Vœš‹ωY‹Ή<χ f¦/Ε©Ή8μ/`‡w0[]Γ¨ d£ag)£Ÿu/σ2>fAφ+TY|Q;‹'Λ.cQΥ*Ž Dφˆ4ώ’ωSJ΄"n«Ύ‹Αώνή—I`mCŒΦ‰†‘2Κ8ˆb*Ά‘#HΏTΕΠ}ŠΧ¨οꀃ:ΓA‘AΆ^Ι4ΗΧTΩΌ^y&―T.`yέT*9hΒΐ*TH™_Z τΓΜL_Κ9Ω/q\Ζ‡œω>οVŸΔϊ _Ήf¨œrΓΉΆ>¬ό+ύ;¬°Mα‡5ηΜΊΧ[ΤΕ’™†οΉnάf€Qη’ Aˆ|Ꭰr=±ˆϋζ6μΓΚ^_?–ΧMεÚγψ΄f.›=#pφ„°Η)]δedΚj¬‡OX©$‹‘Σςi³μZγ {ΖΚ€ΚΗ΅QOΰ›h° s$N5XαšΒu{䈿χυ½ssώ‡C«kήύρYž^Ζ99/q|Ζ‰η+.ΰ Χ¬ΔΝbΛζwΫ3[J~ίGύΝm|ƒΣ0GίCi Ÿ›χ·aη±ώWΥ“·HΞΜ~ϋ]ΗrΧTξ,Ή·ŒRΧWͺmΩvru―ςΰsx~ΰ\ϋ<9zE§Ί!{%*w?Ρs[%*%0Σœ"0ΠxθΘχψΚ5ƒΏίΜdηΚψά; §e½Ι― Γ³σJε‚VύΞ™Z5σ³σδ€Λψί sΉ χy΅ΪNy–Ί7C ˜ͺΘ8ŠζEƒ ₯M€6ΊGρpι΅\‘χ8§d½W²$—η=Α±ιKψλα›9μ‹!_ͺ(ΐγ3?ΰρWπδ€Λ˜κ\žπvθž“λ…*Π6 Uγ)‡ζ{ӎΔ;Kš7ΝMΔΑSε—’ ƒkσNH±Έt½– ξe»goT»Ό’ΰuœ“σΟΊ€‹rώ›σ ‘ϋ8²JF?`rπ՟z'•lεχmiηΘ₯P²©―?•…εM2' ‡ύΌ^u&gd-bXΪΦΔDΒLηR¦;—ρBεωxŒ΄6~ˆm;φ»Ž ξ ‡qΖ©? Zq T%£7ͺE[«d΄…ΐ‘r3‘5°B³Ϋ~ΐB•e­Γœ‹'¬qgΏ―˜Σ3ί@FΒ:J‡^Ηό¬ΕόωΠ-μςdDZΫ+όεθάΥη62΄ώ|θ–θ“b@jΈρB`E¨Ρ―½+„~+ϋΓPΪ:ς|@iι4”ΉήΨΔ'sΜD+\SΘ«k_—p+gΊcnΓΞχhFΨ7·λxQΗ­…@"ψΣ‘Ÿα•ρ«S”ZΕ₯:P#_¨tk8A4Œ3Ρ>HUΌί&ΌJ&Ίΰ“±ΪΫzˆ»Šn£ŸmoΗwF²8΄Ψ i'’Μe‰™έ ­!4eE™ΎcQ³ΨΥi΄g•ƒΠώ{Ρpq1ΡΉΚDhΒΐ©Ή¨62ρΛΔ+@―΄α6μ8΅6DΩ$Lw.γ½ξ‹{)95π@ΤΓΟ…ΐZC*Ω!52—£LΰŽΤYΆ’$@e‚―#…qΠ_¨|ΐœτΟΘΦ+H$‚\½œ4αA ιgέΛzχjtlzbΛf–ς¨0rθkίrΙyσbΕy|νšΦ!/EςXPΏΚAgϋR£I·’\T‘I{―+΄M!ΰνκSΈzΟ?(²–₯U… <Ιρ ·τώ3“+™ΰXΝΚΏΝ_r-ε‰{FlφŒΐc€12mS\w]h=ΘUω±²nrΈje{|!YLM eΞΗCΏšΈYH)ψΌvv85oΏ―˜ύ‡ΏίθΕΧiάXp/ύ¬{1Πψ’v«zNΦG‘΅D8žΟ€„s²_βεΚ…ΌS}r»w“œ8Ωΰ„Έ,i’<’ΡΛ•lVΦMn~[½ΓΈqί½\ΏοͺYΌRΉW qμ|}x«κTNΞ|‡Kόω½-‡x€5|;χ?ν^3)yGΰdD™τn:V \ 6•Ž€’&‹ζGΑΪΕ’ώ}hΖS ΡD}‘s!"κ'GΌΧ0@Π°˜Ή¨―Α „?‹,t]Υ‘]„ϋ}Εμτjυ< 4φϋΤΘόiν\>¨9ΎaΰxAΐ‹•η±Λ7Iφo¨“œ"NYhΫΕύύ~@­‘ΞKη΄ω>˜ŽvΪξ>Š„€’š".[ύ$BΧ"V5I-’~₯ƒΙ?ψ; ώίGώΩAX…/ό»P)αΗ&ΌM>ΧE@ύ&Έ?‹π‡ΟIΑβθGžƒš¨ο”BϋGθοΠω}XseΌ65kM ƒΏΎ™£œ_Ζw„ΚdΰΘυΤ~vΰOμχσƒ^χ‡7‘R„Λΰ†‚2Bυ±ƒλE¨υ#"–θ WμDGJA^–#νκDMNJ­Δ!xyσBΎ>4 iˆΨC6“ΑΌŽ\wΈQM)!Τ‘βθšf4θˆ"WfΒ$}κ(jŒŒΆ»…|Rs49όSξ*ΊMUΑŒ*ό9άQr'›=#5;ώΧC7σzε™ κɍ˜‘οθ²~˜€ΤΓο ©5ψή@kχ’Έ&;€Nσ0w}‰Ο°RγΝ Ζ—A7ƒZ_zψες9qϋνΈv<ώ4<4|†Ÿa%`¨"θ†ΤZŸ₯GCΠ ΐy˜y›•Ώ‘Y»Θs”‘‹>ΓJi]>;*³ώΘ6— Κ“ΥΎc΅§mΡyΰΘυτΆβΖ^χ†KΗΆΥF&Ώ*ω «8·Αη₯|Ύ¨ϋŽ:©σ5 ά °h~ΞυBΤΥ‘ͺGHΏaΑoXπ6|+ž@nΏ—ίIίΛη€Φ—&|΅7“jofΈˆμ \>gψwnΏκΌ† oΐ>NƒŽ t>H³z˜Ρο+Ξρ' |Ÿ‘ΉΫ°λnU—ΩŸ¦"’t/½] PεΝbΝαρΌΉύ4^έr6뎌U£s‚f—αδΧ%Ώ¦*Ε ώFŽ₯’ν“€}ήΎάQr'O”]έ΅“ Q#˜ξ,΄0Z !•ΎΤ‚3‘m9wRΤ°β ΨΒAίΛοTδφ9›ΏΖ›A΅Ou CgώΕœ5ό5돌εŸί\ΝWf°£r0Uή,ό†‡₯ŽηaFηo`NίϘΣο3ξšwΧM~gΦ_ΒΓί\ΛΆ²‘υא Τιόαΰ­¬ͺ›ΘO{…™Ξ₯ͺ=cX¦₯Ξpπ^Υ‰όιΠΟψ¬vNbN0A0 œŒ1~ΦᎿς9[iJ˜Φ:TG2σίέyόζj>ί?‹ζgXξVΖτZO‘σ VέG­/=ΥύωxΟ<ž^χ-ςμe,ρ WMxŒŸΞό σ‡,ζΏ.β‹ύ³ΨP:šƒ΅…zBΘμΗΒk•gρ…kgd-βΌμ™β\AΎ^Ϊΐ΄6ΠpNJόE|νšΖ ησnυIT2“r”m ΙWΨ?W:Ο•γ,―₯XSv#Ψ\6‚ίώ ^ίz&£{mΰ[cŸζΈ2 k7N‹«ž€ C£Β“Γϊ1Ό²yΫt.š0Έiϊ=|wΒ£8luΈΌNvUdωΑ©|Ό{KχΟdkΕ0꼎ČΜR%δΆν`xΪŠ,%85~i‘,ΗnοvzqΐίΏaInβ0}*Ζά™4)μnΨDCψ`Χρόθ½»©υ₯σΣ™α‚QΟ“λ(oy,DB6”ζήe7ςόΖ 8{ψ«όα˜[)Κ( o' Α‘Ϊή¬<4™wwžΔ{;OdSΩH<Ύ΄„‘ΉΩsN΄@`Σ„6Qoo?…kήz„‘9ΫxςΜ˘Ψ{•ϊ΅Μ½rΞίΐ}'έΐQΕ_ς‹~O…'‡OΉŽ’τT쉀0γ §fΎΕ©ƒίβ «ΟχΝζεΝ yΧ μ―.ŸOΌ«»’K£sgώΰΧƒ€o‘η#0˜l}žήΪ¦nάμI+J¦pεβΗ™·™ΗNΏŠ‘ων+! ‹“‹ΎaxξVZω=vUβΔAο7Μ ξ;ΓVΛθ^9sΨλœ:ψm œ‡9TΧ›Ί^Π ³ΨI }‹‘ϊ5m “ΐ&@@y].׽󾀍Ÿ~%CrvΔ%JδQ˜qˆγ.጑‹θ—Ήύ5ΕrυNΨ2Ÿ)l&3˜ΰ?λΎΝ²’ιόnή/žΏ%~±ΕΎ5φi.υ,[φc6•ŽŒΙ%60{7Οψ+oœ:Ώ=ϊvϊgνIž ΅$‚Iΰžϋ«‹ydΥ5,ώ '~'ξΛ¬Ϊt/?š~7R ώ΅ϊ;*x€5‰< k7·ΝΎ‹œΟρ?0IάΙI`z‚’στΊΌ½γJj‹ΈjΒcX- EŒ #ς7sήΘyyσBφUυmΣJH8ͺο—όηŒosΕψΗ±ˆxδvv$!C8Ώ9@ξ3σ0€ίΦi βWOΧ„‚ΟoeΡΆ3˜X°ŠΙ…+ΆΘΉ’…#^¦άΛηϋg·+&»8s?χžx#?œϊwlZbKι€ ’ύ$tp\Τt‘1γ0Έί„Ϊ»ΑχMWŸeχ€€ΓVšΐeγžΔis%ŒΐH“ΏžώY{ψlο.ύ\»φ‘•VΕoηݎEσsΟΧ7α3:R¨,υ‘„#0ΤWYxi½ΐyδ>φ³ιΉS’q„€=Υύ©πδ0‘wβJΣ !;­’ΡωΨP:Ώk Eμ'έZΛsοδšI„Σ{*’”ΐΡ$΅e8dίiΗuυ u ͺν @qϊώ„KΣ ϊgξαp]u~Gϋw$ρsοΰŒa ¨Ζ‘BH!‡`€ή2ZaWŸLΚ£Ξοΐ’ωqZ]‰'‚€Μ΄jΌ›2};bDIΘw”ς»yΏdD^ϋNR)H`lGγœ>‘δG+$ΡECj* Ώΰ XΓU9: γ Φrλ¬?`·Έ;εό“ )J` ΨΟΡS¬›£“ΝΎͺΎΈvu§#_AbηΪΛ H2w^β§$t’i«V„‹Η¨)αό‘/pΚΰ·7—ΔHΎYθ6ύ(eFvvυ™$ͺΌY\ϋφΓ,?0•ρ½Χ0Ά`iΊΏa‘ΐy˜Γ_‘oφ>ϊfξ#Mχ°₯|8Η ϊ0‘ηδφΩΩR>œAΩ;qXκβΆίτ΄ZΎ?ε|°ϋxͺ½™‰nΪ€B X‚Θ-'φ"q=ΆW αݝ'QζΚckω0^ή΄0œΏ+4Ι3λ/αΞΉw0₯pC²·σωΎΩ|w⣉›Υ°·Ί›ΛF0Θb4݈߈iΐΡύ>α”Αoσβ†σRΪl+R˜ΐ&š…€•'SαΞiϊ0 U%σσ½³ΉπΥη8~ΰΤ|Ίw.{ͺϊ30{Wb&„,Ωs,nΏyύ?Žϋ1Φ:ξœ{ΥήLήΩqrΈδkwG χUd5@΅*λQ§7τuΤ—–ΰ—θΐ+N†`yΙT £…Ϋ+ ¬.7œΗς’©μ¬Δk[ΟJ˜rgρτΊo1£ΟWL(X&ΗτZΟ§_ΞΕ£Ÿν1ώα%_μΚαήw†F­**X5_ΈΘΈUWο5a`ΥƒEΘ…ͺ]lΣΌαο,š*(zΪGγύιZ Ό.κί‡χ!κχ‘kϊν„_kΊ±ςBπψ‘ο4Q_μ½Αj ’ακ ‘uq#?Cώίεu²φΘΈΦ›0’γ:\u5g {-ώ£°/o^Θηϋf3¦ΧzώΉκjτpϋγeσŸfδ¦iχπζŽΣ(―Λνφρ>)K`Ώ!y|YΟ­>+ͺ™Ψ*D˟G’’Ας$Ν«CΕΝƒŸ…ˆΪψeΡόˆPsαG4!ƒJ=b»Θ}Y5_xu‡P§’klš›ξΕεs²ςΠδΆΩWΦǟΏΌ…ΏctO|n”€ ‡Gση/oΑ°ρMΙ$~Uυͺ½™8«βζA * υπ+τYθ{ΏaiπήgXΓ…ΡύKψ{Ώ΄°·Ί5ތnO^Hao>+φBΏ^υ²/ή€ϊίo¨—a€ŒVΛ)Z/±|hΘ›εm‘ZΫ6β|4+d₯AfΨ‚wΓνj7TΉQ‹ͺ΅ΗΔn‡8’Bπο5W2py./Τx‘Ζ£^΅¨ΎjΫ “ϊΒΌ‘’ι`pΎ$Χ iΊΪΔγ‡ZΨrDπωvψh›`} Έ½$όΑ¬σ9ψυ§ΏFΧN~XΔν1§5ΨU1›?ψ+‹·Οo½ΌmG¬¦Œ”%pfpԊŽΦŸ8C†Θ)ρGn_½άA2»Ό‚Z/ :ƒPPμj<κ;—Wm_ηSˆΗž@}gΰ7 Ϋ ΖK.&™:l:”TΑŽRΑΊ5κZ4ΘO‡Ή’GHΞ›ε.ΙΗۏοlΈ<$ŽΘB€άϊΡΨV1”›gό•Ω»c_l]¨2³KvΛ―>ω Ÿνc/HYwšP²…  GElA 8Jϋ_dGΰ‡:o½UΠ+C2©/¬†gΎΌΆV°zΏ"+ς‚ω‚’,˜3XrΞD8u΄δΤΡπϊZψΫΑͺ}ͺΣ׊Š'IΈόNξϋϊ>Ωs4ΧNz˜3†-’oΖΎϊŽ£ΡBg_뎌εΙ΅—ρτϊoqΔΥΛ$o‚‘|u‘\ΥWΚ1²Ϋέϊ*7<ρ•ΰαΟ‡j`r?8f˜"uίl‰Σ¦:„*7l;"XΊ–lμ)‡ΩƒαGΗIŽ.)­…Uϋ_ξ‚Οw VοƒύUΰo―^n t-ΐΘΌM;` GΙ€¬έdZ«BRλK§€Άˆ₯£ψκΐ Ύ:0#œαd’7N0`ΪŒ£g₯@aw€yΉRž/EV7²VοάϊΊΰ˝pΦxΙwgΑδ~G+ωθ‡jΰݍ‚|"Ψr›+Ήω8I–]}ορÞrXΆ[πΑψl‡`Ϋπϊˆ/™ƒ&΄Π%K6Ν‹oΐ†Ϋo'`艱z2‚dΝΙF=† F€Πߎœ—'—!EZŠΟ(.Ω*ψώσ]ƒ»N—Μ#±Ά± ο‘xΰcΑŸN#ωλIοFaΏRͺ‘xιNΑλΰΓ­‚]eΑc)ή†==9a,δdGηjWψ8ΰM Ιt”ΨŒL—ςΨ<ΙBKΑž}ιNΑ₯τˁœo0¦¨ύϋ πμ ΑO^œ8Rry’GσΫξ(…·6žF°lwηΜb›θ $X,0x rΚ(*RΟ½lf:¦« »;c&™ωΑev8θb]­~dž‘‘BfυžrψΞ³Nψ ˜7T}mΫΌt˜1LLι§&ΥφUŠzσΪDς@‚Pά9wL›ŒhnΤ ΒVu5o^`Ρx!ΐ'a—[ˆ΅ΏDφ²‘τf΅?Ώ~K°t§ΰ_—H&Ηoίc‹ΤLυύŸ¦φƒ‘½Zήήa…ΡEpφx˜6@M’ν.ψ˜Dξj„tnς¨©0η(DŸήΡς}Ω όΈ΅«MθΠ[ ˜άœ €7w­!}|L’λγΟv<*ψΙq’[NŒnLi-,xLΓi…Ώc΄ΰoŠΌ²Zπϋχ`šΥ]…F:77G=&²yοεΰΰA`#$IAd;pŠΘsiΖΔιγQιR“C2©τ±ί€ο='ψr·ΰλ ϊd%ζ8/|#Έζ9Α.•œ1ΆναRkΐΟ^ΌΉAτΤ’R]ƒ;yτiEηu¨Ή’{€Ο fΐί3Z$nάΠ`”Κ.ΎŒjξ7†„t LΙ’rnŽ€(-9gΫ8ώοΞ”ά~Jβ¨Qξ‚“ΤΧGςθE½#ιΑjψΕ"Α_ &‹‹ ΞνS€ˆ;x Βji‘Έπ%p/π:ΰEά’fJθήΰIGγRΰο(Σ΅ΐeD™θΈπIΉj³s€œžέυώγe»U|υ)£:Ύ―–λ„“GJ^\%8\#)jΗH_˜ [(Ι°Α?>ψMΗΑ6ΝΝQ¦ς¨ˆt‡€Z οVΰaΰ?ΐΑΠ‡‘δ…$"pγ yπsΰ%ΰΰ,ιγPά@©bU΅θr}όυn(Ξ†a‰gΓΜAππη*r«(«}ΗΛΆΓ]gHϊT˜Šβ‰ftΡΊΞύ°)τac←tn|Β7m°ψ†τqθςvΉ= ™Ž<Ά τ±ί€­GƒςGK%ƒσ%]°«ζt`?™iπΫω*LσΏ+ΜE΅;Œ8κά–΄αžΡ"4»—Qϊψ:`tγν5ΐ/aM΅Ϋλ`Jͺ}(―ƒα2œœHdΫ•›¨ΜΥρ}ε:αgJΆ• –νĜnB:·OSΫ y[ΥΉ-!%nΡ=£Eδ…τρ™ΐ_ˆΠ .,Bs―&ή+²ͺV₯”Αμ€Ά†JΆΊ¦^ώ8•€”wΝ—δgΠcW;h‚±βΉ9Θy³‘gžŠ9aΡ[ΥΉ· €ηh#y!FΰHtDΏqXˆΥ u μV•"(ƒ½q"QηS Ξ8.wΒΙ•GΑ_?0έK1‘‘ΞΉ9κγVtξΣ(n«:·%€_h{τρ³£Ϊ©w—Γ›λNΜ$UπD° O~Ίum:gIφT<~‰=Α«_QEβιkΦ5ψήΙ«k[cκαζΠΖΈeΪ©s[BJ8„fτρΕ(qT}ˆΤΗ™ΘΉΉ±λγηV nyE [ O– έVlΟ,ΉεΙΘή*^ω“ν*ƒh@nbΫ`ν°j0€W|ΗΚ!½ΰι’;›μm B‹ξΟm‹Ξ5|pί„Ž΅oJš˜Υ₯ΐύ¨^ξZΰΫ@“όŸ°>bC­³r€œΡŠΨ€/v¨χφ–7ό~c‰ΰ«]‚«ό±•uπυΑ€άΔ‘~>Ψ’b‘ΖΉ£ΐeΣ%‹Χ ΎάAŠΜ–$υώ\&ŒEΆΑŸϋʟ{(τaGFέHt»ξ΅Q|υt”YέD‡k|υΎJ8ώ~Ν‡Zh5 ]Gͺ‚ §JώuqΫscΕΖƒpƒΧΟ•όόΔΔtΑΟ_ΌΎVτRιΝ +;AηΆ„nGΰΕWŸ άD+ρΥֈψꑍτρ§Ϋ§=,¨‰₯\rπΠ½2ΰΥοJfŽ?Ή ·Ώ!xΰSΑ“—JN%±'Ȟ:T=ραfzήHάHηφ)R™B©s[BΚ›ΠΝ‘‘>~ψ„Άθγ,δ»Δp#tnQU(cBπ^©†ϋž(¦MΩB±`ΩnΑγ_ ͺέpέσ‚_ —L­―±+ j2šx‘Ο YyδϋΠφ~#ψΉ‘$C~zΘVι!ˆ“ΞMqCθw$b4 \ƒŠ―ŽZ#΄΅ζCnέ7#φ—ͺZmm1›Ώ9]ς“γΪ—l ͺΰ²§4ήۈκy$eΑΰόΰω‡I= HRKΥY" άhΏQ]@*M/_sR£…ΈεPΫzCh«>uγFZΫv |m­f!!Η] Ήβ(IGΓ:Τΐ^<ύ΅ AέΞXλ6‡ΠΦσθ OJϋςss[BOΈ-MΠV},„ͺ°±krε*ΨwaΔήzR…)ώςdΙχζHmν;ο-‡αΦΧ/―ξαJρFPηŒ[.ŒIη.F™Λ ΧΉ-‘GΰΖυ22J*Ÿτqš€:7lڊ\΅JΛ‚νK+J΅φΡΉUYΨΙύˆy4φΐ’΅‚?½― Ίχά»g„tn‘š [~ξRqΡI:·%τψG‘­ϊ8DϊΚ*δšυ°~’Ά–Ψ[€β8{Όδœ 0‘―$ΟIƒΔ‡€Tfϋ+ዝ‚ηW >ΪͺVyθq³ΐ‰@Šιά–Πγ B‘υυΉšΥΗ!λΰaδΚΥmΤΗA½šn‡!ω0¬—€0SΕPϋp€VΫY¦*fLβΖ )¨s[BrœE‘ΣυqKbΦ\ε ~HaΫ’λl’Qκs%V›HιάΑ–ΣΉ-!9Ο*IΠH‘Ύ>W¬ϊ˜ΪZ’¨t`B€Ξ£‡#œ­λά-ΤΧ‘:ŠθχŽIή˜Όg–Dˆ’oΞ&}όΝjΨΪ±‰φC‚Ӂ5&ŒƒάμΤΦΉ-!ωΟ0‰EίM+ϊxχδŠΥ°oυ±‰ΆA‚Ε CΆYηή|A’κά–:gš$θx›Šθ2υqœΡΝunKH½3N΄Y1υqΡTηβt bΠΉO‘Dώάφ"5Ο:‰Π^}|θ0r嚠>nG’DGHηŽ„ ccΉ‡Q:χ!RLηΆ„Τ>ϋ$B#}| Κlκγx£κά–Π="Ipγ:‰¨˜Κ.Bιγ1ΝύFPηAnή ί˜ϊΈyunq!rςD< ηθά–Π½&IE_\N ϊxνX·ΡΤΗaDθά‰γ`T{u„{’ΨŸΫ^tΏ+J"„‰,ˆωΗΡΆ7υq#uξθ ΞΝ‰]η>l}ΨέFέHtί+K"4£ηLI#! Œ―ξ‘ϊΈ}:χ ”ΉάνtnKθώW˜$Έa}ƒjyΤϋM}Bϋuξ=(wKΫzΞ•& Ϊ««ͺ•Έ[κγ8ιάd[NzΦΥ&BD–!Ϊ‘·ν―·Y‚Σ‰=ΒΤΉνAΟΌκ$BΥΗΣΉŸΜ€ξ©Δ ‘g_}’ QώqχΦΗ!ͺ·›ΞύEά©s[‚Ω I„n­C:79ql›tξƒ({8΄›{Mς†aΆD"Š>ΕW·¬„λs%—>nŸΞ} 5IΥγunK0[$‰E‡ς[ΦΗ{‘+V%>–`΅F¬ŸΫΊΞuΡ0nΩΤΉ­ΐl™$G£όγn¬sG€ΣήͺΞέL½?WιάθΟm/ΜVJ1$­>6un—ΐl­E#}|2υωΗ­κγ•«`oΌτ±ΉrΘ ˜<>f«όΉ’/¦ΞνΜVKa4Κ?ιγοc›ϋM€>^΅Ž΄W·_ηήƒš¨2un`Ά^7@#q[υ±\ΏQ«>ŽΤΉ‘ΈeSηvΜμFh€§‘άN1ιγ`ύκ–υq½Ξ•Ζ"LΫυ0[²"bDN£a}υρξ Έ‰>ŽΤΉ ¨wtιΟM(Μν¦ΈaD‹ƒ>n‡Ξύœzn˜ΔM$Μ–νζθˆ>^Ώ ™–†hƒΞ}•κgΖ-wΜΦν!ˆ’oΠ‚>ŽD £ξ!κuξ–Π‡ζ¨Ϋ90[Ή‡!Š>Ύ˜G3ϊΈ„tξ=¨²6¦Ξν˜­έE_ˆŠ―ΓΟ4Œ[6unΒlυŒ(ϊψjΰ šΡΗDΡΉέ΅ήrͺΐlyυρTκγ«³‚›” Hϋ0¦ΞM*˜wΐDβ«ΏJίK0unαr'asyYΉ%tEXtdate:create2020-01-07T11:01:42-05:00{ŽAξ%tEXtdate:modify2020-01-06T15:48:56-05:00+~IENDB`‚vroom/man/figures/lifecycle-archived.svg0000644000176200001440000000243014504102737020100 0ustar liggesusers lifecycle: archived lifecycle archived vroom/man/figures/lifecycle-soft-deprecated.svg0000644000176200001440000000246614504102737021375 0ustar liggesusers lifecycle: soft-deprecated lifecycle soft-deprecated vroom/man/figures/lifecycle-questioning.svg0000644000176200001440000000244414504102737020665 0ustar liggesusers lifecycle: questioning lifecycle questioning vroom/man/figures/lifecycle-superseded.svg0000644000176200001440000000244014504102737020457 0ustar liggesusers lifecycle: superseded lifecycle superseded vroom/man/figures/lifecycle-stable.svg0000644000176200001440000000247214504102737017573 0ustar liggesusers lifecycle: stable lifecycle stable vroom/man/figures/lifecycle-experimental.svg0000644000176200001440000000245014504102737021012 0ustar liggesusers lifecycle: experimental lifecycle experimental vroom/man/figures/lifecycle-deprecated.svg0000644000176200001440000000244014504102737020414 0ustar liggesusers lifecycle: deprecated lifecycle deprecated vroom/man/figures/lifecycle-retired.svg0000644000176200001440000000170514132374245017757 0ustar liggesusers lifecyclelifecycleretiredretired vroom/man/reexports.Rd0000644000176200001440000000154614132374245014524 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tidyselect.R \docType{import} \name{reexports} \alias{reexports} \alias{contains} \alias{select_helpers} \alias{ends_with} \alias{everything} \alias{matches} \alias{num_range} \alias{one_of} \alias{starts_with} \alias{last_col} \title{Objects exported from other packages} \keyword{internal} \description{ These objects are imported from other packages. Follow the links below to see their documentation. \describe{ \item{tidyselect}{\code{\link[tidyselect:starts_with]{contains}}, \code{\link[tidyselect:starts_with]{ends_with}}, \code{\link[tidyselect]{everything}}, \code{\link[tidyselect:everything]{last_col}}, \code{\link[tidyselect:starts_with]{matches}}, \code{\link[tidyselect:starts_with]{num_range}}, \code{\link[tidyselect]{one_of}}, \code{\link[tidyselect]{starts_with}}} }} vroom/man/vroom_write.Rd0000644000176200001440000000650214362130126015033 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom_write.R \name{vroom_write} \alias{vroom_write} \title{Write a data frame to a delimited file} \usage{ vroom_write( x, file, delim = "\\t", eol = "\\n", na = "NA", col_names = !append, append = FALSE, quote = c("needed", "all", "none"), escape = c("double", "backslash", "none"), bom = FALSE, num_threads = vroom_threads(), progress = vroom_progress(), path = deprecated() ) } \arguments{ \item{x}{A data frame or tibble to write to disk.} \item{file}{File or connection to write to.} \item{delim}{Delimiter used to separate values. Defaults to \verb{\\t} to write tab separated value (TSV) files.} \item{eol}{The end of line character to use. Most commonly either \code{"\n"} for Unix style newlines, or \code{"\r\n"} for Windows style newlines.} \item{na}{String used for missing values. Defaults to 'NA'.} \item{col_names}{If \code{FALSE}, column names will not be included at the top of the file. If \code{TRUE}, column names will be included. If not specified, \code{col_names} will take the opposite value given to \code{append}.} \item{append}{If \code{FALSE}, will overwrite existing file. If \code{TRUE}, will append to existing file. In both cases, if the file does not exist a new file is created.} \item{quote}{How to handle fields which contain characters that need to be quoted. \itemize{ \item \code{needed} - Values are only quoted if needed: if they contain a delimiter, quote, or newline. \item \code{all} - Quote all fields. \item \code{none} - Never quote fields. }} \item{escape}{The type of escape to use when quotes are in the data. \itemize{ \item \code{double} - quotes are escaped by doubling them. \item \code{backslash} - quotes are escaped by a preceding backslash. \item \code{none} - quotes are not escaped. }} \item{bom}{If \code{TRUE} add a UTF-8 BOM at the beginning of the file. This is recommended when saving data for consumption by excel, as it will force excel to read the data with the correct encoding (UTF-8)} \item{num_threads}{Number of threads to use when reading and materializing vectors. If your data contains newlines within fields the parser will automatically be forced to use a single thread only.} \item{progress}{Display a progress bar? By default it will only display in an interactive session and not while knitting a document. The display is updated every 50,000 values and will only display if estimated reading time is 5 seconds or more. The automatic progress bar can be disabled by setting option \code{readr.show_progress} to \code{FALSE}.} \item{path}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} is no longer supported, use \code{file} instead.} } \description{ Write a data frame to a delimited file } \examples{ # If you only specify a file name, vroom_write() will write # the file to your current working directory. out_file <- tempfile(fileext = "csv") vroom_write(mtcars, out_file, ",") # You can also use a literal filename # vroom_write(mtcars, "mtcars.tsv") # If you add an extension to the file name, write_()* will # automatically compress the output. # vroom_write(mtcars, "mtcars.tsv.gz") # vroom_write(mtcars, "mtcars.tsv.bz2") # vroom_write(mtcars, "mtcars.tsv.xz") } vroom/man/vroom_progress.Rd0000644000176200001440000000137014362130126015543 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom.R \name{vroom_progress} \alias{vroom_progress} \title{Determine whether progress bars should be shown} \usage{ vroom_progress() } \description{ By default, vroom shows progress bars. However, progress reporting is suppressed if any of the following conditions hold: \itemize{ \item The bar is explicitly disabled by setting the environment variable \code{VROOM_SHOW_PROGRESS} to \code{"false"}. \item The code is run in a non-interactive session, as determined by \code{\link[rlang:is_interactive]{rlang::is_interactive()}}. \item The code is run in an RStudio notebook chunk, as determined by \code{getOption("rstudio.notebook.executing")}. } } \examples{ vroom_progress() } vroom/man/locale.Rd0000644000176200001440000000377714132374245013740 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/locale.R \name{locale} \alias{locale} \alias{default_locale} \title{Create locales} \usage{ locale( date_names = "en", date_format = "\%AD", time_format = "\%AT", decimal_mark = ".", grouping_mark = ",", tz = "UTC", encoding = "UTF-8" ) default_locale() } \arguments{ \item{date_names}{Character representations of day and month names. Either the language code as string (passed on to \code{\link[=date_names_lang]{date_names_lang()}}) or an object created by \code{\link[=date_names]{date_names()}}.} \item{date_format, time_format}{Default date and time formats.} \item{decimal_mark, grouping_mark}{Symbols used to indicate the decimal place, and to chunk larger numbers. Decimal mark can only be \verb{,} or \code{.}.} \item{tz}{Default tz. This is used both for input (if the time zone isn't present in individual strings), and for output (to control the default display). The default is to use "UTC", a time zone that does not use daylight savings time (DST) and hence is typically most useful for data. The absence of time zones makes it approximately 50x faster to generate UTC times than any other time zone. Use \code{""} to use the system default time zone, but beware that this will not be reproducible across systems. For a complete list of possible time zones, see \code{\link[=OlsonNames]{OlsonNames()}}. Americans, note that "EST" is a Canadian time zone that does not have DST. It is \emph{not} Eastern Standard Time. It's better to use "US/Eastern", "US/Central" etc.} \item{encoding}{Default encoding.} } \description{ A locale object tries to capture all the defaults that can vary between countries. You set the locale in once, and the details are automatically passed on down to the columns parsers. The defaults have been chosen to match R (i.e. US English) as closely as possible. See \code{vignette("locales")} for more details. } \examples{ locale() locale("fr") # South American locale locale("es", decimal_mark = ",") } vroom/man/as.col_spec.Rd0000644000176200001440000000061714132374245014660 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/col_types.R \name{as.col_spec} \alias{as.col_spec} \title{Coerce to a column specification} \usage{ as.col_spec(x) } \arguments{ \item{x}{Input object} } \description{ This is most useful for generating a specification using the short form or coercing from a list. } \examples{ as.col_spec("cccnnn") } \keyword{internal} vroom/man/vroom-package.Rd0000644000176200001440000000272714533532502015223 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vroom-package.R \docType{package} \name{vroom-package} \alias{vroom-package} \title{vroom: Read and Write Rectangular Text Data Quickly} \description{ \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} The goal of 'vroom' is to read and write data (like 'csv', 'tsv' and 'fwf') quickly. When reading it uses a quick initial indexing step, then reads the values lazily , so only the data you actually use needs to be read. The writer formats the data in parallel and writes to disk asynchronously from formatting. } \seealso{ Useful links: \itemize{ \item \url{https://vroom.r-lib.org} \item \url{https://github.com/tidyverse/vroom} \item Report bugs at \url{https://github.com/tidyverse/vroom/issues} } } \author{ \strong{Maintainer}: Jennifer Bryan \email{jenny@posit.co} (\href{https://orcid.org/0000-0002-6983-2759}{ORCID}) Authors: \itemize{ \item Jim Hester (\href{https://orcid.org/0000-0002-2739-7082}{ORCID}) \item Hadley Wickham \email{hadley@posit.co} (\href{https://orcid.org/0000-0003-4757-117X}{ORCID}) } Other contributors: \itemize{ \item Shelby Bearrows [contributor] \item https://github.com/mandreyel/ (mio library) [copyright holder] \item Jukka JylΓ€nki (grisu3 implementation) [copyright holder] \item Mikkel JΓΈrgensen (grisu3 implementation) [copyright holder] \item Posit Software, PBC [copyright holder, funder] } } \keyword{internal} vroom/man/gen_tbl.Rd0000644000176200001440000000506614504663315014106 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/generator.R \name{gen_tbl} \alias{gen_tbl} \title{Generate a random tibble} \usage{ gen_tbl( rows, cols = NULL, col_types = NULL, locale = default_locale(), missing = 0 ) } \arguments{ \item{rows}{Number of rows to generate} \item{cols}{Number of columns to generate, if \code{NULL} this is derived from \code{col_types}.} \item{col_types}{One of \code{NULL}, a \code{\link[=cols]{cols()}} specification, or a string. If \code{NULL}, all column types will be imputed from \code{guess_max} rows on the input interspersed throughout the file. This is convenient (and fast), but not robust. If the imputation fails, you'll need to increase the \code{guess_max} or supply the correct types yourself. Column specifications created by \code{\link[=list]{list()}} or \code{\link[=cols]{cols()}} must contain one column specification for each column. If you only want to read a subset of the columns, use \code{\link[=cols_only]{cols_only()}}. Alternatively, you can use a compact string representation where each character represents one column: \itemize{ \item c = character \item i = integer \item n = number \item d = double \item l = logical \item f = factor \item D = date \item T = date time \item t = time \item ? = guess \item _ or - = skip By default, reading a file without a column specification will print a message showing what \code{readr} guessed they were. To remove this message, set \code{show_col_types = FALSE} or set \code{options(readr.show_col_types = FALSE)}. }} \item{locale}{The locale controls defaults that vary from place to place. The default locale is US-centric (like R), but you can use \code{\link[=locale]{locale()}} to create your own locale that controls things like the default time zone, encoding, decimal mark, big mark, and day/month names.} \item{missing}{The percentage (from 0 to 1) of missing data to use} } \description{ This is useful for benchmarking, but also for bug reports when you cannot share the real dataset. } \details{ There is also a family of functions to generate individual vectors of each type. } \examples{ # random 10 x 5 table with random column types rand_tbl <- gen_tbl(10, 5) rand_tbl # all double 25 x 4 table dbl_tbl <- gen_tbl(25, 4, col_types = "dddd") dbl_tbl # Use the dots in long form column types to change the random function and options types <- rep(times = 4, list(col_double(f = stats::runif, min = -10, max = 25))) types dbl_tbl2 <- gen_tbl(25, 4, col_types = types) dbl_tbl2 } \seealso{ \link{generators} to generate individual vectors. } vroom/man/guess_type.Rd0000644000176200001440000000240214132374245014650 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/col_types.R \name{guess_type} \alias{guess_type} \title{Guess the type of a vector} \usage{ guess_type( x, na = c("", "NA"), locale = default_locale(), guess_integer = FALSE ) } \arguments{ \item{x}{Character vector of values to parse.} \item{na}{Character vector of strings to interpret as missing values. Set this option to \code{character()} to indicate no missing values.} \item{locale}{The locale controls defaults that vary from place to place. The default locale is US-centric (like R), but you can use \code{\link[readr:locale]{locale()}} to create your own locale that controls things like the default time zone, encoding, decimal mark, big mark, and day/month names.} \item{guess_integer}{If \code{TRUE}, guess integer types for whole numbers, if \code{FALSE} guess numeric type for all numbers.} } \description{ Guess the type of a vector } \examples{ # Logical vectors guess_type(c("FALSE", "TRUE", "F", "T")) # Integers and doubles guess_type(c("1","2","3")) guess_type(c("1.6","2.6","3.4")) # Numbers containing grouping mark guess_type("1,234,566") # ISO 8601 date times guess_type(c("2010-10-10")) guess_type(c("2010-10-10 01:02:03")) guess_type(c("01:02:03 AM")) } vroom/DESCRIPTION0000644000176200001440000000512514533733452013136 0ustar liggesusersPackage: vroom Title: Read and Write Rectangular Text Data Quickly Version: 1.6.5 Authors@R: c( person("Jim", "Hester", role = "aut", comment = c(ORCID = "0000-0002-2739-7082")), person("Hadley", "Wickham", , "hadley@posit.co", role = "aut", comment = c(ORCID = "0000-0003-4757-117X")), person("Jennifer", "Bryan", , "jenny@posit.co", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-6983-2759")), person("Shelby", "Bearrows", role = "ctb"), person("https://github.com/mandreyel/", role = "cph", comment = "mio library"), person("Jukka", "JylΓ€nki", role = "cph", comment = "grisu3 implementation"), person("Mikkel", "JΓΈrgensen", role = "cph", comment = "grisu3 implementation"), person("Posit Software, PBC", role = c("cph", "fnd")) ) Description: The goal of 'vroom' is to read and write data (like 'csv', 'tsv' and 'fwf') quickly. When reading it uses a quick initial indexing step, then reads the values lazily , so only the data you actually use needs to be read. The writer formats the data in parallel and writes to disk asynchronously from formatting. License: MIT + file LICENSE URL: https://vroom.r-lib.org, https://github.com/tidyverse/vroom BugReports: https://github.com/tidyverse/vroom/issues Depends: R (>= 3.6) Imports: bit64, cli (>= 3.2.0), crayon, glue, hms, lifecycle (>= 1.0.3), methods, rlang (>= 0.4.2), stats, tibble (>= 2.0.0), tidyselect, tzdb (>= 0.1.1), vctrs (>= 0.2.0), withr Suggests: archive, bench (>= 1.1.0), covr, curl, dplyr, forcats, fs, ggplot2, knitr, patchwork, prettyunits, purrr, rmarkdown, rstudioapi, scales, spelling, testthat (>= 2.1.0), tidyr, utils, waldo, xml2 LinkingTo: cpp11 (>= 0.2.0), progress (>= 1.2.1), tzdb (>= 0.1.1) VignetteBuilder: knitr Config/Needs/website: nycflights13, tidyverse/tidytemplate Config/testthat/edition: 3 Config/testthat/parallel: false Copyright: file COPYRIGHTS Encoding: UTF-8 Language: en-US RoxygenNote: 7.2.3.9000 NeedsCompilation: yes Packaged: 2023-12-05 16:46:59 UTC; jenny Author: Jim Hester [aut] (), Hadley Wickham [aut] (), Jennifer Bryan [aut, cre] (), Shelby Bearrows [ctb], https://github.com/mandreyel/ [cph] (mio library), Jukka JylΓ€nki [cph] (grisu3 implementation), Mikkel JΓΈrgensen [cph] (grisu3 implementation), Posit Software, PBC [cph, fnd] Maintainer: Jennifer Bryan Repository: CRAN Date/Publication: 2023-12-05 23:50:02 UTC vroom/build/0000755000176200001440000000000014533652002012513 5ustar liggesusersvroom/build/vignette.rds0000644000176200001440000000035314533652002015053 0ustar liggesusers‹uM ‚@†Χ΄₯Aθμ/πG„Ψ‘KDDΧM7”\uIΊυΛ΅QgEƒ»3σΜΌ;/{΅!bθ™θκ\38vΗ‰ q}cYs*…wβRσ%ςœ·`> stream xΪ•VΛ’γ&έχWh»βΖ€ή©J“ι€:ΥIe?”%χ&n“š­Υ-ηŠL²ffχΎΕΎ3Γ—ζ+¦6έ |7W¬μχΊΏQΛiμΰ΅ώi§’p¬Ξ»ZΠ– j_-RƒόνηLJSo‘z@Ύθ²-mJyKνEΗ[θΤψήΞCšΦ­ r £ΫδqχR Λ4ΞΏω²b*‰\E³d\¦EΝ|μ0€†ίΜWΩ… =oΌK•nkή…ΝNΞ|έͺfkZ˜FΪ5}IΒ«DvTmx)½k’&Cg>„@ί}W₯}Ξ»’Ψjε#¨uς)• §Κ^Z?3ό ŸΛ•"θ*Ε}‘„τΊ±g] βν-ΣεΪΗͺ¨T_‘θDf·½ ).ΡΟΒtΜhEuŸχΕέJ> stream xΪνYݏΪFη―π#–βe?ύQ©}HtwIU₯υUͺ’ ωπβX·x-{ GϊξbΓa >ΣpyΪŒρΜΞΜo~3³‹‘“9ΠΉ½G“[Ÿ:ˆ|μ;ρΒAB}'π!`;qκ|cχŸψΧΙm@ ¦€@¬—ΩŠL[‘Γ΅"ΰ‡d' d™ΟkΧΓ ŽS9_-y‘xϊ“QΑ֝#_<#ƒΠρp`~lͺ–Iυ˜J³ρΊxcτ΅‡ˆk%j΅Js™”yχ±ί<η‰ΰυYΝΊδBδEvφ‘β΅R_ΥΔπ2ψΛΟΝw €ϊt^/O7ΥΩ'+•‹ϊ¬kΑq"RyVοi)π1p‡Ήρtn"h₯°Εν·ΌxΤ‘Εn„Η²vͺσΨζm^–† > ΩXd~Πu³¬dVρΊ>ΥFZΦ―ώMΞYDΞΕ‘ε’`œgWŠΏ]ε"εU_όΦΌ vρ?ΉͺzMΆς“οdρBœM>ržΦ“5¨sΕϋ¬F lg΄poζz ’g_TH sAHΙ\š__―j>1wŠ/K‘œΪ{©½Ϋ;Oxš«\}ž›–ž τZέ 7fΙ·Μ–I•ΑEΏ]D;»‹¦djώ‚ύFι(μrS {Λ"Τ {Zo˜ωξχ?ώž~Έ{ΩkΊ] ωM1—©Κ^Γ μ«ρ>ΎυΒ~+FΊ[ψI‘­’¬—}4ν­πΒ»!–FΎΛT>m2ήKͺ›†ΏŸ"―•©πΓ†ή Ϊ–|ΚώΊσu%ιΣΊθΖDt]@Έyˆšˆ·vZa„ΊEvŸίDz ΚŒΎ#g°ΦCd[›΅ζRhݐDcΰz B{ωφ…F§(ζκOέAΚά6_§w#瓇GkwέN' ΒΟω˜Νe‘ςBχψ‘όᨳ~ΤΫζ’κq6+’%Χ΅`[ ?ω–οΊ‡ς*Q²Θ3Β,δΧK€?ˆϊ:3υ \/bολwύ`XVϊ 3S›r»ησ-ςΧK@4¨ iŽνz#„°EύRκL 8¨τΉώAπ₯žz3lqΏόhP%|­€\κΕC» z΅LΰA…°ΝΔ,ͺβ₯νLί³Ϊ‚ξ§ ϊL–ΚŽ#fArp³ΉωWρ)Y–fπRί·Ό~ Θ;Ό^Θji^1PLl3ωΠΓ xΎX/Μι YΔ―0:£ Ψ.ςΒό‘CνΑφΠγKv-Ο/η¨%ώλ1˜\@υZUvΟ~eΎΣ ψΎΜ X‡ΤBκΣ ¨Ώ…~Χλν^}8ΘηDΗΧ›xτ+ή4Θ endstream endobj 137 0 obj << /Length 1260 /Filter /FlateDecode >> stream xڝW[oΫ6~χ―ϊ2y¨Y^DJzΨCΆ₯C‹`ΐVχ©- Y’²δι’,~‡Ι’¬ΔNQ€’Θ£Γο|ηjμν=μύ±ψu½xχ> ΌΕ‚ o½σƈΒ FœQoy_ό΄Μλε·υΗwοΕP6ΐ…‚‚&#Ε΄Θ;Υ·λΕΏ Kμ‘“VJQ /=,Ύ|Γ^‡=8Š#οшΌ€ ΦΉχiρW―oϊ4Π>„N0ρBΜ#ΡzΨŽ>’(ŽHgΓoΥ’…ΎLΉ\QŽυην‘°λϊ(Sυcš&* £uΦJ3Dΐ+¬4Φ6γΑcpfαύ.λ΄RΗώϊ))SF"oEbβψDΖWΜ1ό‘2ΐt’Biήf²^‚ μ'yn–·« ϋlξe·ql»Μ’&y Μaκο[YΧͺΨO„;rΝζΣ±ΏͺžΘer·$°ίζ ²°GV2p!gΌΓ­ά”Eώτ‚₯+#θ!(ζ|j±v·VaWΘ)άωSΩΪ…\ξwΜUͺšξ+»§₯ΰώ[·σ]†ƒ‘ΚJ֝Q#χ―΄οhŒ8,ΈΟu²—³NΑ @4ޱ€`M}ϋΕ>@lcόΦΡxΞΥa$ϊηη»»Žh@°’ Ε„Μx<“λΕ6yΉWi’_TE#χ²zNp ΉUϋK'α¬l·ΉΌ,—ή'U’6WΠ^Ώ¬±hΫkφώΈpλΰ•F_.$ΐ©·œ/Λ*“•ΜFΗοoξ>έΊs—›"yVδ₯PHu0€ξΚꐌcμΝ›«΅όˆ†ŠW‚Έ”Œ7•.Hϋφ ‹¦~1)/$#ά9ιN£: hN}ƒ½UP6*¨ŒŸ~ΉύG¦Mν*έ½2ϋ²}š©—„AΛΓbP/7?ΏΤ…°.f,‚ΐ±7ε%Ϋm%—”ϋκtŸ*φ΅HΠ$ˆΠL£¬QθκOIE_·Ί­Ρ―ΊΣP›ΒynR‘€ ;ΐΖφ“™™%Dχζ$ΰΗΘωΡήSξœ?ΰŠΌjοu˿Ϝ yˆγ^ΧCU–‡!Ο9U*βξ3-ΰξ8φ?쬍ΠY~Zšήΰ0ΩN€WΊΥ<θdU©L·Γ&…Ρ‡EјMHZ ύggΌςh_\Σn±―Έ‡ϋ΅=ΫB+²«¦΄ΟJξŒλ[§(„/³©ύ 60ΠelΠ[es:Λά}ΆλυZΊd‚vsγΔ?΄γΤ‡”Mον¦Q‘χΊΠ›6ΜΆmΖsΉiΎ€JΐPΜ Ε‰“#.ζA„XΨOG»ΆHυVχ°j,©€sJρŒΝ«‘.›ζvxΝ6§a„yε*Š@SWϊώ}u%Ή)4MΖ{ΜzH/ιΠ»¬s!=pΰp‰™,¬¬šcšΔ}’Έš7ρΎ&=*=nκ{Άκ…ͺ–ٝG¨‚v―ΉW*ΰ^u™ ΜΖ€χΌWΠΛzzυΔs5Ήk’π3Β|§LυΣ―:…ΟΆvηχš5½:&•ž!ZyHM™fk ‘·ΡώΆάΟ:­Pdv₯ ‹«ιp§fΨwΟθ‘‘(“|Vώ¦ι"ΦϋW5Ξ!ΙwέH»‘ϋXΙTB(₯]B ‚lš+…œό*€ΥiΊέ«:”gΦΣ‹f»;όμϋ€Χά endstream endobj 162 0 obj << /Length 1992 /Filter /FlateDecode >> stream xΪ­YϋΫ6ώ=…α¨ ¬R€^ΑŠm›νν!hŠΔA{Έ²D{…Θ’£G}gψ,›»k_ƒύA4Ε~σΰ7C-mgtφΛ³WΟ^ά„b–$τΓΩj3c”.ΒYRpΆΚgυΔβ«ΏΈ‰ς$ aΒAZ’Υe‹«žQ£Ϊ>_܈ΰ@lΙ£HΙ-ύ'΅t)?Ι#ω#\QH’Ψ·»ύtŸ6iΦΙf±<ρ>-XΰΙ¬«ρ· ^½Ρσέ½Τƒ΄,λ…xŸe'J‰?΅\Ω’Ε2Žbοχ{YiK'Y’ΖvoƒuιΤϋ§~όϊώυk-8Α Β+φ' θ€'—›£^Ϊ—Μ³+ΗK?δ$`ρlΙI‚ΰΨQG{qŸΔ‰έ+m`N™—mV£‘ΪΝηΤχ6M½Σ+$τUρ±—ϊυ'tNZφ²ΥοΐŸ·„1ρ©°{~q€‡1»]LxE₯χ6›γˆCϋξσ}‘ݏψϊWΤ>‘!‘Qh|kχ{™6:…+€J%=Ήλ[σΦaQ˜ˆgυn_ΚΞόXE΅%ζ˜ς kG‚p(V°ŽΔάŽŸ%pΜƒmΛτœcŽ tš-€tαSS.‹2]—iŠ!{ΘΩ—xΜ©jΚΊΪκω*Uιgκ—z~μλΞΞ©¬ν3“λu£y€8άΒΚ―‘=1ξZ!¬]„ϊH ¦σΪτU¦½Ν#oŠj± ˆΌφΎnΊΓYζS{ΈχJΪAž@_옾ʻ²ήΟ‡™sμjΑalfnζ%2˜γHΎο½Φ/4¬ΓŽE'†ͺƒ»κͺόκ y½zͺ5p5j“&ηζΥΐžͺ±ΰO6xψήQb)‰Sn_ΏsκΝh8!‰γB@“Σπ4vEΥΙ­lΏ `™!? ρ+lόnΗ—&΅ϊέZ6­“Q|i]l/‡e…FhεΜ›ί"4‡ΰΗΒ€ΝF“XΈK(Φ…ιΫΥ\#?φEcSΟpύ ΒΩK†<‘p…'"ͺΎY΅‡Cœn₯Σ#B$§.Ιλ~=­{C―Ÿ[ζΩΈΒχƒ^£S8χP,ΧΆ]Υ2Φ#”ΧΐAφ¨νkπν… Gψ™½ΪoΑ ‚‘Lh3 4ΐ?Έ(3(”υτ’\Ϋ¨FKQ·Ί…]MZφσ Ž  Œ|$΄ΝΊλΑŸώΥZڗƝΨ$NΙξ"όΨΗ ϊÚk.°σω™Ή*xBoώ3§poώ ϊ“*·W0Σ`Υ}θV?έqΧΝΝχΨ½Ά›τq8ͺΎ›Φ#¬ώ §6jΈ/N}c»™ΏαTΎ‰ `Ξ;Λr«ΌbχΝ<β'Φαcnϊ·<"Aό€»³εxΕͺψε·7οnΘΠ?ͺ‰˜―ΠS,Nl›>&‘q :ο"Ρ¬s>}θυX „WYΒϋWΏ³ΧςF¦ωΨέZswnϊηpGƏ%Ϋ¦ξχ r·K›Ί7 "t€fϋ‘؟o |ί₯ΐž@"Ξο†ι%’;뽃ΕG=f^Wί/Xμ™h»ύΠϊuχΕ`sΩοͺ‹Θg l՞o†Z>ځ‡θγ‡Χ›`Ωύ7€IΌιu};‰‚–]Λ“ ΗJ©=zέΧ½Y²N‡I{λΔ‹jίwξE™γM"‘qΏϊ’β…Δ}₯p~A#8Β^1"Έbπρ‹*:,ΕφD;|8ϊ°‘ΏΗ‹#·'£‚;l|/ΦβCπ˜Ήκ|§%ώSχFΟ²­υ¨Ηΰ@Ή m—bAΘΝJΌ« χ”ž‚„Vρ1ӜWz΄žΞηv>›Ξί͟°ϊM£ŸϊV„#sWΓ‘κŠΖδΎƒ«δΛ—Θ$k?]Š\γ!όC‹ζ0Bψo½]_vΕΎ΄!”±Ϋ“4pκυ8—ΰ> stream xΪνYKoγΘΎλWτqζfWυ;0˜έΕ$ $ΐΒ3’Œ}mŽWˆ$ =Ώ_‘²Dy$ZΡΠ{ΪƒΨT«Ίκ«'«(RFyeIXeE&ΰ£(yEV1γ›Sœ±•εˆ”MI1)ΗaΒ¬\τнςˆ}2 Vg­QΑ;e­ )+λTtFΩ¨bd°PΙ$εH%OΑ`αΌΚ1*'bV`ˆ­ςΝ†•·XAžd)+0ΦcM€X°3δ4πΡ©~DόBN*Š.8Α/ζ XP*`ό2‘b€ΐΖ»‰h•H±… ‰‘―q@…q?q„uRΔ  fKΖ xάΰ[†Ύ„ƒΩŠy’avχJeCˆPζ `΄Α­3’1°N‘Υ­ΈζpN7fKyBΰη"ΌBμ’Ψ ŽtYάh"ΌCr*)ΟNNΑjz<ν=pœμƒΓ)(βT!"XΨς„¬x•Εθ ΙNƒ1ΕCdΕκp\LAά—3\ΰ¦u «`μ€ΖŠl@½ $ΚBΟ%‘‰pΛψzρb ˆ#A;œ£‘pŽ’'>!fΩηδΐΩƒsJ²ΞΩ²Δ nΔΑπNΓLsΑˆ h%Ζΐ=:9ͺ^N!‚(r*¨˜'ͺx§ŠΏUο+Uό¬^]~\κΫjΎΦτZύπΓδΥ•ŸxeŒΉ•K%—Ή\Φ―ŸΎ|˜γfyW.Χε)¬δrEφΙoKΉάΙ₯ά~mIΛΓ’ο¦Mي^NεAZfSΉ4[Άr—ΫίΫߎθx_.ΛzΪTυA!χϋˆΫ»z_p΅έ;.‘Υ€Ή™Ÿ βQ…–χMkΦ#lΚuηζλͺ<Κωag€oάΣΚψ*—Υ€+ζΥνt~Pΐ|«νΦ&σN«ΊΊ™—‹ƒ–^mΝXν4ίb_ ΨχS]U‹C,?ν³l/‹!bΜιΌ©ΛΥΙμϊζάιίlI[τ«…Κm΅jΦ#KοSU۝ζY“ œςΛt±:μύg΅Ώμ§βκΉΩ‰ώXΥ‹is–δϋ‰ΉΨeμ³B?<_βηφξ9σΩ²<ΟΕ­έfϋΥθY"ιξk)ηH|’•χϋ‘΅> ΐΊ©Ο’½ήΕςs">Χ³ζΌύΌ%˜ν%§‰ϋ>_ό;ϋ:£.UρvΦ\ Š‹‹IρUό:½/'ΕOΥ²)—ΝZ%'„“β²\WυmΉ–^°έωgy7›ώX}QŒτΫΩk΄Š 'ςυ|j0P™:κ7ΛevΠ#·’u ·ΛυdCK<)~¬κ»²nΩ›λβοΕ/ΕOψ‚FηZπά6κ±Χν€ ŽV‹kΗ6zlέ»‡yΠ˜-WΌΉΈh%on›Y΅,ήΊόE>―~kšΥϊ―EΡy«ώΛ|v£«ϊώ5`‡ ]½v΄Cζ(hΞv?k~{ΈAK΅(šΩέΧOe½.;΄ηcDS©eFqN'΄Ζ6v‚Ϋ! ±˜­Χθ;zH{!ψοόcΆh™c΄Ϊ‘Q_>ΜηΧGH19t΄p΅t΄gΣΎEˆ+Ι‹·θ’Ρ#wω€F9=ζ† o]΄βHρk]έΎ+a6δΘΟoUρΎό< ά'ΙCdŸfΜ ί“>ΉK˜l»Εu‹ο–Π-±[:•r§ \έJ›•7«έ¬n³ϊΝ6kά¬i³nψ‘ΘάMδ¨6t‡"΅_CΡ› ©AxΜΛθm΅Α gC֘\AχF=­+[ΫΆˆŽ‹ΑEDέaπΨorƒ ΦiۃଦXLΪbNέb°FcŽ<Ž!Ό“΄5½p@MΙΗΗΗΐHMŠ=_§Ι„Ciye•XσΞœ±ΛΡ@f|HDβCL°Ξ@4‰Ψ/Œg+ βρ1 ΉWΨ3ցh 0>d"χ Ψ¬ΝPq 8>Šš{Ł9h3T^‘{ŁeυCαFΗ@HEΣ+ŒŽΓ Ώ:R±W(Em†ͺŸ$™z’Σf¨:°2‘{ՁΠΚ‘κΐn| HEΜ ; h%ςPux HEΪE$‘“ΘCՁχ :hyΙjgΎGϋΨn;c5ΐm·{-ΆΌ'ψΕ)y§ΏiΔ·MωΉΝ·ί4ίΦWσ-ŒΨτJs›<: Ψ AγΠpDωƒ…ΠϋραδΩΎQxκ3NχYŸv;"1#Lό ‡du αOβ?‰HβHΪaf=˜ΠPΉΣhΩc ΚGί%μΧ°ƒ/@$σζΡ ]ΰo ]0ίWθόfΪχyΜ‚GY¦Zωcœ•Ώ*Τ‘`#ζ…p΄βu%]½Ίz=ήΣ’Ρ$ιβ9ɜ1Πx$XΖ;{ΛΊ©WΝlQ>-ΐN/ΐ}Ϊm ώφ‘yŒΨ£ωӈm=βί[‘Δ endstream endobj 172 0 obj << /Length 1149 /Filter /FlateDecode >> stream xΪ΅VێΫ6}χWϋ$k.)ŠΊνΓ6MА’Nϋ†,QΆΊΊ8$νέύϋ/²$G»ΩΝƒ‘αeΘ™3‡gŒ½‡½ίΏ7oβΠKQ‘·.=‚1’aδΕFŒήΊπ>ϊyWΛMή΅o%_~^Ώ½yB‘8 ΰH³ι- μξx½^|Y0±G†γŠ0xδΝβγgμ°ψΦƒ₯4ρξΝΦΖ AqHΑ½ΏžΟ»όšB6΁`…Δ‹1CQš>‘Γω@ žx(MHŸΜ뇬©ZΎ\ ϋjο 8νΨ΄Φ–žWŸ0ςLU]+νlΩ kdφSdΚY₯Xζgbž(₯ˆ}8„ˆΞ2…d6—ίΈΜEuΠQN˜β˜x+’"Δ3~ ÏΜΠ!N¦΄PewK‚}ΈPJ5”4;ΡmεΉ²³Y[ΨΙώiη+εΌ n+“‚YΪ>ΪΙ•ͺڝ4u²ϋK}yv¬U°)ν­\œ+ΐ2ν!R‡ΎΦέEΝ›NͺΎ‚όΛ‘·n4Έ˜π΅Ρ΅υ£΅κJΪ°Φ8‚άWj?eHU.ζ—\LŽFαIe0‘‚7Ak0Ÿ© ΣΎ\³οA‰,Wς"ΓςXΧίδwAΧL’WΥv[χ―CπLq‡ΕΦ!s…X2ζ»„&l]1lb\ Jcχd?ΘlΗg‰ϊRβ&Oχ‘GJ# R”’)#$'aκ“Η!ή M―έ±’ΙgCύFˆ’4}P(Y^Χ}ΉΖ’’εΔRΟ=§1…G%Uo&½44MRό{™ΒΓ¬ό ɘDz)·g:mμ‹BDΟα› β{¨Ή|9 ΐ7‚ˆϊPhbΆEΉ–aη•ύžDΧ5ΊΨΖΨp{•žΈjTž ‰ryΊD“ZKΚ2rz`Ož’|Ϊ‘§(yJUeο>ί(‘(NβΫ:ƒ4Baΰ4xΖ7-0L~Gί|%–4φ΅Έ’»vh§•¨ )O•5ά3›}0FΡΣ F’—6Ηy¦;ͺώ³ηZ!§pΗJ> tbςμ€ψέΡNv₯κ7·άθ$Xζ±’ΠΏk;έξνμ~<ΈηόΘeŠu°ςν–­eMΧͺ½΄ϋ2αφ ~\‚bΑΥ=KG-03mr² sB λ}εY%¬eB€oylσσΈΎmΨnlg.GωΌZ·°CΫ/Ξ{ϋΓΝν΅έΣΉYaΉK'žσκΫ³νˆωR™(μHwa4—λYKΗϋmΧΆVεzdΑE5\Z\6Α?^}°†~ΐ3 ›°%@χ8φJδO77²RUωquΡΔNμf8‘) α±9o£ζΆS• -άψMž$eˆΠσ₯€kwΥΜω ΈŒœϋΛ!Λο ρΞκ3™οΗτιAh4Œΐέk«¬`m²­΅9OΉExƒuΉmX͑̚™¬±ΎΊ}wεv\½χ•܏ώ Αmκ¬έigύ=Bξ/σγΏh_AjχιΑiͺ endstream endobj 187 0 obj << /Length 1043 /Filter /FlateDecode >> stream xΪ΅V[oβ8~ηWDh@ΪΈΆ'€>tfh΅+:Ϋ¨΄μ™Δ@4Ή°Ή0—_ΏΗΨaβ4PΪΥJ”η;ΗίωΞΕΕΦΖΒΦέΰΝbpuλΉV€zΦbmŒ‘γz–οaΔj-"λΣΘ^όqu뷁Žups€lD& ^εE)±¬οNΛΘn¬lκΛMe{SŒ mκTd•iή}^έΊ¬MvbΩd‚Ό‰ς“ζΩ―c›2,WKΎκ2iΣχ=LhΓώΆNeΙ³H/V«BŒ)νc^‰θθ·ΪͺeΖSQ’Ξ;[Ÿa†ͺcψwΝV Η―B|9ž`p΅y^ρTVΠΈ‰f^gν±Ρ B"ŠO—»τς8ήK2κΤΊlH―σB-nξ;1>άΏ\δ„g›šoΔε€nΤaoή>ΘΉΎΪHς'’©* υΊή©|­Ωͺν㹝4©·*ΒF›ψX₯mκ,#$κxˆΦPŠlω8* gΆ r`Cγ~J˜Š"Ά%^Ά™ƒ&>τ!(`L!Cž)δ4Ϋ$qΉm*Cˆ6^€(9ΆyuΆ<”ΣR[ώ†?Γ. ˆ0φ„W0Ow‰¨΄Tΐ’κ*Jρh/k›Η _%’RΛfΞΚ<δ0R?_;ύΖεAηNJQ[π4γΕ‡ρβφ/c‡δ ›ψΑ…M0Θœ†ɟΒ'§αλβLƒ¬°EŽcš± Β±Βtπι3Ά"x Ž‘L¬―hjΉzίu`XσΑ‡“ΑξsΌ=Ο%s "ΎϋΜ χ}Γ”B{‘&ϋwHV?o2gQΌ£šλ™Ά!ψ”χ@iB΅ΥΥχ]37Κz·Λ‹γΔ[ι1·/Ζ.εyzF/ ιυΩKτRw€ΣsGΊA€ά¦Πή‰2,β]νέ[kέ{ΦΌΊξ”ͺ-qΤXo $ε;ˆτ_βt[G²iwΝcٝž―»‘‘X–α–<¬D!+ZŽ>†a”Ζ™Zό¦¬Ωηߌ}ΪΌΨσ€–Ρ΅ή…!Μp]jΤlΊXL?Ξυ/|ΘξΡΏBνΦ£ ΘO–Q^ÐiS\‡•―Κλλ"Λ‹τ"‡YΜ˜_ε°ε1Ξ*±9/#™Πέσpgβν@CwEΎ2°οg³‹B\sYŽ’Ο «ΐ1oΰΩLΊΦn"φ"){Ξlc=…Ν‹H²Ž[ΰΫ›Ω|ΪλD_φxο¨wTιΈ l¦*₯Η'h ξžψΞ₯«ŠSq.WψDͺΆ)”|I[ψŸΜŒ„΄ z-Ϋ ΖœOΥΉ€ζaΰœcΘKτNC†cb>Γ….wΣ‚φX<Ο¨ΡνšuΓύαΟωο…U?ύžDχcΊ ΟΊ¨~†‹·ΓWΥ“Y 7ΪΏš_L endstream endobj 206 0 obj << /Length 1038 /Filter /FlateDecode >> stream xΪ΅WMΫ6½ϋWθEb.©ožŠΙ(‚mδ%Q}Έ”΄»Ω_Ÿ‘HΙ–lol…2©αγγpζΝ[Ή…­w«?Ά«»ϋΠ³(’XΫΜ"#Χ ¬0ΐΘwk›ZŸνœΧ_»Έ\Ωώywš{8@aΰΨ`*“6θwχž`»q£h0ή8‘šάc—M.Vώ‹}\ΏZo|Œm„ ‰Β¬γ"JΘ~EΝ*>˜FΐΛ=ά‹D°ˆ’ υ’ΧrM°χ―»vΖrω\°V@„o_Υ‹#ΞάˆF“7Ά_oΫ­x6šL?»ρΥÚψ6OΊFš~Β!ΉdΡ‘f» Ž\½i%na«DΥWz Y›ωlβu‚πΕΤΜUWμιjμι€fΌφΐʞ·7°γjΟ§])э,ίΦp³ν[Žv€Αε€^§©θDS³RοΘ ζ£)懩k[žΞ‰‰ΊγrZd`Œ›ϋ:Q£φjŸf7Έξ7mͺωΞΏβFCe'›ψr6l½qέh–šΉ«ζ‹E)Ίοzβ‘‹ΌPVV™6μ&ξdUλα₯Κ€—ά\Θ`•-^Οwμ˜+#ƒ„ κ!jY΅+yz½3JώΐΛkcΠe¬«™’«`Χ”r˜S₯žpQ εvl‘i…žλ*¦ˆψtά䯏οߟ¨/A(°1£γάΦL¦£z¦#γC80όPdIunύχŸcΙw}δ8ΣωλΝXnΞyΞ£ΐΧWΎ|7.u₯ή<α™r Ό/ΎEΣ—ιB 3Uμ˜ "“χρX όκχ«£¨ξ«―7E’ΪΗ\ΞK➍//ψΧͺ£qo&Yb$ςb֟ Ž=WŸυ¬¨FΉ]a΄ίKw.ωΠLιό2tΌΪ5REΑ³s *H~Ί³Š‹ŒŽΠVo·«VdPZ2}Yκ£0 ­€Z}ώ‚­^$rid=¦•εA =%£₯υaυχΩ>_ζ Δ6 #δPzτωra8[Z‘1ή©μVΚ3e83ύΙ0ΉοQ:Γν½pΨ @φ9μp¨Y.N`ƒ|hŠox›H±€S鸐‰εgΜΆF°Ζ'hCΦ— yˆy“ί €Ύ2SkŒM³ΛΚΆY.ήηΖ_Bql> nšΉοM―$¬ƒΥCύž2ΙGυTqάς<,ψώ© ―› endstream endobj 220 0 obj << /Length 1714 /Filter /FlateDecode >> stream xΪ₯XmΫ6 ώ~Ώ"ίζΞ²-ΫP νΪ:: M?΅ΓAη(‰QΏ~ι-ϋυ#EΩ±ηšΛ–eκω’(Ή³ν̝ύqσvus{³%[†^8[mfάu™„³(t™π½Ωj=ϋκΔσΏWήήECA?ζ,`΄ΘVΝc†‚7Ah0bΡ Yxvΐ/΅ά*kΨΉην] †šΖ³_²(΄ψζ Ρ΄ œ-aJ-R•Oυ+ύΡ΅?$eVΟΒuΧτψψεώώœδCsΨ«Ÿ‰₯²2‘™²dΧj#Ϋ¬y O¨.όωδdyZΧi±΅†“q‹ λ†όd³Ž4·­ͺλ‡\ώ3O1γa―Υq #zYΠ”ΝNQGZΐάDζz΄’QΠ\Υ:άC†Ve»έ•0 ή}‚Βθ‡L1ΐ "gEkΊ»gsY:E ɍƣΧeaΐ7˜ϊdέPΚ….`iξΉN7YQ6$X•Τ_7fBrβ8C“r^Lλ ΑχΘL”f5Ξβ{Ξ‘lΑ.τ ΚJΫ -e`DZ$•’΅ΑCΰ /Εΐ½ˆ/ςRΔx'§@λvΏΟ4]―yRV•J:ω‚y"IΑ…±f€§² λΙΔωο]@ΊΡxε`xΈ0XH‘"‡,f?bώ1½f©vα%«y)†4Ό${@.Ό"{,EŸsˆ2b³‘iA$”˜©WσEΔ#‘K›χ‡ΰ p6Ϊ™ΠPsξΘdG/Εh‚κgQ£,΄Χ‘υ€ΓR/ΐ§Πƒ^πʚZ’uϋX+#T4 ˜~ιBͺ­Υ 8 Rε€βTτžEΐ’cn7©7‚‚ς’’o2H/ΠwάU XυΈ,q€(‘™ΫΦΚ’")σ½L ”κ;φ•ͺ!Ν œσ΄S•"QuτΝNV€ ͺ)Βz³ΐΚnγ$–΅X€βRο¨θωζyΒ–ϊͺ Ε„p^Έ‘GΡΡ >‡•ZX˜³‘Κν•h……VPv ΠΪZ—νc¦Κ, ¬ά¦P›_…΄±hHšς:ϋήΩφKβKaV§0Tί¦ωu€xF<σ€+ήΓθΝμ*uμ²laiWOχzαΕ,cϋtφφ€ ΣΓ#™vΛ°Θβ”υJΗΟάΤΤυ”6T‘ΰI}}=ν“ԍr¦2ƒΟ{H#φΰ\Υ‹©ό ΗppzG•©O;ΩLΤ±Η8ο+X4€š:‰ΔpŽγV!‘χeΐΕD«ϋ`fŒϋJΔcˆ£X¦ξJε%JQNνb-ζΗ­AΕi‹{ G#»Τ”ξ°#p·ίuΑ⧇Α{ΰΥ»7χŸίOUΜnΜΒΐ>+ΞΜζE,πϋ ¨άλB·M›žž,œq}ϊ˜€uΒƒΡ‰'Ž/;ƒqO0aηxspρρk₯χκΘοﰍEH₯/:πmσΊ oΩΠ·h²:Π›>1θΦ>“‰Σ₯CΧ…uGg`Ÿͺ‘ΦSqώεσ"]°J<7P՞₯ίFQΟ'S³c»―ΩρEοαΨΠ{86^X…p&’ώδc_Λn„dV—ΦmΉ7OuE?0`Σ‰9iMή₯™ΚmM•pΊn₯)z(ƒcRۈ¦‚E0H€l χ«›Ό*‹^ endstream endobj 230 0 obj << /Length 1265 /Filter /FlateDecode >> stream xΪ₯WYoγ6~χ―²/63"u/Ϊ‡΄M]6qžE@K΄-¬WGŽώϊΞπPD―μ€Ϋ ކΓoNŽ]gλΈΞ§Ω/«ΩΕuδ; IB:«C]—x~θD‘K9«ΜΉŸo{ΡΆέΛ^,Ύ>_\‡γΎ’(d OΚ&(2sυ λd—FxΙ"dͺ#·B,–,pη—E[[Η_Wψ#±ΣΰV‘7RΉ=΅. ΓX£•hxW7­₯ΣΉ—wu΅ΊSKiy•ε Μσ¬η…β=.h0)*"Ϊ~Λ&ΌΧc„E‰Ίχκ™—ϋB΄G,±Χ‹k?pbP¦hhBˆ*]Λΐuη ―²ΊT4uΥϊ¬–@-_B‘Oy·ϋώXZ}Yiaˆ€„–Τ%I ‘ぇn](±Ÿ–j!σ/7p©{―…=©Bc”$4Ά¬α…Ύ «ϋ5 ,³ό±YΧdλβ$Ph ώω`ƒ²[nVΛYΎΩb.9nΚ]«αw;alκτΉφvQW[Emκζh4YλΟ;^m5£Ήι«΄Λk­>(’ή#s2΄£[Œ»±GWuyyΰγ³"o;”@ί©XαncΙΆοڏ›ΎΚ7ϊXi,Χ"Λ!kJώl}a2Μ v<†}<6μf€δΌ‘L–ςΥjφχŒΚD‡>ιΗΠ'#ΟIΛΩύWΧΙΰ#h!^;OR΄t|JIδ{@ΞνμΛΡV ›1tƒQG,‘O?d$€S½xΤG’Θ:ΙHSΣ”?α!ΥΝd:IUHͺή¨•λ~'{έ “—€MΕdišΥ5eΤΨφ›hΣ&—9<Ω<5όθG¬Ίϊ©ž³πε[ρώ~~βkτ0EeΚ‘°7Bs %υ|>$#rCΕ­Έ•Έ)j8;ΣΙ}φη₯lcφI­―¨Snϊ¬>‰ ο‹ξA}BUGO+ΜyΥ‰­h,%Χ—ά^Ι#Λ +LQ½υ^^6 •Ϋ—’κN?šΗœ«£τ|8zŒ“% ‘ †)εΧoxΪ‘=Ι`εΚ#ΞΌθ…I+=4μyΣ rp…m©οk‰kζ(ˆΰ τβΘBˆ{DˆkΫ5y΅mqJxΘΔX5ϋFtjΛ[΅–yΫζψ)…Ζ4²XFΜ…)L‹w»\PΟ‰ζΦ*––•šΜ&Ÿ&ΖΕ ‚ͺ Œ•Ζ‘8w₯ΓViξ؎ΕiΗSΟ' κΝςΌ€w{%ϋGδ ₯ƒtZW]S­ΪAaήbIΗρN}SX›΅Ϋ4ψl#΅/xͺ•IΛ BΰEρλ­#έίΓΘΫ©*½»]¦P@Mž‚.ΟΓκs‹όͺŠs£*\λDΌοΤζ₯ξ‘ςJ}k|5=wΫePx‡Ϊε”Λ}P¨έQ*H‰ ‘5ŠͺΡ£Ožρι²ΡΔ—Žε–Χ˜y^ˆ©­Šθ±s"υ` Ϋr½<γ‘’ώ©+ώ βΉ¨:]z›‰4/ρχŠ•Όω¦ωλ|;ζM4u9Ίazgόε’°;]Ό|+Ρ§:Œέ«ίξΏo&Š~•Πh(ˆ›»«‰j†ί* BV3–(ŠD$ΨΉδˁ =€!Ή©5οiWΛά²κΛ΅hZTδωσ| `r`CΪ oΠ2ψIγΔH ΑBV‰σŠF¦c!ρ`΄Hώd‘qQΙδ;γΏ±ΞΦ endstream endobj 242 0 obj << /Length 1643 /Filter /FlateDecode >> stream xΪ•WmoΫF ώξ_!x&φυNι€ϋ­Ι°‘Ϋ°Ζω0tEp–/ΆVYςτ’Φύυ#'ΏUI\΄‰(Šδ‘<ς!Γ½•Η½_F?ΝG―nβΘKY±7πη,ŒbOŜΙ0πζKο½/ψδΓό·W7κX2LKev¬LQeΊ0(7βΞ:˜f½Ζ,PΘ$½λΟz³-Ls’ωΤσΥM$½”Αg°,J"o&S2%{ίMf’sm΅ΚΑ!zy4Y[Υφ03άpβ«Ξ4Ν}»ΫšΈδώί\½½½OIw<wwxΉ9°Η +πgΘ¬σβΧ²5+'Ϋ7].‰XVέΒ…|‰?b<π>{δ &‹QΧώYt‰Λt›Εήγ¬*[—yΉ’χU]uΫύΫFΧ_φd,¦AMe_ΚΦνŸD$1.SΊ5D΅ωζςŒ\π™ύ?rr‰*ΛΕkΌζߞϋ^‘Μ\ύ~b`t=ύ7`‡{bίtRE Ωfτώχ–π NbašxŸ¬δΖ‹„`*Β¦+ΌΫΡ_O6Šmlθ•£ΖγHxR*&yτLΣ*u’°4}—\OBε{+δΞ@σLH‘bOΎ%&₯p”$4zΘΉςΖ4YoΫΌ*±γΨΈ²p"&υ+ ϊ ϊΥβ_ β·uaYv[+ΣΫΆ«¬. 'Έ6D,ΝΓD@“wEΫ+uΫ«–ΔzœΧυŽΨ Σ~2Ζ}Κͺ΄§²Ι,βΚ{WζW‰6¦νšΟϋjP’BΛΡb(όͺΜΜɈ y€Bgθμ’!Wwm΅Ρ-Bg±£o[έ4fΩ[tΚΖρɝc\g[@(E·)βnuέ @T!όy/s–.\λIX= kaSrιgλͺιi<ŸΰuΆ&ς=° sf0Jϊw·Δ½.WEή¬m#ZtγŒ`ƒΖ—žΉ­š&¬vFn‘IxZAmν'αcΎ*MΫψΉΩCίYU¦ŠΕ0ΑœζCUΫΐaRLκ4ώMU»swǜΑ“!‹uΔΠPΕο½2—ׁ†‘ΗXAYQ0αΤ—S±»ωΟΓ‚¦Μͺε~zΔofɘξ} ©Η#'Y*DΨφΞύαZŽJlΈ"B‡|56ήͺۘ²}~νz‘"Žξϋlν;.qΓ φd­k΅¦†JŽ”_›mm —[8Ž@Ε~υ@—zG B/ΰl`YiΟ΅ ’ϊΧ9@OM²B PθrΥaν[6dήρu3T' @/ތHΉŽυπQŸ5'όc‰W―Όž|χU”b‰T}Τ‡Lέ£[Η·υTΪSβCΪ¬35=΅sͺZHg5NfηυbwΉ·©d‘ ΎφφGeΚ”Š{]v&zrΜ,L#¨ΓR1.ΤW00%|;nσ—+k†+ŠύV°++χ» U‘3OΡ<οτώXSΔ¬8§OγέΎέm•Ζ2φ;š·RΡdN^.a$S Κ•8°ΔέšΖ½LϊPcg@ΑΔμʏΔ*4τβ―lΓΐ§’~l₯ …όe³ΑΒγΆ~KΡV#±Aμ²Τbh>¦‚ΙDφ2ƒŠEj_~4Ο±‡3•žΩ™‰θxM}ώB8²33μ1όb;)±0Š@’'Σ–›Ό!V€«DjQ!r!…S ·Ίνœ »«<8sx½–°uŠΤ—ͺ4½αςΌ>§ηΠΣ}+ϋη2ΌΔΗ|Ωα5†pSxΨαŠ&„εΖMH]ADΥ΅δ‘$P΄»–€? λΚ"wνVrœdδ 叀Επmώ€lή ŽV<00DΈα aΒJ,—% \–g7ο0aSΕmye՞ƒωRδ«΅c7΄~bRHυ`cs;w;#00UξaΒ£ŠβT vγέΆ_¦‘½©š–>€]A\JΈ²`₯1U*p©ž^4³8­Yς 8΄;k£?bRι\h‡|ΤΫm]}†vnMί΄’vphι,ΉC€6Sšz£pmΉξ±ž°ΐ;Λ•›Λ' ‹³Α½ώ‚όJ΄h endstream endobj 250 0 obj << /Length 1523 /Filter /FlateDecode >> stream xڝkoΫ6π»…ΰ}˜Δ*E½ μCΦ&]‡bΓbgΓΠ#Ρ‰V‰r))ώϊy€,9NΪAΐΣρxο—‰sνηΝβηΝβΕy9ΉŸ'4q6[' Δ£ΔIβΗ!u6₯σήέΙφͺζMη}άόϊβ<™G$πΣ ˜iΒ P$ bΨ;«$~šgΊ¦κ R]v9EρTrξyn9-—G„‘Ÿ€%ι[o¦Τ€ϊ „nηξCΧσ?KΎυⲑξ a՘'_ZΑOΌUDBχΚ£Δ ΕχhμήιgrdΟμϋ›ͺCθͺk"ΪύSΠL§~–:« πσ8F%O–CQ7½y!ΫCΈΣ*wΎ~Nΰe†ηψςά b·•АΊL‘[΄Νζ=Gd]u=BνΟpΆ’Bc³Β+›»Δvœ›ˆΑŸŸ9RΑ‹7‹Γe‰O‚ΘϊχΊkΕo¬αέψŸ vqξ§ibίϊΰ« uO.«‚ «8ΡxF1$Ξά}&aβ.ΟΦ›₯SWGP WL°²b/M ―ŒFΘrIέ²εζ1€¦²ΰVy›#ΡλυΖK#­0άΏ5t•©…4˜HSβ'tΜOΕτH'>…B04g b.…±ό›eαάθuΟDΙd‰9²ρΒΨ£΄¨σ£ΗI +ή3„Uq¨S‡–—λFΪςdzΕE/Y½D ο ˆ³ X₯‰Ÿg4Lυ©5δ’hΛJ\?}σΞXύzZZaα‡…S™y„2‘  ωΠmχLε~7ωΤ©mΘ€h€³ΤΟ!P!ςͺΫ‚Υ|šΒΊψ„ ?€XnεrJE!PAŠT?x«˜wέύ ‚6»ρ Ή|…?οTpyΙ‹ͺaυ? “ŸσΛ“Q…ΕΩfρyθ’ ΖΦSβ§AβΝβύGβ”p "ύΪπ&mœ2,B€kg½ψγIΧιω0ο xNL(Τsώx>Lά>+Ž  Ζ±8.x/+κμΦ$θŽI•Κ$DH/‚¦·η|ΤΪ(‡AŠόk΅U³™Ε‘o‡ήkή²ΪυU+ަΩa]«!Ύ ŒOneΫΒό‘43C‚ΤmEύ€8,€Κΰu΅| λP 1‘΅AU[Δι9§ϋΛLχAM$ΌHu0_¬Js)fLq=ΩΊNY Bς’ձэP2ˆκ80%iυ`ΤγΡάZ6\ξ³[5°σ“ζΊCΓ!†ζJ=R°U!H­X‘GzVγ՘SϊΛ4m€Ϊ¦ΊΎ18΄GτΘLο€ύ$PW#ύ Œˆ£ΤύΫƒ4v-#]‘κ‘Ĝ-›©]‚j3σLήJ֘ ΖΑK”+lΧέ[‘Ύξ*Ր€2Έν •cG[ŸJ*ΥφΒάnNμš{Σ{œ κ9χ³ξβΏƒαί²zΰ¦ΥμΛÌδότέϊΜΆ ―i|ͺ‹ϊzh`Ξ<ߟΧψώΫ§Λ):V ]ΌΡVBQ>ή=A‡–.έ—/υ1 RΨhΒlΏΡ<77) }’†fp¦ο+Ο~³Yo·G‡9fο7—gΗφ il‰ΤδΟ3χίA/‹yjz  &™ _Ϋv%Rt­Aα2ŒH]Τ 53HΑK¨,hžΗ• `‚gΩΈ7κό9’)όDΘΡ\εψQΉΙ―ƒ1ͺŽ{SL} N΅LΫy"ؚS}Svfχœ™εΚ.Τ΅yΘ„Ή`Ί?*gt‚faπ•υΕVş^k<ΤΨχL–ης{ίaτΎ«•³]P“Άf?δ¬Έ™Ωt`2$€΄^©‡Ft/ηΏμΰ§ΔΦtŸ£ΧJγ9Ρ{έ/¬Iμž3δΊΒγbͺά(E"lΗvMf‚V0XlΛ±έΞ,9±AΩΒDΕΈOŒt­¬+ΑŒ&•Ψ ύ\o2―Σ§ ΣλΛύ޽UΕXυΧ¨§έβcΤvGWι¦U>―+ϊΥO―zϋ#SΩ¬ΖώaγΊ±Ά±>ήMΗά<4GΣΦΊ-ε% endstream endobj 260 0 obj << /Length 1441 /Filter /FlateDecode >> stream xΪ­XKΫ6ΎϋW>ΙΐšKŠΤ+E)Ί[$X$hβœ’ΐΰZ΄-D―Ht6 ϊγ;|HΆ΄τΖ[τDŠ3Ξ‹ί …½‡½Ώf¬fΧ·σR”FAδ­ΆΑQyq„QHo•y},>―^_ίΖ§œ4 QL ΘΡ<ίΪΊ.Ϋ [α7«ΩΧ™’cε·)g?c/Ϊk(iβ=hΞc„ ˜Q˜ήϋΩίƒΈι¨ugα©ξ£ˆ/Ζ!JΙ9΅ΐŒx΄)@i2˜ρNπl± Bμs3d’ΘΛ\ »ϊ γ fžW²ρΚόώˆκ4§ρQJQLΘs¬7’ŽEA$ŒΪŠnΣζΜλjdμΡθq”oIRΔ‚ψ΅z’ιR$(JRsΜ‡Žο„SΏΗϊŽ#Ϋλ›œDυ±¦ω(0”†‘aΨζ…ΈD<&hΓΛcw3ΌωpwηdέΤΕΊβ₯θFμ«wnΞ²ΛΝ„ύ±τθΘή‰BlδeΪδΩe|έ—Όqb'[΅.ωχί«jλζδ#Άrϋ|~eΎζo^Ξa8w~=ΤRŒ6Ο?ΚΤf§CΚRTcoLYOl\‹²‘?Φmύpa|d›—λK™α:ρF¬³ϊ Rϋ9[ξωζKWπn?Ϊuϋςξ½{[QoψδˆLlω‘kCRώ~μbλ΄έAtέ£PD8Οβ…lEs™9†w]7²›(Χ΄bΓ܊υYs(ΧrίŒ·λ›ΫSž”Π΄υλΫ{“ϋ»}ύ°~ζ₯DκΎ―Αnž·γDΖμΰUζnI§I4Ήΐw=ΖRXŒ₯Π₯3+e­pB­:±=XΎ­F˜…UίyΩc€Mι‘IRt²S.C΅H°_mΠ‘ΠΧ»*ο΄j£=±}—1 ή•Α₯‡–7φ…°ήΝΑbΪ™Μ'˜ jœ χ«ΎΔςφ£I1I…Κ // °ŠϋjήΧΠ·•²‚ΩΪcY·=χε‘3”ƒv™šιΪ £}P™•Z’Θ,³ς€ρπ€² …€ΘPœϊ―Ά? έTG㺍bxΈ;& GeDk―LX;ιgTr)…tƒΩ?·-Ό₯Η턍g½uF“!LΜΫαjnDΏδπe‡ώσŸaφb˜ύ6?› ±0½,hD€γd8ΎφžΫT9‚‘δmͺΕώOώ˜Π/WˆcύΐrιΤ<>ΝOΪυyڢؘΩ’8”6΅³3½YΝώ3O‰M endstream endobj 169 0 obj << /Type /ObjStm /N 100 /First 869 /Length 1490 /Filter /FlateDecode >> stream xΪνXΙn[7έΏ―ΰ²ΩPδHF€ H[ CΰdΡΦρBuΤ ¨*Ά $ίsŸUΗ±₯„Š%  HxΙΛΛs2[ )δ’CΛ!ηr#T9[(*Z‹&ΓW―[μυΔΌ^ƒ&riAυš‚6τη`βU|+ͺ•Ba―s(Ελ*ωπͺyέBC9ώ©δ!׊9²’bešΫ؍όH\XŒ§ΖWR3c€WΈz‹„,δ-Kρ,’ΉΉΜ[ΠmΙ1˜Λ»NXΑΠLiάΈ·Έ>\”κ’°·¨/ε€±‚‘ΨaށZi…)¨ϊ< ͺd_ _hŠr $δAΑΌ]Zό 4MέU½ ˜Φh 옱e΄˜†/“ „#"£0) 4 +ϋ<…κ--°K”ΐu…BhΓ%Ή`8ρ1ωω—;Θ(”Υ$ϋhV€Œ,Χ¬AΔΧΔ‘‹J°[ΠJ8©˜#ϋ<Ν'††4ee((F• 9ΉfqΝ( 8x¨(¨/LXN©@Ι~±λ P?bbgž:90άΐ΅ψ(%°=μN]Β)ku΅+°Ν5­Xͺi‚XςC•Š‚8¦˘"“σj0jΧ`μ' 0φ“6ߝ`–‰k'h}8:&Ο–‹U8: “gή7έ1Κ0-0{]Α–[Z—1ζτςΓ‡ΓδωωςμΕlNΒδωΣgaςrφnNtωΤ/ί=CΗτΝl˜<Α2³Εκœ%>LŽgΛΛσ³ΩΕhicӏ³Χo§—οΒ‰7`ο‘`Ur¬ν«MΟ1…;G―yNάKŒύςλo‘΅Θ8šR8J-aq9ŸŸnΓβd―ΐΔΡυ΅°U‰ΰa'ΈPλϊΐ£vr§J OXŠDκUμ/–Ϊ)gŽ­wζΐΰI”$‡N°tnξ>&½5σ ϋΘͺnšΫM CτΚάφfnΠΰmsCΐΈ—ΉUλ7·›Ψk ‰JΪ6nΡCaX΅FjΐRŠNυπœsd„ž.°˜EΟ*·˜ρΦ@ωΑZ·šτš1%»mƞ§ν`Ζ+Fψ£Εb‰OΖ4ΡEΊ΅ςΨ?L^\ώΎλ?Ό]ό9L/Ο_ΟΞΗιδ»Ιχ“''y¬ΈLg،XŠžΓŒΫ“%ŠŒ΄KY£ˆυhTή‹0ωvωr ϋoŽXΔ7³E<ΎœΗΕτ―ΩΧΞ‡£ρ|ΆΧΟ|„½v-Ε&`ΎLsΉ;b“,Β͎δ& χ@=Jw¨—λ=©—Η$pΌP\}eύΥυΧφIM$ΠdW•ΘΘ†‘άDD55ˆkΆ•šgΛωΕMJήOfηxV[Γ­Ι8ϊ]Hύΰ­m•bώφb΅G) ΈX…ΐ+"ψœ ϋΥnš1γ² ‹_P,VY–αbσI)F_±\ΜίοQ%1p?Ζ=96άΙμ€οw)kϋΔΉ,Ο¦σ;.+·~—uϋΉΤh#Ψpα*}ΰΡ±ΨΎbΪ—ϊΎλKθήΎ€\ω *ϋτ9—ˆ»tI5ΡAt«\BΙHG•?ΟfηΣΥςόβ …Λf24γž&Ÿαη«o^=ΈΝQځ£΄ G7­ε¨₯φ₯¦X6$i{#+iΩϊΐ\²;Ι.,V€Ϋ9FGΌ>`ͺ(wXόeκ~6ΖkγΊΧΈ\(Žoi\b’ρM0VŸΤbcΪΚνΧΣΥμ:kΌŠ σιβΝώ,NΩ«?Κ­Σ,1ωkβN‚έ6<ι¦ϊϋYΓΫVx³`ΉΖ¦­L°£>°@Ή΅Υ>0UΒε―|MW4½ΫδΎ±m-žμλšH‰H‡ύ!υ.‰D+[iύσόbΉψi#₯wˆ%²)<²ΰN¬ίχ[Φ€=₯ ζZ£%λγϊ[₯sƒ€³ζ―θυμ.§υ‹8}cλΊg΄m~€—ά6ˆ‘3u‚=₯hΦ&\Έšξž}ίϋhpΈέ endstream endobj 271 0 obj << /Length 2524 /Filter /FlateDecode >> stream xڝَΫFςέ_‘7K€Υa7Ω<XΞΖ^dαxx $HJjiˆε‘ππXΏU]ΥΌLgz`±Ω]]χ%ouZy«½ϊιφΥο£`•ˆ$TακφΈ’ž'ό \E‘'΄―V·‡ΥŸλ/uU›ήώϋ‡χαxsΰIΕ€Ιξ’>nyε1ξΥΦχB%ρj«"οφ:ϋ†O―ƒ…CΪ¦kΈMlΆZϋΛβ“Ϊ&N6οί~ψ΄$Aΐh,A@ήs°εŠ@– δΘ=™Τi τZe‹X†ΜcH8Σ­Š΄Νφiž_nΰœτΦΏKΈIi”|zšv/,2°ψ 1QΡΊ§ˆΎ#bŒ2‘άΞ ψQ‰ΏNρ¬χχiξ[³‘λšVΎl€^›}[Υ›ΐ³BρRϋ‚†”ζa4$<·γd0φІΎτ‡Yœ’βΏYριHψRN₯Gͺ@³R2!³RR2>zΗ—ήΖρΕ’—jΨΚ6Ž “,ΙπμQ“άΆΆΊ~‰ZΊd΄΅κZΌΕipdλd»¨cΆέ-š5MVž6[™xp«φ”ξ£”z»ώψvAέωn ‘„Π™5Β¬I9s¦·”?oΐ`ΣΊΊP ž"-ঢ়q”z*Ο ―f₯ΓΫήtθŠβB:KT±šκxd΅NmˆΤŽ!„Όυΰΐί’ΡΒ<°¨₯0 Dΰχ›¬‚£†λŸ»sž‹"QZυDΨY2€lN Δ·”?δukΖμΨς#-|?˜ΪK‘ ΩKWfw†Ec̏‘~,ύHΪ]mΞiV/πͺQ}°ΖΡ8ΚΆrzΉοm\cτ΅ χ“!h ΦG«LLΊΏGΘg<‚°r€Ο )ͺͺ ΚgΦ/ΰ”΅;Xε  ”M·k oͺYK uέΨΈ€ςρΦ"ŠδXΐwHιS€¬hΕz[ΰΐAQiR–½Ν!Έ” < Q@ΰσ蔁d΄OKβ·kΜDϋͺ8CM)Π;©kΣ@©ζαήΤ†Ά:ε Uz½>z,μ^•K›$ζ›i*=ΌŠs3³ψ—RzΊλΟ­φαω£1lωο5\ΩFlειΆοPVN°•]±{!Y‡ ’CΥνrσ"DωQ^°η{oΗ &ΚΆ{ Y?OωΓκτ%hnΏECεaVΌ a;AxΝwdυ#΅ΤŒΖ¦²‘s7-ΚΆκšegλx±ΒYZϋι‚S­†Υε-–X’B’υtόŒƒ OrυAKΨ!Ωϊ?€΄ΦWcΈqΧeπω adz:’νRDm^‡λ†K{" ξΣv‘Bˆ•C—‰Œ,΅*ѐ§¬μmVΌιΑ»/| D4H,‘ŒΑŽb2-Χ¦Ψ@ΟΌ‘nζ€Λš;ά !νoxΎ‘+Žδ,»™vqό#€ΧχΐΐυΓέΠ~Œ5{m0$½:˜qΞίžΎrtͺ£φ±:ΫRSœXΎžXRΘΎ!Φ£a•Λ^s‘CΟΗ“€u΅ “J‹ YθΒ τβRνΡ6¬O«T‘ετĘπ©έ¬Ž±(±οyς8BJ‘DωcΊ[cšΔ“6Mβ&›&'Ηh]iS‘•Ω6OΛSg?¦KS‚ω˜*Σϊζ†8Ws#¬“ΖMpΡGSθ–¨b‰W±ΠκξBΟ’¦ ֟›ΕJŠ /ΪΗG(’‘ˆ₯ž“D•EUGRκͺ½ A2υuΚύJg+ͺ*@xΦΏbΠΐšΐΩ[‚•Ν=vIt‚;}€:ŠxΞ-εX-ΕLMμ‚@OωΙx$9˜ΪΌϊš£ήCΣΌ©ˆ'ίσD$g‘ξχζl«›ˆζ–π€”oκlO/ύdΰ¬<³XΚB°ωdžUO*‘‡fωΗ6;\ˆ‡››ήqιa‡Ρ;(•Ω@Τ±³-@ΣbCΗΤΰΟڟr[•nςbάΠgP%–LΗγήΟ.ϊΚ:ъ?½ΆC‡D­_ Z°ƒ;£ψŒm_5sδχsR σψΜήΉΏ§qs₯X–ρ~,"ΤrςšFμΚyoθ€20nAglh>Šμ°: †ύ€ Ikβ Φ–ίΜpOkB„¨ΖΈz6,iόΧΘ,Yο\νNMξσ•μŠ•§©ω#UΞΤR©£Μ³p«qŸ©…vζhCnU8±pc0Œ―ύ%“@o*G½ZQΰtεΫΠMzβλρί–œ:dλ‡ΘεΞͺbλJA8₯Β5†Λ|fΝΞέj–tv*£QE³Ώώ’δτΘ½Ο<)K‘‡°‡’:»Qήυδœ$lxͺΊMaž¦»_Σ―YΡ“ȍKηάΊA/ͺμ6ε;zF΅Πw‰~£ρΏIφέ<)0p‘gΝ,ŽhŽIͺ₯Wϋ‡#< χΗ!t@aI©0ΐσvW8F\ƒρjυύ²‘οlIΠvpηƒsΘ6ύ?Ή;<{‚ρeLπ£&αB…αΤ$ώξͺΦ<]ψŸΰΦάΜΫuϊ‘qNΰh'ά£ισŒ@χ|wϋκ2ƒΪν endstream endobj 282 0 obj << /Length 2884 /Filter /FlateDecode >> stream xΪ₯ZIoΫHΎηWžC(ΐͺp_Œ4NOιg.IC(Q΄L˜‹š€μ¨ύΌ₯ͺΈˆΆεA=‹UυΆο-΄½Ψ-μΕo―~Ύ~υζ}θ/‘„nΈΈΎY8Ά-½ΉΥxΙ=JN‡¬UζZρ(ιR­}ΒŸ(eΥ>mȎνˆ8Fυ"NbΕy“—λ³,ΨΫ—DV‘Ι-y½‡jͺΤlΧΘΌ0Σ·y—΅{™fότ»ΨoΏόςα€'νtΉiaΓΣ›Lηoηάϋ¦Μ#Id2½eκ»m»yY€ΞtNΔτ^6­Β*iχΣyή?YΦ¦rŸ­·υaSdηKξ]MŒΊλ Όc‘hή•ι?uΗ‹}΄%š£γX¬ΌE Έ‰‚€Π?π†\Vο;ς}€η}?ŠAŒξ3ΎοιzΡeΟ»}‹8ŠΖ*1ΖΛr~}qρš)γAΚͺ₯FζjW¨ΕΔώ₯~ρυΛρYih#Σ»ΆννKZ©Κs”ͺ€0ͺ`Vϊ³ΌζΡ(θvŸ₯Ή,ψG°P{k]³ΰ‘K²T€vY΅š3χ¬‘ν"Όž¬f΄κ%b­―{H ŒuŒ Θ–·σ©DCŠ82αΗyΊFΝ£a;’b›y™c gΐ΄!έπΖ ©,}ΫΊœ€YƒΒΉ™uu£ΦΧʞΆΚηάGo*«+ς»₯p1η~2L¨Ύ;ž«9ΏπEβΌ~ΪN=ί ΗPRΤ©| †\“P"OΏHtZW]S-Ϊf7Θ™<δa‘‹Fρ3vΚζΘΏj΅/Ÿ‘$1κ)ΜΠ’Έ?u°χι5_¦ΆϊυΛ*goςmΪ#ό7Β§™Ου—Lo–m:ώq¬L°εŽΗ²‚"^4(2&?ŸH܁΄&ˆB-:Ύ*O‘ε ‘ϋžpϊtˆ].Ρd²ΛΜΝ¦8»QΧΣ@š„2_DώΤβΞ<³@?E…iό Η’§wςR=ϊ«-½ Ά²*­1$«Ÿ[π€’‘'΄JΩά©ωMΎΞ Υ*¨”mερM —UQ΅’e֊σCfΔΫν ‰iΧ₯όqΎ©.δε ΤIΰΨCΉΑJιϊ†G†MH’Žp$`Fβ¦V‹ι`Ššψ‹σ$υΞqœ@τ²­/Ω,‘pcχ‰g]G&tΑ;­h“‹KΥϊ‘ !ΏΉ“;…_βαLiΫ\h;œˆ!Ž„PΉΡψVšf›ua=—ν…ΎpΐΪFj€ςΒοω:ψ…­Žu\Μδ(·r#Gœ&π4‰ηής94iβ½ΔT€EΤv+Λ±ˆαΥ’‚h(VN?ΈΗT)ΐpc— %FHζMh’βŒl³1š7W΄žΥθ²§(αφ δω_κ™7°Ο–ŠπPα:@ΰk[ΩΙΉΈˆ@ζΥŎUeG€αΜCŽAiUΚ¨'œ… ρ¦+U"DWΓ]sEΤ瀩zŒ!εκšS6™|oΞΗ†Y9‹O%]μΦΛ#ΑΎ©w€/Pε»Ό…d„r‡°³ ύΪHl‡xƒό|δ”§–Kμ€Πu "G‹ (₨νπ ;žUιί yHsΤλœ΄[κκρψ β>Ÿ$I οͺΌλ؊X‚Ιm°±…9W¨œ ζ>ωη€sx8ηiJ‘Ψ¨Χ€# ιώlΉΌQύ―6ΣΧ@•r©x !‘A ]’½­ΦCUN4˜@ σ‚A 5‹ͺοΎVŸΔ,¨e#Ο£έβ՚γΤΛCΤ° ω§*Ώ]Αϋ&*,8ΠWSuX•{>bΒ χ ΎΥσXp‚L40 ΘSgρ΅d£Kr¨L τ΄y ΈΔωΕ‘§Ψ0†ΎŒ‘ΩŽ­|ξΊ‘ ™Mό¬² ΰφegίJΓ“πjD0Τ±5γ[T”ŽΉσKB$&šϊϊργά B]ΊAά$Mα!ƒš{K8ωΘΝ…”4‘d ΒIOφT""UŽ[Ψϋ"O rt)ΪkK½¦JQF v€©LQ;c;±/‚8˜EoάΣRΖq_Κ>S Ϊ‘π’dμ>ώ5D}™7η;ΟΏώT―Κ(ώβhμhUG¬όHAgϋ“ΗF0»•Xήη\pΑuIlώ΄€cV΅κ—Μ0~ΚG¦2υ‘87€uq¨ς?ΩŌD½όvζypα.Hte·lM“ ΖΚοɟ5vΉnεJΛ·‡ύΎn /ΊIw±ΝyT4”ρέuƒ§TPχ»(σ ‹Ώ‹ΗΜΒh늹ψTχ%ή):Lo³τ½ΤmUςžZ‹ΩRœ2N·”€j;°ΟԐ @JαQ/qΓΏΓνγσ]€3Δ„qndš•ˆ JΏK]‡³ž΄ΖΨ°ΖZδΓ5 l'~MŠN³%ΰb€(£ΏΓ5icύ8ο!Ά²β‰’«Ηέ7ΥLΖ¨ΌoΚιuφ£;1>’d3ƒΥ }Ž˜Ε&,¦!φΕΙiχYΣΞ»@ΰCQηO$3ΦΉι@,™y"πύgυΐϋ°hμ£=BYe:eέ­9τIι)»X|ϋ±ρϋ«ηΖPήU)†`Δ#KξχΕ‘Ÿ€‡ΆΓ>¬qΟMŒkγ,ΊD&vβr6Oq…έΧ•Γh0μh”ς.1ψ_91*œ€#¦28”ώΒe$ίvΗb‚€1j†‡Sα± > stream xΪ΅W[£6~Ÿ_—]‰‹ΘmΤΎl΅©¦έͺΚKΥ­Rœ`0΅ΝLζΗΧγ˜\Ϋ™ζ‡œϋ9ίηΰΰότπi󬑳ςWσιάΩμ€Fsg1ώ,œ:›ΔωγΓ3£4ψηζη`=·…#ύΕRZj€ΰL‰<m;XG3g)e₯Š”υΒe¨„½ιBι·*€(+±έ“ +U©ζM‘Ώ‚ΛφΧο>z3>ό†QžφL†ΡœPϋ(‘H4Ÿ”ωφΘiΕbΜΫ/ή[>?όΥlυ_"΅5›~3p*…όœόN«φ£B»Θ8mOΗ'w:Ω„0‹¬0i‚˜δ"FŒϋ1žΨa4΅]h­τ Υ₯eUρ8`ό©ˆ³*!ΕAK㜊^”CU™€B”ό1D€ΥΏi’ΤϘq4bC/AŽH‚‹E‚ †2Yφ3ω\$Κ—ΠM•Vβ ;«νυ¦sΑLή/€€`½„,`$α ρ¦1τW+CNδ/€xR‘vΡΫ ύ(Z ™ςΰ|ξCoT»1>9ΊυWFt§Ν3t£ζΩΤρNa~€Y•λ±δ8“Ε!΄x+θzχχ+βzΜcΛsςκTwŠ >b~­m‚Ϊρκw)ΞGΒΥ5ώ·Ά…τk%œΣg\ݝ±σ’ΎέΤ2‘~B8’mέ<ώω_mbгRBο]Βε1Α· Νά%clˆύVΧΖK“=‰ναΰοWeΙ-½—v‡±$§Z€ωV―ζ΅Έ Q—οrS ]UŸτπ'xͺLΈΦE §³Βœwa˜qθ yŠSkf”ώmΈιŠΙeθΦΠ>H­)­Δ™?Eε ›ξΓΆχ΅…δΆ/Η2#11ΈoΩ¦kyΉ”¨λv7@H γΰ䌠Mk€¬ξκα™$ρd$@/bΞΧΣύ%6ΎώίΰU "δ― ­vΝώΞΤβ1YYΜΡOy­ΆΒχΕΓΰU"7……Bξ-”ά©.Š$i€δύξΊ“;ηYωΖ{σ­Α/_$˜$χˆ\ϋ}5>_GfόzoœO‹²Έn/Šν.τ\)»PΌόm3p-΅v)άξκ­Ό†[kί{υmρεe;Β½άڎ½Αjχ]ά&όΣζbΘNεh­.Χ·”&,Χhκ_Σ6π^rΑ…Ρ“€Ά#N;έ ϊNοž4ΟΤο'ύεΞ ‘ͺŸοΗ—)»§δr±FrπVαθ%™$WŒ4§!+šεY˜•βyŠŽrUUΌι<’%œ]Y|Σξdr%kΚYξ•\IYV›[soο֟ŀ3ΨΪ‹κZ’€Κφ/°¦ω£ή=?oώάG, endstream endobj 296 0 obj << /Length 1492 /Filter /FlateDecode >> stream xΪ½X[oΫ6~χ―‚=Θ@Μς"QRΡ=Έ‰ΣeHΪΝQφj3±PYr%ΉYώύy$ZvΧΕ #@xH‘ηςiκ<8Τy7x^]HΟ‰H$Ήtβ{‡QJ„'@Rβ ξΔsη£Λδπsόη«‹ »Sψ!‘>fΟχ²(–wIV—j₯wh#γΥ…η;!9pl$QŸnφΐŒiΐΔ(‰όΘr1’'ρΰΫ€ΑWκ0k‘ #†Β™-?SgQθ<š­KΗcŒž:sn[vGXՁ ԐsdΐIό‘ Ψ:ΛI²Θ›Eρ8qŸΊ‹t6dΎ»ΐι¬ΘΦΛιϊi₯*$“r(Wαd]₯ω’clΎ¬φ`^–ϋL0dDOΘH‘GVœ«jV¦«:-ς-Ϋ·’’Γ#O†$”β9vΪχ­Ηw’Πζ’n–4ΰ|±`¨9Rσ΄T³:{jkKσΥΊnπ,šq‘PΜΆv²Ύ΄ΪΈtGΑ‰"»­²Π}X/UήH)ξ,ΰ„Nωΰ΄δτ">%Ά²« DΘm<Ρ#’]wA†$‘I@κ7Αz[%ͺΧSΣs:ˆ˜ΜΏγπώφκͺγΟ-έX«–Dncΐ6V{Υ{I-‘{€ $$!oGΒχέΩ")“Y­Jœ~ΧفT4sπ'Œf¦^k2ӐއHŒ1pΜ™{`Kd8’άwΟ’—“lΘάͺΐI|Υζχ…$=EΦΡρτv\Oډύ aΈTR·^Œ―nzP‚–a›5ΖΐΆζh5­ ΔtΛ2$VEU₯_²foϋ9/reΣΓ&a§Κυ… ΗLۊ•sU'iV½PnΆ»άNΠ‚ήͺΜ“:ςΦΛ¦Xΐ ”)’i…c’i ‡{ν₯d™ΆgŒ!0ͺ|θQχ{ZyS {}°L@Β0τGXy]NΤ7„…oΒȈ.‘;jŽZ‡’όJlcΤξl ‰ΚΔ l1URΐΞPΜΤK½R©‰΄ΩςέΆ―ϊad;?™ͺάΨΨ/τψM…ώD)ΟΤ)0Χΰ~/bξcZ/zΔΐύΒχlFυ"­^ΏVσ΄Ύ+ο™{šγ\aΔ¦aΪ»&Α@tu9δΤά νΨ&Ά³‘‚.¦]%9:C wŠΣΗ&:€Dχΐ'λX|ζ\6‘[Χy’ecH€Y‚Ι΄Yˆ7ΈUŸ ₯Vθώ„³€ld'λΊXκ8fΰθΜΔ#¬Φλ27­ 0ۍ’ΖΜ$Ÿο$ΰn¬Β’ΡXΧ&0Ž 9C}$ˆnΌ‡mΦΆΖa θΤ‚ž’MQ؈¨Λ΅κs;άηi―{έΊ‡lξcΑΡΰ`6/ΰΓ”±&½Xv΅2ς`6}Ω°₯ν©-Α=Ων“ °;ιX‘­z w2A;αΰ+ΖšΔ‡RυόQ°α¨;PΨrόgϊαΓυένΝδn|O'έ½Ώ½žL/ΟnzsχΪ£#΄λ²i$¦ νmPψ¬ιήΖC jϋ›«^Φ'U€P‹@·“nκ»sσfQΨΦYέuΔ ήA‘(75Τg€©U'άܘ4Τά–z,~[lΓ*ΝηΨγω:Ιpm7;a {7*Ρ—7MiΓ^cΞΦe ν « Σ¨žd…·ώ‹^=ϋcΪ-…+½΄Φvπ«Z£‘υSΆ]Ή φ2%άϋΉj±ΜL’€Ώ2–«_Ί˜Œπ΄Μ„ψ/ΞβƒzΏBΰεϋψΈΎ½|w|‘ηo―Ž+ŠΰqύxυξΘžΗρυ‘%ŽγΙq%Ζ—Χ“ή79ΠlηI>ω7Y2Uώ*ίόΚf8ήOξg΅₯ΣΌ>ќε¦/I‡ξ5W‘gΏv΄γ$όΤ  endstream endobj 305 0 obj << /Length 1104 /Filter /FlateDecode >> stream xΪΕVKoά6Ύο―C$ ’ω%zp;h`h½>9F@ki―P=Ά’vόϋ̐’¬•Ητ`sDQσψ8ί7K½{zοWΏ―W'η±τR’*ΌυΗ(%B*/V”D‚{λwνšΊ.?λ’kΜξs½λΪΰfύαδ\M?””‘8―φ γ‘ν㜭W˜Τc!8'\1/+WΧ7ΤΫΐΛΌJοΑ-=Ιΐ©`ήεκ―Ρί|΅uΘhZ£DIζΕT%Ψ3uŒN‰ψΘ'iΒ†Š.·υCςˆϊΫ< XδoέcVϋ²rvχugZgκ&±oάΓΎΝ«{gžBtϋfgƒ/"£RN$€ d,J,ά€Šb"WΔ;ΣfMΎλςΊ:*}X=–x!§„ŠΘ}pύΞμš€%ΎΙtg67 Lλmήp·―2Θ> »πη|υϋύϋ»€f§ώՍΫο\Δ㻍$‰ΩΨkΣ[ύD# l!O•’X€ΓG€?1E,MDΚ؝Όjυ½YDkΎΞӜaω€ϋ0Yl&h€ˆR7·|ΌΊΈ˜”p” ΊœζvΪ lχϋT³f~en.c6‘+ Π; BE~ΆΥΞ:ΣΈΗ²Βd]έ?ΓεΑͺF†ΰ^ΟkΦnέ·Ζ§χΝ8-„ŠGώΊrΫΊ˜ίΦξ‘Σ`ωf‘Ex"IšŽ·½ώϋκl‘%Έ R‰αD|κΰNΰμI£σΣ‹ΛE‡14bŽύζ‘ϋX mψ‘ΕuQ8cW·m~[τg‡ΧU]™‘ NdΆfͺ6ίU™p"˜ϊΉϊ*θk’¦nΎθrW˜Whλ{ΣυθWΥ»CHθ€ΊtΕΑωςτ3‰ύ”,†I½L@gύ°(AΘR%Ν[,j_m EάxΘ΄΄[ͺ}y‹,CΫΧΦ‘lνO”ςbp‡’jΧίx›Wmwb_Ίξτ[·ΏΙΛΦ―²Lγ°wΥ.ΡIˆ”ΘH+nΟν3š±gΈΫ"o»ΖήFAδλΌΠ#+\ύύ»ƒΥfiDHE’$YLψ™|γ˜p6§ocΊ&7˜ŠΈY¦O{vdμ$ΫΕyΓ%‡y“όΤy3)3 !Θ_ΚΨχNƒ²Wl²Ώx4}ΤεL/9ΘΜΕ±Κχ>BΰΊPjŒkxΥTδ ϊ―ύ~_θΖmΈ6€ί= οψ{χμl„Υ%Wi+‚s7αE©»l €ŒΈπ\ό” ΐhqx5 ΝΐδΡpθWηΫΝOYφ˜TŸnΜPWh;'δ¨ήjNκ}S™Νb›§k»£%z6ρ5z!hί P½―_\s^X9AΛΦgΓX>šIP$Μ°4J€i–*P);g‡NΠΒ»ΓΥ ΐ$ΰγaΈ7e—ι¦%Y{x3„~‚Œ»oιςd= endstream endobj 318 0 obj << /Length 1912 /Filter /FlateDecode >> stream xΪ½X[Τ6~ί_ρBFΪ1vœk₯>,e‘ ²0Π@(“xv"rrΩK}}Ο8vZU«Y;φ±}.ίΉΨΤΉt¨σβδικδΙσΠw’„^θ¬6£”p?t’’€{Ξ*w>Ί,^|^½zς<:€δOXœΐ>ŠζͺmšκΛ¦i«΄—Τ'TŸqΎ:ωvΒ KΆίή I;Yuςρ3ur˜{εΐL;׊²r|ΖHδsθ—Ξ»“7f»i«DπƒC%‘Ϝˆ&$bρμT‘΅Ψ#IΜF©~kκ…Oέ+Ρφ‹₯P7Ε&O{έΫ΄ Έi%π³ol:QUΡ‹?»Ύ-κKuώ¬ZΒ$"K~D/hB>cΒ0’$’ ςLtY[μϊ:―±³d±4‘Z½ΪH@#ΫΠί†bαξ•ό—–’ξqδΗναΔN{ιŒέ·/¦†Šα·tέ‚Ά>рM eqϊ$ ’qρ)œfX/<κš•’ξz‘ζψΡloΉΏ4ΑΘ¬jσ’ϋͺw(τβVτC[k‘S\‹Φ#π‘„ξΛήVΘ-ͺ΄-Κ[Ι4(Ο cΠ2’29tb3”6M‹!αs“V»Rt:u>‘ιEΧ«“•ZΖ^JK3QΖρχ]z)fM|ΧδΆΫΨ&?ti%–€ζΆP7§j†Ϊ£ χ‹e@©ϋ+6>1ξχf©ESή₯­ηiλΤ&½8›§ΛšςK nΩYδ«·οΟmςP³ΠeιNX΄™ϋQή λRΐxά:ΝΎveΪmΝHέΤβ‘Dλ,ί†¦ŸΫΆ"ΉΩ$-Λ#7\7Ά^ŸŸύρξ|^SCυ₯ίΆΰΆΠ²zζΠ՜εŒ}ΗΙ‡pwqΊ—C‘ »χγξζ>§BΞήθτgwc±‰ΓΖuŠ5˜ΟŽΝ*ΖΨC2ΙΙ—K}486‰);DωΡό>ΣΙXγ^"ΓAŽ=Ι…l;±KΫTς&ΏthD!'π#XΏ‘:N‡4Όδœκh;UhΒ LbV~§-h…xN<ί$Ί‘­ΕUΊΆΩΚ'|!f<κ“»˜‘«wt”z?Qκ•RŽc5Ό Gkw΅•|ƒš„ŒŸάUΜ—eQ ΙΆ H†π1ώΛa°¨8€žϋΊιzMΪTUSC8Η=‹~ «ξ*ΫγΰFΧ:pΝhΫγ„Η&]©Θ.χ}_7x^Χί–£Fƒ„ψP•Ψή,€Ϊ―₯,έ©Αψ]†J[ ΅ίεŠ{„G‰Ε•άψ/Yσuή¨#»±vΉ鐗ϋmΚ Œτ’ΔvαG[υVLκXt+3VEΧ™yΛa8xŠν0‡ώώψbΑ Œ1χρ;ώ>Ή-ΘΛ͌±βE¦R‘|:>IhxX鄁δ`¨jμλ4η…Ύ{]”₯lzμ¬ΆE•C4„©žν·zΊovΨQ•~ΖΧΞ+UσbxΎG(7,Κ$;p λΝΘ±Τ¨O bς§%ΚHŒAbd€(ŒŒ’Iy"yTΓZέΘ )†ΘOηfΜαΒ`άΐ2ξD?">3ή₯9 d¬ό*‘&π<­:πΠέιd@E 9ζpβΧϋhͺ€y©«j( Em0;γζ1ψ•‰ήιn'£ή vΗ†ί‡|5 ,d6u]τcQXq}»SρFWΊΊΔ:τCpi“·£ΈͺbKαVO£.Ζνe·Ε{°oεOžά'rLόΠ( ½οICΆΌ‡K”PΗ¨υ­ζTξkbPMψ^ϊp=‚ϋΪ2²<θΗY7Eι‘o€έ"ƒΠr‹Ck½F‘ Χ5Ψ’ δΊ9Fε½ΚΌœl5Fd<•*w2ϋΘsΎ:ωΎ§ I endstream endobj 331 0 obj << /Length 1338 /Filter /FlateDecode >> stream xڝWKoΫ8ΎϋW9Ι@͐zk=t±Ι’EP`³Ξ©- Z’a%Q•¨:Ι―ίαC²iˎ»Π#rήΓωHbgλ`η―ΩΛΩν}8)J#/r–‡`Œό rβ£Πχœeξ|uΆœW«Νn3Ύό|{ ˜ 8mŠ“€’e†ώ»εμnj‰²Wν…(φˆ“U³―ί±“Γβg–ΔΩ)ΦΚ ( | KηŸΩί£ΎγQω„‡ώŒ’€81NPMΈ?ΚBΰ±%θ‘4!C Œζσ…b—καΖή 3s»"Οϋω’iΊ¨·„D±^Γ’4;™Š(PύR*TΘ‘?Q²(JP‡Ϊ?Y—΅E# ^[Qο£·ͺHgA%—’Ÿ“Πύ8ςz!Mzςޜ•ΜxU±ΪFJNdJ&YgΥς―εγΣέ€~ΡΥκZfiΑβΔΣΡΚ‚Z|ŸκΝ$ηX~‹šu’›–’eΝ;F‡Ό+ވξ¨hMΛ2*X~Ή`}΅Ο-΄Έ-Η¬\ΤΠ΄|ΫBtβΓEωξ™οVΏΈωQM+Ά‚ΈiΡΪ;¦―‹=»QB‹Γ^7ι’n =₯Δ@ΐ…~>ήzgƒΓ‰)‡ Γτ~žΐΨόRθͺr¨©w=8Ρu l„3©―΄F«σσ‘\mio„” „ΠΐυθlηD"₯Μ[wό/ƒΎ¬ίΡιnoq‡»7ξw…xf°‹Ω$NΑ#v|nΧ„tl0Mυ0/Π€λ‚υψO·%£ΰΈξοΤG8΄£ζ΅yύΐ(ΛΚ’Vǐ Πa\3λm΄Goύo™—lΒhR桦ΑΤθΆz‹ O1zΖ{cΕΓƒkoipΔhΩ|x0Γ‡§έ:ωxrKΨ##zfeΡ¬9mσΓβœ;›ΓyΙπJσνψέ΄Ό2ΟΙη!ώΧN039ZC“7xy-°Eg endstream endobj 350 0 obj << /Length 2583 /Filter /FlateDecode >> stream xΪ₯ZλΫ6žΏΒθ—Κ@¬PΤ{C±½ΛZ)l>šΓ-ΣΆ= IΞΦχΧίί=α`νΚZΌ’12‹¬.žu›wy]΅#6!cΨ9‘vηΏΧΕ±¬–+/NΏόνr%…tTΛγY£U§7ό²>ζπΟM ξΑδ§’z  —φ§„σ=ιςНΎŠPΐŸχš˜aΰΖih—Ύ½|Σΐ …?άς9ίtϋφ’=#ύ’Τ­›+N*ΰ rΈk―ΜK6Žα°^Ώ±‹Sαš}ί ΞΚσά4 ωΫγ2N½\ω"tΰZ6HEN^ρH]'iu‘3Ί4*„ΤΕ/Φ—Β9ΆϊŠ£ΎΗίΩδωh« Žœ_Ά,e·Χ, ݏΥ[2ί"§PmΗ‘^β fΦHΤ>Y° …“·όόΎ”‘£š\­ Ν#(©b²Q»*it9σ]ΘBγYής@{<P™H“(H(DιΚp°Ίΰ7eψΈτCηޝθeμΖΖW^ϊυΟ^ܝϊ ώ­BαRGf΄‹/άΨOμ܏_>|ΰIγΈ€ψ±SΧψΉΓuˆώ †/έDτFίt–γ-d Υ‰–*τA# =ΪΙ«Q œΟZΟΨ“žλ{}ψžο*έux³βt˜ζ+ΧDœ$v#τfΞήΏ@St=Ψaδ…eέ‹ΨθNεEΛ.+`–ο&ΐ–fύ2w!@#β₯χαϋ±£Š‚ kπH³­ Ω kΝΟΌΪꦑoΫ¦.ητ™HΘώά»£nΫ§Rύ1#€/ιm©©Ρɞ[ζN^‹νϋΝΗξ­ρΫ p=™Œu˜WnΰΦ›–œ1’°Ά©»}}D‹|γvπέbηqO^Γφ™χθτ^~™λΚ¬'Ÿ― σνCχ쬋G‘³^b,4“«Ίγ‰MΝγmg6δk€ΛˆoΒSΚ#%Γφ>`\5fΒsSW;ŠΌ‘sͺ?’ tUπ­’S§«™C^aΊmηL[B€ˆ.»)^0Hd΄… i$θ^›»‰|7 ƒρα²μ&3‘Μ I«ΨŽΝ=C _ΔSo&•$g S~μϊg#-rΊΗK"LΚ›ς9‘HήΡϋ•GLxT0Pˆ+‘΄%}™Μ°e’κ':\’Q\"B«l”oψΈΜ3+~†›b‚!RΟδδ° ν GD ₯ψΡΧ­6“jÍ=ή«};ηΧ‘ LzCύ>‘ ΧCƒ•bάQ8Ώχ˜ t‡qŠƒ¨όέ?Ο*ΚTΕΗΕ U‘ΥεAeFœ{¬n!Π nζy―))ΐT{7ΐa―ΰ ›9…υ\Œw‘e ΑZΎi f4 _₯ Η³~_…0ŸΏvC!ΞS'ΩκΟxε#^΅Q•»ΉU#nΥ±\ίΘh3b΄©υnaTŒυ.ΟT1Ο)|ΣvΔ‰NΦΥ·ογσAάΌ‰ΝγK6Τσς6†έˆαΝl~β4dΨP.»‰Ο“q†Ÿ«‘tν·όΐρaΞy~>1pΪhΎͺcΡa–Љδλ„»xc ΅:F)ƒοCTφ"rρYδ Œ—p|΅ΣsA’έ3Ξ"y°ή«n„Τ}D%€; dΑ3A[ΨcŒΗ>™] ˜ΉΊE_rβp£Λgq8΅ sΣ(0Η0νΜ"3x3I\yΖηpΌη§QΥƒm ¨ΓγI64Wϊpασϋ™ƒωΨΚ°tgΠΖ\Sϊ‚•u†Žku;`h>Λ8pp?LI―Χrˆš„ήΛbŽλπkϋ1 GΰE”£P4`ΒβΈΡ<ˆ8‚>ξΝδ0](AV  Š-=0fLmΈ’RN’Τ6ZΦͺRσ€2―ςU‘ͺέm>ͺvζκΔΈπޘlξξψ„ΓΜ==`ŒΠƜ`G‚.΅ “vˆAF’ZΑ£λ?+ ?8_fΡ±ΐ@$=0Θ^‘Θ‹έΔ §"qφΒTw$’²-Hέ`b’œ―½Ψ4[ €ςȏώΐάήΒyQŠhdΥ ―`ŸAκHŽOλμPΑ( %ΦΒ3΅c±ŸΤΐΤ¦ˆ dŽ{SE[ΫVΈ±'Η‡RY¦„Hb.»αiZ7yΖ/}ΏθΌΪπa}&}I~)”nx.sκςΝ‰ΟpwΧ[γ+Žγ , ’aΕNBmh‘2ΕΉΉΑ0υ§W8ιπ ―’>,Ηυœ|sΉΣ?ξ2+ςI€y Ύ!ΟJ:7Οη}N0H2a•M‡,O›Έ>¨nOφΨ2Z˜2ZϊΥYήΪμbsαLj*!μδξ°…\π3Ι[ΆόΒxH ½ΖΞ5ί6jι9² ϊyC6L£δΤ—!rιάΘ»,€cΈΖμMΈŽ@Θe2Ϋ₯ρ‘ώς/Ϊ4RΊ+ν$Œ7½ 0Ε F»΅ι’Λt’ύqΣTΚ‘t°Ή7ΆΊ’πͺ―΅> stream xΪ₯ΩnΫHς=_!ψ‰¬›7 ,™#;dς8»Ι@hQm‰0EjxXρ|ύVuu7юœ…X,6»λΎ(w±[Έ‹ΏϊωφΥλ·q°HYyΡβφnΑ]—ωA΄ˆ#—…Ύ·Έέ.Ύ8uUΦw§»ε_·Ό~ _\ΞβvS+=ŽK^Ήzs}ύ6ο¬όΔΗ—V^Œ8z΅­σΓϊԌޟœG,Moes™€§_έΠ}σι—wοθV=i¦;ˆMλ8‘7μ›r‹€1+ΞY†DΤ]]–+/t)²=A_]Χ“H)ήlδ]UK‚’nyx“·?MxΛL3Ώβ‹x@§5χωρr}θYΣiΥ]A<Θ4‚mEW΅ι­΅‘0ήlE+Ψ‹©-ΧρνrrίςC§₯Y^F;RωrΒvlš$Ξηkβόž8_‡Χ‘€T P“†άΙͺ’;”ϊΗ£lΨrqΧωο2σ( }˜\z‘σ°δ‘cΞčglρ@Κσ<§έ‹@e °(\@=Ής]ξ|’’ό~$H¦, R#‡|WΚΆ•θTWΔΘJρpuml=»;’se^‘ϋ/Zx…GΧWΚλfΒ ΌΘ}sͺ$Ύt°vΊ•-8}σΌp/bqŽ­@m-_ΰNΏTe[W {\”»σ^ˆV" ΄>βήΠ9 @-YΆ’Ν«²AYρΤ‘9¨©¦Υ‚.Ω^ΤK5Θ²D0£{γ#Š€λύωK\nxΉύψω·i{ xdΡIS;pA’fΕΫ7ο?Νξ°Τ΅ϋ0"MΫ~,YΤ(s?ž©Μγ, mš‘„D C‹3Ζς€β<ζyι™Υτ@‡ήu±ωxQxDˆšuulΟr˜€ }ω `ΙP€hεφ―ΛC—―cjwX·{t™dΞ[’ +‹λ±2•ΌΔwΜξꑊ³p%c€D@^ζm. ½‡Ml°J¦ˆ-Δ?υΙ„pD4RŠYyiθΌCΚάΘy¬Ίš |ˆPΞΎ“— α)6žLΜ \)/i­ΞΓϊ EBX%Xθšρ’k«ψe&ŠβqΞΗΐVΐ0^‚`φyC± ΐέb·P†wˆziEΧN‹ΐͺ”fAΒ±Qθθ0ό{₯xG\—ίΡΔCΐ}IK-Z/Dώ‹ΩL±:N+@€ξ*0QQӘβE_υ}#ξdΣNκ ΩΞΔκGgΖ4‡φ¬ήYa. WΫT Q$ߎEžε­‘ΫsΞy“^0φa°ύΔγΈ―ys,šuΜU¬†‹έEέmDύh0φŸΥ2μ†L£+ ’·„S„Υ'•ηRκWΠvx†žUš{Ȑςή.θAƒN\•sκ&•UK€―BxŽά’eF‘b Ϋ*됦ΠΓPΐ[‘Ϋηπ.pNθL ΐFΏ‰M1_Ao­Ή΄Άΰ„¨ͺy™θ1JYΩΌ JN|ZU9Ρ`3ξΫlζ9›ΥΌΐ{iZ{6gΔ ‹ύ‰½)J‘^XSp±Υ½»›§9rύ‹hŽΝͺklhP/^S²Da:„ΰ •ψ\Υ«rKX[β ]ΖaψqIΒ4ύ^½α³$΅\B679)_ylΞ Kaͺ τ‚„A':Ά$K`ΔM'δΪήUτS ‘jΥ^…‘GΊXS"ΠtG, β(πŸb*Π«ι΅>Ώ?Ο`hωΓΚ…D gτA‘Υ gdb€"‘η[ΣI|»!σ•UΝπmλ?›MIh™Δ;a gŠΨΈŽ#)9£–€ξ ϋf":™ΧβΘΌ§Υ|Μό>„ U νt y6xœ…n2v&VŠƒ\C &ςϊrWϊ‚‘ž@ώWέ›*+΄ιŒΫ’m±|Ύ ΥE«6r/–ž Κ·ˆΒdˆWΥΉhmM§Ljξ€Ω8]Οv^ΐ3¬Πτ_ueώw'―f€λC―ŠnώΟ’s΄l ²1-³©Ή­‘ Eύοrc6Φ@Πͺ €›‘t« `~ΰ•ρΥσΒηT˜ ΓφΥͺΗƒ(ζ˜€Νίjλ†Έψ K ”ά9¦ͺΟφ2»oΝ,C}UΚZΦΰσ¦•e&Η#­ϋ1Ώ‘ϐ[ŸAυϊ#ά>­±ΐIςa|ΩRτ€a@vΪ\‹γOq‹¬ž¬K+Γk‘ΧH#ŒήχΗv¦xž‘-sSΝuψ#\+m¬Ÿζ2yμ'E—O):p£XτΊ¨ό©07ηJ!sSλ#Z63a<)Iζ=a0ž€§I<ΘΊ™w0€;˜Hf¬sjf¬ΚΟ9!ρΑwυΑϋi΅ζΊ,¨1³)λ΄Ζ“ΰ;¬/Ύ€ύΈΞ άK Ω.3¬ζ€‘ Š™ε‘žd]ΣβψaΈο₯Φ΅‹.!َΝM4xκ1·ος‡Ω`ΨIΔ½d1ψ_ρρq/jη‡2=‘•|Σ>“θ1Fc>’·N…G&p©θΤ­«λν}58M”P£₯lL7"Ύ6₯τ &-< mR QξnnD³6›^2pIS†ΠΠa°φa'Š[jœΣx”ώ9ζΙQPΥ‰pUκΥsVΞ‘Ίυ‡ν€©ΞΝ<齁:€ ₯γ²Έ/Ÿ²ΆnnndΆ)λΈD:άΈƒx² τή.˜³Ot₯ υΌgΛ3›3£P8kκλqŠ&μ‹ΪŒFUŽ΄£Qz<(ˆg2ϊ³€›9Ϋε$ΫΟ,AJ‘ Hμd=H΅ΨΧVΑΛό³\wπPp}Eœ6ΥP(IBλaŽz μ²Ύ.ςΤ ֌‹X˜·s£6;΄‘“‰ ~ |±€NωΆέΏ@ΓτuήAΪ9Uΰp₯ΟgιIzI8ŸΥG@Xϊ‘J)XΟ6t=οSœC φΑœΧ«ήσ~©68±Ϋ©6`ύΩsZ›“ό_ξM+κφΪΈΤφr9}Βυ\Ζ U£χ°HŒ=y™]3˜Q7‰KU“«Ο ΄ZΩ&ξ@*ΥϋFψ9JIΡFΈXθ—I¬š€ΡΫsΒ"iŸ}FΤήk€­2ΧιŸ/ώˆ|c—ΛτjχΜ€Γ%―ͺ•ΑV’rήP3—* [š@#β†ˆ‚“ϊΊV6Χ―:Ϊ-ut{¨'tψLΝ†(!ηΥ؍ΰ: V^RΞΚΣ~ #=;F@#Z51©θ¦¦±φ―X?Ρΰ.Œρ”%wtTŒϋΒ&2‚@-GγȎ0¦%l™(;CνY”ρ€ϊˆΘΔB&GD© # V“‹(₯adκ²ά΅{‚‰{΅ƒωΊ7ˆ­—τ; _ΥrRKƒ13ARω--₯]a{2ύxΰY(“ΔΣ2ΑdB -«fG‘ΟβΎήφ;­bΕΌ”HΪPgιXT€3 BeM*]τχ€”΅κ° gA0™αΈœbΝu`V5+νΗζΊ©P € ΓΖπγΧ* Θc½₯^’GθΊv™γH†lH6‹τ›ͺv‰ Oϋ~i¦b±gg rΦͺο–nΣ,a·e_&Jš” xΫ‡ω―’ ?(£”ͺf&tΣ©œ†λΒΊύόρ½Ύo‘—Η³„Εv-}bΒ§€ ϊσK€_ΣC©IΉ τŒa2ΦmŸιTQ%pέ~Ί;"§«§<“j¨›8οJ»Έ1Λ‘»iΔh‡Os}ΏΑΏΤz—ŠžmΙdΚ’Ϋ~z«SΛΰ²>«}ΤPfάQσΒf­ζ·ΫWmΩ endstream endobj 368 0 obj << /Length 1744 /Filter /FlateDecode >> stream xΪ₯XέoΫ6Ο_!d/603")κ£X:,):Γ–¦C[ͺMΫBυαItζ―ߏR$Gvά "~άο~wΌ;Ϊχ֞スψυώβκ& Ό„%‘½ϋ•Η}ŸΙ τ’ΠgJ ο~ι}œ1ύ|ϋΥMΤ§”!g‰1–δ[]UΕ<ΟJέ ν…οNρ²Η4s\3αρ^?€Ε6?`<φ½Ί ”3¨ϊΖތ£,N²VϋΥΌ±β¦3εϋ“_fτ%ύ4τΙWώ%PΈ’™s k€Cfάg‰JHΰΎΞŒΎE»«g&NŸNCφΎf‚nVΔO€ΒίՎ‹΄€A³Υ‹lυέ­VωpΫͺΙLV• M³–^ΣušΣdŸ~o^iμŽγŒΎowΊqrΎ€^°:8Ι­θ«‹­¨ΥŒD`C(~žΞ€”ΦVΞθ.ˆ—iN·ΣΧn•<“ՍΉt„—yΪ›4&5ϊiΦ”—-πΗόηЍ7.τΒTυΠθU¦s‡Ξ>[šΝΪlEΐρβΆ΅FψnwΊΊ YŒH\Ί•'ivΎΩψgšΥ­ŸΙΠfh)œP¦₯#Τνΰ)φϊ'Εηšί±w΄¦JΠξ›ΰ%HΞ!p όόΞ’΄^ο ]š¦uΩόgΞp ΪA«―{ζ΅ΆŠ>&`εsJ9Δθ Τ ΠO:'θZΫΉΨFυ`«»‡vsΡ·ςβϊώ⟠:ωοκ“’ S‘ςΕΕΗΟΎ·„MP›Ι$φφ–΄πΞY`…Κ½χ­(ΆBQιA0> Έ§„`Š‹—*\ xKbήΦΕ;‚„ς'Δi‡«z¨IUΠ,₯Ο'ίP΅Ž[ JI.Δb*οr€ΌQΒBŽΏιfQg[[ΖJπ!:Xr‘«θ0­ί‹0b‘ [X2D"I&MVdyZΣΔTΔ78MqζϋQΛWœ·/œ€%ͺεΐZΑωdSM…šμ5ώ6εj’έ‘™‘/JvJ™¦Aλ2¦Yώ½]ώ:ε)XπΗb―^{νπξ9d S(’A³Τ· ί. z¬€Εar`JS‘Υvf/+τT’ω€ν ܟ(EΔΠ(ΈB(1Pω9ΐο“λrm6'Π…{+ώ€ˆ@ζσŽ`°ž&@ZΏ$M΄&ΝςSΊ ψ]KŠ)ύΉ<²ΔοΤ뚷c"ΑdΨ‘c;7Δσ Fˆ’“eUjZ)v‹ ­UνΦτ Γ /φ"ƒμkγ0ύV;Cΐcβ :ƒ1+Χ.?δ9 (.a°LKYi*έ1§ύ έΖ» ­Ά~h΅>ΏΟΉτρθ₯·Ά ΅²Κ°L΄%₯·QΞ‹τaυί•«QΚζkΆϊγΣaνά€uΊ0Ίn};d ŸdS“:―«}3pσζφύυθQy΅Hσa-[κUΊΛ͜ΆΖΟtάinj=΄θώξΓυ Ϊy΅5ΝΑqΫZ/  .OUٸΜΦc'繝“Άu΅»χΓ€½έκ_o֏rοfŒΝ75¦¨ΆQ9£/$ ΊSY5‚w9εn£-2²₯XΖ“mŠύ.ΨΫΥnDFCί/ͺ²„&r-KΘΛ5mδπp΄―5δtWΦΚPΎξΌ[Ρ άw *;65έ~WC²:Εβ΅σ•5lχq6’ϋ€b*ξΚI ΣaJ†b ύŒ'ApšcέΑζ>»BŸξ¦ΡτI ·]•΅Kkδ!δΙάLω$³/t\ Lς†8ZΞ”"JΕ,Ti%ΟCi―/η(žd»]盆uΜO\ηs“εφΨχ“Ž0ΞΚπΒ€)ΡΆ~Αd'Apͺœ…ŒΨ—G1&†"<_ΚΓ9ͺ@—`ƒi€"F,Ž:Jφιv4*dΧSν3,Iˆ”ψ¦;SΠp@φΓ*'9Ω•‹ͺΨb†ΠKΖ‘ Œα–|Ωϊ;|ΉHατJy.ά^uE~cΜφΥΥΥX3½StΊ³ˆή—ΣtFΗΣ“΄:¦Πa―3“"<κ.“žΐcšα_ρW9Ί‘ΌσŠλE± βCψ[qlfl\ζUΊDŸA²ηπ€)*£‰bΎ»² Mp›)I]’p,‡ρμΗH½Ϊa}^κΓj/°$‚Ϋ6ΗJ)\Ž•’Ϋηζ \Ω5z΅st+›a` 1W΅?>Z2ϋ3nݘ!Pjr?ύIEΦ.Ψ‡ς[­ΛΜώžfέ‰mΙΟ¨ήΟ”—φuΊ=θι²r»s9νHΰsŸ³Xt!ύξDΧ:Όόl΄˜Β#σ_~φ O endstream endobj 379 0 obj << /Length 2227 /Filter /FlateDecode >> stream xΪ΅XλoγΈžΏΒΘ'ˆ΅"©η’E±Χέ½»’wΉ‹³ν‡έE K΄MDW’“xϊΞpHYr”‡ †‘απ5œΗo†τfλ™7ϋωμ§›³wŸ#–ΈIΘΓΩΝjΖ<Ο~8‹BΟ Ÿέδ³―Ξ}SΧενΆ©ΧlΫ‹ο7xχ9Ξς=ζF1,©‡sCΞ<³‰ύΎϋμƒ9s œ4ηςhju[¦£ΩG;E‘›Δάξτ[ϊ¨Κ]y1ηηT»r)’λ} UΙ–Θ¦o#Σά=Ϊb,˜ΩcΞ|Χ Ϊ©½SΫ·Λυ»Ex±Ex‘Y( ~υ’šZΚUέHˆͺjMyΪ₯ξΕ܏…σλŠ4?–%Ύ0»oV—₯¬Ί P²ν8eiwΫm‘dN§Υ œ=uΑRs\μ#ΈλƒKΜs“  pœw€δ„ZWp œ$‰’#‹X/rΊκ@SOζ ό/±£PS[PΚΛ†cIβ‚΄cΛUιΫνφχMΪ€Y§MGΞ=κCf]mΪΪ”πm»dAΖ!˜ lΩQ3mι[ͺΆ%kκyΰ€ΕNΆ`ӈ{ΞΒο6ΚL¨·ͺ+Γ­'Lξ-‘Y™Ώy6‘Μ ryΨSΪHPU²΄3« ·C_ΤΌπ]FZ²˜σƒΡneΉνφ·MύΠΎΑ sΖα`Œ¦/6υ@‹=gY€Υ‘M2=΄¦CΧϊ IwυZvΩό BΗƒ^WΊDύΊ’ρ€o\C2§»`ŽV»^¬P;@ιpD£Δ›λ/Ÿ¦ά–ƒs‹^ΣY @ρωn88T:ͺ(ˆͺκŽ}Nψ6\¬΅Α—€qΜ€΄(πˆ~LGŽ2=“§awr~ώπΟΕδi"7ό£Σΐ’@¨‹;%₯>3FfϋγC/χS‚Ψ½`Ώ˜j<δΰ’Φ« i‘ψ7ΖΓ³ΊΨ•Υkž ©,Žγ1†u–ςν8r£·Œ„¨ι¬Ί¦.ZjεruΑ@Κ]Ρi9΅-‘ŽΤμ©΅jκ’¨m‘ff1Γ–†Q|Ψu°φS1ΘŽQδΛbž½•ΑZB8&…ΊΓe$q[.‰^^vν:jμλYZ±k­ΰηΖ³UFδυ3ˆ« κΑ‰DbΩs*χ…ΛΒxjZHŸμ%kˆ’π2βYm ­•n\Τw#_ko3!Πν μ=TND>†μ‘κυUšu%QAμΘ*«1Η›f.3U¦ +ΣζΞπ—j=δ ΝšμY™œ§ϋw%»1`ž–―Aφ¨Κ‰ΝrE‘{BΎ$υΐžŒ; šm¨EΑFt·ίκθdL»†ζ} ­4³‡‹S_{‰άΔ‘ œF§fQΨ`ƒpa35v 2υ„sΫjPΛq9:"tc½ν>λK¨Ί™/(m’WAΥw―_Η%Ρς„Ϊ(zΟ§κœLχ–ΈδηΌ—rU›’ω@μ,rζ²KUρŠρ0xZ.“4·PΛ[CνΊd…°(φΔZš ΰ UŒΞŸΨΠΕyS1†5c!mΖE؛Xk‡s: ο›o3εGΥB>Τi“iΘ€OΏŠn-S, €ΑŸφ”u5`³>Wr*›"fυ”>‚¦ςαZηΈQeΫ`vΐ$…"ƒSG Ϋcmω,~ƒz¨ΠPT«Θ»JuΉPκσ3―³^Ύ0ηΙωΐοIΝΑ±a.›Ψ:E#±4ΣΰDι²0ešχ€>Σ&l₯νIΧ“§Ψ&HΗθΫnκ‡—ή ’ŠΠpΒ'!•ϋόTL₯b„SIθFJP@ω-hδ§Η΄ά²|°˜|ΐˆaEΨ%Γ"1‚΅|SΉPHpΛψ˜ΎΝΊ‘™š’φDΖyΩešnΦޟ#l[θFKΘ',2KΛjέm¨&£΅ϊa Ÿ$0¨»₯χƒ.I„Š>₯n*ύ8΅ν3œάͺrzώΩ§›³œ1ίXΰ$04\ψ³ςμλwo–C'μδŠ$ž=θ‘εΜηΔ Ν›³ΕٟΟΪIΏjδ }6ƒŠΟ\<χͺ5°τψΙ―₯q_|”θ%œ•")T  Μ ά„pέ\@eH1=ƒΐ5­Φ^ns‡%ήCυ‚†ΐ#χOѐΦΔ( μb Μ μΪ¬Q}`O8ώψώ…Ž»ad‘kιAν« XŸTŒ>Ξ^qqμŸτ}"Υy1=η& υ³ΰx”uCΠ„[(ΓΖW-€ί΅ ­VΤxίΒΑΚ^δ0―κ’ Izδƒ<œ+] R{Sωϋ‘ΎfLΐΝT+Η Œ—}γ<ϊͺΓA·π|ΒfαζQΞwτΥϊq[¨LuŞϊz˜¦)†Ϋ32ι \(N@aχͺ©+ύ¨{Ν•Rα*S‘οΖaΣΧυΥΥo·‹_ώ}ϋΗυΥΟן‹iτ£ψEτž‹2zttρ?_₯E+Ο§ž)QŒd„γΕΝαΖ—­q₯ω’~ΙlΉ}“4†kvΥΡS}«ζΣI\'AΔ/͌Ά― l(οO¨βƒΠ DρmŠ΄ZΏ―ΪΫ^†ϋ7έƒ¨#ˆΨsJΐ†ž 5–X­!…Zï֚f˜ojΎΧ‹n—«šPΗΘe]ί™U6» /³άγZeΘͺ Ϋ“―@<δ.”7φLkΩ]ixI³iυ†έΛ•2Ϋa$œ?σώ‰w{~€’γϊΐ!j£p\|iΣ΅|{qπ#γ©\sτR;'ώΏ*”η₯x²d ‰:„ endstream endobj 273 0 obj << /Type /ObjStm /N 100 /First 883 /Length 1920 /Filter /FlateDecode >> stream xΪΥZ[o[7 ~χ―Πcϋ"KβERtθΩlΐφa[Ϋ‡Τq;cΨN/~OΧ‰Ϋ“ϊ€ν€::Š"ω‘Υ”ƒ .)»(ŒQ\*ψ©:Žcv"cqZρ>'WΨήWW ή—θ*~)«‹1εAΚΩΕ€ OŠ‹2.*X+h Ή˜^L–1C*ΑΕνhjΐ«Š›0Q)Έ`ό©˜ Œ©’KΖμ]’Ϊ ΚTώ%ΣT1$άU|(ٞΨ° ΆH¬’‚IŒΖ–#4 f:1Ή³MgΔjcΒΘμΩ(G‘μ5Θ ;ώGΛYΚφ?Š: ˜p‘φl8.0? >ˆΈαjΰ•Ψ">Χf>|•a†˜0U5ξ&~…q`ͺ΅(ǁρuŠΈ€ξ ΜExΝnP=hR―ĎA ~Υ±˜”bΑbD«­…°R-q@ΰΐ9ΩΌΚΆΌζνIΖ…-†Šγj‹Τ\m1ΖΈΦfENB5qΔ‰Ω‹ΐK`!ˆl~TLEΕIβδ€» §!Ωημ4šϊα`š†κ”AH[ΕBšΙžΐ!s†–!“,…`A­ Mq9šNΑ+›™IΙe¬έ4Œ 8ipYLέp𬦨όΰ‡Π\ΑŸWΜCٞ€3ψ•`²C3%ΐΙΰ`nC™p;‚[—dΖ„Šˆΰ†€ ΔΙβŠT†Ο>žέπ“7γΑπρ|ΆΟVKgξάρ`x<^ΞΟ£ρ²ZσθχριδδΡόƒ{nx•*ή0W8ϊR_ΐiΰΐkς‡³ΩŸ7ΘΖ“ΩλQΫ1·ciΖ—ƒ6$kΎ ŸžΏZ5χΏMf†ζ‹Σρ’‘ Όώ2όuψψylnLζΡΚ=O’>­‘½Y/•μαiš €θ‘;ωHλ€ξa9 Ί[W£Υbypπn瑎{Ώ˜¬Ά kœt†ν&ν±+Ιυˆ΅˜G₯ŽΔΰnBΜMΔlΫΘό#dΊΦΉ°ΆΠ-܏8l»_β^;(#cΥ‘z±EJ²ΎXπξΝπtrφj~²8½‘Ά.[gηΫ€½ς'μ°RΌb-@UL3b˜‚:’xM'ζ΄ώHρZm>lC΄'`Φ΅›ΥΞλ1·γZTkΉGjGnΗφ;nΏγάk!E/‰¬λyέυ™aέέ|ύώuτΗoΟV{ή%ΓΟ €t!‘ £wιύδtυΟ²_™ ½–+™#Uξ,ΣΩ|9±~BΟbQε˜Q°FΗ‘½δ„1~/©’4¨&ΈΩρ‘Ws)ΐ]ΏΠ]ν[=λ^³υ»cαΛ^³5ΌC)ίΆΧ|!DΫkώ’ ύkγ²ίŒ ±ΪY\ΫoVΨ₯ζΪ­ίά³Ϊž³eϋλeΟΥ }}ΟΉŸΞj" 5pΫYMΨΒ'Ρ/tVo*DψΦ}§UβΧζ6½ΦρΆσΆύr›΄ΉLΪ\&ύvΊΫ3\EbΌμ1+±,ί±ΗL(υ™/›Μ”“§Rˆ&sƒ-λΣ΅Mf²¨L=υ˜ν\·si­7υ˜±ω·¦η>5ίU™ΧΗIιυ=ΊκžΈhuϋjΑ".‰UD0₯ ν[‡°ί5s(·jΒ’ήbg7mΆ™Q8J7b!λ"πsEύΪQdF­K]'F±PnίWέτΚ~Ο*)ΧkΊ>bάΓCs»ΛΘν.£=»μωŒRΓΊΩfRΠ­κ7?£”ρΩώηN›@‘RΊuω/ΆΡ endstream endobj 389 0 obj << /Length 845 /Filter /FlateDecode >> stream xΪ΅V]oΫ }Ο―`ΩK"%π·΄=dZ;iš&MΝ΄‡ΆͺˆMoΨN1iΊ? Ψ5­—ΆSχ`αάΛ½η^Žΐ πiτa9:9 }ΐ$$!XFz~’Αΐ#`™‹ ρ§WΛΟ'gQι)‰BεGcnEUΧ‘KΦ€GΘqΊ݌°š"€ο½γ(i1ΊΈB S›ŸΪJbpΠΠψΓΘχԜƒσΡ·ΞίΓQ§ΰύ0‚‘A„θ~?ΌZ '8•RδX˜ΔΈMι\Š}*χbκE6“Mͺ΅W?Y*kνl0Γ0ρ ‚—dhŠα # ϋ6“¬NEΎ“yU:Ήάηδ4sœ(B¬ωy^δœ “†¬Laώ|QDZe—(@κΑλx'‘J)j±«)A“½4ΎΉάšYQ K_^+QP»^P―f²ΰR°C.΄ηυ™ΗτΓΜI=™cΏΧtΓΉx̍Ϋ)Έιš€Ιψ—²BcΥ?~!¦M6ϋ‚•Ά#ώρψ»½ι0…ͺ5»ͺPΓΤνΕU%†M|mβ{zG‹gυσιŠ•CIo¬β―·Σy€T‘·¬4³}Ν23«μJY•sjK«Χν½Ρ/ύCΞy»z Ώ-bΕΜΈ¦ΌΦVaΝΥυN‚d b…L©¨Ϋ²5P‚a‚m΄…4ΎήΝΝ¨M3γƒbš…±qΣϊvάx›ƒρl<{ψ{3€Ϊ0݊3ΞV\[χγ9z –­!ωΏ‚驆ρΌψΈž—Μ―{ΣΆhF₯­EΣ­΄`φ88Ζ•2Ι¦šΧK„g'Δ‹ Δ^ς:ϊκω1ŒHό*ϊzœg”ψΌ²φιz7 ©;ΆA{ ¦n‘ϊ”šu½ά[]ηœ n輜‹2ΎΔž/Η.:4hVρΗΨr<θΉ€.τλb—VόΊT΄Χό ένX™ Z˜-~Άψr~:ΎΩW’ (AΙXΖ²N (ηέ\i"³Ί2ΔAέΗ¬Ϊ―8λΌ¬hϊ«ζ΄ή>εΧΊͺŠg¦Tξ‹kΉŒf.g¦CμNϋO0θa'ͺ`υy»uܞ6Ώ=یνK©Ίύυ…{ά Ž>μ%#Ž…ͺ^ endstream endobj 399 0 obj << /Length 2286 /Filter /FlateDecode >> stream xΪ½YmoάΈώž_±π}ΈέΒΛPΤ{Ρ’HΈ½’—’ΝζŠβr0΄ν,‰ͺ€νόϊΞp(­¨•νέ€θ'‡ωΜpήHρΕν‚/ώόκνζΥλ«Π[Δ,D°Ψά,Ξ™λ‹0ΰΜwΕb“-~]~i”*―ο›Ό“«ί6}}Œ?ρΈΓΒΦΣs…S^q³ΜuGsΧfςZ„Θ£Oή4+‡/oχ₯¬ΊΦϊzΪΎΎςό1XXΘ‰XΡ:ӝΗ(Γ€Ε‘θQΎY­…Ο—Y%Dέ4I)‰T ΅]Ύέ†Χ)jI+ΛΫ;6ΩΩBΉ6[― €›Ό§Γ½Κ‹ ΈTU•L»\UΟ<V&‹Ό<Χ;œ(WΔΛ}+3’Ά­¬“&ADΨϋ²ώ2)φ²e«΅ο…πύ }²/ΰΰΧ«εΠfŸsμ0ξΗύŸΧλf Ρq™πœ~VΑhD£JΆ6¬l‚ ΫδsA/‹ύΐlΙ}Ύωψ 4)χ3η’@9ΞΥ°TΕιϊέμ9(JVΥ‹–κ†E^Iβ€;%₯3€-6°α,@Ι˟UΫ™©ͺ,UU<š5σn_«[ΈγƒΆ/PέΥŌΎ…Λά(μηέ(ƒΰS•?Π~mχXτ:υcζ…‘ΡͺOŸ€ ψ{”₯½μϋΟYμF ζIT`n[¨pα­<ΎΜ«Lι-[b@Mޱ<ͺ„FΗφ±VΙι§ϊ±kςκ–Ά%—Ρ¨GZζm;Œ[.γ‚―Ψ.3vύ?¬\"›γ8ΛΟ7ΜTΧΑφtA~Ί™9¬˜‰ΠΧ›Ώ}|?g:‹yΠΟΒ³|D°/+’ ής>/ ΓT[Im^₯Ε>Σ„‰‰νvfΈS5θ5£ΟΈ/ͺΤχηΕž`ά nώωiNH1#ΗΪX}0B’eυFF>ΘθΗƒŒœDN/btcΔ¨ΩF`Ί΅LsCf—s!3rYΰ n`ξ4‡Μsο: ιVΞ2ΉCc“†±3„ͺk՚b„t!:Χ>„Tm›·9φΏ¬BΩ`Ά3žk !<©k |3ζ³( Ώδ>sΗ6φΓΚίeιQŸdιαΘB\%»"\*T ι₯2UΞCήv: g°Xίs—?ΑΡΜ!r\Ξ’³mΦFdT£Ή”Cα„8Y‚ω̏…:·ͺΫ™š%i‡8Ÿί+ΨΙqB5%•’&Ά‘Ήϋό€€Ί‰΄WΘΝχi#1͟ ³WέΪ_ΤHwI•#ρd‘Xχ»<έ …\—δΖ'†\ή‡φ]υ’φy’_}kVΦH'B6?ι³8ͺΝ-=DΜ ŸΒύδœλΉ.‹άAξ5XC,ΡIG§)b$$Β”@N’ρτ±₯]~uŠ30Ρ$ΰ°Νgƒ~τ²ΤdCΩ 9ύ’lΟಂής.œGy~’³5)` e$Ε7θ,)Š9wƒπκz#…!Šhσ"SΦρvd"SXΒ‡"ΎW₯ͺΉ{›χƒ °ς¨›‘=ρ(❌‹hRχΆiRΛ3K_mοuχ11Γ,eyΤQ½kυ Fγ6Ž€νQ†jwπ ψΏvžLν·…|±6]?ƒ“d4.Ώ}μγ"¬;”† B9΅βΏ[qΏϊ6IοΪ"iwσ…‚+Δ7£71»nd*³AŠaΓ©(ΎΟΒψdυ³BrIsŠΡ‡ΆΥlΥΛ—iη…#Œό:άΓx8LJ2 ²Kh>mΦ’αςνί6C΅Ϊ Ψκ{›Wex`ι›&|4Μ9€|{bŠΕqjƒ%+\/‘t ΄Y`-bd㜎·l“•€Ϋ ™γφ/10Bχ ς·ϋ²6oΐΠ†s©He‘]@ήii<ο¨5%,Μ„΅RIΜα#κκΈ-ΤΝ₯ˆ, %ΰπy_Θ Α#U ˆέ›I•ͺƒ]㋁V½~3x±ό˜Τ§ΥΎΌξv=έ€>μΛ­Ϊ‘ X‘T΄ˆfj‘‘₯` „9˜‡³;°¬(‘xjς€ΘΏš1Χ€†΄S ήH=ΠΥFΥήlώ”M.G4±cέΈ‰ƒΦθQΑ=8u΄ZF=½€:Έ N²οΐΝSΘ§Δښo΄df!E-©Ώ³€ššοίΓΣίΞΨf ¬fΰ†ΗΞ>ΠΊQ·lΟ8Νwy[ Š::vB3¬’{Ϋ€ωψ^θ.ίκi ψΓγMΙMk\(*ȐΚΖh΅γFUί‡“‡Rtto£(α[τΘ™γ%³9qΫ=\οΚ;Ί>`ΟΘ“©TΏϊβ%7τMΞG9,h-1χuFOvΨ—)fžΟ/9η4|x6yh—έΎNEήaoθδϊ’₯I ·Ÿ€ξΑaΰk‘ζƒ–ZŸZŠUfx:°³T~z€›4I chΎλ9¬Ϊ¦Q(Ο:}O U•αHjA‚κΜp©mε nπ9Δι³μΈΓϋ JΩ°v§ξ―Η<1ά ƒ0=Ό―»vyΜψδ%δ΄Χ‘ς.wXMςnt»η~C؞φλ;Y7+Ή)žοo3ˆόp¬•|ΈR[¨κΆ―–Ϋ}]«¦ΓW™‘0Ή¨ƒ6όΓO“ώoΐ³΅ ΞŽƒ™‰Φoy,ΰ‘Ύ,„}{”u!Ÿ₯bι5‚Ak„Ž> stream xڝVKoΫ8ΎϋW½Tj–OI<τ’Ν’Εn€έ8θ‘)Ε’c’˜•θ8ι―_Ύ,›Ž8{08žΞ|Γy &w Lώ˜}^Μ>žη4α€η8O«AΝ“"‡€œ,κδgŠσμΧβϋΗσβP“Π"7vœΞC―T{³ν₯7μΔ`―Μ`pτu1ϋw† ΄χ) Ι²ύό“ΪˆΎ'FΐΛdλΫ„" J έ$—³ΏGkΗ§ ƒ²Γ09EI @Kz D^Yΐ€—hή{+›cSΥ‘Zω³ςΗ5„ΈΞκdΐ9g—μ-!ϋ ‘‰ ε9Ήθΰ}Γ²—χZͺ. j\œε2™£\_Αš[k8Μώ΅ζ†κNLΒx+ΞZ λYΎ!ƒΦ»BŒ.œ1―όψΑI`Μ]ΙFL „j²9ƒ0ύδw׈Πξέ€nWΕͺgΣzΥύ½θκHχόμΟΛ―±rx€nΣήθu/ͺzˆnψ ƒΔl~Θ˜OD½Ύ˜’ wΦg¦w›Vtzx55―§δρ¨e’ͺ*rΣ1xΧ1gΎl–λͺ―–ZτώοC†X*–Zυc)82Ήž{sΔ§ϋ|žαάj;·*Έ_ͺ3ήm›D5Ύέw€Vo@…φΥt2ͺΕΪx"΄H]±Z¦jεΆΘ=ηΰΥ¬ΐΒ΄μΝ @6Ο!NRƒͺͺmUΧ<›R―Ν-WnlΖ5δ; ‘άCιΠH²Ψι­T@pΥΙGοoΠO>¦Β7+ 8κ‘a–n]Γ~_9 'e¨Α€> stream xΪ3ΆΤ32V0P°P06S02U01SH1δ*δ24 (Be’sΉœ<ΉτΓ ΝΈτ=€Β\ϊžΎ %E₯©\ϊNΞ †\ϊ. Ρ† ±\ž.  ώψγΏϊό™3Τ3πqΉzrrJi` endstream endobj 423 0 obj << /Length1 2836 /Length2 19770 /Length3 0 /Length 21200 /Filter /FlateDecode >> stream xڜvTœέ²%ξn‘qΧ Α]ƒ»h qw‡ΰ, ξξΑ!Hpw·ΰlϊΏχέ?Ήwζ½™5‹΅šoΧ©ͺ]§Ξσ}ΤδΚjΜ’¦vΖ );°33; @lbv²³:=-9™UAζ.6@Gd …šZέΩτίy‘Pk‚,νΐ|ϋ‹;‚€Ξ‹Π¦ξ(=μœv>n>Θ;οΏνω –&@ @ΝθhB‘·³χp΄4·pζόύ 3‘‡²q3C’9˜ͺ@{ €<ΘΥώ·:3KG'g0ΠΔbόηƒˆΉ-Π†ΕΔΞ–žιKΘΞώΟ„Ξκ)Νβρ,vŽζ§Θ·Μ…r«όΛπΟ­θlώρ βbβdΚ2u‘gω77yKΨ d p›‚j²ς3ΘΉlώi‡8€A`#€•¦cΐ_‡&)τ?Ο[6…³³=+λ_qf-±8™±€AΞ¬τK‚MΕνlmA`g'v6€©₯‰3ΐdn Faύ+•Ί€Fv€)ΘμŸXθμhιΠccacc°ύυχχΣH+Lνΐ6Ώέ!Ν°j½ΧSeόoTτ·³˜˜;ΐ‹ ΐΜΞΓΰbγΌ{Λπωχ€Κ@Λ*Šνw ,ΨΜπ―βM]μ΅Χ @χαΡώ=•’3€‡Ίί§¦ΟΖΕfωaodτ‡I=Εώ)ƒ?ώKF’Ξ?ΦώVΟω2ϊ—tώςωOυόΒ’αωΏιζ/ŸθυΣ,ύΟΟίΜόsςŸV)›¨ξω ϊsό—SR@[K-ςŸžZ ώί%RϋS¨²Ξ@HU’`s›ΏM–NR–ξ SeKg €ΠΖ τO»Ζ_›±±ƒ”νœ,Ί(!#ΑΕφkκ–&Φ`“ΰΏ–@`Σ(@R½©%ΨΐΑΕ ::=PΨ ΓΜΑΕπbXB2Ή@ξnV°3$`οβμι #Κ_SΔΝ`ύΛτOΔ `ϋx¬βΏΡ;«ΔoΔ `•όρ°X₯~#v«τoΔ`•ωήXe#ŸόoαSψ |ŠΏ„OιoτΒ§όAT#ƒΪoΔ `U »Υψ 욿„]λ7‚°kFvΏ/Δψ7β„°CFδhιdύΫnόA\Œ&ΦNY²ψΫΚΞΑω—Ωρ·„ΕδoΔIabgΖίLœYlms³³Aaϊ7| ِ©Ν9Ω!χ6+θwRˆΗ?ύχAώ΅ξΰ™ΩΏ³@QŒ Πφ,˜ύΞρ0³tύ#ν_Λv.B\Μ€οœr φ π›εR¨Υrλ? €7Ώ ζ†4Αζ―!ω½ιδΥC^¬Ώ©Έ Ήΐαϊ½a5ΨΕΦψ―7‰ω%±CZcχ»hHN»?’ΨΩ!›Ά½ α°:‚ΐ6 ³ίέεd/«γΏ5ύ-€{#δώϊΓ•ϋ6K;Σ?( ]tψ½ΟΏ Θι·Εί©82ΪAΎ Œmώ„X8 vΘ·‡ ²'­εΏ«‹ βyν]€Ζ,δΖfύ]0δ½ΕκlαϊC8»ΩύΙας[ߐ*ώq‡;™Ψ9ώΩHΘiΊώ!εΉύ1%€ξ@«ΗržΏk†dς9ώ³‚»'M\!§ΰό―Θ%ϊ/lf Ή©A w ΚΒ¬ G«šmwU’ΔnΜ;cάΰKΚKΧ•L_ωVά؄ƒ7§¬σσ«μψ€κ4γO°]»ͺjqύ,ΏΈožΜ'alΐ‰=Ή²eΏ9έ7$:I˜cα}κ­ yΧeDδΣΝ,:₯”1PP’―ύ!²˜&ˆ ώι°/p“M―Ά]“γ‡k3ύ‚ χ½='°cABωϋ38Ά¬IaΩ#iΗ#X‡­Θψ«°'0αD©\>Σ(W<&ͺŒ€ΕjŸλβ€€l ΄Εƒ1Α’S3*!ΡR`ΥWΚ΄/Εz„π[cŽ}—μAπ-Κ §8vς^ho*ο‹ Œ•+ΡΆ/Œθ2@ŒΐΆ|AZ2ΚͺΞ„^@/•± uΌΈxΗ>{¨*aΠQEA3ˆ8Bi§ΣCGμ9αPMΕ†MΪΣψΡ/Ω Σ―ix€δ\‚‰_€~NIΙ…Θ&™βλŠ—ΆΆϊhελ φVζΟ—¨V=h9ζhWζ―³‰Ο5rρ—:‡Αΰ± gW½έQΜδΦ•8ο<ηݎ₯Ξ-ΌyN}Œ#δ’½žJζφΈFί|‚ηΦԏŠΜ-Η[Κsιj ΐγ…+Fo…FFΑaŸjΎ— ΆVMr>6Vj!νΫΆ`ΖI$ΊMΥ/4Q/’/„)’κυΣέ9Ι†' ―Œιsντδ(£¦υw –'˜†} >Q1aΠ—E΄Xm°G… ‰"œΡT”»Υ/ XšόΣzΐΖafwςτ(/RφŒ,IΧϋΞ ρfφο5T²ΆΌΥΊ_ΠwxμψΧΟY§‘ηύ›ά£βήπ“QUΠ§φΣ>ΔcςοtΪqέ˜Μήs?‹ΡKH_θƒυρ 9Σγ}·ε”ΰ)Ετv"Γn j{ν©ς5ΆΒ…€(Δ~tUNw.ο7¨oΎ’Ή4ή‡Z©*™C[2]κ·ψ­bGψˆ4Jϊ:z€žψ¦8ΌΟ΅r‰’ό¬+‹Zf›² ΔξžGSBiΆπxδΠΡd'tρ±XB>ϋGATι–r(g0£ΟΥ.Ε«0E,Ÿ™$Qε%!&ΏRJηΥX΄8—UΛ₯κΏD‹c…Θ@¦cj=)Ώ›pιIΚeΨΩΧςŸΓ)¦Ι˜?‰&4Ι”s] 8τwξMΝΈΑ»mo§₯`Jσl¬V)šFαέί UΒH†ιλŸuιUΛα»šoyΗμku{4dδ{qύ†_=ΓΓ²‰Ζ³ω Q™οQίqϋ€W‡*pd…φΜ4`zpφfow™^'v΅d^šZΡD”qΦ­ζhdX #Κb첈€»ˆΒ'IwF‘ΰOΌtΈ^“άr?A›Ώόα•EYoΗπH³^²μ©kŠ/Χ»“Ϊyƒφ|$’μΔ%η”iKθξλδ³υΌΔv“ςmέ[“ϋxρ6qΐJ)#)Όω&ν£·υφπλκΒDΌ¨9βηΗΈeEσ=}? NΔPε93pσ(5m0{δϋP¨έεVνO[NPωΑ!'λΟΕΖόά+“–aΣ‡]ŽΗΩή|Ϋχτ,γR»h·cψΌ`~£ΰž:‹³3…7D=r¨ΕџVzJϋΪ}Œ’{’ΧΚπΩ0υ+QEΡD[fR‘ُάWιc,ZvΏβž[ιέςE³X±Mαφ,1SφσΈxΗh ϋ WΛ]₯ο5x4΄{­ά“η GΖ0‰\;ΔiδŸ„_™³ήCge&R0έ―acfXV pΘύ4N₯IΥθ\v$H&`Aλ‰V1s­Kœ`c…χΟeŠΘuğB~o>›CFΧη|Μiλ…LΦ[―֍N œŽχsv”RΡm †ͺDoΓqUƒEΡΝ\SΜsμKχτj\lχ6R'‘ΖUœσΗ)T•„bΡέ†―V ύΠβΠκ=CIŸrγΜ“5ξ΅’ƒ/_—{ι—#bΜ…ξM·S n΄hΑωΌjU,oαΪ!hχwώ^­όΌΝDrΕ™M$ “ZJ [₯ I5ΙΈd,½bάI~»d);j[\(+σMYΰ™L§OœKχ$$‡Ύ₯@ΞΟΰ™UQ0‚7\0­Μd܈ͺχ °`₯<ΙD`h%Š΄;Χίΐtzr.ωš@ŽJΔ%t¨Ά… ·nŠD &ILΖο@NΉ;) oUυwνή'A1ϊ:䨩qTλΘΩΑ销ͺσΣgΛzBοϊ}@›μΫb=1倆 †#΄ I°ΨβΏδ Δ€x;–Νυώ )ιs(yΊγ8z”Ύοm:Eށ€8SΗΦglΣ#v šcwΣ‚›΅Χ΅Χ]Χ€7zξυ&g˜gμ{r©…qbƒΪΚχΤ>+=oB‚d—bς*ϋjΫΜϋ–ϊh•ΡjσmΛα1 c#’91xF1[Ν„ξW9’r vz’~šΖVf(©Nu«n%$RlΈΓ?‰rD„FrΆaJ™™Μ‡σoΈ€±:‘ήδˆG£>Θ[θcE€βΡίPž)U3pΣ,¬½bςΖΨ œΟ½6^¬4ͺ.ηNؐΈ†_ML_6N |Ašœ~žεnσ)ν€ηs5%>Άύ« Ϊ5jό0ΉΗοό¦L(XΛΈ"TύDΔΊm.eŸu Œειμ(”Κ–f;%_ςΈVV™u 6wΠŸθ¨f ‘•[΅Ze f½pσΰή΅J³f›?Ϊ]kRΥ=— <Z¦λG?zΟ_ΰ–"kδŠ0œ%o<!Γ·/Ε©=K―Χ̍`< |p¨HXς*<½ž!ζ‰υf5*X‡PyΌ*…Rg9–ϋLκSμβAΩl‘<Ψ΅ΖjΏΡ@>ŠiTžΡ7 Υ%š3]νΧ$TοuΏ΅—·ʈp1ηβ™Ϊ5€F9!ύšŠ5>Ζd―VώθšϋΩαšΔOݝjŽˆ›°o»υ[#\p>B<[5ˎ»―σ]ΈYθhš6ͺ‹Ζ~pϋϋxυš“΅DΞ‹4šΆmίe!c 8ΣΑ–§/–΅Άι€Φmu¬½/ ₯Sέd@>ύkΌΚΜR?!™ΐ01Π~Ύζ5X~y;:ζαυ‰b‘­ŠͺJ§ΓΘ£,·y/ΒψτΙ~iWΞ;JNI7ͺ:Ζ!:τ"‘½’ϊ~Ό¨†T1‚τœΟύ μLUήBοzs ΔΩA»A’ε>L¦ΫΔe`;wσΨ}#ή9sα―ν°\+υ…UY‰Œ|₯ΰŠe3)X6Nš4Ό2Lθ‡3΄Ηlο&ΩΛ’ΝςQšμv*‡: •¦c1…ρrx¬5ωόvŒMιdHK¨£—鏣c>bŒφσ·Ρ(o ψ±€ωy¬΄P·M°–8)<;5μΦέ-—?†·VΧrWt©©kΊ²ΨBήWœ7Λ»crρ©ƒΨψκ(α‰οOΩ0ά’ίΈΌbϋј»Λ`mO5ΧL›ΐMΩΑΠgx/‹…`A6z€žΤ…Ζ>51ADζΕΉμ‡3ύšΰ; ˜‘x,`¬4„Η3zρ^ˆi/υΩqΛΟ!Ιπμk Ρ•¦ SͺY7Μ:rcγ,›ώ}…Ίζgx2$=jGΒΉ°ΉΘXO#}HΣgΫ瑎w±½8˟w* –₯θg°₯fΎτ|“d98ghOΣ5QHΤΉ¦’0­ ‘γt^~ͺ²Ž&Έ_eh&Ÿ†ή=ŠhΎύ‰\eΗΏΩ lRP¬V β7γA ”ψf2Œά*5Yσ =fΥυXΕXΒΎ A=Nρ=^ςn£ΆYG'_&œ‹ιvΖΨΔΠ‘ζτ4 +΄Qς…žeWYe§₯'c–bΈΗuxΌKސO< t‚b<Ξ/ΚvΘΛ#E‰²pTΓ]Ό-NdŒƒ#4.f©˜ϋh—#τΐ¨ΠΪŒεqΪύ)Sv±\£_α]vωδό•xΙzξΟ.eͺΤ·|dA 0Oφσbw&AL£MΓ³ΕTφΩ}s]` ΅ΫR»–i o¬.<­J~ M˜ˆˆΈ1η₯κ₯Ÿί'σuΝUΑ35oδΡ2=α}3~΅0wS"sρ–ΥδβR†RG3ΨΘΉρXJŸ˜4ΉFΦ |»+O~j"Ο³'­6ΤΑΑ‡X-Ψ_C³ύώσ7X&ή—bqwΰ]¬±V]₯‰χ ΄i–γ͞I-eΖ…c™Ό5ΟσŸt}l¨κ%]ŒΎ©n[ŒΏkͺΚΝr§Uσΐ*ΒΏ1«·½{ςΓΗιΝΊŒΓ;OgΧΕΝk†UωΧ’oq2ε…ί.—ž -«Δ0ψζ9ΐˆ =όd©Ο¬‘[­e§Ρq%B#φΪΜ.’fΖπkpœ³R#Ίΰ넉8sωyŠΖ¦μ₯•ς8œΨ~ΆΟœ+nΖ5‘ΊH"Ψ°gg5?D₯Χ ‰S^δ¨[§'1"‰Ÿ~οcζ˜θeΏ‚u&Β9ͺn­2sc‚±ypx–b`‚ œƒ“Ώ«^‘Έ£–›x+ΈDžΆt ΞρŽφ…QΓκΘ&›šH“„ΟΟ}Ϊ΅ΠŒb―ήPΪ|ViΌZRΊk*‘ϊʏ“‰! ‹=ί±% Ξ}νκ’θΞπvR΅Ήz[ΆŸΔλσλ¬9°z/³wLbΊϊζΎ<ώ{E)sŝśڜ"§’;£ ϋ"ΩφωΟΕ57Wμœ:¦sΗJζϊΓ2r_*z*n²±R³…~‰τGF―YΜz“(KE©XIγΐ³Δ~ 'ΓlϋΠφ΅σφ§υ›\ύo‚5sf"ιΈΌ–X‘`η½βκΒΙjΣ1O³:ζšzV²ηΣoΚ$<™™:(Δω“Ωσq\0Β‹՜ž>‡^Ύ‹ηFθY±†WivλΙS°xΙΒD½6­]b―›–0Τ*ΡΈώΙζΦ `T―Jή }έιz/ΑIΪΆ™…ΛΓεΦπ£3v™fΞ·ΈΣOδŸt½?‹oΎK^§°ΘϊΌσΗΣ²1ογλ©φJ‡<=SΞβŸφLΑDΝ_#x¨Φƒo¦-mΝπ–M#JΈqŠγ”MχN-ΉΨΩΕέ)jP xΝίJš8"ΊgŒΎJ5›°š˜?ǜ)όύ:Π^υΛIκ[ˆΞŠ&…­ΠZΝ%Ώ+ΩώHΦ–W…«O«<6¬Cx3ξ˜9{ͺRά·jαžPO*U'_¨g³Γ,ΦΆΨΣ<Λ1ζ9Ώ|hPn{@³G«Ψψ.€»‹W‰HσMvpλ― ϊΫΟnhŽP|=†‡»1{™₯Dk?Θς7'.­¦%Ё½σ)]D˜”ΏP·IQs_6I?₯$mwaα.:|{-J;δ‚Β…ΉΨt~ۚρ-ΚŽˆu|Pjγ`1tΐΨνg|gΞ›/mΠο9–‹ΠΧͺg{9f¨ylŽ?,pLεωg‘©œ\λ«rb΄”"cΠ-}=»oΞσ¦”ΰ.;VΠlψϊY€ψΎi Κڞ$‰²ΎΉZ²άb³y$8‘ζβ0΄ώ2 +…σ₯" τσ%Ήι­xQ<ΝΆ”Š-ͺ3(&o~ž€DΓΘβ•ήLΏραmžάΜ@P:–Θ9aІэάYΞνμ—ž $#}όϊ΅b#Π•φVy!<ΏΕN·τržΟ$μ6ωc,Π₯ ‰(•9Uυσ9YŒ«^θ5QύΚρ¨νΆ.¬ύΟ‹Ω§¦μ{κ“Ή1ŒJ–~ΧN(FάuγΆUŸ6ϋO~&Έ­ͺŸΣ€†Μ‘ΧZΏ†ω‹”As%ΨΉΞ²Κ‹xmͺΦ_bςŽ_3NHJXtΞ­;»I‘-SΓο9­ΗλYΌ[οβhgΏΘDΨqŽ«Dλ:΄7₯hςQ© Ύ-D±΄ε-E]UŒ€‹ή†±ϊbCVς"ς‰r6qk4› f‚5-lΕΕy―ϋ7tζWH‡Υ0θα―έΚS΄)“ΕtDω’"-Μ˜‰¨ϊ8·²PΔ¨?›v`ΐ―ΰϊςyΥ©ˆΙ|λ(^ƒ‡ος5­™VΞxm½γ:‹9ngn>'m–‰η;©†Ί„Ρκ’R D=|њΙϊl½=H«‰=AΓKΠo”»DΙλ%d’ύ-R£Q> %Υz:Φ Ή UCΞu’:€"’>°- O7`άΊέ/oW`Λbs»ˆK¨wΎ0$Šό « jόŒ…¨κωο«ήΗ\3ς‹€ ‡v Ε’ϊ¬_χG!Ίt='Χ…T“ˆeL9άk·©­2+Š"λG`œτa7μ> Q§}xCqώΜn”=‘ΞΝ^Δ νΖ-Μέ‘"kξυλ2Ÿ»|TD3‘€`ŠE­_ΨΌiR@Ώλΰ¬zΘ;9Y ˜ξ‚JLIϊ8;Η{lΰAVgΤ¦λšvƒ{Ή&.=ͺ―0œZ<:ϋ8Λ°Ν3 λ γCG=kψ©%ύ%¨ ‘Ι…<’χ²―εϊ‘ΦτšΉίγ֎σΗΒψΠ–¨Ώ`9…XVΚμWΆ 13ζΥzRέ@3ρΣo°”tiƒ:Ζ9"Iv@Ν-ΣΨάύ„Rυ»― zri ƒΆSλΝK2ϊ a}u λό^)¦!š;JlΈπ σφη4C\Έ““xΩ:njK­;}hFΎjYγ‡(σJφΥΝΊŸ‘Ÿ&ΝK8Yή*…œTΩII_UWδ˜Χ­’£yS›βŽ)G4λ:σ4ΈS^Όθ©€†“,[ž:L™>sbΩϋŠ.j1@«ϋ=¦*Ÿ_O_ŽΔέƒτΣ’±‹"βΠmΎš<—ύΌ4žtžO™˜λ> Ψ'³“wβ±ΓE:ωͺ‰Λ­s~›(=χΥc©±ˆ«ι ό–ƒxZ@Ž_Kρή·ηc8ωk }ͺ“Ζ>Œ^lˈž‘jΈΌ -y|!ύ!¨‘Θ .А†D¬ošžlΏΤ―vrsASΝΞe]^ڈΫ$iTzΣ™bπ…$/!»1’ !fΚ ηXS3ΰ†ϋ GΘ¬νυΰ\Χτ6θΨ‘.4/_ €λF„z ―άΑFvNθOαΡ›Κ­Θyσεμ;T•}Δq›ίR,‡eqoBŒ ΗFβηM·@ CφΎ£7j¦˜ξ4ω4χŒŒK"Rt_€Ϋ1aη2σ*—–ƒΡνψŒ²ϋΑk>ΎοΑ‘FW5ΨW:ΛoUͺpJivΌΛ­οζΞ•εq² ’&’—STΕκ\Σ’Dυ« Ž+Ί’ΦΜݝ?†¬Χ|˜οEΨSήζι^ζ~€Ζ¬ k΄f,GFώ΅XyO{Ά£Έ_pQμ)ϋMΌ—2ξω}"~Ύ‹Χ`ΣΒΧ 2Ό1Ž;a+|ZŸϊνx΅ΠΚΕ/€˜ώ Ι²οsΑ§ΚΙYŽ-£πœΎY&’6/Ί„Ϋ) ΓΦτοΝμH:-“ϊΉ?dlΪιš~’ΕΥ|{$ux¬k ±ϊ\`h(£ντέ {DD€ΰ–ΝόqΑ Bμ{σhΔ~‹ιe¨_Ο©υ8gΛP$–α/ ug―Œ’w«1θωvΐ₯u˜ώ‡‘θ Cε>ΏΛžΩΣπ5·oe±έ‚Iۍsοͺ£Fa†’π+–Ϊ‰‚ή“ΤμΎ₯:BΛνZρ#ˆd)’ Π₯?—mρDΕ€Ξ ’JM±-< ˆ]AθΦΙ#rJ)`‘ΣΖ-“έi+Wφ‚w/E$˜ͺΓB…YΪqϊ½¬ <κ"Ύ,hݟnω@]ΐeIšέ­Ei •2v‘z©fZˆR°εŽYbl½ J9՜9 4V£‰%σ2 ν.Γͺ΄L—iD­$5Ϋ‡’Šb"b›Ϊ=„ §“Δ/ «Šσ=“η"”Ό―v­dκμŠυ°ΝgψJ’>fkχhίΏΩ£J%π³Ώ[ρ‰•Ϋ…rΩΘΚ«Έ‰ ξύ±>'XΌνΓή Ϋψ}υSr_ϋ\@ƍƒΙk­LΨΎ”!]V¬"|h]3ΓΊ:ˆΓ δy₯Ώa?[Š…Τ1έΌ]NRkšΎΞ}Ι/i•α"ύ-Rτ\§§>au|DΑύπ’Q–@.~~kYu§SgΛΑγΡ½qΜ°Ώ€ΚK υ!τjC:Ο'Z”Ω;¬€θžCP‡?vάdγ±B²Ν6‡Ιv Ε©'―PλO…’Q7Ϊέέήmβ?|ΡzwvRϋΖΉ•pΐθ™!D4˜>š`Vιαπ„\ηK‚ΰŒD$Ρ¦Λƒjώ™nž‹ΝnΎ6”6Τ²L5x½ΫUqŸΏσ΄»½3‹ΰ@‘@L?π«‘g7UοF­ι%#δβNfa‘νγβ±=\l>7‘[§μ($£O?*Ό‚U½γpΞΎ₯L°Œqœ3ΡWψ’{γ€β’ήτfψζο~Ϋχt˜‡ψEώ€„>UhΤ¦Ά$Zƒ›pΐ9§>±ΠcrI ϊwLۊΝcV†TΆ%ΝRŒrΟινθϋ©αΣ”[ξќعƬxymͺΊφ¨Πi„ο݁ζ}Ώ¨ =†š‡~&~ρš„3ςή¨(όˆœ_ώ–fΰηCvTS% ‹―ΐ|DΈ½#jlS~šG/Y 1Τ8³ΪΡζχ‰η΄.!φi.ƒͺΔ5Γ#gΒY'υΥ³Χ’” 7ςwΜ9ΓV‘f@¦wƒΒ‘~AF‚­œ‹ΦϊλψšŸϊΓa‘~•ηδa‚Ρ³KΐΈϊ0_TΡσμT{\(%˜<-ξmQαSžΩUJά€ϊ’VΆέeUJΠ”™‘oœs²`/₯‹Kόshƒr@₯Γ š|k"ΟώΌŠvqr³vΏ FΥ³';εž‡€)‡ψ+χΠ'ΑVιύΊΡ–qώ΄ΐΌ~%._2L0؝ΠΙ‚’h`wπ ­l7€Cφ²[ΣdCτdŐά(§dδڌQΓ€Ήω,fO§¬ΉκώHŽXQވΒκjYΫ g]δiœΣΔ}yqΌΧE6’ ›$ }kΖ‹h¨ΓžήfesΡ·&*‘ΘΟʐτκθA„μXΈΌWΰu|Όf5EŠΜ‘…Χ΅py(‹ εη¬3!±ŒΗŸ}Λb'*½ΓbψŒΙ™,έ C΄=β^XΥ\ΒsεIdώžΠ]u.άlα–*|ΌΥqΆΡk]2Νά―όωKɁ‘5dυ3΄)mΨpbβ+±ν tΦz.ίΌ+HόvDo¬0ŒzLtPˆ₯ο\©v6R`eΈXƒό7(Ω₯Ž\ΛϊΆ ·s J·ψΟI(6ˆUfτh)K9I…Vί‘Eσ8k°³©šα~ˆ§Aπ~€…ό½e it΅ρρH@JΡ΄uŸΙΏ•…Ι^“2wšŠϋ'7WΟs„›Ώ}‡ίQΞΙ(-½5Ήη[Ϋ›e}Rm~˜N–ΪΣ:]3!|ΪΞ:‰‘3Β­’Ψw©ŠΎ7l ΐςF1Όψφ¬-AύQΆAYœ ΄½??(x‡λj7fΰ9<¦Vφ{!α(ΟΧΩU֎YΖ_G+Εtκ ‹k]’~¬ D¬ΕaͺŽRΌ€g₯9ˆ~LΛ"ŸjmHν˜Σ·šΙ’t’ΆΪΆ‹ˆΐ½q©t6 =δLz‘ψφlΰΏ›^mžΦΠΫ5i° a>”n6eVc m‘ ύΧ>½οy:JΌAx―pΝOqχ‡uŒΈT[€άš_χΝΔΛ,`E^Kλ4„φ›Α7’hΚΌt\Φ΄Υm±πΙ·>`BV cΉ“aIΎŽα;β†n²Q΅ΤΚwΑδ:|¬o\™ΛžOΣ„ίΓθ…2χtΞI|βΐ·£ΒΛAΘ^fDkyί2Σ²0^*Βτ±žTΫQΆυQΕπ[ \©ˆiυ¦―]>œΙBm-{1]’|H •aΣ‡R(,1Ο GΥhΨCAoΪSΑ€―ώΘΪUτNΗIφŸ ©RQΝ4`p%`Άm ˆCŠƒ6›άΏλe¨‡μ)fΜ֍„.‡žu‘ΰ¨G#’χœi^΄ [_²›hK`±miΉ?’ΗεΓίꖐ뭌­·?ΫρESˆΞFx "»~‘/Μ6'ΟTœ@L,,εΩ퍡?Zjε@²[ρœq5eš#l€‘³4c³Gm²ο»›ˆ8H₯ ¦ΧtΧAχlŽv|ψ¨hŠ₯yΑκhϊt_NνΖ 4€dΆogΉω΄˜5g£Ϊ]f ŠΘ™NŽΨWA™ΟίnτνHγ6P •‹κ‡R€#ωπAs7η\ζ²4}Ν²ιmUz˜ƒ•Rύj·Ε(bχz»TωέW§κ{ yBΆωuUθKωf5‹ˆmθ†β=hΌ½:ΟφV₯΄7ν…D^wΘiUχβίލ ΄qη}ΣίNˆgfyˆΚ>ύ5xΌ>Π5!ί†:Ι>Š–μ„Tž“Ζb’spσΒΠΛυ%›·―ӘΎe‘"ψUέΧΎτT}‹ια¬ΑόΞσ;6«ΘEι‘8noήλ©’υŸ`9t9KΪ ‡€ σ—D¦%)ŽŽ\ηCw¨d]ί7GqΚͺJτiSΊΖcλ†ˆ½ΗΦucΆRjυLvjϊα>&»ΏrtΏ―3#+RΧ2+€ΒˆŽη‘OζΟ²/«ΦwnF$׌½gD"σΛφ%ΘΪ­’^K}ζCλΪ gΩ¨…OγklΜk©Ύ‰ΌPGap&sκξWQθθgPYνχ]i‡Ό&+`³~υs5ΚρFEΚFE`;7‚^°€΄Ν}Ρ‡>ιξλΕ’ 5δη,‘ • Εβω`ΥN}YVτ¬‡bƒ«τΚθ<Ξ̏ξ–R8ΉΟ‹ΥΜε"Ok:°1ζΚ―‘}fο&³‡•~Uθω|{EM|–ύDM¬š*e’΅w₯yκͺςλΟΙΧ‘ΓO%Zƒ[εε)X1 ΈŽ0¬‘YΌΛF_ΜAχhύZζ W·…e"’SΈŽŽgjxΝήχδπι6Q£ΏL^k…΅fm= eH,n1?ΝχτΉYβ;@•Ž$ΪΤ₯+‰‹8~x‘ξ–Η²XΦp@]ωΝΊ«¬ΤS} μφjΥ3ƒw• ~Γϊώa™hΌ‹“\ωq₯g>vψkP΄ΥvσωΩφLfX(ζ4§JΡΥOΒ°«§ί‹ΔD©λ‘λ±’ A‘υ Ί™υ!Ζo¬n~œ(|έ«χO^Η'έΈjόΡ΄PΦ:=ω%t–¨»0«‡hΕ‚`6y7«JεRρˆz‚·€Ooψ+9^³’½εϋƒ Zδ|kΑk―-PIšΧ”#ζ5b¬ΜρΙk[ύh“?g σύΎW‡‰ΤΘ»ή¬Δ>ζ䧉ΜΧ“hιΤικ . χrΉΡΫoGΏrYΈΉάή\‘*•W9ςJΕΧsΨg5άO’ϋk v”²)^ξ0ϋ ’Ig[f;Ό|ΏW;Ή%€Ι雍©qδ ΞͺΤКx|κسڝ«XΤ₯~e*πYΑ ›1Ό,IΎΈOQ?θ“t2—„£6₯“1€ά²ηίMΊF=eΛ{i vgt}f‰–#ƒ†΄sv™Γ0vϊNπϋΝ—κu5Sϊ{š9DΤ‚φl—ιU’“ƒΜKJ)ΨO˜ϋ‘ w_ϋ‹₯zκ{_Ο£»¦φHr”#―H­ΛΙ–qκ’xŽ2!λ}Q‘κB»‰ΒdDœEjΖ>°ΪŸͺΓΥΤ…1 lCΌŽ₯x–΄~‹HΪ y'³η:βΆΆβhοi|<β¨bΆε€ς0—ΆhH68—Υqϋ˜Νν‚»[ώϊ36χ$,?΅šϊ8 ‘ΩtΉΓ (1¨˜ε°MVά­ΠΗ€ΏοŸQp₯K­|1#(ΫςΙ†ΌE·*+]½a(`κ ΓrθYάΑ•J€YqωΩ9G§:δ=z+KuKΎΓȍ$‰?Kγ<Μ{J}6n—=Νlτκ²)΄:eβήN±„—9#ΜΏpz¨ξ–b”?ψyœ'ί‡έŠθ#ͺZέ¦ΙΦ>ζ±Φnξγb™_ώΪ-!Ρ·†ƒΖ(mτ‘f A¦˜οOyJ؈.pΕX°ίG•2ΔΚ5Oο¨ηΏpΰ£–Bk‘„»UpQUϋxΚή"œ{™GXPEζZΌΣ"·οD•KF‡μ>=°NаώΦv―Œ_7…·χŒRš4qΧ)AϊΜΝbUύζΞ]ιΣϋœŒ¦7Lc‰€οg=ϋ}aΰY₯& άΖΫΎ§K£©9Lύbσ—$•«Ό!Ό)_†_’ JΪϊ1fQW?HΈ0ΫkŽ&γ;—Mn‰tΊΪwΰ)€#ΉπK΅^)ϊφ#\Ώs-ΡθιhυΜF΄κ© eβ„ΒσΗ½υψR#ΝίεJ₯΄ucκIΚί”ήSΞΡ$}0φΊd ξΧ¬z¨o+€,—³œ˜’Ω*rλΰΟζ “ži£σήοDΛβ‡oeEν‡θΔ;οg…~θήφνaγθLh «“\χA¨’~P“Vv₯ν†JŸ›AJΒy{™ΗΪΔKŸŽvjcŠ`Ψ‹ͺά3yδΖΆή«fBŠh {U%TWΥΪ‘[ΎU’Hσ‘‘7:Uφ>Bsμ#λœΙ™!2Ζ+6˜)Όžͺ6tŠδα—μΝ6x.ΰξ‹ίύM‡‘•jφ½+ ΊΙT.χŒcιO-s6μ’‚ΘεšLO,3°u†`πΓ•W[Fls8ξ’ΥŸJϊ½x§bŒšNΗ ŸD &MvŠπ<‹Δ€Sf­_Ξxt)Oή9HnWΨ¦Βα„ ΆYΚ©nxϋ--K]UΕ€\w”n=>#kέΌΏD7b₯ΕΎ{κν¬†φ‰λ6Β]ήw<§2ό’π0RZΧε—Fς<Σ9ͺΔTκMΒ—7ΚY‰‡ œ’dΌω€€Ω΅ΫSdW[ψ{e(s4˜±θ_₯Α·²y·π6hΜ cλξsΏ F†Wχ| ˜iαχϊ=ίIΛΜς㱩ݚ&ΆΰXfΡξψλlνΒQΌ­Dω5ΉŒ1Ί―'ZΒp΄lΣAš‰Qd9ΓΒΥe€χφΖ'DkρFͺΐ(R 5zœλ.$xBqKt›;γ<ω&Ycδmp―žmξβΌύBY8Š'ΦŸ™ΕπFyoJE„΄bΣμz)ΐ Dζ#ˆUcFξ”ŠΚ„žI€αΙέp΅?'xJTwƒLςΡο1*«Ψ­΄τ-ΊΡ―ϋχΓeΟ5ςΓZ’VΊ > ”zJc}£Ž8|:/Ψ^(­Ήͺlύ±Ip!˜ τΣ«’ϊ²†ˆTΑ~·>&Λ>Œ~€5 gΕτxφΓ³qΪδη—j΅"Ι»PDΉelίΧ7 j¨AΤ· έ3gxυΚε€ή λξΐm³wjqŽuΰZ²†.ΨVn‚xΌ˜Ψ}f„Bπ¬ζνγh ~–χψd^„ŸΕι a[ΰ’»yςΨ°bJ£,όπFm¬•ϋ”€Κμ€οr ym€T6ΛXbuGœΡcfvJ6·½nT„2Ε=Κ6hKΰΌB³“{œ7^…Α ώξd₯VθΝΦά0g_KAπ^v„†Α\όJΩγΚ‘Ξ,›ΌΖ7΅χγNLΑΓ(SdΌW‘λ9#^΅·ΪΩ€_gβΫΆΦKXξYΤp{ΝΒ――ΏqDw¨ΈΫ©ΊΑς^b=“D―½©š²tωΌVΟ{\ώC°(&V_Α2)Έ*I:¨ŒJ>sO­²> š”ˆu σvλΣ’σ\”™-!ΐ5¦Υe'*@ύέ§kΌ’O12½Η††P‚cΎB6{Ύ±νxΉ³r!©6_…ͺ\'οί|ϊ¨’°Ύ€•μ3Ήeτš.BΝρnSœσ²,™ΏC»μΓcS_Xω+Δςnp`PyY&ΦΞω•ΐΌz힌*žungz7ι$¨υ•xΖνvύ;³«Ϋrwœλχvεγόά(ύˆΖ[†,—4ΈηtoΖχDOMςŠƒ«ί©Ύv.ό¨›ΡC₯μMN"bΙ^NI M/ZΆƒΣbσ)ΝuRφYΫχGB&·1JΟpŸ¨Η–=ΈΎk —R\/h%“Οœ Φ-†Ya$ΆF‰t)χC―hoa‘05>ϋυ iΓ iΤ·1λTόύ³Δ8½έ ν 8Χ{Xasœb“g^ͺ£$ζl“£‚3Ÿe‘…σ‹o—Δ•s°{ΡK<=ΒP[ΪFp)η―9 ]οΔΡΏΦN[XNP4]bχ2dΌ1ίl BΥΡ%\Ž ϊe0ΊwŸφY£G ΟCƒβUΓήf|ΥClη zΝWŠ&Ώ©fκ›ιΑiEKr—,EG‚-Ύ8€ϋ‹`Gy–XzRΙlb²λνsΎύZ±ο-0\’·k‰Ÿ(9vώv[Έ*Γ(). 99Χ¬H; I”yέ’ŸLxΓ&ΊB§ο}C₯LIσώΥ>ί‡„νk¦oμ5ŠkΟΕjΑŽλΟΟγΡσEμτx„ :ZΤ%gΡΫtQϋ½š§6ΞΗάOn¦O!Š^_.-»ΪυnjŽ $†™We>ŸΑ±-gα€ΏB#½Fγ©AKT)—bΞ°Ό\»qμdBc~Η hSυx0–]o1ωΆ²&ν½ςοm`Ϋ.7 }ΌTkκ­•vDV”CίwςEa αΊsέ§ «GͺŽ“’ξhρ%™υQAftlΤ’μ—€ §V¨"ίλΜο)ψVb&3[p6»ΑΜ—ν­I1ƒ²ΟΉOμ-²5²>±+GŒ»Λ΅%)₯"›κqYc΅ ;ΕZŸώ` 0υT©ήϊΪ?YιEžέΘeP·au z_°VUžΐ‘+μGπΡ΄ΠίκΤzΡ$η5 >3‰ωAΒ‘5™Ϋ8ΙυFΫ€-ƒ5eΥm(Š>₯Υ‡μ˜σŒ¬»Qd?έ2/&b–‘‹['ΔQ;x&MΘ+:38ωΊ›`ΓΰnσΜεBF oΖVπρ&γΛΝw0vB>…%¨¬γ£{‘ͺ Οo°-d6~&ΙyΓ^›qSoώ£·Aέ)MεΡA»”Ύ†ΠϋζΊ΄Ν7₯k–ύτEx•ώhφƒ„ƒyήόuιώA+oΒ£žω+ζβO5Ÿtpˆ«ŸΩΕYs{ΆζψIΚ~ωxSRY™o§zΉ°ηΦ7/‘ΩπFΏό`ΕδΜ8ΟγŸ|κΖΒvΩ„?i@Μπ³ΝΫ…cWZΦUKV7H­ΰΕ7…ρ|ΌλQ8°Iƍ‰0—ψΨ– Ύj§ηό¬Y³yύυ—‡ΠΩήΔSλ܈A§S}Ϋ‰VΌΫτβά«`¦~^ξ§(‹d ‘‘ΧμΖ2-ήςοΫ°“N8‹OΉίΥ‘~]laθ5ΦΓs³j$xούιMςΑώt:tSvεέA2:žθf‘ :‰\ ,ΆΠA«JΖφG9ψ‘ ζΣ~σ ΜφCΟ5»½ζƒ§φt†Ηb*ZοΉ`ζ8KTšή.‘ύpAiKWυόOc|]ŽΨœ T–6c1zΎΊ΅W£ G!jB™Χ94ΔμΣΟo“α4•χ|xόJ„kΙېœ€ι/›;ωΌḾWA=ƒηN’ι3ν§Ρώ$©ύβΒʊ+Οt"ξΜεלψAXBWΊE₯7χŠ₯†—CYΌ¨"φοM‚§oΣηeΤ ΚόiςτΰIδ^%Ρ{»°’+« <xψΨ‰bΊ&RΌϊπβV[υuΜΘd―B!Y—σ"bW€δσΞ$‡δeΖ[κφϋΤ›eΑdf―“ΪψoΊΙ>UΎx+Ωδ {ΨE‘ 6σ„&Ÿ“Ϋ²³΅@—ϋwiϋ-vΟlA©x†/¦β˜’Ϋ›MΜΝ΄ιυcο φgFυΆ™ή€ΖFo«*3}7…;+€Υ_σλ»~0“‡GΧ€q[» h²#φΥΏΛεL)!ςmzSΓΝ2$ΣΗέ7νHψ,ς`Σ©ΌΘ/€_ΚΘ΅Fφj*&Ζη'y:DυΗNwEθρ"‘‘{D3q ΏΦ,άz>h8qωτl¬YτΙ†™­1DσkO_uCέ+Ι/hΑŸZ]μv s²ΟΑυεΥ/Λχ  Υλ”Ήο&χ¬ƒP7–bωέ―p ;e@n/G¨]‰§θρΠ‰cHΘΥ7–ΈxαΟκίΏΎLί5&ψ–ΙΓΠ‰ΨςBx«ζΙc©" ―Χδη~>ߌ½ Σϋ(ήHΚ‹©J’€ΦTFIͺ“υπΡ'uϊϊg£W§;Β€Qzξt€ϊ±ΕΠ>#¦Γ~V ŠS²t|(-΅τ―HŸM’3‘H‘ΉNλ–c-yWm]9[ψ3Ν.Žn(š&MjΞwΑ―η1}<Πp―xœCβB«ή(ɏaŒ”γGcud6χOοΎkΔσΤ—Γ!nΗGc»ΚΣӘ€!“χ)Τ·½ ³“j―f5`μ=t«aq‚M1#Η.Άn±753Ξ'M€&ΤφrΛ^ι½Ώ:{σαXΟ—>••Š78²ψŠ6ΟAδΎ=TξYNX’ΓE†oP+‘ΚΐΥτ~9/`郋ΨΟͺ†Έgα¨λ$±}₯ΠΌΐSόόλŠΜΑΫψ Δd%ύάόa­ +Π #λg0Ώ=τBŒ£ˆH’\YΎi~ζ;MHπ(|lΗΡlήI#R3ΣηŒoι[Oηtxύ·’œΒΗ~ΒΚθθQ·J5ΒΛqPζη†ρ/~R(Ϊ~ρa}φ„ΔNΚΥ!θ”„BΤ5VΝ ΜJPӁ«œιͺΒ‘<η«Ύr#ΚΆT «Šsœ½—”˜’DίLG§δ·ΕΉΎφiGo–œβ0;aΐq/κη’u;7nΗ»Wm<‘n†δxJΕ~4ίᚱa£ΣžΞΞ»A΅ΜΣ'·ύ*ΙΞ ₯F€†ΆBJnƒΒΒY9Ό.Χ°ΊύHaμυeeETœω—™ΧΑ$§΅žη«Ν|ΊκΚo'ά Ον,°qΎŸΉIW)»(όQΨΞή vOL6ψΟn$HMJj £q»ŠˆMlŽ?ε0Z:vς}”ˆeΈRοΎ―Ρ|Y*iΖςbμXr§ZJΔέEi­³ΏΏW`hώ΄ΞE‘ΣH!3ΪZι]Άη{·@ΙMCμEˆŠηvΎΓΊ,]Π{''H(+#)„t€‚SŒΘΛڜpσ’uP‡0Ή΄νή„%ΔΡ–w‚ηνήόZΚ§ξηο6@ΩηRD₯³λOYΆ0“§ΫΊΧbΰσ>͈N8Ԉ-?ž₯ΊΤžyφ#s[JτΌtΩSΊ=‘εΓ‘ž 1Κ§ΣΪ"nΜ^UΑ.έ­MHc–‰@~$?ΪΤ² τt½°Ήθ›%S’EDU*li8σ%rΤ΄6Τ6²MέJέϘΦπυΤB”\œAΐ1ΦO†+= 1QΕ…+Qηp‘ψς%AWŒΈδϋ\ΝΎΫdΗxŠ‚’΅H\™‰ΔΟXٚ;.v΅+[λ²OηY£»κ7ίχΣψΥ>sΠϋ¬Ψ85ώ|ΊaΦ·O,±«ΟΫΎΙ]Ϊ|Ώυήεϋt¬+βϊ ‘y°θδ6ό#B;τΗ. >¬ΑaχνΎ(…ΚΞ‰ͺ4€63 'Λjͺ”“ϊδώR@ήΩBΖ‡š9Α[½X1Ξz ]€lSνLτ7ΥCΚέBuσΛV@Τ₯ύ””UΝIή ήιŠ›8σε`R;OτΒ=I_g΅ΈτG5ž uooI @έK-£ιŽΏ7Kυ™/ilœ¬ΨΌ1ΛΛ± ζΧ!;ε;Ϋ”s’:+qψ½­wφ!>”—$Ώ³S됼wzˆΕŸΠ—Α}lOΊŸΦoΩeΎ&G'3퐂Ν8“>j`€š“Ώ²}B₯Κ:»πPמθKΟΓ€+o$πλ%e•KΒr(_|£Γ}?Sq;.Ο‰3"Ȋ’½QƒqΣ?…QX¦κΰλŽJάάΝmŽ8@‡EΊ-sΥ”ͺ₯zΧ>ΚK9Zr~Ύz΅ρΩΡ­M‘ ΰ€° KΥG5€ϊβām˜Oήκ|ΆνΩhW­^»3€ŽΟ<£#±¦"£Λs›gZ Ρ£ΐ%‹‘Ωά,ΦtJ7Ω«nž%Ω~œpBψ²ςͺz[!Β™uμj›ό@E#e ωftώUŒύ^ιG6ΪZ­Ÿk¨s³; 1³–>ε}΅φ-ΩΙ‘έ$S;΄άn1^²ƒάϊ*ό³TFLl| \l οA8·κzšΩd™ZP,@dσŠ’T? YΑkΏQmΙjlί;¬žΠή~;-]¦4@ΠΝiͺŽ}ž<˜ "€οΠ2܁mcλi±“Αϊ0Ωπ:DΰfμxήMκ.δ@ϊ†I˜&œŸ'r€œŸ>νiwV\:‚†όβ+³ΰ«‚‰Ε/Ό;ΜyιŒ8"ν&v΅ψJvfRίΪ ΄ΆΞ67}‘Γ³’_SΤv>ZΗώPΣiΙΩ‘Rb ίθϋƒ€ι4¦!μDޜλ½Ώlnη‘ημj‡p]ΎUΡF‘KŒ1™%±,LU«σ#5ΖΰEΚ@>sι=#Ξ{cEθΈΉ"x’w*ω»kύ’Σ¨’ΨΘ†ίO‰‰7ΝΪΝΟDL7ͺ=€Όη~υ;'5e?^+π—1i§υ₯‘‘§Œyi±Έ('v@„ω]8auψ_α΅>wˆuΪEoZ(’‚š;d"?ŒΕ;QŒdΠΣg—€χEm\uξvɟf³a*‰h3:)αk}‘ξ€±Œδ[2KΏΆ‡ck^“‡j€dΏW.μ7dδd=¬μΝ‰n —MΜ9‚ΈSκΥ’‘žβΏ-ώbΰ$4ɍψ(`ΊZGϊ ͺμόʜ·ΕΛ€ήh΄—Zώ/Υ*}3,g#Χ wRœ~GΜ₯οΉδ‡0uρχ§Ρη`!WΆίσΎί€XƜƒ !F₯ͺιŽΟo—)—1eJM€ ΦήlΓβ­mΔ:ΣXy‚HΒmηc#…›*έ* =βθΖΙ…Γ†ώ+1^™νΚΟΗώa=ΈJE@τ`8;9ΙMKAξ…:‡YQJΖNθΐ p]—\΅Α–-Κ!Ai‰»‘ωζx“29‘w―“$ :ΦβΊ9˜@Ι_t;Γ4Ϋ;b€KV΄Dqτ"9Χxň©Δ—Ό§ξ6B4{­Δ =$¬«nyxΏ{ιNMKχjρW½»ΚnVx!‹s Ν^_š3[pχ΄tGi =žΌ‘tq΅§ ‡•i»ύ_ΐkΦσΦ΅2£‚¬«ˆώΝψl'WΈΥ'όΣ3¦|?δ²RψVχT’tΈcΞΫΜυ»θ€ŠφΙ{'_ ΏθΑYyHVΡ• hΠ°ϊ[0 †Θg£‘O2[$‘8π/ʎבΚUΦΒ,ڏΩΛά\οή8CjΩg: ¦iDύ‡­Ί'Ο.›?QΤ’Ϋ.Χyψ³Ώi^‡g(flόpϋŽΞ~όPQ eꁷA¨έ‘ν"ΫυH½EP™―Sξ½p†/Ÿ³˜lΥdΒΩY‚{œO4аTJ/₯ΝΙ•jΝ“Gΐbθ—4 7’ώΥ7{VΈξJΣf“/ΣΰαŸ+OXi33T‹„·ιf·}²ξΟuˆή9uSΌαhΥ:ζα#ΜΙ¨OΟΑ}έΐΑΆΠωx¬ύ†5ήΜDZ’ͺΝR0Ί΄clγӍψΨk|ξQ¨})rΗφ(C₯=ΚSuYύ_ΖeCϊΐρdΥ4ρNrϊŸΚ8#V(†ΙagΙΉ˜}Ψ>€–œΏ*A#IΆnχ² -"9Έ¦sD,8σqΉ‰τJrT„u5Qbf n‚/ςm[Ž„ϋ”jΦ^Ÿ)Ÿ”D…*zδŒVεu]u0•}”[ύ+Ϋ‹ΑMXŽπbŸ~|τˆh!yΨZΒΥ!$8ΦΏ­Κ+ΰ%εBΫκσΐU )»QΩ•ΐδX;h…-f%¨ρΆ?…+DΕ!‚—Ωž9’8±α~ΫΐΜ Œ°l£ΦCet"‰y LκήόΡψTΘΆŐ_," ΰ–.™Ω‰@―…―‚—Εώ@Φ >κΦ­κI}«Μ^ «ΌγΔλκή₯>ω«hΕKνQ—JΚiΥ,ιm0 xήzμ'ˆΤΡ’θl―φ―P0ΆΜ/*$&°°we’D‘Ɋ"Iζ’w°΄ΑP¬ρqcLςΗ'pŒQΪvρ‚ΑDS9XαφzΜTrOΏ&О‘±‰ι kzE6ΙPKΥn©0iπ œ ΊOLͺGξzΤ\³™fφ_K <Ωί‹† „Ϊ‰“δv“tLbXΙ!#Aς—?ήι•N•?-b s9ή±ς¬3sSΡ†ώH˜{e­6~˜ΘK"1c•EEϊΦΘηΞ»)΄Ν ΒYΜϊ^)^ψΫl'‡ΕH_‘U&΄Db³Uί;›/Ι]ΆO+ AΒ$„+ΩcΒω<ΜΊ0*‡&ΠzŽΪKπ«½wPΰ‚δK}Αδ!ŸxΝϊκ—«2.qLΗΏε@G‰„γτ\Bwgq1暳3ͺω%|s*vσhkΪYzγ4 YXž|τΏ|1₯ΦΞƒ†9‰dbδ?οPπΝU©ΰK.ΙHε}b“jj6B2`h³/ZŠˆθ%ˆEQU“Άβ#0!m9» 4Τ]ϊ/Έ˜Η:ωy"Λ.€)ΚνόφΎϊ8Ζ)ΐ°ΰβ΅°£ΈΛ’—& ΚΘbŒθ1z}Ύ ¨βΉΗŸ^υw!)š±³σα"ΤDJΏΒετͺ;5ψΛzYF -mΚ~Τ ηί‘XϊΰΌHt +•ήωαw…O#&ιGezκ`ν­ή'όΑjΓϋ Α LΥυŽά14Κΐ—)ˆ!d3,Τ½ΤfƒͺHέβ΄oͺo«KχψgεI'B:—“(εΘΗzbώ‘[v$«δ5χyωF˜θ χU§ρ°ωO‘Ι£σ|σΑvZcƒ[­€{›gέZWLΑνeεϊ€ θ¬Μ’ΣΥΙέ*ŒκSu£Ύέ—ΐtΨ:χ“β΅§ΰ.„Oϋr«?0x€•48\Q_ΔΣG hωέ`%G`¬ΪlηΐΑYB»œΈ'}ΐ„9=C“yN₯½*{Gΐ"Ÿžnv1Έάms%’ε,ΡrΘzCμ'%ίΦ΄¦eψΕ1Ζ`†*Ϋΐ΅l{―ž #κŸ™™•N΅ΦάΊ21ζ9| tڧ峆\P,'©χd—χpχ*^aWFš˜ZΩ.Ž U σΖο9οΦμ› _U΅›S;{Q{ϊ»u&! ;XΚk’³Yp’‘M„Ι`;5Gά» ζH¦[ΊZ’'υ]™%mΙ©9J^#νͺ­‡(„{Κ¬όΏ3n-΅‹¬£vv›+Χ>ˆT“¨·?„λ?›™‘ŠPΪqυtQ Yς€qboΠƒ―ͺŒ+.ΓJ-Žύyž_W3‰ΩQ&ˆw;W•IΝήs$`Τ1ƒ-=Ÿ·mΩβνε2Q†p¦?iˆΏ{I$y΅°Lη7‹g7ΖjυAεpΗqςκΖ]%kjΈα$οt@pΔeGBϊd£γohΖ%‹07y/νιuΆ|&§Μνθν֝ͺΐ0֜ ⏠Ÿ!ͺΉΡ½5—zŽ{Λ.:}/ώЍ`•~[9":¨>ž†ύˆ·lΐπY•™Έω\„z2l«π.κ’Θ0žZhχͺ ±"7–0¬$>ΎΔώΙΧcΐύk]HΦ'ςΑͺWOͺFGδΟ[:ύ^g/ϋ@Oίc^MVIΚ3°η ρηΎΞΕbςΣΎ²¬€RlcŽ0±c΄«"Σηˆ0¬LΝΎ(TPƒOΝ'Α< ΅‹g„>&στ‡6‰gew'k€­šψmσ .ι~wοΗΝ6™ήθΖT°ά>­’Ϊ@ΐ*³^`Χ.ˆν"ίνΫό†άΦ:ι©΄:sF‘‰θΌ΄Άΰ’|Οj(Q€œ8›Ώ~Uh{ŽΙφΝ(4Ν°H x†Ί«;ϋ± ¦ °°`K)[γl|cšžΣδ/°ΓJ©’όI!ˆΎD’/γ܊N › "ρ Γk Κ(–Sϊς@κΧωΉ9EϊΪ.ΨΌΤqŽξ„ΆΥΑ5%57;Ѝ΅;θPžƒ\C hώ5³τS|€Z|ΪIšW‘Τ‚gZρνθτΖ#―?Ο؎ݝ‘³ς{E5v„?6ŽT²‘ϋΨε-­ώΆΖݍej2Ξ9ρΊ8Α›yHˆ·qΫ7ρ†ι10)―!ΜψXΘόΘj.8l!‹δ REΰχ‘•iΞ1LύΗ’βw^©«Σxbͺ5χlΙjKœƒJθαΈOύAX[»Sΐ‹ &"Η 4Ήώ‚J)\XRwZ:,ί:ΞΓΊΦ@-‘‹Ž§n ε±™ ³τΗ<΅ασσ;™·ϊɞΚο: £1u|λΝ΅όρI[ƒΣ "θQ?Ι6ΥG˜(5Cβ-ΙYΐΣβWeά|†Θ”\pΟ‡‚5ηXδiλ/hΔkο"W}υM>Γμέ_IU@h@ΰMaιF[ΰΣ‹’T2Tφ˜G-?Φ+η‡8”1Žη"cOpfΦ•;βcvŒηΒ KnC!½&ώ6aν;]Ϋ‘Tλh­}ς¦‡Ψθ5E'_ xHφ΄0‡7cΦ(@¦> stream xΪuSy‚QΚaˆ"AXHρx\dϊƒDΔg€"ΐB+ϋ1+ΰ„πΔ|ˆΝ&όW­Y³φ‚#]ό…œAΔ†#t rήt&TΒ„A>Z4s:–Β’­gBΒιvŽPΘ#š™ρX4ΕL,Sš­B ]3Θiβτ̜!>Θ@››Ν[ŒΔΐq?ΐ,fΞ΄ΔρΜ6ΐP”twώ'…ΏalPΰ±ζXΦ£p+ƒc62@ΜgHά4Lƒ™Ϋγx`ΡΈp;Δџbœ€ BΎάχΏΔχ;E`B !@Ωθ5|SGa5»χ¦ ωΠV kŠΕβμτχu‚^(Ήβoα>΄H0σΪθξαξ²fnο_£Tƒ³Ά0ζ6xΤ)¨"o1W‘Bƒώ©ϋν°;ΜBΒlαθΔΎ ς¨ “ΓΎWςA„LΎ™d3Eύώp?5ΟwόO-47‡‹ˆΛιίdΆqν\xΣ½siόΒi‘Wό“sΑYŸw! 1Θ0›ϋuLΐΪ 2)Α™5Ζ,ΎfΞΌ8‚ ι7 `pxά.€1"`P @έ7C0sNΚυ0aB0  QΡψΜ―ΐ4Νρωθxf.=ϋeΟ‚ΠAp+ΘPΌ/AΆIαg“κ'ϐ΅c0ΟnY΄Xm˜ψ,Zn©Έ—+ϋΔt^³fD©Άερ·6‡r)mϋΚ`9o9ΩδLνυ{­c"Ž ¦ jκ»ΤζΉ₯i½0=gηCT~΅Π€σ5ΆEkΝμcϝΔWάFW”OΪ;έT£&#ρΊ“›€ƒQΎςΪzε‹|%«ΏΎί1βλΖύ͏(ίu½ΡΎ#%[Ιγ…ΫSΖ›”.Όx§^Ρ Ά₯[ꨂοJvUž}EΧ*΅&6ήΌdώžΣΰιΠ(Ϋ΄φdzΎ±tΜ¬]ιΣ£λ«»”ε5μΧνS^¬©Ω‘ˆχŠ΄½ΟX¨}€ϋDkΓm“λΚ6χ0dxΛ0ύtŒ~zli^Ύ¦ΘKΑυΡΐƒΙšvσΆž†{kŒ²'ικϊšΦ©#%‘Λϊξ„΄ύνΤάθωψI»LΥf=?xδτΡ$δΒΠ6wjΨε‚ΓVu–αr‘έhυu(Ύ±237kας‘<]iu4τM¨Ρ‚΅”ς‰ΦΈ­'©°" ^…υύ&Η?»Ήφψΐ’κS^PZo“#fΉχn=q-—ήž'ΒI©S£ 6Ζ‰g'v5+ΒΑ:+E-%F#/7σΛμ‚―Oω—$1ΧθwίΤι_θHϋ\έεΏδWΟ8™Ά·PξAΎ›λσξέ“¨ύ©ηγŠ»ϋΨ@BώžT9εΖ>2…§hZJ?UΪ7Η[qΛf€Ά*νLΠ&½\ΆϋκΊͺ#žo£ΥΞXlρ-‘Aςiχ‘Ϋh\•υρ―FΡΉw‘φ)Ό΅}Κ­ΤDΨ‘x5{bαΩΛ Ίœ2+ξ:£ M―Ά°κ%wδϋ:~ίuxιώΪe[Ψ€Ει=ΓjΧΜ*/ή§ ήš¨z:…¬žŸV]ωq[ΣeΕ"Y)ΉsaΪ@šύΈΤό&YΤδDtd[Ÿ–ψ«―­Σ~³L²wρp[Ν:e…MY1¦ušX|ΖΉΪͺ~9qluL,a$+άo”[{‘$έmš—r>[š4ψΙηα|½x~βέp7>QεiνŠΟΎ\ΓξΘXZρ¨T< 0ε‹\ž8Ψeλ@<2}ll’DωφΣU§ή­\™QΓ½=πD-£)p#1Νve©9Θk|Uίή\ά΄3)¨J)΄Ί}©AؾގΔWάχuˆΠ‰<τΪ―4kη©lΧi3[΅}!ςFW½Ώ7ύε=ΗΫ81μ&A¦ς―[Ή%ξϊE΅ Rˆ9e§tIΠΩηŠ“Šš%?Hdφ)‰―gΧ֍{}:Ζτ†dr—±ήˆ‚λζσ>~Όs’Ψ@žhReQ?©δέό βύ– {Α₯Μσϊ#Ώ†‡θn»q69WζxKλοKΤ‡‘Άnα7ϋr§κ²œΚp=ν*VψmI.xžΥu$ #c|₯œ?΅­M>―σΥ™e–†:ιΛοΘY©`Ž{Yt2φιCƒ eΊΝΊχiۍΤ{6iΑ8Uζ捞5 ΨKίΛΥΦ­^τ]παͺ‰Ϋ%΄«+Ν Ϊ©#‰υVEσ–\ξ~Eω₯"P•½kωμ£ΟΞ~%²lžχΈLΝs+Δ™yŽΥoj† UVΙHFŽͺ`εΨiέιΝΆϋΘσ8QΏοŠO ³½6²ͺk“κƒΥK½Z$ΦM‡ξžŒ αίτΰsS—ƒC]·‘Υσ χΧΔ…h„vφϊ~ΈBω1‰Jμa:Β™¬ΐœΌν`:Ψφ>°LcœKRΝaΏΊƒ-β–†4&€w(Ω[πˆnRη·ΧΛ(UΏγ’KΪΣ}γ5*ρςa㧬槐'RΚ4₯ο―>o R:`¬4€VΰρΜ·χχ(Φ2λθͺžrn­ηΗκx¨‰κεj¦ΟΧo \sšΏΜΝ“ςτTΦ…Š Ψ§P²΄ΣPΛΫhy£Ψ`#qRΓ£ΘvEjΖΑA fRπ[SiNΏσλuC%ώeΝ³Ny՝Υ©ΘsξαGƒ©έΕ9ήΔή·h{άΟάΆcΈdE>!₯Gm,)hiώ|κί-MŸθξ7ΒQ21dՈαœέΐ²Η±Γͺh½ήEΝm μ©’\hŽ endstream endobj 427 0 obj << /Length1 1626 /Length2 11744 /Length3 0 /Length 12578 /Filter /FlateDecode >> stream xΪ­yePά–-άέ₯qww·wχ‡nά-Έ»άέέ-xA‚»B°ΗχέΉs§ξ›χgήόθͺ>[Φ^{―}NuUΣRͺi²JZ9[圝 ¬œlB£…;XΓΩQΕYP‰UhΌΩy‘ii₯݀搳“Œ9(ΠZd€–..§   2-@ΪΩΕΫ dc 0hkθ223³όΛςWΐΒϋŸž·L0ΘΖ @χφΕθΰμβt‚ΌAό5@Δ°9ͺjϊŠ*ςym€<Π θfξPs·pY”@–@'0`νμpψΗ`ιμdϊ«50Ϋ–$`»-Aoi@/K Λ_.€ ΠΝΏ}€ΐ7s'ΘΫ Ξ“₯ƒ»Υ_ήμΦΞrqs~‹p|󽁩9ƒ!`K7 πVUMFξEˆωΫH$lήdα`γψ‡–y­Τ@K[€΅ΉΓΫΌώΆk;Yέ@Nΐ7])€•“ƒγί|ZΆ K{§Ώΰύ‡ θdυοτί€ϊ›<»ΎŽΊ²ΎσσΈώ¨φΆ-o—7nъ²³Υώ‚‘’rφψ²rς XΉy8ίξή!A>¦δί@œ:+›Cά@^Γ·Ύ98ξώ?>:Œ¬“₯³Υ_k£ 1w²zΫ΄4όεΆtws{ψοΛΦυ?Οο<θ΄D^]vΆ³ΛΜΙ‚4ŒMΛ pΎ w)oΦ*) ͺsξ̌ά¬6{gk™zιτώzκςg=ΣΑΔΎ}:πΗ'RjΖΑ"¬ Ίn~ζƒv“r΄¬3έXίΛ%₯-X>ƒiu “²gx²Ωnn7ΔΛ{Ζ j’ \š;τˌ¦ΌΜ(μ†βΣ3Ί”γϋ;ϊ‘Ιρ±ΡώŸpƒϋ$Μω H΄Βζi§”©o3·›fΛΈG~(νIwƒχ«lIV­]ΩΚΦ΅L9μΓΜ΅zl;_λλξ~9*§ι„ΗJ9ή•Ι¨»Χ/6Κ»ΒΆŒ|%ƒjNΜ¦¬„{ΤNΈk…ΐFLή!Οσsπ²7ΐ½ηiθ`ˆ²ΐ‰:b₯€­ψΰΕ“8ι#ΚΕ›6Wάhΐ«ς,f‘}μ½<’ϊ(x«Ÿν³r<aΏVͺW%‚ψ“"NΆ₯ϋ›;α }`DfΎR‘ΰšΓΥζ„Εxζ»Blx‰α€dΖI½ψΪm !‚‰;ͺβηΝ!ΦΊωγ5;$od‚YΠΪ2nΦΑfΊήM λΖ«(Ιλ·P"ΆΟΏ‘I—J”˜/(^δ0€Υ\!¦·O²’dUΞhκ.Ÿ™mΦy†ˆΨ)&Ϊαη[ †7πσ‘w@tΕγ™…™'δKlƒxςML'eUΠ2vZαΡΊVˆw°»hN/ι¨j4„€άu~q³EΥ ‘,7ϊόО­GX’Ï/P‚φ¦₯Ζ6Θ§7±?7J낍ToaV?‰YbQCΒ€ΐmrd²‘˜ι0Ί`¬ΰέ>*œhχεNΞJ†X8Ž­ZGΡΤΜhΡU;νώρn0tM ’˜>νΆFErκŸ[]r:~ΪΊƒ2~1ˆΏIjυΛM¬ί²ζ—G ϊΌ]Ν.S2*ή”sς3Ζ–)άτΕΊ‚έ%£fΚ1»μ¦ωV½υηΖ.VMβϋ©.:η'ΛZ?’/a”kfπ͘žΜž‚Ί…Cσx]^Iω+Γαζ‰ŒfdιܞΙv$’βL nχkΔ4ύRškμ'ΰ|—ςΐ>Κ[iAoSξ;(MΔ m€ hg§ά}…ͺpCξ?†Mο–SώΌξ φ›ΰo•Xω'±šύš1ΦΙΰxχša y?[~ΰŒR)6€°tεΧs‚}Qa±JͺΆίΨΪ]ο%? Υ«,DmcΣƌΗ2hΫά™ uψžΪεΪQVuΉ°όΜfIΈ΄pύJˆeώ Ρό ŸΌεώ²ς*ϊ9-7¦RΚͺ«1ΆAy ±[m‘άπS9ξ%t\Ρ>ήέΗΟή»’†ήvY<‚yΉΖtκ…’Œ-ΈA_τ@?ξH>₯\λ‡ήΜPGFkΥ ΑΨ χΙkΥ 2f —?0Β5Ξ_ξ,꘧œΘ¦ƒ {* ‘Χ"Ά ΌΏ6žœΆΒkαλΈ+ΡPΖtνΣ3dœ½yΘ؊ό·ΙCPΏ ίBΗjλτί“O‡Ρ& lύ‰ωz€“^7n0P΅ŠΕαK"7‘Cw–PπOkεšVMΔηΟΑ5zνa[ΏΞδ"Ϊ‘ V ¬%ŽM…IΫΟ‹μHί α+ϊρrLrCπωO{(ΖWlbaq^7PδΥ…ΓΊΑΣπPΓa8¬©bορ{Ϊ©θm7Γά=eΔέT›£Αo XΧΦ₯¦’ Ι³R±'Λrί;υ\ΉφŒΥQΨΰΰΎ†ƒ~™`ΠΗ”CCΒ—π8ΞMΚ J]8{“C€K^ D½YηΑΥ4–pGŽΚςξθne s„^;σ‡k€Λ΄βζW‰ΙwΙχOlB—ͺPΦ΅l X‚Ugχ²ζ?—aO­|€ˆ™O±ά[f—VΌ=£μτQ˜ε²b$Ϋάƒ‹kŸΟλϋ/γ α€™ƒN¬3š'€Ρ{ɞχΚ?–H7ˆ[ƒ‰Vφ,L«s¬VΥΡ8©ρόœ ‚ν?·8, /“5•ήςUυρ6dŸμ&¨έΥ鼐ΘΝNΕ’(ΙE7Žvϋˆ­²£―…$_βžϊΏŽοLωzi-72LyΩrϊρ*g`%<$TRί™΄ζF-ΆδΞ1FšR̞ Iμώ@79Ή³ΈrXΔ.οtΖ[©Hς)·„ωIyΒ#§vŠŠΞ&’ή―Tζφβ†pmρ g“•nvξtΓ(œΓΒν© o.»uw1a„d‰5'κG«]όωͺF₯Ά ͺ_¦šˆ;αξ£?“Ώ‘d]Ϋ‘ιRΩԞGΈΰΥ—B•œOx;' =(ΐΩ#)O-ρκGΤ]Ώdš6ψPΎm€£ηd#j_uE9 „φ3"Μ(/‘Π}ΧXθjPrquh˜S;`nύGGiήΪY‰ο~Cο4SŸ©U₯©…ƒSνΰH?‘Τ’Ωwγέ/|£θΟς[‡O† ?GΔ#ΔHšL΅7δΙRGΒ³·ΝŸδd+¦ŠΑ$[>O*{­M¬Šyh\3<˜;‚Σρ™igTw έ2œ}ςΞ¦*ΊwSϊ†wοŒ…?n˜ϊpͺa3OΌςΚ‘π'cug¨[Dψ nmn‘ψ»ΊƒΞ/4ˆ|κπQ§RΏ p=γήΈΒυ°γάΆ::-/ομʊΊχκ /Υ..ΎΐπMΩλΜ·CP(‰Ύ±aό¬Krεp±Oρal΄ˆ–σ+nΚstΰ;5ΐ=KΈ΄π½Pu4"λoMD^u»πέΈ5y$8UΑ―΄ψžͺjιˆgτΓ41gί,’~FΗϊ‡#ξ5η:©\ kηόμΰ/ά…Ψ₯ό>£Η Π—*_ΰ/Ι+υ£9ΑνX4`D–±PΆΔdΩώΤΘΛ;sίLj­(,ΜψsΧͺ 4h“˜βέμZV&Ήγ">Piόψ£πΜ~:9gZUm€c kτΚ^a²ΆUΧ0Ζσ%†˜r‘πΦΫs•ρ{ξΘE±Ί7Τi&85y’όΘ}Ϋ pΦ1²ΓZŸq°^Εΰjm’Γ‚Ι…. υ8ƒέ:΅Ύ—ξŠΝEεYy)'“l“1π*hiΩT§YŽœQυ›§3'DLΏHΡ9’ύƒΐΦΚΗ\\΄qkQϞέ+3Υy=š‹`ŠQMMέ$Y„‹ςωΗMqr―ί…άI©°—δόΘύ…BK¬&Pq¬Υ¦g>‰=χα)1IΤ³M2πΗ G%3†aξPr(ΣΌ&ΖUXΟιr?ƒ…1W”½p`υGΆmφ\I‹Πς}Α/tn%J^₯g[’D<ΣyŠ1 ‚ΐχ/ΧχΙ+„ΕV­O8hZ`Bφ/!Υθ½)Ρ'Ηq«%›-8τ3zδ YƒNˆΩ­vχΧL}ΐ&ώpK€–ΡΑzDd@5ΒΥbΙ7³£B^vΤ cθό/1hΧYΐώONϊΞΥw #UΛZΡ YιQπ ίtχy/΄`H¦“υαoΆF9›lW(Ab4RN΅ ξΰΟΓB·9‘ί‡ Ϋ;=И*Πquίπ£ί=ŒI  t«¬ΚdΑΏxCΣα(š]^β\ΥΒkεθPͺgyήΘ~_8ρbϋvΝmhqέ“ώΰv2‡νŠ|ͺκλgSlA‚ΰνΝctζž’EϋI#qθ^‘}~Y|)ΐ~ι­&΄Ν,Ι` 9"Π‰Š―ρΆi˜²ϋηλxsg―\¦`»'‚kπz%βuΠ‹dλh― ΘJΡ"‹Pg=ς¨άΈ“—y8•»ςν(Ω¦Nρω₯v*?C'ΙΉ‘ιΓ²²ŒqL£9Šίτrxf6χ{Qv2”πl°b“zΘM[~ŽyȐ΅‡ήΧΦθ«zαϋ’σΐfΞΓ™G(‹\ό½νυ_ΝΰLU¦₯˜;-7χ½½g~aχ¦α.σΚA±R·, ‘ΦΆˆqΕ«QO`£ϋλέΥς6ΛΆ|lJϋΈnωLξΗ~+Nn/½Υ‡ΪΤΏΜ #Wί°`I­Ηn–4Žό+½―Ž«ΌBĎ*Μ‚όκ—SR\ζ(€• Ί(•ήωc—…[νSγ.Sχ†…y˜O1bcΥfzbJ:ΒΦΑΠ/-xΪσΡ³·ΪΉΠο›BlŽhΧθGέΔXD·š°έΝ¦"V“@[4Šc%YwΚ%ŠΪ_ \k …ή+Ÿκ¬ŠGˆ( Ÿ–ρ#I»Ύ·—Oδΐ`4Ž7I&‘'‚V»%χ‚fPW φκΩL ·G;΄©ΝK7“Ω™wδVLθΠ«Xgά“qΕΈO”θ˜ΤΗŽ:,ͺ:eΤζΎΨξWVμ,\X±ΡK<Άcκ!‡πΆΤ΅΅³sΘ‘±πˆk\`†gτ5zζš{k†Š;Μ@ΟFW0°R(g>λζUός:Έ)ŽJ'B OYuΥ}§’€XάΰNuˆB0Zπ0~2€g»•½fPQπEΨ+±λσήCZ[š)‚ϋ‘ Z.τηgSΤvθZyidδœVσ§~I₯νa™–ά4Šζ8_Έ¦Ξ..λIYΡ ¬–ΕγΤιγ‘Iΰπ₯0od'€QFb λίz›<ΉΥμ"Y‘]K™“žΔ&§}XΙ=hΝh]ۏžμU}‰x‡’γ’"=:2ςρ1D&ϋZtΠ™<"κ ζo•–δ™O?Ξ―…΅ά&οΝ :5ΕQlSΔBλ²–ΟXΏ?£™΄#š₯%†λY=Γ‘‚_~±g(₯ΔύTYώΎΎ—2Q(ͺTŒ‡ν›κI-[_ΠΚtρΉ·¦o»˜°Ω £™Y‡Vαιώ^f»XηB©ξ«G ΨgkΛ‡έΏΛ²ϋχ}–΄'’ “‘ ž½„°‰Σ:Mνψ&Ρώc Τι‘θ¦ΧΗΒός‰U7Iα‡ͺ}$O ιλB­ίh²—‚ςΎp1λ_3Η±J³xεκ‡Τ²uψ‰*Π¦ςX¦&AˆΔG)ώ$ΔQΛ5E*KFž=‹7ιbό‘'dxχ9wΏ„ό­{βa[©p…}«“ωή’c·ͺhƒΊΕIˆ!•›·tgJ5+Sp†Ο₯sSοόόσΒ–n}zΘO¨υ₯!:ΪΉ‰ œΧΑ– jˆVYG@θ]ϋXŒΩh\rЧΒDΌiΆ†\ϊ'uL³Š<‚_cκΩ4‘Υ ꑬΗγ$&$’(’θΟ{« ΦTςΡp€:ς bˆ^jJĊ±pmΆ΄Ληi;a3[œcϋSιΨFΧ―…x°©ε£¬ΈxΛ…†[V>3ŠjIϋn Κ(”|=#Π Ιw¨P‰²$½μ”ΑWy !{£Λ)Ίœ;πΑes…l―w³«Ή·!wK|•ώΠρ¨ ωݏ™Awϊΰ°Ώ‘Q,}‚pλcˆΩŽΖ ΍A%e„tωεCΏ!ώυ75Ά€?ž’šΗF$η'Ώ₯v&~Η} @)@TΨs§oj²9©‹@νDȜ'ςWu#O#ς@½]vπγFVΜΈSΞςRΣG*»Š”Z%}ΤΉ5όύ¦_ `³XθŠ„>)m_…{ύύ―˜#!κbϊτ™©0lώ̊οΕτγA”ΪΕΆ)Φι&ΐwy $ β¨ΪaφO+Ρ\)…¬ž±ZE„CοΖΛΉΓM]Σw†,?±πϋ‹;€z¨˜–ζ• <Μ|ΧBIβ)%4†ΩΚJvSΥΜϊM1tη~Αά~t˜!ωές τνΐ6φτ‘Ÿ1&―]‹:ΰτž‰κq$m§άM\†n&±άΫdΥx·9<ύ‹΄dΔΥυ1ΑšβA“Πa–γώ9ΰΟp¦Η™xaTƒ ΧXΏ1“@Hθ…]Ί‹ŠΑ3Υr׎ƒΎΨΖ£±εΔ‘Ώ)§™½Dά(Πx%ό¦*N΄ Η2Wςε ϊτ£AΒ‹‹‘”›²~ΐΘvσΔQΆΊŒΠ±Žw†ό8θŸδ¬’Ψκpdxζ,,U­‡’;¦•>έ&Tx^_uό€Ζ9|@Ϊ '`θΚ:%ΙYv5LΦ3†Β=brͺ―_Β§΅Ί?Κq£ .}¦ς°JlΘL{ςm2=tΠ[βΗJ ‘ŽΦΥ…YRbƒη₯vŠ) ΐωΡ¦ͺώ(q˜ι"pΊΒΑvOρ^«NΓηΚ3‚„ƐEΘ)–Y<-’εwevnk3œΉηœZp•·³£ύWεhŒρΙ’OΗ¨χ΅>ηHΧΡi–IΆ``ά…ώίκ"αΜ Ι™¦š\1YZμ’΅$M9—Τw><#,φΡζ «tWd¨Έ[F:Bβ­eοά:5ŠiϊρHšIή€ΝΒ‹ΗœTΕa‘NΠ:?NνΩιxίρε―©6ε"Ύ“Χ›ωΣϊΖΐF¦ύd’*JvθKYΙΓμ™XοέiŠ':j8ΤΊΗ9lJe4’β’to¦vϋ}%‹S¦ΖE6‹ΒO‡1ό…Όσ. eΧΊΎVυΤQ 'Η€xΒΈζά¬±UXΊ[А‡½©¨ ƒy΄f3`ΊΨcZΙ©ˆWA“³)Ž˜©·[F؁mV\αZ .V© `SΊχ9ΪIΚqΟάy=ΜΣΧΊ:sοΣ™?!3gmήΆΛ‘9Ο]BRΫΌΖ±)Τθ{φbcwL‘½ˆίq“ζά™ΫnŠπ³«Œχžκ7YΗ\ŸΝ‘ŒΎ‚!]]K a&ω£Ž1~%{1$"D»’ml_4οσ$½Μ™ΥδΐφJ‚>FŒς;hhT’§½\­"²olηΕΆΟ½£δ8y7”LΊυ=Φ?Ή#KpjΒλN£‚2Ά΄B½‘ΥΡΥέ&Ζ—7,©ΔžuΎ>† †0Y"%‡~ˆǺgM d`˜Υ4.šΏΈψω&9RΊώ½§ήz%Ύa’Μˆτ•H²6ΪkΘϊh‡¨hH› ž'–ώε8γmxΣYΚ³"‘Πκ°oϋΌqfέ9ΊΘO/s}'ΦJG-2Εβ ,Ω]sυ+ΤθύΣ,—γΔB’Ώχ–Β₯s&vnu'Έ–_θv€’σxXη Ξ²‚΅ώxΣφš1-#,ΖyΚ XΰΗ?BΧδqa£ „v τ“Β»έΓB§6ϊΦYYΆΕͺ•£7£‚w(xΥ”AΓ6JηΙmΠ©JΏUZ›P πeΙ…ζ[§JS7΅Gϊg°α/„6©Œ_£<ώ>{fIŽ=ζά; !A±™ˆΤˆ_Σ3 Υ€‘ ϋ Ϊ±>,yl7uŸf(ε γT‡ώ}š…ΫMήR«½eλ«ˆ­Έ18— ΞίλβηόVΥ ΅+*ΙΓ*V{ίV]#IE“:X&š}ΜV“<Γν΅Ο‘Ι?ί[ι«,15ζO`^•β€άάεo{ͺvuΏΧηΑζΕ™œΒ’zΝ0=dϊ“ΔΩ«šΦkκXσΚf dšΔEFε³ ‚ ±³h-ιΙΪ‚»Ξώ!υλ‘ΙLpΞ]Ÿ―0Υ2Σ#|ζΡΞ~Ώ_#M=jΪ1Ξ[εZžn{)λΒrρ―ϋD1Œω>ά—›G0θ+ι/Λlό¨v΅)‘λ8όμ0ΪλuΣξFκ$χ*:¨9―W)vVsΛZREϋ‘ΓΜfz4+FΟσβƒάUl€²ΕW€4LmP[·Σ87μ}‘+ΡЍШϊH˜ "~Ί—αOc[Y/kτ ŽβJŽc·•ώ€$‹ηg½ΝAλ›ΙΧs’ρ—›ΕΫ±o7rV(Nίxψ’ž>{Hj£†δ„1ΐ”ΰδΜ Μ{p8ωo•W­ΚΑi+ΤχƒBΌ“γ”Γ&cDΉCŒ-«χ†Ζ„UΑŠη£ύ?ϊŽΤIοbσ&—NIΗaΝJΰΡ~<π> 4³Hdvϊ…!(«¨΅Ž™Ω7ό’FβM\χC οnz£FλΈCΕ·H+«.ΐš°n˜z@uN?Sγ` ε·Ψώ]dΣ!—G-Δ2Ljζ·ΉύΣωJ΅&ΑΪΧ6KqdœΞ–ΨB€‹lJ’Ωβς<]αOD5ςBI F»θΫΓΡz“l(ηψυ3e”zy” αί2s"„Αk¦E,g ^˜M9Α»4¬ίYβήSˆdΈωq‚n¨bϋgO₯ι‘MƒŠ“½G7(,j«dλ–Ι…8tΥ€”TξάίΩnΈ%‚€\Y‰iϋ±΅-@ϋ\nω1ϋSΗρ@=“Ο~υ?9e€₯Τ©ΒdςQΕ›“)%ώσœ―O’δF„#~ƒŽκΖ†δ—mΏ sΡWύ2™&€Cp0ά) yθχ‹¦UjejξΨ 4Všœ“ΉHEΗ )cgA=5!ΡNna»³d»β$ ΎNοβΑ-N_«γ―z?cμ"䀁ΗO©NΏΘ¦ΝgΆ†³ι]ƒ"$a₯ΓlFxωHςwωcP¨±ϋ°tbG,ΝΒO/‹ή9BAlhLπ#ͺ =AšUΑ[Qd7‰΅muj¬ˆ<@‰οε’Ρ€βΕχΨ2“4%Ϋ“›’ξMMΎˆ)3ρ:²ΎJim썺vŽ5ψ"™GΧΞ£™ή œa–εp$ΰεέρ‹ŒΨͺžκ6 Ψ †‚l>4(έ^δ 3υ–‡—ΫœΛ‹!ύζ`M^οq‘?πF({ΎΧ8…T\½tθ0ώμa’ˍΖcEΖχ›«άΡΔφ‰Χˆ½Ι£φVŽά§Χ‰r}Α’E§ΘŠξ7ωTΈ*žCLϊΙ1OύΥ•ͺW²έ₯ ΥIt'b§|i§­Μz©mYyv^ν#8{μ›Ί¬Ν†…ω3τιdΦΎ,¨ξN ΛΗ 6σŒŽ…ήΦ2»XΣ6~γ1γrS΅·ƒ5œ½Ο΅8ΡϊŠCz‘\e2mvMRΛΫ#κ…ϋ Δ_w–p!AιVtΙ«ˆΛˆσTQ;Ώσ’‘Gώ•ŒΡηΧ}δξ‘’ZžΞφΟiΣiέύ"­οi²8ΌζŽΰ,­|w•p0"W~7€_ϊΨn η€Ζύ€δεŒΦΣ½jyΪIZ8@35[Τύψ0΄nΧ™Oƒ"Τκ#F@ΤdBaǜκs ±'»s`VΪ\vg.ΪvΣ"Ÿ ικDŒιk|ρb5Ž(ƒξά‹_,1#bΖΛ`=œu(£Β⑬·_τTm`£›Cηω«R…ZηoΛΏ ο‘ΌΈ‘@}LnΎσAj_ΦΫ‘+yg5"ΗwίʈΣίΪn'ΆCH‹8?›]|JHκ\hw©T+vΦίMλhήψ`€$νVlWo6‘ϋ”r™χž½Ψδ½Ό ‹—t#;!O»gΞσ/jηwiΉmΑakglί“w₯Hƒž£ƒ,^”―βΎ Ϋ€|ͺg5I˜lšΘκ™ͺ|ύΞGVΉτŽξΐ‚~XŸ3ΨΕ&Η¬Γ”97O™ˆϊ&P8N€2ƐΠ%W}ΈωδΖ#iR„’Τ•ΑΪ/κ›ιν" %ΖeJ ΓΕαƒΎ&E? Ž2εFΎc :-‘‰εχhbk#9ΘΥ”_€χ‚μ”Œ/I8ZŽWJ]Tδ• ΣώήφΩ'₯’©©ςT«Sߝ!ΎlΨ2L8 ί8“‰κĈˆΪE|λrE’GγX2σ΅˜ΰθξζψΧ.ƒlΞΙλ9μU­ςΣdΗhΉ4TBΟ/8‘^[JΐOGT£€ΞX€ƒ+I˜y^β#’―Ν±Ά&„M°ɊrImΪV… ΤάΗΥ³Τ5+ έ( “_{μ[)κωFe…SŸΜn4α>©|DˆkνzΣhr-ω¦¦ΌζΟzMz(8€λK±(Мwɐ&Ṟ©HZ|­ο Cκ<‘'sΓπ¬₯=°ώψ§ΰΫœbY•Yš/2λ’JΥςBΨΠπDϋ‘¬χ‹#pQ’Rv+|shϋΩΛΞ-Μ'―½gˆ«Ά_Uεp•·Ήf£+t‘Ώ₯αL‚!1ΡΠE§ ΝΚμUt"Η GˆdzUϋœΦoψ*Š{9 ³EΈ:~taG·•uIw‰ƒT!u,’›A±<šlΧC#0¬=ύCΫR 8N±1kfŸΜΡφ˜ κ;N½_ƒο+FάW–©6˜aZΌJav΄‘+©Ηφu™­P9,",³„Λ£(ϊξd7£ς+ΗSbWζ ΆΨ’†bK•ΓΩΞ]ήoί:]F*‹d.•μ›ξο„V€O$ΰΒς ぀ε•F\Ήαηak{τΉί€΄Sόκ°ξς>§¨γ*x Ό|•ŠŠqλdοJ1O9ΞΡ5©Dφ&nb“ΦΥ>C—Š»Ν(ΰ!œώ’¦_Ι ‘?γΔUa§μώ•Λ.`nΎΠ1€ΨΪηŸuJ>uRΎγZΚk_Φ Ώ(*MŠα‡iƒkmcbήΌΏUΝ6υ„χtάΌz“κΠhUύ΄ϊ‚'9·rrΑ„VŽΊΦΎ&@φ§± eD%φŸΉ‹²΅bΒέvˆςκχ:όΘΎΪ–³X¦¬Ti6Π…Ν -{ίB+¦šlX- ,uaxΚε”ωCUήA‡ΆΛSV\―]ςΕn|Βα¦˜˜₯ܟ α€'ι™’>΅ΒΗιfp5~ΰ‰χύλ *ŽΠ]§Αͺ:­(1ύF”VrΎž8RΖXHΒς+zΡίNΎρ#v˜Ό9Έ¦vA©m”½€C“•“Xτ'μ*2RRΪ™+²& Jϋϋ‚γψΈ@Ι‡H½©c=‘f»©>rίƒ+B{z#ϋP;n!_tcωˆ4Οq•Χ±οΟVipfνz˜…4w&χ ν~―^Έσ³€{ktδο‚ΛklVUKπηv#΄ε‘ŠΌDΌ@w:Έ7Eϊ}ΗlΖeƁϋ±GΔ_‰Mψ'2λŒΓ9ρQDΜm~sλ q«$‰9ΆfPTQ§=›e±ΑλQLw4χΜΌr!Do^σ kΨ6nž³ΘHεt~7$οkΨ‹A˜JKvό"nC}$Œ§ΜTJΨϋδΒ0ZΨOΨB ¨³‰Ϊ,ώŒ&I‘¬"Η Ά Ο–ΜbAa#WΒBDFŸtJŽWμΨ–/ΓPε“GZΛJ`¬ΥMΜ&uΌϋM²Ϋν‡ρ \Φπ[5“O)6)U‚*)±ί`9€θλPY ›Β 0ˆ§cY?SeΑeoYU,A5qεiVKμ₯— ˜NΪ=Z£=†ƒ€'c’Ρ31v7μz.Œu6 Ϊ+3―;1ύ}—‚[ο#―ΨΙ‰_«πVΩx0γ)φΰ˜ ŸqwΏεξPρ•d’/hbΰηƒHAΡΔ~©φύ•@.}Τ ·:=3Aξφ,Ϋδ―ΧTω~j”€%|Ψ¦eΐΈ†\|ίΒ©‘ΌN€ϊΕ"<š]5c“bΓ¬e;<^Ιp’aŽψ:“X©>Ÿ!b.’±Υ”lŒ(ππ s₯qx|ζVο™Γo›²ϋšnŽ,^1‰3΅ν<žμ]Φυ΅‹Ϋˆ]»έ‹Ί΄έgήγχΙL’Q¨%Mϋ‚P{Η!€ip€ο7v£š ‡ΙφΔυJΦ:™χ>ykΦ ž89zhΆ†$hnς]CΎeΐzς‹.² Ή]ij‰Μύά‹δƒΙζΒtSΒi~2ήΉl.u>a‘²μg?L™,Κ/(­ΦvH3φY"6ͺMή‹{>nc(‚гε³l(t1ο άƒ‹°Š ‡˜&5¨ŒάΫπ™Τ%S "yΘXj?Χ―Œœοu„E’τώΆΌžΊf*ΖTΪ!9JsCΝ~ΨΫ™ͺ€ΐυgΖ1sŽuϊ uάosυθ³ΨΠ.Φ· υμΙKLίNΪψΎQθI¦ξμWΑ΅ΤΥžΕ°w0Bλ©­ˆυδ&σSωΆ»g$„δΨΆpθ‡dφD’ͺ³M†ού5>ͺ;_¨ϊŠJ‰ψZΩΔX?}Ξ«52b )cΐ ¦'œt.ΙmΝ‡<δυ1¦Ω>Zxkξ‡ΓEa~4’Q@/»7JŠH)zŒώ_˜Š‡§NΛ‘ w‡‡Vl¨†%Φ}όΚvƒŒ€υ˜V?³v°Στνν§ζ37cπh(ƒ[·—ׁ‘W–…Ρ~›™ύψvx[Νϊε\»Έΰ@Ξ¨‚z™J¨ί<•]3‘ƒ‚γ endstream endobj 429 0 obj << /Length1 1630 /Length2 17468 /Length3 0 /Length 18317 /Filter /FlateDecode >> stream xΪ¬ΆeT]έΆ%ŠCpwΩΈ»»»»ΛF‚o܁ΰΑέ!Έk€ΰξξξξ./ί9uλV»―κOΥύ±Z[sHΗkQ’ͺ¨3Šš;˜ZH9€\Y™XψJΦφ¦n.jφJΌ Œj–n€ΏrNxJJqg  «΅HθjΑΠΆ0HX˜ΨΨ¬ΌΌΌπ”qG/gkK+W¦š6-==ΓJώ1˜zύ‡ζ―§‹΅%@υχΕέΒΞΑΡήδϊβΪQέΒΰjeψjmgWVΡ•U’ΠH+i€-@Ξ@;€Š›©΅@ΑΪΜδbA ψκΰ °ϋχ`ζ2·ώ§4¦ΏX’. ΐΕΡΒΜϊ―›…§™…γ?*€£…³½΅‹Λίw€΅ ΐrύΫW€5ΘΜΞΝόŸώΚΏ:ό+!Gg‡ΏφuΑT\\]̜­]£ͺHHύ;OW+ λ?±]¬ͺ_Zš;˜ΉύSΏtaώj]Φ €«…§λ?±L-ζΦ.Žv@―Ώ±‚9:[+ 7kεfΐpΆ°:›ΫYΈΈό…ω‹ύOwώ³NΐR=ΠΡΡΞλ_ή²ϊŸ9X»ΊXΨ}e‚geϋΣΜυolKk<σ?³" ϊκ`eω·άάΝρ?tξΞjΝ?3Cϋ7  ΉΘΞ `nρžYΙΑυoHΝΛL}$7PόίBπ ½oδώWŽώ—KόzŸ+΄”›Πώοό{Ηώ. πwΟ,; σΟhomηυςϊ―ΦΪNχ&λ όΫQε_jX˜Xώ-΄v‘²φ΄0W±v5³|ΪύνΩΏδš s g;kΕ_nΥV#+ ΛΡiXY›Ω‚ώ!σί* ω­ΰ/]ʟYIU[\KŠώ³`e¨ςw\5Όζφ?ͺQt0Ÿ‡`ΔΔ<>Œ¬\<F6ΦΏχοoBΌlΎ›bύΟ³"ΠΥΩΪ ·nΦU?ž<ώI™ƒω?££ξ ™Ά)ψGmζζμό—δ-€ΏUΗω_soaαiaΏΌΰ`Ζd“š‘ζZƒ“30&‘ίΣΕ 9μXR―Q˜ο_επη{jΨoΉΙ[u0SΓίG«Χό‰γϋžέώPΆυŸd‹‹ϊ“»ηϋcR<8"ϋ™₯ΤΕb΅£6€‘ΧœœR%=>Pχ τΉ†ξή# ΟŽύBΙΔρK:!Mtυ2qΎ«7ϋ€~qηv©˜ΟlΦBKΠτπ&x]p#‘ΈΩΒ`M;g'ΆόίeY0•χΈ Aˆa+ό)Δ@2">©!gTΫΎ {ΊΔ΅rV'Δ—j›±ΔcpΠΉ J^ΘΉρ¦E#"Σ7—‘o.£ΕΒƒš¦ͺ«)8ΙΝQ‹/L))„)Rγ?\–Έ-RmτΫΥ—N°¬R#΅·6λT…q@Π’1šυΐ#βΞI€EDDΧ&„ξ4κφE­”#FxG"y½[¬lά¦bf`V8΅1¦­ΪCώb—’ύ/MfέΎ„/J6‡ςφΫm’QσύŽΞTIΞH³Β&“‡= ςΕΫp^υΊ2η`ΏδΏΕ(εϊq$靾#Ο™Vχ740θGl•dΆ‰%gό‰ΰΪIδ'Κ<\r`οPΉ kζ% …xFΨ"ΟΖτΧ Λ ƒί3[²ΨΛUι|©πΦ‹ΔΥZχ”!ikΊ΄ύ]αm| VόBλ;DbΉΗZn«c$₯γξ—oξξσqΉ9•Xδ»ΔσοAn]>Iΰ8ΊΣEŽυiŽk“ξ0™«ΙqV…¦ΆCfUDΪΙPΰγBͺr₯ž9ξžΆ ™ͺ£ ”ΪQόςίθTJ›“—λτ—©+RΘ’¨δ΄G΅ωeμYλ’…ϊΰξθˆΙzŸ―Β. ¦hβ½ΣA•@Ή=/Τ~ͺ$Γ€Ž`·7WΓ5ΚήQ0ΟUZρƒj©>ψ©Μ˜[ω,±wΑbw1yZcslG‰zέΔZ uΫΠ:S-’²|nΟuηI‚„*‘ ΐ‡• ³‚ n Zφ%BovJs–Bτζ”ZiΕ‘gΣ»Rδ x˜°ΛA½όΌΗζ¬k:ί7ΎΜ.Y_nΘφKΌ³ΧΘžŒŸΤΞ>vei陣W°ΌΕt†‘–Bβͺhg@L| bΎ‘Kό\ξy˜tͺ°ΑΔ‹6ήJ€‘‹ιγΆ¨PΚξK½5jβιp½'h+l6 3}8ΗΛ*Ω4YΉΉψσ―ΦΪήE^b_<€O3‰ΖΑi Ί\Ψ±XWΧN|λ!!†Γm¦ππ—"Ω§j8ι9νRύ–ά€IϊέZσΒ.„;Β΄ΛΌ―΅“0σΓƒ<i―£κCŒζ=Η‡SΦσ}αΝ+,΅2ΑΜj€BΟ G^Ά‘eͺΐ{ΐμε κβYμύ@·μ8m―ϊŽZŒ€wΣΐΥkυgjΦ«ΌŒiμΝΤPιθΆΆ-ΤΛ΅U…džωΐ„HΒΞυ/œώ–κυGWΔͺι•xB?€OΗFςq%¨WΙdτΈΏͺ>I5Œ5cπΉγ)—Ο|wΦ–Ψ4l=qΚσ©šM V'oO¬CΩηΪβ§ή₯’cρŒτ|Τ±ΉΟ›ξ3βg<Θmθ ²ό"²Aoΰ»GΆp99:² Κή`Ζ‹y‚˜κΟWXiΎσΖ%>‰1m©0ZOΡ|ψΌW8ŠώΈ5Σ%,½ώΗε΅"f£vΡ=¬Ρ3I©¨?ΥcΔ,S›­f1YΓΖn±(}ο^ˆλF hΥΕΡ4κQŠt°7O|1TΉq?^zΜ΅κœk(Γk£ϊ ©u+ΏšwΑΪ#Λˆj+ZyqΝ$Χ4H–s³γά^&B/ 8\^Ό)mώŠdί­ΰνH|Ώ‚ζR§½i¨(8 ƒ—`=j|¬$&ςήΛΉjή”‡α΅„ Ρ ;=Κw£ >›Ί:άά™ΤρΈΣΉ€XA$μγ…;Ka?G‹ό²;΄-Ή=ŸŸO£E‚-ξHdΪ•ρΑ”^ŠΗ­&ΒΜSάjτϋϊ2Ι A ε͝sαyqΣGκ©κƒΏtQŸ6Έ₯Šτ«Ž½‘–*­%Κ„ͺ©ΌR‡NV,£v֟θg»‰12„ά‘’,}N_Šυ%kM`3~yδ—ug‚Ž’žV§7”Η’['ξ+™Ž!―χ…`έO<œΘeΥRIK @?’άΌ`ͺOω/P4+”‹”Ξ)ΤέΔƒ}Σ!›ΔΡtΧn.’ό.ζDΰS\ΣbpχΜpΑ­Š˜ŠΔνΜζd0a°­†KKš΅x‘Ο“O>ωΒ‡.χδ7.$Άλ•p’§0#ΰ0ͺ‰›4ϊ$WL αAZC>u·ΎqέβΤSS}οŠΞe—„!Ω[žy8%΄<άΆ;‘Ί"b2,Δυ·Ξ˜ϊ5.ζŸ„–ό4ΈOΩb {ΫV­5mjί±4~—τClXcIy{άDR«§plI«•ϋƒ²PXΧ<ΙΨQυ »ΉΞ΅š;¨©±Ύ˜α»pwW gjۚ,¨΅”m¬²τ Γԟμa•|δϊŒκ“gHHΊ0b:·hn჻€jhq‡₯ Ξ„τ2~κΚ§_¬ƒ΄σΠ_0€aΤιοάΕΝŒ±K&[*Ξ¦4S ž”υϋΥj™yMn%!΅’ξγΛ ³Ζ”M¦0ΰξ[±BΟcν-‚’ΒTt&Ul™Λ‹jτλάͺ ΉœWαƒπxuδ œϋ&ϊŽ˜ί$=Ρ½‘O&uΣu•o˜Σ”9ό€’Ζ™ƒ`c9₯Ρ‘.όΗΣΈνw)vΊπώ§κ?€8Fe9ό΄ϋΜτ,#u™T†σF» α€Pμ]œδf8~ΖY€?ΗNΝ+τ£‚δ«i™RZ”UΛύ‹=…u_nβψΰ[Ÿ!ΩίA›έ”X†¨΅‘…&4ύ…iX‘yRΪ…”鉡ΘukΛ΄ςA ›F͍-φƒ­yμ>Νg2Β‡Γ³€wL²Ε»%ΒΧX(|ί+σo’n₯―‚~$μ†ΣΓœ·šυpFZ…p~SA‡ ԎAaJ$dšψΈq―|,aκ!νΎ€$8βm΄Α‘v/œ›Ό“Z₯[etrak „£wrΉ¦Ί₯σX'‚~Ν'S:m˜εg›§γg‚uζχdz[m―ξ)«ίΆ8Λ³KŠ’θ%PXQηIή C&ε#ΧkŒιΕGXyΥΗρrOΎΨ‰Ή…Ÿ/Ι&όzΠP²aͺgΉ‡f hVΊOjmϊΜμT2#Ζ δέΉώ-³ΖZN{e}’ {¬ΝΆξ;d³žχ[,3]¦ΜLδή“Ϊ½=[‘ŒvΩΊ†“oό¬ρvζςπΪ†ˆ~hϊϋJεΙό`ž‘ Υ‘ΖVL.ί,Ώ+ΕΕΓΑ1bΈάpC …>pΘwαήeα|θήξ»LΪH¨ͺΥσ sW Ζ‡K7xUR&ΩρŒ‘ΐxΥd.D j_―^x&Δ›Ή5d{!)€ον,SΖ5±n_i‰XιΩ&ιBέ$g©·mE±Τ$ι—κΧΟwOοςy`mDX±Ϋβ~2Qωb˜u”Π…θ―σκΔaΗάΊΡ\ ·s΅τAεπ(€%--£Š«ΧJ–˜Ξe• Ώ3e:―XšY¬˜ˆζΊ[ΧTγ1*8Jθ wυŽŽΐx^ΕCCΉPV.‘œ r!Jδ Ω¨6―o₯±'q=Ξ0ϊ³u―»£WEέx\ŠN3;.Σϋ £ͺœ ›6Φ;aδiq Ξλ™2ηlT °₯wε8š“Mwαn½ς•πΒDݍt›Όm|ΊUυ ©NXηJE€63₯ΡΗΜa‹BXξO¨}4εΝ=rΒm‘Nƒfύ5tYz, δ‘έQΠYBώ˜˜ΘS°―p_z‘AΎŒτ'¨Ίd°ιF­)'IΆ{H—ž2K—`Λΰ"[μG ΅•χί©S%ΐb·N3€ΐE*ΎΰF’Ά$άίφ,ϊ`΄θ|‹΄\jΞφ'|,ƒ«:a±%όω«qb»7AβœKkΩgΖӍ7xŸν>{‚€yΓβϋdŒC±X1YƒˆŽp_RIΉω3šPγuĝ§4Σν­μΟU Ίaρ«km³έ‘ρΏ.w!)„½Eά3‘aέΏˆhStφt›p/ΧΩ…1¦Xς9Άύ½–~@<3,ήψw½ΌίM£Β¦Ιπ«]ΝφP8.vΛQmΨ]LIV\¬qΈΎ‘ΝΕE½©«hνY*ν?YGψ;εζU^τo}8>-pξ§{χvkΉw•d—ξŒŸϋΗ<Τ%B‹_¨ΜxΆ[λιΜ¦tϋΕK“»=ψΒy=( }μ0LΚA3)rφ9δΥ¦%Mδ<ϋ=θΗΘπαΥw ¦υΗ²^w¬n’U¬d„•@xΉ¨ώ3IRαΞ[ξ{-ό3mH’1s"K»i*Œί‘]³}ζe˜ &ΩL>²ˆκ³Ω6ψƒ°}"ZφAλθruΦΤ~9 θƒξU.^'”Ζ†S―9‡Όδuπωžu·8yψ²Ωa4ŠΜ½gQή±τήIžίuh­“Ζ!S{T̕ψakyKy_¦±C„δΪeόƒo`ΤΫΆ1ωΪΨΒV‘ΰν₯Ϋ•₯{4ρπg<&Ί1OπH3+CjΆ*‘ΊœΗ€‘Ÿ`ςφŽΔ(Γ²ͺ‘hΤh‡ϋ 6ιrˆ΅έ2έγiΜ&’YDτIΛ2%ΰˆk³| iu«¦Ό³&‰ƒΛ¨ŽgΔΣAbΒΟΣΡ·rώ‘(g»i5pΗ˜iέ‘G76Γ§ΑΨΖμ3|‘μγ‚ͺŸΧΌœq£ύps—ΫR¬<4Λ¬M‚ΦγjΟίη>E6ίšζ‘›‘ΑΡrCK»q6lͺχVΪ‚,5YŸ$ΐ” وSΙ₯Ι¨“Xλ#‘v>νπΐ8ύ@bΟΖ΅˜ΈΊε&sψ*ˆΦ±ϋxͺbν–ΙVπiώ4Σ5‚ȝ₯f%Ύ Z‰*$ŠΣ‡6δπέ@QΑΚPΚ¦ŸΐŠQΙζXfvu »"''0₯Ρ?ͺΛΩzΗssίϊ[v|Ϊf0ΔΚ†ηXˆΊOΔΈυ[΄4|~¬YJ&«7¬₯ΥUAΗΜΫ˜.ΐ ·jΨ‘\ύ”πάb―TI–L+ίΚ—P;€…Ё??΄WRΥF\&HνdΌ+˜2A€ν9RΡδy|‚w:εΨΟά‰Ιl|m:Yƒ©$C€³1%μ$½4¬*‚"4[…έάΓμ ZζO£δςh(:ΙCωB˜—4žηC΄α„ Έη&*ϋ©ΖΖ‹ϊΓ`ι²ΉΔ±Ω*«΅ζ[ϊSJΆέ…€Ή@:3ήβKF:φΑ%H‹ hs%Ψ₯θΟοۚMBR oΞ΄ψ‚Ϊ¬ žςH#HI*I4Dэθ SΛρρ«](ROΧ¬ωρ^œΟ`uβχ¬δ?V―"Δ‹ΦΫ΅γšΣψ» ι‰ŒΤ†‡„³%ΰClJ{ρƒάΰ¨qζζzzΆ ;Δ.\ψ°|β›§dsνρ¨»:Ωk{ς«ΎDšΜFΙnΦε³=Ύ"˜7WΌ_PΦ\[μškoΓπήτρ ώΤ"š?QԌjrΜΪΨE¬Ÿϋr„+‚ώmηηθΥ¨ϊ‰ρΥvj.ZύΪ՚vŒΠΩΩτ‡ά”U4ζ}‘ ΕSςlΤZh ΆΑ}Θ7Η`ΡΐkΝ-‰ηjι‘P³―°vΉοΧq«}δN ‹ΡωVeΐg])\±$’ΰΠ€a@8+xΒŒ2ϋu¬τΑK|3W˜‘}C’Ÿ+*ΕWώtΒ»¦ύ4TΡί©|§€T8:zP΄›žxψν«m/ŒŽρσ+σΞηΤ€ΎζU‰,]ŒK-δΤF³λy:Œ.€gσ5έ5ΦqΌύό)ŽφvόŠ—ρ¦Σ42‘!Z‡―šΩ`bΝJIΖΡ6να}%kRw{B0œ”‹ŒμΪ―·±ZŠν—υ—Ϋω4ʘ„_·̐-gώew?Γ7”e@Βχ02Ÿ{*ΫγΜ({— ‘)»J˜#§`œΉΉYθψ:HC?ΕΈ0c“BNr­ΕωGΨεt6VΙ›Τ|Y•ƒƒ92ωœϋΌ<ΐΞp[ƒCCΑΕaΐΏΔ΄̊UF‡όZ§{V{U²˜Ϋcφ²€±η±’,’aε6Sž:―Θ¦sΕψjΆ³…€• Iˆvg˜3^*$ˆˆ::~WξK})ΐL hˆ3ρ€{ι7 ŸΓάUDΩ¬zBξi’vώCδ“ΠΰννB§D·ζš½ͺ…ρv8sLΖΗΏx―μΎήόςvκ–»ΪI;\ΐ\ƒF‚ΊΗHσζΟ’ϊΎ*Ή\Œ™Νͺΰ*άHΔ=,βΧtεό,ΏΠiΓ₯@ίR2CQσœ —LΊΎΔ«˜χ†]N-\Ζ£ΆχΚdBέPO;ž Η@ 'Ύ^ ?hPGBAΧβ3k=™/\ϋއΣEZ’γΒ¬8©Ώ·knHϋsC²Ά$'[JrS|ΝΒΖr‘αΥ=όHΊ7όΊΛZrΥ Γ~t/ΝF‘8ΩΙ)”ΠΉΐΠ:αΟZk8“ΕͺϊΩ“ω€Χτ‡UqΧD'oH(Έΐ’―ˆ½!!@S]9Λn©»cη «s)ζι_Β=! ή’―Fc£ƒτμπΓ!Ζρˆ+τ›ό^TγRfφμΌΥzQv,’ΖΊΆΎΌ˜Žώ ˆW¦d)ΚH8γΗάojΫd-‘ρΝn΅0ϊ^Π―α†}tM κ`Ηu”>Ϊ”œσϊ$£“n„,5vμΤ|›a” š¨ηϊ«,PsdΦe }‹šŽ|3ܐ2Oψ幏ζ&Υΐω“zŒoš,.C>”Lΰ˜&©ΰŸm“X―Ω?W’ˆΥ;†Ζ=Κ‘Rελ1ΨƒYΙΜM–Št$„,~=²KhΥέmLλ7⠐‚¦pϋrfΝnjBŽ»ΕΘ)ΑπΞZ‹ΰαk ΉdΙ$!ͺ)‰Κ>q¬(^L5ΌώυΗ΄Ώ½QŒOχN!Οc,+ϊ“βAZ͐g‘§$W–γ³jΏXY5΅νόφ²š«qƒLvAž$ϋx,jΣMϊ£QωNΓΝ<ήΜdA',ς 6ΫT€Ž‡”γ°‹ΓΈ3pΈΪ©Κ-Ip™ΰ•‰/dΕρ—+4βB%/γ¬3’Ž6ΎΡC4­šN˜ͺqˆ[-ΖMhύXΎU‘—Dϋ&k¬`στ4§ΩΒΦZi¦€mwo p«‹Uύώώ4«,&THΖ¬ μ]υvΛ> -7yDT\qS²ΜO'!²ΝΓψΓς) ν§¦Vδ·Έ6‰p”œΖ4dJ$ά],hZ8UχΑ©αύ°ϊ•A%W±ώ“Ρ3«m΅;Œ\&Λ~Η`.₯:~ ŸΫz{ΆωηtBv‚ςžμ9₯FΊK_ ΄(ϋ7ƒ: gΉI°^€Ύ+S°ΧΜχ¨‚ύΒΔΏωDF-ρ_’†€IΐΩ.Η€1}?nγ8uW^2³GŠ œ.Η¨οgk –…m4pGίε?[£m#6Ψ?qΣ͘+”ͺ—!Ήœ8€ρ‘6β₯7ΜPAbίΔ΄b»h¬ΑρΤ]λΐQ=ΗLΛζ“άΚΧΜνu+…ι₯5Ρ ²"ŸΩΓΔΡέf5p@›ΫEΥχ7ŸpƒuŠτ8ϊ9hΥ©ž΄­ Ÿf.“\)‘–π³Η\Š:UλΘζwΔλ¬-°Η( χHΞ>¦ρόI‰†Σμ”―ƒΫI‚(χ+zνά–€β5w9ΟΊπρ‹Ω¬ΓˆΗKΎ7»$ίχάƒ θNΣCmΈGΥΐΩ…ά’Š Ώ_Z&ΪΧp€F#χΨ.ώ&£/†Οr=€%·.ϋκb|ΐŸF=BŠ©ά85 vλ:½š;ς—½γdϊ]ΙυN·©FW‡›)κ-4ΉΡJӁƈ~μP΅Pš­ήzaμ0?¨?ϊϋεzŒkώ.>να£Λψφάnfœ ςΈΜάO‘ύhΔΦΛZϊΨSvٚfR Eίη›»>ycΟƒRUλGJΘGˆ1όεζ©™d+ζ ΓϊΔΌ])βΜ ΐ€―˞θ©6—£ρΞ1όqαWΊdΫƒŒ`‘ ―–πm·½tτγ‡ρ3"Λh•%Iη;xδA£όΖΚEDŠΗgΆX¦Νοιςω©μ-7ρ§8?ͺpς%—qξΆW=š_du!ΈqαΈBέ}qŠψ=„Υ»šτΥψή$ΨKp!7[Šnυΐγΰ^ε™—ŠLΰ7λ“{η -g‘ΡŸν‰x1η_ό―ilυ1”_ Δ –ΔΧTήΝ_Ι …$He:ϊψΟ]yΆx±Οw|qs’˜’©-?βe·DζΙ’ΛΜhy% Oφ·Ε-RΧΉyγ¬4β:ΘΫ‘Τ¨f³_•ύ‚D-χ­±‚S9oIΩ*mΔu•eπ ™Ηώ6£iˆ\d-dOMn­…Ώ‘WQί€H·°f‘χλw$κ—,·# ³δlfΘ•ΔχZϊQϋ]Γ± EΈ Ίqgq΅ΊMx²Pƒ—­ξτ^«넨§β“:“œ’±ΉA|ΫtφWζ‚z«ΎhΠπγy£ “+˜θot:-bζ{y†–%Ÿ·±`j.Ό6βnRΣξ?HC‘Θ5zžG”ͺV <[ͺΦOβi—¨ƒ€9·ζΈϋ‡₯Άς΄[sη@ον4.:(z‘jšς'Ξ θ·A0―ƒυŽ5Œϋ\Η; 0X Ρλδφν Ο^*―ϊ3άαJΆΔΝ;ϋsE°KΦ’UΔφoσ.” ½_βjΉrθPXƒ5‹Iƒ!Λ¦Œ€)¦u¦a2:²hzœl”vL€TŽ d°m΄φŠΰHm΅η}$Jdη=~PMοm~%ΪS©+’WS.Α]φπ žvX»:ΊΘ̎Θ ‘ͺ«‚›υ)L1‘ ͺ’Ξ¬±œ“ˆζ:0,άπ~reΔΘ"ίEόVβΚ:Ϋ4σΐ―μ2―Η’Ήxz7MΏy;·&0:WU‹ΫEωΉ‘ίΞχΤβŽ™Eώζ…}«κΕ-»=Aό>ί*7€δδ;4Ξ` »+A˜¨ #χAΥι'σšT7 jFγ¬ΐ€ρ-\Z•…5–S[‰y1,ƒ³·ρώτW`‡Mh½bωζa p#u7ΙΒǘyϋΨ‡žΕ΄i§ψy €ψNη0?OΌQ™Xά;o˜~`»>ΐ~ η<†6nμ:T=Τζ³Ί€σαΛPΉt3[Ω9λ΅ΤΤ¬ΥΥ¦5’ ΞN(?*ϊhφwD―-sJΧ π^©@o‡(HSn,C!Χ³X³€J‡Η₯h‘γBΖB©A-ηΧ }™Κ^&'Wž)‰|Ήζ= o!¦ΐnjث˜–έΟΣΣΦ΅h˜Ο4xkeI’κ]ΝιΆ8εKDύd΄ΚΆvWOΞ τ\½/Π#˜jiΰΣH°0ΠΪ¬0:‹γχ ΚJΪODΌύΒ<Œv¬εBI’Πu&? αŽXU*9ŒήT¦^C½Ηvψ#B?tδ¦!ςυb9,βgQl…:?€zΙ6;ιγBŸΎ»Ύš…ΤŽcΚΨeΝη¨ύ~Ο"ζ;ΗmŠ¦ά¬kαQTΟ%λύ9δz‰ύΡ|ΐkΝ.—Ie­¨μf΄Aˆ΅.Θγ›ΑRVopGξΤ `Δο6Ωσ0°·ϊ†Ζ¬ύ'cά»©¬·”Ο£™’DηήY~ Τ½RιNCψΞϊ΄γΜι4UKβ…!H‚€Ε„AΪfDYTβLΣΞXc–ŒQυ†ƒqΫ*Ά1ε˜ύƒΩΰLŽcέ]Ή½(1Υ1fCΎσd³‘Ήd­ό“δ‹ΚΎƒ&ΠγlψϋΕξ€Q|O?ωLšAMDυJ$κδΥ<q ―…Vύψ„«ώ-ο›+cΦ5Œ\C€― ωš―Oϊ›P{υ€―™’‚fDΜKΦgΉ!ΫΘτΛX’±‰rb›CξΒƒ|ώjβΒ›O€«<©œŒqζΎ»Ψ$νΝ+φV°Ύ»^―7’‹°a£ΤΪε6q†ΆβŽ]οZ{w»a^Z?XκJ Jf:·…†SΨ—bπύ-κΤ%<ΝvhιŽ@²ο_Ηϋ«5Ώ΅ΐNL³wV'x2<η(ΉΫ~ω!tzΆι T1srμcL`rΗΩ Ό ”ρ4PΪ& >³WΒA“―i§~'ƒΰL?ψΨ9 W7’΄Zv92-ΧUκ+0eQ+Nΰό w{šˆΉ`?ΐoπςƒj©κmθοͺ‰αψXςθ…”Sš‰4λfϊ—ͺΨj«C¬Pί›$ΗPXn•9Jχ&πRΐ·f9|Χ ΅ΐ£±αΨIωβͺ/Qœύ]#Ÿτ{b‘ζG^ΤΖ~Θΐi-τΐ .lΎIfjGJ γo‘:Ξ:Θά©?,?”ζΨ:΅―ŠsΑϋˆ-ˆ—εVeοZL6βϋ:'1Yc£Δnΐέ¨μ›$(δED_ ±cα ‡ΟŠxε[ZΠ‰OU%Y΄pαΆλ>λύιš2ώ`ΠCΜ1`έibϋ^ˆ9iΙ°±9 N™„6πnΞ,§DMaM .‘•:WoΈΚ}s³€ˆEš½‹ƒTΏμ-™o#½Ηί Ξ—*{PnΕSͺyΞK2z4q0Τy΅}²©CYΈ+Γ\ΠUσdΝ%‚«Ž5 ’ΑQ$a1ž^€ Κtͺ…ΛHέίκ*ξ’/\ƒΟkTΤ|IΚκ¨/˜ΟΒυ Ο{w[‹Ίt ‚Ρ >”…k7i2—@0χGπ?GΫΟr„”) žK₯V7ΚώΘέyb…IHj ―e}[y5hς\aυ.€£οΊζ1όίfΠ—πέprŽUœ―Zοπω‚―»₯}nφΑΩ±Mc’$IχQdd…Y.Œ‹dC{ιΠJΓΑ,^ΓιΞ’pqΕSΓGφ|}|ΡΜ&¬χΫή'ήaZΖ¨όHwΡ² ©cL·φ 9B”1M4τy»J†’Xήeηœk+NτƒςΤ†ίaŽΌ)zs,―τΘοΛEΞWJBnΕU‘<³εhˆ’`£ΏΩˆΤρΈ]ΆjUΉ!Γd<¦ ΠtΌaΪΎkςε‚‘3Εͺ4ΆŒ~bh>Uζ±ΜΜψ₯ϊ ,δ ΡoΫΨB²H„…Ύ4+u_½1Ζ°ρΏ˜΄*Bάhr«ΎKYΨω=λφEˆ. Χ¦»tΩ¦aF†‘Ԑq…ΐΟ`šΪ£(‘}3»9,†‘^M―5S+šς+Ηηͺ*uE:Jη$χƒΩ+9ΏΥ‡bk±Wo·γέμzΑ?9²@Ρ6=$€ρ0~—S^ϊεΩ’…ξgΨΔn:eŸ>%^œΐ3I―#ߜζcΝχ1Ωhc2ύ±Έ`XΜ‹oZεη©U~ί2NΤ#Χ½Υ/όκ'²kSB$Š6**wΧ›χI”k»BάΫφq€>tS²Βλ§ͺu€nP‹±μ—š?”˜cή,έ—Y‘Ι½&Φ;μ5ΓPΤxΪξρΟΧγN6uΠƒ"6~Ύωεέι‡Β§£Φ߁‚­FΣ›„η+?Ο–d€Δp•)—γκdg”εœP€Ϊ<Šα8«6Ιjά^GΓ'μr΅Ρ­EL°΅oΞ†Ύ ‡oƒμ*_„μRΞvΊ nΣ’n\Pz΅Lͺ¬dαϋrνK²o'/m wg£υ΄ΛTv€;a»ˆ˜Ι3οXPŽSMBΘf2δ?…Οϊ΄oΓHLμ*ψ’χ"`’ DΫΎΟtœ”ͺ0β£wδΤΙΚf‘B†ŽΠcω+mΘ#ο‘πώzθ“S ϋ'jΜA²Ζ‘ΛŸήΑ¬%V4pVo'tϋ"ΒvͺUuΧͺn†a«β€ΔΗ-Μ£|°M2œΕόfiG"œ`Α[beέ23aΑ%τh5X#ψŸη`š˜ηNΐστFq˜xή5+ ’¨ΞσCqgXc&9j‘?oΜ€‹wχO'δίg5+rΝμΫ―E΄`4 ύ˜A?žŸ3 ΅„Ρ˜Ό£―Κ3A”ψιΫδ―Ιu~δμ²ΫΫεJޟOβɐυTέ…˜R؊ΠΓρΗψ+Ψ k’’‡”˜\ΑB_#ΚC”βZΏeΡά1ZWΠκύAρε(”ϊNœψ‘G/X­~Δ“• B7Μ~œί θΧ°KB[ GβΊsλλ~|n•”Ρ–>;\MŒΨΑ™ΏέabeφΕηΑ@C`"7ΩΎ!ω‘)3m »ϋΓ?JEνHά±Ρ°^³?πOχ`m7ZΤT$YΆ’ͺΏ[₯™p7γ+čŸεF€zΠ0ΧφέμGto‚28J²·#GώaQ„˜μ±Π—Ώ6+ŒΊDΆˆ–άΓΨβ―R˜γ΄!}?κ>»Ν¦ΓθDWŽνq€i°ζ…·P~=Τ “ ˆΒΨ›ŠΫ… ‘ˆ¦Œ¦Χu=’΅EςΎ χ~O{‹ιοϊόsηϊΔ»{3πΣύZΤi«>©4u1(^P ΉΆœlEfΦI4+bΣ{μTOvφ ;Tž›Μ<13•)δrr–…T1fBDBqbtΞήεα„EMφQ4”3χYpX ηwζΟΦuVά-ΩιQ.³4λfόζ3`₯Zy`Ί²‹=4-ώ„„˜NrS«ΦI’ζtι|gfHόY‘ζœάφ4Ž––^“ΘκnͺL6CλβRAηΡD8Š ΪeασγDVέβWεhy›ς>Ω>Ώi?AΕ QΉ5˜ίΡ>9Σ4[•»vΗ‘ώ6€L~–d; Ά½[[4ΛΚC%ΓΉ(ΫjU'1ŽήνŽ‘‹ΠBC”=g’š~™ίΎ9 .^Ο’E>#Νk°&³Z+θΓ(Όω!SͺέQχ₯ΩΙj9šr"4φ­ΚG·ΧH?rΞζ¦₯Δύ—Χx=ŒLbOβξ*Ε₯Ιζg³κΣƌ<Š‹ƒdρϊ½ΊάŽν½]{ Hφ ηΈξk{οή4p£κ%Π“΅2A›―Ικ½Ίσ.ΏzVΙήΨΣΊ2Νϋ†αΉ\ΊΜ°ƒ`Ύυ {ΟςΝ_ —νΟ₯Α`Iγ…Ν+=1ό Z”UδΦΛuvω—|’εάΝοŽ4=€9^Ω-οBuR»φΣύ'‚τη˜Hχr9ξpz:Ω]―p&Œ”‘¬gHώ­’―‘ZY[ύΧ~Z†δcΧ<|$OΦΜS%ξ::ι‹“–αΕΨnΏI0¦„†•’YΚI"Ϋη₯—dδψΓQdΔεΕ΅v.‚Οcγž;vŽN.C„0Ο™ΒMΥ?άq06­Λ8υ2ŽεΫ΅ny`C}Τ* ΙML`φ(<ή6ww-ΰΉ S`{Μδδ ƒk.kHΤRY?κιdβΒd'ΉŒμlSb#p7s{,d ySp ͺ05ί Ζo Z}Δ\Ύ|ƒΧg2Ÿtek˜ΡU³ŠΥΑpΙΆωBΪΥρ˜πΥζ—ΜVΝς=]CήΜA$ΣMξ[•Σ–κΕΠΞea=ΣΣ£^Φ»~χ“νΣξyΕΩΕ[ŠcQΑ«Γ!)Zμ"Β–²άf³~δ+’C\οςΰ··μ™)τπŸ<’ΐΔ,Ωώ̝±o­‹9ΤD9A[γ'|RŸή‰A\_(γͺw\ˆ% τŸ~²0Ξ•ί\Ά 5=φ½―ήΫξpΏ|ΖSγ>«dεΈCΥ„εψΠ½Žj„F2tb ν6€•‰˜ ŸΒθŸ3ψFνΥY_Y|nh οwΣώΉF‘ΡGE@ρ*ι‡GϋξJ£+}ΣΎ“―ΒΩHηέΎJΥΜlΈUΎ6¨•g[i'»{Τ,ν§€θG₯T·B7θ$}¦cOtϋu*i(¬Τϊύ<+ ^“ΣΕ9ƒ•,"Φ:: CNΫΤQ!?mcIπU퉩Ifώ©kVQ«€»σͺμ7ήa"d­ίΉHŒfΌΰΡHζ¨ϊω6Y-©‰}ͺ/όΡθ]˜΅M~ζr߁ulτμuWπM•8}~κ’yΫh΅Aς…Ϊ1b»’«γCΛ`ΊVιŠΞy#.[C”³¦ZT‚N—Υ‘rυ²ε28Φ8  H‹€Φ?oύŒZΖθʞ_GζΧrssŠtw 4Ημo΄bτ°ύνA2eέΔ΄―z¨±)Q Θ: Ϋj C~₯Ο@Ά©ΜΔ’.$pwΦU O*pΙΑp2Ρ=½―pZ3‚Zg4‚žάΎΜ GqΕ„θŒίSσ%“]œQ DΦΪΗ79b/¬9ζ+›f,‰CI3ml->’6–z†Ο=ε[0ϋεǡNJBw’ Σbφά΄Ινΐ°€–π°9Θ³¬1yυC4ηs™Χΰ Ω—±άψ·pπΥ¨}Œ[§β·ζQμ·Ύ·AKΚΪυΑ0{ebYœU-aaγžaΏ‘ŽBt|Βφαή3ςΠΰ_ΠXΘυ£Ζέ…νωP+6<ZsΔ~kMm5Ξ³˜`₯Β-νΆϋ£Ζ|ΖMPοΥƒ―τy]€ΐ†TH¬< Mλ'³Ν/Rδ€+xŽΉ fQަ²γ-©{+9²Ύ™ΜΔΈˆΆΦK$6₯q)žy₯±™8{Q=E7^s™΄O¬Τ!xrλ&V΅Ο’#ψ^ΪteΉf“|ϊvΥPzΨ‘’pΣwDfΑLr ΟC%fνvΠΤψ­ΰ2σgS*Š °{/G`βΔοdΩΨ|Sψ‘±lhxrBΓ‘ ~ς‚•©gK ŠΘΖάNΩ–WΎŸ.£€₯eԌY@ωε'Τ>!Ww‘ξ»$ΗάJ¬ν4ΊU#ZνΙ6MžΎWpmf§0‰¬·iξδΰcομ7JκuδK'ω8φJΆταOΟζ© kζ'σ»NΑx¬Β#ξΆ*τΆ΄?U–ιε xΉίJV}1]ǎ0nƒ›ΐc_ά¨e>[ΦΟά" » ΰv0‹β€fšΆ+ΔL‰QΉ‡Μω)'\‹ε.AςΊγš“%GδΦu2²L‘ŒΚύh*§B3πέÜiη”λ]άꁽCŒx\―JcuJηš₯Ι`o­oO*ΆžΘK\G„Έ{•:Ύ{¨sΗπƒ§禀|g΄Θg«?όΓ2‘μ χ]œ'ηIxŸV/UPΜ—?Έ― zYΔ <+Υ²αΤ%샕=ϋN(±Γ<ΦΗƒD©n7or§£‰fζέ‘gmyΗA.―›4Ž(°}[kS ύΎk+';wš/ϊ~IΘίI–ƒΙΦf―ΒΕΒγ@7;^±ΨjΗτ«šΙ—³S~‡Ÿ…ΉŽ† Σ"¬ΚVHmγφθŽΨσφ†•ς¨WΎ,Σ‡η=dΛTpa™·:ΐώm—|5 ­ E)FŠ™ŸSFύδΔφYzaGΑ»$»Μͺkώ»Zƒ–:JΑQF~³Ÿ ]7&ΡXΈQ|šnΙ²¦ϋώq΅ΉζH·εNΌN,sK¨ϋ—·ύkΖ#ςMΥη¨W©(\ώYuΏτΉ³Ίβ-ψΪx`c6{WK\T‚λ‹›e·Τ36Š?lΛ¦‚,ύ TθρΨ><Α'ΤΜ֐ώ1Dwu.j>-ά’ΥH~z2Χ`drxμŒ,·ΧO%= άk& œsP_’Ϊ’ߍ˴Ξ7œ3FψW_ΙΔtZ‘'©SίC[GŒ96“ϊ]KZewά†yGΕηqe=)l Ξδ3υ›°pσsτ‚|ΊηX­θΎbdΟ?AΥKΑ·ωΰ_›Λ3μ|d“Γ }jPσΡβ7~3όΚbƒ(ž£dU‹w6y3ν GΙ¨πo$ΎˆV‚|žτ¦{U’;κό„Ζ0‘ όΠVXΰ«ΐξ(‚ε’ψΖ‰‚ςΌύš‚γw‘1«(ͺΖm™π:υZ8ΨA˜aI7ΰ‚Ν¨ϋ>Ϋη5οΑ9LMθ $Φσ“μΩΛίA,έυZύ•ΒW½šE>«²€˜ΑFRVΔ}‘Œ°lός=WnκkrΏœd 04ΧaΆ5?ĭڍΜΚ8Υ“ ΖIMŽΦΉ}΄ΫΚΣΊνύ;α‡ άΔrΎΦaϊ'„VΙό@>Ό-mίΩ=ΝlaτWέΩ'§G8QΦπžDj8#…ΪXφΫ:«ζδi|ηΪΝ¬4₯°kώ΅Qͺ9Ρf3ZκLc΅#=cΑή+²:Άθp,\Sž]ΎͺšrΊ'ŠŸΠ„Mμ–’έcγHhΔ&φͺ΄YψhP淐0ΰΙZΐμ {/*rƒjewwίAτ6 +Υv‚ό#Pxͺ΅χi %²‰ΡςΦμOΡΏ–cΓŽWJFγE^@‚_«ή!²"οs…5TiŠΨμBs5;γ•jcωKhΔ“„ύΌΎ¬^σΖ™#•φ:`ΗΙRπm}KE”Ζ=ΠΧμv+κν”²J6BΜa€ηv  £G±OE ΞΛtl_»nγQξ•e υňθκδ§ιAΎόΝqžd`ώ_Υ7Άψ/E Oς)2 Βδί,j$Οί#PκΗΒZ# ό*BΚQφ(’sž‹6μlNz6›8ψ(―Ο‚cŽmμI‹‡]ψnυGΏ r$ŽI™γΡΫΈΉΞ—ΤBω=zɝ§KύVFΘ†n–ϊηξ[L5SΕ₯ΓvJ νύΜͺα€?B6©q_eŒέ―ͺάYϋΈΘ΅ΒγKυ¬εo2b?˜(Μ=K: œ¬­?}±I€ί–sgχΊ(½V‚ΐ7ΐΙ!Γ₯Sεν΅(w²1ξUŸ—NO‰ό“΄@:ω ₯Δ?%@Ό{ Ε)XB…DdgLˆmίκΆΓΑ3ΧΉΰjφ°pΞΛ6ΠίΏΪIίhiyΗ¬thdΐΎΘχ)ΊΤ)Έ3)”ͺΖqxΫ4gM$j °‘ZφΙΏΈί' P•ΒNιΰ4«ΨAžp‰Η>Z―b‹ΩϊI·,›"­ι"Ξ|ψΜΒQ#π ½bf”W_λ|H$Κΰ­―¬ ΝοήςΦEύqŸίxΫγ{ \jωϋ‘™†pK₯χ j•`₯Βa/±«}‡~η F£α.…Αl© €4ΘΧΩέ[,:3Sט’cOZ0Hΐh0~όφ;7³6SšdΟ€›κΗ«DάώFB#­ά/KφΣΧr ;†Z4)#eŽVΐgΒvƒξΔxΧΎϋTΥvΏ‘;ŠO|₯aΝΖ}τΓ:ΆηΙΠbΔfJΗ+ΗD%ϊβε7ΪΡHΰ€²OάW€Φ£o+›ΉφSMνsM„ΚsZΣZ &zι+fˆΖΓω΄π;™κΕχ}6Ύ;`£Ÿ8Ϋνš‘Ρ w‡ue`ϊωΊτ™ΪΔ"Ώ­Pν9Λ±κν€y(qmߐέͺTΰ²A€yS„‘Ε αȚψ50Z}χΓΆˆYΈΎ£?Žωκ ΜdˊώŒϋZ¬ΡψΐWˆŠΦΦθ‹g‚9φBi.›HΞAͺ–"΅}ήλ$$σ΅x–iΖ+cήά8@ε Ίcr)Ϋf‘ξσ'lœrΥζη %;¨ΰUΌΠ$W⍩ΫλΩθϋa›g<ͺ΄Όυ°ξή_υΪφŽ"Ž8oΛtΕΎ—ο…_ψ‘ΊΞΨF ™™Ρgr\ͺ‘βζ)»8.‘ΰ‚Λ {ΏμΪϊ,”Ω&d’<žλCΩ§"ŽΩυ}j΅“ω·`σ—!R²-Γ0ΞBaNy$¦υWλ$ιVu—Ϊ©°Œ%–‰Ό`nMz^…a³ΟS2κΥoΤJuΘ‘|[…ΒΛΦZω)§¬λΡlw‘•Žlϊ!yE2κ'ʰČΞςθ.Δϋί³W– Υ sC5M[eε _QL«u4Œ|ϋŠ5tG‘ΜΉi&Α^#ͺ~‘¨ [ρλm[`ζϋ! ’β]ώΗX†“@fZž…Qχμ)\νΙι3)Τ‡΅%³xˆͺK±D ώ&¦Ηϋ9όB=iΡΉZB{pόuΨtω“&Q±k½TηγρnχΊ>‰€Ϊ7zTZ›zέ₯Iy‡\ ζά‚•by͎Ε;-sŸη3’^ζQ΄RfΫR0ΤΌŸE[η­”ΑmςΎPβHDΌ‰Δɘ[+8κ[νL4+φA'ν~Νκύ•―#ΕRΆˆ€CρΘX^’ZBhά¬ΣnΗΒ!¦₯uTΩW'T4ζvWυ sUtošάƒRl¦eΛ₯ίζίίΈ]ηΌyΎγφ#GͺOΏ kΪ3ΟυŽΛϋŠ2C}ςˆΩs³vnΛΣnΡ‘5 %΍ǡaΡΊA5”ΟŒρ_―όeΏΆ4Š*<—6ˆΟe+Š…™g>ƒΥ7;',Τnn^OωZοp>J5.ρΓ)ΚϋŽF—·f·γ\’C‡`Χϊqκϊ+ υ"ηŸQ`§»ΦM|ΡύLr>™ΨηzλΖΰ ₯9ΌμέσΪpΊώŸΝp$Β—'‡§ΠΑκΆ’t"ˆίX_ι_koo™;zu50xuŽΘ uUΉ$QJ52 –GςxΖm[²£~'Ɵ7m`žO$τLΤUr+GJRςς(Θώ=―§ρFŠ„ηΐΆΨη±Έl_ƒ‰rΤgVέΏ½BmρM+2Ήύ–ΐ(©Ρ«3H#π€9…‹s@Ώιλ=wΏύ0I-xΑX#Ζ/rmΒ>©ΓΙλ]c8Ÿ"‹@6…(zjύZ–ΣωšΩ°SΘ[¬6Μ!dš+?Ξ₯ZΘ}98.ξP¦@ώ½~ΐz»cΓ”‚‹|ςp2ΛC›΅†δž·ŽnΖΔ@`™φή/φN!ϊKΊΫRV>ςχ»?ωυn7Y1Š·—Χ <ΗύžLώωίΐI^0 β {œ"TI!^|M¦°‘.}ΰ­BνΦκ ~;|{}΄oγηc;β6•ΚΑ–Jγ„AYΚι2^ϋ–r©ˆsς.²I°ŸmY½’(‘”ϊ’LΓέjži^½niό=·Ά킆»θΪ²ΩΈδ°mw¨€†@‰ -6¬Ωί“ρJœπ-%kΰ|»=‹]ν ͺ”ΰy±ΖΔ<‘ƒμ|½ήmQšΐ‚ Ϊ–j(a¨@Σ©rψοΑGη^YΕ±σζn£σ;Χό[­Ύ‡šΝ¨%p ±δA#ω4υ'ϊμ'ι7A πjIyτφœ›'§3ιΚπCɐ–>©³²«ϊ9Ω/‡xωΥMΙ ω€0φ6§qΜt€αžjΪlώ‰`akAλΪ8[Z<ξΨ°B%bŽlΑμqέHΫ9¦…u^Υ1μσZέpά„Θ£-ά¦`#AΠ.’q Zθq.§9tκ8ˆq‡΄ιΪ£)2€+KςMα'Υ:Η[³’Hς»¨ΨIqΈί:Τσδ?αxEβΪςτ²Έά³#…φΦ\Ττvˆ«šrχ .EٍVιN—a)X”{1vΒΞ4Κ β  Ωξ *όωχΆΞQ₯1&ύWΟυςY6;Ή° πϋΪϊjεχ>%'χͺΠ£ΞV‘ε©΄{ΠQ—Β.±Ι;Ίΰž«@#ήΐλfίΘΖ'²ˆ& ΎΣ„₯Ϋ–ƒVδξ_Ύ]΄Y-…t&ZG΄˜C ε‚ά»ΑΣΙ7`@ώ |‘=Ψ;˜Ϊa1$ζgΨ…­u‘…E«ζ#VwΕ¦gΰ{μνaDfTQ5d.7­Ι‡Άdμ‘΅Ki ‘„ί`%k-ΦMΗΩ»QWS²kB@Νς'l“H•EΟb?(Τn^¨Ά,ΐ‹yhΎ›]Λ"qKfj–@ πΐ"(㲬Α iT¬Θ‘™ˆ‘ΰj({Νπ-W?WƒΚΚ‚•Χjw€ye/MΔH·Β‹»ξLω O―ͺ—~ΟΐVD¨ΦJ?>sχ5‘_L0+§vΐΰ°tNŠ­`Τ:ΔdΟUe] €69fŸw@Ψ›Υ—M‚σΕΤT™pΠ­&”AhPKΐ)gPΨ£Υ 6ξͺ#ύGJ„’ΰΠ½.•ah Ρ•ό1Hϊ€%€Ζt%‰O€ΐτ²ηlΆ8›4ή£ŒεpZŸJtΓvυνΒηί|ΜΥΛ™ZΠΙέΰ»&Qt’•φCž΄>1Ί5qΞOŸ€Dlc͈%Š>‡Ÿ" δ‚ι`r{32…ξIE ήpθ‡>­ DŚο”λ/AωGΨ¬#diΤ 8ίήh{ŸSpY†Œ” rxŒy7Šκ΄Μ|ο,ξ$;7  υ?Σkσ²—³ϊtσΞΝ.ΧBŽ vs!ΚΑ]_i,ΞP Ώ7yρ[‰Ι‰φγ»•+IšκΟ5ρ4-]M%U’θβΎχΙ7mΐε™=™$|•±ό+bOuνObE0vη}"(δZ„°ΙЧ―Πι>¨‚'歐ΣK2g…Χ~AεΓρφp–’«K4RΪτ— /“ž‘kŽ`eŽψ¬ζ\1νΛuς€[ρ-j›ΤšλX8ιd~ψ’"Τγt¬ί™LŸΓ°‘Dς0j.ζ{A)κ lO\Υ­0 endstream endobj 431 0 obj << /Length1 1644 /Length2 8902 /Length3 0 /Length 9752 /Filter /FlateDecode >> stream xΪ­xeTάΆ-Nqw()Nq‡βξξ  ΕŠŠ;Εέέ½”—Rά‹K‹w{τϋή9ηŽsίύsίω•μ5χšKζΪkd„ώ•†6›€³PΞβΖΖΕΞω v²r‡i9;©9 ©°imέΟv>tzziW ₯Ψ"cι|ΠΪd€Φnn—:=@Ϊκε Ά΅s0ιjι3³°°ώΛςη ΐΚλΘ³' l 0<ρ::C€·gŠ΅£6p³@`G @Z]ΓPQMΐ$―¦ B€–Ž w+G°5@l „ΐ€Μ³+ΐροΐΪbώSŒύ™K°ΐ @kπ³ΠΣύ± @W'0 φό†l]-!nΟ=ps€!֎ξ6xΆƒœJκκό|Γι{&Σp†ΉΑ¬]ΑP7ΐsT ΉΏσt³³tϋ~†Ξ η›6ΞΦξJϊ {¦yFέ,Αΐ θιφ'–`†A-½žc?“A]Α₯αCl•+ΐhkιjγ„ΑžižΉtη_uώKυ–P¨£Χ_ήΞέϊg`7ΠĎΞΕύΣΪν9Ά-‚ΞρgV! gηίvwθ?0 λ_ bϊ33ΜΟIXΪ8C½6@:‡š³ΫsHΣNeφœΘ‰#Gδύχί5ϊ/ψχ=;΅œ»££š₯Σσό½cΟKΖxή3ΐŸEγhιϊί|,ΐŽ^“ΧΏίΦώξ@¦θfωάIˆν³4lBμΌ‚œB`˜Ψh£v³Ά€,Ÿϋφ—]btuC€ΟϊώΥZ'ηΏa:v`kΘ!ψώ†€›―βY²ΏjΰP—1RP—eωοK–MΫρyΨΜΉψώvΡx 7/(πγι«:Ϋόσπ‡PJΚΩΰΓΖΕ/`γδz~Ο© qσΎϋ‹ˆλ_gUK7W°'ΐ˜“““λ™τΟ'ηŸΨ4LFbνlσg΄έ,!6Ο³χOΓΨΪέΥυYςΏΦΑsύ8υ €@O 5ϊΒ¬³΅p°}jFš[-INwγξN.ΔώΠ’Βό€jη―ώ©akBχ5ΨGί<ΆyΝμAΆ”^μ$vdόš όGωŽ–Ή+o™‘]€εη{³¬΄}ύ(Ÿ£i•U$#~N½Ÿλί5΅ΜŠοQ¨FΫy\_]1Πzδ]B±ύ¬Sκc‰>γ6ΒαΧμν3$ξ\]2φ τχ}=AξΪ’`ɎE£Ά$ρKΪ{υΡΝΛΒυΌΑϊωΦCV9“Ω’_—¨ϋΦ›βn֝šξ"f€+νη₯ν“$ό&§Δ¬U¬ςΫί‘!1ά… b¬Τί€Ηt”Μκ>Ο*ξΟσ8¨{“κ1/ΖψΥ9+ŸJ[Θ·ςˆ§Η$‘Π;ao£ Η)ˆ›¦i¨):&ΐ[G.N/+†)Q0Tώq]’(Ϊ¬ένέΛ’_ΰͺtR{κ²φ5ΨϊEΑˆlΡ\ΏήFœ»ˆpJHΪ‡ΌήΊD½Υ*γί!GH^n$–*±―œμ_@FOmŠιΐ«y«ό{“žη–,OaΩ©‘ϊM”bύ™οY’YΛWγŽ/©²|ﱦΔ-ΖΆ»•‹Χ_x5τ*|9„+UF‹QΛυγM2ΪΐžΆͺιkld5Žά1Y« Θό$•œρ5‚γ£0Uΰςˆ—xƒΑC\7/ωVˆg„φTL_-Ύό…9‡-§γR΅Z=@Έέ(’ΤT!λ„k5Ώξ―r?²„*}‡ ηπ±βνRn4’Ί‰ζλα‘1—›SED»Ωρrζ!Ψ½³Ι' žΔp’Θڐ]σ@Ι\L޳+΄r΄¦OF‚'ΠλΠlR’(σΜρπΤqXΉTΙΤvͺΠλG +ϋ²|­’·‘­0θσW¨/RΙ’©βsΒu )5ΰYΣΓ½τπ ‚r]δkπHΒiš…Ξ pepΞ$ υ―«h#xΐπ6Z€ζΑΩš(6Ήj?όZk/ΤΩr«ndΆ~σBx`ζO01οz”€Χiμ’­X—= šΨf‘%.ηΣϊtWž,D¬ ™~HΝ4{WεƒΙEλO™_‘§e9σ!FΣjmΜΨS革fJ&‚μΔεla!η].Ί%΄©yΫέ£Εξ e™žZεΒ½‘Γΐ†ν©«Ξ,=#όJΞϋ˜ΐ/a―ΚI5τ3F-Mb|i,Kό`‚μΥΔpE+χ%ςΨΕ,qk 8εeή:΅ρ―I½G™+νW 3ί9σT$Vqλr π η/}zΈ½}+ΏŸΡD5ΟLΡ γ!βZ\Ϊ{W―’Žˆ0nOΙ:NF>Ι3^ΛΗΒ瘘κ·`)cΩ¬³ι/,ΰΗ8§ΜŒYο?Σ5τЦ©T§©o?FPΚ`dgζuH1Π™Ή r…)³γ #·ψoή0‘C^{u«ςόžμœrowοkΎ°LϋώR*΄ πέΞOu—[Ω)υTrZιϊt{I£ν7ƒ^λB6HΩ€Οˆ6Η/ς„ρŽuςbπΎΫϊ»ΑΪ―SΗe³fŠŸ }3ω!<;Π•2[O[GšFΜrynUΆή«°―Κͺς•c|ρ#Φ„ω^0€ςŠWζ(wμΝπ‡,›M'9;Ξw…κ`^“+D…X’ΕΗzλU%k0η‘H¨N,Ξ@ΏΑ4ρ „S!Ψ‹ώ4]Kσψ‹ΏXΠ6χθΆTνx8Ζ΄.;¬Ω› Ρ9³τJTητβΰ†Œ{ΔVΥQ y]ݎ4γy; σΖ pύ±;p_v΅g$`Σ φ YτxI‡ι؁έR₯€J_¬Ά&d„`–Χ&CGώňΧͺβ―ΤV0‰ΝΔΓνωΟs;ΊΟgαε/„5γu’ŠΚbJ3―CfsbLR4z‡Ά²`ΗΚ)[Ξ,eΡ$y.φ…4ν΄}ΠϋτΖt9£a3r{ ‘!ƒ»`8:Aφλγ`ςŽayς’ΰ0+x=X‰έKt*MHΧ,¦yΎω©ρ8₯!Ϊšbΐ£Ω¨«™Χ(gΞ_8Λ W5 Νε2qλ‘€ΙΊςw§4Ž»ε'ρμaZIηύ8œ6 Θ)σ¬™!ψVf¨抚S₯ΘWh.Ώ…Wa¦%E±Φ2ujš#Ι?ΚJR‘*_θθ¦0.`ΔzΤ οΨkγφcΆ•8^ΐ*4πœ~ά—Η¦ΝΎ™|ϋrω΅ς2Ν5ϋχ'£P†6x/ €™χ›ς3ίV=rψΗ ΪYwΔdεŽω(—ί"2Ί:5±Ώnώό19¬C]|ͺfθ[’Δθ.49»·‚&N6A°£jύwx―f;x@Wς™&nΠlrη-c+Iή›¦Kž“΄Jϋ—₯΄ΒTεQν• bA)ΩlρέοΉ%%€δHπU‚zi­·e“η€ΎtΨt,ΙΓΧΰΖSΔ7Zΰ“XcEw₯[ό£ύF’eΆΆΙ,T«Ξ‡QδΊ"ΞΫ©Ÿ6ŸΖ‹”Σ—7­½»f₯ΝΛo~ΝCφ"€θη_ ΛϋzΘ’nΰ<|n½“x+AX4πΆ=ΘP2œώž‚p«‘L€ςTχ’ϊΝE²ρ wi0—ΜΡSχβΟκ“4)”oG{N6TŠ-Š8h_‰['<ωxΝό«€jgΞ·IN/܎……gFŠ'6{–>w}6ΝKλ#‚Kύ‘ˆ‚!—³%ΰ'³Xϋž‘ԈƒLv¬TՎ₯"k1Ώ*4eΫf $Γ3D@^έοΈηξuφΦ‘ΟΊŠŸX΅N ΫθΕ„ΫΐXΈŸL»R»?} "ίάu&YαVOΤ%bŒςιiO¬ύΒΟμ]­RΐnA!y3ό Ν?αΘαΗWvΚ γΣ~fŽošΕŒ9²RTiψ>™’wΡ0 yͺΰ 9( i―>α—_Bϋμωš—w<Ω‹”ώ¨FbSKĊ©L­5I―¦ͺΐφs6“£»i7M ν—q*ΫθeW%ͺkηI‡"(5ˆ»<0˜σ΅5e©…m•ŸG+Px!υ¬―£°Ψ γδmΎ qμcζ]X)zΟ0Y…κΣYϊΦd|,Eρχ­CΪ4ΛiΝ TιZ-ς­θ—O>i$}m ‹ΫЏΨy#&ςOT6©[ΑΝHΚί";ΛHmΪΧ0RνΖι–Ή5Ζ0ρτuuιF'­tι˜Δlξ±δ|o{Ÿ’jά£ϊ_Ϋ+(ΗΟ§ϊ4Β‰tnτλ±,SέFΏHOΠ_ξXΘζZeŒ³φ“v4+¬ΨΝΨζžΥŒ<*†<5L{qΧ‡Lΐb,:έϊ{)ΥΣΨΧE4Κ%JMp Ε’eο[πSOΉ":’‡* Ό­ 4ڎ,/¦ˆ—μαPT‘o='Ϊ—ϊβRQ¨½πnŒ¬:~/\όξn/ƒqȈRpVQK°‹ƒ9Ι‰0™±iΝf¬½/™>šcηkά¬ΞΣ fϋ„s7œΉΛ;₯ϊό ρ³‚-Σ§TΎ8·ΫJݕ퐇 JŠΔJ™Ξυxu}ΦDšΘ¦ΣPΑΧs@ &!o‚ηF¬ “Rc:ytνA"ξΗD¨y rY¬ou”³γώφCΰx#X{BΑ5<‰ΟJΖ²εFΨξ·ξƒΌξ'MΔβΪςλ‹αœ"γ=ϊ₯γΞνίZFθmEnˆιΖΩΰ•’·ͺ( ݍ¬D;Ιo&_ζeΊ εώΡD%κΪκΦΏυΌΨ±ο»Y2—€ƒQυΏR qK•λΦΫd 2dΤR`Pός6θβΞkbI¨Εψ™~€dg2_Θp ˆVΎμΫ₯Τt5R"3¨ŠYΪ‘™‰T8šΐ1δχ―G²MO›ir=ΆX”‰‘x€CΔη"“ΪI]``jσ”Ώ“ΉςxEfοhΜΎό=ώŽ|bpp[¨r/Έ‹…2±1δΟj¨‰2ψύ΄Σ―ΰς—ΜVκυμυκ)³56ݘQ°½ΡsʍχŠΪΗΡΐ—…CΠΛ›ιμ.ζε§βψΪ²ίρέ…jΑ8IՎ_ϊ1΄7΄WaΔVχΩ½\ΊΊŠ;\¨±>”U’ˆ50Ί#Ή~5nϋ„aQΓpmΘ{ŸžIŒ§‚£Α2nHu "§@ηΣ±υjyΪ ΧcE[Pη•Χ1N}•ΫπΕ}7ββeΕ@ΡΒX:ξ8[NRQ6 %7έ³rΠ^kjή=|4τMϊΘ"Φ>ΖIΝ·ߚυe#¬e Ρgs§ήύ°WΧYˌRΤήDΟ‰~@>τs‡λ=ο ύ£C»ζ΅ΐ+οΔφ;ςχ Q€Lͺu—Bν³h<•@芐œͺθ+Qε6“+ΔΠ~Ζ+χC»ϋ}ρͺΡΈβδ<₯Ξάkξ9Έ›§_™˜¦ΌDW―γr¬Χ(ΛJβΖhΧƒ3‘0¨Έ‘[Ζt΄Π—>΄~ˆšUώ3.Πbμ{`”φΐε Zm‹DθžιζΕ) VKžφ‹‹qRN#~σ;/ΛΑŒŒˆΤ―6$} ™GΛ9―΄28!]JG·7X3Υί©sΏqAΕ2[c U+šM§ΏW¬βλk“¬υ Μ††Q[σ](ΚΧ³'½θβpœ”§ΜW)ΠΛΕΑIΨGΜŒOœy7œ1b@ 5ωXς+8φΦs?ίθΣNα9’ώχPƒ\αΡ^cnŸψv‘³ŒεˆDVdώ¬άΎqρ–€*΅~qP$tω†ΕΜMO ξl5κΡΰυ4N{d₯νΊο€ΎόΙ―Lώ.σϋ)& tн,.& •Sκcϋ‘Κu•VD¨DΰœJjστIΎB¨gΓ€¦…ΞΣ­!%6‘@}φΔΘ(}$?xΎR_*Exm|θ‘" ˜3— έά”ύ4YfPωˆœ_,•Ό73h!΅y$‘αΝ•o²άMqψ¦JT~’G‹ΰΛim O0ΒEHŸxv@–7λ΄πT \΅ΌZ΅\Oχ ΧyŠ(°tiέ€»€g_μβ‚Όο,Ο.­‡"K*…Άω[4/«Π6οζf}% ΘΟCϊεό)u ΟΎώ¬ύC•sΚή†1‘2»jGp΄Κ‡ΙνξΪŽBΟ³TΏ(ζιp.Ϋξ—ΗΏUwζ^Τo‘΅¦\˜τt€ZΕ:n<,ΊΓL"π~XΌQΝ?1ZΩΑΩ3ME αS|Ι―ŠXύνΤ&%>—8I Φ|Aχ MAΧΫΠΐίTσš}΄*x3{ [_4ύη ζ](Ί$w°ΟΰζΚJ<4]1Τχ³-YgΣAέζΞ ”]Vσγ#Wζ=΅Ή)ΆυβhEυίϊν¦ΗέΣ‡iήJˆ˜Žή3=HΏίβdk8όUfsΧ–q-zxΧ©–έί·Χ5Άa³;pΚΐφ3βΪy]QΪοoΏvy‘­ήΑΜψ€ά3 D;ΫΕ–)Β%…π–Σ­Γ§|Ψ.˜Ψh΄kσ'γ /3\ξHp–sήΣMΔ΅ΒσΑ•<†«χδ‚\Ά —z•ά:ξpήF:_άυΎ?έvIlPzolΗ0ψ^6―π#zβdƒωΥ Φχ2O7&Zρ ·["CΘLQ{gζΓ―šς"Ϊ\J?6H “ΔH ¨sΩbΛλœ$ΙgMœΣπβje`ΖΘ$c «;lwβσ妌Α~₯]ς[ΐŠ~L±ΐΣάj΅e§Γ5Z?Ό¬N·Ϋ‚Ώw©ΊŽΒŠ€•_ I£ΒƒJθRI7ꍣ"άzΜGFΈ7ζ)Uψ…iΙ0ω= YͺΦψΑ―kψg’Ÿgφ·ςDRέd°Ύ\κυU­qW~%ε”ααp“Θτ9pύΙ"Τbο\ŽΎή\]ό4ΥΡαZ›ΖΠvq‚Κ[λαiGsΩ3 +?Ε!l6ΦέNΝwΎwkΡιγηݍcgEΦ= …rΧN-"m"ΐ ·Λυ&ΙunΘŽ % ?.―’9χ.ζŸ3S9ΎζNl‹”€ŽTΏM‘­+A‡VΧ£sΫfJ*ͺΈ4„-―ν>€}ΩΣ:D‹Σϋ]€]εφΏ7ΐβίκ£r[Sρτgc*–ŒΈ±z“¨Iv€]Ν ‚V¬ΪTχ©ΒΚJ[›ΔŽλ_• σ›nγζ&l`ϊΞ_l¨8©Qω|3TΓ΄Lί"Šό4΅ς ρζpQ.Ž’U§ bΣnρΒrοΌKΙ’¦ΙLšƒΕŒˆ=KŽ4|D*CΝ…‰| 7H)‰ΎμΨΔzYΏΧΨ`ΌΓ‚όέΧ7leKm7Ϋ-X=šΚ>Κ ~ΑOζ Š™½ϊΰIϋJ'™χYνΪ•ζr²’ς˜ώql¬%[P1;ΎKΜώ@Y™Z#GμδMK=½Λ7$œSœχ‘ƒ™N}9ܰ=7‹!½λήVhΐU΅v!ήΑNμ^+«»Ή“€–Iiώ2ΰ n‹Ί)"{>‘7=‘.)5X4ΟSγŽL‹=έ›.iόωFž\CσώG!ξ†šg^ “&κTuΤΣΫΞΑ ͺWF*fwž6­IΌ6ΔKŸχ·χ/²Q|ινΝY™Κ’ΫE%?cgΈ€φ–»;,ͺo3ΰ₯Ζ΄53Pφeœ~μ:1%fazL`i&―xŽm%ŸxΙυΉΆ€όΪώ8wοi9ˆ]2NtƒHšΗF½ΪΕΣΛυΣ% ο›l}Ν6ΔΕsHZ—žΝΔΈ«*ΠΠ'-έΥ£ "ΠΆ4%ϋ³{|<³Œm"Qόΐφ³>a!p©tΪ:.ΪΘΗ°VΑJq‘[έtΔΦ‡ΗπhJ7€0Ζ¬ύΐš«9{-ΧΖΐ€”έgUΑΘlΌόyΟ;zΙ&Λκš†2xŸ+#Ρ-‚$s¬ i`GϊŒuΥΐz~L[‡8Κ'3Μ‚λ±β‰M7mWL"ΉΖ+QŒΙ²ΓYŸŽŸ}S"¦Eξ»-έ3VgΏ\jΓ„ŸMB»Έ˜πšH΄vε£Ή5+Ώc—Πάg x©Ή„H(N `$ž|d;BΪ \m ΝΘy¨dή'z°ζœO!ŸίΛu(z4ΐΰ½°Ί!ΒXΌI\׌Μηo‘ιΩϊ9Q`hš%VτβEGrb‡D“’aΌ?!C}i]X£­RΔτs>ζα(\\―ž’@ƒ,“daˆ “Β-5‰ˆ‘οlμ™―η©…€qάνObΊ[3Dr:”έx\/]±]±gήσNΊI'‚,fκ³κ ͺz ΑΡύ»‡eξμώτ31šοd †h²χΉ{*ΗΛI©eˆnqŠ4iΝrΔͺΤ"°ΰ½φ³0¦ύCƒςόΊ!Σχ,/=ŒΟ±Ž<;Œnζ2cέΤiT‹vΌGZGόdεw €Ά½,ˆ#ΘUsUJ›E›ζž Η§G­Γ)ŽσΫsΔηv?šΌtΛss.%ήx7{;ή@ͺaQ;|2 jhϊuš[5i2 ‚[{ΥκJ›ΈuyoΔ΅c€\zά!ΜR^G “}M’ξςR@Ιakίβ•HC@X›=9HItWϊΐφ…gA{ΔFΞΜ/mB―.ΫAμΩλΘ½ΆΗFσλEφΊXtΪ‡ξšΗ)Δ0– ΗΡ/Η Iδp€Ί5›ΑE`‚΄PΘ~HŠΑ^‡Ρ,­τόzΏΖBL.‘ρ΅dh$ΰ[_‘ΡθυˆΚzkM}ŽaX•šs7ъ°cΰΓLLN9OTΑvϊšKΪlβw†jωXhimnαWKyΙΌΗ(΄ξn9»Ύ¬³^<9ΈΡ\™ηΜIίϋuIΒVq’7ͺk­"΄XHl%Y~αόϋN~ΠΊ“,•«Ά‰ηPΆT_&;€¦ΦC3iνς”;v§W‘–ψΪ’Ή(}³˜L‹άΙ|:εχLNή0!9šδ:εέ‹SV%ƒΪΡxΦ%ώž²1˜HhΤ ΟΆώV:GmΝυ{ώΗ_»’Ζ ”ώϋ:©…ώ§Α²3ˆΒΉ/°<¦τμX4g|ΰXee^}Ξ;€ͺί,ͺ"3Yo9πivώbvk}cd’-?‰χm£-TΣΡ=ͺOxεk»OυΊ_v””ΐh΄ΙΙqWq‹O)Ε&G`ASΐ§0Κ(‘«‹.郑ΕEI6™\΅σ&²Z—œέ‹:7―τHεJM\΄‘g퉐0„Ιb9FύQΔ0*DzζU|lOjΠφέz”τmZaκΚ­GŒG³yZrβ“FΘB†Ιμw»"­rω³ͺ-tjΔTη―zx%¨–X!‘ΐAχ ­Μ°Ϊ FΆΧψρPϊ@„ΡΦή{HOuζρ¦SξκΣtlYYό 4yΘΆ―xQ[χΧ ·YοϋDyε·8ςχ揳kŸZ§˜ͺ©­ψ »Ώ¦”ΎE/AJd½6Ψ»%:ώb„dB.<%U°vŒοP9/θŒΘ\ή`‚΄ΖE^%ˆξAΦ'q¬š^’—i} —2ό.NgpΡΉ|&ΰε%ϊC,€xj'§ΩΓT‹Ϋ5Ιmvρqtυ5s ΄>\LΠ³ͺθ‘›γπ₯#'XΉ ½]&|3zΊ¨ϋv³[³ χ+ς>gKΆ†¬?υoρϊag- KtF:E¨ϋΦ„œ½ΈKίϋ©qήEE€ΌΝί'‡†791}‹1ρΜ¬+;XS‘B‚–yΡΌ·ΊΝT ¬±‚(η €ΔL±§jZ”Ξί™PŸ jβΚ[πΣOl1Z//Kͺςμ—&‚ΖΔ#³ΏΏc·^)£ΑΖft‰4Ciμm@§;ιGS`SxΥ,€Jοu‡π:Ρ5Wψ~+2ΠQ4#Ω£.‚|ώΙbήΪγQˆξ6Ÿ‰˜")λuΪχ”75MIR'r²sΠ,5οJ?πΔd5EΞMΛz”5΅Zu‡PƒŸ\βχΝΜΎ '/¬~ώ­Λ―Gΐ†£²έΚΏρμΜΟ=s&Pί»•e<\ɝ“ωFFhQΰξΉ&{ο]JiŒz³$};ξοφ©OQ„σΙΫjξΞƒ€μΊLτĘπΛ'οΡx’©/_Ε7r‚>^lp%ή(Dψ’va;6Ιebm¦Έ^ίΕ‡ˆΐUD^Bά7u J‘‘σd“~44BΈˆ—jJ-³Hšή ­ΑY«ͺ£+-Μωˆ‘ζ<‘˜)ϊVΊ―5yrΜ3z”έχΘ(ͺw Ά rz‡NBγqFˆͺށΰBˆx^OΎ²vΑξ_’ΐKbίΎ‹u‘ψ½~m0Oœ΅“υΒ£u"ψ6MiRŠέΟ掽ι³ΕXoίΙΒSͺLqΡuzΊžΆό›: ζKΟ†U‹‚š鍻ršl΅£ϋΒ8λΠά‡zžόΎ2«³νg‚G•Ό hςώlΩΈ[=:Ϋ lβ~"ιhI+Ζ7ƒ³Θ܈l2ϋ’‚ Fα—±]μξΎEι=’„²Τyυ‹^Ήτί6uj((qyUmM έ Τώ:•”ΒΌθ‰Y;R3 /½²>yFωƒ —ΫΠD2! ±e`Θ5»ΎΈθy‹@΄Šζ_ιo”)x^άm@~gŸ?pτ΄ kaΈέA[Ÿh²{†Νͺσzͺέ)πΥO½F’χ³΄Q¨ ΘYj²ΎKκ©Z₯£Z΄>gπΜύψŽͺ8!4i΅M ”MδΊΣŠή l@˜Z?­Eη’"‘αŒΩ©[‡ endstream endobj 433 0 obj << /Length1 1647 /Length2 10819 /Length3 0 /Length 11674 /Filter /FlateDecode >> stream xΪ­weT]έ’-ξξ΁ΰ\‚KΠΰξr€ά%‚»Kpwάέ!Έ»Cpy|ίνΫ·Η}―τλ{½jšU΅f­cSS(©~5΅3IΪΩ:dcfε(€mŒ]œTμlμxε>ͺ€Μ]dœΦ€wŒ ‰šϊ³#θ Ά³:ƒψš S€8ΘΐΞ`γεεE’|Ά³χp›[8θΤU4ι™ώeωk ΐΨγŸΘ»§Ψά@σώα ²Ά³·Ω:ΏSόUA €³`Ά>+*iΛ(Hθ€ΤR [γ{J.ΖΦ`€Ψdλ’˜Ω9¬±˜Ψٚ‚*Ν‰ωKΤ 8ΩƒLΐοn wύ_ΐδhvrz€ζŽ@[ηχ3pΆ€mM¬]LJΰέnfχwBφŽvο;lή±w2%;'g'G°½3ΰ=ͺ’Έδ?ςtΆ:Ϋ όμΜήwšΪ™ΈόUίΨ;Ν;κ Ϋ:œAξΞΕ2LΑNφΦ@χΨοdφŽΰΏΣpqۚ+&€#Θθhj rrz§yηώλtώU'ΰΏT΄··φψΫΫξο]™ΨΩ dmƌΔΖώΣΔω=Ά9Ψ‰ε―~‘±5³°±ώΓnκbOΜδψχΡύΥ3τοIMνl­=¦ 3$;ηχΊ™ΚΜ{"/HόΏ"πŠΌβώ»Fεήη§–t±ΆVΪΌ7ΐ?ζ ΰ}Πmο³ ψkΨX °Ιε ΄[{όwΞΎ[τ¬ƒσία„΅5Wθ#3Χ?Μ`'I°;ΘT μlb0ZΏήίvu[S£5Ψτ.ςίηϋξΔΚϊo˜šΨΔΚφ/5ΈώlM½†wέώ€ESZ\^SŠρΏ™ΆoVzο g5{ΰ?"iΚΫ™ώηβ/*11;w€ηG6n^ΐGvΦχΛψ~yΩ9Ώύ?ΒώMΔφ―΅<ΠΩμΠeefeeΌΏωόk₯o4Ά&v¦υ‘ͺ3ΠΦτ½υώΣπlββθψψίΣΰ½ςΎ ;ΘιχΌ  eJzͺs5~V¨Έnw't}QZ~o₯]‡OJΘo™ΡsUsύ8ίk³Ηά‘ύΛΞ†έΑNψΩR¦Ψ}ω:ύΐξKΧZt0'–μTχΫ’žυλyΕgΑ€ο$AΟi<^~’Υ’L‚―.0vΊω ³’$1~QOϋεͺεϋ§n1 ΥK’…Ž#νDΖύ ˜­ Τβ΄hβ—\’SY&MGV½sQn<π!Β‰ΛΙ\pνΟ™μˆ$―ͺ•νͺ+°• =xŠƒΛZΗs;.τ’PCΰΐ΄€Ζϊ›υxHy{β(¦R©jΖ"tT³œΛZXfω;βκλs­΄χΈ N;vφτ7ƒ^™rH―K–»&ΆΖΚ\qγλŽύΟkΏJ~,qOUpCΕ`yJTΎΩ=„ΡŒŠΌ¨,άDĘθΕzJTOήΘ•¦ >3γ}ώ)d\V‡_7±0­Œ WJbΚ ŸLcͺŒκm4Ψ‚,έ< E9c I”?ˆ^ΰƒ§ΫD€ΛΉŽ€ν9Fϊ=/ώ"Δ‡˜K\MΠaβmŒ&£p( ©»βiŒ³™‰*φ£²8„ς,y±εν‹ΚGΎξkΪ$2‚t<nƒVQˆCέiˆ=ΖΉ†Ι_7]΄ajEαγ6β½]•ίnμ˜%I|=. °<δέPy‰ fιž‡K{Η…Ξ'°TŸ΅΅Βγ¨AeΉ]΅™γγΤς ΒŸΒ›Ώxη_Z£eΣFε3ξΪTΤWξp'oΐ%’§ŠνȝεE`ŽΈΛΦk δε+a!E¬"_‚ υΪψ…Vu*p{…,ŽG±ΛJ•ήG¨oK)&δυ=-Λ’‡έq‡β6"9ˆpΓ(Β¦YυoΣύͺ δZv4υGΒψ(9—«ΰl Μ‰›kΥΆϊ£ΌγgH7βΓ)…‚ Ϋ/žθe>αφ6tX)²Γ ½ϋΆ|qΎŽK4ΧδNγ£.š·>χŽUgIJs-O†ά?’gcaM!`Μm[,#qP9‡y{Ϋ,»Ttv4‘§Ÿ 1.φCΐλ“ΧSΔ«+z°―ΌNΘh©“₯[ˆŽ<>0·ήy?]mpnπq 6πγΜνσΛ›ΝˆΫ"Εί–ΪΈ;ήοEqΦB” ΜΚ~α‹mRXšυνς§―UA›=ui{λ Ά„ώ Κα6Ι‡Δ—£λ­ϊ_Ηe!ͺ~±)£‘οπtω=­Ι¦Α(Ω:Υo'.ηΖp†–5Φg%ϋΛƒ‘ΩnχJφŸ8-3ošͺ/ >kV=c7}―•䛦:]yω$λ8ΒΚJ…΅PP'αήΥ8£ψvζ’pΣάO³yΑ|Ώ{CΩC‚πO:³4‚ τΎR±»)ΎΦΪ!ΜέpΘώμΛ-ˆ――D;άα\Uξ‚GwuH΄Ύs1 ₯ΘσDαgY<ή"“ϊ΅3ο'μv(~―Οtgb~|Nπ·WΧ½ί›αΧ‰ˆt^Ý4ρ„ŒΣF]ΜΉgάΪ€­Δ,!μ„[ 挹)’¬υ2QκWqΪΫΊYUυφκK:ΣZ&ρ­Ή^7ΎΥš­$vˆ+9@φΤpόMBΥv'§jΒty1!ΗυP₯ίrχ!ί“!γE)Αp·9$ακ†žξ •σ#’5ΛJuε"ΞΜ]ϋO‘\ŠbΟΰβ9ut£>‚3©vd_GσΠ@ nΜDН™Jκγ£=+υpJ±•]MΩ77ΝΨΟa~ Ko£ΐΕρ€0°ΔΗ^+ β;8ϋRά–Hƒ*•“w>ΔΖˆW0gΏaέ7ύSΛηύ‡”…Ά[Z.ž΄ ΔΥΎžkΤ…™Φ#†ΔmΪ)'o†™…)‹xύόΞIϋMDκ|)4PŽ.™”! W²ψ€‹~ν΄ηκβQ₯—ώ9Ή¨_Μέp;'£ &ϊEΛ%υϋιΫGεΨt"TMΝί:Ϋ¦ΌŠέΆ¨Pύ°!“MΏG£=gΞQ<Έ8—yŽWμӈNZβΨU`σ †ˆΚμ»΅šΦ„όG υŠ ϋΧMώrO|ώΚ:GmO N<#€`luŒ«d·­KΩFΨj) HGz˜˜a/Vε΅KΟݚWΐ/ߚ&M @e[aŐiΆW₯B­ο¨νέut•|ΖT x…"+Π’;έ±(ל6+wAΔυ–EΜάέ׊ΌxKXΖ†ΤŠΡ$Uνcύ1±½:=ι4·¬zž! ΧPθρB)£φυρ§0φεz’³Ι­ΧWό…b­y­Κ+£“€΅RΔΠΑΛa ξσ5’―?*ψ~΅ΞέZόœς»`ςΉκΙhy¨‹κ’ι/Ήχ-Ή­ΩκˆH ŒΊθΣΙ΅Zpε–as³B‡%†Ψ@‚ xpcDR«avύLCSβΪΫϊ¬-³Ύ³™νε§’ΰ>ζ½., ©ροAxρˆ;ωβόWUΘ χΤ_hq’MD-‹ν#λ0ξτ‘g²šΫVϋ%sUOΕ\±ξΫΆΌόΧ™·άuθνΧ9υxΫQ(ΘWΈβΔά^Τ₯‰Ÿœσ= “κZJ·– η֎‹mŽΕŠNΈΗDξ δ…Βv˜CCδϋξ|ŽyEƒD’~Bb³i·Ÿ₯ ŸˆύHH‘zCYϋI;²¨τfaMVŒϋ—0:s6‹»η‘θoΆ‘‹_¦8΅iσˆn=η(Cy™±h?¨e―ΆRΝ& PŸS0\ŸnU—–Ε ύšΧCz^TJωΕ‘5"vηΑ΅,λp¨•fω…ΰΣ«# ښ~"ΥΤAδvZ²Α*Gέ ςΒUH50‚|VDΛώ”·eΗB2ΕΕ•A―“Hκ„,½oEβ23«ηn\¨£έΠΧ¦ΡόΣρYO˜T—³¦^χDy”ˆHθ/yƒΫ $Iαμ­έ›ž.ΓW(eŸ~SΜΤ…΅L~o^Lan‘±φ΅₯ېςmαw/5ž»¨6ΧθΎπˆFδ ±ϋ <¨Gΰoςaime.šμ'‰ψρΧτ dhΌeρψΏ ›ε:78ΓvΰzΒ,Wό±vjeΫmtA.BGŽΠ£Φ6ξ@΄_%T ο2#•[p–V»΄}Ωζ5*,ΎκX†α[ANxδ°ο`·V Ϋ°¦ΌzldΘaQSηψ²θ:έ#%±ΞμΧz^m³τ(&jΊδ C#~ή/ΫŠqδΛ3l!cŸj‘Ÿž¦1ˆŠ ΄@˜ Β“ϋ”|ρ²œ 'd;.bˆ^;ν3­ψΪMΪœV1LjgœσclF>;‘Vφ5Kς…ΐ”iφΈ΅Β1AQκCο}αή‘‰ΆLΦSNJ6Vβڌ> YA’ΝH˜j©ΪιA|H[Jfšΰξžα 2Š@ΒύHΡώΆΫց§.7. Ά%\ x[σVά2μ .ˎΦυ=Rνυλ£­τ‚„„Θ\ϋάΘ™ΰ5E"‹΄Φzr7DΔ&ω«άΐ˜<ŽΈI-‘φ'Κ>ίγΦΨ*Ν’wŠεN^ςqΌΚΌΞε@ΥRΏ)χ ΟΡ·€-΄ή­g6;λ )H·4e’&ƒο^u}9εYβΖ Κ†ΉΣ¬S4€Α€zϋνωά/UQλCŒω‡_8Μρα(y»sΌΞ/*؁ΤL­ζ/p…PlwsŒ+υήPVσ]ωͺΐ4υKj¬Žθ―vΞό8iWΝ4"ƒx{f‚1₯σΝΣuLh\CœΌig·OVf€Έ/ΓyUΟ€‹„=ΉPωϋ.ͺ]…°‰B΅αη4%νzViΩS‡gαΌyξH tΡΧJamŸ«ζM­±yU~ς·ΝCΠͺ Bo?˜dΝ(“/NŽθ6w^ψ}χds3[ΛE„Ζ‹΅ΰ¦§πOΪ9'#Pˆ8cώ`=VxH(λρ}bΫmΐ§σ}1λ‘Xκ₯d,όφ“¬+βδΫΆ&Ηΐq1wA8‚δ.§ΐΊλA˜ ο> o1kk(v†oυGAn(cΕΖΔVΓT―Ό›JυŸΤΙsΥώ\Π­η•ά¦έΧd½ΥωΉr—–)ΑN5πu#υŽΈΛq‹_=ΪΚa δbRό˝7΄&ςΐnƒ&AΊ.‹/ese"ξθ΄–O¨“<’Lp$ΨΛΎ§rNsb“²· TσΟe€Κ|Q=o˜οkύ„…όX·y―Ϊ†+X-ί£¬¨u?‘›¨―vΞΔιΞ| ₯HσΦ¦ͺΉ.|εR•5‰1γ™‘· …CŸs%m2#`σŽύ’ɍΌϊ.·ͺπ›BRΙΌcδ#,5„’ΐΰ΅κ’t;IH δδ‰Œόσε-…ρ=‘χ“BgΦBς)g­ζ­%#Ә^oΥYΨ ό΄k]εlΒvbσΕτaχ΅=JΪDb&kυΌ­5­ΛλΖκζdAcςΕFcvφeΒ³•ŠAοΓ~xΟ`rY²‡*]jžAϋ–T_k”ΟasΘάs4e)"‰ϋ’]σžΒηgQTΌ„”ζέ¬cΠNSBdY‰JUœ”„Μ —HΊŸΔsΑ±έ\‚ΗN^_œβϊέΪ[%α%η8τ‘₯ΐτ—!>@=ΚΪTιJ!’7ε¦BΉ|6ύζξϊdωλνωEΘ­κ%φ1YκΥwhΧΤ ’vΘΆ#ΈϋοΟΘKacS;hOΉΑ…£ k2Οk’蚳3TΙXRdAΡ;Ξ …Eƒω:Δ@UΉΣΫγ;’ρΈ]>y€Ώ°2j\‰,œρdΖ±œΰGΓ36Ϊμ°™Ζ™ΚH·Σ‚^»ΌψΫ»j0ό\C¨ ΕNΪ±ΝEΌ/cJR$IΖw˜ϋR΅LNuαŽS‰θδΕnΤͺvJfpά–pΏψ.Η€rίβ8ωη6Ψ|VΖέ²ŠxΩž¦~.βΛN!Ζ!ΝέX'-„¨9ζ\ U¨_Θ{[Z—Α7I…ƒgΕHΨ₯ˆš;ζΆπ䕆0ji:Γ?ώ^ϊp3q\ΥpU,0†Όr4D‹ΰŠm|Έεδ3…¬ΊΒAΰjά@ψ0(α–f)o1χcΘω– ½ZΧ«e²9 "–Σδ‘Χj{M– (Ξψ ǁ|#Χ|vκ°œxΘp‚Ϋ,R¨ŒϋGΛ\ΞwDGaιΞT ωφŒΧΥ“—‹>±, -oν Σu]Σνχ•MI{MךSΌfΛ·Γl₯X)} OΡ|„o–ΓŒvώΔzχj”j·άΫNwOσ ΒΚ±ΘπρHυ0/ζδγIxtώ€ϊΜVsˆιώ«ΒKΫ ―βN§1ό^Ν―γΜΧvϊx σιΝŠ~;»N£_ιœMސ7Fn‘ΥοŸΏŽk)QΣΘŽ«Ά.KνPΥθAes+ιξͺQd ]9_Α3 £gGoψR°±YοΎν\–P”>Ξ|Ÿ2Ι δ$l¨›.O€MςN_ζΌC‡Yύ ζς9°΄}5·dάήiγΥa.‘ξΥ η¦³?bf‡1ΉMτη"ΕέΰŸ+ŸЎρJΈ`ο}ŽΤ(ιWq¦2X¨)m©™ˆ+1Ώ²υŠ8ΐ IΪΔ± εLM,9w%ζΐρs΅#²:·ων-KκΓ_­.˜0(œσΐ,}ψΘ§TλΤ™#1+θΌΛ>žψ Α9&¦$h#Ηm˜ΞšAͺl%Z¨AΑ2ΠQΝU YSˆ7oγgŸtBƒθΗ'<2R+›ϊD ΄­τηP:ΆΡ8ΉήΧΣ­^NόΣ0Κι `JΎΣεΓQΤpαQ%άω£φ›Ί ι <ψp7h¦Δ܌ΛΠQάƒŽϋΓpϊεVθœ}Γ“ŒgΫWΨτ£B”^νΈΥ^/±JeˆΒήIhΓ1Ήόφg£Ξμ<opθ]i8¨€1±KΓ…>ΘHΩ ·˜zœ6ϋΡΚgz'βi>s„ύ―–Έψ1νƒVΑhUU„χaiHm@e7'ΝΨΖJΫLT·ΨKΌˆ<ΘqΛΔTΝ#…ω,΄Ί5gbaaιFn;ώJV΄°xFšq –‘9Ι„ΠλF€€šφAfGf‘₯ΑΥXVαέΐΟΚ½r쌘qŸHt…8rV sτ8e€α―/~Ψ§k>›Ε–Z¨4ΔΏ|›¦X4ϊ!W­μΗγiΎ“;γΙ;$Σwr^σ} *σY™Δ*ρΖα‘@EεΏψŠIλv&α›Ž«΄7”+ φW3‡]yΒEsžF]ˆΎ™ό`ο7X\b9Ωκ²λRLr¬Ια“ώ°'Αͺ½ŸΛώ»λdQwΫΥK Dpƒ…δ\i0 7ρ(¬Φϋg₯ΘrσέΧί’ΣΟΒΠ…Ηh’Ώ₯Ίx‹ύ¦ΟFzν~$Ά(Τ jΰk=ωpž{ιF" ₯(€ΰ-Ί₯`»rŸΰέx&-όJΝίγAπFΛ^£₯m|ƒl₯^`ΒvΚt‹ΠΐΏ‚B€B’DpΝΞ%?ΌεšΟΤ~γι½ΩΫ§O ’[t"jϋ3=(Ό°WID…ίϊθύQ·γZά0"*μ±@ ¦eš‘°p§„J=x“Δ!ΓρΗό#?Ά«NσšOU,2x7‘ήρx>\’“qπΡ+“4θ½—8$SP€`|½N΅υά0ΰ“3f&… ϊσIΊ‚Ύ^I”|qoτͺ<6GtγΧxgiο½π{ΨIήa<ΤτƒψΔ8―θ*N²P4Ψ%¦]«3˜ΐ―±¨a{Ό'σ}{]Υ υVSŠž€#QΖΑ’―0/Ÿ!ΡΎc2D‘–lIζ.κ^y"l<θ„ Ϊχθά‡Ϋ”|Guβ%§.:_'δ3΅ΘQτ{ΉPΫ+e+ωΝπώηί€ή§ΥΕpΣο–cαβB˜Ά+ςPΝσν[w€&rΓl€>οd ­Ε"<ǎ†?ωƒŸ=­Δ"qΘήͺ₯J•©ίξ$ύ%@?“_gвω…`Θ„Θz wκϋ?Κ4:²l`ΆρQΉ<‚g׊^2UchbK†<Ό1ZYγξœΕQ^8ν/ΠΛ―CR΄ςFͺΙ/κ΄Σ‰C#πΞ‚=1‰KΦ¨;Φ¬Oc†―%>f+‘OsΞ%eLμu!Υ"ƒ=¦έN7Ηρ·Ή;Θj!ρRλ›hΚEΡΆngα‘yQ€!ΐK7ΜωίΡ7δŽ)˜ζ<cες ’eŠϊΝ)΅š|‹LAtΖά2ŸR/cΩΑ~&ά9α]ρƒa0rΧΒΫ―φ9αν…Η†ΣηΗΝΤͺΆΜkήύζΥ: EEν\φJφΘ/€D―š«ωw σdςq‚p^4ιƒ(έ΅Άž&^o ΏΡ'³¬² WιfΊt­uνY ΖΑ_ g'\X(+«ΡrΏ°›#ΪgΣRω)<ΌΩ$/{”pPs-ΙΖa@~Ÿ"uv)$=‘HήA)‰tΞ=*ί.ιφ½ζg1Ιτ±ΑΓvΝώ –nΠχ“­ϊ#f+=¨(ΓB7Lυzρ?\ Θ–&Ήž5·ˆΜL'YΥŞy‘ €·i©FzzΜΈ‘ε³Ώ!t‘―…?)gκkϊωθž#ԌgrzΓάΑŸ…5Κλ<;’yv±~εbγQπγ½ΞNEΏύ:ιb %茼8"YΨ(%^€Ώg%.‘#ΰ5)]€ο>-ΛͺρΨ™’σGν$ΆΠ$n ADIΗX»άθ—‘U£SψΩ™ϋŽ;΅₯°v§ύ%ŽNg‹ρhΣcy穞Η|KΝD…­μΙξ» Ώ‡@Œδ—ΈFΈν8TŠh•Ά6¬[ΛΦ" l­€Ο/Œ9δΡ€‹m>4ΐr[ήe΄uΉα6ζΉmB?κ(d―άΐι96pκοVςε&έξί=EξX…€χαΎ9q―κ%ΘιόT<δμ½―‘Qu2cν„„£<ϊ€X9ΥVŽΩΐF-‡N!’Α ΑO[λ0VSt3Ύjσ`ΊΨ‰πJ~,Zc€φ<‘ΰΈSβ ΖZq%p΅vεΤΧ±¬°/9ψ¬Ώά£*CVT°)I¦y/γЍ*«΅kθbά™²\ΖΥPαROΡ0ͺ  nbΑΓN°P—Ήzυ‹ΰΕ»λ[Κα  Xπ <Ξ1#Α¬¬J₯%ς9-'€<}§F?„δŸηΞ³£R,jΡ₯τ=<_§Y`θŒͺ«₯φ9<͎ywn]¦ƒZ!“H8—΅agςN’RaŽjγφaΗ»w —`'巌~KQ&ZphETœ&vF}εοŽbφΌδΆβoPΠr΄&=ΰ–<.ώα” >ςGΊω|ψ¦cV9‰ͺT’…X•«Ρ™θjψg‚ΡQQš½)ˆX‘Ή³:‡ΰ)^XxΌίš3UTσΧ§Ψ9Εη³v― Κ{j}«ή@‰‡5Υ¬Ζνϋ%Ηϋi35*o¬Ž‹Ga± ώμΖΊ―9#ε‹gPhΫ'±xŠβ[fςΛ™»πLΙvΣ­9’•{-œήο…/Λ•ji™”κ°NЬ&4Ή‘/IΎΐ#Žή!X[]Œbιrw ρg§ΈU‚/Ε¨ΐγ!Kͺϊ†—ϋδJΠyιν&¨w©’J²ϊ τŒrN(jƒήω';λΑ?΅λuz -©ϊΦ€iξ‘ώR"Ψq)ί)lytͺΒd;ZυXe‹σ>υήΥέV‘b*’A–“ίθΝiͺ<°³+ύPI€°ξ‰Sμhέ „$pkΩ›΄ͺ)C: m΄zrϊ|€>Ζ…cύ šφ(hυw‰£ζ’%Β¦i΅jRQ`‰μšΥ'qk1ΖίͺKφ4š¨xH%AR\x“XΉύ’FnόM3ˆ–΅·›Ίt’P™ΘGΣcfΧw*CΊrxρΪb†ΎΛ£q+M"VτύΕKλ#¬ω…Ί‰θ5_…ι ρε/7α’m]gP§­˜CΫ‚ΨΑ§ΗbXγΟχΙ>ΡB “ ΗΰΐΗ³Μέq/”_£¦ω6ΐyŠω,lmΞe…ΘΠΚΞ7(6Β/§‘Gξ¦0ΜΎΞKŸ]ϋ΄4+φCεgc© >ΉΛΧΈζι€ η]UΛY­ajHϋ\ΥΉ8gXp{€!2°H€»Aƒš΅Κ*υ˜?ΐξ’u*9rΡ]7VχΝ •§ΖϊSΊy”/‡q‚Αΐηˍϋu$s5Μ8ζ.½‘θbό†šθδΘ„Σ¨| λ_‡ϊΊk°ξ 5ΐ ικΔΊϋό1¨3L7/>Ϊ΄V‚’I“”V‘|yΆΧkΛ¬μŠ\Ϋ7δ…ƒ5gc7m…’Όψέ­‚p ωj9ΥΆ«§ΞžΩœ¨Šλ:_GΌ3;/yξηVͺ,Ÿικ μνbϋŸώD–Žέ*­ œƒΜύΎΖσpΑΞ¨lψγΡ€ BΝnύ}¨Ω;¬ΆΐΦ`Œυί3gι&/„Δf‘³θp•=†h^ΡΊ‘½ϊμF•‘-‘₯f„FcYA!s^eσBϊ”ιE₯'XΝ Ό™ŠšΚϊu^%κTo€T?όeΡΫΉΑڈΚΗσ]Π.ΦηGόXΙΛΊΧ]]’ξ/§N΅©κδΞsή βOŽΧΈTΈηAς# Qυ|=`^MT2ωŠΉβχSL~nιY#ΤΑυ€¦QC2w)ήΡί ₯‚kν³ΕυύioC!™ΝΙWwm>‘ δ>dL°Z6ΐA:=£=€ ȏͺ·ΗιhW|vι7R,'/+«ŠΓ"7C”ƒΚ5χCvŠtρή©*IΌq#wΥG|pΜn­|dŒ΄Εh6["{X»Ψgΐb,ρ‘™°πV‚yιθΪ‹%^ΑN*CŒdqڏ“wŸϊ\"ηͺ^ŠΙ γz¨‡€ΫŠ~ο3hό‘2vζ Η§κ8(E_/K&ΆΑΆ<χjω€}sCC‘ Cρ>Q%°’ζΉ™ί5@δςšͺξ‚&·τΑΘ(mš—ͺ.π`οΪvί υεr\³kZ~’w'Έ7ΝC¬7/΄O5χrς’%›‹F‘Ϋ„4…MG¨MKΦ—‡-tΕυωTˆq …0_ ?Ό]7ΐ]€:Δ#Y.ΨψΨ,·ΊJΣod¬">\#γi"Υ^™R~ρI2ΪΆ–j5ωHϋ•2Γ™D*-ŠQΔĘ@ aφLΪ{3—­¬KJ ϊ(„ΑGΧ5™ώ²“‘wdιŸf’n`)|ι~Ψή'Ο‘.=)Ρ€rzχκ<Υl6πλ7ύγ.άOA>Χ‡‘p†ισΩ!šυ]-—°ά«8~LΎ€IQNqγœqϋ\)ΑΈ΅κcUΜ@21qŸ dЍDφX%Ώ;ρeΡxŒ(>Τ}δΧάΒ€šGmžυΨΘ£„UΆ‡‡Ο6Νι£₯όpβSI?μτŽˆgD(Ηƒ+ΌΩŽ€‹ΏΜBΧ₯cνZΌVΑ ]`σwƒ$υ§ΔΆΝ4œ‹©]ιοωρθDd7εk)>˜2ŠYθV·oγΙ:Ο˜Q«S‡ρΟ$#jΕil~³ΥZPϋŠY_]ΙρG`& šΟ쏠X>h ·NΣDεB Ÿ 's+)ŽΕΡγlωΰ+½!ˆΓ»ίRXΔ1έ”6•ω kQ;]ηyXσJr€iτ;ΣΫuυΎN8pΌΡ€œ6wΪόσ!ΖKΩ‚3+rm‘lh|€@¬Wκ[MσΡU>‚ £Q> $_ᏟM.ηνœΐ΄f-,mTΑσ ^)ΩδbΏΧ±WΌ–£±.:Αθl;‚Η‘φ +Ω’Ÿλ‰ν q7f[.DΈ™ŸFΆWrΒγΕΖ(“1Ώl„œΐήλY“ 3†…€…/΅¬EžwjΕι'·Wό¦Ζ4β˜uωJθ€Έρ7aqBθ4bSφΗ―xj€œΚq|m² ­VΦΒ¦οϋθλ‘6%JƒΖνυ9ά‚Ύ·fPZ22ΫΥ:sIΏΣσ Ζlδc'Ψ?6ΎlkπT²ζ²΅H@G—ϊvrω;ڟh‰&2ΜcšGݜυfίΈ–$7—ΪΌ4¦½£ΒΚ^ΩdŽ€Κν?OηSΦ™Θ¦FM°άΈEΉ‚±ζΈ ӎ|F$ΟΊ‘d#žρ γš{mBIt9ڏwfi(OD¨ŠsΠ¦˜έμ»οjšυH0a :¦#n:&ΛΑT7νΌ!ΐ ·Ž&lΘX}εΚΎ§ ρTσϊrα Έ%wάάYΧ'wE‘|}fΐύκi’‰ŸΡκ*E›Α{|τ3ώ²o(‚{MΗ>inίσ6Elσ0ŒPX)MΒ Λ„)δ Η‘3‘όΤvΊ9λlΌc΄έPΒw+»AS{ΩΛ8{²eλ¦9`,|LMb —.΅CKθύΛ=M½MAMŒišΌώ‘ Sΐ²Lƒ `Άν$°&pvψ’DSYgX0π₯’OfQg{•„>λnb‹#eZ0 Λ‚λrά)‚^³U/sšŸ«k­QέψΤQÝ.₯WΝρF―ν™ –φχŽMλژ|φ;«Υ0φϊΞvϋS7εΜ~6&¬„J³"ΌF1ηUΈ}­―ymγεU7Œe*αΔγ+7οC1fΪΑΥ(*%$mς/HDjF™a •e+hW?$\wMΘQmfΩ’ψδ*S] ˆΪšζϋΝ{Νλ'UΞ\’nΌϋ='ŽΛ‹ιπi+ΚA¦*€>"+7<Λ|‘U.WΉ9.ώ›ˆ.λΨG£]ΕLŒ—’­θ]ΤR«ΙΤΌΑ:/₯Κ•&«Sφ€‡70QΟ¦h€ώψΧιΎ²χΑ·‘˜Χοf+L†ζ―ΖV•KƒΫuή4'yΊ‹Υ6 S0MΩMgέŸφ[σΠZd―[ώΈ„ΑεΔD.(Od¨ΊZ›ρžΝSχΝ|<ψ‘;ίΡλ Κ {;βa.€AG_d18«α!>P8ΆζJΑtΌθ£Aγ©ΨεΜγL6τϊC}˜qX"r^ΊΜΤq!ϋ?œ ^B endstream endobj 435 0 obj << /Length 700 /Filter /FlateDecode >> stream xΪmTΑn£0½σήC₯φΖ†@LE"€Ά­šj΅Χœ©D’•ϊχ;oΖ€έj₯Ÿgζ½y_ύxάN²Ί{q“θV«'wμΞCε&ωΟ]\]]u>Έφtο\νκqχx§‡ΪΊ“ΊΞ7Ε¦mN7”Όi«χsνΖ¬'­άkΣ~¦€G]?»ί“ώνΟ`‡ ύ4ςž›Σ;νίR΄V—΅βΤ_n86]{§Μ­Φš€u[ηέΊΑΤs«ι¨fί΄υΰ¨Θ L¨κ¦:ω?«€βνΗρδ›vί‹…š>Ρζρ4|°²›`ϊ0ΤnhΪWu}QEθφάχο ”–KU»=5£Yοw§¦ί‡Ίl?τN…Ό6’¦κjwμw•vν« Z/Υ’,—kλo{&–’—ύ˜›PžγaffIΐqΙΐΌ ΐXŠMJ0Lf„hr¦˜dΔ₯–€ΔP<ηF:AS‹l –PJˆβtΖ€Ι #• ‘G–s =2τ+ζ¬ (@»–Œ΄λs‹°5€—Θ.W PL²KF1 ˆυθLυΆΘπλ°HαI(£$τηuτΉ†x-ƒΜΰŽŽ₯όΪJΌBœJœ!^I Ρ:ggM«5γ9€ζ7F7ΜFŒαN°ž}Y{}&ώƒF“Θ„ϋf.œpΡdkζ_ Μ’‹NΕ0VG9ΧϊΧΚ±ηΠϊwώDKŒω¬Δ4XΓ=CψaCɁ–g2)4X( ΝΖrb0§/sŽω4κlΖ΅¬Η‡ΛΓΔάϊΙb]ˌΜ[r<ƒΞΤs!?υοf*΅Œ{.ψ“z.τI=ΞmZˆoΰJ™+Ξ9ΗRΰΚ ρΟ¨xθ?WϊπαβrΉ\Υyθ¦ΰˆo|Mλ.—Tίυ¨β?ίnγύ‰ΥCό_er¨ endstream endobj 436 0 obj << /Length 699 /Filter /FlateDecode >> stream xΪmTΑn£0½σήC₯φΖ†@LE"€Ά­šj΅Χœ.RˆC~ηΝ8i·Z)Aγη™yoΖ7?ž·“¬ξφnέkυβNέy¨ά$Ήλƒ››’«ΞG׎ΞΥΎμžΤσΠU[7ͺΫ|SlΪfΌ£δM[½ŸkwΙϊΚ½5νg xΤν«ϋ=ιΗγήϊiδ½6γ;νίR΄VΧ΅βΤ_n85]ϋ Μ½Φš€u[ηέΊOΑΤs«ιEΝ‘iλΑ P{Θ L¨κ¦ύŠŸΥ‘ @ρφγ4Ίγ¦=tΑb‘¦/΄y‡VvLŸ†Ϊ Mϋ¦n―ͺݞϋώέAΑr©jw f4λγξθΤτϋPΧν׏ީΧFΤT]νNύrΓ}sΑBλ₯Z”ε2pmύmΟΔR²?\rΚΥs<ΜΜ, X#.˜K±Iι†ΙŒ€νCΣ€ŒΈΐŠηάH'hj‘mΑJIQœΞ09d€’a"2τΘrξ‘‘G†~Ε\€5hΧ’Q€v]`nΆ†τΩεŠŠ @v)Β(†A'b}q¦ϊ³Θπλ°HαI(£$τηuτΉ†x-ƒΜΰŽŽ₯όΪJΌBœJœ!^I Ρ:ggM«5γ9€ζ7F7ΜFŒαN°ž}Y{}&ώƒF“Θ„ϋf.œpΡdkζ_ Μ’‹NΕ0VG9ΧϊΧΚ±ηΠϊwώDKŒω¬Δ4XΓ=CψaCɁ–g2)4X( ΝΖrb0§/sŽω4κlΖ΅¬Η‡ΛΓΔάϊΙb]ˌΜ[r<ƒΞΤs!?υοf*΅Œ{.ψ“z.τI=ΞmZˆoΰJ™+Ξ9ΗRΰΚ ρΟ¨xθ?WϊπαβrΉ^Υyθ¦ΰˆo|Mλ—Tίυ¨β?ίn—ϋ«§2ψ %τrg endstream endobj 437 0 obj << /Length 699 /Filter /FlateDecode >> stream xΪmTΑn£0½σήC₯φΖ†@LE"€Ά­šh΅Χœ.RˆC~ηΝ8i·Z)Aγη™yoΖ7?ž·“¬ξ^έ$ΊΧκŝΊσPΉIώsί77EW«]}Ω==¨η‘«ΆnT·ω¦Ψ΄ΝxGΙ›Άz?Χξ’υ€•{kΪΟπ¨Ϋϋ=ιΗγ`‡ ύ4ςvΝψNϋί·­Υu­8υ—NMΧ>(s―΅&`έΦyw„ξS0υάjzQshΪzπΤ+δ&TuS~ΕΟκH xϋqέqΣΊ`±PΣΪ<Γ+» ¦OC톦}S·WU„nΟ}ξ @ι`ΉT΅;P3šυqtjϊ}¨λφξ£w*δ΅5UW»SΏ―ά°oί\°Πz©eΉ \[Ϋ3±”Ό.Ή εκ9ff–¬— Μ Œ₯Ψ€τΓdF@ˆφ!ΧiŠ @F\ ` H Εsn€4΅ΘΆ` ₯$(Ng ˜2RΙ0zd9χΘΠ#CΏb.ΐš€΄kΙ(@».0·[Cz‰μrΕΕ »aΓ ‹±Ύ8SύΩδψuX€π$”Qϊσ:ϊ\CΌ–AfpGΗR~m%^!N%Ξ―$†h³³&„ΥšρR σ›£ζ#ΖΏp'XΟΎ¬½>‹A£Iδ Β}3NΈh2Ž5σ―gNΡE'b«£œkύkεΨΏsθ ύ»’%Ζ|Vβ ¬αž!ό°‘δΐΛ3™¬?Πfc91˜ΣŠ—9Η|u 6γZΦcW‚Cƒεƒabξ ύd1ΧeFζ-9žAg깐ŸϊχΖ3•ZΖ=όI=ϊ€ž η6-Δ7p₯Μηœγ?)pe…ψΖgT<τŸ«?}ψpqΉ\―ƒκ< tSπ Δ·Ύ¦uΧKͺοzTρŸo·Λύ‰ΥSό¨r· endstream endobj 438 0 obj << /Length 700 /Filter /FlateDecode >> stream xΪmTΑn£0½σήC₯φΖ†@LE"€Ά­šj΅Χœ.RˆC~ηΝ8M»])AγΗΜΌ7ΟΖW?·“¬ξ^ά$ΊΥκΙ»ΣPΉIώsΧWWEW]}~{ΌSCWmέ¨σM±i›ρ†’7mυvͺέ9λI+χΪ΄—π¨λgχ{‡‘±Γ„~‰ΟΝψF ίή)ΤPœόΛ Η¦kο”ΉΥZ°nλΌ;@ϊ1˜zz5= Ϊ7m=x κŠͺΊ©FΏβgu PΌ}?Žξ°iχ]°X¨ι½<ŽΓ;k» ¦C톦}UΧYoO}ζ Aι`ΉT΅ΫS7šχ~wpjϊmχΟο½S!―θ©ΊΪϋ]ε†]ϋκ‚…ΦK΅(ΛeΰΪϊŸw&–’—ύ97‘\=ΗΓΜΜ’€5⒁yA€±›”a˜ΜΡ>δ:M1ȈK,‰‘x΍t‚¦Ω,‘”€ΕιŒ“@F*&" C,ηzdθWΜXP€v-hΧζakH/‘] ˜d—"Œbtv"Φggͺ?»|Ώ‹ž„2JB^G—5Δkdwt,uΰΧVββTβ ρJbˆΦ9;kBX­Ο!Υ0Ώ‰0Ίaώ0bόw‚υμΣΪλ3ρW M";χΝ\8α’Ι8ΦΜΏœ9EŠa¬ŽrυΫΚ±ίsθ ύށ?Ρc>+q ΦpΟ~ΨPrΰ‡ε™L Φ h³±œΜiΕ˜c>:‚›q-λ±+Α‘ΑςΑ01χ„~²˜kΧ2#σ–Ο 3υ\ΘOύΎρΑL₯–qΟRΟ…>©ηΒΉM ρ \)sΕ9ηψO \Y!ΎρύηκO>\\/χAuΊ*ψβ[ίΣΊkͺοzTρŸο·σ%ŠΥCό0tΔ endstream endobj 439 0 obj << /Length 700 /Filter /FlateDecode >> stream xΪmTΑn£0½σήC₯φΖ†@LE"€Ά­šj΅Χœ.RˆC~ηΝ8M»])AγΗΜΌ7ΟΖW?·“¬ξ^ά$ΊΥκΙ»ΣPΉIώsΧWWEW]}~{ΌSCWmέ¨σM±i›ρ†’7mυvͺέ9λI+χΪ΄—π¨λgχ{‡‘³Γ„~‰ΟΝψF ίή)ΤPœόΛ Η¦kο”ΉΥZ°nλΌ;@ϊ1˜zz5= Ϊ7m=x κŠͺΊ©FΏβgu PΌ}?Žξ°iχ]°X¨ι½<ŽΓ;k» ¦C톦}UΧYoO}ζ Aι`ΉT΅ΫS7šχ~wpjϊmχΟο½S!―θ©ΊΪϋ]ε†]ϋκ‚…ΦK΅(ΛeΰΪϊŸw&–’—ύ97‘\=ΗΓΜΜ’€5⒁yA€±›”a˜ΜΡ>δ:M1ȈK,‰‘x΍t‚¦Ω,‘”€ΕιŒ“@F*&" C,ηzdθWΜXP€v-hΧζakH/‘] ˜d—"Œbtv"Φggͺ?»|Ώ‹ž„2JB^G—5Δkdwt,uΰΧVββTβ ρJbˆΦ9;kBX­Ο!Υ0Ώ‰0Ίaώ0bόw‚υμΣΪλ3ρW M";χΝ\8α’Ι8ΦΜΏœ9EŠa¬ŽrυΫΚ±ίsθ ύށ?Ρc>+q ΦpΟ~ΨPrΰ‡ε™L Φ h³±œΜiΕ˜c>:‚›q-λ±+Α‘ΑςΑ01χ„~²˜kΧ2#σ–Ο 3υ\ΘOύΎρΑL₯–qΟRΟ…>©ηΒΉM ρ \)sΕ9ηψO \Y!ΎρύηκO>\\/χAuΊ*ψβ[ίΣΊkͺοzTρŸο·σ%ŠΥCό¬2tβ endstream endobj 440 0 obj << /Length 814 /Filter /FlateDecode >> stream xΪuUΑn›@½σΫC€δΰx ,‘ei#εΠ$J’ͺWΦ)R ΆιΧwίΜ8•šζ`λρx3σζΦίžf_ό,ΉΦκΡΖΣΤϊYυ}³..κ±=νόpΌσΎσέωξαF=Lcϋδκ²Ί­o‡ώxΔ·CϋvκόYυQι_ϋα―sΤε³9;šΩο~1Ντ¬_ΰBΟ4τΟύρ-θΎ’¨ΐ«OΌ’~:τγp£Μ΅Φ:λ‘«Ζφ9Dsρ€ζg—Ϋ~θ&1¦^`32±κϊφ(WτέξB0(~z?ύξv؎Ρr©ζαζα8½“Σ«h~?u~κ‡WuωΙ]ΈϋtΪοί<œ(­VͺσΫΠ4dq·Ωy5jΩΩσϋή«˜ »kΗΞφ›ΦO›αΥGK­WjΩ4«Θέ?χLΒ%/Ϋ³v΄Ί _±NŠU΄4(61¦‘‚Θ˜H`ZpΪ0a‘A‘SgAQ@Q LE…)5χ8ZΦPΤ\RC±¦%4k(4mΈ€AΣ%MΕJšχλŸχ΄‹σήν―Ν$ιΈ*C™6TQ.€cζ3ΰ„pτ‚4ctΚ| œ1vΐ9Χζΐ–y\Ϋ;ζ‘΄.y.i*ζΙOΝY£§IΈΌa%aΞ2A†{&˜c―xΣο”sΜΚ4cΜ²΄» «lcw3Ζξ6aŒέν‚ρ˜v7δΝfŒ©gΘ4–½aŸΘΚ’O³€g[2†O[1†[3¦ώτKš?ΎmΓgΑώSΜ-Ψ }Αώ3θ φŸΒOΑώ3ͺev/ΨNzφŸ‘ž}ζπ\°Ο™μ-!={3τpσ“οΰίIΆΨΛIΆ)°dKΙύd‹gΓIΆ˜ε$[dθ$[HΆΨΧIΆπμ$[με$[μλ$[dε$[κ/ΩΒΏ“lα³”l1·”l‘/%[θKΙ~JΙ–j%[μ^JΆ€—lIoων$,ηv)%sμRJζxφʚ³₯>όf—T+™cVΝύSμήί°¦nδ$ 7G"Žρ·=MS8‹ι¬§σ'k?ψΏƒύΈG}θδό†«ϋ&ϊι¦Ώr endstream endobj 385 0 obj << /Type /ObjStm /N 100 /First 860 /Length 3078 /Filter /FlateDecode >> stream xΪνZmsΣHώξ_1‘ΆβΡΌk(j―€l€[^–,°©+ΕQ-Žl™~ύ==zρΨV'[uuW ΜHκιιyϊιž–dε,K˜P‚ “2•&L&­`R;f“žNq(‡VΥύT3-qξӎΞSfœ%™uPγζ$υ™Γ‘Ό`©€T₯†Ξσ4‹ΧΜc%qŽ{F$47θ€r ΌeBΠD©C‡fς)ΓΛtBVc.`”Ζd…ΣAΨΊ=6QΔαp¦άrΪξ8O"Π—*ΊγΡΑς΅€b/ιŠFΗ%˜ά§™2ΤΑZ•1L ZΡ苐*(#ΡΡΦ€0•°]’ν’τϊƒΩ₯Δ$ΐΖΊ"a‹Ε©±K·hBX)₯K+’«ΥsA¦b€ @—BΛO˜°R¦X…Δ$ ₯`ΚZκH:‘FZλZZf­ςŽV Šhς(ζ@G3ki6Μ¦4Txr2LΕgΰ3 γΘί)nx-AΏ re©„3Α‹Tƒ@Dk )tŒ’€7MΡQ"žΜLˆ$ԁ^HΚΔ ΰ~/AM`κΡ,E .Y’“΅Μ&Μ…₯@Ξ'ΈΔΓd¦!η.Θ₯’8„Φ§Ήΰh¬%I4y Aθjκ£# 9ˆ8 x ηHVφh k­¨½χοψ›―—9γ&“²πƒωqΟ“ώ°œžδΣ 09βOψSώθƒ'ΎŸ*φΌ*EdK‡°Ε$jƒΑ?Η φ€έΏΟψγΛ7%γ»μΞώιd8g“³{χŠΩp>“*Ÿf£ͺψœq珻wΩΟ?πŸ¬ΫΕˆ‘„ν3ώϋ»χ€a¨΄…ΥPƒΆ“ωx|t•,  ΒΪΘ‘š[ +“ 5hρ„“tHtΩJXx54~K›…φC‰ ²$ΌWNͺ€ύ˜KόΓφ@A°ΏξkC|―ϋH” ’P‘>”ρWΣrtΓٌΏΪέcόMώ₯bέ<5^egω€?œω€š!#UΔ’Y9ŸŽςYȐασό€Θ–_XΰΒrHι2ΥΜa^eS¨aFβΡΣπ νΙI5L܏ΆN χ[ K9Lό[ #gΣmΝ0R½°Ϋ k!†ΎŽί-¬l2΄Ψ€·†fΪ‰―ΰnLΧ%@ξϊuξϊq—v†ξϊpΧχΡQλ‘ΑΌ•°L²―Έq>X@γ{KubWa€‚ε»`€zi[—dQ­‡φ?.jZ΅­°’Ψ3ΔvΒ΄ŸšΥ ζQTG₯g jbQqCΥ€ΏΘ.θ^*jHŸgΥ΄ J”γώ-ϊG΅ΤΓ@[ΤTŠ*Z”§μ(¦8ΉeΥ§ˆŒ‡μ(@ΎWLgΥ£σlŠŠoΐŸeΡΙaqR#JPΈ…(ωe2*OŠΙY¨§Γ%ν³PY7±€έŸΕλmG‚XΕιiŽh"Ϋ>xΛ3o;‘O‰½ΣcPπΣA•½B9a¨Έ§‡%τιHΒ_눧t6yΊ"]VΟΏp?<Τ}ͺφΫyRœ»H˜Χi†VžZߎΉξhυŽφ λ(Λ™ΕZΓAO}T•7ηΤ§#Ψ‡›ξΣϊΘΎXΆmΓ\ΖvηAŽγZ‹Kΐ -=Λ΄v΅6m―ϊt΄Χ]3vi}„y£ΏΥA6R«©ΰώΠj1HνAw½lVιxUνJZH{·²¦,iWά #η‘ΏνΣυ–Qλ;β•·mŒLg{Σw=žomŒ‘‹’$ εΡ‚Ν?ώ–ƒoψίζbΒzώ >Φ±ΨzΊόAώ]ώHkσΝpΫόρ?=u οΨ’Π-"O΄(O–UΦ(χ)Φϋ°ΙMτ΄άΗz΄ΞG#ΫΜΆΥ.C«oΌnŒιΜ ¦₯E:Θ\ςέΫ ιθΪfTΉΉs)Q†άέΈ„ΰ°£Oχ:χΣy+ΧP&Φ+ybR±ΌΟ`pΰIΣΖΧ»1M?mϊνύnL³Ώ·χ–²μrq΅›ΟFΣβ²*§u•T—V‡ΏΎψςύOOQLfε8«²o…ήΩΟΟζγŒΗΩ*™ΈJ؎@½j« ½>šθQΐšΥOvω$/ΞΞqͺ”6y}# γi•‹ΡƒΙΩ8gΈyPεoaρ€ήŒΡΖΥ•fwψώ?β»όΎΗ‡W[Οψsώ‚Ώδ―ψ>?ΰoψoό-?δΏσw<γ(Σςi1ϋȏωq6ϊ8g³sτ¦|ΔG帜ΰοΕEΖOψI9ΖΪxΞΓ€<4ΟΖ<2gό”ŸŸs~ŠR‘Ÿρs~ώυς<Ÿπ‚Ι?ς1η³Ώΰ>)&9ŸΜ/Žσι¬8›π’—ΈpΙ/ι±hœŸVuofΈΜ§„΅EyΒ?ρOσ|V0ιΣΌ¬ς“γ1ŸςŸεEmι¬ψΒkϋ+^OσœW•|Ξη“Μ7*§9Μβ_ψWώΛ§εέΪA{Υrι‘μ:<{ϋτŸOχ~zQ\ΟgΩδYp~―ηΙ‹l‡^ƒη‚^.Όο„Œ½N;οK‘π~jή7RΕήί_^”ΉΙ’ή½}ύόέ«fQϋεΕ‹?Ϋ‘'Ϊ+ΦeSΆCοΪCόz›D¬φΙ«}Μj™ψ+Φ%tΡڊ~Z‘_,‘9#φNAίΌ ,jϊ5Z"Ÿ€»§EΰghIŒ,ΑΎ@§0.τκQZ ™ζ-o–±u7ΑφΕλΓGoχV°έΐΒV¦-ΆRGΨΊεŒaΔgμœΡ&Ω6cΌξΙOνy΅–#–“e…&1ŒΫάp6Ν3@|]ŽΈ.1PBΈΟg½Y‘ן‘;+“&m|ΞΏ;eψ›0ΰεξϋ'/ιaΐΞΑ8›TBΩπƒΉ°γ· Ήw)dΰ΅"Š―Z‡h96”Έ 2‡OvŸ>ξA†ŒΏ :*‚Δ*&Φ/aB§‹Υ―δΤaΊ=5@Ρ+Ι§ŽΗˆ ЇΓ¦ .«όΩƒS_κP={Νo6d’1S"¦0SΊγ;S>&z_Θ`Ζ:\κ@mB―‰£šDξtu(„ ρΒ$Lί!FE˜MκΠ­CΎ ο6(ΑΘυμΒœέ”`˜q2GεΉ”Ο±Ζά[{μ‘ΦKi~Υ%ό}_jrΩΡVήPͺ-yΊl•3M‰Τ.x’λΟ_mΙ’}‡Cπ·ζ¨Z85ε όΑRΉξΉ¨ —αgήέw©©ΘΙΝθ›άœvόΕ‡5έ0ξΧβιΊωh"κοLΤ‘Α„uλš6 ׏n3…l>nΙΪpϊ}HέΦbh›sγo?‡«ΏxQ X·N_ϭꯏ¨ά­ηPuX£°¨u«#e]έ6?Οh>uošC^9GσI·ω ΈQ‡mωfΝ’i]³Φšν§Τ]/ηΥ»β¬a!k(I$d‰πz"α»‘κ ζš(zΐ\3¨[K‹―¦ωgϊύSΜΙz¨κ†&†Ϊ&έΏ O’Ξλ±ΎΥcν&=:ΦΣc5ΉIOι±iΡκ1ι=ΖΖzτΊΣAk6Akd¬'ιΡΣαl6α¬cœMΞΊΓYoΒYΗ8›œu‡³ή„³ŽqΦ=8λg΅ gγ¬{pVΞjΞ*ΖYχΰ¬:œΥ&œeŒ³κΑYv8ΛM8ΛgΥƒ³μp–›p–1Ξ²gΩα,6α,bœeΞ’ΓYlΒYΔ8ΛœE‡³Ψ„s ³θy‘56‘0Vƒq±θW.aJπHΣwvΛΡΞA•M«»¬ήNλίCώ£*NΎΞπ„6ͺξέ£ίFΞ‘ζο†ίαv2/Η³rτά ΏΛνnŒΖΕεq™MOκOͺfO­ο•γπΛγβ¬ϋ©εY>½ΛDŸΤ₯G6ͺκϋ²ή1ψ³β’¨VlΏbTΨ_ώ kigΰ endstream endobj 486 0 obj << /Producer (pdfTeX-1.40.25) /Author(\376\377\000J\000i\000m\000\040\000H\000e\000s\000t\000e\000r\000;\000\040\000H\000a\000d\000l\000e\000y\000\040\000W\000i\000c\000k\000h\000a\000m\000;\000\040\000J\000e\000n\000n\000i\000f\000e\000r\000\040\000B\000r\000y\000a\000n)/Title(\376\377\000v\000r\000o\000o\000m\000:\000\040\000R\000e\000a\000d\000\040\000a\000n\000d\000\040\000W\000r\000i\000t\000e\000\040\000R\000e\000c\000t\000a\000n\000g\000u\000l\000a\000r\000\040\000T\000e\000x\000t\000\040\000D\000a\000t\000a\000\040\000Q\000u\000i\000c\000k\000l\000y)/Subject()/Creator(LaTeX with hyperref)/Keywords() /CreationDate (D:20231205084654-08'00') /ModDate (D:20231205084654-08'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.141592653-2.6-1.40.25 (TeX Live 2023) kpathsea version 6.3.5) >> endobj 445 0 obj << /Type /ObjStm /N 41 /First 362 /Length 1868 /Filter /FlateDecode >> stream xΪ•YKsΫ6ΎλWΰXe¦1 ,^žLf:Ν­ΟIΫS“ƒ,ӎ¦zD;ΝΏ/>”@mίή―ϊnιHY¦κ7;―¦‚ϊπt·υJ™+Vλώp‚RεΚΗ§ξ|†NηΊΝΎο;Ώ@ϋyqσσf·ιΛΈ‹K?/ήΏ―ζ»=TTΦϋξaυ΄νC…\}‰<ή_‡¨±’ΔAšMΎ²HΒhΡνŽύ78/ΔυΊ±<ΧηMΏ9μ=89£}b|έάχ_ΌUSŽ«‹ι'yΤ–9xμφK_V'7dόn΅QG}l–—UaΗZQPƒΝRΌ 6+sεuxΩbλ°ΧΟ&~`VηΠPP™‚jnΦζΚώΞyΔ[ψURyyΣΜ%;c“₯΅<»ΣΚ‘Œ­g›IΤ πxbώq \¦­C$DM‰₯;tA*σΙTθ=nϋSw„YΚv@Z^SL-΅ς‘m=ςpΚ{ ΞF½Βγ‹ΩΥ°ϋw΅;’GEΫΜθ½=c(ΓQ™²QJζγv§έͺG€"9ƒώγ›U€ϊ€“†Wt~a±ήΫΝή'¨›ͺΦ/Άσωπ+λ_HήmέΗ“Ώξ SLo4U,ΆΫ/h¬¦’σ ‹EώzΪΰ \T΅ώ†χB\S¨xx‚Α,ͺ5uΈΣΪθPXw`έ°yΩ8Ž«GίΘψοΖΝ„1ίA‚³‚Σ|ŠΔΛΪ,·AΑαtX`’σXB5(r‰/’±DΑŠ%Β½D½XU€,7±C ‰$ΐήR,h02c\£LΕΨ9V‰¦ŒΛ,―Ve\\`ω$AΖ%Z£‰% GΔ0঍Ӄ%аs4*£’πSγΛ#πՈaά b§7€qξόόfhC]’ :1Μ ]•h]mRί¨„Ά©̌έv•‚C©y›Μ/’7Ω0œ ™XΟΧΈΆipΧdTYHu"υ=š ˜¦ΈΡ«ΌmκΈυrj]Ǎ&ηY1ΡGœ§Τ’9O©Es‘fŽ>ζ"σ –UΝΔz8¨o=W)¨Ώ’]€h™β –¨₯Τ­K€κΈΝrj]Η-@-₯u V¦$ P+3ˆ 6OΤΚ 8X’uΰ@3±žjU »-‹J V₯Ε'P«lΤfϋ•ΐ’Υuάv9΅γ&PkΣ-Dώ`JG )\T›Tͺ!Ν27¦…$ ©©š‰u8F–L[YβΰjRάKJ —”p) M —©¬βžŒpIs°%ξ_)‡Μς”C f³ι#Α¬H™•`V€uΔWυ"'ΈηΈ±"%Vω[AJ–±*³±:MQX¦¨@’Uά@3±žfMΚ·³& f³#SΩl β φΣ`ΙΆuάj9΅γΦώ»cŠPƒZ›"Τ ΦfA­M‡΅6pΣͺΧ“£^ΟuŠΦp•7¦ν©-€i§ΰΖͺSάΖQKmZGά΅t}~λΙI_˜&ύκΞ})nόλ#•mοH=_œό΄ΉwVI ο|)όΐAα£αg ΫUβΓaύύύκΤΏβg~† pRx)Dα€pπ‘”ηuγάWψ1pΘ*Ό^Α/gΓηP=όn6|Ά―όςœήΓCœp.R8 )Lͺγi>_q—ŽμΡ}€Ζ…W…QD3ΓgzΈΖ›8Δ―Ά(τ0Ν5θ|kι@vΈςSΈδS˜g^ Π0Ιj­Uπ‘;;3fέ(όσΫ±c7?ϊΥφπΈΈωέριlΘΗXάόφΤϋ―ΒNΒΙ°‰hƒύ/‡ϋξζ―s7»eΗnƒΟ– Y ΪqΩBd endstream endobj 487 0 obj << /Type /XRef /Index [0 488] /Size 488 /W [1 3 1] /Root 485 0 R /Info 486 0 R /ID [<872911280B79A6B93867AD468A7A4D65> <872911280B79A6B93867AD468A7A4D65>] /Length 1165 /Filter /FlateDecode >> stream xΪ%–ΛOUWΖχΊ"aƒ\PΔΘε‘β |‹ ’I“vδ@‡vΣi“:hΣI£5iΪ€“&έiΪQMŒ3Ϋ4i€ΆM{~_'Ώμ΅Ξ9ϋξσ­o­sCαίR₯`‘ωe`΅\¬ΣVJΌ‚άα6PA–ά}Βν ’\ ΉMΒ*PMξ0Ή{„5`Ή9r„;A-Ή δΦ λΐ.r7Ιέ%¬ δ6Ι­FΠX„QΏ{‡° μ&ΧEξ6a3(“λ#w‹°΄’!w“pΨKn‚ά Β6°ά:Ήλ„ν ƒPΊ\#μϋ ₯ΛUΒ.ΠMΘ!Σ*a8@¨ V{A8ϊΑ!0†,„zUζ08yv,‚sΰ<Ž‚p 'ΐI0 Ζΐ)0&ΐ$˜Σ`Μ‚9pœ‹`ΨBΕ¬4Οο g ΐ9j$ΣP 獜]ΠΚ7³t‚ap άa+;ΐ>φΣΞ²Ε’…κτ»ηΉΊΔκΈ.Λΰ °R\₯PTz-τ}¦ν±žc=Ηz~„ΧZf5 0—c.Η\ŽΉϋ8nςΒ§o΅ †s ηΞΗΨeˆ•ΆΒkŽU|άΒΘ?zΧ9s\ηΞ1œΟ*νψΐ©₯SZ_΄0&:Ύw,ΰXΐ±€cW-W-L₯ϋ¨ΎS}§πNαΒϋη“ΑΊ……w¦N₯ύξʱΆz?nU  +™£± ο³Jiˆk¦Ž;3"Φƒj k_θ±:° 0ͺ’Ζζ‘&\ƒ…wFu_δC+jΜ΅ƒ& ο}¬«i{€YΠψ:K‡…GίλfζUd@ΕΐXнΰ 臀ŽΑβI0hαɚ6ΐχρ8Np4Ξ©yd@ET\gΑ0jαi6“` Lƒ0 ψLΔΣlzθ,̺Ȉ|¨"GΖKΰ ˜·πώΧΪώ2ρ‰7JΌ`«‚«ΰΰcW,|XΦ³ΧΑ pά·,|ς«bšxhšέ|™’¬’‘ TnYψΌ^!φIΨ'αœT ͺ cmι*UH ΖTcαΫ επKΒ o$L“@ΐ‰J§²…οΎΤϊ⃄Ί$}…Ϊ,δWΊΣ$†o'«ΛΒύΊ@-΅Lͺ₯΄§ζi¨“©`b\'Š—&ŠgŸιΩI³r»VSf›ZM›­ΌΥjΖμSύΟH³f?~€ΥœΩ›―‚Ωoίψύ‡όRΰΟηώ~­[TAͺšΠ4+5΄r!Ӎ93rfΊ1£iFΣLKf΄Οt^¦σ2Jf:/#gFΌœ93ΆΝ΄ZF،°™RdΊ1£iFӌ¦Ε3½•ι­Œœ™οyFӌ¦™ΏΩ3UΝ.c½Œ 3vΜύVͺ}QΌQΛ³πœψέ endstream endobj startxref 137172 %%EOF vroom/build/stage23.rdb0000644000176200001440000006503214533651773014500 0ustar liggesusers‹ν½y|IvXA‚$€ΰΝζ•š$@ͺΐ $A‚$xƒΝ«A²§»έθDUH1+«:3 Ψ3Ω–dΩ–-[>$ΫΊf$Λ–F’εc|Θ–mΙ‡|ίΗzοΓλ]ο©Υžy6_Δ—UQ…,‘•»ύλδχ€JT|οΕ‹ϋΕ‡Ϋ2™LG¦sγ†LΗFw…tf2 w†?οqCψ›­!fχ,ΧςΜΐ*s+†W~΅²`ΉgǍbΩpˁaν€>X4έbψ5Βίnž.ΞζB(‰ίΨuλΙ£Gw?o~Ωχ ž5葟Ψ'υ'[Η†Ώκ jήv,žφίo4½²ρŽ[h’°ιΕσ»#—šήΫbΉ…rΡvšJ φεMY²ό¦―έr§φ ™ίτ[;όƒΐ,UBF½τΑΧ~Ψv}£ω› ŽιGίάeσι“gS‚¨¬ΨAL2[¨0ˆ[3΅%―\.妛UΤ±\lzs4_ρμ₯Pέω TΙO‡ά~εψΟ>ΜOΟUm§xζάθΩ cηΗ.\›Ο³/ε,"&[Ώέ¬ξΊ†Z½RS}τE εΏa³ ΝΦωόΞ‡°’Žo7Ω͈€x轍α{ίV’1tάxκX¦oq‹/– Υ’εf`—]Γvι<ŠΈΫF³o ά²„C‡΄ K—%?, »x&«Œ%žw¦oΖP"χΤμ†ά­B)6ΥM3Ό¦Δ&»ΈςφτBΙχˆg=BNv p;d₯’‰MuσŒιΨ¦ί"έ ΐΘjE‘₯JΎW@< idpΪ2‹†i-Η.ΩΤΰ’㠝KPΨssŽ%IxtDx ς©υ؁Wƒ6AG„§!ŸN__”|V@<ŠϊzηmιlšΎυδφ>›‘‚ρhK«ΙClΊ ˜FY“Νƒ™mVšΞPΨ(kΩaπϊdL_<|(Ok+¨l­ύCMZ» £PvfY72€φ|ϊΕyjΫ@‡p7δέIQ V*ŒšZ©uƒNw’ΤφpjΎεX…@™›Π€gφ@ή£Ν­Η0μ’2'‘QK°ΫnώK»²•§Τ ½™${A}†αΞ–ΜW!§)w^žU˜τ5Κϊ6οš!₯ΒΠΐ@Φx<90,OmG¦ζ΄ωЁ}^-VΘn`&d'M«Tϊe}Z…r‰†@DLΦNPΩ™I―ΎΓ-~Φ*U‚•Y―Ό¬ξ]ΡEΛ ιgς;ΪwFΰΩ₯Y b‚m‰₯¨IlΏaX~Α¬X³Εr5μi*Σ\jf?δύΪτΥθΝ™…—Ύcϊ‹!Γ»“Ÿ)Pά Z„‡ ¦xΨ0œrΑdEW΄ζΝͺΜς_ )x“}ΰExςamŽ» c‘jω>\π™Q…†APͺXI4™υ†ιžUQ6ΌΰB˜œ£;ρš-WŸ©ΆβYšUQ«ΰI2G Ρ&y,lΏͺ₯Ω`Ρ ‡D’ ’ŸUx7ΒciσŽ€#L¦©Ψq7,Aί°\Z9¦•DIV'ΐδDν}Υqt\έܘ[x-[5‰Ι>ΰΘJΉͺIΙυ߁œ@W&«’ƒΐCΥ—±Ξ27χϊ¬ŠF‚œ†FOB>ΉNCN°ŠΌRͺ"£ΐ³Ο¦£SΐsΟi+dλ)θδ<πδK Φ’ΧvEE)ׁ7!ίLG)—· ίVΚ>cΩv֝¨ε’ΨΣqVdΫ<"uψ1䏡ɅΓΥBΉT‘QΏUΜΌτΣ ¨ \ϋ²ζt’ΓΘ‰™SΧbTΖσωV€Zμ– 2Γΐ<δ|‚΅dys-’ΊΗ£GΧΑνRϊg€g!+9ΉXl!ψjJΉ ΌωF:J9œ„<ΉNJΉ ΌYΝΫΔ)eσΌj=y|ωI:*Ή | ωi2­‘¬›% ο_@~‘œϋšWͺ)ΤMκφ@VZ“–S‹°R\ƒΦTΛυψφΠ(–—]§l©šΆJ΄ΠΊπšj}qP.«P"ί|Y^ηt+ξ†ι’%4ͺ²„‡Q„λΈcλ’Ž0™1φύ‡βdν₯²Uߚ―:Ζ|Ψy΅^™₯ ©œΚ1°όΐΟΟΩdŒκ‚kϋTΖΎln„‚Μά‡|_Ώχ$N=feΟ¬Α"m¬T₯ή“°½Ύ ½§SCΓ²}ρ,L1‹V5GΉΎx4i–m”ukANA#*oao‘ϋΠ~J1ΙFͺ§dssιϋJ>/ ž΄iˆgτ\ΡZk ›ΨξDIj€œ. ΦΎxFis₯‹O\‹Fτ₯²gΥ§’}ς—EZΑώδ°‘΄œ’ΟŽνΚ/}ω^ΰmΘ·΅ΥΌ•šξœ15oHΙCΕ„[!oMΜvϊ±¬ΜγΕ<΅#£ΈΡBΞ RrΫ€»3΅ν₯šŠ9ΕΪ¨ho»G­0Ϋ‰BΫά½r‰}κ[QVάΰ5ΘΧΣν Μ¦ΐ™€ώ5蟯Π?γτΟ•馍hήZ­t΄z8y>ύ¦’_O»›Ά¨9£d!/¦ί¦PςΆ€x¦1Šˆ°=MΫΦΪwIz4QΤμƒά§άΌk c_ΤNΰ~ΘjΫcΫΪς&λIˆΚQΰd₯n»œ'‘δ!j±όT₯.π8δγ‰idΫ[ͺ’’,pςh:*9dM•μγkϊΊ»0οΛπ>dύQpΨQL΅δΜΗHΟxœεJg˜άvQi_|IG˜ΜŒΗFωπy$N(œ€]OGTΆ{ '9Ω’6Qrΐ^Θϊ3—²¬;o{~`xεeκιŠΣ6|v²ΚgšΨ'άΌe ŠXχοBΎ«Ν~ŒU²,› c\)ψΓ±/8Uš ‡€¬ƒO3lσ^ψ²Κ μμŽp ςΨ:»tβ2œ„¬΄r#g„”άEΰMΘ7΅ΥΈ5«hUDγπ=Θϊ[Κ.ς‘ρ/ΤB4LtžΙž Ÿs†€]½`J¬7pLΫΥ I――«Ώ„Δ “wυoμχ·¬jΔgp'δν―j”\'Αp8Πr‘¦8Vχžx+°d:UΑπ―/kTDz7πδΪδορЉ&Š» x·hΔθΤ ‹[ ›™Λ0FΒ{οigf(–rΉg‘Α’e;†„λΈε ’Ž0·²υ‘νΣ6rcH’ΞUP L~"΅γρ€¬WΉŠ ϋ!+uτεΌ %· ¨εΜϋaΓ £^ε’v3t7Λ¦G―yUcϋ³«(kRΔyπ>dύ!ΫȖ'‹ΥRid6‹"Τ;dΏo0—;#k_Dε πδ#ν·/Jnπ(δ£Ϊ:’Ÿ†‘τ ΰ1ΘΗΥΘY OCNaυ˜’FKιϊ«Η‡Y—ΧΈ]­86;m¨²›¨§ OiSmα…"'T2‹a‡QΜΎeɚΨ5n G!&fbέΒYJYK#F“SRrg€7!λ·Σκ.ήρʎ¬a“[ΐǐk3κ_ ϋkΑbΨ‘/–]KvΉι:¬…Pk’―…~Z,7’έ YΏν—ξRς»Δ“6(ΫΈάĎkK£zΫμΛθ.7m^nb»)€‡θ“°Β6,7©lG˜δΉc8ImΉ‰’;LnΉ©“Žτ*θδ]ΰIΘjη™"Δξ‘νΨoΉcn-nW€7 ίHP9ŽνΎδΦ+άt‘bbΨe·yžu‚² σ@­A±ίœ rv;Ό YΏήeψ«`ΟSWGΡeέ1Ί|Ym³Ήψι6ΗΝ₯§QnςJΐPi—v+u IGΈn³³·‘8aβT\m¨†0Ε…8J3Sί˜ΜBάPθϊΓα †Q<ΐK4si—*Υ Ϊ’¦ ·>ΰδδ―ΆΦβ6©(oxςt”wx²ώ:δfšΒ•φ‡{ΐ‡jsΉWv…ΙoΫ₯]²•π‘m²‹^ΉΊ°XΟ'ŸiSκs dΒ!Φ’εΪ;o(!Λfζ·†ΙM’ίŸ7ύ`8kΜ…”imΧ+ΟUύ€-β²}!E:™–υ±ΕΚθS”‘pK¦ϊŒέήiNΆLž Έ²ϊ¦"ι2yεπ~’ecΨa™Π¬Τ‚|‰Ό/ΰNΘ;Σ+‘i”Βt’%²έpΓq«₯9ω™°²F΅y†Bx–pΠ5όΎΩy&ΰ:ΘsΒσ„λŒˆS^ ƒ²%ς\ΐu¨3/P /6‘ω°DζM•ƒ/\ω…πA’²ΝΈMuΖ €kΜφAξK―8Ύ„"ψR’ΕΡgJ΄8Ίλaq°εΩςψHΐw€W£ >NΈΚΜdθHX(tW•l™|, ^•?•G|εa2#Ι{7W’»“²lζŠν"ζ‘ΡZP5γ'ΓΨ~%bς³]3ΘarKͺ{J‘Ι› »M†EΆ]4C’Ϊ' C(\­₯Xšη·6Q{­κE«Iβrh@6T8ΙΝεPr{Η «€ΕO³΅8DΑ’΅b,[žΕ‚yV©ΌdρέΗPdΛ €ΦRήπQ¦vΝ‡&γNšB•dς)L‡0ωΕν½«ξKR:jM亁δμŠ’Ϋ<YίΆ°•n5M ‡!'¦©γ|Η¦?Δ*~.^o­¦ΊΧTά(π9δηι(ξπdύŒς“»”ώΐ/AώRbΩίΒΊ'φλΈxΒUœ™ΝhΆΒr£I!ΩΘιχA(ωβI›Ζg(Υ»BkoΨVΏϊX’Ÿ™α‡LΌί•QμE3^Ϋ›x]β»S|l£h Q0Ος̟3>X ?S~ld"}υΊχ—lΧqLw‘J=5Sϊ@κNΨY}’£Yι}Ŋ³βs½ΛΗΡ%Vϋ' ŸhΏc¦δvOBΦΏωε_Ξ·<„dΪ岐3/δ7 Η!ΰ£L-dwB άXWZu”p+δ­νWZΦK˜ά>όqΥ{N6X ½-¦qƒ¦q›υŠΕ,Ŝ1ιΠoa‘υΊeIΌ»·!ίΦζΏ—έIΫvΊ΅”.½¦c’άŠ(OΒ½χ&fdonZΪ: <‘©έΧΤn[£δφOBΦw§xΈ~³P°θ^“Φz,Ο.DΆ[΄^εŒg–΄“ žCΐχ!+mŸZs·Ιžλ]\αŠ%IͺSv8y,A±οd—@_ƒ}π(δ£ͺμW}sπ"d₯ Pr–nq=3β.iZϊΆχkΎκ8FΡ hCΎΑ#Θ6ΡΊ |²ša7Bέ½Gv“$6²"D]ItΧ{‹‘)$«_DόTzHA_΄G@„¬>ιΑ’ιΩζ¦Ξ©ο°nrτ#?—DΏς‡£ “,2Λ'ΟrVΕ«Ψ°Ba'©f~ŽΧVά²!δέΓqY?σ Gx²Ra9?³(${²RΏK―‚Sς'Δ“6—(Ϋγg:iΉM’™“αή…p[FχΎρΝMŒF³ΝH΄:μVYl…H†φŸ  Y₯Κb‹dO/@Ύ ­Έm¬ΞW(v‚B1Œ―CΎήώΖΛ βδι·”ό€€x¦α’"lOγ΅Ι]γμp+jε o±υ/Šj¦d<2_Ω₯j [jZ1jΉd­™XφG ΄ίšΛΠar.}3’δσβI›F%a›†|)Ιλs˜/‘ώ―³‰Ο[MašΙ˜ω0 [αGΟ h˜TBd\Δ™ζ₯ λΟQ+ Ÿ@~’ߚπ~b-ΙΗƒΊ υβEE,Ώ"BŒϊ!+φ“λ1zP α!Θ‡΄t”T‘Gσ ‡²Mφ€ ΅Γΐ+“<&ήΒk~.‹ς9mwEΙOˆ'm>J Β6υ>―–ΩυpxƜeiGΰδ³ΠrαžΘΪ5‘ŒkδCešΘφΟCN²ίΒ¨¨ŽPΈ‹$mk’δΗΔ“6*J ΒφυfZ‘[‚)φd4n'Ο€\γε “ΡάodΞv1δhΟΧΦa»?³λUΨ' ¬Κ<ιή#Ό ωΆ’³ (^ήΝ’ϊ4γ8+Ρ\€lΕ\† ΧF·»b. Ɏ@VθΥJ>' ž΄iΌB D؞ŠΩG³³V©¬ΜRΠ5I’+¨”„{ +-a1r[šΘ=[,W’1η˜ξKŽ…οΓ—ιε‹qvΞB3ω:qί|YvΤC·₯7›½†² “ο‘+έΡFTϊ»!+-˜ΚuΝ)ΉnΰΘϊ €WΘ‰»’Q‰wΥ␅D2geفΒ0ί | ωibͺT»κμ TΣ/2š]E9]~K&άy»Ά.Έ.ω‘ŽΪΕDu Κ‡M#~=ΐd5οŸΠΝ6Dδp,S»6/ }ε!λ_§v0Ί‹ΠCu ϋ;e;)Dμpςtϋ;)+‚ φΣξPςΟΔ“6― "lΣθ!μ₯—f₯;'_ΝpοF˜όθα :'Nt€‘MσLΫα‡Cσρ+fΑ2†&Ÿέšš2Ψ>˜σ‡ΙSQΆJD,[ς_Ε{„ŸBώTΫ³HcσΆE½.Ύ<]1=6qeΧ%)~' ‚π0δΓν―‘_’=ωHϊUƒ’?* ž΄i| %a{jhε̊5ΛÌHRό.ΤΝοΒ—wew²ΏήΤD-w»lω΅P'Κ'Ψ™F™ <|₯t]Άx‰σ^ΰ-Θ·΄΅ΌOΟΚ#‰ί}ξƒΌo}GΏ…WF†Η ko†’Ϋ€< ­¦CΒ=ŸΖɁ“B41YC"fƒΐk―i3άm>Ÿ»eŸ Yž”νhύVX‘ΦnY97ώ]B²zgβΕO₯ύ'%ΏW@A{½v;;'Εοsψy)Iί <ωDbύ­šσ–>ž8/CΎά~OɝŽCV *ήX%h3€`f²FEl@ΦߐxΪFwJΧ ŸκFΡrμ’Mk5ΓγΝ_«~)μλ{ai„Ι…P9fΐ,£j+h8φKι ΑΏΔBVλxǞυœ™q[ϊΰΞτΝ„NOA>•`}X²ΌΉΙΐӐ•.HmlΉd»”|V@<νξ>|·`븘DΙη\§Ε€οC D؞ξΓf§\0₯‡Ώ#Γ; „Ϋ!+Ν ΗξΎΏϊίΈκ›»€G +MrΙυ±~Œ‚P―m?½ΐξ$fG»Ψν@Fyٍ,ΥΠZΕ ΩfM²wYΫ&Κπ9dύσ{P9)€ρΊμ†γlΛ K,†šRΨ«)™ήΛ°‚Ϊ h&ΈhδKaή€Γ’?ʞ0ΉŽΨfvέΈl»‘ώοΖΟ8Ά»B²]»oY)ω-βI›ΖοA D؞ώχ›΅’χPa_FχϊεζέγkœQ Y:FϘΣ4―x%€t OόwίƒόžΆΆ;ŸY£œί %^^LξΤjͺΕ·N/Ω ΦΠ/―V^Y£…)VΚ„1ΐ’Λ ΄jΕ[NγnΰΘJϋ/εš.Jnp ²:zCAνdΖΕ¦s£AΦ}Ÿ‹ΐϋ•fδάηφ3y*}ΏEΙ?OΪ4~J Β6L'π¬Š$·ΜpI¨?>jžT½x‹χ­hΙΊvΙnj$ί9Ι(7]θβKI%ς}ΐ;υ―šz׊‚8bŽφ°LHGqϊύP?ỐίMΜ±*-š•Σΐd₯I9IΙζ!ηυ;…K‰Α(πδs‰ιDmq^ƒ¬΄`'―”σΐ됯λχό•f‡ΐ;Υ*υZΣ½K^Ή\šεΎSzΰdψ±.ΒaΘΓ ͺiIƒž ‘ΉοΰΘTy―ϊζ.ΰ)ΘING·0ά?ΐUΛπ4dύιθ½qΚ'!OΆΏφƒB‘ά„|3ύΞ%K@ γG·•{,Ί];Σ7\k™ΟΠS”JΫε'ψή>:oζ›NsΙζεΓLοBΎ«—σf5(‡eLΧE:+΄λ*nψΎ+~ω1ΆΨr=±‹Ωe l ΟC>ίώΘ Ιc(J~L@³¬aKΠ7P) OAVZ]J°έ".g€ §°γˆ’; ƒ¬?¬y‡G;!UΉΦR8R&MΉTΧeM‹x]>…¬fZ±un–ΥΤOfΈ»$ԊΔ/§©Ÿ„‘vCVκΨ4ξ 6†„έΑΓu½1ρaTXΑLv=šcΒρUγmΧΆOCY΅R&Ά§!OkgfλB8ͺR 8ϋS(OΒ­·&fe[ίΤH·4΅ŸΒ‹„ϋ!οoΏ©ύ,›πδιwf)ωwΔΣξΞμΧc8Y)Ζ―^£JΙOΪ4ώJ Βφtf·ηh[¬gULΫ“dψΣξƒ wBή©ά•νkb6vίt‹,φFy>κΒς#†xζiΞZ4—lšDΆ}…‘4qί|Y‹ι~Λυ«žΥ@›9wIvz'άYΙ Ε΅¨ΊφηUk@Φ;£ΐO΄ί;Rr€'!λ_UdδŒL/΄ ίΰ΅€βΎš΅`sΙZ±ήƒ¬ϊ¦›v){U—%τ'`8„zύ2’#Ί~νg2Ό]‹"Œl#σ³(ΒM7ι{wC²~Ά ρ$Υρ K­K¦#]΅‰Opd₯ΐ*rU›’λξΝhΔi(†‰qγq™_x‡ͺ]¦ΐVα₯Ÿ Ž•²[4ζLί.„}lΫ,·`QcΓ[Y»&κϋ€Ο +νP³λoΒ–ΏΉΎvύΝ&LΦ•[¬oΒ” wCVšu“3λoΒ”Ώ™ΡŒ€άxjέŠz?,ΊΤπΈρΘ|Ž©ΛQοkπ’ͺ­q±0ΞVύMTHΒχ!ΏŸžU,ωηΦΧͺ “΅κήζ“fmϋη`Ο„ο@Nr΄ΦΒΆφLx²ώhνψ8έ‡"ΈlΆ€P}ŽDA}‡€7 ίHL}›ΉήTΤvψ²―ςj›>‚¬Άt(~Ϊ!νX(ύΗΐ'Ÿ€ηX~ž+’α::–ŸoΒdK75—K–η+t‰Qpd₯πrζIΙuχCVρŠŸφ£…€Wή@*θιpςπz{"3 Ό9…• Jξp ²ώΚΕΦQρWά€φθ€ύ ±ΉΌ ωnzώδΈZ&ηOFŒIcΎκ²νΘγ†Y©8+F‘κεRΓ¨fΘΚ-δ€Ο²ώ‚€g!ŸMΜ¦χ σŠΖ„Q «›j”žŠ'z—O!?mΏ•Srη€οCVλψŠŸJΟ–PςΣβΡ€1HgβΈ#΄Ω †¬8lπއ)Z”tύϋ“0zΒAΘƒιΥΏ_D’Ώ˜hύ;ΦΏJΥσΌ^>¦[vWJ4UΥΚ¬αΛ‡ηψE³•’ή­y4v·η˜ξΒψΈιΟFl₯ΘΓ‡ΐYΘ³ VΎ5Θφsϊ{S§TΉ―ϊζsΐΟ Φ~—DɍMΘζ:tέ)ύ9`r!±μoaUά~W?ΓΝ<σ§€κkbβ§N™’οOΪ4ώ4J ΒdJγ Clz l4­ͺULŸ"Ι~iιŽσŸ/Β#$Χq^sέ²e—‚Θž‚œΒ!wJξ(π4dύCξ Ϋ.ˆA˜‡œOΌΪ³T<||Ι*Μ†nœυ7€[ ’ψX„\L§ΪΙω‹τΘ_>‚¬4ΩϋΝc@ ²•Ž η!Ο―CDι/!/jσΈI‘Φ(f΅*pyΏπ“°oX^ Wυ°~sm­˜ΒmΛζγΟςjΓπ&δ›ΪωΨAAΏ]v]\’“dυ-0ωVν}†‰W‹>όO ΙφCV:3©Χ¬Rς;Δ“Pξ·ΝDM«“φ1dϋΟe4štz#ω“ΏD©C@υNφ#ιΡωσ~β!B<šΖςξ44kqς‹ό6 Ϋ₯(τF`ΟΝ9Bώ΄@ψ.δw³§ν3EΛ/xv₯>BZUN”β_Τ±(}λω‹°˜υ¬ηΚΫiΩό₯ ί !Νb98h,XAΨaΩΕ ―ΜRΕα7’H3όKPar»!OΩn₯Μ2«Ύ:bπTΰ94P ¦ηη ώΐ°4αΏ ’„z›ϋΕO»λ„₯ύ2Xόr&ɝΫςΖφWv„ΙΤΑ]ƒσV,8½ΙŒNšΩ_BtαΥbk–Π_CΪ&SB)²Γ/W=Ί`qDη?ιLύ 2BψςγίjU°ωΥDΥΎ“y“‘z…•χ!dwfj½MbƒΖGΈχΐt|˜ΚΝ7ŠΆg§εIυ–DΘ@Π&Ίw{dMOό7Aˆp/δ½λP«ŽPo {s”Φ}¬~-Γw›F¨ΎσTότ9™‚SeW<«Dχ(1γ’₯χ·QB„ Π¦χ…c[°ƒΕκ\P.ε»ΈB»¬<{'ο™Λω’i»yΫυƒΌυ* °,ω·±ΔVYϊ;Θα#ΘΚσ13ΕE:νZλφώRϋ»Ϊ¦§W(ύΥϋ½z<ώΚ ΒdΚγδ ρΔ«9Ρ¨Iς]sZšρΩΤΠπgl>ž„¬ΏΣ― SC―²+3ξ™μΩχ\φόŒ;0,οch‚άΖSB-yόC€ab=§[8l9<šFηI:S!LηtqΠxjϊ~㩜²Žξ‹Φ+«ή ‘Ο6›=“fΑ˜π"δ‹ΪμΟ7χ²”‘Y0 C₯rΡrΒί„,X¦§`Υt ΅BM5PΟΎ‘ϊ™¬q.kœ9£@ωŸ‚&a²ΪbΊ^EόgH;Βd*βQζa©ΓΚf›k•qΡr*aΣ-ΝςŸƒαQΘG΅Yžyƒ‚ύΐτ–Z„‘’Š»ύΰJxς™uΠςΏDΪ&֎6 P<‹mοŠ|ΓcΙkϋ_!arνθΔ«sΨc€=_άQ‚v7ΧΠ°‚ζ#π&œ€<±šΧH;Β€Z~«†Φ …|Cϋ##„Ι5΄·…ΘpY>€εw,αŠfΨst ‡S.ΏdqΧγΜθθ¨α•—εmύ?}ΒΫΥ‚ΣˆŸΎ3H‘ΛΥ…Εr5ΰW膃"ί Z.YΆ$ψŸ‚α;•Ξi4ΌQwά]¬°NK-jD؝aqŠ―Yͺ˜…¦0-O“ΆΜΖκ„7 ίΠΞFuߜ{^=&ŒbAaFδ?QΒδά:8‘iG˜Œ`¨Ω±ύ@82ΟΚM^Λ%Θ&7Ώυθm΅L™’_πKΖ‡πqν"₯–δΏB>΅f#4ΰΏFΪ&Φ’ά₯U$Ϊ…–δΏAF“kI6IšΜΏƒŸ7pΤ$3T³^3;Zon4{67ʍ—-“{Κ*x§ ‡ %QvBΩύ[ψ·‰–έαzΩΝΌπf”ž|ύwΰExςamŽ»B7Κn ‹VΖW=Ψ&·²"XήWxα}e΅ε}EΑςώ(ώ»D-Oή%ώH;ΒΔ\"[*C—Ν7Μ‚Wφ}£vKνhaώ­}₯t¦ώGd„09—8Ξ'δgηVf +ΞκEό†Ÿόh%iD₯šύO M8y\;½ &υ?ƒa/δήu0ΩiG˜ŒΙήΕ%4Θ:ω$₯83Ιτ›­Ÿ…ΆζΓΎΌmΉτqΨş³έΦΫ[fζEοBΎ«™=άm5θ[ή |χ@Φ’ ―π iG˜ŒΒοβΓ΅ψ΅Φ«€:ΉόRΆ€` Y6ψ™]όŒ^φ¬ΟΓqu`˜Ωω dπ>dύ`λωΑΪΐƒξfχ¬%ΛtΈ9Wέ’ε9+4Πgۏ”v.όο`J˜‡œΧf}:,λš'm΄Χ¬a©uε[HšΨ, “»Ϊn0dό.ηDΔιΤYΗPύΧςLO°#Τ;Β'~ΪR’¦ς!yΒNȝ*Tb“έ:51I’ϋΏ­ζ&)ωρ¨•ΐͺ·ΨF«ŒώϋνŒJΦιζέΗτ»ϊΫ%Φ ƒf31ψ`Oh³ώ­‚ώ‚ΠηQˆΤz2ΆΔΌέρU{iOΣ 3ΣΒ§½MŸvΞζ\γηb© |h#qZΝ'φ6Νψ‘ύ /oy©kΖΏZ,α΅ξ¦WΆΜΚnΑͺˆιvΆ$H‘e 6}Yνΰ]흫8•¬`±\^Ω“ή*Έͺczο5qzv>ξ˚Ύη/ސAa+ˆX¦Ν ž{+φ’{¨½Έ©…*7Ο<³^UČnΥeρMΊμš™.>aχNΦ^ΩσM3•— _³κ3φόσθ―ΈεŠo‹ιmŽMΟϊ\,Υݍfέό4• ‹O%όuίώbγLA,ΚΥ²\m=Ϊl™ρ-κs”W›LΛdλ—ŠωlΚ‹U quΙΗδΊκ› βwmZόͺΏΪͺŸ6c±]ό’Fήκž ύ™εQ€Ί˜*±κK{f*αϋe―dμZΝ–ucσL`ŽΥψ•MErιble©‘Κ·.!Σ±Ν7YΥιΒσ•ŠŸΡ5K³{Ζ΅–ΓΡPΙtEϋVqŠε†*Χ›­ͺ'*~{Λσ6Ι†uΚ-4VΠfwΝΞtΜ‰YοI±ιπGKεmš)Ψk:ŸθLV텝-kJtGcνΥ½o‘ͺ”…&’]-uηWηVΏηΕΆ…Y-YtOxsGΜ›ΡAΟΪK=±™γ]η·¨Y3Εy‘]+S3NΛm¦h?«^κΉ+₯Υί·ͺ΅o6­ΈχΆ’y•i?κ&QΏε³₯Q™.­ΆϋfΛb'EίΠ ‹―e1/ &“iιΆΜΌ΄V–Λ^1σf5œ΄knΛbО§b›9ŸUmas3Β|νjrM―•›+l³?ή43Wvbτσ]f5μx«“\νu-·ΨΠ1\εN¨’<;_pLί_x³ͺϋ3MΉ)i{~Uω6}nc}S†7…νpeρδΒ¦TμΉoo$ΗF‡[€°k¦b^†-ψmΡ·vD_u Ε@±Ιi΄Δ0οΆkCηβέζ½Η/{–_uΒ+fΩ•ΨsUΫ)~ςEP.;ώψψψtq€f?40xf`ψ«1#Ή£œ,ΓyΘσυ‘œ8’ίmZ50ρόξΘ%ώαζζ3όχ[(AͺΙ&υΤ©ΡΡοkΓÎΘFŸ>y6υa!ˆŒύΤK~[sδΪsžι­δοz‘?λϋK??›~Θΐ;J~ώ|ξ܈ι•ΖΞη§-ςΙϋ‹¦gε§‹y>ΞΜϋ+~ΨΆε’owsj£ωŠg/…MH>(UςΣα?·_9ώ³σΣL g΍ž½0v~μΒ…±yΎQΎΡnV†»ΒΡ0ŒΉΥ+«ι1α§ΣJFWh~εΤ*k0+ϊlφιτΠΰ™XCΪ γ!Ό ωjݐŠo= Ρ’#ψΠ|n}(dtΏzF£kς…κςΥWΏ ±Β_πΎϊΥ―~Α…˜<žEΎc–94σΈu†9€XFΙθ36›3‘ψσ_ ž‘œ†Ζdσ²F8yB5›M­ς­ιΙΗl\šDΗΨ¬ΝΡ‰ϊξάτHXŠίv seo!_8fτ ³|ΗdYXΫρ‘‘ε°ϋδ‡,¬dLχ8~g¨I#.WΒQΘ£:φ7nδ%όœ “Πκ5ω¦ŽI[7rΒ±‹Μǐ?Φ-‡·^φEε3φ¨ΨP<‰Ί!_B6OΏu ;β3ŸBώ41ίVο*έyvkzκισ©'“) …Ž‘ΐ‘uy›β:Fo_ εΠ•Γ$:γIΑuω"ΰcΦΉ@Ž g Ο$βΓρpΩN&ί#oΡa kKa]0sς mοŒtόΘ΄ΩEυVBzΎ%―ηRBλR8Žœ!χŽM©΄ΚAOs&d;ταHΑεRm”PοΫT(ƜΗfOΩ`ηϊ―g’BwρΔΟ;7ξY.›–bχq{εW+ –{vά(–ω] E; kV±ΚpΊn=yτθΞγΈ₯AaU2ξΔ“τΰ£Υ‡]ΝΉεN}bιί7ΏΖ ¦λIwσL¦„˜-W7χNo½—”8" žH©Ÿ«-;7ž:…τe&]k$]c:2{y+μY˜­ƒŠ„₯ˍ’Έ†ύ«l°½'ΰ7fx {Β}χ©Π‹M5š/Mv0ζβΈ΄”CΙΟo吣άάY©”bS­­rΕ¦»( ζΦ%Ž€xΞΠ³Ες²±ΌhΞ’π»‰|Ϊ6Ι4'ΙzE˜‡œOPiXMwpςhϊJ£δΟˆGQiΪ»"7CβΡ,cUΥβ–4aΠΝΡς{ΫΊ 1B²‘ Υ`Ι?Άx6 X„~[ΪV³…λΊ†x­ζν#ρ΄ CŸmΊΗήm»‰™Œ$΅m™ΪΕβ,f\W¦!fάΫSc”Ίš(]Ÿ4jg،%«”½ζ³z΄«—vψrηHhsΖ- —-gΚD/π!δ‡ΪΦ·) »t$©ρχβ.hΠll:ŸOΏΈ#λΊay„}ϋμ›΅ˆ‘ά-ΰΘ;΄•I‘Ltά ywb:Ωtwςα3%₯…|4₯μ m₯δWWζ°;F₯μϋφ]ΈβnΩe―‹‘e‰μ1ΰΘO¨ήδ†$©lηΐP«z·P[‹ΡΫ„d£¦vsϊΝ+%ί% ž„rΏvΘθ­Θ6f"Χ«‡AΨ! ^c³.ήψGžΞFtxb{‹AΙ‘dΦή€|CΉƒ±α } +Ρ*ͺcΟ[…•‚cεΌǞcΛ¨¦Ψαoό<›oφs”›Α’Uρ¬‚Ιφ‹J¦ΣΝΨώZ†FκŒsώΒΊ³ί:Θwzωγ‘Ϋ&N~ωvκ''Χ‹hlͺυΠ)'ΡHɜš΄]TΙzΥδ‰6h¦žp΄Ε·= Σ«ΦlΆ_-”]—oϊ½Φβ[Ψ‘%ύ–ŠΘO ˆG³ΑbΧ&EW•Ρ΅Iu·D3νσζλI²έb&Τ›f½zZœgiy7OΛΞ:½”^…|51ClΩY§—†'΄5( ½s xςυΔ²¦›za ύŽ½‘ό]!D©C@εΞ`k?°*Ν违ΘH&©β ίυΡο°ΪνL½]ωΝΆφ=₯Ύφ»Zωv¦υΊχ‘έ;“όΊχvωεω―wϋj><ϋvλέΌpε-°„;β-P‘΄šχˆ€κeΧ'ΨZ+"ΙŠΈΦέΉO…V‹ήΐ[¬uοΘΤzKi+…’οΟz+E\γήY©tbS}»5’VZZ‘δw ˆ')­„ϊ˜΅J•`ER+β"všZ±ΧQ+› ‰“ΥΚ6Κ²] e7„ˆ‹Ωύ•ŠGR-ΒΤuά΅7i©…ž]βIH-=€–JΩgηΰd5Σmξ†Ό;͈ϋb‚O₯₯J~―€xΜL‘μΘ*EΨοΑ|`Ϋ•m¦ΨRϋ‹uiραj˜l‹WQΞΫ―ΒΡsg1—QJRή Ί¬― Y©ΫBc­6Om…–…^Ϊ£ΟrβQΤΨ‘·₯ΣrώlTaΣζ)Εbιυ‡€‰Ϋ4bLTd΄Ε`¦›•¦#.+ ΅^“Ξ%ƒE“΅EΖ„QλΘ ρ ό—v%kΈα',nύlΙ|5,O?šE"Ž_jίm4ΔΚ¦ qςΤ’ΩCΒݐ•ΤƈFγUjάϊΐ‡0ΉF·Ηΰ‘όΤ8νΘΤΪQ­šœ†S.˜‘WŸˆξK˜εΏR07‘GYέά\“]λ105O(Pz΅ šΫ2·ΝπPˆΖyZβaAÚ΄ή1˜ΰe–n³ιΡ5y‚BYΕ]‘Hp§až]šΥ &ΤΛ‡,ΫyΙ…¬Fε)ν B‘αΠ€Τš?ΉΣ”;/Οj˜ ŽM“Υ.£ή0…ΜΌ*”—p0$.VΉ"³~Γΰ Κ¦u\“›€8ρb ΜίFλ³*ΎV¨ŒΤdςm’ΗBc«–fƒE/μόIή•ΓΟ*<…«ΓΩώΐ 5y5 Šjα…X#ύB…₯x”(δ£Ϊ,„ήd±Ό<«ί[ZΣΈ;κνDŽ&΅g鈩MWf T]ϋσͺ5 MP0ΑΫ‰ωƒ‚φΔε’vτZς0v„M«γŠ[;ΔauYΚ—™ΏZ< Ÿz™½‹΄#L¦ΜΞ5L₯±λ³†εΫΣ-A1~ΐ9ΘηΦ‘O ν“)ΑήhΚs(—ΛΙŽp™bά%Ϊσh­Ž“Eϋέ£`Ι†τ<%ί! Εy΄ϋΊt†3ΌΓaΣq2I:”™ΨΝή|ƒ„³S>g*Σp²Lžc4ΠΔθβ›έWd²» 6-Μ›%Ίν/ΪN™₯S&]gd:μR Ωβ%ς=ΐ[oiέ©!+bO‡+Ί7œΞΘžΉŒσqΓΫς„[η Σ$bθ₯δκꎻμΞ" ε–•ν’¬Δ=7; οP+Νudcnᡬ/%&ϋ€ +υ­γ“m΅‰”’λΎ9sVE!‡ JL!ΉΉΧgU42<ωD:9 < ωδ:id8 y8Α*ςJ©ŠŒΟB>›ŽBNΟAΦοnΚ*U% 灗 «­€ΔΧ’ΧvEE)ׁ7!ίLG)—· λ·Ζ›eΫiyΫZZΉ œ‚<₯?Ί™³ ³”Kf`LΗY1ͺ.]^L“KV1gπ z¨ dχήJΟq#`¨7Έ‰3§.:t7žΟ·"Υb;‘ή€|#Aƒβ1±c“=œ„<™€Ϋ•5¦κQ†}SΩ°cχΈD!%•’Οπ^;a/δδFz-•’‡QφAξ[‡Ά0χϋ!'·ΗeσΌJ=!.ϋ‡!+-sΚ«d'πdύ₯֐(BLL+]σΚ5εpςh:jyxςm΅μcν‘ΡάΙϊ4"uxς}mrwŠεeΧ)›Ej §­R9°Œ…ΧΈΝΆ€X1«ΈυΏ2L·ε‘­²2Κ-…αΘw΄³[΄„¦]–Σπ Ԛ†ΣιŸE&3ΏP˜³‘₯₯²P4‘ωͺΓ"‰D·”‘6 Ί}ΞΟΟ™ή½°P\Ϋ'M·ΌΓΊUn„ώPάΕ ŠΉ9/Ξ@±λk Ϋ­T£T srΖlΠ²gV*lΓ£|―ξ<؞o”“ΈMɐ%W€'Ϊ?F δ.―AΎ¦_W³Μ§`žNΦ¬ˆΛuΰCΘ΅9MΚ,2 Μΐ ³_Au\kΩplΧbΝ+£p[-ώ@6;`_„Sυ‡>ϋ1Κ¬ί¬eBΪ1ށaLόΟ΄γE$a2ޱ‹j”nT8S/ή„‘·Αd­ΠέΗΜ•M―Ψo¬E0Ό ωr‚nƒί…Fnvλ>ΪiβΧΈ+0?ΜBVή`Ύκ›χΗ!'X&-\)%·xςm>Ξ»—TΦυ+ΎωEEF­θs eψδΟ,‘1Έ’%JΦ„l¦οk(ω9ρ€Mγ2J Bu—·ΦZdOΓώuIŠT{Ί€R©Άούuw5γfYc— ϋ Fψ‡QTΆ{ _HήYΧ7φΘ:k"φ ψ δORuΦo<…Ίσ›ΐ§Ÿ&β¬ι―?…όiϋ5%7œ…<«νδ§2)ύΟ€&d5—Έfμ*a甬ΡRSvx ²ΉΌΡξ¨νΪ‡tΧ ή<YiΑ4φ›»€§!ŸnΏΥ^αΊe˜\T •€°Δ`8š©­;&l·}»ΧTLwψδΟ1έ]uΣUm|1ΐπ δ'‰YοE  YΙιΘ[οΰdύ~Χ ›ΚbύcΫ ΗηΊΑ•…}ˆyΫrŠΎ΄S&‚ΰχ@ώύjFα}%™if„8ΏΉͺ‘;<Yi2m]ͺ±ξBVZ!‰ύζnΰΘJύQΉjFΙmŽAΣΆή39cŠ…žΖs‡ΪphR AιmίX2=Ϋ€pΥC{‰ξEΰδmΪ‘€ ΡnƒRΩ³Œ’˜ΆγK―G‘“ΐː/§ί›˜„©D˜ΜzδΖ)ι&ϊ&'βWgMTΆ{ χ΄ίd)ΉN`/d΅š,~螝†{΅’ ]Ά;oyž……7Ευ/BVλΗφCk1T΄w xr %w x²~£Υc°˜)j—"•ϋΐ琟kSΊΟ6+e)ψšε…MͺG{«‚E―\]X,W6hΞ`χ„Κξ’εΪt|ˆ6hΝ‡£Φ‘Zεζ·†ΙmΖΊ?R¦€ά^yκ΅If~”±ΪEΛtEΦX)WO†΅Η΅θΓrX ΄žηKλF8½™›dkυz»€}™δ#·¨=Β6JόCM垀‰a:€P½]!€RCM†ΆλΜK7αD²xς:LάE9E˜L~ ‹Φ eUλ{ Fx ς±δΗ{Žνγ=βt­ͺ'Ή“fρή¦ b¬ΐ7< ωl"C=aœΕ–†2Š›{δκ=%7Ό ωͺΆ «,ψƒ ΰ Θ7’7S₯i βτπSȟ¦c¦κΣDφπΘ$f«Ρ3[Η4luψδΟ΄m΅—oϋΖ^\Ω6€Έ˜@²«Νιm.Δ΄|³½Y»Ηœu—Βv”/K.›a/ΐZ₯τϊˆΠ JπTΕAΏ:η[A΄œΓiϋYCa­r €…Xb {ˆnͺj³Tžn‚ˆ]j‡•w}u7ΑΈ+0ŽAVZι‰ύζLύLξ@F1b”œ― δο@Φ7fω{Ή(ύ»ΐ{υG₯]Υ¨.&ΣU½;ι„CP7τLK–³Β†fμΜΥm ϋRͺ˜agη<ŠqθΧnΐY^΄Β1Ή2ΩΌΌώοeΊΰΊέξϊeθ5¦ΎQχΓγ²4‚‘~·{ŸPλΏM@<Št2-λc‹₯δΗ(˜K™•Φg(p€9Ω2y,ΰnΘj‘€•Κδ ΚαI’ecΨa™ΠδΠ‚|‰<p'δι•ΘS”ΒΣDKd; xθVKsςςTΐ~Θκηs€ δ}Βϋ Eη+–«sŽτΌΩϋCL£¦3NX NyŽ'Λ–Θ΄€λPgž‘ž%l"σtIηe 䙀λ`"ΟQΟ-mΖmͺ3f ]cž ΨΉ/½βx"x‘hqτΟQF`—€Λδ…€λΠς~€rψ a‘»*Ερ€λ`"_B|)Ρβθ6Gw–Θ–Η—άyGzερ!ΚΰΓ„«Μ,M‹Ž„…B‘šeΛδC΅ͺLlͺ[XYΨ―γƏPe’-μšρS‚l3A%cςs~_F…u"ΝΜμ)…uή\°XΘ{C`Ρ Ij3 C¨wσNά,ί&6_ΦΚ1΄Zœ%.‡€d#±:Ψr2‹’Ϋ <Ym*Aό4[_c_΄VŒeΛ³p₯T^ ›uΪ<EJο ¦ΐGi3dI&Ÿΐt“9ΰ"Ύ΅wΥ₯w'>»#kWDh@NΑ(ΉMΐcυν*ΟύCM}Vπσ>¬εβ‹j8§ ΠΰSΘO,«}‚λ‚ £ς΄[J~Z@€ό@[Ωύ%Ϋ΅GΣ]¨Rmϊ†$­Ο pΒ~Θκγϋf₯χ+Ί7>Ξυ.ηŠXνž€|’ύΎ”’Ϋ < Y?bτ Ύœ;oy$ ¨΄ˆ.ΥΘ/δχΎΗ!ΰ#Θj­slp²‚ΌLΤQΒ­·Ά_i&¬—0ΊΟBmQDόtœtEυžνSM—­#qƒ¦»υŠE Ŝ1ιPη~a‘υ·dIΌ»·!+-6vjΨ•΄qΤ‘K΅θξ :!ΙmεIΈςήČμΝMCK[#B‡' §ΰ (Ή}ΐ“υΔ)`Σ,,ΊJΞ€eΛ³ ΅#³nΡzΕΞ΅ΘΪρΎY­Γ³Φn‹=Χ»ΈΒ9>5J’Tιψόp ςX‚]λ|Έ@_ƒ}π(δ£ͺμW}sπ"d₯}²r–^ΰzf˜άUΓΨή§ωͺγDGB―ͺ²+Ÿh]ΎYΏ'Ώ«ξή#;ΩQV„Z—`ʍlf…dwCV›‹?•RPς{Δ“6 %a{F6Άl4Cϊζ.ΰΘJ׏3>››ψ\{v-©?IϋτΜzγa,/Ϊv“πΞ ?[Βnyͺ0‘-cbΣ ΌY$Ι=Δ#^^΄άΪj)Ω'ŠπΛγR³ ΕlQHŸ‡4vkY“ΝΜŒ„0Ή)Τ,°O±$Ββ‡hĎ1 UωZ}c@%/‚%‘^§ΔNό•sΐ1ΘIΆΰ-Z*Jnx²ώA’ύΖSŸΞ6άrΨγ<ιΙybu ψςmv;λΑTp¦DΆ²a3„;!οl;5/$«Υ<κ5”όnρ€Mγ;PΆ§Ϊμ” ¦τUƒag™΅O„Ϋ!+]‚ΫV]₯ΆŠσb»φ½rΨDe#Gi%h…Ÿ©­8fΑ’φ‹ Ϋw)}ΐυgΰ<―»‡('au|ρl€`…ω GoCŽύ2¦‡³φΥΚfΐi ΑύΕmᙐή0VsΆ“Ÿpς@:·ν|ΓΌ’}έΰ~ΘJQmbΏΉ 8Y9VΨΫ·ƒ%XỐίΥ6σ TοxΓΞzεe72wVMk΅7X ϋjΎAF/kΰDω8π#Θιχ%…œνθ1^—)€Ώε†%hΣ1δ’U°K¦c”LοeXKνHΤ·,š+ωR˜7ι­ν.ʞ0ΉΎδfvϋ²l+_FϊeόΌc»[ω—B²]»o^)ω-βI›F%a›F£)ΙλshησL£ΡΞ&>wnΥΞbঌpTʏ‘`Ν-ό¨βYŒJΆΟξ``70ψ4Υ(Ν₯SVΊO λχΆ·ρ5pb-ΙΗƒΊ υΦ&βOΉEE,ΏXCŒϊ!+Γ“k€<(…πd΅ΫiΕOrƒ*n*‹†jMφ€ ΅Γΐ+“<ΖέΒk~.‹5mwEΙOˆ'm>J ΒφxΝ.Z ϋλ’δ‚ w—„={”]gG©‹“Ρy»ͺ)Ő£=Ώb€nθ"'έ#°^…ξsž|¬Βz€χοBΎ«­θ,(ΦOtΥC%ωΆ~β¬φ‚[φδ'-ͺ°B­I/ΉŠɎ@IΏFPς9ρ€Mc %a{*fm―ζ·PΜRτ&I’Λ¨”„{ +-D0r[šΘ=[,W’1η˜ξK\Ёq£6L'(/X47}έ°s’!Ιwfˆϋ~ΰ#Θϊϋ/£ΞŒ-½eθ”M˜|g¦σωτ ι=—D₯Έrrϋι[φb(ΉnΰΘϊΛ\WΘ‰»’Q1ΏIq΅ΨŒ8ΠLP8ΊœA–e ‹7D|/π)䧉©r“ώΩTΣhΗvλr–LΈ²~…ΦS†&Ÿέšš2ΨXN7ηόaςT”­"šΚ–όxπSȟjΐa/‰έΤœ§m Σcc|;Έ.Iρ+0ΒзΏ†~!${ς‘τ«%T@/ ž΄iό”@„ν±ζ7†ZnEο·ΒŠ ϋ2Ίζlj’5Ύ†EΣΩΪΜ̘“ΓƒΓKοQ!ώ;οA~O[ΫηΎΔ&¬₯{γYί4gŠΥQYβΏ 6Axς9m❠wx|7R'Lώπιϊ\Ρ#LΡΩ°ZxIGm& ΥΫ=ˆq70Ε+ΰΎ›[<Γ±LRWΐν\}»‡‚"/οCVΪY,ηχ«`?S§wΈ”όρ€Mγ{PΆik’ιžU‘δφ½ξθ υ·&6Oξ_ΌΕw4aΫ|ÝδD'ε¦P˜ςΧ‡ω>`tO€~pΣw-›hϋΜφ – ιC€Ώκ'ΤΫX–ΨΌ?Q9 ΜAVκ£ΙyLJξ80YΏO¦hŒΟAVkv“›ΐ'.γΐk―₯£”σΐλ™Ϊ-nšJΩ¬tή’8άj]}·ζNέή%―\.Νrί)½_χϋ2|λ α0δαΥ΄Ζ~ݞ ‘ΉοΰΘTy―ϊζ.ΰ)ΘI^^ίΒpΏ«–αiΘJΧΓ7όζG,›ŽYΚφΑˆS8 y²ύ}°οŠδ&δ›ιw~(ω[βI›Ζο@ D؞>X7―³εJ »ρ;QYϋ!χλΈ»„[ν\ JŽΓέΐg•VΕ2’CnU”Wƒ βησŽ=oV Ž•σF{.Wφς¦ΨαoόΌ„ΓE?GΉ,R/’v-Ά―–ΓK6??€<ό@’ω9ύ°}Ÿ†oΞ[>»/L!¬κο)Βdξβίκ<1aœiΥb΄šN"VΐQΘ£ν•Sr‡€g ŸΡΦΪvŠηΨ;-PAMg•δΘ΅Ψί/XΗ5ΘJΣyzΝ"%]@pC’τQΒ<δ|b­Β.!ŽτŒ½eΛ@ΜΖ€χ!'Ή@ά’e δFS§΄΅Ω6ή²vE AV;”άκΟΜp·C˜βρ? %LξψΖΩ6š’οO»Ϋθrί YΙολ5Ž”|Ÿ€x¦ρ‡PΆ§ξmŒ/Ιρ‡P5wCV:±ΖΈu7qΫ(HŽˆμ€|`} q1€οBVΪq ηK(Ήw€Η!Χ6γΣΩ°;ΑΊd8l\έΩΡ°eИš—5x"zψςΣΔt§΄3γ‡aΣ„Z›ηεTχÜ?ΓnΘJ­OC L¦³lψuΝ‰ΛΦ’ε4;ζgzaΗ²Z GyVQ%’#QίΌY­χ›XDG’ς8 y:eNŸA^‡£l”όs“9Κφ¨1$/ŚWƒa‘ϋ¨Ο ΤμL6KUƒπd΅Ž£ψiW8ζ φ’dώκE‰ŠέRώ¦Ί₯ΥŸ^ΰ^Θ{Ϋoυ”άΰ>Θϋ΄u³-4Ÿ>–UPΟ~ΰ1ΘΗΪί₯ύ!Α* €_ω)ωAρ€M㏒"lO—v{Ž’ΛΝzVΕ΄=I†?’α΅—p'δΚΪΎ&fcχM·θ`-ή‘GΒ3Δψ›sΦ’ΉdΣΎν+Œ§‰ϋΰΘϊ3υϋ-Χ―zVmζΜ%Ωύ(τώ£¨„2Š‘*γΓ~T]ϋσͺ5 λ&‰ΞQΰ Θ'Ϊο&)Ήΐ“υ―ΰ0rΖ¦Zƒ‘Z@˜ΜΐZ°ΉΞd-ŠΨ οAΦ_iμ¦vΏμVq\–ЏΑpυ:ΘdDGtύڏgψπ B<Št2²ΜO “»vƒ!Y ?Ρ„x’κ Π%a%Σ‘ΪΔ§Έ²R|ΉͺMΙuχBVκx5βƍΗe~/ͺvΩ3 ‹Vαe8‚›³VΚnј3}»v°m?°ά»@·2²vMΤχŸAVΊ(ΩυΧaΛ___»ώz&kΧΚ-ΦΧaΚ„»!+Ν½Ι™υΧaΚ_ΟhΖ"k(…«ΖPΤϋa‘k†ΗGζKš‚π,‘―Α‹ͺΆΕͺI[υΧQ! ί‡ό~zVύ Xς7ΦΧͺΏΡ„ΙZuοσI³ŠΆύ Ψ3α;ίiΏmφLx²ΪρΣγγΔUpΩ<„>+ ϊœˆ‚ϊo@Ύ‘˜ϊ6s½©¨νπ!δ‡ι¨mψ²ώ‚¬4ΩϋΝc@ ²•Ž η!Ο―CDι/!/jσΈI±ωΝΝ‘μpY}Ψ7,―T»ƒZ\+¦{dσρ³ΌΪ0Ό ω¦ώX:τ–f‘ΐ²SΚI²ϊ&˜|³φ>ΓČ«EώG„dϋ!+2ΦkV)ωβI›ΖΟ‘"lΟώΆWNΕ΅ύ|†OΞφetΓ―6Σ½ΓƒΩΡΦΥ,­ΪΖD΅kΨ€ΐ~'pςdϋ-όη‘GΒδͺ»΄iQς·\§ GΏ€ˆ°=ΎAvάψ'aΝβQ΄μŽ&:OW_€ _Zn8δρψIJvb=ταtΠή+…oρσιa[S-UΟΚ Η/e r΅ ψ䏴uߏ3ι;~ftTΆBώ"τO¨εϋε*δŸ’έ y|ύ/ΒX"Δ“6?…ˆ°=rσ²] eΫ›?šH¨ω΅ΉV^ϊq’JYΏm$gΌ»{'iΤ»}μ˜~€ΛH–-ιΐΔΎx²Ϊ*…ψι;QΤΣπΜ…ΊΎeyžω Ωjψg uΒw 'Ή— E5όΣB²zα(΄μŸ’?$ ž΄iόY”@„ν©†ΫόΐτΊ½(Ιο[¨~ίΒϋ]ΕαAlUΌόŒxEAuBv$ΩnΑ©ϊφ’5lTΚΎΝ‚½5UWΩbώΎYˆώ₯ΊΣ`ΎΒ’95a$VS ƒπδ.|ω–μeΘ—Σ―"”όΈ€x¦ρηQΆ§¦nΜεd­γ/ Zn…ΌUΉŠ6ΗI?Α5₯^©GξXΡL±ΙΓ\Ν{α˜L:,:qέ<ωŒΆNo²ΫΓξi©r]4—,v˟[-Yž]ˆnϊγΎ†9)jT8Ό –Λμ*NiΏςa„Ι ςn=_Œ9OBZX2=›€ˆ§cπ"n6Δ›²ω%'ΌY˜8]γΛϋtΨΡ4Λ] VXφ|xŸe* ?Γ'‘:²υ¬²Ά+j dσυ—ΒiΘΣΪω:Xk—Έ P-X+%Iπ/ƒaςΡΉΦp'-WLˆΙπ]Θ)Λ§δCΦ?–.ߍ Φ ‘ΖΨΐΊΑ{ΙΪ1<œ‚<₯Ντ0E‘#g$ψ#κY˜sε%ιΓ/Γ’C>άώΓ_’Υ[?•nͺ)ω£βI(χΫf’S?&νadϋ―d4Ί τFςP‰R‡€xΤ{0ztώj†ΊŽ¦±œ˜¦ ΅°fΫ―Β:ΟΗςlFΝvC`=6*²\τ@xς‰Δ,jϋLΡς ž]©ο|YUR”β―θΨ”Ύύό l&B=ϋi?'MηW3|F3B½Γ…Ν-gηγrΠj—n+Fg‹a;’’X₯Κb‹d7“‹Š1n(d;°rOb ιGΟ³μ`SΛ}-»4Δh7πd₯vIKCΙυBVj‡ β"€S^ms7i£ζΫπ«…EΦk0^L?$Αv+Υ μ`ϋ²Υ‹Hΐ  Mώ‹$ΌΜ" {–ΟbJΦgJθ&jDH©ε(XΆ VȊΦδ ¦o!.l–ώ*α#ΘϊηaΞΞΡ…[Δ)/»NΩ,bΔμDΓζΉ†K₯G™\ ΟB>›˜ωn™ΑΎ‡>–Rϋ[™umy(ωυZžΎ·₯ӟόZ†{ΪρhKŽ<œo–*‘έ\1ψ%JΦ+φ‹‘πΓώa.x K“ώΫΠarN€΅μ…ƒ‡΄Ό9Δ³₯Ξ‘z^†ε©ώΠ#|²TSΟiG˜ŒωŸ4>*WYHWΏbμω•h§>έ:EŸΒ—›ŽA‘ΙZ‚hIύο.αyΘηυMtΠ8“3ξUyXZμX³9zj £™6iD~’&ϊ ·ΛΠ"«Μυώ„ψΫΪcΒ(„΅Žόχ@Φ  ~B?0‹ Ύ;0¬`Ϋω"ός'Ϊy<2hœΝ“Β-Xˆ`σn$!ˆ&7ΨZœηPaθμhΦ8CΟΩα,)€t‘HΉ#dƒπ!d₯3ΈQšs9γ©i{a]ΰ…Οf&-λυZšυ?SΒ<δ|;Q£κβLΦ87ΚΤ@Z9ί¬EEόdγŸ$ͺˆ³ƒΖωœρ8δV¬mhη7;$¦‹ ²„Zέ°ΖE‹ΦΊ}‘?Δ‘/ ΅q–΄–;ϋωΧ‰‚ώΘή„|S;#CƒΖ…@σ¦θŒώ9(A¦;ωvεN–Ο|]n1•~θδ‹ύ_€;α$δδ6"nAW1kHξ_fΦ΅OΙw¨ά}‹έe΅*ΝθΏ…lgT²No4OΘοϊθwόKϋΏ©/£sMϋ!^«ξ›ΜW<{)4γ|Pͺδ§ΓnΏrόgζ§ηͺΆS²Η{–kΡyvcW~΅²`ΉgΗ£PΙV1Η‡,š-7¦tέzςθѝΗqΦ$2;ΥŒ4ι%σόw›VUΤΟοŽ`Εζo7q?μZ5dΎγΚ4”―—ψbΖ ζk¬`ΊΎΡdWQ†ž>y6υa!ˆΜŒύΤΏh[sb½Β°ͺ¦‡ξζ·Feάh0«ΜΏ+4šΑXγ•„©oD@<‘:«Š#–γΖSΗ’ƒΞΜΨ’+?Ψuξ4ʚΞ7―Όv€2a̘E‘ΆtιQςyρ(Ј| ©±wlnΜπ‰pΒώΜͺϋδޞX‹ΉWj§[$»Έ3Σΰ΄ΣT %ΏKΐ¦έΎλ§rŽ[€;!+•Olͺ›gLΗ6γz"ΠEg¦ΆJ±@΄τBΙοOBzΩΛ!ωž6§mά—Jqά΅†|8AvΰΔU!¦Bάΰ?-QςGΔ£¨’νΙΐΝPA„x4‹₯Gœ4•ζ$T―uM{΄9m1˜ΉΆάΥΧ’ΞPΨ(kι3 wΆdΎ Η]SξΌ<«­`ΒΎ rŸ6«ν†αΏ΄+!©QyJΫ@ƒ0ζ>#EJ»Γ‚2in :>74,OMXAf^qCFΥ3ŠŸδ₯Ε'\gΩή¦ ƒέ#ΟP(°Έ-sŠ †S.˜ τqήj–B₯…Κ·K‘cΏAwž{Y]Ϋ"Ο«\ϋ!+υΖgŸ#^³εJΰ³Œ.W)<‘’&Ψ3ŒΖ»JΉηΕΟ*ΰ#Θϊσ:§[qgϋW‹–Π¨Κ>„’&< YbCzŒ-¬†$8ΖΎPœ¬³}£Tφ:χ?_uX€Χhs$ζjω…n`“1^X¨ M‡δOΙ 3κμ:χ ™†λάU{OβΤcΦXφΜ ;RΖσ)υžŽ‚aς½§Sςg2‰Ιp¦›ΙJζ(Χ§δYΘYύZSΠLj€Κ›έZδΎE€†=‚δ ηχ”|^@IvδΪφC>œ 'YγjΪ %Σ&¦=@€V;hϋΝ]ΐ#΄Ώ΅ΙΓ(B>ͺmαΨ½žEƒβΠz=£ΌμF–Ξjh­β†γš–!{—΅m’lŸCΦχΔχ‘rvΙ2^—]‹nyβΑi²΄_Δ.™ŽQ2½—a΅ ±½$ζJΎζmQ63£({Β{οigf³έžgώόΌc»ψ!Yα~Ϊ-+%ΏEΐd"HΣ8‹ˆ°M \ΌΕƒ±Όhjaξ‚• Ώ"Ϊ˜δ‡΅k£ uΘ—Ύ-‰Θχο@Ύ£­ηw£«―Ϋ-ΟσLHο="ΝΖξ]y ’r˜ΛΤmνn­(Ήγΐ<δΌΆΞ:ΞaK{Μ‚F3ͺGB’›` .γΐk―₯£”θΉ^Gέ¦‹Φb€ϋ”φ ΰΘj•z­ώ.‚Ηqί)έλ₯y„CΐaΘΓιτz{&Dζ Ό{€ H€οKίΨ•©/\weR9”|«Ά6Ι“Μήέ½lg,{ͺŽcΰΩ>qΚ'!+­ ΛυΑΞ ErςΝτ;?”ό-ρ€Mc %a{ϊ`έB”I‚QYϋ3a#7·Q «·ͺ§°”†»Ο +ΝΪeD‡άͺ(―D‡›{ή*¬+η8φ\μ-δι˜\ψ?οζB8*’ά Φcκ΄ΚX‹Sιfl-C#uΖ9iaέY‹oδKoώ8-NœόςνΥONΡΨT»fζν…ͺΧ(΄7αM3‹μ₯”Μ©YAΫE•¬WMn‘?σΉ λpX³Ώm¦@o¬ ½ύj=Η΅ίΒoGίwo‘\Λ~Lψ²τlY+*zΝ,Qz! ž΄[{απvΆφBΈ4I‚—3Ό…'Τjν£ΰΉ Δ.Υw|GΡά‚2›piΌwέ-₯ΠxΆιΨ―ΓίΘσeΤ$Βχ λΩΉ‹kBΨf6εΜΞ¬!€ŽoΈΦ2ίmLηΌl—_δβ³eЊιωaΎi[6/γ0Β»οjηε|γqΕ9‹F~Ω.ιÌβTq=eΧY‘_‹Aq7΅k|qYHV~₯]Υ)ω1ρ€MCΨkΧF³%Šs(ɎΆίu{!χ*»›ζ›I/έΆύŠcPΜΈ(0γœι]7nΤ–_μ€ο-!7Šό}Ω2&φύΐϋυΟ’ΎGΫ(\Ύ•Ψ ›ρ%ΛπΓΠΎ3r΄₯iy‘*|ιΪ‹ΞaΦ’δσΫΝku\6CΧ`(„οAΦχŸyQ l΅9t;a‘Σeρl7oρ|`w$ιλ J˜‡œΧ¨6ίEnΠΛω‹εεΩ7{ΛΙΣλ™Ϊ!f'cͺv"7Eɍ§ Oik³3l*dνŠ<>‚¬Ά3ΉνξvSά2wJ˜ά–9ιγΜ”|€xΪέFOΉο…¬δχυGJΎO@< ε~νϋζw#Ϋ“†Ή=ƒ%’Τ! ΕjΪ―Kη&Œ$Β^ec‰σ qό‡e½ΝwΡQί¬σφήγΤ}+“d¬χ>Ϊ"ζΫ%Ϋ1=…σ@DfO¦~ΊuΗ„τΤM πCu-ž‡œδHd -½›©/3ΰΰ€¦–g°BwΜRη£hΪ―He#[·ˆΪpς΄6ΕνŽωΪϋΣl‹‘$£Ϋ0gB½†q­YΚ.Vλ₯ΧU‰Τ1ΰIΘJa3[λͺ›'pϋ—4αΐ#¨^υΝ»€C‡Ϊ_§(Ήΰ0δamƒέ“5όr8ξ +οΨ5Ξͺ–{ x ς΅ΔόίΗr‚EyηGtξB~˜Ž’ej}~MEΙGΥ£τŸ@~’˜V6/†ώWM'Ώ ωΛιθδ)pςΜ:ιδ৐?MN'΄BM'π; G::™Ύ„όR['›Ψ‘‚Z`r%9ΖoκUSΜ2π;!g:Šωψ5Θ_KŒJΙ—€x4iά‹¦ίΚe”ͺ…E£D!w¬ωy»`‡#Vg…­b”«A}UGΊx >7LKvΉΓν‚‘ΦτΨT·Ο-ΏΰΩlϊ°Ε@Λ'κCn}Rςκ ―›'½εοΗ#Et ˆG³XΞς΅°«#†8ΐζ2b: ”‚‚ιωΉ‚Ώ4 p/··1ξ^χ€υΩ’ΗŽ0»κE‡œ|α<Βδ¦Ψv±ώ£”5θBυ³ςΜ„…oέΕZ}=fΑζa’Μφ£α7ί~m©Ρ{J„' yΟ5œυγDM\ΪuRς*»ΞΨ·ΦΎ‡ώ ²QΙ:½ρτ=τ·4ξ‘oΌ½ΥMττYνΒφΞ¨ΣΘΎώ–Ό[ά\ΞΆ³Φ?Ϋήpλ=œh3ƒΜΏ…N˜0=vroom/tests/0000755000176200001440000000000014132374245012563 5ustar liggesusersvroom/tests/spelling.R0000644000176200001440000000024114132374245014520 0ustar liggesusersif(requireNamespace('spelling', quietly = TRUE)) spelling::spell_check_test(vignettes = TRUE, error = FALSE, skip_on_cran = TRUE) vroom/tests/testthat/0000755000176200001440000000000014533733452014427 5ustar liggesusersvroom/tests/testthat/fwf-trailing-crlf.txt0000644000176200001440000000002214132374245020473 0ustar liggesusers123 123 123 123 vroom/tests/testthat/test-connection.R0000644000176200001440000000521214226130145017653 0ustar liggesuserstest_that("reading from connection is consistent with reading directly from a file", { skip_if(is_windows() && on_github_actions()) expected <- vroom(vroom_example("mtcars.csv"), col_types = list()) # This needs to be small enough to have a few blocks in the file, but big # enough to fit on the first line (until #47 is fixed) withr::with_envvar(c("VROOM_CONNECTION_SIZE" = 100), { actual <- vroom(file(vroom_example("mtcars.csv"), ""), delim = ",", col_types = list()) }) expect_equal(actual, expected) }) test_that("reading from connection is consistent with reading directly from a file with quoted fields", { ir <- iris ir$Species <- as.character(ir$Species) # add some commas locs <- vapply(nchar(ir$Species), sample.int, integer(1), 1) substr(ir$Species, locs, locs) <- "," out <- tempfile() vroom_write(ir, out, delim = ",") expected <- vroom(out, col_types = list()) # This needs to be small enough to have a few blocks in the file, but big # enough to fit on the first line (until #47 is fixed) withr::with_envvar(c("VROOM_CONNECTION_SIZE" = 100), { actual <- vroom(file(out), col_types = list()) }) expect_equal(actual, expected) }) test_that("vroom errors when the connection buffer is too small", { withr::with_envvar(c("VROOM_CONNECTION_SIZE" = 32), { expect_error(vroom(file(vroom_example("mtcars.csv")), col_types = list()), "not large enough") }) }) test_that("vroom can read files with only a single line and no newlines", { f <- tempfile() on.exit(unlink(f)) writeChar("a,b,c", eos = NULL, f) # with a header expect_named(vroom(f, delim = ",", col_types = list()), c("a", "b", "c")) expect_named(vroom(f, col_types = list()), c("a", "b", "c")) # without a header expect_equal(vroom(f, col_names = FALSE, delim = ",", col_types = list()), tibble::tibble(X1 = "a", X2 = "b", X3 = "c")) expect_equal(vroom(f, col_names = FALSE, col_types = list()), tibble::tibble(X1 = "a", X2 = "b", X3 = "c")) }) test_that("vroom works with file connections and quoted fields", { f <- tempfile() on.exit(unlink(f)) writeLines('a,b,c\n"1","2","3"\n"4","5","6"', f) withr::with_envvar(c("VROOM_CONNECTION_SIZE" = 8), { x <- vroom(file(f), delim = ",", col_types = list()) }) expect_equal(x, tibble::tibble(a = c(1, 4), b = c(2, 5), c = c(3, 6))) }) test_that("vroom works with windows newlines and a connection size that lies directly on the newline", { tf <- tempfile() on.exit(unlink(tf)) writeChar("1,2\r\na,bbb\r\ne,f\r\n", tf, eos = NULL) withr::with_envvar(c("VROOM_CONNECTION_SIZE" = 12), { x <- vroom(file(tf), col_types = "cc") }) expect_equal(x[[1]], c("a", "e")) }) vroom/tests/testthat/test-col_types.R0000644000176200001440000000175014422654617017535 0ustar liggesuserstest_that("You can use !! in cols and cols_only", { var <- "xyz" expect_equal( cols(!!var := col_character()), cols(xyz = col_character()) ) expect_equal( cols_only(!!var := col_character()), cols_only(xyz = col_character()) ) }) test_that("format(col_spec) contains the delimiter if specified", { expect_match(fixed = TRUE, format(cols(.delim = "\t")), '.delim = "\\t"' ) }) test_that("col_types are truncated if you pass too many (#355)", { res <- vroom(I("a,b,c,d\n1,2,3,4"), col_types = "cccccccc") expect_equal(res, tibble::tibble(a = "1", b = "2", c = "3", d = "4")) }) test_that("all col_types can be reported with color", { local_reproducible_output(crayon = TRUE) dat <- vroom( I(glue::glue(" skip,guess,character,factor,logical,double,integer,big_integer,\\ number,date,datetime skip,a,b,c,TRUE,1.3,5,10,\"1,234.56\",2023-01-20,2018-01-01 10:01:01")), col_types = "_?cfldiInDT" ) expect_snapshot(spec(dat)) }) vroom/tests/testthat/test-factor.R0000644000176200001440000001505414362130126016777 0ustar liggesuserstest_that("strings mapped to levels", { test_vroom("a\nb\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = c("a", "b"))), equals = tibble::tibble(X1 = factor(c("a", "b"))) ) }) test_that("can generate ordered factor", { res <- test_vroom("a\nb\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = c("a", "b"), ordered = TRUE)), equals = tibble::tibble(X1 = ordered(c("a", "b"))) ) expect_true(is.ordered(res$X1)) }) test_that("NA if value not in levels", { test_vroom("a\nb\nc\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = c("a", "b"))), equals = tibble::tibble(X1 = factor(c("a", "b", NA))) ) }) test_that("NAs silently passed along", { test_vroom("a\nb\nNA\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = c("a", "b"), include_na = FALSE)), equals = tibble::tibble(X1 = factor(c("a", "b", NA))) ) }) test_that("levels = NULL", { test_vroom("a\nb\nc\nb\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = NULL)), equals = tibble::tibble(X1 = factor(c("a", "b", "c", "b"))) ) }) test_that("levels = NULL orders by data", { test_vroom("b\na\nc\nb\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = NULL)), equals = tibble::tibble(X1 = factor(c("b", "a", "c", "b"), levels = c("b", "a", "c"))) ) }) test_that("levels = NULL is the default", { test_vroom("a\nb\nc\nd\n", col_names = FALSE, col_types = list(X1 = "f"), equals = tibble::tibble(X1 = factor(c("a", "b", "c", "d"))) ) }) test_that("NAs included in levels if desired", { test_vroom("NA\nb\na\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = c("a", "b", NA))), equals = tibble::tibble(X1 = factor(c(NA, "b", "a"), levels = c("a", "b", NA), exclude = NULL)) ) test_vroom("NA\nb\na\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = c("a", "b", NA), include_na = TRUE)), equals = tibble::tibble(X1 = factor(c(NA, "b", "a"), levels = c("a", "b", NA), exclude = NULL)) ) test_vroom("NA\nb\na\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = NULL, include_na = FALSE)), equals = tibble::tibble(X1 = factor(c(NA, "b", "a"), levels = c("b", "a"))) ) test_vroom("NA\nb\na\n", col_names = FALSE, col_types = list(X1 = col_factor(levels = NULL, include_na = TRUE)), equals = tibble::tibble(X1 = factor(c(NA, "b", "a"), levels = c(NA, "b", "a"), exclude = NULL)) ) }) test_that("Factors handle encodings properly (#615)", { encoded <- function(x, encoding) { Encoding(x) <- encoding x } f <- tempfile() on.exit(unlink(f)) writeBin(charToRaw(encoded("test\nA\n\xC4\n", "latin1")), f) x <- test_vroom(f, delim = ",", col_types = cols(col_factor(c("A", "\uC4"))), locale = locale(encoding = "latin1"), equals = tibble::tibble(test = factor(c("A", "\uC4"), levels = c("A", "\uC4"))) ) }) test_that("factors parse like factor if trim_ws = FALSE", { test_vroom("a\na \n", col_names = FALSE, trim_ws = FALSE, col_types = list(X1 = col_factor(levels = "a")), equals = tibble::tibble(X1 = factor(c("a", "a "), levels = c("a"))) ) test_vroom("a\na \n", col_names = FALSE, trim_ws = FALSE, col_types = list(X1 = col_factor(levels = "a ")), equals = tibble::tibble(X1 = factor(c("a", "a "), levels = c("a "))) ) test_vroom("a\na \n", col_names = FALSE, trim_ws = FALSE, col_types = list(X1 = col_factor(levels = c("a ", "a"))), equals = tibble::tibble(X1 = factor(c("a", "a "), levels = c("a ", "a"))) ) }) test_that("Can parse a factor with levels of NA and empty string", { x <- c("NC", "NC", "NC", "", "", "NB", "NA", "", "", "NB", "NA", "NA", "NC", "NB", "NB", "NC", "NB", "NA", "NA") x_in <- paste0(paste(x, collapse = "\n"), "\n") test_vroom(x_in, col_names = FALSE, col_types = list(X1 = col_factor(levels = c("NA", "NB", "NC", ""))), na = character(), skip_empty_rows = FALSE, equals = tibble::tibble(X1 = factor(x, levels = c("NA", "NB", "NC", ""))) ) }) test_that("encodings are respected", { loc <- locale(encoding = "ISO-8859-1") expected <- c("fran\u00e7ais", "\u00e9l\u00e8ve") x <- vroom(test_path("enc-iso-8859-1.txt"), delim = "\n", locale = loc, col_types = c(X1 = "f"), col_names = FALSE) expect_equal(x[[1]], factor(expected, levels = expected)) y <- vroom( test_path("enc-iso-8859-1.txt"), delim = "\n", locale = loc, col_types = list(X1 = col_factor(levels = expected)), col_names = FALSE ) expect_equal(y[[1]], factor(expected, levels = expected)) }) test_that("Results are correct with backslash escapes", { obj <- vroom(I("A,T\nB,F\n"), col_names = FALSE, col_types = list("f", "f"), escape_backslash = TRUE) exp <- tibble::tibble(X1 = factor(c("A", "B")), X2 = factor(c("T", "F"), levels = c("T", "F"))) expect_equal(obj, exp) obj2 <- vroom(I("A,T\nB,F\n"), col_names = FALSE, col_types = list("f", "f"), escape_backslash = FALSE) expect_equal(obj2, exp) }) test_that("subsetting works with both double and integer indexes", { x <- vroom(I("X1\nfoo"), delim = ",", col_types = "f") expect_equal(x$X1[1L], factor("foo")) expect_equal(x$X1[1], factor("foo")) expect_equal(x$X1[NA_integer_], factor(NA_character_, levels = "foo")) expect_equal(x$X1[NA_real_], factor(NA_character_, levels = "foo")) }) test_that("results are correct even with quoted values", { expect_equal( vroom(I('day\n"Sun"\n"Sat"\n"Sat"'), altrep = FALSE, col_types = "f", delim = ",")$day, factor(c("Sun", "Sat", "Sat"), levels = c("Sun", "Sat")) ) }) test_that("NAs are correctly encoded and not included in levels unless desired", { test_vroom("X\nunknown\nb\na\n", col_types = list(X = col_factor(levels = NULL, include_na = FALSE)), na = "unknown", equals = tibble::tibble(X = factor(c(NA, "b", "a"), levels = c("b", "a"), exclude = NULL)) ) test_vroom("X\nunknown\nb\na\nmissing\n", col_types = list(X = col_factor(levels = NULL, include_na = FALSE)), na = c("unknown", "missing"), equals = tibble::tibble(X = factor(c(NA, "b", "a", NA), levels = c("b", "a"), exclude = NULL)) ) test_vroom("X\nunknown\nb\na\n", col_types = list(X = col_factor(levels = NULL, include_na = TRUE)), na = "unknown", equals = tibble::tibble(X = factor(c(NA, "b", "a"), levels = c(NA, "b", "a"), exclude = NULL)) ) test_vroom("X\nunknown\nb\na\nmissing\n", col_types = list(X = col_factor(levels = NULL, include_na = TRUE)), na = c("unknown", "missing"), equals = tibble::tibble(X = factor(c(NA, "b", "a", NA), levels = c(NA, "b", "a"), exclude = NULL)) ) }) vroom/tests/testthat/test-big-int.R0000644000176200001440000000364514531406420017056 0ustar liggesusersas.integer64 <- bit64::as.integer64 test_that("integers are returned correctly", { # empty fields are returned as NAs test_vroom("foo,bar,baz\n1,,3\n", col_types = list(.default = "I"), delim = ",", equals = tibble::tibble(foo = as.integer64(1), bar = as.integer64(NA), baz = as.integer64(3)) ) # numbers which are not integers are returned as NAs test_vroom("foo,bar,baz\n1,1.5,3\n", col_types = list(.default = "I"), delim = ",", equals = tibble::tibble(foo = as.integer64(1), bar = as.integer64(NA), baz = as.integer64(3)) ) # fields with non-digits are returned as NAs test_vroom("foo,bar,baz\n1,32xyz,3\n", col_types = list(.default = "I"), delim = ",", equals = tibble::tibble(foo = as.integer64(1), bar = as.integer64(NA), baz = as.integer64(3)) ) # 2^31 - 1 is the maximum representable integer with 32 bit ints test_vroom("foo,bar,baz\n1,2147483647,3\n", col_types = list(.default = "I"), delim = ",", equals = tibble::tibble(foo = as.integer64(1), bar = as.integer64("2147483647"), baz = as.integer64(3)) ) # But 2^31 should also work test_vroom("foo,bar,baz\n1,2147483648,3\n", col_types = list(.default = "I"), delim = ",", equals = tibble::tibble(foo = as.integer64(1), bar = as.integer64("2147483648"), baz = as.integer64(3)) ) # As well as -2^31 test_vroom("foo,bar,baz\n1,9223372036854775807,3\n", col_types = list(.default = "I"), delim = ",", equals = tibble::tibble(foo = as.integer64(1), bar = as.integer64("9223372036854775807"), baz = as.integer64(3)) ) # But 2^63 should be NA test_vroom("foo,bar,baz\n1,9223372036854775808,3\n", col_types = list(.default = "I"), delim = ",", equals = tibble::tibble(foo = as.integer64(1), bar = as.integer64(NA), baz = as.integer64(3)) ) }) test_that("NA can be a big int value", { test_vroom(I("x\n1\n2\n"), delim = ",", col_types = "I", na = "1", equals = tibble::tibble(x = as.integer64(c(NA_integer_, 2L)))) }) vroom/tests/testthat/empty-file0000644000176200001440000000000014132374245016407 0ustar liggesusersvroom/tests/testthat/multi-byte-ascii.txt0000644000176200001440000000006313445253572020351 0ustar liggesusersid||name||age 1||ed||36 2||leigh||NA 3||nathan||14 vroom/tests/testthat/test-multi-byte.R0000644000176200001440000000101414132374245017612 0ustar liggesuserstest_that("multi-byte reading works with unicode delimiters and UTF-8 encoding", { test_vroom(test_path("multi-byte-ascii.txt"), delim = "||", equals = tibble::tibble(id = 1:3, name = c("ed", "leigh", "nathan"), age = c(36, NA, 14)) ) }) test_that("multi-byte reading works with unicode delimiters and UTF-8 encoding", { skip_on_os("solaris") test_vroom(test_path("multi-byte-unicode.txt"), delim = "\U2764", equals = tibble::tibble(id = 1:3, name = c("ed", "leigh", "nathan"), age = c(36, NA, 14)) ) }) vroom/tests/testthat/test-logical.R0000644000176200001440000000205014516024472017132 0ustar liggesuserstest_that("TRUE and FALSE parsed", { test_vroom("TRUE\nFALSE\n", col_names = FALSE, equals = tibble::tibble(X1 = c(TRUE, FALSE))) }) test_that("true and false parsed", { test_vroom("true\nfalse\n", col_names = FALSE, equals = tibble::tibble(X1 = c(TRUE, FALSE))) }) test_that("True and False parsed", { test_vroom("True\nFalse\n", col_names = FALSE, equals = tibble::tibble(X1 = c(TRUE, FALSE))) }) test_that("T and F parsed", { test_vroom("T\nF\n", col_names = FALSE, equals = tibble::tibble(X1 = c(TRUE, FALSE))) }) test_that("t and f parsed", { test_vroom("t\nf\n", col_names = FALSE, equals = tibble::tibble(X1 = c(TRUE, FALSE))) }) test_that("1 and 0 parsed", { # 1 and 0 are never guessed as logical, but they can be parsed as such if you # explicitly set the column type. test_vroom("1\n0\n", col_types = "l", col_names = FALSE, equals = tibble::tibble(X1 = c(TRUE, FALSE))) }) test_that("NA can be a logical value", { test_vroom("1\n0\n", col_types = "l", col_names = FALSE, na = "1", equals = tibble::tibble(X1 = c(NA, FALSE))) }) vroom/tests/testthat/test-int.R0000644000176200001440000000230514132374245016315 0ustar liggesuserstest_that("integers are returned correctly", { # empty fields are returned as NAs test_vroom("foo,bar,baz\n1,,3\n", col_types = list(.default = "i"), delim = ",", equals = tibble::tibble(foo = 1L, bar = NA_integer_, baz = 3L) ) # numbers which are not integers are returned as NAs test_vroom("foo,bar,baz\n1,1.5,3\n", col_types = list(.default = "i"), delim = ",", equals = tibble::tibble(foo = 1L, bar = NA_integer_, baz = 3L) ) # fields with non-digits are returned as NAs test_vroom("foo,bar,baz\n1,32xyz,3\n", col_types = list(.default = "i"), delim = ",", equals = tibble::tibble(foo = 1L, bar = NA_integer_, baz = 3L) ) # 2^31 - 1 is the maximum representable integer with 32 bit ints test_vroom("foo,bar,baz\n1,2147483647,3\n", col_types = list(.default = "i"), delim = ",", equals = tibble::tibble(foo = 1L, bar = 2147483647L, baz = 3L) ) test_vroom("foo,bar,baz\n1,2147483648,3\n", col_types = list(.default = "i"), delim = ",", equals = tibble::tibble(foo = 1L, bar = NA_integer_, baz = 3L) ) }) test_that("NA can be a int value", { test_vroom(I("x\n1\n2\n"), delim = ",", col_types = "i", na = "1", equals = tibble::tibble(x = c(NA_integer_, 2L))) }) vroom/tests/testthat/test-problems.R0000644000176200001440000001426114504102737017350 0ustar liggesuserstest_that("problems returns a detailed warning message", { expect_snapshot(vroom(I("a,b,c\nx,y,z,,"), altrep = FALSE, col_types = "ccc")) }) test_that("problems with data parsing works for single files", { expect_warning( x <- vroom(I("x,y\n1,2\n1,1.x\n"), col_types = "dd", altrep = FALSE), class = "vroom_parse_issue" ) probs <- problems(x) expect_equal(probs$row, 3) expect_equal(probs$col, 2) expect_equal(probs$expected, "a double") expect_equal(probs$actual, "1.x") }) test_that("problems works for multiple files", { out1 <- file.path(tempdir(), "out1.txt") out2 <- file.path(tempdir(), "out2.txt") on.exit(unlink(c(out1, out2))) writeLines("x,y\n1,2\n1,1.x\n2,2", out1) writeLines("x,y\n3.x,4\n1,2\n2,2", out2) expect_warning( x <- vroom(c(out1, out2), delim = ",", col_types = "dd", altrep = F), class = "vroom_parse_issue" ) probs <- problems(x) expect_equal(probs$row, c(3, 2)) expect_equal(probs$col, c(2, 1)) expect_equal(probs$expected, c("a double", "a double")) expect_equal(probs$actual, c("1.x", "3.x")) expect_equal(basename(probs$file), basename(c(out1, out2))) }) test_that("problems with number of columns works for single files", { expect_warning(probs3 <- problems(vroom(I("x,y,z\n1,2\n"), col_names = TRUE, col_types = "ddd", altrep = FALSE)), class = "vroom_parse_issue" ) expect_equal(probs3$row, 2) expect_equal(probs3$col, 2) expect_equal(probs3$expected, "3 columns") expect_equal(probs3$actual, "2 columns") expect_warning(probs3 <- problems(vroom(I("x,y,z\n1,2\n"), col_names = FALSE, col_types = "ddd", altrep = FALSE)), class = "vroom_parse_issue" ) expect_equal(probs3$row[[4]], 2) expect_equal(probs3$col[[4]], 2) expect_equal(probs3$expected[[4]], "3 columns") expect_equal(probs3$actual[[4]], "2 columns") expect_warning(probs4 <- problems(vroom(I("x,y\n1,2,3,4\n"), col_names = TRUE, col_types = "dd", altrep = FALSE)), class = "vroom_parse_issue" ) expect_equal(probs4$row[[2]], 2) expect_equal(probs4$col[[2]], 4) expect_equal(probs4$expected[[2]], "2 columns") expect_equal(probs4$actual[[2]], "4 columns") expect_warning(probs2 <- problems(vroom(I("x,y\n1,2,3,4\n"), col_names = FALSE, col_types = "dd", altrep = FALSE)), class = "vroom_parse_issue" ) expect_equal(probs2$row[[4]], 2) expect_equal(probs2$col[[4]], 4) expect_equal(probs2$expected[[4]], "2 columns") expect_equal(probs2$actual[[4]], "4 columns") }) test_that("parsing problems are shown for all datatypes", { skip_if(getRversion() < "3.5") types <- list( "an integer" = col_integer(), "a big integer" = col_big_integer(), "a double" = col_double(), "a number" = col_number(), "value in level set" = col_factor(levels = "foo"), "date in ISO8601" = col_date(), "date in ISO8601" = col_datetime(), "time in ISO8601" = col_time() ) for (i in seq_along(types)) { type <- types[[i]] expected <- names(types)[[i]] res <- vroom(I("x\nxyz\n"), delim = ",", col_types = list(type), altrep = TRUE) # This calls the type_Elt function expect_warning(res[[1]][[1]], class = "vroom_parse_issue") expect_equal(problems(res)$expected, expected) res <- vroom(I("x\nxyz\n"), delim = ",", col_types = list(type), altrep = TRUE) # This calls the read_type function expect_warning(vroom_materialize(res, replace = FALSE), class = "vroom_parse_issue") expect_equal(problems(res)$expected, expected) } expect_warning(res <- vroom(I("x\nxyz\n"), delim = ",", col_types = list(col_logical())), class = "vroom_parse_issue" ) }) test_that("problems that are generated more than once are not duplicated", { # On versions of R without ALTREP the warnings will happen at different times, # so we skip this test in those cases skip_if(getRversion() < "3.5") res <- vroom(I("x\n1\n2\n3\n4\n5\na"), col_types = "i", delim = ",") # generate first problem expect_warning(res[[1]][[6]], class = "vroom_parse_issue") # generate the same problem again res[[1]][[6]] probs <- problems(res) expect_equal(probs$row, 7) expect_equal(probs$col, 1) expect_equal(probs$expected, "an integer") }) test_that("problems return the proper row number", { expect_warning( x <- vroom(I("a,b,c\nx,y,z,,"), altrep = FALSE, col_types = "ccc"), class = "vroom_parse_issue" ) expect_equal(problems(x)$row, 2) expect_warning( y <- vroom(I("a,b,c\nx,y,z\nx,y,z,,"), altrep = FALSE, col_types = "ccc"), class = "vroom_parse_issue" ) expect_equal(problems(y)$row, 3) expect_warning( z <- vroom(I("a,b,c\nx,y,z,,\nx,y,z,,\n"), altrep = FALSE, col_types = "ccc"), class = "vroom_parse_issue" ) expect_equal(problems(z)$row, c(2, 3)) }) # https://github.com/tidyverse/vroom/pull/441#discussion_r883611090 test_that("can promote vroom parse warning to error", { make_warning <- function() { x <- vroom( I("a\nx\n"), delim = ",", col_types = "d", altrep = TRUE ) # Trigger vroom parse warning while inside R's internal C code for `[` and ensure it doesn't crash R. # `[` -> R's C function `do_subset()` -> ALTREP calls `vroom::real_Elt()` -> `vroom::warn_for_errors()` # To avoid calling `cpp11::unwind_protect()` (which throws on longjmp, i.e. on `abort()`) while inside # R's internal C code (which doesn't catch C++ exceptions), `vroom::warn_for_errors()` warns # with cli called from base R's machinery, rather than from `cpp11::package()` # https://github.com/r-lib/cpp11/issues/274 # https://github.com/tidyverse/vroom/pull/441#discussion_r883611090 x$a[1] } expect_error( # This fails hard if we unwind protect the warning (aborts RStudio) # - Try to throw error after catching the warning withCallingHandlers( expr = make_warning(), vroom_parse_issue = function(cnd) { abort("oh no") } ) ) }) test_that("emits an error message if provided incorrect input", { # user provides something other than a data frame a_vector <- c(1, 2, 3) expect_snapshot(problems(a_vector), error = TRUE) # user provides a data frame from an incorrect source a_tibble <- tibble::tibble(x = c(1), y = c(2)) expect_snapshot(problems(a_tibble), error = TRUE) }) vroom/tests/testthat/test-vroom.R0000644000176200001440000006552614422654617016711 0ustar liggesuserstest_that("vroom can read a tsv", { test_vroom("a\tb\tc\n1\t2\t3\n", delim = "\t", equals = tibble::tibble(a = 1, b = 2, c = 3) ) }) test_that("vroom can read a csv", { test_vroom("a,b,c\n1,2,3\n", delim = ",", equals = tibble::tibble(a = 1, b = 2, c = 3) ) }) test_that("vroom guesses columns with NAs", { test_vroom("a,b,c\nNA,2,3\n4,5,6\n", delim = ",", equals = tibble::tibble(a = c(NA, 4), b = c(2, 5), c = c(3, 6)) ) test_vroom("a,b,c\nfoo,2,3\n4,5,6\n", delim = ",", na = "foo", equals = tibble::tibble(a = c(NA, 4), b = c(2, 5), c = c(3, 6)) ) test_vroom("a,b,c\nfoo,2,3\n4.0,5,6\n", delim = ",", na = "foo", equals = tibble::tibble(a = c(NA, 4), b = c(2, 5), c = c(3, 6)) ) test_vroom("a,b,c\nfoo,2,3\nbar,5,6\n", delim = ",", na = "foo", equals = tibble::tibble(a = c(NA, "bar"), b = c(2, 5), c = c(3, 6)) ) }) test_that("vroom can trim whitespace", { test_vroom('a,b,c\n foo , bar ,baz\n', delim = ",", equals = tibble::tibble(a = "foo", b = "bar", c = "baz") ) test_vroom('a,b,c\n\tfoo\t,\t\tbar\t\t,baz\n', delim = ",", equals = tibble::tibble(a = "foo", b = "bar", c = "baz") ) # whitespace trimmed before quotes test_vroom('a,b,c\n "foo" , "bar" ,"baz"\n', delim = ",", equals = tibble::tibble(a = "foo", b = "bar", c = "baz") ) # whitespace trimmed inside quotes test_vroom('a,b,c\n"foo "," bar","\t\tbaz"\n', delim = ",", equals = tibble::tibble(a = "foo", b = "bar", c = "baz") ) }) test_that("vroom can read files with quotes", { test_vroom('"a","b","c"\n"foo","bar","baz"\n', delim = ",", equals = tibble::tibble(a = "foo", b = "bar", c = "baz") ) test_vroom('"a","b","c"\n",foo","bar","baz"\n', delim = ",", equals = tibble::tibble(a = ",foo", b = "bar", c = "baz") ) test_vroom("'a','b','c'\n',foo','bar','baz'\n", delim = ",", quote = "'", equals = tibble::tibble(a = ",foo", b = "bar", c = "baz") ) }) test_that("vroom escapes double quotes", { test_vroom('"a","b","c"\n"""fo""o","b""""ar","baz"""\n', delim = ",", equals = tibble::tibble(a = "\"fo\"o", b = "b\"\"ar", c = "baz\"") ) }) test_that("vroom escapes backslashes", { test_vroom('a,b,c\n\\,foo,\\"ba\\"r,baz\\"\n', delim = ",", escape_backslash = TRUE, equals = tibble::tibble(a = ",foo", b = "\"ba\"r", c = "baz\"") ) }) test_that("vroom ignores leading whitespace", { test_vroom('\n\n \t \t\n \n\na,b,c\n1,2,3\n', delim = ",", equals = tibble::tibble(a = 1, b = 2, c = 3) ) }) test_that("vroom ignores comments", { test_vroom('\n\n \t #a,b,c\na,b,c\n1,2,3\n', delim = ",", comment = "#", equals = tibble::tibble(a = 1, b = 2, c = 3) ) }) test_that("vroom respects skip", { test_vroom('#a,b,c\na,b,c\n1,2,3\n', delim = ",", skip = 1, equals = tibble::tibble(a = 1, b = 2, c = 3) ) test_vroom('#a,b,c\na,b,c\n1,2,3\n', delim = ",", skip = 1, comment = "#", equals = tibble::tibble(a = 1, b = 2, c = 3) ) test_vroom('#a,b,c\nasdfasdf\na,b,c\n1,2,3\n', delim = ",", skip = 2, comment = "#", equals = tibble::tibble(a = 1, b = 2, c = 3) ) test_vroom('\n\n#a,b,c\nasdfasdf\na,b,c\n1,2,3\n', delim = ",", skip = 4, comment = "#", equals = tibble::tibble(a = 1, b = 2, c = 3) ) }) test_that("vroom respects col_types", { test_vroom('a,b,c\n1,2,3\n', delim = ",", col_types = "idc", equals = tibble::tibble(a = 1L, b = 2, c = "3") ) test_vroom('a,b,c,d\nT,2,3,4\n', delim = ",", col_types = "lfc_", equals = tibble::tibble(a = TRUE, b = factor(2), c = "3") ) }) test_that("vroom handles UTF byte order marks", { # UTF-8 expect_equal( vroom(as.raw(c(0xef, 0xbb, 0xbf, # BOM 0x41, # A 0x0A # newline )), delim = "\n", col_names = FALSE, col_types = list() )[[1]], "A") # UTF-16 Big Endian expect_equal( vroom(as.raw(c(0xfe, 0xff, # BOM 0x41, # A 0x0A # newline )), delim = "\n", col_names = FALSE, col_types = list() )[[1]], "A") # UTF-16 Little Endian expect_equal( vroom(as.raw(c(0xff, 0xfe, # BOM 0x41, # A 0x0A # newline )), delim = "\n", col_names = FALSE, col_types = list() )[[1]], "A") # UTF-32 Big Endian expect_equal( vroom(as.raw(c(0x00, 0x00, 0xfe, 0xff, # BOM 0x41, # A 0x0A # newline )), delim = "\n", col_names = FALSE, col_types = list() )[[1]], "A") # UTF-32 Little Endian expect_equal( vroom(as.raw(c(0xff, 0xfe, 0x00, 0x00, # BOM 0x41, # A 0x0A # newline )), delim = "\n", col_names = FALSE, col_types = list() )[[1]], "A") }) test_that("vroom handles vectors shorter than the UTF byte order marks", { skip_on_os("solaris") expect_equal( charToRaw(vroom(as.raw(c(0xef, 0xbb, 0x0A)), delim = "\n", col_names = FALSE, col_types = list())[[1]]), as.raw(c(0xef, 0xbb)) ) expect_equal( charToRaw(vroom(as.raw(c(0xfe, 0x0A)), delim = "\n", col_names = FALSE, col_types = list())[[1]]), as.raw(c(0xfe)) ) expect_equal( charToRaw(vroom(as.raw(c(0xff, 0x0A)), delim = "\n", col_names = FALSE, col_types = list())[[1]]), as.raw(c(0xff)) ) }) test_that("vroom handles windows newlines", { expect_equal( vroom(I("a\tb\r\n1\t2\r\n"), trim_ws = FALSE, col_types = list())[[1]], 1 ) }) test_that("vroom can read a file with only headers", { test_vroom("a\n", equals = tibble::tibble(a = character()) ) test_vroom("a,b,c\n", delim = ",", equals = tibble::tibble(a = character(), b = character(), c = character()) ) }) test_that("vroom can read an empty file", { test_vroom("\n", equals = tibble::tibble() ) f <- tempfile() file.create(f) on.exit(unlink(f)) capture.output(type = "message", expect_equal(vroom(f, col_types = list()), tibble::tibble()) ) capture.output(type = "message", expect_equal(vroom(f, col_names = FALSE, col_types = list()), tibble::tibble()) ) expect_equal(vroom(character(), col_types = list()), tibble::tibble()) }) test_that("vroom_examples() returns the example files", { expect_equal(vroom_examples(), list.files(system.file("extdata", package = "vroom"))) }) test_that("vroom_example() returns a single example files", { expect_equal(vroom_example("mtcars.csv"), system.file("extdata", "mtcars.csv", package = "vroom")) }) test_that("subsets work", { res <- vroom(I("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14"), delim = "\t", col_names = FALSE, col_types = list()) expect_equal(head(res[[1]]), c(1:6)) expect_equal(tail(res[[1]]), c(9:14)) expect_equal(tail(res[[1]][3:8]), c(3:8)) }) test_that("n_max works with normal files", { expect_equal( NROW(vroom(vroom_example("mtcars.csv"), n_max = 2, col_types = list())), 2 ) # headers don't count expect_equal( NROW(vroom(vroom_example("mtcars.csv"), n_max = 2, col_names = FALSE, col_types = list())), 2 ) # Zero rows with headers should just have the headers expect_equal( dim(vroom(vroom_example("mtcars.csv"), n_max = 0, col_types = list())), c(0, 12) ) # If you don't read the header or any rows it must be empty expect_equal( dim(vroom(vroom_example("mtcars.csv"), n_max = 0, col_names = FALSE, col_types = list())), c(0, 0) ) }) test_that("n_max works with connections files", { expect_equal( NROW(vroom(vroom_example("mtcars.csv.gz"), n_max = 2, col_types = list())), 2 ) # headers don't count expect_equal( NROW(vroom(vroom_example("mtcars.csv.gz"), n_max = 2, col_names = FALSE, col_types = list())), 2 ) # Zero rows with headers should just have the headers expect_equal( dim(vroom(vroom_example("mtcars.csv.gz"), n_max = 0, col_types = list())), c(0, 12) ) # If you don't read the header or any rows it must be empty expect_equal( dim(vroom(vroom_example("mtcars.csv.gz"), n_max = 0, col_names = FALSE, col_types = list())), c(0, 0) ) }) test_that("vroom truncates col_names if it is too long", { test_vroom("1\n2\n", col_names = c("a", "b"), equals = tibble::tibble(a = c(1, 2)) ) }) test_that("vroom makes additional col_names if it is too short", { test_vroom("1,2,3\n4,5,6\n", col_names = c("a", "b"), delim = ",", equals = tibble::tibble(a = c(1, 4), b = c(2, 5), X3 = c(3, 6)) ) }) test_that("vroom reads newlines in data", { test_vroom('a\n"1\n2"\n', equals = tibble::tibble(a = "1\n2")) }) test_that("vroom reads headers with embedded newlines", { test_vroom("\"Header\nLine Two\"\nValue\n", delim = ",", equals = tibble::tibble("Header\nLine Two" = "Value") ) test_vroom("\"Header\",\"Second header\nLine Two\"\nValue,Value2\n", delim = ",", equals = tibble::tibble("Header" = "Value", "Second header\nLine Two" = "Value2") ) }) test_that("vroom reads headers with embedded newlines 2", { test_vroom("\"Header\nLine Two\"\n\"Another line\nto\nskip\"\nValue,Value2\n", skip = 2, col_names = FALSE, delim = ",", equals = tibble::tibble("X1" = "Value", "X2" = "Value2") ) }) test_that("vroom uses the number of rows when guess_max = Inf", { tf <- tempfile() df <- tibble::tibble(x = c(1:1000, "foo", 1001)) vroom_write(df, tf, delim = "\t") # The type should be guessed wrong, because the character comes at the end expect_warning(res <- vroom(tf, delim = "\t", col_types = list(), altrep = FALSE)) expect_type(res[["x"]], "double") expect_true(is.na(res[["x"]][[NROW(res) - 1]])) # The value should exist with guess_max = Inf res <- vroom(tf, delim = "\t", guess_max = Inf, col_types = list()) expect_type(res[["x"]], "character") expect_equal(res[["x"]][[NROW(res) - 1]], "foo") }) test_that("vroom adds columns if a row is too short", { test_vroom("a,b,c,d\n1,2\n3,4,5,6\n", delim = ",", equals = tibble::tibble("a" = c(1,3), "b" = c(2,4), "c" = c(NA, 5), "d" = c(NA, 6)) ) }) test_that("vroom removes columns if a row is too long", { test_vroom("a,b,c,d\n1,2,3,4,5,6,7\n8,9,10,11\n", delim = ",", col_types = c(d = "c"), equals = tibble::tibble("a" = c(1,8), "b" = c(2,9), "c" = c(3, 10), "d" = c("4,5,6,7", "11")) ) }) # Figure out a better way to test progress bars... #test_that("progress bars work", { #withr::with_options(c("vroom.show_after" = 0), { #expect_output_file(vroom(vroom_example("mtcars.csv"), progress = TRUE), "mtcars-progress") #}) #}) test_that("guess_type works with long strings (#74)", { expect_s3_class( guess_type("https://www.bing.com/search?q=mr+popper%27s+penguins+worksheets+free&FORM=QSRE1"), "collector_character" ) }) test_that("vroom guesses types if unnamed column types do not match the number of columns", { test_vroom(I("a,b\n1,2\n"), delim = ",", col_types = "i", equals = tibble::tibble(a = 1L, b = 2L)) }) test_that("column names are properly encoded", { skip_on_os("solaris") nms <- vroom(I("f\U00F6\U00F6\nbar\n"), delim = "\n", col_types = list()) expect_equal(Encoding(colnames(nms)), "UTF-8") }) test_that("Files with windows newlines and missing fields work", { test_vroom("a,b,c,d\r\nm,\r\n\r\n", delim = ",", skip_empty_rows = FALSE, equals = tibble::tibble(a = c("m", NA), b = c(NA, NA), c = c(NA, NA), d = c(NA, NA)) ) }) test_that("vroom can read files with no trailing newline", { f <- tempfile() on.exit(unlink(f)) writeBin(charToRaw("foo\nbar"), f) expect_equal(vroom(f, col_names = FALSE, delim = ",", col_types = list())[[1]], c("foo", "bar")) f2 <- tempfile() on.exit(unlink(f2), add = TRUE) writeBin(charToRaw("foo,bar\n1,2"), f2) expect_equal(vroom(f2, delim = ",", col_types = list()), tibble::tibble(foo = 1, bar = 2)) }) test_that("Missing files error with a nice error message", { f <- tempfile() expect_error(vroom(f, col_types = list()), "does not exist") expect_error(vroom("foo", col_types = list()), "does not exist in current working directory") }) test_that("Can return the spec object", { x <- vroom(I("foo,bar\n1,c\n"), col_types = list()) obj <- spec(x) expect_s3_class(obj, "col_spec") exp <- as.col_spec(list(foo = "d", bar = "c")) exp$delim <- "," expect_equal(obj, exp) }) test_that("vroom handles files with trailing commas, windows newlines, missing a final newline and not null terminated", { f <- tempfile() on.exit(unlink(f)) writeChar(paste(collapse = "\r\n", c('foo,bar,', '1,2,')), con = f, eos = NULL) expect_message(regexp = "New names", expect_equal( vroom(f, col_types = list()), tibble::tibble(foo = 1, bar = 2, "...3" = NA) ) ) }) test_that("vroom uses the delim if it is specified in the col_types", { # if we give a tab delim in the spec there should only be one column expect_equal( ncol(vroom(I("a,b,c\n1,2,3\n"), col_types = list(.delim = "\t"))), 1 ) # But specifying an explicit delim overrides the spec expect_equal( ncol(vroom(I("a,b,c\n1,2,3\n"), col_types = list(.delim = "\t"), delim = ",")), 3 ) expect_equal( ncol(vroom(I("a,b,c\n1,2,3\n"), col_types = list(.delim = ","), delim = "\t")), 1 ) }) test_that("vroom supports NA and NA_integer_ indices", { data <- vroom(vroom_example("mtcars.csv"), col_types = list()) expect_equal(data[NA, 1, drop = TRUE], rep(NA_character_, nrow(data))) expect_equal(data[NA_integer_, 1, drop = TRUE], NA_character_) }) test_that("vroom supports NA and NA_integer_ indices with factors and datetimes", { data <- vroom(I("x\ty\nfoo\t2020-01-01 12:00:01"), col_types = "fT") expect_equal(data[NA, 1, drop = TRUE], factor(NA, levels = "foo")) expect_equal(data[NA, 2, drop = TRUE], .POSIXct(NA_real_, tz = "UTC")) expect_equal(data[NA_integer_, 1, drop = TRUE], factor(NA, levels = "foo")) expect_equal(data[NA_integer_, 2, drop = TRUE], .POSIXct(NA_real_, tz = "UTC")) }) test_that("vroom works with windows newlines and files without a trailing newline (#219)", { f <- tempfile() on.exit(unlink(f)) writeBin(charToRaw("X,Y\r\n1,12/08/2016\r\n2,05/01/2018"), f) res <- vroom(f, col_types = cols(Y = "c")) expect_equal(res$Y[[2]], "05/01/2018") }) test_that("vroom works with `id` and skipped columns", { data <- vroom(vroom_example("mtcars.csv"), col_types = c(mpg = "_"), id = "File") expect_true(ncol(data) == 12) expect_true(names(data)[[1]] == "File") expect_false("mpg" %in% names(data)) }) test_that("vroom works with n_max, windows newlines and files larger than the connection buffer", { f <- tempfile() on.exit(unlink(f)) writeBin(charToRaw("X,Y\r\n1,2\r\n3342343242312312,442342432423432432\r\n432424324,532432324"), f) withr::with_envvar(c("VROOM_CONNECTION_SIZE" = 25), res <- vroom(f, delim = ",", n_max = 1, col_types = list()) ) expect_equal(res$X, 1) expect_equal(res$Y, 2) }) test_that("subsetting works with both double and integer indexes", { x <- vroom(I("X1\nfoo"), delim = ",", col_types = list()) expect_equal(x$X1[1L], "foo") expect_equal(x$X1[1], "foo") expect_equal(x$X1[NA_integer_], NA_character_) expect_equal(x$X1[NA_real_], NA_character_) }) test_that("quotes inside fields are ignored", { x <- vroom(I("x\nfoo\"bar\nbaz\n"), delim = ",", quote = "\"", col_types = list()) expect_equal(x$x[[1]], "foo\"bar") expect_equal(x$x[[2]], "baz") }) test_that("quotes at the beginning and end of lines are used", { y <- vroom(I("x\n\"foo\"\"bar\"\nbaz\n"), delim = ",", quote = "\"", col_types = list()) expect_equal(y$x[[1]], "foo\"bar") expect_equal(y$x[[2]], "baz") }) test_that("quotes at delimiters are used", { z <- vroom(I("x,y,z\n1,\"foo\"\"bar\",2\n3,baz,4"), delim = ",", quote = "\"", col_types = list()) expect_equal(z$y[[1]], "foo\"bar") expect_equal(z$y[[2]], "baz") }) test_that("vroom reads files with embedded newlines even when num_threads > 1", { tf <- tempfile() con <- file(tf, "wb") on.exit({ unlink(tf) }) writeLines(c("x", rep("foo", 1000), '"bar\nbaz"', rep("qux", 1000)), con, sep = "\n") close(con) res <- vroom(tf, delim = ",", num_threads = 5, col_types = list()) expect_equal(nrow(res), 1000 + 1 + 1000) expect_equal(res$x[[1001]], "bar\nbaz") }) test_that("multi-character comments are supported", { res <- vroom(I("## this is a comment\n# this is not"), delim = "\t", comment = "##", col_names = FALSE, col_types = list()) expect_equal(res[[1]], "# this is not") }) test_that("vroom works with quoted fields at the end of a windows newline", { f <- tempfile() on.exit(unlink(f)) con <- file(f, "wb") writeLines(c('"x"', 1), con, sep = "\r\n") close(con) res <- vroom(f, delim = ",", col_names = FALSE, col_types = list()) expect_equal(res[[1]], c("x", 1)) }) test_that("vroom can handle NUL characters in strings", { test_vroom(test_path("raw.csv"), delim = ",", progress = FALSE, equals = tibble::tibble(abc = "ab", def = "def") ) }) test_that("n_max is respected in all cases", { expect_equal(dim(vroom(I("x\ty\tz\n1\t2\t3\n4\t5\t6\n"), n_max = 1, col_types = list())), c(1, 3)) }) test_that("comments are ignored regardless of where they appear", { out1 <- vroom(I('x\n1#comment'), comment = "#", col_types = "d", delim = ",") out2 <- vroom(I('x\n1#comment\n#comment'), comment = "#", col_types = "d", delim = ",") out3 <- vroom(I('x\n"1"#comment'), comment = "#", col_types = "d", delim = ",") expect_equal(out1$x, 1) expect_equal(out2$x, 1) expect_equal(out3$x, 1) out4 <- vroom(I('x,y\n1,#comment'), comment = "#", delim = ",", col_types = "cc", progress = FALSE, altrep = FALSE) expect_equal(out4$y, NA_character_) expect_warning(out5 <- vroom(I("x1,x2,x3\nA2,B2,C2\nA3#,B2,C2\nA4,A5,A6"), comment = "#", delim = ",", col_types = "ccc", altrep = FALSE, progress = FALSE)) expect_warning(out6 <- vroom(I("x1,x2,x3\nA2,B2,C2\nA3,#B2,C2\nA4,A5,A6"), comment = "#", delim = ",", col_types = "ccc", altrep = FALSE, progress = FALSE)) expect_warning(out7 <- vroom(I("x1,x2,x3\nA2,B2,C2\nA3,#B2,C2\n#comment\nA4,A5,A6"), comment = "#", delim = ",", col_types = "ccc", altrep = FALSE, progress = FALSE)) chk <- tibble::tibble( x1 = c("A2", "A3", "A4"), x2 = c("B2", NA_character_, "A5"), x3 = c("C2", NA_character_, "A6")) expect_true(all.equal(chk, out5, check.attributes = FALSE)) expect_true(all.equal(chk, out6, check.attributes = FALSE)) expect_true(all.equal(chk, out7, check.attributes = FALSE)) }) test_that("escaped/quoted comments are ignored", { out1 <- vroom(I('x\n\\#'), comment = "#", delim = ",", escape_backslash = TRUE, escape_double = FALSE, progress = FALSE, col_types = "c") out2 <- vroom(I('x\n"#"'), comment = "#", progress = FALSE, delim = ",", col_types = "c") expect_equal(out1$x, "#") expect_equal(out2$x, "#") }) test_that("name repair with custom functions works", { add_y <- function(x) { paste(x, "y", sep = "_") } out <- vroom(I("x,y,z\n1,2,3"), col_types = "iii", .name_repair = add_y) expect_equal(colnames(out), c("x_y", "y_y", "z_y")) }) test_that("col_types are based on the final (possibly repaired) column names (#311)", { suppressMessages( out <- vroom(I("x,\n1,2\n3,4"), delim = ",", col_types = list(x = col_double(), "...2" = col_double())) ) expect_equal(out[["...2"]], c(2, 4)) }) test_that("mismatched column names throw a classed warning", { expect_warning( vroom( I("x,y\n1,2\n3,4\n"), col_types = list( x = col_double(), y = col_double(), z = col_double() ) ), class = "vroom_mismatched_column_name" ) }) test_that("empty files still generate the correct column width and types", { out <- vroom(I(""), col_names = c("foo", "bar"), col_types = list()) expect_equal(nrow(out), 0) expect_equal(ncol(out), 2) expect_equal(names(out), c("foo", "bar")) expect_type(out[[1]], "character") expect_type(out[[2]], "character") out <- vroom(I(""), col_types = "ii") expect_equal(nrow(out), 0) expect_equal(ncol(out), 2) expect_equal(names(out), c("X1", "X2")) expect_type(out[[1]], "integer") expect_type(out[[2]], "integer") }) test_that("leading whitespace effects guessing", { out <- vroom(I('a,b,c\n 1,2,3\n'), delim = ",", trim_ws = FALSE, progress = FALSE, col_types = list()) expect_type(out[[1]], "character") out <- vroom(I('a,b,c\n 1,2,3\n'), delim = ",", trim_ws = TRUE, progress = FALSE, col_types = list()) expect_type(out[[1]], "double") }) test_that("UTF-16LE encodings can be read", { bom <- as.raw(c(255, 254)) # This is the text. text <- "x,y\n\U104371,2\n" # This is a 4 byte UTF-16 character from https://en.wikipedia.org/wiki/UTF-16 # Converted to UTF-16LE text_utf16 <- iconv(text,from="UTF-8", to="UTF-16LE", toRaw = TRUE)[[1]] # Write the BOM and the text to a file tmp_file_name <- tempfile() fd <- file(tmp_file_name, "wb") writeBin(bom, fd) writeBin(text_utf16, fd) close(fd) # Whether LE or BE is determined automatically by the BOM out <- vroom(tmp_file_name, locale = locale(encoding = "UTF-16"), col_types = "ci") expect_equal(out$x, "\U104371") expect_equal(out$y, 2) }) test_that("supports unicode grouping and decimal marks (https://github.com/tidyverse/readr/issues/796)", { test_vroom(I("1\u00A0234\u02D95"), locale = locale(grouping_mark = "\u00A0", decimal_mark = "\u02D9"), col_types = "n", col_names = FALSE, delim = ",", equals = tibble::tibble(X1 = 1234.5) ) }) test_that("handles quotes within skips", { data <- I(paste0(collapse = "\n", c("a\tb\tc", "1a\t1b\t1c", "2a\t2b\t2c\"", "3a\t3b\t3c\"", "4a\t4b\t4c" ))) test_vroom(data, col_names = c("a", "b", "c"), skip = 2, quote = "", delim = "\t", equals = tibble::tibble( a = c("2a", "3a", "4a"), b = c("2b", "3b", "4b"), c = c("2c\"", "3c\"", "4c") ) ) test_vroom(data, col_names = c("a", "b", "c"), skip = 3, quote = "", delim = "\t", equals = tibble::tibble( a = c("3a", "4a"), b = c("3b", "4b"), c = c("3c\"", "4c") ) ) test_vroom(data, col_names = c("a", "b", "c"), skip = 4, quote = "", delim = "\t", equals = tibble::tibble( a = c("4a"), b = c("4b"), c = c("4c") ) ) }) test_that("skipped columns retain their name", { test_vroom(I("1,2,3\n4,5,6"), col_names = "x", col_types = "i__", equals = tibble::tibble( x = c(1L, 4L) )) test_vroom(I("1,2,3\n4,5,6"), col_names = "y", col_types = "_i_", equals = tibble::tibble( y = c(2L, 5L) )) test_vroom(I("1,2,3\n4,5,6"), col_names = "z", col_types = "__i", equals = tibble::tibble( z = c(3L, 6L) )) test_vroom(I("1,2,3\n4,5,6"), col_names = c("x", "z"), col_types = "i_i", equals = tibble::tibble( x = c(1L, 4L), z = c(3L, 6L) )) }) test_that("skipped columns retain their name", { test_vroom(I("1,2,3\n4,5,6"), col_names = "x", col_types = "i__", equals = tibble::tibble( x = c(1L, 4L) )) test_vroom(I("1,2,3\n4,5,6"), col_names = "y", col_types = "_i_", equals = tibble::tibble( y = c(2L, 5L) )) test_vroom(I("1,2,3\n4,5,6"), col_names = "z", col_types = "__i", equals = tibble::tibble( z = c(3L, 6L) )) test_vroom(I("1,2,3\n4,5,6"), col_names = c("x", "z"), col_types = "i_i", equals = tibble::tibble( x = c(1L, 4L), z = c(3L, 6L) )) }) test_that("unnamed column types can be less than the number of columns", { test_vroom("x,y\n1,2\n", col_types = "i", equals = tibble::tibble( x = 1L, y = 2L )) }) test_that("always include the last row when guessing (#352)", { f <- tempfile() on.exit(unlink(f)) vroom_write(data.frame("x" = c(rep(NA, 10), 5)), delim = ",", file = f) x <- vroom(f, col_types = "?", guess_max = 5, delim = ",") expect_type(x[[1]], "double") }) test_that("vroom works with quote even in the first two lines (#1262)", { text <- c("1,'I am sam' 2,'sam I am'") test_vroom(text, col_names = FALSE, quote = "'", delim = ",", equals = tibble::tibble(X1 = c(1, 2), X2 = c("I\nam\nsam", "sam\nI\nam"))) }) test_that("vroom works when grouping_mark is empty (#1241)", { x <- vroom(I("foo\nbar"), locale = locale(grouping_mark = ""), delim = ",", col_names = FALSE, col_types = "c") expect_equal(x[[1]], c("foo", "bar")) }) test_that("vroom works if given col_names and col_types less than the number of columns (https://github.com/tidyverse/readr/issues/1271)", { x <- vroom( I("a\tb\n"), delim = "\t", col_names = c("x"), col_types = list("x" = "c") ) expect_equal(x[["x"]], "a") expect_equal(x[["X2"]], "b") }) test_that("vroom works with CR line endings only", { test_vroom(I("a,b\r1,2\r3,4\r"), delim = ",", equals = tibble::tibble(a = c(1, 3), b = c(2, 4)) ) }) test_that("vroom works with quotes in comments", { test_vroom(I("a,b\n#bar \" xyz\n1,2"), delim = ",", comment = "#", equals = tibble::tibble(a = 1, b = 2) ) test_vroom(I("#foo \" \na,b\n#bar \" xyz\n1,2"), delim = ",", comment = "#", equals = tibble::tibble(a = 1, b = 2) ) }) test_that("vroom works with comments at end of lines (https://github.com/tidyverse/readr/issues/1309)", { test_vroom(I("foo,bar#\n1,#\n2#\n#\n3\n"), delim = ",", comment = "#", equals = tibble::tibble(foo = c(1,2,3), bar = c(NA, NA, NA)) ) }) test_that("vroom does not erronously warn for problems when there are embedded newlines and parsing needs to be restarted (https://github.com/tidyverse/readr/issues/1313))", { withr::local_seed(1) sample_values <- function(n, p_safe) { sample(c("safe", "UNSAFE\n"), n, replace = TRUE, prob = c(p_safe, 1 - p_safe)) } n <- 300 df <- tibble::tibble( a = sample_values(n, p_safe = .99), b = sample_values(n, p_safe = .01), c = sample_values(n, p_safe = .01) ) # write to temp file path <- tempfile(pattern = "quoted_newlines_", fileext = ".csv") withr::defer(unlink(path)) vroom_write(df, path, delim = ",") x <- vroom(path, delim = ",", col_types = list()) y <- utils::read.csv(path, stringsAsFactors = FALSE) expect_warning(expect_equal(as.data.frame(x), y), NA) }) test_that("n_max works with files without a trailing newline for file connections (https://github.com/tidyverse/readr/issues/1321)", { f <- tempfile() on.exit(unlink(f)) writeBin(charToRaw("foo,bar 1,2 3,4 5,6"), f) x <- vroom(f, n_max = Inf, delim = ",", col_types = list()) y <- vroom(f, n_max = 4, delim = ",", col_types = list()) z <- vroom(f, n_max = 5, delim = ",", col_types = list()) expect_equal(y, x) expect_equal(z, x) }) # https://github.com/tidyverse/vroom/issues/453 test_that("vroom can read a date column with no data and skip 1", { test_vroom("date\n", delim = ",", col_names = 'date', col_types = 'D', skip = 1, equals = tibble::tibble(date = as.Date(character())) ) }) # https://github.com/tidyverse/vroom/issues/453 test_that("vroom can read a datetime column with no data and skip 1", { test_vroom("dt\n", delim = ",", col_names = 'dt', col_types = 'T', skip = 1, equals = tibble::tibble(dt = as.POSIXct(character())) ) }) vroom/tests/testthat/test-path.R0000644000176200001440000001422614505145252016462 0ustar liggesuserstest_that("vroom errors if the file does not exist", { tf <- tempfile() expect_error(vroom(tf, col_types = list()), "does not exist") }) test_that("vroom works with compressed files", { mt <- vroom(vroom_example("mtcars.csv"), col_types = list()) expect_equal(vroom(vroom_example("mtcars.csv.gz"), col_types = list()), mt) expect_equal(vroom(vroom_example("mtcars.csv.bz2"), col_types = list()), mt) expect_equal(vroom(vroom_example("mtcars.csv.xz"), col_types = list()), mt) expect_equal(vroom(vroom_example("mtcars.csv.zip"), col_types = list()), mt) }) test_that("read_file works via https", { skip_on_cran() mt <- vroom(vroom_example("mtcars.csv"), col_types = list()) url <- "https://raw.githubusercontent.com/tidyverse/vroom/main/inst/extdata/mtcars.csv" expect_equal(vroom(url, col_types = list()), mt) }) test_that("vroom works via https on gz file", { skip_on_cran() mt <- vroom(vroom_example("mtcars.csv"), col_types = list()) url <- "https://raw.githubusercontent.com/tidyverse/vroom/main/inst/extdata/mtcars.csv.gz" expect_equal(vroom(url, col_types = list()), mt) }) test_that("vroom errors via https on non-gz file", { skip_on_cran() url <- "https://raw.githubusercontent.com/tidyverse/vroom/main/inst/extdata/mtcars.csv.bz2" expect_error(vroom(url, col_types = list()), "Reading from remote `bz2` compressed files is not supported") }) test_that("split_path_ext works", { expect_equal( split_path_ext(character()), list(path = character(), extension = "") ) expect_equal( split_path_ext(""), list(path = "", extension = "") ) expect_equal( split_path_ext("foo"), list(path = "foo", extension = "") ) expect_equal( split_path_ext("foo/bar.baz"), list(path = "foo/bar", extension = "baz") ) expect_equal( split_path_ext("foo/bar.tar.gz"), list(path = "foo/bar", extension = "tar.gz") ) }) test_that("vroom() informs user to use I() for literal data (or not)", { expect_snapshot( x <- vroom("a,b,c,d\n1,2,3,4", show_col_types = FALSE) ) }) test_that("can write to a zip file if the archive package is available", { skip_on_cran() skip_if_not_installed("archive") tempfile <- file.path(tempdir(), "mtcars.zip") on.exit(unlink(tempfile)) vroom_write(mtcars, tempfile) # PK is the zip magic number expect_equal( readBin(tempfile, raw(), n = 2), as.raw(c(0x50, 0x4b)) ) }) test_that("can write to a tar.gz file if the archive package is available", { skip_on_cran() skip_if_not_installed("archive") tempfile <- file.path(tempdir(), "mtcars.tar.gz") on.exit(unlink(tempfile)) vroom_write(mtcars, tempfile) # 1F 8B is the gz magic number expect_equal( readBin(tempfile, raw(), n = 2), as.raw(c(0x1f, 0x8b)) ) res <- archive::archive(tempfile) expect_equal(res$path, "mtcars") expect_equal(res$size, 1281) }) # https://github.com/tidyverse/vroom/issues/394 test_that("can read file w/o final newline, w/ multi-byte characters in path", { pattern <- "no-trailing-n\u00e8wline-m\u00fblti-byt\u00e9-path-" tfile <- withr::local_tempfile(pattern = pattern, fileext = ".csv") writeChar("a,b\nA,B", con = tfile, eos = NULL) expect_equal( vroom(tfile, show_col_types = FALSE), tibble::tibble(a = "A", b = "B") ) }) # for completeness, w.r.t. test above test_that("can read file w/ final newline, w/ multi-byte characters in path", { pattern <- "yes-trailing-n\u00e8wline-m\u00fblti-byt\u00e9-path-" tfile <- withr::local_tempfile(pattern = pattern, fileext = ".csv") writeLines(c("a,b", "A,B"), tfile) expect_equal( vroom(tfile, show_col_types = FALSE), tibble::tibble(a = "A", b = "B") ) }) test_that("can write to path with non-ascii characters", { pattern <- "cr\u00E8me-br\u00FBl\u00E9e-" tfile <- withr::local_tempfile(pattern = pattern, fileext = ".csv") dat <- tibble::tibble(a = "A", b = "B") vroom_write(dat, tfile, delim = ",") expect_equal(readLines(tfile), c("a,b", "A,B")) }) test_that("can read/write a compressed file with non-ascii characters in path", { skip_on_cran() skip_if_not_installed("archive") # https://github.com/r-lib/archive/issues/75 skip_if(l10n_info()$`Latin-1`) make_temp_path <- function(ext) file.path(tempdir(), paste0("d\u00E4t", ext)) gzfile <- withr::local_file(make_temp_path(".tar.gz")) bz2file <- withr::local_file(make_temp_path(".tar.bz2")) xzfile <- withr::local_file(make_temp_path(".tar.xz")) zipfile <- withr::local_file(make_temp_path(".zip")) dat <- tibble::tibble(a = "A", b = "B") vroom_write(dat, gzfile) vroom_write(dat, bz2file) vroom_write(dat, xzfile) vroom_write(dat, zipfile) expect_equal(detect_compression(gzfile), "gz") expect_equal(detect_compression(bz2file), "bz2") expect_equal(detect_compression(xzfile), "xz") expect_equal(detect_compression(zipfile), "zip") expect_equal(vroom(gzfile, show_col_types = FALSE), dat) expect_equal(vroom(bz2file, show_col_types = FALSE), dat) expect_equal(vroom(xzfile, show_col_types = FALSE), dat) expect_equal(vroom(zipfile, show_col_types = FALSE), dat) }) test_that("can read fwf file w/ non-ascii characters in path", { tfile <- withr::local_tempfile(pattern = "fwf-y\u00F6-", fileext = ".txt") writeLines(c("A B", "C D"), tfile) expect_equal( spec <- fwf_empty(tfile, col_names = c("a", "b")), list(begin = c(0L, 2L), end = c(1L, NA), col_names = c("a", "b")) ) expect_equal( vroom_fwf(tfile, spec, show_col_types = FALSE), tibble::tibble(a = c("A", "C"), b = c("B", "D")) ) }) test_that("standardise_path() errors for a mix of connection and not connection", { file <- test_path("multi-file", "foo") conn <- file(test_path("multi-file", "bar")) # wrap it, so we can check the caller is displayed correctly f <- function(some_arg_name) { standardise_path(some_arg_name) } expect_snapshot( error = TRUE, f(list(file, conn)) ) }) test_that("standardise_path() errors for invalid input", { files <- test_path("multi-file", c("foo", "baz")) # wrap it, so we can check the caller is displayed correctly f <- function(some_arg_name) { standardise_path(some_arg_name) } expect_snapshot( error = TRUE, f(as.list(files)) ) })vroom/tests/testthat/test-chr.R0000644000176200001440000000167214531406431016301 0ustar liggesusers# Encoding ---------------------------------------------------------------- test_that("locale encoding affects parsing", { x <- c("ao\u00FBt", "\u00E9l\u00E8ve", "\u00E7a va") #expect_equal(Encoding(x), rep("UTF-8", 3)) y <- iconv(paste0(x, collapse = "\n"), "UTF-8", "latin1") #expect_equal(Encoding(y), "latin1") fr <- locale("fr", encoding = "latin1") z <- vroom(I(y), delim = ",", locale = fr, col_names = FALSE, col_types = list()) # expect_equal(Encoding(z[[1]]), rep("UTF-8", 3)) # identical coerces encodings to match, so need to compare raw values as_raw <- function(x) lapply(x, charToRaw) expect_identical(as_raw(x), as_raw(z[[1]])) }) test_that("encodings are respected", { loc <- locale(encoding = "ISO-8859-1") expected <- c("fran\u00e7ais", "\u00e9l\u00e8ve") x <- vroom(test_path("enc-iso-8859-1.txt"), delim = "\n", locale = loc, col_names = FALSE, col_types = list()) expect_equal(x[[1]], expected) }) vroom/tests/testthat/fwf-trailing.txt0000644000176200001440000000002014132374245017545 0ustar liggesusers123 123 123 123 vroom/tests/testthat/raw.csv0000644000176200001440000000002114132374245015722 0ustar liggesusersabc,def abc,def vroom/tests/testthat/test-vroom_fwf.R0000644000176200001440000002630614151705507017536 0ustar liggesuserstest_that("trailing spaces omitted", { spec <- fwf_empty(test_path("fwf-trailing.txt")) expect_equal(spec$begin, c(0, 4)) expect_equal(spec$end, c(3, NA)) df <- vroom_fwf(test_path("fwf-trailing.txt"), spec, col_types = list()) expect_equal(df$X1, df$X2) }) test_that("dos newlines handles", { spec <- fwf_empty(test_path("fwf-trailing.txt")) x <- vroom_fwf(test_path("fwf-trailing.txt"), spec, col_types = list()) y <- vroom_fwf(test_path("fwf-trailing-crlf.txt"), spec, col_types = list()) expect_equal(x, y) z <- vroom_fwf(file(test_path("fwf-trailing-crlf.txt")), spec, col_types = list()) expect_equal(z, x) }) test_that("connections and normal files produce identical output", { spec <- fwf_empty(test_path("fwf-trailing.txt")) y <- vroom_fwf(test_path("fwf-trailing-crlf.txt"), spec, col_types = list()) x <- vroom_fwf(file(test_path("fwf-trailing-crlf.txt")), spec, col_types = list()) expect_equal(x, y) }) test_that("respects the trim_ws argument", { x <- I("a11 b22 c33\nd e f ") out1 <- vroom_fwf(x, fwf_empty(x), trim_ws = FALSE, col_types = list()) expect_equal(out1$X1, c("a11", "d ")) expect_equal(out1$X2, c("b22", "e ")) expect_equal(out1$X3, c("c33", "f ")) out2 <- vroom_fwf(x, fwf_empty(x), trim_ws = TRUE, col_types = list()) expect_equal(out2$X1, c("a11", "d")) expect_equal(out2$X2, c("b22", "e")) expect_equal(out2$X3, c("c33", "f")) }) test_that("respects the trim_ws argument with empty fields", { x <- I("a11 b22 c33\nd f ") out1 <- vroom_fwf(x, fwf_empty(x), trim_ws = FALSE, col_types = list()) expect_equal(out1$X1, c("a11", "d ")) expect_equal(out1$X2, c("b22", " ")) expect_equal(out1$X3, c("c33", "f ")) out1 <- vroom_fwf(x, fwf_empty(x), trim_ws = TRUE, na = "NA", col_types = list()) }) test_that("skipping column doesn't pad col_names", { x <- I("1 2 3\n4 5 6") out1 <- vroom_fwf(x, fwf_empty(x), col_types = 'd-d') expect_named(out1, c("X1", "X3")) names <- c("a", "b", "c") out2 <- vroom_fwf(x, fwf_empty(x, col_names = names), col_types = 'd-d') expect_named(out2, c("a", "c")) }) test_that("fwf_empty can skip comments", { x <- I("COMMENT\n1 2 3\n4 5 6") out1 <- vroom_fwf(x, fwf_empty(x, comment = "COMMENT"), comment = "COMMENT", col_types = list()) expect_equal(dim(out1), c(2, 3)) }) test_that("fwf_empty can skip lines", { x <- I("foo\nbar\baz\n1 2 3\n4 5 6\n") obj <- fwf_empty(x, skip = 3) exp <- list(begin = c(0L, 2L, 4L), end = c(1L, 3L, NA_integer_), col_names = c("X1", "X2", "X3")) expect_equal(obj, exp) }) test_that("passing \"\" to vroom_fwf's 'na' option", { expect_equal(vroom_fwf(I("foobar\nfoo "), fwf_widths(c(3, 3)), na = "", col_types = list())[[2]], c("bar", NA)) }) test_that("ragged last column expanded with NA", { x <- vroom_fwf(I("1a\n2ab\n3abc"), fwf_widths(c(1, NA)), col_types = list()) expect_equal(x$X2, c("a", "ab", "abc")) }) #test_that("ragged last column shrunk with warning", { #expect_warning(x <- vroom_fwf(I("1a\n2ab\n3abc"), fwf_widths(c(1, 3)))) #expect_equal(x$X2, c("a", "ab", "abc")) #}) test_that("read all columns with positions, non ragged", { col_pos <- fwf_positions(c(1,3,6),c(2,5,6)) x <- vroom_fwf(I("12345A\n67890BBBBBBBBB\n54321C"), col_positions = col_pos, col_types = list()) expect_equal(x$X3, c("A", "B", "C")) }) test_that("read subset columns with positions", { col_pos <- fwf_positions(c(1,3),c(2,5)) x <- vroom_fwf(I("12345A\n67890BBBBBBBBB\n54321C"), col_positions = col_pos, col_types = list()) expect_equal(x$X1, c(12, 67, 54)) expect_equal(x$X2, c(345, 890, 321)) }) test_that("read columns with positions, ragged", { col_pos <- fwf_positions(c(1,3,6),c(2,5,NA)) x <- vroom_fwf(I("12345A\n67890BBBBBBBBB\n54321C"), col_positions = col_pos, col_types = list()) expect_equal(x$X1, c(12, 67, 54)) expect_equal(x$X2, c(345, 890, 321)) expect_equal(x$X3, c('A', 'BBBBBBBBB', 'C')) }) test_that("read columns with width, ragged", { col_pos <- fwf_widths(c(2,3,NA)) x <- vroom_fwf(I("12345A\n67890BBBBBBBBB\n54321C"), col_positions = col_pos, col_types = list()) expect_equal(x$X1, c(12, 67, 54)) expect_equal(x$X2, c(345, 890, 321)) expect_equal(x$X3, c('A', 'BBBBBBBBB', 'C')) }) #test_that("vroom_fwf returns an empty data.frame on an empty file", { ##expect_true(all.equal(vroom_fwf(test_path("empty-file")), tibble::data_frame())) #}) #test_that("check for line breaks in between widths", { #txt1 <- paste( #"1 1", #"2", #"1 1 ", #sep = "\n" #) #expect_warning(out1 <- vroom_fwf(txt1, fwf_empty(txt1))) #expect_equal(n_problems(out1), 2) #txt2 <- paste( #" 1 1", #" 2", #" 1 1 ", #sep = "\n" #) #expect_warning(out2 <- vroom_fwf(txt2, fwf_empty(txt2))) #expect_equal(n_problems(out2), 2) #exp <- tibble::tibble(X1 = c(1L, 2L, 1L), X2 = c(1L, NA, 1L)) #expect_true(all.equal(out1, exp)) #expect_true(all.equal(out2, exp)) #}) #test_that("error on empty spec (#511, #519)", { #txt = "foo\n" #pos = fwf_positions(start = numeric(0), end = numeric(0)) #expect_error(vroom_fwf(txt, pos), "Zero-length.*specifications not supported") #}) #test_that("error on negatives in fwf spec", { #txt = "foo\n" #pos = fwf_positions(start = c(1, -1), end = c(2, 3)) #expect_error(vroom_fwf(txt, pos), ".*offset.*greater than 0") #}) test_that("fwf spec can overlap", { x <- vroom_fwf(I("2015a\n2016b"), fwf_positions(c(1, 3, 5), c(4, 4, 5)), col_types = list()) expect_equal(x$X1, c(2015, 2016)) expect_equal(x$X2, c(15, 16)) expect_equal(x$X3, c("a", "b")) }) # fwf_cols test_that("fwf_cols produces correct fwf_positions object with elements of length 2", { expected <- fwf_positions(c(1L, 9L, 4L), c(2L, 12L, 6L), c("a", "b", "d")) expect_equal(fwf_cols(a = c(1, 2), b = c(9, 12), d = c(4, 6)), expected, ignore_attr = TRUE) }) test_that("fwf_cols produces correct fwf_positions object with elements of length 1", { expected <- fwf_widths(c(2L, 4L, 3L), c("a", "b", "c")) expect_equal(fwf_cols(a = 2, b = 4, c = 3), expected, ignore_attr = TRUE) }) test_that("fwf_cols throws error when arguments are not length 1 or 2", { expect_error(fwf_cols(a = 1:3, b = 4:5)) expect_error(fwf_cols(a = c(), b = 4:5)) }) test_that("fwf_cols works with unnamed columns", { expect_equal(ignore_attr = TRUE, fwf_cols(c(1, 2), c(9, 12), c(4, 6)), fwf_positions(c(1L, 9L, 4L), c(2L, 12L, 6L), c("X1", "X2", "X3")) ) expect_equal(ignore_attr = TRUE, fwf_cols(a = c(1, 2), c(9, 12), c(4, 6)), fwf_positions(c(1L, 9L, 4L), c(2L, 12L, 6L), c("a", "X2", "X3")) ) }) # fwf_positions --------------------------------------------------------------- test_that("fwf_positions always returns col_names as character (#797)", { begin <- c(1, 2, 4, 8) end <- c(1, 3, 7, 15) # Input a factor, should return a character nms <- factor(letters[1:4]) info <- fwf_positions(begin, end, nms) expect_type(info$begin, "double") expect_type(info$end, "double") expect_type(info$col_names, "character") }) # Robustness test_that("vroom_fwf() is robust to improper inputs", { expect_error_free( vroom_fwf(I("foo bar baz\n1 2\n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4\n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4 5\n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4 5 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4 5 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4 5 \n"), col_types = list()) ) expect_error_free( vroom_fwf(I("foo bar baz\n1 2 \n4 5 6\n"), col_types = list()) ) expect_error_free( vroom_fwf(I("A\n a\n"), col_types = list()) ) }) test_that("Errors if begin is greater than end", { positions <- fwf_positions( start = c(1, 3, 5), end = c(3, 1, NA), col_names = c("foo", "bar", "baz") ) expect_error( vroom_fwf(I("1 2 3\n"), positions, col_types = list()), "`col_positions` must have begin less than end" ) }) test_that("vroom_fwf respects n_max (#334)", { out <- vroom_fwf(I("foo 1\nbar 2\nbaz 3\nqux 4"), n_max = 0, col_types = list()) expect_named(out, c("X1", "X2")) expect_equal(out[[1]], character()) expect_equal(out[[2]], character()) out <- vroom_fwf(I("foo 1\nbar 2\nbaz 3\nqux 4"), n_max = 1, col_types = list()) expect_named(out, c("X1", "X2")) expect_equal(out[[1]], c("foo")) expect_equal(out[[2]], c(1)) out <- vroom_fwf(I("foo 1\nbar 2\nbaz 3\nqux 4"), n_max = 2, col_types = list()) expect_named(out, c("X1", "X2")) expect_equal(out[[1]], c("foo", "bar")) expect_equal(out[[2]], c(1, 2)) }) test_that("vroom_fwf respects n_max when reading from a connection", { f <- tempfile() on.exit(unlink(f)) writeLines(rep("00010002", 1000), f) out1 <- vroom_fwf(file(f), col_positions = fwf_widths(c(4, 4)), col_types = "ii") expect_equal(dim(out1), c(1000, 2)) out2 <- vroom_fwf(file(f), n_max = 900, col_positions = fwf_widths(c(4, 4)), col_types = "ii") expect_equal(dim(out2), c(900, 2)) out3 <- vroom_fwf(file(f), n_max = 100, col_positions = fwf_widths(c(4, 4)), col_types = "ii") expect_equal(dim(out3), c(100, 2)) out4 <- vroom_fwf(file(f), n_max = 10, col_positions = fwf_widths(c(4, 4)), col_types = "ii") expect_equal(dim(out4), c(10, 2)) out5 <- vroom_fwf(file(f), n_max = 1, col_positions = fwf_widths(c(4, 4)), col_types = "ii") expect_equal(dim(out5), c(1, 2)) }) test_that("vroom_fwf works when skip_empty_rows is false (https://github.com/tidyverse/readr/issues/1211)", { f <- tempfile() on.exit(unlink(f)) writeLines(rep(" ", 10), f) out <- vroom_fwf(f, fwf_cols(A = c(1, NA)), col_types = "c", na = " ", trim_ws = FALSE, skip_empty_rows = FALSE) expect_equal(out[[1]], rep(NA_character_, 10)) }) test_that("vroom_fwf correctly reads files with no trailing newline (https://github.com/tidyverse/readr/issues/1293)", { f <- tempfile() on.exit(unlink(f)) writeBin(charToRaw("111222\n333444"), f) out <- vroom_fwf(f, fwf_widths(c(3, 3)), col_types = "ii") expect_equal(out, tibble::tibble(X1 = c(111, 333), X2 = c(222, 444))) out2 <- vroom_fwf(file(f), fwf_widths(c(3, 3)), col_types = "ii") expect_equal(out, out2) }) test_that("vroom_fwf correctly reads DOS files with no trailing newline (https://github.com/tidyverse/readr/issues/1293)", { f <- tempfile() on.exit(unlink(f)) writeBin(charToRaw("111222\r\n333444"), f) out <- vroom_fwf(f, fwf_widths(c(3, 3)), col_types = "ii") expect_equal(out, tibble::tibble(X1 = c(111, 333), X2 = c(222, 444))) out2 <- vroom_fwf(file(f), fwf_widths(c(3, 3)), col_types = "ii") expect_equal(out, out2) }) vroom/tests/testthat/helper.R0000644000176200001440000000436214132374245016032 0ustar liggesuserstest_vroom <- function(content, delim = ",", col_types = list(), ..., equals) { if(any(grepl("\n", content))) { content <- I(content) } suppressWarnings({ # with altrep withr::with_envvar(c("VROOM_USE_ALTREP_CHR" = "true", "VROOM_USE_ALTREP_NUMERICS" = "true"), { expect_equal( vroom(content, delim = delim, col_types = col_types, ...), equals ) }) # without altrep withr::with_envvar(c("VROOM_USE_ALTREP_CHR" = "false", "VROOM_USE_ALTREP_NUMERICS" = "false"), { expect_equal( vroom(content, delim = delim, col_types = col_types, ...), equals ) }) if (!file.exists(content)) { tf <- tempfile() on.exit(unlink(tf)) out_con <- file(tf, "wb", encoding = "UTF-8") writeBin(charToRaw(content), out_con) close(out_con) con <- file(tf, "rb") } else { con <- file(content, "rb") } on.exit(close(con), add = TRUE) res <- vroom(con, delim = delim, col_types = col_types, ...) expect_equal(res, equals) for (i in seq_along(res)) { force_materialization(res[[i]]) } expect_equal(res, equals) }) invisible(res) } test_parse_number <- function(x, expected, ...) { test_vroom(paste0(paste0(x, collapse = "\n"), "\n"), delim = "\t", col_names = FALSE, col_types = "n", ..., equals = tibble::tibble(X1 = expected) ) } test_parse_datetime <- function(x, expected, format = "", ...) { test_vroom(paste0(paste0(x, collapse = "\n"), "\n"), delim = "\t", col_names = FALSE, col_types = cols(X1 = col_datetime(format = format)), ..., equals = tibble::tibble(X1 = expected) ) } test_parse_date <- function(x, expected, format = "", ...) { test_vroom(paste0(paste0(x, collapse = "\n"), "\n"), delim = "\t", col_names = FALSE, col_types = cols(X1 = col_date(format = format)), ..., equals = tibble::tibble(X1 = expected) ) } test_parse_time <- function(x, expected, format = "", ...) { test_vroom(paste0(paste0(x, collapse = "\n"), "\n"), delim = "\t", col_names = FALSE, col_types = cols(X1 = col_time(format = format)), ..., equals = tibble::tibble(X1 = expected) ) } expect_error_free <- function(object, ...) { expect_error(object, regexp = NA) } on_github_actions <- function() { identical(Sys.getenv("GITHUB_ACTIONS"), "true") } vroom/tests/testthat/test-vroom_lines.R0000644000176200001440000000364614132374245020070 0ustar liggesuserstest_that("vroom_lines works with normal files", { infile <- vroom_example("mtcars.csv") expected <- readLines(infile) actual <- vroom_lines(infile) expect_equal(length(actual), length(expected)) expect_equal(head(actual), head(expected)) expect_equal(tail(actual), tail(expected)) expect_equal(actual, expected) }) test_that("vroom_lines works with connections files", { infile <- vroom_example("mtcars.csv") con <- file(infile) expected <- readLines(con) close(con) actual <- vroom_lines(file(infile)) expect_equal(length(actual), length(expected)) expect_equal(head(actual), head(expected)) expect_equal(tail(actual), tail(expected)) expect_equal(actual, expected) }) test_that("vroom_lines works with files with no trailing newline", { f <- tempfile() on.exit(unlink(f)) writeBin(charToRaw("foo"), f) expect_equal(vroom_lines(f), "foo") f2 <- tempfile() on.exit(unlink(f2), add = TRUE) writeBin(charToRaw("foo\nbar"), f2) expect_equal(vroom_lines(f2), c("foo", "bar")) }) test_that("vroom_lines respects n_max", { infile <- vroom_example("mtcars.csv") expect_equal(vroom_lines(infile, n_max = 2), readLines(infile, n = 2)) }) test_that("vroom_lines works with empty files", { f <- tempfile() file.create(f) on.exit(unlink(f)) expect_equal(vroom_lines(f), character()) }) test_that("vroom_lines uses na argument", { expect_equal(vroom_lines(I("abc\n123"), progress = FALSE), c("abc", "123")) expect_equal(vroom_lines(I("abc\n123"), na = "abc", progress = FALSE), c(NA_character_, "123")) expect_equal(vroom_lines(I("abc\n123"), na = "123", progress = FALSE), c("abc", NA_character_)) expect_equal(vroom_lines(I("abc\n123"), na = c("abc", "123"), progress = FALSE), c(NA_character_, NA_character_)) }) test_that("vroom_lines works with files with mixed line endings", { expect_equal(vroom_lines(I("foo\r\n\nbar\n\r\nbaz\r\n")), c("foo", "", "bar", "", "baz")) }) vroom/tests/testthat/_snaps/0000755000176200001440000000000014514305012015674 5ustar liggesusersvroom/tests/testthat/_snaps/col_types.md0000644000176200001440000000121514517036474020236 0ustar liggesusers# all col_types can be reported with color Code spec(dat) Output cols( skip = col_skip(), guess = col_character(), character = col_character(), factor = col_factor(levels = NULL, ordered = FALSE, include_na = FALSE), logical = col_logical(), double = col_double(), integer = col_integer(), big_integer = col_big_integer(), number = col_number(), date = col_date(format = ""), datetime = col_datetime(format = ""), .delim = "," ) vroom/tests/testthat/_snaps/path.md0000644000176200001440000000157514517036506017176 0ustar liggesusers# vroom() informs user to use I() for literal data (or not) Code x <- vroom("a,b,c,d\n1,2,3,4", show_col_types = FALSE) Condition Warning: The `file` argument of `vroom()` must use `I()` for literal data as of vroom 1.5.0. # Bad: vroom("X,Y\n1.5,2.3\n") # Good: vroom(I("X,Y\n1.5,2.3\n")) # standardise_path() errors for a mix of connection and not connection Code f(list(file, conn)) Condition Error in `f()`: ! `some_arg_name` cannot be a mix of connection and non-connection inputs # standardise_path() errors for invalid input Code f(as.list(files)) Condition Error in `f()`: ! `some_arg_name` is not one of the supported inputs: * A filepath or character vector of filepaths * A connection or list of connections * Literal or raw input vroom/tests/testthat/_snaps/problems.md0000644000176200001440000000160314517036510020050 0ustar liggesusers# problems returns a detailed warning message Code vroom(I("a,b,c\nx,y,z,,"), altrep = FALSE, col_types = "ccc") Condition Warning: One or more parsing issues, call `problems()` on your data frame for details, e.g.: dat <- vroom(...) problems(dat) Output # A tibble: 1 x 3 a b c 1 x y z,, # emits an error message if provided incorrect input Code problems(a_vector) Condition Error in `problems()`: ! The `x` argument of `vroom::problems()` must be a data frame created by vroom: x `x` has class --- Code problems(a_tibble) Condition Error in `problems()`: ! The `x` argument of `vroom::problems()` must be a data frame created by vroom: x `x` seems to have been created with something else, maybe readr? vroom/tests/testthat/multi-file/0000755000176200001440000000000014234262064016470 5ustar liggesusersvroom/tests/testthat/multi-file/baz0000644000176200001440000000001414132374245017164 0ustar liggesusersA,B,C 1,2,3 vroom/tests/testthat/multi-file/foo0000644000176200001440000000001014132374245017167 0ustar liggesusersA,B 1,2 vroom/tests/testthat/multi-file/qux0000644000176200001440000000000414151705507017224 0ustar liggesusersA,B vroom/tests/testthat/multi-file/bar0000644000176200001440000000001014132374245017150 0ustar liggesusersC,D 3,4 vroom/tests/testthat/test-select.R0000644000176200001440000000650614363145201017003 0ustar liggesuserstest_that("col_select works", { expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = 1, col_types = list())), "model") expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = 1:3, col_types = list())), c("model", "mpg", "cyl")) expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = c(1, 5, 7), col_types = list())), c("model", "hp", "wt")) expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = c("model", "hp", "wt"), col_types = list())), c("model", "hp", "wt")) expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = c(model:cyl), col_types = list())), c("model", "mpg", "cyl")) }) test_that("col_select with negations works", { expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = -1, col_types = list())), c("mpg", "cyl", "disp", "hp", "drat", "wt", "qsec", "vs", "am", "gear", "carb") ) expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = -(1:3), col_types = list())), c("disp", "hp", "drat", "wt", "qsec", "vs", "am", "gear", "carb") ) expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = -c(1, 5, 7), col_types = list())), c("mpg", "cyl", "disp", "drat", "qsec", "vs", "am", "gear", "carb") ) expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = -c(model, hp, wt), col_types = list())), c("mpg", "cyl", "disp", "drat", "qsec", "vs", "am", "gear", "carb") ) }) test_that("col_select with renaming", { expect_equal(colnames(vroom(vroom_example("mtcars.csv"), col_select = c(car = model, everything()), col_types = list())), c("car", "mpg", "cyl", "disp", "hp", "drat", "wt", "qsec", "vs", "am", "gear", "carb") ) }) test_that("col_select works with vroom_fwf", { spec <- fwf_empty(test_path("fwf-trailing.txt")) expect_equal( colnames( vroom_fwf( test_path("fwf-trailing.txt"), spec, col_select = c(foo = X1, X2), col_types = list() ) ), c("foo", "X2") ) }) test_that("col_select can select the id column", { expect_named( vroom(vroom_example("mtcars.csv"), id = "path", col_select = c(model, mpg, path), col_types = list()), c("model", "mpg", "path") ) expect_named( vroom(vroom_example("mtcars.csv"), id = "path", col_select = c(path, model, mpg), col_types = list()), c("path", "model", "mpg") ) }) test_that("id column is automatically included in col_select (#416)", { expect_named( vroom(vroom_example("mtcars.csv"), id = "path", col_select = c(model, mpg), show_col_types = FALSE), c("path", "model", "mpg") ) }) test_that("referencing columns by position in col_select works with id column (#455)", { expect_named( vroom(vroom_example("mtcars.csv"), id = "path", col_select = c(1:3), show_col_types = FALSE), c("path", "model", "mpg", "cyl") ) expect_named( vroom(vroom_example("mtcars.csv"), id = "path", col_select = c(1:3,6:8), show_col_types = FALSE), c("path", "model", "mpg", "cyl", "drat", "wt", "qsec") ) }) test_that("col_select works with col_names = FALSE", { res <- vroom(I("foo\tbar\n1\t2\n"), col_names = FALSE, col_select = 1, col_types = list()) expect_equal(res[[1]], c("foo", "1")) res2 <- vroom(I("foo\tbar\n1\t2\n"), col_names = FALSE, col_select = c(X2), col_types = list()) expect_equal(res2[[1]], c("bar", "2")) }) vroom/tests/testthat/test-multi-file.R0000644000176200001440000001270614505144207017575 0ustar liggesuserstest_that("vroom adds the id column from the filename for one file", { res <- vroom(vroom_example("mtcars.csv"), id = "filename", col_types = list()) expect_true(all(res$filename == vroom_example("mtcars.csv"))) }) test_that("vroom adds the id column from the filename for multiple files", { dir <- tempfile() dir.create(dir) splits <- split(mtcars, mtcars$cyl) for (i in seq_along(splits)) { vroom_write(splits[[i]], file.path(dir, paste0("mtcars_", names(splits)[[i]], ".tsv")), delim = "\t") } files <- list.files(dir, full.names = TRUE) res <- vroom(files, id = "filename", col_types = list()) # construct what the filename column should look like filenames <- paste0("mtcars_", rep(names(splits), vapply(splits, nrow, integer(1))), ".tsv") expect_equal(basename(res$filename), filenames) }) test_that("vroom adds the id column from the filename for multiple connections", { dir <- tempfile() dir.create(dir) splits <- split(mtcars, mtcars$cyl) for (i in seq_along(splits)) { # write_tsv will automatically gzip them vroom_write(splits[[i]], file.path(dir, paste0("mtcars_", names(splits)[[i]], ".tsv.gz")), delim = "\t") } files <- list.files(dir, full.names = TRUE) res <- vroom(files, id = "filename", col_types = list()) # construct what the filename column should look like filenames <- paste0("mtcars_", rep(names(splits), vapply(splits, nrow, integer(1))), ".tsv.gz") expect_equal(basename(res$filename), filenames) }) test_that("vroom works with many files", { skip_on_os("solaris") dir <- tempfile() dir.create(dir) on.exit(unlink(dir, recursive = TRUE)) for (i in seq_len(200)) { vroom_write( tibble::tibble( x = rnorm(10), y = rnorm(10), ), file.path(dir, paste0(i, ".csv")), delim = "," ) } files <- list.files(dir, pattern = ".*[.]csv", full.names = TRUE) res <- vroom::vroom(files, col_types = list()) expect_equal(colnames(res), c("x", "y")) expect_equal(NROW(res), 2000) }) test_that("vroom works with many connections", { skip_on_os("solaris") dir <- withr::local_tempdir() # the number of files is intentionally larger than 128, which has # historically been the maximum number of connections allowed by R # in R >= 4.4, 128 is likely to be the new default, with higher values # allowed # https://github.com/tidyverse/vroom/issues/64 # https://github.com/tidyverse/vroom/commit/a41465d70db37ab2bc628ff1e606b71c410fb0e3 for (i in seq_len(200)) { vroom_write( tibble::tibble( x = rnorm(10), y = rnorm(10), ), file.path(dir, paste0(i, ".csv.gz")), delim = "," ) } files <- list.files(dir, pattern = ".*[.]csv[.]gz", full.names = TRUE) # vroom manages the connections internally res <- vroom::vroom(files, col_types = list()) expect_equal(colnames(res), c("x", "y")) expect_equal(NROW(res), 2000) # use explicit connections, so we don't ask for anything close to R's max connections <- lapply(files[1:20], gzfile) res <- vroom::vroom(connections, col_types = list()) expect_equal(colnames(res), c("x", "y")) expect_equal(NROW(res), 200) }) test_that("vroom errors if numbers of columns are inconsistent", { files <- test_path("multi-file", c("foo", "baz")) expect_error(vroom::vroom(files, col_types = list()), "must all have") }) test_that("vroom errors if column names are inconsistent", { files <- test_path("multi-file", c("foo", "bar")) expect_error(vroom::vroom(files, col_types = list()), "consistent column names") }) test_that("vroom works if a file contains no data", { files <- test_path("multi-file", c("foo", "qux")) res <- vroom(files, col_types = list()) expect_equal(res, tibble::tibble(A = 1, B = 2)) }) test_that("vroom works if some files contain no data, regardless of order (#430)", { destdir <- withr::local_tempdir("testing-multiple-files") vroom_write_lines(c("A,B"), file.path(destdir, "header_only.csv")) vroom_write_lines(c("A,B"), file.path(destdir, "another_header_only.csv")) vroom_write_lines(c("A,B", "1,2"), file.path(destdir, "header_and_one_row.csv")) files <- file.path(destdir, c("header_only.csv", "header_and_one_row.csv")) res <- vroom(files, show_col_types = FALSE) expect_equal(res, tibble::tibble(A = 1, B = 2)) files <- file.path(destdir, c( "header_only.csv", "another_header_only.csv", "header_and_one_row.csv" )) res <- vroom(files, show_col_types = FALSE) expect_equal(res, tibble::tibble(A = 1, B = 2)) files <- file.path(destdir, c( "header_only.csv", "header_and_one_row.csv", "another_header_only.csv" )) res <- vroom(files, show_col_types = FALSE) expect_equal(res, tibble::tibble(A = 1, B = 2)) files <- file.path(destdir, c( "header_and_one_row.csv", "header_only.csv", "another_header_only.csv" )) res <- vroom(files, show_col_types = FALSE) expect_equal(res, tibble::tibble(A = 1, B = 2)) files <- file.path(destdir, c( "header_only.csv", "another_header_only.csv" )) res <- vroom(files, show_col_types = FALSE) x <- tibble::tibble(A = "", B = "", .rows = 0) expect_equal(res, x) }) test_that("vroom works for indxes that span file boundries (#383)", { x <- vroom(c(vroom_example("mtcars.csv"), vroom_example("mtcars.csv")), col_types = list()) y <- rbind(mtcars, mtcars) idx <- c(c(34, 33), sample(NROW(x), size = 25, replace = T)) expect_equal(x[idx, 5, drop = TRUE], y[idx, 4]) }) vroom/tests/testthat/enc-iso-8859-1.txt0000644000176200001440000000001713445253572017275 0ustar liggesusersfranηais ιlθve vroom/tests/testthat/test-dbl.R0000644000176200001440000000316714151705507016273 0ustar liggesuserstest_that("Large exponents can parse", { res <- vroom(I("1e-63,1e-64\n"), delim = ",", col_types = "dd", col_names = FALSE) expect_equal(res[[1]], 1e-63) expect_equal(res[[2]], 1e-64) }) test_that("Doubles parse correctly with comma as decimal separator", { res <- vroom(I("23,4\n"), delim='\t', altrep=FALSE, locale=locale(decimal_mark=','), col_types='d', col_names=FALSE) expect_equal(res[[1]], 23.4) res2 <- vroom(I("23,4\n"), delim='\t', altrep=TRUE, locale=locale(decimal_mark=','), col_types='d', col_names=FALSE) expect_equal(res2[[1]], 23.4) }) test_that("NA can be a double value", { test_vroom(I("x\n1\n2\n"), delim = ",", col_types = "d", na = "1", equals = tibble::tibble(x = c(NA_real_, 2))) }) test_that("NaN values are guessed and parsed as doubles (https://github.com/tidyverse/readr/issues/1277)", { test_vroom(I("x\nNaN\n"), delim = ",", col_types = "?", equals = tibble::tibble(x = c(NaN))) }) test_that("Test that values with only a NaN prefix are _not_ parsed as doubles", { test_vroom(I("x\nNaNa\n"), delim = ",", col_types = "?", equals = tibble::tibble(x = c("NaNa"))) }) test_that("Inf and -Inf values are guessed and parsed as doubles (https://github.com/tidyverse/readr/issues/1283)", { test_vroom(I("x\nInf\n-Inf\n+Inf"), delim = ",", col_types = "?", equals = tibble::tibble(x = c(Inf, -Inf, Inf))) }) test_that("Test that values with only a Inf prefix are _not_ parsed as doubles", { test_vroom(I("x\nInfa\n-Infb\n+Infc"), delim = ",", col_types = "?", equals = tibble::tibble(x = c("Infa", "-Infb", "+Infc"))) }) vroom/tests/testthat/test-datetime.R0000644000176200001440000002750114362130126017315 0ustar liggesuserstest_that("datetime parsing works", { test_vroom( "date,time,datetime 2018-01-01,10:01:01 AM,2018-01-01 10:01:01 2019-01-01,05:04:03 AM,2019-01-01 05:04:03 ", delim = ",", equals = tibble::tibble( date = c(as.Date("2018-01-01"), as.Date("2019-01-01")), time = c(hms::hms(1, 1, 10), hms::hms(3, 4, 5)), datetime = vctrs::vec_c(as.POSIXct("2018-01-01 10:01:01", tz = "UTC"), as.POSIXct("2019-01-01 05:04:03", tz = "UTC")) ) ) }) # Parsing ---------------------------------------------------------------------- r_parse <- function(x, fmt) as.POSIXct(strptime(x, fmt, tz = "UTC")) test_that("%d, %m and %y", { target <- utctime(2010, 2, 3, 0, 0, 0, 0) test_parse_datetime("10-02-03", "%y-%m-%d", expected = target) test_parse_datetime("10-03-02", "%y-%d-%m", expected = target) test_parse_datetime("03/02/10", "%d/%m/%y", expected = target) test_parse_datetime("02/03/10", "%m/%d/%y", expected = target) }) test_that("Compound formats work", { target <- utctime(2010, 2, 3, 0, 0, 0, 0) test_parse_datetime("02/03/10", "%D", expected = target) test_parse_datetime("2010-02-03", "%F", expected = target) test_parse_datetime("10/02/03", "%x", expected = target) }) test_that("%y matches R behaviour", { test_parse_datetime("01-01-69", "%d-%m-%y", expected = r_parse("01-01-69", "%d-%m-%y")) test_parse_datetime("01-01-68", "%d-%m-%y", expected = r_parse("01-01-68", "%d-%m-%y")) }) test_that("%e allows leading space", { test_parse_datetime("201010 1", "%Y%m%e", expected = utctime(2010, 10, 1, 0, 0, 0, 0)) }) test_that("%OS captures partial seconds", { test_parse_datetime("2001-01-01 00:00:01.125", "%Y-%m-%d %H:%M:%OS", expected = utctime(2001, 1, 1, 0, 0, 1, .125)) test_parse_datetime("2001-01-01 00:00:01.133", "%Y-%m-%d %H:%M:%OS", expected = utctime(2001, 1, 1, 0, 0, 1, .133)) }) test_that("%Y requires 4 digits", { test_parse_date("03-01-01", "%Y-%m-%d", expected = as.Date(NA)) test_parse_date("003-01-01", "%Y-%m-%d", expected = as.Date(NA)) test_parse_date("0003-01-01", "%Y-%m-%d", expected = as.Date("0003-01-01")) test_parse_date("00003-01-01", "%Y-%m-%d", expected = as.Date(NA)) }) test_that("invalid dates return NA", { test_parse_datetime("2010-02-30", "%Y-%m-%d", expected = .POSIXct(NA_real_, tz = "UTC")) }) test_that("failed parsing returns NA", { test_parse_datetime(c("2010-02-ab", "2010-02", "2010/02/01"), "%Y-%m-%d", expected = .POSIXct(rep(NA_real_, 3), tz = "UTC")) }) test_that("invalid specs returns NA", { test_parse_datetime("2010-02-20", "%Y-%m-%m", expected = .POSIXct(NA_real_, tz = "UTC")) }) test_that("ISO8601 partial dates are not parsed", { test_parse_datetime("20", "", expected = .POSIXct(NA_real_, tz = "UTC")) test_parse_datetime("2001", "", expected = .POSIXct(NA_real_, tz = "UTC")) test_parse_datetime("2001-01", "", expected = .POSIXct(NA_real_, tz = "UTC")) }) test_that("Year only gets parsed", { test_parse_datetime("2010", "%Y", expected = ISOdate(2010, 1, 1, 0, tz = "UTC")) test_parse_datetime("2010-06", "%Y-%m", expected = ISOdate(2010, 6, 1, 0, tz = "UTC")) }) test_that("%p detects AM/PM", { test_parse_datetime(c("2015-01-01 01:00 AM", "2015-01-01 01:00 am"), "%F %I:%M %p", expected = .POSIXct(c(1420074000, 1420074000), "UTC") ) test_parse_datetime(c("2015-01-01 01:00 PM", "2015-01-01 01:00 pm"), "%F %I:%M %p", expected = .POSIXct(c(1420117200, 1420117200), "UTC") ) test_parse_datetime("12/31/1991 12:01 AM", "%m/%d/%Y %I:%M %p", expected = .POSIXct(694137660, "UTC")) test_parse_datetime("12/31/1991 12:01 PM", "%m/%d/%Y %I:%M %p", expected = .POSIXct(694180860, "UTC")) test_parse_datetime("12/31/1991 01:01 AM", "%m/%d/%Y %I:%M %p", expected = .POSIXct(694141260, "UTC")) test_parse_datetime(c("12/31/1991 00:01 PM", "12/31/1991 13:01 PM"), "%m/%d/%Y %I:%M %p", expected = .POSIXct(rep(NA_real_, 2), tz = "UTC")) }) test_that("%b and %B are case insensitive", { ref <- as.Date("2001-01-01") test_parse_date("2001 JAN 01", "%Y %b %d", expected = ref) test_parse_date("2001 JANUARY 01", "%Y %B %d", expected = ref) }) test_that("%. requires a value", { ref <- as.Date("2001-01-01") test_parse_date("2001?01?01", "%Y%.%m%.%d", expected = ref) test_parse_date("20010101", "%Y%.%m%.%d", expected = as.Date(NA)) }) test_that("%Z detects named time zones", { ref <- .POSIXct(1285912800, "America/Chicago") ct <- locale(tz = "America/Chicago") test_parse_datetime("2010-10-01 01:00", "", expected = ref, locale = ct) test_parse_datetime("2010-10-01 01:00 America/Chicago", "%Y-%m-%d %H:%M %Z", locale = ct, expected = ref) }) test_that("%Z detects named time zones", { ref <- .POSIXct(1285912800, "UTC") test_parse_datetime("1285912800", "%s", expected = ref) }) test_that("parse_date returns a double like as.Date()", { ref <- as.Date("2001-01-01") res <- test_parse_date("2001-01-01", "", expected = ref) expect_type(res[[1]], "double") }) test_that("parses NA/empty correctly", { expect_equal( vroom(I("x\n\n"), delim = ",", col_types = list(x = "T"), skip_empty_rows = FALSE), tibble::tibble(x = .POSIXct(NA_real_, tz = "UTC")) ) expect_equal( vroom(I("x\n\n"), delim = ",", col_types = list(x = "D"), skip_empty_rows = FALSE), tibble::tibble(x = as.Date(NA)) ) test_parse_datetime("TeSt", "", na = "TeSt", expected = .POSIXct(NA_real_, tz = "UTC")) test_parse_date("TeSt", "", na = "TeSt", expected = as.Date(NA)) }) ## Locales ----------------------------------------------------------------- test_that("locale affects months", { jan1 <- as.Date("2010-01-01") fr <- locale("fr") test_parse_date("1 janv. 2010", "%d %b %Y", locale = fr, expected = jan1) test_parse_date("1 janvier 2010", "%d %B %Y", locale = fr, expected = jan1) }) test_that("locale affects day of week", { a <- as.POSIXct("2010-01-01", tz = "UTC") b <- .POSIXct(unclass(as.Date("2010-01-01")) * 86400, tz = "UTC") fr <- locale("fr") test_parse_datetime("Ven. 1 janv. 2010", "%a %d %b %Y", locale=fr, expected = a) test_parse_datetime("Ven. 1 janv. 2010", "%a %d %b %Y", locale=fr, expected = b) }) test_that("locale affects am/pm", { skip_on_os("windows") skip_on_os("solaris") skip_if_not(l10n_info()$`UTF-8`) expected <- hms::hms(hours = 13, minutes = 30) test_parse_time("01:30 PM", "%H:%M %p", expected = expected) test_parse_time("\UC624\UD6C4 01\UC2DC 30\UBD84", "%p %H\UC2DC %M\UBD84", expected = expected, locale = locale("ko")) }) test_that("locale affects both guessing and parsing", { out <- vroom(I("01/02/2013\n"), delim = ",", col_names = FALSE, col_types = list(), locale = locale(date_format = "%m/%d/%Y")) expect_equal(out[[1]][[1]], as.Date("2013-01-02")) }) ## Time zones ------------------------------------------------------------------ test_that("same times with different offsets parsed as same time", { # From http://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC same_time <- paste("2010-02-03", c("18:30Z", "22:30+04", "1130-0700", "15:00-03:30")) test_parse_datetime(same_time, format = "", expected = rep(utctime(2010, 2, 3, 18, 30, 0, 0), 4)) }) test_that("offsets can cross date boundaries", { expected <- as.POSIXct("2015-02-01 01:00:00Z", tz = "UTC") test_parse_datetime("2015-01-31T2000-0500", expected = expected) test_parse_datetime("2015-02-01T0100Z", expected = expected) }) test_that("unambiguous times with and without daylight savings", { skip("Not working on CI") skip_on_cran() # need to figure out why this fails melb <- locale(tz = "Australia/Melbourne") # Melbourne had daylight savings in 2015 that ended the morning of 2015-04-05 expected_melb <- .POSIXct(c(1428109200, 1428285600), "Australia/Melbourne") test_parse_datetime(c("2015-04-04 12:00:00", "2015-04-06 12:00:00"), locale = melb, expected = expected_melb) # Japan didn't have daylight savings in 2015 expected_ja <- .POSIXct(c(1428116400, 1428289200), "Japan") ja <- locale(tz = "Japan") test_parse_datetime(c("2015-04-04 12:00:00", "2015-04-06 12:00:00"), locale = ja, expected = expected_ja) }) test_that("ambiguous times always choose the earliest time", { ny <- locale(tz = "America/New_York") format <- "%Y-%m-%d %H:%M:%S%z" expected <- as.POSIXct("1970-10-25 01:30:00-0400", tz = "America/New_York", format = format) test_parse_datetime("1970-10-25 01:30:00", locale = ny, expected = expected) }) test_that("nonexistent times return NA", { ny <- locale(tz = "America/New_York") expected <- .POSIXct(NA_real_, tz = "America/New_York") test_parse_datetime("1970-04-26 02:30:00", locale = ny, expected = expected) }) test_that("can use `tz = ''` for system time zone", { withr::local_timezone("Europe/London") system <- locale(tz = "") expected <- as.POSIXct("1970-01-01 00:00:00", tz = "Europe/London") test_parse_datetime("1970-01-01 00:00:00", locale = system, expected = expected) }) test_that("can catch faulty system time zones", { withr::local_timezone("foo") expect_error(locale(tz = ""), "Unknown TZ foo") }) ## Guessing --------------------------------------------------------------------- test_that("DDDD-DD not parsed as date (i.e. doesn't trigger partial date match)", { expect_type(vroom(I("1989-90\n1990-91\n"), delim = "\n", col_types = list())[[1]], "character") }) test_that("leading zeros don't get parsed as date without explicit separator", { expect_type(vroom(I("00010203\n"), col_names = FALSE, delim = "\n", col_types = list())[[1]], "double") expect_s3_class(vroom(I("0001-02-03\n"), col_names = FALSE, delim = "\n", col_types = list())[[1]], "Date") }) test_that("must have either two - or none", { expect_s3_class(vroom(I("2000-10-10\n"), col_names = FALSE, delim = "\n", col_types = list())[[1]], "Date") expect_type(vroom(I("2000-1010\n"), col_names = FALSE, delim = "\n", col_types = list())[[1]], "character") expect_type(vroom(I("200010-10\n"), col_names = FALSE, delim = "\n", col_types = list())[[1]], "character") expect_type(vroom(I("20001010\n"), col_names = FALSE, delim = "\n", col_types = list())[[1]], "double") }) test_that("times are guessed even without AM / PM", { expect_s3_class(guess_type("01:02:03"), "collector_time") expect_s3_class(guess_type("01:02:03 AM"), "collector_time") expect_s3_class(guess_type("01:02:03 PM"), "collector_time") }) test_that("subsetting works with both double and integer indexes", { x <- vroom(I("X1\n2020-01-01 01:01:01"), delim = ",", col_types = "T") dt <- as.POSIXct("2020-01-01 01:01:01", tz = "UTC") na_dt <- .POSIXct(NA_real_, tz = "UTC") expect_equal(x$X1[1L], dt) expect_equal(x$X1[1], dt) expect_equal(x$X1[NA_integer_], na_dt) expect_equal(x$X1[NA_real_], na_dt) }) test_that("malformed date / datetime formats cause R errors", { expect_error( vroom(I("x\n6/28/2016"), delim = ",", col_types = list(x = col_date("%m/%/%Y")), altrep = FALSE), "Unsupported format" ) expect_error( vroom(I("x\n6/28/2016"), delim = ",", col_types = list(x = col_datetime("%m/%/%Y")), altrep = FALSE), "Unsupported format" ) }) test_that("single digit dates and hours are parsed correctly (https://github.com/tidyverse/readr/issues/1276)", { test_vroom(I("4/9/2021 2:18:25 PM"), col_types = list(col_datetime("%m/%d/%Y %H:%M:%S %p")), col_names = "X", delim = ",", equals = tibble::tibble(X = as.POSIXct("2021-04-09 14:18:25", tz = "UTC")) ) }) test_that("durations", { parse_time <- function(x, format, ...) { vroom(I(x), delim = ",", col_names = FALSE, col_types = list(col_time(format = format)), altrep = FALSE)[[1]] } expect_warning(parse_time("25:00:00", format = "%H:%M:%S")) expect_equal(parse_time("25:00:00", format = "%h:%M:%S"), hms::hms(hours = 25)) expect_equal(parse_time("1000000000:00:00", format = "%h:%M:%S"), hms::hms(hours = 1e9)) expect_equal(parse_time("-1:23:45", format = "%h:%M:%S"), hms::as_hms(-hms::hms(45, 23, 1))) expect_equal(parse_time("-1:23:45.67", format = "%h:%M:%OS"), hms::as_hms(-hms::hms(45.67, 23, 1))) }) vroom/tests/testthat/test-vroom_write.R0000644000176200001440000002253514422655005020104 0ustar liggesuserstest_that("empty columns create an empty file", { out <- tempfile() no_cols <- mtcars[, FALSE] no_rows_or_cols <- mtcars[FALSE, FALSE] expect_equal(vroom_write(no_cols, out), no_cols) expect_true(file.exists(out)) unlink(out) expect_equal(vroom_write(no_rows_or_cols, out), no_rows_or_cols) expect_true(file.exists(out)) }) test_that("empty rows print the headers", { out <- tempfile() on.exit(unlink(out)) no_rows <- mtcars[FALSE, ] expect_equal(vroom_write(no_rows, out), no_rows) expect_equal(strsplit(readLines(out), "\t")[[1]], colnames(mtcars)) }) test_that("strings are only quoted if needed", { x <- c("a", ',') csv <- vroom_format(data.frame(x), delim = ",",col_names = FALSE) expect_equal(csv, 'a\n\",\"\n') ssv <- vroom_format(data.frame(x), delim = " ",col_names = FALSE) expect_equal(ssv, 'a\n,\n') }) test_that("read_delim/csv/tsv and write_delim round trip special chars", { x <- stats::setNames(list("a", '"', ",", "\n","at\t"), paste0("V", seq_len(5))) output <- tibble::as_tibble(x) output_space <- vroom(I(vroom_format(output, delim = " ")), trim_ws = FALSE, progress = FALSE, col_types = list()) output_csv <- vroom(I(vroom_format(output, delim = ",")), trim_ws = FALSE, progress = FALSE, col_types = list()) output_tsv <- vroom(I(vroom_format(output, delim = "\t")), trim_ws = FALSE, progress = FALSE, col_types = list()) expect_equal(output_space, output) expect_equal(output_csv, output) expect_equal(output_tsv, output) }) test_that("special floating point values translated to text", { df <- data.frame(x = c(NaN, NA, Inf, -Inf)) expect_equal(vroom_format(df), "x\nNA\nNA\nInf\n-Inf\n") }) test_that("NA integer values translated to text", { df <- data.frame(x = c(NA, 1L, 5L, 1234567890L)) expect_equal(vroom_format(df), "x\nNA\n1\n5\n1234567890\n") }) test_that("logical values give long names", { df <- data.frame(x = c(NA, FALSE, TRUE)) expect_equal(vroom_format(df), "x\nNA\nFALSE\nTRUE\n") }) test_that("roundtrip preserved floating point numbers", { input <- data.frame(x = runif(100)) output <- vroom(I(vroom_format(input, delim = " ")), delim = " ", col_types = list()) expect_equal(input$x, output$x) }) test_that("roundtrip preserves dates and datetimes", { x <- as.Date("2010-01-01") + 1:10 y <- as.POSIXct(x) attr(y, "tzone") <- "UTC" input <- data.frame(x, y) output <- vroom(I(vroom_format(input, delim = " ")), delim = " ", col_types = list()) expect_equal(output$x, x) expect_equal(output$y, y) }) test_that("fails to create file in non-existent directory", { expect_error(vroom_write(mtcars, file.path(tempdir(), "/x/y"), "\t"), "Cannot open file for writing") }) test_that("includes a byte order mark if desired", { tmp <- tempfile() on.exit(unlink(tmp)) vroom_write(mtcars, tmp, bom = TRUE) output <- readBin(tmp, "raw", file.info(tmp)$size) # BOM is there expect_equal(output[1:3], charToRaw("\xEF\xBB\xBF")) # Rest of file also there expect_equal(output[4:6], charToRaw("mpg")) }) test_that("does not writes a trailing .0 for whole number doubles", { expect_equal(vroom_format(tibble::tibble(x = 1)), "x\n1\n") expect_equal(vroom_format(tibble::tibble(x = 0)), "x\n0\n") expect_equal(vroom_format(tibble::tibble(x = -1)), "x\n-1\n") expect_equal(vroom_format(tibble::tibble(x = 999)), "x\n999\n") expect_equal(vroom_format(tibble::tibble(x = -999)), "x\n-999\n") expect_equal(vroom_format(tibble::tibble(x = 123456789)), "x\n123456789\n") expect_equal(vroom_format(tibble::tibble(x = -123456789)), "x\n-123456789\n") }) test_that("can write windows eol characters if desired (#263)", { expect_equal(vroom_format(tibble::tibble(x = 1), eol = "\r\n"), "x\r\n1\r\n") }) test_that("write_csv can write to compressed files", { mt <- vroom(vroom_example("mtcars.csv"), col_types = list()) filename <- file.path(tempdir(), "mtcars.csv.bz2") on.exit(unlink(filename)) vroom_write(mt, filename) is_bz2_file <- function(x) { # Magic number for bz2 is "BZh" in ASCII # https://en.wikipedia.org/wiki/Bzip2#File_format identical(charToRaw("BZh"), readBin(x, n = 3, what = "raw")) } expect_true(is_bz2_file(filename)) expect_equal(vroom(filename, col_types = list()), mt) }) test_that("write_csv writes large integers without scientific notation #671", { x <- data.frame(a = c(60150001022000, 60150001022001)) expect_equal(vroom_format(x), "a\n60150001022000\n60150001022001\n") }) test_that("write_csv writes large integers without scientific notation up to 1E15 #671", { x <- data.frame(a = c(1E13, 1E14, 1E15, 1E16)) expect_equal(vroom_format(x), "a\n10000000000000\n100000000000000\n1e15\n1e16\n") }) #test_that("write_csv2 and format_csv2 writes ; sep and , decimal mark", { #df <- tibble::tibble(x = c(0.5, 2, 1.2), y = c("a", "b", "c")) #expect_equal(format_csv2(df), "x;y\n0,5;a\n2,0;b\n1,2;c\n") #filename <- tempfile(pattern = "readr", fileext = ".csv") #on.exit(unlink(filename)) #write_csv2(df, filename) #expect_equivalent(df, suppressMessages(read_csv2(filename))) #}) #test_that("write_csv2 and format_csv2 writes NA appropriately", { #df <- tibble::tibble(x = c(0.5, NA, 1.2), y = c("a", "b", NA)) #expect_equal(format_csv2(df), "x;y\n0,5;a\nNA;b\n1,2;NA\n") #}) test_that("Can change the escape behavior for quotes", { df <- data.frame(x = c("a", '"', ",", "\n")) expect_error(vroom_format(df, "\t", escape = "invalid"), "should be one of") expect_equal(vroom_format(df, "\t"), 'x\na\n""""\n,\n"\n"\n') expect_equal(vroom_format(df, "\t", escape = "double"), "x\na\n\"\"\"\"\n,\n\"\n\"\n") expect_equal(vroom_format(df, "\t", escape = "backslash"), "x\na\n\"\\\"\"\n,\n\"\n\"\n") expect_equal(vroom_format(df, "\t", escape = "none"), "x\na\n\"\"\"\n,\n\"\n\"\n") }) test_that("hms NAs are written without padding (#930)", { df <- data.frame(x = hms::as_hms(c(NA, 34.234))) expect_equal(vroom_format(df), "x\nNA\n00:00:34.234\n") }) test_that("vroom_write equals the same thing as vroom_format", { df <- gen_tbl(100, 8, col_types = c("dilfcDtT"), missing = .1) tf <- tempfile() on.exit(unlink(tf)) # Temporarily run with 2 lines per buffer, to test the multithreading withr::with_envvar(c("VROOM_WRITE_BUFFER_LINES" = "2"), vroom_write(df, tf, "\t") ) expect_equal(readChar(tf, file.info(tf)$size), vroom_format(df)) }) test_that("vroom_format handles empty data frames", { df <- data.frame() expect_equal(vroom_format(df), "") df <- data.frame(a = 1:2, b = 2:3) df <- df[0, ] expect_equal(vroom_format(df), "a\tb\n") }) test_that("vroom_write() / vroom_read() roundtrips an empty data frame", { df <- tibble::tibble() t <- tempfile(fileext = ".csv") on.exit(unlink(t)) vroom_write(df, t) expect_equal(vroom(t, show_col_types = FALSE), df) }) test_that("vroom_write(append = TRUE) works with R connections", { df <- data.frame(x = 1, y = 2) f <- tempfile(fileext = ".tsv.gz") on.exit(unlink(f)) vroom::vroom_write(df, f) vroom::vroom_write(df, f, append = TRUE) expect_equal(vroom_lines(f), c("x\ty", "1\t2", "1\t2")) }) test_that("vroom_write() works with an empty delimiter", { df <- data.frame(x = "foo", y = "bar") f <- tempfile(fileext = ".tsv.gz") on.exit(unlink(f)) vroom::vroom_write(df, f, delim = "") expect_equal(vroom_lines(f), c("xy", "foobar")) }) test_that("vroom_write_lines() works with empty", { f <- tempfile(fileext = ".txt") on.exit(unlink(f)) vroom::vroom_write_lines(character(), f) expect_equal(vroom_lines(f), character()) }) test_that("vroom_write_lines() works with normal input", { f <- tempfile(fileext = ".txt") on.exit(unlink(f)) vroom::vroom_write_lines(c("foo", "bar"), f) expect_equal(vroom_lines(f), c("foo", "bar")) }) test_that("vroom_write_lines() does not escape or quote lines", { f <- tempfile(fileext = ".txt") on.exit(unlink(f)) vroom::vroom_write_lines(c('"foo"', "bar"), f) expect_equal(vroom_lines(f), c('"foo"', "bar")) }) test_that("vroom_write() always outputs in UTF-8", { x <- iconv(c("ao\u00FBt", "\u00E9l\u00E8ve", "\u00E7a va"), "UTF-8", "latin1") data <- tibble::tibble(X = x) names(data) <- iconv("fran\u00E7aise", "UTF-8", "latin1") f <- tempfile() vroom_write(data, f, delim = ",") expected_data <- charToRaw( paste0(collapse = "\n", c( enc2utf8(names(data)), enc2utf8(data[[1]]), "") ) ) actual_data <- readBin(f, "raw", file.size(f)) expect_equal(actual_data, expected_data) }) test_that("vroom_format() does not quote strings that start with the `na` string (#426)", { names_df <- tibble::tibble(x = c(NA, "NA", "NATHAN", "JONAH")) simple_output <- vroom_format(names_df) expect_equal( simple_output, "x\nNA\nNA\nNATHAN\nJONAH\n" ) output_unique_NA <- vroom_format(names_df, na = "JON") expect_equal( output_unique_NA, "x\nJON\nNA\nNATHAN\nJONAH\n" ) }) test_that("na argument modifies how missing values are written", { df <- data.frame(x = c(NA, "x"), y = c(1, 2)) expect_equal(vroom_format(df, ",", na = "None"), "x,y\nNone,1\nx,2\n") }) test_that("vroom_write() does not overwrite file when appending empty data frame", { tf <- withr::local_tempfile() data <- tibble::tibble( a = "1", b = "2", c = "3" ) vroom_write(data, file = tf, delim = ",") vroom_write(data.frame(), file = tf, append = TRUE, delim = ",") expect_equal(vroom_lines(tf, altrep = FALSE), c("a,b,c", "1,2,3")) }) vroom/tests/testthat/multi-byte-unicode.txt0000644000176200001440000000007313445253572020710 0ustar liggesusersid❀name❀age 1❀ed❀36 2❀leigh❀NA 3❀nathan❀14 vroom/tests/testthat/test-num.R0000644000176200001440000000075714132374245016333 0ustar liggesusers# Flexible number parsing ------------------------------------------------- test_that("col_number only takes first number", { test_parse_number("XYZ 123,000 BLAH 456", 123000) }) test_that("col_number helps with currency", { test_parse_number("$1,000,000.00", 1e6) es_MX <- locale("es", decimal_mark = ",") test_parse_number("$1.000.000,00", locale = es_MX, 1e6) }) test_that("invalid numbers don't parse", { test_parse_number(c("..", "--", "3.3.3", "4-1"), c(NA, NA, 3.3, 4.0)) }) vroom/tests/testthat.R0000644000176200001440000000006613445253572014555 0ustar liggesuserslibrary(testthat) library(vroom) test_check("vroom") vroom/src/0000755000176200001440000000000014533652003012204 5ustar liggesusersvroom/src/vroom_types.h0000644000176200001440000000003214132374245014742 0ustar liggesusers#include "vroom_errors.h" vroom/src/vroom_rle.h0000644000176200001440000000572514533531371014376 0ustar liggesusers#pragma once #include "altrep.h" #include "r_utils.h" #ifdef HAS_ALTREP class vroom_rle { public: static R_altrep_class_t class_t; static SEXP Make(SEXP input) { SEXP res = R_new_altrep(class_t, input, R_NilValue); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // ALTREP methods ------------------- // The length of the object static inline R_xlen_t Length(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return Rf_xlength(data2); } R_xlen_t sz = 0; SEXP rle = R_altrep_data1(vec); int* rle_p = INTEGER(rle); for (R_xlen_t i = 0; i < Rf_xlength(rle); ++i) { sz += rle_p[i]; } return sz; } // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_rle (len=%" R_PRIdXLEN_T ", materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } // ALTSTRING methods ----------------- // the element at the index `i` static SEXP string_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return STRING_ELT(data2, i); } SEXP rle = R_altrep_data1(vec); int* rle_p = INTEGER(rle); SEXP nms = Rf_getAttrib(rle, Rf_install("names")); R_xlen_t idx = 0; while (i >= 0 && idx < Rf_xlength(rle)) { i -= rle_p[idx++]; } return STRING_ELT(nms, idx - 1); } // --- Altvec static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } R_xlen_t sz = Length(vec); SEXP rle = R_altrep_data1(vec); int* rle_p = INTEGER(rle); SEXP out = PROTECT(Rf_allocVector(STRSXP, sz)); R_xlen_t idx = 0; SEXP nms = Rf_getAttrib(rle, Rf_install("names")); for (R_xlen_t i = 0; i < Rf_xlength(rle); ++i) { for (R_xlen_t j = 0; j < rle_p[i]; ++j) { SET_STRING_ELT(out, idx++, STRING_ELT(nms, i)); } } UNPROTECT(1); R_set_altrep_data2(vec, out); return out; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } static const void* Dataptr_or_null(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 == R_NilValue) return nullptr; return STDVEC_DATAPTR(data2); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { vroom_rle::class_t = R_make_altstring_class("vroom_rle", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); // altstring R_set_altstring_Elt_method(class_t, string_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_rle(DllInfo* dll); vroom/src/vroom_fwf.cc0000644000176200001440000000644714132374245014536 0ustar liggesusers#include #include #include #include #include "LocaleInfo.h" #include "columns.h" #include "fixed_width_index.h" #include "unicode_fopen.h" [[cpp11::register]] cpp11::list vroom_fwf_( const cpp11::list& inputs, const std::vector& col_starts, const std::vector& col_ends, bool trim_ws, cpp11::sexp col_names, cpp11::sexp col_types, cpp11::sexp col_select, cpp11::sexp name_repair, size_t skip, const char* comment, bool skip_empty_rows, ptrdiff_t n_max, SEXP id, const cpp11::strings& na, const cpp11::list& locale, ptrdiff_t guess_max, size_t num_threads, size_t altrep, bool progress) { std::vector filenames; bool add_filename = !Rf_isNull(id); // We need to retrieve filenames now before the connection objects are read, // as they are invalid afterwards. if (add_filename) { filenames = get_filenames(inputs); } auto idx = std::make_shared( inputs, col_starts, col_ends, trim_ws, skip, comment, skip_empty_rows, n_max, progress); auto errors = new std::shared_ptr(new vroom_errors()); return create_columns( idx, std::move(col_names), std::move(col_types), std::move(col_select), std::move(name_repair), id, filenames, na, locale, altrep, guess_max, errors, num_threads); } template std::vector find_empty_cols(Iterator begin, Iterator end, ptrdiff_t n) { std::vector is_white; size_t row = 0, col = 0; for (Iterator cur = begin; cur != end; ++cur) { if (n > 0 && row > static_cast(n)) { break; } switch (*cur) { case '\n': col = 0; row++; break; case '\r': case ' ': col++; break; default: // Make sure there's enough room if (col >= is_white.size()) is_white.resize(col + 1, true); is_white[col] = false; col++; } } return is_white; } [[cpp11::register]] cpp11::list whitespace_columns_( const std::string& filename, size_t skip, ptrdiff_t n, const std::string& comment) { std::error_code error; auto mmap = make_mmap_source(filename.c_str(), error); if (error) { // We cannot actually portably compare error messages due to a bug in // libstdc++ (https://stackoverflow.com/a/54316671/2055486), so just print // the message on stderr return REprintf("mapping error: %s", error.message().c_str()); return cpp11::list(); } size_t s = find_first_line( mmap, skip, comment.data(), /* skip_empty_rows */ true, /* embedded_nl */ false, /* quote */ '\0'); std::vector empty = find_empty_cols(mmap.begin() + s, mmap.end(), n); std::vector begin, end; bool in_col = false; for (size_t i = 0; i < empty.size(); ++i) { if (in_col && empty[i]) { end.push_back(i); in_col = false; } else if (!in_col && !empty[i]) { begin.push_back(i); in_col = true; } } if (in_col) end.push_back(empty.size()); using namespace cpp11::literals; return cpp11::writable::list({"begin"_nm = begin, "end"_nm = end}); } vroom/src/gen.cc0000644000176200001440000000124614132374245013273 0ustar liggesusers#include #include #include [[cpp11::register]] cpp11::strings gen_character_( int n, int min, int max, std::string values, uint32_t seed, uint32_t seed2) { std::mt19937 gen1(seed); std::mt19937 gen2(seed2); cpp11::writable::strings out(n); std::uniform_int_distribution<> char_dis(0, values.length() - 1); std::uniform_int_distribution<> len_dis(min, max); for (int i = 0; i < n; ++i) { std::string str; auto str_len = len_dis(gen1); for (int j = 0; j < str_len; ++j) { auto c = char_dis(gen2); str.push_back(values[c]); } out[i] = str.c_str(); } return out; } vroom/src/vroom_num.h0000644000176200001440000000537314533531371014412 0ustar liggesusers#pragma once #include #include "altrep.h" #include "r_utils.h" #include "vroom_vec.h" #include "parallel.h" using namespace vroom; double parse_num( const char* begin, const char* end, const LocaleInfo& loc, bool strict = false); cpp11::doubles read_num(vroom_vec_info* info); #ifdef HAS_ALTREP /* Vroom number */ class vroom_num : public vroom_vec { public: static R_altrep_class_t class_t; static SEXP Make(vroom_vec_info* info) { SEXP out = PROTECT(R_MakeExternalPtr(info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, vroom_vec::Finalize, FALSE); SEXP res = R_new_altrep(class_t, out, R_NilValue); UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // ALTREP methods ------------------- // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_num (len=%" R_PRIdXLEN_T ", materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } // ALTREAL methods ----------------- // the element at the index `i` static double real_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return REAL(data2)[i]; } auto& info = vroom_vec::Info(vec); double out = parse_value( i, info.column, [&](const char* begin, const char* end) -> double { return parse_num(begin, end, *info.locale); }, info.errors, "a number", *info.na); info.errors->warn_for_errors(); return out; } // --- Altvec static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } auto out = read_num(&Info(vec)); R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { vroom_num::class_t = R_make_altreal_class("vroom_num", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altinteger R_set_altreal_Elt_method(class_t, real_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_num(DllInfo* dll); vroom/src/guess_type.cc0000644000176200001440000000735514132374245014720 0ustar liggesusers#include #include #include "DateTime.h" #include "DateTimeParser.h" #include "LocaleInfo.h" #include "r_utils.h" #include "vroom_dbl.h" #include "vroom_lgl.h" #include "vroom_num.h" typedef bool (*canParseFun)(const std::string&, LocaleInfo* pLocale); bool canParse( const cpp11::strings& x, const canParseFun& canParse, LocaleInfo* pLocale) { for (auto&& i : x) { if (i == NA_STRING) continue; if (i.size() == 0) continue; if (!canParse(std::string(i), pLocale)) return false; } return true; } bool allMissing(const cpp11::strings& x) { for (auto&& i : x) { if (i != NA_STRING && i.size() > 0) return false; } return true; } bool isLogical(const std::string& x, LocaleInfo* /* pLocale */) { const char* const str = x.data(); int res = parse_logical(str, str + x.size()); return res != NA_LOGICAL; } bool isNumber(const std::string& x, LocaleInfo* pLocale) { // Leading zero not followed by decimal mark if (x[0] == '0' && x.size() > 1 && !matches(x.data() + 1, x.data() + x.size(), pLocale->decimalMark_)) return false; auto str = vroom::string(x); auto num = parse_num(str.begin(), str.end(), *pLocale, true); return !ISNA(num); } bool isInteger(const std::string& x, LocaleInfo* /* pLocale */) { // Leading zero if (x[0] == '0' && x.size() > 1) return false; double res = 0; std::string::const_iterator begin = x.begin(), end = x.end(); return parseInt(begin, end, res) && begin == end; } bool isDouble(const std::string& x, LocaleInfo* pLocale) { // Leading zero not followed by decimal mark if (x[0] == '0' && x.size() > 1 && x[1] != pLocale->decimalMark_[0]) return false; double res = bsd_strtod(x.data(), x.data() + x.size(), pLocale->decimalMark_[0]); return !ISNA(res); } bool isTime(const std::string& x, LocaleInfo* pLocale) { DateTimeParser parser(pLocale); parser.setDate(x.c_str(), x.c_str() + x.size()); return parser.parseLocaleTime(); } bool isDate(const std::string& x, LocaleInfo* pLocale) { DateTimeParser parser(pLocale); parser.setDate(x.c_str(), x.c_str() + x.size()); return parser.parseLocaleDate(); } static bool isDateTime(const std::string& x, LocaleInfo* pLocale) { DateTimeParser parser(pLocale); parser.setDate(x.c_str(), x.c_str() + x.size()); bool ok = parser.parseISO8601(); if (!ok) return false; DateTime dt = parser.makeDateTime(); return dt.validDateTime(); } std::string guess_type__( cpp11::writable::strings& input, const cpp11::strings& na, LocaleInfo* pLocale, bool guess_integer = false) { if (input.size() == 0) { return "character"; } if (allMissing(input)) { return "logical"; } for (R_xlen_t i = 0; i < input.size(); ++i) { for (R_xlen_t j = 0; j < na.size(); ++j) { if (STRING_ELT(input, i) == STRING_ELT(na, j)) { input[i] = NA_STRING; break; } } } // Work from strictest to most flexible if (canParse(input, isLogical, pLocale)) return "logical"; if (guess_integer && canParse(input, isInteger, pLocale)) return "integer"; if (canParse(input, isDouble, pLocale)) return "double"; if (canParse(input, isNumber, pLocale)) return "number"; if (canParse(input, isTime, pLocale)) return "time"; if (canParse(input, isDate, pLocale)) return "date"; if (canParse(input, isDateTime, pLocale)) return "datetime"; // Otherwise can always parse as a character return "character"; } [[cpp11::register]] std::string guess_type_( cpp11::writable::strings input, const cpp11::strings& na, const cpp11::list& locale, bool guess_integer = false) { LocaleInfo locale_(locale); return guess_type__(input, na, &locale_, guess_integer); } vroom/src/vroom_dbl.cc0000644000176200001440000001466414151705507014515 0ustar liggesusers#include "vroom_dbl.h" #include /* for tolower */ /* An STL iterator-based string to floating point number conversion. This function was adapted from the C standard library of RetroBSD, which is based on Berkeley UNIX. This function and this function only is BSD license. https://retrobsd.googlecode.com/svn/stable/vroom_time.h|31 col 32| for (const autosrc str : info->column.slice(start, end)) {/libc/stdlib/strtod.c */ double bsd_strtod(const char* begin, const char* end, const char decimalMark) { if (begin == end) { return NA_REAL; } int sign = 0, expSign = 0, i; double fraction, dblExp; const char* p = begin; char c; /* Exponent read from "EX" field. */ int exp = 0; /* Exponent that derives from the fractional part. Under normal * circumstances, it is the negative of the number of digits in F. * However, if I is very long, the last digits of I get dropped * (otherwise a long I with a large negative exponent could cause an * unnecessary overflow on I alone). In this case, fracExp is * incremented one for each dropped digit. */ int fracExp = 0; /* Number of digits in mantissa. */ int mantSize; /* Number of mantissa digits BEFORE decimal point. */ int decPt; /* Temporarily holds location of exponent in str. */ const char* pExp; /* Largest possible base 10 exponent. * Any exponent larger than this will already * produce underflow or overflow, so there's * no need to worry about additional digits. */ static int maxExponent = 307; /* Table giving binary powers of 10. * Entry is 10^2^i. Used to convert decimal * exponents into floating-point numbers. */ static double powersOf10[] = { 1e1, 1e2, 1e4, 1e8, 1e16, 1e32, 1e64, 1e128, 1e256, }; #if 0 static double powersOf2[] = { 2, 4, 16, 256, 65536, 4.294967296e9, 1.8446744073709551616e19, //3.4028236692093846346e38, 1.1579208923731619542e77, 1.3407807929942597099e154, }; static double powersOf8[] = { 8, 64, 4096, 2.81474976710656e14, 7.9228162514264337593e28, //6.2771017353866807638e57, 3.9402006196394479212e115, 1.5525180923007089351e231, }; static double powersOf16[] = { 16, 256, 65536, 1.8446744073709551616e19, //3.4028236692093846346e38, 1.1579208923731619542e77, 1.3407807929942597099e154, }; #endif /* * check for a sign. */ if (p != end && *p == '-') { sign = 1; ++p; } else if (p != end && *p == '+') ++p; /* NaN */ if (end - p == 3 && tolower(p[0]) == 'n' && tolower(p[1]) == 'a' && tolower(p[2]) == 'n') { return NAN; } /* Inf */ if (end - p == 3 && tolower(p[0]) == 'i' && tolower(p[1]) == 'n' && tolower(p[2]) == 'f') { return sign == 1 ? -HUGE_VAL : HUGE_VAL; } /* If we don't have a digit or decimal point something is wrong, so return * an NA */ if (!(isdigit(*p) || *p == decimalMark)) { return NA_REAL; } /* * Count the number of digits in the mantissa (including the decimal * point), and also locate the decimal point. */ decPt = -1; for (mantSize = 0; p != end; ++mantSize) { c = *p; if (!isdigit(c)) { if (c != decimalMark || decPt >= 0) break; decPt = mantSize; } ++p; } /* * Now suck up the digits in the mantissa. Use two integers to * collect 9 digits each (this is faster than using floating-point). * If the mantissa has more than 18 digits, ignore the extras, since * they can't affect the value anyway. */ pExp = p; p -= mantSize; if (decPt < 0) decPt = mantSize; else --mantSize; /* One of the digits was the point. */ if (mantSize > 2 * 9) mantSize = 2 * 9; fracExp = decPt - mantSize; if (mantSize == 0) { fraction = 0.0; p = begin; goto done; } else { int frac1, frac2; for (frac1 = 0; mantSize > 9 && p != end; --mantSize) { c = *p++; if (c == decimalMark) c = *p++; frac1 = frac1 * 10 + (c - '0'); } for (frac2 = 0; mantSize > 0 && p != end; --mantSize) { c = *p++; if (c == decimalMark) c = *p++; frac2 = frac2 * 10 + (c - '0'); } fraction = (double)1000000000 * frac1 + frac2; } /* * Skim off the exponent. */ p = pExp; if (p != end && (*p == 'E' || *p == 'e' || *p == 'S' || *p == 's' || *p == 'F' || *p == 'f' || *p == 'D' || *p == 'd' || *p == 'L' || *p == 'l')) { ++p; if (p != end && *p == '-') { expSign = 1; ++p; } else if (p != end && *p == '+') ++p; while (p != end && isdigit(*p)) exp = exp * 10 + (*p++ - '0'); } if (expSign) exp = fracExp - exp; else exp = fracExp + exp; /* * Generate a floating-point number that represents the exponent. * Do this by processing the exponent one bit at a time to combine * many powers of 2 of 10. Then combine the exponent with the * fraction. */ if (exp < 0) { expSign = 1; exp = -exp; } else expSign = 0; if (exp > maxExponent) exp = maxExponent; dblExp = 1.0; for (i = 0; exp; exp >>= 1, ++i) if (exp & 01) dblExp *= powersOf10[i]; if (expSign) fraction /= dblExp; else fraction *= dblExp; done: if (p != end) { return NA_REAL; } return sign ? -fraction : fraction; } cpp11::doubles read_dbl(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::doubles out(n); const char decimalMark = info->locale->decimalMark_[0]; try { parallel_for( n, [&](size_t start, size_t end, size_t) { R_xlen_t i = start; auto col = info->column->slice(start, end); for (auto b = col->begin(), e = col->end(); b != e; ++b) { out[i++] = parse_value( b, col, [&](const char* begin, const char* end) -> double { return bsd_strtod(begin, end, decimalMark); }, info->errors, "a double", *info->na); } }, info->num_threads); } catch (const std::runtime_error& e) { Rf_errorcall(R_NilValue, "%s", e.what()); } info->errors->warn_for_errors(); return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_dbl::class_t; void init_vroom_dbl(DllInfo* dll) { vroom_dbl::Init(dll); } #else void init_vroom_dbl(DllInfo* dll) {} #endif vroom/src/delimited_index_connection.h0000644000176200001440000000123514132374245017730 0ustar liggesusers#include "delimited_index.h" namespace vroom { class delimited_index_connection : public delimited_index { std::string filename_; public: delimited_index_connection( SEXP in, const char* delim, const char quote, const bool trim_ws, const bool escape_double, const bool escape_backslash, const bool has_header, const size_t skip, const size_t n_max, const char* comment, const bool skip_empty_rows, const std::shared_ptr errors, const size_t chunk_size, const bool progress); ~delimited_index_connection() { remove(filename_.c_str()); } }; } // namespace vroom vroom/src/vroom_int.h0000644000176200001440000000466514533531371014410 0ustar liggesusers#pragma once #include "altrep.h" #include "r_utils.h" #include "vroom_vec.h" int strtoi(const char* begin, const char* end); cpp11::integers read_int(vroom_vec_info* info); #ifdef HAS_ALTREP class vroom_int : public vroom_vec { public: static R_altrep_class_t class_t; static SEXP Make(vroom_vec_info* info) { SEXP out = PROTECT(R_MakeExternalPtr(info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, vroom_vec::Finalize, FALSE); SEXP res = R_new_altrep(class_t, out, R_NilValue); UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // ALTREP methods ------------------- // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_int (len=%" R_PRIdXLEN_T ", materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } // ALTINTEGER methods ----------------- static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } auto out = read_int(&Info(vec)); R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } // the element at the index `i` static int int_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return INTEGER(data2)[i]; } auto& info = vroom_vec::Info(vec); int out = parse_value( i, info.column, strtoi, info.errors, "an integer", *info.na); info.errors->warn_for_errors(); return out; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { vroom_int::class_t = R_make_altinteger_class("vroom_int", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altinteger R_set_altinteger_Elt_method(class_t, int_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_int(DllInfo* dll); vroom/src/vroom_fct.cc0000644000176200001440000000474314362130126014516 0ustar liggesusers#include "vroom_fct.h" #include bool matches(const string& needle, const std::vector& haystack) { for (auto& hay : haystack) { if (needle == hay) { return true; } } return false; } cpp11::integers read_fct_explicit( vroom_vec_info* info, const cpp11::strings& levels, bool ordered) { R_xlen_t n = info->column->size(); cpp11::writable::integers out(n); std::unordered_map level_map; for (auto i = 0; i < levels.size(); ++i) { if (levels[i] == NA_STRING) { for (const auto& str : *info->na) { level_map[str] = i + 1; } } else { level_map[levels[i]] = i + 1; } } auto col = info->column; R_xlen_t i = 0; for (auto b = col->begin(), e = col->end(); b != e; ++b) { auto str = *b; out[i++] = parse_factor(b, col, level_map, *info->locale, info->errors, *info->na); } info->errors->warn_for_errors(); out.attr("levels") = static_cast(levels); if (ordered) { out.attr("class") = {"ordered", "factor"}; } else { out.attr("class") = "factor"; } return out; } cpp11::integers read_fct_implicit(vroom_vec_info* info, bool include_na) { R_xlen_t n = info->column->size(); cpp11::writable::integers out(n); cpp11::writable::strings levels; std::unordered_map level_map; auto nas = cpp11::as_cpp>(*info->na); size_t max_level = 1; auto start = 0; auto end = n; auto i = start; auto col = info->column->slice(start, end); int na_level = NA_INTEGER; for (const auto& str : *col) { auto val = level_map.find(str.str()); if (val != level_map.end()) { out[i++] = val->second; } else { if (matches(str, nas)) { if (include_na) { if (na_level == NA_INTEGER) { na_level = max_level++; levels.push_back(NA_STRING); } level_map[str.str()] = na_level; out[i++] = na_level; } else { out[i++] = NA_INTEGER; } } else { out[i++] = max_level; level_map[str.str()] = max_level++; levels.push_back( info->locale->encoder_.makeSEXP(str.begin(), str.end(), false)); } } } out.attr("levels") = static_cast(levels); out.attr("class") = "factor"; return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_fct::class_t; void init_vroom_fct(DllInfo* dll) { vroom_fct::Init(dll); } #else void init_vroom_fct(DllInfo* dll) {} #endif vroom/src/vroom_date.cc0000644000176200001440000000331114132374245014654 0ustar liggesusers#include "vroom_date.h" using namespace vroom; double parse_date( const char* begin, const char* end, DateTimeParser& parser, const std::string& format) { parser.setDate(begin, end); bool res = (format == "") ? parser.parseLocaleDate() : parser.parse(format); if (res) { DateTime dt = parser.makeDate(); if (dt.validDate()) { return dt.date(); } } return NA_REAL; } cpp11::doubles read_date(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::doubles out(n); auto err_msg = info->format.size() == 0 ? std::string("date in ISO8601") : std::string("date like ") + info->format; try { parallel_for( n, [&](size_t start, size_t end, size_t) { R_xlen_t i = start; DateTimeParser parser(info->locale.get()); auto col = info->column->slice(start, end); for (auto b = col->begin(), e = col->end(); b != e; ++b) { out[i++] = parse_value( b, col, [&](const char* begin, const char* end) -> double { return parse_date(begin, end, parser, info->format); }, info->errors, err_msg.c_str(), *info->na); } }, info->num_threads, true); } catch (const std::runtime_error& e) { Rf_errorcall(R_NilValue, "%s", e.what()); } info->errors->warn_for_errors(); out.attr("class") = "Date"; return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_date::class_t; void init_vroom_date(DllInfo* dll) { vroom_date::Init(dll); } #else void init_vroom_date(DllInfo* dll) {} #endif vroom/src/unicode_fopen.h0000644000176200001440000000522314533531371015200 0ustar liggesusers#pragma once #include // clang-format off #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wsign-compare" #include # pragma clang diagnostic pop #else #include #endif // clang-format on #include #ifdef _WIN32 #include #else #include "cpp11/r_string.hpp" #endif // useful for print debugging file path encoding // inline void print_hex(const char* string) { // unsigned char* p = (unsigned char*) string; // for (int i = 0; i < 300 ; i++) { // if (p[i] == '\0') break; // Rprintf("%c 0x%02x ", p[i], p[i]); // if ((i%16 == 0) && i) // Rprintf("\n"); // } // Rprintf("\n"); // } // This is needed to support wide character paths on windows inline FILE* unicode_fopen(const char* path, const char* mode) { FILE* out; #ifdef _WIN32 // First convert the mode to the wide equivalent // Only usage is 2 characters so max 8 bytes + 2 byte null. wchar_t mode_w[10]; MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_w, 9); // Then convert the path wchar_t* buf; size_t len = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0); if (len <= 0) { Rf_error("Cannot convert file to Unicode: %s", path); } buf = (wchar_t*)R_alloc(len, sizeof(wchar_t)); if (buf == NULL) { Rf_error("Could not allocate buffer of size: %zu", len); } MultiByteToWideChar(CP_UTF8, 0, path, -1, buf, len); out = _wfopen(buf, mode_w); #else // the path has UTF-8 encoding, because we do that unconditionally on the R // side (but also because cpp11 is eager to use UTF-8) // however, we need to pass the path to fopen() in the native encoding const char* native_path = Rf_translateChar(cpp11::r_string(path)); out = fopen(native_path, mode); #endif return out; } inline mio::mmap_source make_mmap_source(const char* file, std::error_code& error) { #ifdef __WIN32 wchar_t* buf; size_t len = MultiByteToWideChar(CP_UTF8, 0, file, -1, NULL, 0); if (len <= 0) { Rf_error("Cannot convert file to Unicode: %s", file); } buf = (wchar_t*)malloc(len * sizeof(wchar_t)); if (buf == NULL) { Rf_error("Could not allocate buffer of size: %zu", len); } MultiByteToWideChar(CP_UTF8, 0, file, -1, buf, len); mio::mmap_source out = mio::make_mmap_source(buf, error); free(buf); return out; #else // the path has UTF-8 encoding, because we do that unconditionally on the R // side (but also because cpp11 is eager to use UTF-8) // however, we need to pass the path to fopen() in the native encoding const char* native_path = Rf_translateChar(cpp11::r_string(file)); return mio::make_mmap_source(native_path, error); #endif } vroom/src/vroom_time.h0000644000176200001440000000623414533531371014546 0ustar liggesusers#pragma once #include #include "r_utils.h" #include "vroom.h" #include "vroom_dttm.h" using namespace vroom; double parse_time( const char* begin, const char* end, DateTimeParser& parser, const std::string& format); cpp11::doubles read_time(vroom_vec_info* info); #ifdef HAS_ALTREP class vroom_time : public vroom_dttm { public: static R_altrep_class_t class_t; static SEXP Make(vroom_vec_info* info) { vroom_dttm_info* dttm_info = new vroom_dttm_info; dttm_info->info = info; dttm_info->parser = std::unique_ptr(new DateTimeParser(info->locale.get())); SEXP out = PROTECT(R_MakeExternalPtr(dttm_info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, vroom_dttm::Finalize, FALSE); cpp11::sexp res = R_new_altrep(class_t, out, R_NilValue); res.attr("class") = {"hms", "difftime"}; res.attr("units") = "secs"; UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_time (len=%" R_PRIdXLEN_T ", materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } // the element at the index `i` static double time_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return REAL(data2)[i]; } auto info = Info(vec); auto err_msg = info->info->format.size() == 0 ? std::string("time in ISO8601") : std::string("time like ") + info->info->format; double out = parse_value( i, info->info->column, [&](const char* begin, const char* end) -> double { return parse_time(begin, end, *info->parser, info->info->format); }, info->info->errors, err_msg.c_str(), *info->info->na); info->info->errors->warn_for_errors(); return out; } // --- Altvec static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } auto inf = Info(vec); auto out = read_time(inf->info); R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { vroom_time::class_t = R_make_altreal_class("vroom_time", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altreal R_set_altreal_Elt_method(class_t, time_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_time(DllInfo* dll); vroom/src/vroom_time.cc0000644000176200001440000000334014132374245014677 0ustar liggesusers#include "vroom_time.h" double parse_time( const char* begin, const char* end, DateTimeParser& parser, const std::string& format) { parser.setDate(begin, end); bool res = (format == "") ? parser.parseLocaleTime() : parser.parse(format); if (res) { DateTime dt = parser.makeTime(); if (dt.validDuration()) { return dt.time(); } } return NA_REAL; } cpp11::doubles read_time(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::doubles out(n); auto err_msg = info->format.size() == 0 ? std::string("time in ISO8601") : std::string("time like ") + info->format; try { parallel_for( n, [&](size_t start, size_t end, size_t) { R_xlen_t i = start; DateTimeParser parser(info->locale.get()); auto col = info->column->slice(start, end); for (auto b = col->begin(), e = col->end(); b != e; ++b) { out[i++] = parse_value( b, col, [&](const char* begin, const char* end) -> double { return parse_time(begin, end, parser, info->format); }, info->errors, err_msg.c_str(), *info->na); } }, info->num_threads, true); } catch (const std::runtime_error& e) { Rf_errorcall(R_NilValue, "%s", e.what()); } info->errors->warn_for_errors(); out.attr("class") = {"hms", "difftime"}; out.attr("units") = "secs"; return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_time::class_t; void init_vroom_time(DllInfo* dll) { vroom_time::Init(dll); } #else void init_vroom_time(DllInfo* dll) {} #endif vroom/src/vroom_dbl.h0000644000176200001440000000527314533531371014353 0ustar liggesusers#include #include "altrep.h" #include "parallel.h" #include "r_utils.h" #include "vroom_vec.h" double bsd_strtod(const char* begin, const char* end, const char decimalMark); cpp11::doubles read_dbl(vroom_vec_info* info); #ifdef HAS_ALTREP /* Vroom Dbl */ class vroom_dbl : public vroom_vec { public: static R_altrep_class_t class_t; static SEXP Make(vroom_vec_info* info) { SEXP out = PROTECT(R_MakeExternalPtr(info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, vroom_vec::Finalize, FALSE); SEXP res = R_new_altrep(class_t, out, R_NilValue); UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // ALTREP methods ------------------- // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_dbl (len=%" R_PRIdXLEN_T ", materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } // ALTREAL methods ----------------- // the element at the index `i` static double real_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return REAL(data2)[i]; } auto& info = vroom_vec::Info(vec); double out = parse_value( i, info.column, [&](const char* begin, const char* end) -> double { return bsd_strtod(begin, end, info.locale->decimalMark_[0]); }, info.errors, "a double", *info.na); info.errors->warn_for_errors(); return out; } // --- Altvec static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } auto out = read_dbl(&Info(vec)); R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { vroom_dbl::class_t = R_make_altreal_class("vroom_dbl", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altinteger R_set_altreal_Elt_method(class_t, real_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_dbl(DllInfo* dll); vroom/src/delimited_index.h0000644000176200001440000003143214232533745015516 0ustar liggesusers#pragma once #include // clang-format off #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wsign-compare" #include # pragma clang diagnostic pop #else #include #endif // clang-format on #include "index.h" #include "utils.h" #include #include "multi_progress.h" #include "vroom_errors.h" namespace vroom { struct cell { const char* begin; const char* end; }; class delimited_index : public index, public std::enable_shared_from_this { class newline_error {}; public: delimited_index( const char* filename, const char* delim, const char quote, const bool trim_ws, const bool escape_double, const bool escape_backslash, const bool has_header, const size_t skip, size_t n_max, const char* comment, const bool skip_empty_rows, std::shared_ptr errors, const size_t num_threads, const bool progress, const bool use_threads = true); class column_iterator : public base_iterator { std::shared_ptr idx_; size_t column_; bool is_first_; bool is_last_; size_t i_; public: column_iterator(std::shared_ptr idx, size_t column) : idx_(idx), column_(column), is_first_(column == 0), is_last_(column == (idx_->columns_ - 1)), i_((idx_->has_header_ * idx_->columns_) + column_) {} void next() override { i_ += idx_->columns_; } void advance(ptrdiff_t n) override { i_ += idx_->columns_ * n; } bool equal_to(const base_iterator& it) const override { return i_ == static_cast(&it)->i_; } ptrdiff_t distance_to(const base_iterator& it) const override { ptrdiff_t i = i_; ptrdiff_t j = static_cast(&it)->i_; ptrdiff_t columns = idx_->columns_; return (j - i) / columns; } string value() const override { return idx_->get_trimmed_val(i_, is_first_, is_last_); } column_iterator* clone() const override { return new column_iterator(*this); } string at(ptrdiff_t n) const override { size_t i = ((n + idx_->has_header_) * idx_->columns_) + column_; return idx_->get_trimmed_val(i, is_first_, is_last_); } std::string filename() const override { return idx_->filename_; } size_t index() const override { return i_ / idx_->columns_; } size_t position() const override { size_t begin, end; std::tie(begin, end) = idx_->get_cell(i_, is_first_); return begin; } virtual ~column_iterator() = default; }; class row_iterator : public base_iterator { std::shared_ptr idx_; size_t row_; size_t i_; public: row_iterator(std::shared_ptr idx, size_t row) : idx_(idx), row_(row), i_((row_ + idx_->has_header_) * idx_->columns_) {} void next() override { ++i_; } void advance(ptrdiff_t n) override { i_ += n; } bool equal_to(const base_iterator& it) const override { return i_ == static_cast(&it)->i_; } ptrdiff_t distance_to(const base_iterator& it) const override { return ( static_cast(static_cast(&it)->i_) - static_cast(i_)); } string value() const override { return idx_->get_trimmed_val(i_, i_ == 0, i_ == (idx_->columns_ - 1)); } row_iterator* clone() const override { return new row_iterator(*this); } string at(ptrdiff_t n) const override { size_t i = (row_ + idx_->has_header_) * idx_->columns_ + n; return idx_->get_trimmed_val(i, i == 0, i == (idx_->columns_ - 1)); } std::string filename() const override { return idx_->filename_; } size_t index() const override { return i_ - (row_ + idx_->has_header_) * idx_->columns_; } size_t position() const override { size_t begin, end; std::tie(begin, end) = idx_->get_cell(i_, i_ == 0); return begin; } virtual ~row_iterator() = default; }; delimited_index() : rows_(0), columns_(0){}; string get(size_t row, size_t col) const override; size_t num_columns() const override { return columns_; } size_t num_rows() const override { return rows_; } std::string filename() const { return filename_; } std::string get_delim() const override { return delim_; } std::shared_ptr get_column(size_t column) const override { auto begin = new column_iterator(shared_from_this(), column); auto end = new column_iterator(shared_from_this(), column); end->advance(num_rows()); return std::make_shared(begin, end, column); } std::shared_ptr get_row(size_t row) const override { auto begin = new row_iterator(shared_from_this(), row); auto end = new row_iterator(shared_from_this(), row); end->advance(num_columns()); return std::make_shared(begin, end, row); } std::shared_ptr get_header() const override { auto begin = new row_iterator(shared_from_this(), -1); auto end = new row_iterator(shared_from_this(), -1); end->advance(num_columns()); return std::make_shared(begin, end, 0); } public: using idx_t = std::vector; std::string filename_; mio::mmap_source mmap_; std::vector idx_; bool has_header_; char quote_; bool trim_ws_; bool escape_double_; bool escape_backslash_; size_t skip_; const char* comment_; size_t rows_; size_t columns_; bool progress_; size_t delim_len_; std::string delim_; std::locale loc_; void skip_lines(); void trim_quotes(const char*& begin, const char*& end) const; const string get_escaped_string(const char* begin, const char* end, bool has_quote) const; const string get_trimmed_val(size_t i, bool is_first, bool is_last) const; std::pair get_cell(size_t i, bool is_first) const; enum csv_state { RECORD_START, FIELD_START, UNQUOTED_FIELD, QUOTED_FIELD, QUOTED_END }; inline static csv_state quoted_state(csv_state in) { switch (in) { case RECORD_START: return QUOTED_FIELD; case FIELD_START: return QUOTED_FIELD; case UNQUOTED_FIELD: return UNQUOTED_FIELD; // throw std::runtime_error("invalid 1"); case QUOTED_FIELD: return QUOTED_END; case QUOTED_END: return QUOTED_FIELD; } throw std::runtime_error("should never happen"); } inline static csv_state comma_state(csv_state in) { switch (in) { case RECORD_START: return FIELD_START; case FIELD_START: return FIELD_START; case UNQUOTED_FIELD: return FIELD_START; case QUOTED_FIELD: return QUOTED_FIELD; case QUOTED_END: return FIELD_START; } throw std::runtime_error("should never happen"); } inline static csv_state newline_state(csv_state in) { switch (in) { case RECORD_START: return RECORD_START; case FIELD_START: return RECORD_START; case UNQUOTED_FIELD: return RECORD_START; case QUOTED_FIELD: return QUOTED_FIELD; case QUOTED_END: return RECORD_START; } throw std::runtime_error("should never happen"); } inline static csv_state other_state(csv_state in) { switch (in) { case RECORD_START: return UNQUOTED_FIELD; case FIELD_START: return UNQUOTED_FIELD; case UNQUOTED_FIELD: return UNQUOTED_FIELD; case QUOTED_FIELD: return QUOTED_FIELD; case QUOTED_END: return QUOTED_END; } throw std::runtime_error("should never happen"); } void resolve_columns( size_t pos, size_t& cols, size_t num_cols, idx_t& destination, std::shared_ptr errors) { // Remove extra columns if there are too many if (cols >= num_cols) { errors->add_parse_error(pos, cols); while (cols > 0 && cols >= num_cols) { destination.pop_back(); --cols; } } else if (cols < num_cols - 1) { errors->add_parse_error(pos, cols); // Add additional columns if there are too few while (cols < num_cols - 1) { destination.push_back(pos); ++cols; } } } /* * @param source the source to index * @param destination the index to push to * @param delim the delimiter to use * @param quote the quoting character * @param start the start of the region to index * @param end the end of the region to index * @param offset an offset to add to the destination (this is needed when * @param pb the progress bar to use * @param update_size how often to update the progress bar * reading blocks from a connection). */ template size_t index_region( const T& source, idx_t& destination, const char* delim, newline_type nlt, const char quote, const std::string& comment, const bool skip_empty_rows, csv_state& state, const size_t start, const size_t end, const size_t file_offset, const size_t n_max, size_t& cols, const size_t num_cols, std::shared_ptr errors, P& pb, const size_t num_threads, const size_t update_size) { const char newline = nlt == CR ? '\r' : '\n'; // If there are no quotes quote will be '\0', so will just work std::array query = {delim[0], newline, '\\', '\0', '\0', '\0'}; auto query_i = 3; if (quote != '\0') { query[query_i++] = quote; } if (!comment.empty()) { query[query_i] = comment[0]; } auto last_tick = start; auto buf = source.data(); // The actual parsing is here size_t pos = start; size_t lines_read = 0; while (pos < end && lines_read < n_max) { auto c = buf[pos]; if (escape_backslash_ && c == '\\') { ++pos; if (state == RECORD_START) { destination.push_back(pos + file_offset); state = FIELD_START; } ++pos; continue; } else if ( state != QUOTED_FIELD && is_comment(buf + pos, buf + end, comment)) { if (state != RECORD_START) { if (num_cols > 0 && pos > start) { resolve_columns( pos + file_offset, cols, num_cols, destination, errors); } destination.push_back(pos + file_offset); } cols = 0; pos = skip_rest_of_line(source, pos); ++pos; state = newline_state(state); continue; } if (state == RECORD_START) { if (is_empty_line(buf + pos, buf + end, skip_empty_rows)) { pos = skip_rest_of_line(source, pos); ++pos; continue; } // REprintf("RS: %i\n", pos); destination.push_back(pos + file_offset); } if (state != QUOTED_FIELD && strncmp(delim, buf + pos, delim_len_) == 0) { state = comma_state(state); destination.push_back(pos + file_offset); ++cols; } else if (c == newline) { if (state == QUOTED_FIELD) { // This will work as long as num_threads = 1 if (num_threads != 1) { if (progress_ && pb) { pb->finish(); } throw newline_error(); } ++pos; continue; } if (num_cols > 0 && pos > start) { resolve_columns( pos + file_offset, cols, num_cols, destination, errors); } state = newline_state(state); cols = 0; destination.push_back(pos + file_offset); ++lines_read; if (lines_read >= n_max) { if (progress_ && pb) { pb->finish(); } return lines_read; } if (progress_ && pb) { size_t tick_size = pos - last_tick; if (tick_size > update_size) { pb->tick(pos - last_tick); last_tick = pos; } } } else if (c == quote) { state = quoted_state(state); } else { state = other_state(state); ++pos; size_t buf_offset; if (pos < end) { buf_offset = strcspn(buf + pos, query.data()); pos = pos + buf_offset; } continue; } // REprintf( //"%i\t'%c'\t%c\n", // pos, // c, // state == RECORD_START //? 'R' //: state == FIELD_START //? 'F' //: state == UNQUOTED_FIELD //? 'U' //: state == QUOTED_FIELD //? 'Q' //: state == QUOTED_END ? 'E' : 'X'); ++pos; } if (progress_ && pb) { pb->tick(end - last_tick); } return lines_read; } }; } // namespace vroom vroom/src/LocaleInfo.cpp0000644000176200001440000000216214132374245014730 0ustar liggesusers#include "cpp11/as.hpp" #include "cpp11/list.hpp" #include "cpp11/strings.hpp" #include #include #include "LocaleInfo.h" LocaleInfo::LocaleInfo(const cpp11::list& x) : encoding_(cpp11::as_cpp(x["encoding"])), encoder_(Iconv(encoding_)) { std::string klass = cpp11::as_cpp(x.attr("class")); if (klass != "locale") cpp11::stop("Invalid input: must be of class locale"); cpp11::list date_names(x["date_names"]); mon_ = cpp11::as_cpp>(date_names["mon"]); monAb_ = cpp11::as_cpp>(date_names["mon_ab"]); day_ = cpp11::as_cpp>(date_names["day"]); dayAb_ = cpp11::as_cpp>(date_names["day_ab"]); amPm_ = cpp11::as_cpp>(date_names["am_pm"]); decimalMark_ = cpp11::as_cpp(x["decimal_mark"]); groupingMark_ = cpp11::as_cpp(x["grouping_mark"]); dateFormat_ = cpp11::as_cpp(x["date_format"]); timeFormat_ = cpp11::as_cpp(x["time_format"]); tz_ = cpp11::as_cpp(x["tz"]); } vroom/src/vroom_chr.h0000644000176200001440000000626014533531371014363 0ustar liggesusers#pragma once #include #include "altrep.h" #include "r_utils.h" #include "vroom_vec.h" cpp11::strings read_chr(vroom_vec_info* info); SEXP check_na(SEXP na, SEXP val); #ifdef HAS_ALTREP struct vroom_chr : vroom_vec { public: static R_altrep_class_t class_t; // Make an altrep object of class `stdvec_double::class_t` static SEXP Make(vroom_vec_info* info) { SEXP out = PROTECT(R_MakeExternalPtr(info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, vroom_vec::Finalize, FALSE); // make a new altrep object of class `vroom_chr::class_t` SEXP res = R_new_altrep(class_t, out, R_NilValue); UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // ALTREP methods ------------------- // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_chr (len=%" R_PRIdXLEN_T ", materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } // ALTSTRING methods ----------------- static SEXP Val(SEXP vec, R_xlen_t i) { auto& info = Info(vec); auto&& col = info.column; auto str = col->at(i); auto val = PROTECT(info.locale->encoder_.makeSEXP(str.begin(), str.end(), true)); if (Rf_xlength(val) < str.end() - str.begin()) { auto&& itr = info.column->begin(); info.errors->add_error( itr.index(), col->get_index(), "", "embedded null", itr.filename()); } val = check_na(*info.na, val); info.errors->warn_for_errors(); UNPROTECT(1); return val; } // the element at the index `i` // // this does not do bounds checking because that's expensive, so // the caller must take care of that static SEXP string_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return STRING_ELT(data2, i); } SPDLOG_TRACE("{0:x}: vroom_chr string_Elt {1}", (size_t)vec, i); return Val(vec, i); } // --- Altvec static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } SPDLOG_TRACE("{0:x}: vroom_chr Materialize", (size_t)vec); auto out = read_chr(&Info(vec)); R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { class_t = R_make_altstring_class("vroom_chr", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altstring R_set_altstring_Elt_method(class_t, string_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_chr(DllInfo* dll); vroom/src/vroom_num.cc0000644000176200001440000001024014132374245014535 0ustar liggesusers#include "vroom_num.h" #include "utils.h" enum NumberState { STATE_INIT, STATE_LHS, STATE_RHS, STATE_EXP, STATE_FIN }; // First and last are updated to point to first/last successfully parsed // character template bool parseNumber( const std::string& decimalMark, const std::string& groupingMark, Iterator& first, Iterator& last, Attr& res) { Iterator cur = first; // Advance to first non-character for (; cur != last; ++cur) { if (*cur == '-' || matches(cur, last, decimalMark) || (*cur >= '0' && *cur <= '9')) break; } if (cur == last) { return false; } else { // Move first to start of number first = cur; } double sum = 0, denom = 1, exponent = 0; NumberState state = STATE_INIT; bool seenNumber = false, exp_init = true; double sign = 1.0, exp_sign = 1.0; for (; cur < last; ++cur) { if (state == STATE_FIN) break; switch (state) { case STATE_INIT: if (*cur == '-') { state = STATE_LHS; sign = -1.0; } else if (matches(cur, last, decimalMark)) { cur += decimalMark.size() - 1; state = STATE_RHS; } else if (*cur >= '0' && *cur <= '9') { seenNumber = true; state = STATE_LHS; sum = *cur - '0'; } else { goto end; } break; case STATE_LHS: if (matches(cur, last, groupingMark)) { cur += groupingMark.size() - 1; } else if (matches(cur, last, decimalMark)) { cur += decimalMark.size() - 1; state = STATE_RHS; } else if (seenNumber && (*cur == 'e' || *cur == 'E')) { state = STATE_EXP; } else if (*cur >= '0' && *cur <= '9') { seenNumber = true; sum *= 10; sum += *cur - '0'; } else { goto end; } break; case STATE_RHS: if (matches(cur, last, groupingMark)) { cur += groupingMark.size() - 1; } else if (seenNumber && (*cur == 'e' || *cur == 'E')) { state = STATE_EXP; } else if (*cur >= '0' && *cur <= '9') { seenNumber = true; denom *= 10; sum += (*cur - '0') / denom; } else { goto end; } break; case STATE_EXP: // negative/positive sign only allowed immediately after 'e' or 'E' if (*cur == '-' && exp_init) { exp_sign = -1.0; exp_init = false; } else if (*cur == '+' && exp_init) { // sign defaults to positive exp_init = false; } else if (*cur >= '0' && *cur <= '9') { exponent *= 10.0; exponent += *cur - '0'; exp_init = false; } else { goto end; } break; case STATE_FIN: goto end; } } end: // Set last to point to final character used last = cur; res = sign * sum; // If the number was in scientific notation, multiply by 10^exponent if (exponent) { res *= pow(10.0, exp_sign * exponent); } return seenNumber; } double parse_num( const char* start, const char* end, const LocaleInfo& loc, bool strict) { double ret; auto start_p = start; auto end_p = end; bool ok = parseNumber(loc.decimalMark_, loc.groupingMark_, start_p, end_p, ret); if (ok && (!strict || (start_p == start && end_p == end))) { return ret; } return NA_REAL; } cpp11::doubles read_num(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::doubles out(n); parallel_for( n, [&](size_t start, size_t end, size_t) { R_xlen_t i = start; auto col = info->column->slice(start, end); for (auto b = col->begin(), e = col->end(); b != e; ++b) { out[i++] = parse_value( b, col, [&](const char* begin, const char* end) -> double { return parse_num(begin, end, *info->locale); }, info->errors, "a number", *info->na); } }, info->num_threads); info->errors->warn_for_errors(); return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_num::class_t; void init_vroom_num(DllInfo* dll) { vroom_num::Init(dll); } #else void init_vroom_num(DllInfo* dll) {} #endif vroom/src/vroom_dttm.h0000644000176200001440000001326214533531371014557 0ustar liggesusers#pragma once #include #include #include "r_utils.h" #include "vroom.h" #include "vroom_vec.h" #include "DateTimeParser.h" #include "parallel.h" #ifdef VROOM_LOG #include "spdlog/spdlog.h" #endif using namespace vroom; double parse_dttm( const char* begin, const char* end, DateTimeParser& parser, const std::string& format); cpp11::doubles read_dttm(vroom_vec_info* info); #ifdef HAS_ALTREP /* Vroom dttm */ struct vroom_dttm_info { vroom_vec_info* info; std::unique_ptr parser; }; class vroom_dttm : public vroom_vec { public: static R_altrep_class_t class_t; static SEXP Make(vroom_vec_info* info) { vroom_dttm_info* dttm_info = new vroom_dttm_info; dttm_info->info = info; dttm_info->parser = std::unique_ptr(new DateTimeParser(info->locale.get())); SEXP out = PROTECT(R_MakeExternalPtr(dttm_info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, vroom_dttm::Finalize, FALSE); cpp11::sexp res = R_new_altrep(class_t, out, R_NilValue); res.attr("class") = {"POSIXct", "POSIXt"}; res.attr("tzone") = info->locale->tz_; UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // ALTREP methods ------------------- // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_dttm (len=%" R_PRIdXLEN_T ", materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } static void Finalize(SEXP ptr) { if (ptr == nullptr || R_ExternalPtrAddr(ptr) == nullptr) { return; } auto info_p = static_cast(R_ExternalPtrAddr(ptr)); delete info_p->info; delete info_p; info_p = nullptr; R_ClearExternalPtr(ptr); } static inline vroom_dttm_info* Info(SEXP x) { return static_cast(R_ExternalPtrAddr(R_altrep_data1(x))); } static inline R_xlen_t Length(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return Rf_xlength(data2); } auto inf = Info(vec); return inf->info->column->size(); } static inline string Get(SEXP vec, R_xlen_t i) { auto inf = Info(vec); return inf->info->column->at(i); } // ALTREAL methods ----------------- // the element at the index `i` static double dttm_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return REAL(data2)[i]; } auto info = Info(vec); auto err_msg = info->info->format.size() == 0 ? std::string("date in ISO8601") : std::string("date like ") + info->info->format; double out = parse_value( i, info->info->column, [&](const char* begin, const char* end) -> double { return parse_dttm(begin, end, *info->parser, info->info->format); }, info->info->errors, err_msg.c_str(), *info->info->na); info->info->errors->warn_for_errors(); return out; } // --- Altvec static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } auto inf = Info(vec); auto out = read_dttm(inf->info); R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } template static SEXP Extract_subset(SEXP x, SEXP indx, SEXP) { SEXP data2 = R_altrep_data2(x); // If the vector is already materialized, just fall back to the default // implementation if (data2 != R_NilValue) { return nullptr; } // If there are no indices to subset fall back to default implementation. if (Rf_xlength(indx) == 0) { return nullptr; } auto idx = get_subset_index(indx, Rf_xlength(x)); if (idx == nullptr) { return nullptr; } auto inf = Info(x); auto info = new vroom_vec_info{ inf->info->column->subset(idx), inf->info->num_threads, inf->info->na, inf->info->locale, inf->info->errors, inf->info->format}; return T::Make(info); } static SEXP Duplicate(SEXP x, Rboolean deep) { SEXP data2 = R_altrep_data2(x); SPDLOG_TRACE( "Duplicate dttm: deep = {0}, materialized={1}", deep, data2 != R_NilValue); /* If deep or already materialized, do the default behavior */ if (deep || data2 != R_NilValue) { return nullptr; } /* otherwise copy the metadata */ auto inf = Info(x); auto info = new vroom_vec_info{ inf->info->column, inf->info->num_threads, inf->info->na, inf->info->locale, inf->info->errors, inf->info->format}; return Make(info); } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { vroom_dttm::class_t = R_make_altreal_class("vroom_dttm", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); R_set_altrep_Duplicate_method(class_t, Duplicate); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altreal R_set_altreal_Elt_method(class_t, dttm_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_dttm(DllInfo* dll); vroom/src/index.h0000644000176200001440000000614614151705507013477 0ustar liggesusers#pragma once #include "iterator.h" #include "vroom.h" #include #include namespace vroom { class index { public: class subset_iterator : public base_iterator { size_t i_; mutable size_t prev_; mutable iterator it_; iterator start_; std::shared_ptr> indexes_; public: subset_iterator( const iterator& it, const std::shared_ptr>& indexes) : i_(0), prev_(0), it_(it), start_(it), indexes_(indexes) {} void next() override { ++i_; } void advance(ptrdiff_t n) override { i_ += n; } bool equal_to(const base_iterator& other) const override { auto other_ = static_cast(&other); return i_ == other_->i_; }; ptrdiff_t distance_to(const base_iterator& that) const override { auto that_ = static_cast(&that); return that_->i_ - i_; }; string value() const override { size_t cur = (*indexes_)[i_]; ptrdiff_t diff = cur - prev_; if (diff < 0) { it_ = start_ + cur; } else { it_ += diff; } prev_ = cur; return *it_; }; subset_iterator* clone() const override { auto copy = new subset_iterator(*this); return copy; }; string at(ptrdiff_t n) const override { return it_[(*indexes_)[n]]; } std::string filename() const override { return it_.filename(); } size_t index() const override { return it_.index(); } size_t position() const override { return (it_ + (*indexes_)[i_]).position(); } virtual ~subset_iterator() {} }; class range { const iterator begin_; const iterator end_; const size_t index_; public: range(const iterator& begin, const iterator& end, size_t index) : begin_(begin), end_(end), index_(index) {} range(base_iterator* begin, base_iterator* end, size_t index) : begin_(begin), end_(end), index_(index) {} iterator begin() { return begin_; } iterator end() { return end_; } size_t size() const { return end_ - begin_; } string at(size_t i) const { return begin_[i]; } std::shared_ptr subset(const std::shared_ptr>& idx) const { auto begin = new subset_iterator(begin_, idx); auto end = new subset_iterator(begin_, idx); end->advance(idx->size()); return std::make_shared(begin, end, index_); } std::shared_ptr slice(size_t start, size_t end) const { return std::make_shared( begin_ + start, begin_ + end, index_); } size_t get_index() { return index_; } }; using column = range; using row = range; virtual std::shared_ptr get_row(size_t row) const = 0; virtual std::shared_ptr get_header() const = 0; virtual std::shared_ptr get_column(size_t col) const = 0; virtual size_t num_columns() const = 0; virtual size_t num_rows() const = 0; virtual string get(size_t row, size_t col) const = 0; virtual std::string get_delim() const = 0; virtual ~index() {} }; } // namespace vroom vroom/src/mio/0000755000176200001440000000000014533652003012770 5ustar liggesusersvroom/src/mio/LICENSE0000644000176200001440000000207613445253572014013 0ustar liggesusersMIT License Copyright (c) 2018 https://github.com/mandreyel/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. vroom/src/mio/README.md0000644000176200001440000003330614132374245014260 0ustar liggesusers# mio An easy to use header-only cross-platform C++11 memory mapping library with an MIT license. mio has been created with the goal to be easily includable (i.e. no dependencies) in any C++ project that needs memory mapped file IO without the need to pull in Boost. Please feel free to open an issue, I'll try to address any concerns as best I can. ### Why? Because memory mapping is the best thing since sliced bread! More seriously, the primary motivation for writing this library instead of using Boost.Iostreams, was the lack of support for establishing a memory mapping with an already open file handle/descriptor. This is possible with mio. Furthermore, Boost.Iostreams' solution requires that the user pick offsets exactly at page boundaries, which is cumbersome and error prone. mio, on the other hand, manages this internally, accepting any offset and finding the nearest page boundary. Albeit a minor nitpick, Boost.Iostreams implements memory mapped file IO with a `std::shared_ptr` to provide shared semantics, even if not needed, and the overhead of the heap allocation may be unnecessary and/or unwanted. In mio, there are two classes to cover the two use-cases: one that is move-only (basically a zero-cost abstraction over the system specific mmapping functions), and the other that acts just like its Boost.Iostreams counterpart, with shared semantics. ### How to create a mapping NOTE: the file must exist before creating a mapping. There are three ways to map a file into memory: - Using the constructor, which throws a `std::system_error` on failure: ```c++ mio::mmap_source mmap(path, offset, size_to_map); ``` or you can omit the `offset` and `size_to_map` arguments, in which case the entire file is mapped: ```c++ mio::mmap_source mmap(path); ``` - Using the factory function: ```c++ std::error_code error; mio::mmap_source mmap = mio::make_mmap_source(path, offset, size_to_map, error); ``` or: ```c++ mio::mmap_source mmap = mio::make_mmap_source(path, error); ``` - Using the `map` member function: ```c++ std::error_code error; mio::mmap_source mmap; mmap.map(path, offset, size_to_map, error); ``` or: ```c++ mmap.map(path, error); ``` **NOTE:** The constructors **require** exceptions to be enabled. If you prefer to build your projects with `-fno-exceptions`, you can still use the other ways. Moreover, in each case, you can provide either some string type for the file's path, or you can use an existing, valid file handle. ```c++ #include #include #include #include #include int main() { // NOTE: error handling omitted for brevity. const int fd = open("file.txt", O_RDONLY); mio::mmap_source mmap(fd, 0, mio::map_entire_file); // ... } ``` However, mio does not check whether the provided file descriptor has the same access permissions as the desired mapping, so the mapping may fail. Such errors are reported via the `std::error_code` out parameter that is passed to the mapping function. **WINDOWS USERS**: This library *does* support the use of wide character types for functions where character strings are expected (e.g. path parameters). ### Example ```c++ #include #include // for std::error_code #include // for std::printf #include #include #include int handle_error(const std::error_code& error); void allocate_file(const std::string& path, const int size); int main() { const auto path = "file.txt"; // NOTE: mio does *not* create the file for you if it doesn't exist! You // must ensure that the file exists before establishing a mapping. It // must also be non-empty. So for illustrative purposes the file is // created now. allocate_file(path, 155); // Read-write memory map the whole file by using `map_entire_file` where the // length of the mapping is otherwise expected, with the factory method. std::error_code error; mio::mmap_sink rw_mmap = mio::make_mmap_sink( path, 0, mio::map_entire_file, error); if (error) { return handle_error(error); } // You can use any iterator based function. std::fill(rw_mmap.begin(), rw_mmap.end(), 'a'); // Or manually iterate through the mapped region just as if it were any other // container, and change each byte's value (since this is a read-write mapping). for (auto& b : rw_mmap) { b += 10; } // Or just change one value with the subscript operator. const int answer_index = rw_mmap.size() / 2; rw_mmap[answer_index] = 42; // Don't forget to flush changes to disk before unmapping. However, if // `rw_mmap` were to go out of scope at this point, the destructor would also // automatically invoke `sync` before `unmap`. rw_mmap.sync(error); if (error) { return handle_error(error); } // We can then remove the mapping, after which rw_mmap will be in a default // constructed state, i.e. this and the above call to `sync` have the same // effect as if the destructor had been invoked. rw_mmap.unmap(); // Now create the same mapping, but in read-only mode. Note that calling the // overload without the offset and file length parameters maps the entire // file. mio::mmap_source ro_mmap; ro_mmap.map(path, error); if (error) { return handle_error(error); } const int the_answer_to_everything = ro_mmap[answer_index]; assert(the_answer_to_everything == 42); } int handle_error(const std::error_code& error) { const auto& errmsg = error.message(); std::printf("error mapping file: %s, exiting...\n", errmsg.c_str()); return error.value(); } void allocate_file(const std::string& path, const int size) { std::ofstream file(path); std::string s(size, '0'); file << s; } ``` `mio::basic_mmap` is move-only, but if multiple copies to the same mapping are needed, use `mio::basic_shared_mmap` which has `std::shared_ptr` semantics and has the same interface as `mio::basic_mmap`. ```c++ #include mio::shared_mmap_source shared_mmap1("path", offset, size_to_map); mio::shared_mmap_source shared_mmap2(std::move(mmap1)); // or use operator= mio::shared_mmap_source shared_mmap3(std::make_shared(mmap1)); // or use operator= mio::shared_mmap_source shared_mmap4; shared_mmap4.map("path", offset, size_to_map, error); ``` It's possible to define the type of a byte (which has to be the same width as `char`), though aliases for the most common ones are provided by default: ```c++ using mmap_source = basic_mmap_source; using ummap_source = basic_mmap_source; using mmap_sink = basic_mmap_sink; using ummap_sink = basic_mmap_sink; ``` But it may be useful to define your own types, say when using the new `std::byte` type in C++17: ```c++ using mmap_source = mio::basic_mmap_source; using mmap_sink = mio::basic_mmap_sink; ``` Though generally not needed, since mio maps users requested offsets to page boundaries, you can query the underlying system's page allocation granularity by invoking `mio::page_size()`, which is located in `mio/page.hpp`. ### Single Header File Mio can be added to your project as a single header file simply by including `\single_include\mio\mio.hpp`. Single header files can be regenerated at any time by running the `amalgamate.py` script within `\third_party`. ``` python amalgamate.py -c config.json -s ../include ``` ## CMake As a header-only library, mio has no compiled components. Nevertheless, a [CMake](https://cmake.org/overview/) build system is provided to allow easy testing, installation, and subproject composition on many platforms and operating systems. ### Testing Mio is distributed with a small suite of tests and examples. When mio is configured as the highest level CMake project, this suite of executables is built by default. Mio's test executables are integrated with the CMake test driver program, [CTest](https://cmake.org/cmake/help/latest/manual/ctest.1.html). CMake supports a number of backends for compilation and linking. To use a static configuration build tool, such as GNU Make or Ninja: ```sh cd mkdir build cd build # Configure the build cmake -D CMAKE_BUILD_TYPE= \ -G <"Unix Makefiles" | "Ninja"> .. # build the tests < make | ninja | cmake --build . > # run the tests < make test | ninja test | cmake --build . --target test | ctest > ``` To use a dynamic configuration build tool, such as Visual Studio or Xcode: ```sh cd mkdir build cd build # Configure the build cmake -G <"Visual Studio 14 2015 Win64" | "Xcode"> .. # build the tests cmake --build . --config # run the tests via ctest... ctest --build-config # ... or via CMake build tool mode... cmake --build . --config --target test ``` Of course the **build** and **test** steps can also be executed via the **all** and **test** targets, respectively, from within the IDE after opening the project file generated during the configuration step. Mio's testing is also configured to operate as a client to the [CDash](https://www.cdash.org/) software quality dashboard application. Please see the [Kitware documentation](https://cmake.org/cmake/help/latest/manual/ctest.1.html#dashboard-client) for more information on this mode of operation. ### Installation Mio's build system provides an installation target and support for downstream consumption via CMake's [`find_package`](https://cmake.org/cmake/help/v3.0/command/find_package.html) intrinsic function. CMake allows installation to an arbitrary location, which may be specified by defining `CMAKE_INSTALL_PREFIX` at configure time. In the absense of a user specification, CMake will install mio to conventional location based on the platform operating system. To use a static configuration build tool, such as GNU Make or Ninja: ```sh cd mkdir build cd build # Configure the build cmake [-D CMAKE_INSTALL_PREFIX="path/to/installation"] \ [-D BUILD_TESTING=False] \ -D CMAKE_BUILD_TYPE=Release \ -G <"Unix Makefiles" | "Ninja"> .. # install mio ``` To use a dynamic configuration build tool, such as Visual Studio or Xcode: ```sh cd mkdir build cd build # Configure the project cmake [-D CMAKE_INSTALL_PREFIX="path/to/installation"] \ [-D BUILD_TESTING=False] \ -G <"Visual Studio 14 2015 Win64" | "Xcode"> .. # install mio cmake --build . --config Release --target install ``` Note that the last command of the installation sequence may require administrator privileges (e.g. `sudo`) if the installation root directory lies outside your home directory. This installation + copies the mio header files to the `include/mio` subdirectory of the installation root + generates and copies several CMake configuration files to the `share/cmake/mio` subdirectory of the installation root This latter step allows downstream CMake projects to consume mio via `find_package`, e.g. ```cmake find_package( mio REQUIRED ) target_link_libraries( MyTarget PUBLIC mio::mio ) ``` **WINDOWS USERS**: The `mio::mio` target `#define`s `WIN32_LEAN_AND_MEAN` and `NOMINMAX`. The former ensures the imported surface area of the Win API is minimal, and the latter disables Windows' `min` and `max` macros so they don't intefere with `std::min` and `std::max`. Because *mio* is a header only library, these defintions will leak into downstream CMake builds. If their presence is causing problems with your build then you can use the alternative `mio::mio_full_winapi` target, which adds none of these defintions. If mio was installed to a non-conventional location, it may be necessary for downstream projects to specify the mio installation root directory via either + the `CMAKE_PREFIX_PATH` configuration option, + the `CMAKE_PREFIX_PATH` environment variable, or + `mio_DIR` environment variable. Please see the [Kitware documentation](https://cmake.org/cmake/help/v3.0/command/find_package.html) for more information. In addition, mio supports packaged relocatable installations via [CPack](https://cmake.org/cmake/help/latest/manual/cpack.1.html). Following configuration, from the build directory, invoke cpack as follows to generate a packaged installation: ```sh cpack -G -C Release ``` The list of supported generators varies from platform to platform. See the output of `cpack --help` for a complete list of supported generators on your platform. ### Subproject Composition To use mio as a subproject, copy the mio repository to your project's dependencies/externals folder. If your project is version controlled using git, a git submodule or git subtree can be used to syncronize with the updstream repository. The [use](https://services.github.com/on-demand/downloads/submodule-vs-subtree-cheat-sheet/) and [relative advantages](https://andrey.nering.com.br/2016/git-submodules-vs-subtrees/) of these git facilities is beyond the scope of this document, but in brief, each may be established as follows: ```sh # via git submodule cd git submodule add -b master https://github.com/mandreyel/mio.git # via git subtree cd git subtree add --prefix /mio \ https://github.com/mandreyel/mio.git master --squash ``` Given a mio subdirectory in a project, simply add the following lines to your project's to add mio include directories to your target's include path. ```cmake add_subdirectory( path/to/mio/ ) target_link_libraries( MyTarget PUBLIC ) ``` Note that, as a subproject, mio's tests and examples will not be built and CPack integration is deferred to the host project. vroom/src/mio/include/0000755000176200001440000000000013667104530014417 5ustar liggesusersvroom/src/mio/include/mio/0000755000176200001440000000000014533652003015177 5ustar liggesusersvroom/src/mio/include/mio/detail/0000755000176200001440000000000014533652003016441 5ustar liggesusersvroom/src/mio/include/mio/detail/mmap.ipp0000644000176200001440000003447714132374245020130 0ustar liggesusers/* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MIO_BASIC_MMAP_IMPL #define MIO_BASIC_MMAP_IMPL #include "mio/mmap.hpp" #include "mio/page.hpp" #include "mio/detail/string_util.hpp" #include #ifndef _WIN32 # include # include # include # include #endif namespace mio { namespace detail { #ifdef _WIN32 namespace win { /** Returns the 4 upper bytes of an 8-byte integer. */ inline DWORD int64_high(int64_t n) noexcept { return n >> 32; } /** Returns the 4 lower bytes of an 8-byte integer. */ inline DWORD int64_low(int64_t n) noexcept { return n & 0xffffffff; } template< typename String, typename = typename std::enable_if< std::is_same::type, char>::value >::type > file_handle_type open_file_helper(const String& path, const access_mode mode) { return ::CreateFileA(c_str(path), mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); } template typename std::enable_if< std::is_same::type, wchar_t>::value, file_handle_type >::type open_file_helper(const String& path, const access_mode mode) { return ::CreateFileW(c_str(path), mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); } } // win #endif // _WIN32 /** * Returns the last platform specific system error (errno on POSIX and * GetLastError on Win) as a `std::error_code`. */ inline std::error_code last_error() noexcept { std::error_code error; #ifdef _WIN32 error.assign(GetLastError(), std::system_category()); #else error.assign(errno, std::system_category()); #endif return error; } template file_handle_type open_file(const String& path, const access_mode mode, std::error_code& error) { error.clear(); if(detail::empty(path)) { error = std::make_error_code(std::errc::invalid_argument); return invalid_handle; } #ifdef _WIN32 const auto handle = win::open_file_helper(path, mode); #else // POSIX const auto handle = ::open(c_str(path), mode == access_mode::read ? O_RDONLY : O_RDWR); #endif if(handle == invalid_handle) { error = detail::last_error(); } return handle; } inline size_t query_file_size(file_handle_type handle, std::error_code& error) { error.clear(); #ifdef _WIN32 LARGE_INTEGER file_size; if(::GetFileSizeEx(handle, &file_size) == 0) { error = detail::last_error(); return 0; } return static_cast(file_size.QuadPart); #else // POSIX struct stat sbuf; if(::fstat(handle, &sbuf) == -1) { error = detail::last_error(); return 0; } return sbuf.st_size; #endif } struct mmap_context { char* data; int64_t length; int64_t mapped_length; #ifdef _WIN32 file_handle_type file_mapping_handle; #endif }; inline mmap_context memory_map(const file_handle_type file_handle, const int64_t offset, const int64_t length, const access_mode mode, std::error_code& error) { const int64_t aligned_offset = make_offset_page_aligned(offset); const int64_t length_to_map = offset - aligned_offset + length; #ifdef _WIN32 const int64_t max_file_size = offset + length; const auto file_mapping_handle = ::CreateFileMapping( file_handle, 0, mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE, win::int64_high(max_file_size), win::int64_low(max_file_size), 0); if(file_mapping_handle == invalid_handle) { error = detail::last_error(); return {}; } char* mapping_start = static_cast(::MapViewOfFile( file_mapping_handle, mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE, win::int64_high(aligned_offset), win::int64_low(aligned_offset), length_to_map)); if(mapping_start == nullptr) { error = detail::last_error(); return {}; } #else // POSIX char* mapping_start = static_cast(::mmap( 0, // Don't give hint as to where to map. length_to_map, mode == access_mode::read ? PROT_READ : PROT_WRITE, MAP_SHARED, file_handle, aligned_offset)); if(mapping_start == MAP_FAILED) { error = detail::last_error(); return {}; } #endif mmap_context ctx; ctx.data = mapping_start + offset; ctx.length = length; ctx.mapped_length = length_to_map; #ifdef _WIN32 ctx.file_mapping_handle = file_mapping_handle; #endif return ctx; } } // namespace detail // -- basic_mmap -- template basic_mmap::~basic_mmap() { conditional_sync(); unmap(); } template basic_mmap::basic_mmap(basic_mmap&& other) : data_(std::move(other.data_)) , length_(std::move(other.length_)) , mapped_length_(std::move(other.mapped_length_)) , file_handle_(std::move(other.file_handle_)) #ifdef _WIN32 , file_mapping_handle_(std::move(other.file_mapping_handle_)) #endif , is_handle_internal_(std::move(other.is_handle_internal_)) { other.data_ = nullptr; other.length_ = other.mapped_length_ = 0; other.file_handle_ = invalid_handle; #ifdef _WIN32 other.file_mapping_handle_ = invalid_handle; #endif } template basic_mmap& basic_mmap::operator=(basic_mmap&& other) { if(this != &other) { // First the existing mapping needs to be removed. unmap(); data_ = std::move(other.data_); length_ = std::move(other.length_); mapped_length_ = std::move(other.mapped_length_); file_handle_ = std::move(other.file_handle_); #ifdef _WIN32 file_mapping_handle_ = std::move(other.file_mapping_handle_); #endif is_handle_internal_ = std::move(other.is_handle_internal_); // The moved from basic_mmap's fields need to be reset, because // otherwise other's destructor will unmap the same mapping that was // just moved into this. other.data_ = nullptr; other.length_ = other.mapped_length_ = 0; other.file_handle_ = invalid_handle; #ifdef _WIN32 other.file_mapping_handle_ = invalid_handle; #endif other.is_handle_internal_ = false; } return *this; } template typename basic_mmap::handle_type basic_mmap::mapping_handle() const noexcept { #ifdef _WIN32 return file_mapping_handle_; #else return file_handle_; #endif } template template void basic_mmap::map(const String& path, const size_type offset, const size_type length, std::error_code& error) { error.clear(); if(detail::empty(path)) { error = std::make_error_code(std::errc::invalid_argument); return; } const auto handle = detail::open_file(path, AccessMode, error); if(error) { return; } map(handle, offset, length, error); // This MUST be after the call to map, as that sets this to true. if(!error) { is_handle_internal_ = true; } } template void basic_mmap::map(const handle_type handle, const size_type offset, const size_type length, std::error_code& error) { error.clear(); if(handle == invalid_handle) { error = std::make_error_code(std::errc::bad_file_descriptor); return; } const size_type file_size = detail::query_file_size(handle, error); if(error) { return; } if(offset + length > file_size) { error = std::make_error_code(std::errc::invalid_argument); return; } const auto ctx = detail::memory_map(handle, offset, length == map_entire_file ? (file_size - offset) : length, AccessMode, error); if(!error) { // We must unmap the previous mapping that may have existed prior to this call. // Note that this must only be invoked after a new mapping has been created in // order to provide the strong guarantee that, should the new mapping fail, the // `map` function leaves this instance in a state as though the function had // never been invoked. unmap(); file_handle_ = handle; is_handle_internal_ = false; data_ = reinterpret_cast(ctx.data); length_ = ctx.length; mapped_length_ = ctx.mapped_length; #ifdef _WIN32 file_mapping_handle_ = ctx.file_mapping_handle; #endif } } template template typename std::enable_if::type basic_mmap::sync(std::error_code& error) { error.clear(); if(!is_open()) { error = std::make_error_code(std::errc::bad_file_descriptor); return; } if(data()) { #ifdef _WIN32 if(::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0 || ::FlushFileBuffers(file_handle_) == 0) #else // POSIX if(::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0) #endif { error = detail::last_error(); return; } } #ifdef _WIN32 if(::FlushFileBuffers(file_handle_) == 0) { error = detail::last_error(); } #endif } template void basic_mmap::unmap() { if(!is_open()) { return; } // TODO do we care about errors here? #ifdef _WIN32 if(is_mapped()) { ::UnmapViewOfFile(get_mapping_start()); ::CloseHandle(file_mapping_handle_); } #else // POSIX if(data_) { ::munmap(const_cast(get_mapping_start()), mapped_length_); } #endif // If `file_handle_` was obtained by our opening it (when map is called with // a path, rather than an existing file handle), we need to close it, // otherwise it must not be closed as it may still be used outside this // instance. if(is_handle_internal_) { #ifdef _WIN32 ::CloseHandle(file_handle_); #else // POSIX ::close(file_handle_); #endif } // Reset fields to their default values. data_ = nullptr; length_ = mapped_length_ = 0; file_handle_ = invalid_handle; #ifdef _WIN32 file_mapping_handle_ = invalid_handle; #endif } template bool basic_mmap::is_mapped() const noexcept { #ifdef _WIN32 return file_mapping_handle_ != invalid_handle; #else // POSIX return is_open(); #endif } template void basic_mmap::swap(basic_mmap& other) { if(this != &other) { using std::swap; swap(data_, other.data_); swap(file_handle_, other.file_handle_); #ifdef _WIN32 swap(file_mapping_handle_, other.file_mapping_handle_); #endif swap(length_, other.length_); swap(mapped_length_, other.mapped_length_); swap(is_handle_internal_, other.is_handle_internal_); } } template template typename std::enable_if::type basic_mmap::conditional_sync() { // This is invoked from the destructor, so not much we can do about // failures here. std::error_code ec; sync(ec); } template template typename std::enable_if::type basic_mmap::conditional_sync() { // noop } template bool operator==(const basic_mmap& a, const basic_mmap& b) { return a.data() == b.data() && a.size() == b.size(); } template bool operator!=(const basic_mmap& a, const basic_mmap& b) { return !(a == b); } template bool operator<(const basic_mmap& a, const basic_mmap& b) { if(a.data() == b.data()) { return a.size() < b.size(); } return a.data() < b.data(); } template bool operator<=(const basic_mmap& a, const basic_mmap& b) { return !(a > b); } template bool operator>(const basic_mmap& a, const basic_mmap& b) { if(a.data() == b.data()) { return a.size() > b.size(); } return a.data() > b.data(); } template bool operator>=(const basic_mmap& a, const basic_mmap& b) { return !(a < b); } } // namespace mio #endif // MIO_BASIC_MMAP_IMPL vroom/src/mio/include/mio/detail/string_util.hpp0000644000176200001440000001024713445253572021532 0ustar liggesusers/* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MIO_STRING_UTIL_HEADER #define MIO_STRING_UTIL_HEADER #include namespace mio { namespace detail { template< typename S, typename C = typename std::decay::type, typename = decltype(std::declval().data()), typename = typename std::enable_if< std::is_same::value #ifdef _WIN32 || std::is_same::value #endif >::type > struct char_type_helper { using type = typename C::value_type; }; template struct char_type { using type = typename char_type_helper::type; }; // TODO: can we avoid this brute force approach? template<> struct char_type { using type = char; }; template<> struct char_type { using type = char; }; template struct char_type { using type = char; }; template struct char_type { using type = char; }; #ifdef _WIN32 template<> struct char_type { using type = wchar_t; }; template<> struct char_type { using type = wchar_t; }; template struct char_type { using type = wchar_t; }; template struct char_type { using type = wchar_t; }; #endif // _WIN32 template struct is_c_str_helper { static constexpr bool value = std::is_same< CharT*, // TODO: I'm so sorry for this... Can this be made cleaner? typename std::add_pointer< typename std::remove_cv< typename std::remove_pointer< typename std::decay< S >::type >::type >::type >::type >::value; }; template struct is_c_str { static constexpr bool value = is_c_str_helper::value; }; #ifdef _WIN32 template struct is_c_wstr { static constexpr bool value = is_c_str_helper::value; }; #endif // _WIN32 template struct is_c_str_or_c_wstr { static constexpr bool value = is_c_str::value #ifdef _WIN32 || is_c_wstr::value #endif ; }; template< typename String, typename = decltype(std::declval().data()), typename = typename std::enable_if::value>::type > const typename char_type::type* c_str(const String& path) { return path.data(); } template< typename String, typename = decltype(std::declval().empty()), typename = typename std::enable_if::value>::type > bool empty(const String& path) { return path.empty(); } template< typename String, typename = typename std::enable_if::value>::type > const typename char_type::type* c_str(String path) { return path; } template< typename String, typename = typename std::enable_if::value>::type > bool empty(String path) { return !path || (*path == 0); } } // namespace detail } // namespace mio #endif // MIO_STRING_UTIL_HEADER vroom/src/mio/include/mio/shared_mmap.hpp0000644000176200001440000004014313445253572020203 0ustar liggesusers/* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MIO_SHARED_MMAP_HEADER #define MIO_SHARED_MMAP_HEADER #include "mio/mmap.hpp" #include // std::error_code #include // std::shared_ptr namespace mio { /** * Exposes (nearly) the same interface as `basic_mmap`, but endowes it with * `std::shared_ptr` semantics. * * This is not the default behaviour of `basic_mmap` to avoid allocating on the heap if * shared semantics are not required. */ template< access_mode AccessMode, typename ByteT > class basic_shared_mmap { using impl_type = basic_mmap; std::shared_ptr pimpl_; public: using value_type = typename impl_type::value_type; using size_type = typename impl_type::size_type; using reference = typename impl_type::reference; using const_reference = typename impl_type::const_reference; using pointer = typename impl_type::pointer; using const_pointer = typename impl_type::const_pointer; using difference_type = typename impl_type::difference_type; using iterator = typename impl_type::iterator; using const_iterator = typename impl_type::const_iterator; using reverse_iterator = typename impl_type::reverse_iterator; using const_reverse_iterator = typename impl_type::const_reverse_iterator; using iterator_category = typename impl_type::iterator_category; using handle_type = typename impl_type::handle_type; using mmap_type = impl_type; basic_shared_mmap() = default; basic_shared_mmap(const basic_shared_mmap&) = default; basic_shared_mmap& operator=(const basic_shared_mmap&) = default; basic_shared_mmap(basic_shared_mmap&&) = default; basic_shared_mmap& operator=(basic_shared_mmap&&) = default; /** Takes ownership of an existing mmap object. */ basic_shared_mmap(mmap_type&& mmap) : pimpl_(std::make_shared(std::move(mmap))) {} /** Takes ownership of an existing mmap object. */ basic_shared_mmap& operator=(mmap_type&& mmap) { pimpl_ = std::make_shared(std::move(mmap)); return *this; } /** Initializes this object with an already established shared mmap. */ basic_shared_mmap(std::shared_ptr mmap) : pimpl_(std::move(mmap)) {} /** Initializes this object with an already established shared mmap. */ basic_shared_mmap& operator=(std::shared_ptr mmap) { pimpl_ = std::move(mmap); return *this; } #ifdef __cpp_exceptions /** * The same as invoking the `map` function, except any error that may occur * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ template basic_shared_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(path, offset, length, error); if(error) { throw std::system_error(error); } } /** * The same as invoking the `map` function, except any error that may occur * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ basic_shared_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(handle, offset, length, error); if(error) { throw std::system_error(error); } } #endif // __cpp_exceptions /** * If this is a read-write mapping and the last reference to the mapping, * the destructor invokes sync. Regardless of the access mode, unmap is * invoked as a final step. */ ~basic_shared_mmap() = default; /** Returns the underlying `std::shared_ptr` instance that holds the mmap. */ std::shared_ptr get_shared_ptr() { return pimpl_; } /** * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows, * however, a mapped region of a file gets its own handle, which is returned by * 'mapping_handle'. */ handle_type file_handle() const noexcept { return pimpl_ ? pimpl_->file_handle() : invalid_handle; } handle_type mapping_handle() const noexcept { return pimpl_ ? pimpl_->mapping_handle() : invalid_handle; } /** Returns whether a valid memory mapping has been created. */ bool is_open() const noexcept { return pimpl_ && pimpl_->is_open(); } /** * Returns true if no mapping was established, that is, conceptually the * same as though the length that was mapped was 0. This function is * provided so that this class has Container semantics. */ bool empty() const noexcept { return !pimpl_ || pimpl_->empty(); } /** * `size` and `length` both return the logical length, i.e. the number of bytes * user requested to be mapped, while `mapped_length` returns the actual number of * bytes that were mapped which is a multiple of the underlying operating system's * page allocation granularity. */ size_type size() const noexcept { return pimpl_ ? pimpl_->length() : 0; } size_type length() const noexcept { return pimpl_ ? pimpl_->length() : 0; } size_type mapped_length() const noexcept { return pimpl_ ? pimpl_->mapped_length() : 0; } /** * Returns the offset, relative to the file's start, at which the mapping was * requested to be created. */ size_type offset() const noexcept { return pimpl_ ? pimpl_->offset() : 0; } /** * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping * exists. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > pointer data() noexcept { return pimpl_->data(); } const_pointer data() const noexcept { return pimpl_ ? pimpl_->data() : nullptr; } /** * Returns an iterator to the first requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ iterator begin() noexcept { return pimpl_->begin(); } const_iterator begin() const noexcept { return pimpl_->begin(); } const_iterator cbegin() const noexcept { return pimpl_->cbegin(); } /** * Returns an iterator one past the last requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > iterator end() noexcept { return pimpl_->end(); } const_iterator end() const noexcept { return pimpl_->end(); } const_iterator cend() const noexcept { return pimpl_->cend(); } /** * Returns a reverse iterator to the last memory mapped byte, if a valid * memory mapping exists, otherwise this function call is undefined * behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > reverse_iterator rbegin() noexcept { return pimpl_->rbegin(); } const_reverse_iterator rbegin() const noexcept { return pimpl_->rbegin(); } const_reverse_iterator crbegin() const noexcept { return pimpl_->crbegin(); } /** * Returns a reverse iterator past the first mapped byte, if a valid memory * mapping exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > reverse_iterator rend() noexcept { return pimpl_->rend(); } const_reverse_iterator rend() const noexcept { return pimpl_->rend(); } const_reverse_iterator crend() const noexcept { return pimpl_->crend(); } /** * Returns a reference to the `i`th byte from the first requested byte (as returned * by `data`). If this is invoked when no valid memory mapping has been created * prior to this call, undefined behaviour ensues. */ reference operator[](const size_type i) noexcept { return (*pimpl_)[i]; } const_reference operator[](const size_type i) const noexcept { return (*pimpl_)[i]; } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `path`, which must be a path to an existing file, is used to retrieve a file * handle (which is closed when the object destructs or `unmap` is called), which is * then used to memory map the requested region. Upon failure, `error` is set to * indicate the reason and the object remains in an unmapped state. * * `offset` is the number of bytes, relative to the start of the file, where the * mapping should begin. When specifying it, there is no need to worry about * providing a value that is aligned with the operating system's page allocation * granularity. This is adjusted by the implementation such that the first requested * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at * `offset` from the start of the file. * * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ template void map(const String& path, const size_type offset, const size_type length, std::error_code& error) { map_impl(path, offset, length, error); } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `path`, which must be a path to an existing file, is used to retrieve a file * handle (which is closed when the object destructs or `unmap` is called), which is * then used to memory map the requested region. Upon failure, `error` is set to * indicate the reason and the object remains in an unmapped state. * * The entire file is mapped. */ template void map(const String& path, std::error_code& error) { map_impl(path, 0, map_entire_file, error); } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `handle`, which must be a valid file handle, which is used to memory map the * requested region. Upon failure, `error` is set to indicate the reason and the * object remains in an unmapped state. * * `offset` is the number of bytes, relative to the start of the file, where the * mapping should begin. When specifying it, there is no need to worry about * providing a value that is aligned with the operating system's page allocation * granularity. This is adjusted by the implementation such that the first requested * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at * `offset` from the start of the file. * * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ void map(const handle_type handle, const size_type offset, const size_type length, std::error_code& error) { map_impl(handle, offset, length, error); } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `handle`, which must be a valid file handle, which is used to memory map the * requested region. Upon failure, `error` is set to indicate the reason and the * object remains in an unmapped state. * * The entire file is mapped. */ void map(const handle_type handle, std::error_code& error) { map_impl(handle, 0, map_entire_file, error); } /** * If a valid memory mapping has been created prior to this call, this call * instructs the kernel to unmap the memory region and disassociate this object * from the file. * * The file handle associated with the file that is mapped is only closed if the * mapping was created using a file path. If, on the other hand, an existing * file handle was used to create the mapping, the file handle is not closed. */ void unmap() { if(pimpl_) pimpl_->unmap(); } void swap(basic_shared_mmap& other) { pimpl_.swap(other.pimpl_); } /** Flushes the memory mapped page to disk. Errors are reported via `error`. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > void sync(std::error_code& error) { if(pimpl_) pimpl_->sync(error); } /** All operators compare the underlying `basic_mmap`'s addresses. */ friend bool operator==(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ == b.pimpl_; } friend bool operator!=(const basic_shared_mmap& a, const basic_shared_mmap& b) { return !(a == b); } friend bool operator<(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ < b.pimpl_; } friend bool operator<=(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ <= b.pimpl_; } friend bool operator>(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ > b.pimpl_; } friend bool operator>=(const basic_shared_mmap& a, const basic_shared_mmap& b) { return a.pimpl_ >= b.pimpl_; } private: template void map_impl(const MappingToken& token, const size_type offset, const size_type length, std::error_code& error) { if(!pimpl_) { mmap_type mmap = make_mmap(token, offset, length, error); if(error) { return; } pimpl_ = std::make_shared(std::move(mmap)); } else { pimpl_->map(token, offset, length, error); } } }; /** * This is the basis for all read-only mmap objects and should be preferred over * directly using basic_shared_mmap. */ template using basic_shared_mmap_source = basic_shared_mmap; /** * This is the basis for all read-write mmap objects and should be preferred over * directly using basic_shared_mmap. */ template using basic_shared_mmap_sink = basic_shared_mmap; /** * These aliases cover the most common use cases, both representing a raw byte stream * (either with a char or an unsigned char/uint8_t). */ using shared_mmap_source = basic_shared_mmap_source; using shared_ummap_source = basic_shared_mmap_source; using shared_mmap_sink = basic_shared_mmap_sink; using shared_ummap_sink = basic_shared_mmap_sink; } // namespace mio #endif // MIO_SHARED_MMAP_HEADER vroom/src/mio/include/mio/page.hpp0000644000176200001440000000474513445253572016647 0ustar liggesusers/* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MIO_PAGE_HEADER #define MIO_PAGE_HEADER #ifdef _WIN32 # include #else # include #endif namespace mio { /** * This is used by `basic_mmap` to determine whether to create a read-only or * a read-write memory mapping. */ enum class access_mode { read, write }; /** * Determines the operating system's page allocation granularity. * * On the first call to this function, it invokes the operating system specific syscall * to determine the page size, caches the value, and returns it. Any subsequent call to * this function serves the cached value, so no further syscalls are made. */ inline size_t page_size() { static const size_t page_size = [] { #ifdef _WIN32 SYSTEM_INFO SystemInfo; GetSystemInfo(&SystemInfo); return SystemInfo.dwAllocationGranularity; #else return sysconf(_SC_PAGE_SIZE); #endif }(); return page_size; } /** * Alligns `offset` to the operating's system page size such that it subtracts the * difference until the nearest page boundary before `offset`, or does nothing if * `offset` is already page aligned. */ inline size_t make_offset_page_aligned(size_t offset) noexcept { const size_t page_size_ = page_size(); // Use integer division to round down to the nearest page alignment. return offset / page_size_ * page_size_; } } // namespace mio #endif // MIO_PAGE_HEADER vroom/src/mio/include/mio/mmap.hpp0000644000176200001440000004530314132374245016653 0ustar liggesusers/* Copyright 2017 https://github.com/mandreyel * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MIO_MMAP_HEADER #define MIO_MMAP_HEADER #include "mio/page.hpp" #include #include #include #include #ifdef _WIN32 # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif // WIN32_LEAN_AND_MEAN # include #else // ifdef _WIN32 # define INVALID_HANDLE_VALUE -1 #endif // ifdef _WIN32 namespace mio { // This value may be provided as the `length` parameter to the constructor or // `map`, in which case a memory mapping of the entire file is created. enum { map_entire_file = 0 }; #ifdef _WIN32 using file_handle_type = HANDLE; #else using file_handle_type = int; #endif // This value represents an invalid file handle type. This can be used to // determine whether `basic_mmap::file_handle` is valid, for example. const static file_handle_type invalid_handle = INVALID_HANDLE_VALUE; template struct basic_mmap { using value_type = ByteT; using size_type = size_t; using reference = value_type&; using const_reference = const value_type&; using pointer = value_type*; using const_pointer = const value_type*; using difference_type = std::ptrdiff_t; using iterator = pointer; using const_iterator = const_pointer; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; using iterator_category = std::random_access_iterator_tag; using handle_type = file_handle_type; static_assert(sizeof(ByteT) == sizeof(char), "ByteT must be the same size as char."); private: // Points to the first requested byte, and not to the actual start of the mapping. pointer data_ = nullptr; // Length--in bytes--requested by user (which may not be the length of the // full mapping) and the length of the full mapping. size_type length_ = 0; size_type mapped_length_ = 0; // Letting user map a file using both an existing file handle and a path // introcudes some complexity (see `is_handle_internal_`). // On POSIX, we only need a file handle to create a mapping, while on // Windows systems the file handle is necessary to retrieve a file mapping // handle, but any subsequent operations on the mapped region must be done // through the latter. handle_type file_handle_ = INVALID_HANDLE_VALUE; #ifdef _WIN32 handle_type file_mapping_handle_ = INVALID_HANDLE_VALUE; #endif // Letting user map a file using both an existing file handle and a path // introcudes some complexity in that we must not close the file handle if // user provided it, but we must close it if we obtained it using the // provided path. For this reason, this flag is used to determine when to // close `file_handle_`. bool is_handle_internal_ = false; public: /** * The default constructed mmap object is in a non-mapped state, that is, * any operation that attempts to access nonexistent underlying data will * result in undefined behaviour/segmentation faults. */ basic_mmap() = default; #ifdef __cpp_exceptions /** * The same as invoking the `map` function, except any error that may occur * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ template basic_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(path, offset, length, error); if(error) { throw std::system_error(error); } } /** * The same as invoking the `map` function, except any error that may occur * while establishing the mapping is wrapped in a `std::system_error` and is * thrown. */ basic_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) { std::error_code error; map(handle, offset, length, error); if(error) { throw std::system_error(error); } } #endif // __cpp_exceptions /** * `basic_mmap` has single-ownership semantics, so transferring ownership * may only be accomplished by moving the object. */ basic_mmap(const basic_mmap&) = delete; basic_mmap(basic_mmap&&); basic_mmap& operator=(const basic_mmap&) = delete; basic_mmap& operator=(basic_mmap&&); /** * If this is a read-write mapping, the destructor invokes sync. Regardless * of the access mode, unmap is invoked as a final step. */ ~basic_mmap(); /** * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows, * however, a mapped region of a file gets its own handle, which is returned by * 'mapping_handle'. */ handle_type file_handle() const noexcept { return file_handle_; } handle_type mapping_handle() const noexcept; /** Returns whether a valid memory mapping has been created. */ bool is_open() const noexcept { return file_handle_ != invalid_handle; } /** * Returns true if no mapping was established, that is, conceptually the * same as though the length that was mapped was 0. This function is * provided so that this class has Container semantics. */ bool empty() const noexcept { return length() == 0; } /** Returns true if a mapping was established. */ bool is_mapped() const noexcept; /** * `size` and `length` both return the logical length, i.e. the number of bytes * user requested to be mapped, while `mapped_length` returns the actual number of * bytes that were mapped which is a multiple of the underlying operating system's * page allocation granularity. */ size_type size() const noexcept { return length(); } size_type length() const noexcept { return length_; } size_type mapped_length() const noexcept { return mapped_length_; } /** * Returns the offset, relative to the file's start, at which the mapping was * requested to be created. */ size_type offset() const noexcept { return mapped_length_ - length_; } /** * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping * exists. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > pointer data() noexcept { return data_; } const_pointer data() const noexcept { return data_; } /** * Returns an iterator to the first requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > iterator begin() noexcept { return data(); } const_iterator begin() const noexcept { return data(); } const_iterator cbegin() const noexcept { return data(); } /** * Returns an iterator one past the last requested byte, if a valid memory mapping * exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > iterator end() noexcept { return data() + length(); } const_iterator end() const noexcept { return data() + length(); } const_iterator cend() const noexcept { return data() + length(); } /** * Returns a reverse iterator to the last memory mapped byte, if a valid * memory mapping exists, otherwise this function call is undefined * behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); } /** * Returns a reverse iterator past the first mapped byte, if a valid memory * mapping exists, otherwise this function call is undefined behaviour. */ template< access_mode A = AccessMode, typename = typename std::enable_if::type > reverse_iterator rend() noexcept { return reverse_iterator(begin()); } const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); } /** * Returns a reference to the `i`th byte from the first requested byte (as returned * by `data`). If this is invoked when no valid memory mapping has been created * prior to this call, undefined behaviour ensues. */ reference operator[](const size_type i) noexcept { return data_[i]; } const_reference operator[](const size_type i) const noexcept { return data_[i]; } /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `path`, which must be a path to an existing file, is used to retrieve a file * handle (which is closed when the object destructs or `unmap` is called), which is * then used to memory map the requested region. Upon failure, `error` is set to * indicate the reason and the object remains in an unmapped state. * * `offset` is the number of bytes, relative to the start of the file, where the * mapping should begin. When specifying it, there is no need to worry about * providing a value that is aligned with the operating system's page allocation * granularity. This is adjusted by the implementation such that the first requested * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at * `offset` from the start of the file. * * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ template void map(const String& path, const size_type offset, const size_type length, std::error_code& error); /** * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the * reason is reported via `error` and the object remains in a state as if this * function hadn't been called. * * `path`, which must be a path to an existing file, is used to retrieve a file * handle (which is closed when the object destructs or `unmap` is called), which is * then used to memory map the requested region. Upon failure, `error` is set to * indicate the reason and the object remains in an unmapped state. * * The entire file is mapped. */ template void map(const String& path, std::error_code& error) { map(path, 0, map_entire_file, error); } /** * Establishes a memory mapping with AccessMode. If the mapping is * unsuccesful, the reason is reported via `error` and the object remains in * a state as if this function hadn't been called. * * `handle`, which must be a valid file handle, which is used to memory map the * requested region. Upon failure, `error` is set to indicate the reason and the * object remains in an unmapped state. * * `offset` is the number of bytes, relative to the start of the file, where the * mapping should begin. When specifying it, there is no need to worry about * providing a value that is aligned with the operating system's page allocation * granularity. This is adjusted by the implementation such that the first requested * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at * `offset` from the start of the file. * * `length` is the number of bytes to map. It may be `map_entire_file`, in which * case a mapping of the entire file is created. */ void map(const handle_type handle, const size_type offset, const size_type length, std::error_code& error); /** * Establishes a memory mapping with AccessMode. If the mapping is * unsuccesful, the reason is reported via `error` and the object remains in * a state as if this function hadn't been called. * * `handle`, which must be a valid file handle, which is used to memory map the * requested region. Upon failure, `error` is set to indicate the reason and the * object remains in an unmapped state. * * The entire file is mapped. */ void map(const handle_type handle, std::error_code& error) { map(handle, 0, map_entire_file, error); } /** * If a valid memory mapping has been created prior to this call, this call * instructs the kernel to unmap the memory region and disassociate this object * from the file. * * The file handle associated with the file that is mapped is only closed if the * mapping was created using a file path. If, on the other hand, an existing * file handle was used to create the mapping, the file handle is not closed. */ void unmap(); void swap(basic_mmap& other); /** Flushes the memory mapped page to disk. Errors are reported via `error`. */ template typename std::enable_if::type sync(std::error_code& error); /** * All operators compare the address of the first byte and size of the two mapped * regions. */ private: template< access_mode A = AccessMode, typename = typename std::enable_if::type > pointer get_mapping_start() noexcept { return !data() ? nullptr : data() - offset(); } const_pointer get_mapping_start() const noexcept { return !data() ? nullptr : data() - offset(); } /** * The destructor syncs changes to disk if `AccessMode` is `write`, but not * if it's `read`, but since the destructor cannot be templated, we need to * do SFINAE in a dedicated function, where one syncs and the other is a noop. */ template typename std::enable_if::type conditional_sync(); template typename std::enable_if::type conditional_sync(); }; template bool operator==(const basic_mmap& a, const basic_mmap& b); template bool operator!=(const basic_mmap& a, const basic_mmap& b); template bool operator<(const basic_mmap& a, const basic_mmap& b); template bool operator<=(const basic_mmap& a, const basic_mmap& b); template bool operator>(const basic_mmap& a, const basic_mmap& b); template bool operator>=(const basic_mmap& a, const basic_mmap& b); /** * This is the basis for all read-only mmap objects and should be preferred over * directly using `basic_mmap`. */ template using basic_mmap_source = basic_mmap; /** * This is the basis for all read-write mmap objects and should be preferred over * directly using `basic_mmap`. */ template using basic_mmap_sink = basic_mmap; /** * These aliases cover the most common use cases, both representing a raw byte stream * (either with a char or an unsigned char/uint8_t). */ using mmap_source = basic_mmap_source; using ummap_source = basic_mmap_source; using mmap_sink = basic_mmap_sink; using ummap_sink = basic_mmap_sink; /** * Convenience factory method that constructs a mapping for any `basic_mmap` or * `basic_mmap` type. */ template< typename MMap, typename MappingToken > MMap make_mmap(const MappingToken& token, int64_t offset, int64_t length, std::error_code& error) { MMap mmap; mmap.map(token, offset, length, error); return mmap; } /** * Convenience factory method. * * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`, * `std::filesystem::path`, `std::vector`, or similar), or a * `mmap_source::handle_type`. */ template mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset, mmap_source::size_type length, std::error_code& error) { return make_mmap(token, offset, length, error); } template mmap_source make_mmap_source(const MappingToken& token, std::error_code& error) { return make_mmap_source(token, 0, map_entire_file, error); } /** * Convenience factory method. * * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`, * `std::filesystem::path`, `std::vector`, or similar), or a * `mmap_sink::handle_type`. */ template mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset, mmap_sink::size_type length, std::error_code& error) { return make_mmap(token, offset, length, error); } template mmap_sink make_mmap_sink(const MappingToken& token, std::error_code& error) { return make_mmap_sink(token, 0, map_entire_file, error); } } // namespace mio #include "detail/mmap.ipp" #endif // MIO_MMAP_HEADER vroom/src/connection.h0000644000176200001440000000356114132374245014525 0ustar liggesusers#pragma once #include #include #include #include // We need to undefine these here as they may be previously defined in windows headers #undef TRUE #undef FALSE #include #ifdef VROOM_USE_CONNECTIONS_API // clang-format off #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wkeyword-macro" #endif #define class class_name #define private private_ptr #include #undef class #undef private #ifdef __clang__ # pragma clang diagnostic pop #endif // clang-format on #if R_CONNECTIONS_VERSION != 1 #error "Missing or unsupported connection API in R" #endif #if R_VERSION < R_Version(3, 3, 0) /* R before 3.3.0 didn't have R_GetConnection() */ extern "C" { extern Rconnection getConnection(int n); static Rconnection R_GetConnection(SEXP sConn) { return getConnection(Rf_asInteger(sConn)); } } #endif #else #pragma once inline SEXP R_GetConnection(SEXP con) { return con; } inline size_t R_ReadConnection(SEXP con, void* buf, size_t n) { static auto readBin = cpp11::package("base")["readBin"]; cpp11::raws res( readBin(con, cpp11::writable::raws(static_cast(0)), n)); memcpy(buf, RAW(res), res.size()); return res.size(); } inline size_t R_WriteConnection(SEXP con, void* buf, size_t n) { static auto writeBin = cpp11::package("base")["writeBin"]; cpp11::writable::raws payload(n); memcpy(RAW(payload), buf, n); writeBin(payload, con); return n; } #endif inline std::string con_description(SEXP con) { static auto summary_connection = cpp11::package("base")["summary.connection"]; return cpp11::as_cpp(cpp11::list(summary_connection(con))[0]); } inline bool is_open(SEXP con) { static auto isOpen = cpp11::package("base")["isOpen"]; cpp11::logicals res(isOpen(con)); return res[0]; } vroom/src/r_utils.h0000644000176200001440000000325314533531371014045 0ustar liggesusers#pragma once #include "utils.h" #include #include #include #include #include #ifndef R_PRIdXLEN_T # ifdef LONG_VECTOR_SUPPORT # define R_PRIdXLEN_T "td" # else # define R_PRIdXLEN_T "d" # endif #endif namespace vroom { inline std::string get_pb_format(const std::string& which, const std::string& filename = "") { auto fun_name = std::string("pb_") + which + "_format"; auto fun = cpp11::package("vroom")[fun_name.c_str()]; return cpp11::as_cpp(fun(filename)); } inline int get_pb_width(const std::string& format) { auto pb_width = cpp11::package("vroom")["pb_width"]; return cpp11::as_cpp(pb_width(format)); } template static char guess_delim( const T& source, size_t start, size_t guess_max, size_t end, const char quote) { std::vector lines; if (end == 0) { end = source.size(); } size_t nl; newline_type nlt; std::tie(nl, nlt) = find_next_newline( source, start, /* comment */ "", /* skip_empty_rows */ false, /* embedded_nl */ true, /* quote */ quote); while (nl > start && nl <= end && guess_max > 0) { auto str = std::string(source.data() + start, nl - start); lines.push_back(str); start = nl + 1; std::tie(nl, nlt) = find_next_newline( source, start, /* comment */ "", /* skip_empty_rows */ false, /* embededd_nl */ true, quote); --guess_max; } auto guess_delim = cpp11::package("vroom")["guess_delim"]; char delim; delim = cpp11::as_cpp(guess_delim(lines)); return delim; } } // namespace vroom vroom/src/vroom_fct.h0000644000176200001440000001555214533531371014367 0ustar liggesusers#include #include #include #include "altrep.h" #include "r_utils.h" #include "vroom.h" #include "vroom_vec.h" #include using namespace vroom; cpp11::integers read_fct_explicit( vroom_vec_info* info, const cpp11::strings& levels, bool ordered); cpp11::integers read_fct_implicit(vroom_vec_info* info, bool include_na); template int parse_factor( const I& itr, const C& col, const std::unordered_map& level_map, LocaleInfo& locale, std::shared_ptr& errors, SEXP na) { auto str = *itr; SEXP str_sexp = locale.encoder_.makeSEXP(str.begin(), str.end(), false); auto search = level_map.find(str_sexp); if (search != level_map.end()) { return search->second; } else { if (!is_explicit_na(na, str.begin(), str.end())) { errors->add_error( itr.index(), col->get_index(), "value in level set", std::string(str.begin(), str.end() - str.begin()), itr.filename()); } return NA_INTEGER; } } template int parse_factor( R_xlen_t i, const C& col, const std::unordered_map& level_map, LocaleInfo& locale, std::shared_ptr& errors, SEXP na) { auto str = col->at(i); SEXP str_sexp = locale.encoder_.makeSEXP(str.begin(), str.end(), false); auto search = level_map.find(str_sexp); if (search != level_map.end()) { return search->second; } else { if (!is_explicit_na(na, str.begin(), str.end())) { auto&& itr = col->begin() + i; errors->add_error( itr.index(), col->get_index(), "value in level set", std::string(str.begin(), str.end() - str.begin()), itr.filename()); } return NA_INTEGER; } } #ifdef HAS_ALTREP struct vroom_factor_info { vroom_vec_info* info; std::unordered_map levels; }; struct vroom_fct : vroom_vec { public: static R_altrep_class_t class_t; // Make an altrep object of class `vroom_factor::class_t` static SEXP Make(vroom_vec_info* info, cpp11::strings levels, bool ordered) { vroom_factor_info* fct_info = new vroom_factor_info; fct_info->info = info; for (auto i = 0; i < levels.size(); ++i) { if (levels[i] == NA_STRING) { for (const auto& str : *info->na) { fct_info->levels[str] = i + 1; } } else { fct_info->levels[levels[i]] = i + 1; } } SEXP out = PROTECT(R_MakeExternalPtr(fct_info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, Finalize, FALSE); // make a new altrep object of class `vroom_factor::class_t` cpp11::sexp res = R_new_altrep(class_t, out, R_NilValue); res.attr("levels") = static_cast(levels); if (ordered) { res.attr("class") = {"ordered", "factor"}; } else { res.attr("class") = "factor"; } UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // ALTREP methods ------------------- // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_factor (len=%" R_PRIdXLEN_T ", materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } static void Finalize(SEXP ptr) { if (ptr == nullptr || R_ExternalPtrAddr(ptr) == nullptr) { return; } auto info_p = static_cast(R_ExternalPtrAddr(ptr)); delete info_p->info; delete info_p; info_p = nullptr; R_ClearExternalPtr(ptr); } static inline vroom_factor_info& Info(SEXP x) { return *static_cast( R_ExternalPtrAddr(R_altrep_data1(x))); } static inline R_xlen_t Length(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return Rf_xlength(data2); } auto inf = Info(vec); return inf.info->column->size(); } static inline string Get(SEXP vec, R_xlen_t i) { auto inf = Info(vec); return inf.info->column->at(i); } // ALTSTRING methods ----------------- static int Val(SEXP vec, R_xlen_t i) { auto info = Info(vec); double out = parse_factor( i, info.info->column, info.levels, *info.info->locale, info.info->errors, *info.info->na); info.info->errors->warn_for_errors(); return out; } // the element at the index `i` // // this does not do bounds checking because that's expensive, so // the caller must take care of that static int factor_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return INTEGER(data2)[i]; } return Val(vec, i); } // --- Altvec static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } // allocate a standard character vector for data2 R_xlen_t n = Length(vec); cpp11::writable::integers out(n); for (R_xlen_t i = 0; i < n; ++i) { out[i] = Val(vec, i); } R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } static SEXP Extract_subset(SEXP x, SEXP indx, SEXP) { SEXP data2 = R_altrep_data2(x); // If the vector is already materialized, just fall back to the default // implementation if (data2 != R_NilValue) { return nullptr; } // If there are no indices to subset fall back to default implementation. if (Rf_xlength(indx) == 0) { return nullptr; } cpp11::sexp x_(x); auto idx = get_subset_index(indx, Rf_xlength(x)); if (idx == nullptr) { return nullptr; } auto inf = Info(x); auto info = new vroom_vec_info{ inf.info->column->subset(idx), inf.info->num_threads, inf.info->na, inf.info->locale, inf.info->errors, inf.info->format}; bool is_ordered = Rf_inherits(x_, "ordered"); return Make(info, cpp11::strings(x_.attr("levels")), is_ordered); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { class_t = R_make_altinteger_class("vroom_fct", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altinteger R_set_altinteger_Elt_method(class_t, factor_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_fct(DllInfo* dll); vroom/src/index_collection.h0000644000176200001440000000645114151705507015711 0ustar liggesusers#pragma once #include "index.h" #include "iterator.h" #include "vroom_errors.h" #include #include #ifdef VROOM_LOG #include "spdlog/spdlog.h" #endif namespace vroom { class index_collection : public index, public std::enable_shared_from_this { public: // For delimited files index_collection( const cpp11::list& in, const char* delim, const char quote, const bool trim_ws, const bool escape_double, const bool escape_backslash, const bool has_header, const size_t skip, const size_t n_max, const char* comment, const bool skip_empty_rows, const std::shared_ptr& errors, const size_t num_threads, const bool progress); // For fixed width files index_collection( const cpp11::list& in, const std::vector& col_starts, const std::vector& col_ends, const bool trim_ws, const size_t skip, const char* comment, const bool skip_empty_rows, const size_t n_max, const bool progress); string get(size_t row, size_t col) const override; size_t num_columns() const override { return columns_; } size_t num_rows() const override { return rows_; } std::vector row_sizes() const { std::vector out; for (const auto& index : indexes_) { out.push_back(index->num_rows()); } return out; } std::string get_delim() const override { return indexes_[0]->get_delim(); } public: class full_iterator : public base_iterator { size_t i_; std::shared_ptr idx_; size_t column_; size_t end_; iterator it_; iterator it_end_; iterator it_start_; public: full_iterator(std::shared_ptr idx, size_t column); void next() override; void advance(ptrdiff_t n) override; inline bool equal_to(const base_iterator& other) const override { auto other_ = static_cast(&other); return i_ == other_->i_ && it_ == other_->it_; } ptrdiff_t distance_to(const base_iterator& it) const override; string value() const override; full_iterator* clone() const override; string at(ptrdiff_t n) const override; std::string filename() const override { return it_.filename(); } size_t index() const override { return it_.index(); } size_t position() const override { return it_.position(); } virtual ~full_iterator() {} }; std::shared_ptr get_column(size_t column) const override { auto begin = new full_iterator(shared_from_this(), column); auto end = new full_iterator(shared_from_this(), column); end->advance(rows_); return std::make_shared(begin, end, column); } std::shared_ptr get_row(size_t row) const override { for (const auto& idx : indexes_) { auto sz = idx->num_rows(); if (row < sz) { return idx->get_row(row); } row -= sz; } /* should never get here */ return indexes_[0]->get_header(); } std::shared_ptr get_header() const override { return indexes_[0]->get_header(); } private: std::vector> indexes_; size_t rows_; size_t columns_; }; } // namespace vroom vroom/src/vroom_date.h0000644000176200001440000000620614533531371014524 0ustar liggesusers#pragma once #include #include "r_utils.h" #include "vroom_dttm.h" using namespace vroom; double parse_date( const char* begin, const char* end, DateTimeParser& parser, const std::string& format); cpp11::doubles read_date(vroom_vec_info* info); #ifdef HAS_ALTREP /* no support for altrep before 3.5 */ class vroom_date : public vroom_dttm { public: static R_altrep_class_t class_t; static SEXP Make(vroom_vec_info* info) { vroom_dttm_info* dttm_info = new vroom_dttm_info; dttm_info->info = info; dttm_info->parser = std::unique_ptr(new DateTimeParser(info->locale.get())); SEXP out = PROTECT(R_MakeExternalPtr(dttm_info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, vroom_dttm::Finalize, FALSE); cpp11::sexp res = R_new_altrep(class_t, out, R_NilValue); res.attr("class") = {"Date"}; UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_date (len=%" R_PRIdXLEN_T ", materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } // the element at the index `i` static double date_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return REAL(data2)[i]; } auto info = Info(vec); auto err_msg = info->info->format.size() == 0 ? std::string("date in ISO8601") : std::string("date like ") + info->info->format; double out = parse_value( i, info->info->column, [&](const char* begin, const char* end) -> double { return parse_date(begin, end, *info->parser, info->info->format); }, info->info->errors, err_msg.c_str(), *info->info->na); info->info->errors->warn_for_errors(); return out; } // --- Altvec static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } auto inf = Info(vec); auto out = read_date(inf->info); R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { vroom_date::class_t = R_make_altreal_class("vroom_date", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altreal R_set_altreal_Elt_method(class_t, date_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_date(DllInfo* dll); vroom/src/utils.h0000644000176200001440000001720514533531371013526 0ustar liggesusers#pragma once #include #include #include #include #include #include namespace vroom { inline bool is_comment(const char* begin, const char* end, const std::string& comment) { if (comment.empty() || comment.size() >= static_cast(end - begin)) { return false; } return strncmp(comment.data(), begin, comment.size()) == 0; } template inline size_t skip_rest_of_line(const T& source, size_t start) { auto out = memchr(source.data() + start, '\n', source.size() - start); if (!out) { return (source.size()); } return static_cast(out) - source.data(); } inline bool is_empty_line(const char* begin, const char* end, const bool skip_empty_rows) { if (!skip_empty_rows) { return false; } if (skip_empty_rows && *begin == '\n') { return true; } while (begin < end && (*begin == ' ' || *begin == '\t' || *begin == '\r')) { ++begin; } return *begin == '\n'; } inline std::pair is_blank_or_comment_line( const char* begin, const char* end, const std::string& comment, const bool skip_empty_rows) { bool should_skip = false; bool is_comment = false; if (!skip_empty_rows && comment.empty()) { return std::pair(should_skip, is_comment); } if (skip_empty_rows && (*begin == '\n' || *begin == '\r')) { should_skip = true; return std::pair(should_skip, is_comment); } while (begin < end && (*begin == ' ' || *begin == '\t')) { ++begin; } if (skip_empty_rows && (*begin == '\n' || *begin == '\r')) { should_skip = true; return std::pair(should_skip, is_comment); } if (!comment.empty() && strncmp(begin, comment.data(), comment.size()) == 0) { should_skip = true; is_comment = true; return std::pair(should_skip, is_comment); } return std::pair(should_skip, is_comment); } inline bool is_crlf(const char* buf, size_t pos, size_t end) { return buf[pos] == '\r' && pos + 1 < end && buf[pos + 1] == '\n'; } enum newline_type { CR /* linux */, CRLF /* windows */, LF /* old macOS */, NA /* unknown */ }; template static std::pair find_next_non_quoted_newline(const T& source, size_t start, const char quote) { if (start > source.size() - 1) { return {source.size() - 1, NA}; } std::array query = {'\r', '\n', quote, '\0'}; auto buf = source.data(); size_t pos = start; size_t end = source.size() - 1; bool in_quote = false; while (pos < end) { size_t buf_offset = strcspn(buf + pos, query.data()); pos = pos + buf_offset; auto c = buf[pos]; if (c == '\n' || c == '\r') { if (in_quote) { ++pos; continue; } if (c == '\n') { return {pos, LF}; } if (is_crlf(buf, pos, end)) { return {pos + 1, CRLF}; } return {pos, CR}; } else if (c == quote) { in_quote = !in_quote; } ++pos; } if (pos > end) { return {end, NA}; } return {pos, NA}; } template static std::pair find_next_newline( const T& source, size_t start, const std::string& comment, const bool skip_empty_rows, bool embedded_nl, const char quote, newline_type type = NA) { if (start >= source.size()) { return {source.size() - 1, NA}; } if (embedded_nl) { size_t value; newline_type nl; std::tie(value, nl) = find_next_non_quoted_newline(source, start, quote); // REprintf("%i\n", value); return {value, nl}; } const char* begin = source.data() + start; const char* end = source.data() + source.size(); std::array query; switch (type) { case NA: query = {'\n', '\r', '\0'}; break; case CR: query = {'\r', '\0'}; break; case CRLF: case LF: query = {'\n', '\0'}; break; } bool should_skip; while (begin && begin < end) { size_t offset = strcspn(begin, query.data()); // REprintf("%i\n", offset); begin += offset; break; if (!(begin && begin + 1 < end)) { std::tie(should_skip, std::ignore) = is_blank_or_comment_line(begin + 1, end, comment, skip_empty_rows); if (should_skip) { break; } } } if (!begin) { return {source.size() - 1, NA}; } size_t pos = begin - source.data(); if (begin[0] == '\n') { return {pos, LF}; } if (begin[0] == '\r') { if (is_crlf(source.data(), pos, end - source.data())) { return {pos + 1, CRLF}; } return {pos, CR}; } return {pos, NA}; } template T get_env(const char* name, T default_value) { char* p; p = getenv(name); if (!p || strlen(p) == 0) { return default_value; } std::stringstream ss(p); double out; ss >> out; return out; } inline bool is_space(const char* c) { return *c == ' ' || *c == '\t' || *c == '\0' || *c == '\r'; } inline void trim_whitespace(const char*& begin, const char*& end) { while (begin != end && is_space(begin)) { ++begin; } while (end != begin && is_space(end - 1)) { --end; } } template size_t skip_bom(const T& source) { /* Skip Unicode Byte Order Marks https://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding 00 00 FE FF: UTF-32BE FF FE 00 00: UTF-32LE FE FF: UTF-16BE FF FE: UTF-16LE EF BB BF: UTF-8 */ auto size = source.size(); auto begin = source.data(); switch (begin[0]) { // UTF-32BE case '\x00': if (size >= 4 && begin[1] == '\x00' && begin[2] == '\xFE' && begin[3] == '\xFF') { return 4; } break; // UTF-8 case '\xEF': if (size >= 3 && begin[1] == '\xBB' && begin[2] == '\xBF') { return 3; } break; // UTF-16BE case '\xfe': if (size >= 2 && begin[1] == '\xff') { return 2; } break; case '\xff': if (size >= 2 && begin[1] == '\xfe') { // UTF-32 LE if (size >= 4 && begin[2] == '\x00' && begin[3] == '\x00') { return 4; } else { // UTF-16 LE return 2; } } break; } return 0; } // This skips leading blank lines and comments (if needed) template size_t find_first_line( const T& source, size_t skip, const char* comment, const bool skip_empty_rows, const bool embedded_nl, const char quote) { auto begin = skip_bom(source); /* Skip skip parameters, comments and blank lines */ bool should_skip, is_comment; std::tie(should_skip, is_comment) = is_blank_or_comment_line( source.data() + begin, source.data() + source.size(), comment, skip_empty_rows); while (begin < source.size() - 1 && (should_skip || skip > 0)) { std::tie(begin, std::ignore) = find_next_newline( source, begin, "", /* skip_empty_rows */ false, embedded_nl, is_comment ? '\0' : quote); /* don't deal with quotes in comment lines*/ ++begin; skip = skip > 0 ? skip - 1 : skip; std::tie(should_skip, is_comment) = is_blank_or_comment_line( source.data() + begin, source.data() + source.size(), comment, skip_empty_rows); } return begin; } inline bool matches(const char* start, const char* end, const std::string& needle) { if (end <= start || needle.empty() || static_cast(end - start) < needle.size()) { return false; } bool res = strncmp(start, needle.data(), needle.size()) == 0; return res; } inline bool has_expected_line_ending(newline_type nl, const char value) { if (nl == CR && value == '\r') { return true; } return value == '\n'; } } // namespace vroom vroom/src/vroom_write.cc0000644000176200001440000003301414533531371015074 0ustar liggesusers#include "grisu3.h" #include #include #include #include #include #include #include #include #include "RProgress.h" #include "connection.h" #include "r_utils.h" #include "unicode_fopen.h" typedef enum { quote_needed = 1, quote_all = 2, escape_double = 4, escape_backslash = 8, bom = 16 } vroom_write_opt_t; size_t get_buffer_size( const cpp11::list& input, const std::vector& types, size_t start, size_t end) { // First need to determine how big the buffer(s) should be // - For characters we need the total nchar() + 2 (for quotes if needed) // (they are converted to UTF-8 in R) // - For factors we need max(nchar(levels)) (but currently we just convert to // character in R) // - For decimal numbers we need 24 // source: https://stackoverflow.com/a/52045523/2055486 // - For 32 bit integers we need 11 (10 for digits plus the sign) // - For logical we need 5 (FALSE) // // - Currently we convert dates, times and datetimes to character before // output. If we wanted to do it in C it would be // - For dates we need 10 (2019-04-12) // - For times we need 8 (01:00:00) // - For datetimes we need 20 (2019-04-12T20:46:31Z) size_t buf_size = 0; size_t num_rows = end - start; for (int i = 0; i < input.size(); ++i) { switch (types[i]) { case STRSXP: { for (size_t j = start; j < end; ++j) { auto sz = Rf_xlength(STRING_ELT(input[i], j)); buf_size += sz + 2; } break; } case LGLSXP: buf_size += 5 * num_rows; break; case REALSXP: buf_size += 24 * num_rows; break; case INTSXP: buf_size += 11 * num_rows; break; } } // Add size of delimiters + newline buf_size += input.size() * num_rows; return buf_size; } bool needs_quote(const char* str, const char delim, const char* na_str) { for (const char* cur = str; *cur != '\0'; ++cur) { if (*cur == '\n' || *cur == '\r' || *cur == '"' || *cur == delim) { return true; } } return false; } // adapted from https://stackoverflow.com/a/28110728/2055486 template void append_literal(std::vector& buf, const char (&str)[N]) { std::copy(std::begin(str), std::end(str) - 1, std::back_inserter(buf)); } inline bool is_utf8(cetype_t ce) { switch (ce) { case CE_ANY: case CE_BYTES: case CE_UTF8: return true; default: return false; } } void str_to_buf( SEXP str, std::vector& buf, const char delim, const char* na_str, size_t na_len, size_t options) { if (str == NA_STRING) { std::copy(na_str, na_str + na_len, std::back_inserter(buf)); return; } const char* str_p; size_t len; if (is_utf8(Rf_getCharCE(str))) { str_p = CHAR(str); len = Rf_xlength(str); } else { str_p = Rf_translateCharUTF8(str); len = strlen(str_p); } bool should_quote = options & quote_all || (options & quote_needed && needs_quote(str_p, delim, na_str)); if (should_quote) { buf.push_back('"'); } auto end = str_p + len; bool should_escape = options & (escape_double | escape_backslash); auto escape = options & escape_double ? '"' : options & escape_backslash ? '\\' : '\0'; buf.reserve(buf.size() + len); while (str_p < end) { if (should_escape && *str_p == '"') { buf.push_back(escape); } buf.push_back(*str_p++); } if (should_quote) { buf.push_back('"'); } return; } std::vector fill_buf( const cpp11::list& input, const char delim, const std::string& eol, const char* na_str, size_t options, const std::vector& types, const std::vector& ptrs, size_t begin, size_t end) { auto buf = std::vector(); auto na_len = strlen(na_str); for (size_t row = begin; row < end; ++row) { for (int col = 0; col < input.size(); ++col) { switch (types[col]) { case STRSXP: { auto str = STRING_ELT(input[col], row); str_to_buf(str, buf, delim, na_str, na_len, options); break; } case LGLSXP: { int value = static_cast(ptrs[col])[row]; switch (value) { case TRUE: append_literal(buf, "TRUE"); break; case FALSE: append_literal(buf, "FALSE"); break; default: std::copy(na_str, na_str + na_len, std::back_inserter(buf)); break; } break; } case REALSXP: { auto value = static_cast(ptrs[col])[row]; if (!R_FINITE(value)) { if (ISNA(value)) { std::copy(na_str, na_str + na_len, std::back_inserter(buf)); } else if (ISNAN(value)) { std::copy(na_str, na_str + na_len, std::back_inserter(buf)); } else if (value > 0) { append_literal(buf, "Inf"); } else { append_literal(buf, "-Inf"); } } else { char temp_buf[33]; int len = dtoa_grisu3(static_cast(ptrs[col])[row], temp_buf); std::copy(temp_buf, temp_buf + len, std::back_inserter(buf)); } break; } case INTSXP: { auto value = static_cast(ptrs[col])[row]; if (value == NA_INTEGER) { std::copy(na_str, na_str + na_len, std::back_inserter(buf)); } else { // TODO: use something like https://github.com/jeaiii/itoa for // faster integer writing char temp_buf[12]; auto len = snprintf(temp_buf, sizeof(temp_buf), "%i", value); std::copy(temp_buf, temp_buf + len, std::back_inserter(buf)); } break; } } if (delim != '\0') { buf.push_back(delim); } } if (delim != '\0') { buf.pop_back(); } for (auto c : eol) { buf.push_back(c); } } return buf; } template void write_buf(const std::vector& buf, T& out) {} template <> void write_buf(const std::vector& buf, std::FILE*& out) { std::fwrite(buf.data(), sizeof buf[0], buf.size(), out); } template <> void write_buf(const std::vector& buf, std::vector& data) { std::copy(buf.begin(), buf.end(), std::back_inserter(data)); } template <> void write_buf(const std::vector& buf, SEXP& con) { R_WriteConnection(con, (void*)buf.data(), sizeof buf[0] * buf.size()); } #ifdef VROOM_USE_CONNECTIONS_API void write_buf_con( const std::vector& buf, Rconnection con, bool is_stdout) { if (is_stdout) { std::string out; std::copy(buf.begin(), buf.end(), std::back_inserter(out)); Rprintf("%.*s", buf.size(), out.c_str()); } else { R_WriteConnection(con, (void*)buf.data(), sizeof buf[0] * buf.size()); } } #else void write_buf_con(const std::vector& buf, SEXP con, bool is_stdout) { if (is_stdout) { std::string out; std::copy(buf.begin(), buf.end(), std::back_inserter(out)); Rprintf("%.*s", (int) buf.size(), out.c_str()); } else { write_buf(buf, con); } } #endif std::vector get_types(const cpp11::list& input) { std::vector out; for (auto col : input) { out.push_back(TYPEOF(col)); } return out; } std::vector get_ptrs(const cpp11::list& input) { std::vector out; for (auto col : input) { switch (TYPEOF(col)) { case REALSXP: out.push_back(REAL(col)); break; case INTSXP: out.push_back(INTEGER(col)); break; case LGLSXP: out.push_back(LOGICAL(col)); break; default: out.push_back(nullptr); } } return out; } std::vector get_header( const cpp11::list& input, const char delim, const std::string& eol, size_t options) { cpp11::strings names(input.attr("names")); std::vector out; for (R_xlen_t i = 0; i < names.size(); ++i) { auto str = STRING_ELT(names, i); str_to_buf(str, out, delim, "", 0, options); if (delim != '\0') { out.push_back(delim); } } if(!out.empty()) { if (delim != '\0') { out.pop_back(); } for (auto c : eol) { out.push_back(c); } } return out; } template void vroom_write_out( const cpp11::list& input, T& out, const char delim, const std::string& eol, const char* na_str, bool col_names, bool append, size_t options, size_t num_threads, bool progress, size_t buf_lines) { size_t begin = 0; size_t num_rows = Rf_xlength(input[0]); std::array>>, 2> futures; futures[0].resize(num_threads); futures[1].resize(num_threads); std::future write_fut; int idx = 0; auto types = get_types(input); auto ptrs = get_ptrs(input); if (!append && options & bom) { std::vector bom{'\xEF', '\xBB', '\xBF'}; write_buf(bom, out); } if (col_names) { auto header = get_header(input, delim, eol, options); write_buf(header, out); } std::unique_ptr pb = nullptr; if (progress) { pb = std::unique_ptr( new RProgress::RProgress(vroom::get_pb_format("write"), 1e12)); } while (begin < num_rows) { size_t t = 0; while (t < num_threads && begin < num_rows) { auto num_lines = std::min(buf_lines, num_rows - begin); auto end = begin + num_lines; futures[idx][t++] = std::async( fill_buf, std::cref(input), delim, eol, na_str, options, types, ptrs, begin, end); begin += num_lines; } if (write_fut.valid()) { auto sz = write_fut.get(); if (progress) { pb->tick(sz); } } write_fut = std::async([&, idx, t] { size_t sz = 0; for (size_t i = 0; i < t; ++i) { auto buf = futures[idx][i].get(); write_buf(buf, out); sz += buf.size(); } return sz; }); idx = (idx + 1) % 2; } // Wait for the last writing to finish if (write_fut.valid()) { write_fut.get(); if (progress) { pb->update(1); } } } [[cpp11::register]] void vroom_write_( const cpp11::list& input, const std::string& filename, const char delim, const std::string& eol, const char* na_str, bool col_names, bool append, size_t options, size_t num_threads, bool progress, size_t buf_lines) { char mode[3] = "wb"; if (append) { strcpy(mode, "ab"); } std::FILE* out = unicode_fopen(filename.c_str(), mode); if (!out) { std::string msg("Cannot open file for writing:\n* "); msg += '\'' + filename + '\''; cpp11::stop(msg.c_str()); } vroom_write_out( input, out, delim, eol, na_str, col_names, append, options, num_threads, progress, buf_lines); // Close the file std::fclose(out); } // TODO: Think about refactoring this so it and vroom_write_ can share some // code [[cpp11::register]] void vroom_write_connection_( const cpp11::list& input, const cpp11::sexp& con, const char delim, const std::string& eol, const char* na_str, bool col_names, size_t options, size_t num_threads, bool progress, size_t buf_lines, bool is_stdout, bool append) { char mode[3] = "wb"; if (append) { strcpy(mode, "ab"); } size_t begin = 0; size_t num_rows = Rf_xlength(input[0]); auto con_ = R_GetConnection(con); bool should_open = !is_open(con); if (should_open) { cpp11::package("base")["open"](con, mode); } bool should_close = should_open; std::array>>, 2> futures; futures[0].resize(num_threads); futures[1].resize(num_threads); std::future write_fut; int idx = 0; auto types = get_types(input); auto ptrs = get_ptrs(input); if (col_names) { auto header = get_header(input, delim, eol, options); write_buf_con(header, con_, is_stdout); } std::unique_ptr pb = nullptr; if (progress) { pb = std::unique_ptr( new RProgress::RProgress(vroom::get_pb_format("write"), 1e12)); } while (begin < num_rows) { size_t t = 0; while (t < num_threads && begin < num_rows) { auto num_lines = std::min(buf_lines, num_rows - begin); auto end = begin + num_lines; futures[idx][t++] = std::async( fill_buf, std::cref(input), delim, eol, na_str, options, types, ptrs, begin, end); begin += num_lines; } for (size_t i = 0; i < t; ++i) { auto buf = futures[idx][i].get(); write_buf_con(buf, con_, is_stdout); auto sz = buf.size(); if (progress) { pb->tick(sz); } } idx = (idx + 1) % 2; } if (progress) { pb->update(1); } // Close the connection if (should_close) { cpp11::package("base")["close"](con); } } [[cpp11::register]] cpp11::strings vroom_format_( const cpp11::list& input, const char delim, const std::string& eol, const char* na_str, bool col_names, bool append, size_t options, size_t num_threads, bool progress, size_t buf_lines) { std::vector data; vroom_write_out( input, data, delim, eol, na_str, col_names, append, options, num_threads, progress, buf_lines); cpp11::writable::strings out(1); out[0] = Rf_mkCharLenCE(data.data(), data.size(), CE_UTF8); return out; } vroom/src/DateTime.h0000644000176200001440000000645414132374245014066 0ustar liggesusers#ifndef READR_DATE_TIME_H_ #define READR_DATE_TIME_H_ #include #include #include #include class DateTime { int year_, mon_, day_, hour_, min_, sec_, offset_; double psec_; std::string tz_; public: DateTime( int year, int mon, int day, int hour = 0, int min = 0, int sec = 0, double psec = 0, const std::string& tz = "UTC") : year_(year), mon_(mon), day_(day), hour_(hour), min_(min), sec_(sec), offset_(0), psec_(psec), tz_(tz) {} // Used to add time zone offsets which can only be easily applied once // we've converted into seconds since epoch. void setOffset(int offset) { offset_ = offset; } // Is this a valid date time? bool validDateTime() const { return validDate() && validTime(); } bool validDate() const { // vroom does not allow negative years, date does if (year_ < 0) return false; return (date::year{year_} / mon_ / day_).ok(); } bool validTime() const { if (sec_ < 0 || sec_ > 60) return false; if (min_ < 0 || min_ > 59) return false; if (hour_ < 0 || hour_ > 23) return false; return true; } bool validDuration() const { if (sec_ < -59 || sec_ > 59) return false; if (min_ < -59 || min_ > 59) return false; return true; } double datetime() const { return (tz_ == "UTC") ? utctime() : localtime(); } int date() const { return utcdate(); } double time() const { return psec_ + sec_ + (min_ * 60.) + (hour_ * 3600.); } private: // Number of number of seconds since 1970-01-01T00:00:00Z. // Compared to usual implementations this returns a double, and supports // a wider range of dates. Invalid dates have undefined behaviour. double utctime() const { return utcdate() * 86400.0 + time() + offset_; } // Find number of days since 1970-01-01. // Invalid dates have undefined behaviour. int utcdate() const { if (!validDate()) return NA_REAL; const date::year_month_day ymd{date::year(year_) / mon_ / day_}; const date::sys_days st{ymd}; return st.time_since_epoch().count(); } double localtime() const { if (!validDateTime()) return NA_REAL; const date::time_zone* p_time_zone; if (!tzdb::locate_zone(tz_, p_time_zone)) { throw std::runtime_error( "'" + tz_ + "' not found in the time zone database."); } const date::local_seconds lt = std::chrono::seconds{sec_} + std::chrono::minutes{min_} + std::chrono::hours{hour_} + date::local_days{date::year{year_} / mon_ / day_}; date::local_info info; if (!tzdb::get_local_info(lt, p_time_zone, info)) { throw std::runtime_error( "Can't lookup local time info for the supplied time zone."); } switch (info.result) { case date::local_info::unique: return (lt.time_since_epoch() - info.first.offset).count() + psec_ + offset_; case date::local_info::ambiguous: // Choose `earliest` of the two ambiguous times return (lt.time_since_epoch() - info.first.offset).count() + psec_ + offset_; case date::local_info::nonexistent: return NA_REAL; } throw std::runtime_error("should never happen"); } }; #endif vroom/src/grisu3.h0000644000176200001440000000334314132374245013600 0ustar liggesusers#ifndef FASTREAD_GRISU3_H_ #define FASTREAD_GRISU3_H_ /* Copyright Jukka JylΓ€nki Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* This file is part of an implementation of the "grisu3" double to string conversion algorithm described in the research paper "Printing Floating-Point Numbers Quickly And Accurately with Integers" by Florian Loitsch, available at http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf */ extern "C" { /// Converts the given double-precision floating point number to a string representation. /** For most inputs, this string representation is the shortest such, which deserialized again, returns the same bit representation of the double. @param v The number to convert. @param dst [out] The double-precision floating point number will be written here as a null-terminated string. The conversion algorithm will write at most 25 bytes to this buffer. (null terminator is included in this count). The dst pointer may not be null. @return the number of characters written to dst, excluding the null terminator (which is always written) is returned here. */ int dtoa_grisu3(double v, char *dst); } #ifdef __cplusplus #include std::string dtoa_grisu3_string(double v); #endif #endif vroom/src/vroom_vec.h0000644000176200001440000001157414132374245014370 0ustar liggesusers#pragma once #include #include #include "LocaleInfo.h" #include "altrep.h" #include "index_collection.h" #include "vroom_errors.h" using namespace vroom; struct vroom_vec_info { std::shared_ptr column; size_t num_threads; std::shared_ptr na; std::shared_ptr locale; std::shared_ptr errors; std::string format; }; namespace vroom { inline bool is_explicit_na(SEXP na, const char* begin, const char* end) { R_xlen_t n = end - begin; for (R_xlen_t i = 0; i < Rf_xlength(na); ++i) { SEXP str = STRING_ELT(na, i); R_xlen_t str_n = Rf_xlength(str); const char* v = CHAR(STRING_ELT(na, i)); if (n == str_n && strncmp(v, begin, n) == 0) { return true; } } return false; } template struct StringAnnotationTypeMap { static const std::string annotation; }; template <> inline double na() { return NA_REAL; } template <> inline int na() { return NA_INTEGER; } template static auto parse_value( const I& itr, const C& col, F f, std::shared_ptr& errors, const char* expected, SEXP na) -> V { auto str = *itr; if (is_explicit_na(na, str.begin(), str.end())) { return vroom::na(); } V out = f(str.begin(), str.end()); if (cpp11::is_na(out)) { errors->add_error( itr.index(), col->get_index(), expected, std::string(str.begin(), str.end() - str.begin()), itr.filename()); } return out; } template static auto parse_value( R_xlen_t i, const C& col, F f, std::shared_ptr& errors, const char* expected, SEXP na) -> V { auto&& str = col->at(i); if (is_explicit_na(na, str.begin(), str.end())) { return vroom::na(); } V out = f(str.begin(), str.end()); if (cpp11::is_na(out)) { auto itr = col->begin() + i; errors->add_error( itr.index(), col->get_index(), expected, std::string(str.begin(), str.end() - str.begin()), itr.filename()); } return out; } } // namespace vroom #ifdef HAS_ALTREP class vroom_vec { public: // finalizer for the external pointer static void Finalize(SEXP ptr) { if (ptr == nullptr || R_ExternalPtrAddr(ptr) == nullptr) { return; } auto info_p = static_cast(R_ExternalPtrAddr(ptr)); delete info_p; info_p = nullptr; R_ClearExternalPtr(ptr); } static inline vroom_vec_info& Info(SEXP x) { return *static_cast(R_ExternalPtrAddr(R_altrep_data1(x))); } // ALTREP methods ------------------- // The length of the object static inline R_xlen_t Length(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return Rf_xlength(data2); } auto& inf = Info(vec); return inf.column->size(); } static inline string Get(SEXP vec, R_xlen_t i) { auto& inf = Info(vec); return inf.column->at(i); } // ALTVec methods ------------------- static const void* Dataptr_or_null(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 == R_NilValue) return nullptr; return STDVEC_DATAPTR(data2); } static std::shared_ptr> get_subset_index(SEXP indx, R_xlen_t x_len) { auto idx = std::make_shared>(); R_xlen_t n = Rf_xlength(indx); idx->reserve(n); int i_val; double d_val; for (R_xlen_t i = 0; i < n; ++i) { switch (TYPEOF(indx)) { case INTSXP: i_val = INTEGER_ELT(indx, i); if (i_val == NA_INTEGER || i_val > x_len) { return nullptr; } idx->push_back(i_val - 1); break; case REALSXP: d_val = REAL_ELT(indx, i); if (ISNA(d_val) || d_val > x_len) { return nullptr; } idx->push_back(d_val - 1); break; default: Rf_error("Invalid index"); } } return idx; } template static SEXP Extract_subset(SEXP x, SEXP indx, SEXP) { SEXP data2 = R_altrep_data2(x); // If the vector is already materialized, just fall back to the default // implementation if (data2 != R_NilValue) { return nullptr; } // If there are no indices to subset fall back to default implementation. if (Rf_xlength(indx) == 0) { return nullptr; } vroom_vec_info* info; // This block is here to avoid a false positive from rchck { auto& inf = Info(x); auto idx = get_subset_index(indx, Rf_xlength(x)); if (idx == nullptr) { return nullptr; } info = new vroom_vec_info{ inf.column->subset(idx), inf.num_threads, inf.na, inf.locale, inf.errors, inf.format}; } return T::Make(info); } }; #endif vroom/src/grisu3.c0000644000176200001440000003373614422654617013612 0ustar liggesusers/* Copyright Jukka JylΓ€nki Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Modifcations to dtoa_grisu3() referenced mikkelfj: are under the following * Copyright (c) 2016 Mikkel F. JΓΈrgensen, dvide.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. http://www.apache.org/licenses/LICENSE-2.0 */ /* This file is part of an implementation of the "grisu3" double to string conversion algorithm described in the research paper "Printing Floating-Point Numbers Quickly And Accurately with Integers" by Florian Loitsch, available at http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf */ #include // uint64_t etc. #include // assert #include // ceil #include // snprintf #include #ifdef _MSC_VER #pragma warning(disable : 4204) // nonstandard extension used : non-constant aggregate initializer #endif #define D64_SIGN 0x8000000000000000ULL #define D64_EXP_MASK 0x7FF0000000000000ULL #define D64_FRACT_MASK 0x000FFFFFFFFFFFFFULL #define D64_IMPLICIT_ONE 0x0010000000000000ULL #define D64_EXP_POS 52 #define D64_EXP_BIAS 1075 #define DIYFP_FRACT_SIZE 64 #define D_1_LOG2_10 0.30102999566398114 // 1 / lg(10) #define MIN_TARGET_EXP -60 #define MASK32 0xFFFFFFFFULL #define CAST_U64(d) (*(uint64_t*)&d) #define MIN(x,y) ((x) <= (y) ? (x) : (y)) #define MAX(x,y) ((x) >= (y) ? (x) : (y)) #define MIN_CACHED_EXP -348 #define CACHED_EXP_STEP 8 typedef struct diy_fp { uint64_t f; int e; } diy_fp; typedef struct power { uint64_t fract; int16_t b_exp, d_exp; } power; static const power pow_cache[] = { { 0xfa8fd5a0081c0288ULL, -1220, -348 }, { 0xbaaee17fa23ebf76ULL, -1193, -340 }, { 0x8b16fb203055ac76ULL, -1166, -332 }, { 0xcf42894a5dce35eaULL, -1140, -324 }, { 0x9a6bb0aa55653b2dULL, -1113, -316 }, { 0xe61acf033d1a45dfULL, -1087, -308 }, { 0xab70fe17c79ac6caULL, -1060, -300 }, { 0xff77b1fcbebcdc4fULL, -1034, -292 }, { 0xbe5691ef416bd60cULL, -1007, -284 }, { 0x8dd01fad907ffc3cULL, -980, -276 }, { 0xd3515c2831559a83ULL, -954, -268 }, { 0x9d71ac8fada6c9b5ULL, -927, -260 }, { 0xea9c227723ee8bcbULL, -901, -252 }, { 0xaecc49914078536dULL, -874, -244 }, { 0x823c12795db6ce57ULL, -847, -236 }, { 0xc21094364dfb5637ULL, -821, -228 }, { 0x9096ea6f3848984fULL, -794, -220 }, { 0xd77485cb25823ac7ULL, -768, -212 }, { 0xa086cfcd97bf97f4ULL, -741, -204 }, { 0xef340a98172aace5ULL, -715, -196 }, { 0xb23867fb2a35b28eULL, -688, -188 }, { 0x84c8d4dfd2c63f3bULL, -661, -180 }, { 0xc5dd44271ad3cdbaULL, -635, -172 }, { 0x936b9fcebb25c996ULL, -608, -164 }, { 0xdbac6c247d62a584ULL, -582, -156 }, { 0xa3ab66580d5fdaf6ULL, -555, -148 }, { 0xf3e2f893dec3f126ULL, -529, -140 }, { 0xb5b5ada8aaff80b8ULL, -502, -132 }, { 0x87625f056c7c4a8bULL, -475, -124 }, { 0xc9bcff6034c13053ULL, -449, -116 }, { 0x964e858c91ba2655ULL, -422, -108 }, { 0xdff9772470297ebdULL, -396, -100 }, { 0xa6dfbd9fb8e5b88fULL, -369, -92 }, { 0xf8a95fcf88747d94ULL, -343, -84 }, { 0xb94470938fa89bcfULL, -316, -76 }, { 0x8a08f0f8bf0f156bULL, -289, -68 }, { 0xcdb02555653131b6ULL, -263, -60 }, { 0x993fe2c6d07b7facULL, -236, -52 }, { 0xe45c10c42a2b3b06ULL, -210, -44 }, { 0xaa242499697392d3ULL, -183, -36 }, { 0xfd87b5f28300ca0eULL, -157, -28 }, { 0xbce5086492111aebULL, -130, -20 }, { 0x8cbccc096f5088ccULL, -103, -12 }, { 0xd1b71758e219652cULL, -77, -4 }, { 0x9c40000000000000ULL, -50, 4 }, { 0xe8d4a51000000000ULL, -24, 12 }, { 0xad78ebc5ac620000ULL, 3, 20 }, { 0x813f3978f8940984ULL, 30, 28 }, { 0xc097ce7bc90715b3ULL, 56, 36 }, { 0x8f7e32ce7bea5c70ULL, 83, 44 }, { 0xd5d238a4abe98068ULL, 109, 52 }, { 0x9f4f2726179a2245ULL, 136, 60 }, { 0xed63a231d4c4fb27ULL, 162, 68 }, { 0xb0de65388cc8ada8ULL, 189, 76 }, { 0x83c7088e1aab65dbULL, 216, 84 }, { 0xc45d1df942711d9aULL, 242, 92 }, { 0x924d692ca61be758ULL, 269, 100 }, { 0xda01ee641a708deaULL, 295, 108 }, { 0xa26da3999aef774aULL, 322, 116 }, { 0xf209787bb47d6b85ULL, 348, 124 }, { 0xb454e4a179dd1877ULL, 375, 132 }, { 0x865b86925b9bc5c2ULL, 402, 140 }, { 0xc83553c5c8965d3dULL, 428, 148 }, { 0x952ab45cfa97a0b3ULL, 455, 156 }, { 0xde469fbd99a05fe3ULL, 481, 164 }, { 0xa59bc234db398c25ULL, 508, 172 }, { 0xf6c69a72a3989f5cULL, 534, 180 }, { 0xb7dcbf5354e9beceULL, 561, 188 }, { 0x88fcf317f22241e2ULL, 588, 196 }, { 0xcc20ce9bd35c78a5ULL, 614, 204 }, { 0x98165af37b2153dfULL, 641, 212 }, { 0xe2a0b5dc971f303aULL, 667, 220 }, { 0xa8d9d1535ce3b396ULL, 694, 228 }, { 0xfb9b7cd9a4a7443cULL, 720, 236 }, { 0xbb764c4ca7a44410ULL, 747, 244 }, { 0x8bab8eefb6409c1aULL, 774, 252 }, { 0xd01fef10a657842cULL, 800, 260 }, { 0x9b10a4e5e9913129ULL, 827, 268 }, { 0xe7109bfba19c0c9dULL, 853, 276 }, { 0xac2820d9623bf429ULL, 880, 284 }, { 0x80444b5e7aa7cf85ULL, 907, 292 }, { 0xbf21e44003acdd2dULL, 933, 300 }, { 0x8e679c2f5e44ff8fULL, 960, 308 }, { 0xd433179d9c8cb841ULL, 986, 316 }, { 0x9e19db92b4e31ba9ULL, 1013, 324 }, { 0xeb96bf6ebadf77d9ULL, 1039, 332 }, { 0xaf87023b9bf0ee6bULL, 1066, 340 } }; static int cached_pow(int exp, diy_fp *p) { int k = (int)ceil((exp+DIYFP_FRACT_SIZE-1) * D_1_LOG2_10); int i = (k-MIN_CACHED_EXP-1) / CACHED_EXP_STEP + 1; p->f = pow_cache[i].fract; p->e = pow_cache[i].b_exp; return pow_cache[i].d_exp; } static diy_fp minus(diy_fp x, diy_fp y) { diy_fp d; d.f = x.f - y.f; d.e = x.e; assert(x.e == y.e && x.f >= y.f); return d; } static diy_fp multiply(diy_fp x, diy_fp y) { uint64_t a, b, c, d, ac, bc, ad, bd, tmp; diy_fp r; a = x.f >> 32; b = x.f & MASK32; c = y.f >> 32; d = y.f & MASK32; ac = a*c; bc = b*c; ad = a*d; bd = b*d; tmp = (bd >> 32) + (ad & MASK32) + (bc & MASK32); tmp += 1U << 31; // round r.f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); r.e = x.e + y.e + 64; return r; } static diy_fp normalize_diy_fp(diy_fp n) { assert(n.f != 0); while(!(n.f & 0xFFC0000000000000ULL)) { n.f <<= 10; n.e -= 10; } while(!(n.f & D64_SIGN)) { n.f <<= 1; --n.e; } return n; } static diy_fp double2diy_fp(double d) { diy_fp fp; uint64_t u64 = CAST_U64(d); if (!(u64 & D64_EXP_MASK)) { fp.f = u64 & D64_FRACT_MASK; fp.e = 1 - D64_EXP_BIAS; } else { fp.f = (u64 & D64_FRACT_MASK) + D64_IMPLICIT_ONE; fp.e = (int)((u64 & D64_EXP_MASK) >> D64_EXP_POS) - D64_EXP_BIAS; } return fp; } // pow10_cache[i] = 10^(i-1) static const unsigned int pow10_cache[] = { 0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; static int largest_pow10(uint32_t n, int n_bits, uint32_t *power) { int guess = ((n_bits + 1) * 1233 >> 12) + 1/*skip first entry*/; if (n < pow10_cache[guess]) --guess; // We don't have any guarantees that 2^n_bits <= n. *power = pow10_cache[guess]; return guess; } static int round_weed(char *buffer, int len, uint64_t wp_W, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t ulp) { uint64_t wp_Wup = wp_W - ulp; uint64_t wp_Wdown = wp_W + ulp; while(rest < wp_Wup && delta - rest >= ten_kappa && (rest + ten_kappa < wp_Wup || wp_Wup - rest >= rest + ten_kappa - wp_Wup)) { --buffer[len-1]; rest += ten_kappa; } if (rest < wp_Wdown && delta - rest >= ten_kappa && (rest + ten_kappa < wp_Wdown || wp_Wdown - rest > rest + ten_kappa - wp_Wdown)) return 0; return 2*ulp <= rest && rest <= delta - 4*ulp; } static int digit_gen(diy_fp low, diy_fp w, diy_fp high, char *buffer, int *length, int *kappa) { uint64_t unit = 1; diy_fp too_low = { low.f - unit, low.e }; diy_fp too_high = { high.f + unit, high.e }; diy_fp unsafe_interval = minus(too_high, too_low); diy_fp one = { 1ULL << -w.e, w.e }; uint32_t p1 = (uint32_t)(too_high.f >> -one.e); uint64_t p2 = too_high.f & (one.f - 1); uint32_t div; *kappa = largest_pow10(p1, DIYFP_FRACT_SIZE + one.e, &div); *length = 0; while(*kappa > 0) { uint64_t rest; int digit = p1 / div; buffer[*length] = (char)('0' + digit); ++*length; p1 %= div; --*kappa; rest = ((uint64_t)p1 << -one.e) + p2; if (rest < unsafe_interval.f) return round_weed(buffer, *length, minus(too_high, w).f, unsafe_interval.f, rest, (uint64_t)div << -one.e, unit); div /= 10; } for(;;) { int digit; p2 *= 10; unit *= 10; unsafe_interval.f *= 10; // Integer division by one. digit = (int)(p2 >> -one.e); buffer[*length] = (char)('0' + digit); ++*length; p2 &= one.f - 1; // Modulo by one. --*kappa; if (p2 < unsafe_interval.f) return round_weed(buffer, *length, minus(too_high, w).f * unit, unsafe_interval.f, p2, one.f, unit); } } static int grisu3(double v, char *buffer, int *length, int *d_exp) { int mk, kappa, success; diy_fp dfp = double2diy_fp(v); diy_fp w = normalize_diy_fp(dfp); // normalize boundaries diy_fp t = { (dfp.f << 1) + 1, dfp.e - 1 }; diy_fp b_plus = normalize_diy_fp(t); diy_fp b_minus; diy_fp c_mk; // Cached power of ten: 10^-k uint64_t u64 = CAST_U64(v); assert(v > 0 && v <= 1.7976931348623157e308); // Grisu only handles strictly positive finite numbers. if (!(u64 & D64_FRACT_MASK) && (u64 & D64_EXP_MASK) != 0) { b_minus.f = (dfp.f << 2) - 1; b_minus.e = dfp.e - 2;} // lower boundary is closer? else { b_minus.f = (dfp.f << 1) - 1; b_minus.e = dfp.e - 1; } b_minus.f = b_minus.f << (b_minus.e - b_plus.e); b_minus.e = b_plus.e; mk = cached_pow(MIN_TARGET_EXP - DIYFP_FRACT_SIZE - w.e, &c_mk); w = multiply(w, c_mk); b_minus = multiply(b_minus, c_mk); b_plus = multiply(b_plus, c_mk); success = digit_gen(b_minus, w, b_plus, buffer, length, &kappa); *d_exp = kappa - mk; return success; } static int i_to_str(int val, char *str) { int len, i; char *s; char *begin = str; if (val < 0) { *str++ = '-'; val = -val; } s = str; for(;;) { int ni = val / 10; int digit = val - ni*10; *s++ = (char)('0' + digit); if (ni == 0) break; val = ni; } *s = '\0'; len = (int)(s - str); for(i = 0; i < len/2; ++i) { char ch = str[i]; str[i] = str[len-1-i]; str[len-1-i] = ch; } return (int)(s - begin); } int dtoa_grisu3(double v, char *dst) { int d_exp, len, success, decimals, i; uint64_t u64 = CAST_U64(v); char *s2 = dst; assert(dst); // Prehandle NaNs // Why size = 22? // 5 for "NaN()" // 16 for two hexadecimal intgers at width 8 // 1 for null terminator if ((u64 << 1) > 0xFFE0000000000000ULL) return snprintf(dst, 22, "NaN(%08X%08X)", (uint32_t)(u64 >> 32), (uint32_t)u64); // Prehandle negative values. if ((u64 & D64_SIGN) != 0) { *s2++ = '-'; v = -v; u64 ^= D64_SIGN; } // Prehandle zero. if (!u64) { *s2++ = '0'; *s2 = '\0'; return (int)(s2 - dst); } // Prehandle infinity. if (u64 == D64_EXP_MASK) { *s2++ = 'i'; *s2++ = 'n'; *s2++ = 'f'; *s2 = '\0'; return (int)(s2 - dst); } success = grisu3(v, s2, &len, &d_exp); // If grisu3 was not able to convert the number to a string, then use old sprintf (suboptimal). // (Putative) rationale for size = 30: // 17 digits after decimal at most // 1 for the `.` // 1 for a possible `-`, if the number is negative // 5 for a possible e+308 if it chooses exponential form and uses the largest // exponent possible // 1 for null terminator // -- // 25 total so far // 5 left for displaying the value before the decimal (in the worst case, // which I'm not even sure is possible) // More context: when vroom calls dtoa_grisu3(), dst points to a buffer of // size 33 (at the time of writing), and that's where s2 starts out FWIW. if (!success) return snprintf(s2, 30, "%.17g", v) + (int)(s2 - dst); // handle whole numbers as integers if they are < 10^15 if (d_exp >= 0 && d_exp <= MAX(2, 15 - len)) { while(d_exp-- > 0) s2[len++] = '0'; s2[len] = '\0'; return (int)(s2+len-dst); } // We now have an integer string of form "151324135" and a base-10 exponent for that number. // Next, decide the best presentation for that string by whether to use a decimal point, or the scientific exponent notation 'e'. // We don't pick the absolute shortest representation, but pick a balance between readability and shortness, e.g. // 1.545056189557677e-308 could be represented in a shorter form // 1545056189557677e-323 but that would be somewhat unreadable. decimals = MIN(-d_exp, MAX(1, len-1)); // mikkelfj: // fix zero prefix .1 => 0.1, important for JSON export. // prefer unscientific notation at same length: // -1.2345e-4 over -1.00012345, // -1.0012345 over -1.2345e-3 if (d_exp < 0 && (len + d_exp) > -3 && len <= -d_exp) { // mikkelfj: fix zero prefix .1 => 0.1, and short exponents 1.3e-2 => 0.013. memmove(s2 + 2 - d_exp - len, s2, len); s2[0] = '0'; s2[1] = '.'; for (i = 2; i < 2-d_exp-len; ++i) s2[i] = '0'; len += i; } else if (d_exp < 0 && len > 1) // Add decimal point? { for(i = 0; i < decimals; ++i) s2[len-i] = s2[len-i-1]; s2[len++ - decimals] = '.'; d_exp += decimals; // Need scientific notation as well? if (d_exp != 0) { s2[len++] = 'e'; len += i_to_str(d_exp, s2+len); } }// Add scientific notation? else if (d_exp < 0 || d_exp > 2) { s2[len++] = 'e'; len += i_to_str(d_exp, s2+len); } // Add zeroes instead of scientific notation? /*s2[len] = '\0'; // grisu3 doesn't null terminate, so ensure termination.*/ return (int)(s2+len-dst); } vroom/src/Makevars0000644000176200001440000000012414531447467013713 0ustar liggesusersPKG_CPPFLAGS=-Imio/include -DWIN32_LEAN_AND_MEAN -Ispdlog/include -DFMT_HEADER_ONLY vroom/src/vroom_big_int.h0000644000176200001440000000566314533531371015230 0ustar liggesusers#pragma once #include #include "altrep.h" constexpr long long NA_INTEGER64 = 0x8000000000000000LL; #include "r_utils.h" #include "vroom.h" namespace cpp11 { inline bool is_na(long long x) { return x == NA_INTEGER64; } } // namespace cpp11 namespace vroom { template <> inline long long na() { return NA_INTEGER64; } } // namespace vroom #include "vroom_vec.h" long long vroom_strtoll(const char* begin, const char* end); cpp11::doubles read_big_int(vroom_vec_info* info); union vroom_big_int_t { long long ll; double dbl; }; #ifdef HAS_ALTREP class vroom_big_int : public vroom_vec { public: static R_altrep_class_t class_t; static SEXP Make(vroom_vec_info* info) { SEXP out = PROTECT(R_MakeExternalPtr(info, R_NilValue, R_NilValue)); R_RegisterCFinalizerEx(out, vroom_vec::Finalize, FALSE); cpp11::sexp res = R_new_altrep(class_t, out, R_NilValue); res.attr("class") = {"integer64"}; UNPROTECT(1); MARK_NOT_MUTABLE(res); /* force duplicate on modify */ return res; } // ALTREP methods ------------------- // What gets printed when .Internal(inspect()) is used static Rboolean Inspect(SEXP x, int, int, int, void (*)(SEXP, int, int, int)) { Rprintf( "vroom_big_int (len=%" R_PRIdXLEN_T ", materialized=%s)\n", Length(x), R_altrep_data2(x) != R_NilValue ? "T" : "F"); return TRUE; } // ALTREAL methods ----------------- static SEXP Materialize(SEXP vec) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return data2; } auto out = read_big_int(&Info(vec)); R_set_altrep_data2(vec, out); // Once we have materialized we no longer need the info Finalize(R_altrep_data1(vec)); return out; } // the element at the index `i` static double real_Elt(SEXP vec, R_xlen_t i) { SEXP data2 = R_altrep_data2(vec); if (data2 != R_NilValue) { return REAL(data2)[i]; } auto info = vroom_vec::Info(vec); vroom_big_int_t res; res.ll = parse_value( i, info.column, vroom_strtoll, info.errors, "a big integer", *info.na); info.errors->warn_for_errors(); return res.dbl; } static void* Dataptr(SEXP vec, Rboolean) { return STDVEC_DATAPTR(Materialize(vec)); } // -------- initialize the altrep class with the methods above static void Init(DllInfo* dll) { vroom_big_int::class_t = R_make_altreal_class("vroom_big_int", "vroom", dll); // altrep R_set_altrep_Length_method(class_t, Length); R_set_altrep_Inspect_method(class_t, Inspect); // altvec R_set_altvec_Dataptr_method(class_t, Dataptr); R_set_altvec_Dataptr_or_null_method(class_t, Dataptr_or_null); R_set_altvec_Extract_subset_method(class_t, Extract_subset); // altreal R_set_altreal_Elt_method(class_t, real_Elt); } }; #endif // Called the package is loaded [[cpp11::init]] void init_vroom_big_int(DllInfo* dll); vroom/src/delimited_index_connection.cc0000644000176200001440000001540314226327056020072 0ustar liggesusers#include #include "delimited_index_connection.h" #include "connection.h" #include #include // std::async, std::future #include #include "r_utils.h" #ifdef VROOM_LOG #include "spdlog/sinks/basic_file_sink.h" // support for basic file logging #include "spdlog/spdlog.h" #endif #include "unicode_fopen.h" using namespace vroom; delimited_index_connection::delimited_index_connection( SEXP in, const char* delim, const char quote, const bool trim_ws, const bool escape_double, const bool escape_backslash, const bool has_header, const size_t skip, size_t n_max, const char* comment, const bool skip_empty_rows, const std::shared_ptr errors, const size_t chunk_size, const bool progress) { has_header_ = has_header; quote_ = quote; trim_ws_ = trim_ws; escape_double_ = escape_double; escape_backslash_ = escape_backslash; comment_ = comment; skip_ = skip; progress_ = progress; filename_ = cpp11::as_cpp(cpp11::package("vroom")["vroom_tempfile"]()); std::FILE* out = unicode_fopen(filename_.c_str(), "wb"); auto con = R_GetConnection(in); bool should_open = !is_open(in); if (should_open) { cpp11::package("base")["open"](in, "rb"); } /* raw connections are always created as open, but we should close them */ bool should_close = should_open || Rf_inherits(in, "rawConnection"); std::array, 2> buf = { std::vector(chunk_size), std::vector(chunk_size)}; // std::vector(chunk_size)}; // A buf index that alternates between 0,1 auto i = 0; idx_ = std::vector(2); idx_[0].reserve(128); size_t sz = R_ReadConnection(con, buf[i].data(), chunk_size - 1); buf[i].resize(sz + 1); if (sz == 0) { std::fclose(out); if (should_close) { cpp11::package("base")["close"](in); } return; } bool has_quoted_newlines = quote != '\0'; // Parse header size_t start = find_first_line( buf[i], skip_, comment_, skip_empty_rows, has_quoted_newlines, quote); if (delim == nullptr) { delim_ = std::string(1, guess_delim(buf[i], start, 20, sz, quote)); } else { delim_ = delim; } delim_len_ = delim_.length(); size_t first_nl; newline_type nl; std::tie(first_nl, nl) = find_next_newline( buf[i], start, comment, skip_empty_rows, has_quoted_newlines, quote); bool single_line = first_nl == buf[i].size() - 1; if (sz > 1 && !has_expected_line_ending(nl, buf[i][first_nl])) { // This first newline must not have fit in the buffer, throw error // suggesting a larger buffer size. // Try reading again, if size is 0 we are at the end of the file, so should // just go on. size_t next_sz = R_ReadConnection(con, buf[i].data(), chunk_size - 1); if (!(next_sz == 0)) { if (should_close) { cpp11::package("base")["close"](in); } std::stringstream ss; ss << "The size of the connection buffer (" << chunk_size << ") was not large enough\nto fit a complete line:\n * Increase it " "by " "setting `Sys.setenv(\"VROOM_CONNECTION_SIZE\")`"; cpp11::stop("%s", ss.str().c_str()); } } std::unique_ptr pb = nullptr; if (progress_) { pb = std::unique_ptr( new RProgress::RProgress(get_pb_format("connection"), 1e12)); pb->tick(start); } bool n_max_set = n_max != static_cast(-1); n_max = n_max_set ? n_max + has_header_ : n_max; std::unique_ptr empty_pb = nullptr; // Index the first row size_t cols = 0; csv_state state = RECORD_START; size_t lines_read = index_region( buf[i], idx_[0], delim_.c_str(), nl, quote, comment_, skip_empty_rows, state, start, first_nl + 1, 0, n_max, cols, 0, errors, empty_pb, 1, -1); columns_ = idx_[0].size() - 1; SPDLOG_DEBUG( "first_line_columns: {0} first_nl_loc: {1} size: {2}", columns_, first_nl, sz); size_t total_read = 0; std::future parse_fut; std::future write_fut; // We don't actually want any progress bar, so just pass a dummy one. while (sz > 0) { if (parse_fut.valid()) { parse_fut.wait(); } n_max = n_max > lines_read ? n_max - lines_read : 0; if (n_max > 0) { parse_fut = std::async([&, i, sz, first_nl, total_read] { lines_read = index_region( buf[i], idx_[1], delim_.c_str(), nl, quote, comment_, skip_empty_rows, state, first_nl + 1, sz, total_read, n_max, cols, columns_, errors, empty_pb, 1, -1); }); } if (write_fut.valid()) { write_fut.wait(); if (n_max == 0) { break; } } write_fut = std::async( [&, i, sz] { std::fwrite(buf[i].data(), sizeof(char), sz, out); }); if (progress_) { pb->tick(sz); } total_read += sz; i = (i + 1) % 2; sz = R_ReadConnection(con, buf[i].data(), chunk_size - 1); if (sz > 0) { buf[i].resize(sz + 1); buf[i][sz] = '\0'; } first_nl = -1; // SPDLOG_DEBUG("first_nl_loc: {0} size: {1}", first_nl, sz); } if (parse_fut.valid()) { parse_fut.wait(); } if (write_fut.valid()) { write_fut.wait(); } std::fclose(out); if (progress_) { pb->update(1); } /* raw connections are always created as open, but we should close them */ if (should_close) { cpp11::package("base")["close"](in); } std::error_code error; mmap_ = make_mmap_source(filename_.c_str(), error); if (error) { cpp11::stop("%s", error.message().c_str()); } size_t file_size = mmap_.size(); if (!has_expected_line_ending(nl, mmap_[file_size - 1])) { if (columns_ == 0 || single_line) { idx_[0].push_back(file_size); ++columns_; } else { idx_[1].push_back(file_size); } } size_t total_size = std::accumulate( idx_.begin(), idx_.end(), std::size_t{0}, [](size_t sum, const idx_t& v) { sum += v.size() > 0 ? v.size() : 0; return sum; }); rows_ = columns_ > 0 ? total_size / (columns_ + 1) : 0; if (rows_ > 0 && has_header_) { --rows_; } #ifdef VROOM_LOG auto log = spdlog::basic_logger_mt( "basic_logger", "logs/index_connection.idx", true); for (auto& i : idx_) { for (auto& v : i) { SPDLOG_LOGGER_DEBUG(log, "{}", v); } SPDLOG_LOGGER_DEBUG(log, "end of idx {0:x}", (size_t)&i); } spdlog::drop("basic_logger"); #endif SPDLOG_DEBUG("columns: {0} rows: {1}", columns_, rows_); } vroom/src/vroom_lgl.h0000644000176200001440000000357514132374245014373 0ustar liggesusers#pragma once #include #include "parallel.h" #include "vroom_vec.h" const static char* const true_values[] = { "T", "t", "True", "TRUE", "true", (char*)NULL}; const static char* const false_values[] = { "F", "f", "False", "FALSE", "false", (char*)NULL}; inline bool isTrue(const char* start, const char* end) { size_t len = end - start; for (int i = 0; true_values[i]; i++) { size_t true_len = strlen(true_values[i]); if (true_len == len && strncmp(start, true_values[i], len) == 0) { return true; } } return false; } inline bool isFalse(const char* start, const char* end) { size_t len = end - start; for (int i = 0; false_values[i]; i++) { if (strlen(false_values[i]) == len && strncmp(start, false_values[i], len) == 0) { return true; } } return false; } inline int parse_logical(const char* start, const char* end, bool strict = true) { auto len = end - start; if (isTrue(start, end) || (!strict && len == 1 && *start == '1')) { return TRUE; } if (isFalse(start, end) || (!strict && len == 1 && *start == '0')) { return FALSE; } return NA_LOGICAL; } inline cpp11::logicals read_lgl(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::logicals out(n); parallel_for( n, [&](size_t start, size_t end, size_t) { R_xlen_t i = start; auto col = info->column->slice(start, end); for (auto b = col->begin(), e = col->end(); b != e; ++b) { out[i++] = parse_value( b, col, [&](const char* begin, const char* end) -> int { return parse_logical(begin, end, false); }, info->errors, "1/0/T/F/TRUE/FALSE", *info->na); } }, info->num_threads); info->errors->warn_for_errors(); return out; } vroom/src/fixed_width_index_connection.cc0000644000176200001440000001043514151705507020426 0ustar liggesusers#include #include #include "connection.h" #include "fixed_width_index_connection.h" #include "r_utils.h" #include "unicode_fopen.h" #include #include #include // std::async, std::future #include #ifdef VROOM_LOG #include "spdlog/sinks/basic_file_sink.h" // support for basic file logging #include "spdlog/spdlog.h" #endif using namespace vroom; fixed_width_index_connection::fixed_width_index_connection( SEXP in, std::vector col_starts, std::vector col_ends, bool trim_ws, const size_t skip, const char* comment, const bool skip_empty_rows, const size_t n_max, const bool progress, const size_t chunk_size) { col_starts_ = std::move(col_starts); col_ends_ = std::move(col_ends); trim_ws_ = trim_ws; filename_ = cpp11::as_cpp(cpp11::package("vroom")["vroom_tempfile"]()); std::FILE* out = unicode_fopen(filename_.c_str(), "wb"); auto con = R_GetConnection(in); bool should_open = !is_open(in); if (should_open) { cpp11::package("base")["open"](in, "rb"); } std::array, 2> buf = { std::vector(chunk_size), std::vector(chunk_size)}; // A buf index that alternates between 0,1 auto i = 0; newlines_.reserve(128); size_t sz = R_ReadConnection(con, buf[i].data(), chunk_size - 1); buf[i][sz] = '\0'; // Parse header size_t start = find_first_line( buf[i], skip, comment, skip_empty_rows, /* embedded_nl */ false, /* quote */ '\0'); // Check for windows newlines size_t first_nl; newline_type nl_type; std::tie(first_nl, nl_type) = find_next_newline( buf[i], start, comment, skip_empty_rows, false, /* quote */ '\0'); bool n_max_set = n_max != static_cast(-1); std::unique_ptr pb = nullptr; if (progress) { pb = std::unique_ptr( new RProgress::RProgress(get_pb_format("connection"), 1e12)); pb->tick(start); } size_t total_read = 0; std::future parse_fut; std::future write_fut; size_t lines_read = 0; size_t lines_remaining = n_max; std::unique_ptr empty_pb = nullptr; if (n_max > 0) { newlines_.push_back(start - 1); } while (sz > 0) { if (parse_fut.valid()) { parse_fut.wait(); } if (lines_read >= lines_remaining) { break; } lines_remaining -= lines_read; parse_fut = std::async([&, i, start, total_read, sz] { lines_read = index_region( buf[i], newlines_, start, sz, total_read, comment, skip_empty_rows, lines_remaining, empty_pb); }); if (write_fut.valid()) { write_fut.wait(); } write_fut = std::async( [&, i, sz] { std::fwrite(buf[i].data(), sizeof(char), sz, out); }); if (progress) { pb->tick(sz); } total_read += sz; i = (i + 1) % 2; sz = R_ReadConnection(con, buf[i].data(), chunk_size - 1); if (sz > 0) { buf[i][sz] = '\0'; } start = 0; SPDLOG_DEBUG("first_nl_loc: {0} size: {1}", start, sz); } if (parse_fut.valid()) { parse_fut.wait(); } if (write_fut.valid()) { write_fut.wait(); } std::fclose(out); if (progress) { pb->update(1); } /* raw connections are always created as open, but we should close them */ bool should_close = should_open || Rf_inherits(in, "rawConnection"); if (should_close) { cpp11::package("base")["close"](in); } std::error_code error; if (n_max != 0) { mmap_ = make_mmap_source(filename_.c_str(), error); if (error) { cpp11::stop("%s", error.message().c_str()); } } char last_char = mmap_[mmap_.size() - 1]; bool ends_with_newline = last_char == '\n' || last_char == '\r'; if (!n_max_set && !ends_with_newline) { newlines_.push_back(mmap_.size()); } #ifdef VROOM_LOG auto log = spdlog::basic_logger_mt( "basic_logger", "logs/fixed_width_index_connection.idx", true); log->set_level(spdlog::level::debug); for (auto&& v : newlines_) { SPDLOG_LOGGER_DEBUG(log, "{}", v); } SPDLOG_LOGGER_DEBUG(log, "end of idx {0:x}", (size_t)&newlines_); spdlog::drop("basic_logger"); #endif } vroom/src/vroom_big_int.cc0000644000176200001440000000305314132374245015355 0ustar liggesusers#include "vroom_big_int.h" #include "parallel.h" #include // A version of strtoll that doesn't need null terminated strings, to avoid // needing to copy the data long long vroom_strtoll(const char* begin, const char* end) { unsigned long long val = 0; bool is_neg = false; if (begin == end) { return NA_INTEGER64; } if (begin != end && *begin == '-') { is_neg = true; ++begin; } while (begin != end && isdigit(*begin)) { val = val * 10 + ((*begin++) - '0'); } if (val > LLONG_MAX) { return NA_INTEGER64; } // If there is more than digits, return NA if (begin != end) { return NA_INTEGER64; } return is_neg ? -val : val; } // Normal reading of integer vectors cpp11::doubles read_big_int(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::doubles out(n); parallel_for( n, [&](size_t start, size_t end, size_t) { R_xlen_t i = start; auto col = info->column->slice(start, end); for (auto b = col->begin(), e = col->end(); b != e; ++b) { vroom_big_int_t res; res.ll = parse_value( b, col, vroom_strtoll, info->errors, "a big integer", *info->na); out[i++] = res.dbl; } }, info->num_threads); info->errors->warn_for_errors(); out.attr("class") = {"integer64"}; return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_big_int::class_t; void init_vroom_big_int(DllInfo* dll) { vroom_big_int::Init(dll); } #else void init_vroom_big_int(DllInfo* dll) {} #endif vroom/src/vroom_errors.h0000644000176200001440000000660214362130126015114 0ustar liggesusers#pragma once #include "index.h" #include #include #include #include #include #include #include #include using namespace cpp11::literals; class vroom_errors { struct parse_error { size_t position; size_t column; parse_error(size_t pos, size_t col) : position(pos), column(col) {} }; public: vroom_errors() {} void add_error( size_t row, size_t column, std::string expected = "", std::string actual = "", std::string filename = "") { std::lock_guard guard(mutex_); rows_.push_back(row + 1); columns_.push_back(column + 1); expected_.emplace_back(expected); actual_.emplace_back(actual); filenames_.emplace_back(filename); } void add_parse_error(size_t position, size_t column) { std::lock_guard guard(mutex_); parse_errors_.emplace_back(position, column); } void resolve_parse_errors(const vroom::index& idx) { if (parse_errors_.size() == 0) { return; } // Sort the parse errors by their position std::sort( parse_errors_.begin(), parse_errors_.end(), [](const parse_error& lhs, const parse_error& rhs) { return lhs.position < rhs.position; }); auto row = idx.get_column(0)->begin(); auto row_end = idx.get_column(0)->end(); for (const auto& e : parse_errors_) { while (row != row_end && e.position > row.position()) { ++row; } std::stringstream ss_expected, ss_actual; ss_expected << idx.num_columns() << " columns"; ss_actual << e.column + 1 << " columns"; add_error( row.index() - 1, e.column, ss_expected.str(), ss_actual.str(), row.filename()); } } cpp11::data_frame error_table() const { return cpp11::writable::data_frame( {"row"_nm = rows_, "col"_nm = columns_, "expected"_nm = expected_, "actual"_nm = actual_, "file"_nm = filenames_}); } bool has_errors() const { return rows_.size() > 0; } void warn_for_errors() const { if (!have_warned_ && rows_.size() > 0) { have_warned_ = true; // it is intentional that we aren't using cpp11::package // https://github.com/tidyverse/vroom/commit/984a3e5e37e124feacfec3d184dbeb02eb1145c4 static auto cli_warn = Rf_findFun( Rf_install("cli_warn"), Rf_findVarInFrame(R_NamespaceRegistry, Rf_install("cli"))); cpp11::strings bullets({ "w"_nm = "One or more parsing issues, call {.fun problems} on your data frame for details, e.g.:", " "_nm = "dat <- vroom(...)", " "_nm = "problems(dat)"}); cpp11::sexp cli_warn_call = Rf_lang3( cli_warn, bullets, Rf_mkString("vroom_parse_issue")); Rf_eval(cli_warn_call, R_EmptyEnv); } } void clear() { std::lock_guard guard(mutex_); rows_.clear(); columns_.clear(); expected_.clear(); actual_.clear(); filenames_.clear(); parse_errors_.clear(); } private: mutable bool have_warned_ = false; std::mutex mutex_; std::vector filenames_; std::vector parse_errors_; std::vector rows_; std::vector columns_; std::vector expected_; std::vector actual_; }; vroom/src/vroom_int.cc0000644000176200001440000000255714132374245014544 0ustar liggesusers#include "vroom_int.h" #include "parallel.h" // A version of strtoi that doesn't need null terminated strings, to avoid // needing to copy the data int strtoi(const char* begin, const char* end) { double val = 0; bool is_neg = false; if (begin == end) { return NA_INTEGER; } if (begin != end && *begin == '-') { is_neg = true; ++begin; } while (begin != end && isdigit(*begin)) { val = val * 10 + ((*begin++) - '0'); } // If there is more than digits, return NA if (begin != end) { return NA_INTEGER; } if (val > INT_MAX) { return NA_INTEGER; } return is_neg ? -val : val; } // Normal reading of integer vectors cpp11::integers read_int(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::integers out(n); parallel_for( n, [&](size_t start, size_t end, size_t) { R_xlen_t i = start; auto col = info->column->slice(start, end); for (auto b = col->begin(), e = col->end(); b != e; ++b) { out[i++] = parse_value( b, col, strtoi, info->errors, "an integer", *info->na); } }, info->num_threads); info->errors->warn_for_errors(); return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_int::class_t; void init_vroom_int(DllInfo* dll) { vroom_int::Init(dll); } #else void init_vroom_int(DllInfo* dll) {} #endif vroom/src/iterator.h0000644000176200001440000000440614151705507014216 0ustar liggesusers#pragma once #include "vroom.h" #include namespace vroom { class base_iterator { public: virtual void next() = 0; virtual void advance(ptrdiff_t n) = 0; virtual bool equal_to(const base_iterator& it) const = 0; virtual ptrdiff_t distance_to(const base_iterator& it) const = 0; virtual string value() const = 0; virtual base_iterator* clone() const = 0; virtual string at(ptrdiff_t n) const = 0; virtual ~base_iterator() {} virtual std::string filename() const = 0; virtual size_t index() const = 0; virtual size_t position() const = 0; }; class iterator { base_iterator* it_; public: using iterator_category = std::forward_iterator_tag; using value_type = string; using pointer = string*; using reference = string&; iterator() : it_(nullptr){}; iterator(base_iterator* it) : it_(it) {} iterator& operator=(const iterator& other) { base_iterator* original = it_; it_ = other.it_->clone(); delete original; return *this; } iterator(const iterator& other) : it_(other.it_->clone()) {} iterator operator++(int) { /* postfix */ iterator copy(*this); it_->next(); return copy; } iterator& operator++() /* prefix */ { it_->next(); return *this; } bool operator!=(const iterator& other) const { return !it_->equal_to(*other.it_); } bool operator==(const iterator& other) const { return it_->equal_to(*other.it_); } string operator*() const { return it_->value(); } iterator& operator+=(ptrdiff_t n) { it_->advance(n); return *this; } iterator& operator-=(ptrdiff_t n) { it_->advance(-n); return *this; } iterator operator+(ptrdiff_t n) const { iterator copy(*this); copy.it_->advance(n); return copy; } iterator operator-(ptrdiff_t n) const { iterator copy(*this); copy.it_->advance(-n); return copy; } ptrdiff_t operator-(const iterator& other) const { return -it_->distance_to(*other.it_); } string operator[](ptrdiff_t n) const { return it_->at(n); } std::string filename() const { return it_->filename(); } size_t index() const { return it_->index(); } size_t position() const { return it_->position(); } ~iterator() { if (it_ != nullptr) { delete it_; } } }; } // namespace vroom vroom/src/vroom_chr.cc0000644000176200001440000000223014132374245014512 0ustar liggesusers#include "vroom_chr.h" SEXP check_na(SEXP na, SEXP val) { for (R_xlen_t i = 0; i < Rf_xlength(na); ++i) { SEXP v = STRING_ELT(na, i); // We can just compare the addresses directly because they should now // both be in the global string cache. if (v == val) { return NA_STRING; } } return val; } cpp11::strings read_chr(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::strings out(n); SEXP nas = *info->na; cpp11::unwind_protect([&] { auto i = 0; auto col = info->column; for (auto b = col->begin(), e = col->end(); b != e; ++b) { auto str = *b; auto val = info->locale->encoder_.makeSEXP(str.begin(), str.end(), true); if (Rf_xlength(val) < str.end() - str.begin()) { info->errors->add_error( b.index(), col->get_index(), "", "embedded null", b.filename()); } SET_STRING_ELT(out, i++, check_na(nas, val)); } }); info->errors->warn_for_errors(); return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_chr::class_t; void init_vroom_chr(DllInfo* dll) { vroom_chr::Init(dll); } #else void init_vroom_chr(DllInfo* dll) {} #endif vroom/src/vroom_errors.cpp0000644000176200001440000000035514132374245015455 0ustar liggesusers#include "vroom_errors.h" #include #include [[cpp11::register]] cpp11::data_frame vroom_errors_(cpp11::external_pointer> errors) { return (*errors)->error_table(); } vroom/src/delimited_index.cc0000644000176200001440000002373414362130126015650 0ustar liggesusers#include "delimited_index.h" #include "parallel.h" #include "multi_progress.h" #include #include #include #include #ifdef VROOM_LOG #include "spdlog/sinks/basic_file_sink.h" // support for basic file logging #include "spdlog/spdlog.h" #endif #ifndef VROOM_STANDALONE #include "r_utils.h" #endif #include "unicode_fopen.h" using namespace vroom; delimited_index::delimited_index( const char* filename, const char* delim, const char quote, const bool trim_ws, const bool escape_double, const bool escape_backslash, const bool has_header, const size_t skip, size_t n_max, const char* comment, const bool skip_empty_rows, std::shared_ptr errors, size_t num_threads, bool progress, const bool use_threads) : filename_(filename), has_header_(has_header), quote_(quote), trim_ws_(trim_ws), escape_double_(escape_double), escape_backslash_(escape_backslash), skip_(skip), comment_(comment), rows_(0), columns_(0), progress_(progress), delim_len_(0) { std::error_code error; mmap_ = make_mmap_source(filename, error); if (error) { // We cannot actually portably compare error messages due to a bug in // libstdc++ (https://stackoverflow.com/a/54316671/2055486), so just print // the message on stderr return #ifndef VROOM_STANDALONE REprintf("mapping error: %s\n", error.message().c_str()); #else std::cerr << "mapping error: " << error.message() << '\n'; #endif return; } size_t file_size = mmap_.cend() - mmap_.cbegin(); if (!(mmap_[file_size - 1] == '\n' || mmap_[file_size - 1] == '\r')) { #ifndef VROOM_STANDALONE REprintf("Files must end with a newline\n"); #else std::cerr << "Files must end with a newline\n"; #endif return; } bool has_quoted_newlines = quote != '\0'; size_t start = find_first_line( mmap_, skip_, comment_, skip_empty_rows, has_quoted_newlines, quote); // If an empty file, or a file with only a newline. if (start >= file_size - 1) { return; } if (delim == nullptr) { #ifndef VROOM_STANDALONE delim_ = std::string(1, guess_delim(mmap_, start, /* guess_max */ 20, 0, quote)); #else throw std::runtime_error("Must specify a delimiter"); #endif } else { delim_ = delim; } delim_len_ = delim_.length(); size_t first_nl, second_nl; newline_type nl; std::tie(first_nl, nl) = find_next_newline( mmap_, start, comment_, skip_empty_rows, has_quoted_newlines, quote); std::tie(second_nl, std::ignore) = find_next_newline( mmap_, first_nl + 1, comment, skip_empty_rows, has_quoted_newlines, quote); size_t one_row_size = second_nl - first_nl; size_t guessed_rows = one_row_size > 0 ? (file_size - first_nl) / (one_row_size * 1.1) : 0; std::unique_ptr pb = nullptr; if (progress_) { #ifndef VROOM_STANDALONE auto format = get_pb_format("file", filename); auto width = get_pb_width(format); pb = std::unique_ptr( new multi_progress(format, file_size, width)); pb->tick(start); #endif } bool nmax_set = n_max != static_cast(-1); if (nmax_set) { n_max = n_max + has_header_; num_threads = 1; } // We want at least 10 lines per batch, otherwise threads aren't really // useful size_t batch_size = file_size / num_threads; size_t line_size = second_nl - first_nl; if (batch_size < line_size * 10) { num_threads = 1; } start_indexing: try { idx_ = std::vector(num_threads + 1); // Index the first row size_t cols = 0; csv_state state = RECORD_START; size_t lines_read = index_region( mmap_, idx_[0], delim_.c_str(), nl, quote, comment_, skip_empty_rows, state, start, first_nl + 1, 0, n_max, cols, 0, errors, pb, num_threads, -1); if (idx_[0].size() > 0) { columns_ = idx_[0].size() - 1; } std::vector> threads; if (nmax_set) { threads.emplace_back(std::async(std::launch::async, [&] { n_max = n_max > lines_read ? n_max - lines_read : 0; index_region( mmap_, idx_[1], delim_.c_str(), nl, quote, comment_, skip_empty_rows, state, first_nl + 1, file_size, 0, n_max, cols, columns_, errors, pb, num_threads, file_size / 100); })); } else { threads = parallel_for( file_size - first_nl, [&](size_t start, size_t end, size_t id) { newline_type nl; idx_[id + 1].reserve((guessed_rows / num_threads) * columns_); std::tie(start, nl) = find_next_newline( mmap_, first_nl + start, comment, skip_empty_rows, /* has_quote */ false, quote); ++start; std::tie(end, std::ignore) = find_next_newline( mmap_, first_nl + end, comment, skip_empty_rows, /* has_quote */ false, quote); ++end; size_t cols = 0; csv_state state = RECORD_START; index_region( mmap_, idx_[id + 1], delim_.c_str(), nl, quote, comment_, skip_empty_rows, state, start, end, 0, n_max, cols, columns_, errors, pb, num_threads, file_size / 100); }, num_threads, use_threads, false); } if (progress_) { #ifndef VROOM_STANDALONE pb->display_progress(); #endif } for (auto& t : threads) { t.get(); } } catch (newline_error& e) { num_threads = 1; errors->clear(); goto start_indexing; } size_t total_size = std::accumulate( idx_.begin(), idx_.end(), std::size_t{0}, [](size_t sum, const idx_t& v) { sum += v.size() > 0 ? v.size() : 0; return sum; }); rows_ = columns_ > 0 ? total_size / (columns_ + 1) : 0; if (rows_ > 0 && has_header_) { --rows_; } // REprintf("columns_: %i rows_: %i\n", columns_, rows_); #ifdef VROOM_LOG auto log = spdlog::basic_logger_mt("basic_logger", "logs/index.idx"); log->set_level(spdlog::level::debug); for (auto& i : idx_) { for (auto& v : i) { SPDLOG_LOGGER_DEBUG(log, "{}", v); } SPDLOG_LOGGER_DEBUG(log, "end of idx {0:x}", (size_t)&i); } spdlog::drop("basic_logger"); #endif SPDLOG_DEBUG( "columns: {0} rows: {1} total_size: {2}", columns_, rows_, total_size); } void delimited_index::trim_quotes(const char*& begin, const char*& end) const { if (begin != end && (*begin == quote_)) { ++begin; } if (end != begin && *(end - 1) == quote_) { --end; } } const string delimited_index::get_escaped_string( const char* begin, const char* end, bool has_quote) const { if (end <= begin) { return {begin, begin}; } // If not escaping just return without a copy if (!((escape_double_ && has_quote) || escape_backslash_)) { return {begin, end}; } std::string out; bool needs_escaping = false; auto cur = begin; auto prev = begin; while (cur < end) { if ((escape_double_ && has_quote && *cur == quote_) || (escape_backslash_ && *cur == '\\')) { if (!needs_escaping) { out.reserve(end - begin); needs_escaping = true; } std::copy(prev, cur, std::back_inserter(out)); ++cur; prev = cur; } ++cur; } if (needs_escaping) { std::copy(prev, cur, std::back_inserter(out)); return out; } return {begin, end}; } inline std::pair delimited_index::get_cell(size_t i, bool is_first) const { auto oi = i; auto i_row = i / (columns_); auto i_col = i % (columns_); auto ni = i_row * (columns_ + 1) + i_col; i = ni; for (const auto& idx : idx_) { auto sz = idx.size(); if (i + 1 < sz) { auto start = idx[i]; auto end = idx[i + 1]; if (start == end) { return {start, end}; } // By relying on 0 and 1 being true and false we can remove a branch // here, which improves performance a bit, as this function is called a // lot. if (!is_first) { start = start + delim_len_; } // REprintf( //"oi: %i ni: %i i: %i start: %i end: %i\n", oi, ni, i, start, end); return {start, end}; } i -= sz; } std::stringstream ss; ss.imbue(std::locale("")); ss << "Failure to retrieve index " << std::fixed << oi << " / " << rows_; throw std::out_of_range(ss.str()); /* should never get here */ return {0, 0}; } const string delimited_index::get_trimmed_val(size_t i, bool is_first, bool is_last) const { size_t begin_p; size_t end_p; std::tie(begin_p, end_p) = get_cell(i, is_first); const char* begin = mmap_.data() + begin_p; const char* end = mmap_.data() + end_p; // Check for windows newlines if the last column */ if (is_last) { if (begin < end) { if (*(end - 1) == '\r') { --end; } } } if (trim_ws_) { trim_whitespace(begin, end); } bool has_quote = false; if (quote_ != '\0' && begin < end) { has_quote = *begin == quote_; if (has_quote) { trim_quotes(begin, end); } if (trim_ws_) { trim_whitespace(begin, end); } } return get_escaped_string(begin, end, has_quote); } string delimited_index::get(size_t row, size_t col) const { auto i = (row + has_header_) * columns_ + col; return get_trimmed_val(i, col == 0, col == (columns_ - 1)); } vroom/src/vroom.cc0000644000176200001440000000537014236060002013652 0ustar liggesusers#include #include #include #include #include #include "LocaleInfo.h" #include "columns.h" #include "connection.h" #include "index.h" #include "index_collection.h" #include "vroom_rle.h" #include #include #include #include #include "unicode_fopen.h" #include "vroom_errors.h" [[cpp11::register]] SEXP vroom_( const cpp11::list& inputs, SEXP delim, const char quote, bool trim_ws, bool escape_double, bool escape_backslash, const char* comment, const bool skip_empty_rows, size_t skip, ptrdiff_t n_max, bool progress, const cpp11::sexp& col_names, cpp11::sexp col_types, cpp11::sexp col_select, cpp11::sexp name_repair, SEXP id, const cpp11::strings& na, const cpp11::list& locale, ptrdiff_t guess_max, size_t num_threads, size_t altrep) { bool has_header = TYPEOF(col_names) == LGLSXP && cpp11::logicals(col_names)[0]; std::vector filenames; bool add_filename = !Rf_isNull(id); // We need to retrieve filenames now before the connection objects are read, // as they are invalid afterwards. if (add_filename) { filenames = get_filenames(inputs); } auto errors = new std::shared_ptr(new vroom_errors()); auto idx = std::make_shared( inputs, Rf_isNull(delim) ? nullptr : cpp11::as_cpp(delim), quote, trim_ws, escape_double, escape_backslash, has_header, skip, n_max, comment, skip_empty_rows, *errors, num_threads, progress); (*errors)->resolve_parse_errors(*idx); return create_columns( idx, col_names, std::move(col_types), std::move(col_select), std::move(name_repair), id, filenames, na, locale, altrep, guess_max, errors, num_threads); } [[cpp11::register]] bool has_trailing_newline(const cpp11::strings& filename) { std::FILE* f = unicode_fopen(CHAR(filename[0]), "rb"); if (!f) { return true; } std::setvbuf(f, nullptr, _IONBF, 0); fseek(f, -1, SEEK_END); char c = fgetc(f); fclose(f); return c == '\n'; } [[cpp11::register]] SEXP vroom_rle(const cpp11::integers& input) { #ifdef HAS_ALTREP return vroom_rle::Make(input); #else R_xlen_t total_size = std::accumulate(input.begin(), input.end(), 0); cpp11::writable::strings out(total_size); cpp11::strings nms = input.names(); R_xlen_t idx = 0; for (R_xlen_t i = 0; i < Rf_xlength(input); ++i) { for (R_xlen_t j = 0; j < input[i]; ++j) { SET_STRING_ELT(out, idx++, nms[i]); } } return out; #endif } vroom/src/multi_progress.h0000644000176200001440000000454014151705507015442 0ustar liggesusers#pragma once #include #include #ifdef VROOM_STANDALONE // A stub class that doesn't do anything namespace RProgress { class RProgress { public: RProgress( std::string format = "[:bar] :percent", double total = 100, int width = 80 - 2, const char* complete_char = "=", const char* middle_char = "=", const char* incomplete_char = "-", bool clear = true, double show_after = 0.2) {} void update(double) {} void tick(double) {} void set_reverse(bool) {} }; } // namespace RProgress #else #include #include "RProgress.h" #endif class multi_progress { public: multi_progress( std::string format = "[:bar] :percent", size_t total = 100, int width = 78, const char* complete_char = "=", const char* incomplete_char = "-", bool clear = true, double show_after = 0.2) : pb_(new RProgress::RProgress( format, total, width, complete_char, complete_char, incomplete_char, clear, show_after)), progress_(0), total_(total), last_progress_(0), last_time_(std::chrono::system_clock::now()), update_interval_(10) { pb_->set_reverse(false); } void tick(size_t progress) { std::lock_guard guard(mutex_); progress_ += progress; cv_.notify_one(); } void finish() { std::lock_guard guard(mutex_); progress_ = total_; cv_.notify_one(); } void display_progress() { while (true) { std::unique_lock lk(mutex_); if (progress_ < total_ - 1) { cv_.wait(lk); auto now = std::chrono::system_clock::now(); std::chrono::duration diff = now - last_time_; if (diff > update_interval_) { pb_->tick(progress_ - last_progress_); last_progress_ = progress_; last_time_ = std::chrono::system_clock::now(); } } else { break; } } pb_->update(1); } private: std::unique_ptr pb_; size_t progress_; size_t total_; size_t last_progress_; std::chrono::time_point last_time_; std::chrono::milliseconds update_interval_; std::mutex mutex_; std::condition_variable cv_; }; vroom/src/columns.h0000644000176200001440000001471714504643364014057 0ustar liggesusers#pragma once #include #include #include #include #include "vroom.h" #include "vroom_big_int.h" #include "vroom_chr.h" #include "vroom_date.h" #include "vroom_dbl.h" #include "vroom_dttm.h" #include "vroom_fct.h" #include "vroom_int.h" #include "vroom_lgl.h" #include "vroom_num.h" #include "vroom_rle.h" #include "vroom_time.h" #include "vroom_vec.h" #include "connection.h" #include "index_collection.h" #include "collectors.h" namespace vroom { inline std::vector get_filenames(SEXP in) { auto n = Rf_xlength(in); std::vector out; out.reserve(n); for (R_xlen_t i = 0; i < n; ++i) { SEXP x = VECTOR_ELT(in, i); if (TYPEOF(x) == STRSXP) { out.emplace_back(cpp11::as_cpp(x)); } else { out.emplace_back(con_description(x)); } } return out; } inline SEXP generate_filename_column( const std::vector& filenames, const std::vector& lengths, size_t rows) { #ifdef HAS_ALTREP // suppress compiler warning about unused parameter, as this is only used // without altrep. (void)rows; cpp11::writable::integers rle(filenames.size()); for (R_xlen_t i = 0; i < R_xlen_t(lengths.size()); ++i) { rle[i] = lengths[i]; } rle.names() = filenames; return vroom_rle::Make(rle); #else std::vector out; out.reserve(rows); if (static_cast(filenames.size()) != lengths.size()) { cpp11::stop("inputs and lengths inconsistent"); } for (size_t i = 0; i < filenames.size(); ++i) { for (size_t j = 0; j < lengths[i]; ++j) { out.push_back(filenames[i]); } } return cpp11::as_sexp(out); #endif } inline cpp11::list create_columns( std::shared_ptr idx, cpp11::sexp col_names, cpp11::sexp col_types, cpp11::sexp col_select, cpp11::sexp name_repair, SEXP id, std::vector& filenames, cpp11::strings na, cpp11::list locale, size_t altrep, size_t guess_max, cpp11::external_pointer> errors, size_t num_threads) { R_xlen_t num_cols = idx->num_columns(); auto locale_info = std::make_shared(locale); R_xlen_t i = 0; bool add_filename = !Rf_isNull(id); cpp11::writable::list res(num_cols + add_filename); cpp11::writable::strings res_nms(num_cols + add_filename); if (add_filename) { res[i] = generate_filename_column(filenames, idx->row_sizes(), idx->num_rows()); res_nms[i] = cpp11::strings(id)[0]; ++i; } auto my_collectors = resolve_collectors( col_names, col_types, col_select, name_repair, idx, na, locale_info, guess_max, altrep); for (R_xlen_t col = 0; col < num_cols; ++col) { auto collector = my_collectors[col]; auto col_type = collector.type(); if (col_type == column_type::Skip) { continue; } // This is deleted in the finalizers when the vectors are GC'd by R auto info = new vroom_vec_info{ idx->get_column(col), num_threads, std::make_shared(na), locale_info, *errors, std::string()}; res_nms[i] = collector.name(); switch (collector.type()) { case column_type::Dbl: if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_dbl::Make(info); #endif } else { res[i] = read_dbl(info); delete info; } break; case column_type::Int: if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_int::Make(info); #endif } else { res[i] = read_int(info); delete info; } break; case column_type::BigInt: if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_big_int::Make(info); #endif } else { res[i] = read_big_int(info); delete info; } break; case column_type::Num: if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_num::Make(info); #endif } else { res[i] = read_num(info); delete info; } break; case column_type::Lgl: // if (collector.use_altrep()) { //#if defined HAS_ALTREP && R_VERSION >= R_Version(3, 6, 0) // res[i] = vroom_lgl::Make(info); //#endif //} else { res[i] = read_lgl(info); delete info; //} break; case column_type::Fct: { auto levels = collector["levels"]; if (Rf_isNull(levels)) { res[i] = read_fct_implicit( info, cpp11::as_cpp(collector["include_na"])); delete info; } else { bool ordered = cpp11::as_cpp(collector["ordered"]); if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_fct::Make(info, levels, ordered); #endif } else { res[i] = read_fct_explicit(info, levels, ordered); delete info; } } break; } case column_type::Date: info->format = cpp11::as_cpp(collector["format"]); if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_date::Make(info); #endif } else { res[i] = read_date(info); delete info; } break; case column_type::Dttm: info->format = cpp11::as_cpp(collector["format"]); if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_dttm::Make(info); #endif } else { res[i] = read_dttm(info); delete info; } break; case column_type::Time: info->format = cpp11::as_cpp(collector["format"]); if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_time::Make(info); #endif } else { res[i] = read_time(info); delete info; } break; default: if (collector.use_altrep()) { #ifdef HAS_ALTREP res[i] = vroom_chr::Make(info); #endif } else { res[i] = read_chr(info); delete info; } } ++i; } if (i < num_cols) { // Resize the list appropriately SETLENGTH(res, i); SET_TRUELENGTH(res, i); SETLENGTH(res_nms, i); SET_TRUELENGTH(res_nms, i); } res.attr("names") = res_nms; cpp11::writable::list spec(my_collectors.spec()); spec["delim"] = cpp11::writable::strings({idx->get_delim().c_str()}); spec.attr("class") = "col_spec"; res.attr("spec") = spec; res.attr("problems") = errors; return res; } } // namespace vroom vroom/src/fixed_width_index_connection.h0000644000176200001440000000106014132374245020262 0ustar liggesusers#pragma once #include "fixed_width_index.h" namespace vroom { class fixed_width_index_connection : public fixed_width_index { std::string filename_; public: fixed_width_index_connection( SEXP in, std::vector col_starts, std::vector col_ends, bool trim_ws, const size_t skip, const char* comment, const bool skip_empty_rows, const size_t n_max, const bool progress, const size_t chunk_size); ~fixed_width_index_connection() { remove(filename_.c_str()); } }; } // namespace vroom vroom/src/Iconv.h0000644000176200001440000000102714132374245013437 0ustar liggesusers#ifndef READ_ICONV_H_ #define READ_ICONV_H_ #include #include "R_ext/Riconv.h" #include #include class Iconv { void* cd_; std::string buffer_; public: Iconv(const std::string& from, const std::string& to = "UTF-8"); virtual ~Iconv(); SEXP makeSEXP(const char* start, const char* end, bool hasNull = true); std::string makeString(const char* start, const char* end); private: // Returns number of characters in buffer size_t convert(const char* start, const char* end); }; #endif vroom/src/altrep.cc0000644000176200001440000001036414132374245014012 0ustar liggesusers#include "altrep.h" #include "vroom_big_int.h" #include "vroom_chr.h" #include "vroom_date.h" #include "vroom_dbl.h" #include "vroom_dttm.h" #include "vroom_fct.h" #include "vroom_int.h" #include "vroom_lgl.h" #include "vroom_num.h" #include "vroom_time.h" #include #include #include [[cpp11::register]] void force_materialization(SEXP x) { #ifdef HAS_ALTREP DATAPTR(x); #endif } bool vroom_altrep(SEXP x) { #ifdef HAS_ALTREP return R_altrep_inherits(x, vroom_chr::class_t) || R_altrep_inherits(x, vroom_date::class_t) || R_altrep_inherits(x, vroom_dbl::class_t) || R_altrep_inherits(x, vroom_dttm::class_t) || R_altrep_inherits(x, vroom_fct::class_t) || R_altrep_inherits(x, vroom_int::class_t) || // R_altrep_inherits(x, vroom_lgl::class_t) || R_altrep_inherits(x, vroom_num::class_t) || R_altrep_inherits(x, vroom_time::class_t) || R_altrep_inherits(x, vroom_big_int::class_t); #else return false; #endif } [[cpp11::register]] SEXP vroom_materialize(SEXP x, bool replace) { #ifdef HAS_ALTREP for (R_xlen_t col = 0; col < Rf_xlength(x); ++col) { SEXP elt = VECTOR_ELT(x, col); // First materialize all of the non-character vectors if (vroom_altrep(elt)) { DATAPTR(elt); } } // If replace replace the altrep vectors with their materialized // vectors if (replace) { for (R_xlen_t col = 0; col < Rf_xlength(x); ++col) { SEXP elt = PROTECT(VECTOR_ELT(x, col)); if (vroom_altrep(elt)) { SET_VECTOR_ELT(x, col, R_altrep_data2(elt)); R_set_altrep_data2(elt, R_NilValue); } UNPROTECT(1); } } #endif return x; } [[cpp11::register]] SEXP vroom_convert(SEXP x) { #ifdef HAS_ALTREP SEXP out = PROTECT(Rf_allocVector(VECSXP, Rf_xlength(x))); SHALLOW_DUPLICATE_ATTRIB(out, x); for (R_xlen_t col = 0; col < Rf_xlength(x); ++col) { SEXP elt = VECTOR_ELT(x, col); if (!ALTREP(elt)) { SET_VECTOR_ELT(out, col, elt); } else { R_xlen_t nrow = Rf_xlength(elt); switch (TYPEOF(elt)) { case LGLSXP: { SET_VECTOR_ELT(out, col, Rf_allocVector(LGLSXP, nrow)); int* out_p = LOGICAL(VECTOR_ELT(out, col)); int* in_p = LOGICAL(elt); for (R_xlen_t row = 0; row < nrow; ++row) { out_p[row] = in_p[row]; } break; } case INTSXP: { SET_VECTOR_ELT(out, col, Rf_allocVector(INTSXP, nrow)); int* out_p = INTEGER(VECTOR_ELT(out, col)); int* in_p = INTEGER(elt); for (R_xlen_t row = 0; row < nrow; ++row) { out_p[row] = in_p[row]; } break; } case REALSXP: { SET_VECTOR_ELT(out, col, Rf_allocVector(REALSXP, nrow)); double* out_p = REAL(VECTOR_ELT(out, col)); double* in_p = REAL(elt); for (R_xlen_t row = 0; row < nrow; ++row) { out_p[row] = in_p[row]; } break; } case STRSXP: { SET_VECTOR_ELT(out, col, Rf_allocVector(STRSXP, nrow)); SEXP out_elt = VECTOR_ELT(out, col); DATAPTR(elt); for (R_xlen_t row = 0; row < nrow; ++row) { SET_STRING_ELT(out_elt, row, STRING_ELT(elt, row)); } break; } } SHALLOW_DUPLICATE_ATTRIB(VECTOR_ELT(out, col), elt); } } UNPROTECT(1); return out; #else return x; #endif } [[cpp11::register]] std::string vroom_str_(const cpp11::sexp& x) { std::stringstream ss; #ifdef HAS_ALTREP if (ALTREP(x)) { auto csym = CAR(ATTRIB(ALTREP_CLASS(x))); auto psym = CADR(ATTRIB(ALTREP_CLASS(x))); bool is_altrep = ALTREP(x); bool materialzied = R_altrep_data2(x) != R_NilValue; ss << std::boolalpha << "altrep:" << is_altrep << '\t' << "type:" << CHAR(PRINTNAME(psym)) << "::" << CHAR(PRINTNAME(csym)); // We would have to dispatch to get the length of an object if (!Rf_isObject(x)) { ss << '\t' << "length:" << LENGTH(x); } ss << '\t' << "materialized:" << materialzied << '\n'; } #else if (false) { } #endif else { ss << std::boolalpha << "altrep:" << false << '\t' << "type: " << Rf_type2char(TYPEOF(x)); if (!Rf_isObject(x)) { ss << '\t' << "length:" << LENGTH(x); } ss << '\n'; } return ss.str(); } vroom/src/iconv_file.cc0000644000176200001440000000631114132374245014635 0ustar liggesusers#include #include #include "R_ext/Riconv.h" #include "connection.h" #define VROOM_BUFSIZ 1024 // Adapted from // https://www.gnu.org/software/libc/manual/html_node/iconv-Examples.html [[cpp11::register]] size_t convert_connection( SEXP in_con, SEXP out_con, const std::string& from, const std::string& to) { static auto isOpen = cpp11::package("base")["isOpen"]; static auto open = cpp11::package("base")["open"]; static auto close = cpp11::package("base")["close"]; char inbuf[VROOM_BUFSIZ]; char wrbuf[VROOM_BUFSIZ * 4]; char* wrptr = wrbuf; size_t insize = 0; void* cd; bool should_close_in = !isOpen(in_con); bool should_close_out = !isOpen(out_con); if (should_close_in) { open(in_con, "rb"); } if (should_close_out) { open(out_con, "wb"); } cd = Riconv_open(to.c_str(), from.c_str()); if (cd == (void*)-1) { /* Something went wrong. */ if (errno == EINVAL) { if (should_close_in) { close(in_con); } if (should_close_out) { close(out_con); } cpp11::stop("Can't convert from %s to %s", from.c_str(), to.c_str()); } else { if (should_close_in) { close(in_con); } if (should_close_out) { close(out_con); } cpp11::stop("Iconv initialisation failed"); } } size_t avail = VROOM_BUFSIZ * 4; size_t bytes_wrote = 0; while (avail > 0) { size_t nread; size_t nconv; const char* inptr = inbuf; /* Read more input. */ nread = R_ReadConnection(in_con, inbuf + insize, sizeof(inbuf) - insize); if (nread == 0) { /* When we come here the file is completely read. This still could mean there are some unused characters in the inbuf. */ /* Now write out the byte sequence to get into the initial state if this is necessary. */ Riconv(cd, nullptr, nullptr, &wrptr, &avail); break; } insize += nread; /* Do the conversion. */ nconv = Riconv(cd, &inptr, &insize, &wrptr, &avail); if (nconv == (size_t)-1) { /* Not everything went right. It might only be an unfinished byte sequence at the end of the buffer. Or it is a real problem. */ if (errno == EINVAL) { /* This is harmless. Simply move the unused bytes to the beginning of the buffer so that they can be used in the next round. */ memmove(inbuf, inptr, insize); } else { /* It is a real problem. Maybe we ran out of space in the output buffer or we have invalid input. */ if (should_close_in) { close(in_con); } if (should_close_out) { close(out_con); } cpp11::stop("iconv failed"); break; } } R_WriteConnection(out_con, wrbuf, wrptr - wrbuf); bytes_wrote += wrptr - wrbuf; wrptr = wrbuf; avail = sizeof(wrbuf); } if (Riconv_close(cd) != 0) { if (should_close_in) { close(in_con); } if (should_close_out) { close(out_con); } cpp11::stop("Iconv closed failed"); } if (should_close_in) { close(in_con); } if (should_close_out) { close(out_con); } return bytes_wrote; } vroom/src/LocaleInfo.h0000644000176200001440000000071714132374245014401 0ustar liggesusers#ifndef FASTREAD_LOCALINFO #define FASTREAD_LOCALINFO #include "Iconv.h" #include #include #include class LocaleInfo { public: // LC_TIME std::vector mon_, monAb_, day_, dayAb_, amPm_; std::string dateFormat_, timeFormat_; // LC_NUMERIC std::string decimalMark_, groupingMark_; // LC_MISC std::string tz_; std::string encoding_; Iconv encoder_; LocaleInfo(const cpp11::list&); }; #endif vroom/src/vroom.h0000644000176200001440000000417414132374245013531 0ustar liggesusers#pragma once #ifndef VROOM_LOG #define SPDLOG_TRACE(...) (void)0 #define SPDLOG_DEBUG(...) (void)0 #define SPDLOG_INFO(...) (void)0 #else #include "spdlog/spdlog.h" #endif #include #include namespace vroom { enum column_type { Chr = 1, Fct = 2, Int = 4, Dbl = 8, Num = 16, Lgl = 32, Dttm = 64, Date = 128, Time = 256, BigInt = 512, Skip = 1024 }; // A custom string wrapper that avoids constructing a string object unless // needed because of escapes. class string { public: string(const std::string& str) : str_(str) { begin_ = str_.c_str(); end_ = begin_ + str_.length(); } string(std::string&& str) : str_(std::move(str)) { begin_ = str_.c_str(); end_ = begin_ + str_.length(); } string(const char* begin, const char* end) : begin_(begin), end_(end) {} const char* begin() const { return begin_; } const char* end() const { return end_; } size_t length() const { return end_ - begin_; } size_t size() const { return end_ - begin_; } bool operator==(const string& other) const { return length() == other.length() && strncmp(begin_, other.begin_, length()) == 0; } bool operator==(const std::string& other) const { return length() == other.length() && strncmp(begin_, other.data(), length()) == 0; } std::string str() const { if (size() > 0 && str_.size() == 0) { return std::string(begin_, end_); } else { return str_; } } private: const char* begin_; const char* end_; const std::string str_; }; template inline T na(); } // namespace vroom // Specialization for our custom strings, needed so we can use them in // unordered_maps // [1]: https://stackoverflow.com/a/17017281/2055486 // [2]: https://stackoverflow.com/a/34597485/2055486 namespace std { template <> struct hash { std::size_t operator()(const vroom::string& k) const { const char* begin = k.begin(); const char* end = k.end(); size_t result = 0; const size_t prime = 31; while (begin != end) { result = *begin++ + (result * prime); } return result; } }; } // namespace std vroom/src/DateTimeParser.h0000644000176200001440000003317014151705507015236 0ustar liggesusers#ifndef FASTREAD_DATE_TIME_PARSER_H_ #define FASTREAD_DATE_TIME_PARSER_H_ #include "DateTime.h" #include "LocaleInfo.h" #include #include #include // Parsing --------------------------------------------------------------------- template inline bool parseInt(Iterator& first, Iterator& last, Attr& res) { char buf[65]; size_t expected_size = last - first; if (expected_size > sizeof(buf) - 1) { res = NA_INTEGER; return false; } std::copy(first, last, buf); buf[expected_size] = '\0'; long lres; char* endp; errno = 0; lres = strtol(buf, &endp, 10); size_t parsed_size = endp - buf; /* next can happen on a 64-bit platform */ if (res > INT_MAX || res < INT_MIN) lres = NA_INTEGER; if (errno == ERANGE) lres = NA_INTEGER; res = static_cast(lres); first += parsed_size; return res != NA_INTEGER; } template inline bool parseDouble( const char /* decimalMark */, Iterator& first, Iterator& last, Attr& res) { char buf[65]; // It can't be a double if it is over 64 characters long. bool too_long = last - first > 64; if (too_long) { res = NA_REAL; return false; } std::copy(first, last, buf); buf[last - first] = '\0'; char* endp; errno = 0; res = strtod(buf, &endp); if (errno > 0) { res = NA_REAL; } else { first += endp - buf; } return !ISNA(res); } class DateTimeParser { int sign_, year_, mon_, day_, hour_, min_, sec_; double psec_; int amPm_; bool compactDate_; // used for guessing int tzOffsetHours_, tzOffsetMinutes_; std::string tz_; LocaleInfo* pLocale_; std::string tzDefault_; const char* dateItr_; const char* dateEnd_; public: DateTimeParser(LocaleInfo* pLocale) : pLocale_(pLocale), tzDefault_(pLocale->tz_), dateItr_(NULL), dateEnd_(NULL) { reset(); } // Parse ISO8601 date time. In benchmarks this only seems ~30% faster than // parsing with a format string so it doesn't seem necessary to add individual // parsers for other common formats. bool parseISO8601(bool /* partial */ = true) { // Date: YYYY-MM-DD, YYYYMMDD if (!consumeInteger(4, &year_)) return false; if (consumeThisChar('-')) compactDate_ = false; if (!consumeInteger(2, &mon_)) return false; if (!compactDate_ && !consumeThisChar('-')) return false; if (!consumeInteger(2, &day_)) return false; if (isComplete()) return true; // Spec requires T, but common to use space instead char next; if (!consumeChar(&next)) return false; if (next != 'T' && next != ' ') return false; // hh:mm:ss.sss, hh:mm:ss, hh:mm, hh // hhmmss.sss, hhmmss, hhmm if (!consumeInteger(2, &hour_)) return false; consumeThisChar(':'); consumeInteger(2, &min_); consumeThisChar(':'); consumeSeconds(&sec_, &psec_); if (isComplete()) return true; // Has a timezone tz_ = "UTC"; if (!consumeTzOffset(&tzOffsetHours_, &tzOffsetMinutes_)) return false; return isComplete(); } bool parseLocaleTime() { return parse(pLocale_->timeFormat_); } bool parseLocaleDate() { return parse(pLocale_->dateFormat_); } // A flexible time parser for the most common formats bool parseTime() { if (!consumeInteger(2, &hour_, false)) return false; if (!consumeThisChar(':')) return false; if (!consumeInteger(2, &min_)) return false; consumeThisChar(':'); consumeSeconds(&sec_, NULL); consumeWhiteSpace(); consumeString(pLocale_->amPm_, &amPm_); consumeWhiteSpace(); return isComplete(); } bool parseDate() { // Date: YYYY-MM-DD, YYYY/MM/DD if (!consumeInteger(4, &year_)) return false; if (!consumeThisChar('-') && !consumeThisChar('/')) return false; if (!consumeInteger(2, &mon_)) return false; if (!consumeThisChar('-') && !consumeThisChar('/')) return false; if (!consumeInteger(2, &day_)) return false; return isComplete(); } bool isComplete() { return dateItr_ == dateEnd_; } void setDate(const char* start, const char* end) { reset(); dateItr_ = start; dateEnd_ = end; } bool parse(const std::string& format) { consumeWhiteSpace(); // always consume leading whitespace std::string::const_iterator formatItr, formatEnd = format.end(); for (formatItr = format.begin(); formatItr != formatEnd; ++formatItr) { // Whitespace in format matches 0 or more whitespace in date if (std::isspace(*formatItr)) { consumeWhiteSpace(); continue; } // Any other characters must much exactly. if (*formatItr != '%') { if (!consumeThisChar(*formatItr)) return false; continue; } if (formatItr + 1 == formatEnd) throw std::runtime_error("Invalid format: trailing %"); formatItr++; switch (*formatItr) { case 'Y': // year with century if (!consumeInteger(4, &year_)) return false; break; case 'y': // year without century if (!consumeInteger(2, &year_)) return false; year_ += (year_ < 69) ? 2000 : 1900; break; case 'm': // month if (!consumeInteger(2, &mon_, false)) return false; break; case 'b': // abbreviated month name if (!consumeString(pLocale_->monAb_, &mon_)) return false; break; case 'B': // month name if (!consumeString(pLocale_->mon_, &mon_)) return false; break; case 'd': // day if (!consumeInteger(2, &day_, false)) return false; break; case 'a': // abbreviated day of week if (!consumeString(pLocale_->dayAb_, &day_)) return false; break; case 'e': // day with optional leading space if (!consumeIntegerWithSpace(2, &day_)) return false; break; case 'h': // hour, unrestricted if (!consumeHours(&hour_, &sign_)) return false; break; case 'H': // hour, 0-23 if (!consumeInteger(2, &hour_, false)) return false; if (hour_ < 0 || hour_ > 23) { return false; } break; case 'I': // hour if (!consumeInteger(2, &hour_, false)) return false; if (hour_ < 1 || hour_ > 12) { return false; } hour_ %= 12; break; case 'M': // minute if (!consumeInteger(2, &min_)) return false; break; case 'S': // seconds (integer) if (!consumeSeconds(&sec_, NULL)) return false; break; case 'O': // seconds (double) if (formatItr + 1 == formatEnd || *(formatItr + 1) != 'S') throw std::runtime_error( "Invalid format: %%O must be followed by %%S"); formatItr++; if (!consumeSeconds(&sec_, &psec_)) return false; break; case 'p': // AM/PM if (!consumeString(pLocale_->amPm_, &amPm_)) return false; break; case 'z': // time zone specification tz_ = "UTC"; if (!consumeTzOffset(&tzOffsetHours_, &tzOffsetMinutes_)) return false; break; case 'Z': // time zone name if (!consumeTzName(&tz_)) return false; break; // Extensions case '.': if (!consumeNonDigit()) return false; break; case '+': if (!consumeNonDigits()) return false; break; case '*': consumeNonDigits(); break; case 'A': // auto date / time if (formatItr + 1 == formatEnd) throw std::runtime_error( "Invalid format: %%A must be followed by another letter"); formatItr++; switch (*formatItr) { case 'D': if (!parseDate()) return false; break; case 'T': if (!parseTime()) return false; break; default: throw std::runtime_error("Invalid %%A auto parser"); } break; // Compound formats case 'D': parse("%m/%d/%y"); break; case 'F': parse("%Y-%m-%d"); break; case 'R': parse("%H:%M"); break; case 'X': case 'T': parse("%H:%M:%S"); break; case 'x': parse("%y/%m/%d"); break; default: throw std::runtime_error("Unsupported format"); } } consumeWhiteSpace(); // always consume trailing whitespace return isComplete(); } DateTime makeDateTime() { DateTime dt(year_, mon_, day_, hour(), min_, sec_, psec_, tz_); if (tz_ == "UTC") dt.setOffset(-tzOffsetHours_ * 3600 - tzOffsetMinutes_ * 60); return dt; } DateTime makeDate() { DateTime dt(year_, mon_, day_, 0, 0, 0, 0, "UTC"); return dt; } DateTime makeTime() { DateTime dt( 0, 1, 1, sign_ * hour(), sign_ * min_, sign_ * sec_, sign_ * psec_, "UTC"); return dt; } bool compactDate() { return compactDate_; } int year() { return year_; } private: int hour() { if (hour_ == 12) { // 12 AM if (amPm_ == 1) { return hour_ - 12; } // 12 PM return hour_; } // Rest of PM if (amPm_ == 2) { return hour_ + 12; } // 24 hour time return hour_; } inline bool consumeHours(int* pHour, int* pSign) { if (dateItr_ == dateEnd_) return false; int sign = 1; if (*dateItr_ == '-') { sign = -1; ++dateItr_; } else if (*dateItr_ == '+') { ++dateItr_; } if (!consumeInteger(10, pHour, false)) return false; *pSign = sign; return true; } inline bool consumeSeconds(int* pSec, double* pPartialSec) { double sec; if (!consumeDouble(&sec)) return false; *pSec = (int)sec; if (pPartialSec != NULL) *pPartialSec = sec - *pSec; return true; } // Assumes `pOut` is 1-indexed inline bool consumeString(const std::vector& haystack, int* pOut) { // haystack is always in UTF-8 std::string needleUTF8 = pLocale_->encoder_.makeString(dateItr_, dateEnd_); std::transform( needleUTF8.begin(), needleUTF8.end(), needleUTF8.begin(), ::tolower); for (size_t i = 0; i < haystack.size(); ++i) { std::string hay = haystack[i]; std::transform(hay.begin(), hay.end(), hay.begin(), ::tolower); if (needleUTF8.find(hay) != std::string::npos) { *pOut = i + 1; dateItr_ += hay.size(); return true; } } return false; } inline bool consumeInteger(int n, int* pOut, bool exact = true) { if (dateItr_ == dateEnd_ || *dateItr_ == '-' || *dateItr_ == '+') return false; const char* start = dateItr_; const char* end = std::min(dateItr_ + n, dateEnd_); bool ok = parseInt(dateItr_, end, *pOut); return ok && (!exact || (dateItr_ - start) == n); } // Integer with optional space inline bool consumeIntegerWithSpace(int n, int* pOut) { if (consumeThisChar(' ')) n--; return consumeInteger(n, pOut); } inline bool consumeDouble(double* pOut) { if (dateItr_ == dateEnd_ || *dateItr_ == '-' || *dateItr_ == '+') return false; return parseDouble(pLocale_->decimalMark_[0], dateItr_, dateEnd_, *pOut); } inline bool consumeWhiteSpace() { while (dateItr_ != dateEnd_ && std::isspace(*dateItr_)) dateItr_++; return true; } inline bool consumeNonDigit() { if (dateItr_ == dateEnd_ || std::isdigit(*dateItr_)) return false; dateItr_++; return true; } inline bool consumeNonDigits() { if (!consumeNonDigit()) return false; while (dateItr_ != dateEnd_ && !std::isdigit(*dateItr_)) dateItr_++; return true; } inline bool consumeChar(char* pOut) { if (dateItr_ == dateEnd_) return false; *pOut = *dateItr_++; return true; } inline bool consumeThisChar(char needed) { if (dateItr_ == dateEnd_ || *dateItr_ != needed) return false; dateItr_++; return true; } inline bool consumeAMPM(bool* pIsPM) { if (dateItr_ == dateEnd_) return false; if (consumeThisChar('A') || consumeThisChar('a')) { *pIsPM = false; } else if (consumeThisChar('P') || consumeThisChar('p')) { *pIsPM = true; } else { return false; } if (!(consumeThisChar('M') || consumeThisChar('m'))) return false; return true; } // ISO8601 style // Z // Β±hh:mm // Β±hhmm // Β±hh inline bool consumeTzOffset(int* pHours, int* pMinutes) { if (consumeThisChar('Z')) return true; // Optional +/- (required for ISO8601 but we'll let it slide) int mult = 1; if (*dateItr_ == '+' || *dateItr_ == '-') { mult = (*dateItr_ == '-') ? -1 : 1; dateItr_++; } // Required hours if (!consumeInteger(2, pHours)) return false; // Optional colon and minutes consumeThisChar(':'); consumeInteger(2, pMinutes); *pHours *= mult; *pMinutes *= mult; return true; } inline bool consumeTzName(std::string* pOut) { const char* tzStart = dateItr_; while (dateItr_ != dateEnd_ && !std::isspace(*dateItr_)) dateItr_++; pOut->assign(tzStart, dateItr_); return tzStart != dateItr_; } void reset() { sign_ = 1; year_ = -1; mon_ = 1; day_ = 1; hour_ = 0; min_ = 0; sec_ = 0; psec_ = 0; amPm_ = -1; compactDate_ = true; tzOffsetHours_ = 0; tzOffsetMinutes_ = 0; tz_ = tzDefault_; } }; #endif vroom/src/fixed_width_index.h0000644000176200001440000001677414151705507016065 0ustar liggesusers#pragma once #include "index.h" // clang-format off #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wsign-compare" #include # pragma clang diagnostic pop #else #include #endif // clang-format on #ifndef VROOM_STANDALONE #include "r_utils.h" #include "RProgress.h" #else #include "utils.h" #define NA_INTEGER INT_MIN #endif #ifdef VROOM_LOG #include "spdlog/sinks/basic_file_sink.h" // support for basic file logging #include "spdlog/spdlog.h" #endif #include "unicode_fopen.h" namespace vroom { class fixed_width_index : public index, public std::enable_shared_from_this { protected: fixed_width_index() : trim_ws_(0) {} std::vector newlines_; std::vector col_starts_; std::vector col_ends_; mio::mmap_source mmap_; bool trim_ws_; std::string filename_; public: fixed_width_index( const char* filename, std::vector col_starts, std::vector col_ends, bool trim_ws, const size_t skip, const char* comment, const bool skip_empty_rows, const size_t n_max, const bool progress) : col_starts_(col_starts), col_ends_(col_ends), trim_ws_(trim_ws), filename_(filename) { std::error_code error; mmap_ = make_mmap_source(filename, error); if (error) { // We cannot actually portably compare error messages due to a bug in // libstdc++ (https://stackoverflow.com/a/54316671/2055486), so just print // the message on stderr return #ifndef VROOM_STANDALONE REprintf("mapping error: %s\n", error.message().c_str()); #else std::cerr << "mapping error: " << error.message() << '\n'; #endif return; } size_t file_size = mmap_.size(); size_t start = find_first_line( mmap_, skip, comment, skip_empty_rows, /* embedded_nl */ false, /* quote */ '\0'); // Check for windows newlines size_t first_nl; newline_type nl; std::tie(first_nl, nl) = find_next_newline( mmap_, start, comment, skip_empty_rows, /* embedded_nl */ false, /* quote */ '\0'); std::unique_ptr pb = nullptr; if (progress) { #ifndef VROOM_STANDALONE auto format = get_pb_format("file", filename); auto width = get_pb_width(format); pb = std::unique_ptr( new RProgress::RProgress(format, file_size, width)); pb->tick(start); #endif } bool n_max_set = n_max != static_cast(-1); if (n_max > 0) { newlines_.push_back(start - 1); } index_region( mmap_, newlines_, start, file_size - 1, 0, comment, skip_empty_rows, n_max, pb, file_size / 1000); if (!n_max_set) { if (nl == CRLF) { newlines_.push_back(file_size - 2); } else { newlines_.push_back(file_size - 1); } } if (progress) { #ifndef VROOM_STANDALONE pb->update(1); #endif } #ifdef VROOM_LOG auto log = spdlog::basic_logger_mt( "basic_logger", "logs/fixed_width_index.idx", true); log->set_level(spdlog::level::debug); for (auto& v : newlines_) { SPDLOG_LOGGER_DEBUG(log, "{}", v); } SPDLOG_LOGGER_DEBUG(log, "end of idx {0:x}", (size_t)&newlines_); spdlog::drop("basic_logger"); #endif } size_t num_rows() const override { return newlines_.size() - 1; } size_t num_columns() const override { return col_starts_.size(); } std::string get_delim() const override { /* TODO: FIXME */ return ""; } string get(size_t row, size_t col) const override { auto begin = mmap_.data() + (newlines_[row] + 1 + col_starts_[col]); auto line_end = mmap_.data() + (newlines_[row + 1]); if (line_end > begin && *(line_end - 1) == '\r') { --line_end; } const char* end; if (col_ends_[col] == NA_INTEGER) { end = mmap_.data() + newlines_[row + 1]; } else { end = mmap_.data() + (newlines_[row] + 1 + col_ends_[col]); } if (begin > line_end) { begin = line_end; } if (end > line_end) { end = line_end; } if (trim_ws_) { trim_whitespace(begin, end); } return {begin, end}; } class column_iterator : public base_iterator { std::shared_ptr idx_; size_t column_; size_t i_; public: column_iterator(std::shared_ptr idx, size_t column) : idx_(idx), column_(column), i_(0) {} void next() override { ++i_; } void advance(ptrdiff_t n) override { i_ += n; } bool equal_to(const base_iterator& it) const override { return i_ == static_cast(&it)->i_; } ptrdiff_t distance_to(const base_iterator& it) const override { return static_cast( static_cast(&it)->i_) - static_cast(i_); } string value() const override { return idx_->get(i_, column_); } column_iterator* clone() const override { return new column_iterator(*this); } string at(ptrdiff_t n) const override { return idx_->get(n, column_); } std::string filename() const override { return idx_->filename_; } size_t index() const override { return i_ / idx_->num_columns(); } size_t position() const override { return i_; } virtual ~column_iterator() = default; }; std::shared_ptr get_column(size_t column) const override { auto begin = new column_iterator(shared_from_this(), column); auto end = new column_iterator(shared_from_this(), column); end->advance(num_rows()); return std::make_shared(begin, end, column); } template size_t index_region( const T& source, std::vector& destination, size_t start, size_t end, size_t offset, const char* comment, const bool skip_empty_rows, size_t n_max, std::unique_ptr& pb, size_t update_size = -1) { size_t pos; newline_type first_nl, nl; std::tie(pos, first_nl) = find_next_newline( source, start, comment, skip_empty_rows, /* embededd_nl */ false, /* quote */ '\0'); size_t lines_read = 0; auto last_tick = start; while (pos < end) { ++lines_read; destination.push_back(offset + pos); if (lines_read >= n_max) { return lines_read; } if (pb) { auto tick_size = pos - last_tick; if (tick_size > update_size) { pb->tick(pos - last_tick); last_tick = pos; } } /* For this search we need to look only for either a CR or a LF, since a * CRLF could potentially be split between two buffers, and has been in * practice. */ std::tie(pos, nl) = find_next_newline( source, pos + 1, comment, skip_empty_rows, /* embedded_nl */ false, /* quote */ '\0', /* type */ first_nl); } if (pb) { pb->tick(end - last_tick); } return lines_read; } std::shared_ptr get_row(size_t) const override { // TODO: UNUSED return nullptr; } std::shared_ptr get_header() const override { // TODO: UNUSED return nullptr; } std::string filename() const { return filename_; } }; } // namespace vroom vroom/src/parallel.h0000644000176200001440000000422114132374245014154 0ustar liggesusers#pragma once #include #include #include #include #include // adapted from https://stackoverflow.com/a/49188371/2055486 /// @param[in] nb_elements : size of your for loop /// @param[in] functor(start, end) : /// your function processing a sub chunk of the for loop. /// "start" is the first index to process (included) until the index "end" /// (excluded) /// @code /// for(int i = start; i < end; ++i) /// computation(i); /// @endcode /// @param use_threads : enable / disable threads. /// /// static std::vector> parallel_for( size_t nb_elements, std::function functor, size_t nb_threads, bool use_threads = true, bool cleanup = true) { #if !defined __APPLE__ && defined _LIBCPP_VERSION nb_threads = 1; #endif if (nb_threads == 1) { use_threads = false; } size_t batch_size = nb_elements / nb_threads; size_t batch_remainder = nb_elements % nb_threads; auto my_threads = std::vector>(nb_threads); if (use_threads) { // Multithread execution for (size_t i = 0; i < (nb_threads - 1); ++i) { size_t start = i * batch_size; my_threads[i] = std::async(std::launch::async, functor, start, start + batch_size, i); } // Last batch includes the remainder size_t start = (nb_threads - 1) * batch_size; my_threads[nb_threads - 1] = std::async( std::launch::async, functor, start, start + batch_size + batch_remainder, nb_threads - 1); } else { // Single thread execution (for easy debugging) for (size_t i = 0; i < (nb_threads - 1); ++i) { size_t start = i * batch_size; functor(start, start + batch_size, i); } // Last batch includes the remainder size_t start = (nb_threads - 1) * batch_size; functor(start, start + batch_size + batch_remainder, nb_threads - 1); return std::vector>(); } // Wait for the other thread to finish their task if (use_threads && cleanup) { for (auto& t : my_threads) { t.get(); } } return my_threads; } vroom/src/index_collection.cc0000644000176200001440000002127514504662520016047 0ustar liggesusers#include #include "delimited_index_connection.h" #include "fixed_width_index.h" #include "fixed_width_index_connection.h" #include "index.h" #include "index_collection.h" #include #include #include "r_utils.h" using namespace vroom; // Class index_collection::column::iterator index_collection::full_iterator::full_iterator( std::shared_ptr idx, size_t column) : i_(0), idx_(std::move(idx)), column_(column), end_(idx_->indexes_.size() - 1) { auto n_inputs = idx_->indexes_.size(); auto n_rows_total = idx_->rows_; auto n_rows_current_input = idx_->indexes_[i_]->num_rows(); if (n_rows_current_input == 0 && n_inputs > 1 && n_rows_total > 0) { while(n_rows_current_input == 0) { ++i_; n_rows_current_input = idx_->indexes_[i_]->num_rows(); } } auto col = idx_->indexes_[i_]->get_column(column_); it_ = col->begin(); it_end_ = col->end(); it_start_ = col->begin(); } void index_collection::full_iterator::next() { ++it_; while (it_ == it_end_ && i_ < end_) { ++i_; it_ = idx_->indexes_[i_]->get_column(column_)->begin(); it_end_ = idx_->indexes_[i_]->get_column(column_)->end(); } } void index_collection::full_iterator::advance(ptrdiff_t n) { if (n == 0) { return; } if (n > 0) { while (n > 0) { auto diff = it_end_ - it_; if (n < diff) { it_ += n; return; } it_ += (diff - 1); n -= diff; next(); } return; } if (n < 0) { throw std::runtime_error("negative advance not supported"); } } ptrdiff_t index_collection::full_iterator::distance_to(const base_iterator& that) const { auto that_ = static_cast(&that); if (i_ == that_->i_) { ptrdiff_t res = that_->it_ - it_; return res; } ptrdiff_t count = 0; size_t i = i_; if (i_ < that_->i_) { count = it_end_ - it_; ++i; while (i < that_->i_) { count += idx_->indexes_[i]->num_rows(); ++i; } auto begin = idx_->indexes_[i]->get_column(column_)->begin(); count += that_->it_ - begin; return count; } count = it_start_ - it_; --i; while (i > that_->i_) { count -= idx_->indexes_[i]->num_rows(); --i; } auto end = idx_->indexes_[i]->get_column(column_)->end(); count += that_->it_ - end; return count; } string index_collection::full_iterator::value() const { return *it_; } index_collection::full_iterator* index_collection::full_iterator::clone() const { auto copy = new index_collection::full_iterator(*this); return copy; } string index_collection::full_iterator::at(ptrdiff_t n) const { return idx_->get(n, column_); } std::shared_ptr make_delimited_index( const cpp11::sexp& in, const char* delim, const char quote, const bool trim_ws, const bool escape_double, const bool escape_backslash, const bool has_header, const size_t skip, const size_t n_max, const char* comment, const bool skip_empty_rows, const std::shared_ptr& errors, const size_t num_threads, const bool progress) { auto standardise_one_path = cpp11::package("vroom")["standardise_one_path"]; auto x = standardise_one_path(in); bool is_connection = TYPEOF(x) != STRSXP; if (is_connection) { return std::make_shared( x, delim, quote, trim_ws, escape_double, escape_backslash, has_header, skip, n_max, comment, skip_empty_rows, errors, get_env("VROOM_CONNECTION_SIZE", 1 << 17), progress); } auto filename = cpp11::as_cpp(x); return std::make_shared( filename.c_str(), delim, quote, trim_ws, escape_double, escape_backslash, has_header, skip, n_max, comment, skip_empty_rows, errors, num_threads, progress); } void check_column_consistency( const std::shared_ptr& first, const std::shared_ptr& check, bool has_header, size_t i) { if (check->num_columns() != first->num_columns()) { std::stringstream ss; ss << "Files must all have " << first->num_columns() << " columns:\n" "* File " << i + 1 << " has " << check->num_columns() << " columns"; cpp11::stop("%s", ss.str().c_str()); } // If the files have a header ensure they are consistent with each other. if (has_header) { auto first_header = first->get_header()->begin(); auto check_header = check->get_header(); auto col = 0; for (auto header : *check_header) { if (!(header == *first_header)) { std::stringstream ss; ss << "Files must have consistent column names:\n" "* File 1 column " << col + 1 << " is: " << (*first_header).str() << "\n" "* File " << i + 1 << " column " << col + 1 << " is: " << header.str(); cpp11::stop("%s", ss.str().c_str()); } ++first_header; ++col; } } } // Index_collection index_collection::index_collection( const cpp11::list& in, const char* delim, const char quote, const bool trim_ws, const bool escape_double, const bool escape_backslash, const bool has_header, const size_t skip, const size_t n_max, const char* comment, const bool skip_empty_rows, const std::shared_ptr& errors, const size_t num_threads, const bool progress) : rows_(0), columns_(0) { std::shared_ptr first = make_delimited_index( in[0], delim, quote, trim_ws, escape_double, escape_backslash, has_header, skip, n_max, comment, skip_empty_rows, errors, num_threads, progress); indexes_.push_back(first); columns_ = first->num_columns(); rows_ = first->num_rows(); for (int i = 1; i < in.size(); ++i) { std::shared_ptr idx = make_delimited_index( in[i], delim, quote, trim_ws, escape_double, escape_backslash, has_header, skip, n_max, comment, skip_empty_rows, errors, num_threads, progress); check_column_consistency(first, idx, has_header, i); rows_ += idx->num_rows(); indexes_.emplace_back(std::move(idx)); } } std::shared_ptr make_fixed_width_index( const cpp11::sexp& in, const std::vector& col_starts, const std::vector& col_ends, const bool trim_ws, const size_t skip, const char* comment, const bool skip_empty_rows, const size_t n_max, const bool progress) { auto standardise_one_path = cpp11::package("vroom")["standardise_one_path"]; auto x = standardise_one_path(in); bool is_connection = TYPEOF(x) != STRSXP; if (is_connection) { return std::make_shared( x, col_starts, col_ends, trim_ws, skip, comment, skip_empty_rows, n_max, progress, get_env("VROOM_CONNECTION_SIZE", 1 << 17)); } else { auto filename = cpp11::as_cpp(x); return std::make_shared( filename.c_str(), col_starts, col_ends, trim_ws, skip, comment, skip_empty_rows, n_max, progress); } } index_collection::index_collection( const cpp11::list& in, const std::vector& col_starts, const std::vector& col_ends, const bool trim_ws, const size_t skip, const char* comment, const bool skip_empty_rows, const size_t n_max, const bool progress) : rows_(0), columns_(0) { auto first = make_fixed_width_index( in[0], col_starts, col_ends, trim_ws, skip, comment, skip_empty_rows, n_max, progress); columns_ = first->num_columns(); rows_ = first->num_rows(); indexes_.push_back(first); for (int i = 1; i < in.size(); ++i) { auto idx = make_fixed_width_index( in[i], col_starts, col_ends, trim_ws, skip, comment, skip_empty_rows, n_max, progress); check_column_consistency(first, idx, false, i); rows_ += idx->num_rows(); indexes_.emplace_back(std::move(idx)); } } string index_collection::get(size_t row, size_t column) const { for (const auto& idx : indexes_) { if (row < idx->num_rows()) { return idx->get(row, column); } row -= idx->num_rows(); } /* should never get here */ return std::string(""); } vroom/src/collectors.h0000644000176200001440000001270614226075356014545 0ustar liggesusers#pragma once #include #include #include #include class collector { const cpp11::list data_; const SEXP name_; const column_type type_; const size_t altrep_; private: column_type derive_type(const std::string& t) { if (t == "collector_skip") { return column_type::Skip; } if (t == "collector_double") { return column_type::Dbl; } if (t == "collector_integer") { return column_type::Int; } if (t == "collector_big_integer") { return column_type::BigInt; } if (t == "collector_number") { return column_type::Num; } if (t == "collector_logical") { return column_type::Lgl; } if (t == "collector_factor") { return column_type::Fct; } if (t == "collector_date") { return column_type::Date; } if (t == "collector_datetime") { return column_type::Dttm; } if (t == "collector_time") { return column_type::Time; } return column_type::Chr; } public: collector(cpp11::list data, SEXP name, size_t altrep) : data_(data), name_(name), type_(derive_type(cpp11::strings(data_.attr("class"))[0])), altrep_(altrep) {} column_type type() const { return type_; } SEXP name() const { return name_; } SEXP operator[](const char* nme) { return data_[nme]; } bool use_altrep() { switch (type()) { case column_type::Skip: return false; case column_type::Dbl: return altrep_ & column_type::Dbl; case column_type::Int: return altrep_ & column_type::Int; case column_type::BigInt: return altrep_ & column_type::BigInt; case column_type::Num: return altrep_ & column_type::Num; case column_type::Fct: return altrep_ & column_type::Fct; case column_type::Date: return altrep_ & column_type::Date; case column_type::Dttm: return altrep_ & column_type::Dttm; case column_type::Time: return altrep_ & column_type::Time; case column_type::Chr: return altrep_ & column_type::Chr; default: return false; } } }; class collectors { cpp11::list spec_; cpp11::list collectors_; size_t altrep_; public: collectors(cpp11::list col_types, size_t altrep) : spec_(col_types), collectors_(col_types["cols"]), altrep_(altrep) {} collector operator[](int i) { return { collectors_[i], cpp11::strings(collectors_.attr("names"))[i], altrep_}; } cpp11::list spec() { return spec_; } }; inline cpp11::strings read_column_names( std::shared_ptr idx, std::shared_ptr locale_info) { cpp11::writable::strings nms(idx->num_columns()); auto col = 0; auto header = idx->get_header(); for (const auto& str : *header) { nms[col++] = locale_info->encoder_.makeSEXP(str.begin(), str.end(), false); } return nms; } std::string guess_type__( cpp11::writable::strings& input, const cpp11::strings& na, LocaleInfo* locale, bool guess_integer); inline collectors resolve_collectors( cpp11::sexp col_names, cpp11::sexp col_types, cpp11::sexp col_select, cpp11::sexp name_repair, std::shared_ptr idx, cpp11::strings na, std::shared_ptr locale_info, size_t guess_max, size_t altrep) { R_xlen_t num_cols = idx->num_columns(); auto num_rows = idx->num_rows(); auto vroom = cpp11::package("vroom"); cpp11::writable::strings col_nms; auto make_names = vroom["make_names"]; if (num_rows == 0 && num_cols == 0) { if (TYPEOF(col_names) == STRSXP) { col_nms = static_cast(col_names); } else { if (TYPEOF(col_types) == VECSXP) { col_nms = static_cast( make_names(R_NilValue, Rf_xlength(VECTOR_ELT(col_types, 0)))); } } } if (TYPEOF(col_names) == STRSXP) { col_nms = static_cast(col_names); } else if (TYPEOF(col_names) == LGLSXP && cpp11::logicals(col_names)[0]) { col_nms = read_column_names(idx, locale_info); } auto col_types_standardise = vroom["col_types_standardise"]; cpp11::list col_types_std(col_types_standardise( col_types, num_cols, col_nms, col_select, name_repair)); R_xlen_t guess_num = std::min(num_rows, guess_max); auto guess_step = guess_num > 0 ? num_rows / guess_num : 0; cpp11::writable::list my_collectors(col_types_std["cols"]); for (R_xlen_t col = 0; col < num_cols; ++col) { cpp11::writable::list my_collector(my_collectors[col]); std::string my_col_type = cpp11::strings(my_collector.attr("class"))[0]; if (my_col_type == "collector_guess") { cpp11::writable::strings col_vals(guess_num); for (R_xlen_t j = 0; j < guess_num - 1; ++j) { size_t row = j * guess_step; auto str = idx->get(row, col); SET_STRING_ELT( col_vals, j, locale_info->encoder_.makeSEXP(str.begin(), str.end(), true)); } // Always include the last row in the guess if (num_rows > 0 && guess_num - 1 >= 0) { size_t row = num_rows - 1; auto str = idx->get(row, col); col_vals[guess_num - 1] = locale_info->encoder_.makeSEXP(str.begin(), str.end(), true); } auto type = guess_type__(col_vals, na, locale_info.get(), false); auto fun_name = std::string("col_") + type; auto col_type = vroom[fun_name.c_str()]; my_collectors[col] = col_type(); } } return {col_types_std, altrep}; } vroom/src/cpp11.cpp0000644000176200001440000003370514510121462013637 0ustar liggesusers// Generated by cpp11: do not edit by hand // clang-format off #include "vroom_types.h" #include "cpp11/declarations.hpp" #include // altrep.cc void force_materialization(SEXP x); extern "C" SEXP _vroom_force_materialization(SEXP x) { BEGIN_CPP11 force_materialization(cpp11::as_cpp>(x)); return R_NilValue; END_CPP11 } // altrep.cc SEXP vroom_materialize(SEXP x, bool replace); extern "C" SEXP _vroom_vroom_materialize(SEXP x, SEXP replace) { BEGIN_CPP11 return cpp11::as_sexp(vroom_materialize(cpp11::as_cpp>(x), cpp11::as_cpp>(replace))); END_CPP11 } // altrep.cc SEXP vroom_convert(SEXP x); extern "C" SEXP _vroom_vroom_convert(SEXP x) { BEGIN_CPP11 return cpp11::as_sexp(vroom_convert(cpp11::as_cpp>(x))); END_CPP11 } // altrep.cc std::string vroom_str_(const cpp11::sexp& x); extern "C" SEXP _vroom_vroom_str_(SEXP x) { BEGIN_CPP11 return cpp11::as_sexp(vroom_str_(cpp11::as_cpp>(x))); END_CPP11 } // gen.cc cpp11::strings gen_character_(int n, int min, int max, std::string values, uint32_t seed, uint32_t seed2); extern "C" SEXP _vroom_gen_character_(SEXP n, SEXP min, SEXP max, SEXP values, SEXP seed, SEXP seed2) { BEGIN_CPP11 return cpp11::as_sexp(gen_character_(cpp11::as_cpp>(n), cpp11::as_cpp>(min), cpp11::as_cpp>(max), cpp11::as_cpp>(values), cpp11::as_cpp>(seed), cpp11::as_cpp>(seed2))); END_CPP11 } // guess_type.cc std::string guess_type_(cpp11::writable::strings input, const cpp11::strings& na, const cpp11::list& locale, bool guess_integer); extern "C" SEXP _vroom_guess_type_(SEXP input, SEXP na, SEXP locale, SEXP guess_integer) { BEGIN_CPP11 return cpp11::as_sexp(guess_type_(cpp11::as_cpp>(input), cpp11::as_cpp>(na), cpp11::as_cpp>(locale), cpp11::as_cpp>(guess_integer))); END_CPP11 } // iconv_file.cc size_t convert_connection(SEXP in_con, SEXP out_con, const std::string& from, const std::string& to); extern "C" SEXP _vroom_convert_connection(SEXP in_con, SEXP out_con, SEXP from, SEXP to) { BEGIN_CPP11 return cpp11::as_sexp(convert_connection(cpp11::as_cpp>(in_con), cpp11::as_cpp>(out_con), cpp11::as_cpp>(from), cpp11::as_cpp>(to))); END_CPP11 } // vroom.cc SEXP vroom_(const cpp11::list& inputs, SEXP delim, const char quote, bool trim_ws, bool escape_double, bool escape_backslash, const char* comment, const bool skip_empty_rows, size_t skip, ptrdiff_t n_max, bool progress, const cpp11::sexp& col_names, cpp11::sexp col_types, cpp11::sexp col_select, cpp11::sexp name_repair, SEXP id, const cpp11::strings& na, const cpp11::list& locale, ptrdiff_t guess_max, size_t num_threads, size_t altrep); extern "C" SEXP _vroom_vroom_(SEXP inputs, SEXP delim, SEXP quote, SEXP trim_ws, SEXP escape_double, SEXP escape_backslash, SEXP comment, SEXP skip_empty_rows, SEXP skip, SEXP n_max, SEXP progress, SEXP col_names, SEXP col_types, SEXP col_select, SEXP name_repair, SEXP id, SEXP na, SEXP locale, SEXP guess_max, SEXP num_threads, SEXP altrep) { BEGIN_CPP11 return cpp11::as_sexp(vroom_(cpp11::as_cpp>(inputs), cpp11::as_cpp>(delim), cpp11::as_cpp>(quote), cpp11::as_cpp>(trim_ws), cpp11::as_cpp>(escape_double), cpp11::as_cpp>(escape_backslash), cpp11::as_cpp>(comment), cpp11::as_cpp>(skip_empty_rows), cpp11::as_cpp>(skip), cpp11::as_cpp>(n_max), cpp11::as_cpp>(progress), cpp11::as_cpp>(col_names), cpp11::as_cpp>(col_types), cpp11::as_cpp>(col_select), cpp11::as_cpp>(name_repair), cpp11::as_cpp>(id), cpp11::as_cpp>(na), cpp11::as_cpp>(locale), cpp11::as_cpp>(guess_max), cpp11::as_cpp>(num_threads), cpp11::as_cpp>(altrep))); END_CPP11 } // vroom.cc bool has_trailing_newline(const cpp11::strings& filename); extern "C" SEXP _vroom_has_trailing_newline(SEXP filename) { BEGIN_CPP11 return cpp11::as_sexp(has_trailing_newline(cpp11::as_cpp>(filename))); END_CPP11 } // vroom.cc SEXP vroom_rle(const cpp11::integers& input); extern "C" SEXP _vroom_vroom_rle(SEXP input) { BEGIN_CPP11 return cpp11::as_sexp(vroom_rle(cpp11::as_cpp>(input))); END_CPP11 } // vroom_dttm.cc cpp11::writable::doubles utctime_(const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& min, const cpp11::integers& sec, const cpp11::doubles& psec); extern "C" SEXP _vroom_utctime_(SEXP year, SEXP month, SEXP day, SEXP hour, SEXP min, SEXP sec, SEXP psec) { BEGIN_CPP11 return cpp11::as_sexp(utctime_(cpp11::as_cpp>(year), cpp11::as_cpp>(month), cpp11::as_cpp>(day), cpp11::as_cpp>(hour), cpp11::as_cpp>(min), cpp11::as_cpp>(sec), cpp11::as_cpp>(psec))); END_CPP11 } // vroom_errors.cpp cpp11::data_frame vroom_errors_(cpp11::external_pointer> errors); extern "C" SEXP _vroom_vroom_errors_(SEXP errors) { BEGIN_CPP11 return cpp11::as_sexp(vroom_errors_(cpp11::as_cpp>>>(errors))); END_CPP11 } // vroom_fwf.cc cpp11::list vroom_fwf_(const cpp11::list& inputs, const std::vector& col_starts, const std::vector& col_ends, bool trim_ws, cpp11::sexp col_names, cpp11::sexp col_types, cpp11::sexp col_select, cpp11::sexp name_repair, size_t skip, const char* comment, bool skip_empty_rows, ptrdiff_t n_max, SEXP id, const cpp11::strings& na, const cpp11::list& locale, ptrdiff_t guess_max, size_t num_threads, size_t altrep, bool progress); extern "C" SEXP _vroom_vroom_fwf_(SEXP inputs, SEXP col_starts, SEXP col_ends, SEXP trim_ws, SEXP col_names, SEXP col_types, SEXP col_select, SEXP name_repair, SEXP skip, SEXP comment, SEXP skip_empty_rows, SEXP n_max, SEXP id, SEXP na, SEXP locale, SEXP guess_max, SEXP num_threads, SEXP altrep, SEXP progress) { BEGIN_CPP11 return cpp11::as_sexp(vroom_fwf_(cpp11::as_cpp>(inputs), cpp11::as_cpp&>>(col_starts), cpp11::as_cpp&>>(col_ends), cpp11::as_cpp>(trim_ws), cpp11::as_cpp>(col_names), cpp11::as_cpp>(col_types), cpp11::as_cpp>(col_select), cpp11::as_cpp>(name_repair), cpp11::as_cpp>(skip), cpp11::as_cpp>(comment), cpp11::as_cpp>(skip_empty_rows), cpp11::as_cpp>(n_max), cpp11::as_cpp>(id), cpp11::as_cpp>(na), cpp11::as_cpp>(locale), cpp11::as_cpp>(guess_max), cpp11::as_cpp>(num_threads), cpp11::as_cpp>(altrep), cpp11::as_cpp>(progress))); END_CPP11 } // vroom_fwf.cc cpp11::list whitespace_columns_(const std::string& filename, size_t skip, ptrdiff_t n, const std::string& comment); extern "C" SEXP _vroom_whitespace_columns_(SEXP filename, SEXP skip, SEXP n, SEXP comment) { BEGIN_CPP11 return cpp11::as_sexp(whitespace_columns_(cpp11::as_cpp>(filename), cpp11::as_cpp>(skip), cpp11::as_cpp>(n), cpp11::as_cpp>(comment))); END_CPP11 } // vroom_write.cc void vroom_write_(const cpp11::list& input, const std::string& filename, const char delim, const std::string& eol, const char* na_str, bool col_names, bool append, size_t options, size_t num_threads, bool progress, size_t buf_lines); extern "C" SEXP _vroom_vroom_write_(SEXP input, SEXP filename, SEXP delim, SEXP eol, SEXP na_str, SEXP col_names, SEXP append, SEXP options, SEXP num_threads, SEXP progress, SEXP buf_lines) { BEGIN_CPP11 vroom_write_(cpp11::as_cpp>(input), cpp11::as_cpp>(filename), cpp11::as_cpp>(delim), cpp11::as_cpp>(eol), cpp11::as_cpp>(na_str), cpp11::as_cpp>(col_names), cpp11::as_cpp>(append), cpp11::as_cpp>(options), cpp11::as_cpp>(num_threads), cpp11::as_cpp>(progress), cpp11::as_cpp>(buf_lines)); return R_NilValue; END_CPP11 } // vroom_write.cc void vroom_write_connection_(const cpp11::list& input, const cpp11::sexp& con, const char delim, const std::string& eol, const char* na_str, bool col_names, size_t options, size_t num_threads, bool progress, size_t buf_lines, bool is_stdout, bool append); extern "C" SEXP _vroom_vroom_write_connection_(SEXP input, SEXP con, SEXP delim, SEXP eol, SEXP na_str, SEXP col_names, SEXP options, SEXP num_threads, SEXP progress, SEXP buf_lines, SEXP is_stdout, SEXP append) { BEGIN_CPP11 vroom_write_connection_(cpp11::as_cpp>(input), cpp11::as_cpp>(con), cpp11::as_cpp>(delim), cpp11::as_cpp>(eol), cpp11::as_cpp>(na_str), cpp11::as_cpp>(col_names), cpp11::as_cpp>(options), cpp11::as_cpp>(num_threads), cpp11::as_cpp>(progress), cpp11::as_cpp>(buf_lines), cpp11::as_cpp>(is_stdout), cpp11::as_cpp>(append)); return R_NilValue; END_CPP11 } // vroom_write.cc cpp11::strings vroom_format_(const cpp11::list& input, const char delim, const std::string& eol, const char* na_str, bool col_names, bool append, size_t options, size_t num_threads, bool progress, size_t buf_lines); extern "C" SEXP _vroom_vroom_format_(SEXP input, SEXP delim, SEXP eol, SEXP na_str, SEXP col_names, SEXP append, SEXP options, SEXP num_threads, SEXP progress, SEXP buf_lines) { BEGIN_CPP11 return cpp11::as_sexp(vroom_format_(cpp11::as_cpp>(input), cpp11::as_cpp>(delim), cpp11::as_cpp>(eol), cpp11::as_cpp>(na_str), cpp11::as_cpp>(col_names), cpp11::as_cpp>(append), cpp11::as_cpp>(options), cpp11::as_cpp>(num_threads), cpp11::as_cpp>(progress), cpp11::as_cpp>(buf_lines))); END_CPP11 } extern "C" { static const R_CallMethodDef CallEntries[] = { {"_vroom_convert_connection", (DL_FUNC) &_vroom_convert_connection, 4}, {"_vroom_force_materialization", (DL_FUNC) &_vroom_force_materialization, 1}, {"_vroom_gen_character_", (DL_FUNC) &_vroom_gen_character_, 6}, {"_vroom_guess_type_", (DL_FUNC) &_vroom_guess_type_, 4}, {"_vroom_has_trailing_newline", (DL_FUNC) &_vroom_has_trailing_newline, 1}, {"_vroom_utctime_", (DL_FUNC) &_vroom_utctime_, 7}, {"_vroom_vroom_", (DL_FUNC) &_vroom_vroom_, 21}, {"_vroom_vroom_convert", (DL_FUNC) &_vroom_vroom_convert, 1}, {"_vroom_vroom_errors_", (DL_FUNC) &_vroom_vroom_errors_, 1}, {"_vroom_vroom_format_", (DL_FUNC) &_vroom_vroom_format_, 10}, {"_vroom_vroom_fwf_", (DL_FUNC) &_vroom_vroom_fwf_, 19}, {"_vroom_vroom_materialize", (DL_FUNC) &_vroom_vroom_materialize, 2}, {"_vroom_vroom_rle", (DL_FUNC) &_vroom_vroom_rle, 1}, {"_vroom_vroom_str_", (DL_FUNC) &_vroom_vroom_str_, 1}, {"_vroom_vroom_write_", (DL_FUNC) &_vroom_vroom_write_, 11}, {"_vroom_vroom_write_connection_", (DL_FUNC) &_vroom_vroom_write_connection_, 12}, {"_vroom_whitespace_columns_", (DL_FUNC) &_vroom_whitespace_columns_, 4}, {NULL, NULL, 0} }; } void init_vroom_big_int(DllInfo* dll); void init_vroom_chr(DllInfo* dll); void init_vroom_date(DllInfo* dll); void init_vroom_dbl(DllInfo* dll); void init_vroom_dttm(DllInfo* dll); void init_vroom_fct(DllInfo* dll); void init_vroom_int(DllInfo* dll); void init_vroom_num(DllInfo* dll); void init_vroom_rle(DllInfo* dll); void init_vroom_time(DllInfo* dll); extern "C" attribute_visible void R_init_vroom(DllInfo* dll){ R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); init_vroom_big_int(dll); init_vroom_chr(dll); init_vroom_date(dll); init_vroom_dbl(dll); init_vroom_dttm(dll); init_vroom_fct(dll); init_vroom_int(dll); init_vroom_num(dll); init_vroom_rle(dll); init_vroom_time(dll); R_forceSymbols(dll, TRUE); } vroom/src/tzfile.h0000644000176200001440000001303413445253572013664 0ustar liggesusers#ifndef TZFILE_H #define TZFILE_H /* ** This file is in the public domain, so clarified as of ** 1996-06-05 by Arthur David Olson. */ /* ** This header is for use ONLY with the time conversion code. ** There is no guarantee that it will remain unchanged, ** or that it will remain at all. ** Do NOT copy it to any system include directory. ** Thank you! */ /* ** Information about time zone files. */ #ifndef TZDIR #define TZDIR "/usr/local/etc/zoneinfo" /* Time zone object file directory */ #endif /* !defined TZDIR */ #ifndef TZDEFAULT #define TZDEFAULT "UTC" // needs to be a valid timezone, PR#16503 #endif /* !defined TZDEFAULT */ /* We don't ship posixrules, which is usually a link to a USA timezeone. So choose one instead. */ #ifndef TZDEFRULES #define TZDEFRULES "America/New_York" #endif /* !defined TZDEFRULES */ /* ** Each file begins with. . . */ #define TZ_MAGIC "TZif" struct tzhead { char tzh_magic[4]; /* TZ_MAGIC */ char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */ char tzh_reserved[15]; /* reserved--must be zero */ char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ char tzh_leapcnt[4]; /* coded number of leap seconds */ char tzh_timecnt[4]; /* coded number of transition times */ char tzh_typecnt[4]; /* coded number of local time types */ char tzh_charcnt[4]; /* coded number of abbr. chars */ }; /* ** . . .followed by. . . ** ** tzh_timecnt (char [4])s coded transition times a la time(2) ** tzh_timecnt (unsigned char)s types of local time starting at above ** tzh_typecnt repetitions of ** one (char [4]) coded UT offset in seconds ** one (unsigned char) used to set tm_isdst ** one (unsigned char) that's an abbreviation list index ** tzh_charcnt (char)s '\0'-terminated zone abbreviations ** tzh_leapcnt repetitions of ** one (char [4]) coded leap second transition times ** one (char [4]) total correction after above ** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition ** time is standard time, if FALSE, ** transition time is wall clock time ** if absent, transition times are ** assumed to be wall clock time ** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition ** time is UT, if FALSE, ** transition time is local time ** if absent, transition times are ** assumed to be local time */ /* ** If tzh_version is '2' or greater, the above is followed by a second instance ** of tzhead and a second instance of the data in which each coded transition ** time uses 8 rather than 4 chars, ** then a POSIX-TZ-environment-variable-style string for use in handling ** instants after the last transition time stored in the file ** (with nothing between the newlines if there is no POSIX representation for ** such instants). ** ** If tz_version is '3' or greater, the above is extended as follows. ** First, the POSIX TZ string's hour offset may range from -167 ** through 167 as compared to the POSIX-required 0 through 24. ** Second, its DST start time may be January 1 at 00:00 and its stop ** time December 31 at 24:00 plus the difference between DST and ** standard time, indicating DST all year. */ /* ** In the current implementation, "tzset()" refuses to deal with files that ** exceed any of the limits below. */ #ifndef TZ_MAX_TIMES #define TZ_MAX_TIMES 1200 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES #ifndef NOSOLAR #define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ #endif /* !defined NOSOLAR */ #ifdef NOSOLAR /* ** Must be at least 14 for Europe/Riga as of Jan 12 1995, ** as noted by Earl Chew. */ #define TZ_MAX_TYPES 20 /* Maximum number of local time types */ #endif /* !defined NOSOLAR */ #endif /* !defined TZ_MAX_TYPES */ // increased from 50, http://mm.icann.org/pipermail/tz/2015-August/022623.html #ifndef TZ_MAX_CHARS #define TZ_MAX_CHARS 100 /* Maximum number of abbreviation characters */ /* (limited by what unsigned chars can hold) */ #endif /* !defined TZ_MAX_CHARS */ #ifndef TZ_MAX_LEAPS #define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ #endif /* !defined TZ_MAX_LEAPS */ #define SECSPERMIN 60 #define MINSPERHOUR 60 #define HOURSPERDAY 24 #define DAYSPERWEEK 7 #define DAYSPERNYEAR 365 #define DAYSPERLYEAR 366 #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) #define SECSPERDAY ((int_fast32_t)SECSPERHOUR * HOURSPERDAY) #define MONSPERYEAR 12 #define TM_SUNDAY 0 #define TM_MONDAY 1 #define TM_TUESDAY 2 #define TM_WEDNESDAY 3 #define TM_THURSDAY 4 #define TM_FRIDAY 5 #define TM_SATURDAY 6 #define TM_JANUARY 0 #define TM_FEBRUARY 1 #define TM_MARCH 2 #define TM_APRIL 3 #define TM_MAY 4 #define TM_JUNE 5 #define TM_JULY 6 #define TM_AUGUST 7 #define TM_SEPTEMBER 8 #define TM_OCTOBER 9 #define TM_NOVEMBER 10 #define TM_DECEMBER 11 #define TM_YEAR_BASE 1900 #define EPOCH_YEAR 1970 #define EPOCH_WDAY TM_THURSDAY #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) /* ** Since everything in isleap is modulo 400 (or a factor of 400), we know that ** isleap(y) == isleap(y % 400) ** and so ** isleap(a + b) == isleap((a + b) % 400) ** or ** isleap(a + b) == isleap(a % 400 + b % 400) ** This is true even if % means modulo rather than Fortran remainder ** (which is allowed by C89 but not C99). ** We use this to avoid addition overflow problems. */ #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) #endif /* !defined TZFILE_H */ vroom/src/altrep.h0000644000176200001440000000144614132374245013655 0ustar liggesusers#include #include #if R_VERSION >= R_Version(3, 5, 0) #define HAS_ALTREP #endif #ifdef HAS_ALTREP #if R_VERSION < R_Version(3, 6, 0) // workaround because R's not so conveniently uses `class` // as a variable name, and C++ is not happy about that // // SEXP R_new_altrep(R_altrep_class_t class, SEXP data1, SEXP data2); // // clang-format off #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wkeyword-macro" #define class klass # pragma clang diagnostic pop #else #define class klass #endif // clang-format on // Because functions declared in have C linkage extern "C" { #include } // undo the workaround #undef class #else extern "C" { #include } #endif #endif vroom/src/Iconv.cpp0000644000176200001440000000463014132374245013775 0ustar liggesusers#include "Iconv.h" #include #include Iconv::Iconv(const std::string& from, const std::string& to) { if (from == "UTF-8") { cd_ = nullptr; } else { cd_ = Riconv_open(to.c_str(), from.c_str()); if (cd_ == (void*)-1) { if (errno == EINVAL) { cpp11::stop("Can't convert from %s to %s", from.c_str(), to.c_str()); } else { cpp11::stop("Iconv initialisation failed"); } } // Allocate space in buffer buffer_.resize(1024); } } Iconv::~Iconv() { if (cd_ != nullptr) { Riconv_close(cd_); cd_ = nullptr; } } size_t Iconv::convert(const char* start, const char* end) { size_t n = end - start; // Ensure buffer is big enough: one input byte can never generate // more than 4 output bytes size_t max_size = n * 4; if (buffer_.size() < max_size) buffer_.resize(max_size); char* outbuf = &buffer_[0]; size_t inbytesleft = n, outbytesleft = max_size; size_t res = Riconv(cd_, &start, &inbytesleft, &outbuf, &outbytesleft); if (res == (size_t)-1) { switch (errno) { case EILSEQ: cpp11::stop("Invalid multibyte sequence"); case EINVAL: cpp11::stop("Incomplete multibyte sequence"); case E2BIG: cpp11::stop("Iconv buffer too small"); default: cpp11::stop("Iconv failed to convert for unknown reason"); } } return max_size - outbytesleft; } int my_strnlen(const char* s, int maxlen) { for (int n = 0; n < maxlen; ++n) { if (s[n] == '\0') return n; } return maxlen; } #if defined(__sun) #define readr_strnlen my_strnlen #else #define readr_strnlen strnlen #endif // To be safe, we need to check for nulls - this also needs to emit // a warning, but this behaviour is better than crashing SEXP safeMakeChar(const char* start, size_t n, bool hasNull) { size_t m = hasNull ? readr_strnlen(start, n) : n; if (m > INT_MAX) { Rf_error("R character strings are limited to 2^31-1 bytes"); } return Rf_mkCharLenCE(start, m, CE_UTF8); } SEXP Iconv::makeSEXP(const char* start, const char* end, bool hasNull) { if (cd_ == nullptr) return safeMakeChar(start, end - start, hasNull); int n = convert(start, end); return safeMakeChar(&buffer_[0], n, hasNull); } std::string Iconv::makeString(const char* start, const char* end) { if (cd_ == nullptr) return std::string(start, end); int n = convert(start, end); return std::string(&buffer_[0], n); } vroom/src/vroom_rle.cc0000644000176200001440000000030014132374245014514 0ustar liggesusers#include "vroom_rle.h" #ifdef HAS_ALTREP R_altrep_class_t vroom_rle::class_t; void init_vroom_rle(DllInfo* dll) { vroom_rle::Init(dll); } #else void init_vroom_rle(DllInfo* dll) {} #endif vroom/src/vroom_dttm.cc0000644000176200001440000000522614132374245014716 0ustar liggesusers#include "vroom_dttm.h" double parse_dttm( const char* begin, const char* end, DateTimeParser& parser, const std::string& format) { if (format == "%s") { double out; bool ok = parseDouble('.', begin, end, out); if (!ok) { return NA_REAL; } return out; } parser.setDate(begin, end); bool res = (format == "") ? parser.parseISO8601() : parser.parse(format); if (res) { DateTime dt = parser.makeDateTime(); if (dt.validDateTime()) { return dt.datetime(); } } return NA_REAL; } cpp11::doubles read_dttm(vroom_vec_info* info) { R_xlen_t n = info->column->size(); cpp11::writable::doubles out(n); auto err_msg = info->format.size() == 0 ? std::string("date in ISO8601") : std::string("date like ") + info->format; try { parallel_for( n, [&](size_t start, size_t end, size_t) { R_xlen_t i = start; DateTimeParser parser(info->locale.get()); auto col = info->column->slice(start, end); for (auto b = col->begin(), e = col->end(); b != e; ++b) { out[i++] = parse_value( b, col, [&](const char* begin, const char* end) -> double { return parse_dttm(begin, end, parser, info->format); }, info->errors, err_msg.c_str(), *info->na); } }, info->num_threads, true); } catch (const std::runtime_error& e) { Rf_errorcall(R_NilValue, "%s", e.what()); } info->errors->warn_for_errors(); out.attr("class") = {"POSIXct", "POSIXt"}; out.attr("tzone") = info->locale->tz_; return out; } #ifdef HAS_ALTREP R_altrep_class_t vroom_dttm::class_t; void init_vroom_dttm(DllInfo* dll) { vroom_dttm::Init(dll); } #else void init_vroom_dttm(DllInfo* dll) {} #endif [[cpp11::register]] cpp11::writable::doubles utctime_( const cpp11::integers& year, const cpp11::integers& month, const cpp11::integers& day, const cpp11::integers& hour, const cpp11::integers& min, const cpp11::integers& sec, const cpp11::doubles& psec) { int n = year.size(); if (month.size() != n || day.size() != n || hour.size() != n || min.size() != n || sec.size() != n || psec.size() != n) { cpp11::stop("All inputs must be same length"); } cpp11::writable::doubles out(n); for (int i = 0; i < n; ++i) { DateTime dt( year[i], month[i], day[i], hour[i], min[i], sec[i], psec[i], "UTC"); out[i] = dt.datetime(); } out.attr("class") = {"POSIXct", "POSIXt"}; out.attr("tzone") = "UTC"; return out; } vroom/vignettes/0000755000176200001440000000000014533652003013425 5ustar liggesusersvroom/vignettes/vroom.Rmd0000644000176200001440000002750514510121442015235 0ustar liggesusers--- title: "Get started with vroom" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Get started with vroom} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_knit$set(root.dir = tempdir()) knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) options(tibble.print_min = 3) ``` The vroom package contains one main function `vroom()` which is used to read all types of delimited files. A delimited file is any file in which the data is separated (delimited) by one or more characters. The most common type of delimited files are CSV (Comma Separated Values) or TSV (Tab Separated Values) files, typically these files have a `.csv` and `.tsv` suffix respectively. ```{r} library(vroom) ``` This vignette covers the following topics: - The basics of reading files, including - single files - multiple files - compressed files - remote files - Skipping particular columns. - Specifying column types, for additional safety and when the automatic guessing fails. - Writing regular and compressed files ## Reading files To read a CSV, or other type of delimited file with vroom pass the file to `vroom()`. The delimiter will be automatically guessed if it is a common delimiter; e.g. ("," "\t" " " "|" ":" ";"). If the guessing fails or you are using a less common delimiter specify it with the `delim` parameter. (e.g. `delim = ","`). We have included an example CSV file in the vroom package for use in examples and tests. Access it with `vroom_example("mtcars.csv")` ```{r} # See where the example file is stored on your machine file <- vroom_example("mtcars.csv") file # Read the file, by default vroom will guess the delimiter automatically. vroom(file) # You can also specify it explicitly, which is (slightly) faster, and safer if # you know how the file is delimited. vroom(file, delim = ",") ``` ## Reading multiple files If you are reading a set of files which all have the same columns (as in, names and types), you can pass the filenames directly to `vroom()` and it will combine them into one result. vroom's example datasets include several files named like `mtcars-i.csv`. These files contain subsets of the `mtcars` data, for cars with different numbers of cylinders. First, we get a character vector of these filepaths. ```{r} ve <- grep("mtcars-[0-9].csv", vroom_examples(), value = TRUE) files <- sapply(ve, vroom_example) files ``` Now we can efficiently read them into one table by passing the filenames directly to vroom. ```{r} vroom(files) ``` Often the filename or directory where the files are stored contains information. The `id` parameter can be used to add an extra column to the result with the full path to each file. (in this case we name the column `path`). ```{r} vroom(files, id = "path") ``` ## Reading compressed files vroom supports reading zip, gz, bz2 and xz compressed files automatically, just pass the filename of the compressed file to vroom. ```{r} file <- vroom_example("mtcars.csv.gz") vroom(file) ``` `vroom()` decompresses, indexes and writes the decompressed data to a file in the temp directory in a single stream. The temporary file is used to lazily look up the values and will be automatically cleaned up when all values in the object have been fully read, the object is removed, or the R session ends. ### Reading individual files from a multi-file zip archive If you are reading a zip file that contains multiple files with the same format, you can read a subset of the files at once like so: ```{r} zip_file <- vroom_example("mtcars-multi-cyl.zip") filenames <- unzip(zip_file, list = TRUE)$Name filenames # imagine we only want to read 2 of the 3 files vroom(purrr::map(filenames[c(1, 3)], ~ unz(zip_file, .x))) ``` ## Reading remote files vroom can read files directly from the internet as well by passing the URL of the file to vroom. ```{r, eval = as.logical(Sys.getenv("NOT_CRAN", "false"))} file <- "https://raw.githubusercontent.com/tidyverse/vroom/main/inst/extdata/mtcars.csv" vroom(file) ``` It can even read gzipped files from the internet (although not the other compressed formats). ```{r, eval = as.logical(Sys.getenv("NOT_CRAN", "false"))} file <- "https://raw.githubusercontent.com/tidyverse/vroom/main/inst/extdata/mtcars.csv.gz" vroom(file) ``` ## Column selection vroom provides the same interface for column selection and renaming as [dplyr::select()](https://dplyr.tidyverse.org/reference/select.html). This provides very efficient, flexible and readable selections. For example you can select by: - A character vector of column names ```{r} file <- vroom_example("mtcars.csv.gz") vroom(file, col_select = c(model, cyl, gear)) ``` - A numeric vector of column indexes, e.g. `c(1, 2, 5)` ```{r} vroom(file, col_select = c(1, 3, 11)) ``` - Using the selection helpers such as `starts_with()` and `ends_with()` ```{r} vroom(file, col_select = starts_with("d")) ``` - You can also rename columns ```{r} vroom(file, col_select = c(car = model, everything())) ``` ## Reading fixed width files A fixed width file can be a very compact representation of numeric data. Unfortunately, it's also often painful to read because you need to describe the length of every field. vroom aims to make it as easy as possible by providing a number of different ways to describe the field structure. Use `vroom_fwf()` in conjunction with one of the following helper functions to read the file. ```{r} fwf_sample <- vroom_example("fwf-sample.txt") cat(readLines(fwf_sample)) ``` - `fwf_empty()` - Guess based on the position of empty columns. ```{r} vroom_fwf(fwf_sample, fwf_empty(fwf_sample, col_names = c("first", "last", "state", "ssn"))) ``` - `fwf_widths()` - Use user provided set of field widths. ```{r} vroom_fwf(fwf_sample, fwf_widths(c(20, 10, 12), c("name", "state", "ssn"))) ``` - `fwf_positions()` - Use user provided sets of start and end positions. ```{r} vroom_fwf(fwf_sample, fwf_positions(c(1, 30), c(20, 42), c("name", "ssn"))) ``` - `fwf_cols()` - Use user provided named widths. ```{r} vroom_fwf(fwf_sample, fwf_cols(name = 20, state = 10, ssn = 12)) ``` - `fwf_cols()` - Use user provided named pairs of positions. ```{r} vroom_fwf(fwf_sample, fwf_cols(name = c(1, 20), ssn = c(30, 42))) ``` ## Column types vroom guesses the data types of columns as they are read, however sometimes the guessing fails and it is necessary to explicitly set the type of one or more columns. The available specifications are: (with single letter abbreviations in quotes) * `col_logical()` 'l', containing only `T`, `F`, `TRUE`, `FALSE`, `1` or `0`. * `col_integer()` 'i', integer values. * `col_big_integer()` 'I', Big integer values. (64bit integers) * `col_double()` 'd', floating point values. * `col_number()` 'n', numbers containing the `grouping_mark` * `col_date(format = "")` 'D': with the locale's `date_format`. * `col_time(format = "")` 't': with the locale's `time_format`. * `col_datetime(format = "")` 'T': ISO8601 date times. * `col_factor(levels, ordered)` 'f', a fixed set of values. * `col_character()` 'c', everything else. * `col_skip()` '_, -', don't import this column. * `col_guess()` '?', parse using the "best" type based on the input. You can tell vroom what columns to use with the `col_types()` argument in a number of ways. If you only need to override a single column the most concise way is to use a named vector. ```{r} # read the 'hp' columns as an integer vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i")) # also skip reading the 'cyl' column vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i", cyl = "_")) # also read the gears as a factor vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i", cyl = "_", gear = "f")) ``` You can read all the columns with the same type, by using the `.default` argument. For example reading everything as a character. ```{r} vroom(vroom_example("mtcars.csv"), col_types = c(.default = "c")) ``` However you can also use the `col_*()` functions in a list. ```{r} vroom( vroom_example("mtcars.csv"), col_types = list(hp = col_integer(), cyl = col_skip(), gear = col_factor()) ) ``` This is most useful when a column type needs additional information, such as for categorical data when you know all of the levels of a factor. ```{r} vroom( vroom_example("mtcars.csv"), col_types = list(gear = col_factor(levels = c(gear = c("3", "4", "5")))) ) ``` ## Name repair Often the names of columns in the original dataset are not ideal to work with. `vroom()` uses the same `.name_repair` argument as tibble, so you can use one of the default name repair strategies or provide a custom function. A great approach is to use the [`janitor::make_clean_names()`](https://sfirke.github.io/janitor/reference/make_clean_names.html) function as the input. This will automatically clean the names to use whatever case you specify, here I am setting it to use `ALLCAPS` names. ```{r, eval = FALSE} vroom( vroom_example("mtcars.csv"), .name_repair = ~ janitor::make_clean_names(., case = "all_caps") ) ``` ## Writing delimited files Use `vroom_write()` to write delimited files, the default delimiter is tab, to write TSV files. Writing to TSV by default has the following benefits: - Avoids the issue of whether to use `;` (common in Europe) or `,` (common in the US) - Unlikely to require quoting in fields, as very few fields contain tabs - More easily and efficiently ingested by Unix command line tools such as `cut`, `perl` and `awk`. ```{r} vroom_write(mtcars, "mtcars.tsv") ``` ```{r, include = FALSE} unlink("mtcars.tsv") ``` ### Writing CSV delimited files However you can also use `delim = ','` to write CSV files, which are common as inputs to GUI spreadsheet tools like Excel or Google Sheets. ```{r} vroom_write(mtcars, "mtcars.csv", delim = ",") ``` ```{r, include = FALSE} unlink("mtcars.csv") ``` ### Writing compressed files For gzip, bzip2 and xz compression the outputs will be automatically compressed if the filename ends in `.gz`, `.bz2` or `.xz`. ```{r} vroom_write(mtcars, "mtcars.tsv.gz") vroom_write(mtcars, "mtcars.tsv.bz2") vroom_write(mtcars, "mtcars.tsv.xz") ``` ```{r, include = FALSE} unlink(c("mtcars.tsv.gz", "mtcars.tsv.bz2", "mtcars.tsv.xz")) ``` It is also possible to use other compressors by using `pipe()` with `vroom_write()` to create a pipe connection to command line utilities, such as - [pigz](https://zlib.net/pigz/), a parallel gzip implementation - lbzip2, a parallel bzip2 implementation - [pixz](https://github.com/vasi/pixz), a parallel xz implementation - [Zstandard](https://facebook.github.io/zstd/), a modern real-time compression algorithm. The parallel compression versions can be considerably faster for large output files and generally `vroom_write()` is fast enough that the compression speed becomes the bottleneck when writing. ```{r, eval = nzchar(Sys.which("pigz"))} vroom_write(mtcars, pipe("pigz > mtcars.tsv.gz")) ``` ```{r, include = FALSE} unlink("mtcars.tsv.gz") ``` ### Reading and writing from standard input and output vroom supports reading and writing to the C-level `stdin` and `stdout` of the R process by using `stdin()` and `stdout()`. E.g. from a shell prompt you can pipe to and from vroom directly. ```shell cat inst/extdata/mtcars.csv | Rscript -e 'vroom::vroom(stdin())' Rscript -e 'vroom::vroom_write(iris, stdout())' | head ``` Note this interpretation of `stdin()` and `stdout()` differs from that used elsewhere by R, however we believe it better matches most user's expectations for this use case. ## Further reading - `vignette("benchmarks")` discusses the performance of vroom, how it compares to alternatives and how it achieves its results. - [πŸ“½ vroom: Because Life is too short to read slow](https://www.youtube.com/watch?v=RA9AjqZXxMU&t=10s) - Presentation of vroom at UseR!2019 ([slides](https://speakerdeck.com/jimhester/vroom)) - [πŸ“Ή vroom: Read and write rectangular data quickly](https://www.youtube.com/watch?v=ZP_y5eaAc60) - a video tour of the vroom features. vroom/vignettes/benchmarks.Rmd0000644000176200001440000003455114363145201016214 0ustar liggesusers--- title: "Vroom Benchmarks" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Vroom Benchmarks} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) library(ggplot2) library(forcats) library(dplyr) library(tidyr) library(fs) pretty_sec <- function(x) { x[!is.na(x)] <- prettyunits::pretty_sec(x[!is.na(x)]) x } pretty_lgl <- function(x) { case_when( x == TRUE ~ "TRUE", x == FALSE ~ "FALSE", TRUE ~ "" ) } read_benchmark <- function(file, desc) { vroom::vroom(file, col_types = c("ccccddddd")) %>% filter(op != "setup") %>% mutate( altrep = case_when( grepl("^vroom_no_altrep", reading_package) ~ FALSE, grepl("^vroom", reading_package) ~ TRUE, TRUE ~ NA ), reading_package = case_when( grepl("^vroom", reading_package) ~ "vroom", TRUE ~ reading_package ), label = fct_reorder( glue::glue("{reading_package}{altrep}\n{manip_package}", altrep = ifelse(is.na(altrep), "", glue::glue("(altrep = {altrep})")) ), case_when(type == "real" ~ time, TRUE ~ 0), sum), op = factor(op, desc) ) } generate_subtitle <- function(data) { rows <- scales::comma(data$rows[[1]]) cols <- scales::comma(data$cols[[1]]) size <- fs_bytes(data$size[[1]]) glue::glue("{rows} x {cols} - {size}B") } plot_benchmark <- function(data, title) { subtitle <- generate_subtitle(data) data <- data %>% filter(reading_package != "read.delim", type == "real") p1 <- data %>% ggplot() + geom_bar(aes(x = label, y = time, fill = op, group = label), stat = "identity") + scale_fill_brewer(type = "qual", palette = "Set2") + scale_y_continuous(labels = scales::number_format(suffix = "s")) + coord_flip() + labs(title = title, subtitle = subtitle, x = NULL, y = NULL, fill = NULL) + theme(legend.position = "bottom") p2 <- data %>% group_by(label) %>% summarise(max_memory = max(max_memory)) %>% ggplot() + geom_bar(aes(x = label, y = max_memory / (1024 * 1024)), stat = "identity") + scale_y_continuous(labels = scales::number_format(suffix = "Mb")) + coord_flip() + labs(title = "Maximum memory usage", x = NULL, y = NULL) + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) library(patchwork) p1 + p2 + plot_layout(widths = c(2, 1)) } make_table <- function(data) { data %>% filter(type == "real") %>% select(-label, -size, -type, -rows, -cols) %>% spread(op, time) %>% mutate( total = read + print + head + tail + sample + filter + aggregate, max_memory = as.character(bench::as_bench_bytes(max_memory)) ) %>% arrange(desc(total)) %>% mutate_if(is.numeric, pretty_sec) %>% mutate_if(is.logical, pretty_lgl) %>% select(reading_package, manip_package, altrep, max_memory, everything()) %>% rename( "reading\npackage" = reading_package, "manipulating\npackage" = manip_package, memory = max_memory ) %>% knitr::kable(digits = 2, align = "r", format = "html") } desc <- c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate") ``` vroom is a new approach to reading delimited and fixed width data into R. It stems from the observation that when parsing files reading data from disk and finding the delimiters is generally not the main bottle neck. Instead (re)-allocating memory and parsing the values into R data types (particularly for characters) takes the bulk of the time. Therefore you can obtain very rapid input by first performing a fast indexing step and then using the Altrep framework available in R versions 3.5+ to access the values in a lazy / delayed fashion. ## How it works The initial reading of the file simply records the locations of each individual record, the actual values are not read into R. Altrep vectors are created for each column in the data which hold a pointer to the index and the memory mapped file. When these vectors are indexed the value is read from the memory mapping. This means initial reading is extremely fast, in the real world dataset below it is ~ 1/4 the time of the multi-threaded `data.table::fread()`. Sampling operations are likewise extremely fast, as only the data actually included in the sample is read. This means things like the tibble print method, calling `head()`, `tail()` `x[sample(), ]` etc. have very low overhead. Filtering also can be fast, only the columns included in the filter selection have to be fully read and only the data in the filtered rows needs to be read from the remaining columns. Grouped aggregations likewise only need to read the grouping variables and the variables aggregated. Once a particular vector is fully materialized the speed for all subsequent operations should be identical to a normal R vector. This approach potentially also allows you to work with data that is larger than memory. As long as you are careful to avoid materializing the entire dataset at once it can be efficiently queried and subset. # Reading delimited files The following benchmarks all measure reading delimited files of various sizes and data types. Because vroom delays reading the benchmarks also do some manipulation of the data afterwards to try and provide a more realistic performance comparison. Because the `read.delim` results are so much slower than the others they are excluded from the plots, but are retained in the tables. ## Taxi Trip Dataset This real world dataset is from Freedom of Information Law (FOIL) Taxi Trip Data from the NYC Taxi and Limousine Commission 2013, originally posted at . It is also hosted on [archive.org](https://archive.org/details/nycTaxiTripData2013). The first table trip_fare_1.csv is 1.55G in size. #> Observations: 14,776,615 #> Variables: 11 #> $ medallion "89D227B655E5C82AECF13C3F540D4CF4", "0BD7C8F5B... #> $ hack_license "BA96DE419E711691B9445D6A6307C170", "9FD8F69F0... #> $ vendor_id "CMT", "CMT", "CMT", "CMT", "CMT", "CMT", "CMT... #> $ pickup_datetime "2013-01-01 15:11:48", "2013-01-06 00:18:35", ... #> $ payment_type "CSH", "CSH", "CSH", "CSH", "CSH", "CSH", "CSH... #> $ fare_amount 6.5, 6.0, 5.5, 5.0, 9.5, 9.5, 6.0, 34.0, 5.5, ... #> $ surcharge 0.0, 0.5, 1.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0, 0... #> $ mta_tax 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0... #> $ tip_amount 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0... #> $ tolls_amount 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.8, 0.0, 0... #> $ total_amount 7.0, 7.0, 7.0, 6.0, 10.5, 10.0, 6.5, 39.3, 7.0... ### Taxi Benchmarks code: [bench/taxi](https://github.com/tidyverse/vroom/tree/main/inst/bench/taxi) All benchmarks were run on a Amazon EC2 [m5.4xlarge](https://aws.amazon.com/ec2/instance-types/m5/) instance with 16 vCPUs and an [EBS](https://aws.amazon.com/ebs/) volume type. The benchmarks labeled `vroom_base` uses `vroom` with base functions for manipulation. `vroom_dplyr` uses `vroom` to read the file and dplyr functions to manipulate. `data.table` uses `fread()` to read the file and `data.table` functions to manipulate and `readr` uses `readr` to read the file and `dplyr` to manipulate. By default vroom only uses Altrep for character vectors, these are labeled `vroom(altrep: normal)`. The benchmarks labeled `vroom(altrep: full)` instead use Altrep vectors for all supported types and `vroom(altrep: none)` disable Altrep entirely. The following operations are performed. - The data is read - `print()` - _N.B. read.delim uses `print(head(x, 10))` because printing the whole dataset takes > 10 minutes_ - `head()` - `tail()` - Sampling 100 random rows - Filtering for "UNK" payment, this is 6434 rows (0.0435% of total). - Aggregation of mean fare amount per payment type. ```{r, fig.height = 8, fig.width=10, warning = FALSE, echo = FALSE, message = FALSE} taxi <- read_benchmark(path_package("vroom", "bench", "taxi.tsv"), desc) plot_benchmark(taxi, "Time to analyze taxi trip data") make_table(taxi) ``` (*N.B. Rcpp used in the dplyr implementation fully materializes all the Altrep numeric vectors when using `filter()` or `sample_n()`, which is why the first of these cases have additional overhead when using full Altrep.*). ## All numeric data All numeric data is really a worst case scenario for vroom. The index takes about as much memory as the parsed data. Also because parsing doubles can be done quickly in parallel and text representations of doubles are only ~25 characters at most there isn't a great deal of savings for delayed parsing. For these reasons (and because the data.table implementation is very fast) vroom is a bit slower than fread for pure numeric data. However because vroom is multi-threaded it is a bit quicker than readr and read.delim for this type of data. ### Long code: [bench/all_numeric-long](https://github.com/tidyverse/vroom/tree/main/inst/bench/all_numeric-long) ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} all_num <- read_benchmark(path_package("vroom", "bench", "all_numeric-long.tsv"), desc) plot_benchmark(all_num, "Time to analyze long all numeric data") make_table(all_num) ``` ### Wide code: [bench/all_numeric-wide](https://github.com/tidyverse/vroom/tree/main/inst/bench/all_numeric-wide) ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} all_num_wide <- read_benchmark(path_package("bench", "all_numeric-wide.tsv", package = "vroom"), desc) plot_benchmark(all_num_wide, "Time to analyze wide all numeric data") make_table(all_num_wide) ``` ## All character data code: [bench/all_character-long](https://github.com/tidyverse/vroom/tree/main/inst/bench/all_character-long) All character data is a best case scenario for vroom when using Altrep, as it takes full advantage of the lazy reading. ### Long ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} all_chr <- read_benchmark(path_package("vroom", "bench", "all_character-long.tsv"), desc) plot_benchmark(all_chr, "Time to analyze long all character data") make_table(all_chr) ``` ### Wide code: [bench/all_character-wide](https://github.com/tidyverse/vroom/tree/main/inst/bench/all_character-wide) ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} all_chr_wide <- read_benchmark(path_package("vroom", "bench", "all_character-wide.tsv"), desc) plot_benchmark(all_chr_wide, "Time to analyze wide all character data") make_table(all_chr_wide) ``` # Reading multiple delimited files code: [bench/taxi_multiple](https://github.com/tidyverse/vroom/tree/main/inst/bench/taxi_multiple) ```{r, echo = FALSE, message = FALSE, eval = TRUE} mult <- read_benchmark(path_package("vroom", "bench", "taxi_multiple.tsv"), desc) ``` The benchmark reads all 12 files in the taxi trip fare data, totaling `r scales::comma(mult$rows[[1]])` rows and `r mult$cols[[1]]` columns for a total file size of `r format(fs_bytes(mult$size[[1]]))`. ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} plot_benchmark(mult, "Time to analyze multiple file data") make_table(mult) ``` # Reading fixed width files ## United States Census 5-Percent Public Use Microdata Sample files ```{r, echo = FALSE, message = FALSE, eval = TRUE} fwf <- read_benchmark(path_package("vroom", "bench", "fwf.tsv"), desc) ``` This fixed width dataset contains individual records of the characteristics of a 5 percent sample of people and housing units from the year 2000 and is freely available at . The data is split into files by state, and the state of California was used in this benchmark. The data totals `r scales::comma(fwf$rows[[1]])` rows and `r fwf$cols[[1]]` columns with a total file size of `r format(fs_bytes(fwf$size[[1]]))`. ## Census data benchmarks code: [bench/fwf](https://github.com/tidyverse/vroom/tree/main/inst/bench/fwf) ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} plot_benchmark(fwf, "Time to analyze fixed width data") make_table(fwf) ``` # Writing delimited files code: [bench/taxi_writing](https://github.com/tidyverse/vroom/tree/main/inst/bench/taxi_writing) The benchmarks write out the taxi trip dataset in a few different ways. - An uncompressed file - A gzip compressed file using `gzfile()` _(readr and vroom do this automatically for files ending in `.gz`)_ - A gzip compressed file compressed with multiple threads (natively for data.table and using a `pipe()` connection to [pigz](https://zlib.net/pigz/) for the rest). - A [Zstandard](https://facebook.github.io/zstd/) compressed file (data.table does not support this format). ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} taxi_writing <- read_benchmark(path_package("vroom", "bench", "taxi_writing.tsv"), c("setup", "writing")) %>% rename( package = reading_package, compression = manip_package ) %>% mutate( package = factor(package, c("base", "readr", "data.table", "vroom")), compression = factor(compression, rev(c("gzip", "multithreaded_gzip", "zstandard", "uncompressed"))) ) %>% filter(type == "real") subtitle <- generate_subtitle(taxi_writing) taxi_writing %>% ggplot(aes(x = compression, y = time, fill = package)) + geom_bar(stat = "identity", position = position_dodge2(reverse = TRUE, padding = .05)) + scale_fill_brewer(type = "qual", palette = "Set2") + scale_y_continuous(labels = scales::number_format(suffix = "s")) + theme(legend.position = "bottom") + coord_flip() + labs(title = "Writing taxi trip data", subtitle = subtitle, x = NULL, y = NULL, fill = NULL) taxi_writing %>% select(-size, -op, -rows, -cols, -type, -altrep, -label, -max_memory) %>% mutate_if(is.numeric, pretty_sec) %>% pivot_wider(names_from = package, values_from = time) %>% arrange(desc(compression)) %>% knitr::kable(digits = 2, align = "r", format = "html") ``` ## Session and package information ```{r, echo = FALSE, warning = FALSE, message = FALSE} si <- vroom::vroom(path_package("vroom", "bench", "session_info.tsv")) class(si) <- c("packages_info", "data.frame") select(as.data.frame(si), package, version = ondiskversion, date, source) %>% knitr::kable() ``` vroom/R/0000755000176200001440000000000014533651065011625 5ustar liggesusersvroom/R/vroom_lines.R0000644000176200001440000000364114504102737014303 0ustar liggesusers#' Read lines from a file #' #' `vroom_lines()` is similar to `readLines()`, however it reads the lines #' lazily like [vroom()], so operations like `length()`, `head()`, `tail()` and `sample()` #' can be done much more efficiently without reading all the data into R. #' @inheritParams vroom #' @examples #' lines <- vroom_lines(vroom_example("mtcars.csv")) #' #' length(lines) #' head(lines, n = 2) #' tail(lines, n = 2) #' sample(lines, size = 2) #' @export vroom_lines <- function(file, n_max = Inf, skip = 0, na = character(), skip_empty_rows = FALSE, locale = default_locale(), altrep = TRUE, altrep_opts = deprecated(), num_threads = vroom_threads(), progress = vroom_progress()) { if (!is_missing(altrep_opts)) { deprecate_warn("1.1.0", "vroom_lines(altrep_opts = )", "vroom_lines(altrep = )") altrep <- altrep_opts } file <- standardise_path(file) if (!is_ascii_compatible(locale$encoding)) { file <- reencode_file(file, locale$encoding) locale$encoding <- "UTF-8" } if (n_max < 0 || is.infinite(n_max)) { n_max <- -1 } if (length(file) == 0 || n_max == 0) { return(character()) } col_select <- quo(NULL) # delim = "\1" sets the delimiter to be start of header, which should never # appear in modern text. This essentially means the only record breaks will # be newlines. Ideally this would be "\0", but R doesn't let you have nulls # in character vectors. out <- vroom_(file, delim = "\1", col_names = "V1", col_types = cols(col_character()), id = NULL, skip = skip, col_select = col_select, name_repair = "minimal", na = na, quote = "", trim_ws = FALSE, escape_double = FALSE, escape_backslash = FALSE, comment = "", skip_empty_rows = skip_empty_rows, locale = locale, guess_max = 0, n_max = n_max, altrep = vroom_altrep(altrep), num_threads = num_threads, progress = progress ) if (length(out) == 0) { return(character()) } out[[1]] } vroom/R/sysdata.rda0000644000176200001440000007513014132374245013770 0ustar liggesusersBZh91AY&SYετN²@υWΰ?πΏπυψwΎ@ΰƒ~#C‹ί]σΓΦΎ€ο»mχ4W^μΠttΩπλ'¬ο»C  ξςw*Φ(ι’Ύνz-›ΎOΌYΩ|+zΞΩnWuΉ:Ϋ»»Ϋ§½Ϊvϋή \=»_X6±{»v‘ΞΒΪΗΊνΧVw»½«yΥοqη½hc-lν΅Ϊφο/vŠΛέΫ#ο}πχgFY―½οwžζφϋοs}n}Χ›‹ΥΨk˜λwuξrυλOvέμ»Χ½ΪΤ»šBΐρ(’ξλšξΪΆvυΪΧ\ΧMWΊφκςnΝ)]w.ο―KvΎž}τ{eν›m{et2lP °¦«Z’…hD–͐χ}»Ρς Ζλ*¬yΌuθ€σ½½«Ϋάτ{Μθ*€(R!EP vΐnޝξκ«N§=ζ6χ½NOf,“vήΐΠ‚ ‘¦@“"hΑš4¦Τ6”π 0™yOSυ2bšhυ<  1Sb)νMͺiνSΕ£ΚzM4!΄ΔΤmPυ4ΠP @€4Π @‚F)ψ„Ÿ i4ž§‘4Ι“L&††šOB2ih&ƒA i¦š $$@€š‘£@MOFšM0‰ν"z2™4¦ΤΖSτ¦G‘4σI4£idς™4$š™4=MOΣSTύ Sj1’!¦€Θ4£!ι@4€@𠦂E ΔΑ2˜ 4Π&SΠSΡ‘3S@ςŒΙ6§©£ΤΝA¦¦ƒΤΙ¦Τ @h2`α‚oΡΒ€lΘaAU ς#€ΐ"¨ΐ 2Š( ¨¨φθθ!Uω^•Υ}εο»sζ~—cσΗέώ½_³ξώΟX/έl_BwΏό©πPύ}=ϊήό―}·αϊGΪό«cΦ%°`}vψβIσUΓSΏΫσ›ρ^υήS™ύD…T>%­°ύ«Ž‰]…Υ@Ζ_ηίωvW4ώ½6ό΄… OκOωhŠ™˜)ϊAχ Τ˜R+C#dπ”.[πΕE.9«χW²l―”ˆ ϋΠπNνwΠ—ςΌ ΄έh’ƒWkqX>g―Ζξν*gφ¬QXϊžό6μκή~φͺfτͺˆBΌ45fΏHΗKρhόΫσfΟ³€ΚPŸΪFλλΌω“σΉ”μkSoςeˆe‰κ•OΗΟ~ή{‡εώ?ΡwY±–…$3Wγ Cteeσgςί—τHޞύJYαnπΫW pΈXŒuŒεόμ[Zfς!‘­¨Π ’†ΒΠΓ¨ΨΆ©§'g¦ΪΆ¬WΖ€‡¨Ά‹Ί›―ρρόΫΣ†ΓfψCΔς©z―'γΎ‡«%Ί>mV9­ιΆ₯g&<Ψλš?&¬B££’‘*σ+w…Ό:–΄ξοp˜S'€?±φ‚/ςύί“ν]nωΦωώΗAΙ’8Ϋm·Τh#mΌΥ󯏢"‡KT°Ε«2°“AρΈ4ΨΪ6±tq||Όί‘ΟΣδΉέ33Θm±}-•₯Nυωͺo\ΦνG ϋ?˜Τ₯ΰ…8 ¦Πc΅UWΧkc•y”Υα…κ:)‹Α‰]“‘A‘¨(τηOTŸ‡‘ΐ8Yuδ%ϋ—L›"ΔS‘jΒ™ι/ž”Χ₯Η φHtCΧ₯ κ$’@zτa–<]έČ·υq“ώχ±Λχ~χφ<žŸί₯ θ{WλφΈ=gsφύΊŽyή–Wz;όχš©ΔHA‰"H’$‰"HΰτύΧ›η|'WΣρϊS{}η΅—έfN»?Ϊ08›ΐάΪ8½ji/‰UgA λψ¬›μͺΤ­2š½ΉΆΏKvΚ[υΑŒh[f¬η:ύ8 ϊΤhÍΪUΞ;Σ†ͺτQ‘΄Όk}οΓψwβmͺ΅b¦ )νZw½ΦΧφ}!Qžnf ύ•7Σ5Q+ΣΫ'χ±ψL‹υ­R‡ΑιΡθ'"ξa ΐΘ2$ΎεzχΟή˜΄Υά6=,šΊkΉ΄ΘΡD=©z« ΑXΙFCxΫέB’6Ӎk gR).R-c[·*©°|Wί•q΄ξ±ϋ  ΖiJθδκ%Θrπž.EvδβUλΐύKHbψΏWφχ’YΫΛ¨w—πonνΠ·΄vR솃c²©Σ‘Ι lO½>{γzΑ%(cέ–cΊ!ŽΦ@Ε™ͺT‡¬»QΞY€o#΄Z―YιΚΏ“›,ΊkΚγΐd Pΐ­n}ό ΗVήδψ\ζΎ©ύ!˜υ~ 8ΑΔ9ν¬’:O€do –¬ƒΠ©n©Hˆ Τιœί‡ΰΦηψ»ΉΫ'ΓηηΎtp[σyΤ!€πϊ —>iΟfΜό笌 ΐ―G]Ν΄ή«XQ­΅―`!(1)€ύΓΒ`Έ…|\Σ πΑ©ευ|’w&τ NŸAυ{.©½ λυžηG0 oE811ψαuba·±0zPε2qΐρl},ΐΗFSλ?Cρότέ‚Ϋ·$ΙΉ]”Œ4p–*΅υΑ›τΩνx ‹1.v"@žΣQωςαΏ½·‡gΗ{qi›ωο³vž–kΰ΄ΠrVΩ­L₯C>3mΆΞ\o‡΄«½fό¬― J1΅NνΌΨ–‘ εzχ‹Εβυ<^/XgBυV,ϊ§ ‡Deς»°3-;—@g2 ΰ#UΪslά'“Zw™Υ4G€]ΐπ-pƒ ο>cη™3ε2DΟΚTPDς”›MH0`6Ή(ΪΤrύόΙΎ}¦B~F-sM¬IΚΗίέD]7b9ι ς°Φ·[šύϊμΗ1Y… .¦tE±άύχΡϊ>ŽΈ2€$~ϊ9KX†ˆσ‹ε,-ΘΚT—ζŸg„¨Ÿ/G%Yω,Ό7±μWφτΜgVΔw}ε¦!Υuίΰ‰iχž7…ΰuΗLΧ?KεŒπ‡|g²…Γ܁ξ―”Νϋ·Kώξν΄σΚXΓψϋ=JŠ‹ΝtTΛ+4ό±ΒΫϊ,C:ž€Œ<4@c`` C¬2ŒB³γ9/kηYU*Ε•P¨F¬ŸUλ (κ5‘3ψNCζ˜> ή>αsYgK©ΒπX_[—ΐΡ[roӐ_r80½ :=HΥ‘ΘζΈθΰ¦5jΑ‘σŒιΞ›‹VμΖ9˜Ϋεs‰u›―§ZUΆ§Kfγε8|«Ϋ‰Ζΐ¦Ÿzη§«Np]`chjXqD‚Ψρ`’{FzɊ°β&{}Α“’𨬀ͺ,95°ja•9²φ+ΜΫΥ}ΰΔ[ͺσgΚζlύŒbΓDG φςϊώ!MݚτRˆΐ[ΚAΕͺμτΩqp£²UλΛl„n) PΘÁeKyŒ[%|N77nyW$툢 [ άΩfˆF›<αŽ‹„πMΘσηOΏ›=Xoπ|η^qΚwOT‘xΛOΣ D^ΰΚ)wQξ ¨ΈυΑi#φΤˆ“aΆaΗ\DAC₯ 2ΈJΫ½‹€<Ί%εΒ߈P―ŸΦνqqͺπ°(έ ;ψiρΡΈg„δ˜Ύ(ܟ?p~Ρ`d‘x]d/w§Ί–ψuQΥ<€eAΎυΐηMϊJoΣ©φDyl1§ Qtκστ_…°Πw”+&χW˜8d3•)¨c¨œΤQ•¦˜ύΥMwΊ’a§>Η―ͺζ³'2‚υυ ns‡yZo:άκh©Η8Ζ#―πΊ* .πΔ;εΖ6ΠtΑ 0ƒ D5²%J# ΉtE―’Α‹ͺ5_k š{ΖΗ‚ψ…3ήΑ !|r “"δ+„Js©–tAf UFb*”4σ³Σ;`zœ΅1ƒδ@q£ΫΑέ«,ΒΓ…o5RA₯˜·©:—ƒEύώ–†‰ž0ήq[@§ež—3ŸPNΑ‚šΐcΆ#~ ”PRŒcQ)ι#6λCΏ—$9w{‡pΓ”CV mΆΛD©ΰΞ°β­π1Νρ€o Α­AVυ,F‹e8^ΛCzŠαEΤ€@aNOΓΫ^η_E[~^uqn΄Ο)SC„ »\2ο ρ=‘Δ7vFƒ0a”uB §œ"ξΎδ^w§ΪuΝeη€α±œ† c=[Θ5Ε:{CDΡ?³HmyTJ@a±hUŒβΥδςγu:!:6ρψ9ΨϊΛ²dζ†ν‘§m²Θ΄β,C ™CF°oWΔCωΆ_|]½ƒrμC”AΖ‘J-Κυˆ!Ϋη4Q—HΑ €βηΩΛ½p6ϊ>ΪVΜήχab5²qί'“Ι'ζf?aΗ›ν{^ΤΝέ°%½κLί9ί™2dɎύ ‰ή{ DΖv<@8BWώ_³9Sπψ½#+\ψΫψά wν΄ζνA§*kVKμή¨ύ·dkZήδ3½,νc|£.»ν+i«Ή–g»±uΤΊ/€ΥnW¬οΘW::Υ>mΖΖΕμΊΗΌλ/›RΫ\»›-ͺ}w½03“μrϋλ³ ,†έo~)±B΅’‘‹νZΰχ¬Βo‹pOΏβς0!οΘ€³ X°σΠ=Z¨ηg06 °ίU”έΥdΉμ[¨ŽH$Α#εd­ “zϊϊϊέ³Ή,ι>ωžC•οΦyJΓ •Q’š$‚ΖLω>?lνjύ?– •@€ΔZ…­>O΅‚UΈ‰’ΦBΒκ h©γ™žm₯ "γΦ\cΤ­Z~ήν™ρœdE <‰_΅©|+Δθ‰qaεo’έΰΪtΝ!¨ΫKxΔΈzT σˆΐ©tΑO΄J+ά€F ”)£Ž3"D"«οˆ*€΅vΎΕipL`kΔΥ―L[±.hάk~αδ xύΨυΒ£°(yυ‘k¨Σnμu#v^Ε‹ό­Φ/Zγϊ7¨’‚ԍ ±žŠΫΥόΉΑWHΆΆΆΈΉ1Τ«R!^ ­\₯RbI$’ADn††††‰‚hδXΎ±<[Ÿ§[tφ6φ,ΞιDC–­BR(δir΅ξŒΖ>ώΛBNλyL΄Ρδ*€²VΠ«%JDe˜nοΕ՚MΗΛza i‘+¦Ϊ/Ί}˜!τΚ£OΊά‘τ‡οΧžL{ qnΊΥBxω8υcΎ‹€­£sM₯‹%Ί-»Ξ½“6qŒ™˜˜Elΐk±Λώνψ όLV΄o½ŸiΒ·>ΩLprΉυα0μ0υΉηΜΩ}‹e ΪΙ2Ω‘ˆ€βŸΞδ δD4"3ΔεR Κ•ίKλ)¦aχ¬@ )§7ΉΙκwωγR =’·.l–ξ\·9dΕνw3㣞D­±]]ΙΛ,³Y₯?Τ’©j|dVR;<-M€N¨WWΗw¨²x”ρΈυΫNˆλ)38CΈ^9Œƒ ͺo–b¦ΰώm4ιΨηMΆ*ZaŸΚ}ΐ¬Ή^ /’&{$ϋ @TU’βξϊτ=ΊτΎ’’X.2VQs³Sϊ^§ΝωPd( :ΕΦGC€Υ†6«Jͺ4`ΦϊνΎX‰ξ΅G|π²ΛΎL†Œ}•P8«•οdeE%œŒ%†λΑ‹<Ωg^κT@ζq’~c‡?Yϋ€‘ηήΑ(£Avl Ϋ|œœœœœœΜ­gψηΝ©>poVτk€†Ίq²zi¦ši¦•Ώ ‚^SPΒΛοXݖАΞ;;LίΎ±°FΌΡ]ŒχαάΎ2Ύ.ΣΈΓΰ ‘Žqu[ZƒϊΉͺ™ΡAϋξ&Xσ]·t­sΖ/ηV'³3›—$Όέή΅eeέsδΰ΅ΰΚ’œ‡ Χ‰Μu‡)\ ΕD&ΗTeΘJ’²O΅ιο ΝΉώςΖ\XrGs`­R!˜ C¦ΑΞ›²β’•ήlΚβŽkΟ3ςεΊρ–t T₯π OTHΑC6IΡα§›Οίίίίί³6Uƒ1ΝκiLm;Α±A†Β·΄—Ž£X΅F«AF^«<:ζ,ΧΨ6–bΗns“*N6˜ΞŠOΩσNιΧε‚?’β΁άS(¬ZECKT8:‡U[j ©™pϊ~žzwR œςΕmi&«lΖl1™•ϊ’ΞΕ‘ο0αΪΓΊ§2sΖ& S­°¨αsMzγQŽ—*ιk %d³©DQΈ°<³2<€u¨΄M\‡¨ΝSΩΞνΆΛW±Φ8gf 49LZ†–Q έ€ˆΌŒςH(>ϊYωΡ—rέtΚήf"ΘκηGi‘˜`φ΅€5XŒ¦aΤΔ³%(tπ‡ ͺ™ €Λ.ξVάF˜mc![ΓΥΥΥΥΥεςΚνKyΪ‘–Φ#A0q  @σ΅1˜Μf3ŠΊZ³RfъMΨ™ΰ°Ύχ4Ÿά φc*nσ"…V½+ζV°)ω\§X…§FΣͺ2Ι±‰Ω΅Λ1fW@+ΨPΉΛ πΠΒ§!JgT~ϋEDWA›,A|Χ΄³  kΪUμΆͺΊ` uγP—TΆΥšFŒΝ6¨p_-@@W<€L½°Ÿμ©CžιβΠ’ΣhΟ·ww»ΛxZΨΆO²NώŠ£Ρ(ΐmΝΙΘ ²θA¨Β WXγET”W°Ί‚ˆ˜Λ½Ε Ǟπ|XovτΞ5ΙbυpB&‘”*![n–‹SL(«€φnSΟ}ύ§ι1hP§Ÿs Θέ₯ο:„χΡ™ 8Θ\QΩύέΒΰΞ­#u±€@*³Σ7λeΐs ΣP•CΘpγSƒ)¨=hπgEΙYΤ%φQ‚6 Mr²^Ό–Œ…πkb( Bt΄,(Ό5ͺ©ͺš©ˆ½’Vΐ 2²–!‘ >+|π2(šΆρδMβΈΈ}Ω–€":`(Ήξ!¬€’θ―eqpπΠYLE-‚ϊ¬ƒ}kGXuΝ‰p4]ΩηA6άΌΊ=°ΈΓ@΄θ&"P₯X{Θ}ukκΉ•Πop8ŠΝ›LwxΗwΆ²šk•‹ε΄β/{ε]q:Q₯šΟΩΧu)ΆšE@ςΊNΔgšδX,€€Π 2*ͺ₯t ΅Ε³£ΰ[3Œλ A@γ š,4M Β τπM•ZΫ…• Ρ«­'c–ΪιœRζ„ε|TPZ@Qƒ …G ][}θδᡌδP—"€Ždmn 錎ΉΌΔd—ΆΌp,τ \ΖWέ΄Z™Y¦›kF‘EMJφ _«UJΗUT•ί1ƒԝL!ˆR–ΫW2Β]«œEQ ΐ8 υ\­žωλKH ͺ3BŠ@Œ²e2sgΛ"ŒYy έH(‡ΑUD”DUutΨ<τ /Ÿ$F!(Qh‘UH•E"TJYD`€₯¦&šfJX$(€’’&HH†XJB& $"H‚ ‘ZhR *€(ˆ"Q …’(d…IŠ …(˜€ ™bF%…@„*–bIX„bJQ¦„• ’$bT‘¨©B™H‚"%‚ he()b€`@ "€ˆ‘ @’€€ hIͺˆ‘(h †„ˆ‘A₯( b"ZB%@d%)€₯!IR€h ͺBš )R•*j) ‰$ F‘™*aˆhhR¨ B¨–*I" B˜¨’ЍHJ”¨€( ‚‚’€˜’’‘Z& ’)‰"ͺ”(`”Šš(Š ˜‰Raˆ* f˜’B"J €˜)‰ ™( (aͺ)™) ‚‚hB¦((JͺHj*( fh ‰f’XŠ"D¨ˆ’Š‘‚š&j¨( ‘ͺT $Š’ ‰H"‚Zš™H†)’d€ͺ‘ ‚†JŠ™(†¦ …f*¦)`™ ‰’¨ˆ”ˆ ˜`™d‰hX *ͺ iV‚†©$ˆˆi(‘’&*)’Š! bhJ©(€¦•Rͺ*‰’ !E*…‘H!I„š‰B!Q’$€("QIϋ wΨA\ΐ€ηΐ /N$ΆΨOgƒΰν^ι³―wΜΞxrBH:θκΦ¬λ aMŽ˜€M ©3 Ewd{rˆ) Fy¨’ αg΅«Α֍Σ5dyEΨ.`Q‘ 'βΊΜ€Œe•xŒΌ+?χ.œ<ζRΣ0ηC±(ΪΦγˆΑp²-ŽeƒaϊSζώςXθέ(lσ`€Θ:°m²ήζͺ-iE‘…^ΰu"KύΨ3˜ϊ™nΜΐΏž5kV¨Νν0έ(ω†@|ƒ5³ΚsηWL )›΄ i1šš ΕσCρœ™y$:ΤŠ»Q”‘ύ„ΐd)IxΘ³ Pw…Άπ“ †=.²†FΠ·)R+ˆΘ6dmHγΤB&Σq‘·QΆ£#jΚ΅–δnmŠKŽ‹¬§PεAΣj4R";ΛIΜαϋσ?kO˜6Œ±Θ2δeνΌ§EΧ…·Δ6'gΤuη²ΗD‚gηΜφt˜Px«3Ιγυ}œVVςσ/Olΐυ`X ο©‘ŒΣi‘‘«cVB3ŸΥφ$“6gη§K²}ώΏΰπwϋο^ωβυ:6}ΉόŽΩSΚZœJ+Ύ₯΄΄yΨfdκf?²cί>ΏΨkϊ`ΖC 3 ’ρq ΰ0ή·M«PΣ4•^ΚΎΑΩύΞVάχc΅υχ;|Ύηn^ΞήωΦfX`υŽΰ˜ω¨βU0ΖνΤTB——ά–YΌ™σΰdά:Dαg^ <Άœ:kξ9ΎΈ}πΊΫ[Pe7˜ϋΜΎφ:~O?_—ΤϋŸD Œy3ϋ»ϋΌ1ϊκ\0ƒι•US πAdΕSήrlf~AάMwgξrwAλ&dέQ £Ξh΅j>V]Υ^« Ψ† T[δCgΫ—ΐώs}”όŸ€Ί5ΐ{kWη<ο‚zo.φ1qI&/)E^€οeو%@CΚΩSm#δέ;•νTW7q30ΰηΝ6o,5ReΔψ΅[‹Uε PT5(ΰM @Ζ• χϋσ£νlAΙI\ΛGΆόΨ“Ϊ΅©Ϋψ°ήaόΞ`/\tnι9αή₯“ ζ‚BαΏΉk§sΰϊXGΖ§ο€@[Ÿ½³ΖΎ.Ÿ:~θφ9wώ·³Ε·³Ήi54)H¦'*άν@€2.Πτώr)“ohyΑe§τQΕ6k$Λ{|‰-οE”zQŽτ’,Κ'ΏAdAμ««[=;E–@³;Œ…H1VV΅­kQZΐ LŠjœη9Ξbs€™ΰΉΞsœΔζπ'2'ΊlŒb#{’*TΦ΅¬V¨•jΠRJ"Q¦†”zQφ}ΉχV'δώγC#ΰόL`δ»ύgXλ—a'Œwάσ‡ξOΟ»Οvtμ4†Eρ­ψίθUŠΝ½"-–Q0¬±εΓ,,0Ak44騜‚Y•g'θmΰ<²ΘA¨™ΒΚΕ gZ6UήCŒRŠ ³”T“FFu”„­]R”Q-Έ0`ζΒB³ΩlΏ’wOΠ>Ρφ†~iΦω_]ώ1ΥΧΨ Κυβ3ElKlόΠPΈ,Οξ<Œ†ΑE*kjU΅sΔ ­ΟŸό'ςi΅ΰ: ­fχ“Š˜Η2–`ΙΛ"1Γ5Σ₯™‚|‘tq6¦Σ*"4Κ“1JΗ[΅ΎσqY@CνžF]ηλΎA‡Ð?G–΄Ώ°±bŠ4ζΈSΕβπ}Wwf;UNi²‡5>¬€Ψ»c{¦™­Έ6Φ7£œ'‰΅(␱λ:»EεB•5<• ƒΣ—η–Θ’+€$θͺzωW―ΖόŸ{t쾍Υa“+ΝT$ωX¨`HFέ­K£ηΰ+¨΅‰νRζ –±Χ­ŽH(`P ΅q@φ²  ₯²ˆ6βΫ%Ά™7‘Nrͺ*wΑvi"ξ4μXV‘1”¬,‚4Κ­qTŒ’J• …ρ20PN \gηίΗ"ς=ΔΏ—’Ο pnΣΟnLœAΐƒMv˜u—qΩΆͺG5Jc–ε±:ΈΦ νg”ο,₯ΣΐŽΠΚ ·ρ ΄Φu7Mh΅9]tΚε#ΉF)αWΊ';»¦YqΛe†ΎeΝ†²`Σ~ŽΆνβώΩ€ dΩbŠŸžiƒΝ™ώι,5§©ΜΘ΅‡#œ…Ϋ μκΦϊ7:΅–&‡MΧΤ p»ΕδͺuΧ9j'ή] %ƒΰ]{,ς†AցΩLC~Ž’dθ ?,bFšεLΘΦIιΠΒ74ˆΫθγh[Φ–ξφ¬d±x¨'VtB­QC±vY»_LϊNφ¦ΡσΨfDΊψMŒΠhͺs4΄2―.4Žτ«R‘Ρ+RŽU­θβ4f,GΡΏ§½Fή)8ΝzžέΝΝkežϋΘε*;μ4uxΤΏt£*;.οδTVkΔ36k·ηFΣΌuΟι \qšΠuΐΙe²«β°*γ°X›8O8as”šΫqγΝ†ylMLjΔHQ)ŠrΡkΪΨ†ͺΡk;:WvγdWOn]¬α•cκfw'|4eΗ„σ0Ό» Ξ½%S$›^jM捁a†Bχ“*KΡ#mΗ9εαpΈ4 λL˜‹6ZΥWE‘žΕDΪo30υ ³pcχTuΡRA\Ό«»?(Da6ώš©w[qšqΕΡ–ΟXH«I 4 `I&KY€:@9”8QUŽχτnβ-Œ%έ΄‘q„)bІm–!xU­Τ±dlΡ‚Υ€ΑF©oOΫ¨„ΡLPΐ=DΙ9Κ±ƒ’ lS 7·9€m6GyΔRPo―₯Ίψvϊ@ϊ·ώŸH_ Eƒ’ˆ5E.½ž³APΫjQ!f8νŠC.¦ω©L%* 976§&vQȝEΥQb'Η]™h”ΥAtNa„Α“¬’Κν!V•‚”Ε¦l”τeΩ‹ΑŠ•Ζϋω9Q*PX-θΫΕΘ\l,°κεT5LφγΞθμΣΜb=Φ\‰Ϊ#Α’όŽ( γL|:œςνᐩxI•%™Χ°ν₯WŽ[ρΡφfI·γ0Γ":ρΨ$Ά">Α`{˜―ΤΩ§7njΚφ7Eνάό!ΥΤ«ΞtΜ’sφ”γ <έΛΟ^c&Θ"…φΒΥonδNiMζ©Χ‘’Φν(eB—EX€¨‡€η.σϋ+,¨‡6Z(=M^χ₯T[ΔC3^²«φΏψκAεuœ«§½θΆ3υ‘Œ΅ςzI¬™?NωΘvχU(γ?>Έχ/뚹d­υYΎνΧYωΝϊεžΉNΩ»ο½J‚­3†o9Xόͺ³*}ߝ•œpΨŠžΞn<¬8UjQηQ†ιΣ«§<ΰ 1Β! >t5 λΥίQΘ2Κϊ(BΠX]†Qf8 w6q_Β /ρK„ Hk <Υ p\ŸΕ^ “ΕρΦ·ΕΈQκ7Pι8₯°j‚Ά#+j\8ARΊ\P˜pΌ~Κm1G=Μ «‚Q󍢡5οOΦZtΈ5υ«ͺt­*·M­zΔ³6ΒγtοV@ “IλlCVI$Ε ₯τζζH!VJΧhJΆΕ«I­dpD 9α ’1%j-Ty(kis ¦XšpΚ7Ž€dΪ²6Š5Y©Ύ\κΨežμ3YD5χ&©±‰²ΐΩ g²DCΫ3ΆVQžG Ζ6FFΙΆ[μpΈmΘ!œŽU *”Σc#/^* [H½ρœ­Ό’Š-ΌΌ\―Βrτ/匍} #ττα½ί§΄ξ;ΐί}–}lόO›v[φUjm©όneΟ‹Ό˜Z„ΚΕωzΤˆ_eΧηˆMΝφ^σ@#¬}χοχ*)ψΒΐμcE6J-)dδ6x‘l#3ZΏRoŒφD²β―ψA΄ώ@ΪIO½Β§Ι’4°wι„r˜AΤΜβ?˜Œ:beNΌφλεΔQδΒd(PX!€iΣhκ[˜uRώνά6έ" ‡P%°4TέΛKύέ8!ΜΓΔT¬ˆa0ƒ’ΓKGhΗΡΟIΏΣεΏό\ωΧWΗΡϋΪE°Σ&†hώŽ}vΜ IΆΔ1‰ΎνbΝW5Άe+εX—}°q-ρςγqόν9ϋΉ±=gΏ¦’žν‚ξ~„˜ ƒPG™8τrt™AReυH‹&-jΡι#a²h©²Ά^λ+ΦΘΗ²ʝŠ€ϊ¬οeZrχuΏ“(u ΧmΤ γuΔ2n)' σ°J γΝΚυ&:ωˆR€DAς Aήη΅’¨Ί{tU@XnœΝβκĚ£ΕΘψP!V2ί1₯!ής yψΨ6 FTηαΔ{)€ρK†{Ή#νωPΎϋΌ£š«@ξ?½6Σdε πˆρΝ΄•Xΰ„μ•ϋšιEΟΥοΟ}νΡΔlγ«9#76!δ λFΫY©”aγ±λŒM„ι6]ΜqκwμΞύΏΖαίaζ8ƒW<<ώΎRΖ¨VWn}±i( R .λ£ί2+εpnΫ]‰­ˆ1Ÿ“ζΥxFg»uŒ5wyς­œ}ΐIΏ~εΠΈΗš<>#RΕ/ΩΓΣ'4dXλ~ `xξ%Ύ`‡€ςΗ ° KRΔ΅θ4ˆ-„$%M8lΙ£=5vΡyεr\#ΫΆΚΔfή–΅ DŠtȘk:;”Xς‚½:―»Ψ¬aOψ‰RΌ&ΨU‘Ή±€k‹4ƒΣα$€Τς„£Že(ΦΡοs  >"lOδγ3κ²φqœΪtq£¨ςζ >m§qmq@BΉ‚.sΤ0‡ρψ$ 4ΜY0VhK4dΟIDTX˜G¦Δˆ8iρDΰ8©#lFΎ­wdƒH²J:isaΎXν«Gγ…MΓ’l$p –[ΒθΡVWnμΠ¨€ύ α‰Ύ(ΔY% Rς9±»X ΦWu>θη cP\Шˏ³OoAcνξΖε€=mΣc7b3…‰°‘ήΕΆ`~σ€ΖήζΎ&nώiνδΏ–—Ο5yώR‘ξΦ;|α{An2σ zk›μH{ MλaeΙםf?YΉzΗ²r‡‡Mͺ†Ddψš¦˜ι qΕAΈσdBuη(L§Ζz3β»]=TΛ9qδ²H8 €5&npw}dE4M3—•όWΆe^Τφ‹Ϊ½½†…2Σ&ΩXYbΖ9αΛΫ)Ί9ۏ ;­Υοs/n6K²žλ-¬ΡθRπ%vͺκ:˜ΊbŒ ³I»ŒΦχa@€YgΘn9ίdΡ›Άš1η'L>έMŸ/Ε¦3¦.=ι¨Μ£Γh@νεΦών·|QΌl"@ΐ^BYŒœ Sx~MΎ ΔQ§ΡΡ‘άνζ8κs¨lξž7ν0aΜ`ŒΜε½Κ8Ύγ§ΉΞh”T4υψιoVθΕuˆSmλΫ0ΆΑ*``Χ†Šn‰vο€]qέ,wθeƒγ€€Yώ 7’,4Ψ 5±^‘΅ ΓlId)ς Q{Γ&R,ή6«a€`ΝO«+νΣM·P+DB vPrωyxΨYΐΣΔφβ’nεΘxα| k!›Σ ˜eΆθLΩ/άΒBDR`˜D TJ,λg:oψ9–b‚›‘™]AΧυKՏΪ œŽinβώoi2 ŸΛoΔΪ·5zi6―]yŽyK―B˜±ίΛψ1€Έa<œ%ΰ‘m΅ΒςL “”H»ΠZΧ²J†j’VCK…ƒ0Ν%υ]dΠdԁ/鍹2Υ .Ή¦₯Ύ K„‰Ξ»\½έ:‘5σ―"νξιH^υ{uƒέ‘ζ μSΡχΡύνυ‘ ΞΏεAΣό³ Θ—V6ΒVƒς㬀”=ω1ƝWn·ŠΙδG^’b‚ΐ!Ί ς{κηΆΣ<Ιf¦Hˆό­―ΌnχΗV"eŽύκƒv‘NOOe€Zx{΅Όz™”<Μf¬/AqύOvpκϋψ`G23 «ΖŸ… υνςΛ·α\|rpίΥf‘yd―2‚Δΰ`Υ ΚνΛ.©.k4šŠ+jϊtΪyȁ’ytclN’žDΉ• ,lψ–ΧVͺ ή…ΘΞπ• ­eE``ΡM@­°υΩ…ξjΙ«±².^Nr‰7Gιρp°Ιl-¨Δ Ξ'0 e°ΖΘ”K3όΈ{·μ]Oc·Ωςχ»#Ν…†AiUαI±)¦©"΄©iŒό;9|ŒΟΒΦά6Śn5Mi#”§ŠˆdnωΰCΚοXΏ'YΦή·£·ν”u\΄θεΘΝψ5φΊ,½?S#Υ.ŒJβ±4w†Ll βk& Θ…šg«C{½ Ή2*εJQ†³’2εξ„V·@Ο‹™³ΊBπ ]υwΣ/+^h,Φzϋ±ΑvD/”X3αΒ Ή–‡Η'ε1€N†uν#όqΗΣ(VNωhE΄/Μ©½‡™Λή»4^/X"Η{JOHκ@ͺu”a`cΟGΝΒ©jΆσ2ƒD4Œ¬]„*ِήνd <››ϊ{ŒhκCίq'˜‘¬ό[‰χMέύΰ½ #z!δgΛΞ5Ώ†<ηΫςϋ›jXŽW³Υ5Ÿ3ΕjηΜ‘Ίϋή>ΰkpα> #χ–tάΙ3K+”ƒ·Mz½ΆžΎ09xxώ+ƒX½Θ« QΥz’"·[γ€R½Ψ)ΗZέWS4ΉΩΤQ›r­‹8-ΐ€ŽΘΠ–@βγηΰ_³J2fv 0‘ƒlmΞ΅eD„Pow[:°ν+— ΛZ›=.#αevέb Π< %4γΫ)̜Wu’WrYsΰIεœ~Ύ\”mΆ±ΝΚ$OΡD³„K%芯6δΰΡίkmΔjT6£4Ώ#ςΆϋΞδ 1΄Žͺ!ͺ[y$ͺesƒn,%#`1±Œ©δ…j!ήτx6ΖϋztI`V`›0Π»VΎύώΝJ’Φ_ζ{T o—eJ>#ž€ίe@¨ ·ΜɎ:jΚjρΡ tκƒ\2όRή jOΘΒ¦εCeζBSzΝΫhXζ)Ώ Sž_»ζδ•?@©šΈƒ³ ƒ’;α²ηaΎΘ$‹†0ͺr†<Qtœ)αlCqQs„aηWΊΞWeR}π-–F%ϊ±†‹¨'.ŽP‡aЎΗ2τ.f‡ΆχρKP ΚS·DΰjΏόa’ΎΜλτ)Άnψlβ‘XhֈDΖj@  ƒ‚2jY—₯οςcwΠ4A°ΐδŸ‡6YEmgo’ρεzΑήGRœΌ΄u έžηG/‘ΡΝF+2¦™œW=j½ΝΨΙSBM—!ŒF%t% ΰR6°–uΕ:awκΟ-)ΪBΊΙ‡GT‘EμŸfωΚ¬nήBFΌιz;\Η%Q^τΧΉ„Ώ ΅ν!ιΪ ΊΑλWm_€HaΑ΄²X!ε­ΟΏ ‡}—ΙkΔh/+ΎτΉ7ιdΉω²wCCutF»> ζξΞQh*hD››ΏΆϋϊ₯Ԋ1γ)ΦTe#)f8u_…†00†1ΉΤΤΔ‚{¬ΤZθP+l"]€ζv²E7·S/tOΥβˆδέ•ΈvΈ.A±‘΄ήNF§wΆZ»X(Hu ΦΛdkΕΝppˆC³'ΑqΝ:R1ιǜΌεΥΔ«ŒcαuΗ―αθΞ ”)8wύ|ΆέΦ0“xνδ …Κ;c†εQ2IUα―aŽ{T6:§t₯N¬lώ&ϋITΗξέΌυΦ_N&R:Aξa‡ Δ4€E\ ΊT6ΐD’sλ›p…΅q“„ιYsθΠ>£0¦ UbK<ΪΠ/[F‚ρ@½£1Ηvu°A4Ό”τ潡Qˆ¬–”'7kΨΊdM [μΦ4GLΑ! ζ†£Ϊ }U€œΣ€Ϋo^ν]|¦Tͺ› υϊjTδ‡`™ψφŒrΪ₯6΄ψσλ–ž@TΟB΅Ÿ˜|+ZcΰzΣπ}“ΦΠτΈ3‡dydά`~JJNΙΡΏ-—‘DŽ~ήω-ǝŠί―’ϋœςφρjΎi-ξΞμcΪ$%3~Ϋ’61ΒHI !$ oΠH£θPc§,ΝyΖ­΄ 0θώ/gt•”Rd%χτΫdƒσ-bΰΑFοw)@β“Λ!ν ˘ƈ}ΞϋFF³―jΉΘ|¬ζ==4HκΟΰ«όιgω±TG2?,”υwͺ†ϊ4«0 š)ύΡ3ξηFbόΣ…@Ψͺ·ζ(2t2_αŸάύ¦^°εΧ”θk 8FΫ&ω&σΓίω=ŸrŠ€©°Ύ*ͺ{9b²°UΆάσζ2EΖ\Š3ƒ:})„:hGΤμgeaŒ¦˜ΠFw³­ι%Ί,7&N"0 ζŽkΒ‰G‘ϋΛΧΚ/μκsLυν’¦:EΐaΞΒ,2ΙώUΰ‘Ν”λ΅ŠPB{β6.pSPKn -€ -£ΙΣE₯ΫΝΑSžI] RέΆ>|Ϋγ2ˆhCIΊ9§ϊžMΑηΘλ@½₯§„κ—UΕ}{ͺάeƒA»V»ΌπyŒκΑϊ-ͺ>pΘδΨρΚψš¦Ee9ΊŠ=ρ›¨c-u.o’΅y@οιYko{O6Ε©Γ€ΧK3" Β½7οιΕ€l“k <›­3ΙfΛ¬ρQž‘² ΫΫ^Χ†4¨FO›n±ή¬=|…΄F`τΦhhΪ Η…τΔ„0h¦iΡ“Z΅ Ε!ŽPθj‹ŠTYΨ'.9γKŠX EC0γ\RMZ#Ξκ΅B‡Kί`‚ΠͺŒ–Ζ:m’>22.\i€δ2Όr]Α'%ElUΛ-ΑΡd†Ž-ʂڏ£ Θf7ζό:κƒYLΒkEQTSM0W ¨tž#ςΊ6ό΄ΉΓӎΙZθΖp‡G§›^Ϋ ύ$· ¦SΔS˜?k5© ·ϋΒh€„Z °ΜP-€±n)0Cό+ 8‘³bπ lΔ ‚r‡ζα‡=ψσπ–κχwώΌŠΓxΔ_·τ¬Ξ’γ0΄βŠ”AJwΞ ,G0u\ήˆΑ ‰.4 U`Α@§π_‰[1Α8>ΔΤΐeητ Ή~„ω8“k.[—‡1Θ/b-yω…šm8 F€:υ ϊO’χΤ‚)NΙHξωθi΄θ3,'وθ4μΣΪΫ‹ΎB€†1ΟT‘†?¦`‡Φ00δ€uΑ έ>}“nΡ΄Xδ›Œ’K3Δώ‚*(EΥ7γk3©£Ξγ)lGιέ‰š“ΠκΔC%ξν·’ΉΗWΗπ‡Λ₯šVΛo ±ι7$:γΖ;b0 ~0}!ϊU:•‘XŒE‹Kc‘~γ†u_ΚAAJΓΣzέa=¨TύίT…ΧlΊΘΔ'γ#·}uӜ‚² lg•ύς‰N^‡Ψ4@WAW#‡λtδ-ζΥεX}GΕbQD€g«ηe£‡6―±νΫΔ#θxk ΩΤόΎtΗηοΆm€€ο?Pμύ­§'Ε—‰χ܍1HΚΚlΝΝ£Ύ«: :ˆΩo‰ψ¨ΙβσΗω²κύ΄‘>σ‘Ÿ-xYŒπ`ϊQθz;YΈ,>νηViΞ/±r , @`κΚU©3κσΚΓqΑΰQ· ―`Ύέ§³wj` ”eρ†—NΑ(gVΎ'yKαΖηΉώ| Ύ9Ÿ‰/Σ3ιϋ²lβχς~?mη!+΅Y9bΤg"γzκΜŠϊρ]˜NžP1νE*Ό HMh6φHΑlΟΌΕ 'x΅·ΩηƒΩ-VeΎ˜G,Σ*3‚±pŸ’[όΆ“ϊNDπ²Gžyr2σg&DDQQX/’Τ³L;Ÿ1`]]‘—i΄› ¨TŒh¦|To.#"Ψ ψ έŸ\4ΊΞ~:^ΦΛYf1y°βn²Υ9J]*l³ –ΪbnΙP΅iœX °γΜΜ+-ψη£*4 δ―@#. Υ«€ΪϊzΪͺς3vžλ&Ύ’Θγ³αH|«˜0οΝt―‹ c˜υo&[ŸX±ΆŽ†¬*J/Μ0ŽΌ ˆTœΊ#Hcάί ͺR₯Χ5ρ\β–Ε€c uBΧ–PΉ‚έD =ξs>΅„μ˜AεΚ”IΠՍnUhΑ;}Ή`XΉςG cšΥͺCƒί—W©F5κΦΥBβd|Ϟ²o(­h0΅x97Υ³ΟlΜfU‰fu Ÿ~ώwσ—SXy(Ί5Β3ƒ‚^έ/«!!ψ΅|o_Φ§παθ=ŒόΌΝέ―­Α[[τ5XΤh€¦$ΥΟμ’„.¦HΙiˆft=*B82α`%% {:ΐlΠ–4%‹y°ŠŽ#Kχ―f!1βΥ₯΅eΐέ~*2©»?OŸΕςφ›΅¬X“ΥμΪ‹f"0χ0mϋΟέϋΟο8Ή{ΓZΨ£HmA‚€ΕαΧ¦gψΨ–;Η=Συ»¨piη©^;­6p«o'οO€‡@ΓΡκπρx΅·Ύ΅ΜεŠη”½Gž¨†£9ͺ=”YΣ…_]J€ /νή{ωi·Ρα³L ’τ½;Χ‚Πd·Ή~ς~Α—j’¦ †A“*”4'ΟJΞJ T³υ‘R?(š,†g`«˜+V6›L&K€ίΟZPyΒQΓΓ ζځΗOΉν£b4~†Ύ¬`ύΓάφώw Χ *ΌJΈ–ςdάθ Aρ‘ΝΗ¬‰ž`daΕ„b Λ£ —3ΈΞaδ/&pκR$ dahΈ°’ wι5FΛγ>>κlw2M»ίrA_dQΉΉEΓ¨7οκpάSq δΡ…‚Α’C;Ίc35Π…?œM ‚©­¦M1;ŠGπ4ζ’Σμ‡Άm:ΥUδγvΎ»ž-ŽŽ8Za’_π™05J#pΒΜκ;Ήμ½ήΗ,ψΩΏ-Ύ―o ½ΪΫuψ8Ϊ›{ΎI“E‚OOΡ½Hφe ’Tͺͺ؝d3»κ㒈ΒEάυXζΐΜPNΊ"cl~lΉ­R΅·Ό;܌ŸΡtκ5’“ƒ ‹ 9YŠAMβ:튬ΓH£‚κ ちšgL`F!‹ύφι`:‡Ξ6Έh`PqΫ«2νHςΣξy\½όqχw}λφ6ω›zsνiΔέΈϊ[άujpcixKΟαL ρςgτY]7wΑϊΏp TόXΣŽ8šκ·=G½BQk*i-Κ14ž¦’ΪB¦–θ”|œyΫbΚ<+Ξϋο>‹\š΅Μ›.κΌCσŠh£Vσ‡vΈάΚρV4{ΰψ;][΄:ύV~¦NϊϋΉ'πΟ₯6 π3νδΫ³Πε;εψ^γ…Ύμf}΄εΆγMSσ{OΩσmςΊΪ!…O€"oτΈMΌ;Ϋtτ‚m)'w°!δH$=z€C‡ £θΆΉ9Η¨£ψiι˜™Zʔt‰Wd0‘7*ˆjKρ}ζέ?#ΦYνλ§ρ§cuΒήΉδ-Υ½Ξdγ²υ˜ύ‚B™vdΖΛ.Ώ³θγσ6eχY•Λ5Ϋν/)Pκ@'܅凉Df[Ψ§¬-wžXΦ]68eΦ\²C+αΛ™/Ό4Ά0$πiGšEΜ¦‡ 9iDBͺŠ=œΙaζθ*οΟΥ•>†Y‘Ώ˜ΐ ϊΌO γ·7Q#ͺDοrQGtΘΩ³T"Ο»^ ‘ΑΖfLεϋ] ΠΗψ8’1(˜C@Η0MvR@ΐή]™t5κΫxΪΫM±X–V#i\ h(wIRZ\‰έγ&ŒΥ>5ουτb2gΎΌlΩλ§f5ΫrΚύې„OΌτ|Βg`<2Θz/Ά{xΨΨ u6œ“@mφ¦=«8³Tζ±ͺ4ΪΎŒ- Ά+βUS’[]Ψ£xv—¨΅)9ζƒsβ7ͺ{βMΌ]"J„[Συ–ϋ³H~ΓKΥ›trσΚΥΊλΜ,ΒY9΄" ;¬F;ΨάΤ„ ΉΰϋΥCι~Ε΄xΦ (S}_χ‘‰|Ή~ Η&i:™rj’,τ :UΣ6₯†²Ήα@΅]‚‰­[(ΥΩ˜][”ς29¨˜nT^‘N‹’φ± wΣ-2S ύ–ΐ’C7λŸήρώΐς`y{ُ» ώΜα[pέΖx;η·ωYMΨ #<³”‰&υLWˆΘL ι†ε)yτ=ͺhμy0ΜΎ.βzά²Αˆκ[ŸΒ­[HpcSβ0ΑxH’²Η‰&ΆsT_Ή 鳆qLΐlνυρφ˜ΫmkEύ†Gόmικρό‡–Ύ¨–NxΤ…9  ΑΎΕ… &§Μ%ι»–Όΐ>3‘ι^²Œ]f ―μ’•}τŸ†˜kσψ6³±/!ςr‚rrAQΩF— @ 1~“|4?*€£ταδN`ΩΐeύC“¨ ZX—³ς§οsΡξxCͺχ₯I†qΖΌ™(ΝI`mώ>κ7V0‰ρ)§ΧωU·š3Ρφ97t˜ ’ο%m3ΠΧ”/!ΰžP¦ Θ*·„³8pΒzχεν"nΠδΤγAς†mς0<SZZ„n! 7Υ_gͺΎhYΪm6žSˆΌ!R²³€9{¨tβζˆ_pΚσ{1%LL΄ωo)ΈNk₯ ab…€ϋœΪ«X¬π hL` eΊvΛς˜²=LψxOX±½*μc’Χ'<Οg‘^Ύπ’Ώ‹^Όuš.Rxˆ»F[™οŠ SŸ2έθ†ΩS‘ˆo»ά8ΐ뒞κ£π-ŒΐίσύSΣ@=sτ=§Κws”@±LK‡lζαR§χEoF·Sέ†z,Π{”ED h‘ %λ‚B»±R₯ν™\Ό™‰-3·uڝz:λ3;?‹ύb湝¦ΣιΙ§1ΙΙFhγ»/Ο‘pΞjΟψu£‘ˆmH(H‹!Β;=μIŠ„˜Ϊe/ ΪD_U؏ǜTτ“‰HA,Ž%₯žΝ6W3ΩμΗOθ»NΞβΓ/%χζ~ϋώo@(–ΟΊ+ΰίψ•‰–ސN‘o_ˆ%ZΓPW>'i7+HX¨θΔ¬¦ρεϊωiΩι6°―£qK³Γμοc{»3ΎϋžŒ_+9l&:W^20zuͺ6©§y–«W‘Γ±«ήλί 5daφάbWz―υmͺ―YlΔmŒo³ •ΡˆώΞέωΫ™‚Κ_TΆU3Α‰Β™γ}I@Ζwž†ΒγpxŒ…R—jΓbŽΧ°©΄― ‡ƒd άbHp¨Dα@›Z6ώ‡βιΌΡIΣΩϊηLσ€j64ŠT1f ΤOYγ‰τ·qΐΰβΧ~ˆi©ΚgΤήβ·H)F€ αΑQ‘vΙλ9\n5 ΑΓ±§–Ωο‹Τ.yQΌqqεfθ šΉίΊ£¦ι‘πo½―¬P`γM«Ί­₯0εtΖΆ|ι{ €1‘Ž G ž.¦ͺαΛισ=1ͺρρŸ>­¨^ͺ’‰-(ΚƒΞ©πvΓΞ¨w&6c8Τ4δOͺz 8qY Ɉma#ƒo L}™lΓ2ΰέΗ¬¬p1 ΩjξEM,f:¨Ζ­¨cq†TWVxlάL6μΣb9ΓXjz{AT32v:ΑΌΣ έU•Ε:Jα% σ Θq…ρxΙn€P)Ϊ’ͺŠL]=žm―oλφυš%§υΔYJκΩqΤΉY…PμΙ,|ΟΫadΈ^Χσpδ10ΤZ΄! }-.₯‡Ύ˜π~66‡5Ό[pž|α}ϊ€αA«€ˆ΄οΎdτu’dW[CQ.‰δWu›ˆΡV`…e&m7oΧdvm₯T€?%λ½s˜X©C &Z“FP‡§ͺ%Έ{iϊSΐš(Χ ΈΌΊ{ςŒρnάΜ-zƒM6†ΣyΘι;ž^œ6<ΤKj‡ΡκAΠ„Ύ#ŽFΈ‘ϋΞLΐ‚§YΚN7Wjά±Tά_Z§&Β#–$>΅wθζŸ ς2έ 2[‹‘>K’™B ₯aΓI•bœˆi $…"'ΝaΛ1.”-cFFΫ’Υ*Ξ&ΗΔΑ}^K Α°ττό톻πΛΝτmƒcŠ4κμͺyΩ7φŸΟ“γ\€'Ξ†>ŒΙ£·f™·Νk…LΛ?eΖμš>x™Βfσœ1’ςeΆΛΔ$}ΓυCπΈσGΌ9Žπύ!ΛΨF|ΎeΚΞΡR§έRYŽ…1a¬ΛƒIkˆλύΆS^ΕΆdM[˜ϊŒy}Λj”(EˆNψT_ι‚;DXϊCg8Ž έ0”€»)tαUAF^-G—ԝΗx Ά½JΟ-TTP`' ΤVγ‹Yύο§ςδοΈγMνν-ψ΄~š(Y€to•’y΄]Ε 5eI”-'e‰eΤ³3ΐ‚  \rF¨2ΣΦΠκniςjΖ½}ψ/ονMΨUDfꐒ‚“Ψκ ν€šzͺ.91NχΨ€τ›S1WΐβC,ΞΚ‰’fΫΩΥύ‡^ηΆ_pώÈψL „ 1εξ=z¨ίFlk$γr3Μ4’CjΖνϋΜ!Ϋn#7…ςj4G`¨¨,@»tΌεςΠ"’)D lN$§}Tγ~Y‚ΰXNφ ™ƒAχΎυi=¨žQL”jF ΆΓΓκͺ|!EΞJxq(tYG%Ψΐ,K)τK§υΟΦv˜„% ’ϋ*I­4zC[V–s!sŸ’~IŠAΑ/ϋLO'ΑšΫ‚rt'`…²²Εφœ+y9VOγ\Τρωπ³V]Φγuρϋ˜-ΦΖρOLΨ¬>™ΐ,ώpξ"ΑŠKΊyάάB ± –˜ ΅CSΚaN0†΅„η[iƒw–ΟŽ}6―ΐ"z “eά±Κ—{ΆzείšώυΏN7P·NRΝ1΅›6š’Βy ₯c[JΛCυmΰ ³ΘΘάM,MGΑΟ–b|}\¬h`Ξηr€"«Ψˆˆ‹~Ψ<^,[ΰ°xϊ.χpε.^Ϊεfμ½+qπ0ιΠ‚ΣjΜ%¨s”g0&Τ+WJW₯+5Ψ“SBΗ›rΓƒJsδQ.QJ’ξI~‹-ΌΏ₯― \ΙΓ6ƒƒTι‘²=«qYk―{Ιe" «νkΥYϊξž9Gλ¬ sYI ²q1’ˆΏj`m⨃ε΄ψΎΐη œš^uΪ8Α}μήΜ掽Lͺ¬2Ύ2›Ύ$|9X©U˜2S\hά!~‘˜ϊϋ@σY²‹MδοΎj$ΟA¨Ήψ+ΛZέ56"戴3 Ρ`ΒHθΔ™žζtΈε’ ‘³ŠάMœ ΖΰY$ψ&Ύ]Sw\NΨŸ\%f½’©ž­AE0~ΒΘόCTKΖ ΐ±’Teqž΅„ΡwW'Ψ‘4³o ): ‚#δ0—«δ„Lμ>iCAφrόυΈΔ€λ¨}l‹κ>.οΧ³Ο‘νΥόάρWΣ­¨‚₯*P%uζπζzŸƒn΄”MΈ‰©}cϊ}Α‡Σ2γ~Β^Ώ[3t¦όD©υzΈ.n ί­ͺ\~Xh/’VΗE΄Ιθ£[€ΊΝμ‰ΑAλ£=X(`PQy @Οcόγ§›ΪUΈg₯‡ͺφ± cE6»ϊTK±ΊlΜαΕ@#uf3¨ΗΜΖΕ3΅Tβ‚ώ¨ύΐϋB°XfDCΑ4&1ΊκŒSΫC·_ϊz‘2°Ι9œ(ήTθœb$ΗΈ=N#ΗrΗΑ1γE* MΩΤ{8‘|~.ztηΣFν—Ό |§B4ς(&ƒσVΒtvΥ€<–`6`Κ|U² JΏ"[tρxχ Ψ`ΣZθτΰŸ¦΅j-ša,cnxυSΜρquTœNί μΕ£ˆ:·ˆ<ŠψΗ½{DΤ#ω/κt΄ΊuΠ’ZγnVaD€@ςΏ ­ƒ»ΊJ*‚< Sί₯ΛMω/¨¨d Lρ"pmž2θΕq83MχrPn­γ™μΈ·νHoΐCΌ7eήpα>|ΌˆŠ=Š©:3sX !ZlΠ›Β2ξͺ(1AŠM£.ΣgΛΫΕ?*HŠΝ’e»6τd¬„γΧV³ΫΝ ε7ρφDWΕ„λR9–P™·§=LΊϋOΆΦ xh5ήυ‹°Τ@„θΗ―bZώΨΰX7ƒxQMΙ‚2&1ιŠƒ)έ›Ϋ' ;ύYωΛm9zβš‡3ΜUΑμμǏo9#>Λ)YΉΪ{Λ2”>)˜lζ»Ώ―ΦX°<pf©„%EYJΫΜhχιΰφ“{Rž31Ό^&κ <έZœ3`§’¬=—o-‡VξC!Ξ9—]<όQυ±iK_ρpτ~μΙ¬νρ(eΟ­­ŽD|`_]D>)sX: Ό6§“xμ+ΈͺW₯γ­½±ŽaQ±› ΠΏΓ’Λ―σΕ'(λ‡λύΟ_ ¬%ΡίŠuΦV@¬:ψω¬€ΰεK;•#ΏΣΥξΜrŒΓ<ΓKPmCwœf IO~Ί`2ŒϋωNώ£Σkε>Ξ8„½9m‡lͺυ”σK=^uŽ™λbVξχ©σΓq蒁±`ZdEeh-ζ4ό”ΆˆM@±%DΫ6λΞΙ $2zќk’^ϋULΗ―Γ‚“:΅— ŸEœ‰²N «l =ΎΗ΄βύWέχ΄0ΫLό­ύœύ\],ΎΡ=T,άΊσCc¦΄`@ιπλ΅Ηύ¦lΡ:Η·Γ±όHpΩ„L›½1/$ή±,¬lςω ΣZΚΔbΑLεekšΦ±Si"lœ•,σ8}=@¨ΑŒcΗ38a §”XkΑ λ•ωζ†θŒΖΏ½hφ9όoπέΗ„άε27m«MΪι^ΐT_7ΈΑ@κ{1†ͺ!‚n z-ΙχΙWxf½T<u5Ζπ %¨:κβξΑ³'ϋΧGŸΖ‡»XPΔPj-¬eά ŽHuΡCλ[Ϋc³H0ΦtΦΰ₯§ˆ‰ώθζν‚   –‰…Χtz«@£"if°άf”‘·€ΦRΦ― Μ7³Εμ1Šp `χOγ:Ά’QϊΤ@«W2š'ΜC2₯yvΔ*‘Š“νA½w­ή₯Ϊ »QtΒ5FΗ`!₯MΏg¨φ|φ›D₯ΜFV€z{J4Τ<§…Sj*V‚)“u9kαX|gd»U3$Tw^)΅­@`a‹·”Ηd2φφY“ •‡!ωΗΪ§]Bx¨ΤI΄Θ§ !Lj;ΔχΉŽ`Ό)ہDzKyΫ§TΫ)Qοα.zξεˆκ+”bS€ήUœ1 ‘fχυ₯>ΫJh“‰ΖB\‡ζΔΨd4Ά±,{ϊ­ƒ‹χ(X-ϊgJ’’Š7ζ‹Άψ™1Η¨ξtΤ0‡pΔϋ•gg½Εs°έoRD°γv#% gu• hDMρ˜RžuJωΊ`NΣ;[ΔΔύeX-ίο8Ζn  Ψ˜έΪ2β₯k£ϊ»YΏvυJ~χ•­•†=֏dί‡AVŸ¨΄"/?Mϊ’4μέ«²=‘Œ ‡φ;aeRU…W' \α@ώ4” CAΏN©:ηRXτΰ°xΟν’ΰΰό˜ €P ‡n—Σ|ŸΧI?J„―Pu~ΟˆΕ‡Θ…!3ζ³ΰ’ ;,6΄$άb Ξ‚DΖ€Gόΐ›jοΕWΆlξC¨E  t©©.”5 ¨5¨}Έn=iα_i}/ˆΊΟw™\ζ€ΐΐΐ½ξ+ΥΕώ»Y εbyt—CλΆGt<ΫP ƒ`5Ά΅ύ3q΄/6bm{NθzŝMG$μ›Τζϊψ,°ξ† Ό('8J“Ž’h@n7°8!+ϊ :Y“œ_~ΐΌΈ€/ΒΕk+K£€ΏxΞЕb3 ,*Uͺΐμ6‹h₯nΆαγΞBCΒOwφa0u„ΰ™ΑCΊ’‰Ο}΄κζΧ9'%μ‡l °ιΌšƒkψpΙΖh`sɚ>n㠈‘)”ŒBσ†1 HCΚi '^@Φ ς `ΓΖ-†ƒI¬υ™O›‡?žf†"PPg$Žc)0QZ¦’ev[³ΤώœA!ΌB Ζ½ Ή`ZΛ•θηJ²“Ζ/λ‹Φ:XΌi+½z΅_2‘₯―°κ‘Qλ&ΰUuΧW“PhfΣ’?²Α± ά4’RF €6X}εiο\™ϊΗΊl:ΖκΏφ' {C1Ε‡‘“lu0DJ+ΠκW 9π1Γα  –^ρt„OD}\—’`ΒMHΔX€“˜¨J„€ G‚ΣωΖ–ͺh²λ„·΄CΓζyZ³ΘΑbyV ΛPȚ"gv"šlΝΉΒ .”€ ΡP$ΊEZ‘.“*οe•fΔ@–~ž#Qλ5φf^οωQr’€ )1‘.lJe€ΣΜ;`‘…,$q!qƒDAM$Vr±1€&—qΓ ζ½–£U»~σ˜uΒΪέYβΩ·HΐΒΤ """-[ Q|αq…Ž­₯Ž&(mPξΡΝ,’2 ΉGΉΑH"#· ¨@Ž€$uIP‘€–d,ΐŽ0»?IΌQSA΄Ϊ@ψ4 Άΐγ>@sβ €Wό AόΦ₯Ε0mΙ¨€¬ 1zCά=Σέ=Σγ-A⦄tfπ/meρ³9ΏΈ?ιPξψ#|½ΙMp]ƒΜyL„Ώ«>(ΘςωϋB*^d™Μ—(ќΘ.0ϋI+ΔdΜd1ϊ Βκ·R=Cƒ)="Š9I#Ϊn ύβEe‡YgΧΐ^=τ ppiά¨(ΚkC!rΞ «Γ Z§Δ²?³$Ÿ–pLκQ³"+\d(QA—%ΙΝbΈΠΨ/ΐŠ₯άTCb;¬ jγγ-G“ Π`Γ`ƒ–₯¨ΞSΕ«Wύ‰w-ομkϋG§οjk/: ΘΝ*ι<ϊ±εI ϊq> B¬8†’L`»€:€ςx}A€½θΰΔǜ\λhβ7τ#pΝη—Θό­°ξ0”g(„δ#MΖoh―@’Π±;ΘxΞ=zKω7ο–ώŒ»{RώΚ yU vΣ€Y,‰bΈ €³#21ή‘Έ#NΠ€sγΘ3Q@h+5 Zυ‚¨)B>±*NPW…€ ΔBx:JθΓΣΒ΅ ±Αΐ§Y™ –«’ja;”†Α`L5,“Ž2rC £„Θ"δC8ŽΣy€±Κ (h zΠVΙκ<£2sRf"–!.c1h³q%¬ΐVVTR1ΜXΟp6hZA€ZŽ 8ƒιaΦΟ4`*χ’RΪI6―ϋ€Ο‘ά‘ΑˆΑˆPΕχ>0FiI!9ωθ1A¨4™ΕŸΪid‹$€„ΩΞ \Ίΐ\ΐ\ΐ\ΐΰkYƒ9¬Π#ZZ‰λn·LR3‘1q ij5"’εJ ‘ηθςά$vϊ:ό«―·ΉΆ2Όy‚Δ+cq›ΚΕX3@h ™fSξωξθκlκ]'Œ5&6#Ζ3P€dW!­T ‘V†€u£­³X΅`΄ Z¨KΰΦΎλ7¦ˆ1#`ΐϋ¨7$ŽC*u|δ˜7†Ν¦£7΅¬{SΜώΛΠκ†°lcU–€Θζœ dΒ©EΣ%LA&4Μƒς fAΞ.pη9Ξc˜ζ/Jρ`ŽPΪƒBΏ1FsΥ4EJ’ ΐh…‘#B©@¨ŒEΑwΈƒΌΘ!`wœ`–9Λ½>œδ v‚θ •¬ΰͺFaf/ύάήδ\BΨ!|Γ@!Ba7™ ΰΠΔ­K©’εΞ`Ύ@χDgΓK2kη"‚2fCˆΤ ˆ {φ$‹1QzΔΐψ˜ΛΌ\e[γ ¨ƒˆ 8€EήoΘΙ”:ΛΧ«%»―</Ύf ΏΏ/}Ά^““v³ ©λ€g!XeΚ‹LqΗz02£I¨gq¨•W“#πμςιν*lkG<"ΩR•4–’iXj5©20dŽλΚͺόg)΄DγRPœΰ$9­ Ε΄’Φ *2‹(» RΔFδ±4 ΙΘ0ωΩβ €uμonΒϋϋηΛw―–U–n#§  pP#cln[˜LΣ•‚ΐΑ5„Δ`Δ„@œΚ …αXq‘x€U%¬Λ—.PΗΟr!@Δ!€ΐ茠(Σ–›¬@nBƒœN˜€GBBΐ:: :ΠD±η<ζpYχ­~{Y΅.±YφNΝΪ‘ΈΉ’+Ύb—0Ά!l †Γa¬Φk5¬‘‰Ÿ?§ϊ“νοΨ’Q˜³i7[,ͺ]έάwMΕΨ7p ΔF°5¬ Κc–ξΠ΄ΨδΠυΔP#,a0ΒĎ7pz©‹DFθŠΑW΄6›Mf£Τ+Βαj3*c3θο9І “““&‚yŒΦD4­I 'ΔHB)I%pŠΠΈzW₯ΜεYN%ΟθύY‡ΧΉX1#ΐ1ŒύΠHΟJ`~‰ξsδp 1δŸδώ™ϋΪ:£ϊ¦%'€»YΈΫΩ:OψΩ ¨‰αξ /λξƒϊ‡φ£EΡx³¨™ό:Cg—7LΣŽή…κΨ‘3ώ |f180Iΰšμά9†XUω‘‘)‘„„1ްμ„|‡γ9(0Φ 5†δ<ΕgΒ +_|;Zε” ήG1J†­ΉάGJlΩΏΩ-oYŽ<ϋΙR>—Sf¬ Ε+Œ%Ρl/°yΝ5†±Ÿ ϊΛΕw›Θ9υξ3*}δτc―Η§ΟΘΝ/4λω$°M@¬ˆΚͺe•–Ζ.Ξά‹J la“e" ‡ίœhώ·¬,θ=g ΰpΌλΟ™±Χ^Ι¬ΘυZΣ’~ _An½%ŽϋΤΒjDΗηΡ"›ό‘“5‡ΥΜ=DW±ΚL‹—›²λm Π0ύ5ώA’fΑ“[2Ύ(Bd²iΙ>¦qx mgMΥ_θUG:/ Ζ|E‡ΘUXcCCΝpFŸˆlωπΚςΞχα•ΰ=Δύ{Ν8Χ7Ω °F?€E (ΐ, $ƒ˜·σƒίΒ‹Y\˜QvRHm§ ”.\½% A…(Ι*”Cχ3MΎ/Tυ½ησίΡ~㝊n›ω.;Άώ{ŽŸΠqέΧΰΑχΗμ}o<ϊ?—/P_lλφ žC—ϊίεΙλτ˜[£Ύ+‰’hυΓl(ƒΦζμˆgŸcύ"«„§X <~αœΑ(ιΏp>SόPšΞžDΟz\ŒcΌϋΫ"kΞδvέRˆ ‚ι†δmύ£ίžΟρηΚε>ΣϊΥO½³{’ψ£x/qςΊ°?ZκΓο›fέ“ΆtΟά?‘2 … 6­%λyk‘Ήΰϊ–uΏ Š[–¬ΨΒ₯~DΒΌoύU3εW`ΝMj‡α|i«s§Š'eΟαV ϋ4―ΡGΆ“ώ«*«5ΈΚhϋd}γX¦­²cΥ\₯ΔΡϋ%4Žν;ήΪ6O?J~w­θގ•‡#Χ‰λIΚ‰Ζq7B7Cήκa;ΛC“m‡CRIάθ_¨ε:ΉψσΚό"ž?† [ŠΗξ~ˆ;φ'ό'uσi·«ς<ϊd–§N&Θ'i‘f„Mjͺg6υw Qœί#υΉ RΧζ»/^€m#με~ί?GS©κϊοK 3εΈ­~ε¬:“β(Β‰mΝψrrΦ‚~`ά΄ΪνL,βιάnEˆR‰B%’Μ£Žμϊ%|s+³ΗEδωΉL؏7F•šΣΡ!IΆ½2;τ{~cιi6–FΔ<·? zΈφβ>ΥD‘‹ž“%u7χΊυ؎²d ‹E•DL2BθωGB‰PΨΠΰο*ό6ξΓΗΝΝΪψ—¨ι,NœΩ O§g9‘žPl>$ξ±]=|ΛfBbsοδ›ΉΉ,q₯!ΤpζΚ>[48ZΘ²£³Λze­MΫΓ «;‹ΝάB„Œό.}π8| κ]cςσD jψ;εkλmΝ_βΤρβœBρ³σƒ/΅9οΙ―qoD§’Η“sTUB‹»8 h”MΫπN;ύ1αͺΨθyzΰ_ΜcͺGž(’RμŽzΖυπ‘£ξγƒμ«ΛŽrο$9NβŽΫ’ξ¦cƒ6¨3$dΙPPa)δΟ‡Α1R. λ€zWπΥmΉηΙλλ^-'ˆΡΡJΤaΖό²‘2£Xno8ΓΒΓ"λβў6λƞ3δΥ½Υ)ΥΗE©Ξͺϊ"απΰ @ΛΙr܌7―EC³°bg GνF"ΝόŽύ±ρΊΕςŸny2Ώ–κ]Ώ­tVδ«ςΉuε½jΥB‘ιk}οΔ7φΕρπψ}°d0ϋΛQ < @Η€>ΪgδŒyL{>$­―AN: ΙυΪD‘ςΌZΕ‘‚ϋ-€2†Ψ<‚Πjš5S€Ϋf"ΙQ΅0B ε‡y‘ΡψΔ“δ6MF‘‰‘`k ˜oΓƒ!ϊυΙcΣ3Α·ΒΆΞP‘Γ1@*X8xςΥ‘[eω°δ‰Χ₯f…ξ/ίΣUΐb!x77$Ds·…l8‡Β»…hCMΌgGΆ ΄½ bEqζhζΥwόUΦ„z#RžΦ΄š•Άλ<%Πςί³ΟΉ _Xԏ1ͺƈωCĜl-π"‹ ŽΊζ{‡±L!B΄Yμ X²΅²%ӠƏ6Ϊ–NΚͺ₯v–ΪiΏ΄ό2ω\˜Κ°φ*]?p>‘dˆΩT(Ί§Οϊnύސ58Υ߷ßgN›r˜Ζ΅ΡςλmάwLO֏¦–hg»…ΗX&9ΐ«@ݚfρc£xρlΛΟ_‰Ϊ<=Φςβά#sΟϊ¦Ά΅„ϋ}-iM8ϊŠSv&΅Η˜<ω”@ZΧ9ϋξ_xB‚&ύ™φhΠΣGζ)­―sρͺΚΖι:K΄DD€°\g‹œό 7±tDŒ“` Li11B+[†ΛΨ’ˆyc΄x3CMΖ (Θ2Ύσj_8©K#P »˜L|½ς$r‡!R Σ0( f…‘l[–ς%Eκ[¨% t.ΛJ£@^2jΰ¨7‹ζ5#°Lί!§ΐfŽb‘Τ$0cb(F(κ½Ώά0')P”ŠͺΒΰM ΑΐXHF@ήhΑ£ΎPΈAγΔηβŠ(‘Δk6@eΞN₯œΐ˜’P ‡έ&–A{Ζ`ˆ€π/•―pŒό\ΑΒ xr‡QΪaέΞ kAOWYγπx©δΆ¨“ͺΰ‹‰Π…’·ρ†j?™nZ*α™ΈΆCKοœ'?ΓD¨‘qhτή\­.|D …β’”Ν\ŐPPΠ"6d8Ζ2  ²ζη €  ‘½Π°σšΓ¬2%ε6β°‰¬!Ζ€Β-σ„Β°” ‡Dƒμ¬²Ο)Ϊ}x@ρžBΊj¬˜P@ΐ>…O† η)d,Ήs|!g܊&₯2 g—€ρςυΓͺ7N]ζδ JU>Γ™ζ F^ιQɈη>sθΐ–9 œπσxqTy3ν½¨~V΄0”ε/AΈχ{ΚβZηžφKKιΈUYω[ΔΘ. b*‡<ŠA(:-Žs#‡zΔΔΤXy[BͺΉO8eΖtΞ^Vy Β3β)ΩQ°¨σXXNΏq‰ι/Ύγƒ^rX[ ‡Q5QvΨAAΆά,²¨x·3Iœ‡³|8m’G1uΩv½6Η9Κr%yl.!9τ7Ζj,+&Ξ; ύΞ’P‹ P3ΰ)X"ZršLΖ³VomPζΙtŒ™5τ³G!@±c°d-· 0$L€‰ΘSMΧHν3”QΠs% Οϊ (‰΄‘A+ˆ9qœΗ €\e βπΎ%aN—‚‚ŽΛ”Ύ*•P}κηXΒ!eƒˆ3–Έd:DZ߁vΜBBvw²ΐΡsΊExzά:ЁCpD‘«9Ώ‘Τς”υ“ψθΗkΝQ_χΖ^r6;ˆο4 ƒ―ŽEΊΐtΔ‚x·QΌ«“‘‰Ύ±~ΣXοt/R*ͺœH@S"G“OVΙΎ³―}Vq–Χ Š!)”›­Ίƒ Ήt²Όœ aD.2 5˜xα!ϊC€j¦€Τ¨δΩ³σΈ’Δ†R'„PλΰC=@k^`¬F’Σ”›8PΫ.:Θ‘'H"<₯`ΰpUkζΡΙΛ(τœE<Β}$B…”‘4£QΠX[Νq\O₯œΩh.ΊˆB1.ΘΔΦwΜ1¨&_£Œάo#ΣτξI8cnμδ1ηkkhπ ͺ-J #εœ!ΒΔqχ4’CΌk;₯ΛVg|₯vfwŒ}Bm‹ί_ƒΞͺ½­pμwAπ‹C¦`9 ­»pcŒqΔxaΈ-’*ΌΆ'w{-ξ™Ϋ‰œυ‘Žα£ΜΒ³šΕ!Τi!—Τ=>a2t’Λ4ΆΑ4σ—°i…3l+0XVˆDΆA˜DXyO)ε2Λθ15g”ΚR _˜™P©θ;kθeξθγΒvlŒIΫ†ΈΈ4@Γ8ΐΣtέ₯ Κ~°7yƒΐ}ό¦SθϞν₯~Ž―νX΅ύΑ†“PΟΜ?ΞΓYυzZDπτӟ.ς%iW•Ήžid³·SH©†Œ`ƒdŽε6ΎT£O heO˜κkΞ-Vπ» ΦXΜζxπ—~ώΤ¨ώπ΄EEΕ(‰§έ±ΉΕBW2`ͺ%l¬ˆ ¦ΠVά³_kpΓτRΒDμΙ¦|ΈΐςΔ C²•Ÿ' QNUΖ“ΣΩCΙXνΛX;‘ΩGiοψ=vdƒO˜€hδzΎoϏC•Ξθέ»z€©i[c²ύϊqaΊ˜ƒgΤ9ΗƒγΎPμŽlX3Œ`F[ €['ίR•δ―Δ{Ug|VIΞr0£œΡbό6_rΪPμΩ΄ο˜σ5‘ŽϊNL—-–ΨΫ­KƒžΗΔϊy{F+°ϋε‡†ζxsu—Υ|c}Ηi…ςΈ‰LFx0π9TrNΑτoπ3GΣυύΟ OΩέL«•yβRΒ’‚@ζ2 $#S‡πΓΉ8B±_][Φ%s…PλF€Ν)A/49™©%©ΆΫm·­kV Z“9ΞχaμΑΉQΆΫmΆΫϋ¬ΦΑΔθ^#Τ<‡Ο(’‹‘Θf"‘β}ŽAξJc#NFΆ.ζΉΌΠ#α‚ ΑF%–‰_Σ)ΞU“ιƒΟG8Ή_ς‰jΗ™5‰q“< HJ+9Žfβ|ϊΞς υχ½D+mΪl7ω’gI3ί}e%A¬Lσ Uh—Fη[πΘ bxΗdΣJΝ°Ό˜BΒPβGX‚`ε_†xσ0u(ΫΗ ΊX  ‡hέGΘ Δθυμ§ cKhuΒ%ΧLlσ‚qψ  t³`πΒm0ήtpά2χ=‚?Fͺ‰K°ΕAαή\ς{Bκος£iVsˆ£’zJ!f[šB>.^#wHγξeFSeόŽ©π<5­„Ad—wΎ-°(ΨM3(δy­·F*Ί&ώΓνE.δΖ„8‚c·nerΔΚtΞ €%–βdρΈL>˜–DΥ>fΗ¦Υ ·‘&ΣΊšWήδ7Δ‚k‘IΙΦ5Ήθ8ο<κ‡yC@ΐω”οϋJΑΌŠΕ3NΏ¦R»ο…FdζΡΰ8Νgέ»Β± Ίd#TL+ο˜† $γ1ά Χάί†ƒC::1[Œ —„(ƒ οƒK£ΒΛμ$˜d£GΞ¨Άb|†ό*V.υ―YεΪό§£Y^oU]„!A¦ƒΣ‰GA=-: >ΩάN.[Υg·§C―δζN5Άίγ3¦ )#ΩFΌ6 ³zσΒ1މLΎκ"?‡KG2Φθ9(tR₯ΔδΗ4Lρ"1ΦM™žΎb7eσ%—(Λb₯;Dλ¦{0ΒqΨbΔ^W3C’ψΠ.Xραs‰Apν›ΛŽs"Η1 gή>R%& H•Œ.1$|&“ΖTb3¨π—ž"g€Όζ>“1aÌυ›Ž³°τžƒ”όΑ—΄ε2“0 Ε±\:9B ααd<DBΙσ‡οŒ'‡›όΐΒ oή…ΝSΚΌ>ΘύΠΗΘ8Ξ'« ϟνi|hφΌ_:tτNIΏ₯π,θύΒμΈq>Ο–‘Ÿͺ"άυT†ΟάύΏεRK?ή?φ‡Θ1’.$~‰ϊΑΨyΚȐ$žΈΞΡέθχ¬d`m34,Ay Μ Ž}Sο 8ΐT`@δ(’ΕFγκΠ¬ ˜f„4,r›ŒJ(£P`l06ƒ7ㇴ›A@ϊΖ²Ε2f$-μδ6—7°Νg1bŒM cε!π 6FC8Ξဴ:§ΰϋ Ιο:₯"ͺͺPq“ιhOͺvΘ†|ξ:άϋνμ³γ;&°¦<‡Sθ<ήΖΏ,:»gΆL'-9iΛNZrΣ–œ΄©°oG˜ηΊήυκ{Ο3ŸΖDβbͺ·Ή>ΰoŠβjw|3π#Ρ,ygί σΨ›·l‚± ‘V£Τͺ€\’©_φœζοffόαυηdΠόPς?jzΚΒΩν‰AύCψ‡YˆΞsφ€ήNg_9Ρ―9ͺΗύΔΠ†cέ-bƒx8κΠ ΘΣ37G΅ξθθޞC?@‡ςCΘy!δ<†g zΨ§Ž£sρOΆψμrxIœΗΐH‰ξ}_¦νYXΚO¬‰μMΣύi°=“¨Ά­ |!}H{ƒ!yμŽΪঊЧ¦ Q°žmοίs gΗ49Tω§¦|r±RĐ=²Xη!{Q‘‘ν`?VŒδιŸ,Φ΅«šΗ,υ3Ϋ¨ƒ#"‡ΌΓΜpˆ¦$ψI/Νψ»’)Β„‡/ :pvroom/R/utils.R0000644000176200001440000000605214533651065013113 0ustar liggesusersenv_to_logical <- function (var, default = TRUE) { res <- Sys.getenv(var, default) if (res %in% c("1", "yes", "true")) { TRUE } else if (res %in% c("0", "no", "false")) { FALSE } else { default } } is_windows <- function() { identical(tolower(Sys.info()[["sysname"]]), "windows") } is_loaded <- function(pkg) { isTRUE(pkg[[1]] %in% loadedNamespaces()) } `%||%` <- function(x, y) if (is.null(x)) y else x collapse_transformer <- function(regex = "[*]$", ...) { function(text, envir) { if (grepl(regex, text)) { text <- sub(regex, "", text) res <- eval(parse(text = text, keep.source = FALSE), envir) glue::glue_collapse(res, ...) } else { glue::identity_transformer(text, envir) } } } is_named <- function (x) { nms <- names(x) if (is.null(nms)) { return(FALSE) } all(nms != "" & !is.na(nms)) } deparse2 <- function(expr, ..., sep = "\n") { paste(deparse(expr, ...), collapse = sep) } is_syntactic <- function(x) make.names(x) == x # Conditionally exported in zzz.R # @export compare.spec_tbl_df <- function (x, y, ...) { attr(x, "spec") <- NULL attr(x, "problems") <- NULL attr(y, "spec") <- NULL attr(y, "problems") <- NULL class(x) <- setdiff(class(x), "spec_tbl_df") class(y) <- setdiff(class(y), "spec_tbl_df") NextMethod("compare") } # Conditionally exported in zzz.R # @export compare_proxy.spec_tbl_df <- function(x, path) { attr(x, "spec") <- NULL attr(x, "problems") <- NULL class(x) <- setdiff(class(x), "spec_tbl_df") if ("path" %in% names(formals(waldo::compare_proxy))) { list(object = x, path = path) } else { x } } # Conditionally exported in zzz.R # @export as_tibble.spec_tbl_df <- function(x, ...) { attr(x, "spec") <- NULL attr(x, "problems") <- NULL class(x) <- setdiff(class(x), "spec_tbl_df") NextMethod("as_tibble") } # Conditionally exported in zzz.R # @export all.equal.spec_tbl_df <- function(target, current, ...) { attr(target, "spec") <- NULL attr(target, "problems") <- NULL attr(current, "spec") <- NULL attr(current, "problems") <- NULL class(target) <- setdiff(class(target), "spec_tbl_df") class(current) <- setdiff(class(current), "spec_tbl_df") NextMethod("all.equal") } # Conditionally exported in zzz.R # @export as.data.frame.spec_tbl_df <- function(x, ...) { attr(x, "spec") <- NULL attr(x, "problems") <- NULL class(x) <- setdiff(class(x), "spec_tbl_df") NextMethod("as.data.frame") } is_rstudio_console <- function() { !(Sys.getenv("RSTUDIO", "") == "" || Sys.getenv("RSTUDIO_TERM", "") != "") } is_rstudio_version <- function(min, max = .Machine$integer.max) { tryCatch( expr = { version <- rstudioapi::getVersion() version >= min && version < max }, error = function(e) FALSE ) } #' @importFrom methods setOldClass setOldClass(c("spec_tbl_df", "tbl_df", "tbl", "data.frame")) utctime <- function(year, month, day, hour, min, sec, psec) { utctime_(as.integer(year), as.integer(month), as.integer(day), as.integer(hour), as.integer(min), as.integer(sec), as.numeric(psec)) } vroom/R/zzz.R0000644000176200001440000000304714504102737012604 0ustar liggesusers.onUnload <- function(libpath) { library.dynam.unload("vroom", libpath) } .onLoad <- function(...) { tzdb::tzdb_initialize() # only register conflicting S3 methods if readr is not already loaded. if (!"readr" %in% loadedNamespaces()) { s3_register("base::format", "col_spec") s3_register("base::print", "col_spec") s3_register("base::print", "collector") s3_register("base::print", "date_names") s3_register("base::print", "locale") s3_register("utils::str", "col_spec") s3_register("base::all.equal", "spec_tbl_df") s3_register("base::as.data.frame", "spec_tbl_df") s3_register("tibble::as_tibble", "spec_tbl_df") s3_register("testthat::compare", "spec_tbl_df") s3_register("waldo::compare_proxy", "spec_tbl_df") } } .conflicts.OK <- TRUE s3_register <- function(generic, class, method = NULL) { stopifnot(is.character(generic), length(generic) == 1) stopifnot(is.character(class), length(class) == 1) pieces <- strsplit(generic, "::")[[1]] stopifnot(length(pieces) == 2) package <- pieces[[1]] generic <- pieces[[2]] if (is.null(method)) { method <- get(paste0(generic, ".", class), envir = parent.frame()) } stopifnot(is.function(method)) if (package %in% loadedNamespaces()) { registerS3method(generic, class, method, envir = asNamespace(package)) } # Always register hook in case package is later unloaded & reloaded setHook( packageEvent(package, "onLoad"), function(...) { registerS3method(generic, class, method, envir = asNamespace(package)) } ) } vroom/R/col_types.R0000644000176200001440000004664114533651065013764 0ustar liggesusers#' Create column specification #' #' `cols()` includes all columns in the input data, guessing the column types #' as the default. `cols_only()` includes only the columns you explicitly #' specify, skipping the rest. #' #' The available specifications are: (long names in quotes and string abbreviations in brackets) #' #' | function | long name | short name | description | #' | ---------- | ----------- | ---------- | ------------- | #' | `col_logical()` | "logical" | "l" | Logical values containing only `T`, `F`, `TRUE` or `FALSE`. | #' | `col_integer()` | "integer" | "i" | Integer numbers. | #' | `col_big_integer()` | "big_integer" | "I" | Big Integers (64bit), requires the `bit64` package. | #' | `col_double()` | "double", "numeric" | "d" | 64-bit double floating point numbers. #' | `col_character()` | "character" | "c" | Character string data. | #' | `col_factor(levels, ordered)` | "factor" | "f" | A fixed set of values. | #' | `col_date(format = "")` | "date" | "D" | Calendar dates formatted with the locale's `date_format`. | #' | `col_time(format = "")` | "time" | "t" | Times formatted with the locale's `time_format`. | #' | `col_datetime(format = "")` | "datetime", "POSIXct" | "T" | ISO8601 date times. | #' | `col_number()` | "number" | "n" | Human readable numbers containing the `grouping_mark` | #' | `col_skip()` | "skip", "NULL" | "_", "-" | Skip and don't import this column. | #' | `col_guess()` | "guess", "NA" | "?" | Parse using the "best" guessed type based on the input. | #' #' @param ... Either column objects created by `col_*()`, or their abbreviated #' character names (as described in the `col_types` argument of #' [vroom()]). If you're only overriding a few columns, it's #' best to refer to columns by name. If not named, the column types must match #' the column names exactly. In `col_*()` functions these are stored in the #' object. #' @param .default Any named columns not explicitly overridden in `...` #' will be read with this column type. #' @param .delim The delimiter to use when parsing. If the `delim` argument #' used in the call to `vroom()` it takes precedence over the one specified in #' `col_types`. #' @export #' @aliases col_types #' @examples #' cols(a = col_integer()) #' cols_only(a = col_integer()) #' #' # You can also use the standard abbreviations #' cols(a = "i") #' cols(a = "i", b = "d", c = "_") #' #' # Or long names (like utils::read.csv) #' cols(a = "integer", b = "double", c = "skip") #' #' # You can also use multiple sets of column definitions by combining #' # them like so: #' #' t1 <- cols( #' column_one = col_integer(), #' column_two = col_number()) #' #' t2 <- cols( #' column_three = col_character()) #' #' t3 <- t1 #' t3$cols <- c(t1$cols, t2$cols) #' t3 cols <- function(..., .default = col_guess(), .delim = NULL) { col_types <- list2(...) is_character <- vapply(col_types, is.character, logical(1)) col_types[is_character] <- lapply(col_types[is_character], col_concise) if (is.character(.default)) { .default <- col_concise(.default) } col_spec(col_types, .default, .delim) } #' @export #' @rdname cols cols_only <- function(...) { cols(..., .default = col_skip()) } # col_spec ---------------------------------------------------------------- col_spec <- function(col_types, default = col_guess(), delim) { stopifnot(is.list(col_types)) stopifnot(is.collector(default)) is_collector <- vapply(col_types, is.collector, logical(1)) if (any(!is_collector)) { stop("Some `col_types` are not S3 collector objects: ", paste(which(!is_collector), collapse = ", "), call. = FALSE) } structure( list( cols = col_types, default = default, delim = delim ), class = "col_spec" ) } is.col_spec <- function(x) inherits(x, "col_spec") #' Coerce to a column specification #' #' This is most useful for generating a specification using the short form or coercing from a list. #' #' @param x Input object #' @keywords internal #' @examples #' as.col_spec("cccnnn") #' @export as.col_spec <- function(x) UseMethod("as.col_spec") #' @export as.col_spec.character <- function(x) { if (is_named(x)) { return(as.col_spec(as.list(x))) } letters <- strsplit(x, "")[[1]] col_spec(lapply(letters, col_concise), col_guess(), delim = NULL) } #' @export as.col_spec.NULL <- function(x) { col_spec(list(), delim = NULL) } #' @export as.col_spec.list <- function(x) { do.call(cols, x) } #' @export as.col_spec.col_spec <- function(x) { if (!"delim" %in% names(x)) { x["delim"] <- list(NULL) } x } #' @export as.col_spec.default <- function(x) { stop("`col_types` must be NULL, a list or a string", call. = FALSE) } # Conditionally exported in zzz.R # @export print.col_spec <- function(x, n = Inf, condense = NULL, colour = crayon::has_color(), ...) { cat(format.col_spec(x, n = n, condense = condense, colour = colour, ...)) invisible(x) } #' @description #' `cols_condense()` takes a spec object and condenses its definition by setting #' the default column type to the most frequent type and only listing columns #' with a different type. #' @rdname spec #' @export cols_condense <- function(x) { types <- vapply(x$cols, function(xx) class(xx)[[1]], character(1)) counts <- table(types) most_common <- names(counts)[counts == max(counts)][[1]] x$default <- x$cols[types == most_common][[1]] x$cols <- x$cols[types != most_common] x } # Conditionally exported in zzz.R # @export format.col_spec <- function(x, n = Inf, condense = NULL, colour = crayon::has_color(), ...) { if (n == 0) { return("") } # condense if cols >= n condense <- condense %||% (length(x$cols) >= n) if (isTRUE(condense)) { x <- cols_condense(x) } # truncate to minumum of n or length cols <- x$cols[seq_len(min(length(x$cols), n))] default <- NULL if (inherits(x$default, "collector_guess")) { fun_type <- "cols" } else if (inherits(x$default, "collector_skip")) { fun_type <- "cols_only" } else { fun_type <- "cols" type <- sub("^collector_", "", class(x$default)[[1]]) default <- paste0(".default = col_", type, "()") } delim <- x$delim if (!is.null(delim) && nzchar(delim)) { delim <- paste0('.delim = ', glue::double_quote(delim), '') } cols_args <- c( default, vapply(seq_along(cols), function(i) { col_funs <- sub("^collector_", "col_", class(cols[[i]])[[1]]) args <- vapply(cols[[i]], deparse2, character(1), sep = "\n ") args <- paste(names(args), args, sep = " = ", collapse = ", ") col_funs <- paste0(col_funs, "(", args, ")") col_funs <- colourise_cols(col_funs, colour) col_names <- names(cols)[[i]] %||% "" # Need to handle unnamed columns and columns with non-syntactic names named <- col_names != "" non_syntactic <- !is_syntactic(col_names) & named col_names[non_syntactic] <- paste0("`", gsub("`", "\\\\`", col_names[non_syntactic]), "`") out <- paste0(col_names, " = ", col_funs) out[!named] <- col_funs[!named] out }, character(1) ), delim ) if (length(x$cols) == 0 && length(cols_args) == 0) { return(paste0(fun_type, "()\n")) } out <- paste0(fun_type, "(\n ", paste(collapse = ",\n ", cols_args)) if (length(x$cols) > n) { out <- paste0(out, "\n # ... with ", length(x$cols) - n, " more columns") } out <- paste0(out, "\n)\n") out } colourise_cols <- function(cols, colourise = crayon::has_color()) { if (!isTRUE(colourise)) { return(cols) } fname <- sub("[(].*", "", cols) for (i in seq_along(cols)) { cols[[i]] <- switch(fname, col_skip = , col_guess = cols[[i]], col_character = , col_factor = crayon::red(cols[[i]]), col_logical = crayon::yellow(cols[[i]]), col_double = , col_integer = , col_big_integer = , col_number = green(cols[[i]]), col_date = , col_datetime = , col_time = blue(cols[[i]]) ) } cols } # This allows str() on a tibble object to print a little nicer. # Conditionally exported in zzz.R # @export str.col_spec <- function(object, ..., indent.str = "") { # Split the formatted column spec into strings specs <- strsplit(format(object), "\n")[[1]] cat(sep = "", "\n", # Append the current indentation string to the specs paste(indent.str, specs, collapse = "\n"), "\n") } #' Examine the column specifications for a data frame #' #' `spec()` extracts the full column specification from a tibble #' created by readr. #' #' @family parsers #' @param x The data frame object to extract from #' @return A col_spec object. #' @export #' @examples #' df <- vroom(vroom_example("mtcars.csv")) #' s <- spec(df) #' s #' #' cols_condense(s) spec <- function(x) { stopifnot(inherits(x, "tbl_df")) attr(x, "spec") } col_concise <- function(x) { switch(x, "_" = , "skip" =, "NULL" =, "-" = col_skip(), "NA" = , "?" = col_guess(), character =, c = col_character(), factor =, f = col_factor(), double =, numeric =, d = col_double(), integer =, i = col_integer(), big_integer =, I = col_big_integer(), logical = , l = col_logical(), number = , n = col_number(), date = , Date = , D = col_date(), datetime = , POSIXct = , T = col_datetime(), time =, t = col_time(), stop("Unknown shortcut: ", x, call. = FALSE) ) } vroom_enquo <- function(x) { if (quo_is_call(x, "c") || quo_is_call(x, "list")) { return(as_quosures(get_expr(x)[-1], get_env(x))) } x } vroom_select <- function(x, col_select, id) { spec <- attr(x, "spec") # Drop any NULL columns is_null <- vapply(x, is.null, logical(1)) x[is_null] <- NULL # reorder and rename columns if (inherits(col_select, "quosures") || !quo_is_null(col_select)) { if (inherits(col_select, "quosures")) { vars <- tidyselect::vars_select(c(names(spec(x)$cols), id), !!!col_select) } else { vars <- tidyselect::vars_select(c(names(spec(x)$cols), id), !!col_select) } if (!is.null(id) && !id %in% vars) { names(id) <- id vars <- c(id, vars) } # This can't be just names(x) as we need to have skipped # names as well to pass to vars_select() x <- x[vars] names(x) <- names(vars) } attr(x, "spec") <- spec x } col_types_standardise <- function(spec, num_cols, col_names, col_select, name_repair) { if (num_cols == 0) { if (length(spec$cols) > 0) { num_cols <- length(spec$cols) } else if (length(col_names) > 0) { num_cols <- length(col_names) } } if (length(col_names) == 0) { col_names <- make_names(NULL, num_cols) } col_names <- vctrs::vec_as_names(col_names, repair = name_repair) type_names <- names(spec$cols) if (length(spec$cols) == 0) { # no types specified so use defaults spec$cols <- rep(list(spec$default), num_cols) names(spec$cols) <- col_names[seq_along(spec$cols)] } else if (is.null(type_names)) { # unnamed types & names guessed from header: match exactly if (num_cols < length(spec$cols)) { spec$cols <- spec$cols[seq_len(num_cols)] } else { spec$cols <- c(spec$cols, rep(list(spec$default), num_cols - length(spec$cols))) } names(spec$cols) <- col_names[seq_along(spec$cols)] } else { # named types if (num_cols > length(col_names)) { col_names <- make_names(col_names, num_cols) } bad_types <- !(type_names %in% col_names) if (any(bad_types)) { warn(paste0("The following named parsers don't match the column names: ", paste0(type_names[bad_types], collapse = ", ")), class = "vroom_mismatched_column_name") spec$cols <- spec$cols[!bad_types] type_names <- type_names[!bad_types] } default_types <- !(col_names %in% type_names) if (any(default_types)) { defaults <- rep(list(spec$default), sum(default_types)) names(defaults) <- col_names[default_types] spec$cols[names(defaults)] <- defaults } spec$cols <- spec$cols[col_names] } if (inherits(col_select, "quosures") || !quo_is_null(col_select)) { if (inherits(col_select, "quosures")) { to_keep <- names(spec$cols) %in% tidyselect::vars_select(names(spec$cols), !!!col_select, .strict = FALSE) } else { to_keep <- names(spec$cols) %in% tidyselect::vars_select(names(spec$cols), !!col_select, .strict = FALSE) } spec$cols[!to_keep] <- rep(list(col_skip()), sum(!to_keep)) } # Set the names, ignoring skipped columns kept <- !vapply(spec$cols, inherits, logical(1), "collector_skip") # Fill the column names if they are shorter than what is kept. if (length(col_names) == length(spec$cols)) { names(spec$cols)[kept] <- col_names[kept] } else if (length(col_names) == sum(kept)) { names(spec$cols)[kept] <- col_names } else { col_names <- make_names(col_names, sum(kept)) names(spec$cols)[kept] <- col_names } spec } #' Guess the type of a vector #' #' @inheritParams readr::guess_parser #' @examples #' # Logical vectors #' guess_type(c("FALSE", "TRUE", "F", "T")) #' # Integers and doubles #' guess_type(c("1","2","3")) #' guess_type(c("1.6","2.6","3.4")) #' # Numbers containing grouping mark #' guess_type("1,234,566") #' # ISO 8601 date times #' guess_type(c("2010-10-10")) #' guess_type(c("2010-10-10 01:02:03")) #' guess_type(c("01:02:03 AM")) #' @export guess_type <- function(x, na = c("", "NA"), locale = default_locale(), guess_integer = FALSE) { type <- guess_type_(x, na = na, locale = locale, guess_integer = guess_integer) get(paste0("col_", type), asNamespace("vroom"))() } guess_parser <- function(x, na = c("", "NA"), locale = default_locale(), guess_integer = FALSE) { guess_type_(x, na = na, locale = locale, guess_integer = guess_integer) } show_dims <- function(x) { cli_block(class = "vroom_dim_message", { cli::cli_text(" {.strong Rows: }{.val {NROW(x)}} {.strong Columns: }{.val {NCOL(x)}} ") }) } collector_value <- function(x, ...) { UseMethod("collector_value") } #' @export collector_value.collector_character <- function(x, ...) { character() } #' @export collector_value.collector_double <- function(x, ...) { numeric() } #' @export collector_value.collector_integer <- function(x, ...) { integer() } #' @export collector_value.collector_numeric <- function(x, ...) { numeric() } #' @export collector_value.collector_logical <- function(x, ...) { logical() } #' @export collector_value.collector_factor <- function(x, ...) { factor() } # the more obvious as.POSIXct(double()) doesn't work on R < 4.0 # https://github.com/tidyverse/vroom/issues/453 #' @export collector_value.collector_datetime <- function(x, ...) { vctrs::vec_ptype(Sys.time()) } # the more obvious as.Date(double()) doesn't work on R < 4.0 # and again: https://github.com/tidyverse/vroom/issues/453 #' @export collector_value.collector_date <- function(x, ...) { vctrs::vec_ptype(Sys.Date()) } #' @export collector_value.collector_time <- function(x, ...) { hms::hms() } #' @export collector_value.collector_guess <- function(x, ...) { character() } #' @export summary.col_spec <- function(object, width = getOption("width"), locale = default_locale(), ...) { if (length(object$cols) == 0) { return(invisible(object)) } type_map <- c("collector_character" = "chr", "collector_double" = "dbl", "collector_integer" = "int", "collector_number" = "num", "collector_logical" = "lgl", "collector_factor" = "fct", "collector_datetime" = "dttm", "collector_date" = "date", "collector_time" = "time", "collector_guess" = "???") col_types <- vapply(object$cols, function(x) class(x)[[1]], character(1)) col_types <- droplevels(factor(type_map[col_types], levels = unname(type_map))) type_counts <- table(col_types) n <- length(type_counts) types <- format(vapply(names(type_counts), color_type, character(1))) counts <- format(glue::glue("({type_counts})"), justify = "right") col_width <- min(width - (crayon::col_nchar(types) + nchar(counts) + 4)) columns <- vapply(split(names(object$cols), col_types), function(x) glue::glue_collapse(x, ", ", width = col_width), character(1)) fmt_num <- function(x) { prettyNum(x, big.mark = locale$grouping_mark, decimal.mark = locale$decimal_mark) } delim <- object$delim %||% "" txt <- glue::glue( .transformer = collapse_transformer(sep = "\n"), entries = glue::glue("{format(types)} {counts}: {columns}"), ' {if (nzchar(delim)) paste(bold("Delimiter:"), glue::double_quote(delim)) else ""} {entries*} ') cli_block(class = "vroom_spec_message", { cli::cli_h1("Column specification") cli::cli_verbatim(txt) }) invisible(object) } show_col_types <- function(x, locale) { show_dims(x) summary(spec(x), locale = locale) cli_block(class = "vroom_spec_message", { cli::cli_verbatim("\n\n") cli::cli_alert_info("Use {.fn spec} to retrieve the full column specification for this data.") cli::cli_alert_info("Specify the column types or set {.arg show_col_types = FALSE} to quiet this message.") }) } cli_block <- function(expr, class = NULL, type = rlang::inform) { msg <- "" withCallingHandlers( expr, message = function(x) { msg <<- paste0(msg, x$message) invokeRestart("muffleMessage") } ) msg <- sub("^\n", "", msg) msg <- sub("\n+$", "", msg) type(msg, class = class) } color_type <- function(type) { switch(type, chr = , fct = crayon::red(type), lgl = crayon::yellow(type), dbl = , int = , num = green(type), date = , dttm = , time = blue(type), "???" = type ) } #' @rdname cols #' @export col_logical <- function(...) { collector("logical", ...) } #' @rdname cols #' @export col_integer <- function(...) { collector("integer", ...) } #' @rdname cols #' @export col_big_integer <- function(...) { collector("big_integer", ...) } #' @rdname cols #' @export col_double <- function(...) { collector("double", ...) } #' @rdname cols #' @export col_character <- function(...) { collector("character", ...) } #' @rdname cols #' @export col_skip <- function(...) { collector("skip", ...) } #' @rdname cols #' @export col_number <- function(...) { collector("number", ...) } #' @rdname cols #' @export col_guess <- function(...) { collector("guess", ...) } #' @inheritParams readr::col_factor #' @rdname cols #' @export col_factor <- function(levels = NULL, ordered = FALSE, include_na = FALSE, ...) { collector("factor", levels = levels, ordered = ordered, include_na = include_na, ...) } #' @inheritParams readr::col_datetime #' @rdname cols #' @export col_datetime <- function(format = "", ...) { collector("datetime", format = format, ...) } #' @rdname cols #' @export col_date <- function(format = "", ...) { collector("date", format = format, ...) } #' @rdname cols #' @export col_time <- function(format = "", ...) { collector("time", format = format, ...) } vroom/R/vroom_fwf.R0000644000176200001440000001414014505650504013750 0ustar liggesusers#' Read a fixed width file into a tibble #' #' @details #' *Note*: `fwf_empty()` cannot take a R connection such as a URL as input, as #' this would result in reading from the connection twice. In these cases it is #' better to download the file first before reading. #' @inheritParams readr::read_fwf #' @inheritParams vroom #' @export #' @examples #' fwf_sample <- vroom_example("fwf-sample.txt") #' writeLines(vroom_lines(fwf_sample)) #' #' # You can specify column positions in several ways: #' # 1. Guess based on position of empty columns #' vroom_fwf(fwf_sample, fwf_empty(fwf_sample, col_names = c("first", "last", "state", "ssn"))) #' # 2. A vector of field widths #' vroom_fwf(fwf_sample, fwf_widths(c(20, 10, 12), c("name", "state", "ssn"))) #' # 3. Paired vectors of start and end positions #' vroom_fwf(fwf_sample, fwf_positions(c(1, 30), c(20, 42), c("name", "ssn"))) #' # 4. Named arguments with start and end positions #' vroom_fwf(fwf_sample, fwf_cols(name = c(1, 20), ssn = c(30, 42))) #' # 5. Named arguments with column widths #' vroom_fwf(fwf_sample, fwf_cols(name = 20, state = 10, ssn = 12)) vroom_fwf <- function(file, col_positions = fwf_empty(file, skip, n = guess_max), col_types = NULL, col_select = NULL, id = NULL, locale = default_locale(), na = c("", "NA"), comment = "", skip_empty_rows = TRUE, trim_ws = TRUE, skip = 0, n_max = Inf, guess_max = 100, altrep = TRUE, altrep_opts = deprecated(), num_threads = vroom_threads(), progress = vroom_progress(), show_col_types = NULL, .name_repair = "unique") { verify_fwf_positions(col_positions) if (!is_missing(altrep_opts)) { deprecate_warn("1.1.0", "vroom_fwf(altrep_opts = )", "vroom_fwf(altrep = )") altrep <- altrep_opts } file <- standardise_path(file) if (!is_ascii_compatible(locale$encoding)) { file <- reencode_file(file, locale$encoding) locale$encoding <- "UTF-8" } if (length(file) == 0 || (n_max == 0 & identical(col_positions$col_names, FALSE))) { out <- tibble::tibble() class(out) <- c("spec_tbl_df", class(out)) return(out) } if (n_max < 0 || is.infinite(n_max)) { n_max <- -1 } if (guess_max < 0 || is.infinite(guess_max)) { guess_max <- -1 } col_select <- vroom_enquo(enquo(col_select)) has_col_types <- !is.null(col_types) col_types <- as.col_spec(col_types) out <- vroom_fwf_(file, as.integer(col_positions$begin), as.integer(col_positions$end), trim_ws = trim_ws, col_names = col_positions$col_names, col_types = col_types, col_select = col_select, name_repair = .name_repair, id = id, na = na, guess_max = guess_max, skip = skip, comment = comment, skip_empty_rows = skip_empty_rows, n_max = n_max, num_threads = num_threads, altrep = vroom_altrep(altrep), locale = locale, progress = progress) out <- tibble::as_tibble(out, .name_repair = .name_repair) out <- vroom_select(out, col_select, id) class(out) <- c("spec_tbl_df", class(out)) if (should_show_col_types(has_col_types, show_col_types)) { show_col_types(out, locale) } out } #' @rdname vroom_fwf #' @inheritParams readr::read_fwf #' @export #' @param n Number of lines the tokenizer will read to determine file structure. By default #' it is set to 100. fwf_empty <- function(file, skip = 0, col_names = NULL, comment = "", n = 100L) { file <- standardise_one_path(standardise_path(file)[[1]]) if (inherits(file, "connection")) { stop("`file` must be a regular file, not a connection", call. = FALSE) } if (n < 0 || is.infinite(n)) { n <- -1 } out <- whitespace_columns_(file[[1]], skip, comment = comment, n = n) out$end[length(out$end)] <- NA col_names <- fwf_col_names(col_names, length(out$begin)) out$col_names <- col_names out } #' @rdname vroom_fwf #' @export #' @param widths Width of each field. Use NA as width of last field when #' reading a ragged fwf file. #' @param col_names Either NULL, or a character vector column names. fwf_widths <- function(widths, col_names = NULL) { pos <- cumsum(c(1L, abs(widths))) fwf_positions(pos[-length(pos)], pos[-1] - 1L, col_names) } #' @rdname vroom_fwf #' @export #' @param start,end Starting and ending (inclusive) positions of each field. #' Use NA as last end field when reading a ragged fwf file. fwf_positions <- function(start, end = NULL, col_names = NULL) { stopifnot(length(start) == length(end)) col_names <- fwf_col_names(col_names, length(start)) tibble::tibble( begin = start - 1L, end = end, # -1 to change to 0 offset, +1 to be exclusive, col_names = as.character(col_names) ) } #' @rdname vroom_fwf #' @export #' @param ... If the first element is a data frame, #' then it must have all numeric columns and either one or two rows. #' The column names are the variable names. The column values are the #' variable widths if a length one vector, and if length two, variable start and end #' positions. The elements of `...` are used to construct a data frame #' with or or two rows as above. fwf_cols <- function(...) { x <- lapply(list(...), as.integer) names(x) <- fwf_col_names(names(x), length(x)) x <- tibble::as_tibble(x) if (nrow(x) == 2) { fwf_positions(as.integer(x[1, ]), as.integer(x[2, ]), names(x)) } else if (nrow(x) == 1) { fwf_widths(as.integer(x[1, ]), names(x)) } else { stop("All variables must have either one (width) two (start, end) values.", call. = FALSE) } } fwf_col_names <- function(nm, n) { nm <- nm %||% rep("", n) nm_empty <- (nm == "") nm[nm_empty] <- paste0("X", seq_len(n))[nm_empty] nm } verify_fwf_positions <- function(col_positions) { is_greater <- stats::na.omit(col_positions$begin > col_positions$end) if (any(is_greater)) { bad <- which(is_greater) stop("`col_positions` must have begin less than end.\n* Invalid values at position(s): ", paste0(collapse = ", ", bad), call. = FALSE) } } vroom/R/example.R0000644000176200001440000000142614505365054013405 0ustar liggesusers#' Get path to vroom examples #' #' vroom comes bundled with a number of sample files in #' its 'inst/extdata' directory. Use `vroom_examples()` to list all the #' available examples and `vroom_example()` to retrieve the path to one #' example. #' @param path Name of file. #' @param pattern A regular expression of filenames to match. If `NULL`, all #' available files are returned. #' @export #' @examples #' # List all available examples #' vroom_examples() #' #' # Get path to one example #' vroom_example("mtcars.csv") vroom_example <- function (path) { system.file("extdata", path, package = "vroom", mustWork = TRUE) } #' @rdname vroom_example #' @export vroom_examples <- function (pattern = NULL) { list.files(system.file("extdata", package = "vroom"), pattern = pattern) } vroom/R/cpp11.R0000644000176200001440000000562414510121461012666 0ustar liggesusers# Generated by cpp11: do not edit by hand force_materialization <- function(x) { invisible(.Call(`_vroom_force_materialization`, x)) } vroom_materialize <- function(x, replace) { .Call(`_vroom_vroom_materialize`, x, replace) } vroom_convert <- function(x) { .Call(`_vroom_vroom_convert`, x) } vroom_str_ <- function(x) { .Call(`_vroom_vroom_str_`, x) } gen_character_ <- function(n, min, max, values, seed, seed2) { .Call(`_vroom_gen_character_`, n, min, max, values, seed, seed2) } guess_type_ <- function(input, na, locale, guess_integer) { .Call(`_vroom_guess_type_`, input, na, locale, guess_integer) } convert_connection <- function(in_con, out_con, from, to) { .Call(`_vroom_convert_connection`, in_con, out_con, from, to) } vroom_ <- function(inputs, delim, quote, trim_ws, escape_double, escape_backslash, comment, skip_empty_rows, skip, n_max, progress, col_names, col_types, col_select, name_repair, id, na, locale, guess_max, num_threads, altrep) { .Call(`_vroom_vroom_`, inputs, delim, quote, trim_ws, escape_double, escape_backslash, comment, skip_empty_rows, skip, n_max, progress, col_names, col_types, col_select, name_repair, id, na, locale, guess_max, num_threads, altrep) } has_trailing_newline <- function(filename) { .Call(`_vroom_has_trailing_newline`, filename) } vroom_rle <- function(input) { .Call(`_vroom_vroom_rle`, input) } utctime_ <- function(year, month, day, hour, min, sec, psec) { .Call(`_vroom_utctime_`, year, month, day, hour, min, sec, psec) } vroom_errors_ <- function(errors) { .Call(`_vroom_vroom_errors_`, errors) } vroom_fwf_ <- function(inputs, col_starts, col_ends, trim_ws, col_names, col_types, col_select, name_repair, skip, comment, skip_empty_rows, n_max, id, na, locale, guess_max, num_threads, altrep, progress) { .Call(`_vroom_vroom_fwf_`, inputs, col_starts, col_ends, trim_ws, col_names, col_types, col_select, name_repair, skip, comment, skip_empty_rows, n_max, id, na, locale, guess_max, num_threads, altrep, progress) } whitespace_columns_ <- function(filename, skip, n, comment) { .Call(`_vroom_whitespace_columns_`, filename, skip, n, comment) } vroom_write_ <- function(input, filename, delim, eol, na_str, col_names, append, options, num_threads, progress, buf_lines) { invisible(.Call(`_vroom_vroom_write_`, input, filename, delim, eol, na_str, col_names, append, options, num_threads, progress, buf_lines)) } vroom_write_connection_ <- function(input, con, delim, eol, na_str, col_names, options, num_threads, progress, buf_lines, is_stdout, append) { invisible(.Call(`_vroom_vroom_write_connection_`, input, con, delim, eol, na_str, col_names, options, num_threads, progress, buf_lines, is_stdout, append)) } vroom_format_ <- function(input, delim, eol, na_str, col_names, append, options, num_threads, progress, buf_lines) { .Call(`_vroom_vroom_format_`, input, delim, eol, na_str, col_names, append, options, num_threads, progress, buf_lines) } vroom/R/date.R0000644000176200001440000000464114533651065012672 0ustar liggesusers#' Create or retrieve date names #' #' When parsing dates, you often need to know how weekdays of the week and #' months are represented as text. This pair of functions allows you to either #' create your own, or retrieve from a standard list. The standard list is #' derived from ICU (`https://site.icu-project.org`) via the _stringi_ package. #' #' @param mon,mon_ab Full and abbreviated month names. #' @param day,day_ab Full and abbreviated week day names. Starts with Sunday. #' @param am_pm Names used for AM and PM. #' @export #' @examples #' date_names_lang("en") #' date_names_lang("ko") #' date_names_lang("fr") date_names <- function(mon, mon_ab = mon, day, day_ab = day, am_pm = c("AM", "PM")) { stopifnot(is.character(mon), length(mon) == 12) stopifnot(is.character(mon_ab), length(mon_ab) == 12) stopifnot(is.character(day), length(day) == 7) stopifnot(is.character(day_ab), length(day_ab) == 7) structure( list( mon = enc2utf8(mon), mon_ab = enc2utf8(mon_ab), day = enc2utf8(day), day_ab = enc2utf8(day_ab), am_pm = enc2utf8(am_pm) ), class = "date_names" ) } #' @export #' @rdname date_names #' @param language A BCP 47 locale, made up of a language and a region, #' e.g. `"en_US"` for American English. See `date_names_langs()` #' for a complete list of available locales. date_names_lang <- function(language) { stopifnot(is.character(language), length(language) == 1) symbols <- date_symbols[[language]] if (is.null(symbols)) { stop("Unknown language '", language, "'", call. = FALSE) } symbols } #' @export #' @rdname date_names date_names_langs <- function() { names(date_symbols) } # Conditionally exported in zzz.R # @export print.date_names <- function(x, ...) { cat("\n") if (identical(x$day, x$day_ab)) { day <- paste0(x$day, collapse = ", ") } else { day <- paste0(x$day, " (", x$day_ab, ")", collapse = ", ") } if (identical(x$mon, x$mon_ab)) { mon <- paste0(x$mon, collapse = ", ") } else { mon <- paste0(x$mon, " (", x$mon_ab, ")", collapse = ", ") } am_pm <- paste0(x$am_pm, collapse = "/") cat_wrap("Days: ", day) cat_wrap("Months: ", mon) cat_wrap("AM/PM: ", am_pm) } is.date_names <- function(x) inherits(x, "date_names") cat_wrap <- function(header, body) { body <- strwrap(body, exdent = nchar(header)) cat(header, paste(body, collapse = "\n"), "\n", sep = "") } vroom/R/path.R0000644000176200001440000002037214531407007012701 0ustar liggesusersis_ascii_compatible <- function(encoding) { identical(iconv(list(charToRaw("\n")), from = "ASCII", to = encoding, toRaw = TRUE)[[1]], charToRaw("\n")) } # this is about the encoding of the file (contents), not the filepath reencode_file <- function(path, encoding) { if (length(path) > 1) { stop(sprintf("Reading files of encoding '%s' can only be done for single files at a time", encoding), call. = FALSE) } if (inherits(path[[1]], "connection")) { in_con <- path[[1]] } else { in_con <- file(path[[1]]) } out_file <- tempfile() out_con <- file(out_file) convert_connection(in_con, out_con, encoding, "UTF-8") withr::defer(unlink(out_file), envir = parent.frame()) return(list(out_file)) } # These functions adapted from https://github.com/tidyverse/readr/blob/192cb1ca5c445e359f153d2259391e6d324fd0a2/R/source.R standardise_path <- function(path, arg = caller_arg(path), call = caller_env(), user_env = caller_env(2)) { if (is.raw(path)) { return(list(rawConnection(path, "rb"))) } if (inherits(path, "connection")) { return(list(standardise_connection(path))) } if (is_list(path)) { is_connection <- vapply(path, function(x) inherits(x, "connection"), logical(1)) if (all(is_connection)) { return(lapply(path, standardise_connection)) } if (any(is_connection)) { cli::cli_abort( "{.arg {arg}} cannot be a mix of connection and non-connection inputs", call = call ) } } if (!is.character(path)) { cli::cli_abort( c( "{.arg {arg}} is not one of the supported inputs:", "*" = "A filepath or character vector of filepaths", "*" = "A connection or list of connections", "*" = "Literal or raw input" ), call = call ) } if (inherits(path, "AsIs")) { if (length(path) > 1) { path <- paste(path, collapse = "\n") } return(list(chr_to_file(path, envir = parent.frame()))) } if (any(grepl("\n", path))) { lifecycle::deprecate_soft( "1.5.0", "vroom(file = 'must use `I()` for literal data')", details = c( " " = "", " " = "# Bad:", " " = 'vroom("X,Y\\n1.5,2.3\\n")', " " = "", " " = "# Good:", " " = 'vroom(I("X,Y\\n1.5,2.3\\n"))' ), user_env = user_env ) return(list(chr_to_file(path, envir = parent.frame()))) } as.list(enc2utf8(path)) } standardise_connection <- function(con) { # If the connection is `stdin()`, change it to `file("stdin")`, as we don't # support text mode connections. if (con == stdin()) { return(file("stdin")) } con } standardise_one_path <- function (path, write = FALSE) { if (is.raw(path)) { return(rawConnection(path, "rb")) } if (!is.character(path)) { return(path) } if (is_url(path)) { if (requireNamespace("curl", quietly = TRUE)) { con <- curl::curl(path) } else { inform("`curl` package not installed, falling back to using `url()`") con <- url(path) } ext <- tolower(tools::file_ext(path)) return( switch(ext, zip = , bz2 = , xz = { close(con) stop("Reading from remote `", ext, "` compressed files is not supported,\n", " download the files locally first.", call. = FALSE) }, gz = gzcon(con), con ) ) } path <- enc2utf8(path) p <- split_path_ext(basename_utf8(path)) if (write) { path <- normalizePath_utf8(path, mustWork = FALSE) } else { path <- check_path(path) } if (is_installed("archive")) { formats <- archive_formats(p$extension) extension <- p$extension while(is.null(formats) && nzchar(extension)) { extension <- split_path_ext(extension)$extension formats <- archive_formats(extension) } if (!is.null(formats)) { p$extension <- extension if (write) { if (is.null(formats[[1]])) { return(archive::file_write(path, filter = formats[[2]])) } return(archive::archive_write(path, p$path, format = formats[[1]], filter = formats[[2]])) } if (is.null(formats[[1]])) { return(archive::file_read(path, filter = formats[[2]])) } return(archive::archive_read(path, format = formats[[1]], filter = formats[[2]])) } } if (!write) { compression <- detect_compression(path) } else { compression <- NA } if (is.na(compression)) { compression <- tools::file_ext(path) } if (write && compression == "zip") { stop("Can only read from, not write to, .zip", call. = FALSE) } switch(compression, gz = gzfile(path, ""), bz2 = bzfile(path, ""), xz = xzfile(path, ""), zip = zipfile(path, ""), if (!has_trailing_newline(path)) { file(path) } else { path } ) } split_path_ext <- function(path) { regex <- "^([^.]*)[.](.*)" res <- regexpr(regex, path, perl = TRUE) if (length(res) == 0 || res == -1) { return(list(path = path, extension = "")) } starts <- attr(res, "capture.start")[1, ] lengths <- attr(res, "capture.length")[, ] list( path = substr(path, starts[[1]], starts[[1]] + lengths[[1]] - 1), extension = substr(path, starts[[2]], starts[[2]] + lengths[[2]] - 1) ) } # Adapted from archive:::format_and_filter_by_extension # https://github.com/r-lib/archive/blob/125f9930798dc20fa12cda30319ca3e9a134a409/R/archive.R#L73 archive_formats <- function(ext) { switch(ext, "7z" = list("7zip", "none"), "cpio" = list("cpio", "none"), "iso" = list("iso9660", "none"), "mtree" = list("mtree", "none"), "tar" = list("tar", "none"), "tgz" = list("tar", "gzip"), "taz" = list("tar", "gzip"), "tar.gz" = list("tar", "gzip"), "tbz" = list("tar", "bzip2"), "tbz2" = list("tar", "bzip2"), "tz2" = list("tar", "bzip2"), "tar.bz2" = list("tar", "bzip2"), "tlz" = list("tar", "lzma"), "tar.lzma" = list("tar", "lzma"), "txz" = list("tar", "xz"), "tar.xz" = list("tar", "xz"), "tzo" = list("tar", "lzop"), "taZ" = list("tar", "compress"), "tZ" = list("tar", "compress"), "tar.zst"= list("tar", "zstd"), "warc" = list("warc", "none"), "jar" = list("zip", "none"), "zip" = list("zip", "none"), "Z" = list(NULL, "compress"), "zst" = list(NULL, "zst"), NULL) } is_url <- function(path) { grepl("^((http|ftp)s?|sftp)://", path) } check_path <- function(path) { if (file.exists(path)) { return(normalizePath_utf8(path, mustWork = FALSE)) } stop("'", path, "' does not exist", if (!is_absolute_path(path)) { paste0(" in current working directory ('", getwd(), "')") }, ".", call. = FALSE ) } is_absolute_path <- function(path) { grepl("^(/|[A-Za-z]:|\\\\|~)", path) } zipfile <- function(path, open = "r") { files <- utils::unzip(path, list = TRUE) file <- files$Name[[1]] if (nrow(files) > 1) { inform(paste0("Multiple files in zip: reading '", file, "'")) } unz(path, file, open = open) } utils::globalVariables("con") chr_to_file <- function(x, envir = parent.frame()) { out <- tempfile() con <- file(out, "wb") writeLines(sub("\n$", "", x), con, useBytes = TRUE) close(con) withr::defer(unlink(out), envir = envir) normalizePath_utf8(out) } detect_compression <- function(path) { bytes <- readBin(path, "raw", n = 6) if (length(bytes) >= 2 && bytes[[1]] == 0x1f && bytes[[2]] == 0x8b) { return("gz") } if (length(bytes) >= 6 && bytes[[1]] == 0xFD && bytes[[2]] == 0x37 && bytes[[3]] == 0x7A && bytes[[4]] == 0x58 && bytes[[5]] == 0x5A && bytes[[6]] == 0x00) { return("xz") } if (length(bytes) >= 3 && bytes[[1]] == 0x42 && bytes[[2]] == 0x5a && bytes[[3]] == 0x68) { return("bz2") } # normal zip if (length(bytes) >= 4 && bytes[[1]] == 0x50 && bytes[[2]] == 0x4B && bytes[[3]] == 0x03 && bytes[[4]] == 0x04) { return("zip") } # empty zip if (length(bytes) >= 4 && bytes[[1]] == 0x50 && bytes[[2]] == 0x4B && bytes[[3]] == 0x05 && bytes[[4]] == 0x06) { return("zip") } # spanned zip if (length(bytes) >= 4 && bytes[[1]] == 0x50 && bytes[[2]] == 0x4B && bytes[[3]] == 0x07 && bytes[[4]] == 0x08) { return("zip") } NA_character_ } basename_utf8 <- function(path) { enc2utf8(basename(path)) } normalizePath_utf8 <- function(path, winslash = "/", mustWork = NA) { enc2utf8(normalizePath(path, winslash = winslash, mustWork = mustWork)) } vroom/R/collector.R0000644000176200001440000000044414533651065013740 0ustar liggesuserscollector <- function(type, ...) { structure(list(...), class = c(paste0("collector_", type), "collector")) } is.collector <- function(x) inherits(x, "collector") # Conditionally exported in zzz.R # @export print.collector <- function(x, ...) { cat("<", class(x)[1], ">\n", sep = "") } vroom/R/vroom-package.R0000644000176200001440000000073414504102737014502 0ustar liggesusers#' @keywords internal #' @aliases vroom-package #' @useDynLib vroom, .registration = TRUE "_PACKAGE" ## usethis namespace: start #' @import rlang #' @importFrom bit64 integer64 #' @importFrom crayon blue #' @importFrom crayon bold #' @importFrom crayon cyan #' @importFrom crayon green #' @importFrom crayon reset #' @importFrom crayon silver #' @importFrom glue glue #' @importFrom lifecycle deprecate_warn #' @importFrom lifecycle deprecated ## usethis namespace: end NULL vroom/R/locale.R0000644000176200001440000000770414533651065013217 0ustar liggesusers#' Create locales #' #' A locale object tries to capture all the defaults that can vary between #' countries. You set the locale in once, and the details are automatically #' passed on down to the columns parsers. The defaults have been chosen to #' match R (i.e. US English) as closely as possible. See #' `vignette("locales")` for more details. #' #' @param date_names Character representations of day and month names. Either #' the language code as string (passed on to [date_names_lang()]) #' or an object created by [date_names()]. #' @param date_format,time_format Default date and time formats. #' @param decimal_mark,grouping_mark Symbols used to indicate the decimal #' place, and to chunk larger numbers. Decimal mark can only be `,` or #' `.`. #' @param tz Default tz. This is used both for input (if the time zone isn't #' present in individual strings), and for output (to control the default #' display). The default is to use "UTC", a time zone that does not use #' daylight savings time (DST) and hence is typically most useful for data. #' The absence of time zones makes it approximately 50x faster to generate #' UTC times than any other time zone. #' #' Use `""` to use the system default time zone, but beware that this #' will not be reproducible across systems. #' #' For a complete list of possible time zones, see [OlsonNames()]. #' Americans, note that "EST" is a Canadian time zone that does not have #' DST. It is *not* Eastern Standard Time. It's better to use #' "US/Eastern", "US/Central" etc. #' @param encoding Default encoding. #' @export #' @examples #' locale() #' locale("fr") #' #' # South American locale #' locale("es", decimal_mark = ",") locale <- function(date_names = "en", date_format = "%AD", time_format = "%AT", decimal_mark = ".", grouping_mark = ",", tz = "UTC", encoding = "UTF-8") { if (is.character(date_names)) { date_names <- date_names_lang(date_names) } stopifnot(is.date_names(date_names)) if (missing(grouping_mark) && !missing(decimal_mark)) { grouping_mark <- if (decimal_mark == ".") "," else "." } else if (missing(decimal_mark) && !missing(grouping_mark)) { decimal_mark <- if (grouping_mark == ".") "," else "." } stopifnot(is.character(decimal_mark), length(decimal_mark) == 1) stopifnot(is.character(grouping_mark), length(grouping_mark) == 1) if (decimal_mark == grouping_mark) { stop("`decimal_mark` and `grouping_mark` must be different", call. = FALSE) } tz <- check_tz(tz) check_encoding(encoding) structure( list( date_names = date_names, date_format = date_format, time_format = time_format, decimal_mark = decimal_mark, grouping_mark = grouping_mark, tz = tz, encoding = encoding ), class = "locale" ) } is.locale <- function(x) inherits(x, "locale") # Conditionally exported in zzz.R # @export print.locale <- function(x, ...) { cat("\n") cat("Numbers: ", prettyNum(123456.78, big.mark = x$grouping_mark, decimal.mark = x$decimal_mark, digits = 8), "\n", sep = "") cat("Formats: ", x$date_format, " / ", x$time_format, "\n", sep = "") cat("Timezone: ", x$tz, "\n", sep = "") cat("Encoding: ", x$encoding, "\n", sep = "") print(x$date_names) } #' @export #' @rdname locale default_locale <- function() { loc <- getOption("vroom.default_locale") if (is.null(loc)) { loc <- locale() options("vroom.default_locale" = loc) } loc } check_tz <- function(x) { stopifnot(is.character(x), length(x) == 1) if (identical(x, "")) { x <- Sys.timezone() if (identical(x, "") || identical(x, NA_character_)) { x <- "UTC" } } if (x %in% tzdb::tzdb_names()) { x } else { stop("Unknown TZ ", x, call. = FALSE) } } check_encoding <- function(x) { stopifnot(is.character(x), length(x) == 1) if (tolower(x) %in% tolower(iconvlist())) return(TRUE) stop("Unknown encoding ", x, call. = FALSE) } vroom/R/vroom_write.R0000644000176200001440000001500014363145201014307 0ustar liggesusers#' Write a data frame to a delimited file #' #' @inheritParams readr::write_tsv #' @inheritParams vroom #' @param escape The type of escape to use when quotes are in the data. #' - `double` - quotes are escaped by doubling them. #' - `backslash` - quotes are escaped by a preceding backslash. #' - `none` - quotes are not escaped. #' @param quote How to handle fields which contain characters that need to be #' quoted. #' - `needed` - Values are only quoted if needed: if they contain a delimiter, #' quote, or newline. #' - `all` - Quote all fields. #' - `none` - Never quote fields. #' @param bom If `TRUE` add a UTF-8 BOM at the beginning of the file. This is #' recommended when saving data for consumption by excel, as it will force #' excel to read the data with the correct encoding (UTF-8) #' @param delim Delimiter used to separate values. Defaults to `\t` to write #' tab separated value (TSV) files. #' @param na String used for missing values. Defaults to 'NA'. #' @param path `r lifecycle::badge("deprecated")` is no longer supported, use #' `file` instead. #' @export #' @examples #' # If you only specify a file name, vroom_write() will write #' # the file to your current working directory. #' out_file <- tempfile(fileext = "csv") #' vroom_write(mtcars, out_file, ",") #' #' # You can also use a literal filename #' # vroom_write(mtcars, "mtcars.tsv") #' #' # If you add an extension to the file name, write_()* will #' # automatically compress the output. #' # vroom_write(mtcars, "mtcars.tsv.gz") #' # vroom_write(mtcars, "mtcars.tsv.bz2") #' # vroom_write(mtcars, "mtcars.tsv.xz") vroom_write <- function(x, file, delim = '\t', eol = "\n", na = "NA", col_names = !append, append = FALSE, quote = c("needed", "all", "none"), escape = c("double", "backslash", "none"), bom = FALSE, num_threads = vroom_threads(), progress = vroom_progress(), path = deprecated()) { if (lifecycle::is_present(path)) { file <- path lifecycle::deprecate_soft( when = "1.5.0", what = "vroom_write(file)" ) } input <- x quote <- match.arg(quote) escape <- match.arg(escape) opts <- get_vroom_write_opts(quote, escape, bom) # Standardise path returns a list, but we will only ever have 1 output file. file <- standardise_one_path(file, write = TRUE) if (NCOL(x) == 0) { if (!append && !inherits(file, "connection")) { # if file already exists, it is overwritten with an empty file! file.create(file) } return(invisible(input)) } # This seems to work ok in practice buf_lines <- max(as.integer(Sys.getenv("VROOM_WRITE_BUFFER_LINES", nrow(x) / 100 / num_threads)), 1) # Run `output_column()` before `vroom_convert()` to ensure that any ALTREP # vectors created by `output_column()` will be fully materialized (#389) x[] <- lapply(x, output_column) # We need to convert any altrep vectors to normal vectors otherwise we can't fill the # write buffers from other threads. This should be the last manipulation done # to `x` before writing to ensure that no new altrep vectors are created. x <- vroom_convert(x) if (inherits(file, "connection")) { vroom_write_connection_(x, file, delim, eol, na_str = na, col_names = col_names, options = opts, num_threads = num_threads, progress = progress, buf_lines = buf_lines, is_stdout = file == stdout(), append = append) } else { vroom_write_(x, file, delim, eol, na_str = na, col_names = col_names, append = append, options = opts, num_threads = num_threads, progress = progress, buf_lines = buf_lines) } invisible(input) } get_vroom_write_opts <- function(quote, escape, bom) { v_opts <- vroom_write_opts() bitwOr( v_opts[paste0("quote_", quote)], bitwOr( v_opts[paste0("escape_", escape)], if (bom) v_opts["bom"] else 0) ) } vroom_write_opts <- function() c( "quote_none" = 0L, "escape_none" = 0L, "quote_needed" = 1L, "quote_all" = 2L, "escape_double" = 4L, "escape_backslash" = 8L, "bom" = 16L ) #' Convert a data frame to a delimited string #' #' This is equivalent to [vroom_write()], but instead of writing to #' disk, it returns a string. It is primarily useful for examples and for #' testing. #' #' @inheritParams vroom_write #' @export vroom_format <- function(x, delim = "\t", eol = "\n", na = "NA", col_names = TRUE, escape = c("double", "backslash", "none"), quote = c("needed", "all", "none"), bom = FALSE, num_threads = vroom_threads()) { stopifnot(is.data.frame(x)) if (NCOL(x) == 0) { return("") } quote <- match.arg(quote) escape <- match.arg(escape) opts <- get_vroom_write_opts(quote, escape, bom) # This seems to work ok in practice buf_lines <- max(as.integer(Sys.getenv("VROOM_WRITE_BUFFER_LINES", nrow(x) / 100 / num_threads)), 1) x[] <- lapply(x, output_column) vroom_format_(x, delim = delim, eol = eol, na_str = na, col_names = col_names, append = FALSE, options = opts, num_threads = vroom_threads(), progress = vroom_progress(), buf_lines = buf_lines) } #' Write lines to a file #' #' @param x A character vector. #' @inheritParams vroom_write #' @export vroom_write_lines <- function(x, file, eol = "\n", na = "NA", append = FALSE, num_threads = vroom_threads()) { stopifnot(is.character(x)) x <- list(X1 = x) class(x) <- "data.frame" attr(x, "row.names") <- c(NA_integer_, -length(x[[1]])) vroom_write(x, file = file, delim = "", col_names = FALSE, eol = eol, na = na, append = append, quote = "none", escape = "none", num_threads = num_threads ) } #' Preprocess column for output #' #' This is a generic function that applied to each column before it is saved #' to disk. It provides a hook for S3 classes that need special handling. #' #' @keywords internal #' @param x A vector #' @examples #' # Most types are returned unchanged #' output_column(1) #' output_column("x") #' #' # datetimes are formatted in ISO 8601 #' output_column(Sys.Date()) #' output_column(Sys.time()) #' @export output_column <- function(x) { UseMethod("output_column") } #' @export output_column.default <- function(x) { if (!is.object(x)) return(x) as.character(x) } #' @export output_column.double <- function(x) { x } #' @export output_column.POSIXt <- function(x) { format(x, "%Y-%m-%dT%H:%M:%OSZ", tz = "UTC", justify = "none") } #' @export output_column.character <- function(x) { x } #' @export output_column.factor <- function(x) { # TODO: look into doing writing directly in C++ as.character(x) } vroom/R/generator.R0000644000176200001440000001327514504102737013741 0ustar liggesusers#' Generate individual vectors of the types supported by vroom #' #' @param n The size of the vector to generate #' @param min The minimum range for the vector #' @param max The maximum range for the vector #' @param values The explicit values to use. #' @param f The random function to use. #' @inheritParams base::sample.int #' @param ... Additional arguments passed to internal generation functions #' @name generators #' @examples #' # characters #' gen_character(4) #' #' # factors #' gen_factor(4) #' #' # logical #' gen_logical(4) #' #' # numbers #' gen_double(4) #' gen_integer(4) #' #' # temporal data #' gen_time(4) #' gen_date(4) #' gen_datetime(4) #' @export gen_character <- function(n, min = 5, max = 25, values = c(letters, LETTERS, 0:9), ...) { if (min > max) { max <- min } # The seed for the C++ RNG used is an unsigned 32 bit integer, which is why I # multiply int max by 2. Possibly an off by one error here though... seeds <- sample.int(2 * .Machine$integer.max, 2) gen_character_(n, min, max, paste(values, collapse = ""), seeds[[1]], seeds[[2]]) } #' @rdname generators #' @export gen_double <- function(n, f = stats::rnorm, ...) { f(n, ...) } #' @rdname generators #' @export gen_number <- gen_double #' @rdname generators #' @export gen_integer <- function(n, min = 1L, max = .Machine$integer.max, prob = NULL, ...) { max <- max - min + 1L sample.int(max, size = n, replace = TRUE, prob = prob) + min - 1L } #' @rdname generators #' @param num_levels The number of factor levels to generate #' @param ordered Should the factors be ordered factors? #' @param levels The explicit levels to use, if `NULL` random levels are generated using [gen_name()]. #' @export gen_factor <- function(n, levels = NULL, ordered = FALSE, num_levels = gen_integer(1L, 1L, 25L), ...) { if (is.null(levels)) { levels <- gen_name(num_levels) } res <- gen_integer(n, max = length(levels), ...) attr(res, "levels") <- levels if (ordered) { class(res) <- c("ordered", "factor") } else { class(res) <- "factor" } res } #' @rdname generators #' @param fractional Whether to generate times with fractional seconds #' @export gen_time <- function(n, min = 0, max = hms::hms(days = 1), fractional = FALSE, ...) { res <- hms::hms(seconds = stats::runif(n, min = min, max = max)) if (!fractional) { res <- hms::as_hms(floor(res)) } res } #' @rdname generators #' @export gen_date <- function(n, min = as.Date("2001-01-01"), max = as.Date("2021-01-01"), ...) { structure(as.numeric(gen_integer(n, min = min, max = max)), class = "Date") } #' @rdname generators #' @param tz The timezone to use for dates #' @export gen_datetime <- function(n, min = as.POSIXct("2001-01-01"), max = as.POSIXct("2021-01-01"), tz = "UTC", ...) { structure(stats::runif(n, min = min, max = max), class = c("POSIXct", "POSIXt"), tzone = tz) } #' @rdname generators #' @export gen_logical <- function(n, ...) { c(TRUE, FALSE)[sample.int(n = 2, n, replace = TRUE)] } all_col_types <- tibble::tribble( ~ type, ~ class, "character", "character", "factor", "character", "double", "numeric", "integer", "numeric", "number", "numeric", "date", "temporal", "datetime", "temporal", "time", "temporal", ) #' Generate a random tibble #' #' This is useful for benchmarking, but also for bug reports when you cannot #' share the real dataset. #' #' There is also a family of functions to generate individual vectors of each #' type. #' #' @param rows Number of rows to generate #' @param cols Number of columns to generate, if `NULL` this is derived from `col_types`. #' @param missing The percentage (from 0 to 1) of missing data to use #' @seealso [generators] to generate individual vectors. #' @inheritParams vroom #' @examples #' # random 10 x 5 table with random column types #' rand_tbl <- gen_tbl(10, 5) #' rand_tbl #' #' # all double 25 x 4 table #' dbl_tbl <- gen_tbl(25, 4, col_types = "dddd") #' dbl_tbl #' #' # Use the dots in long form column types to change the random function and options #' types <- rep(times = 4, list(col_double(f = stats::runif, min = -10, max = 25))) #' types #' dbl_tbl2 <- gen_tbl(25, 4, col_types = types) #' dbl_tbl2 #' @export gen_tbl <- function(rows, cols = NULL, col_types = NULL, locale = default_locale(), missing = 0) { if (is.null(cols) && is.null(col_types)) { stop("One of `cols` or `col_types` must be set", call. = FALSE) } spec <- as.col_spec(col_types) if (is.null(cols)) { cols <- length(spec$cols) } nms <- make_names(names(spec$cols), cols) specs <- col_types_standardise(spec, length(nms), nms, vroom_enquo(quo(NULL)), "unique") res <- vector("list", cols) for (i in seq_len(cols)) { type <- sub("collector_", "", class(specs$cols[[i]])[[1]]) if (type == "guess") { type <- sample(all_col_types[["type"]], 1) specs$cols[[i]] <- do.call(paste0("col_", type), list()) } fun_nme <- paste0("gen_", type) res[[i]] <- do.call(fun_nme, c(rows, specs$cols[[i]])) } if (missing > 0) { res[] <- lapply(res, function(x) { x[sample(c(TRUE, FALSE), size = rows, prob = c(missing, 1 - missing), replace = TRUE)] <- NA x }) } names(res) <- nms attr(res, "spec") <- specs tibble::as_tibble(res) } # Name and adjective list from https://github.com/rstudio/cranwhales/blob/93349fe1bc790f115a3d56660b6b99ffe258d9a2/random-names.R #' @rdname generators #' @export gen_name <- local({ # This will run during build / installation, but that is OK adjectives <- readLines(system.file("words", "adjectives.txt", package = "vroom")) animals <- readLines(system.file("words", "animals.txt", package = "vroom")) function(n) { paste0(sample(adjectives, n, replace = TRUE), "_", sample(animals, n, replace = TRUE)) } }) vroom/R/vroom.R0000644000176200001440000005067414507360767013135 0ustar liggesusers#' Read a delimited file into a tibble #' #' @param file Either a path to a file, a connection, or literal data (either a #' single string or a raw vector). `file` can also be a character vector #' containing multiple filepaths or a list containing multiple connections. #' #' Files ending in `.gz`, `.bz2`, `.xz`, or `.zip` will be automatically #' uncompressed. Files starting with `http://`, `https://`, `ftp://`, or #' `ftps://` will be automatically downloaded. Remote gz files can also be #' automatically downloaded and decompressed. #' #' Literal data is most useful for examples and tests. To be recognised as #' literal data, wrap the input with `I()`. #' @param delim One or more characters used to delimit fields within a #' file. If `NULL` the delimiter is guessed from the set of `c(",", "\t", " ", #' "|", ":", ";")`. #' @param col_names Either `TRUE`, `FALSE` or a character vector #' of column names. #' #' If `TRUE`, the first row of the input will be used as the column #' names, and will not be included in the data frame. If `FALSE`, column #' names will be generated automatically: X1, X2, X3 etc. #' #' If `col_names` is a character vector, the values will be used as the #' names of the columns, and the first row of the input will be read into #' the first row of the output data frame. #' #' Missing (`NA`) column names will generate a warning, and be filled #' in with dummy names `...1`, `...2` etc. Duplicate column names #' will generate a warning and be made unique, see `name_repair` to control #' how this is done. #' @param col_types One of `NULL`, a [cols()] specification, or #' a string. #' #' If `NULL`, all column types will be imputed from `guess_max` rows #' on the input interspersed throughout the file. This is convenient (and #' fast), but not robust. If the imputation fails, you'll need to increase #' the `guess_max` or supply the correct types yourself. #' #' Column specifications created by [list()] or [cols()] must contain #' one column specification for each column. If you only want to read a #' subset of the columns, use [cols_only()]. #' #' Alternatively, you can use a compact string representation where each #' character represents one column: #' - c = character #' - i = integer #' - n = number #' - d = double #' - l = logical #' - f = factor #' - D = date #' - T = date time #' - t = time #' - ? = guess #' - _ or - = skip #' #' By default, reading a file without a column specification will print a #' message showing what `readr` guessed they were. To remove this message, #' set `show_col_types = FALSE` or set `options(readr.show_col_types = FALSE)`. #' @param id Either a string or 'NULL'. If a string, the output will contain a #' variable with that name with the filename(s) as the value. If 'NULL', the #' default, no variable will be created. #' @param skip Number of lines to skip before reading data. If `comment` is #' supplied any commented lines are ignored _after_ skipping. #' @param n_max Maximum number of lines to read. #' @param na Character vector of strings to interpret as missing values. Set this #' option to `character()` to indicate no missing values. #' @param quote Single character used to quote strings. #' @param comment A string used to identify comments. Any text after the #' comment characters will be silently ignored. #' @param skip_empty_rows Should blank rows be ignored altogether? i.e. If this #' option is `TRUE` then blank rows will not be represented at all. If it is #' `FALSE` then they will be represented by `NA` values in all the columns. #' @param trim_ws Should leading and trailing whitespace (ASCII spaces and tabs) be trimmed from #' each field before parsing it? #' @param escape_double Does the file escape quotes by doubling them? #' i.e. If this option is `TRUE`, the value '""' represents #' a single quote, '"'. #' @param escape_backslash Does the file use backslashes to escape special #' characters? This is more general than `escape_double` as backslashes #' can be used to escape the delimiter character, the quote character, or #' to add special characters like `\\n`. #' @param locale The locale controls defaults that vary from place to place. #' The default locale is US-centric (like R), but you can use #' [locale()] to create your own locale that controls things like #' the default time zone, encoding, decimal mark, big mark, and day/month #' names. #' @param guess_max Maximum number of lines to use for guessing column types. #' See `vignette("column-types", package = "readr")` for more details. #' @param altrep Control which column types use Altrep representations, #' either a character vector of types, `TRUE` or `FALSE`. See #' [vroom_altrep()] for for full details. #' @param altrep_opts \Sexpr[results=rd, stage=render]{lifecycle::badge("deprecated")} #' @param col_select Columns to include in the results. You can use the same #' mini-language as `dplyr::select()` to refer to the columns by name. Use #' `c()` to use more than one selection expression. Although this #' usage is less common, `col_select` also accepts a numeric column index. See #' [`?tidyselect::language`][tidyselect::language] for full details on the #' selection language. #' @param num_threads Number of threads to use when reading and materializing #' vectors. If your data contains newlines within fields the parser will #' automatically be forced to use a single thread only. #' @param progress Display a progress bar? By default it will only display #' in an interactive session and not while knitting a document. The automatic #' progress bar can be disabled by setting option `readr.show_progress` to #' `FALSE`. #' @param show_col_types Control showing the column specifications. If `TRUE` #' column specifications are always show, if `FALSE` they are never shown. If #' `NULL` (the default) they are shown only if an explicit specification is not #' given to `col_types`. #' @param .name_repair Handling of column names. The default behaviour is to #' ensure column names are `"unique"`. Various repair strategies are #' supported: #' * `"minimal"`: No name repair or checks, beyond basic existence of names. #' * `"unique"` (default value): Make sure names are unique and not empty. #' * `"check_unique"`: no name repair, but check they are `unique`. #' * `"universal"`: Make the names `unique` and syntactic. #' * A function: apply custom name repair (e.g., `name_repair = make.names` #' for names in the style of base R). #' * A purrr-style anonymous function, see [rlang::as_function()]. #' #' This argument is passed on as `repair` to [vctrs::vec_as_names()]. #' See there for more details on these terms and the strategies used #' to enforce them. #' @export #' @examples #' # get path to example file #' input_file <- vroom_example("mtcars.csv") #' input_file #' #' # Read from a path #' #' # Input sources ------------------------------------------------------------- #' # Read from a path #' vroom(input_file) #' # You can also use paths directly #' # vroom("mtcars.csv") #' #' \dontrun{ #' # Including remote paths #' vroom("https://github.com/tidyverse/vroom/raw/main/inst/extdata/mtcars.csv") #' } #' #' # Or directly from a string with `I()` #' vroom(I("x,y\n1,2\n3,4\n")) #' #' # Column selection ---------------------------------------------------------- #' # Pass column names or indexes directly to select them #' vroom(input_file, col_select = c(model, cyl, gear)) #' vroom(input_file, col_select = c(1, 3, 11)) #' #' # Or use the selection helpers #' vroom(input_file, col_select = starts_with("d")) #' #' # You can also rename specific columns #' vroom(input_file, col_select = c(car = model, everything())) #' #' # Column types -------------------------------------------------------------- #' # By default, vroom guesses the columns types, looking at 1000 rows #' # throughout the dataset. #' # You can specify them explicitly with a compact specification: #' vroom(I("x,y\n1,2\n3,4\n"), col_types = "dc") #' #' # Or with a list of column types: #' vroom(I("x,y\n1,2\n3,4\n"), col_types = list(col_double(), col_character())) #' #' # File types ---------------------------------------------------------------- #' # csv #' vroom(I("a,b\n1.0,2.0\n"), delim = ",") #' # tsv #' vroom(I("a\tb\n1.0\t2.0\n")) #' # Other delimiters #' vroom(I("a|b\n1.0|2.0\n"), delim = "|") #' #' # Read datasets across multiple files --------------------------------------- #' mtcars_by_cyl <- vroom_example(vroom_examples("mtcars-")) #' mtcars_by_cyl #' #' # Pass the filenames directly to vroom, they are efficiently combined #' vroom(mtcars_by_cyl) #' #' # If you need to extract data from the filenames, use `id` to request a #' # column that reveals the underlying file path #' dat <- vroom(mtcars_by_cyl, id = "source") #' dat$source <- basename(dat$source) #' dat vroom <- function( file, delim = NULL, col_names = TRUE, col_types = NULL, col_select = NULL, id = NULL, skip = 0, n_max = Inf, na = c("", "NA"), quote = '"', comment = "", skip_empty_rows = TRUE, trim_ws = TRUE, escape_double = TRUE, escape_backslash = FALSE, locale = default_locale(), guess_max = 100, altrep = TRUE, altrep_opts = deprecated(), num_threads = vroom_threads(), progress = vroom_progress(), show_col_types = NULL, .name_repair = "unique" ) { # vroom does not support newlines as the delimiter, just as the EOL, so just # assign a value that should never appear in CSV text as the delimiter, # 001, start of heading. if (identical(delim, "\n")) { delim <- "\x01" } if (!is_missing(altrep_opts)) { deprecate_warn("1.1.0", "vroom(altrep_opts = )", "vroom(altrep = )") altrep <- altrep_opts } file <- standardise_path(file) if (!is_ascii_compatible(locale$encoding)) { file <- reencode_file(file, locale$encoding) locale$encoding <- "UTF-8" } if (length(file) == 0 || (n_max == 0 & identical(col_names, FALSE))) { return(tibble::tibble()) } if (n_max < 0 || is.infinite(n_max)) { n_max <- -1 } if (guess_max < 0 || is.infinite(guess_max)) { guess_max <- -1 } # Workaround weird RStudio / Progress bug: https://github.com/r-lib/progress/issues/56#issuecomment-384232184 if ( isTRUE(progress) && is_windows() && identical(Sys.getenv("RSTUDIO"), "1")) { Sys.setenv("RSTUDIO" = "1") } col_select <- vroom_enquo(enquo(col_select)) has_col_types <- !is.null(col_types) col_types <- as.col_spec(col_types) na <- enc2utf8(na) out <- vroom_(file, delim = delim %||% col_types$delim, col_names = col_names, col_types = col_types, id = id, skip = skip, col_select = col_select, name_repair = .name_repair, na = na, quote = quote, trim_ws = trim_ws, escape_double = escape_double, escape_backslash = escape_backslash, comment = comment, skip_empty_rows = skip_empty_rows, locale = locale, guess_max = guess_max, n_max = n_max, altrep = vroom_altrep(altrep), num_threads = num_threads, progress = progress) # Drop any NULL columns is_null <- vapply(out, is.null, logical(1)) out[is_null] <- NULL # If no rows expand columns to be the same length and names as the spec if (NROW(out) == 0) { cols <- attr(out, "spec")[["cols"]] for (i in seq_along(cols)) { out[[i]] <- collector_value(cols[[i]]) } names(out) <- names(cols) } out <- tibble::as_tibble(out, .name_repair = identity) class(out) <- c("spec_tbl_df", class(out)) out <- vroom_select(out, col_select, id) if (should_show_col_types(has_col_types, show_col_types)) { show_col_types(out, locale) } out } should_show_col_types <- function(has_col_types, show_col_types) { if (is.null(show_col_types)) { return(isTRUE(!has_col_types)) } isTRUE(show_col_types) } make_names <- function(x, len) { if (len == 0) { return(character()) } if (length(x) == len) { return(x) } if (length(x) > len) { return(x[seq_len(len)]) } nms <- make.names(seq_len(len)) nms[seq_along(x)] <- x nms } #' Determine whether progress bars should be shown #' #' By default, vroom shows progress bars. However, progress reporting is #' suppressed if any of the following conditions hold: #' - The bar is explicitly disabled by setting the environment variable #' `VROOM_SHOW_PROGRESS` to `"false"`. #' - The code is run in a non-interactive session, as determined by #' [rlang::is_interactive()]. #' - The code is run in an RStudio notebook chunk, as determined by #' `getOption("rstudio.notebook.executing")`. #' @export #' @examples #' vroom_progress() vroom_progress <- function() { env_to_logical("VROOM_SHOW_PROGRESS", TRUE) && is_interactive() && # some analysis re: rstudio.notebook.executing can be found in: # https://github.com/r-lib/rlang/issues/1031 # TL;DR it's not consulted by is_interactive(), but probably should be # consulted for progress reporting specifically !isTRUE(getOption("rstudio.notebook.executing")) } pb_file_format <- function(filename) { # Workaround RStudio bug https://github.com/rstudio/rstudio/issues/4777 withr::with_options(list(crayon.enabled = (!is_rstudio_console() || is_rstudio_version("1.2.1578")) && getOption("crayon.enabled", TRUE)), glue::glue_col("{bold}indexing{reset} {blue}{basename(filename)}{reset} [:bar] {green}:rate{reset}, eta: {cyan}:eta{reset}") ) } pb_width <- function(format) { ansii_chars <- nchar(format) - crayon::col_nchar(format) getOption("width", 80L) + ansii_chars } pb_connection_format <- function(unused) { withr::with_options(list(crayon.enabled = (!is_rstudio_console() || is_rstudio_version("1.2.1578")) && getOption("crayon.enabled", TRUE)), glue::glue_col("{bold}indexed{reset} {green}:bytes{reset} in {cyan}:elapsed{reset}, {green}:rate{reset}") ) } pb_write_format <- function(unused) { withr::with_options(list(crayon.enabled = (!is_rstudio_console() || is_rstudio_version("1.2.1578")) && getOption("crayon.enabled", TRUE)), glue::glue_col("{bold}wrote{reset} {green}:bytes{reset} in {cyan}:elapsed{reset}, {green}:rate{reset}") ) } # Guess delimiter by splitting every line by each delimiter and choosing the # delimiter which splits the lines into the highest number of consistent fields guess_delim <- function(lines, delims = c(",", "\t", " ", "|", ":", ";")) { if (length(lines) == 0) { return("") } # blank text within quotes lines <- gsub('"[^"]*"', "", lines) splits <- lapply(delims, strsplit, x = lines, useBytes = TRUE, fixed = TRUE) counts <- lapply(splits, function(x) table(lengths(x))) num_fields <- vapply(counts, function(x) as.integer(names(x)[[1]]), integer(1)) num_lines <- vapply(counts, function(x) (x)[[1]], integer(1)) top_lines <- 0 top_idx <- 0 for (i in seq_along(delims)) { if (num_fields[[i]] >= 2 && num_lines[[i]] > top_lines || (top_lines == num_lines[[i]] && (top_idx <= 0 || num_fields[[top_idx]] < num_fields[[i]]))) { top_lines <- num_lines[[i]] top_idx <- i } } if (top_idx == 0) { stop(glue::glue(' Could not guess the delimiter.\n {silver("Use `vroom(delim =)` to specify one explicitly.")} '), call. = FALSE) } delims[[top_idx]] } cached <- new.env(parent = emptyenv()) vroom_threads <- function() { res <- as.integer( Sys.getenv("VROOM_THREADS", cached$num_threads <- cached$num_threads %||% parallel::detectCores() ) ) if (is.na(res) || res <= 0) { res <- 1 } res } vroom_tempfile <- function() { dir <- Sys.getenv("VROOM_TEMP_PATH") if (!nzchar(dir)) { dir <- tempdir() } tempfile(pattern = "vroom-", tmpdir = dir) } #' Show which column types are using Altrep #' #' `vroom_altrep()` can be used directly as input to the `altrep` #' argument of [vroom()]. #' #' Alternatively there is also a family of environment variables to control use of #' the Altrep framework. These can then be set in your `.Renviron` file, e.g. #' with `usethis::edit_r_environ()`. For versions of R where the Altrep #' framework is unavailable (R < 3.5.0) they are automatically turned off and #' the variables have no effect. The variables can take one of `true`, `false`, #' `TRUE`, `FALSE`, `1`, or `0`. #' #' - `VROOM_USE_ALTREP_NUMERICS` - If set use Altrep for _all_ numeric types #' (default `false`). #' #' There are also individual variables for each type. Currently only #' `VROOM_USE_ALTREP_CHR` defaults to `true`. #' #' - `VROOM_USE_ALTREP_CHR` #' - `VROOM_USE_ALTREP_FCT` #' - `VROOM_USE_ALTREP_INT` #' - `VROOM_USE_ALTREP_BIG_INT` #' - `VROOM_USE_ALTREP_DBL` #' - `VROOM_USE_ALTREP_NUM` #' - `VROOM_USE_ALTREP_LGL` #' - `VROOM_USE_ALTREP_DTTM` #' - `VROOM_USE_ALTREP_DATE` #' - `VROOM_USE_ALTREP_TIME` #' #' @param which A character vector of column types to use Altrep for. Can also #' take `TRUE` or `FALSE` to use Altrep for all possible or none of the #' types #' @examples #' vroom_altrep() #' vroom_altrep(c("chr", "fct", "int")) #' vroom_altrep(TRUE) #' vroom_altrep(FALSE) #' @export vroom_altrep <- function(which = NULL) { if (!is.null(which)) { if (is.logical(which)) { types <- names(altrep_vals()) if (isTRUE(which)) { which <- as.list(stats::setNames(rep(TRUE, length(types)), types)) } else { which <- as.list(stats::setNames(rep(FALSE, length(types)), types)) } } else { which <- match.arg(which, names(altrep_vals()), several.ok = TRUE) which <- as.list(stats::setNames(rep(TRUE, length(which)), which)) } } args <- list( getRversion() >= "3.5.0" && which$chr %||% vroom_use_altrep_chr(), getRversion() >= "3.5.0" && which$fct %||% vroom_use_altrep_fct(), getRversion() >= "3.5.0" && which$int %||% vroom_use_altrep_int(), getRversion() >= "3.5.0" && which$dbl %||% vroom_use_altrep_dbl(), getRversion() >= "3.5.0" && which$num %||% vroom_use_altrep_num(), getRversion() >= "3.6.0" && which$lgl %||% vroom_use_altrep_lgl(), # logicals only supported in R 3.6.0+ getRversion() >= "3.5.0" && which$dttm %||% vroom_use_altrep_dttm(), getRversion() >= "3.5.0" && which$date %||% vroom_use_altrep_date(), getRversion() >= "3.5.0" && which$time %||% vroom_use_altrep_time(), getRversion() >= "3.5.0" && which$big_int %||% vroom_use_altrep_big_int() ) out <- 0L for (i in seq_along(args)) { out <- bitwOr(out, bitwShiftL(as.integer(args[[i]]), i - 1L)) } structure(out, class = "vroom_altrep") } #' Show which column types are using Altrep #' #' @description #' \Sexpr[results=rd, stage=render]{lifecycle::badge("deprecated")} #' This function is deprecated in favor of `vroom_altrep()`. #' #' @inheritParams vroom_altrep #' @export vroom_altrep_opts <- function(which = NULL) { deprecate_warn("1.1.0", "vroom_altrep_opts()", "vroom_altrep()") vroom_altrep(which) } altrep_vals <- function() c( "none" = 0L, "chr" = 1L, "fct" = 2L, "int" = 4L, "dbl" = 8L, "num" = 16L, "lgl" = 32L, "dttm" = 64L, "date" = 128L, "time" = 256L, "big_int" = 512L, "skip" = 1024L ) #' @export print.vroom_altrep <- function(x, ...) { vals <- altrep_vals() reps <- names(vals)[bitwAnd(vals, x) > 0] cat("Using Altrep representations for:\n", glue::glue(" * {reps} ", reps = glue::glue_collapse(reps, "\n * ")), "\n", sep = "") } vroom_use_altrep_chr <- function() { env_to_logical("VROOM_USE_ALTREP_CHR", TRUE) } vroom_use_altrep_fct <- function() { # fct is a numeric internally env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_FCT", FALSE) } vroom_use_altrep_int <- function() { env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_INT", FALSE) } vroom_use_altrep_big_int <- function() { env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_BIG_INT", FALSE) } vroom_use_altrep_dbl <- function() { env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_DBL", FALSE) } vroom_use_altrep_num <- function() { env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_NUM", FALSE) } vroom_use_altrep_lgl <- function() { env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_LGL", FALSE) } vroom_use_altrep_dttm <- function() { env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_DTTM", FALSE) } vroom_use_altrep_date <- function() { env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_DATE", FALSE) } vroom_use_altrep_time <- function() { env_to_logical("VROOM_USE_ALTREP_NUMERICS", FALSE) || env_to_logical("VROOM_USE_ALTREP_TIME", FALSE) } vroom/R/problems.R0000644000176200001440000000310014362130126013553 0ustar liggesusers#' Retrieve parsing problems #' #' vroom will only fail to parse a file if the file is invalid in a way that is #' unrecoverable. However there are a number of non-fatal problems that you #' might want to know about. You can retrieve a data frame of these problems #' with this function. #' #' @param x A data frame from `vroom::vroom()`. #' @param lazy If `TRUE`, just the problems found so far are returned. If #' `FALSE` (the default) the lazy data is first read completely and all #' problems are returned. #' @return A data frame with one row for each problem and four columns: #' - row,col - Row and column number that caused the problem, referencing the #' original input #' - expected - What vroom expected to find #' - actual - What it actually found #' - file - The file with the problem #' @export problems <- function(x = .Last.value, lazy = FALSE) { if(!inherits(x, "tbl_df")) { cli::cli_abort(c( "The {.arg x} argument of {.fun vroom::problems} must be a data frame created by vroom:", x = "{.arg x} has class {.cls {class(x)}}" )) } if (!isTRUE(lazy)) { vroom_materialize(x, replace = FALSE) } probs <- attr(x, "problems") if (typeof(probs) != "externalptr") { cli::cli_abort(c( "The {.arg x} argument of {.fun vroom::problems} must be a data frame created by vroom:", x = "{.arg x} seems to have been created with something else, maybe readr?" )) } probs <- vroom_errors_(probs) probs <- probs[!duplicated(probs), ] probs <- probs[order(probs$file, probs$row, probs$col), ] tibble::as_tibble(probs) } vroom/R/tidyselect.R0000644000176200001440000000107314132374245014117 0ustar liggesusers#' @aliases select_helpers #' @importFrom tidyselect contains #' @export tidyselect::contains #' @importFrom tidyselect ends_with #' @export tidyselect::ends_with #' @importFrom tidyselect everything #' @export tidyselect::everything #' @importFrom tidyselect matches #' @export tidyselect::matches #' @importFrom tidyselect num_range #' @export tidyselect::num_range #' @importFrom tidyselect one_of #' @export tidyselect::one_of #' @importFrom tidyselect starts_with #' @export tidyselect::starts_with #' @importFrom tidyselect last_col #' @export tidyselect::last_col vroom/R/altrep.R0000644000176200001440000000145014132374245013234 0ustar liggesusers#' Structure of objects #' #' Similar to `str()` but with more information for Altrep objects. #' #' @param x a vector #' @examples #' # when used on non-altrep objects altrep will always be false #' vroom_str(mtcars) #' #' mt <- vroom(vroom_example("mtcars.csv"), ",", altrep = c("chr", "dbl")) #' vroom_str(mt) #' @export vroom_str <- function(x) { UseMethod("vroom_str") } #' @export vroom_str.data.frame <- function(x) { classes <- glue::glue_collapse(glue::single_quote(class(x)), ", ", last = ", and ") rows <- nrow(x) cols <- ncol(x) cat(glue::glue("{classes}: {rows} obs., {cols} vars.:\n\n"), sep = "") nms <- names(x) for (i in seq_along(x)) { cat("$", nms[[i]], ":\t", sep = "") vroom_str(x[[i]]) } } #' @export vroom_str.default <- function(x) { cat(vroom_str_(x)) } vroom/NEWS.md0000644000176200001440000004563014533651702012530 0ustar liggesusers# vroom 1.6.5 * Internal changes requested by CRAN around format specification (#524). # vroom 1.6.4 * It is now possible (again?) to read from a list of connections (@bairdj, #514). * Internal change for compatibility with cpp11 >= 0.4.6 (@DavisVaughan, #512). # vroom 1.6.3 * No user-facing changes. # vroom 1.6.2 * There was no CRAN release with this version number. # vroom 1.6.1 * `str()` now works in a colorized context in the presence of a column of class `integer64`, i.e. parsed with `col_big_integer()` (@bart1, #477). * The embedded implementation of the Grisu algorithm for printing floating point numbers now uses `snprintf()` instead of `sprintf()` and likewise for vroom's own code (@jeroen, #480). # vroom 1.6.0 * `vroom(col_select=)` now handles column selection by numeric position when `id` column is provided (#455). * `vroom(id = "path", col_select = a:c)` is treated like `vroom(id = "path", col_select = c(path, a:c))`. If an `id` column is provided, it is automatically included in the output (#416). * `vroom_write(append = TRUE)` does not modify an existing file when appending an empty data frame. In particular, it does not overwrite (delete) the existing contents of that file (https://github.com/tidyverse/readr/issues/1408, #451). * `vroom::problems()` now defaults to `.Last.value` for its primary input, similar to how `readr::problems()` works (#443). * The warning that indicates the existence of parsing problems has been improved, which should make it easier for the user to follow-up (https://github.com/tidyverse/readr/issues/1322). * `vroom()` reads more reliably from filepaths containing non-ascii characters, in a non-UTF-8 locale (#394, #438). * `vroom_format()` and `vroom_write()` only quote values that contain a delimiter, quote, or newline. Specifically values that are equal to the `na` string (or that start with it) are no longer quoted (#426). * Fixed segfault when reading in multiple files and the first file has only a header row of column names, but subsequent files have at least one row (#430). * Fixed segfault when `vroom_format()` is given an empty data frame (#425) * Fixed a segfault that could occur when the final field of the final line is missing and the file also does not end in a newline (#429). * Fixed recursive garbage collection error that could occur during `vroom_write()` when `output_column()` generates an ALTREP vector (#389). * `vroom_progress()` uses `rlang::is_interactive()` instead of `base::interactive()`. * `col_factor(levels = NULL)` honors the `na` strings of `vroom()` and its own `include_na` argument, as described in the docs, and now reproduces the behaviour of readr's first edition parser (#396). # vroom 1.5.7 * Jenny Bryan is now the official maintainer. * Fix uninitialized bool detected by CRAN's UBSAN check (https://github.com/tidyverse/vroom/pull/386) * Fix buffer overflow when trying to parse an integer field that is over 64 characters long (https://github.com/tidyverse/readr/issues/1326) * Fix subset indexing when indexes span a file boundary multiple times (#383) # vroom 1.5.6 * `vroom(col_select=)` now works if `col_names = FALSE` as intended (#381) * `vroom(n_max=)` now correctly handles cases when reading from a connection and the file does _not_ end with a newline (https://github.com/tidyverse/readr/issues/1321) * `vroom()` no longer issues a spurious warning when the parsing needs to be restarted due to the presence of embedded newlines (https://github.com/tidyverse/readr/issues/1313) * Fix performance issue when materializing subsetted vectors (#378) * `vroom_format()` now uses the same internal multi-threaded code as `vroom_write()`, improving its performance in most cases (#377) * `vroom_fwf()` no longer omits the last line if it does _not_ end with a newline (https://github.com/tidyverse/readr/issues/1293) * Empty files or files with only a header line and no data no longer cause a crash if read with multiple files (https://github.com/tidyverse/readr/issues/1297) * Files with a header but no contents, or a empty file if `col_names = FALSE` no longer cause a hang when `progress = TRUE` (https://github.com/tidyverse/readr/issues/1297) * Commented lines with comments at the end of lines no longer hang R (https://github.com/tidyverse/readr/issues/1309) * Comment lines containing unpaired quotes are no longer treated as unterminated quotations (https://github.com/tidyverse/readr/issues/1307) * Values with only a `Inf` or `NaN` prefix but additional data afterwards, like `Inform` or no longer inappropriately guessed as doubles (https://github.com/tidyverse/readr/issues/1319) * Time types now support `%h` format to denote hour durations greater than 24, like readr (https://github.com/tidyverse/readr/issues/1312) * Fix performance issue when materializing subsetted vectors (#378) # vroom 1.5.5 * `vroom()` now supports files with only carriage return newlines (`\r`). (#360, https://github.com/tidyverse/readr/issues/1236) * `vroom()` now parses single digit datetimes more consistently as readr has done (https://github.com/tidyverse/readr/issues/1276) * `vroom()` now parses `Inf` values as doubles (https://github.com/tidyverse/readr/issues/1283) * `vroom()` now parses `NaN` values as doubles (https://github.com/tidyverse/readr/issues/1277) * `VROOM_CONNECTION_SIZE` is now parsed as a double, which supports scientific notation (#364) * `vroom()` now works around specifying a `\n` as the delimiter (#365, https://github.com/tidyverse/dplyr/issues/5977) * `vroom()` no longer crashes if given a `col_name` and `col_type` both less than the number of columns (https://github.com/tidyverse/readr/issues/1271) * `vroom()` no longer hangs if given an empty value for `locale(grouping_mark=)` (https://github.com/tidyverse/readr/issues/1241) * Fix performance regression when guessing with large numbers of rows (https://github.com/tidyverse/readr/issues/1267) # vroom 1.5.4 * `vroom(col_types=)` now accepts column type names like those accepted by utils::read.table. e.g. vroom::vroom(col_types = list(a = "integer", b = "double", c = "skip")) * `vroom()` now respects the `quote` parameter properly in the first two lines of the file (https://github.com/tidyverse/readr/issues/1262) * `vroom_write()` now always correctly writes its output including column names in UTF-8 (https://github.com/tidyverse/readr/issues/1242) * `vroom_write()` now creates an empty file when given a input without any columns (https://github.com/tidyverse/readr/issues/1234) # vroom 1.5.3 * `vroom(col_types=)` now truncates the column types if the user passes too many types. (#355) * `vroom()` now always includes the last row when guessing (#352) * `vroom(trim_ws = TRUE)` now trims field content within quotes as well as without (#354). Previously vroom explicitly retained field content inside quotes regardless of the value of `trim_ws`. # vroom 1.5.2 * `vroom()` now supports inputs with unnamed column types that are less than the number of columns (#296) * `vroom()` now outputs the correct column names even in the presence of skipped columns (#293, [tidyverse/readr#1215](https://github.com/tidyverse/readr/issues/1215)) * `vroom_fwf(n_max=)` now works as intended when the input is a connection. * `vroom()` and `vroom_write()` now automatically detect the compression format regardless of the file extension for bzip2, xzip, gzip and zip files (#348) * `vroom()` and `vroom_write()` now automatically support many more archive formats thanks to the archive package. These include new support for writing zip files, reading and writing 7zip, tar and ISO files. * `vroom(num_threads = 1)` will now not spawn any threads. This can be used on as a workaround on systems without full thread support. * Threads are now automatically disabled on non-macOS systems compiling against clang's libc++. Most systems non-macOS systems use the more common gcc libstdc++, so this should not effect most users. # vroom 1.5.1 * Parsers now treat NA values as NA even if they are valid values for the types (#342) * Element-wise indexing into lazy (ALTREP) vectors now has much less overhead (#344) # vroom 1.5.0 ## Major improvements * New `vroom(show_col_types=)` argument to more simply control when column types are shown. * `vroom()`, `vroom_fwf()` and `vroom_lines()` now support multi-byte encodings such as UTF-16 and UTF-32 by converting these files to UTF-8 under the hood (#138) * `vroom()` now supports skipping comments and blank lines within data, not just at the start of the file (#294, #302) * `vroom()` now uses the tzdb package when parsing date-times (@DavisVaughan, #273) * `vroom()` now emits a warning of class `vroom_parse_issue` if there are non-fatal parsing issues. * `vroom()` now emits a warning of class `vroom_mismatched_column_name` if the user supplies a column type that does not match the name of a read column (#317). * The vroom package now uses the MIT license, as part of systematic relicensing throughout the r-lib and tidyverse packages (#323) ## Minor improvements and fixes * `vroom() correctly reads double values with comma as decimal separator (@kent37 #313) * `vroom()` now correctly skips lines with only one quote if the format doesn't use quoting (https://github.com/tidyverse/readr/issues/991#issuecomment-616378446) * `vroom()` and `vroom_lines()` now handle files with mixed windows and POSIX line endings (https://github.com/tidyverse/readr/issues/1210) * `vroom()` now outputs a tibble with the expected number of columns and types based on `col_types` and `col_names` even if the file is empty (#297). * `vroom()` no longer mis-indexes files read from connections with windows line endings when the two line endings falls on separate sides of the read buffer (#331) * `vroom()` no longer crashes if `n_max = 0` and `col_names` is a character (#316) * `vroom()` now preserves the spec attribute when vroom and readr are both loaded (#303) * `vroom()` now allows specifying column names in `col_types` that have been repaired (#311) * `vroom()` no longer inadvertently calls `.name_repair` functions twice (#310). * `vroom()` is now more robust to quoting issues when tracking the CSV state (#301) * `vroom()` now registers the S3 class with `methods::setOldClass()` (r-dbi/DBI#345) * `col_datetime()` now supports '%s' format, which represents decimal seconds since the Unix epoch. * `col_numeric()` now supports `grouping_mark` and `decimal_mark` that are unicode characters, such as U+00A0 which is commonly used as the grouping mark for numbers in France (https://github.com/tidyverse/readr/issues/796). * `vroom_fwf()` gains a `skip_empty_rows` argument to skip empty lines (https://github.com/tidyverse/readr/issues/1211) * `vroom_fwf()` now respects `n_max`, as intended (#334) * `vroom_lines()` gains a `na` argument. * `vroom_write_lines()` no longer escapes or quotes lines. * `vroom_write_lines()` now works as intended (#291). * `vroom_write(path=)` has been deprecated, in favor of `file`, to match readr. * `vroom_write_lines()` now exposes the `num_threads` argument. * `problems()` now prints the correct row number of parse errors (#326) * `problems()` now throws a more informative error if called on a readr object (#308). * `problems()` now de-duplicates identical problems (#318) * Fix an inadvertent performance regression when reading values (#309) * `n_max` argument is correctly respected in edge cases (#306) * factors with implicit levels now work when fields are quoted, as intended (#330) * Guessing double types no longer unconditionally ignores leading whitespace. Now whitespace is only ignored when `trim_ws` is set. # vroom 1.4.0 ## Major changes and new functions * vroom now tracks indexing and parsing errors like readr. The first time an issue is encountered a warning will be signaled. A tibble of all found problems can be retrieved with `vroom::problems()`. (#247) * Data with newlines within quoted fields will now automatically revert to using a single thread and be properly read (#282) * NUL values in character data are now permitted, with a warning. * New `vroom_write_lines()` function to write a character vector to a file (#291) * `vroom_write()` gains a `eol=` parameter to specify the end of line character(s) to use. Use `vroom_write(eol = "\r\n")` to write a file with Windows style newlines (#263). ## Minor improvements and fixes * Datetime formats used when guessing now match those used when parsing (#240) * Quotes are now only valid next to newlines or delimiters (#224) * `vroom()` now signals an R error for invalid date and datetime formats, instead of crashing the session (#220). * `vroom(comment = )` now accepts multi-character comments (#286) * `vroom_lines()` now works with empty files (#285) * Vectors are now subset properly when given invalid subscripts (#283) * `vroom_write()` now works when the delimiter is empty, e.g. `delim = ""` (#287). * `vroom_write()` now works with all ALTREP vectors, including string vectors (#270) * An internal call to `new.env()` now correctly uses the `parent` argument (#281) # vroom 1.3.2 * Test failures on R 4.1 related to factors with NA values fixed (#262) * `vroom()` now works without error with readr versions of col specs (#256, #264, #266) # vroom 1.3.1 * Test failures on R 4.1 related to POSIXct classes fixed (#260) * Column subsetting with double indexes now works again (#257) * `vroom(n_max=)` now only partially downloads files from connections, as intended (#259) # vroom 1.3.0 * The Rcpp dependency has been removed in favor of cpp11. * `vroom()` now handles cases when `id` is set and a column in skipped (#237) * `vroom()` now supports column selections when there are some empty column names (#238) * `vroom()` argument `n_max` now works properly for files with windows newlines and no final newline (#244) * Subsetting vectors now works with `View()` in RStudio if there are now rows to subset (#253). * Subsetting datetime columns now works with `NA` indices (#236). # vroom 1.2.1 * `vroom()` now writes the column names if given an input with no rows (#213) * `vroom()` columns now support indexing with NA values (#201) * `vroom()` no longer truncates the last value in a file if the file contains windows newlines but no final newline (#219). * `vroom()` now works when the `na` argument is encoded in non ASCII or UTF-8 locales _and_ the file encoding is not the same as the native encoding (#233). * `vroom_fwf()` now verifies that the positions are valid, namely that the begin value is always less than the previous end (#217). * `vroom_lines()` gains a `locale` argument so you can control the encoding of the file (#218) * `vroom_write()` now supports the `append` argument with R connections (#232) # vroom 1.2.0 ## Breaking changes * `vroom_altrep_opts()` and the argument `vroom(altrep_opts =)` have been renamed to `vroom_altrep()` and `altrep` respectively. The prior names have been deprecated. ## New Features * `vroom()` now supports reading Big Integer values with the `bit64` package. Use `col_big_integer()` or the "I" shortcut to read a column as big integers. (#198) * `cols()` gains a `.delim` argument and `vroom()` now uses it as the delimiter if it is provided (#192) * `vroom()` now supports reading from `stdin()` directly, interpreted as the C-level standard input (#106). ## Minor improvements and fixes * `col_date` now parses single digit month and day (@edzer, #123, #170) * `fwf_empty()` now uses the `skip` parameter, as intended. * `vroom()` can now read single line files without a terminal newline (#173). * `vroom()` can now select the id column if provided (#110). * `vroom()` now correctly copies string data for factor levels (#184) * `vroom()` no longer crashes when files have trailing fields, windows newlines and the file is not newline or null terminated. * `vroom()` now includes a spec object with the `col_types` class, as intended. * `vroom()` now better handles floating point values with very large exponents (#164). * `vroom()` now uses better heuristics to guess the delimiter and now throws an error if a delimiter cannot be guessed (#126, #141, #167). * `vroom()` now has an improved error message when a file does not exist (#169). * `vroom()` no longer leaks file handles (#177, #180) * `vroom()` now outputs its messages on `stdout()` rather than `stderr()`, which avoids the text being red in RStudio and in the Windows GUI. * `vroom()` no longer overflows when reading files with more than 2B entries (@wlattner, #183). * `vroom_fwf()` is now more robust if not all lines are the expected length (#78) * `vroom_fwf()` and `fwf_empty()` now support passing `Inf` to `guess_max()`. * `vroom_str()` now works with S4 objects. * `vroom_fwf()` now handles files with dos newlines properly. * `vroom_write()` now does not try to write anything when given empty inputs (#172). * Dates, times, and datetimes now properly consider the locale when parsing. * Added benchmarks with _wide_ data for both numeric and character data (#87, @R3myG) * The delimiter used for parsing is now shown in the message output (#95 @R3myG) # vroom 1.0.2 ## New Features * The column created by `id` is now stored as an run length encoded Altrep vector, which uses less memory and is much faster for large inputs. (#111) ## Minor improvements and fixes * `vroom_lines()` now properly respects the `n_max` parameter (#142) * `vroom()` and `vroom_lines()` now support reading files which do not end in newlines by using a file connection (#40). * `vroom_write()` now works with the standard output connection `stdout()` (#106). * `vroom_write()` no longer crashes non-deterministically when used on Altrep vectors. * The integer parser now returns NA values for invalid inputs (#135) * Fix additional UBSAN issue in the mio project reported by CRAN (#97) * Fix indexing into connections with quoted fields (#119) * Move example files for `vroom()` out of `\dontshow{}`. * Fix integer overflow with very large files (#116, #119) * Fix missing columns and windows newlines (#114) * Fix encoding of column names (#113, #115) * Throw an error message when writing a zip file, which is not supported (@metaOO, #145) * Default message output from `vroom()` now uses `Rows` and `Cols` (@meta00, #140) # vroom 1.0.1 ## New Features * `vroom_lines()` function added, to (lazily) read lines from a file into a character vector (#90). ## Minor improvements and fixes * Fix for a hang on Windows caused by a race condition in the progress bar (#98) * Remove accidental runtime dependency on testthat (#104) * Fix to actually return non-Altrep character columns on R 3.2, 3.3 and 3.4. * Disable colors in the progress bar when running in RStudio, to work around an issue where the progress bar would be garbled (https://github.com/rstudio/rstudio/issues/4777) * Fix for UBSAN issues reported by CRAN (#97) * Fix for rchk issues reported by CRAN (#94) * The progress bar now only updates every 10 milliseconds. * Getting started vignette index entry now more informative (#92) # vroom 1.0.0 * Initial release * Added a `NEWS.md` file to track changes to the package. vroom/MD50000644000176200001440000003763114533733452011747 0ustar liggesuserse79a45243061deec264374225f6d3d12 *DESCRIPTION 39e66b68a353a8b8fe293fcb134b0bd6 *LICENSE b264f8f06659cefbcc4bd49a48ba4f9b *NAMESPACE 8b6e5a561a8d0a6d80000fffad3d87ca *NEWS.md d7578264258a633b154d1ea963e14f13 *R/altrep.R cea5bee00b0da619f1a038b065ed12cc *R/col_types.R 7e38e98c5c023b59dcfa340dd18b74a9 *R/collector.R 906d45b4b55ec294c67a4c42abfa9991 *R/cpp11.R a7399ea4bb7e979fd965e85e69ff0ed2 *R/date.R da487c4e68869cf63174e9e3a1f3c426 *R/example.R cc4c6c7576c2c336f7c4efb2c775322a *R/generator.R 5d1649cb20f8177dad6bb1916e7d281e *R/locale.R 6961f413398229915811ffd33ab9adeb *R/path.R b8702f9c307e4cd003954bc6ac9576c9 *R/problems.R 44c86bd029ec5ce1cda465ae14421c9c *R/sysdata.rda 8d970b8724b592bddb1e4e50d16ec6d3 *R/tidyselect.R 4f60caeb4688ef2e73c93ea76fc501ac *R/utils.R 7cf58801069c1e9ea9e0fd94f2ce4730 *R/vroom-package.R e1209a0cc5de9c92da4a22787f1a3325 *R/vroom.R a450bdc0ff58951fe08cd5e2d23d9f26 *R/vroom_fwf.R fcd6717acfc273d5c9fec0adaf6ee3c9 *R/vroom_lines.R 5710a474640fc790ac7553bd862cda47 *R/vroom_write.R 1314012d59eccc8f8e43299eb5f586ec *R/zzz.R 6f5f13985a7d5f56574893bf5f831278 *README.md e7b966327341c90218a4dc0e3fa0a467 *build/stage23.rdb cff00164e4a0aac3f60fa53ab62cf1c7 *build/vignette.rds 6ed3362ee7637ad6f2f77d96d96064a5 *build/vroom.pdf f66fad5f29df5d746eba05092f5db402 *inst/COPYRIGHTS 606d5b9a74cb921587b7d8b380c014db *inst/WORDLIST 079547f9bdda65602ec9546b56591cd9 *inst/bench/GNUmakefile 81b65cd5dafc0ef9280e7012a96eb28f *inst/bench/README.md 962d06b985e9e8193a542aa49664461d *inst/bench/all_character-long.tsv adb19a2ad5f8df1a425c3f141bbb1ca6 *inst/bench/all_character-long/data.table-data.table.R dc62df65db108c3edf6b5a5e01f3c0a2 *inst/bench/all_character-long/input.R b7e86e3149030918d62d9085c9dadd04 *inst/bench/all_character-long/read.delim-base.R f019cf85f14ec96e800472b1352fffde *inst/bench/all_character-long/readr-dplyr.R 6217f8f584aceb27f04ce2cab2302e98 *inst/bench/all_character-long/vroom-base.R 85de11b01605e56086180437bdb0ca45 *inst/bench/all_character-long/vroom-dplyr.R b58b6986b69cf07ac7964270c94dbb8b *inst/bench/all_character-long/vroom_no_altrep-dplyr.R d2c0a521f2f5a23609e93c8ba945c3ac *inst/bench/all_character-wide.tsv adb19a2ad5f8df1a425c3f141bbb1ca6 *inst/bench/all_character-wide/data.table-data.table.R dc62df65db108c3edf6b5a5e01f3c0a2 *inst/bench/all_character-wide/input.R b7e86e3149030918d62d9085c9dadd04 *inst/bench/all_character-wide/read.delim-base.R f019cf85f14ec96e800472b1352fffde *inst/bench/all_character-wide/readr-dplyr.R 6217f8f584aceb27f04ce2cab2302e98 *inst/bench/all_character-wide/vroom-base.R 85de11b01605e56086180437bdb0ca45 *inst/bench/all_character-wide/vroom-dplyr.R b58b6986b69cf07ac7964270c94dbb8b *inst/bench/all_character-wide/vroom_no_altrep-dplyr.R 6191cb718a65d44c0daa2a68a26678be *inst/bench/all_numeric-long.tsv 75d64c09a3c43c1217bf1c3d9c98d290 *inst/bench/all_numeric-long/data.table-data.table.R e91cda9b9eb6f49a2548549ecf9ed467 *inst/bench/all_numeric-long/input.R da8f45a2672500ed9ecb2c9b59ea2a64 *inst/bench/all_numeric-long/read.delim-base.R 9a9f4b4f35b233b38814c243c3f1815f *inst/bench/all_numeric-long/readr-dplyr.R 123685f2b589d8f51662fc63681d23d9 *inst/bench/all_numeric-long/vroom-base.R 4648ea621c2e2938d7823aa4119e16c1 *inst/bench/all_numeric-long/vroom-dplyr.R 81f9b037b915cc475618706f6b710979 *inst/bench/all_numeric-long/vroom_no_altrep-base.R 9a6290051b3b157163bd6afc61efa4ea *inst/bench/all_numeric-long/vroom_no_altrep-dplyr.R 53814663e32460a5ecc75aad6c23ad11 *inst/bench/all_numeric-wide.tsv 75d64c09a3c43c1217bf1c3d9c98d290 *inst/bench/all_numeric-wide/data.table-data.table.R e91cda9b9eb6f49a2548549ecf9ed467 *inst/bench/all_numeric-wide/input.R da8f45a2672500ed9ecb2c9b59ea2a64 *inst/bench/all_numeric-wide/read.delim-base.R 9a9f4b4f35b233b38814c243c3f1815f *inst/bench/all_numeric-wide/readr-dplyr.R 123685f2b589d8f51662fc63681d23d9 *inst/bench/all_numeric-wide/vroom-base.R 4648ea621c2e2938d7823aa4119e16c1 *inst/bench/all_numeric-wide/vroom-dplyr.R 81f9b037b915cc475618706f6b710979 *inst/bench/all_numeric-wide/vroom_no_altrep-base.R 9a6290051b3b157163bd6afc61efa4ea *inst/bench/all_numeric-wide/vroom_no_altrep-dplyr.R 6684603ba8dd737f4c3cbef4afbef69f *inst/bench/download-data.sh 272eb2473e8927f9a70985f4baae314a *inst/bench/fwf.tsv 5ff8c048c6a2857e100f759acce6f57d *inst/bench/fwf/read.delim-base.R 13fee535654a32c302044cc380400811 *inst/bench/fwf/readr-dplyr.R e4a0bc2e0e47fa3a14f02ce7fa4005dd *inst/bench/fwf/vroom-base.R 7c846584c6e0e5e4c2fb1f27af3d6d9e *inst/bench/fwf/vroom-dplyr.R 72faf515d2a3e87a140b15203a51422d *inst/bench/fwf/vroom_no_altrep-dplyr.R a9bef83722c563c8889b1ecaec91073f *inst/bench/run-bench-fwf.R 46fe1a77f4af4bb647c17b1e5b3f6d0e *inst/bench/run-bench.R cb525dd7d173f28932d6620e1e7aaf4e *inst/bench/script.sh 8aa4f38f904c3623e9b916907f77474e *inst/bench/session_info.R 739020b62d11d339fcd031adcc51fdb8 *inst/bench/session_info.tsv 4ab3d6b59a213b29ba612815a83678bf *inst/bench/summarise-benchmarks.R 26e194ad5ae7130a5ee491b7629d33a4 *inst/bench/taxi.tsv 67993afdf5abbc57c367492e103d98b2 *inst/bench/taxi/data.table-data.table.R a932ed0ffbeacde593f5a6065cac8762 *inst/bench/taxi/read.delim-base.R 2416b4a645cdc93327c0d7cc3aee1b32 *inst/bench/taxi/readr-dplyr.R 94ac2bea499d537469f3ab25af03a6bf *inst/bench/taxi/vroom-base.R ed53d4dc02d668ccfe4f826a5817131d *inst/bench/taxi/vroom-dplyr.R 2f5c39593ca034162f7a804b792ecb2d *inst/bench/taxi/vroom_no_altrep-dplyr.R 1e41ec0c73e72d2259961877f5e60030 *inst/bench/taxi_multiple.tsv aeff388f7d649d76028e1238270d6483 *inst/bench/taxi_multiple/data.table-data.table.R ac9b0e14211305511911366bc7f39b53 *inst/bench/taxi_multiple/readr-dplyr.R 5f45cc33ee2da55da015df6f4e42eaca *inst/bench/taxi_multiple/vroom-base.R df54197ddbcc59fc2bfce4fc27d9284c *inst/bench/taxi_multiple/vroom-dplyr.R 486f016a15d9ca9f859b08fa0fa1ca3f *inst/bench/taxi_multiple/vroom_no_altrep-dplyr.R 1a269cc94a87f78d77b5460ef7f0f92e *inst/bench/taxi_writing.tsv bce900b3729f5169c2a0e3ff69066e2f *inst/bench/taxi_writing/base-gzip.R ec78a353534aac0dc72f34de7817d365 *inst/bench/taxi_writing/base-multithreaded_gzip.R 59aa3d11d4e89fe086c6653160733ce0 *inst/bench/taxi_writing/base-uncompressed.R 65d54f18d2ae928dc35113b7d83c9724 *inst/bench/taxi_writing/base-zstandard.R 2452746d818a560078b619f58eef2585 *inst/bench/taxi_writing/data.table-gzip.R 08acfab7dc00869e0802968c6232e688 *inst/bench/taxi_writing/data.table-multithreaded_gzip.R 5e403204b04f1164999719aa31041ba5 *inst/bench/taxi_writing/data.table-uncompressed.R 220bb0e090cde9eebd6c36d204804d7c *inst/bench/taxi_writing/readr-gzip.R ba6b5437d3646624706c6480272e4e40 *inst/bench/taxi_writing/readr-multithreaded_gzip.R 2e67ae5451b9016d0ceb5fe54478ca71 *inst/bench/taxi_writing/readr-uncompressed.R f36cf746c35c0938ed1ce52fc682f9fc *inst/bench/taxi_writing/readr-zstandard.R f55501606ca8159731a842638a5dc782 *inst/bench/taxi_writing/vroom-gzip.R d66a90ed9df1fab170da6bbfeb3dac1b *inst/bench/taxi_writing/vroom-multithreaded_gzip.R 11eaebca3f055e57e42ee3c56916e9f2 *inst/bench/taxi_writing/vroom-uncompressed.R 50e91970e6b3ab9c44280d973cdc8a93 *inst/bench/taxi_writing/vroom-zstandard.R f3a20df6512f61f766d0f4f398cd9e39 *inst/doc/benchmarks.R 2c7711d2eefe67f23fb9d488b8456f6a *inst/doc/benchmarks.Rmd ea4525bfa0ce83165c1406741ab5a040 *inst/doc/benchmarks.html 83a154f6a8e2c356793514dee99378c8 *inst/doc/vroom.R 5588eb1f7eaf8d22de3f0fc5d6bcf408 *inst/doc/vroom.Rmd 3128de7c6db8c05831569148de286380 *inst/doc/vroom.html 891bca40aeba031848809c3e587b20d7 *inst/extdata/fwf-sample.txt c25b678e1bf7c33f1438853930348b5f *inst/extdata/mtcars-4.csv 2a4acab046ab5735b2589365a29888fa *inst/extdata/mtcars-6.csv 86fea77ddd827fa7a394d1a20736920f *inst/extdata/mtcars-8.csv c25e0f1ec4043e2839e1a64743844c48 *inst/extdata/mtcars-multi-cyl.zip c502359c26a0931eef53b2207b2344f9 *inst/extdata/mtcars.csv 397ea2199f10f07a4a0c49800f24d4a5 *inst/extdata/mtcars.csv.bz2 fe1d9b4a0c1eb859115f6b9a8ff7f5f7 *inst/extdata/mtcars.csv.gz 0dfbcad9416210addd8b23f740e3ae72 *inst/extdata/mtcars.csv.xz c2636e23318e78960fafbcab4b7b5a97 *inst/extdata/mtcars.csv.zip dacabb877353b4cc5ca46d51f3a980f8 *inst/words/adjectives.txt 031ecab52f8891f5939ba4ffdc46aa8b *inst/words/animals.txt 1545e6ae5677bec5c3fabcec2e640a9a *man/as.col_spec.Rd 7c37ad5cb939a21b9b291339d8955fac *man/cols.Rd c327af5267f8e7ff8291681bb608ed61 *man/date_names.Rd a1cbaf3f328e8d74e747faacf640c7fc *man/figures/lifecycle-archived.svg 6f521fb1819410630e279d1abf88685a *man/figures/lifecycle-defunct.svg 391f696f961e28914508628a7af31b74 *man/figures/lifecycle-deprecated.svg 691b1eb2aec9e1bec96b79d11ba5e631 *man/figures/lifecycle-experimental.svg 405e252e54a79b33522e9699e4e9051c *man/figures/lifecycle-maturing.svg f41ed996be135fb35afe00641621da61 *man/figures/lifecycle-questioning.svg 46de21252239c5a23d400eae83ec6b2d *man/figures/lifecycle-retired.svg 306bef67d1c636f209024cf2403846fd *man/figures/lifecycle-soft-deprecated.svg ed42e3fbd7cc30bc6ca8fa9b658e24a8 *man/figures/lifecycle-stable.svg 99f6e77d8cd1072e42e744a4950e143a *man/figures/lifecycle-superseded.svg 273b88eb801722e06ecf3e14e8aad67e *man/figures/logo.png 20a2781f5e16f591e6e2c1d5b20fc802 *man/gen_tbl.Rd 79199e713c01afb921c7b7e7302005e1 *man/generators.Rd 5a081fc6a179c2c3c69a526b598f6d59 *man/guess_type.Rd ed6764f79d88f000296189a14e33c4b1 *man/locale.Rd c1bfc3f64e9c81a4886e7fc618d94b92 *man/output_column.Rd 063b5bcd7f49a92a563c1442a758079d *man/problems.Rd 8ea663763ab5298343bfd97f43925321 *man/reexports.Rd d2960252b4b247bef56c46bc9e4b4c38 *man/spec.Rd 8d31976948099f7c1e43cb2e42542a5b *man/vroom-package.Rd 4c496efeb26b70f00a8adc1a60b52b3a *man/vroom.Rd 5134b3078fe1ef9ddc466372eaa5c95b *man/vroom_altrep.Rd 2b269584f37dbfc0720f322254b6c0d2 *man/vroom_altrep_opts.Rd 0234d282d26b72d0b9e9596b9d647eb0 *man/vroom_example.Rd 26184bc6d367a6ab8144181774eeba42 *man/vroom_format.Rd 29be61a688d1e556de39275ad921f98f *man/vroom_fwf.Rd 432dc803ec5b16a8e7cd2f3cfa4127f5 *man/vroom_lines.Rd 40f2f168b355e61f6907402826208290 *man/vroom_progress.Rd c40ee20a33c6cb2fbe7f177339742a21 *man/vroom_str.Rd 5f4eb1dfa518de50f3be12293de887d4 *man/vroom_write.Rd c18b9659d6b61259b0969ecc20aee93a *man/vroom_write_lines.Rd f47b13e3b0712c14612a722e75325d30 *src/DateTime.h e5c3afdbc303bf5627b30fcbdf0403d8 *src/DateTimeParser.h 0d0ad673b72273bb6e4e1d1e479cdf16 *src/Iconv.cpp 78fb08b4a0ff380fc612de22c4fa0f8c *src/Iconv.h 63b5460e3126815b41d4ef15c6ae3709 *src/LocaleInfo.cpp e53e95e6227bb57203b78aa543db284f *src/LocaleInfo.h 218fe0451665a9e5d682c106e7557af2 *src/Makevars 2f48b5329dc1e2326238428ad6859b89 *src/altrep.cc 981800e144b27a60da8293a147457b09 *src/altrep.h d3272bdb78ca04f6fb983b7ae9ef2a77 *src/collectors.h 227d50952dc94e96b144b4b984b42a84 *src/columns.h 69cf09d6f4e60bb39691070d4516b06f *src/connection.h e52df5dabfe1ec909ceb39484996104a *src/cpp11.cpp f3776dafc2f5be6bdbe16f5747f024ad *src/delimited_index.cc 625b366beac6451187de29565509d19c *src/delimited_index.h 737d7b4f7dbcdc852e84539b19975dd7 *src/delimited_index_connection.cc f952f303a08a5f67efbcd34cb7beaee1 *src/delimited_index_connection.h b033f37b66e1d7323f10e033bfc70730 *src/fixed_width_index.h 391c85514dec38ac523ef13686d31f89 *src/fixed_width_index_connection.cc d457cb616fca2821cb3d65122e329101 *src/fixed_width_index_connection.h da10ba3830f76c5f2e2caae63d9e6621 *src/gen.cc 7ed02f64ba7bfa6a7e4bf9df6a0710d5 *src/grisu3.c d763bbf07076d54cf56b950534f343ab *src/grisu3.h 16c7f364f19a34cf31f4387b9c3a7f86 *src/guess_type.cc 1ca643821616d125a0bad00d7ca97c8c *src/iconv_file.cc 0a5e66a402095f5aeb9046fe6d6a3c1e *src/index.h 7ad56fcf6aab214a82041820273ad9f7 *src/index_collection.cc 0d41fcd469cc6bc4769dd9dcf0339dc5 *src/index_collection.h 1960b51b7beff4084c076accc5e62052 *src/iterator.h e1c9a711c35a90bc5b390b5353f4bb71 *src/mio/LICENSE 8cf1647ef048f533fd8f5c607f6c17d0 *src/mio/README.md 8cf194a0ba13d524b3edfa0f08e6a4df *src/mio/include/mio/detail/mmap.ipp b7f5fafa0aa67c0dd638a38a4a69d24e *src/mio/include/mio/detail/string_util.hpp 55dd33f9d3e166f6cc32952d86cdaa8c *src/mio/include/mio/mmap.hpp 3a884ac9f9863e5d2f58a3293a0a6880 *src/mio/include/mio/page.hpp 5d82bbbaf755ddcb59af919fc9efe29d *src/mio/include/mio/shared_mmap.hpp 778a0d3d0056150fe5337759b80c6ccd *src/multi_progress.h 1e3105050c184dd3a18f0a4220561354 *src/parallel.h 560847141d75123372385fb1f8ec57a1 *src/r_utils.h d261cb4daf619cef259029b993429e7e *src/tzfile.h 75cc7f7bca2f5d1760923dafb50f4606 *src/unicode_fopen.h df30f275cf54af47d5f8d5fbd8d1b75d *src/utils.h 89463ed14c213bb9d0927ae7e26e17cd *src/vroom.cc bc6e59a97792f73522c4b706c649e3b5 *src/vroom.h bcd039412d33b49e5ac2f570b241e93f *src/vroom_big_int.cc ef7925466294e58e59c1d1b8700dc2cb *src/vroom_big_int.h eb8d41f82dc2789b3e08019f4840dc7b *src/vroom_chr.cc 0df81f7d9c84525c9b8db60c59614441 *src/vroom_chr.h 66eb93fb8d0d9e07d443893468363711 *src/vroom_date.cc 44ce3c9fe5fc5254675f4e2ac5a54dcc *src/vroom_date.h 52f8c1c415eb8f1ee27624c72b467cca *src/vroom_dbl.cc ee8cd8faac1f61204079aa15531c5a58 *src/vroom_dbl.h af3a86fe7d992356fbc42ea6c3a71004 *src/vroom_dttm.cc 00b3dbdb68d85f7a4cf47fe5785ec5ed *src/vroom_dttm.h ea3604e25a2c3442de752290a23217ff *src/vroom_errors.cpp 0272ffbe8ef3562d5e3806f9ecd470f6 *src/vroom_errors.h 65339e9cf76ef64f806df7ada86f7d1b *src/vroom_fct.cc 1cd8a72518586f6dab1eb266608719f0 *src/vroom_fct.h 923b1d8cf6ad43048735f0906fbface0 *src/vroom_fwf.cc d0620846d5d9c5d1bb46de4c471e8836 *src/vroom_int.cc 38d0e0737bb44a156f9f4d9f0e481da9 *src/vroom_int.h 54b804a64842b414b3bb5e4c8daf4657 *src/vroom_lgl.h 43ab6ea9ec923d28955d34a6e77d5666 *src/vroom_num.cc 013e4fb64d48e4697e8b88bcd35aedbe *src/vroom_num.h b75c4a68af405e5a7c7e3ffd618d6fad *src/vroom_rle.cc 1f41187cb53cb1156ce28e198146ddf5 *src/vroom_rle.h eb9e65723f8164b55f51aa1cb57b1bf3 *src/vroom_time.cc bcfb330d096b300842281c9762c3ff10 *src/vroom_time.h 622e4959c76f105da75c4ae1440ea6e9 *src/vroom_types.h 05c88a65030ce08af2106cf71c7be934 *src/vroom_vec.h 9ead246987b2fa7e9b8323baeaeff0cd *src/vroom_write.cc 0622a97a2aaa3c342f09636052c2d7f5 *tests/spelling.R c82a2577cb9993b282b951cca140d5d9 *tests/testthat.R 4dd1c15175fa069866284a492ba8d563 *tests/testthat/_snaps/col_types.md 6c0aad2e972959d3f583a8af8ae0d916 *tests/testthat/_snaps/path.md 5e5bf6c0d03c97162d08023ae678bd24 *tests/testthat/_snaps/problems.md d41d8cd98f00b204e9800998ecf8427e *tests/testthat/empty-file 7c924d6682b55bb601d4e5b428123709 *tests/testthat/enc-iso-8859-1.txt 6e78e051cd53e60db1e95d692e1d6201 *tests/testthat/fwf-trailing-crlf.txt 2b4d8a640b79cf108e795e2a81a9cb4b *tests/testthat/fwf-trailing.txt c22dc4abbe3f38c60e008ec0e92509fe *tests/testthat/helper.R 716a6dfa9225688ecb2f91dab9954fcb *tests/testthat/multi-byte-ascii.txt 17a6ec92cea2c1a5caa57f6612f3393c *tests/testthat/multi-byte-unicode.txt 1079c96176280816d6627e9d96750120 *tests/testthat/multi-file/bar 365bb8566485f194fac0ae108cbf22cb *tests/testthat/multi-file/baz 0cad2bebd3e80c23b2dad26a746df5c1 *tests/testthat/multi-file/foo 1a7acac5f18865215e5bdc6bbe618907 *tests/testthat/multi-file/qux ea427f49d3ef99f68cc3902c7d317a87 *tests/testthat/raw.csv 952c30f579ed3438656f98f3d39f4af1 *tests/testthat/test-big-int.R 499edc14e0e51ca0e4fe827e1b237df1 *tests/testthat/test-chr.R c8220a4e044ea8701f17cba981206910 *tests/testthat/test-col_types.R 837e8ff294790892a522f89163667e51 *tests/testthat/test-connection.R b1c9765db0213f3150af02273dcf3c31 *tests/testthat/test-datetime.R 913abdb993e2926cc13ad335f772d0b8 *tests/testthat/test-dbl.R b71c6618a964165380db141023a9c4e1 *tests/testthat/test-factor.R 2d7dba6067b90d4d874ed642efb18031 *tests/testthat/test-int.R 89eee040ccb0ba243901045b26e05b98 *tests/testthat/test-logical.R 2a945d9305d538d52195b02df2534791 *tests/testthat/test-multi-byte.R 82cbfdf461847cd3a35106381b393a0d *tests/testthat/test-multi-file.R fac6d3f989eda914f12e380f494baa21 *tests/testthat/test-num.R ed987761890328bbbc5b73e68738ff23 *tests/testthat/test-path.R 3a025881d3de142950cbc20a5eb2d6d5 *tests/testthat/test-problems.R d486a0f4bc7e06f5ebf50cfcfebbbf45 *tests/testthat/test-select.R 8a43ffdc55781562742fce7b65b7891a *tests/testthat/test-vroom.R e15e4b20e49fea5fbf8fe02a1d8ddbbd *tests/testthat/test-vroom_fwf.R d0d2e9b9819c731774723a38503a1fde *tests/testthat/test-vroom_lines.R 75c6e6dcf1b0fcfb73c020110d2d95e9 *tests/testthat/test-vroom_write.R 2c7711d2eefe67f23fb9d488b8456f6a *vignettes/benchmarks.Rmd 5588eb1f7eaf8d22de3f0fc5d6bcf408 *vignettes/vroom.Rmd vroom/inst/0000755000176200001440000000000014533652002012371 5ustar liggesusersvroom/inst/doc/0000755000176200001440000000000014533652002013136 5ustar liggesusersvroom/inst/doc/vroom.html0000644000176200001440000022142114533652002015170 0ustar liggesusers Get started with vroom

Get started with vroom

The vroom package contains one main function vroom() which is used to read all types of delimited files. A delimited file is any file in which the data is separated (delimited) by one or more characters.

The most common type of delimited files are CSV (Comma Separated Values) or TSV (Tab Separated Values) files, typically these files have a .csv and .tsv suffix respectively.

library(vroom)

This vignette covers the following topics:

  • The basics of reading files, including
    • single files
    • multiple files
    • compressed files
    • remote files
  • Skipping particular columns.
  • Specifying column types, for additional safety and when the automatic guessing fails.
  • Writing regular and compressed files

Reading files

To read a CSV, or other type of delimited file with vroom pass the file to vroom(). The delimiter will be automatically guessed if it is a common delimiter; e.g.Β (β€œ,” β€œβ€ ” β€œ|” β€œ:” β€œ;”). If the guessing fails or you are using a less common delimiter specify it with the delim parameter. (e.g.Β delim = ",").

We have included an example CSV file in the vroom package for use in examples and tests. Access it with vroom_example("mtcars.csv")

# See where the example file is stored on your machine
file <- vroom_example("mtcars.csv")
file
#> [1] "/private/tmp/RtmpDxlsSX/Rinst130257392eda0/vroom/extdata/mtcars.csv"

# Read the file, by default vroom will guess the delimiter automatically.
vroom(file)
#> Rows: 32 Columns: 12
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr  (1): model
#> dbl (11): mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 12
#>   model          mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Mazda RX4     21       6   160   110  3.9   2.62  16.5     0     1     4     4
#> 2 Mazda RX4 W…  21       6   160   110  3.9   2.88  17.0     0     1     4     4
#> 3 Datsun 710    22.8     4   108    93  3.85  2.32  18.6     1     1     4     1
#> # β„Ή 29 more rows

# You can also specify it explicitly, which is (slightly) faster, and safer if
# you know how the file is delimited.
vroom(file, delim = ",")
#> Rows: 32 Columns: 12
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr  (1): model
#> dbl (11): mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 12
#>   model          mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Mazda RX4     21       6   160   110  3.9   2.62  16.5     0     1     4     4
#> 2 Mazda RX4 W…  21       6   160   110  3.9   2.88  17.0     0     1     4     4
#> 3 Datsun 710    22.8     4   108    93  3.85  2.32  18.6     1     1     4     1
#> # β„Ή 29 more rows

Reading multiple files

If you are reading a set of files which all have the same columns (as in, names and types), you can pass the filenames directly to vroom() and it will combine them into one result. vroom’s example datasets include several files named like mtcars-i.csv. These files contain subsets of the mtcars data, for cars with different numbers of cylinders. First, we get a character vector of these filepaths.

ve <- grep("mtcars-[0-9].csv", vroom_examples(), value = TRUE)
files <- sapply(ve, vroom_example)
files
#>                                                            mtcars-4.csv 
#> "/private/tmp/RtmpDxlsSX/Rinst130257392eda0/vroom/extdata/mtcars-4.csv" 
#>                                                            mtcars-6.csv 
#> "/private/tmp/RtmpDxlsSX/Rinst130257392eda0/vroom/extdata/mtcars-6.csv" 
#>                                                            mtcars-8.csv 
#> "/private/tmp/RtmpDxlsSX/Rinst130257392eda0/vroom/extdata/mtcars-8.csv"

Now we can efficiently read them into one table by passing the filenames directly to vroom.

vroom(files)
#> Rows: 32 Columns: 12
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr  (1): model
#> dbl (11): mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 12
#>   model        mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>      <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Datsun 710  22.8     4  108     93  3.85  2.32  18.6     1     1     4     1
#> 2 Merc 240D   24.4     4  147.    62  3.69  3.19  20       1     0     4     2
#> 3 Merc 230    22.8     4  141.    95  3.92  3.15  22.9     1     0     4     2
#> # β„Ή 29 more rows

Often the filename or directory where the files are stored contains information. The id parameter can be used to add an extra column to the result with the full path to each file. (in this case we name the column path).

vroom(files, id = "path")
#> Rows: 32 Columns: 13
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr  (1): model
#> dbl (11): mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 13
#>   path   model   mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>  <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 /priv… Dats…  22.8     4  108     93  3.85  2.32  18.6     1     1     4     1
#> 2 /priv… Merc…  24.4     4  147.    62  3.69  3.19  20       1     0     4     2
#> 3 /priv… Merc…  22.8     4  141.    95  3.92  3.15  22.9     1     0     4     2
#> # β„Ή 29 more rows

Reading compressed files

vroom supports reading zip, gz, bz2 and xz compressed files automatically, just pass the filename of the compressed file to vroom.

file <- vroom_example("mtcars.csv.gz")

vroom(file)
#> Rows: 32 Columns: 12
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr  (1): model
#> dbl (11): mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 12
#>   model          mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Mazda RX4     21       6   160   110  3.9   2.62  16.5     0     1     4     4
#> 2 Mazda RX4 W…  21       6   160   110  3.9   2.88  17.0     0     1     4     4
#> 3 Datsun 710    22.8     4   108    93  3.85  2.32  18.6     1     1     4     1
#> # β„Ή 29 more rows

vroom() decompresses, indexes and writes the decompressed data to a file in the temp directory in a single stream. The temporary file is used to lazily look up the values and will be automatically cleaned up when all values in the object have been fully read, the object is removed, or the R session ends.

Reading individual files from a multi-file zip archive

If you are reading a zip file that contains multiple files with the same format, you can read a subset of the files at once like so:

zip_file <- vroom_example("mtcars-multi-cyl.zip")
filenames <- unzip(zip_file, list = TRUE)$Name
filenames
#> [1] "mtcars-4.csv" "mtcars-6.csv" "mtcars-8.csv"

# imagine we only want to read 2 of the 3 files
vroom(purrr::map(filenames[c(1, 3)], ~ unz(zip_file, .x)))
#> Rows: 25 Columns: 12
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr  (1): model
#> dbl (11): mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 25 Γ— 12
#>   model        mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>      <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Datsun 710  22.8     4  108     93  3.85  2.32  18.6     1     1     4     1
#> 2 Merc 240D   24.4     4  147.    62  3.69  3.19  20       1     0     4     2
#> 3 Merc 230    22.8     4  141.    95  3.92  3.15  22.9     1     0     4     2
#> # β„Ή 22 more rows

Reading remote files

vroom can read files directly from the internet as well by passing the URL of the file to vroom.

file <- "https://raw.githubusercontent.com/tidyverse/vroom/main/inst/extdata/mtcars.csv"
vroom(file)

It can even read gzipped files from the internet (although not the other compressed formats).

file <- "https://raw.githubusercontent.com/tidyverse/vroom/main/inst/extdata/mtcars.csv.gz"
vroom(file)

Column selection

vroom provides the same interface for column selection and renaming as dplyr::select(). This provides very efficient, flexible and readable selections. For example you can select by:

  • A character vector of column names
file <- vroom_example("mtcars.csv.gz")

vroom(file, col_select = c(model, cyl, gear))
#> Rows: 32 Columns: 3
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr (1): model
#> dbl (2): cyl, gear
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 3
#>   model           cyl  gear
#>   <chr>         <dbl> <dbl>
#> 1 Mazda RX4         6     4
#> 2 Mazda RX4 Wag     6     4
#> 3 Datsun 710        4     4
#> # β„Ή 29 more rows
  • A numeric vector of column indexes, e.g.Β c(1, 2, 5)
vroom(file, col_select = c(1, 3, 11))
#> Rows: 32 Columns: 3
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr (1): model
#> dbl (2): cyl, gear
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 3
#>   model           cyl  gear
#>   <chr>         <dbl> <dbl>
#> 1 Mazda RX4         6     4
#> 2 Mazda RX4 Wag     6     4
#> 3 Datsun 710        4     4
#> # β„Ή 29 more rows
  • Using the selection helpers such as starts_with() and ends_with()
vroom(file, col_select = starts_with("d"))
#> Rows: 32 Columns: 2
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> dbl (2): disp, drat
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 2
#>    disp  drat
#>   <dbl> <dbl>
#> 1   160  3.9 
#> 2   160  3.9 
#> 3   108  3.85
#> # β„Ή 29 more rows
  • You can also rename columns
vroom(file, col_select = c(car = model, everything()))
#> Rows: 32 Columns: 12
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr  (1): model
#> dbl (11): mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 32 Γ— 12
#>   car            mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Mazda RX4     21       6   160   110  3.9   2.62  16.5     0     1     4     4
#> 2 Mazda RX4 W…  21       6   160   110  3.9   2.88  17.0     0     1     4     4
#> 3 Datsun 710    22.8     4   108    93  3.85  2.32  18.6     1     1     4     1
#> # β„Ή 29 more rows

Reading fixed width files

A fixed width file can be a very compact representation of numeric data. Unfortunately, it’s also often painful to read because you need to describe the length of every field. vroom aims to make it as easy as possible by providing a number of different ways to describe the field structure. Use vroom_fwf() in conjunction with one of the following helper functions to read the file.

fwf_sample <- vroom_example("fwf-sample.txt")
cat(readLines(fwf_sample))
#> John Smith          WA        418-Y11-4111 Mary Hartford       CA        319-Z19-4341 Evan Nolan          IL        219-532-c301
  • fwf_empty() - Guess based on the position of empty columns.
vroom_fwf(fwf_sample, fwf_empty(fwf_sample, col_names = c("first", "last", "state", "ssn")))
#> Rows: 3 Columns: 4
#> ── Column specification ────────────────────────────────────────────────────────
#> 
#> chr (4): first, last, state, ssn
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 3 Γ— 4
#>   first last     state ssn         
#>   <chr> <chr>    <chr> <chr>       
#> 1 John  Smith    WA    418-Y11-4111
#> 2 Mary  Hartford CA    319-Z19-4341
#> 3 Evan  Nolan    IL    219-532-c301
  • fwf_widths() - Use user provided set of field widths.
vroom_fwf(fwf_sample, fwf_widths(c(20, 10, 12), c("name", "state", "ssn")))
#> Rows: 3 Columns: 3
#> ── Column specification ────────────────────────────────────────────────────────
#> 
#> chr (3): name, state, ssn
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 3 Γ— 3
#>   name          state ssn         
#>   <chr>         <chr> <chr>       
#> 1 John Smith    WA    418-Y11-4111
#> 2 Mary Hartford CA    319-Z19-4341
#> 3 Evan Nolan    IL    219-532-c301
  • fwf_positions() - Use user provided sets of start and end positions.
vroom_fwf(fwf_sample, fwf_positions(c(1, 30), c(20, 42), c("name", "ssn")))
#> Rows: 3 Columns: 2
#> ── Column specification ────────────────────────────────────────────────────────
#> 
#> chr (2): name, ssn
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 3 Γ— 2
#>   name          ssn         
#>   <chr>         <chr>       
#> 1 John Smith    418-Y11-4111
#> 2 Mary Hartford 319-Z19-4341
#> 3 Evan Nolan    219-532-c301
  • fwf_cols() - Use user provided named widths.
vroom_fwf(fwf_sample, fwf_cols(name = 20, state = 10, ssn = 12))
#> Rows: 3 Columns: 3
#> ── Column specification ────────────────────────────────────────────────────────
#> 
#> chr (3): name, state, ssn
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 3 Γ— 3
#>   name          state ssn         
#>   <chr>         <chr> <chr>       
#> 1 John Smith    WA    418-Y11-4111
#> 2 Mary Hartford CA    319-Z19-4341
#> 3 Evan Nolan    IL    219-532-c301
  • fwf_cols() - Use user provided named pairs of positions.
vroom_fwf(fwf_sample, fwf_cols(name = c(1, 20), ssn = c(30, 42)))
#> Rows: 3 Columns: 2
#> ── Column specification ────────────────────────────────────────────────────────
#> 
#> chr (2): name, ssn
#> 
#> β„Ή Use `spec()` to retrieve the full column specification for this data.
#> β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 3 Γ— 2
#>   name          ssn         
#>   <chr>         <chr>       
#> 1 John Smith    418-Y11-4111
#> 2 Mary Hartford 319-Z19-4341
#> 3 Evan Nolan    219-532-c301

Column types

vroom guesses the data types of columns as they are read, however sometimes the guessing fails and it is necessary to explicitly set the type of one or more columns.

The available specifications are: (with single letter abbreviations in quotes)

  • col_logical() β€˜l’, containing only T, F, TRUE, FALSE, 1 or 0.
  • col_integer() β€˜i’, integer values.
  • col_big_integer() β€˜I’, Big integer values. (64bit integers)
  • col_double() β€˜d’, floating point values.
  • col_number() β€˜n’, numbers containing the grouping_mark
  • col_date(format = "") β€˜D’: with the locale’s date_format.
  • col_time(format = "") β€˜t’: with the locale’s time_format.
  • col_datetime(format = "") β€˜T’: ISO8601 date times.
  • col_factor(levels, ordered) β€˜f’, a fixed set of values.
  • col_character() β€˜c’, everything else.
  • col_skip() ’_, -’, don’t import this column.
  • col_guess() β€˜?’, parse using the β€œbest” type based on the input.

You can tell vroom what columns to use with the col_types() argument in a number of ways.

If you only need to override a single column the most concise way is to use a named vector.

# read the 'hp' columns as an integer
vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i"))
#> # A tibble: 32 Γ— 12
#>   model          mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>        <dbl> <dbl> <dbl> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Mazda RX4     21       6   160   110  3.9   2.62  16.5     0     1     4     4
#> 2 Mazda RX4 W…  21       6   160   110  3.9   2.88  17.0     0     1     4     4
#> 3 Datsun 710    22.8     4   108    93  3.85  2.32  18.6     1     1     4     1
#> # β„Ή 29 more rows

# also skip reading the 'cyl' column
vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i", cyl = "_"))
#> # A tibble: 32 Γ— 11
#>   model           mpg  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <chr>         <dbl> <dbl> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Mazda RX4      21     160   110  3.9   2.62  16.5     0     1     4     4
#> 2 Mazda RX4 Wag  21     160   110  3.9   2.88  17.0     0     1     4     4
#> 3 Datsun 710     22.8   108    93  3.85  2.32  18.6     1     1     4     1
#> # β„Ή 29 more rows

# also read the gears as a factor
vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i", cyl = "_", gear = "f"))
#> # A tibble: 32 Γ— 11
#>   model           mpg  disp    hp  drat    wt  qsec    vs    am gear   carb
#>   <chr>         <dbl> <dbl> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <fct> <dbl>
#> 1 Mazda RX4      21     160   110  3.9   2.62  16.5     0     1 4         4
#> 2 Mazda RX4 Wag  21     160   110  3.9   2.88  17.0     0     1 4         4
#> 3 Datsun 710     22.8   108    93  3.85  2.32  18.6     1     1 4         1
#> # β„Ή 29 more rows

You can read all the columns with the same type, by using the .default argument. For example reading everything as a character.

vroom(vroom_example("mtcars.csv"), col_types = c(.default = "c"))
#> # A tibble: 32 Γ— 12
#>   model        mpg   cyl   disp  hp    drat  wt    qsec  vs    am    gear  carb 
#>   <chr>        <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 Mazda RX4    21    6     160   110   3.9   2.62  16.46 0     1     4     4    
#> 2 Mazda RX4 W… 21    6     160   110   3.9   2.875 17.02 0     1     4     4    
#> 3 Datsun 710   22.8  4     108   93    3.85  2.32  18.61 1     1     4     1    
#> # β„Ή 29 more rows

However you can also use the col_*() functions in a list.

vroom(
  vroom_example("mtcars.csv"),
  col_types = list(hp = col_integer(), cyl = col_skip(), gear = col_factor())
)
#> # A tibble: 32 Γ— 11
#>   model           mpg  disp    hp  drat    wt  qsec    vs    am gear   carb
#>   <chr>         <dbl> <dbl> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <fct> <dbl>
#> 1 Mazda RX4      21     160   110  3.9   2.62  16.5     0     1 4         4
#> 2 Mazda RX4 Wag  21     160   110  3.9   2.88  17.0     0     1 4         4
#> 3 Datsun 710     22.8   108    93  3.85  2.32  18.6     1     1 4         1
#> # β„Ή 29 more rows

This is most useful when a column type needs additional information, such as for categorical data when you know all of the levels of a factor.

vroom(
  vroom_example("mtcars.csv"),
  col_types = list(gear = col_factor(levels = c(gear = c("3", "4", "5"))))
)
#> # A tibble: 32 Γ— 12
#>   model          mpg   cyl  disp    hp  drat    wt  qsec    vs    am gear   carb
#>   <chr>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <fct> <dbl>
#> 1 Mazda RX4     21       6   160   110  3.9   2.62  16.5     0     1 4         4
#> 2 Mazda RX4 W…  21       6   160   110  3.9   2.88  17.0     0     1 4         4
#> 3 Datsun 710    22.8     4   108    93  3.85  2.32  18.6     1     1 4         1
#> # β„Ή 29 more rows

Name repair

Often the names of columns in the original dataset are not ideal to work with. vroom() uses the same .name_repair argument as tibble, so you can use one of the default name repair strategies or provide a custom function. A great approach is to use the janitor::make_clean_names() function as the input. This will automatically clean the names to use whatever case you specify, here I am setting it to use ALLCAPS names.

vroom(
  vroom_example("mtcars.csv"),
  .name_repair = ~ janitor::make_clean_names(., case = "all_caps")
)

Writing delimited files

Use vroom_write() to write delimited files, the default delimiter is tab, to write TSV files. Writing to TSV by default has the following benefits: - Avoids the issue of whether to use ; (common in Europe) or , (common in the US) - Unlikely to require quoting in fields, as very few fields contain tabs - More easily and efficiently ingested by Unix command line tools such as cut, perl and awk.

vroom_write(mtcars, "mtcars.tsv")

Writing CSV delimited files

However you can also use delim = ',' to write CSV files, which are common as inputs to GUI spreadsheet tools like Excel or Google Sheets.

vroom_write(mtcars, "mtcars.csv", delim = ",")

Writing compressed files

For gzip, bzip2 and xz compression the outputs will be automatically compressed if the filename ends in .gz, .bz2 or .xz.

vroom_write(mtcars, "mtcars.tsv.gz")

vroom_write(mtcars, "mtcars.tsv.bz2")

vroom_write(mtcars, "mtcars.tsv.xz")

It is also possible to use other compressors by using pipe() with vroom_write() to create a pipe connection to command line utilities, such as

  • pigz, a parallel gzip implementation
  • lbzip2, a parallel bzip2 implementation
  • pixz, a parallel xz implementation
  • Zstandard, a modern real-time compression algorithm.

The parallel compression versions can be considerably faster for large output files and generally vroom_write() is fast enough that the compression speed becomes the bottleneck when writing.

vroom_write(mtcars, pipe("pigz > mtcars.tsv.gz"))

Reading and writing from standard input and output

vroom supports reading and writing to the C-level stdin and stdout of the R process by using stdin() and stdout(). E.g. from a shell prompt you can pipe to and from vroom directly.

cat inst/extdata/mtcars.csv | Rscript -e 'vroom::vroom(stdin())'

Rscript -e 'vroom::vroom_write(iris, stdout())' | head

Note this interpretation of stdin() and stdout() differs from that used elsewhere by R, however we believe it better matches most user’s expectations for this use case.

Further reading

vroom/inst/doc/vroom.Rmd0000644000176200001440000002750514510121442014747 0ustar liggesusers--- title: "Get started with vroom" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Get started with vroom} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_knit$set(root.dir = tempdir()) knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) options(tibble.print_min = 3) ``` The vroom package contains one main function `vroom()` which is used to read all types of delimited files. A delimited file is any file in which the data is separated (delimited) by one or more characters. The most common type of delimited files are CSV (Comma Separated Values) or TSV (Tab Separated Values) files, typically these files have a `.csv` and `.tsv` suffix respectively. ```{r} library(vroom) ``` This vignette covers the following topics: - The basics of reading files, including - single files - multiple files - compressed files - remote files - Skipping particular columns. - Specifying column types, for additional safety and when the automatic guessing fails. - Writing regular and compressed files ## Reading files To read a CSV, or other type of delimited file with vroom pass the file to `vroom()`. The delimiter will be automatically guessed if it is a common delimiter; e.g. ("," "\t" " " "|" ":" ";"). If the guessing fails or you are using a less common delimiter specify it with the `delim` parameter. (e.g. `delim = ","`). We have included an example CSV file in the vroom package for use in examples and tests. Access it with `vroom_example("mtcars.csv")` ```{r} # See where the example file is stored on your machine file <- vroom_example("mtcars.csv") file # Read the file, by default vroom will guess the delimiter automatically. vroom(file) # You can also specify it explicitly, which is (slightly) faster, and safer if # you know how the file is delimited. vroom(file, delim = ",") ``` ## Reading multiple files If you are reading a set of files which all have the same columns (as in, names and types), you can pass the filenames directly to `vroom()` and it will combine them into one result. vroom's example datasets include several files named like `mtcars-i.csv`. These files contain subsets of the `mtcars` data, for cars with different numbers of cylinders. First, we get a character vector of these filepaths. ```{r} ve <- grep("mtcars-[0-9].csv", vroom_examples(), value = TRUE) files <- sapply(ve, vroom_example) files ``` Now we can efficiently read them into one table by passing the filenames directly to vroom. ```{r} vroom(files) ``` Often the filename or directory where the files are stored contains information. The `id` parameter can be used to add an extra column to the result with the full path to each file. (in this case we name the column `path`). ```{r} vroom(files, id = "path") ``` ## Reading compressed files vroom supports reading zip, gz, bz2 and xz compressed files automatically, just pass the filename of the compressed file to vroom. ```{r} file <- vroom_example("mtcars.csv.gz") vroom(file) ``` `vroom()` decompresses, indexes and writes the decompressed data to a file in the temp directory in a single stream. The temporary file is used to lazily look up the values and will be automatically cleaned up when all values in the object have been fully read, the object is removed, or the R session ends. ### Reading individual files from a multi-file zip archive If you are reading a zip file that contains multiple files with the same format, you can read a subset of the files at once like so: ```{r} zip_file <- vroom_example("mtcars-multi-cyl.zip") filenames <- unzip(zip_file, list = TRUE)$Name filenames # imagine we only want to read 2 of the 3 files vroom(purrr::map(filenames[c(1, 3)], ~ unz(zip_file, .x))) ``` ## Reading remote files vroom can read files directly from the internet as well by passing the URL of the file to vroom. ```{r, eval = as.logical(Sys.getenv("NOT_CRAN", "false"))} file <- "https://raw.githubusercontent.com/tidyverse/vroom/main/inst/extdata/mtcars.csv" vroom(file) ``` It can even read gzipped files from the internet (although not the other compressed formats). ```{r, eval = as.logical(Sys.getenv("NOT_CRAN", "false"))} file <- "https://raw.githubusercontent.com/tidyverse/vroom/main/inst/extdata/mtcars.csv.gz" vroom(file) ``` ## Column selection vroom provides the same interface for column selection and renaming as [dplyr::select()](https://dplyr.tidyverse.org/reference/select.html). This provides very efficient, flexible and readable selections. For example you can select by: - A character vector of column names ```{r} file <- vroom_example("mtcars.csv.gz") vroom(file, col_select = c(model, cyl, gear)) ``` - A numeric vector of column indexes, e.g. `c(1, 2, 5)` ```{r} vroom(file, col_select = c(1, 3, 11)) ``` - Using the selection helpers such as `starts_with()` and `ends_with()` ```{r} vroom(file, col_select = starts_with("d")) ``` - You can also rename columns ```{r} vroom(file, col_select = c(car = model, everything())) ``` ## Reading fixed width files A fixed width file can be a very compact representation of numeric data. Unfortunately, it's also often painful to read because you need to describe the length of every field. vroom aims to make it as easy as possible by providing a number of different ways to describe the field structure. Use `vroom_fwf()` in conjunction with one of the following helper functions to read the file. ```{r} fwf_sample <- vroom_example("fwf-sample.txt") cat(readLines(fwf_sample)) ``` - `fwf_empty()` - Guess based on the position of empty columns. ```{r} vroom_fwf(fwf_sample, fwf_empty(fwf_sample, col_names = c("first", "last", "state", "ssn"))) ``` - `fwf_widths()` - Use user provided set of field widths. ```{r} vroom_fwf(fwf_sample, fwf_widths(c(20, 10, 12), c("name", "state", "ssn"))) ``` - `fwf_positions()` - Use user provided sets of start and end positions. ```{r} vroom_fwf(fwf_sample, fwf_positions(c(1, 30), c(20, 42), c("name", "ssn"))) ``` - `fwf_cols()` - Use user provided named widths. ```{r} vroom_fwf(fwf_sample, fwf_cols(name = 20, state = 10, ssn = 12)) ``` - `fwf_cols()` - Use user provided named pairs of positions. ```{r} vroom_fwf(fwf_sample, fwf_cols(name = c(1, 20), ssn = c(30, 42))) ``` ## Column types vroom guesses the data types of columns as they are read, however sometimes the guessing fails and it is necessary to explicitly set the type of one or more columns. The available specifications are: (with single letter abbreviations in quotes) * `col_logical()` 'l', containing only `T`, `F`, `TRUE`, `FALSE`, `1` or `0`. * `col_integer()` 'i', integer values. * `col_big_integer()` 'I', Big integer values. (64bit integers) * `col_double()` 'd', floating point values. * `col_number()` 'n', numbers containing the `grouping_mark` * `col_date(format = "")` 'D': with the locale's `date_format`. * `col_time(format = "")` 't': with the locale's `time_format`. * `col_datetime(format = "")` 'T': ISO8601 date times. * `col_factor(levels, ordered)` 'f', a fixed set of values. * `col_character()` 'c', everything else. * `col_skip()` '_, -', don't import this column. * `col_guess()` '?', parse using the "best" type based on the input. You can tell vroom what columns to use with the `col_types()` argument in a number of ways. If you only need to override a single column the most concise way is to use a named vector. ```{r} # read the 'hp' columns as an integer vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i")) # also skip reading the 'cyl' column vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i", cyl = "_")) # also read the gears as a factor vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i", cyl = "_", gear = "f")) ``` You can read all the columns with the same type, by using the `.default` argument. For example reading everything as a character. ```{r} vroom(vroom_example("mtcars.csv"), col_types = c(.default = "c")) ``` However you can also use the `col_*()` functions in a list. ```{r} vroom( vroom_example("mtcars.csv"), col_types = list(hp = col_integer(), cyl = col_skip(), gear = col_factor()) ) ``` This is most useful when a column type needs additional information, such as for categorical data when you know all of the levels of a factor. ```{r} vroom( vroom_example("mtcars.csv"), col_types = list(gear = col_factor(levels = c(gear = c("3", "4", "5")))) ) ``` ## Name repair Often the names of columns in the original dataset are not ideal to work with. `vroom()` uses the same `.name_repair` argument as tibble, so you can use one of the default name repair strategies or provide a custom function. A great approach is to use the [`janitor::make_clean_names()`](https://sfirke.github.io/janitor/reference/make_clean_names.html) function as the input. This will automatically clean the names to use whatever case you specify, here I am setting it to use `ALLCAPS` names. ```{r, eval = FALSE} vroom( vroom_example("mtcars.csv"), .name_repair = ~ janitor::make_clean_names(., case = "all_caps") ) ``` ## Writing delimited files Use `vroom_write()` to write delimited files, the default delimiter is tab, to write TSV files. Writing to TSV by default has the following benefits: - Avoids the issue of whether to use `;` (common in Europe) or `,` (common in the US) - Unlikely to require quoting in fields, as very few fields contain tabs - More easily and efficiently ingested by Unix command line tools such as `cut`, `perl` and `awk`. ```{r} vroom_write(mtcars, "mtcars.tsv") ``` ```{r, include = FALSE} unlink("mtcars.tsv") ``` ### Writing CSV delimited files However you can also use `delim = ','` to write CSV files, which are common as inputs to GUI spreadsheet tools like Excel or Google Sheets. ```{r} vroom_write(mtcars, "mtcars.csv", delim = ",") ``` ```{r, include = FALSE} unlink("mtcars.csv") ``` ### Writing compressed files For gzip, bzip2 and xz compression the outputs will be automatically compressed if the filename ends in `.gz`, `.bz2` or `.xz`. ```{r} vroom_write(mtcars, "mtcars.tsv.gz") vroom_write(mtcars, "mtcars.tsv.bz2") vroom_write(mtcars, "mtcars.tsv.xz") ``` ```{r, include = FALSE} unlink(c("mtcars.tsv.gz", "mtcars.tsv.bz2", "mtcars.tsv.xz")) ``` It is also possible to use other compressors by using `pipe()` with `vroom_write()` to create a pipe connection to command line utilities, such as - [pigz](https://zlib.net/pigz/), a parallel gzip implementation - lbzip2, a parallel bzip2 implementation - [pixz](https://github.com/vasi/pixz), a parallel xz implementation - [Zstandard](https://facebook.github.io/zstd/), a modern real-time compression algorithm. The parallel compression versions can be considerably faster for large output files and generally `vroom_write()` is fast enough that the compression speed becomes the bottleneck when writing. ```{r, eval = nzchar(Sys.which("pigz"))} vroom_write(mtcars, pipe("pigz > mtcars.tsv.gz")) ``` ```{r, include = FALSE} unlink("mtcars.tsv.gz") ``` ### Reading and writing from standard input and output vroom supports reading and writing to the C-level `stdin` and `stdout` of the R process by using `stdin()` and `stdout()`. E.g. from a shell prompt you can pipe to and from vroom directly. ```shell cat inst/extdata/mtcars.csv | Rscript -e 'vroom::vroom(stdin())' Rscript -e 'vroom::vroom_write(iris, stdout())' | head ``` Note this interpretation of `stdin()` and `stdout()` differs from that used elsewhere by R, however we believe it better matches most user's expectations for this use case. ## Further reading - `vignette("benchmarks")` discusses the performance of vroom, how it compares to alternatives and how it achieves its results. - [πŸ“½ vroom: Because Life is too short to read slow](https://www.youtube.com/watch?v=RA9AjqZXxMU&t=10s) - Presentation of vroom at UseR!2019 ([slides](https://speakerdeck.com/jimhester/vroom)) - [πŸ“Ή vroom: Read and write rectangular data quickly](https://www.youtube.com/watch?v=ZP_y5eaAc60) - a video tour of the vroom features. vroom/inst/doc/benchmarks.Rmd0000644000176200001440000003455114363145201015726 0ustar liggesusers--- title: "Vroom Benchmarks" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Vroom Benchmarks} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) library(ggplot2) library(forcats) library(dplyr) library(tidyr) library(fs) pretty_sec <- function(x) { x[!is.na(x)] <- prettyunits::pretty_sec(x[!is.na(x)]) x } pretty_lgl <- function(x) { case_when( x == TRUE ~ "TRUE", x == FALSE ~ "FALSE", TRUE ~ "" ) } read_benchmark <- function(file, desc) { vroom::vroom(file, col_types = c("ccccddddd")) %>% filter(op != "setup") %>% mutate( altrep = case_when( grepl("^vroom_no_altrep", reading_package) ~ FALSE, grepl("^vroom", reading_package) ~ TRUE, TRUE ~ NA ), reading_package = case_when( grepl("^vroom", reading_package) ~ "vroom", TRUE ~ reading_package ), label = fct_reorder( glue::glue("{reading_package}{altrep}\n{manip_package}", altrep = ifelse(is.na(altrep), "", glue::glue("(altrep = {altrep})")) ), case_when(type == "real" ~ time, TRUE ~ 0), sum), op = factor(op, desc) ) } generate_subtitle <- function(data) { rows <- scales::comma(data$rows[[1]]) cols <- scales::comma(data$cols[[1]]) size <- fs_bytes(data$size[[1]]) glue::glue("{rows} x {cols} - {size}B") } plot_benchmark <- function(data, title) { subtitle <- generate_subtitle(data) data <- data %>% filter(reading_package != "read.delim", type == "real") p1 <- data %>% ggplot() + geom_bar(aes(x = label, y = time, fill = op, group = label), stat = "identity") + scale_fill_brewer(type = "qual", palette = "Set2") + scale_y_continuous(labels = scales::number_format(suffix = "s")) + coord_flip() + labs(title = title, subtitle = subtitle, x = NULL, y = NULL, fill = NULL) + theme(legend.position = "bottom") p2 <- data %>% group_by(label) %>% summarise(max_memory = max(max_memory)) %>% ggplot() + geom_bar(aes(x = label, y = max_memory / (1024 * 1024)), stat = "identity") + scale_y_continuous(labels = scales::number_format(suffix = "Mb")) + coord_flip() + labs(title = "Maximum memory usage", x = NULL, y = NULL) + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) library(patchwork) p1 + p2 + plot_layout(widths = c(2, 1)) } make_table <- function(data) { data %>% filter(type == "real") %>% select(-label, -size, -type, -rows, -cols) %>% spread(op, time) %>% mutate( total = read + print + head + tail + sample + filter + aggregate, max_memory = as.character(bench::as_bench_bytes(max_memory)) ) %>% arrange(desc(total)) %>% mutate_if(is.numeric, pretty_sec) %>% mutate_if(is.logical, pretty_lgl) %>% select(reading_package, manip_package, altrep, max_memory, everything()) %>% rename( "reading\npackage" = reading_package, "manipulating\npackage" = manip_package, memory = max_memory ) %>% knitr::kable(digits = 2, align = "r", format = "html") } desc <- c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate") ``` vroom is a new approach to reading delimited and fixed width data into R. It stems from the observation that when parsing files reading data from disk and finding the delimiters is generally not the main bottle neck. Instead (re)-allocating memory and parsing the values into R data types (particularly for characters) takes the bulk of the time. Therefore you can obtain very rapid input by first performing a fast indexing step and then using the Altrep framework available in R versions 3.5+ to access the values in a lazy / delayed fashion. ## How it works The initial reading of the file simply records the locations of each individual record, the actual values are not read into R. Altrep vectors are created for each column in the data which hold a pointer to the index and the memory mapped file. When these vectors are indexed the value is read from the memory mapping. This means initial reading is extremely fast, in the real world dataset below it is ~ 1/4 the time of the multi-threaded `data.table::fread()`. Sampling operations are likewise extremely fast, as only the data actually included in the sample is read. This means things like the tibble print method, calling `head()`, `tail()` `x[sample(), ]` etc. have very low overhead. Filtering also can be fast, only the columns included in the filter selection have to be fully read and only the data in the filtered rows needs to be read from the remaining columns. Grouped aggregations likewise only need to read the grouping variables and the variables aggregated. Once a particular vector is fully materialized the speed for all subsequent operations should be identical to a normal R vector. This approach potentially also allows you to work with data that is larger than memory. As long as you are careful to avoid materializing the entire dataset at once it can be efficiently queried and subset. # Reading delimited files The following benchmarks all measure reading delimited files of various sizes and data types. Because vroom delays reading the benchmarks also do some manipulation of the data afterwards to try and provide a more realistic performance comparison. Because the `read.delim` results are so much slower than the others they are excluded from the plots, but are retained in the tables. ## Taxi Trip Dataset This real world dataset is from Freedom of Information Law (FOIL) Taxi Trip Data from the NYC Taxi and Limousine Commission 2013, originally posted at . It is also hosted on [archive.org](https://archive.org/details/nycTaxiTripData2013). The first table trip_fare_1.csv is 1.55G in size. #> Observations: 14,776,615 #> Variables: 11 #> $ medallion "89D227B655E5C82AECF13C3F540D4CF4", "0BD7C8F5B... #> $ hack_license "BA96DE419E711691B9445D6A6307C170", "9FD8F69F0... #> $ vendor_id "CMT", "CMT", "CMT", "CMT", "CMT", "CMT", "CMT... #> $ pickup_datetime "2013-01-01 15:11:48", "2013-01-06 00:18:35", ... #> $ payment_type "CSH", "CSH", "CSH", "CSH", "CSH", "CSH", "CSH... #> $ fare_amount 6.5, 6.0, 5.5, 5.0, 9.5, 9.5, 6.0, 34.0, 5.5, ... #> $ surcharge 0.0, 0.5, 1.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0, 0... #> $ mta_tax 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0... #> $ tip_amount 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0... #> $ tolls_amount 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.8, 0.0, 0... #> $ total_amount 7.0, 7.0, 7.0, 6.0, 10.5, 10.0, 6.5, 39.3, 7.0... ### Taxi Benchmarks code: [bench/taxi](https://github.com/tidyverse/vroom/tree/main/inst/bench/taxi) All benchmarks were run on a Amazon EC2 [m5.4xlarge](https://aws.amazon.com/ec2/instance-types/m5/) instance with 16 vCPUs and an [EBS](https://aws.amazon.com/ebs/) volume type. The benchmarks labeled `vroom_base` uses `vroom` with base functions for manipulation. `vroom_dplyr` uses `vroom` to read the file and dplyr functions to manipulate. `data.table` uses `fread()` to read the file and `data.table` functions to manipulate and `readr` uses `readr` to read the file and `dplyr` to manipulate. By default vroom only uses Altrep for character vectors, these are labeled `vroom(altrep: normal)`. The benchmarks labeled `vroom(altrep: full)` instead use Altrep vectors for all supported types and `vroom(altrep: none)` disable Altrep entirely. The following operations are performed. - The data is read - `print()` - _N.B. read.delim uses `print(head(x, 10))` because printing the whole dataset takes > 10 minutes_ - `head()` - `tail()` - Sampling 100 random rows - Filtering for "UNK" payment, this is 6434 rows (0.0435% of total). - Aggregation of mean fare amount per payment type. ```{r, fig.height = 8, fig.width=10, warning = FALSE, echo = FALSE, message = FALSE} taxi <- read_benchmark(path_package("vroom", "bench", "taxi.tsv"), desc) plot_benchmark(taxi, "Time to analyze taxi trip data") make_table(taxi) ``` (*N.B. Rcpp used in the dplyr implementation fully materializes all the Altrep numeric vectors when using `filter()` or `sample_n()`, which is why the first of these cases have additional overhead when using full Altrep.*). ## All numeric data All numeric data is really a worst case scenario for vroom. The index takes about as much memory as the parsed data. Also because parsing doubles can be done quickly in parallel and text representations of doubles are only ~25 characters at most there isn't a great deal of savings for delayed parsing. For these reasons (and because the data.table implementation is very fast) vroom is a bit slower than fread for pure numeric data. However because vroom is multi-threaded it is a bit quicker than readr and read.delim for this type of data. ### Long code: [bench/all_numeric-long](https://github.com/tidyverse/vroom/tree/main/inst/bench/all_numeric-long) ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} all_num <- read_benchmark(path_package("vroom", "bench", "all_numeric-long.tsv"), desc) plot_benchmark(all_num, "Time to analyze long all numeric data") make_table(all_num) ``` ### Wide code: [bench/all_numeric-wide](https://github.com/tidyverse/vroom/tree/main/inst/bench/all_numeric-wide) ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} all_num_wide <- read_benchmark(path_package("bench", "all_numeric-wide.tsv", package = "vroom"), desc) plot_benchmark(all_num_wide, "Time to analyze wide all numeric data") make_table(all_num_wide) ``` ## All character data code: [bench/all_character-long](https://github.com/tidyverse/vroom/tree/main/inst/bench/all_character-long) All character data is a best case scenario for vroom when using Altrep, as it takes full advantage of the lazy reading. ### Long ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} all_chr <- read_benchmark(path_package("vroom", "bench", "all_character-long.tsv"), desc) plot_benchmark(all_chr, "Time to analyze long all character data") make_table(all_chr) ``` ### Wide code: [bench/all_character-wide](https://github.com/tidyverse/vroom/tree/main/inst/bench/all_character-wide) ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} all_chr_wide <- read_benchmark(path_package("vroom", "bench", "all_character-wide.tsv"), desc) plot_benchmark(all_chr_wide, "Time to analyze wide all character data") make_table(all_chr_wide) ``` # Reading multiple delimited files code: [bench/taxi_multiple](https://github.com/tidyverse/vroom/tree/main/inst/bench/taxi_multiple) ```{r, echo = FALSE, message = FALSE, eval = TRUE} mult <- read_benchmark(path_package("vroom", "bench", "taxi_multiple.tsv"), desc) ``` The benchmark reads all 12 files in the taxi trip fare data, totaling `r scales::comma(mult$rows[[1]])` rows and `r mult$cols[[1]]` columns for a total file size of `r format(fs_bytes(mult$size[[1]]))`. ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} plot_benchmark(mult, "Time to analyze multiple file data") make_table(mult) ``` # Reading fixed width files ## United States Census 5-Percent Public Use Microdata Sample files ```{r, echo = FALSE, message = FALSE, eval = TRUE} fwf <- read_benchmark(path_package("vroom", "bench", "fwf.tsv"), desc) ``` This fixed width dataset contains individual records of the characteristics of a 5 percent sample of people and housing units from the year 2000 and is freely available at . The data is split into files by state, and the state of California was used in this benchmark. The data totals `r scales::comma(fwf$rows[[1]])` rows and `r fwf$cols[[1]]` columns with a total file size of `r format(fs_bytes(fwf$size[[1]]))`. ## Census data benchmarks code: [bench/fwf](https://github.com/tidyverse/vroom/tree/main/inst/bench/fwf) ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} plot_benchmark(fwf, "Time to analyze fixed width data") make_table(fwf) ``` # Writing delimited files code: [bench/taxi_writing](https://github.com/tidyverse/vroom/tree/main/inst/bench/taxi_writing) The benchmarks write out the taxi trip dataset in a few different ways. - An uncompressed file - A gzip compressed file using `gzfile()` _(readr and vroom do this automatically for files ending in `.gz`)_ - A gzip compressed file compressed with multiple threads (natively for data.table and using a `pipe()` connection to [pigz](https://zlib.net/pigz/) for the rest). - A [Zstandard](https://facebook.github.io/zstd/) compressed file (data.table does not support this format). ```{r, fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE} taxi_writing <- read_benchmark(path_package("vroom", "bench", "taxi_writing.tsv"), c("setup", "writing")) %>% rename( package = reading_package, compression = manip_package ) %>% mutate( package = factor(package, c("base", "readr", "data.table", "vroom")), compression = factor(compression, rev(c("gzip", "multithreaded_gzip", "zstandard", "uncompressed"))) ) %>% filter(type == "real") subtitle <- generate_subtitle(taxi_writing) taxi_writing %>% ggplot(aes(x = compression, y = time, fill = package)) + geom_bar(stat = "identity", position = position_dodge2(reverse = TRUE, padding = .05)) + scale_fill_brewer(type = "qual", palette = "Set2") + scale_y_continuous(labels = scales::number_format(suffix = "s")) + theme(legend.position = "bottom") + coord_flip() + labs(title = "Writing taxi trip data", subtitle = subtitle, x = NULL, y = NULL, fill = NULL) taxi_writing %>% select(-size, -op, -rows, -cols, -type, -altrep, -label, -max_memory) %>% mutate_if(is.numeric, pretty_sec) %>% pivot_wider(names_from = package, values_from = time) %>% arrange(desc(compression)) %>% knitr::kable(digits = 2, align = "r", format = "html") ``` ## Session and package information ```{r, echo = FALSE, warning = FALSE, message = FALSE} si <- vroom::vroom(path_package("vroom", "bench", "session_info.tsv")) class(si) <- c("packages_info", "data.frame") select(as.data.frame(si), package, version = ondiskversion, date, source) %>% knitr::kable() ``` vroom/inst/doc/benchmarks.html0000644000176200001440000203177314533652001016155 0ustar liggesusers Vroom Benchmarks

Vroom Benchmarks

vroom is a new approach to reading delimited and fixed width data into R.

It stems from the observation that when parsing files reading data from disk and finding the delimiters is generally not the main bottle neck. Instead (re)-allocating memory and parsing the values into R data types (particularly for characters) takes the bulk of the time.

Therefore you can obtain very rapid input by first performing a fast indexing step and then using the Altrep framework available in R versions 3.5+ to access the values in a lazy / delayed fashion.

How it works

The initial reading of the file simply records the locations of each individual record, the actual values are not read into R. Altrep vectors are created for each column in the data which hold a pointer to the index and the memory mapped file. When these vectors are indexed the value is read from the memory mapping.

This means initial reading is extremely fast, in the real world dataset below it is ~ 1/4 the time of the multi-threaded data.table::fread(). Sampling operations are likewise extremely fast, as only the data actually included in the sample is read. This means things like the tibble print method, calling head(), tail() x[sample(), ] etc. have very low overhead. Filtering also can be fast, only the columns included in the filter selection have to be fully read and only the data in the filtered rows needs to be read from the remaining columns. Grouped aggregations likewise only need to read the grouping variables and the variables aggregated.

Once a particular vector is fully materialized the speed for all subsequent operations should be identical to a normal R vector.

This approach potentially also allows you to work with data that is larger than memory. As long as you are careful to avoid materializing the entire dataset at once it can be efficiently queried and subset.

Reading delimited files

The following benchmarks all measure reading delimited files of various sizes and data types. Because vroom delays reading the benchmarks also do some manipulation of the data afterwards to try and provide a more realistic performance comparison.

Because the read.delim results are so much slower than the others they are excluded from the plots, but are retained in the tables.

Taxi Trip Dataset

This real world dataset is from Freedom of Information Law (FOIL) Taxi Trip Data from the NYC Taxi and Limousine Commission 2013, originally posted at https://chriswhong.com/open-data/foil_nyc_taxi/. It is also hosted on archive.org.

The first table trip_fare_1.csv is 1.55G in size.

#> Observations: 14,776,615
#> Variables: 11
#> $ medallion       <chr> "89D227B655E5C82AECF13C3F540D4CF4", "0BD7C8F5B...
#> $ hack_license    <chr> "BA96DE419E711691B9445D6A6307C170", "9FD8F69F0...
#> $ vendor_id       <chr> "CMT", "CMT", "CMT", "CMT", "CMT", "CMT", "CMT...
#> $ pickup_datetime <chr> "2013-01-01 15:11:48", "2013-01-06 00:18:35", ...
#> $ payment_type    <chr> "CSH", "CSH", "CSH", "CSH", "CSH", "CSH", "CSH...
#> $ fare_amount     <dbl> 6.5, 6.0, 5.5, 5.0, 9.5, 9.5, 6.0, 34.0, 5.5, ...
#> $ surcharge       <dbl> 0.0, 0.5, 1.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0, 0...
#> $ mta_tax         <dbl> 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0...
#> $ tip_amount      <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
#> $ tolls_amount    <dbl> 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.8, 0.0, 0...
#> $ total_amount    <dbl> 7.0, 7.0, 7.0, 6.0, 10.5, 10.0, 6.5, 39.3, 7.0...

Taxi Benchmarks

code: bench/taxi

All benchmarks were run on a Amazon EC2 m5.4xlarge instance with 16 vCPUs and an EBS volume type.

The benchmarks labeled vroom_base uses vroom with base functions for manipulation. vroom_dplyr uses vroom to read the file and dplyr functions to manipulate. data.table uses fread() to read the file and data.table functions to manipulate and readr uses readr to read the file and dplyr to manipulate. By default vroom only uses Altrep for character vectors, these are labeled vroom(altrep: normal). The benchmarks labeled vroom(altrep: full) instead use Altrep vectors for all supported types and vroom(altrep: none) disable Altrep entirely.

The following operations are performed.

  • The data is read
  • print() - N.B. read.delim uses print(head(x, 10)) because printing the whole dataset takes > 10 minutes
  • head()
  • tail()
  • Sampling 100 random rows
  • Filtering for β€œUNK” payment, this is 6434 rows (0.0435% of total).
  • Aggregation of mean fare amount per payment type.
reading package manipulating package altrep memory read print head tail sample filter aggregate total
read.delim base 6.18GB 1m 12.3s 6ms 1ms 1ms 1ms 1.3s 895ms 1m 14.5s
readr dplyr 6.91GB 37.3s 147ms 2ms 1ms 17ms 249ms 538ms 38.3s
vroom dplyr FALSE 6.55GB 18.4s 117ms 2ms 1ms 14ms 961ms 1.2s 20.7s
vroom base TRUE 6.35GB 1.4s 158ms 3ms 1ms 1ms 1.1s 7.4s 10s
data.table data.table 6.38GB 5.8s 12ms 1ms 1ms 1ms 104ms 764ms 6.7s
vroom dplyr TRUE 6.41GB 1.3s 76ms 2ms 1ms 11ms 1.3s 4s 6.7s

(N.B. Rcpp used in the dplyr implementation fully materializes all the Altrep numeric vectors when using filter() or sample_n(), which is why the first of these cases have additional overhead when using full Altrep.).

All numeric data

All numeric data is really a worst case scenario for vroom. The index takes about as much memory as the parsed data. Also because parsing doubles can be done quickly in parallel and text representations of doubles are only ~25 characters at most there isn’t a great deal of savings for delayed parsing.

For these reasons (and because the data.table implementation is very fast) vroom is a bit slower than fread for pure numeric data.

However because vroom is multi-threaded it is a bit quicker than readr and read.delim for this type of data.

Long

code: bench/all_numeric-long

reading package manipulating package altrep memory read print head tail sample filter aggregate total
read.delim base 4.79GB 1m 51.4s 1.4s 1ms 1ms 2ms 4.5s 37ms 1m 57.3s
readr dplyr 2.82GB 13.1s 64ms 2ms 1ms 16ms 18ms 55ms 13.3s
vroom dplyr FALSE 2.75GB 1.3s 48ms 1ms 1ms 14ms 18ms 46ms 1.5s
vroom base FALSE 2.69GB 1.3s 48ms 1ms 1ms 3ms 6ms 55ms 1.4s
vroom dplyr TRUE 3.29GB 604ms 64ms 1ms 1ms 14ms 42ms 235ms 959ms
vroom base TRUE 3.28GB 581ms 55ms 1ms 1ms 3ms 29ms 251ms 920ms
data.table data.table 2.72GB 256ms 13ms 1ms 1ms 4ms 6ms 25ms 302ms

Wide

code: bench/all_numeric-wide

reading package manipulating package altrep memory read print head tail sample filter aggregate total
read.delim base 14.41GB 8m 41s 131ms 7ms 7ms 9ms 75ms 5ms 8m 41.2s
readr dplyr 5.46GB 56.1s 96ms 3ms 3ms 26ms 18ms 39ms 56.3s
vroom dplyr FALSE 5.35GB 6.9s 63ms 3ms 3ms 95ms 14ms 31ms 7.1s
vroom base FALSE 5.34GB 6.9s 61ms 3ms 3ms 5ms 6ms 7ms 7s
vroom dplyr TRUE 7.26GB 3s 68ms 4ms 14ms 23ms 20ms 77ms 3.2s
vroom base TRUE 7.26GB 3s 68ms 4ms 4ms 5ms 11ms 42ms 3.1s
data.table data.table 5.48GB 1.3s 100ms 1ms 1ms 3ms 4ms 4ms 1.4s

All character data

code: bench/all_character-long

All character data is a best case scenario for vroom when using Altrep, as it takes full advantage of the lazy reading.

Long

reading package manipulating package altrep memory read print head tail sample filter aggregate total
read.delim base 4.53GB 1m 43.1s 8ms 1ms 1ms 2ms 28ms 293ms 1m 43.4s
readr dplyr 4.35GB 1m 2.6s 102ms 2ms 1ms 17ms 20ms 215ms 1m 2.9s
vroom dplyr FALSE 4.3GB 50.5s 50ms 2ms 1ms 16ms 21ms 150ms 50.7s
data.table data.table 4.73GB 42.8s 16ms 1ms 1ms 4ms 16ms 149ms 43s
vroom base TRUE 3.22GB 595ms 46ms 1ms 1ms 3ms 163ms 2.1s 2.9s
vroom dplyr TRUE 3.21GB 640ms 58ms 2ms 1ms 16ms 185ms 1.2s 2.1s

Wide

code: bench/all_character-wide

reading package manipulating package altrep memory read print head tail sample filter aggregate total
read.delim base 13.09GB 8m 30.4s 149ms 7ms 8ms 26ms 224ms 59ms 8m 30.9s
readr dplyr 12.21GB 7m 39.4s 217ms 4ms 3ms 29ms 38ms 57ms 7m 39.8s
vroom dplyr FALSE 12.14GB 4m 7.3s 67ms 3ms 3ms 28ms 35ms 37ms 4m 7.5s
data.table data.table 12.66GB 3m 21.8s 135ms 2ms 2ms 33ms 168ms 15ms 3m 22.1s
vroom base TRUE 6.57GB 3.1s 62ms 5ms 4ms 5ms 55ms 252ms 3.5s
vroom dplyr TRUE 6.57GB 3.1s 64ms 5ms 4ms 27ms 82ms 160ms 3.4s

Reading multiple delimited files

code: bench/taxi_multiple

The benchmark reads all 12 files in the taxi trip fare data, totaling 173,179,759 rows and 11 columns for a total file size of 18.4G.

reading package manipulating package altrep memory read print head tail sample filter aggregate total
readr dplyr 63.5GB 7m 55s 837ms 1ms 1ms 15ms 4.2s 13.5s 8m 13.6s
vroom dplyr FALSE 63.1GB 3m 52.3s 2.2s 2ms 1ms 14ms 10.5s 7.2s 4m 12.2s
vroom base TRUE 88.3GB 20.3s 3s 1ms 1ms 1ms 21.5s 2m 22.6s 3m 7.5s
vroom dplyr TRUE 88GB 20.4s 2.8s 1ms 1ms 13ms 23.9s 1m 5.6s 1m 52.7s
data.table data.table 59.6GB 1m 35.3s 7ms 1ms 1ms 1ms 1.1s 4.7s 1m 41.1s

Reading fixed width files

United States Census 5-Percent Public Use Microdata Sample files

This fixed width dataset contains individual records of the characteristics of a 5 percent sample of people and housing units from the year 2000 and is freely available at https://web.archive.org/web/20150908055439/https://www2.census.gov/census_2000/datasets/PUMS/FivePercent/California/all_California.zip. The data is split into files by state, and the state of California was used in this benchmark.

The data totals 2,342,339 rows and 37 columns with a total file size of 677M.

Census data benchmarks

code: bench/fwf

reading package manipulating package altrep memory read print head tail sample filter aggregate total
read.delim base 6.17GB 18m 9.6s 16ms 1ms 2ms 3ms 492ms 90ms 18m 10.2s
readr dplyr 6.19GB 32.6s 48ms 2ms 1ms 17ms 95ms 94ms 32.8s
vroom dplyr FALSE 5.96GB 14.7s 44ms 1ms 1ms 15ms 468ms 91ms 15.3s
vroom base TRUE 4.65GB 164ms 56ms 1ms 1ms 7ms 285ms 1.8s 2.3s
vroom dplyr TRUE 4.62GB 163ms 48ms 2ms 1ms 16ms 306ms 1.3s 1.8s

Writing delimited files

code: bench/taxi_writing

The benchmarks write out the taxi trip dataset in a few different ways.

  • An uncompressed file
  • A gzip compressed file using gzfile() (readr and vroom do this automatically for files ending in .gz)
  • A gzip compressed file compressed with multiple threads (natively for data.table and using a pipe() connection to pigz for the rest).
  • A Zstandard compressed file (data.table does not support this format).
compression base data.table readr vroom
gzip 3m 17.1s 1m 7.8s 2m 0.2s 1m 14.4s
multithreaded_gzip 1m 37.8s 8.9s 53.4s 8.1s
zstandard 1m 39.9s NA 54.2s 12.4s
uncompressed 1m 37.4s 1.5s 52.2s 1.7s

Session and package information

package version date source
base 4.1.0 2021-05-18 local
data.table 1.14.0 2021-02-21 RSPM (R 4.1.0)
dplyr 1.0.7 2021-06-18 RSPM (R 4.1.0)
readr 1.4.0 2020-10-05 RSPM (R 4.1.0)
vroom 1.5.1 2021-06-22 local
vroom/inst/doc/benchmarks.R0000644000176200001440000001503014533652001015374 0ustar liggesusers## ----include = FALSE---------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) library(ggplot2) library(forcats) library(dplyr) library(tidyr) library(fs) pretty_sec <- function(x) { x[!is.na(x)] <- prettyunits::pretty_sec(x[!is.na(x)]) x } pretty_lgl <- function(x) { case_when( x == TRUE ~ "TRUE", x == FALSE ~ "FALSE", TRUE ~ "" ) } read_benchmark <- function(file, desc) { vroom::vroom(file, col_types = c("ccccddddd")) %>% filter(op != "setup") %>% mutate( altrep = case_when( grepl("^vroom_no_altrep", reading_package) ~ FALSE, grepl("^vroom", reading_package) ~ TRUE, TRUE ~ NA ), reading_package = case_when( grepl("^vroom", reading_package) ~ "vroom", TRUE ~ reading_package ), label = fct_reorder( glue::glue("{reading_package}{altrep}\n{manip_package}", altrep = ifelse(is.na(altrep), "", glue::glue("(altrep = {altrep})")) ), case_when(type == "real" ~ time, TRUE ~ 0), sum), op = factor(op, desc) ) } generate_subtitle <- function(data) { rows <- scales::comma(data$rows[[1]]) cols <- scales::comma(data$cols[[1]]) size <- fs_bytes(data$size[[1]]) glue::glue("{rows} x {cols} - {size}B") } plot_benchmark <- function(data, title) { subtitle <- generate_subtitle(data) data <- data %>% filter(reading_package != "read.delim", type == "real") p1 <- data %>% ggplot() + geom_bar(aes(x = label, y = time, fill = op, group = label), stat = "identity") + scale_fill_brewer(type = "qual", palette = "Set2") + scale_y_continuous(labels = scales::number_format(suffix = "s")) + coord_flip() + labs(title = title, subtitle = subtitle, x = NULL, y = NULL, fill = NULL) + theme(legend.position = "bottom") p2 <- data %>% group_by(label) %>% summarise(max_memory = max(max_memory)) %>% ggplot() + geom_bar(aes(x = label, y = max_memory / (1024 * 1024)), stat = "identity") + scale_y_continuous(labels = scales::number_format(suffix = "Mb")) + coord_flip() + labs(title = "Maximum memory usage", x = NULL, y = NULL) + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) library(patchwork) p1 + p2 + plot_layout(widths = c(2, 1)) } make_table <- function(data) { data %>% filter(type == "real") %>% select(-label, -size, -type, -rows, -cols) %>% spread(op, time) %>% mutate( total = read + print + head + tail + sample + filter + aggregate, max_memory = as.character(bench::as_bench_bytes(max_memory)) ) %>% arrange(desc(total)) %>% mutate_if(is.numeric, pretty_sec) %>% mutate_if(is.logical, pretty_lgl) %>% select(reading_package, manip_package, altrep, max_memory, everything()) %>% rename( "reading\npackage" = reading_package, "manipulating\npackage" = manip_package, memory = max_memory ) %>% knitr::kable(digits = 2, align = "r", format = "html") } desc <- c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate") ## ----fig.height = 8, fig.width=10, warning = FALSE, echo = FALSE, message = FALSE---- taxi <- read_benchmark(path_package("vroom", "bench", "taxi.tsv"), desc) plot_benchmark(taxi, "Time to analyze taxi trip data") make_table(taxi) ## ----fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE---- all_num <- read_benchmark(path_package("vroom", "bench", "all_numeric-long.tsv"), desc) plot_benchmark(all_num, "Time to analyze long all numeric data") make_table(all_num) ## ----fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE---- all_num_wide <- read_benchmark(path_package("bench", "all_numeric-wide.tsv", package = "vroom"), desc) plot_benchmark(all_num_wide, "Time to analyze wide all numeric data") make_table(all_num_wide) ## ----fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE---- all_chr <- read_benchmark(path_package("vroom", "bench", "all_character-long.tsv"), desc) plot_benchmark(all_chr, "Time to analyze long all character data") make_table(all_chr) ## ----fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE---- all_chr_wide <- read_benchmark(path_package("vroom", "bench", "all_character-wide.tsv"), desc) plot_benchmark(all_chr_wide, "Time to analyze wide all character data") make_table(all_chr_wide) ## ----echo = FALSE, message = FALSE, eval = TRUE------------------------------- mult <- read_benchmark(path_package("vroom", "bench", "taxi_multiple.tsv"), desc) ## ----fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE---- plot_benchmark(mult, "Time to analyze multiple file data") make_table(mult) ## ----echo = FALSE, message = FALSE, eval = TRUE------------------------------- fwf <- read_benchmark(path_package("vroom", "bench", "fwf.tsv"), desc) ## ----fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE---- plot_benchmark(fwf, "Time to analyze fixed width data") make_table(fwf) ## ----fig.height = 8, fig.width=10, warning = FALSE, message = FALSE, echo = FALSE---- taxi_writing <- read_benchmark(path_package("vroom", "bench", "taxi_writing.tsv"), c("setup", "writing")) %>% rename( package = reading_package, compression = manip_package ) %>% mutate( package = factor(package, c("base", "readr", "data.table", "vroom")), compression = factor(compression, rev(c("gzip", "multithreaded_gzip", "zstandard", "uncompressed"))) ) %>% filter(type == "real") subtitle <- generate_subtitle(taxi_writing) taxi_writing %>% ggplot(aes(x = compression, y = time, fill = package)) + geom_bar(stat = "identity", position = position_dodge2(reverse = TRUE, padding = .05)) + scale_fill_brewer(type = "qual", palette = "Set2") + scale_y_continuous(labels = scales::number_format(suffix = "s")) + theme(legend.position = "bottom") + coord_flip() + labs(title = "Writing taxi trip data", subtitle = subtitle, x = NULL, y = NULL, fill = NULL) taxi_writing %>% select(-size, -op, -rows, -cols, -type, -altrep, -label, -max_memory) %>% mutate_if(is.numeric, pretty_sec) %>% pivot_wider(names_from = package, values_from = time) %>% arrange(desc(compression)) %>% knitr::kable(digits = 2, align = "r", format = "html") ## ----echo = FALSE, warning = FALSE, message = FALSE--------------------------- si <- vroom::vroom(path_package("vroom", "bench", "session_info.tsv")) class(si) <- c("packages_info", "data.frame") select(as.data.frame(si), package, version = ondiskversion, date, source) %>% knitr::kable() vroom/inst/doc/vroom.R0000644000176200001440000001273614533652002014434 0ustar liggesusers## ----setup, include = FALSE--------------------------------------------------- knitr::opts_knit$set(root.dir = tempdir()) knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) options(tibble.print_min = 3) ## ----------------------------------------------------------------------------- library(vroom) ## ----------------------------------------------------------------------------- # See where the example file is stored on your machine file <- vroom_example("mtcars.csv") file # Read the file, by default vroom will guess the delimiter automatically. vroom(file) # You can also specify it explicitly, which is (slightly) faster, and safer if # you know how the file is delimited. vroom(file, delim = ",") ## ----------------------------------------------------------------------------- ve <- grep("mtcars-[0-9].csv", vroom_examples(), value = TRUE) files <- sapply(ve, vroom_example) files ## ----------------------------------------------------------------------------- vroom(files) ## ----------------------------------------------------------------------------- vroom(files, id = "path") ## ----------------------------------------------------------------------------- file <- vroom_example("mtcars.csv.gz") vroom(file) ## ----------------------------------------------------------------------------- zip_file <- vroom_example("mtcars-multi-cyl.zip") filenames <- unzip(zip_file, list = TRUE)$Name filenames # imagine we only want to read 2 of the 3 files vroom(purrr::map(filenames[c(1, 3)], ~ unz(zip_file, .x))) ## ----eval = as.logical(Sys.getenv("NOT_CRAN", "false"))----------------------- # file <- "https://raw.githubusercontent.com/tidyverse/vroom/main/inst/extdata/mtcars.csv" # vroom(file) ## ----eval = as.logical(Sys.getenv("NOT_CRAN", "false"))----------------------- # file <- "https://raw.githubusercontent.com/tidyverse/vroom/main/inst/extdata/mtcars.csv.gz" # vroom(file) ## ----------------------------------------------------------------------------- file <- vroom_example("mtcars.csv.gz") vroom(file, col_select = c(model, cyl, gear)) ## ----------------------------------------------------------------------------- vroom(file, col_select = c(1, 3, 11)) ## ----------------------------------------------------------------------------- vroom(file, col_select = starts_with("d")) ## ----------------------------------------------------------------------------- vroom(file, col_select = c(car = model, everything())) ## ----------------------------------------------------------------------------- fwf_sample <- vroom_example("fwf-sample.txt") cat(readLines(fwf_sample)) ## ----------------------------------------------------------------------------- vroom_fwf(fwf_sample, fwf_empty(fwf_sample, col_names = c("first", "last", "state", "ssn"))) ## ----------------------------------------------------------------------------- vroom_fwf(fwf_sample, fwf_widths(c(20, 10, 12), c("name", "state", "ssn"))) ## ----------------------------------------------------------------------------- vroom_fwf(fwf_sample, fwf_positions(c(1, 30), c(20, 42), c("name", "ssn"))) ## ----------------------------------------------------------------------------- vroom_fwf(fwf_sample, fwf_cols(name = 20, state = 10, ssn = 12)) ## ----------------------------------------------------------------------------- vroom_fwf(fwf_sample, fwf_cols(name = c(1, 20), ssn = c(30, 42))) ## ----------------------------------------------------------------------------- # read the 'hp' columns as an integer vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i")) # also skip reading the 'cyl' column vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i", cyl = "_")) # also read the gears as a factor vroom(vroom_example("mtcars.csv"), col_types = c(hp = "i", cyl = "_", gear = "f")) ## ----------------------------------------------------------------------------- vroom(vroom_example("mtcars.csv"), col_types = c(.default = "c")) ## ----------------------------------------------------------------------------- vroom( vroom_example("mtcars.csv"), col_types = list(hp = col_integer(), cyl = col_skip(), gear = col_factor()) ) ## ----------------------------------------------------------------------------- vroom( vroom_example("mtcars.csv"), col_types = list(gear = col_factor(levels = c(gear = c("3", "4", "5")))) ) ## ----eval = FALSE------------------------------------------------------------- # vroom( # vroom_example("mtcars.csv"), # .name_repair = ~ janitor::make_clean_names(., case = "all_caps") # ) ## ----------------------------------------------------------------------------- vroom_write(mtcars, "mtcars.tsv") ## ----include = FALSE---------------------------------------------------------- unlink("mtcars.tsv") ## ----------------------------------------------------------------------------- vroom_write(mtcars, "mtcars.csv", delim = ",") ## ----include = FALSE---------------------------------------------------------- unlink("mtcars.csv") ## ----------------------------------------------------------------------------- vroom_write(mtcars, "mtcars.tsv.gz") vroom_write(mtcars, "mtcars.tsv.bz2") vroom_write(mtcars, "mtcars.tsv.xz") ## ----include = FALSE---------------------------------------------------------- unlink(c("mtcars.tsv.gz", "mtcars.tsv.bz2", "mtcars.tsv.xz")) ## ----eval = nzchar(Sys.which("pigz"))----------------------------------------- # vroom_write(mtcars, pipe("pigz > mtcars.tsv.gz")) ## ----include = FALSE---------------------------------------------------------- unlink("mtcars.tsv.gz") vroom/inst/COPYRIGHTS0000644000176200001440000000527114132374245014021 0ustar liggesusersThis package is provided you under the terms of the GNU General Public License version 3. Included below is license and copyright information for externally maintained libraries used by vroom. All other code in vroom is copyright RStudio, Inc. ==== mio is licensed for use as follows MIT License Copyright (c) 2018 https://github.com/mandreyel/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==== grisu3 is licensed for use as follows /* Copyright Jukka JylΓ€nki Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Modifcations to dtoa_grisu3() referenced mikkelfj: are under the following * Copyright (c) 2016 Mikkel F. JΓΈrgensen, dvide.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. http://www.apache.org/licenses/LICENSE-2.0 */ ==== vroom/inst/extdata/0000755000176200001440000000000014505365462014035 5ustar liggesusersvroom/inst/extdata/mtcars.csv0000644000176200001440000000324413445253572016046 0ustar liggesusersmodel,mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb Mazda RX4,21,6,160,110,3.9,2.62,16.46,0,1,4,4 Mazda RX4 Wag,21,6,160,110,3.9,2.875,17.02,0,1,4,4 Datsun 710,22.8,4,108,93,3.85,2.32,18.61,1,1,4,1 Hornet 4 Drive,21.4,6,258,110,3.08,3.215,19.44,1,0,3,1 Hornet Sportabout,18.7,8,360,175,3.15,3.44,17.02,0,0,3,2 Valiant,18.1,6,225,105,2.76,3.46,20.22,1,0,3,1 Duster 360,14.3,8,360,245,3.21,3.57,15.84,0,0,3,4 Merc 240D,24.4,4,146.7,62,3.69,3.19,20,1,0,4,2 Merc 230,22.8,4,140.8,95,3.92,3.15,22.9,1,0,4,2 Merc 280,19.2,6,167.6,123,3.92,3.44,18.3,1,0,4,4 Merc 280C,17.8,6,167.6,123,3.92,3.44,18.9,1,0,4,4 Merc 450SE,16.4,8,275.8,180,3.07,4.07,17.4,0,0,3,3 Merc 450SL,17.3,8,275.8,180,3.07,3.73,17.6,0,0,3,3 Merc 450SLC,15.2,8,275.8,180,3.07,3.78,18,0,0,3,3 Cadillac Fleetwood,10.4,8,472,205,2.93,5.25,17.98,0,0,3,4 Lincoln Continental,10.4,8,460,215,3,5.424,17.82,0,0,3,4 Chrysler Imperial,14.7,8,440,230,3.23,5.345,17.42,0,0,3,4 Fiat 128,32.4,4,78.7,66,4.08,2.2,19.47,1,1,4,1 Honda Civic,30.4,4,75.7,52,4.93,1.615,18.52,1,1,4,2 Toyota Corolla,33.9,4,71.1,65,4.22,1.835,19.9,1,1,4,1 Toyota Corona,21.5,4,120.1,97,3.7,2.465,20.01,1,0,3,1 Dodge Challenger,15.5,8,318,150,2.76,3.52,16.87,0,0,3,2 AMC Javelin,15.2,8,304,150,3.15,3.435,17.3,0,0,3,2 Camaro Z28,13.3,8,350,245,3.73,3.84,15.41,0,0,3,4 Pontiac Firebird,19.2,8,400,175,3.08,3.845,17.05,0,0,3,2 Fiat X1-9,27.3,4,79,66,4.08,1.935,18.9,1,1,4,1 Porsche 914-2,26,4,120.3,91,4.43,2.14,16.7,0,1,5,2 Lotus Europa,30.4,4,95.1,113,3.77,1.513,16.9,1,1,5,2 Ford Pantera L,15.8,8,351,264,4.22,3.17,14.5,0,1,5,4 Ferrari Dino,19.7,6,145,175,3.62,2.77,15.5,0,1,5,6 Maserati Bora,15,8,301,335,3.54,3.57,14.6,0,1,5,8 Volvo 142E,21.4,4,121,109,4.11,2.78,18.6,1,1,4,2 vroom/inst/extdata/mtcars-4.csv0000644000176200001440000000113214132374245016174 0ustar liggesusersmodel,mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb Datsun 710,22.8,4,108,93,3.85,2.32,18.61,1,1,4,1 Merc 240D,24.4,4,146.7,62,3.69,3.19,20,1,0,4,2 Merc 230,22.8,4,140.8,95,3.92,3.15,22.9,1,0,4,2 Fiat 128,32.4,4,78.7,66,4.08,2.2,19.47,1,1,4,1 Honda Civic,30.4,4,75.7,52,4.93,1.615,18.52,1,1,4,2 Toyota Corolla,33.9,4,71.1,65,4.22,1.835,19.9,1,1,4,1 Toyota Corona,21.5,4,120.1,97,3.7,2.465,20.01,1,0,3,1 Fiat X1-9,27.3,4,79,66,4.08,1.935,18.9,1,1,4,1 Porsche 914-2,26,4,120.3,91,4.43,2.14,16.7,0,1,5,2 Lotus Europa,30.4,4,95.1,113,3.77,1.513,16.9,1,1,5,2 Volvo 142E,21.4,4,121,109,4.11,2.78,18.6,1,1,4,2 vroom/inst/extdata/mtcars-multi-cyl.zip0000644000176200001440000000247414505365462017776 0ustar liggesusersPKΧvOS©{οaZ mtcars-4.csvM‘Αnƒ0Dο|°±Όkγs“ͺ‡Vκ‘ͺzu J"‘8’*ί1€΄B΄»Ο3³>¦mΫΡρΌ£ζΦΡφ0œi¦mGϊιkhΊ΄kcOMμ?‹u‡Λ©τ¬IDΥd‰uMΑQ΅#QFˆkU1εΝβ₯ν›R¬^“XesΙVΚS% ͺ€ažάΗΝίιVγCFΨεNXζq,Yj22ξλ|xEVΑ–(Έ ΚϊΕΝS:mcωpΈ2z&' ‚aέε(̌oι–F@©O]ΙΐIΖX1U˜`TΥΖe©°(ύ£N‘„•ΛeΡ ‚Gw<*š§8PŸγ|π ;ρΚd™°€aœΌύ‰Ό¦~hφmΨ„€ΊK θ+k Α(ε…ηC­xNγe(7—>γο ‚ƒ+ζ|‰»RΏ€f ½§ξšJΆ²ΙI¦[τ4‘˜‘βλιΪ—ύPKΧvOSGPXcλ mtcars-6.csvu½NΔ0„ϋ{ ?ΐΘ²7ώKΝιDs ΠšΔ ‘ς‡‚§gBΝ£ωvgvœΫ4`\:4_Ϊ~]πΎ ΝqΓ熏55Έ­ˆ#Ί3š˜ίNΧψέFρτj@Ϊ)h­PΙ$± ‹00Ώnρ»Ώˆΰ-΄—ŠδqΞSΪ„ηάί30E6ά)xf¬–Ζ0Δ"τι9}œ6θ Λ"6(ΛΌc?G"%‰ϋ5εFPPe ν©ΌδIUΙE…0eU΅{“;πP↉ϊ .)η˜{qξ§Ήœρ2₯e7ΏŠ³yh+νήݝ~PKΧvOSόλ¬  mtcars-8.csvm’Αnά †οϋ<ΐŒχΨ:j#EΚ­7ΦF»–°q°“(oί²v,!aψψgώωΗΤ‡γ|ξ#B?,3\gθ³_α}…—%tπΆ€α|†Ξησα!ε)¬βyNyυητΊ‚j€ƒL]rŒTΌ NV*ϊ θΓέ벆,Κ5”ζFhδΛZΡb(+Όxx Ήh«η_ j‰DhGHΞeεy!• 1_Θ‰›#α£ϊ€e}ύ-Γ»h}?Δθ;qCXίSκAU₯>tteAΛ£zΛ²Τ±Ω;: S—β$Ϊ4­Γ¦ΥΗe3Ψ9βPσ½ƒν5,‘μϋ=Ξ!Œaq‘0Γ…j& Iό"οR ’½ϊΓt ™{΄l>5€,ΑΥlΎf·λΗc+ώψ·‡i³ΕTXmΒΖ~šΌ!­}Nβ―¦—Νη„ν6arέπlι)T{qOμ9δp2ωx,JXmYͺšΒ%2v“ΊOΉO~’Dyq*Ή)r t” ­ΉJΗ.1€ΐržόBΧΧAόLΩSZ’δ²x‹ –l0ΡώPKΧvOS©{οaZ €mtcars-4.csvPKΧvOSGPXcλ €‹mtcars-6.csvPKΧvOSόλ¬  € mtcars-8.csvPKxvroom/inst/extdata/mtcars.csv.zip0000644000176200001440000000176313445253572016653 0ustar liggesusersPKΒ…RN΄lšI€ mtcars.csvUT œ'k\W@t\ux υu”έnΫ8…ού|–D鲕t)΄E·θcŽYτR²‹μΣοκΗΩmz£Δ?ΝΜ™3sŠmθωι|δ‡—ž·έxζΟgή&?ρŸ{ ~Ή?ρcπ‰|zΪ}ς΄ž}ώnΈ’Όδ²,Έ”Χ’ζJ” Β”‡άps»ΝώςΗ·ˆΚY.(Τ†μύ4^ζpEα=ΞdQρZ¨,•(%—»1 ab†νSw " Β([-a@k‘$βΤΒ@$}ΓΎœcšόSΌLτYΗq™DZZHz²$H Ϊ}σ}η‡|κQ _.(3WuœB©-Ξώ2N!±όU#τ@›³ΒΓ:.­¨Μ’…t`Κ{άB-ˆoJdq΅(kΚ Κ9Ί°\Χ7ΉLΏ5}ΏVsxSο~U*7Δ <•^ͺΈB¦3`6 !!ͺίυ c‹/wΩ¨Y9”ˆKΉ!ŽzΰkkΡϊ†<ΠΉώΡΒizUΎ4€ z“‘_ΡψΆλ{`χ}ΣΟ[΄.ηgœ‚¦ΤE8 ίΚ¬«­'έpˆύΐš8Lέ†ΙχJν$«€3*»₯RΨ<§—±‡ώ8Cκ3ΩfΖΣ”¨"R›άΘϋΞOL*ψEe8²gY’v(ε’Ÿέ«10jMwν\3aAXEǏ₯6Y΅0jχ5ΎΔ PL²pM3 L’±-0²±¨tz‹τŠ<Ν›₯cΈ^ς:kŽμ xœς6±=Φ<ϋΎΓ1$j™₯i@€-Φω±y…Tn·χŸφ§Ώ†ΎΦ.λΒddPmgΟ¬HγO>EφΪI=œ]GΞεEBΌ0rΣϊ‘ΪJΎθRxκR;ΟšT¬» o‘jn|²†ΚMϊ.ίa")ˆWo=’έƒ1KχΣxx¬–ζ W.Βi^γ=  .οChΈ{ˆΣedw—Ο~mlm‘΅”TŠƒ„ΕΏ€ζ8έΗΤ²Gl©<{Θϋ%« Σ̍…xŽΌh—H0\HΙ§Žν»!’ŽF=WL`ύ¨ΝnH‰ύ>"ΒΤ±1yΌΚΝΑRΣDX³l7#Κ…¨vίbLu7ojY0ž’BTyΏoύPKΒ…RN΄lšI€ €mtcars.csvUTœ'k\ux υPKPvroom/inst/extdata/fwf-sample.txt0000644000176200001440000000020114132374245016623 0ustar liggesusersJohn Smith WA 418-Y11-4111 Mary Hartford CA 319-Z19-4341 Evan Nolan IL 219-532-c301 vroom/inst/extdata/mtcars-8.csv0000644000176200001440000000144014132374245016202 0ustar liggesusersmodel,mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb Hornet Sportabout,18.7,8,360,175,3.15,3.44,17.02,0,0,3,2 Duster 360,14.3,8,360,245,3.21,3.57,15.84,0,0,3,4 Merc 450SE,16.4,8,275.8,180,3.07,4.07,17.4,0,0,3,3 Merc 450SL,17.3,8,275.8,180,3.07,3.73,17.6,0,0,3,3 Merc 450SLC,15.2,8,275.8,180,3.07,3.78,18,0,0,3,3 Cadillac Fleetwood,10.4,8,472,205,2.93,5.25,17.98,0,0,3,4 Lincoln Continental,10.4,8,460,215,3,5.424,17.82,0,0,3,4 Chrysler Imperial,14.7,8,440,230,3.23,5.345,17.42,0,0,3,4 Dodge Challenger,15.5,8,318,150,2.76,3.52,16.87,0,0,3,2 AMC Javelin,15.2,8,304,150,3.15,3.435,17.3,0,0,3,2 Camaro Z28,13.3,8,350,245,3.73,3.84,15.41,0,0,3,4 Pontiac Firebird,19.2,8,400,175,3.08,3.845,17.05,0,0,3,2 Ford Pantera L,15.8,8,351,264,4.22,3.17,14.5,0,1,5,4 Maserati Bora,15,8,301,335,3.54,3.57,14.6,0,1,5,8 vroom/inst/extdata/mtcars-6.csv0000644000176200001440000000062014132374245016177 0ustar liggesusersmodel,mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb Mazda RX4,21,6,160,110,3.9,2.62,16.46,0,1,4,4 Mazda RX4 Wag,21,6,160,110,3.9,2.875,17.02,0,1,4,4 Hornet 4 Drive,21.4,6,258,110,3.08,3.215,19.44,1,0,3,1 Valiant,18.1,6,225,105,2.76,3.46,20.22,1,0,3,1 Merc 280,19.2,6,167.6,123,3.92,3.44,18.3,1,0,4,4 Merc 280C,17.8,6,167.6,123,3.92,3.44,18.9,1,0,4,4 Ferrari Dino,19.7,6,145,175,3.62,2.77,15.5,0,1,5,6 vroom/inst/extdata/mtcars.csv.bz20000644000176200001440000000151014132374145016526 0ustar liggesusersBZh91AY&SY=sBί€@ΰ?v]Π>η°Pš‡’SΤ5§ιO&“Θ#F™4 ΝC24Θ&‘SΪτ©=@ hhhΘh‘ M@@υ@i±g# „%ͺχ:$H2δμ=::oοΉξr?ar1G]zκ­¬@‰$V€5v°@Le’n¬' @&«>kg!;j"42vίΤυΗή΄^τxjοk΅:Λμœ‘ίHˆ†ž/\–XŒ2'§%›εNўώϊ΄k΅μ!g]w6ΝLΝdI)S@ΐ[­FΑe6!Ώ€m€ ΐUsrφX3©1σ…n³΅½9.γUcθΎ%Bπ9ωxσιΠΏΞxξgάΔΏj©ίL βd(»!βqSs2_ŠF±’ΤmβlœΠ.ŒH³‹z"$Σk+/-t,fh\^oλ,Z\Ύ9Œγ‰¬IijuΒI0σ–75A—‹†—B[ήςu`tΡ&—³ΉT΅ΆƒΟŠšά"ΩΞν²R^D0ΌxtχάχφχŒ¬ΠtΆΕ‘ΆΌ6έBi„β{pΛ¨Kω՚₯m>oΊ jfΆR532 ΪΓT4SO5θ՜0OΛ.ƒ£Λšε–βhτ8ž1<ˆO"&”±OU-¨P_Ž6εδ·‡9³fA“>7ΞA“PHΫ4 cΕVΧ1Β Ρ[1[ !ΰfͺ•Λ­ΧpϊΎ‘@¬8aΠΧ=σ£«WŠ… xγWF+ΔΖ­„Œ.Ρm[NRQεάλV,ξΒΣϋ§υσέΨDCoψξe1ƒH q ΰ„=Δ`ψΓPΨ`½1ΕόOŽJΉ}ΫΛ— >wΐ ε’„nfη‚G‡„™„m*!½h±pM‡Θ”+.sΤψ±n” t†βξH§ `θ@vroom/inst/extdata/mtcars.csv.gz0000644000176200001440000000154613445253572016470 0ustar liggesusers‹ ‘i\mtcars.csvu”έnΫ8…ού|–D鲕t)΄E·θcŽYτR²‹μΣοκΗΩmz£Δ?ΝΜ™3sŠmθωι|δ‡—ž·έxζΟgή&?ρŸ{ ~Ή?ρcπ‰|zΪ}ς΄ž}ώnΈ’Όδ²,Έ”Χ’ζJ” Β”‡άps»ΝώςΗ·ˆΚY.(Τ†μύ4^ζpEα=ΞdQρZ¨,•(%—»1 ab†νSw " Β([-a@k‘$βΤΒ@$}ΓΎœcšόSΌLτYΗq™DZZHz²$H Ϊ}σ}η‡|κQ _.(3WuœB©-Ξώ2N!±όU#τ@›³ΒΓ:.­¨Μ’…t`Κ{άB-ˆoJdq΅(kΚ Κ9Ί°\Χ7ΉLΏ5}ΏVsxSο~U*7Δ <•^ͺΈB¦3`6 !!ͺίυ c‹/wΩ¨Y9”ˆKΉ!ŽzΰkkΡϊ†<ΠΉώΡΒizUΎ4€ z“‘_ΡψΆλ{`χ}ΣΟ[΄.ηgœ‚¦ΤE8 ίΚ¬«­'έpˆύΐš8Lέ†ΙχJν$«€3*»₯RΨ<§—±‡ώ8Cκ3ΩfΖΣ”¨"R›άΘϋΞOL*ψEe8²gY’v(ε’Ÿέ«10jMwν\3aAXEǏ₯6Y΅0jχ5ΎΔ PL²pM3 L’±-0²±¨tz‹τŠ<Ν›₯cΈ^ς:kŽμ xœς6±=Φ<ϋΎΓ1$j™₯i@€-Φω±y…Tn·χŸφ§Ώ†ΎΦ.λΒddPmgΟ¬HγO>EφΪI=œ]GΞεEBΌ0rΣϊ‘ΪJΎθRxκR;ΟšT¬» o‘jn|²†ΚMϊ.ίa")ˆWo=’έƒ1KχΣxx¬–ζ W.Βi^γ=  .οChΈ{ˆΣedw—Ο~mlm‘΅”TŠƒ„ΕΏ€ζ8έΗΤ²Gl©<{Θϋ%« Σ̍…xŽΌh—H0\HΙ§Žν»!’ŽF=WL`ύ¨ΝnH‰ύ>"ΒΤ±1yΌΚΝΑRΣDX³l7#Κ…¨vίbLu7ojY0ž’BTyΏoύ΄lš€vroom/inst/extdata/mtcars.csv.xz0000644000176200001440000000156413445253572016511 0ustar liggesusersύ7zXZζΦ΄F!t/ε£ΰ£1]6›Θ°WΡTξŠ‘hpD'©φΆq εͺρ(3=ƒy»κd‘OI­Ϋn„¬I£Œ&¬l-δ;ύ5εaύoά"6GY Ζ€g£(<1kλΟqΩ€)€ΎIΒλΥ¦δV J8$(`·ƒ ξ‹Nα]rΥ5•NŒΒa9^W ‚Ό>ψ―fCΡ0ωγΙΩY₯½dΦβY°λžϋΘ^VΑ6ωvιU5Όμ°ƒSeOΩ\4²Ε$=1εeθ^CμΉ0΅ZΥ‹"BJBεŒW Cx8ήάιτΧn@y¨ο3bΒ‘tΤ1 ςu+EΝ ,Γ>β’ΕήρŸ1C^X!„Π#N{2'ηA.ΏΙαBΏςD Ο–Ÿ^ %NώράGͺ…sc[5’)>ƒρ -<†€t^’δ–pλ.uώηΑz)—²qΧsή$NsημY§Wψκ2ύ}‘@¬OpZπ‘*ΆsώWfxž'C6ΓCJO€š^‰8Sx±oΖπX£‘o+b%yI[λF"φˆ9ειœ‰»— ŽξΖ”π9P:gŽ,―T{AάXχ΅Qϊ‡('ΆΚe ~φ—χGΣ4ΞN=€ΟϊΏ‰AžZdWςFδΫΘΖHψΣλψ0ύ›ππΙβ\/ˆΰKΥΚ.-4΅€#ɜ]ΣΏϊΌI)v ν©8WNHX ½ 8£κužΦHUΥ.;#$–χ¨»?ωΒρ%UζΕMwώxΏYΖVaBΛBƒŒ@ηΥod‡gέ “ψΆΝ€ 1²+±ΔgϋYZvroom/inst/bench/0000755000176200001440000000000014533652003013451 5ustar liggesusersvroom/inst/bench/run-bench.R0000755000176200001440000000103214132374245015460 0ustar liggesusers#!/usr/bin/env Rscript args <- commandArgs(trailingOnly = TRUE) source_file <- args[[1]] out_file <- args[[2]] file <- args[-c(1:2)] cat(source_file, "\n") out <- bench::workout_expressions(as.list(parse(source_file, keep.source = FALSE))) x <- vroom::vroom(file, col_types = list()) out$size <- sum(file.size(file)) out$rows <- nrow(x) out$cols <- ncol(x) out$process <- as.numeric(out$process) out$real <- as.numeric(out$real) out$max_memory <- as.numeric(bench::bench_process_memory()[["max"]]) vroom::vroom_write(out, out_file) vroom/inst/bench/all_character-long.tsv0000644000176200001440000001723514132374245017744 0ustar liggesusersreading_package manip_package op type time size max_memory rows cols data.table data.table setup process 0.05641532699999985 400248755 5080014848 1000000 25 data.table data.table setup real 0.05641937255859375 400248755 5080014848 1000000 25 data.table data.table read process 45.553360995 400248755 5080014848 1000000 25 data.table data.table read real 42.82739567756653 400248755 5080014848 1000000 25 data.table data.table print process 0.01535388299999596 400248755 5080014848 1000000 25 data.table data.table print real 0.015356302261352539 400248755 5080014848 1000000 25 data.table data.table head process 2.4213100000025634e-4 400248755 5080014848 1000000 25 data.table data.table head real 2.4390220642089844e-4 400248755 5080014848 1000000 25 data.table data.table tail process 2.0244400000279938e-4 400248755 5080014848 1000000 25 data.table data.table tail real 0.00020360946655273438 400248755 5080014848 1000000 25 data.table data.table sample process 0.003910263000001635 400248755 5080014848 1000000 25 data.table data.table sample real 0.003912210464477539 400248755 5080014848 1000000 25 data.table data.table filter process 0.06275645499999882 400248755 5080014848 1000000 25 data.table data.table filter real 0.01575303077697754 400248755 5080014848 1000000 25 data.table data.table aggregate process 0.19549771300000174 400248755 5080014848 1000000 25 data.table data.table aggregate real 0.14883995056152344 400248755 5080014848 1000000 25 read.delim base setup process 4.0029999999191546e-6 400248755 4863918080 1000000 25 read.delim base setup real 6.198883056640625e-6 400248755 4863918080 1000000 25 read.delim base read process 103.065713264 400248755 4863918080 1000000 25 read.delim base read real 103.06781911849976 400248755 4863918080 1000000 25 read.delim base print process 0.007192154999998479 400248755 4863918080 1000000 25 read.delim base print real 0.007193565368652344 400248755 4863918080 1000000 25 read.delim base head process 2.5602699999183187e-4 400248755 4863918080 1000000 25 read.delim base head real 2.5725364685058594e-4 400248755 4863918080 1000000 25 read.delim base tail process 4.7244900000009693e-4 400248755 4863918080 1000000 25 read.delim base tail real 4.737377166748047e-4 400248755 4863918080 1000000 25 read.delim base sample process 0.0015474770000025728 400248755 4863918080 1000000 25 read.delim base sample real 0.0015492439270019531 400248755 4863918080 1000000 25 read.delim base filter process 0.027589560000009783 400248755 4863918080 1000000 25 read.delim base filter real 0.027591228485107422 400248755 4863918080 1000000 25 read.delim base aggregate process 0.29284924599998874 400248755 4863918080 1000000 25 read.delim base aggregate real 0.29285216331481934 400248755 4863918080 1000000 25 readr dplyr setup process 0.27801725900000007 400248755 4671979520 1000000 25 readr dplyr setup real 0.27802062034606934 400248755 4671979520 1000000 25 readr dplyr read process 62.577469189 400248755 4671979520 1000000 25 readr dplyr read real 62.581751346588135 400248755 4671979520 1000000 25 readr dplyr print process 0.07374980299999834 400248755 4671979520 1000000 25 readr dplyr print real 0.10151100158691406 400248755 4671979520 1000000 25 readr dplyr head process 0.0011762729999986732 400248755 4671979520 1000000 25 readr dplyr head real 0.0011777877807617188 400248755 4671979520 1000000 25 readr dplyr tail process 5.460859999999457e-4 400248755 4671979520 1000000 25 readr dplyr tail real 5.471706390380859e-4 400248755 4671979520 1000000 25 readr dplyr sample process 0.016387897000001317 400248755 4671979520 1000000 25 readr dplyr sample real 0.01638960838317871 400248755 4671979520 1000000 25 readr dplyr filter process 0.019892996999999468 400248755 4671979520 1000000 25 readr dplyr filter real 0.019894838333129883 400248755 4671979520 1000000 25 readr dplyr aggregate process 0.21462222699999245 400248755 4671979520 1000000 25 readr dplyr aggregate real 0.21462440490722656 400248755 4671979520 1000000 25 vroom base setup process 0.23405636800000007 400248755 3461603328 1000000 25 vroom base setup real 0.23406624794006348 400248755 3461603328 1000000 25 vroom base read process 3.9978824160000004 400248755 3461603328 1000000 25 vroom base read real 0.5949602127075195 400248755 3461603328 1000000 25 vroom base print process 0.045972403000000384 400248755 3461603328 1000000 25 vroom base print real 0.0459742546081543 400248755 3461603328 1000000 25 vroom base head process 9.583050000001592e-4 400248755 3461603328 1000000 25 vroom base head real 9.601116180419922e-4 400248755 3461603328 1000000 25 vroom base tail process 5.036210000000096e-4 400248755 3461603328 1000000 25 vroom base tail real 5.049705505371094e-4 400248755 3461603328 1000000 25 vroom base sample process 0.002841930999999853 400248755 3461603328 1000000 25 vroom base sample real 0.0028433799743652344 400248755 3461603328 1000000 25 vroom base filter process 0.16209093400000008 400248755 3461603328 1000000 25 vroom base filter real 0.1620922088623047 400248755 3461603328 1000000 25 vroom base aggregate process 2.1265156109999994 400248755 3461603328 1000000 25 vroom base aggregate real 2.1265347003936768 400248755 3461603328 1000000 25 vroom dplyr setup process 0.3563563940000001 400248755 3446657024 1000000 25 vroom dplyr setup real 0.35636043548583984 400248755 3446657024 1000000 25 vroom dplyr read process 3.6933613389999995 400248755 3446657024 1000000 25 vroom dplyr read real 0.6395699977874756 400248755 3446657024 1000000 25 vroom dplyr print process 0.057654061000000034 400248755 3446657024 1000000 25 vroom dplyr print real 0.057656049728393555 400248755 3446657024 1000000 25 vroom dplyr head process 0.0010614000000002122 400248755 3446657024 1000000 25 vroom dplyr head real 0.0010628700256347656 400248755 3446657024 1000000 25 vroom dplyr tail process 5.792140000000501e-4 400248755 3446657024 1000000 25 vroom dplyr tail real 5.805492401123047e-4 400248755 3446657024 1000000 25 vroom dplyr sample process 0.015052698999999947 400248755 3446657024 1000000 25 vroom dplyr sample real 0.015054941177368164 400248755 3446657024 1000000 25 vroom dplyr filter process 0.1843323209999994 400248755 3446657024 1000000 25 vroom dplyr filter real 0.18434882164001465 400248755 3446657024 1000000 25 vroom dplyr aggregate process 1.2399041610000001 400248755 3446657024 1000000 25 vroom dplyr aggregate real 1.2399187088012695 400248755 3446657024 1000000 25 vroom_no_altrep dplyr setup process 0.33683956000000004 400248755 4617994240 1000000 25 vroom_no_altrep dplyr setup real 0.33684349060058594 400248755 4617994240 1000000 25 vroom_no_altrep dplyr read process 53.144246626 400248755 4617994240 1000000 25 vroom_no_altrep dplyr read real 50.454383850097656 400248755 4617994240 1000000 25 vroom_no_altrep dplyr print process 0.04912083899999686 400248755 4617994240 1000000 25 vroom_no_altrep dplyr print real 0.04912281036376953 400248755 4617994240 1000000 25 vroom_no_altrep dplyr head process 0.0010306440000036332 400248755 4617994240 1000000 25 vroom_no_altrep dplyr head real 0.001032114028930664 400248755 4617994240 1000000 25 vroom_no_altrep dplyr tail process 5.248319999964224e-4 400248755 4617994240 1000000 25 vroom_no_altrep dplyr tail real 5.261898040771484e-4 400248755 4617994240 1000000 25 vroom_no_altrep dplyr sample process 0.015900661999999954 400248755 4617994240 1000000 25 vroom_no_altrep dplyr sample real 0.01590275764465332 400248755 4617994240 1000000 25 vroom_no_altrep dplyr filter process 0.020508452000001398 400248755 4617994240 1000000 25 vroom_no_altrep dplyr filter real 0.02051067352294922 400248755 4617994240 1000000 25 vroom_no_altrep dplyr aggregate process 0.14906932099999892 400248755 4617994240 1000000 25 vroom_no_altrep dplyr aggregate real 0.14907217025756836 400248755 4617994240 1000000 25 vroom/inst/bench/GNUmakefile0000644000176200001440000000235714132374245015536 0ustar liggesusersMAKEFLAGS += --no-builtin-rules BENCH_LONG_ROWS := 10000 BENCH_LONG_COLS := 25 BENCH_WIDE_ROWS := 1000 BENCH_WIDE_COLS := 100 BENCH_INPUTS := all_numeric-long/input.tsv all_numeric-wide/input.tsv all_character-long/input.tsv all_character-wide/input.tsv TAXI_INPUTS := $(wildcard ~/data/small_trip_fare_*.csv) FWF_INPUT := ~/data/small_PUMS5_06.TXT BENCH_SRC := $(wildcard */*.R) BENCH_MARKS := $(BENCH_SRC:.R=.tsv) BENCH_OUT := $(patsubst %/,%.tsv, $(wildcard */)) all: $(BENCH_OUT) session_info.tsv $(BENCH_OUT) : $(BENCH_MARKS) Rscript summarise-benchmarks.R session_info.tsv: session_info.R ./$< %-long/input.tsv: %-long/input.R Rscript $< $(BENCH_LONG_ROWS) $(BENCH_LONG_COLS) $@ %-wide/input.tsv: %-wide/input.R Rscript $< $(BENCH_WIDE_ROWS) $(BENCH_WIDE_COLS) $@ taxi/%.tsv : taxi/%.R $(TAXI_INPUTS) run-bench.R $< $@ $(word 1, $(TAXI_INPUTS)) taxi_multiple/%.tsv : taxi_multiple/%.R $(TAXI_INPUTS) run-bench.R $< $@ $(TAXI_INPUTS) taxi_writing/%.tsv : taxi_writing/%.R $(TAXI_INPUTS) run-bench.R $< $@ $(word 1, $(TAXI_INPUTS)) fwf/%.tsv : fwf/%.R $(FWF_INPUT) run-bench-fwf.R $< $@ $(FWF_INPUT) %.tsv : %.R $(BENCH_INPUTS) run-bench.R $< $@ $(@D)/input.tsv clean: rm -f $(BENCH_INPUTS) $(BENCH_MARKS) $(BENCH_OUT) session_info.tsv vroom/inst/bench/taxi_writing/0000755000176200001440000000000014533733452016171 5ustar liggesusersvroom/inst/bench/taxi_writing/data.table-uncompressed.R0000644000176200001440000000030514132374245023012 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } data.table::fwrite(data, tempfile(fileext = ".tsv"), sep = "\t") vroom/inst/bench/taxi_writing/readr-multithreaded_gzip.R0000644000176200001440000000032214132374245023264 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } readr::write_tsv(data, pipe(sprintf("pigz > %s", tempfile(fileext = ".gz")))) vroom/inst/bench/taxi_writing/base-zstandard.R0000644000176200001440000000044214132374245021212 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } { con <- pipe(sprintf("zstd > %s", tempfile(fileext = ".zst")), "wb") write.table(data, con, sep = "\t", quote = FALSE, row.names = FALSE) close(con) } vroom/inst/bench/taxi_writing/data.table-multithreaded_gzip.R0000644000176200001440000000030414132374245024166 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } data.table::fwrite(data, tempfile(fileext = ".gz"), sep = "\t") vroom/inst/bench/taxi_writing/readr-zstandard.R0000644000176200001440000000032314132374245021373 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } readr::write_tsv(data, pipe(sprintf("zstd > %s", tempfile(fileext = ".zst")))) vroom/inst/bench/taxi_writing/readr-uncompressed.R0000644000176200001440000000026714132374245022117 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } readr::write_tsv(data, tempfile(fileext = ".tsv")) vroom/inst/bench/taxi_writing/vroom-gzip.R0000644000176200001440000000027714132374245020427 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } vroom_write(data, tempfile(fileext = ".gz"), delim = "\t") vroom/inst/bench/taxi_writing/vroom-multithreaded_gzip.R0000644000176200001440000000033314132374245023333 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } vroom_write(data, pipe(sprintf("pigz > %s", tempfile(fileext = ".gz"))), delim = "\t") vroom/inst/bench/taxi_writing/vroom-uncompressed.R0000644000176200001440000000030014132374245022150 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } vroom_write(data, tempfile(fileext = ".tsv"), delim = "\t") vroom/inst/bench/taxi_writing/vroom-zstandard.R0000644000176200001440000000033414132374245021442 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } vroom_write(data, pipe(sprintf("zstd > %s", tempfile(fileext = ".zst"))), delim = "\t") vroom/inst/bench/taxi_writing/base-gzip.R0000644000176200001440000000041514132374245020171 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } { con <- gzfile(tempfile(fileext = ".gz"), "wb") write.table(data, con, sep = "\t", quote = FALSE, row.names = FALSE) close(con) } vroom/inst/bench/taxi_writing/base-uncompressed.R0000644000176200001440000000034014132374245021724 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } write.table(data, tempfile(fileext = ".tsv"), sep = "\t", quote = FALSE, row.names = FALSE) vroom/inst/bench/taxi_writing/readr-gzip.R0000644000176200001440000000026614132374245020360 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } readr::write_tsv(data, tempfile(fileext = ".gz")) vroom/inst/bench/taxi_writing/base-multithreaded_gzip.R0000644000176200001440000000044114132374245023103 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } { con <- pipe(sprintf("pigz > %s", tempfile(fileext = ".gz")), "wb") write.table(data, con, sep = "\t", quote = FALSE, row.names = FALSE) close(con) } vroom/inst/bench/taxi_writing/data.table-gzip.R0000644000176200001440000000032114132374245021252 0ustar liggesusers{ library(vroom) data <- vroom(file, col_types = c(pickup_datetime = "c")) vroom:::vroom_materialize(data, replace = TRUE) } data.table::fwrite(data, tempfile(fileext = ".gz"), sep = "\t", nThread = 1) vroom/inst/bench/taxi_writing.tsv0000644000176200001440000001172114132374245016725 0ustar liggesusersreading_package manip_package op type time size max_memory rows cols base gzip setup process 56.307022157 1641999923 6515875840 14388451 11 base gzip setup real 16.904335260391235 1641999923 6515875840 14388451 11 base gzip writing process 197.11310746799998 1641999923 6515875840 14388451 11 base gzip writing real 197.12195253372192 1641999923 6515875840 14388451 11 base multithreaded_gzip setup process 49.800825008 1641999923 6515879936 14388451 11 base multithreaded_gzip setup real 15.864639043807983 1641999923 6515879936 14388451 11 base multithreaded_gzip writing process 97.815085082 1641999923 6515879936 14388451 11 base multithreaded_gzip writing real 97.83104109764099 1641999923 6515879936 14388451 11 base uncompressed setup process 48.850021397 1641999923 6515879936 14388451 11 base uncompressed setup real 16.385770320892334 1641999923 6515879936 14388451 11 base uncompressed writing process 97.42563251000001 1641999923 6515879936 14388451 11 base uncompressed writing real 97.43097257614136 1641999923 6515879936 14388451 11 base zstandard setup process 57.130642056999996 1641999923 6515888128 14388451 11 base zstandard setup real 18.90769624710083 1641999923 6515888128 14388451 11 base zstandard writing process 99.85411830200002 1641999923 6515888128 14388451 11 base zstandard writing real 99.8676381111145 1641999923 6515888128 14388451 11 data.table gzip setup process 57.120600576 1641999923 6518173696 14388451 11 data.table gzip setup real 19.275273323059082 1641999923 6518173696 14388451 11 data.table gzip writing process 67.75096147299999 1641999923 6518173696 14388451 11 data.table gzip writing real 67.75405859947205 1641999923 6518173696 14388451 11 data.table multithreaded_gzip setup process 52.228046424 1641999923 7046705152 14388451 11 data.table multithreaded_gzip setup real 18.820579051971436 1641999923 7046705152 14388451 11 data.table multithreaded_gzip writing process 67.654484228 1641999923 7046705152 14388451 11 data.table multithreaded_gzip writing real 8.864737272262573 1641999923 7046705152 14388451 11 data.table uncompressed setup process 51.447035740000004 1641999923 6576988160 14388451 11 data.table uncompressed setup real 16.117270708084106 1641999923 6576988160 14388451 11 data.table uncompressed writing process 9.219789825999996 1641999923 6576988160 14388451 11 data.table uncompressed writing real 1.4708619117736816 1641999923 6576988160 14388451 11 readr gzip setup process 52.427885992 1641999923 6816428032 14388451 11 readr gzip setup real 16.014821529388428 1641999923 6816428032 14388451 11 readr gzip writing process 120.15885149100001 1641999923 6816428032 14388451 11 readr gzip writing real 120.1623752117157 1641999923 6816428032 14388451 11 readr multithreaded_gzip setup process 52.571174903999996 1641999923 6816079872 14388451 11 readr multithreaded_gzip setup real 16.05108642578125 1641999923 6816079872 14388451 11 readr multithreaded_gzip writing process 53.408188828 1641999923 6816079872 14388451 11 readr multithreaded_gzip writing real 53.427462577819824 1641999923 6816079872 14388451 11 readr uncompressed setup process 50.408979955 1641999923 6815977472 14388451 11 readr uncompressed setup real 16.516019582748413 1641999923 6815977472 14388451 11 readr uncompressed writing process 52.224211567000005 1641999923 6815977472 14388451 11 readr uncompressed writing real 52.229180574417114 1641999923 6815977472 14388451 11 readr zstandard setup process 53.053004752 1641999923 6815940608 14388451 11 readr zstandard setup real 17.89621663093567 1641999923 6815940608 14388451 11 readr zstandard writing process 54.141594968999996 1641999923 6815940608 14388451 11 readr zstandard writing real 54.15725231170654 1641999923 6815940608 14388451 11 vroom gzip setup process 58.208655639999996 1641999923 6657384448 14388451 11 vroom gzip setup real 18.150486946105957 1641999923 6657384448 14388451 11 vroom gzip writing process 93.763101656999993 1641999923 6657384448 14388451 11 vroom gzip writing real 74.44156193733215 1641999923 6657384448 14388451 11 vroom multithreaded_gzip setup process 52.529673003999996 1641999923 6656368640 14388451 11 vroom multithreaded_gzip setup real 17.085912942886353 1641999923 6656368640 14388451 11 vroom multithreaded_gzip writing process 23.073533718 1641999923 6656368640 14388451 11 vroom multithreaded_gzip writing real 8.076940298080444 1641999923 6656368640 14388451 11 vroom uncompressed setup process 52.838888093 1641999923 7582793728 14388451 11 vroom uncompressed setup real 18.450870513916016 1641999923 7582793728 14388451 11 vroom uncompressed writing process 22.587353183000005 1641999923 7582793728 14388451 11 vroom uncompressed writing real 1.6948139667510986 1641999923 7582793728 14388451 11 vroom zstandard setup process 51.565757179 1641999923 6656360448 14388451 11 vroom zstandard setup real 16.19530463218689 1641999923 6656360448 14388451 11 vroom zstandard writing process 22.155763127999997 1641999923 6656360448 14388451 11 vroom zstandard writing real 12.370139837265015 1641999923 6656360448 14388451 11 vroom/inst/bench/all_numeric-wide.tsv0000644000176200001440000002234114132374245017435 0ustar liggesusersreading_package manip_package op type time size max_memory rows cols data.table data.table setup process 0.051875126999999965 1963219523 5880053760 100000 1000 data.table data.table setup real 0.051879167556762695 1963219523 5880053760 100000 1000 data.table data.table read process 5.577086213 1963219523 5880053760 100000 1000 data.table data.table read real 1.2563517093658447 1963219523 5880053760 100000 1000 data.table data.table print process 0.09918665400000037 1963219523 5880053760 100000 1000 data.table data.table print real 0.09919476509094238 1963219523 5880053760 100000 1000 data.table data.table head process 4.1016100000046407e-4 1963219523 5880053760 100000 1000 data.table data.table head real 4.124641418457031e-4 1963219523 5880053760 100000 1000 data.table data.table tail process 4.3974700000060096e-4 1963219523 5880053760 100000 1000 data.table data.table tail real 4.417896270751953e-4 1963219523 5880053760 100000 1000 data.table data.table sample process 0.002832650000000214 1963219523 5880053760 100000 1000 data.table data.table sample real 0.002834796905517578 1963219523 5880053760 100000 1000 data.table data.table filter process 0.003388150000000145 1963219523 5880053760 100000 1000 data.table data.table filter real 0.003390073776245117 1963219523 5880053760 100000 1000 data.table data.table aggregate process 0.014421183999999698 1963219523 5880053760 100000 1000 data.table data.table aggregate real 0.003484010696411133 1963219523 5880053760 100000 1000 read.delim base setup process 3.0330000000411417e-6 1963219523 15470862336 100000 1000 read.delim base setup real 4.76837158203125e-6 1963219523 15470862336 100000 1000 read.delim base read process 520.970175058 1963219523 15470862336 100000 1000 read.delim base read real 520.9826147556305 1963219523 15470862336 100000 1000 read.delim base print process 0.1300029069999482 1963219523 15470862336 100000 1000 read.delim base print real 0.13001203536987305 1963219523 15470862336 100000 1000 read.delim base head process 0.006249463000017386 1963219523 15470862336 100000 1000 read.delim base head real 0.006251096725463867 1963219523 15470862336 100000 1000 read.delim base tail process 0.006446566000022358 1963219523 15470862336 100000 1000 read.delim base tail real 0.006448507308959961 1963219523 15470862336 100000 1000 read.delim base sample process 0.008708142000045882 1963219523 15470862336 100000 1000 read.delim base sample real 0.008709907531738281 1963219523 15470862336 100000 1000 read.delim base filter process 0.07440451399997983 1963219523 15470862336 100000 1000 read.delim base filter real 0.07440614700317383 1963219523 15470862336 100000 1000 read.delim base aggregate process 0.00439676700000291 1963219523 15470862336 100000 1000 read.delim base aggregate real 0.0043985843658447266 1963219523 15470862336 100000 1000 readr dplyr setup process 0.26801563699999975 1963219523 5863723008 100000 1000 readr dplyr setup real 0.2680184841156006 1963219523 5863723008 100000 1000 readr dplyr read process 56.104649359 1963219523 5863723008 100000 1000 readr dplyr read real 56.11158347129822 1963219523 5863723008 100000 1000 readr dplyr print process 0.0787199290000018 1963219523 5863723008 100000 1000 readr dplyr print real 0.0954742431640625 1963219523 5863723008 100000 1000 readr dplyr head process 0.0027141720000045666 1963219523 5863723008 100000 1000 readr dplyr head real 0.0027158260345458984 1963219523 5863723008 100000 1000 readr dplyr tail process 0.0022157200000023636 1963219523 5863723008 100000 1000 readr dplyr tail real 0.002217531204223633 1963219523 5863723008 100000 1000 readr dplyr sample process 0.02533303300000256 1963219523 5863723008 100000 1000 readr dplyr sample real 0.025334835052490234 1963219523 5863723008 100000 1000 readr dplyr filter process 0.017108968999998808 1963219523 5863723008 100000 1000 readr dplyr filter real 0.017110586166381836 1963219523 5863723008 100000 1000 readr dplyr aggregate process 0.038782436000005305 1963219523 5863723008 100000 1000 readr dplyr aggregate real 0.03878426551818848 1963219523 5863723008 100000 1000 vroom base setup process 0.22703830000000025 1963219523 7791280128 100000 1000 vroom base setup real 0.2270522117614746 1963219523 7791280128 100000 1000 vroom base read process 16.174510832 1963219523 7791280128 100000 1000 vroom base read real 2.998760223388672 1963219523 7791280128 100000 1000 vroom base print process 0.07004265899999851 1963219523 7791280128 100000 1000 vroom base print real 0.06718945503234863 1963219523 7791280128 100000 1000 vroom base head process 0.003972434999997887 1963219523 7791280128 100000 1000 vroom base head real 0.003973960876464844 1963219523 7791280128 100000 1000 vroom base tail process 0.00348148499999823 1963219523 7791280128 100000 1000 vroom base tail real 0.0034830570220947266 1963219523 7791280128 100000 1000 vroom base sample process 0.00464282200000099 1963219523 7791280128 100000 1000 vroom base sample real 0.0046443939208984375 1963219523 7791280128 100000 1000 vroom base filter process 0.049587988999999055 1963219523 7791280128 100000 1000 vroom base filter real 0.010494709014892578 1963219523 7791280128 100000 1000 vroom base aggregate process 0.041909286999999296 1963219523 7791280128 100000 1000 vroom base aggregate real 0.04191112518310547 1963219523 7791280128 100000 1000 vroom dplyr setup process 0.2996260059999998 1963219523 7792242688 100000 1000 vroom dplyr setup real 0.29964709281921387 1963219523 7792242688 100000 1000 vroom dplyr read process 16.673107875 1963219523 7792242688 100000 1000 vroom dplyr read real 2.9666857719421387 1963219523 7792242688 100000 1000 vroom dplyr print process 0.07086207600000094 1963219523 7792242688 100000 1000 vroom dplyr print real 0.06784224510192871 1963219523 7792242688 100000 1000 vroom dplyr head process 0.003975565000001069 1963219523 7792242688 100000 1000 vroom dplyr head real 0.0039768218994140625 1963219523 7792242688 100000 1000 vroom dplyr tail process 0.013946671999999438 1963219523 7792242688 100000 1000 vroom dplyr tail real 0.013948440551757812 1963219523 7792242688 100000 1000 vroom dplyr sample process 0.02246668400000118 1963219523 7792242688 100000 1000 vroom dplyr sample real 0.022468090057373047 1963219523 7792242688 100000 1000 vroom dplyr filter process 0.05683080300000043 1963219523 7792242688 100000 1000 vroom dplyr filter real 0.019407272338867188 1963219523 7792242688 100000 1000 vroom dplyr aggregate process 0.07652678499999865 1963219523 7792242688 100000 1000 vroom dplyr aggregate real 0.07652854919433594 1963219523 7792242688 100000 1000 vroom_no_altrep base setup process 0.22699760899999988 1963219523 5739347968 100000 1000 vroom_no_altrep base setup real 0.22700119018554688 1963219523 5739347968 100000 1000 vroom_no_altrep base read process 40.352723725000004 1963219523 5739347968 100000 1000 vroom_no_altrep base read real 6.934685707092285 1963219523 5739347968 100000 1000 vroom_no_altrep base print process 0.060421132000001876 1963219523 5739347968 100000 1000 vroom_no_altrep base print real 0.06042838096618652 1963219523 5739347968 100000 1000 vroom_no_altrep base head process 0.0024158189999994306 1963219523 5739347968 100000 1000 vroom_no_altrep base head real 0.0024175643920898438 1963219523 5739347968 100000 1000 vroom_no_altrep base tail process 0.0020524330000029067 1963219523 5739347968 100000 1000 vroom_no_altrep base tail real 0.002053976058959961 1963219523 5739347968 100000 1000 vroom_no_altrep base sample process 0.004886046000002864 1963219523 5739347968 100000 1000 vroom_no_altrep base sample real 0.00488734245300293 1963219523 5739347968 100000 1000 vroom_no_altrep base filter process 0.005605907000003185 1963219523 5739347968 100000 1000 vroom_no_altrep base filter real 0.00560760498046875 1963219523 5739347968 100000 1000 vroom_no_altrep base aggregate process 0.0062097089999966215 1963219523 5739347968 100000 1000 vroom_no_altrep base aggregate real 0.006211280822753906 1963219523 5739347968 100000 1000 vroom_no_altrep dplyr setup process 0.2939437899999999 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr setup real 0.2939589023590088 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr read process 41.10774825 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr read real 6.923159122467041 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr print process 0.06295269499999989 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr print real 0.06295442581176758 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr head process 0.0024358220000024744 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr head real 0.0024373531341552734 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr tail process 0.002060569999997597 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr tail real 0.002062082290649414 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr sample process 0.09410682600000086 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr sample real 0.09410929679870605 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr filter process 0.013741070000001798 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr filter real 0.013743400573730469 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr aggregate process 0.030236498000000722 1963219523 5740023808 100000 1000 vroom_no_altrep dplyr aggregate real 0.030238628387451172 1963219523 5740023808 100000 1000 vroom/inst/bench/all_numeric-long.tsv0000644000176200001440000002177614132374245017457 0ustar liggesusersreading_package manip_package op type time size max_memory rows cols data.table data.table setup process 0.05067038700000004 490798139 2920148992 1000000 25 data.table data.table setup real 0.050673484802246094 490798139 2920148992 1000000 25 data.table data.table read process 1.339093691 490798139 2920148992 1000000 25 data.table data.table read real 0.25513172149658203 490798139 2920148992 1000000 25 data.table data.table print process 0.012984569999999973 490798139 2920148992 1000000 25 data.table data.table print real 0.01298666000366211 490798139 2920148992 1000000 25 data.table data.table head process 2.257949999999731e-4 490798139 2920148992 1000000 25 data.table data.table head real 0.00022745132446289062 490798139 2920148992 1000000 25 data.table data.table tail process 1.8293999999974275e-4 490798139 2920148992 1000000 25 data.table data.table tail real 1.842975616455078e-4 490798139 2920148992 1000000 25 data.table data.table sample process 0.0032215970000000205 490798139 2920148992 1000000 25 data.table data.table sample real 0.0032236576080322266 490798139 2920148992 1000000 25 data.table data.table filter process 0.00553502799999972 490798139 2920148992 1000000 25 data.table data.table filter real 0.005329132080078125 490798139 2920148992 1000000 25 data.table data.table aggregate process 0.15706757400000004 490798139 2920148992 1000000 25 data.table data.table aggregate real 0.024374723434448242 490798139 2920148992 1000000 25 read.delim base setup process 3.1689999999695573e-6 490798139 5142343680 1000000 25 read.delim base setup real 5.0067901611328125e-6 490798139 5142343680 1000000 25 read.delim base read process 111.410966158 490798139 5142343680 1000000 25 read.delim base read real 111.41260313987732 490798139 5142343680 1000000 25 read.delim base print process 1.4099199399999947 490798139 5142343680 1000000 25 read.delim base print real 1.4099228382110596 490798139 5142343680 1000000 25 read.delim base head process 2.753560000030575e-4 490798139 5142343680 1000000 25 read.delim base head real 2.765655517578125e-4 490798139 5142343680 1000000 25 read.delim base tail process 4.943510000003926e-4 490798139 5142343680 1000000 25 read.delim base tail real 4.954338073730469e-4 490798139 5142343680 1000000 25 read.delim base sample process 0.0011230939999933298 490798139 5142343680 1000000 25 read.delim base sample real 0.0011248588562011719 490798139 5142343680 1000000 25 read.delim base filter process 4.450478324000002 490798139 5142343680 1000000 25 read.delim base filter real 4.450565814971924 490798139 5142343680 1000000 25 read.delim base aggregate process 0.03682857900000158 490798139 5142343680 1000000 25 read.delim base aggregate real 0.03683161735534668 490798139 5142343680 1000000 25 readr dplyr setup process 0.2670424519999999 490798139 3029172224 1000000 25 readr dplyr setup real 0.267045259475708 490798139 3029172224 1000000 25 readr dplyr read process 13.121372993 490798139 3029172224 1000000 25 readr dplyr read real 13.124317646026611 490798139 3029172224 1000000 25 readr dplyr print process 0.0572878620000008 490798139 3029172224 1000000 25 readr dplyr print real 0.06383562088012695 490798139 3029172224 1000000 25 readr dplyr head process 0.001062892000000204 490798139 3029172224 1000000 25 readr dplyr head real 0.001064300537109375 490798139 3029172224 1000000 25 readr dplyr tail process 4.806849999994256e-4 490798139 3029172224 1000000 25 readr dplyr tail real 4.818439483642578e-4 490798139 3029172224 1000000 25 readr dplyr sample process 0.015185605999999297 490798139 3029172224 1000000 25 readr dplyr sample real 0.015187501907348633 490798139 3029172224 1000000 25 readr dplyr filter process 0.017675308999999473 490798139 3029172224 1000000 25 readr dplyr filter real 0.01767706871032715 490798139 3029172224 1000000 25 readr dplyr aggregate process 0.05443157900000095 490798139 3029172224 1000000 25 readr dplyr aggregate real 0.05443406105041504 490798139 3029172224 1000000 25 vroom base setup process 0.22593527800000013 490798139 3523780608 1000000 25 vroom base setup real 0.2259387969970703 490798139 3523780608 1000000 25 vroom base read process 3.8099602220000004 490798139 3523780608 1000000 25 vroom base read real 0.5808789730072021 490798139 3523780608 1000000 25 vroom base print process 0.05762982400000016 490798139 3523780608 1000000 25 vroom base print real 0.05461859703063965 490798139 3523780608 1000000 25 vroom base head process 9.245710000005403e-4 490798139 3523780608 1000000 25 vroom base head real 0.00092601776123046875 490798139 3523780608 1000000 25 vroom base tail process 4.916189999999432e-4 490798139 3523780608 1000000 25 vroom base tail real 4.928112030029297e-4 490798139 3523780608 1000000 25 vroom base sample process 0.0027541729999995823 490798139 3523780608 1000000 25 vroom base sample real 0.0027556419372558594 490798139 3523780608 1000000 25 vroom base filter process 0.2551561959999997 490798139 3523780608 1000000 25 vroom base filter real 0.028618335723876953 490798139 3523780608 1000000 25 vroom base aggregate process 0.2509484510000002 490798139 3523780608 1000000 25 vroom base aggregate real 0.25095057487487793 490798139 3523780608 1000000 25 vroom dplyr setup process 0.29869805699999996 490798139 3531214848 1000000 25 vroom dplyr setup real 0.29870104789733887 490798139 3531214848 1000000 25 vroom dplyr read process 3.8170653839999997 490798139 3531214848 1000000 25 vroom dplyr read real 0.603179931640625 490798139 3531214848 1000000 25 vroom dplyr print process 0.06604458400000013 490798139 3531214848 1000000 25 vroom dplyr print real 0.06310796737670898 490798139 3531214848 1000000 25 vroom dplyr head process 9.071970000000817e-4 490798139 3531214848 1000000 25 vroom dplyr head real 0.00090885162353515625 490798139 3531214848 1000000 25 vroom dplyr tail process 4.918530000006527e-4 490798139 3531214848 1000000 25 vroom dplyr tail real 0.00049304962158203125 490798139 3531214848 1000000 25 vroom dplyr sample process 0.013811199000000052 490798139 3531214848 1000000 25 vroom dplyr sample real 0.013812541961669922 490798139 3531214848 1000000 25 vroom dplyr filter process 0.2886524169999998 490798139 3531214848 1000000 25 vroom dplyr filter real 0.041882991790771484 490798139 3531214848 1000000 25 vroom dplyr aggregate process 0.23468923700000044 490798139 3531214848 1000000 25 vroom dplyr aggregate real 0.23469161987304688 490798139 3531214848 1000000 25 vroom_no_altrep base setup process 0.22674703399999996 490798139 2886311936 1000000 25 vroom_no_altrep base setup real 0.22675061225891113 490798139 2886311936 1000000 25 vroom_no_altrep base read process 9.917269908 490798139 2886311936 1000000 25 vroom_no_altrep base read real 1.325624942779541 490798139 2886311936 1000000 25 vroom_no_altrep base print process 0.04774370600000033 490798139 2886311936 1000000 25 vroom_no_altrep base print real 0.04775238037109375 490798139 2886311936 1000000 25 vroom_no_altrep base head process 8.502769999996218e-4 490798139 2886311936 1000000 25 vroom_no_altrep base head real 0.00085163116455078125 490798139 2886311936 1000000 25 vroom_no_altrep base tail process 4.878420000000716e-4 490798139 2886311936 1000000 25 vroom_no_altrep base tail real 4.889965057373047e-4 490798139 2886311936 1000000 25 vroom_no_altrep base sample process 0.0028887550000007423 490798139 2886311936 1000000 25 vroom_no_altrep base sample real 0.0028901100158691406 490798139 2886311936 1000000 25 vroom_no_altrep base filter process 0.005902124000000342 490798139 2886311936 1000000 25 vroom_no_altrep base filter real 0.005903720855712891 490798139 2886311936 1000000 25 vroom_no_altrep base aggregate process 0.054749883000001276 490798139 2886311936 1000000 25 vroom_no_altrep base aggregate real 0.05475163459777832 490798139 2886311936 1000000 25 vroom_no_altrep dplyr setup process 0.2972948229999999 490798139 2957496320 1000000 25 vroom_no_altrep dplyr setup real 0.29729771614074707 490798139 2957496320 1000000 25 vroom_no_altrep dplyr read process 10.102860680000001 490798139 2957496320 1000000 25 vroom_no_altrep dplyr read real 1.3375225067138672 490798139 2957496320 1000000 25 vroom_no_altrep dplyr print process 0.04706370399999926 490798139 2957496320 1000000 25 vroom_no_altrep dplyr print real 0.04706525802612305 490798139 2957496320 1000000 25 vroom_no_altrep dplyr head process 8.443339999999466e-4 490798139 2957496320 1000000 25 vroom_no_altrep dplyr head real 8.456707000732422e-4 490798139 2957496320 1000000 25 vroom_no_altrep dplyr tail process 4.372789999997906e-4 490798139 2957496320 1000000 25 vroom_no_altrep dplyr tail real 4.3845176696777344e-4 490798139 2957496320 1000000 25 vroom_no_altrep dplyr sample process 0.013971556000001328 490798139 2957496320 1000000 25 vroom_no_altrep dplyr sample real 0.013973236083984375 490798139 2957496320 1000000 25 vroom_no_altrep dplyr filter process 0.01792857100000056 490798139 2957496320 1000000 25 vroom_no_altrep dplyr filter real 0.017930269241333008 490798139 2957496320 1000000 25 vroom_no_altrep dplyr aggregate process 0.045251213000000234 490798139 2957496320 1000000 25 vroom_no_altrep dplyr aggregate real 0.04525351524353027 490798139 2957496320 1000000 25 vroom/inst/bench/all_character-wide.tsv0000644000176200001440000001762614132374245017741 0ustar liggesusersreading_package manip_package op type time size max_memory rows cols data.table data.table setup process 0.05824787900000006 1600080624 13596225536 100000 1000 data.table data.table setup real 0.05825209617614746 1600080624 13596225536 100000 1000 data.table data.table read process 205.61311071900002 1600080624 13596225536 100000 1000 data.table data.table read real 201.76576399803162 1600080624 13596225536 100000 1000 data.table data.table print process 0.13403993000000014 1600080624 13596225536 100000 1000 data.table data.table print real 0.13405299186706543 1600080624 13596225536 100000 1000 data.table data.table head process 0.001164771000020437 1600080624 13596225536 100000 1000 data.table data.table head real 0.0011682510375976562 1600080624 13596225536 100000 1000 data.table data.table tail process 0.0012540889999854699 1600080624 13596225536 100000 1000 data.table data.table tail real 0.0012569427490234375 1600080624 13596225536 100000 1000 data.table data.table sample process 0.03256328700001632 1600080624 13596225536 100000 1000 data.table data.table sample real 0.03256726264953613 1600080624 13596225536 100000 1000 data.table data.table filter process 0.20218088300001114 1600080624 13596225536 100000 1000 data.table data.table filter real 0.16787242889404297 1600080624 13596225536 100000 1000 data.table data.table aggregate process 0.048374496000008094 1600080624 13596225536 100000 1000 data.table data.table aggregate real 0.014370918273925781 1600080624 13596225536 100000 1000 read.delim base setup process 3.6660000000132698e-6 1600080624 14050525184 100000 1000 read.delim base setup real 5.7220458984375e-6 1600080624 14050525184 100000 1000 read.delim base read process 510.41117117600004 1600080624 14050525184 100000 1000 read.delim base read real 510.42269372940063 1600080624 14050525184 100000 1000 read.delim base print process 0.14892288600003667 1600080624 14050525184 100000 1000 read.delim base print real 0.1489250659942627 1600080624 14050525184 100000 1000 read.delim base head process 0.006831051000062871 1600080624 14050525184 100000 1000 read.delim base head real 0.006834268569946289 1600080624 14050525184 100000 1000 read.delim base tail process 0.007016168999939509 1600080624 14050525184 100000 1000 read.delim base tail real 0.007021188735961914 1600080624 14050525184 100000 1000 read.delim base sample process 0.025838453000005757 1600080624 14050525184 100000 1000 read.delim base sample real 0.025842905044555664 1600080624 14050525184 100000 1000 read.delim base filter process 0.22350397200000316 1600080624 14050525184 100000 1000 read.delim base filter real 0.2235558032989502 1600080624 14050525184 100000 1000 read.delim base aggregate process 0.05853724200005672 1600080624 14050525184 100000 1000 read.delim base aggregate real 0.05854058265686035 1600080624 14050525184 100000 1000 readr dplyr setup process 0.33448946199999985 1600080624 13111070720 100000 1000 readr dplyr setup real 0.33449411392211914 1600080624 13111070720 100000 1000 readr dplyr read process 459.43154994500003 1600080624 13111070720 100000 1000 readr dplyr read real 459.44729495048523 1600080624 13111070720 100000 1000 readr dplyr print process 0.13655689199998733 1600080624 13111070720 100000 1000 readr dplyr print real 0.21697449684143066 1600080624 13111070720 100000 1000 readr dplyr head process 0.0032427959999949962 1600080624 13111070720 100000 1000 readr dplyr head real 0.0032444000244140625 1600080624 13111070720 100000 1000 readr dplyr tail process 0.0025115230000096744 1600080624 13111070720 100000 1000 readr dplyr tail real 0.002513408660888672 1600080624 13111070720 100000 1000 readr dplyr sample process 0.02890705900000512 1600080624 13111070720 100000 1000 readr dplyr sample real 0.02890944480895996 1600080624 13111070720 100000 1000 readr dplyr filter process 0.037404203999983565 1600080624 13111070720 100000 1000 readr dplyr filter real 0.03740739822387695 1600080624 13111070720 100000 1000 readr dplyr aggregate process 0.056806200999972134 1600080624 13111070720 100000 1000 readr dplyr aggregate real 0.056809425354003906 1600080624 13111070720 100000 1000 vroom base setup process 0.27099322299999984 1600080624 7050588160 100000 1000 vroom base setup real 0.27102088928222656 1600080624 7050588160 100000 1000 vroom base read process 16.096324971999998 1600080624 7050588160 100000 1000 vroom base read real 3.1437342166900635 1600080624 7050588160 100000 1000 vroom base print process 0.06175371999999868 1600080624 7050588160 100000 1000 vroom base print real 0.06175589561462402 1600080624 7050588160 100000 1000 vroom base head process 0.004202636999998788 1600080624 7050588160 100000 1000 vroom base head real 0.004204511642456055 1600080624 7050588160 100000 1000 vroom base tail process 0.003743497999998624 1600080624 7050588160 100000 1000 vroom base tail real 0.003745555877685547 1600080624 7050588160 100000 1000 vroom base sample process 0.004824033999998534 1600080624 7050588160 100000 1000 vroom base sample real 0.0048258304595947266 1600080624 7050588160 100000 1000 vroom base filter process 0.05477418799999967 1600080624 7050588160 100000 1000 vroom base filter real 0.054776668548583984 1600080624 7050588160 100000 1000 vroom base aggregate process 0.2514594879999983 1600080624 7050588160 100000 1000 vroom base aggregate real 0.2514626979827881 1600080624 7050588160 100000 1000 vroom dplyr setup process 0.36314286799999995 1600080624 7050731520 100000 1000 vroom dplyr setup real 0.3631572723388672 1600080624 7050731520 100000 1000 vroom dplyr read process 15.844784293 1600080624 7050731520 100000 1000 vroom dplyr read real 3.108229875564575 1600080624 7050731520 100000 1000 vroom dplyr print process 0.06392608800000232 1600080624 7050731520 100000 1000 vroom dplyr print real 0.06392836570739746 1600080624 7050731520 100000 1000 vroom dplyr head process 0.004407775999997199 1600080624 7050731520 100000 1000 vroom dplyr head real 0.004410743713378906 1600080624 7050731520 100000 1000 vroom dplyr tail process 0.0039727230000004 1600080624 7050731520 100000 1000 vroom dplyr tail real 0.0039751529693603516 1600080624 7050731520 100000 1000 vroom dplyr sample process 0.026044930000001187 1600080624 7050731520 100000 1000 vroom dplyr sample real 0.026047945022583008 1600080624 7050731520 100000 1000 vroom dplyr filter process 0.08114447399999847 1600080624 7050731520 100000 1000 vroom dplyr filter real 0.0811469554901123 1600080624 7050731520 100000 1000 vroom dplyr aggregate process 0.1597604110000006 1600080624 7050731520 100000 1000 vroom dplyr aggregate real 0.15976333618164062 1600080624 7050731520 100000 1000 vroom_no_altrep dplyr setup process 0.349206017 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr setup real 0.3492105007171631 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr read process 258.09181479800003 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr read real 247.31999611854553 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr print process 0.06623288399998728 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr print real 0.06623482704162598 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr head process 0.0027959219999615925 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr head real 0.002797365188598633 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr tail process 0.0024252139999703104 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr tail real 0.0024268627166748047 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr sample process 0.027183294999986174 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr sample real 0.02718496322631836 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr filter process 0.034791021999978966 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr filter real 0.03479290008544922 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr aggregate process 0.036543052000013176 1600080624 13033082880 100000 1000 vroom_no_altrep dplyr aggregate real 0.0365450382232666 1600080624 13033082880 100000 1000 vroom/inst/bench/download-data.sh0000755000176200001440000000105014132374245016526 0ustar liggesusers#!/bin/bash # download and extract taxi data mkdir ~/data/ cd ~/data wget -O ~/data/trip_fare.7z https://archive.org/download/nycTaxiTripData2013/trip_fare.7z && \ sudo apt install p7zip-full && \ 7z x trip_fare.7z &> data.out # fix trailing space in header for every file ls *trip_fare*.csv | xargs -P 16 sed -i '1 s/, /,/g' # download the US census data wget -O ~/data/all_California.zip https://www2.census.gov/census_2000/datasets/PUMS/FivePercent/California/all_California.zip && \ sudo apt install unzip && \ unzip all_California.zip vroom/inst/bench/all_numeric-wide/0000755000176200001440000000000014533733452016701 5ustar liggesusersvroom/inst/bench/all_numeric-wide/vroom-dplyr.R0000644000176200001440000000041614132374245021313 0ustar liggesusers({ library(vroom); library(dplyr) }) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 > 3) e <- group_by(x, as.integer(X2)) %>% summarise(avg_X1 = mean(X1)) vroom/inst/bench/all_numeric-wide/input.R0000644000176200001440000000040614132374245020157 0ustar liggesusersargs <- commandArgs(trailingOnly = TRUE) rows <- as.integer(args[[1]]) cols <- as.integer(args[[2]]) output <- args[[3]] set.seed(42) RNGversion("3.5.3") data <- vroom::gen_tbl(rows, cols, col_types = strrep("d", cols)) vroom::vroom_write(data, output, "\t") vroom/inst/bench/all_numeric-wide/readr-dplyr.R0000644000176200001440000000037214132374245021247 0ustar liggesusers({ library(readr); library(dplyr) }) x <- read_tsv(file, trim_ws = FALSE, quote = "", na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 > 3) e <- group_by(x, as.integer(X2)) %>% summarise(avg_X1 = mean(X1)) vroom/inst/bench/all_numeric-wide/vroom-base.R0000644000176200001440000000034514132374245021074 0ustar liggesuserslibrary(vroom) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 > 3, ] e <- tapply(x$X1, as.integer(x$X2), mean) vroom/inst/bench/all_numeric-wide/vroom_no_altrep-dplyr.R0000644000176200001440000000043614132374245023360 0ustar liggesusers({ library(vroom); library(dplyr) }) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = FALSE) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 > 3) e <- group_by(x, as.integer(X2)) %>% summarise(avg_X1 = mean(X1)) vroom/inst/bench/all_numeric-wide/vroom_no_altrep-base.R0000644000176200001440000000036514132374245023141 0ustar liggesuserslibrary(vroom) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = FALSE) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 > 3, ] e <- tapply(x$X1, as.integer(x$X2), mean) vroom/inst/bench/all_numeric-wide/read.delim-base.R0000644000176200001440000000033514132374245021735 0ustar liggesusers({}) x <- read.delim(file, quote = "", na.strings = NULL, stringsAsFactors = FALSE) print(head(x, 10)) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 > 3, ] e <- tapply(x$X1, as.integer(x$X2), mean) vroom/inst/bench/all_numeric-wide/data.table-data.table.R0000644000176200001440000000034414132374245023015 0ustar liggesuserslibrary(data.table) x <- fread(file, sep = "\t", quote = "", strip.white = FALSE, na.strings = NULL) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[X1 > 3, ] e <- x[ , .(mean(X1)), by = as.integer(X2)] vroom/inst/bench/README.md0000644000176200001440000000135514132374245014740 0ustar liggesusers# Benchmarks for vroom The benchmarks are run with a makefile, run `make` to run them. ## Personal notes on running benchmarks on AWS ### Starting up - Create new volume from previous vroom snapshot - `io1` volume type - 2500 IOPS - Attach volume to instance - `/dev/sda1` ### Attach to instance `ssh vroom-bench` When you first start there may be some unattended upgrades installing, use top to monitor and wait until they are done. ### running ``` make -j 1 \ TAXI_INPUTS='$(wildcard ~/data/trip_fare*csv)' \ FWF_INPUT=~/data/PUMS5_06.TXT \ BENCH_LONG_ROWS=1000000 \ BENCH_LOG_COLS=25 \ BENCH_WIDE_ROWS=100000 \ BENCH_WIDE_COLS=1000 ``` ### Tearing down - Shut down instance - Detach volume - Create snapshot - Delete volume vroom/inst/bench/fwf.tsv0000644000176200001440000001442414132374245015002 0ustar liggesusersreading_package manip_package op type time size max_memory rows cols read.delim base setup process 3.063999999941558e-6 709936880 6621343744 2342339 37 read.delim base setup real 5.245208740234375e-6 709936880 6621343744 2342339 37 read.delim base read process 1089.464910198 709936880 6621343744 2342339 37 read.delim base read real 1089.5732316970825 709936880 6621343744 2342339 37 read.delim base print process 0.015051766999931715 709936880 6621343744 2342339 37 read.delim base print real 0.015721797943115234 709936880 6621343744 2342339 37 read.delim base head process 8.085290000963141e-4 709936880 6621343744 2342339 37 read.delim base head real 8.099079132080078e-4 709936880 6621343744 2342339 37 read.delim base tail process 0.0010067789999084198 709936880 6621343744 2342339 37 read.delim base tail real 0.0010082721710205078 709936880 6621343744 2342339 37 read.delim base sample process 0.0029781670000375016 709936880 6621343744 2342339 37 read.delim base sample real 0.0029802322387695312 709936880 6621343744 2342339 37 read.delim base filter process 0.49151955999991515 709936880 6621343744 2342339 37 read.delim base filter real 0.49154090881347656 709936880 6621343744 2342339 37 read.delim base aggregate process 0.08967751000000135 709936880 6621343744 2342339 37 read.delim base aggregate real 0.08968019485473633 709936880 6621343744 2342339 37 readr dplyr setup process 0.12630891799999988 709936880 6644965376 2342339 37 readr dplyr setup real 0.1263124942779541 709936880 6644965376 2342339 37 readr dplyr read process 32.593650083 709936880 6644965376 2342339 37 readr dplyr read real 32.59428787231445 709936880 6644965376 2342339 37 readr dplyr print process 0.04750896499999868 709936880 6644965376 2342339 37 readr dplyr print real 0.04751086235046387 709936880 6644965376 2342339 37 readr dplyr head process 0.0011317969999993238 709936880 6644965376 2342339 37 readr dplyr head real 0.0011332035064697266 709936880 6644965376 2342339 37 readr dplyr tail process 6.720350000009034e-4 709936880 6644965376 2342339 37 readr dplyr tail real 6.732940673828125e-4 709936880 6644965376 2342339 37 readr dplyr sample process 0.016384803000001114 709936880 6644965376 2342339 37 readr dplyr sample real 0.01638650894165039 709936880 6644965376 2342339 37 readr dplyr filter process 0.09458435599999859 709936880 6644965376 2342339 37 readr dplyr filter real 0.09458684921264648 709936880 6644965376 2342339 37 readr dplyr aggregate process 0.09379347599999477 709936880 6644965376 2342339 37 readr dplyr aggregate real 0.0937960147857666 709936880 6644965376 2342339 37 vroom base setup process 7.52309999998424e-5 709936880 4989079552 2342339 37 vroom base setup real 7.796287536621094e-5 709936880 4989079552 2342339 37 vroom base read process 0.16403843000000018 709936880 4989079552 2342339 37 vroom base read real 0.16362786293029785 709936880 4989079552 2342339 37 vroom base print process 0.056157112999999814 709936880 4989079552 2342339 37 vroom base print real 0.05505537986755371 709936880 4989079552 2342339 37 vroom base head process 5.586259999996734e-4 709936880 4989079552 2342339 37 vroom base head real 5.600452423095703e-4 709936880 4989079552 2342339 37 vroom base tail process 8.067250000003412e-4 709936880 4989079552 2342339 37 vroom base tail real 8.080005645751953e-4 709936880 4989079552 2342339 37 vroom base sample process 0.00669999900000029 709936880 4989079552 2342339 37 vroom base sample real 0.006701469421386719 709936880 4989079552 2342339 37 vroom base filter process 0.284221665 709936880 4989079552 2342339 37 vroom base filter real 0.2842233180999756 709936880 4989079552 2342339 37 vroom base aggregate process 2.1088666509999996 709936880 4989079552 2342339 37 vroom base aggregate real 1.7768657207489014 709936880 4989079552 2342339 37 vroom dplyr setup process 0.08314487400000026 709936880 4959260672 2342339 37 vroom dplyr setup real 0.08314847946166992 709936880 4959260672 2342339 37 vroom dplyr read process 0.1629412410000004 709936880 4959260672 2342339 37 vroom dplyr read real 0.16250014305114746 709936880 4959260672 2342339 37 vroom dplyr print process 0.04872845899999989 709936880 4959260672 2342339 37 vroom dplyr print real 0.047605037689208984 709936880 4959260672 2342339 37 vroom dplyr head process 0.001250199999999868 709936880 4959260672 2342339 37 vroom dplyr head real 0.0012516975402832031 709936880 4959260672 2342339 37 vroom dplyr tail process 7.490030000001369e-4 709936880 4959260672 2342339 37 vroom dplyr tail real 0.00075054168701171875 709936880 4959260672 2342339 37 vroom dplyr sample process 0.01527529800000016 709936880 4959260672 2342339 37 vroom dplyr sample real 0.01527714729309082 709936880 4959260672 2342339 37 vroom dplyr filter process 0.305116822 709936880 4959260672 2342339 37 vroom dplyr filter real 0.30511975288391113 709936880 4959260672 2342339 37 vroom dplyr aggregate process 1.2913172750000004 709936880 4959260672 2342339 37 vroom dplyr aggregate real 1.2913398742675781 709936880 4959260672 2342339 37 vroom_no_altrep dplyr setup process 0.08015654499999991 709936880 6397005824 2342339 37 vroom_no_altrep dplyr setup real 0.08015990257263184 709936880 6397005824 2342339 37 vroom_no_altrep dplyr read process 34.306891702 709936880 6397005824 2342339 37 vroom_no_altrep dplyr read real 14.682522296905518 709936880 6397005824 2342339 37 vroom_no_altrep dplyr print process 0.04313145500000104 709936880 6397005824 2342339 37 vroom_no_altrep dplyr print real 0.04313325881958008 709936880 6397005824 2342339 37 vroom_no_altrep dplyr head process 8.676749999949607e-4 709936880 6397005824 2342339 37 vroom_no_altrep dplyr head real 8.692741394042969e-4 709936880 6397005824 2342339 37 vroom_no_altrep dplyr tail process 5.269520000013017e-4 709936880 6397005824 2342339 37 vroom_no_altrep dplyr tail real 5.283355712890625e-4 709936880 6397005824 2342339 37 vroom_no_altrep dplyr sample process 0.01451422600000285 709936880 6397005824 2342339 37 vroom_no_altrep dplyr sample real 0.014516115188598633 709936880 6397005824 2342339 37 vroom_no_altrep dplyr filter process 0.4679828829999977 709936880 6397005824 2342339 37 vroom_no_altrep dplyr filter real 0.46799564361572266 709936880 6397005824 2342339 37 vroom_no_altrep dplyr aggregate process 0.09054751499999725 709936880 6397005824 2342339 37 vroom_no_altrep dplyr aggregate real 0.09055042266845703 709936880 6397005824 2342339 37 vroom/inst/bench/taxi.tsv0000644000176200001440000001742414132374245015170 0ustar liggesusersreading_package manip_package op type time size max_memory rows cols data.table data.table setup process 0.0535136759999999 1641999923 6853369856 14388451 11 data.table data.table setup real 0.05351758003234863 1641999923 6853369856 14388451 11 data.table data.table read process 28.634132631 1641999923 6853369856 14388451 11 data.table data.table read real 5.829838037490845 1641999923 6853369856 14388451 11 data.table data.table print process 0.011972201999999044 1641999923 6853369856 14388451 11 data.table data.table print real 0.011974573135375977 1641999923 6853369856 14388451 11 data.table data.table head process 2.2678200000214588e-4 1641999923 6853369856 14388451 11 data.table data.table head real 2.281665802001953e-4 1641999923 6853369856 14388451 11 data.table data.table tail process 1.8586699999900702e-4 1641999923 6853369856 14388451 11 data.table data.table tail real 0.00018739700317382812 1641999923 6853369856 14388451 11 data.table data.table sample process 6.299819999995293e-4 1641999923 6853369856 14388451 11 data.table data.table sample real 6.320476531982422e-4 1641999923 6853369856 14388451 11 data.table data.table filter process 0.34992646499999935 1641999923 6853369856 14388451 11 data.table data.table filter real 0.10343503952026367 1641999923 6853369856 14388451 11 data.table data.table aggregate process 1.6930106420000008 1641999923 6853369856 14388451 11 data.table data.table aggregate real 0.76350998878479 1641999923 6853369856 14388451 11 read.delim base setup process 2.617000000038061e-6 1641999923 6637604864 14388451 11 read.delim base setup real 4.291534423828125e-6 1641999923 6637604864 14388451 11 read.delim base read process 72.30426375399999 1641999923 6637604864 14388451 11 read.delim base read real 72.30614757537842 1641999923 6637604864 14388451 11 read.delim base print process 0.0059510929999930795 1641999923 6637604864 14388451 11 read.delim base print real 0.0059528350830078125 1641999923 6637604864 14388451 11 read.delim base head process 1.9695099999239574e-4 1641999923 6637604864 14388451 11 read.delim base head real 1.983642578125e-4 1641999923 6637604864 14388451 11 read.delim base tail process 4.1288200000622055e-4 1641999923 6637604864 14388451 11 read.delim base tail real 4.1413307189941406e-4 1641999923 6637604864 14388451 11 read.delim base sample process 3.71048000005203e-4 1641999923 6637604864 14388451 11 read.delim base sample real 3.726482391357422e-4 1641999923 6637604864 14388451 11 read.delim base filter process 1.2673573149999982 1641999923 6637604864 14388451 11 read.delim base filter real 1.2681455612182617 1641999923 6637604864 14388451 11 read.delim base aggregate process 0.8941533619999973 1641999923 6637604864 14388451 11 read.delim base aggregate real 0.8941662311553955 1641999923 6637604864 14388451 11 readr dplyr setup process 0.33623017600000016 1641999923 7415922688 14388451 11 readr dplyr setup real 0.3362598419189453 1641999923 7415922688 14388451 11 readr dplyr read process 37.30245180599999 1641999923 7415922688 14388451 11 readr dplyr read real 37.303383588790894 1641999923 7415922688 14388451 11 readr dplyr print process 0.12003345499999796 1641999923 7415922688 14388451 11 readr dplyr print real 0.14695501327514648 1641999923 7415922688 14388451 11 readr dplyr head process 0.0013562000000035823 1641999923 7415922688 14388451 11 readr dplyr head real 0.0013580322265625 1641999923 7415922688 14388451 11 readr dplyr tail process 6.067529999995713e-4 1641999923 7415922688 14388451 11 readr dplyr tail real 6.082057952880859e-4 1641999923 7415922688 14388451 11 readr dplyr sample process 0.01610033400000077 1641999923 7415922688 14388451 11 readr dplyr sample real 0.01610279083251953 1641999923 7415922688 14388451 11 readr dplyr filter process 0.24887825199999725 1641999923 7415922688 14388451 11 readr dplyr filter real 0.24891424179077148 1641999923 7415922688 14388451 11 readr dplyr aggregate process 0.5376497970000003 1641999923 7415922688 14388451 11 readr dplyr aggregate real 0.5376534461975098 1641999923 7415922688 14388451 11 vroom base setup process 0.233707 1666833417 6815055872 14776615 11 vroom base setup real 0.23376321099931374 1666833417 6815055872 14776615 11 vroom base read process 17.640452000000003 1666833417 6815055872 14776615 11 vroom base read real 1.3566655699978583 1666833417 6815055872 14776615 11 vroom base print process 0.1533760000000015 1666833417 6815055872 14776615 11 vroom base print real 0.15729006798937917 1666833417 6815055872 14776615 11 vroom base head process 0.0020479999999984955 1666833417 6815055872 14776615 11 vroom base head real 0.0020507239969447255 1666833417 6815055872 14776615 11 vroom base tail process 4.980000000003315e-4 1666833417 6815055872 14776615 11 vroom base tail real 5.036990041844547e-4 1666833417 6815055872 14776615 11 vroom base sample process 2.119999999976585e-4 1666833417 6815055872 14776615 11 vroom base sample real 2.1384801948443055e-4 1666833417 6815055872 14776615 11 vroom base filter process 1.1206159999999983 1666833417 6815055872 14776615 11 vroom base filter real 1.1206832609605044 1666833417 6815055872 14776615 11 vroom base aggregate process 8.975451 1666833417 6815055872 14776615 11 vroom base aggregate real 7.352519184001721 1666833417 6815055872 14776615 11 vroom dplyr setup process 0.443401 1666833417 6881202176 14776615 11 vroom dplyr setup real 0.4445208079996519 1666833417 6881202176 14776615 11 vroom dplyr read process 16.979603 1666833417 6881202176 14776615 11 vroom dplyr read real 1.2900599709828384 1666833417 6881202176 14776615 11 vroom dplyr print process 0.07167099999999849 1666833417 6881202176 14776615 11 vroom dplyr print real 0.07509800099069253 1666833417 6881202176 14776615 11 vroom dplyr head process 0.0017300000000020077 1666833417 6881202176 14776615 11 vroom dplyr head real 0.00173125701257959 1666833417 6881202176 14776615 11 vroom dplyr tail process 3.6899999999917554e-4 1666833417 6881202176 14776615 11 vroom dplyr tail real 3.7027703365311027e-4 1666833417 6881202176 14776615 11 vroom dplyr sample process 0.010666000000000508 1666833417 6881202176 14776615 11 vroom dplyr sample real 0.010672853037249297 1666833417 6881202176 14776615 11 vroom dplyr filter process 1.3317319999999988 1666833417 6881202176 14776615 11 vroom dplyr filter real 1.3320457949885167 1666833417 6881202176 14776615 11 vroom dplyr aggregate process 3.956316000000001 1666833417 6881202176 14776615 11 vroom dplyr aggregate real 3.9592064779717475 1666833417 6881202176 14776615 11 vroom_no_altrep dplyr setup process 0.301629645 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr setup real 0.3016328811645508 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr read process 47.375392178000006 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr read real 18.417577743530273 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr print process 0.1009485880000014 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr print real 0.11608505249023438 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr head process 0.001024977000000149 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr head real 0.0010266304016113281 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr tail process 4.7993700000148465e-4 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr tail real 4.813671112060547e-4 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr sample process 0.013319092999999782 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr sample real 0.0133209228515625 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr filter process 0.960716675999997 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr filter real 0.9607300758361816 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr aggregate process 1.2211422310000017 1641999923 7029014528 14388451 11 vroom_no_altrep dplyr aggregate real 1.2211759090423584 1641999923 7029014528 14388451 11 vroom/inst/bench/run-bench-fwf.R0000755000176200001440000000727314132374245016255 0ustar liggesusers#!/usr/bin/env Rscript args <- commandArgs(trailingOnly = TRUE) source_file <- args[[1]] out_file <- args[[2]] file <- args[-c(1:2)] library(vroom) fields <- vroom(col_names = c("begin", "end", "width", "col_names"), delim = "\t", "1 1 1 RECTYPE 2 8 7 SERIALNO 9 9 1 SAMPLE 10 11 2 STATE 12 12 1 REGION 13 13 1 DIVISION 14 18 5 PUMA5 19 23 5 PUMA1 24 27 4 MSACMSA5 28 31 4 MSAPMSA5 32 35 4 MSACMSA1 36 39 4 MSAPMSA1 40 41 2 AREATYP5 42 43 2 AREATYP1 44 57 14 TOTPUMA5 58 71 14 LNDPUMA5 72 85 14 TOTPUMA1 86 99 14 LNDPUMA1 100 101 2 SUBSAMPL 102 105 4 HWEIGHT 106 107 2 PERSONS 108 108 1 UNITTYPE 109 109 1 HSUB 110 110 1 HAUG 111 111 1 VACSTAT 112 112 1 VACSTATA 113 113 1 TENURE 114 114 1 TENUREA 115 116 2 BLDGSZ 117 117 1 BLDGSZA 118 118 1 YRBUILT 119 119 1 YRBUILTA 120 120 1 YRMOVED 121 121 1 YRMOVEDA 122 122 1 ROOMS 123 123 1 ROOMSA 124 124 1 BEDRMS 125 125 1 BEDRMSA 126 126 1 CPLUMB 127 127 1 CPLUMBA 128 128 1 CKITCH 129 129 1 CKITCHA 130 130 1 PHONE 131 131 1 PHONEA 132 132 1 FUEL 133 133 1 FUELA 134 134 1 VEHICL 135 135 1 VEHICLA 136 136 1 BUSINES 137 137 1 BUSINESA 138 138 1 ACRES 139 139 1 ACRESA 140 140 1 AGSALES 141 141 1 AGSALESA 142 145 4 ELEC 146 146 1 ELECA 147 150 4 GAS 151 151 1 GASA 152 155 4 WATER 156 156 1 WATERA 157 160 4 OIL 161 161 1 OILA 162 165 4 RENT 166 166 1 RENTA 167 167 1 MEALS 168 168 1 MEALSA 169 169 1 MORTG1 170 170 1 MORTG1A 171 175 5 MRT1AMT 176 176 1 MRT1AMTA 177 177 1 MORTG2 178 178 1 MORTG2A 179 183 5 MRT2AMT 184 184 1 MRT2AMTA 185 185 1 TAXINCL 186 186 1 TAXINCLA 187 188 2 TAXAMT 189 189 1 TAXAMTA 190 190 1 INSINCL 191 191 1 INSINCLA 192 195 4 INSAMT 196 196 1 INSAMTA 197 200 4 CONDFEE 201 201 1 CONDFEEA 202 203 2 VALUE 204 204 1 VALUEA 205 205 1 MHLOAN 206 206 1 MHLOANA 207 211 5 MHCOST 212 212 1 MHCOSTA 213 213 1 HHT 214 215 2 P65 216 217 2 P18 218 219 2 NPF 220 221 2 NOC 222 223 2 NRC 224 224 1 PSF 225 225 1 PAOC 226 226 1 PARC 227 227 1 SVAL 228 232 5 SMOC 233 235 3 SMOCAPI 236 236 1 SRNT 237 240 4 GRENT 241 243 3 GRAPI 244 244 1 FNF 245 245 1 HHL 246 246 1 LNGI 247 247 1 WIF 248 248 1 EMPSTAT 249 250 2 WORKEXP 251 258 8 HINC 259 266 8 FINC ") fields$begin <- fields$begin - 1 types <- cols( .default = col_double(), RECTYPE = col_character(), SERIALNO = col_character(), STATE = col_character(), PUMA5 = col_character(), PUMA1 = col_character(), MSACMSA5 = col_character(), MSAPMSA5 = col_character(), MSACMSA1 = col_character(), MSAPMSA1 = col_character(), AREATYP5 = col_character(), AREATYP1 = col_character(), LNDPUMA5 = col_character(), TOTPUMA1 = col_character(), LNDPUMA1 = col_character(), SUBSAMPL = col_character(), HWEIGHT = col_character(), PERSONS = col_character(), BLDGSZ = col_character(), ELEC = col_character(), GAS = col_character(), WATER = col_character(), OIL = col_character(), RENT = col_character(), MRT1AMT = col_character(), MRT2AMT = col_character(), TAXAMT = col_character(), INSAMT = col_character(), CONDFEE = col_character(), VALUE = col_character(), MHCOST = col_character(), P65 = col_character(), P18 = col_character(), NPF = col_character(), NOC = col_character(), NRC = col_character(), SMOC = col_character(), SMOCAPI = col_character(), GRENT = col_character(), GRAPI = col_character(), WORKEXP = col_character(), HINC = col_character(), FINC = col_character() ) cat(source_file, "\n") out <- bench::workout_expressions(as.list(parse(source_file, keep.source = FALSE))) x <- vroom::vroom(file, col_types = list()) out$size <- sum(file.size(file)) out$rows <- nrow(x) out$cols <- ncol(x) out$process <- as.numeric(out$process) out$real <- as.numeric(out$real) out$max_memory <- as.numeric(bench::bench_process_memory()[["max"]]) vroom::vroom_write(out, out_file) vroom/inst/bench/session_info.tsv0000644000176200001440000000157414132374245016720 0ustar liggesuserspackage ondiskversion loadedversion path loadedpath attached is_base date source md5ok library base 4.1.0 NA /opt/R/4.1.0/lib/R/library/base NA TRUE TRUE 2021-05-18 local NA /opt/R/4.1.0/lib/R/library data.table 1.14.0 NA /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1/data.table NA FALSE FALSE 2021-02-21 RSPM (R 4.1.0) NA /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1 dplyr 1.0.7 NA /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1/dplyr NA FALSE FALSE 2021-06-18 RSPM (R 4.1.0) NA /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1 readr 1.4.0 NA /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1/readr NA FALSE FALSE 2020-10-05 RSPM (R 4.1.0) NA /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1 vroom 1.5.1 1.5.1 /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1/vroom /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1/vroom FALSE FALSE 2021-06-22 local NA /home/ubuntu/R/x86_64-pc-linux-gnu-library/4.1 vroom/inst/bench/all_numeric-long/0000755000176200001440000000000014533733452016710 5ustar liggesusersvroom/inst/bench/all_numeric-long/vroom-dplyr.R0000644000176200001440000000041614132374245021322 0ustar liggesusers({ library(vroom); library(dplyr) }) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 > 3) e <- group_by(x, as.integer(X2)) %>% summarise(avg_X1 = mean(X1)) vroom/inst/bench/all_numeric-long/input.R0000644000176200001440000000040614132374245020166 0ustar liggesusersargs <- commandArgs(trailingOnly = TRUE) rows <- as.integer(args[[1]]) cols <- as.integer(args[[2]]) output <- args[[3]] set.seed(42) RNGversion("3.5.3") data <- vroom::gen_tbl(rows, cols, col_types = strrep("d", cols)) vroom::vroom_write(data, output, "\t") vroom/inst/bench/all_numeric-long/readr-dplyr.R0000644000176200001440000000037214132374245021256 0ustar liggesusers({ library(readr); library(dplyr) }) x <- read_tsv(file, trim_ws = FALSE, quote = "", na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 > 3) e <- group_by(x, as.integer(X2)) %>% summarise(avg_X1 = mean(X1)) vroom/inst/bench/all_numeric-long/vroom-base.R0000644000176200001440000000034514132374245021103 0ustar liggesuserslibrary(vroom) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 > 3, ] e <- tapply(x$X1, as.integer(x$X2), mean) vroom/inst/bench/all_numeric-long/vroom_no_altrep-dplyr.R0000644000176200001440000000043614132374245023367 0ustar liggesusers({ library(vroom); library(dplyr) }) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = FALSE) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 > 3) e <- group_by(x, as.integer(X2)) %>% summarise(avg_X1 = mean(X1)) vroom/inst/bench/all_numeric-long/vroom_no_altrep-base.R0000644000176200001440000000036514132374245023150 0ustar liggesuserslibrary(vroom) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = FALSE) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 > 3, ] e <- tapply(x$X1, as.integer(x$X2), mean) vroom/inst/bench/all_numeric-long/read.delim-base.R0000644000176200001440000000033514132374245021744 0ustar liggesusers({}) x <- read.delim(file, quote = "", na.strings = NULL, stringsAsFactors = FALSE) print(head(x, 10)) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 > 3, ] e <- tapply(x$X1, as.integer(x$X2), mean) vroom/inst/bench/all_numeric-long/data.table-data.table.R0000644000176200001440000000034414132374245023024 0ustar liggesuserslibrary(data.table) x <- fread(file, sep = "\t", quote = "", strip.white = FALSE, na.strings = NULL) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[X1 > 3, ] e <- x[ , .(mean(X1)), by = as.integer(X2)] vroom/inst/bench/taxi_multiple/0000755000176200001440000000000014533733452016341 5ustar liggesusersvroom/inst/bench/taxi_multiple/vroom-dplyr.R0000644000176200001440000000054414132374245020755 0ustar liggesusers({library(vroom); library(dplyr)}) x <- vroom(file, id = "path", col_types = c(pickup_datetime = "c"), trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = TRUE) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, payment_type == "UNK") e <- group_by(x, payment_type) %>% summarise(avg_tip = mean(tip_amount)) vroom/inst/bench/taxi_multiple/readr-dplyr.R0000644000176200001440000000056114132374245020707 0ustar liggesusers({ library(readr); library(dplyr); library(purrr) }) x <- map_dfr(set_names(file), .id = "path", ~ read_csv(.x, col_types = c(pickup_datetime = "c"), quote = "", trim_ws = FALSE, na = character()) ) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, payment_type == "UNK") e <- group_by(x, payment_type) %>% summarise(avg_tip = mean(tip_amount)) vroom/inst/bench/taxi_multiple/vroom-base.R0000644000176200001440000000050614132374245020533 0ustar liggesuserslibrary(vroom) x <- vroom(file, id = "path", col_types = c(pickup_datetime = "c"), trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = TRUE) print(head(x, 10)) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$payment_type == "UNK", ] e <- tapply(x$tip_amount, x$payment_type, mean) vroom/inst/bench/taxi_multiple/vroom_no_altrep-dplyr.R0000644000176200001440000000054514132374245023021 0ustar liggesusers({library(vroom); library(dplyr)}) x <- vroom(file, id = "path", col_types = c(pickup_datetime = "c"), trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = FALSE) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, payment_type == "UNK") e <- group_by(x, payment_type) %>% summarise(avg_tip = mean(tip_amount)) vroom/inst/bench/taxi_multiple/data.table-data.table.R0000644000176200001440000000046514132374245022461 0ustar liggesuserslibrary(data.table) x <- rbindlist(idcol = "path", lapply(stats::setNames(file, file), fread, sep = ",", quote = "", strip.white = FALSE, na.strings = NULL) ) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[payment_type == "UNK", ] e <- x[ , .(mean(tip_amount)), by = payment_type] vroom/inst/bench/all_character-wide/0000755000176200001440000000000014533733452017173 5ustar liggesusersvroom/inst/bench/all_character-wide/vroom-dplyr.R0000644000176200001440000000043414132374245021605 0ustar liggesusers({ library(vroom); library(dplyr) }) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 == "helpless_sheep") e <- group_by(x, X1) %>% summarise(avg_nchar = mean(nchar(X2))) vroom/inst/bench/all_character-wide/input.R0000644000176200001440000000124614132374245020454 0ustar liggesusersargs <- commandArgs(trailingOnly = TRUE) rows <- as.integer(args[[1]]) cols <- as.integer(args[[2]]) output <- args[[3]] set.seed(42) RNGversion("3.5.3") library(vroom) # We want ~ 1000 rows to filter num_levels <- 5 levels <- c("helpless_sheep", gen_name(num_levels - 1)) filt_p <- 1000 / rows # The prob for the rest should just be evenly spaced rest_p <- rep((1 - filt_p) / (num_levels - 1), num_levels - 1) col_types <- stats::setNames( c(list( col_factor(levels = levels, prob = c(filt_p, rest_p))), rep(list(col_character()), cols - 1) ), make.names(seq_len(cols))) data <- gen_tbl(rows, cols, col_types = col_types) vroom_write(data, output, "\t") vroom/inst/bench/all_character-wide/readr-dplyr.R0000644000176200001440000000041014132374245021532 0ustar liggesusers({ library(readr); library(dplyr) }) x <- read_tsv(file, trim_ws = FALSE, quote = "", na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 == "helpless_sheep") e <- group_by(x, X1) %>% summarise(avg_nchar = mean(nchar(X2))) vroom/inst/bench/all_character-wide/vroom-base.R0000644000176200001440000000037714132374245021373 0ustar liggesuserslibrary(vroom) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 == "helpless_sheep", ] e <- tapply(x$X2, x$X1, function(x) mean(nchar(x))) vroom/inst/bench/all_character-wide/vroom_no_altrep-dplyr.R0000644000176200001440000000045214132374245023650 0ustar liggesusers({library(vroom); library(dplyr)}) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = FALSE) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 == "helpless_sheep") e <- group_by(x, X1) %>% summarise(avg_nchar = mean(nchar(X2))) vroom/inst/bench/all_character-wide/read.delim-base.R0000644000176200001440000000036714132374245022234 0ustar liggesusers({}) x <- read.delim(file, quote = "", na.strings = NULL, stringsAsFactors = FALSE) print(head(x, 10)) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 == "helpless_sheep", ] e <- tapply(x$X2, x$X1, function(x) mean(nchar(x))) vroom/inst/bench/all_character-wide/data.table-data.table.R0000644000176200001440000000035714132374245023313 0ustar liggesuserslibrary(data.table) x <- fread(file, sep = "\t", quote = "", strip.white = FALSE, na.strings = NULL) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[X1 == "helpless_sheep", ] e <- x[ , .(mean(nchar(X2))), by = X1] vroom/inst/bench/session_info.R0000755000176200001440000000035414132374245016303 0ustar liggesusers#!/usr/bin/env Rscript vroom::vroom_write( sessioninfo::package_info(c("vroom", "readr", "dplyr", "data.table", "base"), dependencies = FALSE, include_base = TRUE), here::here("inst", "bench", "session_info.tsv"), delim = "\t" ) vroom/inst/bench/all_character-long/0000755000176200001440000000000014533733452017202 5ustar liggesusersvroom/inst/bench/all_character-long/vroom-dplyr.R0000644000176200001440000000043414132374245021614 0ustar liggesusers({ library(vroom); library(dplyr) }) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 == "helpless_sheep") e <- group_by(x, X1) %>% summarise(avg_nchar = mean(nchar(X2))) vroom/inst/bench/all_character-long/input.R0000644000176200001440000000124614132374245020463 0ustar liggesusersargs <- commandArgs(trailingOnly = TRUE) rows <- as.integer(args[[1]]) cols <- as.integer(args[[2]]) output <- args[[3]] set.seed(42) RNGversion("3.5.3") library(vroom) # We want ~ 1000 rows to filter num_levels <- 5 levels <- c("helpless_sheep", gen_name(num_levels - 1)) filt_p <- 1000 / rows # The prob for the rest should just be evenly spaced rest_p <- rep((1 - filt_p) / (num_levels - 1), num_levels - 1) col_types <- stats::setNames( c(list( col_factor(levels = levels, prob = c(filt_p, rest_p))), rep(list(col_character()), cols - 1) ), make.names(seq_len(cols))) data <- gen_tbl(rows, cols, col_types = col_types) vroom_write(data, output, "\t") vroom/inst/bench/all_character-long/readr-dplyr.R0000644000176200001440000000041014132374245021541 0ustar liggesusers({ library(readr); library(dplyr) }) x <- read_tsv(file, trim_ws = FALSE, quote = "", na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 == "helpless_sheep") e <- group_by(x, X1) %>% summarise(avg_nchar = mean(nchar(X2))) vroom/inst/bench/all_character-long/vroom-base.R0000644000176200001440000000037714132374245021402 0ustar liggesuserslibrary(vroom) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 == "helpless_sheep", ] e <- tapply(x$X2, x$X1, function(x) mean(nchar(x))) vroom/inst/bench/all_character-long/vroom_no_altrep-dplyr.R0000644000176200001440000000045214132374245023657 0ustar liggesusers({library(vroom); library(dplyr)}) x <- vroom(file, trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = FALSE) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, X1 == "helpless_sheep") e <- group_by(x, X1) %>% summarise(avg_nchar = mean(nchar(X2))) vroom/inst/bench/all_character-long/read.delim-base.R0000644000176200001440000000036714132374245022243 0ustar liggesusers({}) x <- read.delim(file, quote = "", na.strings = NULL, stringsAsFactors = FALSE) print(head(x, 10)) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$X1 == "helpless_sheep", ] e <- tapply(x$X2, x$X1, function(x) mean(nchar(x))) vroom/inst/bench/all_character-long/data.table-data.table.R0000644000176200001440000000035714132374245023322 0ustar liggesuserslibrary(data.table) x <- fread(file, sep = "\t", quote = "", strip.white = FALSE, na.strings = NULL) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[X1 == "helpless_sheep", ] e <- x[ , .(mean(nchar(X2))), by = X1] vroom/inst/bench/fwf/0000755000176200001440000000000014533733452014243 5ustar liggesusersvroom/inst/bench/fwf/vroom-dplyr.R0000644000176200001440000000036214132374245016655 0ustar liggesusers({ library(vroom); library(dplyr) }) x <- vroom_fwf(file, fields, col_types = types) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, PERSONS == "06") e <- group_by(x, STATE) %>% summarise(avg_TOTPUM5 = mean(TOTPUMA5)) vroom/inst/bench/fwf/readr-dplyr.R0000644000176200001440000000036114132374245016607 0ustar liggesusers({ library(readr); library(dplyr) }) x <- read_fwf(file, fields, col_types = types) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, PERSONS == "06") e <- group_by(x, STATE) %>% summarise(avg_TOTPUM5 = mean(TOTPUMA5)) vroom/inst/bench/fwf/vroom-base.R0000644000176200001440000000031514132374245016433 0ustar liggesuserslibrary(vroom) x <- vroom_fwf(file, fields, col_types = types) print(head(x, 10)) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$PERSONS== "06", ] e <- tapply(x$TOTPUMA5, x$STATE, mean) vroom/inst/bench/fwf/vroom_no_altrep-dplyr.R0000644000176200001440000000040514132374245020716 0ustar liggesusers({library(vroom); library(dplyr)}) x <- vroom_fwf(file, fields, col_types = types, altrep_opts = FALSE) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, PERSONS == "06") e <- group_by(x, STATE) %>% summarise(avg_TOTPUM5 = mean(TOTPUMA5)) vroom/inst/bench/fwf/read.delim-base.R0000644000176200001440000000033414132374245017276 0ustar liggesusers({}) x <- read.fwf(file, widths = fields$width, col.names = fields$col_names) print(head(x, 10)) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$PERSONS== "06", ] e <- tapply(x$TOTPUMA5, x$STATE, mean) vroom/inst/bench/taxi_multiple.tsv0000644000176200001440000001533214132374245017077 0ustar liggesusersreading_package manip_package op type time size max_memory rows cols data.table data.table setup process 0.049356014000000004 19747530143 63974539264 173179759 11 data.table data.table setup real 0.05027127265930176 19747530143 63974539264 173179759 11 data.table data.table read process 365.63006013800003 19747530143 63974539264 173179759 11 data.table data.table read real 95.29237675666809 19747530143 63974539264 173179759 11 data.table data.table print process 0.006093298999985564 19747530143 63974539264 173179759 11 data.table data.table print real 0.006696939468383789 19747530143 63974539264 173179759 11 data.table data.table head process 2.292589999797201e-4 19747530143 63974539264 173179759 11 data.table data.table head real 2.3102760314941406e-4 19747530143 63974539264 173179759 11 data.table data.table tail process 1.8573600004856416e-4 19747530143 63974539264 173179759 11 data.table data.table tail real 1.8715858459472656e-4 19747530143 63974539264 173179759 11 data.table data.table sample process 6.668740000463913e-4 19747530143 63974539264 173179759 11 data.table data.table sample real 6.690025329589844e-4 19747530143 63974539264 173179759 11 data.table data.table filter process 3.5333796889999576 19747530143 63974539264 173179759 11 data.table data.table filter real 1.0772333145141602 19747530143 63974539264 173179759 11 data.table data.table aggregate process 14.88072779800001 19747530143 63974539264 173179759 11 data.table data.table aggregate real 4.706362962722778 19747530143 63974539264 173179759 11 readr dplyr setup process 0.2852784450000001 19747530143 68142870528 173179759 11 readr dplyr setup real 0.2862823009490967 19747530143 68142870528 173179759 11 readr dplyr read process 475.032626874 19747530143 68142870528 173179759 11 readr dplyr read real 475.0412588119507 19747530143 68142870528 173179759 11 readr dplyr print process 0.4224021850000099 19747530143 68142870528 173179759 11 readr dplyr print real 0.836052656173706 19747530143 68142870528 173179759 11 readr dplyr head process 5.290510000008908e-4 19747530143 68142870528 173179759 11 readr dplyr head real 5.30242919921875e-4 19747530143 68142870528 173179759 11 readr dplyr tail process 5.061389999809762e-4 19747530143 68142870528 173179759 11 readr dplyr tail real 5.07354736328125e-4 19747530143 68142870528 173179759 11 readr dplyr sample process 0.014291614999990543 19747530143 68142870528 173179759 11 readr dplyr sample real 0.014293432235717773 19747530143 68142870528 173179759 11 readr dplyr filter process 4.235514479999949 19747530143 68142870528 173179759 11 readr dplyr filter real 4.235594272613525 19747530143 68142870528 173179759 11 readr dplyr aggregate process 13.503164607999963 19747530143 68142870528 173179759 11 readr dplyr aggregate real 13.503409147262573 19747530143 68142870528 173179759 11 vroom base setup process 0.24334018300000015 19747530143 94792810496 173179759 11 vroom base setup real 0.24334383010864258 19747530143 94792810496 173179759 11 vroom base read process 280.08802939099996 19747530143 94792810496 173179759 11 vroom base read real 20.344595670700073 19747530143 94792810496 173179759 11 vroom base print process 2.7922258899999974 19747530143 94792810496 173179759 11 vroom base print real 3.047116756439209 19747530143 94792810496 173179759 11 vroom base head process 2.6394799999707175e-4 19747530143 94792810496 173179759 11 vroom base head real 2.655982971191406e-4 19747530143 94792810496 173179759 11 vroom base tail process 4.8234499996624436e-4 19747530143 94792810496 173179759 11 vroom base tail real 4.837512969970703e-4 19747530143 94792810496 173179759 11 vroom base sample process 2.3426100000278893e-4 19747530143 94792810496 173179759 11 vroom base sample real 2.353191375732422e-4 19747530143 94792810496 173179759 11 vroom base filter process 21.519468974000006 19747530143 94792810496 173179759 11 vroom base filter real 21.5198495388031 19747530143 94792810496 173179759 11 vroom base aggregate process 162.64416488300003 19747530143 94792810496 173179759 11 vroom base aggregate real 142.6223108768463 19747530143 94792810496 173179759 11 vroom dplyr setup process 0.3229789220000001 19747530143 94496784384 173179759 11 vroom dplyr setup real 0.3229827880859375 19747530143 94496784384 173179759 11 vroom dplyr read process 281.055523347 19747530143 94496784384 173179759 11 vroom dplyr read real 20.365679502487183 19747530143 94496784384 173179759 11 vroom dplyr print process 2.542836164999983 19747530143 94496784384 173179759 11 vroom dplyr print real 2.785041570663452 19747530143 94496784384 173179759 11 vroom dplyr head process 9.97338999980002e-4 19747530143 94496784384 173179759 11 vroom dplyr head real 9.989738464355469e-4 19747530143 94496784384 173179759 11 vroom dplyr tail process 4.8055199999907927e-4 19747530143 94496784384 173179759 11 vroom dplyr tail real 4.818439483642578e-4 19747530143 94496784384 173179759 11 vroom dplyr sample process 0.01243754600000102 19747530143 94496784384 173179759 11 vroom dplyr sample real 0.012439489364624023 19747530143 94496784384 173179759 11 vroom dplyr filter process 23.858187990000033 19747530143 94496784384 173179759 11 vroom dplyr filter real 23.858795642852783 19747530143 94496784384 173179759 11 vroom dplyr aggregate process 65.63478174599999 19747530143 94496784384 173179759 11 vroom dplyr aggregate real 65.63620686531067 19747530143 94496784384 173179759 11 vroom_no_altrep dplyr setup process 0.317607953 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr setup real 0.31838297843933105 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr read process 607.194956447 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr read real 232.3293113708496 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr print process 2.005394704000082 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr print real 2.162087917327881 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr head process 0.0010485940000535265 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr head real 0.0010504722595214844 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr tail process 4.88735000089946e-4 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr tail real 4.901885986328125e-4 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr sample process 0.0136381549999669 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr sample real 0.013639688491821289 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr filter process 10.491835902000048 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr filter real 10.491984844207764 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr aggregate process 7.2028114820000155 19747530143 67751387136 173179759 11 vroom_no_altrep dplyr aggregate real 7.202939510345459 19747530143 67751387136 173179759 11 vroom/inst/bench/taxi/0000755000176200001440000000000014533733452014426 5ustar liggesusersvroom/inst/bench/taxi/vroom-dplyr.R0000644000176200001440000000051214132374245017035 0ustar liggesusers({ library(vroom); library(dplyr) }) x <- vroom(file, col_types = c(pickup_datetime = "c"), trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, payment_type == "UNK") e <- group_by(x, payment_type) %>% summarise(avg_tip = mean(tip_amount)) vroom/inst/bench/taxi/readr-dplyr.R0000644000176200001440000000046614132374245017000 0ustar liggesusers({ library(readr); library(dplyr) }) x <- read_csv(file, col_types = c(pickup_datetime = "c"), quote = "", trim_ws = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, payment_type == "UNK") e <- group_by(x, payment_type) %>% summarise(avg_tip = mean(tip_amount)) vroom/inst/bench/taxi/vroom-base.R0000644000176200001440000000044014132374245016615 0ustar liggesuserslibrary(vroom) x <- vroom(file, col_types = c(pickup_datetime = "c"), trim_ws = FALSE, quote = "", escape_double = FALSE, na = character()) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$payment_type == "UNK", ] e <- tapply(x$tip_amount, x$payment_type, mean) vroom/inst/bench/taxi/vroom_no_altrep-dplyr.R0000644000176200001440000000053014132374245021100 0ustar liggesusers({library(vroom); library(dplyr)}) x <- vroom(file, col_types = c(pickup_datetime = "c"), trim_ws = FALSE, quote = "", escape_double = FALSE, na = character(), altrep = FALSE) print(x) a <- head(x) b <- tail(x) c <- sample_n(x, 100) d <- filter(x, payment_type == "UNK") e <- group_by(x, payment_type) %>% summarise(avg_tip = mean(tip_amount)) vroom/inst/bench/taxi/read.delim-base.R0000644000176200001440000000037514132374245017466 0ustar liggesusers({}) x <- read.delim(file, sep = ",", quote = "", na.strings = NULL, stringsAsFactors = FALSE) print(head(x, 10)) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[x$payment_type == "UNK", ] e <- tapply(x$tip_amount, x$payment_type, mean) vroom/inst/bench/taxi/data.table-data.table.R0000644000176200001440000000037014132374245020541 0ustar liggesuserslibrary(data.table) x <- fread(file, sep = ",", quote = "", strip.white = FALSE, na.strings = NULL) print(x) a <- head(x) b <- tail(x) c <- x[sample(NROW(x), 100), ] d <- x[payment_type == "UNK", ] e <- x[ , .(mean(tip_amount)), by = payment_type] vroom/inst/bench/summarise-benchmarks.R0000755000176200001440000000354314132374245017730 0ustar liggesuserslibrary(vroom) library(dplyr) library(fs) library(purrr) library(tidyr) summarise_dir <- function(dir, desc) { out_file <- path(path_dir(dir), path_ext_set(path_file(dir), "tsv")) col_types <- cols( exprs = col_character(), process = col_character(), real = col_character(), size = col_double(), rows = col_double(), cols = col_double() ) dir_ls(dir, glob = "*tsv") %>% discard(~endsWith(.x, "input.tsv")) %>% vroom(id = "path", col_types = col_types) %>% mutate(path = path_ext_remove(path_file(path))) %>% group_by(path) %>% mutate(op = desc) %>% separate(path, c("reading_package", "manip_package"), "-") %>% pivot_longer(., cols = c(process, real), names_to = "type", values_to = "time") %>% select(reading_package, manip_package, op, type, time, size, max_memory, rows, cols) %>% vroom_write(out_file, delim = "\t") } summarise_dir(here::here("inst/bench/all_numeric-long"), c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate")) summarise_dir(here::here("inst/bench/all_numeric-wide"), c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate")) summarise_dir(here::here("inst/bench/all_character-long"), c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate")) summarise_dir(here::here("inst/bench/all_character-wide"), c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate")) summarise_dir(here::here("inst/bench/taxi"), c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate")) summarise_dir(here::here("inst/bench/taxi_multiple"), c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate")) summarise_dir(here::here("inst/bench/taxi_writing"), c("setup", "writing")) summarise_dir(here::here("inst/bench/fwf"), c("setup", "read", "print", "head", "tail", "sample", "filter", "aggregate")) vroom/inst/bench/script.sh0000755000176200001440000000400714132374245015321 0ustar liggesusers#!/bin/bash # need to run download-data.sh before this script # don't forget to set your GITHUB_PAT before running this script [ -n "${GITHUB_PAT}" ] || { echo >&2 "Set GITHUB_PAT first!"; exit 1; } # Install R runtime prereqs sudo apt-get update -qq && \ DEBIAN_FRONTEND=noninteractive sudo apt-get install -y \ apt-transport-https \ build-essential \ curl \ gfortran \ libatlas-base-dev \ libbz2-dev \ libcairo2 \ libcurl4-openssl-dev \ libicu-dev \ liblzma-dev \ libpango-1.0-0 \ libpangocairo-1.0-0 \ libpcre3-dev \ libssl-dev \ libssh2-1-dev \ libtcl8.6 \ libtiff5 \ libtk8.6 \ libx11-6 \ libxml2-dev libxt6 \ locales \ sudo \ tzdata \ wget \ zlib1g-dev && \ sudo rm -rf /var/lib/apt/lists/* OS_IDENTIFIER=ubuntu-1804 R_VERSION=3.6.0 # download precompiled R binary and install wget -O R-${R_VERSION}.tar.gz https://cdn.rstudio.com/r/${OS_IDENTIFIER}/R-${R_VERSION}-${OS_IDENTIFIER}.tar.gz sudo mkdir -p /opt/R sudo chown ubuntu /opt/R tar zx -C /opt/R -f ./R-${R_VERSION}.tar.gz rm R-${R_VERSION}.tar.gz export PATH=/opt/R/${R_VERSION}/bin:$PATH # clone vroom git clone https://jimhester:${GITHUB_PAT}/github.com/r-lib/vroom.git # download and extract data vroom/inst/bench/download-data.sh & cd vroom # set common Rprofile options echo "options(repos = 'https://cran.rstudio.org', Ncpus = 16)" > ~/.Rprofile # install R dependencies Rscript -e 'install.packages("remotes")' \ -e 'remotes::install_local(dependencies = TRUE)' # install additional packages for benchmarking Rscript -e 'remotes::install_cran(c("data.table", "callr", "here", "sessioninfo"))' sudo apt-get install -y pigz zstd # wait for download job to finish wait make bench PKG_VERSION=$(perl -ne 'print $1 if /Version: (.*)/' DESCRIPTION) git checkout -b bench/R-${R_VERSION}_vroom-${PKG_VERSION} git config --global user.email "james.f.hester@gmail.com" git config --global user.name "Jim Hester" git commit -m 'Update benchmarks' inst/bench/*tsv vroom/inst/words/0000755000176200001440000000000014132374245013534 5ustar liggesusersvroom/inst/words/animals.txt0000644000176200001440000000232114132374245015717 0ustar liggesusersaardvark addax alligator alpaca anteater antelope aoudad ape argali armadillo baboon badger basilisk bat bear beaver bee beetle bighorn bird bison boar budgerigar buffalo bull bunny burro camel canary capybara cat chameleon chamois cheetah chicken chimpanzee chinchilla chipmunk civet coati colt cougar cow coyote crab crocodile crow deer devil dingo doe dog donkey dormouse dromedary dugong eagle eland elephant elk ermine ewe fawn ferret finch fish fox frog gazelle gemsbok giraffe gnu goat gopher gorilla guanaco hamster hare hartebeest hedgehog hippopotamus hog horse hyena ibex iguana impala jackal jaguar jerboa kangaroo kitten koala lamb lemur leopard lion lizard llama lovebird lynx mandrill mare marmoset marten meerkat mink mole mongoose monkey monster moose mouse mule musk-ox muskrat mustang newt ocelot octopus okapi opossum orangutan oryx otter owl ox panda panther parakeet parrot peccary pig platypus pony porcupine porpoise pronghorn puma puppy quagga rabbit raccoon ram rat reindeer rhinoceros rooster salamander seal sheep shrew skunk sloth snake springbok squirrel stallion starfish steer tapir tiger toad turtle vicuna walrus warthog waterbuck weasel whale wildcat wolf wolverine wombat woodchuck yak zebra zebu vroom/inst/words/adjectives.txt0000644000176200001440000000271214132374245016420 0ustar liggesusersable abundant adorable agreeable alive ancient angry bad beautiful better bewildered big bitter black blue boiling brave breeze brief broad broken bumpy calm careful chilly chubby clean clever clumsy cold colossal cooing cool creepy crooked cuddly curly curved damaged damp dead deafening deep defeated delicious delightful different dirty drab dry dusty eager early easy elegant embarrassed empty faint faithful famous fancy fast fat few fierce filthy first flaky flat fluffy freezing fresh full gentle gifted gigantic glamorous good gray greasy great green grumpy handsome happy heavy helpful helpless high hissing hollow hot huge icy immense important inexpensive itchy jealous jolly juicy kind large last late lazy light little lively long loose loud low magnificent mammoth many massive melodic melted miniature modern mushy mysterious narrow nervous new next nice noisy numerous nutritious obedient obnoxious odd old orange other own panicky petite plain powerful prickly proud public puny purple purring quaint quick quiet rainy rapid raspy red relieved repulsive rich right rotten round salty same scary scrawny screeching shallow short shy silly skinny slow small sparkling sparse square steep sticky straight strong substantial sweet swift tall tart tasteless teeny tender thankful thoughtless thundering tiny ugliest uneven uninterested unsightly uptight vast victorious voiceless warm weak wet whispering white wide witty wooden worried wrong yellow young yummy zealous vroom/inst/WORDLIST0000644000176200001440000000126214506416746013601 0ustar liggesusersALTREP Altrep Altrepisode BCP BOM CMD CRAN's Codecov Datetime Dowle EBS FranΓ§ois Gabe Grisu Kalibera KiB Lifecycle Microdata NUL ORCID PBC POSIXct Parsers Preprocess RStudio RStudio's RStudio’s Rcpp Rdatatable Romain TSV Tierney UBSAN UNK UseR Zstandard afl backslashed behaviour benchmarking bool bz bzip centric cpp csv datetime datetimes dbi de delim deterministically dplyr durations extdata fread funder fwf gcc github grisu gz gzip gzipped https lbzip libc libstdc macOS mandreyel mio mis natively nycflights parsers pigz pixz purrr rchk readr readr's recognised relicensing stringi testthat tibble tidyverse tokenizer tsv tz tzdb unicode unterminated vCPUs vroom's xlarge xz xzip