diffobj/0000755000176200001440000000000013466336133011665 5ustar liggesusersdiffobj/inst/0000755000176200001440000000000013442554134012637 5ustar liggesusersdiffobj/inst/css/0000755000176200001440000000000013420351310013412 5ustar liggesusersdiffobj/inst/css/diffobj.css0000755000176200001440000001172113420351310015534 0ustar liggesusers/* Structural CSS ------------------------------------------------------------*/ /* * TBD whether we want a more fully table like structure; some of the visual * cues provided by the current set-up are useful (line wraps, etc.) */ DIV.diffobj-container PRE.diffobj-content { white-space: pre-wrap; margin: 0; } DIV.diffobj-container DIV.diffobj-row { width: 100%; font-family: monospace; display: table; table-layout: fixed; } DIV.diffobj-container DIV.diffobj-line { width: auto; display: table-cell; overflow: hidden; } DIV.diffobj-container DIV.diffobj-line>DIV { width: 100%; display: table; table-layout: auto; } DIV.diffobj-container DIV.diffobj-line.banner>DIV { display: table; table-layout: auto; /* set to fixed in JS */ } DIV.diffobj-container DIV.diffobj-text { display: table-cell; width: 100%; } DIV.diffobj-container DIV.diffobj-gutter { display: table-cell; padding: 0 0.2em; } DIV.diffobj-container DIV.diffobj-gutter DIV { display: table-cell; } #diffobj_content_meta DIV.diffobj-container DIV.diffobj-row { width: auto; } #diffobj_banner_meta DIV.diffobj-container DIV.diffobj-line.banner>DIV { table-layout: auto; } #diffobj_outer { overflow: hidden; } /* Summary -------------------------------------------------------------------*/ DIV.diffobj-container DIV.diffobj-summary DIV.map { word-wrap: break-word; padding-left: 1em; } DIV.diffobj-container DIV.diffobj-summary DIV.detail { padding-left: 1em; } /* Common elements -----------------------------------------------------------*/ DIV.diffobj-container DIV.diffobj-line.banner { font-size: 1.2em; font-weight: bold; overflow: hidden; } /* truncate banners */ DIV.diffobj-container DIV.diffobj-line.banner DIV.diffobj-text DIV{ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 100%; /* need to compute and set in JS */ } DIV.diffobj-container DIV.diffobj-gutter, DIV.diffobj-container DIV.diffobj-guide, DIV.diffobj-container DIV.diffobj-fill, DIV.diffobj-container DIV.context_sep, DIV.diffobj-container SPAN.diffobj-trim { color: #999; } DIV.diffobj-container DIV.diffobj-header { font-size: 1.1em; } DIV.diffobj-container DIV.diffobj-text>DIV.diffobj-match, DIV.diffobj-container DIV.diffobj-text>DIV.diffobj-guide { background-color: #ffffff; } DIV.diffobj-container DIV.diffobj-text>DIV.diffobj-fill { background-color: transparent; } DIV.diffobj-container DIV.diffobj-text>DIV { padding-right: 3px; } DIV.diffobj-container DIV.diffobj-text>DIV { border-left: 1px solid #888888; } DIV.diffobj-container DIV.diffobj-line { background-color: #eeeeee; } DIV.diffobj-container DIV.diffobj-text>DIV, DIV.diffobj-container DIV.diffobj-header { padding-left: 0.5em; } DIV.diffobj-container DIV.diffobj-line>DIV.diffobj-match, DIV.diffobj-container DIV.diffobj-line>DIV.diffobj-fill, DIV.diffobj-container DIV.diffobj-line>DIV.diffobj-guide { border-left: 1px solid #888888; } /* github inspired color scheme - default ------------------------------------*/ DIV.diffobj-container.light.rgb SPAN.diffobj-word.insert, DIV.diffobj-container.light.rgb DIV.diffobj-line>DIV.insert { background-color: #a6f3a6; } DIV.diffobj-container.light.rgb SPAN.diffobj-word.delete, DIV.diffobj-container.light.rgb DIV.diffobj-line>DIV.delete { background-color: #f8c2c2; } DIV.diffobj-container.light.rgb DIV.diffobj-text>DIV.insert { background-color: #efffef; } DIV.diffobj-container.light.rgb DIV.diffobj-text>DIV.insert, DIV.diffobj-container.light.rgb DIV.diffobj-line>DIV.insert { border-left: 1px solid #33bb33; } DIV.diffobj-container.light.rgb DIV.diffobj-text>DIV.delete { background-color: #ffefef; } DIV.diffobj-container.light.rgb DIV.diffobj-text>DIV.delete, DIV.diffobj-container.light.rgb DIV.diffobj-line>DIV.delete { border-left: 1px solid #cc6666; } DIV.diffobj-container.light.rgb DIV.diffobj-header { background-color: #e0e6fa; border-left: 1px solid #9894b6; } /* Yellow Blue variation -----------------------------------------------------*/ DIV.diffobj-container.light.yb SPAN.diffobj-word.insert, DIV.diffobj-container.light.yb DIV.diffobj-line>DIV.insert { background-color: #c0cfff; } DIV.diffobj-container.light.yb SPAN.diffobj-word.delete, DIV.diffobj-container.light.yb DIV.diffobj-line>DIV.delete { background-color: #e7e780; } DIV.diffobj-container.light.yb DIV.diffobj-text>DIV.insert { background-color: #efefff; } DIV.diffobj-container.light.yb DIV.diffobj-text>DIV.insert, DIV.diffobj-container.light.yb DIV.diffobj-line>DIV.insert { border-left: 1px solid #3333bb; } DIV.diffobj-container.light.yb DIV.diffobj-text>DIV.delete { background-color: #fefee5; } DIV.diffobj-container.light.yb DIV.diffobj-text>DIV.delete, DIV.diffobj-container.light.yb DIV.diffobj-line>DIV.delete { border-left: 1px solid #aaaa55; } DIV.diffobj-container.light.yb DIV.diffobj-header { background-color: #afafaf; border-left: 1px solid #e3e3e3; color: #e9e9e9; } DIV.diffobj-container.light.yb DIV.diffobj-line { background-color: #eeeeee; } diffobj/inst/script/0000755000176200001440000000000013014331136014131 5ustar liggesusersdiffobj/inst/script/diffobj.js0000755000176200001440000001322413014331136016077 0ustar liggesusers// diffobj - Compare R Objects with a Diff // Copyright (C) 2016 Brodie Gaslam // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Go to for a copy of the license. /* * Resizes diff by changing font-size using a hidden row of sample output as * a reference * * NOTE: this code is intended to be loaded after the HTML has been rendered * and is assumed to be the only JS on the page. It should only be included * as part of output when in "page" mode and should not be embedded in other * content. For that, use the HTML/CSS only outputs. */ var meta = document.getElementById("diffobj_meta"); var meta_cont = document.getElementById("diffobj_content_meta"); var meta_banner = document.getElementById("diffobj_banner_meta"); var content = document.getElementById("diffobj_content"); var outer = document.getElementById("diffobj_outer"); if( meta == null || content == null || outer == null || meta_cont == null || meta_banner == null ) throw new Error("Unable to find meta and content; contact maintainer."); var row = meta_cont.getElementsByClassName("row"); if(row.length != 1) throw new Error("Unexpected row struct in meta block; contact maintainer."); var lines = meta_cont.getElementsByClassName("line"); if(lines.length != 1 && lines.length != 2) throw new Error("Unexpected lines in meta block; contact maintainer."); var meta_bnr_gutter = document.querySelector("#diffobj_banner_meta .line .gutter"); var meta_bnr_delete = document.querySelector("#diffobj_banner_meta .line .text>.delete"); var meta_bnr_text = document.querySelector("#diffobj_banner_meta .line .text"); var bnr_gutters = document.querySelectorAll("#diffobj_content .line.banner .gutter"); var bnr_text_div = document.querySelectorAll("#diffobj_content .line.banner .text>DIV"); if( meta_bnr_gutter == null || meta_bnr_delete == null || bnr_gutters.length != 2 || bnr_text_div.length != 2 ) throw new Error("Unable to get meta banner objects") // Set the banners to 'fixed'; need to be in auto by default for(i = 0; i < 2; i++) bnr_text_div[i].style.tableLayout = "fixed"; // - Set Min Width ------------------------------------------------------------- // Makes sure that we don't wrap under "native" width // Note we need to pad because scrollWidth appears to truncate floats to int meta.style.display = "block"; var min_width = 0; for(i = 0; i < lines.length; i++) min_width += lines[i].scrollWidth + 1; meta.style.display = "none"; content.style.minWidth = min_width + "px"; function resize_diff_out(scale) { // - Get object refs --------------------------------------------------------- // - Get Sizes --------------------------------------------------------------- meta.style.display = "block"; // The getComputedStyle business won't work on IE9 or lower; need to detect // and implement work-around var b_t, b_d_w, b_d_o, b_g; b_g = parseFloat(window.getComputedStyle(meta_bnr_gutter).width); b_d_o = meta_bnr_delete.offsetWidth; b_d_w = parseFloat(window.getComputedStyle(meta_bnr_delete).width); b_t = parseFloat(window.getComputedStyle(meta_bnr_text).width); meta.style.display = "none"; // - Set Sizes --------------------------------------------------------------- for(i = 0; i < 2; i++) { bnr_gutters[i].style.width = b_g + "px"; // for some reason table fixed width computation doesn't properly account // for padding and lines bnr_text_div[i].style.width = b_t - b_d_o + b_d_w + "px"; } var w = document.body.clientWidth; var scale_size = w / min_width; if(scale_size < 1) { if(scale) { content.style.transform = "scale(" + scale_size + ")"; content.style.transformOrigin = "top left"; content.style.webkitTransform = "scale(" + scale_size + ")"; content.style.webkitTransformOrigin = "top left"; content.style.msTransform = "scale(" + scale_size + ")"; content.style.msTransformOrigin = "top left"; content.style.MozTransform = "scale(" + scale_size + ")"; content.style.MozTransformOrigin = "top left"; content.style.oTransform = "scale(" + scale_size + ")"; content.style.oTransformOrigin = "top left"; var cont_rec_h = content.getBoundingClientRect().height; if(cont_rec_h) { outer.style.height = cont_rec_h + "px"; } } var cont_rec_w = content.getBoundingClientRect().width; if(cont_rec_w) { outer.style.width = cont_rec_w + "px"; } } else { content.style.transform = "none"; content.style.MozTransform = "none"; content.style.webkitTransform = "none"; content.style.msTransform = "none"; content.style.oTransform = "none"; outer.style.height = "auto"; outer.style.width = "auto"; } }; /* * Manage resize timeout based on how large the object is */ var out_rows = content.getElementsByClassName("row").length; var timeout_time; if(out_rows < 100) { timeout_time = 25; } else { timeout_time = Math.min(25 + (out_rows - 100) / 4, 500) } var timeout; function resize_window(f, scale) { clearTimeout(timeout); timeout = setTimeout(f, timeout_time, scale); } function resize_diff_out_scale() {resize_window(resize_diff_out, true);} function resize_diff_out_no_scale() {resize_window(resize_diff_out, false);} diffobj/inst/COPYRIGHTS0000755000176200001440000000127313442554134014263 0ustar liggesusersThe C implementation of the Myers' Diff Algorithm with Linear Space Refinement was originally written by Michael B. Allen with the following license and copyright: diff - compute a shortest edit script (SES) given two sequences Copyright (C) 2004 Michael B. Allen License: MIT The original source code is available at: http://www.ioplex.com/~miallen/libmba/dl/libmba-0.9.1.tar.gz The adapted and heavily modified code is available in src/diff.c, src/diff.h. See those files for additional details. This package is released under the GPL (>= 2) or greater license: diffobj - Compare R Objects with a Diff Copyright (C) 2019 Brodie Gaslam License: GPL (>= 2) diffobj/inst/doc/0000755000176200001440000000000013420351310013367 5ustar liggesusersdiffobj/inst/doc/embed.R0000644000176200001440000000360213466146505014611 0ustar liggesusers## ----echo=FALSE---------------------------------------------------------- library(diffobj) ## ----results='asis'------------------------------------------------------ cat( as.character( diffPrint( 1:5, 2:6, format="html", style=list(html.output="diff.w.style") ) ) ) ## ----results='asis'------------------------------------------------------ cat( as.character( diffPrint( 1:5, 2:6, format="html", style=list(html.output="diff.only") # notice this changed ) ) ) ## ----eval=FALSE---------------------------------------------------------- # options( # diffobj.format="html", # diffobj.style=list(html.output="diff.only") # ) ## ----echo=FALSE---------------------------------------------------------- old.opts <- options( diffobj.format="html", diffobj.style=list(html.output="diff.only") ) ## ----results='asis'------------------------------------------------------ cat(as.character(diffPrint(1:5, 2:6))) ## ----echo=FALSE---------------------------------------------------------- options(old.opts) ## ---- eval=FALSE--------------------------------------------------------- # library(shiny) # shinyApp( # ui=fluidPage(htmlOutput('diffobj_element')), # server=function(input, output) { # output$diffobj_element <- renderUI({ # HTML( # as.character( # diffPrint( # 1:5, 2:6, # format="html", # style=list(html.output="diff.w.style") # ) ) )}) } ) ## ---- eval=FALSE--------------------------------------------------------- # options( # diffobj.format="html", # diffobj.style=list(html.output="diff.only") # ) # shinyApp( # ui=fluidPage( # includeCSS(diffobj_css()), # htmlOutput('diffobj_element') # ), # server=function(input, output) { # output$diffobj_element <- renderUI({ # HTML(as.character(diffPrint(1:5, 2:6,))) # }) } ) diffobj/inst/doc/diffobj.R0000644000176200001440000000546413466146503015146 0ustar liggesusers## ---- echo=FALSE--------------------------------------------------------- library(diffobj) old.opt <- options( diffobj.disp.width=80, diffobj.pager="off", diffobj.format="html" ) ## ---- results="asis"----------------------------------------------------- a <- b <- matrix(1:100, ncol=2) a <- a[-20,] b <- b[-45,] b[c(18, 44)] <- 999 diffPrint(target=a, current=b) ## ---- results="asis", echo=FALSE----------------------------------------- diffPrint(target=a, current=b)[1] ## ---- results="asis", echo=FALSE----------------------------------------- diffPrint(target=a, current=b)[2:10] ## ---- results="asis", echo=FALSE----------------------------------------- diffPrint(target=a, current=b)[3] ## ---- results="asis", echo=FALSE----------------------------------------- diffPrint(target=a, current=b)[6:9] ## ---- results="asis", echo=FALSE----------------------------------------- diffPrint(target=a, current=b)[8:9] ## ---- results="asis"----------------------------------------------------- state.abb2 <- state.abb[-16] state.abb2[37] <- "Pennsylvania" diffPrint(state.abb, state.abb2) ## ---- results="asis"----------------------------------------------------- mdl1 <- lm(Sepal.Length ~ Sepal.Width, iris) mdl2 <- lm(Sepal.Length ~ Sepal.Width + Species, iris) diffStr(mdl1$qr, mdl2$qr, line.limit=15) ## ---- results="asis"----------------------------------------------------- diffChr(letters[1:3], c("a", "B", "c")) ## ---- eval=FALSE--------------------------------------------------------- # x <- diffPrint(letters, LETTERS) # x # or equivalently: `show(x)` ## ---- results="asis"----------------------------------------------------- summary(diffStr(mdl1, mdl2)) ## ---- results="asis", eval=FALSE----------------------------------------- # x <- y <- letters[24:26] # y[2] <- "GREMLINS" # diffChr(x, y) ## ---- results="asis", echo=FALSE----------------------------------------- x <- y <- letters[24:26] y[2] <- "GREMLINS" diffChr(x, y, mode="sidebyside") ## ---- results="asis", echo=FALSE----------------------------------------- x <- y <- letters[24:26] y[2] <- "GREMLINS" diffChr(x, y, mode="unified") ## ---- results="asis", echo=FALSE----------------------------------------- x <- y <- letters[24:26] y[2] <- "GREMLINS" diffChr(x, y, mode="context") ## ---- results="asis"----------------------------------------------------- diffChr(x, y, color.mode="rgb") ## ---- eval=FALSE--------------------------------------------------------- # v1 <- 1:5e4 # v2 <- v1[-sample(v1, 100)] # diffChr(v1, v2, word.diff=FALSE) ## ---- eval=FALSE--------------------------------------------------------- # diffPrint(v1, v2) ## ------------------------------------------------------------------------ ses(letters[1:5], letters[c(2:3, 5)]) ## ---- echo=FALSE--------------------------------------------------------- options(old.opt) diffobj/inst/doc/metacomp.Rmd0000755000176200001440000000751613420351310015654 0ustar liggesusers--- title: "Mean Relative Indifference" subtitle: "A Comparison of R Comparison Functions" author: "Brodie Gaslam" output: rmarkdown::html_vignette: toc: true css: - !expr diffobj::diffobj_css() - styles.css vignette: > %\VignetteIndexEntry{Comparison Functions} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ```{r, echo=FALSE} library(diffobj) old.opt <- options( diffobj.disp.width=80, diffobj.pager="off", diffobj.format="html" ) ``` Most R object comparison functions are good at telling you that objects are different, but less so at conveying _how_ they are different. I wrote `diffobj` to provide an "aha, that's how they are different" comparison. In this vignette I will compare `diffPrint` to `all.equal` and to `testthat::compare`. Disclaimer: I picked the examples here to showcase `diffobj` capabilities, not to carry out a fair and balanced comparison of these comparison functions. Nonetheless, I hope you will find the examples representative of common situations where comparison of R objects is useful. ## Vectors I defined four pairs of numeric vectors for us to compare. I purposefully hid the variable definitions to simulate a comparison of unknown objects. ```{r echo=FALSE} A1 <- 1:10 B1 <- c(1:9, 11) A2 <- 1:20 B2 <- c(20, 1:19) A3 <- 1:20 B3 <- c(20:21, 1:19) ``` ### Stage 1 ```{r} all.equal(A1, B1) ``` The objects are different... At this point I would normally print both `A1` and `B1` to try to figure out how that difference came about since the "mean relative difference" is unhelpful. ```{r} testthat::compare(A1, B1) ``` `testthat::compare` does a better job, but I still feel the need to look at `A1` and `B1`. ```{r, results="asis"} diffPrint(A1, B1) ``` Aha, that's how they are different! ### Stage 2 Let's up the difficulty a little bit: ```{r} testthat::compare(A2, B2) ``` If you look closely you will see that despite a reported 20/20 differences, the two vectors are actually similar, at least in the part visible part of the output. With `diffPrint` it is obvious that `B2` and is the same as `A2`, except that the last value has been moved to the first position: ```{r, results="asis"} diffPrint(A2, B2) ``` ### Stage 3 `testthat::compare` throws in the towel as soon as lengths are unequal: ```{r} testthat::compare(A3, B3) ``` `all.equal` does the same. `diffPrint` is unfazed: ```{r, results="asis"} diffPrint(A3, B3) ``` `diffPrint` also produces useful output for largish vectors: ```{r, results="asis"} A4 <- 1:1e4 B4 <- c(1e4 + 1, A4[-c(4:7, 9e3)]) diffPrint(A4, B4) ``` Do note that the comparison algorithm scales with the square of the number of _differences_, so very large and different vectors will be slow to process. ## Objects R Core and package authors put substantial effort into `print` and `show` methods. `diffPrint` takes advantage of this. Compare: ```{r, R.options=list(max.print=5)} all.equal(iris, iris[-60,]) ``` to: ```{r, results="asis"} diffPrint(iris, iris[-60,]) ``` And: ```{r, R.options=list(max.print=5)} all.equal(lm(hp ~ disp, mtcars), lm(hp ~ cyl, mtcars)) ``` to: ```{r, results="asis"} diffPrint(lm(hp ~ disp, mtcars), lm(hp ~ cyl, mtcars)) ``` In these examples I limited `all.equal` output to five lines for the sake of brevity. Also, since `testthat::compare` reverts to `all.equal` output with more complex objects I omit it from this comparison. ## Parting Thoughts Another candidate comparison function is `compare::compare`. I omitted it from this vignette because it focuses more on similarities than on differences. Additionally, `testthat::compare` and `compare::compare` `print` methods conflict so they cannot be used together. For a more thorough exploration of `diffobj` methods and their features please see the [primary `diffobj` vignette](diffobj.html). ```{r, echo=FALSE} options(old.opt) ``` diffobj/inst/doc/embed.Rmd0000755000176200001440000001013213420351310015107 0ustar liggesusers--- title: "Embed Diffs in R Markdown Or Shiny" author: "Brodie Gaslam" output: rmarkdown::html_vignette: toc: true css: - !expr diffobj::diffobj_css() - styles.css vignette: > %\VignetteIndexEntry{Embed Diffs in R Markdown Or Shiny} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ```{r echo=FALSE} library(diffobj) ``` ## Rmarkdown ### Basic Requirements Any R chunks that produce diffs should include the `results='asis'` option, e.g.: ```` ```{r, comment="", results="asis"}`r ''` # R code here ``` ```` ### Embedded CSS This is what a basic code block should look like: ```` ```{r, comment="", results="asis"}`r ''` cat( # output to screen as.character( # convert to diff to character vector diffPrint( # run diff 1:5, 2:6, format="html", # specify html output style=list( html.output="diff.w.style" # configure html style ) ) ) ) ``` ```` Here we use this same code as an actual markdown R code block: ```{r results='asis'} cat( as.character( diffPrint( 1:5, 2:6, format="html", style=list(html.output="diff.w.style") ) ) ) ``` This is an ugly implementation because it produces illegal HTML. The styles are directly embedded in the body of the document, outside of the HEAD tags. Although this is illegal HTML, it seems to work in most browsers. Another problem is that every diff you use in your document will inject the same CSS code over and over. ### External CSS A better option is to provide the CSS directly by modifying the `output` portion of the [YAML header](https://bookdown.org/yihui/rmarkdown/r-package-vignette.html): ``` --- output: rmarkdown::html_vignette: toc: true css: !expr diffobj::diffobj_css() --- ``` In reality you will probably want to specify multiple CSS files, including the original `rmarkdown` one: ``` --- output: rmarkdown::html_vignette: toc: true css: - !expr diffobj::diffobj_css() - !expr system.file("rmarkdown", "templates", "html_vignette", "resources", "vignette.css", package = "rmarkdown") --- ``` Once you set this up then you can use: ```{r results='asis'} cat( as.character( diffPrint( 1:5, 2:6, format="html", style=list(html.output="diff.only") # notice this changed ) ) ) ``` This will omit the CSS, but since we include it via the YAML everything should work as expected. ### Use Options Almost all `diffobj` parameters can be specified via options: ```{r eval=FALSE} options( diffobj.format="html", diffobj.style=list(html.output="diff.only") ) ``` ```{r echo=FALSE} old.opts <- options( diffobj.format="html", diffobj.style=list(html.output="diff.only") ) ``` Then you can just run the diff as normal: ```{r results='asis'} cat(as.character(diffPrint(1:5, 2:6))) ``` ```{r echo=FALSE} options(old.opts) ``` ## Shiny Shiny usage is very similar to `rmarkdown`. In both cases we want to get `diffobj` to produce HTML output to embed in our document. If we are willing to embed the CSS with each diff, we can use: ```{r, eval=FALSE} library(shiny) shinyApp( ui=fluidPage(htmlOutput('diffobj_element')), server=function(input, output) { output$diffobj_element <- renderUI({ HTML( as.character( diffPrint( 1:5, 2:6, format="html", style=list(html.output="diff.w.style") ) ) )}) } ) ``` If we have many diffs, it may be preferable to use options and external style sheet: ```{r, eval=FALSE} options( diffobj.format="html", diffobj.style=list(html.output="diff.only") ) shinyApp( ui=fluidPage( includeCSS(diffobj_css()), htmlOutput('diffobj_element') ), server=function(input, output) { output$diffobj_element <- renderUI({ HTML(as.character(diffPrint(1:5, 2:6,))) }) } ) ``` Unlike with our [rmarkdown example](#external-css), this CSS is included in the body of the HTML document instead of in the header, so it is technically illegal like in our [embedded css example](#embedded-css). diffobj/inst/doc/embed.html0000644000176200001440000007713013466146505015363 0ustar liggesusers Embed Diffs in R Markdown Or Shiny

Embed Diffs in R Markdown Or Shiny

Brodie Gaslam

Rmarkdown

Basic Requirements

Any R chunks that produce diffs should include the results='asis' option, e.g.:

```{r, comment="", results="asis"}
# R code here
```

Embedded CSS

This is what a basic code block should look like:

```{r, comment="", results="asis"}
cat(                                 # output to screen
  as.character(                      # convert to diff to character vector
    diffPrint(                       # run diff
      1:5, 2:6,
      format="html",                 # specify html output
      style=list(
        html.output="diff.w.style"   # configure html style
      )
) ) )
```

Here we use this same code as an actual markdown R code block:

@@ 1 @@
@@ 1 @@
<
[1] 1 2 3 4 5
>
[1] 2 3 4 5 6

This is an ugly implementation because it produces illegal HTML. The styles are directly embedded in the body of the document, outside of the HEAD tags. Although this is illegal HTML, it seems to work in most browsers. Another problem is that every diff you use in your document will inject the same CSS code over and over.

External CSS

A better option is to provide the CSS directly by modifying the output portion of the YAML header:

---
output:
    rmarkdown::html_vignette:
        toc: true
        css: !expr diffobj::diffobj_css()
---

In reality you will probably want to specify multiple CSS files, including the original rmarkdown one:

---
output:
    rmarkdown::html_vignette:
        toc: true
        css:
          - !expr diffobj::diffobj_css()
          - !expr system.file("rmarkdown", "templates", "html_vignette", "resources", "vignette.css", package = "rmarkdown")
---

Once you set this up then you can use:

@@ 1 @@
@@ 1 @@
<
[1] 1 2 3 4 5
>
[1] 2 3 4 5 6

This will omit the CSS, but since we include it via the YAML everything should work as expected.

Use Options

Almost all diffobj parameters can be specified via options:

Then you can just run the diff as normal:

@@ 1 @@
@@ 1 @@
<
[1] 1 2 3 4 5
>
[1] 2 3 4 5 6

Shiny

Shiny usage is very similar to rmarkdown. In both cases we want to get diffobj to produce HTML output to embed in our document. If we are willing to embed the CSS with each diff, we can use:

If we have many diffs, it may be preferable to use options and external style sheet:

Unlike with our rmarkdown example, this CSS is included in the body of the HTML document instead of in the header, so it is technically illegal like in our embedded css example.

diffobj/inst/doc/metacomp.R0000644000176200001440000000332513466146507015346 0ustar liggesusers## ---- echo=FALSE--------------------------------------------------------- library(diffobj) old.opt <- options( diffobj.disp.width=80, diffobj.pager="off", diffobj.format="html" ) ## ----echo=FALSE---------------------------------------------------------- A1 <- 1:10 B1 <- c(1:9, 11) A2 <- 1:20 B2 <- c(20, 1:19) A3 <- 1:20 B3 <- c(20:21, 1:19) ## ------------------------------------------------------------------------ all.equal(A1, B1) ## ------------------------------------------------------------------------ testthat::compare(A1, B1) ## ---- results="asis"----------------------------------------------------- diffPrint(A1, B1) ## ------------------------------------------------------------------------ testthat::compare(A2, B2) ## ---- results="asis"----------------------------------------------------- diffPrint(A2, B2) ## ------------------------------------------------------------------------ testthat::compare(A3, B3) ## ---- results="asis"----------------------------------------------------- diffPrint(A3, B3) ## ---- results="asis"----------------------------------------------------- A4 <- 1:1e4 B4 <- c(1e4 + 1, A4[-c(4:7, 9e3)]) diffPrint(A4, B4) ## ---- R.options=list(max.print=5)---------------------------------------- all.equal(iris, iris[-60,]) ## ---- results="asis"----------------------------------------------------- diffPrint(iris, iris[-60,]) ## ---- R.options=list(max.print=5)---------------------------------------- all.equal(lm(hp ~ disp, mtcars), lm(hp ~ cyl, mtcars)) ## ---- results="asis"----------------------------------------------------- diffPrint(lm(hp ~ disp, mtcars), lm(hp ~ cyl, mtcars)) ## ---- echo=FALSE--------------------------------------------------------- options(old.opt) diffobj/inst/doc/diffobj.Rmd0000755000176200001440000003734713420351310015457 0ustar liggesusers--- title: "diffobj - Diffs for R Objects" author: "Brodie Gaslam" output: rmarkdown::html_vignette: toc: true css: - !expr diffobj::diffobj_css() - styles.css vignette: > %\VignetteIndexEntry{Introduction to Diffobj} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ```{r, echo=FALSE} library(diffobj) old.opt <- options( diffobj.disp.width=80, diffobj.pager="off", diffobj.format="html" ) ``` ## Introduction `diffobj` uses the same comparison mechanism used by `git diff` and `diff` to highlight differences between _rendered_ R objects: ```{r, results="asis"} a <- b <- matrix(1:100, ncol=2) a <- a[-20,] b <- b[-45,] b[c(18, 44)] <- 999 diffPrint(target=a, current=b) ``` `diffobj` comparisons work best when objects have some similarities, or when they are relatively small. The package was originally developed to help diagnose failed unit tests by comparing test results to reference objects in a human-friendly manner. If your terminal supports formatting through ANSI escape sequences, `diffobj` will output colored diffs to the terminal. If not, it will output colored diffs to your IDE viewport if it is supported, or to your browser otherwise. ## Interpreting Diffs ### Shortest Edit Script The output from `diffobj` is a visual representation of the Shortest Edit Script (SES). An SES is the shortest set of deletion and insertion instructions for converting one sequence of elements into another. In our case, the elements are lines of text. We encode the instructions to convert `a` to `b` by deleting lines from `a` (in yellow) and inserting new ones from `b` (in blue). ### Diff Structure The first line of our diff output acts as a legend to the diff by associating the colors and symbols used to represent differences present in each object with the name of the object: ```{r, results="asis", echo=FALSE} diffPrint(target=a, current=b)[1] ``` After the legend come the hunks, which are portions of the objects that have differences with nearby matching lines provided for context: ```{r, results="asis", echo=FALSE} diffPrint(target=a, current=b)[2:10] ``` At the top of the hunk is the hunk header: this tells us that the first displayed hunk (including context lines), starts at line 17 and spans 6 lines for `a` and 7 for `b`. These are display lines, not object row indices, which is why the first row shown of the matrix is row 16. You might have also noticed that the line after the hunk header is out of place: ```{r, results="asis", echo=FALSE} diffPrint(target=a, current=b)[3] ``` This is a special context line that is not technically part of the hunk, but is shown nonetheless because it is useful in helping understand the data. The line is styled differently to highlight that it is not part of the hunk. Since it is not part of the hunk, it is not accounted for in the hunk header. See `?guideLines` for more details. The actual mismatched lines are highlighted in the colors of the legend, with additional visual cues in the gutters: ```{r, results="asis", echo=FALSE} diffPrint(target=a, current=b)[6:9] ``` `diffobj` uses a line by line diff to identify which portions of each of the objects are mismatches, so even if only part of a line mismatches it will be considered different. `diffobj` then runs a word diff within the hunks and further highlights mismatching words. Let's examine the last two lines from the previous hunk more closely: ```{r, results="asis", echo=FALSE} diffPrint(target=a, current=b)[8:9] ``` Here `b` has an extra line so `diffobj` adds an empty line to `a` to maintain the alignment for subsequent matching lines. This additional line is marked with a tilde in the gutter and is shown in a different color to indicate it is not part of the original text. If you look closely at the next matching line you will notice that the `a` and `b` values are not exactly the same. The row indices are different, but `diffobj` excludes row indices from the diff so that rows that are identical otherwise are shown as matching. `diffobj` indicates this is happening by showing the portions of a line that are ignored in the diff in grey. See `?guides` and `?trim` for details and limitations on guideline detection and unsemantic meta data trimming. ### Atomic Vectors Since R can display multiple elements in an atomic vector on the same line, and `diffPrint` is fundamentally a line diff, we use specialized logic when diffing atomic vectors. Consider: ```{r, results="asis"} state.abb2 <- state.abb[-16] state.abb2[37] <- "Pennsylvania" diffPrint(state.abb, state.abb2) ``` Due to the different wrapping frequency no line in the text display of our two vectors matches. Despite this, `diffPrint` only highlights the lines that actually contain differences. The side effect is that lines that only contain matching elements are shown as matching even though the actual lines may be different. You can turn off this behavior in favor of a normal line diff with the `unwrap.atomic` argument to `diffPrint`. Currently this only works for _unnamed_ vectors, and even for them some inputs may produce sub-optimal results. Nested vectors inside lists will not be unwrapped. You can also use `diffChr` (see below) to do a direct element by element comparison. ## Other Diff Functions ### Method Overview `diffobj` defines several S4 generics and default methods to go along with them. Each of them uses a different text representation of the inputs: * `diffPrint`: use the `print`/`show` output and is the one used in the examples so far * `diffStr`: use the output of `str` * `diffObj`: picks between `print`/`show` and `str` depending on which provides the "best" overview of differences * `diffChr`: coerces the inputs to atomic character vectors with `as.character`, and runs the diff on the character vector * `diffFile`: compares the text content of two files * `diffCsv`: loads two CSV files into data frames and compares the data frames with `diffPrint` * `diffDeparse`: deparses and compares the character vectors produced by the deparsing * `ses`: computes the element by element shortest edit script on two character vectors Note the `diff*` functions use lowerCamelCase in keeping with S4 method name convention, whereas the package name itself is all lower case. ### Compare Structure with `diffStr` For complex objects it is often useful to compare structures: ```{r, results="asis"} mdl1 <- lm(Sepal.Length ~ Sepal.Width, iris) mdl2 <- lm(Sepal.Length ~ Sepal.Width + Species, iris) diffStr(mdl1$qr, mdl2$qr, line.limit=15) ``` If you specify a `line.limit` with `diffStr` it will fold nested levels in order to fit under `line.limit` so long as there remain visible differences. If you prefer to see all the differences you can leave `line.limit` unspecified. ### Compare Vectors Elements with `diffChr` Sometimes it is useful to do a direct element by element comparison: ```{r, results="asis"} diffChr(letters[1:3], c("a", "B", "c")) ``` Notice how we are comparing the contents of the vectors with one line per element. ### Why S4? The `diff*` functions are defined as S4 generics with default methods (signature `c("ANY", "ANY")`) so that users can customize behavior for their own objects. For example, a custom method could set many of the default parameters to values more suitable for a particular object. If the objects in question are S3 objects the S3 class will have to be registered with `setOldClass`. ### Return Value All the `diff*` methods return a `Diff` S4 object. It has a `show` method which is responsible for rendering the `Diff` and displaying it to the screen. Because of this you can compute and render diffs in two steps: ```{r, eval=FALSE} x <- diffPrint(letters, LETTERS) x # or equivalently: `show(x)` ``` This may cause the diff to render funny if you change screen widths, etc., between the two steps. There are also `summary`, `any`, and `as.character` methods. The `summary` method provides a high level overview of where the differences are, which can be helpful for large diffs: ```{r, results="asis"} summary(diffStr(mdl1, mdl2)) ``` `any` returns TRUE if there are differences, and `as.character` returns the character representation of the diff. ## Controlling Diffs and Their Appearance ### Parameters The `diff*` family of methods has an extensive set of parameters that allow you to fine tune how the diff is applied and displayed. We will review some of the major ones in this section. For a full description see `?diffPrint`. While the parameter list is extensive, only the objects being compared are required. All the other parameters have default values, and most of them are for advanced use only. The defaults can all be adjusted via the `diffobj.*` options. ### Display Mode There are three built-in display modes that are similar to those found in GNU `diff`: "sidebyside", "unified", and "context". For example, by varying the `mode` parameter with: ```{r, results="asis", eval=FALSE} x <- y <- letters[24:26] y[2] <- "GREMLINS" diffChr(x, y) ``` we get:
mode="sidebyside"mode="unified"mode="context"
```{r, results="asis", echo=FALSE} x <- y <- letters[24:26] y[2] <- "GREMLINS" diffChr(x, y, mode="sidebyside") ``` ```{r, results="asis", echo=FALSE} x <- y <- letters[24:26] y[2] <- "GREMLINS" diffChr(x, y, mode="unified") ``` ```{r, results="asis", echo=FALSE} x <- y <- letters[24:26] y[2] <- "GREMLINS" diffChr(x, y, mode="context") ```
By default `diffobj` will try to use `mode="sidebyside"` if reasonable given display width, and otherwise will switch to `mode="unified"`. You can always force a particular display style by specifying it with the `mode` argument. ### Color Mode The default color mode uses yellow and blue to symbolize deletions and insertions for accessibility to dichromats. If you prefer the more traditional color mode you can specify `color.mode="rgb"` in the parameter list, or use `options(diffobj.color.mode="rgb")`: ```{r, results="asis"} diffChr(x, y, color.mode="rgb") ``` ### Output Formats If your terminal supports it `diffobj` will format the output with ANSI escape sequences. `diffobj` uses Gábor Csárdi's [`crayon`](https://github.com/gaborcsardi/crayon) package to detect ANSI support and to apply ANSI based formatting. If you are using RStudio or another IDE that supports `getOption("viewer")`, `diffobj` will output an HTML/CSS formatted diff to the viewport. In other terminals that do not support ANSI colors, `diffobj` will attempt to output to an HTML/CSS formatted diff to your browser using `browseURL`. You can explicitly specify the output format with the `format` parameter: * `format="raw"` for unformatted diffs * `format="ansi8"` for standard ANSI 8 color formatting * `format="ansi256"` for ANSI 256 color formatting * `format="html"` for HTML/CSS output and styling See [Pagers](#pagers) for more details. ### Brightness The `brightness` parameter allows you to pick a color scheme compatible with the background color of your terminal. The options are: * "light": for use with light tone terminals * "dark": for use with dark tone terminals * "neutral": for use with either light or dark terminals Here are examples of terminal screen renderings for both "rgb" and "yb" `color.mode` for the three `brightness` levels. The examples for "light" and "dark" have the backgrounds forcefully set to a color compatible with the scheme. In actual use the base background and foreground colors are left unchanged, which will look bad if you use "dark" with light colored backgrounds or vice versa. Since we do not know of a good cross platform way of detecting terminal background color the default `brightness` value is "neutral". At this time the only `format` that is affected by this parameter is "ansi256". If you want to specify your own light/dark/neutral schemes you may do so either by specifying a [style](#styles) directly or with [Palette of Styles](#styles). ### Pagers In interactive mode, if the diff output is very long or if your terminal does not support ANSI colors, `diff*` methods will pipe output to a pager. This is done by writing the output to a temporary file and passing the file reference to the pager. The default action is to invoke the pager with `file.show` if your terminal supports ANSI colors and the pager is known to support ANSI colors as well (as of this writing, only `less` is assumed to support ANSI colors), or if not to use `getOption("viewer")` if available (this outputs to the viewport in RStudio), or if not to use `browseURL`. You can fine tune when, how, and if a pager is used with the `pager` parameter. See `?diffPrint` and `?Pager` for more details. ### Styles You can control almost all aspects of the diff output formatting via the `style` parameter. To do so, pass an appropriately configured `Style` object. See `?Style` for more details on how to do this. The default is to auto pick a style based on the values of the `format`, `color.mode`, and `brightness` parameters. This is done by using the computed values for each of those parameters to subset the `PaletteOfStyles` object passed as the `palette.of.styles` parameter. This `PaletteOfStyles` object contains a `Style` object for all the possible permutations of the `style`, `format`, and `color.mode` parameters. See `?PaletteOfStyles`. If you specify the `style` parameter the values of the `format`, `brightness`, and `color.mode` parameters will be ignored. ## Diff Algorithm The primary diff algorithm is Myer's solution to the shortest edit script / longest common sequence problem with the Hirschberg linear space refinement as described in: > E. Myers, "An O(ND) Difference Algorithm and Its Variations", Algorithmica 1, 2 (1986), 251-266. and should be the same algorithm used by GNU diff. The implementation used here is a heavily modified version of Michael B. Allen's diff program from the [`libmba`](http://www.ioplex.com/~miallen/libmba/dl/libmba-0.9.1.tar.gz) `C` library. Any and all bugs in the C code in this package were most likely introduced by yours truly. Please note that the resulting C code is incompatible with the original `libmba` library. ## Performance Considerations ### Diff The diff algorithm scales with the _square_ of the number of _differences_. For reasonably small diffs (< 10K differences), the diff itself is unlikely to be the bottleneck. ### Capture and Processing Capture of inputs for `diffPrint` and `diffStr`, and processing of output for all `diff*` methods will account for most of the execution time unless you have large numbers of differences. This input and output processing scales mostly linearly with the input size. You can improve performance somewhat by using `diffChr` since that skips the capture part, and by turning off `word.diff`: ```{r, eval=FALSE} v1 <- 1:5e4 v2 <- v1[-sample(v1, 100)] diffChr(v1, v2, word.diff=FALSE) ``` will be ~2x as fast as: ```{r, eval=FALSE} diffPrint(v1, v2) ``` *Note*: turning off `word.diff` when using `diffPrint` with unnamed atomic vectors can actually _slow down_ the diff because there may well be fewer element by element differences than line differences as displayed. For example, when comparing `1:1e6` to `2:1e6` there is only one element difference, but every line as displayed is different because of the shift. Using `word.diff=TRUE` (and `unwrap.atomic=TRUE`) allows `diffPrint` to compare element by element rather than line by line. `diffChr` always compares element by element. ### Minimal Diff If you are looking for the fastest possible diff you can use `ses` and completely bypass most input and output processing. Inputs will be coerced to character if they are not character. ```{r} ses(letters[1:5], letters[c(2:3, 5)]) ``` This will be 10-20x faster than `diffChr`, at the cost of less useful output. ```{r, echo=FALSE} options(old.opt) ``` diffobj/inst/doc/diffobj.html0000644000176200001440000046064113466146505015715 0ustar liggesusers diffobj - Diffs for R Objects

diffobj - Diffs for R Objects

Brodie Gaslam

Introduction

diffobj uses the same comparison mechanism used by git diff and diff to highlight differences between rendered R objects:

@@ 17,6 @@
@@ 17,7 @@
~
[,1] [,2]
~
[,1] [,2]
 
[16,] 16 66
 
[16,] 16 66
 
[17,] 17 67
 
[17,] 17 67
<
[18,] 18 68
>
[18,] 999 68
 
[19,] 19 69
 
[19,] 19 69
~
>
[20,] 20 70
 
[20,] 21 71
 
[21,] 21 71
 
[21,] 22 72
 
[22,] 22 72
@@ 42,6 @@
@@ 43,5 @@
 
[41,] 42 92
 
[42,] 42 92
 
[42,] 43 93
 
[43,] 43 93
<
[43,] 44 94
>
[44,] 999 94
<
[44,] 45 95
~
 
[45,] 46 96
 
[45,] 46 96
 
[46,] 47 97
 
[46,] 47 97

diffobj comparisons work best when objects have some similarities, or when they are relatively small. The package was originally developed to help diagnose failed unit tests by comparing test results to reference objects in a human-friendly manner.

If your terminal supports formatting through ANSI escape sequences, diffobj will output colored diffs to the terminal. If not, it will output colored diffs to your IDE viewport if it is supported, or to your browser otherwise.

Interpreting Diffs

Shortest Edit Script

The output from diffobj is a visual representation of the Shortest Edit Script (SES). An SES is the shortest set of deletion and insertion instructions for converting one sequence of elements into another. In our case, the elements are lines of text. We encode the instructions to convert a to b by deleting lines from a (in yellow) and inserting new ones from b (in blue).

Diff Structure

The first line of our diff output acts as a legend to the diff by associating the colors and symbols used to represent differences present in each object with the name of the object:

After the legend come the hunks, which are portions of the objects that have differences with nearby matching lines provided for context:

@@ 17,6 @@
@@ 17,7 @@
~
[,1] [,2]
~
[,1] [,2]
 
[16,] 16 66
 
[16,] 16 66
 
[17,] 17 67
 
[17,] 17 67
<
[18,] 18 68
>
[18,] 999 68
 
[19,] 19 69
 
[19,] 19 69
~
>
[20,] 20 70
 
[20,] 21 71
 
[21,] 21 71
 
[21,] 22 72
 
[22,] 22 72

At the top of the hunk is the hunk header: this tells us that the first displayed hunk (including context lines), starts at line 17 and spans 6 lines for a and 7 for b. These are display lines, not object row indices, which is why the first row shown of the matrix is row 16. You might have also noticed that the line after the hunk header is out of place:

~
[,1] [,2]
~
[,1] [,2]

This is a special context line that is not technically part of the hunk, but is shown nonetheless because it is useful in helping understand the data. The line is styled differently to highlight that it is not part of the hunk. Since it is not part of the hunk, it is not accounted for in the hunk header. See ?guideLines for more details.

The actual mismatched lines are highlighted in the colors of the legend, with additional visual cues in the gutters:

<
[18,] 18 68
>
[18,] 999 68
 
[19,] 19 69
 
[19,] 19 69
~
>
[20,] 20 70
 
[20,] 21 71
 
[21,] 21 71

diffobj uses a line by line diff to identify which portions of each of the objects are mismatches, so even if only part of a line mismatches it will be considered different. diffobj then runs a word diff within the hunks and further highlights mismatching words.

Let’s examine the last two lines from the previous hunk more closely:

~
>
[20,] 20 70
 
[20,] 21 71
 
[21,] 21 71

Here b has an extra line so diffobj adds an empty line to a to maintain the alignment for subsequent matching lines. This additional line is marked with a tilde in the gutter and is shown in a different color to indicate it is not part of the original text.

If you look closely at the next matching line you will notice that the a and b values are not exactly the same. The row indices are different, but diffobj excludes row indices from the diff so that rows that are identical otherwise are shown as matching. diffobj indicates this is happening by showing the portions of a line that are ignored in the diff in grey.

See ?guides and ?trim for details and limitations on guideline detection and unsemantic meta data trimming.

Atomic Vectors

Since R can display multiple elements in an atomic vector on the same line, and diffPrint is fundamentally a line diff, we use specialized logic when diffing atomic vectors. Consider:

@@ 1,5 @@
@@ 6,5 @@
 
[1] "AL" "AK" "AZ" "AR" "CA" "CO"
 
[11] "HI" "ID"
 
[7] "CT" "DE" "FL" "GA" "HI" "ID"
 
[13] "IL" "IN"
<
[13] "IL" "IN" "IA" "KS" "KY" "LA"
>
[15] "IA" "KY"
 
[19] "ME" "MD" "MA" "MI" "MN" "MS"
 
[17] "LA" "ME"
 
[25] "MO" "MT" "NE" "NV" "NH" "NJ"
 
[19] "MD" "MA"
@@ 6,4 @@
@@ 17,5 @@
~
 
 
[33] "ND" "OH"
 
[31] "NM" "NY" "NC" "ND" "OH" "OK"
 
[35] "OK" "OR"
<
[37] "OR" "PA" "RI" "SC" "SD" "TN"
>
[37] "Pennsylvania" "RI"
 
[43] "TX" "UT" "VT" "VA" "WA" "WV"
 
[39] "SC" "SD"
 
[49] "WI" "WY"
 
[41] "TN" "TX"

Due to the different wrapping frequency no line in the text display of our two vectors matches. Despite this, diffPrint only highlights the lines that actually contain differences. The side effect is that lines that only contain matching elements are shown as matching even though the actual lines may be different. You can turn off this behavior in favor of a normal line diff with the unwrap.atomic argument to diffPrint.

Currently this only works for unnamed vectors, and even for them some inputs may produce sub-optimal results. Nested vectors inside lists will not be unwrapped. You can also use diffChr (see below) to do a direct element by element comparison.

Other Diff Functions

Method Overview

diffobj defines several S4 generics and default methods to go along with them. Each of them uses a different text representation of the inputs:

  • diffPrint: use the print/show output and is the one used in the examples so far
  • diffStr: use the output of str
  • diffObj: picks between print/show and str depending on which provides the “best†overview of differences
  • diffChr: coerces the inputs to atomic character vectors with as.character, and runs the diff on the character vector
  • diffFile: compares the text content of two files
  • diffCsv: loads two CSV files into data frames and compares the data frames with diffPrint
  • diffDeparse: deparses and compares the character vectors produced by the deparsing
  • ses: computes the element by element shortest edit script on two character vectors

Note the diff* functions use lowerCamelCase in keeping with S4 method name convention, whereas the package name itself is all lower case.

Compare Structure with diffStr

For complex objects it is often useful to compare structures:

@@ 1,9 @@
@@ 1,10 @@
 
List of 5
 
List of 5
<
$ qr : num [1:150, 1:2] -12.2474 0.0816 0.0816 0.0816 0.0816 ...
>
$ qr : num [1:150, 1:4] -12.2474 0.0816 0.0816 0.0816 0.0816 ...
 
..- attr(*, "dimnames")=List of 2
 
..- attr(*, "dimnames")=List of 2
<
..- attr(*, "assign")= int [1:2] 0 1
>
..- attr(*, "assign")= int [1:4] 0 1 2 2
~
>
..- attr(*, "contrasts")=List of 1
<
$ qraux: num [1:2] 1.08 1.02
>
$ qraux: num [1:4] 1.08 1.02 1.05 1.11
<
$ pivot: int [1:2] 1 2
>
$ pivot: int [1:4] 1 2 3 4
 
$ tol : num 1e-07
 
$ tol : num 1e-07
<
$ rank : int 2
>
$ rank : int 4
 
- attr(*, "class")= chr "qr"
 
- attr(*, "class")= chr "qr"
3 differences are hidden by our use of `max.level`

If you specify a line.limit with diffStr it will fold nested levels in order to fit under line.limit so long as there remain visible differences. If you prefer to see all the differences you can leave line.limit unspecified.

Compare Vectors Elements with diffChr

Sometimes it is useful to do a direct element by element comparison:

@@ 1,3 @@
@@ 1,3 @@
 
a
 
a
<
b
>
B
 
c
 
c

Notice how we are comparing the contents of the vectors with one line per element.

Why S4?

The diff* functions are defined as S4 generics with default methods (signature c("ANY", "ANY")) so that users can customize behavior for their own objects. For example, a custom method could set many of the default parameters to values more suitable for a particular object. If the objects in question are S3 objects the S3 class will have to be registered with setOldClass.

Return Value

All the diff* methods return a Diff S4 object. It has a show method which is responsible for rendering the Diff and displaying it to the screen. Because of this you can compute and render diffs in two steps:

This may cause the diff to render funny if you change screen widths, etc., between the two steps.

There are also summary, any, and as.character methods. The summary method provides a high level overview of where the differences are, which can be helpful for large diffs:

Found differences in 12 hunks:
45 insertions, 39 deletions, 18 matches (lines)

Diff map (line:char scale is 1:1 for single chars, 1-2:1 for char seqs):
DDDIII.DDDIII.DI.DI..DDDIIIII.DI.DDDDDIIIIIII.DDDIII..DDDIII..DDIII.DDDIII..DDII.

any returns TRUE if there are differences, and as.character returns the character representation of the diff.

Controlling Diffs and Their Appearance

Parameters

The diff* family of methods has an extensive set of parameters that allow you to fine tune how the diff is applied and displayed. We will review some of the major ones in this section. For a full description see ?diffPrint.

While the parameter list is extensive, only the objects being compared are required. All the other parameters have default values, and most of them are for advanced use only. The defaults can all be adjusted via the diffobj.* options.

Display Mode

There are three built-in display modes that are similar to those found in GNU diff: “sidebysideâ€, “unifiedâ€, and “contextâ€. For example, by varying the mode parameter with:

we get:

mode=“sidebyside†mode=“unified†mode=“contextâ€
@@ 1,3 @@
@@ 1,3 @@
 
x
 
x
<
y
>
GREMLINS
 
z
 
z
@@ 1,3 / 1,3 @@
 
x
<
y
>
GREMLINS
 
z
@@ 1,3 / 1,3 @@
 
x
<
y
 
z
~
----------
 
x
>
GREMLINS
 
z

By default diffobj will try to use mode="sidebyside" if reasonable given display width, and otherwise will switch to mode="unified". You can always force a particular display style by specifying it with the mode argument.

Color Mode

The default color mode uses yellow and blue to symbolize deletions and insertions for accessibility to dichromats. If you prefer the more traditional color mode you can specify color.mode="rgb" in the parameter list, or use options(diffobj.color.mode="rgb"):

@@ 1,3 @@
@@ 1,3 @@
 
x
 
x
<
y
>
GREMLINS
 
z
 
z

Output Formats

If your terminal supports it diffobj will format the output with ANSI escape sequences. diffobj uses Gábor Csárdi’s crayon package to detect ANSI support and to apply ANSI based formatting. If you are using RStudio or another IDE that supports getOption("viewer"), diffobj will output an HTML/CSS formatted diff to the viewport. In other terminals that do not support ANSI colors, diffobj will attempt to output to an HTML/CSS formatted diff to your browser using browseURL.

You can explicitly specify the output format with the format parameter:

  • format="raw" for unformatted diffs
  • format="ansi8" for standard ANSI 8 color formatting
  • format="ansi256" for ANSI 256 color formatting
  • format="html" for HTML/CSS output and styling

See Pagers for more details.

Brightness

The brightness parameter allows you to pick a color scheme compatible with the background color of your terminal. The options are:

  • “lightâ€: for use with light tone terminals
  • “darkâ€: for use with dark tone terminals
  • “neutralâ€: for use with either light or dark terminals

Here are examples of terminal screen renderings for both “rgb†and “yb†color.mode for the three brightness levels.

The examples for “light†and “dark†have the backgrounds forcefully set to a color compatible with the scheme. In actual use the base background and foreground colors are left unchanged, which will look bad if you use “dark†with light colored backgrounds or vice versa. Since we do not know of a good cross platform way of detecting terminal background color the default brightness value is “neutralâ€.

At this time the only format that is affected by this parameter is “ansi256â€. If you want to specify your own light/dark/neutral schemes you may do so either by specifying a style directly or with Palette of Styles.

Pagers

In interactive mode, if the diff output is very long or if your terminal does not support ANSI colors, diff* methods will pipe output to a pager. This is done by writing the output to a temporary file and passing the file reference to the pager. The default action is to invoke the pager with file.show if your terminal supports ANSI colors and the pager is known to support ANSI colors as well (as of this writing, only less is assumed to support ANSI colors), or if not to use getOption("viewer") if available (this outputs to the viewport in RStudio), or if not to use browseURL.

You can fine tune when, how, and if a pager is used with the pager parameter. See ?diffPrint and ?Pager for more details.

Styles

You can control almost all aspects of the diff output formatting via the style parameter. To do so, pass an appropriately configured Style object. See ?Style for more details on how to do this.

The default is to auto pick a style based on the values of the format, color.mode, and brightness parameters. This is done by using the computed values for each of those parameters to subset the PaletteOfStyles object passed as the palette.of.styles parameter. This PaletteOfStyles object contains a Style object for all the possible permutations of the style, format, and color.mode parameters. See ?PaletteOfStyles.

If you specify the style parameter the values of the format, brightness, and color.mode parameters will be ignored.

Diff Algorithm

The primary diff algorithm is Myer’s solution to the shortest edit script / longest common sequence problem with the Hirschberg linear space refinement as described in:

E. Myers, “An O(ND) Difference Algorithm and Its Variationsâ€, Algorithmica 1, 2 (1986), 251-266.

and should be the same algorithm used by GNU diff. The implementation used here is a heavily modified version of Michael B. Allen’s diff program from the libmba C library. Any and all bugs in the C code in this package were most likely introduced by yours truly. Please note that the resulting C code is incompatible with the original libmba library.

Performance Considerations

Diff

The diff algorithm scales with the square of the number of differences. For reasonably small diffs (< 10K differences), the diff itself is unlikely to be the bottleneck.

Capture and Processing

Capture of inputs for diffPrint and diffStr, and processing of output for all diff* methods will account for most of the execution time unless you have large numbers of differences. This input and output processing scales mostly linearly with the input size.

You can improve performance somewhat by using diffChr since that skips the capture part, and by turning off word.diff:

will be ~2x as fast as:

Note: turning off word.diff when using diffPrint with unnamed atomic vectors can actually slow down the diff because there may well be fewer element by element differences than line differences as displayed. For example, when comparing 1:1e6 to 2:1e6 there is only one element difference, but every line as displayed is different because of the shift. Using word.diff=TRUE (and unwrap.atomic=TRUE) allows diffPrint to compare element by element rather than line by line. diffChr always compares element by element.

Minimal Diff

If you are looking for the fastest possible diff you can use ses and completely bypass most input and output processing. Inputs will be coerced to character if they are not character.

## [1] "1d0" "4d2"

This will be 10-20x faster than diffChr, at the cost of less useful output.

diffobj/inst/doc/metacomp.html0000644000176200001440000012543013466146507016113 0ustar liggesusers Mean Relative Indifference

Mean Relative Indifference

A Comparison of R Comparison Functions

Brodie Gaslam

Most R object comparison functions are good at telling you that objects are different, but less so at conveying how they are different. I wrote diffobj to provide an “aha, that’s how they are different†comparison. In this vignette I will compare diffPrint to all.equal and to testthat::compare.

Disclaimer: I picked the examples here to showcase diffobj capabilities, not to carry out a fair and balanced comparison of these comparison functions. Nonetheless, I hope you will find the examples representative of common situations where comparison of R objects is useful.

Vectors

I defined four pairs of numeric vectors for us to compare. I purposefully hid the variable definitions to simulate a comparison of unknown objects.

Stage 1

## [1] "Mean relative difference: 0.1"

The objects are different… At this point I would normally print both A1 and B1 to try to figure out how that difference came about since the “mean relative difference†is unhelpful.

## 1/10 mismatches
## [10] 10 - 11 == -1

testthat::compare does a better job, but I still feel the need to look at A1 and B1.

@@ 1 @@
@@ 1 @@
<
[1] 1 2 3 4 5 6 7 8 9 10
>
[1] 1 2 3 4 5 6 7 8 9 11

Aha, that’s how they are different!

Stage 2

Let’s up the difficulty a little bit:

## 20/20 mismatches (average diff: 1.9)
## [1] 1 - 20 == -19
## [2] 2 -  1 ==   1
## [3] 3 -  2 ==   1
## [4] 4 -  3 ==   1
## [5] 5 -  4 ==   1
## [6] 6 -  5 ==   1
## [7] 7 -  6 ==   1
## [8] 8 -  7 ==   1
## [9] 9 -  8 ==   1
## ...

If you look closely you will see that despite a reported 20/20 differences, the two vectors are actually similar, at least in the part visible part of the output. With diffPrint it is obvious that B2 and is the same as A2, except that the last value has been moved to the first position:

@@ 1,2 @@
@@ 1,2 @@
<
[1] 1 2 3 4 5 6 7 8 9 10 11
>
[1] 20 1 2 3 4 5 6 7 8 9 10
<
[12] 12 13 14 15 16 17 18 19 20
>
[12] 11 12 13 14 15 16 17 18 19

Stage 3

testthat::compare throws in the towel as soon as lengths are unequal:

## Lengths differ: 20 is not 21

all.equal does the same. diffPrint is unfazed:

@@ 1,2 @@
@@ 1,2 @@
<
[1] 1 2 3 4 5 6 7 8 9 10 11
>
[1] 20 21 1 2 3 4 5 6 7 8 9
<
[12] 12 13 14 15 16 17 18 19 20
>
[12] 10 11 12 13 14 15 16 17 18 19

diffPrint also produces useful output for largish vectors:

@@ 1,4 @@
@@ 1,4 @@
<
[1] 1 2 3 4 5
>
[1] 10001 1 2 3 8
<
[6] 6 7 8 9 10
>
[6] 9 10 11 12 13
 
[11] 11 12 13 14 15
 
[11] 14 15 16 17 18
 
[16] 16 17 18 19 20
 
[16] 19 20 21 22 23
@@ 1798,5 @@
@@ 1798,5 @@
 
[8986] 8986 8987 8988 8989 8990
 
[8986] 8989 8990 8991 8992 8993
 
[8991] 8991 8992 8993 8994 8995
 
[8991] 8994 8995 8996 8997 8998
<
[8996] 8996 8997 8998 8999 9000
>
[8996] 8999 9001 9002 9003 9004
 
[9001] 9001 9002 9003 9004 9005
 
[9001] 9005 9006 9007 9008 9009
 
[9006] 9006 9007 9008 9009 9010
 
[9006] 9010 9011 9012 9013 9014

Do note that the comparison algorithm scales with the square of the number of differences, so very large and different vectors will be slow to process.

Objects

R Core and package authors put substantial effort into print and show methods. diffPrint takes advantage of this. Compare:

## [1] "Attributes: < Component \"row.names\": Numeric: lengths (150, 149) differ >"
## [2] "Component \"Sepal.Length\": Numeric: lengths (150, 149) differ"             
## [3] "Component \"Sepal.Width\": Numeric: lengths (150, 149) differ"              
## [4] "Component \"Petal.Length\": Numeric: lengths (150, 149) differ"             
## [5] "Component \"Petal.Width\": Numeric: lengths (150, 149) differ"              
##  [ reached getOption("max.print") -- omitted 3 entries ]

to:

@@ 59,5 / 59,4 @@
~
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
 
58 4.9 2.4 3.3 1.0 versicolor
 
59 6.6 2.9 4.6 1.3 versicolor
<
60 5.2 2.7 3.9 1.4 versicolor
 
61 5.0 2.0 3.5 1.0 versicolor
 
62 5.9 3.0 4.2 1.5 versicolor

And:

## [1] "Component \"coefficients\": Names: 1 string mismatch"          
## [2] "Component \"coefficients\": Mean relative difference: 2.778944"
## [3] "Component \"residuals\": Mean relative difference: 0.7074011"  
## [4] "Component \"effects\": Names: 1 string mismatch"               
## [5] "Component \"effects\": Mean relative difference: 0.5907086"    
##  [ reached getOption("max.print") -- omitted 9 entries ]

to:

@@ 1,8 @@
@@ 1,8 @@
 
 
 
 
 
Call:
 
Call:
<
lm(formula = hp ~ disp, data = mtcars)
>
lm(formula = hp ~ cyl, data = mtcars)
 
 
 
 
 
Coefficients:
 
Coefficients:
<
(Intercept) disp
>
(Intercept) cyl
<
45.7345 0.4376
>
-51.05 31.96
 
 
 
 

In these examples I limited all.equal output to five lines for the sake of brevity. Also, since testthat::compare reverts to all.equal output with more complex objects I omit it from this comparison.

Parting Thoughts

Another candidate comparison function is compare::compare. I omitted it from this vignette because it focuses more on similarities than on differences. Additionally, testthat::compare and compare::compare print methods conflict so they cannot be used together.

For a more thorough exploration of diffobj methods and their features please see the primary diffobj vignette.

diffobj/tests/0000755000176200001440000000000013466067350013031 5ustar liggesusersdiffobj/tests/scaling.R0000755000176200001440000000313713014331136014564 0ustar liggesusers# These tests are not actually run since they require manual intervention to # check for browser rendering # These tests should be run on as many browsers as possible as well as in # RStudio, and consist of running the code and resizing the windows to see # what happens if(FALSE) { # prevent running setwd("tests") source("testthat/helper.commonobjects.R") # Text should be allowed to unfurl beyond native width diffStr(mdl1, mdl2, format="html") diffStr(mdl1, mdl2, format="html", style=list(scale=FALSE)) diffPrint(c(mdl1), c(mdl2), format="html") # Revealed problems with pixel rounding in scaling diffPrint(letters[1:6], LETTERS[1:6], format="html") diffPrint(letters[1:6], LETTERS[1:6], format="html", style=list(scale=FALSE)) # Revealed problems with gutter width computations; and scaling mx.2 <- matrix(1:100, ncol=4) mx.4 <- mx.3 <- mx.2 mx.3[15, 2] <- 111L mx.3a <- mx.3[-5, ] diffPrint(mx.2, mx.3a, format="html") # summary stuff summary(diffPrint(letters, LETTERS, format="html")) summary(diffStr(mdl1, mdl2, format="html")) summary(diffPrint(c(mdl1), c(mdl2), format="html")) # Long banners diffPrint( 1:20 + 100 + 100 + 100 + 100 + 100 + 100 + 100, 2:20 + 100 + 100 + 100 + 100 + 100 + 100 + 100, format="html", style=list(scale=FALSE) ) diffPrint( 1:20 + 100 + 100 + 100 + 100 + 100 + 100 + 100, 2:20 + 100 + 100 + 100 + 100 + 100 + 100 + 100, format="html" ) # context diffPrint( 1:20 + 100 + 100 + 100 + 100 + 100 + 100 + 100, 2:20 + 100 + 100 + 100 + 100 + 100 + 100 + 100, format="html", mode="context" ) } diffobj/tests/run.R0000755000176200001440000000421413466067350013764 0ustar liggesusers# Run tests if(!require(testthat)) stop("`testthat` must be available to run tests.") library(diffobj) local({ # so we can use `on.exit` # options that can't be reset to NULL... no.null.opts <- c( "warnPartialMatchArgs", "warnPartialMatchAttr", "warnPartialMatchDollar" ) no.null.opt.list <- Map(getOption, no.null.opts) no.null.nulls <- vapply(no.null.opt.list, is.null, logical(1L)) no.null.opt.list[no.null.nulls] <- FALSE all.opts <- c( list( useFancyQuotes=FALSE, # all.equals uses fancy quotes diffobj.format="ansi8", # force ANSI colors diffobj.color.mode="yb",# force yb diffobj.pager="off", # run tests without pager width=80L, encoding="UTF-8" # so Gabor's name renders properly on win... ) ) old.opts <- options(c(diffobj_set_def_opts(), all.opts)) options( warnPartialMatchArgs=TRUE, warnPartialMatchAttr=TRUE, warnPartialMatchDollar=TRUE ) old.opts <- c(old.opts, no.null.opt.list) on.exit(options(old.opts)) RNGversion("3.5.2"); valgrind <- FALSE if(!valgrind) { filter <- paste0( # so we can run subset of files c( "atomic", "banner", "capture", "check", "context", "core", "diffChr", "diffDeparse", "diffObj", "diffPrint", "diffStr", "file", "guide", "html", "limit", "methods", "misc", if(nchar(Sys.getenv('NOT_CRAN'))) "notcran", "pager", "rdiff", "s4", "ses", # run this file only for valgrind "style", "subset", "summary", "text", "trim", "warning" ), collapse="|" ) test.res <- test_dir("testthat", filter=filter) # test.res <- test_dir("testthat", filter="diffPrint") with( as.data.frame(test.res), { fail <- sum(failed) err <- sum(error) if(fail != 0 || err) stop("Errors: ", err, " Failures: ", fail) }) } else { source('valgrind/tests-valgrind.R') } RNGversion(as.character(getRversion())) }) diffobj/tests/valgrind/0000755000176200001440000000000013442554134014632 5ustar liggesusersdiffobj/tests/valgrind/mdl-cur-all.txt0000755000176200001440000000677413442554134017525 0ustar liggesusersList of 13 $ coefficients : Named num [1:4] 2.251 0.804 1.459 1.947 ..- attr(*, "names")= chr [1:4] "(Intercept)" "Sepal.Width" "Speciesversicolor" "Speciesvirginica" $ residuals : Named num [1:150] 0.0361 0.2379 -0.1228 -0.1424 -0.1442 ... ..- attr(*, "names")= chr [1:150] "1" "2" "3" "4" ... $ effects : Named num [1:150] -71.566 -1.188 0.279 8.525 -0.114 ... ..- attr(*, "names")= chr [1:150] "(Intercept)" "Sepal.Width" "Speciesversicolor" "Speciesvirginica" ... $ rank : int 4 $ fitted.values: Named num [1:150] 5.06 4.66 4.82 4.74 5.14 ... ..- attr(*, "names")= chr [1:150] "1" "2" "3" "4" ... $ assign : int [1:4] 0 1 2 2 $ qr :List of 5 ..$ qr : num [1:150, 1:4] -12.2474 0.0816 0.0816 0.0816 0.0816 ... .. ..- attr(*, "dimnames")=List of 2 .. .. ..$ : chr [1:150] "1" "2" "3" "4" ... .. .. ..$ : chr [1:4] "(Intercept)" "Sepal.Width" "Speciesversicolor" "Speciesvirginica" .. ..- attr(*, "assign")= int [1:4] 0 1 2 2 .. ..- attr(*, "contrasts")=List of 1 .. .. ..$ Species: chr "contr.treatment" ..$ qraux: num [1:4] 1.08 1.02 1.05 1.11 ..$ pivot: int [1:4] 1 2 3 4 ..$ tol : num 1e-07 ..$ rank : int 4 ..- attr(*, "class")= chr "qr" $ df.residual : int 146 $ contrasts :List of 1 ..$ Species: chr "contr.treatment" $ xlevels :List of 1 ..$ Species: chr [1:3] "setosa" "versicolor" "virginica" $ call : language lm(formula = frm2, data = iris) $ terms :Classes 'terms', 'formula' language Sepal.Length ~ Sepal.Width + Species .. ..- attr(*, "variables")= language list(Sepal.Length, Sepal.Width, Species) .. ..- attr(*, "factors")= int [1:3, 1:2] 0 1 0 0 0 1 .. .. ..- attr(*, "dimnames")=List of 2 .. .. .. ..$ : chr [1:3] "Sepal.Length" "Sepal.Width" "Species" .. .. .. ..$ : chr [1:2] "Sepal.Width" "Species" .. ..- attr(*, "term.labels")= chr [1:2] "Sepal.Width" "Species" .. ..- attr(*, "order")= int [1:2] 1 1 .. ..- attr(*, "intercept")= int 1 .. ..- attr(*, "response")= int 1 .. ..- attr(*, ".Environment")= .. ..- attr(*, "predvars")= language list(Sepal.Length, Sepal.Width, Species) .. ..- attr(*, "dataClasses")= Named chr [1:3] "numeric" "numeric" "factor" .. .. ..- attr(*, "names")= chr [1:3] "Sepal.Length" "Sepal.Width" "Species" $ model :'data.frame': 150 obs. of 3 variables: ..$ Sepal.Length: num [1:150] 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ... ..$ Sepal.Width : num [1:150] 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ... ..$ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ... ..- attr(*, "terms")=Classes 'terms', 'formula' language Sepal.Length ~ Sepal.Width + Species .. .. ..- attr(*, "variables")= language list(Sepal.Length, Sepal.Width, Species) .. .. ..- attr(*, "factors")= int [1:3, 1:2] 0 1 0 0 0 1 .. .. .. ..- attr(*, "dimnames")=List of 2 .. .. .. .. ..$ : chr [1:3] "Sepal.Length" "Sepal.Width" "Species" .. .. .. .. ..$ : chr [1:2] "Sepal.Width" "Species" .. .. ..- attr(*, "term.labels")= chr [1:2] "Sepal.Width" "Species" .. .. ..- attr(*, "order")= int [1:2] 1 1 .. .. ..- attr(*, "intercept")= int 1 .. .. ..- attr(*, "response")= int 1 .. .. ..- attr(*, ".Environment")= .. .. ..- attr(*, "predvars")= language list(Sepal.Length, Sepal.Width, Species) .. .. ..- attr(*, "dataClasses")= Named chr [1:3] "numeric" "numeric" "factor" .. .. .. ..- attr(*, "names")= chr [1:3] "Sepal.Length" "Sepal.Width" "Species" - attr(*, "class")= chr "lm" diffobj/tests/valgrind/mdl-tar-all.txt0000755000176200001440000000551313442554134017510 0ustar liggesusersList of 12 $ coefficients : Named num [1:2] 6.526 -0.223 ..- attr(*, "names")= chr [1:2] "(Intercept)" "Sepal.Width" $ residuals : Named num [1:150] -0.644 -0.956 -1.111 -1.234 -0.722 ... ..- attr(*, "names")= chr [1:150] "1" "2" "3" "4" ... $ effects : Named num [1:150] -71.566 -1.188 -1.081 -1.187 -0.759 ... ..- attr(*, "names")= chr [1:150] "(Intercept)" "Sepal.Width" "" "" ... $ rank : int 2 $ fitted.values: Named num [1:150] 5.74 5.86 5.81 5.83 5.72 ... ..- attr(*, "names")= chr [1:150] "1" "2" "3" "4" ... $ assign : int [1:2] 0 1 $ qr :List of 5 ..$ qr : num [1:150, 1:2] -12.2474 0.0816 0.0816 0.0816 0.0816 ... .. ..- attr(*, "dimnames")=List of 2 .. .. ..$ : chr [1:150] "1" "2" "3" "4" ... .. .. ..$ : chr [1:2] "(Intercept)" "Sepal.Width" .. ..- attr(*, "assign")= int [1:2] 0 1 ..$ qraux: num [1:2] 1.08 1.02 ..$ pivot: int [1:2] 1 2 ..$ tol : num 1e-07 ..$ rank : int 2 ..- attr(*, "class")= chr "qr" $ df.residual : int 148 $ xlevels : Named list() $ call : language lm(formula = frm1, data = iris) $ terms :Classes 'terms', 'formula' language Sepal.Length ~ Sepal.Width .. ..- attr(*, "variables")= language list(Sepal.Length, Sepal.Width) .. ..- attr(*, "factors")= int [1:2, 1] 0 1 .. .. ..- attr(*, "dimnames")=List of 2 .. .. .. ..$ : chr [1:2] "Sepal.Length" "Sepal.Width" .. .. .. ..$ : chr "Sepal.Width" .. ..- attr(*, "term.labels")= chr "Sepal.Width" .. ..- attr(*, "order")= int 1 .. ..- attr(*, "intercept")= int 1 .. ..- attr(*, "response")= int 1 .. ..- attr(*, ".Environment")= .. ..- attr(*, "predvars")= language list(Sepal.Length, Sepal.Width) .. ..- attr(*, "dataClasses")= Named chr [1:2] "numeric" "numeric" .. .. ..- attr(*, "names")= chr [1:2] "Sepal.Length" "Sepal.Width" $ model :'data.frame': 150 obs. of 2 variables: ..$ Sepal.Length: num [1:150] 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ... ..$ Sepal.Width : num [1:150] 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ... ..- attr(*, "terms")=Classes 'terms', 'formula' language Sepal.Length ~ Sepal.Width .. .. ..- attr(*, "variables")= language list(Sepal.Length, Sepal.Width) .. .. ..- attr(*, "factors")= int [1:2, 1] 0 1 .. .. .. ..- attr(*, "dimnames")=List of 2 .. .. .. .. ..$ : chr [1:2] "Sepal.Length" "Sepal.Width" .. .. .. .. ..$ : chr "Sepal.Width" .. .. ..- attr(*, "term.labels")= chr "Sepal.Width" .. .. ..- attr(*, "order")= int 1 .. .. ..- attr(*, "intercept")= int 1 .. .. ..- attr(*, "response")= int 1 .. .. ..- attr(*, ".Environment")= .. .. ..- attr(*, "predvars")= language list(Sepal.Length, Sepal.Width) .. .. ..- attr(*, "dataClasses")= Named chr [1:2] "numeric" "numeric" .. .. .. ..- attr(*, "names")= chr [1:2] "Sepal.Length" "Sepal.Width" - attr(*, "class")= chr "lm" diffobj/tests/valgrind/mdl-cur.txt0000755000176200001440000000120113442554134016733 0ustar liggesusersList of 13 $ coefficients : Named num [1:4] 2.251 0.804 1.459 1.947 $ residuals : Named num [1:150] 0.0361 0.2379 -0.1228 -0.1424 -0.1442 ... $ effects : Named num [1:150] -71.566 -1.188 0.279 8.525 -0.114 ... $ rank : int 4 $ fitted.values: Named num [1:150] 5.06 4.66 4.82 4.74 5.14 ... $ assign : int [1:4] 0 1 2 2 $ qr :List of 5 $ df.residual : int 146 $ contrasts :List of 1 $ xlevels :List of 1 $ call : language lm(formula = frm2, data = iris) $ terms :Classes 'terms', 'formula' language Sepal.Length ~ Sepal.Width + Species $ model :'data.frame': 150 obs. of 3 variables: - attr(*, "class")= chr "lm" diffobj/tests/valgrind/tests-valgrind.R0000755000176200001440000001325613442554134017735 0ustar liggesusers# These tests are intended to be run under valgrind so we can make sure there # are no compiled code issues. It's basically impossible to run the full test # suite under valgrind because there are lots of false positives from the PCRE # library. # # Orinally these were the ses tests, but even the testthat overhead caused too # many issues so we're just running the code without checking results. writeLines("basic") # expect_equal(ses(letters[1:10], letters[1:10]), character()) ses(letters[1:10], letters[1:10]) # expect_equal(ses(letters[1:10], LETTERS[1:10]), "1,10c1,10") ses(letters[1:10], LETTERS[1:10]) # expect_equal(ses(letters[1:5], LETTERS[1:10]), "1,5c1,10") ses(letters[1:5], LETTERS[1:10]) # expect_equal(ses(letters[1:10], LETTERS[1:5]), "1,10c1,5") ses(letters[1:10], LETTERS[1:5]) # expect_equal(ses(letters[2:10], letters[1:7]), c("0a1", "7,9d7")) ses(letters[2:10], letters[1:7]) # expect_equal(ses(letters[c(1:5, 1:5, 1:5)], c("e", "d", "a", # "b", "c")), c("1,4d0", "6,8d1", "10d2", "14,15d5")) ses(letters[c(1:5, 1:5, 1:5)], c("e", "d", "a", "b", "c")) # expect_equal(ses(c("e", "d", "a", "b", "c"), letters[c(1:5, 1:5, # 1:5)]), c("0a1,4", "1a6,8", "2a10", "5a14,15")) ses(c("e", "d", "a", "b", "c"), letters[c(1:5, 1:5, 1:5)]) writeLines("trigger edit distance 1 branches") # expect_equal(ses("a", c("a", "b")), "1a2") ses("a", c("a", "b")) # expect_equal(ses(c("a", "b"), "a"), "2d1") ses(c("a", "b"), "a") # expect_equal(ses("c", c("b", "c")), "0a1") ses("c", c("b", "c")) # expect_equal(ses(c("b", "c"), "c"), "1d0") ses(c("b", "c"), "c") # expect_equal(ses("a", character()), "1d0") ses("a", character()) # expect_equal(ses(character(), "a"), "0a1") ses(character(), "a") # expect_equal(ses(character(), character()), character()) ses(character(), character()) ## this is from the atomic tests, haven't dug into why they actually trigger ## the desired branches, but it is fairly complex set.seed(2) w1 <- sample(c("carrot", "cat", "cake", "eat", "rabbit", "holes", "the", "a", "pasta", "boom", "noon", "sky", "hat", "blah", "paris", "dog", "snake"), 25, replace = TRUE) w4 <- w3 <- w2 <- w1 w2[sample(seq_along(w1), 5)] <- LETTERS[1:5] w3 <- w1[8:15] w4 <- c(w1[1:5], toupper(w1[1:5]), w1[6:15], toupper(w1[1:5])) # expect_equal(ses(w1, w4), c("5a6,10", "15,21d19", "23,25c21,25")) ses(w1, w4) writeLines("longer strings") # A bigger string string <- do.call(paste0, expand.grid(LETTERS, LETTERS, LETTERS)) # expect_equal(ses(string, c("hello", string[-c(5, 500, 1000)], # "goodbye")), c("0a1", "5d5", "500d499", "1000d998", "17576a17575")) ses(string, c("hello", string[-c(5, 500, 1000)], "goodbye")) # expect_equal(ses(c(string[200:500], "hello", string[-(1:400)][-c(5, # 500, 1000)]), string), c("0a1,199", "207,306d405", "800a900", # "1299a1400")) ses(c(string[200:500], "hello", string[-(1:400)][-c(5, 500, 1000)]), string) writeLines("max diffs") # expect_warning(ses(letters[1:10], LETTERS[1:10], max.diffs = 5), # "Exceeded `max.diffs`") suppressWarnings(ses(letters[1:10], LETTERS[1:10], max.diffs = 5)) # expect_equal(ses(letters[1:10], LETTERS[1:10], max.diffs = 5, # warn = FALSE), "1,10c1,10") ses(letters[1:10], LETTERS[1:10], max.diffs = 5, warn = FALSE) # expect_equal(ses(letters[1:10], c(letters[1], LETTERS[2:5], letters[6:10]), # max.diffs = 5, warn = FALSE), "2,5c2,5") ses(letters[1:10], c(letters[1], LETTERS[2:5], letters[6:10]), max.diffs = 5, warn = FALSE) # expect_equal(ses(letters[1:10], c(letters[1], LETTERS[2:5], letters[6:8], # LETTERS[9], letters[10]), max.diffs = 5, warn = FALSE), c("2,5c2,5", # "9c9")) ses(letters[1:10], c(letters[1], LETTERS[2:5], letters[6:8], LETTERS[9], letters[10]), max.diffs = 5, warn = FALSE) writeLines("corner cases?") # expect_equal(ses(letters[1:4], letters[1:3]), "4d3") ses(letters[1:4], letters[1:3]) # expect_equal(ses(letters[1:3], letters[1:4]), "3a4") ses(letters[1:3], letters[1:4]) # writeLines("errors") # expect_error(ses("a", "b", max.diffs = "hello"), "must be scalar integer") try(ses("a", "b", max.diffs = "hello"), silent=TRUE) # expect_error(ses("a", "b", warn = "hello"), "must be TRUE or FALSE") try(ses("a", "b", warn = "hello"), silent=TRUE) # We want to have a test file that fully covers the C code in order to run # valgrind with just that one. We were unable to isolate simple diffs that # triggered all the code, but we were able to do it with the below in addition # to the above. # test_that("Repeat tests for full coverage in SES file", { # # From test.diffStr.R # formula display changed writeLines("model prep") frm1 <- as.formula("Sepal.Length ~ Sepal.Width", env=.GlobalEnv) frm2 <- as.formula("Sepal.Length ~ Sepal.Width + Species", env=.GlobalEnv) mdl1 <- lm(frm1, iris) mdl2 <- lm(frm2, iris) writeLines("diff str") # as.character( # diffStr(mdl1, mdl2, # extra = list(strict.width = "wrap"), line.limit = 30) # ) ## we captured the text being diffed above at the actual level, and ## also at the highest level ses( readLines('valgrind/mdl-tar.txt'), readLines('valgrind/mdl-cur.txt') ) ses( readLines('valgrind/mdl-tar-all.txt'), readLines('valgrind/mdl-cur-all.txt') ) # from testthat.warnings.R writeLines("exceeded diff") A3 <- c("a b c", "d e f A B C D", "g h i", "f") B3 <- c("a b c", "xd e f E Q L S", "g h i", "q") suppressWarnings(ses(A3, B3, max.diffs = 2)) writeLines("done") diffobj/tests/valgrind/mdl-tar.txt0000755000176200001440000000112413442554134016734 0ustar liggesusersList of 12 $ coefficients : Named num [1:2] 6.526 -0.223 $ residuals : Named num [1:150] -0.644 -0.956 -1.111 -1.234 -0.722 ... $ effects : Named num [1:150] -71.566 -1.188 -1.081 -1.187 -0.759 ... $ rank : int 2 $ fitted.values: Named num [1:150] 5.74 5.86 5.81 5.83 5.72 ... $ assign : int [1:2] 0 1 $ qr :List of 5 $ df.residual : int 148 $ xlevels : Named list() $ call : language lm(formula = frm1, data = iris) $ terms :Classes 'terms', 'formula' language Sepal.Length ~ Sepal.Width $ model :'data.frame': 150 obs. of 2 variables: - attr(*, "class")= chr "lm" diffobj/tests/testthat/0000755000176200001440000000000013466146510014665 5ustar liggesusersdiffobj/tests/testthat/helper/0000755000176200001440000000000013466336133016146 5ustar liggesusersdiffobj/tests/testthat/helper/diffStr/0000755000176200001440000000000013420351310017527 5ustar liggesusersdiffobj/tests/testthat/helper/diffStr/900.rds0000755000176200001440000000022313014331136020554 0ustar liggesusers‹‹àb```b`fbb`b1À|N &mlœk$-sÀìâ’"œâ=CMˆ  ˜äÚÁÕšÀÕ¡«…š®%mi«§§§Ÿ›YR’š¢`ªoª“™—Z¬£`¨o¨Qš—] ÖËÀÀÂÔÁÕÉœ“š¤xÁB ç20üчÉdiffobj/tests/testthat/helper/diffStr/200.rds0000755000176200001440000000056313014331136020554 0ustar liggesusers‹¥TÁNƒ@]i/jL¼{7¬¡+»]ZKÔôü“†C#IX?Ç/5 ´€Pi%°3 óÞìðžO!1™{ŽË)’c´ŸËÉDÝã:W ý$¯•òåÌåáîŸKõ¸Ë,þõ=ÌšªÅ¸É ÝuŒˆì){Ínç–Ò±§ Iaý |ÚIÿ7êÃU¾­÷Ø)‰ì>Dƒ‹ú_u­32`lljD –Üá¶eæãÀáæˆ1LÈYµX]ÿ>„ eamŸN…V»:ª= ³îø´Ý0Æš/Dn#”6õÄ <Çyß«¿¨¨h¥ü„ŽJ¥ê4ƒÛæYôŸãßó¼'¸½ÿ­ô¼¼Å…ê.PN ¼&´»ÿ½Àmý•´Mú^ýï .ê_êt„ÁZiê{Àí[!! "?É7J†&€AèGhÎt(ûòýµ÷—Ÿždiffobj/tests/testthat/helper/diffStr/700.rds0000755000176200001440000000074413014331136020562 0ustar liggesusers‹¥•Ín‚@Ç)zi“&½÷2!=Ô7,¬_D ÐĘHe¤€ÐØK£ç>DŸ¯é²€|ˆH-‡Ýq²ó›™ÿîºÏW‚ ˆBC±š7l¸šÂ%›w·Ms†l8Àm?ðîÓÆ“ÞTÇØ!›n© #P[ѪÌÇÄyÜ“$X=ç7™»ëŒÇ€å>°©ò‹Iz ‡ûž,?€Õpêª&ý#¸֞ž€:u@gÇù¿ò{¡=-ÔÁÝ80Á:î(rt°®N£ˆ6V‘JzÙdùý¯$ HŽsuê,7¹—[¤ôq·|Bá‰Ë€ä,`\çwµžÆfµÏ—MR!1ƃz°oÕzf$’"0V)BƒrÏG­sŸž¿jS+©ãBöS)ê(ûfmW–* ‡šžV6Ì+ ±¦Z¦/RÞTñ–§ ‚•\LÛJ¯R³ƒëÜrÏp_!£Zgª´ËjSRgV§v3ö|<È ÍmÃ÷¥ÖæK¤µ'•ÏŽó¹[ÓZ,¨GÝ9õÁð(,-Ó¤.¼¼ÃjãÁƧá_ìlÿ–Í¢[$4EƸˆY ›ºüQ ]Ñüó €M$¬wdiffobj/tests/testthat/helper/diffStr/400.rds0000755000176200001440000000061613014331136020555 0ustar liggesusers‹ÅTAKÃ0κ]ï^Bñ PⶤʆÈN;y<(x]°…¦•¦NOþtñ-I—TGëŠÃÂK¿|}//ßk^Bê{òúkx À!ô~òD©¸†q"°Â²,Î’"‘$:×lÃLÜl‚™ –mÁ&ÿÜ.Ål†Y@1¼³äÎ!•Öõt2øTÓw¼\¤ä–gÏe<ÅÙ+,="Ì •ûFHçàoùõ‚•7#W`!Ø3L©'­iéܨÿ!Y–16†dìZ«þßoÕ_yj)¡1ÚPÎÁ:/®ŸuGÌý .•óT·Aö¸QáK^ærá;¢)E¨¹A6{½;šÒ11_De^8ôV-ÌÄ…CS§|ÅS½ì+ÆÆü ªÿoSï©S€Ū¡-Õ­¨jÖÒ°•mévb33ØoþŠ2‰ò4/|Ë„lRÂtôG¸K½Å|´\¼û©ÙžÑØ)M n:!îˆÜI=s7õSžÁëHQHQŸ_öy?¨9diffobj/tests/testthat/helper/diffStr/600.rds0000755000176200001440000000134613014331136020560 0ustar liggesusers‹µWÍo›0gI/›4i×i—'k‡fJ&N³¢mêuÒn9lR”"´Eã#·êiÆÎ;ïÜsÿ´iL0`À°%Ø¿Ÿßûù}˜o/4M›hÓÉD›LÓî+v{­hÏY{ÿf»\Øý<€¬ŸÐø4Øûx»ÞÍòáö‹½'Á§£0)„Í~a¾þžÍ: ..ϱ ¬íº8¢ B”D [ÓÇs#ÈÆ¾x …èp9Ô ÿ \[ÞæjÝÄV´R-Ì×ÿUÝë týˆÇ&ZÞ°Å^óܰeîr‰6u“¬ÅŪû¯H i¬èi °Y7Íz ÝxÏä®ëuB²…e€d`}?Ù ö[ävÛ”ÅÀ»9 ½„và&hö±Ø©*Ìha™?›ê~ÜôçÂrûË­·À¹Žù®ïaÈdÿ%j·°Ì~@ɶ^ÉþÂ|ýim®ã¹É'žùQŒ„b‚6rÝf#?úz·vµWË‹¯¼ÐslTS¤Ë+Û¼¢âv’xW! pðBZ÷¯è}¤ÕT9ǽþa )*þaðú x€Ùè‹Ðßc=¥íìuJyùÄȽ։BÛ MP9%Ïâ¬èRÎûhÕ}»µ7pCËjýß8.ó\6ƒg‡rÀdy°ÁºNcצrfGë9†ÙÀ ©RÒhò)¯*ÇC•}{Ÿªj 0;¬¤·f-—²-¬•# KÝXõe§ÍÌ…?³ï.¢–,†¶óQ³UÀ*³À9] v¹Q’³_€F~qÀÆîÂXwò1ZXåÛ!¶Ãï p§”ª»¸«¶r#ÑSä©^ŸUÈ´<¦G(tËN5£…5ídÂÖÆõ˜únÈš—ÙPú¹­iþl{Ú‰‰diffobj/tests/testthat/helper/diffStr/300.rds0000755000176200001440000000074513014331136020557 0ustar liggesusers‹¥•ÝNÂ0ÇçàFï½9Y¼3šu”¯E À˜&+aq² ‚7>†×>„Ïgìº}0ÆÄ]´§'=¿súo»>_ ‚ QÄFhÞ°æZh —¬ßÝN;û‘µC¸íÞ½mXxÚŸÉ`ë;dÑ-µ`ê¤MK?6&öÓ>˜$Áêéà8¿Á¼={<,€u•_LøsH²Öp8Tl~îpêª&ý#¸֞–€ºu@gÇù¿ò{¡=MÔÀÙØ0Åî*rt°¦Î¢ˆ6V‘JúÙdùý¯$ HŽsuj,7¹—[¤ p¯¼Cá‰Ë€ä,`\çwµžúf-ŸO›¦Bb† õ`ߪõÌI$E`¬0Rº…hüÉ=µÎ}zþªUL­¤Œ ÙO¥¨£ì›¹u L'8T5=­l˜WbM;™u‘òEoyš p­ä*`ÚVú•zœ\ç–{ºó íÔ:ûS¥]xT›’:³:µ#˜°çãAiaé¾/µF°Xy ­=©xvpœÿ‘»{`˜Ë%õ¨³ >è…•iÔ—wp7l|þbçû·lÝ"¡)2ÆEÌjXÔájèŠúŸ_`'oÎwdiffobj/tests/testthat/helper/diffStr/500.rds0000755000176200001440000000171413014331136020556 0ustar liggesusers‹¥X½nÛ0V~–(н#€Ö!Dš’e£)t ºdh @‹J„ŠrJÉNºä):wîܹsçÎ}ˆ>BQJ¦J%EÉ`éHÝwwßÝñ'ŸZ–µimmmX›[ÙësùóÒÚ¶žÈçòÅñhÄßÈß ù{’Š÷#4ò-œ¥ð*ôÓ °zW‚^ö†€C¸ú<ûžð··ºd­‹u•õ3)¸üà !&@>kþîl®5œ6ÊÒ·bœ›ç¯‡a’‚y°Šá|ìn¶\‹½p4jWïå˜ÍY„³Åi¦à=åÌñB¥ìMñI¥­j/ëñH†Gªð”—¿ª½\™q:ØÕ jφ·¤¢Úßõ†ØAZ¹Ùг‰&#HœIAž±Ñðº@Á’Ð_ШH‹d9öILW]eýO=—’;—"™§À.‚¡âCÔõüÚйE‚ñh¬3*Í#Œ½ÒÁUöêkZ‹pŒW`¶:46K} ¹?¤=`uÈî`³´m t×UÖ9Ú#è¸.ÈÒìy·¶½RÊ‘g(t#[÷‘ó|ÒíÉVvJÉFå\«¾6çyÝœÌs;u_ãº*hüI&*ŒÓ•—mwÓºZÄ#ÜÔ‡mj0Ó”ùpI£KZSW]eýo}þ8Ö×!znQDEqTÇ5 YßÏ´ï IB¡[=\Çú~à@U¬vUÔ?L•A“$<õÚ¸Ûe )§¦Ê(ãé»l†§v8Í{S-VWËg¦ëÓ‡Óª»ºé6u”Àõ6¨“‡ˆWß &Þ*ñäÖNÜ:<åå÷|ú¦ÅBÑàG>º£%g6SA“4ÑÆr5yhª _ùûÛÄêuÄ–Lž+òù©ÖZygkr´v!—»µVMœkÖU‹s-²´F4>_Ðs">æ‚/"*/ ¹R ”‘¡nÅäyÉðp¯ª£¤ó>M3µP„Éîƒ:êaºÕýœ2Áåâ]$–€~.÷‡ ¯âé—P»êb¿eðˆ]Ò²ø\^änÌ•ÔM·ñŒ¸‚ø]#»îÊE”ÕG¯µ>ºdò"”ÔTrSó¹ÏdÙõ³¤Ã@È–ìOÜfÁü,·×Ëû빩ŠÛà^¾R2¹¤"¤g‘<´:4uÕ­²¾hšŠÁ«!èͲZìíîƒÙ…½ˆ÷ª»ê*ëûùÌ?”‡pÁâ™,*¸}ŸÅàì ˜/X$,cñ”Ók˜/u§+­íM ²¡À¶"Ëdz|(û‰eýû®×ÓøLdiffobj/tests/testthat/helper/diffStr/1100.rds0000755000176200001440000000042313014331136020627 0ustar liggesusers‹¥RMKÃ@Ý&õ  xïåm¨a×Äj‚H@õäAB“t ‘dƒÉ¶úëÅÉ6•\TÄ9Ì×Î{óÁ>!áº#á¸{ÆêHŒÅ1ÛÇIÉê¡Æ®h‹¬$¬‹Í†Ò9µÈȼiÔÙ å¦!ÛšC€´!xº6ViYúôºMËU¯ (°úEz®?C»v!ßgOShƒÄ.úÌL—â s\ãß÷åøXˆ±ÃýGýnIšÍ©MuCˆOíÈÌ3diffobj/tests/testthat/helper/diffStr/100.txt0000755000176200001440000000030313466141740020605 0ustar liggesusers< str(list(a = 1)) > str(list(a = 1.. @@ 1,2 @@ @@ 1,3 @@ < List of 1 > List of 2 $ a: num 1 $ a : num 1 ~ > $ cabera: num 3 diffobj/tests/testthat/helper/diffStr/100.rds0000755000176200001440000000511513014331136020551 0ustar liggesusers‹å\͋丯™ÙKÜû"Š@woºÛåúèbgYIXXrÈX†à©rõ˜ø£×vwæ´{Î9ç¼ç9ïyÿˆüû„Èß²ô$=©ª+!¨nÙm=I¿÷ýžkþøãÙlöröêÕ‹ÙËWõð§ôÇ_gÍ~D¿¿øj±H?¡?ïRҌ˪¸J÷‰wÝÞSþ£é§Ãä Ÿìë'wëï郫ô³Ïˆw³\ú[³\Mtœ²Z ¦tkýczÖ;7m†_ÄeEòC{zÏoî58?@0H½ž`·Ï û$?k²Ë£Ã!ÞÅQV•dK~¦Ñždç¾ò¶þ†“+gÙ­M/„]{Ç,Ô‹ã"Aê;=bôB1êžœqËCÇn•Žüsœ[×ñ}4{þ@‹°\zÌq\gã̵çË»Éõ]°Öí¡;ã‘Äqn[”ʪÊÇ7džQ|Ëùõk²{W|œ_}®æ‚à„g A>èø“UQ±‹ªë9™=„‰ó‡x_½›KaÑñBF°8ÿò!¢"Y>EE©Ûûß›;ß äÃPRâ]žäÅœ‡a[qqgñ.œËe£ÛÝRí.¢2Þ?†IY?Í*執tßtj)Šb„ÐxÌÂT5Pz.åÊ ‰rU0j]£êwˉî{ŽçyÓþ‚›²ö[SDuÂÎ óÖq«©úû‹5«ï”¬çûîNàüÀçèÎýísI#bÄ¡û×Çi\zñà”Þz2·~sÏ£æÀ§Ÿýsžµô,'wë/ÕPê£]U¶OCŠr{mD-b¢yßÚ·öœåjEjÕÚl­r7œšy›5§f½CÄh™°H£cÛ8KÉ©“@š<ÚwDìÀŒa"µµ÷úŒ*˜ÊŒ ik¯õ¹Ê9/ÒùèÇ ^Ù`ùP“fý¾nï;­-J´·ìŠ4ã\n˜ýyØÆ–ÄYÕ¢§K?¤V&¤’ 6&9ÄUí§0yŒJiL"æG[AÌÂÁ¸ðécg͆Kg³š^zÓËÅär G"(ÉsÜq%Ê?g5½Üø“Ë5‰,Øpòüþ÷ð€îÄ.,Ëø>õ’ÉÉ\â‰kHå&8ÉÉ(Á.—fX)žwÝ_Ìù¶}ía©ÈÃ,'Ëm…¥·eôô¦…oDïÖó¿N¡5^MIæázÁÉ>QB@Ü:ÚYÁ¿IRè¬ A@ÚÇi¯Tc] ÔEÓÉ.ú¦QÒ“áó¬ßNȨéòóM†Î¶@Ðù '+ãEà0¼𳱓VŸ`‚|mˆ%xŠ*²6Ä¢©Ö1]m Úà½?[•hákC‚T|'“ ^Å[ÿR»\ÀWa¤BK•éQaŒ¿ÓÊèLà%ØúÁ•Êܪâß±9ÚÍŸkÈÇ+ >+µ»<«Š°¬ÊùøHkÅ™§òŒû²ædÈÐÑ[Ÿ ãÑÎ5OtÖa¼±e¤vP@Ý©Š(¬`d­÷iƒleÊhˆxÂ^eªÂÇ÷[°qT€ê¢/Ñ NœÊ@°ÓKÇs¶³Zæ~]9ù´È>ÄOy§ ‘ÅG*d§§È’Ós.IºÄZÝUžô¶ݺk%Ö“1¹CSÁ0(…h±›Ô•Bd8ñþq—PÙ§¸ó¯ (ª±ž¬­íNß­™ä§^°QžNÄII*H°R<©Õfõàöš­fÃŽõõšœõ4Þå~4êÚÎÃû$zŠ’¡)Ð<×ÔĆUiÒcÔ\]]Ë`‘r`²È)pÿðܸo!´©\¼aÑ/£*/ÃIR ¯'[ïý¸ LaÐ)„Ö~ìÂ$žÞ’$ÌîÃûˆ$éÕ!/ÒÇ$¼0|[CP†q»O0òºèCÑÉØ Ù‡UHoÇE\^«,8Ì–`Pôjñ¤ùrZÇÿemÞ£’\6·/oÈå!G¡ˆ!ƒÈ·$x6l¸$#sÚú‹(»¯Þ‘oÚKaŸ¶dì÷ÙfõFµhŸ-™ö?gVÛœïpÒüqø6ikß->6¹7HÆÏZ ©ý¾bùtC˜ /ù0žZ2Í´›žŒbŸ'îÑMVd«4·í`æÂ]•¥Pjé÷ƒí×¢H·^ÌÜ}À¸«Þ¶«4ŠìܦIUFÁ¶ 1韴·\æÏ²1"I“Ö‘uÕpe?ª#çi¶'ÇÑròXó‘Ö‘µ¥äAµ‘udY)y”Jå· ©l O]â5© ël…H° °:ã! ¯tþ€Dµo d9VQ²g¶³x°`ùDûÒ:èq’ð-ÍDÆ·XÍm*HÆÞ—š62Tx‚ìDãkŠg^죢w-¢]Çâ9!Ãã̲è¡,‹šžkÜ·Ád¨YOÆ­_DåCž•‘”iÖ“që;¿ÊhÆ™gM©âúõ'Qö$÷•F“!_Ù?¼%¿ûÓo’üm˜P Ÿ[­'›êÎCíiPÎÄã÷6¶$co‹LÃz‰>™…õçë/¬Âzm<$DvavwÍŸºªvqT4Ôs[íá-{L£"ÞÍÇ‘ˆŒñÙD‚?ª³“óñ½õõ/ Û®ŠƒyNß½AóûÝK¾7wÅ+0#teP|f¾„•<ß›ÞÒi¾†åö²VOçPPN]n ñ–.ɱúnBÐЦ¿-úõ¬V »7†rÐV Œ,Cph¢Ê{…¬Èl'/ã.’Át2#8w$pÖô³"Kú|Ђft¾te=Ywþ¶º8=ÂÂY’æüˆÉà{i ǧ~VôsG?Aóñ›± `=¹]ÿÅïmuÛ»d—Žݔ˜gÿ‚¹Í6½›¶dNþkFSÞÚc{‚÷s¸QÉà8i”]ŒÑ_ûGþõºöÇa¹çYÿW±}æÑ1·ãFñ1ÞV™7A•—:š¶±´–Œ}ÞÔvÒ4m,aŸ¶dì÷9M¼O–Lûˆq7 Q÷Ôõ°,#i‘Œ=ž]GŸ3ÃxbÈ\pVÞç3õÁ ®êsf] Û ë÷c9c[aÖ¦’Ã]Õ£©õQ­0h’ ·ÂšKWbœ]q¬ÜŒ´¦ëfÉk{F“¡¸µýΉQ+Ìz2®†l…!¥ß SH¥¶†ÌúQ²('ÈdýÒ4×C·ÂìXuV;kÔ7äÓÄ—ò=,K›*’±÷¥šÎ&Oeg‡¯žªV˜ž`+L‡ç‰eq|­œqh;‹~g‚ïf)}~2Xã0ûâȰ¾ÍdôùùnšÑùå“ÁïAß~2úü|7ÏèüòÉÐù£ñal+Ðz²…íà{x–¶X$co‹MÓ‰=1KkÈ™ýíøa+ТÄwî.ŽÍf‚Ö}ÀæÀ°k³±­Àóñ½5É aG—è[&|Gµ-ù.°ß T` ¾¬'O ÎÌw³7Ì&||Ïéw¯’TºZOžÍ>zI×ÑíãUeô×Oš[õ¸:›ýëߘhŒH‹Udiffobj/tests/testthat/helper/diffStr/1000.rds0000755000176200001440000000035713014331136020634 0ustar liggesusers‹‹àb```b`fbb`b1€ 'N’Ž66ε’–¹ `vqI‘FNq‰ž¡ŽBnb…^NjYjŽ‚­‚©&DH•I®\‡ \‡1vP›ü€³\c … æ¡jj—t´¥A.˜¨“ RB'Zùd—(ä§)"bµ“rS`! 5MAAOˆT ’ÉF€íE@Šì6!´¦à±Œ4ˆPFN%(& +J3±‡šrp›b’ëŠÓ& ‹¡.cÎIÍR¼`!P²f`ø°uñdiffobj/tests/testthat/helper/diffStr/800.rds0000755000176200001440000000022313014331136020553 0ustar liggesusers‹‹àb```b`fbb`b1À|N &mlœk$-sÀìâ’"œâ=CMˆ  ˜äÚÁÕšÀÕ¡«…š®%mi«§§§Ÿ›YR’š¢`ªoª“™—Z¬£`¨o¨Qš—] ÖËÀÀÂÔÁÕÉœ“š¤xÁB ç20üчÉdiffobj/tests/testthat/helper/methods/0000755000176200001440000000000013014331136017574 5ustar liggesusersdiffobj/tests/testthat/helper/methods/200.rds0000755000176200001440000000034313014331136020612 0ustar liggesusers‹‹àb```b`ffd`b1€ '6“Ž66ε’–¹ `v^j¹†RIjqIJfZZ~R–’ŽB¢‚­‚¡&D é6ɵƒë6Á¥Û›n Y®ƒƒ‚¡Ž‰‚>˜r0X'ÌF×Ò ,昧´"5¹D!?M!9'±¸XÅn„JlºƒsòK”•¬BX,Gr¯jX5EÆB\–1„2 ˜†f"Rø¡™h’kD²‰Jx”ÀT2°0U2Bu0ç¤æ)^°(a00ü6¢Å3diffobj/tests/testthat/helper/methods/100.rds0000755000176200001440000000035513014331136020614 0ustar liggesusers‹‹àb```b`ffd`b1€ 'ΓŽ66ε’–¹ `v^j¹†RIjqIJfZZ~R–’ŽB¢‚­‚¡&D9&¹vp}&¸ô¡èƒÚ2ËupP0Ô1QR¸T')ªaþq- rÁbŽy @¥&—(ä§)$ç$+ ¸¡b éú°Ùœ“_¢ ”¨d…j 1L) stop("`split` must be character of length <= 1, or must coerce to that") if(!length(split)) split <- "" plain <- strip_style(x) splits <- re_table(split, plain, ...) chunks <- non_matching(splits, plain, empty = TRUE) # silently recycle `split`; doesn't matter currently since we don't support # split longer than 1, but might in future split.r <- rep(split, length.out=length(x)) # Drop empty chunks to align with `substr` behavior chunks <- lapply( seq_along(chunks), function(i) { y <- chunks[[i]] # empty split means drop empty first match if(nrow(y) && !nzchar(split.r[[i]]) && !head(y, 1L)[, "length"]) { y <- y[-1L, , drop=FALSE] } # drop empty last matches if(nrow(y) && !tail(y, 1L)[, "length"]) y[-nrow(y), , drop=FALSE] else y } ) zero.chunks <- !vapply(chunks, nrow, integer(1L)) # Pull out zero chunks from colored string b/c col_substring won't work # with them res <- vector("list", length(chunks)) res[zero.chunks] <- list(character(0L)) res[!zero.chunks] <- mapply( chunks[!zero.chunks], x[!zero.chunks], SIMPLIFY = FALSE, FUN = function(tab, xx) col_substring(xx, tab[, "start"], tab[, "end"]) ) res } diffobj/tests/testthat/helper/diffFile/s.o.3f1f68.R0000755000176200001440000001737613014331136021420 0ustar liggesusers ## Create a mapping between the string and its style-less version. ## This is useful to work with the colored string. #' @importFrom utils tail map_to_ansi <- function(x, text = NULL) { if (is.null(text)) { text <- non_matching(re_table(ansi_regex, x), x, empty=TRUE) } map <- lapply( text, function(text) { cbind( pos = cumsum(c(1, text[, "length"], Inf)), offset = c(text[, "start"] - 1, tail(text[, "end"], 1), NA) ) }) function(pos) { pos <- rep(pos, length.out = length(map)) mapply(pos, map, FUN = function(pos, table) { if (pos < 1) { pos } else { slot <- which(pos < table[, "pos"])[1] - 1 table[slot, "offset"] + pos - table[slot, "pos"] + 1 } }) } } #' Count number of characters in an ANSI colored string #' #' This is a color-aware counterpart of \code{base::nchar}, #' which does not do well, since it also counts the ANSI control #' characters. #' #' @param x Character vector, potentially ANSO styled, or a vector to be #' coarced to character. #' @param ... Additional arguments, passed on to \code{base::nchar} #' after removing ANSI escape sequences. #' @return Numeric vector, the length of the strings in the character #' vector. #' #' @family ANSI string operations #' @export #' @examples #' str <- paste( #' red("red"), #' "default", #' green("green") #' ) #' #' cat(str, "\n") #' nchar(str) #' col_nchar(str) #' nchar(strip_style(str)) col_nchar <- function(x, ...) { base::nchar(strip_style(x), ...) } #' Substring(s) of an ANSI colored string #' #' This is a color-aware counterpart of \code{base::substr}. #' It works exactly like the original, but keeps the colors #' in the substrings. The ANSI escape sequences are ignored when #' calculating the positions within the string. #' #' @param x Character vector, potentially ANSI styled, or a vector to #' coarced to character. #' @param start Starting index or indices, recycled to match the length #' of \code{x}. #' @param stop Ending index or indices, recycled to match the length #' of \code{x}. #' @return Character vector of the same length as \code{x}, containing #' the requested substrings. ANSI styles are retained. #' #' @family ANSI string operations #' @export #' @examples #' str <- paste( #' red("red"), #' "default", #' green("green") #' ) #' #' cat(str, "\n") #' cat(col_substr(str, 1, 5), "\n") #' cat(col_substr(str, 1, 15), "\n") #' cat(col_substr(str, 3, 7), "\n") #' #' substr(strip_style(str), 1, 5) #' substr(strip_style(str), 1, 15) #' substr(strip_style(str), 3, 7) #' #' str2 <- "another " %+% #' red("multi-", sep = "", underline("style")) %+% #' " text" #' #' cat(str2, "\n") #' cat(col_substr(c(str, str2), c(3,5), c(7, 18)), sep = "\n") #' substr(strip_style(c(str, str2)), c(3,5), c(7, 18)) col_substr <- function(x, start, stop) { if(!is.character(x)) x <- as.character(x) if(!length(x)) return(x) ansi <- re_table(ansi_regex, x) text <- non_matching(ansi, x, empty=TRUE) mapper <- map_to_ansi(x, text = text) nstart <- mapper(start) nstop <- mapper(stop) bef <- base::substr(x, 1, nstart - 1) aft <- base::substr(x, nstop + 1, base::nchar(x)) ansi_bef <- vapply(regmatches(bef, gregexpr(ansi_regex, bef)), paste, collapse = "", FUN.VALUE = "") ansi_aft <- vapply(regmatches(aft, gregexpr(ansi_regex, aft)), paste, collapse = "", FUN.VALUE = "") paste(sep = "", ansi_bef, base::substr(x, nstart, nstop), ansi_aft) } #' Substring(s) of an ANSI colored string #' #' This is the color-aware counterpart of \code{base::substring}. #' It works exactly like the original, but keeps the colors in the #' substrings. The ANSI escape sequences are ignored when #' calculating the positions within the string. #' #' @param text Character vector, potentially ANSI styled, or a vector to #' coarced to character. It is recycled to the longest of \code{first} #' and \code{last}. #' @param first Starting index or indices, recycled to match the length #' of \code{x}. #' @param last Ending index or indices, recycled to match the length #' of \code{x}. #' @return Character vector of the same length as \code{x}, containing #' the requested substrings. ANSI styles are retained. #' #' @family ANSI string operations #' @export #' @examples #' str <- paste( #' red("red"), #' "default", #' green("green") #' ) #' #' cat(str, "\n") #' cat(col_substring(str, 1, 5), "\n") #' cat(col_substring(str, 1, 15), "\n") #' cat(col_substring(str, 3, 7), "\n") #' #' substring(strip_style(str), 1, 5) #' substring(strip_style(str), 1, 15) #' substring(strip_style(str), 3, 7) #' #' str2 <- "another " %+% #' red("multi-", sep = "", underline("style")) %+% #' " text" #' #' cat(str2, "\n") #' cat(col_substring(str2, c(3,5), c(7, 18)), sep = "\n") #' substring(strip_style(str2), c(3,5), c(7, 18)) col_substring <- function(text, first, last = 1000000L) { if (!is.character(text)) text <- as.character(text) n <- max(lt <- length(text), length(first), length(last)) if (lt && lt < n) text <- rep_len(text, length.out = n) col_substr(text, as.integer(first), as.integer(last)) } #' Split an ANSI colored string #' #' This is the color-aware counterpart of \code{base::strsplit}. #' It works almost exactly like the original, but keeps the colors in the #' substrings. #' #' @param x Character vector, potentially ANSI styled, or a vector to #' coarced to character. #' @param split Character vector of length 1 (or object which can be coerced to #' such) containing regular expression(s) (unless \code{fixed = TRUE}) to use #' for splitting. If empty matches occur, in particular if \code{split} has #' zero characters, \code{x} is split into single characters. #' @param ... Extra arguments are passed to \code{base::strsplit}. #' @return A list of the same length as \code{x}, the \eqn{i}-th element of #' which contains the vector of splits of \code{x[i]}. ANSI styles are #' retained. #' #' @family ANSI string operations #' @export #' @examples #' str <- red("I am red---") %+% #' green("and I am green-") %+% #' underline("I underlined") #' #' cat(str, "\n") #' #' # split at dashes, keep color #' cat(col_strsplit(str, "[-]+")[[1]], sep = "\n") #' strsplit(strip_style(str), "[-]+") #' #' # split to characters, keep color #' cat(col_strsplit(str, "")[[1]], "\n", sep = " ") #' strsplit(strip_style(str), "") col_strsplit <- function(x, split, ...) { split <- try(as.character(split), silent=TRUE) if(inherits(split, "try-error") || !is.character(split) || length(split) > 1L) stop("`split` must be character of length <= 1, or must coerce to that") if(!length(split)) split <- "" plain <- strip_style(x) splits <- re_table(split, plain, ...) chunks <- non_matching(splits, plain, empty = TRUE) # silently recycle `split`; note currently `re_table` doesn't use this but # should eventually split.r <- if(length(split) > length(x)) head(split, length(x)) else head(rep(split, ceiling(length(x) / length(split))), length(x)) # Drop empty chunks to align with `substr` behavior chunks <- lapply( seq_along(chunks), function(i) { y <- chunks[[i]] # empty split means drop empty first match if(nrow(y) && !nzchar(split.r[[i]]) && !head(y, 1L)[, "length"]) { y <- y[-1L, , drop=FALSE] } # drop empty last matches if(nrow(y) && !tail(y, 1L)[, "length"]) y[-nrow(y), , drop=FALSE] else y } ) zero.chunks <- !vapply(chunks, nrow, integer(1L)) # Pull out zero chunks from colored string b/c col_substring won't work # with them res <- vector("list", length(chunks)) res[zero.chunks] <- list(character(0L)) res[!zero.chunks] <- mapply( chunks[!zero.chunks], x[!zero.chunks], SIMPLIFY = FALSE, FUN = function(tab, xx) col_substring(xx, tab[, "start"], tab[, "end"]) ) res } diffobj/tests/testthat/helper/diffFile/100.rds0000755000176200001440000000146313014331136020662 0ustar liggesusers‹µW1oÓ@6iXËÑM$×mÒ¨PÑVE‚•bh.ÎÅ>q>[çs›H]˜™™™™™™ùWˆçó%~IãÔIÀƒí÷òÎß÷¾ç÷ÎùðÀqœ†³Õh8­üöœ;ÛÎ}¸úO>E§p>‰ˆ¹z‰×.ìt ¨nt>…ê¨Îÿ€ú=Ž..H»Ýv»äÀ\Û‡<ë†è$—Ü<9Œ,y>l>å©ç‡TQ_3ÕµZdDN÷ õâE5sY%˜ th@Ó™’+>¹ êÛl…ʪ©Ò¨z§ûÈ€<¹Ô,`Ê8¯iV´¬cmz5ÉÅÉŠÜâdcj5Éñ!žT‰SDÝÜ £Œšëò¼‹Ü¼vÁòv‡Ë+*ø€¤Y?ÕŠË€Pd“:Ý)ÃÖ£gÉ}¯¯•ã7/ïÐÍÄÌ—y“:o¬¡Œå¾Í÷éHd¬ÁŸ$±Òl°¢ª‹G•)Ïgb—šökæŽK 0rɺ“b1”f#CA’—Õ~¹8r ‹=>{÷öýëußtˆw:‡îsâαûl³¾lˆ§‰àP,œq¹$”K—xž·²x‹¡ü0“ŸÓ[ ¦xF>rFj h¡~Ìîèåª]’r˜*ZŒ!Aì Fz±÷¢ØùáMdèCÀÏ”2áÈ×›HÓ+œƒ˜¥rO?f)^®Cž"³ŸéùÚþYÕZË#ànj;Ü ÇmêС\ú ÙרIJì¼=¼ËÙÖœãn©©ÖÚü’†q&Hv´2*,³¶\ô_ÕY䲡¬D,ƒÅtH%2Û.2&¥³bó Ä6Çë†|ްUs³Iü®RÓ°÷TÞ/&¤ÜŽ¢æd£ÌcZHêst_Ä E£ -Š4iø:‹˜H+µé|­ªÉl:ÝH±d½kѽxÚ6à;+?üV”ø.…ÉœEÂ@ÌrÂcqÃêvFXƒeêµjµÜeAË’\ÌÜ#}Ò+«ÚSµr€ š$bÜܼ“ÊÙnÔ= ¹’Àå¡qåÿØçÏ_ÅmYÉÌ diffobj/tests/testthat/helper/diffDeparse/0000755000176200001440000000000013014331136020345 5ustar liggesusersdiffobj/tests/testthat/helper/diffDeparse/200.rds0000755000176200001440000000052713014331136021367 0ustar liggesusers‹”ÉNÃ0EN€„ÄžM”H]€Uª„P¦6@ PÂÔ¸@Æoásø¾ñb‡á‚[šfaûØñ{çn\&"A–$¬d9Êà åhˆç£±ÃBANóX’¶ZGa‡­¶Þ1||àÊ™¯ ®ô½ ð*ÕÒ>>e¹lOå‹6O¦²Éõž~Kk¾¢{iRªe}\í;ǎ¼Žåœ ÖO¿ñ³Ž!-´p¥3û£ãâ<₱EšåÍœEË…èz†xŽxxiêjN¥5=´^D\B\F\ùÛ¬—|W¨Ü@Œ%b3k¾UT^Cô×7úËwÊ7ˆ·ˆ-ÄvÖ|›¨¼…¸XA¬ö—/Få;Ä{ÄÄǬùTÞAÜEÜC¬uÌ÷Ò-ß*?kœøõTty%1Á>Jü[Ž('Øq uµ¢°ÉÓˆÚJžr¢÷a+o†ådiffobj/tests/testthat/helper/diffDeparse/100.rds0000755000176200001440000000050313014331136021360 0ustar liggesusers‹­Ò[KÃ0ÀñØîEAðÝ—Ò'…J‹0™—zݼlÕM}ÒYok¦nõúM÷ì÷cRÝþ8'ÌšæÈIÎáTÇ„–°-KXöçrBM£"£>!*“'ž'Ôœ“Ž^ÇQ’DÍ–Ùxt…÷åâwx_‚0 Jåá„/ª0ó2Ÿw沞3£g…†~N´Í’äf¥^Ö¦ô¾{êjfMµÜ3²Fž“yA^’Wä5yCÖɸï÷wªöÖ;AsR2Pƒ¼%ïÈ{²I¶È„| É'ò™|ù™mšj½_ª¯æØ4jôïžl³åÑ>¾t—ºž®¸L®«d@®‘ëä¹In‘Ûdá¯öé‘ 9Yd r—Ü#÷ÉY&Cò€<$+d•<êÛ>¿¦z<äöK]9’^mÇQCýÆõ–Ð[ï’>žªÊdiffobj/tests/testthat/helper/summary/0000755000176200001440000000000013442554134017640 5ustar liggesusersdiffobj/tests/testthat/helper/summary/900.rds0000755000176200001440000000263613420351310020657 0ustar liggesusers‹åY[oÛ6V/{˜{ÝÛYŠÁkÙN½¤‰ãX“è¶`)†õ© ¤#›n%©8Þп¹Ÿ3ìR)–ÙɶÉ@b’ß¹"¥ß:–e=¶ž|öÈzüDý’þ<²žZŸ[ÖWv ¸Æ_Ÿþ|òöÝÙk˜ª(tnäÕŸZ€Ì/ H¡b´N¥6~ÌøåñÖI+Œ•ývžâxù¯ã-…Wª¯À›2!Qg*°¶úN§Š(Õ5`–¥"™åxÜWÓìßh”€&Û‹x8Q\âÄhÒ#>—iÈHjB¤%æ‹M²$S#øúít›@””³L%Ë*lÃP‹“KA˜ÌF0å¾q{% 꼼˗ܠ¶Zz.‹I¾PÖ ¨Â©F@%yä€Çðã¹.ˆ6ju§ÔèZ„¬âoÀI¦‰V@¦Ì÷y<¡ƒAïFkáÖ‡¦ÀþÔyVL_Tú{Ã3kñuÚpybÚ¢Õå²¾. HZD­®H5‰e5æü^ìµ 1h"‹²Tµ,biŸDø†(Fà d¶”’j‡{»m³ZÖáSpyhÔÔ¢é(œ$q `ˆeWÞ'ÍŒÙJc™¡5É'ÎÜÍ‹·ÍO¦d¬›„~×t+ˆ=¦rL©ût=+–º—K|'×l¾lÕ"-³o¨wx*¹¼ÝïåÛ«f™‰]ˆf¼$J©\žažµ'ïãV3évØfbÀÃpÕ<ÃWê½Ä´iÚùÙ÷?ÝDUðÈ$ÛKÂDŒàÙááa»rÖºBiÝÚN§,ˆ˜ò¦m‚°´Ò„Ϙâ2ïb"’,öíksmh“Žw°,¦Ô—õÀ+m/òV¦W€¸ÄH(®©#½¢=WÈ}xv`®5÷5¡Cs­gX›–j§JƒÞ^Ûò¹Þª¬_>K+ïjªÆ…¥º»3D*®¦™K"S.ˆ_L˜AzSbx°ÁÇ€e¡jEôŒÞ uAõÄÄ­6º¾yõH3 Õàjie­Óùâ¦RaûÁí7¦n¥Y>ÝãTýÝeV¾¸±ù¼Þ‹6fÕÒÀjŸ‘x›™¥ød£¡ö†C×76ïŽÀ’ç÷ðýÁRÞà»çíÓµ®y%^ªK÷÷v´BëáÁáwî~Ñíïh¯A[íWaFç_&8Sœ¶s»+ü˜oÚìózÝx«î¨«Œj“÷&£V$¾Ä—ÍçøzìÖŽíÞ¸ÏÛ$¢±Íu£ojÜ]NžãÞý°ïŸìÇ]{ë·ºÅY ?«Z‡úsTÚ1ã¡þ¬iF»]׸ožôUÿû·1ŽÝÄŸßzæèóKàþq·ræî:3à…LÊÅXi³cL…¹ÛuÆ©Àºy´Ù¥Áât[Ô†u´w '"m–=”úµÓ,¾£ –9wÝäuO„)w耦ò_`6z„bž Êçã>A8cW@ß9%% ðfl¤£ÒþŠ…\Âîh—Ž*$ ý&IÌ3Ö…4Ÿ‹åóQX²Œ »N¯7¦ý~|ÛsÍk¹ØuN)e4Çù?ÍÌsÕuÞügg>Læ—Pÿ= ÿjÑfÅ_"œâ{§øW}åá žªò;ì’åRâ5M•æ{…ûUf›·+Že=}|ó–ÇzbLÿ¾0"ý2ȲþúÊàødiffobj/tests/testthat/helper/summary/200.rds0000755000176200001440000000030313412256304020645 0ustar liggesusers‹Õ’Á Â0 †c7 ¢w/9*ˆP&ˆ;Á7<-sŶÓu{}1uŠÏ°C’ÿÿ’K.DB€ˆ‚\rš@ 3Ö yjzWb©«ŠZryÔÖ½»§QØ{j;Ý8¿ ¶$C?‡6ïŠ×F;ò)3^Ãì1´¨ó}‘BíQ¥ «†v7&¡v|é0KO¿ gw«k’ØŒóÑŽPîíy”’_%ÿ÷€Èã2ÿ €)Çë cxº[Pdiffobj/tests/testthat/helper/summary/700.rds0000755000176200001440000000051513412256304020657 0ustar liggesusers‹½VAOƒ0~–]F²Ä»—wt‰1[ÐÌq^L¼z2ñDà!P”nßø€M™² ¥‡¶|m_ßûÞ×ÒXB€°òî%W0‚1ÀøÙ~L7*À@†!e¤|Ò(:3Œ6ê]»6âòžMÙZ¦JßàâŠiûå̘xk?âu×±T¤§¶½bcŒ~”ˆëG^†Ú÷bB©qîÎ1Lê‘|íìÐr.}êi¾õmQ®^»ä‰ëer²ÛjruÝÿ¸“¬*£ÀT·Æ]ù¡£ÆÏ.wcgÏ×ã1ô—‰"ôžÈ8‰Æ2Ò]UæÂîÁÆVåMÖŒùC0Щ;z(öKÚúMçyêïÝaG¤úœ 5!礞Û3W±ÃBh™7ãzm³s­Î›]n |2âŸ3¿¶Fâ÷…VLŠ›I•í×7¯‰ÿj“ diffobj/tests/testthat/helper/summary/400.rds0000755000176200001440000000023613417754216020666 0ustar liggesusers‹MN»Â0 4I"!ñ V—.Àtegaal)&%>a:1œïä“ÏwS @ BþäžÇ:Ø2?Õ%áÛoáÝ;G™¢¥r@Ó*&ó [ êLSÅI‡p¢WÓaÕÏi^R¤X±¿.d=•~ÄÁ¶Ìu@_Ö#NÛš2×èÄßk(2íÖ{ŒÏÄNR¬diffobj/tests/testthat/helper/summary/600.rds0000755000176200001440000000034713412256304020661 0ustar liggesusers‹­’M Â0 †c'ˆÁ»—TœS¦;Á x[æŠ[§ëüûb¦‚à·à¡éÛ§oÒ²’ À„UÉ6‡Ô¡Éz&ùAG©8¦‚tH•Fg„ÉAo'çS&†ŠRåÚôÑaD)]OÎÈÅ,(Äóº©ÒdzRú\ŒéîB¼0 4a*ƒö`l{6Æ93¥7 «{.5ž\ñÅO{Ó«žvÖÎ$[rœg÷ÚÉüGý•éžgúMÏ?ýåçSî ËoøeÃ]ÏmsédÇnp w³Y³]•¿Ëy¹ùG¾oñäßáœOã‹×9ÀÓdØõï Ëïz„‚„¿Àdiffobj/tests/testthat/helper/summary/800.rds0000755000176200001440000000045313412256304020661 0ustar liggesusers‹µSMOÃ0 5í.!qçâ#“ÐÔ­leå:!ñ¸,j]‘hZÁ¯G8…NL &ðÁqžÇNžo# Bož°:€„GѵmM…,KªÉääPLb¬Zóà²q9gÄQÝHkÜ9¦—X¢÷]§¨E“W|îLICnE+NÆèãÉòJÔèr¡¥Ãi6ÅÒ2 Í=#ÞÉyztKOn쯞trz—\èÖKý£ù«àṯÂx¢Wïø/sG½|<ÇŽ:÷éx¿×Ùªõûþî'¸u«eÓ0cp1Ò/mqS0“gñ¢gä;óš„#\kñ2éø¹ælÅÔ+WÙg,[¥<[yLFÁçh@¨ÈðrÜA~Š^ß~°qLdiffobj/tests/testthat/helper/guides/0000755000176200001440000000000013014331136017411 5ustar liggesusersdiffobj/tests/testthat/helper/guides/200.rds0000755000176200001440000000032113014331136020423 0ustar liggesusers‹‹àb```b`ffd`b1€; 'v‘Ž66ε’–¹ `v"„€&¹vp%&¹IhJ f…ÍrL,uL€4X+H! ˜@\Kƒ\…h X˜ ° Ü*J0ͲDRh‰Ý,J få † X‹©B XÚÔÌC65¼Ðµeu--Ñ´a¸ßÔa“©!V÷ãR‚i–’B#ìfáPÂÀÀÂ4‹j&sNjâ ’Ã?^–!adiffobj/tests/testthat/helper/guides/100.rds0000755000176200001440000000034713014331136020432 0ustar liggesusers‹’A‚0EÊBMLܳé0iikhb aB\hâŽzn,C¡hLd13̼ÿ)m/HRÒ•;VÁÚæsÞaŽ6jC±¾õuð؆4§‘æ>Aœ³íƒ©k*u¡¨Í(íÀÈÀéÚ¼ÑÌ`xa¤}l¸(®®¦\xjtýC6ü7hY!޲ ý"s/€zÙ+‚ø½Ï%Šy Ž÷ï½Ïh*Ãé^ë‰l¶~Åý—_\ ™{•X.{E€,µ^‰ó$íãiÓ[ÝÕx´ÃÃøÕdiffobj/tests/testthat/helper/style/0000755000176200001440000000000013410761036017277 5ustar liggesusersdiffobj/tests/testthat/helper/style/200.rds0000755000176200001440000000025613410761036020320 0ustar liggesusers‹‹àb```b`fbb`b1€+ 'vŽ66ε’–¹ `vNjIIjQ±žD $h’kW`’ëãâ U5GÈ1ËupP0Ô1VR ݘÂP=¨v[ä‚™`ÑD(&¨¨îAÒd’ëˆCö%‘cŸùö%“cŸ3.ûX˜€ö1BíeÎIÍR¼`!PT30üØE÷diffobj/tests/testthat/helper/style/400.rds0000755000176200001440000000046613014331136020317 0ustar liggesusers‹•’ÁkÂ0ÆÕËvèy—¿Uůÿ».²$w}³ÉôéV3sä³zºo¶Ñ¹¸xÈÜOÚÖnûl†n6k.¯…o†Äôœ”¯/oLÐßÍsrRFç¤^м¿ææËÖŠf¨é¬Zƒf!ŽÙi3ÆV–bÏ*< b˜Ð¶jZèNMÉÕW‡øeß-‹æít/K¢diffobj/tests/testthat/helper/style/300.rds0000755000176200001440000000271213410757670020331 0ustar liggesusers‹íYÝnãDÎv¹Y®¸^„t$$`QHýÛIW«EìÅ -‹TbUÕIœ&Ý8®b‡eoú \òÜÀÀcÀ#0cÇãùqì5M5•šØ3ÇgΜó}ß±ãïßít:ûƒûè°ó^§sïaçÎ|B¾Íi¼Š‚t«àMF«ùù,]†I2„e¸NWÁ¢ ãx¯zQ< ‘ÕùˆsðÀ×£‹pœB<…ñ"H8û&}»ƒ7gCbt@Ž>x “ùt.†Ãa/úÉ€'Õ!‹±~úÜ®ƒ¿è_6d3C¥õ9éš§èÃ:• •Öx´ß=Åß}ôo’!6n8qó)O™8îbh0ȆªÖ^níá)«Œ„þU#ñsk[ÛùÇëz½ÄÑQ~\E@>‘™YÑjfUͬ|‚xfæn$àƒ8+'m~’xøŸŒ(û¸Ê>™4åõ¿å¥eVˆ“pErÔ±+52c÷Äæ:»Ðe.̦ÝâÌ6«{bóÏ_šÍ²KÉê#é꿺W9³óº ¹½¢Û—P†ÍžÏøÏ™"ÍžWcV<“ãü*üp$±ÚòLPÓOšÐ앚e6Ã2[Í2‡a™£Y¶Ëìz–Ù5,sjYæ4a¿ºÇ¯îUÎîËÌ–5z®Ô½lX¦{ÙnXf´c™îd{Ì1ÝÉnÇšü¸¢Ù‘L7²Ýì°ÉtÛcŠé>¶ŠõYŠY}WÿÂx×™¦›Ùn˜æ´gšîh{Ì3ÝÑvÃ3»–g×ÿ•¹|ÈûÙï8zÇ?êYý>ÞW¶9|nZNV“ ¥¹!‡¦çt¤6¬Ç¬^ø„€¤ôfJo¦ÈZjÃŇ‹îÐäÿ  ßö¤,7˜ˆ<)Õ q•Ÿe«lÅdc{Ä5ãâyoˆ¼7$ò 5Öû»5XX90röRü¸,dXi0¢âW` …e§„‘ra›n·MO¾M¯ºMfƶx'²ÍUc–ì[¼ÂÕí¦$>rUÞ ‰ JÍ„õ¾T¥‰>’ÜqKµòž…Ó`½HatŽsÓó\@2:F®Ó‚ËËU|¹šiH&‹×9€­ ÏÂ(<‚t6O`‡ ,ãfè²p ó%ãt,`„½º·®M´ºÝQ!ÕÒ;¢ß”¨ð 4}—J«#Þ7Q©P›žgŽG½*¡F6Z¨oPÿÕ+JvlªÓb6ë´?*6XÆ(j®ÓlµK™L;6•i²Kf†Ê4u¢ezŸeºÉëŽM2ý ùßPÿšå•"‘$ú~J ’~“_–â¦#)sJIfùÉx_õ%y.,h|ÕòI—?+WH/bVý_e‚,§rxŃÜDa™ïÍ,%ìûqß•p¨Ä—³Tèå{¹Öå||÷l\f}WXŸÁµË@9WxyPMA^·nIÚY¬ÍË ûçº ñêâUÂLÐvSu§Î‹_6M¶#é?’}n¦/l¤h/€)æ¡îr!¾ïj£I¿ªTàÖv-³eמ->nд¤êžÕW ݧ= É¿¼gù|Ï"7†¼/“ú²LÅÃ…¯êYÊÀOÀϺ{Ý™îUáFÝ !Üe@Ý´{ÕÁ½A÷Bd(Ÿ‹6/Û¢{5Lˆº{¡„x•„0e÷ò•ÐÒÝëv/úÌ5K£ÅVoå?ø‚iZÏ‘úâ’ º\„¯ÓËu*v°‡HÜHï;—“mEûüÛ¯^œªºlÃ8….ûHæ«ÿ3JCe£7B7“ËÃVQî&“516ù!àfÙkä åñ?ìŒÉ»@Cdiffobj/tests/testthat/helper/style/500.rds0000755000176200001440000000055113014331136020313 0ustar liggesusers‹½ØËNÂ@à±e£‰‰{\̸i X‚1x©W¼AU”•—zí ^7>šofœÖA$Bbþ¿‹ÎÉœ3ÿôÛ¶>!„°„mY²“rJ¿¦EFŒëõ=ÛðüR¾”÷Õœ.‹Jf9³a*Ç+¨8êt¢V»×“2ÔãÒ8E5ßàü¸ŽªaTk½V÷¼ù ߤx³ª\–ÎŒ[z•ßrHÓœÿÊH[n.¯ÒÖ/ÖÉà)Ù}Faþyf/ êÏÿãÌXä0ÎÀŒ%ãÌXæ0"0#à0.ÀŒãÌXå0®ÀŒ5ãÌXç0nÀŒ ãÌØä0b0£Âa(0c‹Ãh‚ÛÆ˜±Ãa܃»Æ˜±Ça´ÀŒ*‡Ñ3jFÌ9ŒG0cŸÃx38Œg0ãÃx3êÆ+˜qÄa¼Ç#3DÆÒŒ1ñ㨩—Ét+ù7*Äç‚·6diffobj/tests/testthat/helper/style/100.rds0000755000176200001440000000157213410757670020332 0ustar liggesusers‹ÝXÍŽÓ0]ZN\8q × %v']„öÀqFªzXÄ.,V‹àÔGàx8pâÄ#ð¼Ž3qÆöØMÓT‹¨ÔÔ‰Ç3ßüäóÔÏnEQ4‹f³hv ‡Ñmù½]›ø=ŒŸž~¸¼8y[_bù¹»ä¼~(¯UÝŽ_œŸ½{þz±X<¨?¥í„ËêGZ,3ÅX'ÖÌ¥H]êWÇ:Fª˜AoQÇÅ<‹å|@]?ÉÝÉÑKÁüŸöA•Ôê²VWô ]±œ§+ya«~B[½ôJÌââ5ñ2Ÿ¯tîS ëµ/U¦j(AVè–/s´ Vhó"Ò– ­-ñt}Œψ;»©P7“O:…ÂÓª62$nøSˆpW½¶Ô‡»Ê<¸!þ_q÷4­è…ò´«.5ìM=”Ï}ÚQç Éø£ŠŽ_% Èø/*TöùFâžo$Ä1) Á~ A|ÛžDð9G‚·—Œë^CQ >óHê-ˆÅ¶€¨¦o°zñZØ…l¼þ‰¾—R”³Ù?ùüK±UÈ‚“ÇÏ#ó(è< 3h†3[ å=¡Ð¶ˆÕ/L–.Ê Ñ·A.4Ù­dR7G‰@›gUÆû,bÈcRC]òKý5¶diffobj/tests/testthat/helper/diffChr/0000755000176200001440000000000013466057122017511 5ustar liggesusersdiffobj/tests/testthat/helper/diffChr/300.txt0000755000176200001440000000056513466141726020571 0ustar liggesusers< a > b @@ 1,3 @@  @@ 1,3 @@  < hello world > hello world umbrellas umbrellas tomatoes tomatoes diffobj/tests/testthat/helper/diffChr/900.rds0000755000176200001440000000025013014331136020520 0ustar liggesusers‹‹àb```b`fbb`b1€+ 'vŽ66ε’–¹ `v²†R¢’Ž‚žD $h’kW`V  ¤U5GÈ1ËupP0Ô1VR ݘÂP=!¨v[ä‚™‰¦ º®TC-Ì_ HRIØL$ ›9É„ÌÁ¢€… h#Ô<æœÔ< Å EÃ?™ö¡diffobj/tests/testthat/helper/diffChr/200.rds0000755000176200001440000000027613014331136020521 0ustar liggesusers‹‹àb```b`fbb`b1€; 'vŽ66ε’–¹ `¶£„@žI®\I®Š¨9:@1³\CS ¥Uƒ6FÃìq- rÁbަÀ«jNª`ªÀ¢NP&¦Ù¨þBÒd’‰CÔ>/<öEà±(V‡&H„‰Žd™ˆ¶N„‹& 9ŒPó˜sRó€/X”|þÞÖjYdiffobj/tests/testthat/helper/diffChr/400.rds0000755000176200001440000000055113014331136020517 0ustar liggesusers‹TMKÃ@]“‚(Þ½ìQ ‘B b#5Šài“¬Mt7òAéÅá¹g¦¸ÙMí¶M“ä°3óvæíÌÛ$¯Ç!™† ³tOÅrˆzèHس·~Ÿßˆuȱôý0½¸VñþGì[üö¿Ê’UƒæªêÄ‘5ࣾ:·°0{(©ºçVìß›ó /¹tCʨùò¦Tí‡0Ç$¥x Šgë‚w˜,îÞÙS»©êl)¡¯6eëô]QI”Ħ;‰¡HCžì‰kOƶyŒ„uáš¹E½‚'ùÊ(>UÁ^4Ã.êm3 義êì§¾3‰2H)×Þà(É =@dhqùZD³\‹<¾¥ ÆÝo®YGR}aÀ“"_]Ä>Åór*H”dtóì:‰ÂÇÎÃã³kO;0!Ô3DgU‡&£±0'*ýþÑiW)diffobj/tests/testthat/helper/diffChr/250.rds0000755000176200001440000000024713014331136020524 0ustar liggesusers‹‹àb```b`fbb`b1€ riCéhKƒ\¿|…²Ìâ̤œT…”Ì´´Ô¢Ô¼äÔb…¤Ô’òÔÔ<…ü¤¬Ôä’b=éhcË\¨F; Ç8×,¤f+)A8Pä˜äÚÁåMP塦h…Ìr €XH6Q¨1¬è#& ,#TsNjâ |ËÀð±ey¼diffobj/tests/testthat/helper/diffChr/600.rds0000755000176200001440000000053613014331136020524 0ustar liggesusers‹“ßKÃ0Çc»EÁw_ò,þdu0ŒÀ@\eV|JÛ¸“^éÆ|ØŸ.¦I7cÛ¹å!¹ï5÷I.w};BYȶ,dÙ•y*§cÔC‡ruÎÞû}q/ç¡ÀÊ¢ìêNëmÈuÄÃ&ÖQ±ƒ½bÏå®ðí…ƒ¯Õ,Es(T1ù{ÓáPfÄ8}ó"bÓß#X`š1¼„‡'óß¼ƒWŠ´XâP/š ð©·QìÇsüA9oó¾ºyÊË!cÂxã8ÍKS‡ w:C±¼0”°&¯Ïø?#Z×DZ¬>’€áE“I¥QFsÖâiÆÊà]nFó\£Z•q„7!3²weÒnžòÒ$4ú&PÝð¼©G¦cb¸|N£.Ùù~]y4;Bæãº»;b jÆØ}z~õÈlŸz ž%y5׿,‘ˉrU¿2Bß?““«Ôådiffobj/tests/testthat/helper/diffChr/300.rds0000755000176200001440000000324413014331136020520 0ustar liggesusers‹µœÉn#7E«Ý½I€Ùg@!¨A*K@´¬©4OÖ˜e ‹¨ù€|zвZŠLÕeñZÔõ¶¤£ST‘|$io¾‚à!øøð<||ýõÇ øðsð)øîð óÓïiºÿõð½¾<þþÇ_ÿüòôíñùëð¨²ÿí¦rdjo™“-9<—í?~¬†ÕÇÃÓ›_±Ë+qåôÒñ•s9^Ö£ýñ¹v£Ñ¸ýH䋼‘wú¾V0#ZfmNØ0s×’K`»Ški%tƵüÙ]Ì [°aæR“ocÿ›úJ÷ýgk}ü ÀþL”3È6$l˜¹®É4 ãô[…]Õd%r×ä„(fmJØ0CÔäÌ£&˼s‘w!ò¾ˆ¼K‘w%ò®EÞÈ»½W$Ú½ 3ÈÖhºm˜¹ŽDõ«xs‰Dµº£P9–-w90ƒl+†¯ÙÁº¥™ulDÞ­È»ym÷Yämм-‘·íôš+Ò6±"… ²u f®#QEaΉâ¨fŽH4&Ê8Ã"l˜!Æ”©½¾Þ7¦ÌˆRb®Sf˜y•Çg.WÝë^.‰O8’6ÌzEœžèë^׌þç bžd{&l˜ñ‹ÇQ<yÛ"oGäí:½fh˜y†™BOI+a;ÄuO©TŒ>{J—è)=…°a†ˆyÍ®&–¶DÞ¶ÈÛy»"oîá5û Ѻ0ƒl}†™B¬TÏKÆB¬;{à(fmDØ0CÔäXÔB&"ïT䉼s‘w!ò¾ˆ¼K‘wu¯H´&zfmCØ0SˆDYf[Kf±£àš6'Ö´ó%†¿Õ\´£*òDÞ¡È;yÇ"ïDä:½fÎ‚è ˜aÆ {YüÆ ‘÷Eä]zxÍ<Q[˜£aÃL!¾×ªa‚×zµŠ3W˜÷ˆˆ 8'l˜avÂ{š2y‡"ïH䋼‘w*òÎ<¼f™è ˜A¶aà }E÷r)ò®D޵Ȼy·÷j«;¢uafûD2…s^Q>áÕK=>³ŽnM¢˜A¶aÃÌÉö¥¤&ÛýÒš´Ï‘íïcò†åW½¹]v=¼æî#qÏ1Ïw6ÌÚeRµìE%Q-Œ“òUus@´KÈÀvIØ0ã··3í툼]‘7y{"o_䈼C§×Ìv½3ð,aÃL!b¤u[ÄH£óßbX#Æ–(fàhOØ0ãwúi¨i!Ï"oSäm‰¼m‘·#òvEÞ\ä퉼}§×<É?t÷^ÌÀ½F†™B„«>Ù"\µæœ«7FÄš2ðÔaÃŒ_Ľ«&[D)1ƒlm†™BMfi?¡œb’U5™åÀ Ì)6Ì09E{}ùåEÞ¡È;y§"ïLä{xÍlÑj1ÃdëDŸ~)ò®D޵Ȼ¹W+Ø­3pÖNØ0Sˆï5ÛIÚä) kŽøÞ£dà¨EØ0ã7§‹æ´"o.òöœ^ó„Q[˜3Y†™B‹®Ç–ý ñé1ãÕŽ—¢þ±y×"ïFäÝŠ¼;‘·1åšDÞ¦ÈÛyÛ"oGä튼¹ÈÛszÍqyBŒËã2aÃLq\®»a—q92ç>Ùˆ(f`.Ÿ°a†9åb¯/¿´È;óðš§\ˆ»Š¸n&l˜s†bÇ~W~í3gÏ.»jczÛUW¢8µy7ª¸j¿0®N‰¸ W f®ãj§–Sýi9ãê˜(fà †¢½Oolïkg=—]uvãU7^WßxÕ­×U7^uçuÕ—¯Ú˜‰æõ"oSäm9½f–mFdÙ ƒl†™BK#ãŒú›8V‰œ§×'D90³@„ 3LD±×WùlÁYÏ¥寫®Þ׺ÖÄà ²mf ­«Y²‚i9ÿ w4'Vc.aà ³ú˜‹V"ïÌÃk®>ˆ»Š¸ú l˜)´¼,¶Åµ¬ÆYùülC”3pаaÆ/³g¯c¿ÌÞB4pzÍ3¼ ÷]Å ÜS#l˜ñËâ‰îeGä튼¹ÈÛyû"ïà}}`H´ZÌÀñ–°a&>=lNÖÿùåðã‡ãS¯ÿb=þû | Åù}]diffobj/tests/testthat/helper/diffChr/1300.rds0000755000176200001440000000031713014331136020577 0ustar liggesusers‹‹àb```b`ffd`b1€' 308HGçÚIË\0;YÃÏQG!GO"4ɵƒ+0*ÈI-)I…*€š£ä˜å:8(ê˜()„n¨°BªÇ ÕnKƒ\0,êçeÃD`,T‡*¦ó’9‰X4+P€Íœ$Bæ`Q€ÍœdBæ`Q€),žF¤4ÉÅ~ä›èDuqE2 ÐDF¨ÉÌ9©y@Š,ÄúWú— Õdiffobj/tests/testthat/helper/diffChr/500.rds0000755000176200001440000000051613014331136020521 0ustar liggesusers‹•SßKÃ0Ží^Tß}ɳˆ?X D#0W™ŸÒ6.ŤWÒ–1ÿx1Mª‹³²š‡»û®÷}\¿¦Ï!ùž‡<¿)u8D´¯spò2ÊÇ›:áêüÚ⿎à äí770ÜQ/ÉÉ_øÂD ¶‘j³Ÿ›Ž/¥)9ìægŠÙçV˜*†×Pã²|¹!`GÏÙþ—^ £YÞzE·žéÒH˜ä¦!…Z: îÈÁãŽŠŠ ,þÂ;(¶g¤æääcÕH„•••䨙žŸŸ’T‰- vúሼ¦Vb÷ÂT$-Ù 9 ¹ y ùèê (a``ašÅ5“9'5Hñ‚…@Y†á®‚}UMdiffobj/tests/testthat/helper/diffChr/225.rds0000755000176200001440000000022313014331136020520 0ustar liggesusers‹‹àb```b`fbb`b1À|N m(mië—¯P–Yœ™”“ª’™––Z”š—œZ¬”ZRžšš§Ÿ”•š\R¬'ml™ ÕhäçÚ€…ÀìäŒÄ¢Äd=ˆ*˜I®\ÞUž… h #Ô4æœÔ< Å ¹ŽáPÇ<~¸diffobj/tests/testthat/helper/diffChr/500.txt0000755000176200001440000000025713466141730020564 0ustar liggesusers< b > a @@ 1,3 @@ @@ 1,3 @@ ~ > z o o o ~ > p o o o < a b c e > A b c e < x w z f ~ < e f g h ~ diffobj/tests/testthat/helper/diffChr/1400.rds0000755000176200001440000000026013014331136020575 0ustar liggesusers‹‹àb```b`ffd`b1€; 'vŽ66ε’–¹ `v²FNjIIªžD $h’kW`‚¦jŽc–ëà `¤c¤¤º¡Â¦a˜Ý ®¥A.X, ÁT@x`3'™9X@ÍñËÔaÑ‹0Í`QG(“z&:QÝD?Žd``ašÈ5™9'5Hñ‚…@ „áh Å;diffobj/tests/testthat/helper/diffChr/1200.rds0000755000176200001440000000037213014331136020577 0ustar liggesusers‹‹àb```b`fbb`b1€; 'v‘Ž66ε’–¹ `vrF‘ž9„@ŽI®\‰ X‰’¨Y@!³\CS Ñ R„Cªoª, rÁL°h"’Ë’]‰ÄNAb§BµÂ Át?’ñ&¹iH¾J‡°32²² ¡®,ÃãJ7$¸ã0¯;2Rsrò±8¶9xl¯¨¨Àª•€ßÓóóS’*±vVVV’cg%vO"ìtQ@Ò’­£«§®ž€& YŒP3™sRó€/X”ôþVS§diffobj/tests/testthat/helper/diffChr/100.txt0000755000176200001440000000000013420351310020525 0ustar liggesusersdiffobj/tests/testthat/helper/diffChr/100.rds0000755000176200001440000000031013014331136020505 0ustar liggesusers‹‹àb```b`fbb`b1€7 'vŽ66ε’–¹ `¶£!„@žI®\I®Š¨9:@1³\C  ¥Uƒ6ACõx¡Úmi f‚E# L˜ ’{€buh‚D˜èHu©ïFçAâF$s±é& ›9N„ÌÁ¢›9΄ÌÁ¢RX<žÆ‘Ð$7G20°0Md„šÌœ“š¤xÁB ìÅÀð­>h‹ydiffobj/tests/testthat/helper/diffChr/1500.rds0000755000176200001440000000031513014331136020577 0ustar liggesusers‹‹àb```b`ffd`b1€ 'vŽ66ε’–¹ `v²†Ÿ£ŽBŽžD $h’kW`T“ZR’ U5GÈ1ËupP0Ô1QR ÝPaS„0TªÝ–¹`&XÔÏʆ‰*ÀX¨ULæ$s±hV  ›9I„ÌÁ¢›9É„ÌÁ¢RX<7Hh’‹+üÈ7Ñ —‰ ,L@¡&3ç¤æ)^°(¡10ü:ˆ^±ƒdiffobj/tests/testthat/helper/diffChr/400.txt0000755000176200001440000000023113466141730020553 0ustar liggesusers< a > b @@ 1,3 @@ @@ 1,3 @@ < hello world > hello world umbrellas umbrellas tomatoes tomatoes diffobj/tests/testthat/helper/diffChr/200.txt0000755000176200001440000000110313466141726020555 0ustar liggesusersNo visible differences between objects, but there are some differences suppressed by `ignore.white.space`, `convert.hz.white.space`, `strip.sgr`, and/or `trim`. Set all those arguments to FALSE to highlight the differences. < a > b @@ 1,3 @@  @@ 1,3 @@  hello world hello world umbrellas umbrellas tomatoes tomatoes diffobj/tests/testthat/helper/diffChr/1000.rds0000755000176200001440000000026113014331136020572 0ustar liggesusers‹‹àb```b`fbb`b1€+ 'vŽ66ε’–¹ `¶Ry~~L^†žD $h’kW`’›¬R¢U5GÈ1ËupP0Ô1VR ÝPa#„0Ìn×Ò ,4ÁQ€¼  æx¡ú¦,šÒæ › ©C³›ËÒ¹ ‹& 9ŒPó˜sRó€/Xø ÿ^Rп—diffobj/tests/testthat/helper/diffChr/800.rds0000755000176200001440000000050713420351310020521 0ustar liggesusers‹…’MK1†ÓAÁ{/Þ<„¨´‘õ '¡‚B/ûÑénd“,IÚ¢?^šÄj·v¡d&ï¼y2¼ŸBº¤wÔ!ÝžOÏÝrLúäÄÅ—Át|%ž,¹áY…0ãó9j”9ÈЮ%¨ìsk(d ¶teHÝ4JìøÓáXl¸¯ku­ÑœAö /¤ÒÈV%·ÈLæ˜PHr%—¨-+¿þWŒÕ¼f¦Ð m²ß;•³K¥!q‘0˜ …´ª\{ÊøöŠ…@i X÷O“Ÿ”¼(+7Ã#š³&>v›¡¸ „<¿ˆÒˆû±GâîÏ0 ˆ(ÛáøžoDÃ5‚ °=½/ÿÞí·îqAK·)4FÃ{Ž6NvˆÓbhãä‡8-Bú]Çélx½ ¥ gAò¿ï5P8l diffobj/tests/testthat/helper/trim/0000755000176200001440000000000013420351310017101 5ustar liggesusersdiffobj/tests/testthat/helper/trim/200.rds0000755000176200001440000000033413014331136020122 0ustar liggesusers‹…’Í Â0 dzu ‚w/}€šu‘={Ê…ë#øèbÖeÝÆì¡IþMIJ¯ˆAˆbQ»;ÚVÀšl±·Æ¸#í¹“Þ¿7~·(ÌÜ©KÈÜ#H`Ž"ñàŠB¢Ê$ÉI³r[»sízžUX†Òbsná í©EUÊf&ÔÚKCZ8Wx 5Nªœç«xÕ¦ªdlp•ûÿŒž”‰Ï%¢Óÿ}{5h#Lp™mf™Ø·ácÔfôILìˆkˆWõ&³õRý;?fCdiffobj/tests/testthat/helper/trim/300.rds0000755000176200001440000000047313014331136020127 0ustar liggesusers‹eÐMOÂ@à…âÆÄƒ¾ÙxäЙé§7OJô &&„C©EA Jëç¯w6)±É3ÝÝÎöaßÓ6ž×2mO‡æP›Žéêûôb‰r<+òåù<«*ØËÛÁjºUm›²îݼ¬a3{Þ$®@@D@ $@ òAb€PŠ@1(¥`L` 8‡Í‰=ŽÀ18§B†$€„CHŠÀoö˜¿í7íµÏ²fÔ¢Ú½áG£fr­Wrû`sõ¨ 5QOêYMÕL½¨¹Z¨¥*Õ«zS+U­Õ‰­uú®>Ô§úRßêÇþoE6­tnîûý‚ûå›ûÁ=Ã4° ²>ç@È­.Ä[Yv‰Ð…d++.¹®¿ú 2diffobj/tests/testthat/helper/trim/100.rds0000755000176200001440000000052213420351310020115 0ustar liggesusers‹}ÝKÃ0À³u>(¾ïåþ€R×u‘ù O"Â…2èÇnm¤IJ’mè/¦i­Ý—Kî.?~¹¼_BúÄ9둾S¥Wf;'„¼ Ãéˆ= ØPE“aIW+”ÈST Þ"rɦZ¹¬5èÜ\ClB ¶ÃÃ`Êï«õªuYJT —|BD3.$zÛœjôT§¹¥‚oPj/ÿÚ¿QZÒÒS™ŒÜ®ûͺc¾¼"ðȃ9jˆ‹ÂŒ'T5^¶fȵ-àñþiþP%9Íò„ýDwr¯«Ÿ™"`·¶6ë¼]¦œ°»˜°dh<ÕÌ7l6߀9 Ž¶ß®Jó¹?Ÿ-C×_´­cžÐwu þhtijø§=ãlÏÓƧ=Á¶çéAí!ƒ¾ñôŸS 7Ç¥mÛúþqóªèdiffobj/tests/testthat/helper/trim/50.rds0000755000176200001440000000024013327367306020060 0ustar liggesusers‹‹àb```b`f’Ì ¦`f`aàÒÒÑÆÆ¹6@Ò2WÌ6´2†ðPÀ$×®È$×ÈÊ MÔ< °Y®ƒƒ‚¡@h)Å)ÕÛ‚êKƒ\03Ú0ê2ˆŒ‘‚1B›û5CdM\oŠÄ†ø¡‰… èF¨›˜sRó€/Xl ÿý—ä`Qdiffobj/tests/testthat/helper/diffPrint/0000755000176200001440000000000013464701606020072 5ustar liggesusersdiffobj/tests/testthat/helper/diffPrint/175.txt0000755000176200001440000000275313466141736021165 0ustar liggesusers< nhtemp > nhtemp2 @@ 4,4 @@  @@ 4,5 @@  Frequency = 1  Frequency = 1  ~  [1]  49.9 52.3 49.4 51.1 <  [1] 49.9 52.3 49.4 51.1 49.4 47.9 >  [5] -999.0 47.9 49.8 50.9  [7] 49.8 50.9 49.3 51.9 50.8 49.6  [9]  49.3 51.9 50.8 49.6 [13] 49.3 50.6 48.4 50.7 50.9 50.6 [13]  49.3 50.6 48.4 50.7 @@ 8,4 @@  @@ 10,5 @@  ~ [21]  51.8 51.1 49.8 50.2 [19] 51.5 52.8 51.8 51.1 49.8 50.2 [25]  50.4 51.6 51.8 50.9 < [25] 50.4 51.6 51.8 50.9 48.8 51.7 > [29]  48.8 -999.0 51.0 50.6 [31] 51.0 50.6 51.7 51.5 52.1 51.3 [33]  51.7 51.5 52.1 51.3 [37] 51.0 54.0 51.4 52.7 53.1 54.6 [37]  51.0 54.0 51.4 52.7 diffobj/tests/testthat/helper/diffPrint/2300.rds0000755000176200001440000000075113464152072021173 0ustar liggesusers‹V]kƒ0Íl_6 ö¸—ü€̧ côì¡ÐÁ{*¥lBm‡ŽþŽýâ±›-¦FÍ„«÷œ{=÷äâÛ B(B³(BÑL_ÞÁéÍÑ5¬ŸïœpÎ l®ó2¯ßÔw<<ÅS 5H l¾QÅr‰é‚ÆV½¦™Ú¼‹þ›ÅESÅš<Ãï¯`]ÁÚë¯NX8ÈÉGmÁ’P̉Ĕf`ÕîûXmzò…€l¾Ÿn¯à)³$‚d@Lç^\ÖÐퟟHQâ'rtà$|\‡“OX¨‚‚9ÈHAÆÑ|! ¯î²mž&Qÿ×ÝC¤u“uW-I $|šÿB@^K’˜ò41©u Mù§]YåÛãþXêà!Ò:d~"G‡´m­°$Ò´·¿†€œ|Y ÍŒ•´…Gó…€.f&_¨©3s,Ôav$ K³5øð»„€Ü|vß+×öÓÛ‘ ø”—ù!ßöÎÌ׳ÔNm|6ÓØzÖZ­¿† ÏzˆÅ~"WÙHHÍà•`×Ã:€Ü|ê,!˜4C'Éòëž4$ÒÖº³vfNÔ½ŸHÏ : ;šGPו­o¶ß`¹5·ôçB¿\¥†é) diffobj/tests/testthat/helper/diffPrint/900.rds0000755000176200001440000000064113464152072021115 0ustar liggesusers‹­UËNÃ04i/ ¸÷âè¡öú‘H>!E=rA˜Oà#ø`ÄŽ›Änój=Œhgv¼ÞM_o„…X…(VØÞ3܉µ¸æõ}S…Æ*ȸ:î§cÂcÇ2áyžÕd|áHö{é¶Vò2šr—Ç6êß›ºÚ…_ezqŒ®·êÀ 0{Hq]îÿQj+Ý(ÊÚníù¼fo¨dpØIÊ]ü…ÕËè2®âXM y°ðhô`ƬTý¼›¢ŠO*jZóesqÊ6­*O«ŸwØŒR 6~H©W‹2©8CBÅAEãÑ›ÁZ,`õ2V‰K ˜w‹Š*5˜që|²MŒËÙdÍÇž§V:»tƒB- vFåP±z)q-z߯š;¨ÌÐÄ,böo­L6¾ëÚ®×”)ÊO÷o_)†8šVšp–}tt¦ZwäZQB¯X;ãlLÉR£Jβ/DZZU«bq×scâÎÍ8;Uâ!¿DIˆuÁή‡«·O^nã+üõ ñó …`‰diffobj/tests/testthat/helper/diffPrint/2350.rds0000755000176200001440000000063713464152072021203 0ustar liggesusers‹­VÁJÄ0í^=ï¥ ¡iÒ´‘ý‚ö$lq ›EÜ=û~³8íÆ6t#ØC’¦óÞ›—i‡.¯c‹£ˆEq»¼á–ÍØ%ÌëùJJsce’nÝ|4{¾?nЀ !у7çɃ'ÝØÁ&bäЮíן²r^ƒ€1`>®ÉÌ!„ùST"ªY©bsV¯hBÿ¡L¦Ž–i ¯=Îjæ”Þ©¢ˆ1M¶1ùNBÃ^1ëmÿ3*eªŒ©]¢¡Ë„WdDå úŸ­í}X‰RȤjG’÷ÒÏOX_“>¹ƒ1%8OÇ4'1«yùÞ›iã®ÍT‘·Â&Ì›pp[õpÛn—nvá %ôPbtv> 4уQÃ8ª)&0€‘æÌj÷ÛƒÜʤôoðþ¤°Imdiffobj/tests/testthat/helper/diffPrint/2150.rds0000755000176200001440000000110313464152072021166 0ustar liggesusers‹­—ËjÜ0†O6-ºï¢óEµdY¶¡”ìÛE¡”º S“ Æ3a<äñC|Óѱ;±Ï–|ù?«Ö÷#qã‡îJŠQ¼@˜xÓÈ˽{ˆBû `6 è[˜P`ó„ÂZ-ZbtâRßÂ) `±зPýÇBM.è2(ØÂ) ª4×ú¦ãbˆFcÌ*Yºh³TÍúêɦc—º< -|ßÂ) `¾з0£ÀÌs)*¨Mß ¢wéSyªÍî¸?žfX¸hcX¼ìuN1y‰`\ Ør˜!^ŒÅq·X¼˜+>Žˆ §Ë¾Ô¿*q2(äMÛ¾‚b3´Ù'Ö›(ÝE5 ä9×H<Ô}tSëÜ—Qk< @ÀP÷MU•áV÷àà>º•i¯3hi}üdNæ`vs:ƒ ûIŠÖ-Qmzß@]gèwÕË@â¾¥@ÀøEàà>º‹dÞ'\Œ1®x¥}h†8mà86é8Ní<06´‹â "6862rHÇ Ø,Ú®-.ÙuÀ«¼Ù—8½k.Ùÿ9Œ=ÿ:Ük diffobj/tests/testthat/helper/diffPrint/700.rds0000755000176200001440000000035313014331136021102 0ustar liggesusers‹’?‚0ÅÏÂ"‰‰»ƒ]MˆÒ?€$jØü &„ÑÍ:ûéדÒÆÐᵯÇûåh{K€AİÈ.×( İÄy·i”2'ÔÊpZ›×>ÿÚáÚ\ú€¶âO`‹µÂÔ5©æÔœ£¡A)ǵ¶ÊŒ'5©hQ¤Õúb8;Çj#Ò–»E€÷”¹Ü¥diffobj/tests/testthat/helper/diffPrint/2400.rds0000755000176200001440000000127313464152072021174 0ustar liggesusers‹•XMoÔ05Ù^@BâÎeOœVVœ8_Bý*q‰SE#ºRSÙ'~¿a§évò<¶§9l²^¿÷f<öÌd¿¼RJjWªØùÇ7îãºP/Ýýæí׺žÞ»ÏaÚ/ÏÇ_ÇYÏüå~³Ó‡3Ä>@l²j]¹íty¹¯ÆîÝ¥ö‰>ø¯C9™=˜Ðhs~®u³L:Oy&´*Z=`ÉhÉ!«Ößmlܯ5R,3¬î¶Ãµ®¨þ6^ëh†MÔ&±©E2“´‰£±Së—%J1iÂ`–Ø21‘C¢þ·¬ÿMhxôŸ£±Ž¦OЀÿRtg*Æ9´z¶Úf´äÐB =8†Ñ’C¢q5%p¬qm¶£U&®«IÐ`^Ã,Õê†{.¯‰!qÿ1_­þwhxŸöŸ¡±SçÃ¥Aÿ1_µº&Á¬9ÿÅÔÂ<äN 1²ã´Ä¬ƒ¦?¸@qõ ê`f"ú€§šŸ¯Mrh=¢®ÆÓõþ8Þ?Ý®_>oN·Œ–²jý÷&›s ö Ëh¹nµí.uÏR=Îråj¾! ¬ds^`hòä«È4æ¤æ-3¸E±nQ:Xð5h 9@¼¯rh¥ßÍÇkÉ!Ñü”~šÄÎUÏÑøµ¶éµJ~_É!QÿƒÒïÚÈ­ÿ&0œñ?GãýÒþ%Á4lß.‡äz Ú„óZræ÷ÚzÑ{Nf"æw<é–¼´¾Š!ùÚ[žŸ+þJ ‰÷aI}2wÝ›¸©¸Þ#Cã³c™Þ›ÏwC‚_±½§‚ZxªÝ’ ±}ž_k<Á´}YfW’<˜£ñyÀ¤×šà?ý¿Ç‡éõLHâ¿‚y<ý˜¯³ZrˆR…Óz±jîîÆ{w{½ ù¿^”ú÷Ácºv•diffobj/tests/testthat/helper/diffPrint/400.rds0000755000176200001440000000107213464152072021107 0ustar liggesusers‹–ÁNä0 †C‡Ë"!íK¹wQã¸M#¡°€TÍ‘åÄÓ>újã¶ÓšØãA ‘ÍçŸÄÍÿOŸoœs•;T•«ôñgîݵû‘¯îƦÇ<¦©ž?O°üÉòœ~o†%¶jù~?==Õ}“ê|Ù%ˆ6&×ú÷»1µÓ<üÇz¿±Ðcãy€ã>±©³ôÔU¢»æxZrÝÑoÇÿÏ—0¡Ù3¸'¸W5-lïnsYdeqéi˜º8ßúÜ#þ,µÒ™øåÛ¢Tìg`e-tP÷caB318œTM +4Gßî°o©©­¢icgû>z¿—ͳÞoÏ›}ç¥KË1h¥b?À ƒº šÁà jZXé€fØ=^ø_™,׬³àéhzmM&&4YS€šÕÞ™Øù³¡8 °=Ðì³ÀJ—³ƒV*öƒl¡H0ªû±0¡ÉB (Ô¢–}6fôˆÅô§œŠýåœRKs§;§F`ñ”Œ1ªû±0¡ÉB (Ô¢–}6VúÛ¦;ëe²\S`¡(-;m¬ÔDŠH¡˜Ôì4±ógËìÄ-;Ó…ìÄ2;C§•Šý0c#;©þ71¡É\ŠIÍNg¡·Î‚œkbæD2gR=lbB“ÙÉ~I{±1ã,0ûáöþ’¾ðþ"J×/S¼ È̉dΤzØÄ„&3’¹’êAsîºÊšW«öáõå-_nç[ôêîÜ¿ÿ€sŒLÕ diffobj/tests/testthat/helper/diffPrint/3300.rds0000755000176200001440000000037613014331136021166 0ustar liggesusers‹ÒmKÂPÆñÛMƒ p8¯{3)"ñ¡õ`ËLçCJ/²fOKK—¥^úÏîMÿ›‹‹[wGD 1Í”føºÇ##iɲ×òÊ÷‚À›LUò*(Çv]»ÑLLýµ‹ÊÚ?PlÉk»Ü殪oÝ)}¯•àð0Äžux×(Wâ\F'°qŠ3œëxë1馯xƒwŒ0ÆÇº5Ì]p®â®PÃ5ê±Ö¾uDú“éSøÂ ßøYµF¹Î 4ᢅ6:èÆ[s‡¤çL:ékE¹[r½s"iƒÖÔºÝô½Ûîjþ""Ë_^«%=diffobj/tests/testthat/helper/diffPrint/2600.rds0000755000176200001440000000047013464152072021174 0ustar liggesusers‹µ”½NÃ0…/Nºw±˜-Tÿ4ÔBy¤ˆ 3äxt„¯ëÚ7®@%<\ÿsœ8Ÿòr :Æ€u8\û²†\ûþy3hí}µŽ‡ñÛ8ÞËã<7?7î)©LP©©*&ö~µw‡7bÇ}—PÚÞ‹îq3Ø­ å+Tž\æ)}¸cöeÎ|…ÚZ§TT§Ä¨¤x=Ù¹DÕ.¦Äë[V•‰ŠhjûZâœ*ß7ýþèÒÄvuŠ} §L™(œaÓÚš³|CÎ3¨Ý×ÞeNïøb+UR˜bÁèùÞ/Õ3Útþ?£„¹G^ÔQgª2‘Ð -ju-qNÕfÔLHÓNmçfžQÓd”:ÿ@¬˜W^EG÷ñþé»Û°„¿Y€ï v¥Bdiffobj/tests/testthat/helper/diffPrint/600.rds0000755000176200001440000000041313014331136021076 0ustar liggesusers‹S=‹Ã0 Õ9ÒBáö[üB‰?’^à8ò#n(„Œ…uçN;÷—J©ÄôšA¶¤ç§gYÙ­€AưŒ¶ŸhÃ׿¯N)óƒ¶1|Ø›ó¦zºñ‡Qm~'¨&h‚Zîsµi[. Íq ©è”T)À¨‘ܦ4®TWˆ$£ú0ù&Ôr_Ãûc¶E?ê{vDLlšÌÖ¯ö$:>$…ô¤¡ÿíØfjns5ÒS#ù(©ò9•å|­Kƺܵ¶“8Y]‡!rIEb6¨žŠë•aÜäÈY½ñÅ}®[“i/Þéÿ 9Cî[#;îO¸¬‡ý÷S7R+diffobj/tests/testthat/helper/diffPrint/150.rds0000755000176200001440000000034213014331136021077 0ustar liggesusers‹}’1‚0…ÏÂ"‰‰;1¹…(mQi0†ŸaBݬ³“?Ýx½(dx×^¿÷r¹pK@@$ˆÈ·$ ݦš¥­ÖöBj,òÙ¾öÊ_Ã/ÀK{ðÒázßÑËÉ6 ê"?âÁº";z¨N[SX–7+Ž ŸÖæ²#Q]øFdè=­2y‡ý8Êõlz·~qšgÀuAxUÌàõtGl”Q"úÕU’[ ÁÞæ"˜r1bZFå†VqàÚáz‚C,_õ¶èqRÙpËý&Ÿ/–™ÜAdiffobj/tests/testthat/helper/diffPrint/300.rds0000755000176200001440000000033513014331136021076 0ustar liggesusers‹}± Â0†Ï´‹‚à^„l]ж¹ª Štñ„ÐÁÁÍ8;ùèâåÛT¥ü—»ûþ\Èy"!@Dî¸ ™BL M ¢=j+ùl+Õ¦á ðÒ;¼t8^~x/©±µu-1Ï6ríB))•lðÐ>1:·,OVÙÚÛLV4$ª á©l=F鬑þ5J“u§½‡Kß8½§Ã1'¼Êÿà§á±±èÜÅâóœVm::[³•›­Fq pt8pˆáo‹n×;…9—€K¯7Ük‰Ídiffobj/tests/testthat/helper/diffPrint/1650.rds0000755000176200001440000000041013014331136021161 0ustar liggesusers‹‹àb```b`fbb`b1€/ 'ö‘Ž66ε’–¹ `vNfq‰†¡Ž˜ÖӃȀ¤LríàÊL Ê`TÔL Ç,×ÁAÁPÇÐPHCÂ$¨¤)BNY¿ˆki ‹Ž6ŒEpPÊ f– ú¨¨b+XÆÊÄfªßa*À¢`k¡vcê‡Ú†l7 ÊtlV`3Åc FØ]†~uXF¬é8=Ožé¨±bD‘Û-„;üŽ3x©e:öà%l:,ÁÃÓPÁHÁ[H¡ j¦ f b& ZF¨æœÔ f4 @@ 1,2 @@  @@ 1,2 @@   [1] a b c d e f g h i j  [1] a b c d e f g h i j < Levels: a b c d e f g h i j > Levels: a b c d e f g h i j k diffobj/tests/testthat/helper/diffPrint/1300.rds0000755000176200001440000000111013464152072021160 0ustar liggesusers‹ÕX»NÃ05) HHì Xc„ˆ¤DBˆ@lHHV …Iy”÷€˜ùff>‚ÏBØn^·Mãt ‘œœÄçÜGnnÜ,"„Ôrä´Äé2VÑè   @—]Í0æ·A4×Àò @C€nºè €ÿ,š·ÉhH¢y–Ÿµ_^[ö´&kou}víÙ¨Éb{n½uÓ>oËm°n cËú)Zïbc Lö.…ðôoŒÐ«Ìq»‡¾Kíñ€ðŒ«n6ªÅꄆžáê„ÄzŒúÊíú…kËm²®oß–«®[y'4_ 4®"3½ÿ\·ñ"³º¥‘×¶¯[Æ‚_ô[}nuyƒyüQ3ˆªÔl¹™õšºõ:yBGý¶üˆ¡ÝèQMÝ–zò¦O ®Ä›¥¼ÚßÏšHÅ_‚s§Ãªš1ôœŒ{^f¢]0¨J¹\;™­pê<¢ãmÀ\F$jòÊ«ªb*&Dbˆu*ÆÜu 4ïpÕ¹L½•ôü°$/‰? úþÛ˜ ÎCdiffobj/tests/testthat/helper/diffPrint/500.rds0000755000176200001440000000076213464152072021115 0ustar liggesusers‹•V±NÃ05iØ+¤leŸ/vN ÔÏ@²:²f&>ásÒäÚ:nˆT;qÞ=_ß«û~£”*Ôª(T±âÛ»0<¨µºóvãé^ÃH]ï»ï'èå%àØ½pd8ÎÂïÃÛív¥­¨|ŽcxW¬@/Ou‡Ÿ8–ÓBÏæ+½ìå I±-ûšÒ7Õ¾<´Óð§jâÒÜ ¸e¸MÀ_Ž5Š…Nº~C¦k\\š¡º¥("âQ×sSÓ­(l¹é6 ''†Sîu=ÁuͲԋ$ñZO…ñ­Ö£²z‘$’¢WM–bjDÓÀpÈ€†›|4/@Õ÷Ɖí›rž±}Ðü•f;Ñ1pÇ.ÕqBf0'2è‘Kí"!³ èe†6K15¢id8fá"ŒÀat©0¦ö(b ö.g—§+IvÚ^J—Kàd;—…‹0‡Ñ¥Â8Z ëª VŠÓ¬•ŒH¬áĶÙÄ¢H,rbiabñ4±8&–&Okš,ÅÔ´ð?²ÿ)›X®GN,e‹¶—ÙædFaOd{Rî¬ð(…ì(ZvVxŽÂñ¬ œgï#^t3 {"Û“rg…Gá#¸STû,°Û׆7.ŒH‹hc$×Õ˜¡ºÆ»kB0\#|nÊÔá 2¤ÔK(d‰1'oˆbÄØ‚'$Œ>$X˜€æ0BÍcÎIÍR¼`!PQÄÀðÏWG¥diffobj/tests/testthat/helper/diffPrint/1100.rds0000755000176200001440000000062513464152072021170 0ustar liggesusers‹ÝU±NÃ05iØËàQÛg'‘¦~’Õ‘ ó |:Âï’8n݆ˆ„‡çsä÷|¾çS^.„•XT•¨¯"\‹¥83­¼1á>b$ÇO]<52.…‡Ä¥ð<›{wºðø(ÝÚÊ;Ƹ8,×3¶+ßnã?t»ýZí"h€ÀîÆ}¹ÉN@z»ÞÉ>ñZGÚF0M‡Hš ®Ë¸*îÕ´,,IÜí~ÅY¥UÔ´i4} Lh†ºªf"—fTq8\BÅAEcYÓ·¹†À@`q#¥ ®¬H÷¸Mã¿ú–½öo|ãÍTÿAßRç)íÇSÑ{ÇNóJg÷E­2%D‡ËÓ™úxLâZ¸eùn**Ý*;Å+Ê|«S¤:;(–/+~L/{CšõÔ iÜ jPIkgéeïªË¬T,*Dp•8r?î¨ßôc¿J?x‹3³ý8Ô;壿øq¨·ïGlÉY~ˆeõÎzÝÅÛë{œ.ù~B|~¿)ÒUdiffobj/tests/testthat/helper/diffPrint/2100.rds0000755000176200001440000000063713464152072021174 0ustar liggesusers‹­VÁJÄ0í^=ï¥ ¡iÒ´‘ý‚ö$lq ›EÜ=û~³8íÆ6t#ØC’¦óÞ›—i‡.¯c‹£ˆEq»¼á–ÍØ%ÌëùJJsce’nÝ|4{¾?nÐtûEêò¦È´—+%³…s¿4ß Ç¤j‡˜×³ª`ZÔ™<ì²e'œºp†c΂+{ÝèÑÐÕO0ÙÝ¡áaØGÖR‡ia<œÐž!똡p…uî´°N^ï¶ Y"p0Œðwðæê \@ø?ˆh oΦNQ_¡¾6!ÓÂé0ÂßðE®&²ÂVŸÞ –è(ÓL­v›]Ú Ÿºp^Ðsç§Àã&ž ÂãSáÍÄ“Àñ˜Äs»—Â<0Ã|Óþ!´ß&ÌO†V§¶{l Ч.¼oøÜÜ„/rgc ö _‹%¦Ã«ð¹¸ù°6¥¹¡yŒ*]«­ZuoJâž¼-ûÐúÔ’|™·ÁÑØÓð #mäVW×EWþ_ ¡ïŒ }Jf diffobj/tests/testthat/helper/diffPrint/2370.rds0000755000176200001440000000106413014331136021167 0ustar liggesusers‹­˜ÏkÂ0dzêeƒÁî»xÛeËÚô7Œá}; dl cˆ+°*VüóÇš¶³ñE/Òš´†ï·Ÿ—ä½àçcÌa=ÇaNOuoÊÛë³Ë²}¿û~þTÞÓ|PõåF¼¨_œ{iâAþ¼ññƒø:ß )¢|8x÷^2x¬›òÑòª¾£‘”‡ÁHÝüÿ+GÙz²à¯Ùr¶×aªß|ÈŸí¼ô–mÛ!õCõ«2­³©ÌŠV²1ü> Ð)C+é#¡2¹¼rO‰Ÿû<C<ìû.U[dÛU1!p¡†$i—€6O!—Û%jØ—oÚÄK\þù\¨!IšÄ˜6äò ®ð|.Ô$Mâ ÍåîB®¨Ëuˆv·#—ß%jØW mb“ î/}G¨Ùe›BNW‹Õ†À…’¤I\ ´‰´ˆäj7ÚV\¨!IšÄ•š6Fž‡yCÏÌÖ\¨!IšÄå¹À'Ô"VÆþ ´•£³jH’¦qÁú™\ ’™­ç 5$IÓ¸`ýa]>ÆÕž|ìç 3$IÓ¸`ý´Š"ÏGÚb*Ú;¹™É¥œ’·˜!IšÆëÈ8_1rRõÔF·â IÒ4.Xÿcó<Ͻ¡–´„nÅ…’¤i\°þëPðÔàÒ7wbÉ…’¤i\°þùðØ|µ+G¨ÃŽfH’>àb}§´¹hìz‹lY6×Õ+õßc¿<úóûvdiffobj/tests/testthat/helper/diffPrint/2800.rds0000755000176200001440000000045513014331136021170 0ustar liggesusers‹Å”±NÃ0†¯N—‚Ø»x,’‡ÚNZŒ*”‡è€d¥•jèÀÄÈÜ'F\×µ× 7Øw¾?w_ÎQž®€@AbïÞâr Cá¾k)ÍWe¨õ7ëíDTŒ Få]›h ýÒÝï;{?âï÷£e@#½dÓ¨4ͲCãoEä0óuvdÁ}i²}ꬲ“=&Ö§Kfòÿ'C‚u®^±yyÃíÆíO?¿3`Õ¹diffobj/tests/testthat/helper/diffPrint/1400.rds0000755000176200001440000000102513464152072021166 0ustar liggesusers‹ÕX]OÂ0­ƒML|÷¥Aeí.1†@x3!ixÅÏ T•'˜¿ÍØv[×;„hMì’v§]î9÷öö6ÐÛC9¨â8È©ˆ×Þ¡*ÚåãðQŸñ>Œ±|¦³c/ÁfšÆäÇçŠÉ—LÔSÛ âv{®Gñ BÌÑVMÊÌ<0lÄrŽ1¯ßÏ¡?”'s¸;‰œí^t:)2L®ÇQ±›l¿ðŽ1"Âjý„}/±c¾•8—PÔ´cQt Ð@#€®ºè ;€îz((h РG€žz®ÂîÒçRœ˜‚8M@/Ízè  w€EmdšV{¤¹ >߈QM6%)cïnmè'«  Õæ«À*&o “aº•µGe?÷…#/O»åù(Ø“–dæp0æÏ4W’-n Çj>Ú±ªîrJÝ&¿ËIȇ-/ó•wy}‘@l²j]¹íty¹¯ÆîÝ¥ö‰>ø¯C9™=˜Ðhs~®u³L:Oy&´*Z=`ÉhÉ!«Ößmlܯ5R,3¬î¶Ãµ®¨þ6^ëh†MÔ&±©E2“´‰£±Së—%J1iÂ`–Ø21‘C¢þ·¬ÿMhxôŸ£±Ž¦OЀÿRtg*Æ9´z¶Úf´äÐB =8†Ñ’C¢q5%p¬qm¶£U&®«IÐ`^Ã,Õê†{.¯‰!qÿ1_­þwhxŸöŸ¡±SçÃ¥Aÿ1_µº&Á¬9ÿÅÔÂ<äN 1²ã´Ä¬ƒ¦?¸@qõ ê`f"ú€§šŸ¯Mrh=¢®ÆÓõþ8Þ?Ý®_>oN·Œ–²jý÷&›s ö Ëh¹nµí.uÏR=Îråj¾! ¬ds^`hòä«È4æ¤æ-3¸E±nQ:Xð5h 9@¼¯rh¥ßÍÇkÉ!Ñü”~šÄÎUÏÑøµ¶éµJ~_É!QÿƒÒïÚÈ­ÿ&0œñ?GãýÒþ%Á4lß.‡äz Ú„óZræ÷ÚzÑ{Nf"æw<é–¼´¾Š!ùÚ[žŸ+þJ ‰÷aI}2wÝ›¸©¸Þ#Cã³c™Þ›ÏwC‚_±½§‚ZxªÝ’ ±}ž_k<Á´}YfW’<˜£ñyÀ¤×šà?ý¿Ç‡éõLHâ¿‚y<ý˜¯³ZrˆR…Óz±jîîÆ{w{½ ù¿^”ú÷Ácºv•diffobj/tests/testthat/helper/diffPrint/1200.rds0000755000176200001440000000056213464152072021171 0ustar liggesusers‹TÍJÄ0Ží^Dï{ÉÙi’¶)"û>€ "º X½xð´.&iÚN~ ²…NšÎ7ßLf¾öá‚R²(HQšÇkm®È†œëõ~«oµ•#µÏϯ‡ÃçÓ[ß÷7ã7Lj\|¼[`܇ՓÃqvz׌û=UCõâ.Gµ:Eètñ_[%w£5GkéúbB« mêau,ì'†ÎýpTñj˜K¦\ßµÄyþ‹8 }³]’3[{„çfÃfÝͼcà÷Ï2 µ^)S¡.÷c"wæn½›Mê¸_Qß:ÄÞ™¢X²omqJ–Ì“œ]ê8Öt w`‘3¨IiÈ’ ˜IÖ%jRPg`'(²”ŽýÔ!ìOÍ)@R1O›ïÓ4ç&¨/¼ÌyT¡1$Ï#2°ˆ Z®“œMFȦМgŽ»|ùÐË¥}e~·„üühÏ‹‰diffobj/tests/testthat/helper/diffPrint/100.txt0000755000176200001440000000300613466141734021137 0ustar liggesusers< f1 > f2 @@ 1,5 @@  @@ 1,5 @@   [1] 1 2 3 4 5 6 7 8   [1] 1 2 3 4 5 6 7 8   [9] 9 10 11 12 13 14 15 16   [9] 9 10 11 12 13 14 15 16  <  [17] 17 18 19 20 21 22 23 24  > [17] 17 18 19 20 22 23 24 25   [25] 25 26 27 28 29 30 31 32  [25] 26 27 28 29 30 31 32 33   [33] 33 34 35 36 37 38 39 40  [33] 34 35 36 37 38 39 40 41  @@ 11,4 @@  @@ 11,4 @@   [81] 81 82 83 84 85 86 87 88  [81] 82 83 84 85 86 87 88 89   [89] 89 90 91 92 93 94 95 96  [89] 90 91 92 93 94 95 96 97  <  [97] 97 98 99 100 > [97] 98 99 101 < 100 Levels: 1 2 3 4 5 6 7 8 9 ... 100 > 99 Levels: 1 2 3 4 5 6 7 8 9 ... 101 diffobj/tests/testthat/helper/diffPrint/3400.rds0000755000176200001440000000037413014331136021165 0ustar liggesusers‹ÒÉJÃP€áÓ¤-áp®‰´ˆP¤Q[kǨÕâÂ!ޱvH¾ø'U¸«/ÃáBº‹"âˆëfÄq“Û%N9ÉÊ×å’Fa‡£±Î;~êM­ùAà·;ÿªËeõV ºšžy˜_—T{Þ…šK£æ ׸Aˆ[ÜáÞØõõÓG€ at³NN>ºñZJ©@#Ã]¹~ÿOïÊe Æ€%f¹¦0‡”ÏY¨•æv­ŸÙ¾†UJ}ôXi0bγ¢úNŸN‹|Ë){ CG6þ‘ÕªÐ6¼lä}¡¥ë\4dÓox÷?¥Ý<œE-UÞtG–Š„{~ç'lèI-{ ‚«bÊ3Šõ3 ïÍÊD íLQW–¾gÞå”ÔB ¥£~d “æ r²Ÿ6òÄFã¤g HyΜwr»Þ)­lÉüúïHªodiffobj/tests/testthat/helper/diffPrint/1900.rds0000755000176200001440000000102613417754216021202 0ustar liggesusers‹­ÖÏoÓ0p¯Ý$$nvyÚeÒ4BìühS!4‰#! Ò.K³×a”%v˰“®yq)J 94vÒ~ì|íõëSÆØˆÇ'l4¶Íçæã;eOÌùÓÙmâ*XK%ç9½\,°Æ2CsÔ?K¨æß1Óê æ+ýØ´F¸,+} wiž{øc•æw³³Û )6ö¬±_ÂÛªXV%–Îo–˜ITç3¸ÈVµF_€T`Ha‘fºª©ðÙt‚âus 𶬥òT{áØƒàañf‹‡-žýüQââúøŸÂ«ödº½0l×ú8¿\¦¹÷ËýmÓù"ïMû#êîFÛioص ÐAÎÁyüÈãÛvàEÝ î…Û¶ï‰æ¬PW*Ý‹ ½„àþ?áÁ.>!¸ xp(îâ1Á»ˆ Þe4v3ï¢È@ÇÄÿï’8Ÿ¸ø¤‡“ÌébØÖX+™U¹­÷=øÔÅc2A Á“]œnENñäPœûŽ‘ šyHò²Ûrî–hÜç >læn‰FdÝœ|ØÌÝI½­“X„}¼µ¬d)³ý[‘»%‘Ù ò½7·‹;wKtÒ{+úïv‹°_€»%JcÄ38]Ðé Ü-Qº[œ™Ó¼ÄŸqv:2øÉfqŽ¥9=k.ÙÿŒýú ‹½ ndiffobj/tests/testthat/helper/diffPrint/175.rds0000755000176200001440000000035613014331136021113 0ustar liggesusers‹Õ“± Â0†Ï´‹‚н·¹Tm’ª éc¥£›qvòÑÅË¥Ö(ÕQ0Ã]rùþäøI "rÓ„B1Œ)ÏÓFk»£h,òÜ^–Ê/Ãà…Ý÷xápýŸÑÎÆÖ5ê<[ãÊ'Z"+:¨J“[WŽø,øÓšL¶Tn„GÌÑke²»v”!éÖt.½ãÔOëœð2À«WX(¡DôÖ•’K_oTP¹ÕW\¸v¸Àña^ßà¢çt𼆜fBÊŸ; ± |ÔÉ¢ÓñLiÊ%÷›nw%â½hdiffobj/tests/testthat/helper/diffPrint/1500.rds0000755000176200001440000000035613464152072021175 0ustar liggesusers‹’; Â0ÇÏ´‹ÁÝ¥ƒcÓÄGA¤£³“:º5.:9øÑÅË«mbƒ&ä’ßýïA.H’¨ã— R˜â~ZÆä×JæúÜÞ+nìn É屃¸†6dõ”çVÖu¾/v9nÎ]Ñ'—‹2«µÔw˧™BЦé¯ÏhLOG ´|½dõÎ~¯´h\u¦{T[a m¿‚¦”]¦¢è•‘L½ åh¦ ,¦WÆ¡¾òHá߃êW>Ò«ª}¿9H rË'íõ†ÛL_©ïðþů< diffobj/tests/testthat/helper/diffPrint/3000.rds0000755000176200001440000000047013464152072021167 0ustar liggesusers‹•SÁJÄ0Û=¨ x÷zRÜ–Ì&¶[Ùoð$,Ê’¡YצßáGø¡b’mtÓ¦ÅC&y3o^òJ_. ‚8Š ŠÍöZ/ç0ƒ ·7kÆä£^KIì¾ ÇÑÐe.Ÿ~9Üp“œNíY7årµ"8ωN#ÃÍœÿvºw˜cI¥»ÅÛ®ÍFì[׬ê÷½ð{H3$ XÚ—ïš«Z4Å‹ŒÚp“m)z°¯í{z4‘Ë”eÍÅ=²?{¸Ô2¸àKƒcŸ9âË!õ‚­§HÚgØâ, YÉ–yQ†úŸ6àÆ.3;ƒŠ”^¼•çAÕ¶Íí<ÙÔ•RÉÝ” §rBjk|%ɶRm-“IÃOæÌ"­v֩ƵØéte!ó{|ÿGBÌüùdiffobj/tests/testthat/helper/diffPrint/1700.rds0000755000176200001440000000117113014331136021162 0ustar liggesusers‹­˜ÍkÛ0À5§—»ïâSOó¾%£÷î0(cƒžJj:Cœ”8ôÏ/“m¥–žä6rìCüýüÓ{ORò÷#B(C«,CÙª;ül>®Ðº4ûß_î(m¾›OÝäýq½¯Û¢.ÌÝœÆYóãµq64NiüÆ´"šëëÅ*ÿ6ìÌiâÖ¿Ëñ}»S]6Ç÷»­žî7ÅÏjûxøgOþÔæøWuo 'ÃîKOÕº®Ú±!Ûxí÷´¹‹sÐý¼ÀþeZðñ\°×ã² ý¾­»ö> tzû›†UvÛr@ßD Y¡!°\È@CÊÎú†tÂPB qt¾! •¢‚ªå€¾!›0ˆ]à˜AɆ,ÌR­¥d9 oÈ'ê0HáÏÈR–Ñ,ô Å©†cžœg(BC))Ö|9 o(£†24tÊÂT©®öm½Þmvû %4”1œô UÔPìM w€<ÍPACÃzß0 èê Ã`¶À.PÏ7Ô¡a|¶˜3~ m‚² nY0g®êò$Cxif%] á¢ÆÆ00T.P8Àĺ@C‰á9@`56†òM v€©1$ÀPX BÅr@`56† i¼Á[8ICºAà¹Þ?ÖÛz}Òº”1¤Ñ:œ†pQcc¨`—Žãuν.Õ‰†,¨CÍÒù@`5v> ~[”.pÚHO2äa–¾·òNC¸¨‰g)qè¾J4'eé9@`5ñ±té8 nfN2”al,M¢‹Ì?XðjSmÍîS©û+¡—ÿC­÷j¥diffobj/tests/testthat/helper/diffPrint/200.txt0000755000176200001440000000277313466141736021154 0ustar liggesusers< m1 > m2 @@ 1,7 @@  @@ 1,9 @@  Header row 1 Header row 1 header row 2 header row 2 <  [1]  1 2 3 4 5 6 7 8 9 10 11 >  [1]  2 3 4 5 6 7 8 9 10 11 12 [12] 12 13 14 15 16 17 18 19 20 21 22 [12] 13 14 15 16 17 18 19 20 21 22 23 < [23] 23 24 25 26 27 28 29 30 > [23] 24 25 26 27 28 29 30 31 32 33 34 ~ > [34] 35 36 37 38 39 40 41 42 43 44 45 ~ > [45] 46 47 48 49 50 51 Footer row 1 Footer row 1 diffobj/tests/testthat/helper/diffPrint/1000.rds0000755000176200001440000000060013464152072021160 0ustar liggesusers‹”MNÃ0…MÚ HHH,»ñU¢¶Çv"!TXõHV—ì0Gàèˆy“?·iC²xG~ŸÇ3Ó~Ü)¥*µª*U­>°<ªµºå7m’sù…µÉZâ·6ž{ /å×ÁKù}±÷‰w†¼ßë°õúY”çà:Ça“š]ùÕã‡vwÚš#‹…8AüqÜWòH·üö¨»Ä£eNô,®f ˆ´›ñ†Âkx¯u°E¸°$;ñN+.”8R̶iÔ] \®ûºšú"¯èÂ?<ÙLq†7Þ­)—Ñ P,–‘f¼ÍèuÒ 3ñaœŒ„¼&3qé´dlq_ÔÞ SBp¸¾žiâc¯G÷½Ü-€¡i÷§L†Š9ˆC߆:à—tpÊ“-Á-æçW̸•üLŸ£ =Ô¡3Þ/Êï²3Kxçù¿£¶rMOñè aêH¢°(¿Sü"žZW̻鸫¯Ïo~ÝË'üe*õû^DNMdiffobj/tests/testthat/helper/diffPrint/3100.rds0000755000176200001440000000043513464701604021172 0ustar liggesusers‹SÍjÃ0 V“n°Á`÷]LN-k‚U{I £ô1 ¥‡0¼“•C“½IbO9æ8^›diK«ƒe$}?ŠÉúïBÂúúl{ÃƒÍæe#½ÛSs÷¯¢ÈIOvºÄiS Û‘´<Àd 6? óškÛOiµb8“̦áY®öüßݧ n› ׉1Ã,á.þ¬ºrl—ô°Kw㩤X$oOõ+Š£uIV çrQ×±Gëýîÿû[ÖÄ%i¦Œy—•k©ÄÑpó¾›Á·2Œ'òªÚMfчÉË2šžqÑ8¹6¤Yåè3/+CÑÅ›`ãÀj޼vhtaÓ“+Õ¿ ÀÏ/ø¬™Adiffobj/tests/testthat/helper/diffPrint/2200.rds0000755000176200001440000000132513464152072021170 0ustar liggesusers‹­XÍjÜ0V½¹´È=ßzY„eËPJ ‡@-ä“,ÄIˆ÷ÐSž Ï\*y•õx4’&=Ø^­¾o~53Þ__„™Ød™È6öñÌ\¾ŠñÙÜoϯ«jüf®ý˜ÏÏ»—Ý$§Ãý1¿éñû¢†8Y—fG3^\äåVéÜÜIjËÁÜèxÿž_÷Å8_^çk¾,˜­WÃó̓ü1<ÞíïÝ—Ÿ»Ûýý²é(õhÞüêèTŽÜRKu|®d å¿‚d•¨e€!‹Y| óÅüZaŠy‡–íz¹’åÚ×0‡4ÚÐôš N: SƒÉTT'ŠFuKŤöƒY`CÄ„ Úßö×¾âUÔ~ŠFš.Bƒìo1E»2¦$ìçC¬©²ø$«÷ðà(BŒ«*‡‹k½^-q¥hl\U„×5\¥Y`GÕ56$l?®WÎþ+ÞÅí'hôØÚpi°ý¸^5²Á¬(ûÙ, ×!s’-%‹ Á½Yu[(ªç¢ÞœØˆmÀ§ŸîM|’õ†ºöKÇ>|ÊâCœ¬?~n’5Wá¹h^-\ªyÓÇklîš?©ž¯@+Èšç)„,¶²T#Nš§Þ¼ƒrŠ6Ni‘S­Þˆ À¢måC,¯õ+|´,>$Xß¼ÖCÉäõõµŽûÚkýé¼âC‚ö{­ßŒ‘kû•§8aŠÆÚßÇí÷ ‚©È¹IÍp§eñ!¸¾WjÛ±Þ½q}Ç']ƒ—E÷C6$Ý{‹ãsI¿S±!áÙÃo©‹º.7qRQ³G‚ÆVÇ"ž› Ÿï¿$gO>˧º– 9ç±!a_ã Ç—ywÉ©ƒ)[TÜ×õ<üÞ ^†ŸwB"ÿLÃþiºIÊâC„8ÉŒ¬ONææax4·ÓyÉþ$Ä¿ÿ•».Ý)diffobj/tests/testthat/helper/diffPrint/2380.rds0000755000176200001440000000115313014331136021167 0ustar liggesusers‹ÅW¿Ó0öµ·€„ÄÀvKÿ€“eÇNš „n¸…N:!ák£rÒ¹ Iî üÌÌÌÌÌÌÌÌü!Ÿã¦ïå95-’¼ûùûÞ{þêžÝ'„ŒÈx|@FãÛׇæöˆ’{æ™ ¡Ÿ˜{¤'ö}¦Òìœ?ö'¯ oëe>Ký´œ*µÎûMvk¿1C}r2áÇÁÄ<:.l3‡Ý3ì·ÎÌ1bzNè,‰ã9à?¿Ìrk‘½2Ð1¼HO²Éš}¸–Y5Ù ’úmRÁà¸ülæÂí*Ò-%´·ë}®>΀øâLâXÖëqÊÖnë 0sÖÌêw3+@¥äg}µ8ƒò³Ö+µØDŽé¯¦-(ô´´åvLOUž]/¼)g(XÁÏ£!â'?B3wª¤l`UTwˆN}¶J—qøÁBž¦—7qz]IyÔZIÏ·íY¿RÉqæÁ.8¿LVi®.V×ÐÉC:ELQîÙ©ßÌô³õ|ê†Úw˸yªr\Ÿ÷9Dù.‹gÕ„9d_BÖ­h® LšG%1 ²V^‡öÛ^ж¨W wèʽ.>eµ­¸‹,÷Ó"AC¡8Ë! ø>²|We”AñÔã(Ë•rYî§‚"t€ÂÐÞ_v« 7À¡40±Jiœ55X·ú©Cá^÷²=Ù_=Ù€l?ºUWŒ±(rùµí ;êŸûê`„{mÀýý?)A†!‹ž v«eUMhèŽnÜõ³!‡#ƒûÀá_ÅKóx`]·: ùóC7diffobj/tests/testthat/helper/diffPrint/2383.rds0000755000176200001440000000165713464152072021214 0ustar liggesusers‹½š¿nÛ0ÆU'K èÞEÐ ¦DÊP´y€E3¤@Ð!H•D€í¶‘¡C§Îû´E)‰²¥»†LJöà?´tüéxütŸáo¯‚ ˜g“I09+ß¾ÑOŸ‚óà¥~½{{$Ëú9[†Õûb]l¢M=`yè/åòãîiι~·œgfûª¿N—¡x§d¨_qð2ÂþÈ$=t¤‰ü»{ÙtÙ\æO7‹ès¾ºß>ÔWX\?¶ÕAû×f‹ó%ßî>ì2,‹I^ª#T$ºÃÕh©T(¢äCÉ|1ä“Qù¦=øPDÉ#B¾?Œ/±ðÍ _܃EÔÁÚT £úɨ¤…*…TQé¿nµêa%P–ºšB‚t‚ô(‚d‚$˜Y`mxÌ!AZVö‰2 Þ¿cT¢˜†`.e´¬;R‚J¢É&˜A@EÏä©ÔHTÖL’“íFAEÑä`s0ƒ€Š¢Ñ|¯…÷2_*ŠV!6{t`*ŠöÂ9xbÍ©íF©ž¯±v_¾W•P@-žŽ¢Å¸—:%î–¬¿qpg$`ý AÀëuFzN1/ë‡N4/ë‚Úýl§OÑûH¸Ò;›Qzì]† à¹Ç½¤hÇT?ï]hés´áhÍ¡:s„–ýØ>§{’ÝÃRa“‘ Ù…q^ȳHîêa^´v>*{i4íòŽœ5r4ƒðÑEP<° |ÈïHîwêaÉZ,;]e4Ð9>¤’»¡†.‰.ŒjÕ°Éì© Ò É½RÃG—ÄÎGF7½4}~™@j"¹“jøhó¶×É~ê‚€“’¼{lèÏ {t'NJr'UJаóÿ$yç^J˰ó×$¹k ÕÐ× ¼’ä^©”‚aç åY•Å5ä¼À¡Ê®2Oæµÿ¬—Oùm‘oèð!–ŽPSd‹ieioÙM¾}ÜÜÃ7|Ó¨ÚbZYb/¸õY/(Î,Ò‹J„3‹òb²áÌ’z±@)qfiâ9_oŠÛÇÅãú(/;ëo‰ie™{±Ð>̰(7–Ì‹…ö\~,M‡äÆBû+Ã’8²/Y\ó{±Äƒæ¥‘Îçb}_¬ŠÛãîG `‰w·iÓÎ"½X$ÌKæÈ¢¼XÌ‹pdI½XR˜—¹#ËÌ‹eóf Î'šå…a:[ä+ýòº*ÿ ÿþêW^s;!diffobj/tests/testthat/helper/diffPrint/2900.rds0000755000176200001440000000044513464152072021201 0ustar liggesusers‹ÅT=OÃ0=œ.€ØYnL%«ÂMj„P~’$„*Õ Ð‰ßÀ/FÜ98ýˆºTÜp>¿{y¾{CÎ@@!ˆ‚ËKJç0S:ﯼ1á–² ëÕò}]>—J¢•XO%Îf]—Û6ÜýRmOë·åG©nœD3M,ФÝR…¦A%Ò‘ÅŸb"Ø]„¤ó5œÑ]‡XFÔ{Õ¶éÚ7p#†sX±å¥j³­¼ÔëÙ| ÷;9¡›W¡Åz߉*Û~yT [Nõ–v“ï¼3â'Ÿ{ IçqÔ;}€w‡¼²å„Æ9.ÆÐ™šá9§Å?9aŽå„Á ݸ&sÂ0\qrGp&‚tN’^±zy¥ã"BüKøþ ba-diffobj/tests/testthat/helper/diffPrint/1800.rds0000755000176200001440000000101413014331136021157 0ustar liggesusers‹–KNÃ0†MÚ HH¬Ø°ÉåWüêX ±‰R#Z©©í183ÂIÝ’ŒqšE'öïÏÿL&~»BhV¨˜µ·7öt‹æèÒ^—wïœ7ölš²»_¯wxwx>ì;Ñ<ž†ˆÃâæz¶=d³X”ôž‘Ò^ƒÒ­FfÇãÚ¦!Íà¥þúØà§zû¹_¹Æëz¹_ :MâæúúeßÒ,¼ëQa:|ÌqÕŸqè!é^ZÒ ™( 2 l I21Èd‚L$ƒ‰G˜cI&î3iÍ%× ™(“ˆ0I(F“LÂ1JI–‰2U‘|òL—I¦Êg"ÁØÉ &™ËÄ“LÒgRŠSS%d¢L*Ȥ|¦t>)Ȥ">åä“2I,¦1iÈdÎf2&¯¤sÜøLáZ“ã”(—O ØH>õeŽ9®© ñ`>eÅr瓆 Ti&á哯Nå0ÁBîê“·/Hÿƒû2ÇØý¦°‡cÇÚâb’Y±ÈÄ™`!w£>))ôÝ…|‚ûÇçzÿ¿<4ÆöãCÀ\Þ®‘bqº'ÃÿÃä!Í ;×…›s¶©·örÝ=j·þýþCÊ diffobj/tests/testthat/helper/diffPrint/800.rds0000755000176200001440000000037713014331136021111 0ustar liggesusers‹¥“;‚0…kaÑÄÄÝÁ®&¨ôH¢†¿`Bݬ³“?Ýx{y´<|$v8íår>Úœg„J™Â¼^Rêhª®õ}Uew8¥O­ACüÁ°‚^¬³Œñ@±hÄ À®†kÊ4Ô–T¼Fdi›µáØÝ9t ”¬ñc—·4e$@ò!D8Á’Ë=–\sˆB:i\±‘t`¨n8Ïß´ã¯tòîkꥃM.Ü3©úLÖ/8ÑÇÙ°“–)ÇsÁÉ>.ìîÎ~bâ}ìêkìħ`˜ÔFïz¹áÏ`.UóóG³B©/diffobj/tests/testthat/helper/diffPrint/2250.rds0000755000176200001440000000132613464152072021176 0ustar liggesusers‹­X=oÛ0eå,-P {o] A”¨/ (ò:ÈЙ‚FH DiP{è”_Ðß\””ëüx$/@=H2Í÷î“w'ÿ ”ÊÔ*ËT¶rçöòI©÷ö~wqSUãg{íÇõô¼ýµÝåõaÿØßÌøå1ˆ CfYWvG3^^®Ë6k{g©‡pãÌûçâ¦/Æéò2]×Ë‚Ýz=<ß>æ_‡§ûýÃüåÛönÿ°l:Jý4¯~éôÜRçúø\9?/òß Y%MÞ`ÁÈ’C_Ó|±¿VH1í0y{º\å婯ihŒ¥é#4AL@§ÉtT'ŽÆŒsKbRûÁ,°ab"‡íoXûk_ñ*j?Gc,M¡û[¤hOŒ)ûåÕ!°ÉMB–²zHŽfdÉ!Á¸ê8æ¸B¹.qåh\\u„ëV©&¯ °ãêš¶ëÕl‹Šwqû3¶.ÜA´ëU“W$˜g¿‚²°ÙH”l9YböfÝml ¸ž ½9±mÀSMƒÏ÷&9d½¢®†ýÒ±_hǦ²äYÖo?7Ùš«q.šV‹9Õ¼éã%6wMŸTÏפ€lÍó B[Eª1'ÍSoÚÁ9ÅX§´à°Õ49@¼­rÈòZ¿&ÉÇË’C‚õÍký44‘̯§hœ¯MÜ×^ëOç•´ßkývŒ<µ_{Š3ö§hœý}Ü~o00$˜šÛåÔ,A‡p^–‚õ½Ò›Nôî•ØˆõOº!/-šï‡bHº÷Çç’§C³‡ßRuçÜĤâf«ŽE<75žïš¿dgO9eá©®ó†‰óݯñÓñeÚ]Jê`ŠÆÕ÷5Á_??¶Ã—éçÈ»aÿsw›”%‡(u–YYïf™«ÇáÉÞ>NKîï ¥þþq-Ê5)diffobj/tests/testthat/helper/atomic/0000755000176200001440000000000013464162560017421 5ustar liggesusersdiffobj/tests/testthat/helper/atomic/2300.rds0000755000176200001440000000053413014331136020510 0ustar liggesusers‹”ON…0Æ+¼&&îÝô.:-F|Œ:1êÄè#–µ\wh—þS¿uÔIÀ°,KÀ’µ^ïÑNþ?—g¢—Ô9al3î~2”ý%ÈØbæŠYSÌÚŹ™ºPžÒïø]$na3×Ï.¥ò9›òª__Þ𸠯Txõõ ¬[Åã diffobj/tests/testthat/helper/atomic/900.rds0000755000176200001440000000037413014331136020436 0ustar liggesusers‹•’É Â0†ÇT ‚w/!'kE­ˆè[¡÷5îVO>ž%þ]P£âø2™tþù3Ðv‚ˆŒ3¼c [Œ¢GtÓÒ²T»­¸nÉ|ƬU³Np÷i¡¢¤7mIµ¾k‚ºwQ_VÍ&7sEŽðÅÌk}WX?)B§sZÚuúõ}¯ÓAÍ¥éMýO¢#F]-ëiY_ËZ6Ô²‘–Eh÷Çi\þü"1\LÁ Ì °+qïLækíµ/î·kTnÀìÀ¸àŽïÜ¥YùWKep„¯0æƒBÒ¿ò~h¢Ë;RÔëdiffobj/tests/testthat/helper/atomic/200.rds0000755000176200001440000000062613442554006020437 0ustar liggesusers‹Õ•ÍKÃ0Àc·‹‚°»—GÏcºu+"O^<èE2Ò6kMÞHÛÍùÇ‹¯]§[ÕÃ\'ÊËWó~ï#<1Æ,Ö°,f5òa‹D‹5Ù!õÁÉÈqÔÉ‚bìG¦Ó]Îëjk¬¾º|gõ Vo/¬GÒz®†Cè¶8%ÙšìÚ SWÞäÓÁ™uŸ–ëv$âaŽ&ì1¤÷+V½¡úÓ¬úl(YÏ›7=Ï\¯Ì\±c߀á¤N8 ÓHðä 7F€—…´3Ãx&uJ(”‰€4â)()H5Å$‘^,J_—|wÿÁbNЄ"%|îG"€™0‰D 8ßðj×uåXgjìcŒ&éÀ=*á¼]œò¤æfA§¶çW8@ßõÝÕ-D<¡XøhL6MÉ"§‹ü·W!ølÛöüܘdÚOÉ»r~ùw6ò"ÞBØõ\ü­üÿZûgü½äíõ©ÔUÿ^Ì£ŸÔÿ÷·–±¦EüƒÒŽF,4uÇÅRþ3öúÈð %£diffobj/tests/testthat/helper/atomic/2520.rds0000755000176200001440000000120413014331136020507 0ustar liggesusers‹µ–»Ž›@†‰½M"EJ“* °3,#EÑbðu}¿âM)]H™2’GòÙ`¯±¹®KŸ1çœÿ?3ƒ‰Þ†Ñ2Ú­–ÑjN?ðõѸ3Þrüõé«ãÄŸùÖ±y<ï‰ä¢Æ‡eüå+㾬›VÿÆínüøhŠû“CE±Cæ¦i¥?YŸÚŠ“Sß7ÍúfÒ‚®Ÿë1Ò‡ ý˸+ïeùdEUù*Tš6v À…ðÌT²NoV•Éøy¢K²gòÖŒy™k„—'æ0ƒ9,`™íÛëcs« Fô ô ô ô ô ô‹ƒ~]\½QlÅ ­P»† laÑi=íOë陿Sg†JòÉx:.Ê—Û#ÛbŠñjãÕÆ«W¯6^m¼Ú^qÅæVïtØ BzЇ aÔ)®Þ(öæ™"îUÅ#âú™R3"×ç=ØÂ"ØÃ3øO… Øg£ØÜê’¹¬Y©ÙÂÌ™bÎs¦˜3Åœ)U\½QlÅ>PBzÐÒ=0ÒÒCFFðÔÙùd<ž–åËí‘bÍ*¼*¼*¼ºxuñêâÕÅ«+‹{Ô(6·ú˜Â æ°€%¬`]²>Å^ïÛjºˆp^½<ºá±j<ž]óèšG×<º¦éšÅ>ÅæVŸ„t f0‡,akØ„ÅÕÅVìͺиѸѸѸѸÑ©V²%„% þ™o÷AI¾$D§oIöš*ÃÛð¥´ï2WQæjÞêÍ_üþ»æ« y~,P™öʾ,,'s%3Wê?©4îZ¨|“ªmÿøþ“ÃûãÐá…Ú0þþCÁýk diffobj/tests/testthat/helper/atomic/700.rds0000755000176200001440000000061513014331136020432 0ustar liggesusers‹½’±NÃ0†MÚ$$v–S„hŠêÊ U ns­Cc;Ä©Bž„§ã1×$ЖZJìÏöù~Ÿÿ»!„':ž'¼ÎjxD¿}Ñ¥Oˆ§ãQèkú4”ã‰ÊÎ‚Š·6Úꛯఠ[×ù#Ú|¡‡Cè@ݶt«ƒ×!ýv!u®7~×Á¹†Qï¾ÚR.ùÒoÂ3š0Š!£)£#å×é(ûgxi¡©*$UªL:„Äš ±‹™‚ÜB‘É$L¿Xßö¥œynù’÷¼j(Y«À’Ø ƒB¡!%vŽÈ¬! .ŽÆK؉’*™t±óG²Šn´?0`ˆÙk=0š3JiF†‘e”¶r@Ÿ9 ±jˆS·ÐY"pVc®bòÝIÝs…ÜR~s’éA=¦‰B‘ h‰6ÁÝ8 ºx!H³,äò‡Šþæ€× Œz—Ììµ2FŽQÞ"ï¿/.D×#{µÞN‚†ºÃrJ”Sï-“[%Ñdiffobj/tests/testthat/helper/atomic/2400.rds0000755000176200001440000000063213014331136020510 0ustar liggesusers‹µ•ËNÂ@†ÇÂF£‰{7ó,:mÁ™ÄÐ Þ‹7Œ;wŽK—>ŠjüJ+0PJk"É×éæœóŸ™30ÙBx¢æy«%¯ûW½ÆólÞD"LÑ„¾½%†ä®c•ëª?Í*O‡œõ²|ÒÓÈ5óÈe+ö-®AêS7„W¾rc¸ã!­L0œH=%ÊH@ð÷ȤaI7¹Ì[güNÿÉbÐ EL2xÜ „a"‰pžá êN§#ûz¬ú†h¢:< NkÙ.WjnÚµ9…ôÜÜ_ßAÀ#ò…‡ÆŒG1iäá(I_Kq¹ü¯ºmÎO­ÁX{1éa¯ìß]ü¥ø}7v9#ûã®ý3~éñŸI|+ðOe§y}%Ÿ¾ˆ+9©5ÏInȃŸä¤ïocU‹ø{¹•Phj³©ô瀱÷Z w7diffobj/tests/testthat/helper/atomic/3300.rds0000755000176200001440000000050113014331136020503 0ustar liggesusers‹•”ANÃ0EݤB ‰=›9 Ç3vâ ¡)bGw˜5+ÎÂ!8gb˜4&þ!•ÚfaûE¶çÿo'WƘÊÔõÊTõ0¼ÑæÂ¬Í¥öo·=sº×6&ÚÝx;òÑG§Iz˜Kb¿ î¤Å¹þ³Ni»¥æŽI»cå†Ï^’k}ϽF›¨ožÆ)Ù:æÐ 8FZ„!°ÍjT\q ÏD³'Ä-qGI,ICâH˜DH|Ù&'ð±H o&ÀhšÑ4£iΦ ˜k×ñû‰— nÅ×R§ã¬ü‘’–¤#‰4\ÚRy:ýyvÅî|é8Ó÷ Á#„Ç—ý|žÄ!íSc­ w X4˜?ÔejgaÖ•j_eõËîU»ëý«ábÌÏ/VVy¬[diffobj/tests/testthat/helper/atomic/1250.rds0000755000176200001440000000051613014331136020513 0ustar liggesusers‹SÉNÃ0u›J$$î\¬9÷д(BMY¾)â@YãRèÂràSøTÄÇž–E!‘žýœÌ›73Vζ”Rm-Õ˜î`ÙPµ‰}¹›E‘9Àšmù¨âMDÆæP´±I›j{øuÔíklÿ˜qj¯»q#…s*ù˜ôŒÎöÏ«tM.Žn„Ý’—"Vš±§÷æsñ³uÊ,ìyÏ;qºVþé)õ†^›“¦¸.1p t°R;÷ï7këˆ|.#îÏl8¥e^ä&'ös ë·mkK|>Ì{㮸ÎÏ™­„ªlš¦¿«\ÍhàsM¥¶GaOu3ÊB¹S(8–fÀXKàx©s—Nfâ9¶¨ußë{í+\Þ€GÀ1pœ®»«Nî-WEPŽ'ضí+þ5•úü À®Wµdiffobj/tests/testthat/helper/atomic/2600.rds0000755000176200001440000000056413014331136020516 0ustar liggesusers‹TËjƒ@j6m tßÜuñÑ¡”ôõébÁ'–(„þG¿·ôŽÎ8ŽÑ“…3ÊœsÏø½!„XĶ,bÙ|{‡—[² ׸æ÷‘ï³g¼†Ì©÷'·¹1ÿðLÀ^ZXÀNÞ4LÌÜâÉ5Ûl÷!tpÁùfü¿º§pÅœÈýjŽ@J+à(÷? >ƒ¸(´¢¦w|NS >A%¡¨ ÚAØ(Iè>m <ð[µ™ÐÖ¡B%o%&*ƒ¶'i6iÀ|Óã®™4;¨ÍH…jßM¹åâYä®$M]§i7GÇ» º:.‚æ¹¾ÖUF+%Z½UVû3æ¡Q¡ûW­«qª³<ÖÒ˜LS-Xó˜;›J0!8éýgÎwàyZÓcæšs'X‘f¿ÎGH]ˆ6*ŸJì°;"%ÅN4ÀLŒ 11Àœr`!ÅÊ' sâQÌT¶B2' Æ­JÄ)–ò9ñÒ1xy•'~¥r@•aÐŒšK\Ÿz†aDeÈ@è›þ’L³Ù4ªN‡õDÄ{. ÃMÃõ8õaüì[3 ‡ "§ù(~ …ñ²,é“|f³¦rxÞ=3TŸ„†FÌe@Êh¨r™äÂpúlžþìOáÍëUžý^–/ékœy?®b³S/º¾ I-÷º™[_ZÜ ˆ_ûn}™µÿ¢¾Ù~23ðœ)-hÓ¯kù䣼,ôÏàš¨biüRG9 Bk‰)~è#ôðÚTêp diffobj/tests/testthat/helper/atomic/1300.rds0000755000176200001440000000077213014331136020513 0ustar liggesusers‹µ–ÍNÂ@EÇâFîÜ4]»°´($Æ* "‚¢¢Ä(*ÂàˆèÂð¡—Ò¹ ¢…Å49ô†t¾s!Äâ¬Â!ÃF¨çñ² ¦Å îÅ’ãÈ5¼Æ¤éåx?sáIW®s­+ã®õí<¿"76L{iÕÄ-@Ö=XaG–x+|ÓÇ÷Ï[–fɾè?b•-Óª€KpªàÜ€[Ë{z`úÄ¿¦ô¯Þ,?]ZªÊ`Æ?bjV æ;P A܃‡ F®šrÅU¦ëI•ìU5ëæ'Ðmð :àtƒqÊ {Ü2Õ&jލY¯0¿8H€M°¶AòÿF%{YM¹c:Sc¢FŽ£f¥`Nƒ° ö@ìƒlP#N‘ìÑdŠ7Ê•²,[ã5r¹'`Î<8G ŽÁIP£5å=™ž&úŽ\îìS˜‹à œk0âçû³§´Ø£Íôüw£Ž÷Þûø'Û({8¬ì:_˜ººíÃûÞw¾ ö‡n{TÙtn2mi¶;ÜÏÛt&™Rºí¿'¨A‰ diffobj/tests/testthat/helper/atomic/500.rds0000755000176200001440000000065113014331136020430 0ustar liggesusers‹í–ÏKÃ0Çc·‹‚°»—ÇÎc¸®+"O^<èE(c¤mÖ𼑶Ózðà_â_*¦]÷ÓÑÔ`(É#}ï›O^^CO!i1¹ÙÒ]‹4ɱŸÏœ~_\ê~  °½Pu{Ži÷Góɪ¦=,qµ ¶Š`Ó1÷-×÷µç…¡×1AuËåªËÚ€2¤\ë}s¯ƒsáôFsâMû•Àe‚@µ™„LË_¨R Ü4X¸êÀÉÚþ·­… Ñp/Á N{MvÅÈå £—&Ç ’& ¨Ï€'ÀÅãx7ç.A«F°ýEjíìÞª8ÝH+!LP,Ñš <ê…̇S1G 8Ù•Om¿ÖÏvU‘xŠf(mÛæc™Š±‡ª¸ (XˆOÍ= —TeëûŸ£~nî¯ï ¤1Ѓ€‡J¥ÓD‡‡Ó,gÉÓPV€?;ÉöÁÀ$•^¢ó³»òÊö=’ÀܸDßÍX»Rú·s²¿Ú?É?É ù±¯˜4 MrT5"&õpZLå¿4„||G%[ídiffobj/tests/testthat/helper/atomic/2700.rds0000755000176200001440000000053713014331136020517 0ustar liggesusers‹­TËjƒ@h6m)tßÍeÖ]ÄhR„Ròéb †81ÄÐÐM? ]zu:>[ª çæzΜã™k¶·„‡¸îŒ8nQ>àíŽÌÉ ®écäûâï¡€²¾zòÇð…˜@¼Z ®þ8Min¹› xO!àÒ/QìWý1°ÚÿË~§p! òÞ$¤|D9»PcW¶òÃG³g™ ÆnS¯?“ 8 ,G;vàF_9:ÖzíÖôŽžûÑ;ïsŠ£*ïºz}°oÒnÑ@†jñõ$‚&/4[a¡*Î,Ž÷ÅÙUêš´X 4Áã†aM_³Ê)‹KÂl£©$yk+yZïÁC{†§ž…È -GŒÖöÓã1ø Mîh¹´97ä;bÛY©ýÇÑ{‡£•å(ÉRžw|›íë/êdî úL¹pS~Äå¾lâ„|ÿ¾Ukßdiffobj/tests/testthat/helper/atomic/1100.rds0000755000176200001440000000035613014331136020507 0ustar liggesusers‹’K ‚@…o£- ‚öm†YdJ!DØ¿ÄE–½§wV¦Ÿ_•X ßÜѹ眜A™ˆ)ŒS‚iC‘T*¡ú5[×e£)y8ïÛ Í‰Þót²÷Ô¶õ•6NwÑÛ––ŵºÎQrÂÛ_qÒ-}N³)¹­9QK¸$\-Š‘àb <0S0sê^ûx;{ÖÏbø‹_ò7xâeÆ^bÎ%X Ö`¶`—ÚÑÿÚLº­u‡=:àNÀgp×Oéh‰T†ôB¼ eå­Q*á§à¢Ý_‘Ê‚Ãdiffobj/tests/testthat/helper/atomic/2100.rds0000755000176200001440000000052413014331136020505 0ustar liggesusers‹”MKÃ@†Ç¤Á»—=VPØÙM›éÏB/‚7·Çþªøv“M›¯Ö&0IØÝ÷™Ïä㎈2ʳŒ²üðú€Û--`DûÇÊûð†»ß—î•-?U/*ÛzyòÂvÞ[m– ­=h/HïŸ8µ›ágoð8w5Àk§Ÿnžjƒ©x› §=¬€­`kX “šâƒÆƒQwŒã$÷/nr*Á$ýÈKÝ0‰¥‰ÅÖf( ƒÂ 0(\žF4_Ûó^qÙPV¤’Éä@r ¹Õˆ÷ÚÁ,\nmþ©èç)mDZ bÄ&¨ B‚ rËózíÀ{Û'E…R$IARt¬Ç3´“ßA¥mÇT02¶žxü3?QØîwÐåÕûªi íyÑ"C”7M´ù÷×û¸Dqé÷™Šà=Ïdiffobj/tests/testthat/helper/atomic/3200.rds0000755000176200001440000000046613014331136020514 0ustar liggesusers‹•”;NÄ0†½É $$zšÿ¶gìÄ+„öHtxk*ŽÁ©9‰M2^»›Â3_äyücË÷J©FµíJ5íè^¥åL­Õy²/×Q¼Mkˆ˜|«7Ng>ø¥mïæ`ޤ7Þ\ê?¦Í>n·07„d•ŸRj}ÔZƒŽÌCÞR¤Ë9 VI` N‚—ÐIè%„ ¤§–b²U«¤AdAbyPêA,ÒÝ;䃱ß)þ‹›º“ä¿y!Äɾý˜ö`©Ôg &0ƒ؃;ppÕ æ¬'±È`ƒ¿3å@gÊñîÍäýdY¿u2 ʵ–K’€$°'ÁKè$ô‚¯% v_­Z7Iíª¨nŸŸvÉ\N¿ÆG@©Ï/¾Î·Âdiffobj/tests/testthat/helper/atomic/2000.rds0000755000176200001440000000052513014331136020505 0ustar liggesusers‹”ÁNƒ@†WèEï^8j¢ ³Ð²cŒéc˜ÞêÍõì“xöI ¬PÐVH(ìÿÍ?3KŸ/Œ1‰I“Ä$is{ÅéܬcÞ¯ë¢ðœÕgáÞ>H.íï£ËJÿÅ¥¿Ôùm}¯»#ˆ.ÿže¿ÝfrWd\Ž¥kÿ–t¹>ǵj«–]µ,/ˆ’X¢"\‹)¼†…AøãdPðÂËØÉ_é^?“,ºÓž&y–‰pŽÀ8Gª¡§^¼@{˜¿–*2è‚(eÀ²°,, ˲ë™üK´“=q„÷Ä©’I­.NÎáÏáÏáÏÑ'GŸ³rÔát®ÖÚiþ8/¥OJŸ–ÂRX Kaéܬ—h»üÓo¢Ö89ŵ*(o¿€þŸaìaÒÝñ71Ëû‹ÀaÌ*ÁÝYç2}}yãr™ðèëø½ü×diffobj/tests/testthat/helper/atomic/2800.rds0000755000176200001440000000067313464162560020535 0ustar liggesusers‹­UMOÂ@](ML¼{ÙÌÙ¥ÒÄÐp"ÂA˜ÆCiš”Ђ"ñâðì/ñ÷£Ûîv—ít˜™÷úæ-LOBMdY Ô´hxN.g¨…NÈ=¹ð'½&W/Å,ÞÙü¡úCjÜô&osÓ[ß–aNIe7í÷±}éarÛAçý¢X¼}ôÚ)öígþDÁhlo<˜­V)d…9ó?´e˜ªŽlŒ+Æ„Á"6fÉ#È誢5X1  ¸›‘Ìû!ËÔöÉÐ{:Çz=¤MÝN&÷>MÝ FCÈ_¨ÄèõŒ|»­2K`…´¡P¨„^¸¢§Éú¬çÚÎ} ºÂ·ÑV®Þ©‹R\ë`6›oÁˆÙÕÚ²j 1÷¦†ù©9Q¢ C g³bØÆ‘ÁHÆxÚÙ Í…)ê<©qëKu„¶§í:—˜– Û|žWè\eZ3mçκNçÓ¦i;wÑ-:³LÛ†í.wÌ:w™öLÛ¹;æèÜgÊ›¶sÿ+ÐyÀthÚÞ½cÎ"Ó±aû ÷º“SØ!İûPÐ"V«ÖqóÞjÿ *Ä×7u›‹» diffobj/tests/testthat/helper/atomic/2500.rds0000755000176200001440000000244713014331136020517 0ustar liggesusers‹½YËŽãD ݳ ‰=/X²ˆ].—KBh§ó~¿vHìK–|Áš5Á!ŽOÙNª:I»¤™ŽtNú$qÝGÝ{]ž9|ÕjµžZÏOO­§çâÏo@ßµ>´¾Äûïßþ$Äù°>ü»Ñà…Æçëkãs_4¼¶´þ3~žœ?~ Âï£8ÀûccÅÊ—+D£+JKÿØqêöÙü™eAÐr ›&/Yi¬W}ÒÏ.æÅy`©aöjU'3—ÏiÏX46i•¿>¯:}½ìêº7b"\ €@”®©j=ªÔRú:†°íƒùæªL"² ÇdIN^Çð×FÈŘS`̰VY3ßLf¶«G/zÄ=“'7rþ7?ù£qKoæM]Ö¢N´¶::r´p-]:û“y>e~fä9yA^’W7òvoŸMµU1Yª!!*)D…¨æP) ›UAÈ* Y…Åõf³†³Êþ®áØ;`ª©p´æÀÉRYÇ££ÖÌㆼ%ïÈ{òÁ#¿Qíèɉ±D²ô)J®=Œ”¥R +ÆŽ±dlk´×ØßNsè/@è`Œ:}?›Ø½qrú¾Ú¼ »Æ2mÖ¾çuî+­íÌHÛZ8sA„ïÐ÷/ÌOÜ'ÈCòèFÞö}dÝsꈌªf‚@= Tnï@å ô¯Pͪ@°kQ°g€à °VyÃß1"œS`ÌE§ôuÙ¹ö|e©µOß™Ç yJž‘çä…G~&›Ð¸ý¶ôeŒLÄ¢ô)ޝ=Œ¥¥çgaÇX2¶Œ5Úkìïn°À8YŽþÌ=ú~yékê•£×½af·äÝ{ô}ûJKG;s!væBìÌ…X¿CßùH>‘‹qö¦IßÇÖ¦ŽÈ¨j&HÔ“D5IÔ’DåJô¯”ͪ@²%kQ²%g€ä 7îG÷ÎÒ9"ì/@èçÕ™?¿özh©‘¥Æ¹GGåÌh—üBî‘ûy½A“Ù¿—s‰i'qÞ‘èV‰^M$,½K¬ÓV",{x.9S%ÏU’sAr.$Ü Økìï±N0ÀXk`ã3 ê)õÈÑ㋞0ÇSòìFMòY8g÷D8Ú™‰3+gV$êfÁœùY—äyMÞøÎ‚Äºû$Ö‰´Š'HPO ªI¡–*W¡§•hV k1a-*Ö¢â\Pœ ÊãµE„;`€#pªæAÖ½ö¼c©¼ëÑG[æqGÞ“ä#ùä‘_…i§pÞQèL…¾TÈ„ÒÕÿuÚJ­'°4òðWq¦*ž«g€â PÌ»òx¶êv1ó€ÐÀc`Òõèûz?JÝqt~Ñ…]c™¶?ß×¹¯´óLŸ:s!uæBêÌ…T¾Cß÷™ŸyH‘Çäɼ=ìûÔºÓ¤Ö‰´Š'HQO)ª)E-¥¨\ÝÂfU²SÖbÊZL9Š5œUÞðwŠgÀXK`Õ­Î÷V§o,µõéû)ó8#ÏÉ ò’¼º‘ß?ïø«1Ù4Î;©Ñ—™ÐȃNƒ°Ý6®…íWÏ‹|Óœ•šg(Í~×ìwÍëÏQÿ>Úû•¥½¥–:ÞÈ ¿­Þß8úÖ>¹¯ÇçÄ2§ÑUa[X*¶Ô«ŽõŒK§õ׺þ—³»bh}xB _”±<ÿúËoxûšÿÐjý÷?-³cÀdiffobj/tests/testthat/helper/atomic/1200.rds0000755000176200001440000000051213014331136020502 0ustar liggesusers‹“KKÃ@…§IA÷Ý„»î¢1i% ÒÔÇo‰Ög&öícáOñ§Šg23—¢MàË\’9çÜÜi¯„žðýŽð|]á¶'ºbë¦'£H➨ ªSS·¹°3Vg¬Õ¤­Ö¦çØ?RãqöãËŽ0míQØJa“ŠžLê«ý·hyrc¬èì zäꉶöZ{7[vCFåàÜ)¸p†ç¶‡ÕÊpà´ÏœùÂUñwúwýd«>ç…®µŸvÒ¤@Iæ@¯dD4«uT?íª·Èù)î¨tUµÒ"Ë3•Çf$Cžò½,À¬ÀlÀxožÑÈigÜÇœ«Åî’ÇCçð”O‚ 8à\5§óT—œ¹âjÝ”.ºÒ;¶ ¿˜–X«Gú¯)ÄÏ/A£$µdiffobj/tests/testthat/helper/atomic/2540.rds0000755000176200001440000000127513014331136020521 0ustar liggesusers‹¥–ËnÓ@†MÒ HHlX±±º) 3ãz$„êØMš6Mo¹¶bÑJH,j¨âqx0ž„âKì4vêñ"}ñÌÈçòŸ™c{ñʲ¬–Õnµ¬V{5|Ãß[kÏzÉõÇ»Ûn7þÈ¿Žíõ¸/’I7ÊøÓ“­Œª¦mýžÛ½øðЛKE°•ç¦i¤ŸyÚ‰“aØvBˆ;)ÁQë³2€cY»íeþd¼XTù«ÈÒv‘Ó <8ßNSÖé©“e²þ´Ñ%Þsþ6gÆÎú:AË)Œà Æpp Wùºý»mat!È‘üù òä/È_¿Xå¯ÍÑÙVìÐ5ÙN` 3˜Ãbsž–›ótÃJЫ³C%þd<™üÖÈuØb´ºhuÑê¢ÕE«‹V­®o®Q#ÛÂè½ÝAp C8陣7²}öLUñˆØ}¦Ô´(Ô9!Ÿ)Ì` X !O…Ь³‘matÉ^HάԴ0{¦Ø3Åž)öL±gJ™£7²­èƒl#8‚> ´ŽÃ4ô•8 ëôÁŸŒGã2…5RœY…V…V…V­Z=´zhõ¤¹Fl £Èñ Æpp Wp “’óÑÈv·\§iÔµ(ÔéS ŸSãó”ð©šOÕ|ªæS5MÕ´0ëld[ý,¢Jpp Wp ˜Fæèl+ú@s.4j4j4j4j4j´Ï+ÕIZB8Âðf~Þ%þ~$Þw²üeÈr½<‹¶¡»ñ<7[äfËèy¾†ÃQ a½z÷øø};{ßÙŽ÷ïî¾îoçÒay¨Í—äïb…iÝŒ\áts3™›©ÿVx+>?y”q73–™±ÊŒ½Ìø 3öëTÀ²öZè‘Ö¡ýðå—×ë¥Õg¾eýù çãØv diffobj/tests/testthat/helper/atomic/1450.rds0000755000176200001440000000050613014331136020514 0ustar liggesusers‹“[KÃ@…§IAßûö¹I+‘^¼üañ!Ñz͆޽<ø+ü½Å³›Ý¡hSø²C;çœé,¹=""|¿Ež¯Ë<¨M‡87EêÏD¦ž”u“:cuÁÚX›jmz†þƒ°8ö„ik§ˆºýF ›”wdÒSßÍÿ[iM ÏîJ+ñ$ì♫±ÓkíÝnÙ!t©DîÁ˜‚GgxîzX­ {NûÊ™o\å§ÿToÖÌ‘8/L­ý´“ö `æ•9ªwl&Šœ‹â9 W™v1ÊÓ,U©0mÿlF†¼Û’—`Ö`ÞÁø¬ßÌÀig<Çœ«Åþ{‘§}çð…”ƒ ¸WàÜÔ§ó.—œ¹âj]—Nmé-;…ŸO Çæ+ýBmåɬūdiffobj/tests/testthat/helper/atomic/3400.rds0000755000176200001440000000052113014331136020506 0ustar liggesusers‹•T»NÃ@¼Ä‘HHô4S‚”Âûð+B(ŸQ¦NÅ·Pó%|bc/ñ†<¹âîÆw»3;{òÃEJiœ²l”ÆÙj{eÓYš¤s[—× ‘öÎæ¦E·º¡ëRÌ´¸íö ;Õö~¬]p=UGcÿÙ.•í|š*l98<á¿Cœës³Ö&o± GÏJ(P%P5zOšàåPºdƒºàÉI$Tln†4{/ˆ=YÏÏQŒD ”.ÆYÁ9˜À¼Y…íßwW?¿¶³¸b°Ú»‚” RCh%(Cå ‹Cá¿rÑçënòP 2‚*‚:‚&Éÿ4Òëùø·»´¯-è¹(swp—a[:—Þ2 t*Uh-=¢y3xÕœ’"¥ÉØøG®#{}y³å²û´úͤôýœ\úHdiffobj/tests/testthat/helper/atomic/1600.rds0000755000176200001440000000071713464162560020531 0ustar liggesusers‹µUËNÂP-”&&îÝ4w͂ڂ!1|¿•gã¢(>{ ÈÃǯð{çÞöZ°4FIN;…™sf枆ú¼¦iIM×ZRá". ZJ›Ã}´äX_Å5Ï —ý8Ι6_£Z›—âÖê-äçx¡`Xé¬Û 1A­*Ì´«"PòÄc>à gåÂÿݰ ÝRtÇT)riùô/j·A¥cf”æ=)=PäMÕ¤~MUë2ƒµ€Kà h׿Àãê@ýýûÉÊ>,ÅÅI½£"™ÎŠžÛr¹ËdZx _O[ö–W|Ø›˜ML%æü‚™uÞdoãÍäC—:êQôµǤ“Dë`Œ€'àx‰R§þû¤9 h©¾œUµ¯P)ëÀPÊÀ&°5©Nž7g›>äùlÚúç‹6µGÓíStð/ž·Éó‡¤tDÑqän-òé6v¸ìè]t-úœ`‹ãy›¸NHý”¢óf9ݱé-Dç‚IpÈ4væ¿+’‹U€*P‹pMž¯PUŠj‘›±É±u¨4€& gN=o*C Ù ¨ù7^ÐRIh&mÝkwä_ŽøÊ¿|û˜' •diffobj/tests/testthat/helper/atomic/100.rds0000755000176200001440000000072613014331136020427 0ustar liggesusers‹íWOOÂ0¯ƒ‹&&ÞÒ ü$~Rc7l8a!h–µÛÞïO_Û—ìù„b’a£ Ïô휔ɱî_/:–%®õ½) ;žªÖ¦Ïk›kˆ›9¸ƒë…À‰¾«ƒ¯D» µŠº['/ õbÙ\£Çæ¥èÔ^¦ïMù>•ïšùh>ŸÎÆà%ý‚ÉÍ×/^LôåVúñø½8ö úÏT‰ÙTõ3[9¢£QOŽFüżE%p"P= =¦ÀæoT)ö°? MÏ?]M–3BÛ§^!ÂŒÏVŠvá‘Ëú#.û ˜@0=‚ . ‚|Ÿy„5„æªT>?Vù´}Í„ÐCÕg¡ædàPÇc.Œ˜ 8JÀ^^>7[ÙUNE'([­ïÊ¡è:è£ ªð„‚y8®ÄÖì½8á’ªIzþSk ¯»ÇÛðht/NÀA¥†ƒP/‡ƒƒIä%JC²Ü©±½8Y^è ¥êüäDmç$S¬Lè#ºö„™+©w“ýÖ⃓ƒ“;ù³SLʆvr”8*ùLêî4~ý òõ E€-P; diffobj/tests/testthat/helper/atomic/1900.rds0000755000176200001440000000052713014331136020517 0ustar liggesusers‹•”ËNÃ0EMÒ HHìÙøXdœ´ñ „úH;Ø5¬ù>qš‡ã&q["M^òœ™{ÇÉÛ1&3y–™,?Þ>pº5˜ïǦ,ÛÎÚÚîÞ=K!ýóŃeUû’«VH.®Jê°x×î÷VžJËåR¹#øß)C­ŸS­ZÀjä}Ä:–—DEl‰QžÐnùT?R=Çô>Œ$¡pœ„ 0aRX+B€0FÀ©ãžV’­Ú3€týFêPíÆq`ÆqÛµúq2:Ó€Eýi'\1×ùN¸6e¡Õ‡ÁyZó´æÑæ±Èã±G‚G‚×U­Q2ÚÒ€3^û0.Å"£`Œ‚Q0 FWg%{—ÑVœy5zèÿgæ Î唞錢ó½ÕhJŽÐÃcp fëØ ¬ j½Õiæ«äY£èb•™7tz÷Çê ÁÜ:véך¸ÛmЉ˜~ž«N5(jÆèŒN“s¿.èýèBø¹è4ó-òlSÔ wÿÃ,øêõœºõ‚<¥Tž Ï[¶Cç/Ǿå®_ß ô@Û•diffobj/tests/testthat/helper/atomic/3000.rds0000755000176200001440000000025513014331136020506 0ustar liggesusers‹‹àb```b`fbb`b1€3 'N‘Ž66ε’–¹ `v"„˜äÚÁµ˜ä&е+¨Ì,×ÁAÁPHà2d‘ ¡æö úÁÒ 7Ú0¢B©B â+¥D%ˆ¼R’’‚R2§q•X9ÂV$¡ƒ¬¬R)f$²1 ,L@71BÝÆœ“š¤xÁB `g`øP§&¼‘diffobj/tests/testthat/helper/atomic/1700.rds0000755000176200001440000000053013464162560020523 0ustar liggesusers‹“Mkƒ@†·( zïEæ\BµA(%Áú¢Õ„Ђ…è¡ …þ•þØÐ™ÝìF­±õðîºÎ»ÏìÌ:ï2Æ4¦kÓtš^ œ²;Ãñãrišù-ª“|¾Ù¦Û¬—®Vbíøƒß­üN9­ƒ³7hóîÉos“F†}e8´‚h3ßþaàñò|ôê\çËý*>€€^„âß“Ä$$OÀcUòÂišÒé»P€?2T:ëd³¯üQ$.?Dž›É*gŠ*‘1¾…ü]í)ßi¨všè3LÇ B`öˆ9„”RH)E~%›jŸïV»0ÍŠbóµþL‹÷TŽ×ÆRµæH~¡®Ä\Æ( —¸¹6ŽtRö¥Ú„b)B§O5JóÅâNÕÏpR!cÝɬ£!ùdŸ¾Î ÎùýˆŒí~q¯ul£diffobj/tests/testthat/helper/atomic/2530.rds0000755000176200001440000000132313014331136020512 0ustar liggesusers‹µ—ËNÛ@†ÝÀ¦•*u×E7«VÊÂãxTU86 „@®ÔH•ºÀ,*Ôe¥OÒg«ú%ž„8ø©H_lO|.ÿ™s½1 £a4Fã`yúŽ÷Æ¡ñšãÏ·­Vü™O›«ó®\TøãF'þ²±u➬h«£ßs»›¢Ù69”[z®k¡#ýNëTVœœú¾iv €Ð7“œø:X—•œÂ™¿m·£½ÈŸGQ™¿’,M9-p@‚ mðL²Ò7 «J–ɺXoV÷”¿uϘ۾úh9‡\ÀFp W0N×mÛÌèB#ù òä/È_¿ ±Ì_åG¯e[²C²Â æ°€hÝO×ë~ºaÅïTÙ¡N<äùˬ‘m±ÅhµÑj£ÕF«V­6Zm/¿Fµl3£w:L„p]èÁ)œA¿“½–í‹gŠhÊ’GÄî3¥¢E¦Î)ùÌ` ˆànÀx*ù:kÙfFwØ ‡žu#ÌžIöL²g’=“ì™”ùÑkÙ–ÌA@¶!œ@zžÓ@‡>c¥çA•9Èñçăa‘¿ÌIzV¢U¢U¢ÕE«‹V­.Z]'¿Fµl3£Èñ†0‚K¸‚1L`ZеlwçÀ¶J*wæ‹ýçÀ£]ãñ”ð¨šGÕ<ªæQ5EÕ”È×YË63úEH•`—pc˜Àfa~ôZ¶%s è ……………åñ“j%#!,‘óËür ü%&J¿$Þ+f¹Zž‡Ï¡[ñ"u¥®®Ã—ùf7ßÿªø*hnÞs²Ôµ²·RV+u央äÍ2ÕowOO?>6îî¾}* øu[ýOvnÅW­~«ºb-Þëd…ܲ;oéyå!ùÞÝøwâöÖ¹§·ª0Ša6ÐðJk9xøöÈáíjiù/‰aüýämO­ diffobj/tests/testthat/helper/atomic/1425.rds0000755000176200001440000000057513014331136020524 0ustar liggesusers‹•”ËNÂ@…ÇBb41q蓮•&j j1 –›—µIÃB¹¨@AƇðçŸé@+ (ÉwzÉü眒ޯ3Æ,–²,f¥èv²ÊÒl ×`Ëwyu¥­îóúþ/?¬ÌÈóélFæ÷…¿·-œSq²S]ä¦×°æHær¶ØÍÚ¸, #»ÿN„IŸñ}ºÒöEU/áÜæ5ðž¸>^çz1ÆÛhr57ëÙûœŸ2TŽÊR/ç^Ô4ê¶üJhé×:´AtMKÉ#ïÅžú±§ý´dÇ&­AÍ›$-’6I‡¤›Ø|þ|}a¼tþk¬Í›9ˆv3#€1˜,>i_8¦å€ IF$ɘd’Ø2°C‡Ã¬qxGÞȃ p ®€ ±fV¸‘Y5¬¦Õ¸šW‹Òiÿ"RJ  *àÜ€[p—˜>kîQR¤HR")“T~¥³´…ô•°Eª[ïᲡ^ч€±ïÈrïÓ#diffobj/tests/testthat/helper/atomic/1000.rds0000755000176200001440000000037513014331136020507 0ustar liggesusers‹•’Ë Â0EÇT ‚{7!+k‹ZÑ¿РߝÆ÷kåçùYâmMÕˆ`-œdn;3·“¶“""FcÄ ?Ì`IPœ’ØYײd«#y·q” ™¶l>kmÙv‹9³^ËwõPî}äUd«Å͂űý0ó[¾*Ê‘*”ÓUŸÓ)IîšÝGJðHôÄÛ!ˆ¾¦šjj¤©±¦&šš e÷ç4ˆ/ÑO<üj<œÁQ3ˆ™àbÀ,Á ¬Å»'ÿœ?r퇻kVÃdnÁìÁÁ œ¿»;ÿÖÅÜcê- o´Ä–nù?4ÑíˆÅTaëdiffobj/tests/testthat/helper/atomic/3100.rds0000755000176200001440000000026713014331136020512 0ustar liggesusers‹‹àb```b`fbb`b1€3 'N‘Ž66ε’–¹ `v"„˜äÚÁµ˜ä&е+¨Ì,×ÁAÁPHà2d‘ ¡ænAõƒ¥An´a,DXF©B ÉJ‰PžR’’‚R2§(Aeª” š€F \‚äW4£MàZÐ ʤ*!…Ì £X˜€ng„ú9'5Hñ‚…@ÑÃÀ𽯽¹diffobj/tests/testthat/helper/atomic/2200.rds0000755000176200001440000000120613464162560020520 0ustar liggesusers‹µ–½ŽÓ@…½É6 !­hÓÌ ä{ïüØBûH[ á­(¶ÚÇ ¦æIx*ÄIÆû$¶“&‘ÆÎ2ß=g|f’/o«ªÚTÛí]µÙîß>àò¾º¯Þàþ²ëÌúO¸¶½;¼ÿþüó£äúâ _óýç2Ù&ëU“‡þßðåØ?>:ù µÃýB»=x2%^5eèõw×µuÿz¥·9‡˜î\'_ÝÄr¨GY¾÷-U U‰ªHU ÊSe‡êÐþ~Zòã•z UäÕÈ«‘W#¯F^-ÜÒO'‰ü­¥UäÕÈ«‘W%¯J^5ÝÔò£´–J¹QòªäUÉ«’W%¯ÒÞÔùZK¡Üyò*äUÈ«W‘›úñ|Èq-sÙRÕP•¨ŠTªS÷û\Ýäp`¥Áh‡å+›HU`XõtƲ»³-w žèÎà3/îü¨PÀ0LSÀ – Ó©¦29•}™…»ÈJÿqk„a@„áëÙþM‰jé +ýÇ­èð@x <„Â7óýKð²H·Yë_ˆD"€@ Bœíß–he‘nrUNÇøÀ`á˜ÓxÌi6ë9í¤.ÑÊÜ)x’Ó ^Y§1ô°X,K°œ€Hs9íDJȲp·Yé?F=‘€H@$  ˆD3—ÓN´„,‹t Iÿê~ƒþwƒŽí§gÜÞ>Úÿ¯ªÿ›`…|¥ diffobj/tests/testthat/helper/atomic/2900.rds0000755000176200001440000000024713014331136020517 0ustar liggesusers‹‹àb```b`fbb`b1€3 '‘Ž66ε’–¹ `v"„%LríàJMr“p(…ší”6ËupP0TèF4P5§Õ–¹Ñ†±`¥D%ˆœR’’‚R2§(•!lAr7²v$ UJ©JPsaÚX˜€n`„º…9'5Hñ‚…@ÁÈÀð´häadiffobj/tests/testthat/helper/atomic/1800.rds0000755000176200001440000000052313014331136020512 0ustar liggesusers‹•”ÁNƒ@†WÚ‹&&Þ½ìx`JgŒ1} âMoųOâã¿ÒvYµØäJø¿ùwfáåÆ9W¸UQ¸bu¸¼ãpíÖȹÏû¶ªº'ŽÖùþZ¥,ÿ/þx¬îž£¹îfYd>Õãá¦Ûí¼Žµ‚Ã]s‚Üò‹ÒÖsËDÈÁ›âþ"¶€L4±9*Ò»onû6õöœ–5W{Ó,–éYŽÛåÄûP×ÖÙ’È[™¬ˆHh³XJý'À6ñÄ»8˜zåðT<Óˆþÿ}Š]¯‹ú~¾k oH®£œ³ÇH$‡SmR¿\¾¾ø*ÕÀdiffobj/tests/testthat/helper/diffObj/300.rds0000755000176200001440000000134613014331136020517 0ustar liggesusers‹µWÍo›0gI/›4i×i—'k‡fJ&N³¢mêuÒn9lR”"´Eã#·êiÆÎ;ïÜsÿ´iL0`À°%Ø¿Ÿßûù}˜o/4M›hÓÉD›LÓî+v{­hÏY{ÿf»\Øý<€¬ŸÐø4Øûx»ÞÍòáö‹½'Á§£0)„Í~a¾þžÍ: ..ϱ ¬íº8¢ B”D [ÓÇs#ÈÆ¾x …èp9Ô ÿ \[ÞæjÝÄV´R-Ì×ÿUÝë týˆÇ&ZÞ°Å^óܰeîr‰6u“¬ÅŪû¯H i¬èi °Y7Íz ÝxÏä®ëuB²…e€d`}?Ù ö[ävÛ”ÅÀ»9 ½„và&hö±Ø©*Ìha™?›ê~ÜôçÂrûË­·À¹Žù®ïaÈdÿ%j·°Ì~@ɶ^ÉþÂ|ýim®ã¹É'žùQŒ„b‚6rÝf#?úz·vµWË‹¯¼ÐslTS¤Ë+Û¼¢âv’xW! pðBZ÷¯è}¤ÕT9ǽþa )*þaðú x€Ùè‹Ðßc=¥íìuJyùÄȽ։BÛ MP9%Ïâ¬èRÎûhÕ}»µ7pCËjýß8.ó\6ƒg‡rÀdy°ÁºNcצrfGë9†ÙÀ ©RÒhò)¯*ÇC•}{Ÿªj 0;¬¤·f-—²-¬•# KÝXõe§ÍÌ…?³ï.¢–,†¶óQ³UÀ*³À9] v¹Q’³_€F~qÀÆîÂXwò1ZXåÛ!¶Ãï p§”ª»¸«¶r#ÑSä©^ŸUÈ´<¦G(tËN5£…5ídÂÖÆõ˜únÈš—ÙPú¹­iþl{Ú‰‰diffobj/tests/testthat/helper/diffObj/100.rds0000755000176200001440000000061513014331136020513 0ustar liggesusers‹ÅTAKÃ0κ]ï^Bð Pâ¶fʆÈNž<<•.ØBÓJS§'ºø–¤mêj«ebáµ_¾¾/¯ßÛòöB:r†x·=4‚@èíèÑóÄ%Üç+,óì$Ê"Iå©f[.H`⪳Jt‰Mý¤‹å3×Ãðh½Ì–¿–^7ËùXàcMßòÜé Ožòp“ØzB™ b¦ÒK#½Å_êë ‹lF/ fsÌ0¥´^´æ¥§¸Õÿ}´ÊCl,ŒéÔŽNÿ?7ú/2µ•™ ¯¥½Åºþ ¬ÿ×-3wÏ<ˆ¸TÉ } ®ý O3ë\¼*¬à™E{ŽùšÇRD1•æ_PÕ‰úIm³ÎDߘHž§Ò'š×«¢[¨þ{›~/¬¢=˜¯P„[½#kžÉ(Hã4#ëRZ–„ådG¸O¿›¬Ûmó‰5:Iå²™úþ`˜~»»ê÷£©ÕÖÜ6·gÃ{gšÕ34r`& ÌlÆ<Ç¢¢>>Æj„9diffobj/tests/testthat/helper/pager/0000755000176200001440000000000013442554134017241 5ustar liggesusersdiffobj/tests/testthat/helper/pager/300.txt0000755000176200001440000000000013442554134020275 0ustar liggesusersdiffobj/tests/testthat/helper/pager/100.txt0000755000176200001440000000010513466141744020306 0ustar liggesusers< 1 > 2 @@ 1 @@ @@ 1 @@ < 1 > 2 diffobj/tests/testthat/helper/pager/200.txt0000755000176200001440000000010513442554134020302 0ustar liggesusers< 2 > 3 @@ 1 @@ @@ 1 @@ < 2 > 3 diffobj/tests/testthat/helper/limit/0000755000176200001440000000000013014331136017247 5ustar liggesusersdiffobj/tests/testthat/helper/limit/900.rds0000755000176200001440000000042713014331136020277 0ustar liggesusers‹•“=oƒ0†¯&CR©R÷.÷Á˜‚ª*SæŽQ2%%¨ØH@†®ýáUcÒ˜(e¸3çç}ïŒÅâ8ŒsÚå3…1Œ`Byù²B¾QL$êõÇ¡*åWš«®f?T åû‘ÿhq†›sÚˆäl†ÜHḛ́UÝfŒÛ·=oâ˾oZª«u“õ!ë†^õþ)iŸá’ƒ¦6źÚíý—ÃNÍÍ|ÏHGš*#›­åþzÐ!°5a|µÃ]ôðnb÷õæÝ\fóFÇ 8Gä"¹:ï]´éàêŠçyXʼ!y4 ,r•Õ.ò©ÀýA}ÖFŒ4F뙢ô¤Kíðó ׇvMdiffobj/tests/testthat/helper/limit/200.rds0000755000176200001440000000024313014331136020264 0ustar liggesusers‹‹àb```b`fbb`b1€ riéhcã\ i™«f;BØpäšäÚÁ˜ä:¡(€š£4ËupP0Ô1UR PEa3„0Ìn×Ò ,–ˆ`*( ÛŽGÔy°ŒžžžB~nfIIjŠ‚©¾¹BNf^j1Ä:& 2F¨ræœÔ< Å …Ã?öu$diffobj/tests/testthat/helper/limit/700.rds0000755000176200001440000000033113014331136020267 0ustar liggesusers‹eM ‚@†§U‚‚ »—¡³øµ¦þ„ާ°…$w?]úéѸZ ÎáÙwÞg{݃1`F?îIÖ`†ºmeœËi"QÏ—®®ä+/ÔàÍkÆ„òüg‰ ÐÈhÉ4ÅØ>¢«•¿ÒÔ˜L­,ñ¤–·Vœ Šæ•ʱ¾µBƒMKÓ´Ÿß±q£á:zŽï#ú_¤ Å´¤‡íÔp0£‹‘[R«-,\Ÿ½íÜ[ù¡ÊIU¬BÃD†˜®óH³œ¦ê¾ÌúoÃýÏéM#ý ›sDîúdËÓˆ|›ÇÔ_è—„qéqòxï¾¢u‚¥Û¶1qNñ}îã>–Qf!ï;¸=È]vó€ÉÈÓÐ^cIj% àró6ôZkdiffobj/tests/testthat/helper/limit/300.rds0000755000176200001440000000064213014331136020270 0ustar liggesusers‹¥U»NÃ05IºwñT!Nb;–êÄ̈è„J†Ju*µéÀÂÀGð¹ˆëÄyØIŒlË>/_ߨÏ7¡…A€‚P-—0,Ñ]Ãü²Ú¦©¼‡QH\¯Ÿ.§£|ßíËfÏüÁ^&:tÖ£“\;<“› ækŠa *Öï­ö¹ÚŠXÖÃG=â~ »c¹Ã§×ª¨‰ç Výyçõ?…¶rZ‰é;Ä!“T­:À{ºþm9ðŽ“$À¡n/t_ÍáëÃiŽ­ $´8¡É´«Ù¦B¦¸[Áº«èÒSAœwõB[$nH '‰Ý^h»ÓI¶†×µúØêôyŒ7mĪ€ŒãKéÈ냶2ƒC‰ÛÁ=Û_„6lÝ_1kÅólÚuÔ_Õ_)w+Ì'asI˜of$¡Ü­ “|“p# !­ŽÈ›ïp VÃ\™¸‘I µ4|Zk¾Nùt:áY§ÜÈ”Än¤áFQ„r_Ó;"ða_ç‡à®4><%L·õ–úƒCèûvÚbûdiffobj/tests/testthat/helper/limit/1300.rds0000755000176200001440000000021613014331136020346 0ustar liggesusers‹‹àb```b`ffd`b1À|N m'mlœk$-sÀìœÔ’’Ô¢bˆˆHÌ$×.o•×Ó AMÑ’Ž¶4ÈÕÓÓSÈÏÍJ§(é)ädæ¥ë(ê*d”æeCŒd``aê`„êdÎIÍR¼`!³þàÖ³·±diffobj/tests/testthat/helper/limit/500.rds0000755000176200001440000000050213014331136020265 0ustar liggesusers‹•“=oƒ0†]ÈÒJ•²g¹vF`Û©ªø +eª(R !C–üôª‡m¾”†¤ÎÇùžWÜýùDñˆïyÄó;wfMVä÷`³‹cý†6Õ`ücSëS^V66]Fè÷#Ã/ ǼâÒYI !2?úe(—™mv)ÕÆœ…1€©y]åÐ|µ…-zãùT' *«4d €Å)bmS ÷}•I†sdä“Í;ˆ§[˜´³“pMa’ßЙtu®#:ä.¾†t¨A*dRvè…,Ãé3L”‰;¹ÝMõOõ¸W§]*cuóÄŒ‘l¹œ“–v“ ª¿ž[ñ?uMG-輘H†Pë²Å ¼}W°/«â`ÉÊÃÄøû¢ÂíÙ„º7JÈÏ/ãmê ¾diffobj/tests/testthat/helper/limit/1100.rds0000755000176200001440000000025213014331136020344 0ustar liggesusers‹‹àb```b`fbb`b1€ ri'éhcã\ i™«f;BØä›äÚÁU˜ä:¡ª€š¤5ËupP0Ô14PÒ PeØÅ¡º²Pí·4È3Á¢‰P&L§›t™@ÝEÔFˆz===…üÜÌ’’ÔK}CC…œÌ¼ÔbˆËX˜€ê¡ê™sRó€/Xp ÿ³Å#žSdiffobj/tests/testthat/helper/limit/1200.rds0000755000176200001440000000032113014331136020342 0ustar liggesusers‹‹àb```b`fbb`b1€ 'v’Ž66ε’–¹ `¶#„@¾I®\…I®ª ¨Iz@Q³\CC ­U†]ª+ Õ~Kƒ\0,šeÂqº I— ÔýXtacY6:Q`c2Y6:S`c Y6ºP`c*Y6ºR`cY6ºQ`c:Y6ºS`cY6zP`c&Y6zR`cY6zá´‘… h##ÔfæœÔ< Å ‰ ÿŸ¶Ò‘-diffobj/tests/testthat/helper/limit/100.rds0000755000176200001440000000021013014331136020255 0ustar liggesusers‹‹àb```b`fbb`b1À|N í mlœk$-sÀlG€\“\;¸“\'Ps´¤£- rõôôòs3KJRSÌõÍr2óR‹u õ 2Jó²‹Á:X˜€:¡:™sRó€/Xä0†”<ù8³diffobj/tests/testthat/helper/limit/1000.rds0000755000176200001440000000033513014331136020345 0ustar liggesusers‹}ÑÍ ‚@ài¢ èÞeè,þí¢>BÇ S˜ä®àÏ¡—ÆUS‰ÚÃìÎì|ËÀž×ÀÀ` ˜Ñ·–`Švkwá\(FõùÔ”…|&™êjÓ51B?FŒ†¡Þìé"qŒžÅÑÑ‘’aiÕwÆó‰"Wo&…J°¼Öé`¸¬jJõýØ9L‰öº×ѵ]ŸXP¨Ë”Üí§ñçF„ÿŒ®Ø¶…Ìjê@á:B`ž©´²Ðw8Þõ¨:&#³è­‘§Š¶.µŸðz›² ü¯diffobj/tests/testthat/helper/limit/800.rds0000755000176200001440000000033113014331136020270 0ustar liggesusers‹eO ‚@ŧU‚‚ »—¡³¨«f~„ŽA§°…$wÿºôÑ£q×Rpofß¼ßö²cÀ¬~Ü’,Á†u×¹F‘<’¦õ|îêJ¾òBoZ&–§?L8ƒfG‹DfÜ=úZéñ+M É̹¦ÔòÖŠ£AѼR9Ö·Vh°ii÷Ó;.01×1ð8GäQJX[ âîsF;žça%‹–F~˜`Y(ѸÈýz6†›³X«ŠÚF[ý|¾ÒzyÇ{diffobj/tests/testthat/helper/html/0000755000176200001440000000000013420351310017072 5ustar liggesusersdiffobj/tests/testthat/helper/html/350.rds0000755000176200001440000000664513420351310020131 0ustar liggesusers‹íkOãH’}}¸H'ÝO¨Íê&a‡$“]/Ø…õfé>‡fvTŠ¡¡ç9ª· ëë&*.×\Ö÷üÑ.ê%š=q¼(ôŽjшþPÃ11P»àzwÜ)Æ[+"Ü ”xÈ¢fsß§aqË¥ë‹á.ô<ÇáAq&80k–ÍÅT”KÝbŽ™¢èáè# „Ñxü¥EQ„-EÊ ^c•MÌ·Áî@)šC2dŽã]t0X¯oòþBtg«&¦}_ú&ïÄžÞÑyfa'NÌ6&g S”Ú,[Îö‹1µ6ËI)‰ ú˜£¥²×8‰A^²ÈŽF1Gë³ÐèGHG'Š]°$g75ȵæs§·QÔªY*×ó5›™ÔH Ç¢¹¸ÏûhÝhmägÌSjÉtZ‹¼_1gnçdžÜëöPXKøN^®Á àRØLq04#ŠÓŤx½8ð Ÿ"Éæå@_D´ZúcÇ #/šŽ÷ìòJY&à˜]0ÍØ¢¢» yJ8&Ž× AârXÐõ|œÎwªñ0¬uux™jUz}ml[øBîÂ7;;;ÅÜ™ YŽR8H΃ì@Ÿ)»WD 0µú´(³oºR §–ÌÈÕ¯'ÊDúÎ!¬$ ЩpYŒøDØKJ[á݈X˜‘¸LRGx‡5—ï9ðÍkýZ°®˜¡:®_‹ VĀߙLëõWEÝ')UwŸ˜U.bÆïµ&•®§z sHzó‹V3Dv3<ÔÀá.øªP¢ÏÍèuŸª.»Öd ÓâUGÎ\ªœ©f0gNÚ ç¹ Ûv·Øv®éæŠåà§òÒßcbäÜàmoÚ›EÄš™æÏ™c^áù™¥ñg°FŽïmmYÖÖÖ“Å{D±8ó%æþl&Ï™»moãkQñ2yi–¹×ù¶Ëšs¸î¼ÞùÎÚŽ£ýÖXjùÜÿ2é1åa9÷¼õ[<ÑSƒ}ô¤X·×íy+ê<¡ŠØ=O¨ùÉ¿çß¿ÎßÇϦ]8Ðy± ?9΋"7Ì)П*Ü#QŽ3篖£½¼±s&ÎðõjQáæ‡8ség^ˆó-úif*f¾C? ŠQ¬êÚk蓾‰ã¿½Æôãž%œÑÔ™£ãÝ‚çìW&öÜ•ƒÒƒ´Í¯€fCƒñIC Þ„PD%-ÜÃ[8}$ÑÄ=î‡×±Z…_k^àð»]¨m4+ ûô3§ •ýÔöY2¥–VŒ,„ %Ÿ‡¥öl"R g?Ðz62Lhÿšdöm³á_øª¹×ÀO±Š­@<þài60r¨V6'ðgüE­LŒäé<{ ôŸRú³¶Yþëi&È1Ãÿ÷û\Kìá:˜§Bü‹2E?nìnÿ„éÁçX'O€ÅÕdÖÝâjJà_tŸSMc)rÕ”@œŸ´Û'×­¡šf?1ËiåàÍØ€þ¾yóü¼þû]–ŽÑ*?nü‰òuì2+ǰPØZØ^ØY˜/ì–ç)èìßá«ÿ±¼€ÿ$tʇl1>Zøxà·‹Ÿ,|úlþ“W9™Ët[z¡2·éæþgvËÌhy²¬Æm;þp~x W®¯Þ·Nê-Nbq"0GÅ®¶ªÑ\&FÉÄß º¸Ú£†“h·Ñ‡uYCE3Õ…ì6bQãÝÕymë@b¸ G‰Ò}Q/Å לnw"í¥Ô³a÷XÐõ‚nzóƒj£ï,¾yºdFbC½p@U…¨-1†.çrÉ›ãW¹|ß>ÙEÆh%[8œ¬åQÑë˜;84Š/}a®Šýð‡öÅ9ôÐË,NÜY“"€¹bÐãŠü]Þ c±u9êúLA„¦ôóa½ÀöNL ]ˆIES‰ç0ì!?ôŸ2á—)LâËAC$Šhð¾ÅÇxš ÇÖ­/¦’G¦§Bj¿[#oϧA;Z à -ª{](ëèÛø}p„= Ûàz—«s1|4:sªåìv¾¼Úãè=HÄìžq‚@|[€Df«ŸPHšpŠ `š&x°Jž[¥–­©}Ï¿ÿžŠÅ´Ç1V¬«nbþf¸´JwÊ=rï€áDJ!«åî€Òm)è ZrŠ˜wS`¶‚>îøâc!-2Í”hí§d&ŽiÑ»d}^-#\2MüX÷yÐÅuáë}ØÈ‘‰ß…òè„ôÄ4f‘?jé,_Ø7óå¢B**$A&¢i¬¬pðâLn>&±á½€°ÆTìĽ3ûÔ7“¸Î/.G-,KmE|fvºÔõV°nÐ'©Æ§‘O¥ªODã#Í´ucÃ2”ÇJHçÍ!xèûšIxÔ³}SjH¤ìP9»áäl8°ðÐxÓ!˜±@6h!;ã¬{fVèì3Ä{$†ÑÍc¿3B S‡•WK´¼¶¨ÿ¬—6µ BE7£Ušã®½ŠèÆ'Z-ã{ÝR Úª‡µÞö`ß^¾\ôG狀>Œ­ë&ªsÝC…(eÍ¢ÜÔ"Ô´Èâ#µ°,×'¥)^°Œ·h ãzhˆU…*Jw^Æå_9ÀRí×<Ý7CX—-‚ É´qk„S·-jXÜ ß7ò±0ą̈jÜäâ’®ôs©DúŽ'BÓ¤uà—ãxñ‚Žæ¬8­Ël†‰Õš"¼Ü7ϵr3r½„fg:'m'k¶@’c2Ô±¸#X§¦ïø¤.—: ´ãUT -ÑÚUš ÙÒEÚRÞ@$M@æ[ðlc `¤›)§¥[’-*y„{ã=BÞP½uv²C¥¯/†\¦Q‚u+jÀPÓUÕŠ”V4^é[â¦sP«%«ãt†æMÐ[WßÔtº(]H Ó§äTÕ!®Á¸@N WJ3«um¿UC)&k]šnêÂu#®´Í°á‚Ì ¥IfjA”R ¹Jœ6v‰ÖsºDNîúm: § , V²O#º»®¡wº=\rQ=®S¯ié5h¦ïÎ\™;‚GäM¸‡ ¹Äò˜Ù6º†ÊR‹Ûˆ´éXËõ0³¦‚)Ô’1ýËØ¶© ÷±ã ³E(Ý´ÕmÜé©SŒ:z#´4=hsàB—ØÃŠÍ$ƒôI¢I€ÉŒ£›Ìp‚}²¯†¬–QÈ 5”xµÜœü^z¸]#J„@wš9CnÝxª½ Ó)…Y÷£¥ØfÐ ³¼¿.Å3‹_˜©XŠ¥˜Ï°”žS^Gr»C®žÁsDYCåX{ñ5fàêj½§;zÇøè•)zêš`vN±('&sÊÆ¹ ¡äï„HÃ" “øšg˜Š“&‰é ‹¬,$Ã=p?â1öœ(‹³èãî’ 8#Šra§Ü>NäÍ4I™jωšÒç÷¥ûf|ÊsÁÖåqõÊësª=-q:ÖË>“Ýø4Å”/^”žQ t‹êhÒ¸ù{Ù¤\ÓÈ1»½7ig0¦¶GÚÆêY ä²IÍ©U§ž^0գЭºù }!¥W3ôðݼÂOÔnŸ‘ ù Ž3ËsÕ]ƒLª¶},iÛ¥£êÕ=ÑÛ>õ‹'ˆ›•.¡¤[ýóÊÆŽÉ ÈnRŽ)°5ª©‘Ô}>¡@¦å2Ô&‹k\VOnÑ|ç^¤è²Z1(•µÙâ&Ò”òfÓ,¥GåÙãóÉ–=ýp++_~žþ?ÞÊè0øöG=´²òþþãŸVRzÁ7diffobj/tests/testthat/helper/html/200.rds0000755000176200001440000000250313420351310020110 0ustar liggesusers‹ÕYKoÛFVâôPzïeà Qk„²5Ž-;‚ãGEÄAÑ"‚%9”6^>°\ZV‹œú¿‹Î’”LE\i)ûÐR°$ÏÎ~óþ¸´o·Z­‡­­/´né¯_ÓÛƒÖ£Ö—­Ö7§j*Ô4Á—…7j×KÓΰ½»—JfžÊ$pvy ή]lü==‡ÉÕ%L&,RÀ Œ%B 1Å\rGð+„´´GÆ!BíƒkžfLh0/Ã_s}p§ù²—I‰„š¢r²!g)8|'xD&%KÒ§€Êë~O »íöùO¿u}±ûÑñâH1R“ðúÍÅ‚TcþÕòž+tÒ„y8 ãèhÀ#Z™ñh½£ö'fU*ãIÇ}5À^¯÷­F HÙ XÈÅt@y‰âÜ’^ñyšFÒFöF†$¨‹r],…C¶Vº.‹H>7f…ÔáÔ# â"sÀ#øùR7„Y=)5¶æ)[ˆ×p”)E¢ ó}¨Á ×}†a#ÜúԔ؟ÚKõe§Q1hÜij²ÍáŠÂØ¢ÕÕ²¾/æHZf­®I5‰e! æôNì5'10‘EUš–µ,dI‘ŸXú9Q À•È®-¨ÕPx{¶U­Úð)¹\äfjÑtÎâ¸P`HÕMï’ 3cJ™{–ÓZÊÿ$ÎÜ+š·”MÆä¬ ßÄ5Ý ")„3ÕsÚÌ‹¥é%ÁŸGñŒÍ—ý ^¤mÎíÍORž~>ïP½È÷‰]ˆf¼8L¨]E~Î<§˜ã§Všt;´Q ¸«ôr.¸QRLLj—¯_ýz›UÉüØ^,b9€Ç‡‡‡víé|€•dµ >¸4_{YŒR?¹ÙÄ%FB9£Žä†Î\‚ûðø ¿ž+jR‡ùÕÌ1›Vzg‘þzÝç¶í3;ª4oŸ¥ë†Ê¸±Òwk+A¤2âjœ¹Ä!iÂ%ñKžfH½11<8àcÀ2¡¬ˆÞÈè]¡ª+Gîâ ë›W—,£T†P+;kƒ.6›Z…í}¶o,ÝJ·|ºÇ)ý­s«Ølþï™÷ÌÆ­ZX3¯ ™YìÀÞë÷]·ßߨ½5‰¥Èïû½•Ü»çíÓÕÔ½ /Õ•»‡û;Zaõðàðw¿œö?è¬AGíS‘Ñó/“œ)Nǹû=¿•qL7öéF³îõ¼UwÔUNÙÔÝäÔê†ÄøâÀü_m=èhðÆsnSã˜ëAßÔ¹5SN‘ãó»a߽؆À]Ï›:·zÄY _«FûúuT91ã¡~5tÃîÔu¼›ÿ¥oxìókðKÓ—å#Jn¦ngxœH¬Ó£#j§DÆ“ú…Ü¿â)ì3…¼.õ›Šçzý'BïÒÂÂ*þ:C(åK«Õ†2xAðèsuM40Ó wò)}·7ØOé$õႹ5¦y(Ó>3ý'£ûÌÃÜ cf¿\¼}{ñæÒ*FV¶KýJ1oáÉ ìÁɉM¾›Ý‹ÃÿýÆÖëỽ÷0«àÚößfÛ¥.X(»M”½&Ê~el¢l¯JF‘² ÿ7ñ d†³ýÊ"ÙsåÓ&ÊgM”Ï›(_4Qþñ¤|§Ûkù½Õzôðö_r­-}|•‹èWúùç_6ÍÐ_Ädiffobj/tests/testthat/helper/html/400.rds0000755000176200001440000000106113420351310020110 0ustar liggesusers‹íØMoÓ0p¯HTšÄ…»ÕÃrªN Ë¢(¯cÀV`0qÈ‹›fóœ`»ë Ú7â3"ÜÑm$kÿQ rhÅ¿ØÖ“'ÿCö„©_Y"µúäôº9,‘erßm¥ÇœQ=N؆¥Ù‰nùJYN#ˆŽoËxD¿y®Êx(‚[~Ìcù€J¬Ÿ6ìÖÙŽm$õ¹«Ô†Dý~ì(´ &)¦cÏrìD²<Ç„¶r'1«çp33õ\aæ¿gšåßµ.ò«\¯Û-3pn4qË¡Ó댦óNJV° •¸â"Å2 ©0G³'e*i¤sn¥«– ŠÉ‚‚æ• õ«á"Kí¢°©ØêözÝÝY%(\ûÒ&É07˜”¡Ó¡í›wh§3O±ËLµMÿý-­etdfÝo¦écœÙùM·9µtì!ØGp€`†à>‚Cš—•ù×Ó()ÿ\– ½—NÔÜœ£Ú~ˆàG~Œà.‚Ÿ ø)‚Ÿ- ÷þ³X¼Äb„Á‚Ì|„`àÁI‹¥z/{·Ÿ#AðÁ/¼…àWÞFðk¿©b‹Åýö=$¿ A ¬¬©b±Tïeïö[$v¼‹à‚ß!ø=‚? x¯ŠE0×î"±8F‚àk¥ªuóG¤õ?-¬õ[‰dÓsB–k¿¿Ý’:gÂü­œ]"äªùýø O úRídiffobj/tests/testthat/helper/html/300.rds0000755000176200001440000000666713420351310020130 0ustar liggesusers‹íýOãF–~Üé¤û^SÝ&tIÂG—.Ð [N, ’½½ÕªŠÆö8qq<®=!¤-ÿúéÞ›ÇNˆƒC®•.’Œß×¼¯y3óøwieeåË•¯þòÅÊ—_ÑÇ¿ãŸ/V¾^ùëÊÊλ˜×Þ7o/ÛŸ®N 'ûþA:>ù•8s²8Ôç’!ž kü—w»_>䬵G!/ƒ­¿í—%¿“ "ػǢ˜Ëýtk¯ËƒÒ$ÅXŽ|Ñ÷+ ËŽãÊA©ñ´d4°å b>·ZP[âõ] –à;h½…aË`ÈaÈ ú"âà|’Y(ŽïÝpˆ Þ„Xô9n½xÀ|"fx a$n=‡;`Ôc{E¨À)×!0¤<ˆ9‡ªïÈ2ba¼\ÚõU$Ò(•Þžý«îx®+¬Ÿk¤@†`\]ŸLŒÍßPuÞ'y-™Íw‘9¯Á&>賨ë»°Þ,ÝçÐÌŽFb¨éyŽìíÂÆúú?ˆŠ‹À5—õ=´‹z „âDO/}†£JE4¢>ÔpL ä.¸ÞwŠñVŠÈ0g)²¨ÙÜ÷iXÜòÈõÅpzžãð 8“˜5ËÇæ¢*Ê¥n±ÇÇÌ QôpôBk¼þÙ"‡(–"e¯±Ê&æ[„`w %Í!2Çñ‚.:¬×7y!º³Uchß—¾5àãé•gvâÄlcrÚ0E©Í²ål¿3ÀQ£µYNJIlÐÇÀ-•½ÆI ò’Ev46i¬ÏB­9*Qì‚qvS£ŒQk>wqzE­šåá r=_±™I´p,ú˜K€û¼Ö—ÑF~ÆÌ1¥’L¥µØûsæ†v^36ä^·‡ÂZÂwòr N—‚Àf’ƒ¦Sœ.&ŃèÅù<I6(ú"¢ÕÒ;^{ñt¼g—WÊ2Çì‚iÆýÝXà¨Ì³PÂÑq¼V—Ã"€®çûóàT.¸“˜‡y`­«Ã‹T«‘×Wƶ…/¢]øvgg§˜;S¡3ËQ ÇÉyè3i÷Š(á¦RŸÅböM7ƒÀ©%3rÕë‰2‘¾sˈè‚T¸,F|"ì#J[á݈X˜‘x”¤Žðk.ßsàÛ×êµ`]1Cu\½¬ˆ3¾3™þÖ믊ºORª,î>0 ª\ÄŒß=j L*]Oöæ8ô"Ì/JÍÛ=ÌðP‡»làËB‰>7£×}r¨zÔµ&¯:ræ‘Ì™jsæ¤5rž«°mw‹mçšn®X®q2/ý=&–FÎ þ×ö¦½YD¬™i`þœ9枟YŠkäøÞÖ–emm=Y¼G‹3_bîÏfòœ¹Ûö6¾/“—f™{o»¬9‡ëÎëï­mퟰÖÀRûÈàþ—E“–sÏ[¿™yŒžì£'ź½nÏ[Qç UÄîyBÍwHþÿáuþ>~6íÂ΋MøÉq^ĹaNþTá‰rœ9µíå3q†¯W‹ 7?Ä™K?óBœoÑO3S1óúYPŒbU×^CôMÿí5¦÷,ጦÎï^øí"À'‹Ÿ>ƒƒÌ¯uO„y¡ÔMº•âgvËôhyrwtËèÔ•áŽGFuלn®4n”¦L±¨L± ö‹˜YAnïèn®áÒú™Û2†¡'{Àà-‚è±GêðªÇ«°¹¾± p ÇãðŽÅ>ë#Áµ{žj˜èF¬øÑ8Çͨ+‡H~Fb6ª5â¸yCeZt[åIº°jˆúHÐÃM&–+ÔpÁÒ“æŒwàÇ–ùp5°|φsÏæh?`ÈšFâžjÖ :„qJ2´Œ pJ»WuºµÜSm"·¸äÓi×VÂÃ\‘*“$y"$¼Uw>“)j=gúé,Õí?Ñî‰gÔC’8Ç!]ØXIÿÈ‘@`øxÖþñòC/>ÁÇÃëëËö§¦2 îŒßrMÊ뇾‡”q^ äÅ' ïO®D”ã³ó³ö'œœžµ/NZ-8½¼†C¸:¼nŸ8?¼†«×W—­“:@‹“XœÌQ±«¬„jÔwÂq2ñw‚î÷¨o(Þm4†Ãa=ª¡"È™ê"ê6 ¸ñîê¼¶u 1°Ñ­¥û¢^2<ל.ébå¥Ôzc÷XÐõ‚nz‡j£ïÌ\ õ ±˜¡^8 ªBÔ‹‰C—syÄ›ãW¹¸lŸì"c´’-NÖò¨öuôU*ÅŒ¾0W?ü±ýþzèe'n‹Ž¬ÈÀ\1èñ Eþˆî`…¶XȺu}&!FSúŽ~ް^`ûÇCb‘¤©˜9 {Èý§Løe sÇ«‰B Þ·¸ãhOäØªƒIôÈôTDÊïÖÈÛÆóiPã•’BóB‹ª–%Ê:ª©baèR¿ÞåòDßïΜj9{*S^mŽqÔV¤bv‡ŒçßOEH‡ íñ€Á2ºJá&毇K«ÔÐ#÷øN¢HDÕò‡@5²©î"ô%9…áÝT˜-¡?sº§D¦™­ýT€ÌDã£Ñ1-z¬Ï«e„K¦‰ë>º¸.|³92ñ»Cžèþ:òG%å ûf¾\THÅ…$#ÈD4…•^¼€éÁÍÇ$Ö¼V›*ˆ:¦jŸÚŸ×ùeÀ£Q ëN[Ÿ™ Kuµa¬kôÉ@ªæPù©TÕÁ¶9™~@[õ§,Cy¬„tþñ‚‡¾Ÿ¡™„G=Û>3¥†DÊ•³ËNŽø3 7‚ dƒ²3κaf…Î>C¼GbÝÜøZè:¬¼Z¢åµEm„½´7 *ª§°Ò7ÿ¨UDõ¯Ñji®çK%\h«FÔz<؃M|{ùruBÒÏÞOuu¦^W½pçªQÊŠE¹©D¨)!Þ#‹Ô‰´\»›¢øžÝ`¼ÅƒÈÔCC¬*DP‘ªÖ”åKµ[\óTûa]´‚&ÓÆ­NÝf´¨aq+|_ËÇÂ3­ªqk—‹KºTCÌ¥éÛLÜÜ%ФUà—M¼xAGqV œÖe6õ¦/÷õs¥ÜŒ\/a£™Ã™î1HÛÉš­äG#B†:ÖwëâÔÔUm¤Ê¥E-OUµeX…ßè6F™ïšO{ÕBKtè•&H¶T‘¶”7IùÖÐ,Û)ǺÏÍi©Îr‹Ê@ãžE{ˆn¨Þ:;Ù¡Ò×C¥Q‚u+j@SSUÕŠ”V^©Ë-â¦rP«%«ãt†úMÐ[W]¸uº(]H}ï§äTÕ!®Á¸@N WJ3«ue¿UM)&k]šnêÂuc.•Ͱá‚Ì4¥IfrA”R ¹JœÖ¸Dë9]"'wý6†Ó–+Ù'‰Õ$ÙP;ÕåqS=®R¯îÌÖhº}Rw>8‚ÇäM¸‡ y„å1³mt ™¥fºÁ”©XËõ0³¦‚IÔ’6ýKcÛTÐ{ãxÃlJ¦uwzAêãCŽÚí#F#MʸÐe ö°bÓÉ }’h`2ã¨^Aœ`Ÿì« «e2C %^-7ç#_Fn׈„!ÐÕtÆ[7žl/ÃtŠDaÖýx)¶ôÂ,ß‹_—â™Å/ÌT,ÅRÌgXJÏ©¯q»C®žÁsDYCåXyñ5fàêj½§³Çøè•)zêš wNFcˆNÙ8×!”üiX@¤a_“â Sq²Â$1a‘•…d¸îÇÜ`ω2“Ew—\ÀQ” ;åö¹p"l¦IÊT{ÎHÔ”>¿/Ý7Í)Ï{°.7Õ H¯Ï©ö´XÌépzX/û,êšÓ]¾xqzFÐ,ªãIãæïe“rM!vzoÒÎ`LmíµÕ³@Èe“zxR«N=}Ïd*¶êæ+ô…”^MÓkÀ÷kð ?ÑMd$h>¨ãôò\u× “ªmKÚ¶F©Tµº'zÛ§¶ÿq³Ò%”Ôlä•ݤS`kTS#©û|B(LËe¨M"fj\VOnÑ|ç^,é²ZÑ(•µÙâ&Ò”òfÓ,¥GåÙãóÉV¤=õïŒ++_™þ[åÊWè0øö75„_ñ÷?ÿî¶›ˆ9diffobj/tests/testthat/helper/html/100.rds0000755000176200001440000000052513420351310020111 0ustar liggesusers‹Õ•AOƒ0Çß`šhbâGh8Œ“Yˆ7E‚šÝ<é&Ë:VS )ÕéÝÏm,Jg†eÃA£˜á¢¸pºXdÑÃIœq‰)'1š.%zÜÀÏ1q„K•4ÙÊœ`ª2Š0WõkaDóOé“”MüˆÉs¬Ù'n€ªùY]W’—º~­¢È1¯ã«L$H**MÅÌ;;ûã6–Û±t£”D4¸jòAó£´OÖ*}ÐÄÍd:ÜÞµò¡QÀÖíbÎ, NJ/Ây( ÛøÝ½P/‚ÿþÆ–‚>ºÁÌ›#ÝÁÛßÁNÅ¢pÔŽm*¾´þòÄý»£Ö¡3ºŽsÙÂì5|Õ¾î¡3UTJ5ZßwØŒpõ:úœRŸêyÿºVPÏÅdiffobj/tests/testthat/helper/context/0000755000176200001440000000000013420351310017612 5ustar liggesusersdiffobj/tests/testthat/helper/context/200.rds0000755000176200001440000000051613014331136020635 0ustar liggesusers‹–MO1@Ç.ML¼ë¡?À É&ÆãÉ£ñ`䤀"RTüöÇ»¥ $fw{ØÎN^_g;=ìÕŽˆIŒ“äážHM¶ý|ºßmµÜ±fΆ¸7œÖ³Ùûlø8u' " D³±@¢©îm×騿áQÛúÙF‚óóýó׬áBîæ?´Ëc3A¦[ÕD™zª‰ˆh­žî Ù~ Á¾zâK«Rw¶nÕ>Pk'‚Lwª‰2Ý«&"È4TMDéA5Q Ç£J=>/ÓãGµv"È4VMD‰¿X#È4QMDéI5A¦gÕD™^TîÝ´Ò½»(sï^ÕÚ‰(Pû[¥Ú/ËÔþ®ÖN™>TdúTMDéK5A¦oÕDDÿTêñõÚKÍø·âÎÉx0ñÓnHå¿:"¿£¯ÅË diffobj/tests/testthat/helper/context/400.rds0000755000176200001440000000043513014331136020637 0ustar liggesusers‹”O ‚0Æß¦—‚ ºvñˆä¿bÑ©ctˆ:šPÖ‚¬õ᣹ÔT^›êa{÷òlϳßÀM(„Q¢²Ç‡>¨Ðæó|°³m6á#eš¨½ChÐïúûñÚaÓTá…9L%ñIoŒØl¦Ùº­ñIlŒx?ñ–tÈDÏû•Zöû¯ˆO ò7Id¢»Käôüí2»¶(Û…e÷¥Ù1E‘-aG¥ìŽRLQ]Јݲ»“4;¦(²3Ǻ+ Øåû˜ÿUê)*° ±[Õaw“fDzße_×ÉþfÇÅw·\ÝÂÞ=ßÇüŸRLQÝ«»m);P wlÅÎÊÙ¿ð©+Zѯàýœƒ!lådiffobj/tests/testthat/helper/context/150.rds0000755000176200001440000000051713014331136020642 0ustar liggesusers‹–ÍNÂ@F‡–&$îu1`HCÒÄb\¹4.Œ®QDÿ}xãt(ɯí,:·7gÎÜÎE/¶1‘‰ãš‰â,Üñ=S7[~>Þ½êtÜ¡¦Î†¸?œ6ÓÙûlø8qG " D»µ@rSÓ'º®×³íýƒ®õ³Í ÎÏ÷Ï^Ó– ¹ëÿÐ.Í™n¤‰2õ¥‰ˆÜ4Z=Ý9²·yöÕ_Z•¸“u«¨ö¬2ÝIdº—&"È4”&"Èô MDèñ¨ROËôøQÖN™ÆÒD™ø‹A¦‰4A¦'i"‚LÏÒD™^¤‰ˆ÷nZéÞ•¹w¯²v" ÔþV©öó2µ¿ËÚ‰ Ó‡4A¦Oi"‚L_ÒD™¾¥‰ˆ=þ©Ôã˵=6õÈïXËwŽÇƒ‰Ÿ!•ýêóû²wƒ diffobj/tests/testthat/helper/context/300.rds0000755000176200001440000000034213014331136020633 0ustar liggesusers‹‹àb```b`fbb`b1€„ 'V–Ž66ε’–¹ `vrF‘ž%„HJMríàJMÀJ ÕB•JÅÌrLôȰ:¨sTk- rÁL°h ” T@Pí-uHÚuáÝ$'#Yc’ëBظ7 €þ ë,úxËoX€¼aA’7Šèã ¼aŠ #Òb£„>Þ!Åf o˜‘ä*úx# Ÿ5 ,L@íŒPc˜sRó€/XT®00üqê¨Ùrdiffobj/tests/testthat/helper/context/500.rds0000755000176200001440000000051313014331136020635 0ustar liggesusers‹•KkÂ@…o'n,ÝJ7ùAÔDk@DÄ•ËÒ…èÎGñ5Å7ê'c´&žx“d‘¹¹|sÎeN`ºïD$È‚„á•ê•§ eÕÚúìÛ¶¬«·+M]'ë‚{ý¾>ªvdãN8š(TPªl6ͲU1Õ¢7zîßü½O·(uoð_šÏk) Y%DøJ³à™Ü0Ýù%PžÓÃ.G¶£v¡ÙÇììˆ@J¿¬"ÂyÖ"ò¬±yNXD ¥)«„ˆyÎRåÙI’çœHiÁ*!"œg©j}éàByûÈÉú#)­X%DÄÈs*Ïï$ynØÙcömªÙ’̾cgGRÚ³Jˆxº+ËFÿb°ü¬?"Ò‘UBDŒ b @@ 2,3 @@ @@ 2,3 @@ b b < c > C d d @@ 19,3 @@ @@ 19,3 @@ s s < t > T u u diffobj/tests/testthat/helper/context/100.rds0000755000176200001440000000032613014331136020633 0ustar liggesusers‹‹àb```b`fbb`b1€7 '¶“Ž66ε’–¹ `vrF‘ž%„@–I®\Þ,ohU5E È5ËupP0Q`M Yl¢Pi¨öZä‚™`Ñ(&¨¨nAÒc’ë‚]ºû ÀNAs²(îË"Ã}^DºÏ«û,Hr_î "Î}FXÃψ´ð+!Ã}!DºÏ «ûÌHr_î‹Âá>& mŒP[™sRó€/X” þGíŸdiffobj/tests/testthat/helper/context/200.txt0000755000176200001440000000022313466141726020676 0ustar liggesusers< a > b @@ 2,3 @@ @@ 2,3 @@ b b < c > C d d ... omitted 2/3 hunks diffobj/tests/testthat/helper.commonobjects.R0000755000176200001440000000510013014331136021114 0ustar liggesusers# Helper objects to use across multiple test files ## Matrices ----------- ## Lists ------------------- lst.1 <- list( NULL, letters, z=list( list(letters[1:3]), list(NULL), z=list(1:3, 1, 2, z=list(1, z=list(z=5))), matrix(1:9, 3) ) ) lst.2 <- lst.1 lst.2$z$z$z$z$z <- 6 lst.2$z[[1L]][[1L]][2L] <- "bananas" lst.2$z[[4L]] <- matrix(12:1, ncol=3) lst.2$z[[4L]][4, ] <- c(3L, 6L, 9L) lst.3 <- lst.2 lst.3[[1]] <- "hello" lst.3[[2]] <- NULL lst.4 <- list(NULL, z=list(z=list(z=list(z=list(matrix(1:3)))))) lst.5 <- list(NULL, z=list(z=list(z=list(z=list(matrix(2:4)))))) ## Character ---------------- chr.1 <- c( "hello world", "I ran into a rather bizarre bug involving memoise that made it impossible to forget the cached version of crayon:::i_num_colors. Somehow, the binary version of crayon on CRAN has a corrupted copy of the memoised crayon:::i_num_colors function", "goodbye" ) chr.2 <- c( "hello world", "I ran blah a rather bizarre bug involving memoise that made it" ) chr.3 <- letters[1:20] chr.4 <- c( "a phrase long enough to wrap a few lines when looked at on a side by side basis", "lorem ipsum dolor something or other I don't remember what the whole thing was anyway" ) X <- do.call(paste0, expand.grid(LETTERS, LETTERS, LETTERS, LETTERS)) set.seed(1) n <- 500 chr.7 <- chr.8 <- X[1:n] chr.7 <- chr.7[ -unlist( replicate(25, seq(from=sample(n, 1), by=1L, length.out=sample(10, 1))) ) ] chr.8 <- chr.8[ -unlist( replicate(25, seq(from=sample(n, 1), by=1L, length.out=sample(10, 1))) ) ] chr.9 <- chr.10 <- letters ind <- c(4, 10, 18, 20, 26) chr.10[ind] <- LETTERS[ind] ## Data Frames ---------------- set.seed(2) iris.s <- `row.names<-`(iris[c(1:5, 50:55, 100:105), ], NULL) iris.2 <- iris.c <- transform(iris.s, Species=as.character(Species)) # without rounding this is a bit wild, but good corner case to test iris.2$Sepal.Length[sample(nrow(iris.2), 5)] <- rnorm(5, mean(iris.2$Sepal.Length), sd(iris.2$Sepal.Length)) iris.3 <- iris.2 iris.3$Sepal.Length <- round(iris.3$Sepal.Length, 1L) iris.4 <- iris.3 iris.4$Petal.Width[sample(1:nrow(iris.4), 6)] <- round(runif(6), 1) iris.5 <- iris.s attr(iris.5, "test.attr") <- letters # Narrow versions to fit side by side iris.3a <- setNames(iris.3, c("S.L", "S.W", "P.L", "P.W", "Sp")) iris.4a <- setNames(iris.4, c("S.L", "S.W", "P.L", "P.W", "Sp")) ## Arrays ----------------- ## Models ----------------- frm1 <- as.formula("Sepal.Length ~ Sepal.Width", env=.GlobalEnv) frm2 <- as.formula("Sepal.Length ~ Sepal.Width + Species", env=.GlobalEnv) mdl1 <- lm(frm1, iris) mdl2 <- lm(frm2, iris) diffobj/tests/testthat/testthat.diffDeparse.R0000755000176200001440000000113713014331136021055 0ustar liggesuserscontext("diffDeparse") if(!identical(basename(getwd()), "testthat")) stop("Working dir does not appear to be /testthat, is ", getwd()) rdsf <- function(x) file.path(getwd(), "helper", "diffDeparse", sprintf("%s.rds", x)) test_that("deparse", { # First one will be done in unified mode since `deparse` disregards # option(width=), second will be done side by side expect_equal_to_reference( as.character(diffDeparse(letters, LETTERS)), rdsf(100) ) expect_equal_to_reference( as.character( diffDeparse(letters, LETTERS, extra=list(width.cutoff=20)) ), rdsf(200) ) }) diffobj/tests/testthat/testthat.guide.R0000755000176200001440000002016413420351310017734 0ustar liggesuserscontext("Guides") if(!identical(basename(getwd()), "testthat")) stop("Working dir does not appear to be /testthat, is ", getwd()) rdsf <- function(x) file.path(getwd(), "helper", "guides", sprintf("%s.rds", x)) test_that("detect_2d_guides", { iris.dply <- c("Source: local data frame [150 x 5]", "Groups: Species [3]", "", " Sepal.Length Sepal.Width", " (dbl) (dbl)", "1 5.1 3.5", "2 4.9 3.0", "3 4.7 3.2", "4 4.6 3.1", "5 5.0 3.6", "6 5.4 3.9", "7 4.6 3.4", "8 5.0 3.4", "9 4.4 2.9", "10 4.9 3.1", ".. ... ...", "Variables not shown: Petal.Length", " (dbl), Petal.Width (dbl), Species", " (fctr)") expect_equal(diffobj:::detect_2d_guides(iris.dply), 4:5) # wrapping data table with separator (#96) DT.txt <- c( " V1 V2 V3", " 1: 0.3201122 0.6907066 0.5004968", " --- ", "1000: 0.3547379 0.2836985 0.8121208", " V4 V5", " 1: 0.331665 0.6788726", " --- ", "1000: 0.553012 0.7789110" ) expect_equal( diffobj:::detect_2d_guides(DT.txt), c(1L, 5L) ) # Narrow width old.opt <- options(width=40) on.exit(options(old.opt)) expect_equal(diffobj:::detect_2d_guides(capture.output(iris)), c(1, 152)) expect_equal( diffobj:::detect_2d_guides(capture.output(USAccDeaths)), c(1, 8, 15) ) # Time series expect_equal(diffobj:::detect_2d_guides(capture.output(UKgas)), 1) # no row.names (#111) df1 <- capture.output(print(data.frame(a=1:3), row.names=FALSE)) expect_warning(no.rn.guide <- diffobj:::detect_2d_guides(df1), NA) expect_equal(no.rn.guide, 1L) df2 <- capture.output(print(data.frame(x="A"), row.names=FALSE)) expect_warning(no.rn.guide.2 <- diffobj:::detect_2d_guides(df2), NA) expect_equal(no.rn.guide.2, 1L) }) test_that("detect_list_guides", { l.1 <- list(1, 1:3, matrix(1:3, 1)) l.2 <- list(a=1, list(1:3, b=4, c=list(1, b=2)), matrix(1:3, 1)) c.l.1 <- capture.output(l.1) c.l.2 <- capture.output(l.2) # cbind(c.l.2, seq_along(c.l.2) %in% diffobj:::detect_list_guides(c.l.2)) expect_equal(diffobj:::detect_list_guides(capture.output(l.1)), c(1, 4, 7)) expect_equal( diffobj:::detect_list_guides(capture.output(l.2)), c(1, 5, 8, 12, 15, 20) ) }) test_that("detect_matrix_guides", { mx3 <- mx4 <- mx5 <- mx5a <- mx11 <- matrix( c( "averylongwordthatcanlahblah", "causeasinglewidecolumnblah", "matrixtowrapseveraltimes", "inarrowscreen", "onceuponatime", "agreenduckflew", "overthemountains", "inalongofantelopes", "ineedthreemore", "entriesactually", "nowonlytwomore", "iwaswrongearlier" ), nrow=3, ncol=4 ) mx3.c <- capture.output(mx3) expect_equal(diffobj:::detect_matrix_guides(mx3.c, NULL), c(1, 5)) dimnames(mx4) <- list(A=NULL, B=NULL) mx4.c <- capture.output(mx4) expect_equal( diffobj:::detect_matrix_guides(mx4.c, dimnames(mx4)), c(1, 2, 6, 7) ) attr(mx5, "blah") <- letters[1:10] mx5.c <- capture.output(mx5) expect_equal( diffobj:::detect_matrix_guides(mx5.c, dimnames(mx5)), c(1, 5) ) # Simple matrices that don't wrap mx6 <- mx7 <- mx7.1 <- matrix(1:4, 2) mx6.c <- capture.output(mx6) expect_equal(diffobj:::detect_matrix_guides(mx6.c, dimnames(mx6)), 1) dimnames(mx7) <- list(A=letters[1:2], B=LETTERS[25:26]) mx7.c <- capture.output(mx7) expect_equal(diffobj:::detect_matrix_guides(mx7.c, dimnames(mx7)), c(1, 2)) dimnames(mx7.1) <- list(letters[1:2], B=LETTERS[25:26]) mx7.1.c <- capture.output(mx7.1) expect_equal(diffobj:::detect_matrix_guides(mx7.1.c, dimnames(mx7.1)), c(1, 2)) # Single col matrix mx8 <- matrix(1:2, 2) mx8.c <- capture.output(mx8) expect_equal(diffobj:::detect_matrix_guides(mx8.c, dimnames(mx8)), 1) # Wrapping matrices with colnames mx9 <- mx3 dimnames(mx9) <- list(A=letters[1:3], B=LETTERS[20:23]) mx9.c <- capture.output(mx9) expect_equal( diffobj:::detect_matrix_guides(mx9.c, dimnames(mx9)), c(1:2, 6:7) ) mx10 <- mx9 attr(mx10, "blah") <- matrix(1:4, 2) mx10.c <- capture.output(mx10) expect_equal( diffobj:::detect_matrix_guides(mx10.c, dimnames(mx10)), c(1:2, 6:7) ) old.opt <- options(width=30L) on.exit(options(old.opt)) attr(mx11, "blah") <- letters[1:15] mx11.c <- capture.output(mx11) expect_equal( diffobj:::detect_matrix_guides(mx11.c, dimnames(mx11)), c(1, 5, 9, 13) ) }) test_that("detect_array_guides", { a.1 <- array(1:6, dim=c(2, 1, 3)) a.2 <- array(1:6, dim=c(2, 1, 3), dimnames=list(NULL, "X", LETTERS[1:3])) a.3 <- array( 1:6, dim=c(2, 1, 3), dimnames=list(rows=NULL, cols="X", LETTERS[1:3]) ) a.4 <- `attr<-`(a.3, "hello", "random attribute") a.5 <- array(1:36, dim=c(6, 2, 3)) a.6 <- array(1:2, c(2, 1, 1)) c.a.1 <- capture.output(a.1) c.a.2 <- capture.output(a.2) c.a.3 <- capture.output(a.3) c.a.4 <- capture.output(a.4) c.a.5 <- capture.output(a.5) c.a.6 <- capture.output(a.6) # helper funs to vizualize the guide line detection # viz_dag <- function(capt, obj) # cbind( # capt, # seq_along(capt) %in% diffobj:::detect_array_guides(capt, dimnames(obj)) # ) # viz_dag(c.a.1, a.1) # viz_dag(c.a.2, a.2) # viz_dag(c.a.3, a.3) # viz_dag(c.a.4, a.4) # viz_dag(c.a.5, a.5) # viz_dag(c.a.6, a.6) expect_equal( diffobj:::detect_array_guides(c.a.1, dimnames(a.1)), c(1L, 2L, 7L, 8L, 13L, 14L) ) expect_equal( diffobj:::detect_array_guides(c.a.2, dimnames(a.2)), c(1L, 2L, 7L, 8L, 13L, 14L) ) expect_equal( diffobj:::detect_array_guides(c.a.3, dimnames(a.3)), c(1L, 2L, 8L, 9L, 15L, 16L) ) expect_equal( diffobj:::detect_array_guides(c.a.4, dimnames(a.4)), c(1L, 2L, 8L, 9L, 15L, 16L) ) expect_equal( diffobj:::detect_array_guides(c.a.5, dimnames(a.5)), c(1L, 2L, 11L, 12L, 21L, 22L) ) expect_equal( diffobj:::detect_array_guides(c.a.6, dimnames(a.6)), c(1L, 2L) ) }) test_that("detect_s4_guides", { setClass("gtest2", slots=c(hello="integer", `good bye`="list")) setClass("gtest1", slots=c( sub.class="gtest2", blah="character", gah="list", sub.class.2="gtest2" ) ) obj <- new( "gtest1", sub.class=new( "gtest2", hello=1:3, `good bye`=list("a", list(l1=5, l2="wow")) ), blah=letters, gah=list(one=1:10, two=LETTERS), sub.class.2=new( "gtest2", hello=3:1, `good bye`=list("B", list(l1=5, l2="wow")) ) ) # note at this point the nested stuff doesn't work, so we're just shooting for # the simple match c.1 <- capture.output(obj) expect_identical( diffobj:::detect_s4_guides(c.1, obj), c(1L, 2L, 21L, 25L, 34L) ) # small diff as that has a non-default show method diff <- diffChr("a", "b", format='raw') diff.out <- capture.output(show(diff)) expect_equal( diffobj:::detect_s4_guides(diff.out, diff), integer() ) }) test_that("custom guide fun", { a <- b <- matrix(1:100) b[50] <- -99L fun1 <- function(x, y) c(1L, 14L, 53L) expect_equal_to_reference( as.character(diffPrint(a, b, guides=fun1)), rdsf(100) ) if(getRversion() >= "3.2") { expect_warning( capture.output( trim.err <- as.character(diffPrint(a, b, guides=function(x, y) stop("boom"))), type="message" ), "If you did not specify a `guides`" ) expect_equal_to_reference(trim.err, rdsf(200)) } expect_error( diffobj:::apply_guides(1:26, LETTERS, function(x, y) 35L), "must produce an integer vector" ) }) test_that("errors", { expect_error( guidesStr(1:26, rep(NA_character_, 26)), "Cannot compute guides" ) expect_error( guidesPrint(1:26, rep(NA_character_, 26)), "Cannot compute guides" ) }) test_that("corner cases", { expect_equal( diffobj:::split_by_guides(letters, integer()), list(structure(letters, idx=seq_along(letters))) ) expect_error( guidesStr(1:26, rep(NA_character_, 26)), "Cannot compute guides" ) expect_error( guidesPrint(1:26, rep(NA_character_, 26)), "Cannot compute guides" ) }) diffobj/tests/testthat/testthat.diffStr.R0000755000176200001440000000647713420351310020253 0ustar liggesuserscontext("diffStr") if(!identical(basename(getwd()), "testthat")) stop("Working dir does not appear to be /testthat, is ", getwd()) rdsf <- function(x) file.path(getwd(), "helper", "diffStr", sprintf("%s.rds", x)) txtf <- function(x) file.path(getwd(), "helper", "diffStr", sprintf("%s.txt", x)) library(diffobj) test_that("lm models", { # formula display changed if(R.Version()$major >= 3 && R.Version()$minor >= "3.1") expect_equal_to_reference(as.character(diffStr(mdl1, mdl2)), rdsf(100)) # Too strict a line limit, can't get under expect_equal_to_reference( as.character(diffStr(mdl1[7], mdl2[7], line.limit=10)), rdsf(200) ) # Now we can get under expect_equal_to_reference( as.character(diffStr(mdl1[7], mdl2[7], line.limit=15)), rdsf(300) ) }) test_that("Simple structure", { # Character types expect_equal_to_reference(as.character(diffStr(iris.c, iris.s)), rdsf(400)) }) test_that("Strict width", { # formula display changed if(R.Version()$major >= 3 && R.Version()$minor >= "3.1") { expect_equal_to_reference( as.character( diffStr(mdl1, mdl2, extra=list(strict.width="wrap"), line.limit=30) ), rdsf(500) ) } }) test_that("max.diffs", { expect_warning( diffStr(iris, mtcars, max.diff=2), "Exceeded diff limit" ) }) test_that("max.level", { expect_equal_to_reference( as.character(diffStr(mdl1[7], mdl2[7], extra=list(max.level="auto"))), rdsf(600) ) expect_equal_to_reference( as.character(diffStr(mdl1[7], mdl2[7], extra=list(max.level=2))), rdsf(700) ) # Has a difference, but can't get under; the second is just for reference lst.1 <- lst.2 <- lst.3 <- list(a=list(b=list(c=list(d=list(e=list(25)))))) names(lst.2) <- "A" expect_equal_to_reference( as.character(diffStr(lst.1, lst.2, line.limit=2)), rdsf(800) ) expect_equal_to_reference( as.character(diffStr(lst.1, lst.2, line.limit=2)), rdsf(900) ) # Test that initial run shows difference, but too big, but next one down # doesn't so have to increase level names(lst.3$a$b$c$d) <- "E" expect_equal_to_reference( as.character(diffStr(lst.1, lst.3, line.limit=6)), rdsf(1000) ) }) test_that("No visible differences", { expect_equal_to_reference( as.character(diffStr(1:100, c(1:99, 101L))), rdsf(1100) ) }) test_that("Quoted Objects", { expect_equal( as.character(diffStr(quote(zz + 1), quote(zz + 3))), structure( c("\033[33m<\033[39m \033[33mstr(quote(zz +..\033[39m \033[34m>\033[39m \033[34mstr(quote(zz +..\033[39m", "\033[36m@@ 1 @@ \033[39m \033[36m@@ 1 @@ \033[39m", "\033[33m<\033[39m \033[90m\033[39m language zz + \033[33m1\033[39m\033[90m\033[39m \033[34m>\033[39m \033[90m\033[39m language zz + \033[34m3\033[39m\033[90m\033[39m" ), len = 3L ) ) expect_equal( as.character(diffStr(quote(x), quote(y))), structure(c("\033[33m<\033[39m \033[33mstr(quo..\033[39m \033[34m>\033[39m \033[34mstr(quo..\033[39m", "\033[36m@@ 1 @@ \033[39m \033[36m@@ 1 @@ \033[39m", "\033[33m<\033[39m \033[90m\033[39m symbol \033[33mx\033[39m\033[90m\033[39m \033[34m>\033[39m \033[90m\033[39m symbol \033[34my\033[39m\033[90m\033[39m"), len = 3L) ) }) test_that("Spaces with punctuation", { expect_known_output( show(diffStr(list(a=1), list(a=1, cabera=3), format='raw')), txtf(100) ) }) diffobj/tests/testthat/testthat.context.R0000755000176200001440000000321713466146330020341 0ustar liggesusers context("Context") if(!identical(basename(getwd()), "testthat")) stop("Working dir does not appear to be /testthat, is ", getwd()) rdsf <- function(x) file.path(getwd(), "helper", "context", sprintf("%s.rds", x)) txtf <- function(x) file.path(getwd(), "helper", "context", sprintf("%s.txt", x)) test_that("interesting context values", { expect_equal_to_reference( as.character(diffChr(chr.9, chr.10, context=0)), rdsf(100) ) expect_equal_to_reference( as.character(diffChr(chr.9, chr.10, context=-1L)), rdsf(150) ) expect_equal_to_reference( as.character(diffChr(chr.9, chr.10, context="auto")), rdsf(200) ) expect_equal_to_reference( as.character(diffChr(chr.9, chr.10, context=0, mode="context")), rdsf(300) ) }) test_that("with line limit", { expect_equal_to_reference( as.character(diffChr(chr.9, chr.10, context="auto", line.limit=18)), rdsf(400) ) expect_equal_to_reference( as.character(diffChr(chr.9, chr.10, context="auto", line.limit=25)), rdsf(500) ) # default to min context a <- b <- letters b[c(3, 20)] <- LETTERS[c(3,20)] expect_known_output( show(diffChr(a, b, line.limit=c(20, 10), context='auto', format='raw')), txtf(100) ) # trim hunks in auto-context mode a <- b <- letters b[c(3, 10, 20)] <- LETTERS[c(3,10,20)] expect_known_output( show( diffChr( a, b, hunk.limit=c(2, 1), context=auto_context(1, 5), line.limit=20, format='raw' ) ), txtf(200) ) }) test_that("error handling", { expect_error(auto_context(min=-1, max=1:3), "`min` must be") expect_error(auto_context(min=1, max=1:3), "`max` must be") }) diffobj/tests/testthat/testthat.s4.R0000755000176200001440000000243113466133406017200 0ustar liggesuserslibrary(testthat) library(diffobj) context("S4") test_that("diff data validation works", { # These are not currently in use # expect_match(diffobj:::valid_dat("hello"), "should be a list") # D0 <- D1 <- D2 <- D3 <- D4 <- D5 <- D6 <- D7 <- # diffPrint(letters, LETTERS)@tar.dat # expect_match(diffobj:::valid_dat(unname(D0)), "should have names") # length(D1[[1L]]) <- 1L # expect_match(diffobj:::valid_dat(D1), "should have equal length") # D2$orig <- integer(length(D2$orig)) # expect_match(diffobj:::valid_dat(D2), "should be character") # D3$trim.ind.start <- character(length(D3$trim.ind.start)) # expect_match(diffobj:::valid_dat(D3), "should be integer") # D4$word.ind <- integer(length(D4$word.ind)) # expect_match(diffobj:::valid_dat(D4), "should be list") # D5$word.ind <- vector("list", length(D5$word.ind)) # expect_match(diffobj:::valid_dat(D5), "not in expected format") # D6$tok.rat <- D6$tok.rat + 2 # expect_match(diffobj:::valid_dat(D6), "with all values between") # D7$fill <- integer(length(D7$fill)) # expect_match(diffobj:::valid_dat(D7), "should be logical") }) test_that("any", { expect_true(any(diffChr('a', 'b'))) expect_false(any(diffChr('a', 'a'))) expect_error(any(diffChr('a', 'a'), 2), "supports only one argument") }) diffobj/tests/testthat/testthat.capture.R0000755000176200001440000000337713420351310020311 0ustar liggesuserslibrary(testthat) library(diffobj) context("capture") test_that("capture width issues", { old.opt <- options(width=40L) on.exit(options(old.opt)) etc <- new("Settings", style=StyleRaw(), text.width=5L) # impossible width expect_warning( res <- diffobj:::capture(letters, etc, function(...) do.call(cat, list(...))), "Unable to set desired " ) expect_equal(nchar(res), c(40L, 40L, 36L)) }) test_that("errors in capture", { etc <- new("Settings", style=StyleRaw()) expect_error(diffobj:::capture(stop('boom'), etc, function(...) stop(...)), "Failed attempting.*boom" ) print <- function() NULL str <- function() NULL etc@mode <- "auto" etc@frame <- environment() expect_error( diffobj:::capt_print(1, 2, etc, function(...) stop(...), list()), "Unable to compose" ) expect_error( diffobj:::capt_str(1, 2, etc, function(...) stop(...), list(object=1)), "specify `object`" ) expect_error( diffobj:::capt_deparse( stop('a'), stop('b'), etc, function(...) stop(...), list() ), "attempting to deparse" ) expect_error( suppressWarnings( diffobj:::capt_file( tempfile(), tempfile(), etc, function(...) stop(...), list() ) ), "`target`" ) f <- tempfile() on.exit(unlink(f), add=TRUE) writeLines(letters, f) expect_error( suppressWarnings( diffobj:::capt_file(f, tempfile(), etc, function(...) stop(...), list()) ), "`current`" ) expect_error( suppressWarnings( diffobj:::capt_csv( tempfile(), tempfile(), etc, function(...) stop(...), list() ) ), "`target`" ) expect_error( suppressWarnings( diffobj:::capt_csv( f, tempfile(), etc, function(...) stop(...), list() ) ), "`current`" ) }) diffobj/tests/testthat/testthat.style.R0000755000176200001440000001044613466131016020012 0ustar liggesuserscontext("style") if(!identical(basename(getwd()), "testthat")) stop("Working dir does not appear to be /testthat, is ", getwd()) rdsf <- function(x) file.path(getwd(), "helper", "style", sprintf("%s.rds", x)) test_that("style palette", { expect_equal_to_reference( capture.output(diffobj:::display_ansi_256_styles()), rdsf(100) ) }) test_that("crayon settings", { # make sure crayon options are appropriately overriden old.opt <- options(crayon.enabled=FALSE) on.exit(options(old.opt)) expect_identical(crayon::green("green"), "green") # should have ANSI coloring despite crayon disabled expect_equal_to_reference( as.character(diffChr(letters[1:3], LETTERS[1:3])), rdsf(200) ) expect_identical(crayon::green("green"), "green") }) test_that("palette of styles", { pos <- PaletteOfStyles() expect_identical( pos[["ansi256", "light", "rgb"]], getClassDef("StyleAnsi256LightRgb", package="diffobj", inherits=FALSE) ) expect_equal_to_reference( capture.output(show(pos)), rdsf(300) ) expect_equal_to_reference( capture.output(summary(pos)), rdsf(400) ) }) test_that("Palette Subset", { p.o.s <- PaletteOfStyles() p.o.s["ansi256", "light", "yb"] <- list(StyleRaw()) expect_equal(c(p.o.s["ansi256", "light", "yb"]@data), list(StyleRaw())) expect_equal(p.o.s[["ansi256", "light", "yb"]], StyleRaw()) }) test_that("auto style selection", { expect_error( diffChr(letters, LETTERS, style="auto", format="xml"), "`format` must be one of" ) expect_is( diffChr( letters, LETTERS, style="auto", format="auto", brightness="light", term.colors=256 )@etc@style, "StyleAnsi256LightYb" ) expect_is( diffChr( letters, LETTERS, style="auto", format="auto", brightness="light", term.colors=8 )@etc@style, "StyleAnsi8NeutralYb" ) expect_is( diffChr( letters, LETTERS, style="auto", format="auto", interactive=FALSE, term.colors=1 )@etc@style, "StyleRaw" ) expect_is( diffChr( letters, LETTERS, style="auto", format="auto", interactive=TRUE, term.colors=1 # note pager off by default in tests )@etc@style, "StyleRaw" ) expect_is( diffChr( letters, LETTERS, style="auto", format="auto", interactive=TRUE, pager="auto", term.colors=1 )@etc@style, "StyleHtml" ) expect_is( diffChr( letters, LETTERS, style="auto", format="auto", interactive=TRUE, pager="auto", term.colors=9 )@etc@style, "StyleAnsi8NeutralYb" ) expect_is( diffChr( letters, LETTERS, style="auto", format="auto", interactive=TRUE, pager="auto", brightness='light', term.colors=500 )@etc@style, "StyleAnsi256LightYb" ) expect_is( diffChr( letters, LETTERS, style="auto", format="html", interactive=TRUE, pager="auto", color.mode=c("rgb", ansi8="yb") )@etc@style, "StyleHtmlLightRgb" ) expect_is( diffChr( letters, LETTERS, style="auto", format="html", interactive=TRUE, pager="auto", color.mode=c("rgb", html="yb") )@etc@style, "StyleHtmlLightYb" ) }) test_that("palette param selection", { expect_equal_to_reference( as.character( diffChr( letters, LETTERS, style="auto", format="ansi256", brightness=c("light", ansi256="dark") ) ), rdsf(500) ) expect_equal_to_reference( as.character( diffChr( letters, LETTERS, style="auto", format="ansi256", brightness=c("dark") ) ), rdsf(500) ) }) test_that("style validation", { s.f <- StyleFuns() expect_true(validObject(s.f)) s.f@word.insert <- function(x, y) NULL expect_error(validObject(s.f), "word.insert") expect_error( diffChr(1,2, format='html', style=list(scale=1:3)), "must be TRUE or FALSE" ) expect_error( diffChr(1,2, format='html', style=list(html.output="a")), "must be in" ) }) test_that("palette with objects", { pal <- PaletteOfStyles() pal["raw", "neutral", "rgb"] <- list(new(pal[["raw", "neutral", "rgb"]])) expect_warning( diffChr( letters, LETTERS, format="raw", brightness="neutral", color.mode="rgb", palette.of.styles=pal, style=list(na.sub="NA") ), "arguments cannot be applied" ) }) test_that("external files", { expect_true(file_test("-f", diffobj_css())) expect_true(file_test("-f", diffobj_js())) }) diffobj/tests/testthat/testthat.file.R0000755000176200001440000000531413464701604017573 0ustar liggesuserslibrary(diffobj) context("diffFile") if(!identical(basename(getwd()), "testthat")) stop("Working dir does not appear to be /testthat, is ", getwd()) rdsf <- function(x) file.path(getwd(), "helper", "diffFile", sprintf("%s.rds", x)) test_that("Code File", { # # compare two crayon file versions # # These should eventually just be downloaded and made into diffFile tests f.p.1 <- file.path("helper", "diffFile", "s.o.3f1f68.R") f.p.2 <- file.path("helper", "diffFile", "s.o.30dbe0.R") # url.1 <- "https://raw.githubusercontent.com/gaborcsardi/crayon/3f1f68ab177b82a27e754a58264af801f7194820/R/string_operations.r" # url.2 <- "https://raw.githubusercontent.com/gaborcsardi/crayon/30dbe0d4d92157350af3cb3aeebd6d9a9cdf5c0e/R/string_operations.r" # f.1 <- readLines(url.1) # f.2 <- readLines(url.2) # writeLines(f.1, f.p.1) # writeLines(f.2, f.p.2) expect_equal_to_reference(as.character(diffFile(f.p.1, f.p.2)), rdsf(100)) }) test_that("RDS", { f1 <- tempfile() f2 <- tempfile() on.exit(unlink(c(f1, f2))) mx1 <- mx2 <- matrix(1:9, 3) mx2[5] <- 99 saveRDS(mx1, f1) saveRDS(mx2, f2) expect_is(diffobj:::get_rds(f1), "matrix") expect_is(diffobj:::get_rds(f2), "matrix") ref <- as.character(diffPrint(mx1, mx2)) expect_identical(as.character(diffPrint(mx1, f2, cur.banner="mx2")), ref) expect_identical(as.character(diffPrint(f1, mx2, tar.banner="mx1")), ref) expect_identical( as.character(diffPrint(f1, f2, tar.banner="mx1", cur.banner="mx2")), ref ) expect_true(!identical(as.character(diffPrint(mx1, f2, rds=FALSE)), ref)) }) test_that("file", { f1 <- tempfile() f2 <- tempfile() on.exit(unlink(c(f1, f2))) letters2 <- letters letters2[15] <- "HELLO" writeLines(letters, f1) writeLines(letters2, f2) expect_identical( as.character(diffChr(letters, letters2, tar.banner="f1", cur.banner="f2")), as.character(diffFile(f1, f2)) ) # issue 133 h/t Noam Ross, thanks for the test library(diffobj) x <- tempfile() y <- tempfile() on.exit(unlink(c(x, y))) cat("Hello\nthere\n", file = x) file.copy(x, y) expect_identical( as.character(diffFile(x, y, format = "raw")), structure( c("No visible differences between objects.", "< x > y ", "@@ 1,2 @@ @@ 1,2 @@ ", " Hello Hello ", " there there "), len = 5L) ) }) test_that("CSV", { f1 <- tempfile() f2 <- tempfile() on.exit(unlink(c(f1, f2))) iris2 <- iris iris2$Sepal.Length[25] <- 9.9 write.csv(iris, f1, row.names=FALSE) write.csv(iris2, f2, row.names=FALSE) expect_identical( as.character(diffPrint(iris, iris2, tar.banner="f1", cur.banner="f2")), as.character(diffCsv(f1, f2)) ) }) diffobj/tests/testthat/testthat.html.R0000755000176200001440000000370013327367306017622 0ustar liggesuserscontext("html") if(!identical(basename(getwd()), "testthat")) stop("Working dir does not appear to be /testthat, is ", getwd()) rdsf <- function(x) file.path(getwd(), "helper", "html", sprintf("%s.rds", x)) # Tests need to fleshed out # Verify that internal css works test_that("HTML Output Modes", { expect_equal_to_reference( as.character( diffPrint( letters[1:3], LETTERS[1:3], style=StyleHtmlLightYb(html.output="diff.only") ) ), rdsf(100) ) expect_equal_to_reference( as.character( diffPrint( letters[1:6], LETTERS[1:6], style=StyleHtmlLightYb(html.output="diff.w.style") ) ), rdsf(200) ) expect_equal_to_reference( as.character( diffPrint( letters[1:6], LETTERS[1:6], style=StyleHtmlLightYb(html.output="page") ) ), rdsf(300) ) expect_equal_to_reference( as.character( diffPrint( letters[1:6], LETTERS[1:6], mode="unified", style=StyleHtmlLightYb(html.output="page") ) ), rdsf(350) ) }) test_that("Sub CSS", { # Mess up the CSS to test that we can change CSS file f <- tempfile() on.exit(unlink(f)) cat("div.row {background-color: red;}\n", file=f) expect_equal_to_reference( as.character( diffPrint( letters, LETTERS, style=StyleHtmlLightYb(css=f, html.output="diff.w.style") ) ), rdsf(400) ) }) test_that("Tag funs", { div_a <- div_f("A", c(color="red")) expect_equal( div_a(c("a", "b")), c( "
a
", "
b
" ) ) span_a <- span_f() expect_equal(span_a(c("a", "b")), c("a", "b")) expect_error(div_a(TRUE), "must be character") expect_equal(div_a(character()),character()) }) test_that("nchar", { expect_equal(nchar_html("25"), 2) expect_equal(nchar_html("25 "), 3) }) diffobj/tests/testthat/testthat.check.R0000755000176200001440000000700513327367306017735 0ustar liggesuserscontext("check") test_that("is.less_flags", { expect_true(diffobj:::is.less_flags("RVXF")) expect_true(diffobj:::is.less_flags("rvxF")) expect_false(diffobj:::is.less_flags(c("rvxF", "RVXF"))) expect_false(diffobj:::is.less_flags(23)) expect_false(diffobj:::is.less_flags("rv xF")) }) test_that("is.int.2L", { expect_true(diffobj:::is.int.2L(1:2)) expect_true(diffobj:::is.int.2L(as.numeric(1:2))) expect_false(diffobj:::is.int.2L(c(1.3, 2.2))) expect_false(diffobj:::is.int.2L(1:3)) expect_false(diffobj:::is.int.2L(c(1, NA))) }) test_that("arg.funs", { expect_true(diffobj:::is.one.arg.fun(function(x) NULL)) expect_true(diffobj:::is.one.arg.fun(function(x, y=5) NULL)) expect_match( diffobj:::is.one.arg.fun(function(..., x) NULL), "cannot have `...` as " ) expect_match(diffobj:::is.one.arg.fun(NULL), "is not a fun") expect_match(diffobj:::is.one.arg.fun(function() NULL), "have at least") expect_match(diffobj:::is.one.arg.fun(function(x, y) NULL), "cannot have any") expect_true(diffobj:::is.two.arg.fun(function(x, y) NULL)) expect_true(diffobj:::is.two.arg.fun(function(x, y=5) NULL)) expect_match( diffobj:::is.two.arg.fun(function(x, ..., y) NULL), "cannot have `...` as " ) expect_match(diffobj:::is.two.arg.fun(NULL), "is not a fun") expect_match(diffobj:::is.two.arg.fun(function(x) NULL), "have at least") expect_match( diffobj:::is.two.arg.fun(function(x, y, z) NULL), "cannot have any" ) }) test_that("valid_object", { s.h <- StyleHtml() s.h@wrap <- TRUE expect_error( diffobj:::valid_object(s.h, "style", stop), "an invalid `StyleHtml` object" ) pal <- PaletteOfStyles() pal["html", "light", "yb"] <- list(s.h) expect_error( diffChr( "A", "B", palette.of.styles=pal, style="auto", format="html", brightness="light", color.mode="yb" ), "`palette.of.styles` is an invalid" ) }) test_that("brightness", { expect_error(diffPrint(1:3, 3:6, brightness=NA), "must be character") expect_error(diffPrint(1:3, 3:6, brightness="red"), "may only contain values") expect_error(diffPrint(1:3, 3:6, brightness=c(raw='light')), "one empty name") expect_error( diffPrint(1:3, 3:6, brightness=c('light', 'dark')), "have names" ) }) test_that("misc", { expect_match(diffobj:::is.one.file.name(1), "must be character") expect_error(diffPrint(1:3, 2:6, extra="hello"), "must be a list") expect_error(diffPrint(1:3, 2:6, context=TRUE), "Argument `context` must") expect_error(diffPrint(1:3, 2:6, mode=1), "must be character") expect_error(diffPrint(1:3, 2:6, tab.stops=-1), "strictly positive") expect_error(diffPrint(1:3, 2:6, hunk.limit='hello'), "integer vector") expect_error(diffPrint(1:3, 2:6, guides='hello'), "or a function") expect_error( diffPrint(1:3, 2:6, guides=function(x, y, z) NULL), "cannot have any non" ) expect_error( diffPrint(1:3, 2:6, trim='hello'), "TRUE, FALSE, or a function" ) expect_error( diffPrint(1:3, 2:6, trim=function(x, y, z) NULL), "cannot have any non" ) expect_error(diffPrint(1:3, 2:6, interactive='hello'), "must be TRUE or") expect_error(diffPrint(1:3, 2:6, max.diffs=1:10), "must be integer") expect_error(diffPrint(1:3, 2:6, tar.banner=1:10), "must be atomic") expect_error(diffPrint(1:3, 2:6, style=1:10), "must be \"auto\", a") expect_error(diffPrint(1:3, 2:6, pager=1:10), "must be one of") expect_error(diffPrint(1:3, 2:6, format=1:10), "must be character") expect_error( diffPrint(1:3, 2:6, palette.of.styles=1:10), "must be a `PaletteOfStyles`" ) }) diffobj/tests/testthat/testthat.limit.R0000755000176200001440000000475313014331136017766 0ustar liggesuserscontext("limits") if(!identical(basename(getwd()), "testthat")) stop("Working dir does not appear to be /testthat, is ", getwd()) rdsf <- function(x) file.path(getwd(), "helper", "limit", sprintf("%s.rds", x)) library(diffobj) test_that("Simple limit", { A <- B <- letters[1:5] B[2] <- "B" B[6] <- "F" # diffChr(A, B) expect_equal_to_reference( as.character(diffChr(A, B, line.limit=2)), rdsf(100) ) expect_equal_to_reference( as.character(diffChr(A, B, line.limit=3)), rdsf(200) ) }) test_that("More Extensive Limits", { Puromycin2 <- Puromycin set.seed(1) Puromycin2$conc[c(8, 15:19, 22)] <- round(runif(7), 2) Puromycin2$state[17] <- "treated" expect_equal_to_reference( as.character( diffPrint(Puromycin, Puromycin2, line.limit=15, mode="sidebyside") ), rdsf(300) ) # # Not working right # diffPrint(Puromycin, Puromycin2, line.limit=15, mode="context") expect_equal_to_reference( as.character( diffPrint(Puromycin, Puromycin2, line.limit=15, mode="unified") ), rdsf(500) ) expect_equal_to_reference( as.character( diffPrint(Puromycin, Puromycin2, line.limit=5, mode="sidebyside") ), rdsf(600) ) expect_equal_to_reference( as.character( diffPrint(Puromycin, Puromycin2, line.limit=5, mode="context") ), rdsf(700) ) expect_equal_to_reference( as.character( diffPrint(Puromycin, Puromycin2, line.limit=5, mode="unified") ), rdsf(800) ) Puromycin3 <- Puromycin2 names(Puromycin3)[3L] <- "blargh" expect_equal_to_reference( as.character( diffPrint(Puromycin, Puromycin3, line.limit=7, mode="sidebyside") ), rdsf(900) ) expect_equal_to_reference( as.character( diffPrint(Puromycin, Puromycin3, line.limit=6, mode="context") ), rdsf(1000) ) } ) test_that("Dual limit values", { A <- letters[1:10] B <- LETTERS[1:10] expect_equal_to_reference( as.character(diffChr(A, B, line.limit=c(10, 3))), rdsf(1100) ) expect_equal_to_reference( as.character(diffChr(A, B, line.limit=c(13, 3))), rdsf(1200) ) expect_error(diffChr(A, B, line.limit=c(3, 13)), "larger than or") }) test_that("Cause errors", { expect_error(diffChr(letters, LETTERS, line.limit=1:3), "vector of length") }) test_that("Vanishing header", { # issue 64 expect_equal_to_reference( as.character( diffChr( letters, letters[-13], context=auto_context(0, 10), line.limit=1L, pager="off" ) ), rdsf(1300) ) }) diffobj/tests/testthat/testthat.summary.R0000755000176200001440000000467313442554134020360 0ustar liggesuserscontext("summary") if(!identical(basename(getwd()), "testthat")) stop("Working dir does not appear to be /testthat, is ", getwd()) rdsf <- function(x) file.path(getwd(), "helper", "summary", sprintf("%s.rds", x)) txtf <- function(x) file.path(getwd(), "helper", "summary", sprintf("%s.txt", x)) # Note, atomic prints happen in different test file test_that("Any", { expect_false(any(diffPrint(iris.s, iris.s))) expect_warning(res <- any(diffPrint(iris.s, iris.c)), "objects are NOT") expect_false(res) expect_true(any(diffPrint(iris.s, iris.4))) }) test_that("Small Summary", { expect_equal_to_reference( as.character(summary(diffPrint(iris.s, iris.4))), rdsf(100) ) expect_equal_to_reference( as.character(summary(diffPrint(iris.s, iris.2))), rdsf(200) ) expect_equal_to_reference( as.character(summary(diffPrint(iris.s, iris.3))), rdsf(300) ) expect_equal_to_reference( as.character(summary(diffPrint(iris.s, iris.c))), rdsf(400) ) # All equal expect_equal_to_reference( as.character(summary(diffChr(letters, letters))), rdsf(450) ) }) test_that("Big Summary", { # Make sure we test summary reduction, wrapping expect_equal_to_reference( as.character(summary(diffChr(chr.7, chr.8))), rdsf(500) ) expect_equal_to_reference( as.character(summary(diffChr(chr.7, chr.8), scale.threshold=1)), rdsf(600) ) expect_equal_to_reference( as.character(summary(diffChr(chr.7, chr.8), scale.threshold=0)), rdsf(700) ) # Force truncation of summary expect_equal_to_reference( as.character( summary(diffChr(chr.7, chr.8), scale.threshold=0, max.lines=2) ), rdsf(800) ) }) test_that("Show", { expect_true( paste0(capture.output(summary(diffChr(chr.7, chr.8))), collapse="\n") == as.character(summary(diffChr(chr.7, chr.8))) ) }) test_that("HTML summary", { expect_equal_to_reference( as.character( summary( diffPrint( iris.s, iris.4, format="html", style=list(html.output="page") ) ) ), rdsf(900) ) }) test_that("errors", { diff <- diffChr("hello green world", "hello red world") expect_error(summary(diff, max.lines=0), "strictly positive") expect_error(summary(diff, width=1:3), "integer\\(1L\\)") expect_error(summary(diff, scale.threshold=5), "between 0 and 1") }) test_that("width wrap", { diff <- diffChr("hello green world", "hello red world", format='raw') expect_known_output(show(summary(diff, width=5)), txtf(100)) }) diffobj/tests/testthat/testthat.diffObj.R0000755000176200001440000000354213442554134020220 0ustar liggesuserscontext("diffObj") if(!identical(basename(getwd()), "testthat")) stop("Working dir does not appear to be /testthat, is ", getwd()) rdsf <- function(x) file.path(getwd(), "helper", "diffObj", sprintf("%s.rds", x)) test_that("simple diffobj", { # no diff for print expect_equal_to_reference(as.character(diffObj(iris.s, iris.c)), rdsf(100)) # no diff for str expect_equal_to_reference( as.character(diffObj(1:100, c(1:99, 200L))), rdsf(200) ) # diffs for both and must pick one, first one is str, second is print expect_equal_to_reference( as.character(diffObj(mdl1[7], mdl2[7])), rdsf(300) ) expect_equal_to_reference(as.character(diffObj(mdl1, mdl2)), rdsf(400)) }) test_that("fits or doesn't", { # Note, the first test used to favor str until we handicapped print expect_equal( diffObj(matrix(1:20, ncol=2), matrix(2:21, ncol=2), line.limit=5)@capt.mode, "str" ) # test kinda slow, would be better to have one with smaller objects with print # methods expect_equal( diffObj(mdl1, mdl2, line.limit=15, mode='unified')@capt.mode, "print" ) expect_equal(diffObj(1:1000, 1000:1, line.limit=5)@capt.mode, "str") }) test_that("misc", { expect_error(diffObj(1, 2, extra=list(TRUE)), "extra") }) test_that("print error", { x <- structure("hello", class="diffobj_ogewlhgiadfl") y <- structure("goodbye", class="diffobj_ogewlhgiadfl") expect_error(diffObj(x, y), "Error in calling .diffPrint.") }) # Random exmaples to think through `diffObj` output diffObj( pairlist("`logical(2L)` should be length 2 (is 3)"), pairlist("be length 2 (is 3)") ) diffObj( pairlist("`matrix(integer(), nrow = 3)` should be matrix (is list)", "`list(character(1L), 1L)[[2]]` should be type \"integer-like\" (is \"character\")"), pairlist("be class \"matrix\" (is \"list\")", "be type \"integer-like\" (is \"character\") at index [[2]]") ) diffobj/tests/testthat/testthat.text.R0000755000176200001440000001114113465617134017640 0ustar liggesuserslibrary(diffobj) context("text") test_that("simple wrap", { txt1 <- c( "humpty dumpty sat on a wall and had a big fall", "humpty sat on a wall and dumped a big fall" ) res1 <- diffobj:::wrap(txt1, 10, TRUE, sgr.supported=TRUE) expect_identical( gsub(" *$", "", vapply(res1, paste0, character(1L), collapse="")), txt1 ) expect_equal(lapply(res1, nchar), list(rep(10L, 5L), rep(10L, 5L))) txt2 <- "hello world!" expect_identical( unlist(diffobj:::wrap(txt2, nchar(txt2), TRUE, sgr.supported=TRUE)), txt2 ) expect_identical( paste0( unlist(diffobj:::wrap(txt2, nchar(txt2) / 2, TRUE, sgr.supported=TRUE)), collapse="" ), txt2 ) }) test_that("wrap with escape sequences", { txt3 <- c( paste0( "humpty dumpty ", crayon::style("sat on a wall", "red"), " and had a big fall", crayon::style( crayon::style( "humpty sat on a wall and dumped a big fall", "green" ), "bgRed" ), "woohoo" ), paste0( crayon::style("hello ", "inverse"), "beautiful ", crayon::style("world", "blue") ) ) res3 <- diffobj:::wrap(txt3, 10, TRUE, sgr.supported=TRUE) expect_identical( crayon::strip_style( gsub(" *$", "", vapply(res3, paste0, character(1L), collapse="")) ), crayon::strip_style(txt3) ) expect_equal( lapply(res3, crayon::col_nchar), list(rep(10L, 10L), rep(10L, 3L)) ) }) test_that("strip hz whitespace", { old.opt <- options(crayon.enabled=FALSE) on.exit(options(old.opt)) expect_equal( diffobj:::strip_hz_control("a\tb", stops=4L, sgr.supported=TRUE), "a b") expect_equal( diffobj:::strip_hz_control("ab\t", stops=4L, sgr.supported=TRUE), "ab ") expect_equal( diffobj:::strip_hz_control("a\tb\t", stops=4L, sgr.supported=TRUE), "a b ") expect_equal( diffobj:::strip_hz_control("\ta\tb\t", stops=4L, sgr.supported=TRUE), " a b " ) expect_equal( diffobj:::strip_hz_control("\ta\tb\t", stops=c(2L, 4L), sgr.supported=TRUE), " a b " ) expect_equal( diffobj:::strip_hz_control( c("ab\t", "\ta\tb\t"), sgr.supported=TRUE, stops=4L ), c("ab ", " a b ") ) # recall that nchar("\033") == 1 expect_equal( diffobj:::strip_hz_control( "\033[31ma\t\033[39mhello\tb", stops=10L, sgr.supported=FALSE ), "\033[31ma \033[39mhello b" ) expect_equal( diffobj:::strip_hz_control( "\033[31ma\t\033[39mhello\tb", stops=10L, sgr.supported=TRUE ), "\033[31ma\033[39m \033[31m\033[39mhello \033[31m\033[39mb" ) # carriage returns expect_equal( diffobj:::strip_hz_control("hellothere\rHELLO", sgr.supported=TRUE), "HELLOthere" ) expect_equal( diffobj:::strip_hz_control( c("hellothere\rHELLO", "000\r12345678\rabcdef\rABC"), sgr.supported=TRUE ), c("HELLOthere", "ABCdef78") ) expect_equal( diffobj:::strip_hz_control("hellothere\r", sgr.supported=TRUE), "hellothere" ) expect_equal( diffobj:::strip_hz_control(character(), sgr.supported=TRUE), character() ) # newlines expect_equal( diffobj:::strip_hz_control(c("a", "", "\n", "a\nb"), sgr.supported=TRUE), c("a", "", "", "a", "b") ) # with colors options(crayon.enabled=TRUE) expect_equal( crayon::strip_style( diffobj:::strip_hz_control( "\033[31ma\t\033[39mhello\tb", stops=10L, sgr.supported=TRUE) ), "a hello b" ) test.chr <- paste0( crayon::red(crayon::`%+%`("000", crayon::bgBlue("\r12345678"))), "\rabcdef", crayon::green("\rABC") ) # visually inspect these # cat("\n") # cat(test.chr, sep="\n") res <- diffobj:::strip_hz_control(test.chr, sgr.supported=TRUE) # cat(res, sep="\n") expect_equal(crayon::strip_style(res), "ABCdef78") # Mix tabs and carriage returns, visual inspection assumes terminal tab # stops at 8L; note output not exactly the same since it seems tabs don't # ovewrite prior screen state whereas spaces do test.chr.2 <- paste0( crayon::red(crayon::`%+%`("000", crayon::bgBlue("\r123\t456\t78"))), "\rab\tcd f", crayon::green("\rABC") ) # cat("\n") # cat(test.chr.2, sep="\n") res.2 <- diffobj:::strip_hz_control(test.chr.2, stops=8L, sgr.supported=TRUE) # cat(res.2, sep="\n") expect_equal(crayon::strip_style(res.2), "ABC cd f 78") # multi line test.chr.3 <- c(test.chr, test.chr.2) # cat("\n") res.3 <- diffobj:::strip_hz_control(test.chr.3, sgr.supported=TRUE) # cat(res.3, sep="\n") # cat(test.chr.3, sep="\n") expect_equal(crayon::strip_style(res.3), c("ABCdef78", "ABC cd f 78")) }) diffobj/tests/testthat/testthat.core.R0000755000176200001440000001220113014331136017563 0ustar liggesuserslibrary(diffobj) context("core") local({ # The Myers paper strings A <- c("a", "b", "c", "a", "b", "b", "a") B <- c("c", "b", "a", "b", "a", "c") test_that("diff myers simple", { expect_identical( diffobj:::myers_simple(character(), character()), list(target = integer(0), current = integer(0)) ) expect_identical( diffobj:::myers_simple("a", character()), list(target = NA_integer_, current = integer(0)) ) expect_identical( diffobj:::myers_simple(character(), "a"), list(target = integer(0), current = NA_integer_) ) expect_identical( diffobj:::myers_simple("a", "a"), list(target = 0L, current = 0L) ) expect_identical( diffobj:::myers_simple("a", "b"), list(target = 1L, current = 1L) ) expect_identical( diffobj:::myers_simple(c("a", "b"), "b"), list(target = c(NA, 0L), current = 0L) ) expect_identical( diffobj:::myers_simple(c("a", "b"), "a"), list(target = c(0L, NA), current = 0L) ) expect_identical( diffobj:::myers_simple("a", c("a", "b")), list(target = 0L, current = c(0L, NA)) ) expect_identical( diffobj:::myers_simple("b", c("a", "b")), list(target = 0L, current = c(NA, 0L)) ) expect_identical( diffobj:::myers_simple(c("a", "b"), c("b", "c")), list(target = c(NA, 0L), current = c(0L, NA)) ) expect_identical( diffobj:::myers_simple(c("a", "b", "c", "d"), c("a", "c", "d", "b")), list(target = c(0L, NA, 0L, 0L), current = c(0L, 0L, 0L, NA)) ) # Actual Myers sample string expect_identical( diffobj:::myers_simple(A, B), list(target = c(NA, NA, 0L, 0L, 0L, NA, 0L), current = c(0L, NA, 0L, 0L, 0L, NA)) ) } ) test_that("diff myers mba", { expect_identical(ses(character(), character()), character()) expect_identical(ses("a", character()), "1d0") expect_identical(ses(character(), "a"), "0a1") expect_identical(ses("a", "a"), character()) expect_identical(ses("a", "b"), "1c1") expect_identical(ses(c("a", "b"), "b"), "1d0") expect_identical(ses(c("a", "b"), "a"), "2d1") expect_identical(ses("a", c("a", "b")), "1a2") expect_identical(ses("b", c("a", "b")), "0a1") expect_identical(ses(c("a", "b"), c("b", "c")), c("1d0", "2a2")) expect_identical( ses(c("a", "b", "c", "d"), c("a", "c", "d", "b")), c("2d1", "4a4") ) # Actual Myers sample string expect_identical(ses(A, B), c("1,2d0", "4d1", "5a3", "7a6")) # This used to cause errors due to under-allocated buffer vector expect_identical(ses(letters[1:10], LETTERS[1:2]), "1,10c1,2") # A little more complex with changes, this was a problem at some point A2 <- c("A", "B", "C") B2 <- c("X", "A", "Y", "C") expect_identical(ses(A2, B2), c("0a1", "2c3")) # More complicated strings; intended for use with contexts for hunks, # but making sure the diffs are correct A1 <- c("A", "AA", "B", "C", "D", "E", "F", "G", "H") B1 <- c("A", "B", "X", "W", "D", "DD", "E", "Y", "Z") C1 <- c("X", "D", "E", "Y", "Z", "H") expect_identical(ses(A1, B1), c("2d1", "4c3,4", "5a6", "7,9c8,9")) expect_identical(ses(A1, C1), c("1,4c1", "7,8c4,5")) A5 <- c("A", "AA", "B", "C", "D", "E", "F", "G", "H") B5 <- c("A", "B", "X", "W", "D", "E", "F", "W", "G") expect_identical(ses(A5, B5), c("2d1", "4c3,4", "7a8", "9d9")) # NAs treated as strings expect_identical(ses(c(NA, "a", "b"), c("a", "b", NA)), c("1d0", "3a3")) # Coersion to character expect_identical(ses(1:5, 4:6), c("1,3d0", "5a3")) } ) test_that("print/summary", { capture.output( res.1 <- summary(diffobj:::diff_myers(A, B), with.match=TRUE) ) expect_identical( res.1, structure(list(type = structure(c(3L, 1L, 3L, 1L, 2L, 1L, 2L), .Label = c("Match", "Insert", "Delete"), class = "factor"), string = structure(c(2L, 5L, 1L, 3L, 1L, 4L, 5L), .Label = c("a", "ab", "b", "ba", "c"), class = "factor"), len = c(2L, 1L, 1L, 1L, 1L, 2L, 1L), offset = c(1L, 3L, 4L, 5L, 3L, 6L, 6L)), .Names = c("type", "string", "len", "offset"), row.names = c(NA, -7L), class = "data.frame") ) capture.output( res.2 <- summary(diffobj:::diff_myers(A, B), with.match=FALSE) ) expect_identical( res.2, structure(list(type = structure(c(3L, 1L, 3L, 1L, 2L, 1L, 2L), .Label = c("Match", "Insert", "Delete"), class = "factor"), len = c(2L, 1L, 1L, 1L, 1L, 2L, 1L), offset = c(1L, 3L, 4L, 5L, 3L, 6L, 6L)), .Names = c("type", "len", "offset"), row.names = c(NA, -7L), class = "data.frame") ) expect_identical( capture.output(print(diffobj:::diff_myers(A, B))), ses(A, B) ) }) # test_that("translate", { # aa <- c("a", "b", "b", "c", "e") # bb <- c("x", "y", "c", "f", "e") # expect_identical( # diffobj:::diffObjCompact(diffobj:::diff_myers(A, B)), # list(target = c(NA, NA, 0L, NA, 0L, 0L, 0L), current = c(0L, 0L, NA, 0L, 0L, NA)) # ) # expect_identical( # diffobj:::diffObjCompact(diffobj:::diff_myers(aa, bb)), # list(target = c(1L, 2L, NA, 0L, 0L), current = c(1L, 2L, 0L, NA, 0L)) # ) # # } ) }) diffobj/tests/testthat/testthat.pager.R0000755000176200001440000002421713466146334017762 0ustar liggesuserslibrary(diffobj) context("pager") txtf <- function(x) file.path(getwd(), "helper", "pager", sprintf("%s.txt", x)) # void pager, doesn't do anything, just to test side effect of writing to file void <- function(x) NULL test_that("Specifying pager", { style <- gdo("diffobj.style") if(is.null(style)) style <- StyleAnsi8NeutralYb() style@pager@file.ext <- "xyz" # make pager identifiable expect_equal( diffChr( letters, LETTERS, style=style, pager="auto", interactive=TRUE )@etc@style@pager@file.ext, "xyz" ) expect_equal( diffChr( letters, LETTERS, style=style, pager="off", interactive=TRUE )@etc@style@pager, PagerOff() ) expect_identical( diffChr( letters, LETTERS, style=style, pager="auto", interactive=FALSE )@etc@style@pager, PagerOff() ) }) test_that("System Pagers", { less.orig <- Sys.getenv("LESS") pager_mock <- function(...) { warning(Sys.getenv("LESS")) 42 } expect_is(PagerSystem(), "PagerSystem") expect_is( pg.less <- PagerSystemLess(pager=pager_mock, flags="VWF"), "PagerSystemLess" ) expect_warning(res <- pg.less@pager(), "VWF$") expect_equal(res, 42) expect_equal(less.orig, Sys.getenv("LESS")) expect_equal(PagerSystemLess(pager=pager_mock)@flags, "R") expect_error(PagerSystemLess(pager=pager_mock, flags=letters)) }) test_that("use_pager", { with_mock( "diffobj:::console_lines"=function(...) 10L, { expect_true(diffobj:::use_pager(PagerSystem(threshold=0L), 1L)) expect_false(diffobj:::use_pager(PagerSystem(threshold=50L), 25L)) expect_true(diffobj:::use_pager(PagerSystem(threshold=-1L), 25L)) } ) }) test_that("Setting LESS var", { less.orig <- Sys.getenv("LESS") old.opt <- options(crayon.enabled=FALSE) # problems with crayon and LESS on.exit({ diffobj:::reset_less_var(less.orig) # should be tested..., but super simple options(old.opt) }) # Here we change the LESS variable even though we're mocking getenv with_mock( Sys.getenv=function(...) NA_character_, expect_true(is.na(diffobj:::set_less_var("XF"))) ) expect_equal(Sys.getenv("LESS"), "-XF") with_mock( Sys.getenv=function(...) "-X -F", expect_equal(diffobj:::set_less_var("VP"), "-X -F") ) expect_equal(Sys.getenv("LESS"), "-X -FVP") diffobj:::reset_less_var("-XF") expect_equal(Sys.getenv("LESS"), "-XF") diffobj:::reset_less_var(NA_character_) expect_equal(Sys.getenv("LESS"), "") with_mock( Sys.getenv=function(...) "-XF", expect_equal(diffobj:::set_less_var("V"), "-XF") ) expect_equal(Sys.getenv("LESS"), "-XFV") with_mock( Sys.getenv=function(...) NULL, expect_warning(diffobj:::set_less_var("V"), "Unable to set") ) }) test_that("viewer vs browser", { viewer <- function(x) "viewer" old.external <- options(viewer=viewer, browser=function(url) "browser") on.exit(options(old.external)) with_mock( "diffobj::make_blocking"=identity, { pager <- PagerBrowser() expect_equal(pager@pager("blah"), "viewer") options(viewer=NULL) expect_equal(pager@pager("blah"), "browser") options(viewer=function(x) stop("viewer error")) expect_warning(res <- pager@pager("blah"), "IDE viewer") expect_equal(res, "browser") } ) }) test_that("blocking", { # Note that readline just proceeds in non-interactive mode, which is why we # need the mock here with_mock( "diffobj:::interactive"=function() FALSE, "diffobj:::readline"=function(...) warning("readline"), { expect_error(make_blocking("hello"), "must be a function") expect_error(make_blocking(identity, letters), "must be character\\(1L") expect_error(make_blocking(identity, "a", "a"), "must be TRUE") expect_warning(res <- make_blocking(sum)(1:10), "readline") expect_equal(sum(1:10), res) expect_true( withVisible( suppressWarnings(make_blocking(sum, invisible=FALSE)(1:10)) )[['visible']] ) }) with_mock( "diffobj:::interactive"=function() TRUE, "diffobj:::readline"=function(...) warning("readline"), { expect_warning( show( diffChr( "a", "b", format='raw', pager=list(pager=void, make.blocking=TRUE, threshold=0) ) ), "readline" ) expect_warning( show( diffChr( "a", "b", format='html', pager=list(pager=void, make.blocking=NA, threshold=0) ) ), "readline" ) expect_warning( show(diffChr("a", "b", format='html', pager=list(pager=void))), "readline" ) f <- tempfile() on.exit(unlink(f)) expect_warning( show( diffChr( "a", "b", format='html', pager=list(pager=void, make.blocking=NA, file.path=f) ) ), NA ) expect_warning( show( diffChr( "a", "b", format='html', pager=list(pager=void, make.blocking=FALSE, file.path=f) ) ), NA ) expect_warning( show( diffChr("a", "b", format='html', pager=list(pager=void, file.path=f)) ), NA ) } )}) test_that("html page output", { pager <- PagerBrowser( pager=function(x) cat(readLines(x), sep="\n"), make.blocking=FALSE ) expect_equal( capture.output(show(diffChr("A", "B", pager=pager, style=StyleRaw()))), c("< \"A\" > \"B\" ", "@@ 1 @@ @@ 1 @@ ", "< A > B ") ) pager.warn <- PagerBrowser( pager=function(x) cat(readLines(x), sep="\n"), make.blocking=FALSE ) expect_error( diffChr( "A", "B", pager=pager.warn, format="html", style=list(js="notafile") ), "Unable to instantiate `Style` object: Argument `js` .* is not a file" ) expect_error( diffChr( "A", "B", pager=pager.warn, format="html", style=list(css="notafile") ), "Unable to instantiate `Style` object: Argument `css` .* is not a file" ) # Create objects that bypass the validation style.obj.1 <- style.obj.2 <- StyleHtmlLightYb() style.obj.1@css <- "notafile" style.obj.2@js <- "notafile" expect_warning( capture.output( show(diffChr("A", "B", pager=pager.warn, style=style.obj.1)) ), "Unable to read provided css file" ) expect_warning( capture.output( show(diffChr("A", "B", pager=pager.warn, style=style.obj.2)) ), "Unable to read provided js file" ) }) test_that("pager_is_less", { is.less <- pager_is_less() expect_true(diffobj:::is.TF(is.less)) less <- tryCatch( system2("which", "less", stdout=TRUE, stderr=TRUE), error=function(e) NULL, warning=function(e) NULL ) sys.cat <- tryCatch( system2("which", "cat", stdout=TRUE, stderr=TRUE), error=function(e) NULL, warning=function(e) NULL ) if(diffobj:::is.chr.1L(less) && file_test("-x", less)) { local({ old.opt <- options(pager=less) on.exit(options(old.opt)) expect_false(diffobj:::pager_opt_default()) expect_true(pager_is_less()) }) } if(diffobj:::is.chr.1L(sys.cat) && file_test("-x", sys.cat)) { local({ old.opt <- options(pager=sys.cat) on.exit(options(old.opt)) expect_false(diffobj:::pager_opt_default()) expect_false(pager_is_less()) }) } ## force some checks local({ old.opt <- options(pager=NULL) on.exit(options(old.opt)) expect_false(pager_is_less()) }) expect_false(diffobj:::file_is_less(tempfile())) }) test_that("file.path", { f <- tempfile() show( diffChr( "A", "B", format='raw', pager=list(pager=void, file.path=f, threshold=0L) ) ) expect_equal( readLines(f), c("< \"A\" > \"B\" ", "@@ 1 @@ @@ 1 @@ ", "< A > B ") ) expect_error( show( diffChr( "A", "B", format='raw', pager=list(pager=void, file.path=NA, threshold=0L) ) ), NA ) expect_error(Pager(file.path=letters), "must be length 1") expect_error(Pager(file.path=1), "must be character") }) test_that("basic pager", { f <- tempfile() on.exit(unlink(f)) expect_known_output( show( diffChr( 1, 2, pager=Pager(file.path=f, threshold=0L), format='raw' ) ), txtf(100) ) expect_equal(readLines(txtf(100)), readLines(f)) unlink(f) }) test_that("format-pager interaction", { old.opt <- options(crayon.colors=7) crayon::num_colors(TRUE) on.exit({ options(old.opt) crayon::num_colors(TRUE) }) expect_is( diffChr(1, 2, format='auto', pager="on", interactive=TRUE)@etc@style, "StyleHtml" ) expect_is( diffChr(1, 2, format='auto', pager="on", interactive=FALSE)@etc@style, "StyleRaw" ) expect_is( diffChr( 1, 2, format='auto', pager=PagerBrowser(), interactive=FALSE )@etc@style, "StyleHtml" ) }) test_that("format-pager interaction 2", { old.rs <- Sys.getenv('RSTUDIO', unset=NA) old.rsterm <- Sys.getenv('RSTUDIO_TERM', unset=NA) on.exit({ if(is.na(old.rs)) { Sys.unsetenv('RSTUDIO') } else Sys.setenv('RSTUDIO'=old.rs) if(is.na(old.rsterm)) { Sys.unsetenv('RSTUDIO_TERM') } else Sys.setenv('RSTUDIO_TERM'=old.rsterm) }) Sys.unsetenv('RSTUDIO') Sys.unsetenv('RSTUDIO_TERM') old.opt <- options(crayon.colors=8) crayon::num_colors(TRUE) on.exit({options(old.opt); crayon::num_colors(TRUE)}, add=TRUE) Sys.setenv(RSTUDIO='1') expect_is( diffChr(1, 2, format='auto', pager='on', interactive=TRUE)@etc@style, "StyleHtml" ) expect_is( diffChr(1, 2, format='auto', interactive=FALSE)@etc@style, "StyleAnsi" ) Sys.setenv(RSTUDIO_TERM='HELLO') crayon::num_colors(TRUE) expect_is( diffChr(1, 2, format='auto', pager='on', interactive=TRUE)@etc@style, "StyleAnsi" ) }) test_that("format-pager interaction 3", { expect_is( diffPrint(1:3, 3:1, format='auto', interactive=FALSE, term.colors=1)@etc@style, "StyleRaw" ) expect_is( diffPrint(1:3, 3:1, format='auto', interactive=FALSE, term.colors=8)@etc@style, "StyleAnsi" ) }) test_that("Default pager writes to screen", { # issue132 thanks Bill Dunlap f <- tempfile() on.exit(unlink(f)) writeLines("hello world", f) expect_equal(capture.output(new("Pager")@pager(f)), "hello world") }) diffobj/tests/testthat/testthat.diffChr.R0000755000176200001440000001370113466061276020226 0ustar liggesuserscontext("diffChr") if(!identical(basename(getwd()), "testthat")) stop("Working dir does not appear to be /testthat, is ", getwd()) rdsf <- function(x) file.path(getwd(), "helper", "diffChr", sprintf("%s.rds", x)) txtf <- function(x) file.path(getwd(), "helper", "diffChr", sprintf("%s.txt", x)) test_that("Corner Cases", { # Corner cases from https://neil.fraser.name/writing/diff/ # Both of these appear handled correctly by the algorithm here # first one: suboptimal edit script due to two sided approach A1 <- c("X", "A", "X", "C", "X", "A", "B", "C") B1 <- c("A", "B", "C", "Y") expect_equal_to_reference(as.character(diffChr(A1, B1)), rdsf(100)) # second one: failure to find intersection at ends of paths (paths run into # each other eventually) A2 <- c("A", "B", "X", "A", "B") B2 <- c("A", "Y", "B") expect_equal_to_reference(as.character(diffChr(A2, B2)), rdsf(200)) # Simple corner cases expect_equal_to_reference( as.character(diffChr(character(), character())), rdsf(225) ) expect_equal_to_reference(as.character(diffChr("", "")), rdsf(250)) }) test_that("Larger strings", { # diffChr(X[1:2000], X[2001:4000]) expect_equal_to_reference(as.character(diffChr(chr.7, chr.8)), rdsf(300)) # Too slow to run; useful for benchmarking though # X1 <- X[1:2e4] # X2 <- X1[-sample(seq_along(X1), 2e3)] # X2[sample(seq_along(X2), 4e3)] <- "XXXXXX" # res <- diffChr(X1, X2) # res <- diffChr(X[1:10000], X[7500:17500]) # res <- ses(X[1:10000], X[7500:17500]) # res <- diffChr(X[1:25000], X[10001:50000], max.diffs=65000) }) test_that("Sentences", { chr.5 <- c( "hello there how are you doing", "humpty dumpty took a big fall", "lorem ipsum dolor sic est boom", "a computer once wrote a phrase" ) chr.6 <- c( "hello THERE how are you doing", "and another SENTENCE blah blah", "humpty dumpty TOOK a big fall", "a COMPUTER once wrote a phrase" ) expect_equal_to_reference(as.character(diffChr(chr.5, chr.6)), rdsf(400)) expect_equal_to_reference( as.character(diffChr(chr.5, chr.6, mode="unified")), rdsf(500) ) expect_equal_to_reference( as.character(diffChr(chr.5, chr.6, mode="context")), rdsf(600) ) }) test_that("Whitespace", { expect_equal_to_reference( as.character(diffChr(c("a", "b", "c"), c("a ", "b", "c"))), rdsf(800) ) expect_equal_to_reference( as.character( diffChr(c("a", "b", "c"), c("a ", "b", "c"), ignore.white.space=FALSE) ), rdsf(900) ) # New lines count as new elements expect_equal_to_reference( as.character(diffChr("woo\nhoo\nfoo", c("woo", "foo"))), rdsf(1000) ) expect_known_output( diffChr("hello . world", "hello. world", format='raw'), txtf(100) ) }) test_that("SGR", { a <- c("hello \033[31mworld\033[m", "umbrellas", "tomatoes") b <- c("hello world", "umbrellas", "tomatoes") old.opt <- options(diffobj.sgr.supported=TRUE) on.exit(old.opt) expect_warning(diff <- diffChr(a, b), 'contained ANSI CSI SGR') expect_known_output(show(diff), txtf(200)) expect_known_output(show(diffChr(a, b, strip.sgr=FALSE)), txtf(300)) expect_known_output(show(diffChr(a, b, format='raw')), txtf(400)) expect_error(diffChr(a, b, strip.sgr=1:3), "TRUE, FALSE, or NULL") expect_error(diffChr(a, b, sgr.supported=1:3), "TRUE, FALSE, or NULL") }) test_that("Alignment", { chr.7 <- c("a b c d e", "F G h i j k", "xxx", "yyy", "k l m n o") chr.8 <- c("f g h i j k", "hello", "goodbye", "yo", "k l m n o") expect_equal_to_reference(as.character(diffChr(chr.7, chr.8)), rdsf(1100)) expect_equal_to_reference( as.character(diffChr(chr.7, chr.8, align=4/6)), rdsf(1100) # same as above ) # No longer aligns expect_equal_to_reference( as.character(diffChr(chr.7, chr.8, align=4.01/6)), rdsf(1200) ) expect_equal_to_reference( as.character(diffChr(chr.7, chr.8, align=AlignThreshold(min.chars=4))), rdsf(1100) # same as earlier ) expect_equal_to_reference( as.character(diffChr(chr.7, chr.8, align=AlignThreshold(min.chars=5))), rdsf(1200) # same as above ) ## Normally this would not align, but we allow symbols to count towards ## alignment chr.7a <- c("a b c e", "d [ f g") chr.7b <- "D [ f g" a1 <- AlignThreshold(threshold=0, min.chars=2, count.alnum.only=FALSE) expect_equal( as.character(diffChr(chr.7a, chr.7b, align=a1, format='raw')), structure( c("< chr.7a > chr.7b ", "@@ 1,2 @@ @@ 1 @@ ", "< a b c e ~ ", "< d [ f g > D [ f g "), len = 4L) ) # corner case where alignment alog exits early because it runs out of B values # to match A values to. b <- c('a b c e', 'x w z f', 'e f g h') a <- c('z o o o', 'p o o o', 'A b c e') al <- AlignThreshold(threshold=0, min.chars=0) expect_known_output(show(diffChr(b, a, align=al, format='raw')), txtf(500)) }) test_that("NAs", { expect_equal_to_reference( as.character( diffChr(c(NA, letters[1:3]), c(letters[1:3], LETTERS[1:2], NA)) ), rdsf(1300) ) expect_equal_to_reference( as.character( diffChr(c(letters[1:3]), c(letters[1:3], LETTERS[1:2], NA)) ), rdsf(1400) ) expect_equal_to_reference( as.character( diffChr(c(NA, letters[1:3]), c(letters[1:3], LETTERS[1:2])) ), rdsf(1500) ) }) test_that("Nested dots issue 134, h/t Noam Ross", { fn <- function(target, current, ...) { diffChr(target, current, ...) } expect_equal( as.character(fn("a", "b", format = "raw")), structure( c( "< target > current ", "@@ 1 @@ @@ 1 @@ ", "< a > b "), len = 3L ) ) }) test_that("Newlines in input, issue 135, h/t Flying Sheep", { a <- 'A Time Series:\n[1] 1 2 3 4' b <- 'A Time Series:\n[1] 9 4 1 4' expect_equal( c(as.character(diffobj::diffChr(a, b, format = 'raw'))), c("< a > b ", "@@ 1,2 @@ @@ 1,2 @@ ", " A Time Series: A Time Series:", "< [1] 1 2 3 4 > [1] 9 4 1 4 ") ) }) diffobj/tests/testthat/testthat.warnings.R0000755000176200001440000000204713327367310020504 0ustar liggesusers# tests designed to produce warnings context("warnings") test_that("Extra args for `str`", { a <- "hello" b <- "goodbye" expect_warning(diffStr(a, b, extra=list(comp.str="^")), "Specifying") expect_warning(diffStr(a, b, extra=list(comp="^")), "Specifying") expect_warning(diffStr(a, b, extra=list(indent.str="...")), "Specifying") expect_warning(diffStr(a, b, extra=list(indent="...")), "Specifying") }) test_that("Max diffs", { # Max limit warnings work properly; these are not fully fleshed out A3 <- c("a b c", "d e f A B C D", "g h i", "f") B3 <- c("a b c", "xd e f E Q L S", "g h i", "q") expect_warning(diffChr(A3, B3, max.diffs=2), "Exceeded diff") }) test_that("Overriden formals", { expect_warning( diffChr(letters, LETTERS, style=StyleRaw(), format="ansi8"), "Provided `style` argument will override the provided `format` argument" ) expect_warning( diffChr( letters, LETTERS, style=StyleRaw(), format="ansi8", color.mode="rgb" ), "Provided `style` .* `format` and `color.mode` arguments" ) }) diffobj/tests/testthat/testthat.ses.R0000755000176200001440000000760113466141506017450 0ustar liggesuserscontext("ses") # Any tests added here should also be added to the valgrind test file test_that("basic", { expect_equal(ses(letters[1:10], letters[1:10]), character()) expect_equal(ses(letters[1:10], LETTERS[1:10]), "1,10c1,10") expect_equal(ses(letters[1:5], LETTERS[1:10]), "1,5c1,10") expect_equal(ses(letters[1:10], LETTERS[1:5]), "1,10c1,5") expect_equal(ses(letters[2:10], letters[1:7]), c("0a1", "7,9d7")) expect_equal( ses(letters[c(1:5, 1:5, 1:5)], c("e", "d", "a", "b", "c")), c("1,4d0", "6,8d1", "10d2", "14,15d5") ) expect_equal( ses(c("e", "d", "a", "b", "c"), letters[c(1:5, 1:5, 1:5)]), c("0a1,4", "1a6,8", "2a10", "5a14,15") ) # edit distance = 1 }) test_that("trigger edit distance 1 branches", { expect_equal(ses("a", c("a", "b")), "1a2") expect_equal(ses(c("a", "b"), "a"), "2d1") expect_equal(ses("c", c("b", "c")), "0a1") expect_equal(ses(c("b", "c"), "c"), "1d0") expect_equal(ses("a", character()), "1d0") expect_equal(ses(character(), "a"), "0a1") expect_equal(ses(character(), character()), character()) ## this is from the atomic tests, haven't dug into why they actually trigger ## the desired branches, but it is fairly complex set.seed(2) w1 <- sample( c( "carrot", "cat", "cake", "eat", "rabbit", "holes", "the", "a", "pasta", "boom", "noon", "sky", "hat", "blah", "paris", "dog", "snake" ), 25, replace=TRUE ) w4 <- w3 <- w2 <- w1 w2[sample(seq_along(w1), 5)] <- LETTERS[1:5] w3 <- w1[8:15] w4 <- c(w1[1:5], toupper(w1[1:5]), w1[6:15], toupper(w1[1:5])) expect_equal(ses(w1, w4), c("5a6,10", "15,21d19", "23,25c21,25")) }) test_that("longer strings", { # A bigger string string <- do.call(paste0, expand.grid(LETTERS, LETTERS, LETTERS)) expect_equal( ses(string, c("hello", string[-c(5, 500, 1000)], "goodbye")), c("0a1", "5d5", "500d499", "1000d998", "17576a17575") ) expect_equal( ses(c(string[200:500], "hello", string[-(1:400)][-c(5, 500, 1000)]), string), c("0a1,199", "207,306d405", "800a900", "1299a1400") ) }) test_that("max diffs", { expect_warning( ses(letters[1:10], LETTERS[1:10], max.diffs=5), "Exceeded `max.diffs`" ) expect_equal( ses(letters[1:10], LETTERS[1:10], max.diffs=5, warn=FALSE), "1,10c1,10" ) expect_equal( ses( letters[1:10], c(letters[1], LETTERS[2:5], letters[6:10]), max.diffs=5, warn=FALSE ), "2,5c2,5" ) expect_equal( ses( letters[1:10], c(letters[1], LETTERS[2:5], letters[6:8], LETTERS[9], letters[10]), max.diffs=5, warn=FALSE ), c("2,5c2,5", "9c9") ) }) test_that("corner cases?", { expect_equal(ses(letters[1:4], letters[1:3]), "4d3") expect_equal(ses(letters[1:3], letters[1:4]), "3a4") }) test_that("errors", { expect_error(ses('a', 'b', max.diffs='hello'), "must be scalar integer") expect_error(ses('a', 'b', warn='hello'), "must be TRUE or FALSE") a <- structure(1, class='diffobj_ogewlhgiadfl2') expect_error(ses(a, 1), "could not be coerced") expect_error(ses(1, a), "could not be coerced") }) # We want to have a test file that fully covers the C code in order to run # valgrind with just that one. We were unable to isolate simple diffs that # triggered all the code, but we were able to do it with the below in addition # to the above. test_that("Repeat tests for full coverage in SES file", { # From test.diffStr.R # formula display changed if(R.Version()$major >= 3 && R.Version()$minor >= "3.1") { rdsf1 <- function(x) file.path(getwd(), "helper", "diffStr", sprintf("%s.rds", x)) expect_equal_to_reference( as.character( diffStr(mdl1, mdl2, extra=list(strict.width="wrap"), line.limit=30) ), rdsf1(500) ) } # from testthat.warnings.R A3 <- c("a b c", "d e f A B C D", "g h i", "f") B3 <- c("a b c", "xd e f E Q L S", "g h i", "q") expect_warning(diffChr(A3, B3, max.diffs=2), "Exceeded diff") }) diffobj/tests/testthat/testthat.subset.R0000755000176200001440000000172513327367310020163 0ustar liggesusers context("subset") A <- B <- letters[1:5] B[2] <- "B" B[6] <- "F" test_that("subset", { old.opt <- options(diffobj.style=StyleRaw()) on.exit(options(old.opt)) expect_equal( c(as.character(diffChr(A, B)[1:3])), c("< A > B ", "@@ 1,5 @@ @@ 1,6 @@ ", " a a ") ) expect_equal( c(as.character(diffChr(A, B)[1])), c(as.character(head(diffChr(A, B), 1))) ) expect_equal( c(as.character(diffChr(A, B)[7:8])), c(as.character(tail(diffChr(A, B), 2))) ) }) test_that("subset errors", { diff <- diffChr(A, B) expect_error(diff[NA_real_], "contain NAs or both positive") expect_error(diff[c(-1, 1)], "contain NAs or both positive") expect_error(head(diff, 1, 2), "does not support arguments") expect_error(head(diff, NA), "must be integer") expect_error(head(diff, 1:3), "must be integer") expect_error(tail(diff, 1:3), "must be integer") expect_error(tail(diff, 1, 2), "does not support arguments") }) diffobj/tests/testthat/testthat.diffPrint.R0000755000176200001440000002477313466064400020611 0ustar liggesuserscontext("diffPrint") if(!identical(basename(getwd()), "testthat")) stop("Working dir does not appear to be /testthat, is ", getwd()) rdsf <- function(x) file.path(getwd(), "helper", "diffPrint", sprintf("%s.rds", x)) txtf <- function(x) file.path(getwd(), "helper", "diffPrint", sprintf("%s.txt", x)) # Note, atomic prints happen in different test file test_that("Matrices", { mx.2 <- matrix(1:100, ncol=2) mx.4 <- mx.3 <- mx.2 mx.3[31, 2] <- 111L mx.3a <- mx.3[-31, ] set.seed(2) mx.4[cbind(sample(1:50, 6), sample(1:2, 6, replace=TRUE))] <- sample(-(1:50), 6) mx.5 <- matrix(1:9, 3) mx.6 <- matrix(12:1, 4) mx.6[4,] <- c(3L, 6L, 9L) # single value difference expect_equal_to_reference(as.character(diffPrint(mx.2, mx.3)), rdsf(100)) # single value unified expect_equal_to_reference( as.character(diffPrint(mx.2, mx.3, mode="unified")), rdsf(150) ) # single value context expect_equal_to_reference( as.character(diffPrint(mx.2, mx.3, mode="context")), rdsf(175) ) # missing row expect_equal_to_reference(as.character(diffPrint(mx.2, mx.3a)), rdsf(200)) expect_equal_to_reference( as.character(diffPrint(mx.2, mx.3a, mode="unified")), rdsf(300) ) # More differences expect_equal_to_reference(as.character(diffPrint(mx.2, mx.4)), rdsf(400)) expect_equal_to_reference( as.character(diffPrint(mx.2, mx.4, mode="unified")), rdsf(500) ) # Testing alignments expect_equal_to_reference(as.character(diffPrint(mx.5, mx.6)), rdsf(600)) expect_equal_to_reference( as.character(diffPrint(mx.5, mx.6, mode="unified")), rdsf(700) ) expect_equal_to_reference( as.character(diffPrint(mx.5, mx.6, mode="context")), rdsf(800) ) # More complex matrix set.seed(2) A <- B <- matrix(sample(1:80), nrow=16) B[cbind(sample(5:16, 4), sample(1:5, 4))] <- sample(30:80, 4) expect_equal_to_reference(as.character(diffPrint(A, B)), rdsf(900)) expect_equal_to_reference( as.character(diffPrint(A, B, mode="unified")), rdsf(1000) ) expect_equal_to_reference( as.character(diffPrint(A, B, mode="context")), rdsf(1100) ) # Style matrices expect_equal_to_reference( as.character(diffPrint(diffobj:::.mx1, diffobj:::.mx2)), rdsf(1200) ) }) test_that("Lists", { expect_equal_to_reference( as.character(diffPrint(lst.1, lst.3)), rdsf(1300) ) expect_equal_to_reference( as.character(diffPrint(lst.1, lst.3, mode="unified")), rdsf(1400) ) expect_equal_to_reference( as.character(diffPrint(lst.4, lst.5)), rdsf(1500) ) expect_equal_to_reference( as.character(diffPrint(lst.4, lst.5, mode="context")), rdsf(1600) ) # Nested first element (https://github.com/brodieG/diffobj/issues/46) expect_equal_to_reference( as.character(diffPrint(list(1, list(2, list(1:3))), list(list(list(1:3))))), rdsf(1650) ) # Interesting but relatively slow example so we don't actually run it in # tests # diffPrint(unclass(mdl1), unclass(mdl2)) # diffPrint(unclass(mdl1), unclass(mdl2), mode="unified") }) test_that("Data Frames", { expect_equal_to_reference(as.character(diffPrint(iris.s, iris.2)), rdsf(1700)) expect_equal_to_reference( as.character(diffPrint(iris.s, iris.2, mode="sidebyside")), rdsf(1800) ) expect_equal_to_reference(as.character(diffPrint(iris.s, iris.c)), rdsf(1900)) expect_equal_to_reference(as.character(diffPrint(iris.s, iris.3)), rdsf(2000)) expect_equal_to_reference( as.character(diffPrint(iris.s, iris.3, mode="sidebyside")), rdsf(2100) ) expect_equal_to_reference( as.character(diffPrint(iris.s, iris.4, mode="unified")), rdsf(2150) ) expect_equal_to_reference( as.character(diffPrint(iris.s, iris.4, mode="sidebyside")), rdsf(2200) ) expect_equal_to_reference( as.character(diffPrint(iris.5, iris.4, mode="sidebyside")), rdsf(2250) ) expect_equal_to_reference( as.character(diffPrint(iris.3a, iris.4a)), rdsf(2300) ) expect_equal_to_reference( as.character(diffPrint(iris.s, iris.3, mode="sidebyside")), rdsf(2350) ) expect_equal_to_reference( as.character(diffPrint(iris.s, iris.s[-2])), rdsf(2370) ) # This one is interesting because the input is pathological because there # is one line that matches exactly between the two and as such creates a # matching hunk, but it really is matching by coincidence. expect_equal_to_reference( as.character(diffPrint(iris.s, iris.s[-2], mode="sidebyside")), rdsf(2383) ) # Possible example where we may not want to trim the row headers (Issue #39) expect_equal_to_reference( as.character(diffPrint(cars[1:5,], mtcars[1:5,], mode="sidebyside")), rdsf(2380) ) }) test_that("Guides", { # Most guides tests are in the guides file, but this confirms interface works # when starting at `diffPrint` instead of internally expect_equal_to_reference( as.character( diffPrint( iris.s, iris.4, mode="sidebyside", guides=function(x, y) integer() ) ), rdsf(2400) ) expect_equal_to_reference( as.character(diffPrint(iris.s, iris.4, mode="sidebyside", guides=FALSE)), rdsf(2500) ) }) test_that("Arrays", { arr.1 <- arr.2 <- array(1:24, c(4, 2, 3)) arr.2[c(3, 20)] <- 99L expect_equal_to_reference(as.character(diffPrint(arr.1, arr.2)), rdsf(2600)) }) test_that("Mixed", { expect_equal_to_reference( as.character(diffPrint(list(1, 2, 3), matrix(1:9, 3))), rdsf(2700) ) expect_equal_to_reference( as.character(diffPrint(list(25, 2, 3), matrix(1:9, 3))), rdsf(2800) ) expect_equal_to_reference( as.character( diffPrint(list(c(1, 4, 7), c(2, 5, 8), c(3, 6, 9)), matrix(1:9, 3)) ), rdsf(2900) ) }) test_that("`unitizer` corner case", { res1 <- structure( c(-1717, 101, 0.938678984853783), .Names = c("intercept", "slope", "rsq"), class = "fastlm" ) res2 <- structure( c(-3.541306e+13, 701248600000, 0.938679), .Names = c("intercept", "slope", "rsq"), class = "fastlm" ) expect_equal_to_reference(as.character(diffPrint(res1, res2)), rdsf(3000)) expect_equal_to_reference( as.character(diffPrint(unname(res1), unname(res2))), rdsf(3100) ) }) test_that("factors and other meta", { # Thanks Frank expect_equal_to_reference( as.character(diffPrint(factor(1:100), factor(c(1:99, 101)))), rdsf(3200) ) f1 <- factor(1:100) f2 <- factor(c(1:20, 22:99, 101)) expect_known_output(diffPrint(f1, f2), txtf(100), print=TRUE) f3 <- factor(letters[1:10]) f4 <- factor(letters[1:10], levels=letters[1:11]) expect_known_output(diffPrint(f3, f4), txtf(150), print=TRUE) # time series nhtemp2 <- nhtemp nhtemp2[c(5, 30)] <- -999 expect_known_output(diffPrint(nhtemp, nhtemp2), txtf(175), print=TRUE) # Meta on both sides print.diffobj_test_c1 <- function(x, ...) { writeLines(c("Header row 1", "header row 2")) print(c(x)) writeLines(c("", "Footer row 1", "", "footer row2")) } m1 <- structure(1:30, class='diffobj_test_c1') m2 <- structure(2:51, class='diffobj_test_c1') expect_known_output(diffPrint(m1, m2), txtf(200), print=TRUE) }) test_that("Raw output", { expect_equal_to_reference( as.character(diffPrint(letters, LETTERS, format="raw", pager="off")), rdsf(3300) ) }) test_that("Varying Widths", { expect_equal_to_reference( as.character(diffPrint(letters, LETTERS, format="raw", disp.width=40)), rdsf(3400) ) expect_error(diffPrint(letters, LETTERS, disp.width=5)) }) test_that("covr workaround", { # Needed so that the function definition stuff is marked as covered; really # it shouldn't even be eligible for coverage, need to discuss further with # jhester diffobj:::make_diff_fun() }) test_that("Encoding Issues", { # issue81, mixed UTF-8 ASCII, encoding a-acute in hex to avoid cross platform # issues a <- "G\xc3\xa1bor Cs\xc3\xa1rdi" b <- sprintf("%s wow", a) Encoding(a) <- 'UTF-8' Encoding(b) <- 'UTF-8' # No error expect_error( new <-as.character(diffPrint(list(hell=a, b=NULL), list(hell=b, b=list()))), NA ) # can't store this in RDS b/c otherwise won't run properly on oses with # different encoding (e.g. windows) ref <- structure( c("\033[33m<\033[39m \033[33mlist(hell = a, b = N..\033[39m \033[34m>\033[39m \033[34mlist(hell = b, b = l..\033[39m", "\033[36m@@ 1,6 @@ \033[39m \033[36m@@ 1,6 @@ \033[39m", " \033[90m\033[39m$hell\033[90m\033[39m \033[90m\033[39m$hell\033[90m\033[39m ", "\033[33m<\033[39m \033[90m[1] \033[39m\033[33m\"G\xc3\xa1bor Cs\xc3\xa1rdi\"\033[39m\033[90m\033[39m \033[34m>\033[39m \033[90m[1] \033[39m\033[34m\"G\xc3\xa1bor Cs\xc3\xa1rdi wow\"\033[39m\033[90m\033[39m", " ", " \033[90m\033[39m$b\033[90m\033[39m \033[90m\033[39m$b\033[90m\033[39m ", "\033[33m<\033[39m \033[90m\033[39m\033[33mNULL\033[39m\033[90m\033[39m \033[34m>\033[39m \033[90m\033[39m\033[34mlist\033[39m\033[34m()\033[39m\033[90m\033[39m ", " " ), len = 8L ) Encoding(ref) <- 'UTF-8' expect_equal(new, ref) # issue 106, this used to fail when trying to check for an RDS with a bytes # encoded file name bytes <- "\x81" Encoding(bytes) <- "bytes" expect_true(!any(diffPrint(bytes, bytes))) }) test_that("Quoted Objects", { expect_equal( as.character(diffPrint(quote(zz + 1), quote(zz + 3))), structure( c("\033[33m<\033[39m \033[33mquote(..\033[39m \033[34m>\033[39m \033[34mquote(..\033[39m", "\033[36m@@ 1 @@ \033[39m \033[36m@@ 1 @@ \033[39m", "\033[33m<\033[39m \033[90m\033[39mzz + \033[33m1\033[39m\033[90m\033[39m \033[34m>\033[39m \033[90m\033[39mzz + \033[34m3\033[39m\033[90m\033[39m " ), len = 3L ) ) expect_equal( as.character(diffPrint(quote(x), quote(y))), structure( c("\033[33m<\033[39m \033[33mquote(x)\033[39m \033[34m>\033[39m \033[34mquote(y)\033[39m", "\033[36m@@ 1 @@ \033[39m \033[36m@@ 1 @@ \033[39m", "\033[33m<\033[39m \033[90m\033[39m\033[33mx\033[39m\033[90m\033[39m \033[34m>\033[39m \033[90m\033[39m\033[34my\033[39m\033[90m\033[39m "), len = 3L ) ) }) test_that("par_frame", { # check that par_frame is retrieved correctly env <- new.env() env$print <- function(x, ...) stop('boom') expect_error( evalq(diffPrint(1:3, 1:4), env), "Failed attempting .*: boom" ) f <- function(a, b, ...) { print <- function(x, ...) stop('boom2') diffPrint(a, b, ...) } expect_error(f(1:3, 1:4, format='raw'), "Failed attempting .*: boom2") }) diffobj/tests/testthat/testthat.misc.R0000755000176200001440000001117713466063400017610 0ustar liggesuserslibrary(diffobj) context("misc") test_that("trim_str", { a <- structure("hello", class="A", xx="B") b <- structure(1:10, yy=a) long.string <- "I'm a string long enough to force wrapping under most cases so that I may be useful for tests andiamareallylongwordtoseehowwrappingbreakslongwordsthatexceed" obj <- list( a=a, b=b, c=1:50, d=long.string, e=list(1, structure(2, zz=list(a=1, b=list("a", ls=long.string))), e=letters) ) # conditional because of issue113 str.txt <- capture.output(str(obj)) str.txt.w <- capture.output(str(obj, width=30L, strict.width="wrap")) if( getRversion() >= '3.5.0' && as.numeric(R.Version()[['svn rev']]) >= 73780 ) { expect_equal( diffobj:::str_levels(str.txt, wrap=FALSE), c(0L, 1L, 2L, 1L, 2L, 3L, 1L, 1L, 1L, 2L, 2L, 3L, 4L, 4L, 5L, 5L, 2L) ) expect_equal( diffobj:::str_levels(str.txt.w, wrap=TRUE), c(0L, 1L, 2L, 1L, 1L, 2L, 2L, 3L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 3L, 3L, 4L, 4L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 2L, 2L ) ) } else { expect_equal( diffobj:::str_levels(str.txt, wrap=FALSE), c(0L, 1L, 3L, 1L, 2L, 4L, 1L, 1L, 1L, 2L, 2L, 3L, 4L, 4L, 5L, 5L, 2L) ) expect_equal( diffobj:::str_levels(str.txt.w, wrap=TRUE), c(0L, 1L, 1L, 3L, 1L, 1L, 2L, 2L, 4L, 4L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 3L, 3L, 4L, 4L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 5L, 2L, 2L) ) } # cat( # paste( # format(substr(str.txt.w, 1, 20)), diffobj:::str_levels(str.txt.w, TRUE), # sep=": " # ), # sep="\n" # ) }) test_that("rle_sub", { x <- c(1, 1, 1, 2, 2, 1, 1, 3, 3, 4, 4, 4, 5, 2, 2) r <- rle(x) expect_equal(diffobj:::rle_sub(r, r$values == 1L), list(1:3, 6:7)) expect_equal(diffobj:::rle_sub(r, r$values == 2L), list(4:5, 14:15)) expect_true(all(x[unlist(diffobj:::rle_sub(r, r$values == 1L))] == 1)) expect_true(all(x[unlist(diffobj:::rle_sub(r, r$values == 2L))] == 2)) expect_true(all(x[unlist(diffobj:::rle_sub(r, r$values == 3L))] == 3)) }) test_that("myers_simple", { }) test_that("call funs", { # Failure case; assumes no S4 dispatch in testthat calls <- list(quote(a()), quote(b()), quote(notafunctionblah())) expect_equal(diffobj:::which_top(calls), length(calls)) expect_warning(diffobj:::extract_call(calls, new.env()), "Unable to find") # missing param works calls2 <- pairlist( quote(diffChr("a")), quote(diffChr("a")), quote(.local(target, current, ...)) ) expect_equal( diffobj:::extract_call(calls2, new.env()), list(call = quote(diffChr(target = "a", NULL)), tar = "a", cur = NULL) ) # fallback parent frame; can't think of a good way to actually cause this to # happen # expect_equal(diffobj:::par_frame(), .GlobalEnv) }) test_that("lines", { old.val <- Sys.getenv("LINES", unset=NA) on.exit( if(is.na(old.val)) Sys.unsetenv("LINES") else Sys.setenv(LINES=old.val) ) Sys.setenv(LINES="25") expect_equal(console_lines(), 25L) Sys.setenv(LINES="-25") expect_equal(console_lines(), 48L) Sys.unsetenv("LINES") expect_equal(console_lines(), 48L) }) test_that("get_funs", { expect_identical( diffobj:::get_fun(quote(diffobj::diffPrint), .BaseNamespaceEnv), diffobj::diffPrint ) expect_identical( diffobj:::get_fun(quote(diffobj:::diffPrint), .BaseNamespaceEnv), diffobj::diffPrint ) expect_identical( diffobj:::get_fun(quote(diffPrint), getNamespace("diffobj")), diffobj::diffPrint ) expect_warning( gf <- diffobj:::get_fun(quote(notAFunction), getNamespace("diffobj")), "Unable to find function" ) expect_identical(gf, NULL) }) test_that("trimws2", { expect_equal(diffobj:::trimws2("hello world"), "hello world") expect_equal(diffobj:::trimws2(" hello world"), "hello world") expect_equal(diffobj:::trimws2(" hello world "), "hello world") expect_equal(diffobj:::trimws2(" hello world ", 'left'), "hello world ") expect_equal(diffobj:::trimws2(" hello world ", 'right'), " hello world") expect_error(diffobj:::trimws2(" hello world ", 'banana'), "is wrong") }) test_that("string", { expect_error(diffobj:::substr2("hello world", 1, 1:2), "same length") }) test_that("Gutters", { etc <- new("Settings") etc@style <- StyleRaw() etc@style@funs@gutter <- function(x) stop("bad gutters") expect_error(diffobj:::gutter_dat(etc), "Failed attempting to apply gutter.") }) test_that("Finalizer error handling", { expect_error(finalizeHtml(letters, NULL), "must be character") expect_error( finalizeHtml(letters, letters, letters), "must be character\\(1L" ) }) test_that("c.factor", { expect_equal(diffobj:::c.factor(), factor(character())) }) diffobj/tests/testthat/testthat.methods.R0000755000176200001440000000327313327367306020326 0ustar liggesuserscontext("methods") library(testthat) library(diffobj) if(!identical(basename(getwd()), "testthat")) stop("Working dir does not appear to be /testthat, is ", getwd()) rdsf <- function(x) file.path(getwd(), "helper", "methods", sprintf("%s.rds", x)) # try implementing methods that change default behavior outside of package test_that("Force unified", { par.env <- new.env() local( envir=par.env, { suppressWarnings( setClass( "testdiffobj", slots=c(a="integer"), where=par.env ) ) # First check that we do actually output in side by side mode expect_equal_to_reference( as.character(diffObj(new("testdiffobj", a=1L), new("testdiffobj", a=2L))), rdsf(100) ) # Now verify that with our new method, we get unified setMethod("diffObj", c("testdiffobj", "testdiffobj"), function(target, current, ...) { dots <- match.call(expand.dots=FALSE)[["..."]] if("mode" %in% names(dots)) callNextMethod() else callNextMethod(target=target, current=current, ..., mode="unified") }, where=par.env ) on.exit( removeMethod("diffObj", c("testdiffobj", "testdiffobj"), where=par.env) ) expect_equal_to_reference( as.character(diffObj(new("testdiffobj", a=1L), new("testdiffobj", a=2L))), rdsf(200) ) # Make sure we can still get side by side? expect_equal_to_reference( as.character( diffObj( new("testdiffobj", a=1L), new("testdiffobj", a=2L), mode="sidebyside" ) ), rdsf(100) ) expect_error( diffObj(new("testdiffobj", a=1L), new("testdiffobj", a=2L), mode="hello"), "Argument `mode` must be" ) }) }) diffobj/tests/testthat/testthat.rrdiff.R0000755000176200001440000000361613442554134020133 0ustar liggesusers library(diffobj) context("Rdiff") test_that("diff util detection", { expect_false(has_Rdiff(function(...) warning("test warning"))) expect_true(has_Rdiff(function(...) NULL)) }) test_that("errors", { expect_error(Rdiff_chr(stop('hello'), 'goodbye'), "Unable to coerce") expect_error(Rdiff_chr('hello', stop('goodbye')), "Unable to coerce") expect_error(Rdiff_obj(stop('hello'), 'goodbye'), "Unable to store") }) # Only run tests on machines that are likely to have diff utility if(identical(.Platform$OS.type, "unix") && has_Rdiff()) { context("w/ diff") A2 <- c("A", "B", "C") B2 <- c("X", "A", "Y", "C") A3 <- 1:3 B3 <- c(100L, 1L, 200L, 3L) test_that("Rdiff_chr", { ref.res <- c("0a1", "2c3") ref.res.1 <- c("0a1", "> X", "2c3", "< B", "---", "> Y") expect_identical(Rdiff_chr(A2, B2, silent=TRUE, minimal=TRUE), ref.res) capt <- capture.output(res <- Rdiff_chr(A2, B2, silent=FALSE, minimal=TRUE)) expect_identical(res, ref.res) expect_identical(capt, res) capt.1 <- capture.output( res.1 <- Rdiff_chr(A2, B2, silent=FALSE, minimal=FALSE) ) expect_identical(capt.1, ref.res.1) expect_identical(res.1, ref.res.1) # test coersion expect_identical(Rdiff_chr(A3, B3, minimal=TRUE, silent=TRUE), ref.res) }) test_that("Rdiff_obj", { ref.res2 <- c("1c1", "< [1] \"A\" \"B\" \"C\"", "---", "> [1] \"X\" \"A\" \"Y\" \"C\"" ) ref.res3 <- c("1c1") expect_identical(Rdiff_obj(A2, B2, silent=TRUE), ref.res2) expect_identical(Rdiff_obj(A2, B2, minimal=TRUE, silent=TRUE), ref.res3) # with rds f1 <- tempfile() f2 <- tempfile() saveRDS(A2, f1) saveRDS(B2, f2) on.exit(unlink(c(f1, f2))) expect_identical(Rdiff_obj(f1, B2, silent=TRUE), ref.res2) expect_identical(Rdiff_obj(A2, f2, silent=TRUE), ref.res2) expect_identical(Rdiff_obj(f1, f2, silent=TRUE), ref.res2) }) } else { context("w/o diff") } diffobj/tests/testthat/testthat.banner.R0000755000176200001440000000075513014331136020113 0ustar liggesuserslibrary(testthat) context("banner") test_that("Banner Capture", { ref <- as.character( diffPrint(1 + 2, letters, tar.banner="1 + 2", cur.banner="letters") ) expect_identical(as.character(diffPrint(1 + 2, letters)), ref) setMethod( "diffPrint", c("numeric", "character"), function(target, current, ...) callNextMethod() ) expect_identical(as.character(diffPrint(1 + 2, letters)), ref) expect_true( !identical(as.character(diffPrint(1 + 2, LETTERS)), ref) ) } ) diffobj/tests/testthat/testthat.trim.R0000755000176200001440000002362213442554134017631 0ustar liggesuserscontext("trim") if(!identical(basename(getwd()), "testthat")) stop("Working dir does not appear to be /testthat, is ", getwd()) rdsf <- function(x) file.path(getwd(), "helper", "trim", sprintf("%s.rds", x)) .mx.base <- matrix( c( "averylongwordthatcanlahblah", "causeasinglewidecolumnblah", "matrixtowrapseveraltimes", "inarrowscreen", "onceuponatime", "agreenduckflew", "overthemountains", "inalongofantelopes", "ineedthreemore", "entriesactually", "nowonlytwomore", "iwaswrongearlier" ), nrow=3, ncol=4 ) test_that("Atomic", { old.opt <- options(width=80L) on.exit(options(old.opt)) set.seed(1) x <- capture.output(1:50) y <- capture.output(factor(sample(letters, 50, replace=TRUE))) expect_equal( diffobj:::strip_atomic_rh(x), c(" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25", "26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50") ) expect_equal( diffobj:::strip_atomic_rh(y), c("g j o x f x y r q b f e r j u m s z j u y f q d g k a j w i m p m e v r u c", "s k v q u o n u a m t s", "Levels: a b c d e f g i j k m n o p q r s t u v w x y z") ) expect_equal(diffobj:::which_atomic_rh(capture.output(1:5)), 1) expect_equal_to_reference( as.character(diffPrint(1:3, 2:6, trim=FALSE)), rdsf(50) ) # bad headers bh <- c("[1] a b c", "[4] d e f", "[5] h") expect_equal(diffobj:::which_atomic_rh(bh), integer()) }) test_that("Matrix", { mx1 <- mx2 <- matrix(1:3, 3) expect_equal( diffobj:::strip_matrix_rh(capture.output(mx1), dimnames(mx1)), c(" [,1]", " 1", " 2", " 3") ) # shouldn't strip headers from attributes attr(mx2, "blah") <- matrix(1:2, 2) expect_equal( diffobj:::strip_matrix_rh(capture.output(mx2), dimnames(mx2)), c(" [,1]", " 1", " 2", " 3", "attr(,\"blah\")", " [,1]", "[1,] 1", "[2,] 2") ) # Matrices that wrap mx3 <- mx4 <- mx5 <- mx6 <- .mx.base old.opt <- options(width=30) on.exit(options(old.opt)) expect_equal( diffobj:::strip_matrix_rh(capture.output(mx3), dimnames(mx3)), c(" [,1] ", "\"averylongwordthatcanlahblah\"", "\"causeasinglewidecolumnblah\" ", "\"matrixtowrapseveraltimes\" ", " [,2] ", "\"inarrowscreen\" ", "\"onceuponatime\" ", "\"agreenduckflew\"", " [,3] ", "\"overthemountains\" ", "\"inalongofantelopes\"", "\"ineedthreemore\" ", " [,4] ", "\"entriesactually\" ", "\"nowonlytwomore\" ", "\"iwaswrongearlier\"") ) # Add rownames; should no longer strip rownames(mx4) <- 2:4 expect_equal( diffobj:::strip_matrix_rh(capture.output(mx4), dimnames(mx4)), capture.output(mx4) ) # Attributes don't have stuff stripped attr(mx6, "blah") <- letters[1:15] expect_equal( diffobj:::strip_matrix_rh(capture.output(mx6), dimnames(mx6)), c(" [,1] ", "\"averylongwordthatcanlahblah\"", "\"causeasinglewidecolumnblah\" ", "\"matrixtowrapseveraltimes\" ", " [,2] ", "\"inarrowscreen\" ", "\"onceuponatime\" ", "\"agreenduckflew\"", " [,3] ", "\"overthemountains\" ", "\"inalongofantelopes\"", "\"ineedthreemore\" ", " [,4] ", "\"entriesactually\" ", "\"nowonlytwomore\" ", "\"iwaswrongearlier\"", "attr(,\"blah\")", " [1] \"a\" \"b\" \"c\" \"d\" \"e\" \"f\"", " [7] \"g\" \"h\" \"i\" \"j\" \"k\" \"l\"", "[13] \"m\" \"n\" \"o\"") ) # Single row matrix expect_equal( diffobj:::which_matrix_rh(capture.output(matrix(1:2, nrow=1)), NULL), 2 ) }) test_that("Table", { old.opt <- options(width=30) on.exit(options(old.opt)) # Data frames df1 <- as.data.frame(.mx.base) expect_equal( diffobj:::strip_table_rh(capture.output(df1)), c(" V1", "averylongwordthatcanlahblah", " causeasinglewidecolumnblah", " matrixtowrapseveraltimes", " V2", " inarrowscreen", " onceuponatime", "agreenduckflew", " V3", " overthemountains", "inalongofantelopes", " ineedthreemore", " V4", " entriesactually", " nowonlytwomore", "iwaswrongearlier") ) df2 <- df1[c(2, 1, 3), ] expect_equal( diffobj:::strip_table_rh(capture.output(df2)), capture.output(df2) ) # Rownames that start from one and sequential, should get stripped; also, # colon allowed df3 <- df1 rownames(df3) <- paste0(1:3, ":") expect_equal( diffobj:::strip_table_rh(capture.output(df3)), c(" V1", "averylongwordthatcanlahblah", " causeasinglewidecolumnblah", " matrixtowrapseveraltimes", " V2", " inarrowscreen", " onceuponatime", "agreenduckflew", " V3", " overthemountains", "inalongofantelopes", " ineedthreemore", " V4", " entriesactually", " nowonlytwomore", "iwaswrongearlier") ) # Try ts expect_equal( diffobj:::strip_table_rh(capture.output(USAccDeaths)), capture.output(USAccDeaths) ) # Set it so first year is 1 USAD2 <- USAccDeaths tsp(USAD2)[1:2] <- tsp(USAD2)[1:2] - 1972 expect_equal( diffobj:::strip_table_rh(capture.output(USAD2)), c(" Jan Feb Mar Apr", " 9007 8106 8928 9137", " 7750 6981 8038 8422", " 8162 7306 8124 7870", " 7717 7461 7767 7925", " 7792 6957 7726 8106", " 7836 6892 7791 8192", " May Jun Jul Aug", "10017 10826 11317 10744", " 8714 9512 10120 9823", " 9387 9556 10093 9620", " 8623 8945 10078 9179", " 8890 9299 10625 9302", " 9115 9434 10484 9827", " Sep Oct Nov Dec", " 9713 9938 9161 8927", " 8743 9129 8710 8680", " 8285 8466 8160 8034", " 8037 8488 7874 8647", " 8314 8850 8265 8796", " 9110 9070 8633 9240") ) # single row data frame expect_equal(c(diffobj:::which_table_rh(capture.output(data.frame(1, 2)))), 2) # More than 10 rows data.frame expect_equal( c(diffobj:::which_table_rh(capture.output(head(Puromycin, 10L)))), 2:11 ) # Bad wrap bw <- c( " bad", "1 123", "2 456", " dab", "1 123", "2 456", " abd", "1 123") expect_equal( diffobj:::wtr_help(bw, diffobj:::.pat.tbl), c(2L, 3L, 5L, 6L) ) }) test_that("Array", { a <- array(1:6, c(3, 1, 2)) a.c <- capture.output(a) expect_equal( diffobj:::strip_array_rh(a.c, dimnames(a)), c(", , 1", "", " [,1]", " 1", " 2", " 3", "", ", , 2", "", " [,1]", " 4", " 5", " 6", "") ) viz_sarh <- function(capt, obj) cbind( capt, as.integer( seq_along(capt) %in% diffobj:::which_array_rh(capt, dimnames(obj)) ) ) a1 <- a2 <- a3 <- a4 <- array( "averylongphrasethatwillforcemytwocolumnarraytowrapblahblah", c(2, 2, 2) ) ca1 <- capture.output(a1) viz_sarh(ca1, a1) expect_equal( diffobj:::which_array_rh(ca1, dimnames(a1)), c(4L, 5L, 7L, 8L, 13L, 14L, 16L, 17L) ) colnames(a2) <- c("ABC", "DEF") ca2 <- capture.output(a2) viz_sarh(ca2, a2) expect_equal( diffobj:::which_array_rh(ca2, dimnames(a2)), c(4L, 5L, 7L, 8L, 13L, 14L, 16L, 17L) ) rownames(a3) <- 1:2 ca3 <- capture.output(a3) viz_sarh(ca3, a3) expect_equal(diffobj:::which_array_rh(ca3, dimnames(a3)), integer(0L)) attr(a4, "blahblah") <- matrix(1:4, 2) ca4 <- capture.output(a4) viz_sarh(ca4, a4) expect_equal( diffobj:::which_array_rh(ca4, dimnames(a4)), c(4L, 5L, 7L, 8L, 13L, 14L, 16L, 17L) ) }) test_that("List", { l1 <- list( matrix(1:4, 2), b=list(abc=c(letters, LETTERS), list(matrix(4:1, 2))) ) l1.c <- capture.output(l1) expect_equal( diffobj:::strip_list_rh(l1.c, l1), c("[[1]]", " [,1] [,2]", " 1 3", " 2 4", "", "$b", "$b$abc", "\"a\" \"b\" \"c\" \"d\" \"e\" \"f\" \"g\" \"h\" \"i\" \"j\" \"k\" \"l\" \"m\" \"n\" \"o\" \"p\" \"q\" \"r\" \"s\"", "\"t\" \"u\" \"v\" \"w\" \"x\" \"y\" \"z\" \"A\" \"B\" \"C\" \"D\" \"E\" \"F\" \"G\" \"H\" \"I\" \"J\" \"K\" \"L\"", "\"M\" \"N\" \"O\" \"P\" \"Q\" \"R\" \"S\" \"T\" \"U\" \"V\" \"W\" \"X\" \"Y\" \"Z\"", "", "$b[[2]]", "$b[[2]][[1]]", " [,1] [,2]", " 4 2", " 3 1", "", "", "") ) a <- list(list()) aa <- list(list(), "a") b <- list("a", list()) c <- list(list("a"), "b") d <- list("a", "b", "c") expect_identical( diffobj:::strip_list_rh(capture.output(d), d), c("[[1]]", "\"a\"", "", "[[2]]", "\"b\"", "", "[[3]]", "\"c\"", "") ) expect_identical( diffobj:::strip_list_rh(capture.output(a), a), c("[[1]]", "list()", "") ) expect_identical( diffobj:::strip_list_rh(capture.output(aa), aa), c("[[1]]", "list()", "", "[[2]]", "\"a\"", "") ) expect_identical( diffobj:::strip_list_rh(capture.output(b), b), c("[[1]]", "\"a\"", "", "[[2]]", "list()", "") ) expect_identical( diffobj:::strip_list_rh(capture.output(c), c), c("[[1]]", "[[1]][[1]]", "\"a\"", "", "", "[[2]]", "\"b\"", "") ) }) test_that("custom trim fun", { a <- matrix(100:102) b <- matrix(101:103) fun1 <- function(x, y) cbind(rep(1L, 4), rep(5L, 4)) expect_equal_to_reference(as.character(diffPrint(a, b, trim=fun1)), rdsf(100)) if(getRversion() >= "3.2") { expect_warning( capture.output( trim.err <- as.character(diffPrint(a, b, trim=function(x, y) stop("boom"))), type="message" ), "If you did not specify a `trim`" ) expect_equal_to_reference(trim.err, rdsf(200)) } # purposefully bad trim fun expect_error( diffPrint(1:100, 2:100, trim=function(x, y) TRUE), "method return value must be a two " ) expect_error( diffobj:::apply_trim(letters, letters, function(x) TRUE), "Invalid trim function" ) expect_error( diffobj:::apply_trim( letters, letters, function(x, y) cbind(1:25, 1:25) ), "must have as many rows" ) }) test_that("s4", { setClass("DOTrimTest", slots=c(a="numeric", b="list", c="matrix")) obj <- new( "DOTrimTest", a=1:40, b=list(a=1, letters, NULL), c=matrix(1:9, 3) ) expect_equal_to_reference( diffobj:::strip_s4_rh(capture.output(obj), obj), rdsf(300) ) }) diffobj/tests/testthat/testthat.atomic.R0000755000176200001440000001376213464701604020136 0ustar liggesuserslibrary(diffobj) context("Atomic") if(!identical(basename(getwd()), "testthat")) stop("Working dir does not appear to be /testthat, is ", getwd()) rdsf <- function(x) file.path(getwd(), "helper", "atomic", sprintf("%s.rds", x)) test_that("Basic Tests", { expect_equal_to_reference(as.character(diffPrint(chr.1, chr.2)), rdsf(100)) expect_equal_to_reference( as.character(diffPrint(chr.1, chr.2, mode="unified")), rdsf(200) ) expect_equal_to_reference( as.character(diffPrint(chr.1, chr.2, mode="context")), rdsf(400) ) expect_equal_to_reference( as.character(diffPrint(chr.1[2:3], chr.2[2], mode="sidebyside")), rdsf(500) ) # Check that `extra` works expect_equal_to_reference( as.character( diffPrint(chr.1, chr.2, mode="unified", extra=list(quote=FALSE)) ), rdsf(600) ) # make sure blanks line up correctly expect_equal_to_reference( as.character(diffPrint(chr.3, chr.4)), rdsf(700) ) expect_equal_to_reference( as.character(diffPrint(chr.3, chr.4, mode="unified")), rdsf(800) ) }) test_that("Word wrap in atomic", { A <- A.1 <- B <- c(letters, LETTERS) B[15] <- "Alabama" A.1[5] <- "Ee" C <- A[-15] D <- C E <- B[-45] # Test simple changes to vectors; at 80 columns removing 1:8 corresponds to # row deletion expect_equal_to_reference(as.character(diffPrint(A[-(1:8)], A)), rdsf(900)) expect_equal_to_reference(as.character(diffPrint(A, A[-(1:8)])), rdsf(1000)) expect_equal_to_reference(as.character(diffPrint(A[-1], A[-2])), rdsf(1100)) # Replace single word expect_equal_to_reference(as.character(diffPrint(A, B)), rdsf(1200)) expect_equal_to_reference(as.character(diffPrint(B, A)), rdsf(1250)) # Make sure turning off word.diff also turns of unwrapping, but that we can # turn off unwrapping without turning off word diff expect_equal_to_reference( as.character(diffPrint(A, B, word.diff=FALSE)), rdsf(1300) ) expect_equal_to_reference( as.character(diffPrint(A, B, unwrap.atomic=FALSE)), rdsf(1400) ) # Different wrap frequency and removed words that span lines expect_equal_to_reference( as.character(diffPrint(A, A.1[-(13:18)])), rdsf(1425) ) # Removing words expect_equal_to_reference(as.character(diffPrint(C, B)), rdsf(1450)) # Two hunks expect_equal_to_reference(as.character(diffPrint(D, E)), rdsf(1500)) expect_equal_to_reference(as.character(diffPrint(E, D)), rdsf(1600)) # Vignette example state.abb.2 <- state.abb state.abb.2[38] <- "Pennsylvania" expect_equal_to_reference( as.character(diffPrint(state.abb, state.abb.2)), rdsf(1700) ) # Number corner cases expect_equal_to_reference(as.character(diffPrint(1:100, 2:101)), rdsf(1800)) expect_equal_to_reference(as.character(diffPrint(2:101, 1:100)), rdsf(1900)) expect_equal_to_reference( as.character(diffPrint(2:101, (1:100)[-9])), rdsf(2000) ) expect_equal_to_reference( as.character(diffPrint((2:101)[-98], (1:100)[-9])), rdsf(2100) ) # This is one of those that a better in-hunk align algorithm would benefit int.1 <- int.2 <- 1:100 int.2[c(8, 20, 60)] <- 99 int.2 <- c(50:1, int.2) expect_equal_to_reference(as.character(diffPrint(int.1, int.2)), rdsf(2200)) }) test_that("with names", { rand.chrs <- do.call(paste0, expand.grid(LETTERS, LETTERS)) F <- F1 <- F2 <- (2:105)[-98] G <- G2 <- G3 <- G4 <- G5 <- (1:100)[-9] nm.1 <- rand.chrs[seq_along(F)] nm.2 <- rand.chrs[seq_along(G)] names(F1) <- names(F2) <- nm.1 names(G3) <- names(G2) <- names(G3) <- names(G4) <- names(G5) <- nm.2 names(G3)[c(5, 25, 60)] <- c("XXXXX", rand.chrs[c(300, 350)]) names(G4)[c(5, 25, 60)] <- names(G5)[c(5, 25, 60)] <- c("XX", rand.chrs[c(300, 350)]) attr(F2, "blah") <- 1:5 attr(G5, "blah") <- 3:8 expect_equal_to_reference(as.character(diffPrint(F, G)), rdsf(2300)) expect_equal_to_reference(as.character(diffPrint(F1, G2)), rdsf(2400)) # Challenging case b/c the names wrap with values, and you have to pick one or # the other to match when the wrap frequencies are different expect_equal_to_reference(as.character(diffPrint(F1, G3)), rdsf(2500)) expect_equal_to_reference(as.character(diffPrint(F1, G4)), rdsf(2520)) # Attributes expect_equal_to_reference(as.character(diffPrint(F2, G5)), rdsf(2530)) expect_equal_to_reference(as.character(diffPrint(F1, G5)), rdsf(2540)) }) test_that("Original tests", { set.seed(2) w1 <- sample( c( "carrot", "cat", "cake", "eat", "rabbit", "holes", "the", "a", "pasta", "boom", "noon", "sky", "hat", "blah", "paris", "dog", "snake" ), 25, replace=TRUE ) w4 <- w3 <- w2 <- w1 w2[sample(seq_along(w1), 5)] <- LETTERS[1:5] w3 <- w1[8:15] w4 <- c(w1[1:5], toupper(w1[1:5]), w1[6:15], toupper(w1[1:5])) expect_equal_to_reference(as.character(diffPrint(w1, w2)), rdsf(2600)) expect_equal_to_reference(as.character(diffPrint(w1, w3)), rdsf(2700)) expect_equal_to_reference(as.character(diffPrint(w1, w4)), rdsf(2800)) }) test_that("Simple word diffs", { a <- c("a", "b", "c", "d") b <- c("b", "c", "d", "e") expect_equal_to_reference(as.character(diffPrint(a, b)), rdsf(2900)) a <- c("x", "a", "b", "c", "d", "z") b <- c("x", "b", "c", "d", "e", "z") expect_equal_to_reference(as.character(diffPrint(a, b)), rdsf(3000)) a <- c("x", "a", "b", "c", "d", "z") b <- c("z", "b", "c", "d", "e", "x") expect_equal_to_reference(as.character(diffPrint(a, b)), rdsf(3100)) }) test_that("Alignment edge cases", { expect_equal_to_reference( as.character(diffPrint(20:50, 30:62)), rdsf(3200) ) # below is off; should be aligning matching context line, part of the problem # might be that we're doing the realignment without thinking about what the # other hunk has. # # Possible encode each line as hunk#:diff/mix/cont # expect_equal_to_reference( # as.character(diffPrint(20:50, 35:62)), rdsf(3300) # ) # another interesting example where the existing algo seems to lead to a # reasonable outcome expect_equal_to_reference( as.character(diffPrint(c(1:24,35:45), c(1:8, 17:45))), rdsf(3400) ) }) diffobj/src/0000755000176200001440000000000013466146510012452 5ustar liggesusersdiffobj/src/diff.c0000755000176200001440000005700413442554134013536 0ustar liggesusers/* * This file is part of a program that contains a heavily modified version of * Michael B. Allen implementation of the Myers diff algorithm. This * implementation is not compatible with the original one. See next * comment blocks for original copyright and license information. * * Copyright (C) 2019 Brodie Gaslam * * This file is part of "diffobj - Diffs for R Objects" * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Go to for a copy of the license. */ /* ORIGINAL COPYRIGHT NOTICE: * * diff - compute a shortest edit script (SES) given two sequences * Copyright (c) 2004 Michael B. Allen * * The MIT License * * 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. */ /* This algorithm is basically Myers' solution to SES/LCS with * the Hirschberg linear space refinement as described in the * following publication: * * E. Myers, ``An O(ND) Difference Algorithm and Its Variations,'' * Algorithmica 1, 2 (1986), 251-266. * http://www.cs.arizona.edu/people/gene/PAPERS/diff.ps * * This is the same algorithm used by GNU diff(1). Please note that the * code in this file is heavily modified from the original source code * that is available at: * * * * This implementation is not compatible with the original libmba library. */ /* The following is a list of the modifications made to the original Michael * B. Allen code: * * Here is a list of changes from the original implementation: * - Switch memory allocation and error handling to R specific functions * - Removing variable arrays in favor of fixed sized buffers to simplify code; * this results in potential overallocation of memory since we pre-allocate a * 4 * (n + m + abs(n - m)) + 1 vector which is wasteful but still linear so * should be okay. * - Removing ability to specify custom comparison functions * - Adding a failover result if diffs exceed maximum allowable diffs whereby * we failover into a linear time algorithm to produce a sub optimal edit * script rather than simply saying the shortest edit script is longer than * allowable diffs; this is all the `faux_snake` stuff. This algorithm tries * to salvage whatever the myers algo computed up to the point of max diffs * - Adding lots of comments as we worked through the logic */ #include #include "diffobj.h" #define FV(k) _v(ctx, (k), 0) #define RV(k) _v(ctx, (k), 1) // We can't reach some branches through tests so they are untested, they may not // be reachable so we mark them as no-cov; we really should figure the logic out // to make sure they are unreachable, but don't have time for that now. static const char * err_msg_ubrnch = "Internal Error: reached theoretically unreachable branch %d, contact maintainer."; /* we've abandonned the use of varray for both ses and buf, instead we * pre-allocate both the edit script and the buffer to the maximum possible size */ struct _ctx { void *context; int *buf; // used to be varray int bufmax; struct diff_edit *ses; // used to be varray int si; int simax; int dmax; int dmaxhit; }; struct middle_snake { int x, y, u, v; }; /* debugging util */ /* const char * _op_to_chr(diff_op op) { switch (op) { case DIFF_MATCH: return "match"; break; case DIFF_DELETE: return "delete"; break; case DIFF_INSERT: return "insert"; break; case DIFF_NULL: return "NULL"; break; default: error("Logic Error: unexpected faux snake instruction; contact maintainer"); } } */ /* * k = diagonal number * val = x value * r = presumably whether we are looking up in reverse snakes */ static void _setv(struct _ctx *ctx, int k, int r, int val) { int j; int *i; /* Pack -N to N into 0 to N * 2, but also pack reverse and forward snakes * in that same space which is why we need the * 4 */ j = k <= 0 ? -k * 4 + r : k * 4 + (r - 2); if(j > ctx->bufmax || j < 0) { // nocov start error( "Logic Error: exceeded buffer size (%d vs %d); contact maintainer.", j, ctx->bufmax ); // nocov end } i = ctx->buf + j; *i = val; } /* * For any given `k` diagonal, return the x coordinate of the furthest reaching * path we've found. Use `r` to look for the x coordinate for the paths that * are starting from the bottom right instead of top left */ static int _v(struct _ctx *ctx, int k, int r) { int j; j = k <= 0 ? -k * 4 + r : k * 4 + (r - 2); if(j > ctx->bufmax || j < 0) { // nocov start error( "Logic Error: exceeded buffer 2 size (%d vs %d); contact maintainer.", j, ctx->bufmax ); // nocov end } int bufval = *(ctx->buf + j); return bufval; } /* Compare character vector values * * Needs to account for special case when indeces are oob for the strings. The * oob checks seem to be necessary since algo is requesting reads past length * of string, but not sure if that is intentional or not. In theory it should * not be, but perhaps this was handled gracefully by the varray business b4 * we changed it. */ int _comp_chr(SEXP a, int aidx, SEXP b, int bidx) { int alen = XLENGTH(a); int blen = XLENGTH(b); int comp; if(aidx >= alen && bidx >= blen) { // nocov start error(err_msg_ubrnch, 1); comp = 1; // nocov end } else if(aidx >= alen || bidx >= blen) { comp = 0; } else comp = STRING_ELT(a, aidx) == STRING_ELT(b, bidx); return(comp); } /* * Handle cases where differences exceed maximum allowable differences * * General logic is to create a faux snake that instead of moving down * diagonally will chain right and down moves until it hits a path coming * from the other direction. This snake is stored in `ctx`, and is then * written by `ses` to the `ses` list. */ static int _find_faux_snake( SEXP a, int aoff, int n, SEXP b, int boff, int m, struct _ctx *ctx, struct middle_snake *ms, int d, diff_op ** faux_snake ) { /* normally we would record k/x values at the end of the furthest reaching * snake, but here we need pick a path from top left and extend it until * we hit something coming from bottom right. */ /* start by finding which diagonal has the furthest reaching value * when looking from top left */ int k_max_f = 0, x_max_f = -1; int x_f, y_f, k_f; int delta = n - m; for (int k = d - 1; k >= -d + 1; k -= 2) { /* might need to shift by 1 */ int x_f = FV(k); int f_dist = x_f - abs(k); if(x_f > n || x_f - k > m) continue; if(f_dist > x_max_f - abs(k_max_f)) { x_max_f = x_f; k_max_f = k; } } /* didn't find a path so use origin */ if(x_max_f < 0) { // nocov start error(err_msg_ubrnch, 2); x_f = y_f = k_f = 0; // nocov end } else { k_f = k_max_f; x_f = x_max_f; y_f = x_f - k_max_f; } /* * now look for the furthest reaching point in any diagonal that is * below the diagonal we found above since those are the only ones we * can connect to * */ int k_max_r = 0, x_max_r = n + 1; int x_r, y_r; for (int k = -d; k <= k_max_f - delta; k += 2) { int x_r = RV(k); int r_dist = n - x_r - abs(k); /* skip reverse snakes that overshoot our forward snake * ---\ * \ * \ * \ * \--- * where there should be a decent path we can use, but because we are only * tracking the last coordinates we don't really have a way connecting this * type of path so we just go straight to the origin even though that is * even more sub-optinal; not even sure if this is a possible scenario */ if(x_r < x_f || x_r - k - delta < y_f) continue; /* since buffer is init to zero, an x_r value of zero means nothing, and * not all the way to the left of the graph; also, in reverse snakes the * snake should end at x == 1 in the leftmost case (we think) */ if(r_dist > n - x_max_r - abs(k_max_r) && x_r) { x_max_r = x_r; k_max_r = k; } } if(x_max_r >= n) { x_r = n; y_r = m; } else { x_r = x_max_r; /* not 100% sure about this one; seems like k_max_r is relative to the * bottom right origin, so maybe this should be x_r - k_max_r - delta? */ y_r = x_r - k_max_r - delta; } /* * attempt to connect the two paths we found. We need to store this * information as our "faux" snake since it will have to be processed * in a manner similar as the middle snake would be processed; start by * figuring out max number of steps it would take to connect the two * paths */ int max_steps = x_r - x_f + y_r - y_f + 1; int steps = 0; int diffs = 0; int step_dir = 1; /* last direction we moved in, 1 is down */ int x_sn = x_f, y_sn = y_f; /* initialize the fake snake */ if(max_steps < 0) error("Logic Error: fake snake step overflow? Contact maintainer."); // nocov diff_op * faux_snake_tmp = (diff_op*) R_alloc(max_steps, sizeof(diff_op)); for(int i = 0; i < max_steps; i++) *(faux_snake_tmp + i) = DIFF_NULL; /* we have a further reaching reverse snake: * not entirely sure if this should happen, but it seems it does */ while(x_sn < x_r || y_sn < y_r) { if(x_sn > x_r || y_sn > y_r) { error("Logic Error: Exceeded buffer for finding fake snake; contact maintainer."); // nocov } /* check to see if we could possibly move on a diagonal, and do so * if possible, if not alternate going down and right*/ if( x_sn <= x_r && y_sn <= y_r && _comp_chr(a, aoff + x_sn, b, boff + y_sn) ) { x_sn++; y_sn++; *(faux_snake_tmp + steps) = DIFF_MATCH; } else if (x_sn < x_r && (step_dir || y_sn >= y_r)) { x_sn++; diffs++; step_dir = !step_dir; *(faux_snake_tmp + steps) = DIFF_DELETE; } else if (y_sn < y_r && (!step_dir || x_sn >= x_r)) { y_sn++; diffs++; *(faux_snake_tmp + steps) = DIFF_INSERT; step_dir = !step_dir; } else { error("Logic Error: unexpected outcome in snake creation process; contact maintainer"); // nocov } steps++; } /* corner cases; must absolutely make sure steps LT max_steps since we rely * on at least one zero at the end of the faux_snake when we read it to know * to stop reading it */ if(x_sn != x_r || y_sn != y_r || steps >= max_steps) { error("Logic Error: faux snake process failed; contact maintainer."); // nocov } /* modify the pointer to the pointer so we can return in by ref */ *faux_snake = faux_snake_tmp; /* record the coordinates of our faux snake using `ms` */ ms->x = x_f; ms->y = y_f; ms->u = x_r; ms->v = y_r; return diffs; } /* * Advance from both ends of the diff graph toward center until we reach * up to half of the maximum possible number of differences between * a and b (note that `n` is net of `aoff`). As we process this we record the * end points of each path we explored in the `ctx` structure. Once we reach * the maximum number of differences, we return to `_ses` with the number of * differences found. `_ses` will then attempt to stitch back the snakes * together. */ static int _find_middle_snake( SEXP a, int aoff, int n, SEXP b, int boff, int m, struct _ctx *ctx, struct middle_snake *ms, diff_op ** faux_snake ) { int delta, odd, mid, d; delta = n - m; odd = delta & 1; mid = (n + m) / 2; mid += odd; _setv(ctx, 1, 0, 0); _setv(ctx, delta - 1, 1, n); /* For each number of differences `d`, compute the farthest reaching paths * from both the top left and bottom right of the edit graph */ for (d = 0; d <= mid; d++) { int k, x, y; /* reached maximum allowable differences before real exit condition*/ if ((2 * d - 1) >= ctx->dmax) { ctx->dmaxhit = 1; return _find_faux_snake(a, aoff, n, b, boff, m, ctx, ms, d, faux_snake); } /* Forward (from top left) paths*/ for (k = d; k >= -d; k -= 2) { if (k == -d || (k != d && FV(k - 1) < FV(k + 1))) { x = FV(k + 1); } else { x = FV(k - 1) + 1; } y = x - k; ms->x = x; ms->y = y; while(x < n && y < m && _comp_chr(a, aoff + x, b, boff + y)) { /* matching characters, just walk down diagonal */ x++; y++; } _setv(ctx, k, 0, x); /* for this diagonal we (think we) are now at farthest reaching point for * a given d. Then return if: * - If we're at the edge of the addressable part of the graph * - The reverse snakes are already overlapping in the `x` coordinate * * then it means that the only way to get to the snake coming from the * other direction is by either moving down or across for every remaining * move, so record the current coord as `u` and `v` and return * * Note that for the backward snake we reverse xy and uv so that the * matching snake is always defined in `ms` as starting at `ms.(xy)` and * ending at `ms.(uv)` */ if (odd && k >= (delta - (d - 1)) && k <= (delta + (d - 1))) { if (x >= RV(k)) { ms->u = x; ms->v = y; return 2 * d - 1; } } } /* Backwards (from bottom right) paths*/ for (k = d; k >= -d; k -= 2) { int kr = (n - m) + k; if (k == d || (k != -d && RV(kr - 1) < RV(kr + 1))) { x = RV(kr - 1); } else { x = RV(kr + 1) - 1; } y = x - kr; ms->u = x; ms->v = y; while (x > 0 && y > 0 && _comp_chr(a, aoff + x - 1, b, boff + y - 1)) { /* matching characters, just walk up diagonal */ x--; y--; } _setv(ctx, kr, 1, x); /* see comments in forward section */ if (!odd && kr >= -d && kr <= d) { if (x <= FV(kr)) { ms->x = x; ms->y = y; return 2 * d; } } } } error("Logic Error: failed finding middle snake, contact maintainer"); // nocov } /* * Update edit script atom with newest info, we record the operation, and the * offset and length so we can recover the values from the original vector */ static void _edit(struct _ctx *ctx, int op, int off, int len) { struct diff_edit *e; if (len == 0 || ctx->ses == NULL) { return; } /* Add an edit to the SES (or * coalesce if the op is the same) */ e = ctx->ses + ctx->si; if(ctx->si > ctx->simax) error("Logic Error: exceed edit script length; contact maintainer."); // nocov if (e->op != op) { if (e->op) { ctx->si++; if(ctx->si > ctx->simax) error("Logic Error: exceed edit script length; contact maintainer."); // nocov e = ctx->ses + ctx->si; } e->op = op; e->off = off; e->len = len; } else { e->len += len; } } /* * Update edit script with the faux diff data */ static void _edit_faux(struct _ctx *ctx, diff_op * faux_snake, int aoff, int boff) { int i = 0, off; diff_op op; while((op = *(faux_snake + i++)) != DIFF_NULL) { switch (op) { case DIFF_MATCH: { boff++; /* note no break here */ } case DIFF_DELETE: off = aoff++; break; case DIFF_INSERT: off = boff++; break; default: error("Logic Error: unexpected faux snake instruction; contact maintainer"); // nocov } /* use x (aoff) offset for MATCH and DELETE, y offset for INSERT */ _edit(ctx, op, off, 1); } } /* Generate shortest edit script * */ static int _ses( SEXP a, int aoff, int n, SEXP b, int boff, int m, struct _ctx *ctx ) { R_CheckUserInterrupt(); struct middle_snake ms; int d; //Rprintf("m: %d n: %d\n", m, n); if (n == 0) { _edit(ctx, DIFF_INSERT, boff, m); d = m; } else if (m == 0) { _edit(ctx, DIFF_DELETE, aoff, n); d = n; } else { /* Find the middle "snake" around which we * recursively solve the sub-problems. Note this modifies `ms` by ref to * set the beginning and end coordinates of the snake of the furthest * reaching path. The beginning is always the top left part of the snake, * irrespective of whether it was found on a forward or reverse path as * f_m_s will flip the coordinates when appropriately when recording them * in `ms` * * Additionally, if diffs exceed max.diffs, then `faux.snake` will also * be set. `faux_snake` is a pointer to a pointer that points to a the * beginning of an array of match/delete/insert instructions generated * to connect the top left and bottom right paths. _fms() repoints the * pointer to an updated edit list if needed via (_ffs()) */ diff_op fsv = DIFF_NULL; diff_op * faux_snake; faux_snake = &fsv; // // d // diff_op * fsp = NULL; // diff_op fsv = DIFF_NULL; // *fsp = fsv; // **faux_snake = *fsp; d = _find_middle_snake(a, aoff, n, b, boff, m, ctx, &ms, &faux_snake); //Rprintf("d: %d\n", d); if (d == -1) { // nocov start error( "Logic error: failed trying to find middle snake, contact maintainer." ); // nocov end } else if (ctx->ses == NULL) { // nocov start error(err_msg_ubrnch, 6); return d; // nocov end } else if (d > 1) { /* in this case we have something along the lines of (note the non- * diagonal bits are just non-diagonal, we're making no claims about * whether they should or could be of the horizontal variety) * ... - * \ * \ * \- ... * so we will record the snake (diagonal) in the middle, and recurse * on the stub at the beginning and on the stub at the end separately */ /* Beginning stub */ if (_ses(a, aoff, ms.x, b, boff, ms.y, ctx) == -1) { // nocov start error("Logic error: failed trying to run ses; contact maintainer."); // nocov end } /* Now record middle snake * * u should be x coord of end of snake of longest path * v should be y coord of end of snake * x, y should be coord of begining of snake * * record that there is a matching section between the beginning of the * middle snake and the end * * if faux_snake is defined it means that there were too many differences * to complete algorigthm normally so we need to record the faux snake */ if(*faux_snake) { /* for faux snake length of snake will most likely not be ms.u - ms.x * since it will not be a diagonal */ _edit_faux(ctx, faux_snake, aoff + ms.x, boff + ms.y); } else { _edit(ctx, DIFF_MATCH, aoff + ms.x, ms.u - ms.x); } /* Now recurse into the second stub */ aoff += ms.u; boff += ms.v; n -= ms.u; m -= ms.v; if (_ses(a, aoff, n, b, boff, m, ctx) == -1) { // nocov start error("Logic error: failed trying to run ses 2; contact maintainer."); // nocov end } } else { int x = ms.x; int u = ms.u; /* There are only 4 base cases when the * edit distance is 1. Having a hard time finding cases that trigger the * x == u, possibly because the algo eats leading matches, although * apparently we do achieve it somewhere in the test suite. * * n > m m > n * * - | * \ \ x != u * \ \ * * \ \ * \ \ x == u * - | */ //Rprintf("x: %d u: %d y: %d v: %d\n", ms.x, ms.u, ms.y, ms.v); if (m > n) { if (x == u) { _edit(ctx, DIFF_MATCH, aoff, n); _edit(ctx, DIFF_INSERT, boff + (m - 1), 1); } else { _edit(ctx, DIFF_INSERT, boff, 1); _edit(ctx, DIFF_MATCH, aoff, n); } } else if (m < n) { if (x == u) { _edit(ctx, DIFF_MATCH, aoff, m); _edit(ctx, DIFF_DELETE, aoff + (n - 1), 1); } else { _edit(ctx, DIFF_DELETE, aoff, 1); _edit(ctx, DIFF_MATCH, aoff + 1, m); } } else { // Should never get here since this should be a D 2 case // nocov start error( "Very special case n %d m %d aoff %d boff %d u %d\n", n, m, aoff, boff, ms.u ); // nocov end } } } return d; } /* * - ses is a pointer to an array of diff_edit structs that is initialized * outside of this call * - aoff and boff are how far we've moved across the strings, used mostly in the * context of recursion for _ses * - n is the lenght of a, m the length of b */ int diff(SEXP a, int aoff, int n, SEXP b, int boff, int m, void *context, int dmax, struct diff_edit *ses, int *sn ) { if(n < 0 || m < 0) error("Logic Error: negative lengths; contact maintainer."); // nocov struct _ctx ctx; int d, x, y; struct diff_edit *e = NULL; int delta = n - m; if(delta < 0) delta = -delta; int bufmax = 4 * (n + m + delta) + 1; // see _setv if(bufmax < n || bufmax < m) error("Logic Error: exceeded maximum allowable combined string length."); // nocov int *tmp = (int *) R_alloc(bufmax, sizeof(int)); for(int i = 0; i < bufmax; i++) *(tmp + i) = 0; ctx.context = context; /* initialize buffer */ ctx.buf = tmp; ctx.bufmax = bufmax; ctx.ses = ses; ctx.si = 0; ctx.simax = n + m; ctx.dmax = dmax ? dmax : INT_MAX; ctx.dmaxhit = 0; /* initialize first ses edit struct*/ if (ses && sn) { if ((e = ses) == NULL) { error("Logic Error: specifying sn, but ses is NULL, contact maintainer."); // nocov } e->op = 0; } /* The _ses function assumes the SES will begin or end with a delete * or insert. The following will ensure this is true by eating any * beginning matches. This is also a quick to process sequences * that match entirely. */ x = y = 0; while (x < n && y < m) { if(boff > INT_MAX - y) error("Internal error: overflow for boff; contact maintainer"); //nocov if(aoff > INT_MAX - x) error("Internal error: overflow for aoff; contact maintainer"); //nocov if(!_comp_chr(a, aoff + x, b, boff + y)) break; x++; y++; } _edit(&ctx, DIFF_MATCH, aoff, x); d = _ses(a, aoff + x, n - x, b, boff + y, m - y, &ctx); if (ses && sn) { *sn = e->op ? ctx.si + 1 : 0; } return d * (ctx.dmaxhit ? -1 : 1); } diffobj/src/diffobj.h0000755000176200001440000000150713442554134014233 0ustar liggesusers/* * Copyright (C) 2019 Brodie Gaslam * * This file is part of "diffobj - Diffs for R Objects" * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Go to for a copy of the license. */ #ifndef DIFFR_H_ #define DIFFR_H_ #include #include #include "diff.h" SEXP DIFFOBJ_diffobj(SEXP a, SEXP b, SEXP max); #endif diffobj/src/init.c0000755000176200001440000000177213442554134013572 0ustar liggesusers/* * Copyright (C) 2019 Brodie Gaslam * * This file is part of "diffobj - Diffs for R Objects" * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Go to for a copy of the license. */ #include "diffobj.h" #include static const R_CallMethodDef callMethods[] = { {"diffobj", (DL_FUNC) &DIFFOBJ_diffobj, 3}, {NULL, NULL, 0} }; void R_init_diffobj(DllInfo *info) { R_registerRoutines(info, NULL, callMethods, NULL, NULL); R_useDynamicSymbols(info, FALSE); R_forceSymbols(info, TRUE); } diffobj/src/diff.h0000755000176200001440000000537513442554134013547 0ustar liggesusers/* * This file is part of a program that contains a heavily modified version of * Michael B. Allen implementation of the Myers diff algorithm. This * implementation is not compatible with the original one. See next * comment blocks for original copyright and license information. * * Copyright (C) 2019 Brodie Gaslam * * This file is part of "diffobj - Diffs for R Objects" * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Go to for a copy of the license. */ /* ORIGINAL COPYRIGHT NOTICE: * * diff - compute a shortest edit script (SES) given two sequences * Copyright (c) 2004 Michael B. Allen * * The MIT License * * 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 DIFFOBJ_DIFF_H #define DIFFOBJ_DIFF_H /* diff - compute a shortest edit script (SES) given two sequences */ #ifdef __cplusplus extern "C" { #endif typedef enum { DIFF_NULL = 0, DIFF_MATCH, DIFF_DELETE, DIFF_INSERT } diff_op; struct diff_edit { short op; int off; /* off into s1 if MATCH or DELETE but s2 if INSERT */ int len; }; /* consider alternate behavior for each NULL parameter */ int diff(SEXP a, int aoff, int n, SEXP b, int boff, int m, void *context, int dmax, struct diff_edit *ses, int *sn ); #ifdef __cplusplus } #endif #endif diffobj/src/diffobj.c0000755000176200001440000000414413442554134014226 0ustar liggesusers/* * Copyright (C) 2019 Brodie Gaslam * * This file is part of "diffobj - Diffs for R Objects" * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Go to for a copy of the license. */ #include #include "diffobj.h" SEXP DIFFOBJ_diffobj(SEXP a, SEXP b, SEXP max) { int n, m, d; int sn, i; /* allocate max possible size for edit script; wasteful, but this greatly * simplifies code since we don't need any of the variable array logic and * besides is just an (M + N) allocation */ n = XLENGTH(a); m = XLENGTH(b); if( TYPEOF(max) != INTSXP || XLENGTH(max) != 1L || asInteger(max) == NA_INTEGER ) error("Logic Error: `max` not integer(1L) and not NA"); // nocov int max_i = asInteger(max); if(max_i < 0) max_i = 0; struct diff_edit *ses = (struct diff_edit *) R_alloc(n + m + 1, sizeof(struct diff_edit)); d = diff(a, 0, n, b, 0, m, NULL, max_i, ses, &sn); SEXP res = PROTECT(allocVector(VECSXP, 4)); SEXP type = PROTECT(allocVector(INTSXP, sn)); SEXP count = PROTECT(allocVector(INTSXP, sn)); SEXP offs = PROTECT(allocVector(INTSXP, sn)); for (i = 0; i < sn; i++) { struct diff_edit *e = ses + i; switch (e->op) { case DIFF_MATCH: INTEGER(type)[i] = 1; break; case DIFF_INSERT: INTEGER(type)[i] = 2; break; case DIFF_DELETE: INTEGER(type)[i] = 3; break; } INTEGER(count)[i] = e->len; INTEGER(offs)[i] = e->off; } SET_VECTOR_ELT(res, 0, type); SET_VECTOR_ELT(res, 1, count); SET_VECTOR_ELT(res, 2, offs); SET_VECTOR_ELT(res, 3, ScalarInteger(d)); UNPROTECT(4); return res; } diffobj/NAMESPACE0000755000176200001440000000452513466141552013114 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method(as.character,diffobj_ogewlhgiadfl2) S3method(print,diffobj_ogewlhgiadfl) export(AlignThreshold) export(Pager) export(PagerBrowser) export(PagerOff) export(PagerSystem) export(PagerSystemLess) export(PaletteOfStyles) export(Rdiff_chr) export(Rdiff_obj) export(Style) export(StyleAnsi) export(StyleAnsi256DarkRgb) export(StyleAnsi256DarkYb) export(StyleAnsi256LightRgb) export(StyleAnsi256LightYb) export(StyleAnsi8NeutralRgb) export(StyleAnsi8NeutralYb) export(StyleFuns) export(StyleHtml) export(StyleHtmlLightRgb) export(StyleHtmlLightYb) export(StyleRaw) export(StyleText) export(auto_context) export(console_lines) export(cont_f) export(diffChr) export(diffCsv) export(diffDeparse) export(diffFile) export(diffObj) export(diffPrint) export(diffStr) export(diffobj_css) export(diffobj_js) export(diffobj_set_def_opts) export(div_f) export(finalizeHtml) export(gdo) export(guidesChr) export(guidesDeparse) export(guidesFile) export(guidesPrint) export(guidesStr) export(has_Rdiff) export(make_blocking) export(nchar_html) export(pager_is_less) export(ses) export(span_f) export(tag_f) export(trimChr) export(trimDeparse) export(trimFile) export(trimPrint) export(trimStr) export(view_or_browse) exportClasses(AlignThreshold) exportClasses(Diff) exportClasses(PagerOff) exportClasses(PagerSystem) exportClasses(PagerSystemLess) exportClasses(PaletteOfStyles) exportClasses(Style) exportClasses(StyleAnsi) exportClasses(StyleAnsi256DarkRgb) exportClasses(StyleAnsi256DarkYb) exportClasses(StyleAnsi256LightRgb) exportClasses(StyleAnsi256LightYb) exportClasses(StyleAnsi8NeutralRgb) exportClasses(StyleAnsi8NeutralYb) exportClasses(StyleFuns) exportClasses(StyleHtml) exportClasses(StyleHtmlLightRgb) exportClasses(StyleHtmlLightYb) exportClasses(StyleRaw) exportClasses(StyleSummary) exportClasses(StyleSummaryHtml) exportClasses(StyleText) exportMethods("[") exportMethods(diffObj) exportMethods(head) exportMethods(summary) exportMethods(tail) import(crayon) import(methods) importFrom(grDevices,rgb) importFrom(stats,ave) importFrom(stats,frequency) importFrom(stats,is.ts) importFrom(stats,setNames) importFrom(tools,Rdiff) importFrom(utils,browseURL) importFrom(utils,capture.output) importFrom(utils,file_test) importFrom(utils,packageVersion) importFrom(utils,read.csv) useDynLib(diffobj, .registration=TRUE, .fixes="DIFFOBJ_") diffobj/NEWS.md0000755000176200001440000002245713466146342013001 0ustar liggesusers# diffobj ## v0.2.3 This is a bugfix release. * [#136](https://github.com/brodieG/diffobj/issues/136): Documentation for `ignore.white.space` (h/t @flying-sheep) and `max.diffs` parameters listed incorrect defaults. * [#135](https://github.com/brodieG/diffobj/issues/135): Incorrect handling of potential meta data strings when unwrapping atomics would cause a "wrong sign in by argument" error (h/t @flying-sheep). We also fixed other bugs related to the handling of meta data in atomic vectors that were uncovered while debugging this issue. * [#134](https://github.com/brodieG/diffobj/issues/134): Forwarding `...` to `diff*` functions no longer breaks substitution of arguments for diff banners (h/t @noamross).. * [#133](https://github.com/brodieG/diffobj/issues/133): `diffFile` considers files with equal content but different locations to be `all.equal` now (h/t @noamross). * [#132](https://github.com/brodieG/diffobj/issues/132): Duplicate pager slot for baseline `Pager` removed (h/t Bill Dunlap). There are also several other small internal changes that in theory should not affect user facing behavior. ## v0.2.2 * Set `RNGversion()` due to changes to sampling mechanism. ## v0.2.0-1 ### Features * [#129](https://github.com/brodieG/diffobj/issues/129): Allow pager specification via lists rather than full `Pager` objects for easier changes to defaults. As part of this we changed `StyleRaw` objects to use default pager instead of `PagerOff`. * [#126](https://github.com/brodieG/diffobj/issues/126): Add embedding diffs in Shiny to vignette. * [#119](https://github.com/brodieG/diffobj/issues/119): `ignore.whitespace` now also ignores white space differences adjoining punctuation. * [#118](https://github.com/brodieG/diffobj/issues/118): New option to preserve temporary diff file output when using pager (see `?Pager`). * [#114](https://github.com/brodieG/diffobj/issues/114): New options `strip.sgr` and `sgr.supported` allow finer control of what happens when input already contains ANSI CSI SGR and how ANSI CSI SGR is handled in string manipulations. Related to this, `options(crayon.enabled=TRUE)` is no longer set when capturing output prior to diff as it used to be. By default pre-existing ANSI CSI SGR is stripped with a warning prior to comparison. ### Bugs * [#131](https://github.com/brodieG/diffobj/issues/131): Fix missing slot in S4 class definition (discovered by Bill Dunlap). * [#127](https://github.com/brodieG/diffobj/issues/127): Width CSS conflicts with bootstrap (reported by @eckyu, debugged/fixed by @cpsievert). ## v0.1.11 * [#123](https://github.com/brodieG/diffobj/issues/123): Compatibility with R3.1 (@krlmlr). * [#121](https://github.com/brodieG/diffobj/issues/121): Vignette describing how to embed diffs in Rmd documents (@JBGruber). * [#115](https://github.com/brodieG/diffobj/issues/115): Declare HTML page diff encoding/charset as UTF-8 (@artemklevtsov). ## v0.1.10 * Comply with CRAN directive to remove references to packages not in depends/imports/suggests in tests (these were run optionally before). * Fix bugs in corner case handling when we hit `max.diffs`. ## v0.1.9 * Fix test failures caused by changes in tibble output ## v0.1.8 * [#111](https://github.com/brodieG/diffobj/issues/111): Fixed guides with `row.names=FALSE` (thank you @[Henrik Bengtsson](https://github.com/HenrikBengtsson)). * [#113](https://github.com/brodieG/diffobj/issues/113): Adapt tests to new `str` return values (thank you @[Martin Mächler](https://github.com/mmaechler)). ## v0.1.7 * Fix tests for next `testthat` release. * [#107](https://github.com/brodieG/diffobj/issues/107): Diffs on quoted language * [#108](https://github.com/brodieG/diffobj/issues/108): Problems caused by copying `crayon` functions ([@seulki-choi](https://stackoverflow.com/users/7788015/seulki-choi), @gaborcsardi) * [#100](https://github.com/brodieG/diffobj/issues/100): R_useDynamicSymbols * [#97](https://github.com/brodieG/diffobj/issues/97): 2D Guidelines fixes for data.table, tibble * [#96](https://github.com/brodieG/diffobj/issues/96): Warnings when comparing large data tables. * [#94](https://github.com/brodieG/diffobj/issues/94): Guide detection problems in nested lists. * [#105](https://github.com/brodieG/diffobj/issues/105): Copyright tweaks. ## v0.1.6 * [#87](https://github.com/brodieG/diffobj/issues/87): `diffobj` is now GPL (>=2) instead of GPL-3. * [#81](https://github.com/brodieG/diffobj/issues/81): Better handling of mixed UTF-8 / ASCII strings, reported by [jennybc](https://github.com/jennybc) * [#88](https://github.com/brodieG/diffobj/issues/88): correctly handle trimming when empty lists are involved, reported by [wch](https://github.com/wch) * [#77](https://github.com/brodieG/diffobj/issues/77): `diffObj` now favors dispatching to `diffPrint` unless `diffPrint` output is large * [#82](https://github.com/brodieG/diffobj/issues/82): `diffChr` and `ses` now treat `NA` as "NA" (needed with change in `nchar(NA)` in base R) * [#85](https://github.com/brodieG/diffobj/issues/85): Improved alignment of unwrapped atomic vector diffs * [#83](https://github.com/brodieG/diffobj/issues/83): Improve pager auto detection (note now ANSI output is only allowed by default if terminal supports ANSI colors and the system pager is `less`, see `?Pager` for details) * [#92](https://github.com/brodieG/diffobj/issues/92), [#80](https://github.com/brodieG/diffobj/issues/80), [#45](https://github.com/brodieG/diffobj/issues/45): basic implementation of S4 guidelines and trimming (full resolution eventually with [#33](https://github.com/brodieG/diffobj/issues/33)) * [#84](https://github.com/brodieG/diffobj/issues/84): simplify how to call `diffChr` for improved performance, including "optimization" of `convert.hz.whitespace`. * [#64](https://github.com/brodieG/diffobj/issues/64): fix line limit in corner case * More robust handling of external `diff*` methods and of how `diffObj` calls `diffStr` and `diffPrint` ## v0.1.5 * [#71](https://github.com/brodieG/diffobj/issues/71): Buggy diffs b/w data frames when one has sequential row numbers and the other does not, loosely related to [#38](https://github.com/brodieG/diffobj/issues/38) * [#69](https://github.com/brodieG/diffobj/issues/69): Improve performance on outputs with large print/show output, and other assorted minor optimizations * [#72](https://github.com/brodieG/diffobj/issues/72): Warn when `style` parameter overrides other user supplied parameters * [#70](https://github.com/brodieG/diffobj/issues/70): Improve word contrast in YB HTML mode * [#63](https://github.com/brodieG/diffobj/issues/63): Show `all.equal` output when objects are not `all.equal` but there are no visible differences * Add [Mean Relative Indifference](http://htmlpreview.github.io/?https://raw.githubusercontent.com/brodieG/diffobj/master/inst/doc/metacomp.html) vignette and update vignette styling ## v0.1.4 * [#67](https://github.com/brodieG/diffobj/issues/67): Fix CRAN Binaries * Clarified that C code is heavily modified and incompatible with original `libmba` implementation ## v0.1.3 * First version on CRAN * [#51](https://github.com/brodieG/diffobj/issues/51): use RStudio viewport to display HTML diffs when running in RStudio, as per [Noam Ross](https://twitter.com/noamross/status/760115813559009280) * [#54](https://github.com/brodieG/diffobj/issues/54): [#55](https://github.com/brodieG/diffobj/issues/55), scale HTML output to viewport width (see `?Style`) * [#53](https://github.com/brodieG/diffobj/issues/53): default term colors computed on run instead of on package load * [#56](https://github.com/brodieG/diffobj/issues/56): disable wrap for HTML output * HTML output now captured with default width 80 since there is no explicit relationship between HTML viewport width and `getOption("width")` * The `style` parameter now accepts lists to use as instantiation arguments for `Style` objects (see `?Style`) * Fix subtle rendering and formatting application flaws * Switch Travis shields to SVG per Gábor Csárdi * Improve in-hunk alignment of partially matching lines * Compile with `-pedantic`, fix related warnings [Arun](http://stackoverflow.com/users/559784/arun) * Improved coverage and more robust testing * Several internal structure changes to accommodate improvements ## v0.1.2 * [#46](https://github.com/brodieG/diffobj/issues/46): Guide and Trim Problems with Lists * [#47](https://github.com/brodieG/diffobj/issues/47): Output Format in non-ANSI Terminals Without Browser (reported by [Frank](https://github.com/brodieG/diffobj/issues/47)) * [#48](https://github.com/brodieG/diffobj/issues/48): `make_blocking` Default prompt Confusing (reported by [Frank](https://github.com/brodieG/diffobj/issues/47)) * [#49](https://github.com/brodieG/diffobj/issues/49): In-Hunk Word Diffs Issues when Unwrap-diffing Atomics * [#50](https://github.com/brodieG/diffobj/issues/50): CSS Lost in Rstudio Server Sessions (reported by [Steven Beaupré](https://chat.stackoverflow.com/users/4064778/steven-beaupre)) ## v0.1.1 * Turn off unwrapping for _named_ atomic vectors (see [#43](https://github.com/brodieG/diffobj/issues/43)) * [#44](https://github.com/brodieG/diffobj/issues/44): Proper handling of NULL objects in `diffStr` * [#41](https://github.com/brodieG/diffobj/issues/41): Compilation Issues in Winbuilder ## v0.1.0 * Initial Release diffobj/R/0000755000176200001440000000000013466146340012065 5ustar liggesusersdiffobj/R/diff.R0000755000176200001440000010724013466034612013125 0ustar liggesusers# Copyright (C) 2019 Brodie Gaslam # # This file is part of "diffobj - Diffs for R Objects" # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Go to for a copy of the license. #' Diffs for R Objects #' #' Generate a colorized diff of two R objects for an intuitive visualization of #' their differences. See `vignette(package="diffobj", "diffobj")` for details. #' #' @import crayon #' @import methods #' @importFrom utils capture.output file_test packageVersion read.csv #' @importFrom stats ave frequency is.ts setNames #' @importFrom grDevices rgb #' @name diffobj-package #' @docType package NULL # Because all these functions are so similar, we have constructed them with a # function factory. This allows us to easily maintain consistent formals during # initial development process when they have not been set in stone yet. make_diff_fun <- function(capt_fun) { # nocov start function( target, current, mode=gdo("mode"), context=gdo("context"), format=gdo("format"), brightness=gdo("brightness"), color.mode=gdo("color.mode"), word.diff=gdo("word.diff"), pager=gdo("pager"), guides=gdo("guides"), trim=gdo("trim"), rds=gdo("rds"), unwrap.atomic=gdo("unwrap.atomic"), max.diffs=gdo("max.diffs"), disp.width=gdo("disp.width"), ignore.white.space=gdo("ignore.white.space"), convert.hz.white.space=gdo("convert.hz.white.space"), tab.stops=gdo("tab.stops"), line.limit=gdo("line.limit"), hunk.limit=gdo("hunk.limit"), align=gdo("align"), style=gdo("style"), palette.of.styles=gdo("palette"), frame=par_frame(), interactive=gdo("interactive"), term.colors=gdo("term.colors"), tar.banner=NULL, cur.banner=NULL, strip.sgr=gdo("strip.sgr"), sgr.supported=gdo("sgr.supported"), extra=list() ) { # nocov end frame # force frame so that `par_frame` called in this context call.dat <- extract_call(sys.calls(), frame) target # force target/current so if one missing we get an error here current # and not later # Check args and evaluate all the auto-selection arguments etc.proc <- check_args( call=call.dat$call, tar.exp=call.dat$tar, cur.exp=call.dat$cur, mode=mode, context=context, line.limit=line.limit, format=format, brightness=brightness, color.mode=color.mode, pager=pager, ignore.white.space=ignore.white.space, max.diffs=max.diffs, align=align, disp.width=disp.width, hunk.limit=hunk.limit, convert.hz.white.space=convert.hz.white.space, tab.stops=tab.stops, style=style, palette.of.styles=palette.of.styles, frame=frame, tar.banner=tar.banner, cur.banner=cur.banner, guides=guides, rds=rds, trim=trim, word.diff=word.diff, unwrap.atomic=unwrap.atomic, extra=extra, interactive=interactive, term.colors=term.colors, strip.sgr=strip.sgr, sgr.supported=sgr.supported, call.match=match.call() ) # If in rds mode, try to see if either target or current reference an RDS if(rds) { target <- get_rds(target) current <- get_rds(current) } # Force crayon to whatever ansi status we chose; note we must do this after # touching vars in case someone passes `options(crayon.enabled=...)` as one # of the arguments # old.crayon.opt <- options( # crayon.enabled= # is(etc.proc@style, "StyleAnsi") || # (!is(etc.proc@style, "StyleHtml") && etc.proc@sgr.supported) # ) # on.exit(options(old.crayon.opt), add=TRUE) err <- make_err_fun(sys.call()) # Compute gutter values so that we know correct widths to use for capture, # etc. If not a base text type style, assume gutter and column padding are # zero even though that may not always be correct etc.proc@gutter <- gutter_dat(etc.proc) col.pad.width <- nchar2(etc.proc@style@text@pad.col, sgr.supported=etc.proc@sgr.supported) gutt.width <- etc.proc@gutter@width half.width <- as.integer((etc.proc@disp.width - col.pad.width) / 2) etc.proc@line.width <- max(etc.proc@disp.width, .min.width + gutt.width) etc.proc@text.width <- etc.proc@line.width - gutt.width etc.proc@line.width.half <- max(half.width, .min.width + gutt.width) etc.proc@text.width.half <- etc.proc@line.width.half - gutt.width # If in side by side mode already then we know we want half-width, and if # width is less than 80 we know we want unitfied if(etc.proc@mode == "auto" && etc.proc@disp.width < 80L) etc.proc@mode <- "unified" if(etc.proc@mode == "sidebyside") etc.proc <- sideBySide(etc.proc) # Capture and diff diff <- capt_fun(target, current, etc=etc.proc, err=err, extra) diff } } #' Diff \code{print}ed Objects #' #' Runs the diff between the \code{print} or \code{show} output produced by #' \code{target} and \code{current}. Given the extensive parameter list, this #' documentation page is intended as a reference for all the \code{diff*} #' methods. For a high level introduction see \code{vignette("diffobj")}. #' #' Almost all aspects of how the diffs are computed and displayed are #' controllable through the \code{diff*} methods parameters. This results in a #' lengthy parameter list, but in practice you should rarely need to adjust #' anything past the \code{color.mode} parameter. Default values are specified #' as options so that users may configure diffs in a persistent manner. #' \code{\link{gdo}} is a shorthand function to access \code{diffobj} options. #' #' Parameter order after \code{color.mode} is not guaranteed. Future versions #' of \code{diffobj} may add parameters and re-order existing parameters past #' \code{color.mode}. #' #' This and other \code{diff*} functions are S4 generics that dispatch on the #' \code{target} and \code{current} parameters. Methods with signature #' \code{c("ANY", "ANY")} are defined and act as the default methods. You can #' use this to set up methods to pre-process or set specific parameters for #' selected classes that can then \code{callNextMethod} for the actual diff. #' Note that while the generics include \code{...} as an argument, none of the #' methods do. #' #' @section Matrices and Data Frames: #' #' While \code{diffPrint} attempts to handle the default R behavior that wraps #' wide tables, the results are often sub-optimal. A better approach is to set #' the \code{disp.width} parameter to a large enough value such that wrapping is #' not necessary, and a browser-based \code{pager}. In the future we will add #' the capability to specify different capture widths and wrap widths so that #' this is an option for terminal output (see #' \href{https://github.com/brodieG/diffobj/issues/109}{issue 109}). #' #' One thing to keep in mind is that \code{diffPrint} is not designed to work #' with very large data frames. #' #' @export #' @seealso \code{\link{diffObj}}, \code{\link{diffStr}}, #' \code{\link{diffChr}} to compare character vectors directly, #' \code{\link{diffDeparse}} to compare deparsed objects, \code{\link{ses}} #' for a minimal and fast diff @param target the reference object #' @param target the reference object #' @param current the object being compared to \code{target} #' @param mode character(1L), one of: #' \itemize{ #' \item \dQuote{unified}: diff mode used by \code{git diff} #' \item \dQuote{sidebyside}: line up the differences side by side #' \item \dQuote{context}: show the target and current hunks in their #' entirety; this mode takes up a lot of screen space but makes it easier #' to see what the objects actually look like #' \item \dQuote{auto}: default mode; pick one of the above, will favor #' \dQuote{sidebyside} unless \code{getOption("width")} is less than 80, #' or in \code{diffPrint} and objects are dimensioned and do not fit side #' by side, or in \code{diffChr}, \code{diffDeparse}, \code{diffFile} and #' output does not fit in side by side without wrapping #' } #' @param context integer(1L) how many lines of context are shown on either side #' of differences (defaults to 2). Set to \code{-1L} to allow as many as #' there are. Set to \dQuote{auto} to display as many as 10 lines or as few #' as 1 depending on whether total screen lines fit within the number of lines #' specified in \code{line.limit}. Alternatively pass the return value of #' \code{\link{auto_context}} to fine tune the parameters of the auto context #' calculation. #' @param format character(1L), controls the diff output format, one of: #' \itemize{ #' \item \dQuote{auto}: to select output format based on terminal #' capabilities; will attempt to use one of the ANSI formats if they #' appear to be supported, and if not or if you are in the Rstudio console #' it will attempt to use HTML and browser output if in interactive mode. #' \item \dQuote{raw}: plain text #' \item \dQuote{ansi8}: color and format diffs using basic ANSI escape #' sequences #' \item \dQuote{ansi256}: like \dQuote{ansi8}, except using the full range #' of ANSI formatting options #' \item \dQuote{html}: color and format using HTML markup; the resulting #' string is processed with \code{\link{enc2utf8}} when output as a full #' web page (see docs for \code{html.output} under \code{\link{Style}}). #' } #' Defaults to \dQuote{auto}. See \code{palette.of.styles} for details #' on customization, \code{\link{style}} for full control of output format. #' See `pager` parameter for more discussion of Rstudio behavior. #' @param brightness character, one of \dQuote{light}, \dQuote{dark}, #' \dQuote{neutral}, useful for adjusting color scheme to light or dark #' terminals. \dQuote{neutral} by default. See \code{\link{PaletteOfStyles}} #' for details and limitations. Advanced: you may specify brightness as a #' function of \code{format}. For example, if you typically wish to use a #' \dQuote{dark} color scheme, except for when in \dQuote{html} format when #' you prefer the \dQuote{light} scheme, you may use #' \code{c("dark", html="light")} as the value for this parameter. This is #' particularly useful if \code{format} is set to \dQuote{auto} or if you #' want to specify a default value for this parameter via options. Any names #' you use should correspond to a \code{format}. You must have one unnamed #' value which will be used as the default for all \code{format}s that are #' not explicitly specified. #' @param color.mode character, one of \dQuote{rgb} or \dQuote{yb}. #' Defaults to \dQuote{yb}. \dQuote{yb} stands for \dQuote{Yellow-Blue} for #' color schemes that rely primarily on those colors to style diffs. #' Those colors can be easily distinguished by individuals with #' limited red-green color sensitivity. See \code{\link{PaletteOfStyles}} for #' details and limitations. Also offers the same advanced usage as the #' \code{brightness} parameter. #' @param word.diff TRUE (default) or FALSE, whether to run a secondary word #' diff on the in-hunk differences. For atomic vectors setting this to #' FALSE could make the diff \emph{slower} (see the \code{unwrap.atomic} #' parameter). For other uses, particularly with \code{\link{diffChr}} #' setting this to FALSE can substantially improve performance. #' @param pager one of \dQuote{auto} (default), \dQuote{on}, #' \dQuote{off}, a \code{\link{Pager}} object, or a list; controls whether and #' how a pager is used to display the diff output. If you require a #' particular pager behavior you must use a \code{\link{Pager}} #' object, or \dQuote{off} to turn off the pager. All other settings will #' interact with other parameters such as \code{format}, \code{style}, as well #' as with your system capabilities in order to select the pager expected to #' be most useful. #' #' \dQuote{auto} and \dQuote{on} are the same, except that in non-interactive #' mode \dQuote{auto} is equivalent to \dQuote{off}. \dQuote{off} will always #' send output to the console. If \dQuote{on}, whether the output #' actually gets routed to the pager depends on the pager \code{threshold} #' setting (see \code{\link{Pager}}). The default behavior is to use the #' pager associated with the \code{Style} object. The \code{Style} object is #' itself is determined by the \code{format} or \code{style} parameters. #' #' Depending on your system configuration different styles and corresponding #' pagers will get selected, unless you specify a \code{Pager} object #' directly. On a system with a system pager that supports ANSI CSI SGR #' colors, the pager will only trigger if the output is taller than one #' window. If the system pager is not known to support ANSI colors then the #' output will be sent as HTML to the IDE viewer if available or to the web #' browser if not. Even though Rstudio now supports ANSI CSI SGR at the #' console output is still formatted as HTML and sent to the IDE viewer. #' Partly this is for continuity of behavior, but also because the default #' Rstudio pager does not support ANSI CSI SGR, at least as of this writing. #' #' If \code{pager} is a list, then the same as with \dQuote{on}, except that #' the \code{Pager} object associated with the selected \code{Style} object is #' re-instantiated with the union of the list elements and the existing #' settings of that \code{Pager}. The list should contain named elements that #' correspond to the \code{\link{Pager}} instantiation parameters. The names #' must be specified in full as partial parameter matching will not be carried #' out because the pager is re-instantiated with \code{\link{new}}. #' #' See \code{\link{Pager}}, \code{\link{Style}}, and #' \code{\link{PaletteOfStyles}} for more details and for instructions on how #' to modify the default behavior. #' @param guides TRUE (default), FALSE, or a function that accepts at least two #' arguments and requires no more than two arguments. Guides #' are additional context lines that are not strictly part of a hunk, but #' provide important contextual data (e.g. column headers). If TRUE, the #' context lines are shown in addition to the normal diff output, typically #' in a different color to indicate they are not part of the hunk. If a #' function, the function should accept as the first argument the object #' being diffed, and the second the character representation of the object. #' The function should return the indices of the elements of the #' character representation that should be treated as guides. See #' \code{\link{guides}} for more details. #' @param trim TRUE (default), FALSE, or a function that accepts at least two #' arguments and requires no more than two arguments. Function should compute #' for each line in captured output what portion of those lines should be #' diffed. By default, this is used to remove row meta data differences #' (e.g. \code{[1,]}) so they alone do not show up as differences in the #' diff. See \code{\link{trim}} for more details. #' @param rds TRUE (default) or FALSE, if TRUE will check whether #' \code{target} and/or \code{current} point to a file that can be read with #' \code{\link{readRDS}} and if so, loads the R object contained in the file #' and carries out the diff on the object instead of the original argument. #' Currently there is no mechanism for specifying additional arguments to #' \code{readRDS} #' @param unwrap.atomic TRUE (default) or FALSE. Relevant primarily for #' \code{diffPrint}, if TRUE, and \code{word.diff} is also TRUE, and both #' \code{target} and \code{current} are \emph{unnamed} one-dimension atomics , #' the vectors are unwrapped and diffed element by element, and then #' re-wrapped. Since \code{diffPrint} is fundamentally a line diff, the #' re-wrapped lines are lined up in a manner that is as consistent as possible #' with the unwrapped diff. Lines that contain the location of the word #' differences will be paired up. Since the vectors may well be wrapped with #' different periodicities this will result in lines that are paired up that #' look like they should not be paired up, though the locations of the #' differences should be. If is entirely possible that setting this parameter #' to FALSE will result in a slower diff. This happens if two vectors are #' actually fairly similar, but their line representations are not. For #' example, in comparing \code{1:100} to \code{c(100, 1:99)}, there is really #' only one difference at the \dQuote{word} level, but every screen line is #' different. \code{diffChr} will also do the unwrapping if it is given a #' character vector that contains output that looks like the atomic vectors #' described above. This is a bug, but as the functionality could be useful #' when diffing e.g. \code{capture.output} data, we now declare it a feature. #' @param line.limit integer(2L) or integer(1L), if length 1 how many lines of #' output to show, where \code{-1} means no limit. If length 2, the first #' value indicates the threshold of screen lines to begin truncating output, #' and the second the number of lines to truncate to, which should be fewer #' than the threshold. Note that this parameter is implemented on a #' best-efforts basis and should not be relied on to produce the exact #' number of lines requested. In particular do not expect it to work well for #' for values small enough that the banner portion of the diff would have to #' be trimmed. If you want a specific number of lines use \code{[} or #' \code{head} / \code{tail}. One advantage of \code{line.limit} over these #' other options is that you can combine it with \code{context="auto"} and #' auto \code{max.level} selection (the latter for \code{diffStr}), which #' allows the diff to dynamically adjust to make best use of the available #' display lines. \code{[}, \code{head}, and \code{tail} just subset the text #' of the output. #' @param hunk.limit integer(2L) or integer (1L), how many diff hunks to show. #' Behaves similarly to \code{line.limit}. How many hunks are in a #' particular diff is a function of how many differences, and also how much #' \code{context} is used since context can cause two hunks to bleed into #' each other and become one. #' @param max.diffs integer(1L), number of \emph{differences} after which we #' abandon the \code{O(n^2)} diff algorithm in favor of a naive element by #' element comparison. Set to \code{-1L} to always stick to the original #' algorithm (defaults to 50000L). #' @param disp.width integer(1L) number of display columns to take up; note that #' in \dQuote{sidebyside} \code{mode} the effective display width is half this #' number (set to 0L to use default widths which are \code{getOption("width")} #' for normal styles and \code{80L} for HTML styles. Future versions of #' \code{diffobj} may change this to larger values for two dimensional objects #' for better diffs (see details). #' @param ignore.white.space TRUE or FALSE, whether to consider differences in #' horizontal whitespace (i.e. spaces and tabs) as differences (defaults to #' TRUE). #' @param convert.hz.white.space TRUE or FALSE, whether modify input strings #' that contain tabs and carriage returns in such a way that they display as #' they would \bold{with} those characters, but without using those #' characters (defaults to TRUE). The conversion assumes that tab stops are #' spaced evenly eight characters apart on the terminal. If this is not the #' case you may specify the tab stops explicitly with \code{tab.stops}. #' @param tab.stops integer, what tab stops to use when converting hard tabs to #' spaces. If not integer will be coerced to integer (defaults to 8L). You #' may specify more than one tab stop. If display width exceeds that #' addressable by your tab stops the last tab stop will be repeated. #' @param align numeric(1L) between 0 and 1, proportion of #' words in a line of \code{target} that must be matched in a line of #' \code{current} in the same hunk for those lines to be paired up when #' displayed (defaults to 0.25), or an \code{\link{AlignThreshold}} object. #' Set to \code{1} to turn off alignment which will cause all lines in a hunk #' from \code{target} to show up first, followed by all lines from #' \code{current}. Note that in order to be aligned lines must meet the #' threshold and have at least 3 matching alphanumeric characters (see #' \code{\link{AlignThreshold}} for details). #' @param style \dQuote{auto}, a \code{\link{Style}} object, or a list. #' \dQuote{auto} by default. If a \code{Style} object, will override the #' the \code{format}, \code{brightness}, and \code{color.mode} parameters. #' The \code{Style} object provides full control of diff output styling. #' If a list, then the same as \dQuote{auto}, except that if the auto-selected #' \code{Style} requires instantiation (see \code{\link{PaletteOfStyles}}), #' then the list contents will be used as arguments when instantiating the #' style object. See \code{\link{Style}} for more details, in particular the #' examples. #' @param palette.of.styles \code{\link{PaletteOfStyles}} object; advanced #' usage, contains all the \code{\link{Style}} objects or #' \dQuote{classRepresentation} objects extending \code{\link{Style}} that are #' selected by specifying the \code{format}, \code{brightness}, and #' \code{color.mode} parameters. See \code{\link{PaletteOfStyles}} for more #' details. #' @param frame an environment to use as the evaluation frame for the #' \code{print/show/str}, calls and for \code{diffObj}, the evaluation frame #' for the \code{diffPrint} / \code{diffStr} calls. Defaults to the return #' value of \code{\link{par_frame}}. #' @param interactive TRUE or FALSE whether the function is being run in #' interactive mode, defaults to the return value of #' \code{\link{interactive}}. If in interactive mode, pager will be used if #' \code{pager} is \dQuote{auto}, and if ANSI styles are not supported and #' \code{style} is \dQuote{auto}, output will be send to viewer/browser as #' HTML. #' @param term.colors integer(1L) how many ANSI colors are supported by the #' terminal. This variable is provided for when #' \code{\link[=num_colors]{crayon::num_colors}} does not properly detect how #' many ANSI colors are supported by your terminal. Defaults to return value #' of \code{\link[=num_colors]{crayon::num_colors}} and should be 8 or 256 to #' allow ANSI colors, or any other number to disallow them. This only #' impacts output format selection when \code{style} and \code{format} are #' both set to \dQuote{auto}. #' @param tar.banner character(1L), language, or NULL, used to generate the #' text to display ahead of the diff section representing the target output. #' If NULL will use the deparsed \code{target} expression, if language, will #' use the language as it would the \code{target} expression, if #' character(1L), will use the string with no modifications. The language #' mode is provided because \code{diffStr} modifies the expression prior to #' display (e.g. by wrapping it in a call to \code{str}). Note that it is #' possible in some cases that the substituted value of \code{target} actually #' is character(1L), but if you provide a character(1L) value here it will be #' assumed you intend to use that value literally. #' @param cur.banner character(1L) like \code{tar.banner}, but for #' \code{current} #' @param strip.sgr TRUE, FALSE, or NULL (default), whether to strip ANSI CSI #' SGR sequences prior to comparison and for display of diff. If NULL, #' resolves to TRUE if `style` resolves to an ANSI formatted diff, and #' FALSE otherwise. The default behavior is to avoid confusing diffs where #' the original SGR and the SGR added by the diff are mixed together. #' @param sgr.supported TRUE, FALSE, or NULL (default), whether to assume the #' standard output device supports ANSI CSI SGR sequences. If TRUE, strings #' will be manipulated accounting for the SGR sequences. If NULL, #' resolves to TRUE if `style` resolves to an ANSI formatted diff, and #' to `crayon::has_color()` otherwise. This only controls how the strings are #' manipulated, not whether SGR is added to format the diff, which is #' controlled by the `style` parameter. This parameter is exposed for the #' rare cases where you might wish to control string manipulation behavior #' directly. #' @param extra list additional arguments to pass on to the functions used to #' create text representation of the objects to diff (e.g. \code{print}, #' \code{str}, etc.) #' @param ... unused, for compatibility of methods with generics #' @return a \code{Diff} object; this object has a \code{show} #' method that will display the diff to screen or pager, as well as #' \code{summary}, \code{any}, and \code{as.character} methods. #' If you store the return value instead of displaying it to screen, and #' display it later, it is possible for the display to be thrown off if #' there are environment changes (e.g. display width changes) in between #' the time you compute the diff and the time you display it. #' @rdname diffPrint #' @name diffPrint #' @export #' @examples #' ## `pager="off"` for CRAN compliance; you may omit in normal use #' diffPrint(letters, letters[-5], pager="off") setGeneric( "diffPrint", function(target, current, ...) standardGeneric("diffPrint") ) #' @rdname diffPrint setMethod("diffPrint", signature=c("ANY", "ANY"), make_diff_fun(capt_print)) #' Diff Object Structures #' #' Compares the \code{str} output of \code{target} and \code{current}. If #' the \code{max.level} parameter to \code{str} is left unspecified, will #' attempt to find the largest \code{max.level} that fits within #' \code{line.limit} and shows at least one difference. #' #' Due to the seemingly inconsistent nature of \code{max.level} when used with #' objects with nested attributes, and also due to the relative slowness of #' \code{str}, this function simulates the effect of \code{max.level} by hiding #' nested lines instead of repeatedly calling \code{str} with varying values of #' \code{max.level}. #' #' @inheritParams diffPrint #' @seealso \code{\link{diffPrint}} for details on the \code{diff*} functions, #' \code{\link{diffObj}}, \code{\link{diffStr}}, #' \code{\link{diffChr}} to compare character vectors directly, #' \code{\link{diffDeparse}} to compare deparsed objects, #' \code{\link{ses}} for a minimal and fast diff #' @return a \code{Diff} object; see \code{\link{diffPrint}}. #' @rdname diffStr #' @export #' @examples #' ## `pager="off"` for CRAN compliance; you may omit in normal use #' with(mtcars, diffStr(lm(mpg ~ hp)$qr, lm(mpg ~ disp)$qr, pager="off")) setGeneric("diffStr", function(target, current, ...) standardGeneric("diffStr")) #' @rdname diffStr setMethod("diffStr", signature=c("ANY", "ANY"), make_diff_fun(capt_str)) #' Diff Character Vectors Element By Element #' #' Will perform the diff on the actual string values of the character vectors #' instead of capturing the printed screen output. Each vector element is #' treated as a line of text. NA elements are treated as the string #' \dQuote{NA}. Non character inputs are coerced to character. #' #' @inheritParams diffPrint #' @seealso \code{\link{diffPrint}} for details on the \code{diff*} functions, #' \code{\link{diffObj}}, \code{\link{diffStr}}, #' \code{\link{diffDeparse}} to compare deparsed objects, #' \code{\link{ses}} for a minimal and fast diff #' @return a \code{Diff} object; see \code{\link{diffPrint}}. #' @export #' @rdname diffChr #' @examples #' ## `pager="off"` for CRAN compliance; you may omit in normal use #' diffChr(LETTERS[1:5], LETTERS[2:6], pager="off") setGeneric("diffChr", function(target, current, ...) standardGeneric("diffChr")) #' @rdname diffChr setMethod("diffChr", signature=c("ANY", "ANY"), make_diff_fun(capt_chr)) #' Diff Deparsed Objects #' #' Perform diff on the character vectors produced by \code{\link{deparse}}ing #' the objects. Each element counts as a line. If an element contains newlines #' it will be split into elements new lines by the newlines. #' #' @export #' @inheritParams diffPrint #' @seealso \code{\link{diffPrint}} for details on the \code{diff*} functions, #' \code{\link{diffObj}}, \code{\link{diffStr}}, #' \code{\link{diffChr}} to compare character vectors directly, #' \code{\link{ses}} for a minimal and fast diff #' @return a \code{Diff} object; see \code{\link{diffPrint}}. #' @export #' @rdname diffDeparse #' @examples #' ## `pager="off"` for CRAN compliance; you may omit in normal use #' diffDeparse(matrix(1:9, 3), 1:9, pager="off") setGeneric( "diffDeparse", function(target, current, ...) standardGeneric("diffDeparse") ) #' @rdname diffDeparse setMethod("diffDeparse", signature=c("ANY", "ANY"), make_diff_fun(capt_deparse)) #' Diff Files #' #' Reads text files with \code{\link{readLines}} and performs a diff on the #' resulting character vectors. #' #' @export #' @param target character(1L) or file connection with read capability; if #' character should point to a text file #' @param current like \code{target} #' @inheritParams diffPrint #' @seealso \code{\link{diffPrint}} for details on the \code{diff*} functions, #' \code{\link{diffObj}}, \code{\link{diffStr}}, #' \code{\link{diffChr}} to compare character vectors directly, #' \code{\link{ses}} for a minimal and fast diff #' @return a \code{Diff} object; see \code{\link{diffPrint}}. #' @export #' @rdname diffFile #' @examples #' \dontrun{ #' url.base <- "https://raw.githubusercontent.com/wch/r-source" #' f1 <- file.path(url.base, "29f013d1570e1df5dc047fb7ee304ff57c99ea68/README") #' f2 <- file.path(url.base, "daf0b5f6c728bd3dbcd0a3c976a7be9beee731d9/README") #' diffFile(f1, f2) #' } setGeneric( "diffFile", function(target, current, ...) standardGeneric("diffFile") ) #' @rdname diffFile setMethod("diffFile", signature=c("ANY", "ANY"), make_diff_fun(capt_file)) #' Diff CSV Files #' #' Reads CSV files with \code{\link{read.csv}} and passes the resulting data #' frames onto \code{\link{diffPrint}}. \code{extra} values are passed as #' arguments are passed to both \code{read.csv} and \code{print}. To the #' extent you wish to use different \code{extra} arguments for each of those #' functions you will need to \code{read.csv} the files and pass them to #' \code{diffPrint} yourself. #' #' @export #' @param target character(1L) or file connection with read capability; #' if character should point to a CSV file #' @param current like \code{target} #' @inheritParams diffPrint #' @seealso \code{\link{diffPrint}} for details on the \code{diff*} functions, #' \code{\link{diffObj}}, \code{\link{diffStr}}, #' \code{\link{diffChr}} to compare character vectors directly, #' \code{\link{ses}} for a minimal and fast diff #' @return a \code{Diff} object; see \code{\link{diffPrint}}. #' @export #' @rdname diffCsv #' @examples #' iris.2 <- iris #' iris.2$Sepal.Length[5] <- 99 #' f1 <- tempfile() #' f2 <- tempfile() #' write.csv(iris, f1, row.names=FALSE) #' write.csv(iris.2, f2, row.names=FALSE) #' ## `pager="off"` for CRAN compliance; you may omit in normal use #' diffCsv(f1, f2, pager="off") #' unlink(c(f1, f2)) setGeneric( "diffCsv", function(target, current, ...) standardGeneric("diffCsv") ) #' @rdname diffCsv setMethod("diffCsv", signature=c("ANY", "ANY"), make_diff_fun(capt_csv)) #' Diff Objects #' #' Compare either the \code{print}ed or \code{str} screen representation of #' R objects depending on which is estimated to produce the most useful #' diff. The selection process tries to minimize screen lines while maximizing #' differences shown subject to display constraints. The decision algorithm is #' likely to evolve over time, so do not rely on this function making #' a particular selection under specific circumstances. Instead, use #' \code{\link{diffPrint}} or \code{\link{diffStr}} if you require one or the #' other output. #' #' @inheritParams diffPrint #' @seealso \code{\link{diffPrint}} for details on the \code{diff*} methods, #' \code{\link{diffStr}}, #' \code{\link{diffChr}} to compare character vectors directly #' \code{\link{diffDeparse}} to compare deparsed objects, #' \code{\link{ses}} for a minimal and fast diff #' @return a \code{Diff} object; see \code{\link{diffPrint}}. #' @export #' @examples #' ## `pager="off"` for CRAN compliance; you may omit in normal use #' diffObj(letters, c(letters[1:10], LETTERS[11:26]), pager="off") #' with(mtcars, diffObj(lm(mpg ~ hp)$qr, lm(mpg ~ disp)$qr, pager="off")) setGeneric("diffObj", function(target, current, ...) standardGeneric("diffObj")) diff_obj <- make_diff_fun(identity) # we overwrite the body next body(diff_obj) <- quote({ if(length(extra)) stop("Argument `extra` must be empty in `diffObj`.") # frame # force frame so that `par_frame` called in this context # Need to generate calls inside a new child environment so that we do not # pollute the environment and create potential conflicts with ... args # used to run this inside a `local` call, but issues cropped up with the # advent of JIT, and can't recall why just storing arguments at first # was a problem args <- as.list(environment()) call.dat <- extract_call(sys.calls(), frame) err <- make_err_fun(call.dat$call) if(is.null(args$tar.banner)) args$tar.banner <- call("quote", call.dat$tar) if(is.null(args$cur.banner)) args$cur.banner <- call("quote", call.dat$cur) call.print <- as.call(c(list(quote(diffobj::diffPrint)), args)) call.str <- as.call(c(list(quote(diffobj::diffStr)), args)) call.str[["extra"]] <- list(max.level="auto") res.print <- try(eval(call.print, frame), silent=TRUE) res.str <- try(eval(call.str, frame), silent=TRUE) if(inherits(res.str, "try-error")) err( "Error in calling `diffStr`: ", conditionMessage(attr(res.str, "condition")) ) if(inherits(res.print, "try-error")) err( "Error in calling `diffPrint`: ", conditionMessage(attr(res.print, "condition")) ) # Run both the print and str versions, and then decide which to use based # on some weighting of various factors including how many lines needed to be # omitted vs. how many differences were reported diff.p <- count_diff_hunks(res.print@diffs) diff.s <- count_diff_hunks(res.str@diffs) diff.l.p <- diff_line_len( res.print@diffs, res.print@etc, tar.capt=res.print@tar.dat$raw, cur.capt=res.print@cur.dat$raw ) diff.l.s <- diff_line_len( res.str@diffs, res.str@etc, tar.capt=res.str@tar.dat$raw, cur.capt=res.str@cur.dat$raw ) # How many lines of the input are in the diffs, vs how many lines of input diff.line.ratio.p <- lineCoverage(res.print) diff.line.ratio.s <- lineCoverage(res.str) # Only show the one with differences res <- if(!diff.s && diff.p) { res.print } else if(!diff.p && diff.s) { res.str # If one fits in full and the other doesn't, show the one that fits in full } else if( !res.str@trim.dat$lines[[1L]] && res.print@trim.dat$lines[[1L]] ) { res.str } else if( res.str@trim.dat$lines[[1L]] && !res.print@trim.dat$lines[[1L]] ) { res.print } else if (diff.l.p <= console_lines() / 2) { # Always use print if print output is reasonable size res.print } else { # Calculate the trade offs between the two options s.score <- diff.s / diff.l.s * diff.line.ratio.s p.score <- diff.p / diff.l.p * diff.line.ratio.p if(p.score >= s.score) res.print else res.str } res }) #' @export setMethod("diffObj", signature=c("ANY", "ANY"), diff_obj) diffobj/R/subset.R0000755000176200001440000000565713442554134013533 0ustar liggesusers# Copyright (C) 2019 Brodie Gaslam # # This file is part of "diffobj - Diffs for R Objects" # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Go to for a copy of the license. #' @include s4.R NULL #' Subsetting Methods for Diff Objects #' #' Methods to subset the character representation of the diff output. The #' subsetting bears no link to the line numbers in the diffs, only to the actual #' displayed diff. #' #' \code{[} only supports numeric indices, and returns without error if you #' specify out of bound indices. If you apply multiple subsetting methods they #' will be applied in the following order irrespective of what order you #' actually specify them in: \code{[}, then \code{head}, then \code{tail}. #' If you use the same subsetting method multiple times on the same object, #' the last call will define the outcome. #' #' These methods are implemented by storing the chosen indices in the #' \code{Diff} object and using them to subset the \code{as.character} output. #' This mechanism explains the seemingly odd behavior documented above. #' #' @export #' @rdname extract-Diff-method #' @param x \code{Diff} object #' @param i subsetting index, must be numeric #' @param n integer(1L), the size for the resulting object #' @param ... unused, for compatibility with generics #' @return \code{Diff} object with subsetting indices recorded for use by #' \code{show} #' ## `pager="off"` for CRAN compliance; you may omit in normal use #' diff <- diffChr(letters, LETTERS, format="raw", pager="off") #' diff[5:15] #' head(diff, 5) #' tail(diff, 5) #' head(head(diff, 5), 8) ## note not 'typical' behavior setMethod( "[", signature(x="Diff", i="numeric", j="missing", drop="missing"), function(x, i) { if(anyNA(i) || (any(i < 0) && any(i > 0))) stop("`i` may not contain NAs or both positive and negative indices") x@sub.index <- as.integer(i) x } ) #' @export #' @rdname extract-Diff-method setMethod("head", "Diff", function(x, n, ...) { if(length(list(...))) stop("This method does not support arguments other than `x` or `n`") if(!is.int.1L(n)) stop("`n` must be integer(1L) and not NA") x@sub.head <- as.integer(n) x } ) #' @export #' @rdname extract-Diff-method setMethod("tail", "Diff", function(x, n, ...) { if(length(list(...))) stop("This method does not support arguments other than `x` or `n`") if(!is.int.1L(n)) stop("`n` must be integer(1L) and not NA") x@sub.tail <- as.integer(n) x } ) diffobj/R/core.R0000755000176200001440000005517713466133740013162 0ustar liggesusers# Copyright (C) 2019 Brodie Gaslam # # This file is part of "diffobj - Diffs for R Objects" # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Go to for a copy of the license. #' @include s4.R NULL #' Generate a character representation of Shortest Edit Sequence #' #' @keywords internal #' @seealso \code{\link{ses}} #' @param x S4 object of class \code{MyersMbaSes} #' @param ... unused #' @return character vector setMethod("as.character", "MyersMbaSes", function(x, ...) { dat <- as.data.frame(x) # Split our data into sections that have either deletes or inserts and get # rid of the matches dat <- dat[dat$type != "Match", ] d.s <- split(dat, dat$section) # For each section, compute whether we should display, change, insert, # delete, or both, and based on that append to the ses string ses_rng <- function(off, len) paste0(off, if(len > 1L) paste0(",", off + len - 1L)) vapply( unname(d.s), function(d) { del <- sum(d$len[d$type == "Delete"]) ins <- sum(d$len[d$type == "Insert"]) if(del) { del.first <- which(d$type == "Delete")[[1L]] del.off <- d$off[del.first] } if(ins) { ins.first <- which(d$type == "Insert")[[1L]] ins.off <- d$off[ins.first] } if(del && ins) { paste0(ses_rng(del.off, del), "c", ses_rng(ins.off, ins)) } else if (del) { paste0(ses_rng(del.off, del), "d", d$last.b[[1L]]) } else if (ins) { paste0(d$last.a[[1L]], "a", ses_rng(ins.off, ins)) } else { stop("Logic Error: unexpected edit type; contact maintainer.") # nocov } }, character(1L) ) } ) # Used for mapping edit actions to numbers so we can use numeric matrices .edit.map <- c("Match", "Insert", "Delete") setMethod("as.matrix", "MyersMbaSes", function(x, row.names=NULL, optional=FALSE, ...) { # map del/ins/match to numbers len <- length(x@type) edit <- match(x@type, .edit.map) matches <- .edit.map[edit] == "Match" section <- cumsum(matches + c(0L, head(matches, -1L))) # Track what the max offset observed so far for elements of the `a` string # so that if we have an insert command we can get the insert position in # `a` last.a <- c( if(len) 0L, head( cummax( ifelse(.edit.map[edit] != "Insert", x@offset + x@length, 1L) ) - 1L, -1L ) ) # Do same thing with `b`, complicated because the matching entries are all # in terms of `a` last.b <- c( if(len) 0L, head(cumsum(ifelse(.edit.map[edit] != "Delete", x@length, 0L)), -1L) ) cbind( type=edit, len=x@length, off=x@offset, section=section, last.a=last.a, last.b = last.b ) } ) setMethod("as.data.frame", "MyersMbaSes", function(x, row.names=NULL, optional=FALSE, ...) { len <- length(x@type) mod <- c("Insert", "Delete") dat <- data.frame(type=x@type, len=x@length, off=x@offset) matches <- dat$type == "Match" dat$section <- cumsum(matches + c(0L, head(matches, -1L))) # Track what the max offset observed so far for elements of the `a` string # so that if we have an insert command we can get the insert position in # `a` dat$last.a <- c( if(nrow(dat)) 0L, head( cummax(ifelse(dat$type != "Insert", dat$off + dat$len, 1L)) - 1L, -1L ) ) # Do same thing with `b`, complicated because the matching entries are all # in terms of `a` dat$last.b <- c( if(nrow(dat)) 0L, head(cumsum(ifelse(dat$type != "Delete", dat$len, 0L)), -1L) ) dat } ) #' Shortest Edit Script #' #' Computes shortest edit script to convert \code{a} into \code{b} by removing #' elements from \code{a} and adding elements from \code{b}. Intended primarily #' for debugging or for other applications that understand that particular #' format. See \href{GNU diff docs}{http://www.gnu.org/software/diffutils/manual/diffutils.html#Detailed-Normal} #' for how to interpret the symbols. #' #' \code{ses} will be much faster than any of the #' \code{\link[=diffPrint]{diff*}} methods, particularly for large inputs with #' limited numbers of differences. #' #' NAs are treated as the string \dQuote{NA}. Non-character inputs are coerced #' to character. #' #' @export #' @param a character #' @param b character #' @inheritParams diffPrint #' @param warn TRUE (default) or FALSE whether to warn if we hit `max.diffs`. #' @return character #' @examples #' ses(letters[1:3], letters[2:4]) ses <- function(a, b, max.diffs=gdo("max.diffs"), warn=gdo("warn")) { if(!is.character(a)) { a <- try(as.character(a)) if(inherits(a, "try-error")) stop("Argument `a` is not character and could not be coerced to such") } if(!is.character(b)) { b <- try(as.character(b)) if(inherits(b, "try-error")) stop("Argument `b` is not character and could not be coerced to such") } if(is.numeric(max.diffs)) max.diffs <- as.integer(max.diffs) if(!is.int.1L(max.diffs)) stop("Argument `max.diffs` must be scalar integer.") if(!is.TF(warn)) stop("Argument `warn` must be TRUE or FALSE.") if(anyNA(a)) a[is.na(a)] <- "NA" if(anyNA(b)) b[is.na(b)] <- "NA" as.character(diff_myers(a, b, max.diffs=max.diffs, warn=warn)) } #' Diff two character vectors #' #' Implementation of Myer's Diff algorithm with linear space refinement #' originally implemented by Mike B. Allen as part of #' \href{libmba}{http://www.ioplex.com/~miallen/libmba/} #' version 0.9.1. This implementation is a heavily modified version of the #' original C code and is not compatible with the \code{libmba} library. #' The C code is simplified by using fixed size arrays instead of variable #' ones for tracking the longest reaching paths and for recording the shortest #' edit scripts. Additionally all error handling and memory allocation calls #' have been moved to the internal R functions designed to handle those things. #' A failover result is provided in the case where max diffs allowed is #' exceeded. Ability to provide custom comparison functions is removed. #' #' @keywords internal #' @param a character #' @param b character #' @param max.diffs integer(1L) how many differences before giving up; set to #' zero to allow as many as there are #' @param warn TRUE or FALSE, whether to warn if we hit `max.diffs`. #' @return list #' @useDynLib diffobj, .registration=TRUE, .fixes="DIFFOBJ_" diff_myers <- function(a, b, max.diffs=0L, warn=FALSE) { stopifnot( is.character(a), is.character(b), all(!is.na(c(a, b))), is.int.1L(max.diffs), is.TF(warn) ) res <- .Call(DIFFOBJ_diffobj, a, b, max.diffs) res <- setNames(res, c("type", "length", "offset", "diffs")) types <- .edit.map res$type <- factor(types[res$type], levels=types) res$offset <- res$offset + 1L # C 0-indexing originally res.s4 <- try(do.call("new", c(list("MyersMbaSes", a=a, b=b), res))) if(inherits(res.s4, "try-error")) # nocov start stop( "Logic Error: unable to instantiate shortest edit script object; contact ", "maintainer." ) # nocov end if(isTRUE(warn) && res$diffs < 0) { warning( "Exceeded `max.diffs`: ", abs(res$diffs), " vs ", max.diffs, " allowed. ", "Diff is probably suboptimal." ) } res.s4 } # Print Method for Shortest Edit Path # # Bare bones display of shortest edit path using GNU diff conventions # # @param object object to display # @return character the shortest edit path character representation, invisibly # @rdname diffobj_s4method_doc #' @rdname diffobj_s4method_doc setMethod("show", "MyersMbaSes", function(object) { res <- as.character(object) cat(res, sep="\n") invisible(res) } ) #' Summary Method for Shortest Edit Path #' #' Displays the data required to generate the shortest edit path for comparison #' between two strings. #' #' @export #' @keywords internal #' @param object the \code{diff_myers} object to display #' @param with.match logical(1L) whether to show what text the edit command #' refers to #' @param ... forwarded to the data frame print method used to actually display #' the data #' @return whatever the data frame print method returns setMethod("summary", "MyersMbaSes", function(object, with.match=FALSE, ...) { what <- vapply( seq_along(object@type), function(y) { t <- object@type[[y]] o <- object@offset[[y]] l <- object@length[[y]] vec <- if(t == "Insert") object@b else object@a paste0(vec[o:(o + l - 1L)], collapse="") }, character(1L) ) res <- data.frame( type=object@type, string=what, len=object@length, offset=object@offset ) if(!with.match) res <- res[-2L] print(res, ...) } ) # mode is display mode (sidebyside, etc.) # diff.mode is whether we are doing the first pass line diff, or doing the # in-hunk or word-wrap versions # warn is to allow us to suppress warnings after first hunk warning char_diff <- function(x, y, context=-1L, etc, diff.mode, warn) { stopifnot( diff.mode %in% c("line", "hunk", "wrap"), isTRUE(warn) || identical(warn, FALSE) ) max.diffs <- etc@max.diffs # probably shouldn't generate S4, but easier... diff <- diff_myers(x, y, max.diffs, warn=FALSE) hunks <- as.hunks(diff, etc=etc) hit.diffs.max <- FALSE if(diff@diffs < 0L) { hit.diffs.max <- TRUE diff@diffs <- -diff@diffs diff.msg <- c( line="overall", hunk="in-hunk word", wrap="atomic wrap-word" ) if(warn) warning( "Exceeded diff limit during diff computation (", diff@diffs, " vs. ", max.diffs, " allowed); ", diff.msg[diff.mode], " diff is likely not optimal", call.=FALSE ) } # used to be a `DiffDiffs` object, but too slow list(hunks=hunks, hit.diffs.max=hit.diffs.max) } # Compute the character representation of a hunk header make_hh <- function(h.g, mode, tar.dat, cur.dat, ranges.orig) { h.ids <- vapply(h.g, "[[", integer(1L), "id") h.head <- vapply(h.g, "[[", logical(1L), "guide") # exclude header hunks from contributing to range, and adjust ranges for # possible fill lines added to the data h.ids.nh <- h.ids[!h.head] tar.rng <- find_rng(h.ids.nh, ranges.orig[1:2, , drop=FALSE], tar.dat$fill) tar.rng.f <- cumsum(!tar.dat$fill)[tar.rng] cur.rng <- find_rng(h.ids.nh, ranges.orig[3:4, , drop=FALSE], cur.dat$fill) cur.rng.f <- cumsum(!cur.dat$fill)[cur.rng] hh.a <- paste0(rng_as_chr(tar.rng.f)) hh.b <- paste0(rng_as_chr(cur.rng.f)) if(mode == "sidebyside") sprintf("@@ %s @@", c(hh.a, hh.b)) else { sprintf("@@ %s / %s @@", hh.a, hh.b) } } # Do not allow `useBytes=TRUE` if there are any matches with `useBytes=FALSE` # # Clean up word.ind to avoid issues where we have mixed UTF-8 and non # UTF-8 strings in different hunks, and gregexpr is trying to optimize # buy using useBytes=TRUE in ASCII only strings without knowing that in a # different hunk there are UTF-8 strings fix_word_ind <- function(x) { matches <- vapply(x, function(y) length(y) > 1L || y != -1L, logical(1L)) useBytes <- vapply(x, function(y) isTRUE(attr(y, "useBytes")), logical(1L)) if(!all(useBytes[matches])) x <- lapply(x, `attr<-`, "useBytes", NULL) x } # Variation on `char_diff` used for the overall diff where we don't need # to worry about overhead from creating the `Diff` object line_diff <- function( target, current, tar.capt, cur.capt, context, etc, warn=TRUE, strip=TRUE ) { if(!is.valid.guide.fun(etc@guides)) # nocov start stop( "Logic Error: guides are not a valid guide function; contact maintainer" ) # nocov end etc@guide.lines <- make_guides(target, tar.capt, current, cur.capt, etc@guides) # Need to remove new lines as the processed captures do that anyway and we # end up with mismatched lengths if we don't if(any(nzchar(tar.capt))) tar.capt <- split_new_line(tar.capt, sgr.supported=etc@sgr.supported) if(any(nzchar(cur.capt))) cur.capt <- split_new_line(cur.capt, sgr.supported=etc@sgr.supported) # Some debate as to whether we want to do this first, or last. First has # many benefits so that everything is consistent, width calcs can work fine, # etc., but only issue is that user provided trim functions might not expect # the transformation of the data; this needs to be documented with the trim # docs. tar.capt.p <- tar.capt cur.capt.p <- cur.capt if(etc@convert.hz.white.space) { tar.capt.p <- strip_hz_control( tar.capt, stops=etc@tab.stops, sgr.supported=etc@sgr.supported ) cur.capt.p <- strip_hz_control( cur.capt, stops=etc@tab.stops, sgr.supported=etc@sgr.supported ) } # Remove whitespace and CSI SGR if warranted if(etc@strip.sgr) { if(has.style.1 <- any(crayon::has_style(tar.capt.p))) tar.capt.p <- crayon::strip_style(tar.capt.p) if(has.style.2 <- any(crayon::has_style(cur.capt.p))) cur.capt.p <- crayon::strip_style(cur.capt.p) if(has.style.1 || has.style.2) etc@warn( "`target` or `current` contained ANSI CSI SGR when rendered; these ", "were stripped. Use `strip.sgr=FALSE` to preserve them in the diffs." ) } # Apply trimming to remove row heads, etc, but only if something gets trimmed # from both elements tar.trim.ind <- apply_trim(target, tar.capt.p, etc@trim) tar.trim <- do.call( substr, list(tar.capt.p, tar.trim.ind[, 1L], tar.trim.ind[, 2L]) ) cur.trim.ind <- apply_trim(current, cur.capt.p, etc@trim) cur.trim <- do.call( substr, list(cur.capt.p, cur.trim.ind[, 1L], cur.trim.ind[, 2L]) ) if(identical(tar.trim, tar.capt.p) || identical(cur.trim, cur.capt.p)) { # didn't trim in both, so go back to original tar.trim <- tar.capt.p tar.trim.ind <- cbind( rep(1L, length(tar.capt.p)), nchar(tar.capt.p) ) cur.trim <- cur.capt.p cur.trim.ind <- cbind( rep(1L, length(cur.capt.p)), nchar(cur.capt.p) ) } tar.comp <- tar.trim cur.comp <- cur.trim if(etc@ignore.white.space) { tar.comp <- normalize_whitespace(tar.comp) cur.comp <- normalize_whitespace(cur.comp) } # Word diff is done in three steps: create an empty template vector structured # as the result of a call to `gregexpr` without matches, if dealing with # compliant atomic vectors in print mode, then update with the word diff # matches, finally, update with in-hunk word diffs for hunks that don't have # any existing word diffs: # Set up data lists with all relevant info; need to pass to diff_word so it # can be modified. # - orig: the very original string # - raw: the original captured text line by line, with strip_hz applied # - trim: as above, but with row meta data removed # - trim.ind: the indices used to re-insert `trim` into `raw` # - comp: the strings that will have the line diffs run on, these can be # modified to force a particular outcome, e.g. by word_to_line_map # - eq: the portion of `trim` that is equal post word-diff # - fin: the final character string for display to user # - word.ind: for use by `regmatches<-` to re-insert colored words # - tok.rat: for use by `align_eq` when lining up lines within hunks tar.dat <- list( orig=tar.capt, raw=tar.capt.p, trim=tar.trim, trim.ind.start=tar.trim.ind[, 1L], trim.ind.end=tar.trim.ind[, 2L], comp=tar.comp, eq=tar.comp, fin=tar.capt.p, fill=logical(length(tar.capt.p)), word.ind=replicate(length(tar.capt.p), .word.diff.atom, simplify=FALSE), tok.rat=rep(1, length(tar.capt.p)) ) cur.dat <- list( orig=cur.capt, raw=cur.capt.p, trim=cur.trim, trim.ind.start=cur.trim.ind[, 1L], trim.ind.end=cur.trim.ind[, 2L], comp=cur.comp, eq=cur.comp, fin=cur.capt.p, fill=logical(length(cur.capt.p)), word.ind=replicate(length(cur.capt.p), .word.diff.atom, simplify=FALSE), tok.rat=rep(1, length(cur.capt.p)) ) # Word diffs in wrapped form is atomic; note this will potentially change # the length of the vectors. tar.wrap.diff <- integer(0L) cur.wrap.diff <- integer(0L) tar.dat.w <- tar.dat cur.dat.w <- cur.dat if( is.atomic(target) && is.atomic(current) && is.null(dim(target)) && is.null(dim(current)) && length(tar.rh <- which_atomic_cont(tar.capt.p, target)) && length(cur.rh <- which_atomic_cont(cur.capt.p, current)) && is.null(names(target)) && is.null(names(current)) && etc@unwrap.atomic && etc@word.diff ) { # For historical compatibility we allow `diffChr` to get into this step if # the text format is right, even though it is arguable whether it should be # allowed or not. if(!all(diff(tar.rh) == 1L) || !all(diff(cur.rh)) == 1L){ # nocov start stop("Logic Error, row headers must be sequential; contact maintainer.") # nocov end } # Only do this for the portion of the data that actually matches up with # the atomic row headers. diff.word <- diff_word2( tar.dat, cur.dat, tar.ind=tar.rh, cur.ind=cur.rh, diff.mode="wrap", warn=warn, etc=etc ) warn <- !diff.word$hit.diffs.max tar.dat.w <- diff.word$tar.dat cur.dat.w <- diff.word$cur.dat # Mark the lines that were wrapped diffed; necessary b/c tar/cur.rh are # defined even if other conditions to get in this loop are not, and also # because the addition of the fill lines moves everything around # (effectively tar/cur.wrap.diff are the fill-offset versions of tar/cur.rh) tar.wrap.diff <- seq_along(tar.dat.w$fill)[!tar.dat.w$fill][tar.rh] cur.wrap.diff <- seq_along(cur.dat.w$fill)[!cur.dat.w$fill][cur.rh] } # Actual line diff diffs <- char_diff( tar.dat.w$comp, cur.dat.w$comp, etc=etc, diff.mode="line", warn=warn ) warn <- !diffs$hit.diffs.max hunks.flat <- diffs$hunks # For each of those hunks, run the word diffs and store the results in the # word.diffs list; bad part here is that we keep overwriting the overall # diff data for each hunk, which might be slow tar.dat.ww <- tar.dat.w cur.dat.ww <- cur.dat.w if(etc@word.diff) { # Word diffs on hunks, excluding all values that have already been wrap # diffed as in tar.rh and cur.rh / tar.wrap.diff and cur.wrap.diff for(h.a in hunks.flat) { if(h.a$context) next h.a.ind <- c(h.a$A, h.a$B) h.a.tar.ind <- setdiff(h.a.ind[h.a.ind > 0], tar.wrap.diff) h.a.cur.ind <- setdiff(abs(h.a.ind[h.a.ind < 0]), cur.wrap.diff) h.a.w.d <- diff_word2( tar.dat.ww, cur.dat.ww, h.a.tar.ind, h.a.cur.ind, diff.mode="hunk", warn=warn, etc=etc ) tar.dat.ww <- h.a.w.d[['tar.dat']] cur.dat.ww <- h.a.w.d[['cur.dat']] warn <- warn || !h.a.w.d[['hit.diffs.max']] } # Compute the token ratios tok_ratio_compute <- function(z) vapply( z, function(y) if(is.null(wc <- attr(y, "word.count"))) 1 else max(0, (wc - length(y)) / wc), numeric(1L) ) tar.dat.ww$tok.rat <- tok_ratio_compute(tar.dat.ww$word.ind) cur.dat.ww$tok.rat <- tok_ratio_compute(cur.dat.ww$word.ind) # Deal with mixed UTF/plain strings tar.dat.ww$word.ind <- fix_word_ind(tar.dat.ww$word.ind) cur.dat.ww$word.ind <- fix_word_ind(cur.dat.ww$word.ind) # Remove different words to make equal strings tar.dat.ww$eq <- with(tar.dat.ww, `regmatches<-`(trim, word.ind, value="")) cur.dat.ww$eq <- with(cur.dat.ww, `regmatches<-`(trim, word.ind, value="")) } # Instantiate result hunk.grps.raw <- group_hunks( hunks.flat, etc=etc, tar.capt=tar.dat.ww$raw, cur.capt=cur.dat.ww$raw ) gutter.dat <- etc@gutter max.w <- etc@text.width # Recompute line limit accounting for banner len, needed for correct trim etc.group <- etc if(etc.group@line.limit[[1L]] >= 0L) { etc.group@line.limit <- pmax(integer(2L), etc@line.limit - banner_len(etc@mode)) } # Trim hunks to the extent needed to make sure we fit in lines hunk.grps <- trim_hunks(hunk.grps.raw, etc.group, tar.dat.ww$raw, cur.dat.ww$raw) hunks.flat <- unlist(hunk.grps, recursive=FALSE) # Compact to width of widest element, so retrieve all char values; also # need to generate all the hunk headers b/c we need to use them in width # computation as well; under no circumstances are hunk headers allowed to # wrap as they are always assumed to take one line. # # Note: this used to be done after trimming / subbing, which is technically # better since we might have trimmed away long rows, but we need to do it # here so that we can can record the new text width in the outgoing object; # also, logic a bit circuitous b/c this was originally done elsewhere; might # be faster to use tar.dat and cur.dat directly chr.ind <- unlist(lapply(hunks.flat, "[", c("A", "B"))) chr.dat <- get_dat_raw(chr.ind, tar.dat.ww$raw, cur.dat.ww$raw) chr.size <- integer(length(chr.dat)) ranges <- vapply( hunks.flat, function(h.a) c(h.a$tar.rng.trim, h.a$cur.rng.trim), integer(4L) ) # compute ranges excluding fill lines rng_non_fill <- function(rng, fill) { if(!rng[[1L]]) rng else { rng.seq <- seq(rng[[1L]], rng[[2L]], by=1L) seq.not.fill <- rng.seq[!rng.seq %in% fill] if(!length(seq.not.fill)) { integer(2L) } else { range(seq.not.fill) } } } ranges.orig <- vapply( hunks.flat, function(h.a) { with( h.a, c( rng_non_fill(tar.rng.sub, which(tar.dat.ww$fill)), rng_non_fill(cur.rng.sub, which(cur.dat.ww$fill)) ) ) }, integer(4L) ) # We need a version of ranges that adjust for the fill lines that are counted # in the ranges but don't represent actual lines of output. This does mean # that adjusted ranges are not necessarily contiguous hunk.heads <- lapply(hunk.grps, make_hh, etc@mode, tar.dat.ww, cur.dat.ww, ranges.orig) h.h.chars <- nchar2( chr_trim( unlist(hunk.heads), etc@line.width, sgr.supported=etc@sgr.supported ), sgr.supported=etc@sgr.supported ) chr.size <- nchar2(chr.dat, sgr.supported=etc@sgr.supported) max.col.w <- max( max(0L, chr.size, .min.width + gutter.dat@width), h.h.chars ) max.w <- if(max.col.w < max.w) max.col.w else max.w # future calculations should assume narrower display etc@text.width <- max.w etc@line.width <- max.w + gutter.dat@width new( "Diff", diffs=hunk.grps, target=target, current=current, hit.diffs.max=!warn, tar.dat=tar.dat.ww, cur.dat=cur.dat.ww, etc=etc, hunk.heads=hunk.heads, trim.dat=attr(hunk.grps, 'meta') ) } diffobj/R/check.R0000755000176200001440000004145613442554134013300 0ustar liggesusers# Copyright (C) 2019 Brodie Gaslam # # This file is part of "diffobj - Diffs for R Objects" # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Go to for a copy of the license. is.less_flags <- function(x) is.chr.1L(x) && isTRUE(grepl("^[[:alpha:]]*$", x)) # for checking the limits, if successful returns an integer(2L) vector, # otherwise a character vector to sprintf as an error #' @include pager.R check_limit <- function(limit) { if( !is.numeric(limit) || any(is.na(limit)) || !length(limit) %in% 1:2 || !all(is.finite(limit)) || any(round(limit) != limit) || (length(limit) == 2L && diff(limit) > 0) ) { return( paste0( "Argument `%s` must be an integer vector of length 1 or 2 ", "and if length 2, with the first value larger than or equal to ", "the second%s" ) ) } limit <- as.integer(limit) if(length(limit) == 1L) limit <- rep(limit, 2L) limit } # requires a value to be a scalar character and match one of the provided # options string_in <- function(x, valid.x) is.chr.1L(x) && x %in% valid.x # Simple validation functions is.int.1L <- function(x) is.numeric(x) && length(x) == 1L && !is.na(x) && all(x == round(x)) && is.finite(x) is.int.2L <- function(x) is.numeric(x) && length(x) == 2L && !anyNA(x) && all(x == round(x)) && all(is.finite(x)) is.TF <- function(x) isTRUE(x) || identical(x, FALSE) is.chr.1L <- function(x) is.character(x) && length(x) == 1L && !is.na(x) is.valid.palette.param <- function(x, param, palette) { stopifnot(is(palette, "PaletteOfStyles")) stopifnot(isTRUE(param %in% c("brightness", "color.mode"))) valid.formats <- dimnames(palette@data)$format valid.params <- dimnames(palette@data)[[param]] if(!is.character(x) || anyNA(x)) paste0("Argument `", param, "` must be character and not contain NAs") else if(!all(x %in% valid.params)) paste0( "Argument `", param, "` may only contain values in `", dep(valid.params), "`" ) else if( (length(x) > 1L && is.null(names(x))) || !all(names(x) %in% c("", valid.formats)) ) paste0( "Argument `", param, "` must have names if it has length > 1, and those ", "names must include at least an empty name `\"\"` as well as names only ", "from `", dep(valid.formats), "`." ) else if ((!is.null(names(x)) && !"" %in% names(x))) paste0( "Argument `", param, "` must include at least one empty name `\"\"` if ", "it has names." ) else TRUE } is.one.arg.fun <- function(x) { if(!is.function(x)) { "is not a function" } else if(length(formals(x)) < 1L) { "does not have at least one arguments" } else if("..." %in% names(formals(x))[1]) { "cannot have `...` as the first argument" } else { nm.forms <- vapply(formals(x), is.name, logical(1L)) forms.chr <- character(length(nm.forms)) forms.chr[nm.forms] <- as.character(formals(x)[nm.forms]) forms.names <- names(formals(x)) if(any(tail(!nzchar(forms.chr) & nm.forms & forms.names != "...", -1L))) "cannot have any non-optional arguments other than first one" else TRUE } } is.valid.guide.fun <- is.two.arg.fun <- function(x) { if(!is.function(x)) { "is not a function" } else if(length(formals(x)) < 2L) { "does not have at least two arguments" } else if("..." %in% names(formals(x))[1:2]) { "cannot have `...` as one of the first two arguments" } else { nm.forms <- vapply(formals(x), is.name, logical(1L)) forms.chr <- character(length(nm.forms)) forms.chr[nm.forms] <- as.character(formals(x)[nm.forms]) if(any(tail(!nzchar(forms.chr) & nm.forms, -2L))) "cannot have any non-optional arguments other than first two" else TRUE } } is.valid.width <- function(x) if(!is.int.1L(x) || (x != 0L && (x < 10L || x > 10000))) { "must be integer(1L) and 0, or between 10 and 10000" } else TRUE is.one.file.name <- function(x) { if(!is.chr.1L(x)) { "must be character(1L) and not NA" } else if(!file_test("-f", x)) { sprintf("(\"%s\") is not a file", x) } else TRUE } is.non.obj.style <- function(x) string_in(x, "auto") || (is.list(x) && !is.object(x)) # Things that could possibly be output by substitute is.possibly.substituted <- function(x) (is.atomic(x) && length(x) == 1L) || is.null(x) || is.name(x) || is.call(x) # Checks common arguments across functions check_args <- function( call, tar.exp, cur.exp, mode, context, line.limit, format, brightness, color.mode, pager, ignore.white.space, max.diffs, align, disp.width, hunk.limit, convert.hz.white.space, tab.stops, style, palette.of.styles, frame, tar.banner, cur.banner, guides, rds, trim, word.diff, unwrap.atomic, extra, interactive, term.colors, strip.sgr, sgr.supported, call.match ) { err <- make_err_fun(call) warn <- make_warn_fun(call) # Check for conflicting arguments formals <- tail(names(call.match), -1L) style.overrideable <- c("format", "brightness", "color.mode") if( "style" %in% formals && !is.non.obj.style(style) && any(s.ov <- style.overrideable %in% formals) ) warn( "Provided `style` argument will override the provided ", if(sum(s.ov) < 2L) { sprintf("`%s` argument", style.overrideable[s.ov]) } else { paste0( paste0( sprintf("`%s`", head(style.overrideable[s.ov], -1L)), collapse=", " ), " and `", tail(style.overrideable[s.ov], 1L), "` arguments." ) } ) # Check extra if(!is.list(extra)) err("Argument `extra` must be a list.") # Check context msg.base <- paste0( "Argument `%s` must be integer(1L) and not NA, an object produced ", "by `auto_context`, or \"auto\"." ) if( !is.int.1L(context) && !is(context,"AutoContext") && !identical(context, "auto") ) err(sprintf(msg.base, "context")) if(!is(context, "AutoContext")) { context <- if(identical(context, "auto")) auto_context() else { if(is.int.1L(context) && context < 0) { min.cont <- 0 max.cont <- -1 } else if (is.int.1L(context)) { min.cont <- max.cont <- as.integer(context) } else { err("Argument `context` must be integer(1L) and not NA.") } cont <- try(auto_context(min.cont, max.cont)) if(inherits(cont, "try-error")) # nocov start # should not be possible to get here given prior checks err( "Unable to instantiate an `AutoContext` object from provided ", "`context` argument. Value should be integer(1L) and not NA, or ", "an `AutoContext` object as generated by `auto_context()`." ) # nocov end cont } } # any 'substr' of them otherwise these checks fail val.modes <- c("auto", "unified", "context", "sidebyside") fail.mode <- FALSE if(!is.character(mode) || length(mode) != 1L || is.na(mode) || !nzchar(mode)) fail.mode <- TRUE if(!fail.mode && !any(mode.eq <- substr(val.modes, 1, nchar(mode)) == mode)) fail.mode <- TRUE if(fail.mode) err( "Argument `mode` must be character(1L) and in `", deparse(val.modes), "`." ) # Tab stops tab.stops <- as.integer(tab.stops) if( !is.integer(tab.stops) || !length(tab.stops) >= 1L || anyNA(tab.stops) || !all(tab.stops > 0L) ) err( "Argument `tab.stops` must be integer containing at least one value and ", "with all values strictly positive" ) # Limit vars hunk.limit <- check_limit(hunk.limit) if(!is.integer(hunk.limit)) err(sprintf(hunk.limit, "hunk.limit", ".")) if(!is.integer(line.limit <- check_limit(line.limit))) err( sprintf( line.limit, "line.limit", ", or \"auto\" or the result of calling `auto_line_limit`" ) ) # guides if(!is.TF(guides) && !is.function(guides)) err("Argument `guides` must be TRUE, FALSE, or a function") if(is.function(guides) && !isTRUE(g.f.err <- is.two.arg.fun(guides))) err("Argument `guides` ", g.f.err) if(!is.function(guides) && !guides) guides <- function(obj, obj.as.chr) integer(0L) if(!is.TF(trim) && !is.function(trim)) err("Argument `trim` must be TRUE, FALSE, or a function") if(is.function(trim) && !isTRUE(t.f.err <- is.two.arg.fun(trim))) err("Argument `trim` ", t.f.err) if(!is.function(trim) && !trim) trim <- trim_identity # check T F args if(is.null(interactive)) interactive <- interactive() TF.vars <- c( "ignore.white.space", "convert.hz.white.space", "rds", "word.diff", "unwrap.atomic", "interactive" ) msg.base <- "Argument `%s` must be TRUE or FALSE." for(x in TF.vars) if(!is.TF(get(x, inherits=FALSE))) err(sprintf(msg.base, x)) # int 1L vars if(is.null(term.colors)) term.colors <- crayon::num_colors() msg.base <- "Argument `%s` must be integer(1L) and not NA." int.1L.vars <- c("max.diffs", "term.colors") for(x in int.1L.vars) { if(!is.int.1L(int.val <- get(x, inherits=FALSE))) err(sprintf(msg.base, "max.diffs")) assign(x, as.integer(int.val)) } # Banners; convolution here is to accomodate `diffObj` and have it be able # to pass captured target/current expressions chr1LorNULLorLanguage.vars <- c("tar.banner", "cur.banner") msg.base <- "Argument `%s` must be atomic and length(1L), NULL, a symbol, or a call" for(x in chr1LorNULLorLanguage.vars ) { y <- get(x, inherits=FALSE) if(!is.possibly.substituted(y)) err(sprintf(msg.base, x)) } if(!is.chr.1L(tar.banner) && !is.null(tar.banner)) { tar.exp <- tar.banner tar.banner <- NULL } if(!is.chr.1L(cur.banner) && !is.null(cur.banner)) { cur.exp <- cur.banner cur.banner <- NULL } # Align threshold if(!is(align, "AlignThreshold")) { align <- if( is.numeric(align) && length(align) == 1L && !is.na(align) && align %bw% c(0, 1) ) { AlignThreshold(threshold=align) } else if(is.null(align)) { AlignThreshold() } else err( "Argument `align` must be an \"AlignThreshold\" object or numeric(1L) ", "and between 0 and 1." ) } # style valid_object(style, "style", err) if( !is(style, "Style") && !string_in(style, "auto") && !(is.list(style) && !is.object(style)) ) err("Argument `style` must be \"auto\", a `Style` object, or a list.") # pager; 'on' just means use pager already associated with style valid_object(pager, "pager", err) valid.pagers <- c("auto", "off", "on") if( !is(pager, "Pager") && !string_in(pager, valid.pagers) && !(is.list(pager) && !is.object(pager)) ) err( "Argument `pager` must be one of `", dep(valid.pagers), "`, a `Pager` object, or a list." ) pager.args <- list() if(!is(pager, "Pager")) { if(string_in(pager, "off")) { pager <- PagerOff() } else if (is.list(pager)) { pager.args <- pager pager <- "on" } } # palette and arguments that reference palette dimensions if(is.null(palette.of.styles)) palette.of.styles <- PaletteOfStyles() if(!is(palette.of.styles, "PaletteOfStyles")) err("Argument `palette.of.styles` must be a `PaletteOfStyles` object.") palette.params <- c("brightness", "color.mode") for(x in palette.params) if( !isTRUE( msg <- is.valid.palette.param( get(x, inherits=FALSE), x, palette.of.styles ) ) ) err(msg) # Figure out whether pager is allowable or not; note that "auto" pager just # means let the pager that comes built into the style be the pager if(is.character(pager)) pager <- if( (pager == "auto" && interactive) || pager == "on" ) { "on" } else PagerOff() # format; decide what format to use if( !is(style, "Style") && ( string_in(style, "auto") || (is.list(style) && !is.object(style)) ) ) { if(is.list(style)) { style.args <- style style <- "auto" } else style.args <- list() # We only want to allow ansi styles if the pager supports them too; # unfortuantely we cannot have different styles depending on whether the # output is paged or not, at least not at this time pager.could.be.ansi <- if(is(pager, "Pager")) pager@ansi else FALSE if(!is.chr.1L(format)) err("Argument `format` must be character(1L) and not NA") valid.formats <- c("auto", dimnames(palette.of.styles@data)$format) if(!format %in% valid.formats) err("Argument `format` must be one of `", dep(valid.formats) , "`.") if(format == "auto") { if(!is.int.1L(term.colors)) # nocov start err( "Logic Error: unexpected return from `crayon::num_colors()`; ", "contact maintainer." ) # nocov end # No recognized color alternatives, try to use HTML if we can format <- if( nzchar(Sys.getenv('RSTUDIO')) && !nzchar(Sys.getenv('RSTUDIO_TERM')) && interactive ) { "html" } else if( term.colors < 8 ) { if(!pager.could.be.ansi) { if( (interactive && identical(pager, "on")) || is(pager, "PagerBrowser") ) "html" else "raw" } else { if(!pager@threshold) "ansi8" else "raw" } } else if (term.colors < 256) { "ansi8" } else if (term.colors >= 256) { "ansi256" } else stop("Logic error: unhandled format; contact maintainer.") # nocov } style <- palette.of.styles[[ format, get_pal_par(format, brightness), get_pal_par(format, color.mode) ]] if(is(style, "classRepresentation")) { style <- try(do.call("new", c(list(style), style.args)), silent=TRUE) if(inherits(style, "try-error")) { msg <- conditionMessage(attr(style, "condition")) err("Unable to instantiate `Style` object: ", msg) } } else { if(length(style.args)) { warn( "Extra `style` arguments cannot be applied because selected object ", "`palette.of.styles` is a `Style` instance rather than a `Style` ", "\"classRepresentation\". See documentation for the `style` ", "parameter for details." ) } valid_object( style, "palette.of.styles", err, paste0( "Argument `%s` is an invalid `%s` because it contains and invalid ", "`Style` object:" ) ) } } else if(!is(style, "Style")) stop("Logic Error: unexpected style state; contact maintainer.") # nocov # Attach specific pager if it was requested generated; if "on" just let the # existing pager on the style be, which is done by not modifying @pager if(is(pager, "Pager")) { style@pager <- pager } else if (length(pager.args)) { ## this is a bit gnarly, and pager.s <- style@pager old.slots <- sapply(slotNames(pager.s), slot, object=pager.s, simplify=FALSE) pager.args <- c(pager.args, old.slots[setdiff(names(old.slots), names(pager.args))]) style@pager <- do.call("new", c(list(class(pager.s)), pager.args)) } else if(!identical(pager, "on")) stop("Logic Error: Unexpected pager state; contact maintainer.") # nocov # Check display width if(!isTRUE(d.w.err <- is.valid.width(disp.width))) err("Arugment `disp.width` ", d.w.err) disp.width <- as.integer(disp.width) if(disp.width) { style@disp.width <- disp.width } else if(!style@disp.width) { d.w <- getOption("width") if(!is.valid.width(d.w)) { # nocov start this should never happen warn("`getOption(\"width\") returned an invalid width, using 80L") d.w <- 80L # nocov end } style@disp.width <- d.w } disp.width <- style@disp.width # check strip.sgr if(!is.TF(strip.sgr) && !is.null(strip.sgr)) err("Argument `strip.sgr` must be TRUE, FALSE, or NULL") if(is.null(strip.sgr)) strip.sgr <- is(style, "Ansi") # check strip.sgr if(!is.TF(sgr.supported) && !is.null(sgr.supported)) err("Argument `sgr.supported` must be TRUE, FALSE, or NULL") if(is.null(sgr.supported)) sgr.supported <- is(style, "Ansi") || crayon::has_color() # instantiate settings object etc <- new( "Settings", mode=val.modes[[which(mode.eq)]], context=context, line.limit=line.limit, ignore.white.space=ignore.white.space, max.diffs=max.diffs, align=align, disp.width=disp.width, hunk.limit=hunk.limit, convert.hz.white.space=convert.hz.white.space, tab.stops=tab.stops, style=style, frame=frame, tar.exp=tar.exp, cur.exp=cur.exp, guides=guides, tar.banner=tar.banner, cur.banner=cur.banner, trim=trim, word.diff=word.diff, unwrap.atomic=unwrap.atomic, strip.sgr=strip.sgr, sgr.supported=sgr.supported, err=err, warn=warn ) etc } diffobj/R/hunks.R0000755000176200001440000005726413442554134013357 0ustar liggesusers# Copyright (C) 2019 Brodie Gaslam # # This file is part of "diffobj - Diffs for R Objects" # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Go to for a copy of the license. # Convert ses data into raw hunks that include both match hunks as well as # actual hunks # # These hunks are then processed into hunk groups in a separate step # (see `group_hunks`). # # @return a list of atomic hunks, each containing integer vectors A and B where # positive numbers reference character lines from target and negative ones # from current. For "context" and "sidebyside" mode the A vector will contain # the lines from target, and the B vector the lines from current. For # "unified" only the A vector is populated. In addition to the A and B # vectors some other meta data is tracked, such as the range of the hunks is # also stored as tar.rng and cur.rng; mostly inferrable from the actual data # in the hunks, except that in unified mode we no longer have the actual # context strings from the `current` vector. # # starting to have second thoughts about removing all the non index data from # hunks, particularly because it makes the line length calc a pita. setGeneric("as.hunks", function(x, etc, ...) standardGeneric("as.hunks")) setMethod("as.hunks", c("MyersMbaSes", "Settings"), function( x, etc, ... ) { # Split our data into sections that have either deletes/inserts or matches dat <- as.matrix(x) sects <- unique(dat[, "section"]) j <- 0L res.l <- if(!nrow(dat)) { # Minimum one empty hunk if nothing; make this a context hunk to indicate # that there are no differences. This used to be a non-context hunk list( list( id=1L, A=integer(0L), B=integer(0L), context=TRUE, guide=FALSE, tar.rng=integer(2L), cur.rng=integer(2L), tar.rng.sub=integer(2L), cur.rng.sub=integer(2L), tar.rng.trim=integer(2L), cur.rng.trim=integer(2L), completely.empty=TRUE ) ) } else { lapply( seq_along(sects), function(i) { s <- sects[i] d <- dat[which(dat[, "section"] == s), , drop=FALSE] d.del <- d[which(.edit.map[d[, "type"]] == "Delete"), ,drop=FALSE] d.ins <- d[which(.edit.map[d[, "type"]] == "Insert"), ,drop=FALSE] d.mtc <- d[which(.edit.map[d[, "type"]] == "Match"), ,drop=FALSE] del.len <- sum(d.del[, "len"]) ins.len <- sum(d.ins[, "len"]) mtc.len <- sum(d.mtc[, "len"]) tar.len <- del.len + mtc.len cur.len <- ins.len + mtc.len # atomic hunks may only be del/ins or match, not both if((del.len || ins.len) && mtc.len || !(del.len + ins.len + mtc.len)) stop("Logic Error: unknown edit types; contact maintainer.") # nocov # Figure out where previous hunk left off del.last <- if(nrow(d.del)) d.del[1L, "last.a"] else d[1L, "last.a"] ins.last <- if(nrow(d.ins)) d.ins[1L, "last.b"] else d[1L, "last.b"] A.start <- unname(del.last) B.start <- unname(ins.last) # record `cur` indices as negatives tar <- seq_len(tar.len) + A.start cur <- -(seq_len(cur.len) + B.start) context <- !!mtc.len A <- switch( etc@mode, context=tar, unified=c(tar, if(!context) cur), sidebyside=tar, stop("Logic Error: unknown mode; contact maintainer.") ) B <- switch( etc@mode, context=cur, unified=integer(), sidebyside=cur, stop("Logic Error: unknown mode; contact maintainer.") ) # compute ranges tar.rng <- cur.rng <- integer(2L) if(tar.len) tar.rng <- c(A.start + 1L, A.start + tar.len) if(cur.len) cur.rng <- c(B.start + 1L, B.start + cur.len) list( id=i, A=A, B=B, context=context, guide=FALSE, tar.rng=tar.rng, cur.rng=cur.rng, tar.rng.sub=tar.rng, cur.rng.sub=cur.rng, tar.rng.trim=tar.rng, cur.rng.trim=cur.rng, completely.empty=FALSE ) } ) } res.l } ) # Group hunks together based on context, in "auto" mode we find the context # that maximizes lines displayed while adhering to line and hunk limits # Definitely not very efficient since we re-run code multiple times we # probably don't need to. # # Important: context atomic hunks are duplicated anytime there is enough # context that we only show part of the context hunk. # # @return a list containing lists of atomic hunks. Each of these sub-lists # of atomic hunks is treated as a "hunk", but is really a combination of # context and hunks which we will refer to as "hunk group". In each hunk # group, There may be as little as one hunk with no context, or many hunks and # context if the context between hunks is not sufficient to meet the requested # context, in which case the hunks bleed together forming these hunk groups. group_hunks <- function(hunks, etc, tar.capt, cur.capt) { context <- etc@context line.limit <- etc@line.limit ctx.val <- if(is(context, "AutoContext")) { len <- diff_line_len( p_and_t_hunks(hunks, ctx.val=context@max, etc=etc), etc=etc, tar.capt=tar.capt, cur.capt=cur.capt ) len.min <- diff_line_len( p_and_t_hunks(hunks, ctx.val=context@min, etc=etc), etc=etc, tar.capt=tar.capt, cur.capt=cur.capt ) if(line.limit[[1L]] < 0L) { context@max } else if(len.min > line.limit[[1L]]) { context@min } else { ctx.max <- ctx.hi <- ctx <- context@max ctx.lo <- context@min safety <- 0L repeat { if((safety <- safety + 1L) > ctx.max) # nocov start stop( "Logic Error: stuck trying to find auto-context; contact ", "maintainer." ) # nocov end if(len > line.limit[[1L]] && ctx - ctx.lo > 1L) { ctx.hi <- ctx ctx <- as.integer((ctx - ctx.lo) / 2) } else if (len < line.limit[[1L]] && ctx.hi - ctx > 1L) { ctx.lo <- ctx ctx <- ctx + as.integer(ceiling(ctx.hi - ctx) / 2) } else if (len > line.limit[[1L]]) { # unable to get something small enough, but we know min context # works from inital test ctx <- context@min break } else if (len <= line.limit[[1L]]) { break } len <- diff_line_len( p_and_t_hunks(hunks, ctx.val=ctx, etc=etc), etc=etc, tar.capt=tar.capt, cur.capt=cur.capt ) } ctx } } else context res <- process_hunks(hunks, ctx.val=ctx.val, etc=etc) res } # process the hunks and also drop off groups that exceed limit # # used exclusively when we are trying to auto-calculate context p_and_t_hunks <- function(hunks.raw, ctx.val, etc) { c.all <- process_hunks(hunks.raw, ctx.val, etc) hunk.limit <- etc@hunk.limit if(hunk.limit[[1L]] >= 0L && length(c.all) > hunk.limit[[1L]]) c.all <- c.all[seq_along(hunk.limit[[2L]])] c.all } # Subset hunks; should only ever be subsetting context hunks hunk_sub <- function(hunk, op, n) { stopifnot( op %in% c("head", "tail"), hunk$context, all(hunk$tar.rng.sub), length(hunk$tar.rng.sub) == length(hunk$cur.rng.sub), diff(hunk$tar.rng.sub) == diff(hunk$cur.rng.sub), length(hunk$tar.rng.sub) == 2L ) hunk.len <- diff(hunk$tar.rng.sub) + 1L len.diff <- hunk.len - n if(len.diff >= 0) { nm <- c("A", "B", "A.tok.ratio", "B.tok.ratio") hunk[nm] <- lapply(hunk[nm], op, n) # Need to recompute ranges if(n) { if(op == "tail") { hunk$tar.rng.trim[[1L]] <- hunk$tar.rng.sub[[1L]] <- hunk$tar.rng.sub[[1L]] + len.diff hunk$cur.rng.trim[[1L]] <- hunk$cur.rng.sub[[1L]] <- hunk$cur.rng.sub[[1L]] + len.diff } else { hunk$tar.rng.trim[[2L]] <- hunk$tar.rng.sub[[2L]] <- hunk$tar.rng.sub[[2L]] - len.diff hunk$cur.rng.trim[[2L]] <- hunk$cur.rng.sub[[2L]] <- hunk$cur.rng.sub[[2L]] - len.diff } } else { hunk$tar.rng.trim <- hunk$cur.rng.trim <- hunk$tar.rng.sub <- hunk$cur.rng.sub <- integer(2L) } } hunk } # Figure Out Context for Each Chunk # # If a hunk bleeds into another due to context then it becomes part of the # other hunk. # # This will group atomic hunks into hunk groups with matching line in excess of # context removed. process_hunks <- function(x, ctx.val, etc) { context <- ctx.val ctx.vec <- vapply(x, "[[", logical(1L), "context") if(!all(abs(diff(ctx.vec)) == 1L)) # nocov start stop( "Logic Error: atomic hunks not interspersing context; contact maintainer." ) # nocov end hunk.len <- length(x) # Special cases, including only one hunk or forcing only one hunk group, or # no differences if(context < 0L || hunk.len < 2L || !any(ctx.vec)) { res.l <- list(x) } else { # Normal cases; allocate maximum possible number of elements, may need fewer # if hunks bleed into each other res.l <- vector("list", sum(!ctx.vec)) # Jump through every second value as those are the mismatch hunks, though # first figure out if first hunk is mismatching, and merge hunks. This # is likely not super efficient as we keep growing a list, though the only # thing we are actually re-allocating is the list index really, at least if # R is being smart about not copying the list contents (which as of 3.1 I # think it is...) i <- if(ctx.vec[[1L]]) 2L else 1L j <- 1L while(i <= hunk.len) { # Merge left res.l[[j]] <- if(i - 1L) list(hunk_sub(x[[i - 1L]], "tail", context), x[[i]]) else x[i] # Merge right if(i < hunk.len) { # Hunks bleed into next hunk due to context; note that i + 1L will always # be a context hunk, so $A is fully representative while( i < hunk.len && length(x[[i + 1L]]$A) <= context * 2 && i + 1L < length(x) ) { res.l[[j]] <- append(res.l[[j]], x[i + 1L]) if(i < hunk.len - 1L) res.l[[j]] <- append(res.l[[j]], x[i + 2L]) i <- i + 2L } # Context enough to cause a break if(i < hunk.len) { res.l[[j]] <- append( res.l[[j]], list(hunk_sub(x[[i + 1L]], "head", context)) ) } } j <- j + 1L i <- i + 2L } length(res.l) <- j - 1L } # Add back the guide hunks if needed they didn't make it in as part of the # context or differences. It should be the case that the only spot that could # have missing hunk guides is the first hunk in a hunk group if it is a # context hunk # First, determine which guides if any need to be added back; need to do it # first because it is possible that a guide is present at the end context # of the prior hunk group # Helper fun to pull out indices of guide.lines get_guides <- function(hunk, rows, mode) { stopifnot(hunk$context) rng <- hunk[[sprintf("%s.rng", mode)]] rng.sub <- hunk[[sprintf("%s.rng.sub", mode)]] h.rows <- rows[which(!rows %bw% rng.sub & rows %bw% rng)] # If context hunk already contains guide row and there is a non guide at # beginning of hunk, then we don't need to return a guide row if(any(rows %bw% rng.sub) && !rng.sub[[1L]] %in% rows) { integer(0L) } else { # special case where the first row in the subbed hunk is a context row; # note we need to look at the first non-blank row; since this has to be # a context hunk we can just look at A.chr first.is.guide <- FALSE if(rng.sub[[1L]] %in% rows) { first.is.guide <- TRUE h.rows <- c(h.rows, rng.sub[[1L]]) } # we want all guide.lines that abut the last matched guide row if(length(h.rows)) { h.fin <- h.rows[seq(to=max(h.rows), length.out=length(h.rows)) == h.rows] if(first.is.guide) h.fin <- head(h.fin, -1L) # convert back to indeces relative to hunk h.fin - rng[[1L]] + 1L } else integer(0L) } } for(k in seq_along(res.l)) { if(length(res.l[[k]]) && res.l[[k]][[1L]]$context) { h <- res.l[[k]][[1L]] h.o <- x[[res.l[[k]][[1L]]$id]] # retrieve original untrimmed hunk if(! identical( h$tar.rng.sub, h$cur.rng.sub - h$cur.rng.sub[1L] + h$tar.rng.sub[1L] ) ) stop("Logic Error: unequal context hunks; contact mainainer") # nocov # since in a context hunk, everything in tar and cur is the same, so # we just need to recompute the `cur` guidelines relative to tar indices # since the guidelines need not be the same (e.g., in lists that are # mostly the same, but deeper in one object, guideline will be deepest # index entry, which will be different. tar.cand.guides <- intersect( etc@guide.lines@target, seq(h$tar.rng[1L], h$tar.rng[2L], by=1L) ) cur.cand.guides <- intersect( etc@guide.lines@current, seq(h$cur.rng[1L], h$cur.rng[2L], by=1L) ) - h$cur.rng[1L] + h$tar.rng[1L] h.guides <- get_guides( h, unique(c(tar.cand.guides, cur.cand.guides)), "tar" ) if(length(h.guides)) { h.h <- hunk_sub(h.o, "head", max(h.guides)) tail.ind <- if(length(h.guides) == 1L) 1L else diff(range(h.guides)) + 1L h.fin <- hunk_sub(h.h, "tail", tail.ind) h.fin$guide <- TRUE res.l[[k]] <- c(list(h.fin), res.l[[k]]) } } } # Finalize, including sizing correctly, and setting the ids to the right # values since we potentially duplicated some context hunks res.fin <- res.l k <- 1L for(i in seq_along(res.fin)) { for(j in seq_along(res.fin[[i]])) { res.fin[[i]][[j]][["id"]] <- k k <- k + 1L } } res.fin } # Account for overhead / side by sideness in width calculations # Internal funs hunk_len <- function(hunk.id, hunks, tar.capt, cur.capt, etc) { disp.width <- etc@disp.width mode <- etc@mode hunk <- hunks[[hunk.id]] A.lines <- nlines(get_dat_raw(hunk$A, tar.capt, cur.capt), disp.width, mode, etc) B.lines <- nlines(get_dat_raw(hunk$B, tar.capt, cur.capt), disp.width, mode, etc) # Depending on each mode, figure out how to set up the lines; # straightforward except for context where we need to account for the # fact that all the A of a hunk group are shown first, and then all # the B are shown lines.out <- switch( mode, context=c(A.lines, if(!hunk$guide) -B.lines), unified=c(A.lines), sidebyside={ max.len <- max(length(A.lines), length(B.lines)) length(A.lines) <- length(B.lines) <- max.len c(pmax(A.lines, B.lines, na.rm=TRUE)) }, stop("Logic Error: unknown mode '", mode, "' contact maintainer") ) # Make sure that line.id refers to the position of the line in either # original A or B vector l.o.len <- length(lines.out) line.id <- integer(l.o.len) l.gt.z <- lines.out > 0L l.gt.z.w <- which(l.gt.z) line.id[l.gt.z.w] <- seq_along(l.gt.z.w) l.lt.z.w <- which(!l.gt.z) line.id[l.lt.z.w] <- seq_along(l.lt.z.w) cbind( hunk.id=if(length(lines.out)) hunk.id else integer(), line.id=unname(line.id), len=lines.out ) } hunk_grp_len <- function( hunk.grp.id, hunk.grps, etc, tar.capt, cur.capt ) { mode <- etc@mode hunks <- hunk.grps[[hunk.grp.id]] hunks.proc <- lapply( seq_along(hunks), hunk_len, hunks=hunks, etc=etc, tar.capt=tar.capt, cur.capt=cur.capt ) res.tmp <- do.call(rbind, hunks.proc) res <- cbind(grp.id=if(nrow(res.tmp)) hunk.grp.id else integer(0L), res.tmp) # Need to make sure all positives are first, and all negatives second, if # there are negatives (context mode); also, if the first hunk in a hunk # group, add a line for the hunk header, though hunk header itself is added # later extra <- if(length(hunks)) 1L else 0L if(identical(mode, "context")) res <- res[order(res[, "len"] < 0L), , drop=FALSE] if( identical(mode, "context") && length(negs <- which(res[, "len"] < 0L)) && length(poss <- which(res[, "len"] > 0L)) ) { # Add one for hunk header, one for context separator; remember, that lengths # in the B hunk are counted negatively res[1L, "len"] <- res[1L, "len"] + extra res[negs[[1L]], "len"] <- res[negs[[1L]], "len"] - extra } else if(nrow(res)) { res[1L, "len"] <- res[1L, "len"] + extra } res } # Compute how many lines the display version of the diff will take, meta # lines (used for hunk guides) are denoted by negatives # # count lines for each remaining hunk and figure out if we need to cut some # hunks off; note that "negative" lengths indicate the lines being counted # originated from the B hunk in context mode get_hunk_chr_lens <- function(hunk.grps, etc, tar.capt, cur.capt) { mode <- etc@mode disp.width <- etc@disp.width # Generate a matrix with hunk group id, hunk id, and wrapped length of each # line that we can use to figure out what to show do.call( rbind, lapply( seq_along(hunk.grps), hunk_grp_len, etc=etc, tar.capt=tar.capt, cur.capt=cur.capt, hunk.grps=hunk.grps ) ) } # Compute total diff length in lines diff_line_len <- function(hunk.grps, etc, tar.capt, cur.capt) { max( 0L, cumsum( get_hunk_chr_lens( hunk.grps, etc=etc, tar.capt=tar.capt, cur.capt=cur.capt )[, "len"] ) ) + banner_len(etc@mode) } # completely.empty used to highlight difference between hunks that technically # contain a header and no data vs those that can't even contain a header; # unfortunately a legacy of poor design choice in how headers are handled empty_hunk_grp <- function(h.g) { for(j in seq_along(h.g)) { h.g[[j]][c("tar.rng.trim", "cur.rng.trim")] <- list(integer(2L), integer(2L)) h.g[[j]]$completely.empty <- TRUE } h.g } # Remove hunk groups and atomic hunks that exceed the line limit # # Return value is a hunk group list, with an attribute indicating how many # hunks and lines were trimmed trim_hunk <- function(hunk, type, line.id) { stopifnot(type %in% c("tar", "cur")) rng.idx <- sprintf("%s.rng.trim", type) hunk[[rng.idx]] <- if(!line.id) integer(2L) else { if(all(hunk[[rng.idx]])) { c( hunk[[rng.idx]][[1L]], min(hunk[[rng.idx]][[1L]] + line.id - 1L, hunk[[rng.idx]][[2L]]) ) } else integer(2L) } hunk } trim_hunks <- function(hunk.grps, etc, tar.raw, cur.raw) { stopifnot(is(etc, "Settings")) mode <- etc@mode disp.width <- etc@disp.width hunk.limit <- etc@hunk.limit line.limit <- etc@line.limit diffs.orig <- count_diffs(hunk.grps) hunk.grps.count <- length(hunk.grps) if(hunk.limit[[1L]] < 0L) hunk.limit <- rep(hunk.grps.count, 2L) hunk.limit.act <- if(hunk.grps.count > hunk.limit[[1L]]) hunk.limit[[2L]] hunk.grps.omitted <- max(0L, hunk.grps.count - hunk.limit.act) hunk.grps.used <- min(hunk.grps.count, hunk.limit.act) hunk.grps <- hunk.grps[seq_len(hunk.grps.used)] lines <- get_hunk_chr_lens( hunk.grps, etc=etc, tar.capt=tar.raw, cur.capt=cur.raw ) cum.len <- cumsum(abs(lines[, "len"])) cut.off <- -1L lines.omitted <- 0L lines.total <- max(0L, tail(cum.len, 1L)) if(line.limit[[1L]] < 0L) { cut.off <- max(0L, cum.len) } else if(any(cum.len > line.limit[[1L]])) { cut.off <- max(0L, cum.len[cum.len <= line.limit[[2L]]]) } if(cut.off > 0) { lines.omitted <- lines.total - cut.off cut.dat <- lines[max(which(cum.len <= cut.off)), ] grp.cut <- cut.dat[["grp.id"]] hunk.cut <- cut.dat[["hunk.id"]] line.cut <- cut.dat[["line.id"]] line.neg <- cut.dat[["len"]] < 0 # completely trim hunks that will not be shown grps.to.cut <- setdiff(seq_along(hunk.grps), seq_len(grp.cut)) for(i in grps.to.cut) hunk.grps[[i]] <- empty_hunk_grp(hunk.grps[[i]]) hunk.grps.used <- grp.cut hunk.grps.omitted <- max(0L, hunk.grps.count - grp.cut) # Remove excess lines from the atomic hunks based on the limits; we don't # update the ranges as those should still indicate what the original # untrimmed range was # special case for first hunk in group since we need to account for hunk # header that takes up a line; this is not ideal, hunk header should be # made part of hunks eventually if(mode == "context") { # Context tricky because every atomic hunk B data is displayed after all # the A data for(i in seq_along(hunk.grps[[grp.cut]])) { hunk.atom <- hunk.grps[[grp.cut]][[i]] if(!line.neg) { # means all B blocks must be dropped hunk.atom <- trim_hunk(hunk.atom, "cur", 0L) if(i > hunk.cut) { hunk.atom <- trim_hunk(hunk.atom, "tar", 0L) } else if (i == hunk.cut) { hunk.atom <- trim_hunk(hunk.atom, "tar", line.cut) } } else { if(i > hunk.cut) { hunk.atom <- trim_hunk(hunk.atom, "cur", 0L) } else if (i == hunk.cut) { hunk.atom <- trim_hunk(hunk.atom, "cur", line.cut) } } hunk.grps[[grp.cut]][[i]] <- hunk.atom } } else { hunk.atom <- hunk.grps[[grp.cut]][[hunk.cut]] hunk.atom <- trim_hunk(hunk.atom, "tar", line.cut) if(mode == "unified") { # Need to share lines between tar and cur in unified mode line.cut <- max( 0L, line.cut - if(any(hunk.atom$tar.rng)) diff(hunk.atom$tar.rng) + 1L else 0L ) } hunk.atom <- trim_hunk(hunk.atom, "cur", line.cut) hunk.grps[[grp.cut]][[hunk.cut]] <- hunk.atom null.hunks <- seq_len(length(hunk.grps[[grp.cut]]) - hunk.cut) + hunk.cut hunk.grps[[grp.cut]][null.hunks] <- lapply( hunk.grps[[grp.cut]][null.hunks], function(h.a) { h.a <- trim_hunk(h.a, "cur", 0L) h.a <- trim_hunk(h.a, "tar", 0L) h.a } ) } } else if (!cut.off && length(cum.len)) { lines.omitted <- lines.total hunk.grps.omitted <- hunk.grps.count for(i in seq_along(hunk.grps)) hunk.grps[[i]] <- empty_hunk_grp(hunk.grps[[i]]) } diffs.trim <- count_diffs(hunk.grps) attr(hunk.grps, "meta") <- list( lines=as.integer(c(lines.omitted, lines.total)), hunks=as.integer(c(hunk.grps.omitted, hunk.grps.count)), diffs=as.integer(c(diffs.orig - diffs.trim, diffs.orig)) ) hunk.grps } # Helper fun line_count <- function(rng) if(rng[[1L]]) rng[[2L]] - rng[[1L]] + 1L else 0L # Count how many "lines" of differences there are in the hunks # # Counts original diff lines, not lines left after trim. This is because # we are checking for 'str' folding, and 'str' folding should only happen # if the folded results fits fully within limit. # # param x should be a hunk group list count_diffs <- function(x) { sum( vapply( unlist(x, recursive=FALSE), function(y) if(y$context) 0L else line_count(y$tar.rng) + line_count(y$cur.rng), integer(1L) ) ) } # More detailed counting of differences; note that context counting is messed # up b/c context's are duplicated around each hunk. This is primarily used for # the summary method count_diffs_detail <- function(x) { x.flat <- unlist(x, recursive=FALSE) guides <- vapply(x.flat, "[[", logical(1L), "guide") vapply( x.flat[!guides], function(y) if(y$context) c(match=line_count(y$tar.rng), delete=0L, add=0L) else c(match=0L, delete=line_count(y$tar.rng), add=line_count(y$cur.rng)), integer(3L) ) } count_diff_hunks <- function(x) sum(!vapply(unlist(x, recursive=FALSE), "[[", logical(1L), "context")) diffobj/R/word.R0000755000176200001440000006147013465657560013210 0ustar liggesusers# Copyright (C) 2019 Brodie Gaslam # # This file is part of "diffobj - Diffs for R Objects" # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Go to for a copy of the license. # Used to initialize the word difference index lists; represents a non matching # result for use with `regmatches` .word.diff.atom <- -1L attr(.word.diff.atom, "match.length") <- -1L # Matches syntactically valid R variable names .reg.r.ident <- "(?:\\.[[:alpha:]]|[[:alpha:]])[[:alnum:]_.]*" # Helper function when lining up word in a word diff to the lines they came from # # This one used to be simple but grew out of control as we discovered corner # cases; would be good to see if there is a better collapse algorithm that # naturally handles the corner cases (note: we added general handling of the # situation where many hunks share the same line, but have not yet removed # specific handling for corner cases generated by that issue so there is # redundant code in here). # # lines: is a list of what lines are in each hunk, # cont: is a logical vector of same length as lines denoting whether a # particular value in lines is context or diff # hunk.diff: logical vector denoting if for the other object the hunk contains # only differences (seemingly not used in the most recent algorithm) # # What do we do about lines that are fully context? These are flexible in as # much as we can put them anyplace between the two diff hunks. We are trying to # maximize overlapping context elements. reassign_lines2 <- function(lines, cont, hunk.diff) { # Find out what lines show up as duplicated hunk.count <- length(cont) hunk.len <- vapply(lines, length, integer(1L)) hunk.n <- seq_along(cont) nums <- unlist(lines) nums.l <- unlist( lapply(seq_along(lines), function(x) rep(x, length(lines[[x]]))) ) nums.d <- unique(nums[duplicated(nums)]) # For each duplicated number, find range of hunks that contain it and remove # it from inappropriate hunks / add it to proper ones lines.p <- lines for(n in nums.d) { n.r <- range(nums.l[nums == n]) # If any of the non-empty hunks are diff hunks, remove line reference from # every hunk except the first non-empty diff hunk, otherwise remove the # reference from everything except the first non-empty matching hunk b.w <- hunk.n >= n.r[[1L]] & hunk.n <= n.r[[2L]] min.diff.h <- head(which(!cont & b.w & hunk.len), 1L) min.mtch.h <- head(which(cont & b.w & hunk.len), 1L) keep.h <- if(length(min.diff.h)) min.diff.h else min.mtch.h if(length(keep.h)) for(i in n.r[[1L]]:n.r[[2L]]) if(i != keep.h) lines.p[[i]] <- lines.p[[i]][lines.p[[i]] != n] } lines.p } ## Helper Function for Mapping Word Diffs to Lines ## ## Used when we're doing a wrapped diff for atomic vectors. We expect to ## receive `tar/cur.dat` with any meta data lines (e.g. factor levels, time ## series meta data) removed already. The challenge is we need to then be able ## to re-map back each word back to the line it was on originally before ## unwrapping. This may include adding padding line blanks in the case one hunk ## displays across more lines than another. # ## This function does two things: inserts padding lines when hunks in one object ## end up longer than in the other and similar lines don't align, and computes ## mock character strings that will be used by to force alignments. We ## manufacture unique strings that either match or don't match across the two ## objects depending on the word contents of each line, and then pass those ## back as the `comp` component of the `tar.dat` and `cur.dat` returned. The ## subsequent line diff will use `comp` and cause the relevant lines to be lined ## up. This is inefficient and round-about, but has the huge benefit of ## allowing us to plug in the wrapped diff into our existing line diff ## infrastructure ## ## Note that in "word" mode the returned values may be longer than the input ones ## as it may be necessary to add lines to get things to match-up. Added lines ## are indicated by TRUE values in the `fill` component of the `*.dat` return ## values ## ## We have been through several iterations trying to get the most intuitive ## behavior and the result is a fairly non-intuitive and likely inefficient ## algorithm. It works for the most part, so we leave it as is, but is long, ## messy, and should be replaced by a more elegant solution. ## ## @param tar.ends and cur.ends are the indices of the last elements in each ## line of the vector ## @param tar.dat and cur.dat are the data, see `line_diff` body for detailed ## description of them (about 100 lines in). Note that the data has been ## subset to just the portion of it that has row headers (e.g. excluding ## factor levels, etc.) ## @param tar/cur.ends the position in the unwrapped vector of the last "word" ## in each line. word_to_line_map <- function( hunks, tar.dat, cur.dat, tar.ends, cur.ends ) { # Once we've done all the replication and disambiguation, we need to make sure # diff hunks have the same number of lines. Start mix lines or start/end mix # lines should go at beginning (this includes full "context" lines where there # is an insertion or deletion in middle. End mix lines should go at end. # # For each hunk, we need to identify what lines it contains, and whether the # lines are contained in full or not # # If a diff hunk is empty for tar/cur, and the corresponding cur/tar hunk # does not begin/end at beginning of line, then must add lines containing # adjoining elements to the diff find_word_line <- function(h.i, pos, ends.a, ends.b, hunks) { inds_pos <- function(h) c(h$A, h$B)[c(h$A, h$B) > 0L] inds_neg <- function(h) abs(c(h$A, h$B)[c(h$A, h$B) < 0L]) h <- hunks[[h.i]] h.prev <- if(h.i > 1L) hunks[[h.i - 1L]] h.next <- if(h.i < length(hunks)) hunks[[h.i + 1L]] inds.a <- if(pos) inds_pos(h) else inds_neg(h) inds.b <- if(pos) inds_neg(h) else inds_pos(h) ints.a <- c(1L, head(ends.a, -1L) + 1L) ints.b <- c(1L, head(ends.b, -1L) + 1L) ends.b.m <- max(ends.b) # If a diff hunk and empty, but the matching hunk isn't empty, then add # the last element of prior hunk and first element of next hunk if(!h$context && !length(inds.a) && length(inds.b)) { inds.prev <- if(h.i > 1L) if(pos) inds_pos(h.prev) else inds_neg(h.prev) inds.next <- if(h.i < length(hunks)) if(pos) inds_pos(h.next) else inds_neg(h.next) ind.b.min <- min(inds.b) ind.b.max <- max(inds.b) add.left <- if(!ind.b.min %in% ints.b) max(inds.prev) add.right <- if(!ind.b.max %in% ends.b) min(inds.next) inds.a <- if(length(add.left) && length(add.right)) seq(from=add.left, to=add.right, by=1L) else c(add.left, add.right) } sort(unique(findInterval(inds.a, ints.a))) } find_full_diff_line <- function(dat, ends, diffs) { w.t <- vapply( dat$word.ind, function(x) if(is.null(a.val <- attr(x, "word.count"))) -1L else a.val, integer(1L) ) inds.d.l <- findInterval(diffs, c(1L, head(ends, -1L) + 1L)) inds.tab <- tabulate(inds.d.l, length(ends)) diff.full <- which(inds.tab == w.t & inds.tab) } h.seq <- seq_along(hunks) tar.lines <- lapply(h.seq, find_word_line, TRUE, tar.ends, cur.ends, hunks) cur.lines <- lapply(h.seq, find_word_line, FALSE, cur.ends, tar.ends, hunks) # which hunks are context hunks? h.cont <- vapply(hunks, "[[", logical(1L), "context") # Compute what indices are in each lines; we are going to use this to # categorize what type of line this is; some of this might be duplicative with # what we did earlier, but that was so long ago I don't want to get back into # it. tar.idx <- Map(seq, c(1L, head(tar.ends, -1L) + 1L), tar.ends, by=1L) cur.idx <- Map(seq, c(1L, head(cur.ends, -1L) + 1L), cur.ends, by=1L) tar.diff <- unlist( lapply(hunks[!h.cont], function(x) with(x, abs(c(A, B))[c(A, B) > 0])) ) cur.diff <- unlist( lapply(hunks[!h.cont], function(x) with(x, abs(c(A, B))[c(A, B) < 0])) ) # identify whether a line starts with context, ends with context, neither, or # both context_type <- function(idx, diffs) { idx.in <- idx %in% diffs if(!length(idx)) { # arbitrarily assign this case to both "both" } else { if(head(idx.in, 1L) && tail(idx.in, 1L)) { "neither" } else if(head(idx.in, 1L)) { "ends" } else if(tail(idx.in, 1L)) { "starts" } else { "both" } } } tar.end.mix <- vapply(tar.idx, context_type, character(1L), diffs=tar.diff) cur.end.mix <- vapply(cur.idx, context_type, character(1L), diffs=cur.diff) # Handle cases where line is shared by multiple hunks; also need to know which # hunks contain only lines that are fully different (and by extension, are # themselves fully different) as these don't need to have a line from the # opposite object brought in for alignment diff.inds <- unlist(lapply(hunks[!h.cont], "[", c("A", "B"))) if(is.null(diff.inds)) diff.inds <- integer() tar.inds.d <- diff.inds[diff.inds > 0] cur.inds.d <- abs(diff.inds[diff.inds < 0]) tar.tot.diff.l <- find_full_diff_line(tar.dat, tar.ends, tar.inds.d) cur.tot.diff.l <- find_full_diff_line(cur.dat, cur.ends, cur.inds.d) # Remove duplicated line references tar.lines.u <- reassign_lines2(tar.lines, h.cont) cur.lines.u <- reassign_lines2(cur.lines, h.cont) # Search for aligned matching hunks that are empty, and if both those have # adjacent empty diff hunks, remove the matched and diff hunks from both # NOTE: this changes the number of hunks in the word diff! len.orig <- length(tar.lines.u) tar.lines.p <- tar.lines.u cur.lines.p <- cur.lines.u j <- if(h.cont[[1L]]) 1L else 2L l.cont <- as.list(h.cont) k <- 0 while(j < length(tar.lines.p)) { if((k <- k + 1L) > len.orig) { # nocov start stop("Logic Error: infine loop in atomic hunk align; contact maintainer.") # nocov end } if(!length(tar.lines.p[[j]]) && !length(cur.lines.p[[j]])) { if(j > 1L) { tar.lo <- !length(tar.lines.p[[j - 1L]]) cur.lo <- !length(cur.lines.p[[j - 1L]]) } else tar.lo <- cur.lo <- FALSE tar.hi <- !length(tar.lines.p[[j + 1L]]) cur.hi <- !length(cur.lines.p[[j + 1L]]) # Need to remove paired empty match and diff; since we are shortening the # list we don't need to increment J (note possible memory inefficiency # here) if((tar.lo || tar.hi) && (cur.lo || cur.hi)) { if(tar.lo) tar.lines.p[(j - 1L):j] <- NULL else tar.lines.p[j:(j + 1L)] <- NULL if(cur.lo) cur.lines.p[(j - 1L):j] <- NULL else cur.lines.p[j:(j + 1L)] <- NULL l.cont[j:(j + 1L)] <- NULL } else { j <- j + 1L } } else j <- j + 1L } # Update our context vector since we have now possibly removed hunks h.cont <- unlist(l.cont) # If necessary, populate empty diff hunks with matching lines; this happens # if one of tar/cur has differences but the other doesn't steal_matching_line <- function(lines, i) { lines.p <- lines l.len <- length(lines) if(l.len > i && length(lines[[i + 1L]])) { lines.p[[i]] <- head(lines.p[[i + 1L]], 1L) lines.p[[i + 1L]] <- tail(lines.p[[i + 1L]], -1L) } else if (i > 1L && length(lines[[i - 1L]])) { lines.p[[i]] <- tail(lines.p[[i - 1L]], 1L) lines.p[[i - 1L]] <- head(lines.p[[i - 1L]], -1L) } lines.p } tar.lines.f <- tar.lines.p cur.lines.f <- cur.lines.p # lines that are all diffs hunk_diff <- function(vec, tot.diffs) length(vec) && all(vec %in% tot.diffs) tar.tot.diff.h <- vapply(tar.lines, hunk_diff, logical(1L), tar.tot.diff.l) cur.tot.diff.h <- vapply(cur.lines, hunk_diff, logical(1L), cur.tot.diff.l) for(i in seq_along(h.cont)) { if(!h.cont[[i]]) { t.i <- tar.lines.f[[i]] c.i <- cur.lines.f[[i]] if(!length(t.i) && length(c.i) && !cur.tot.diff.h[[i]]) { tar.lines.f <- steal_matching_line(tar.lines.f, i) } else if (!length(c.i) && length(t.i) && !tar.tot.diff.h[[i]]) { cur.lines.f <- steal_matching_line(cur.lines.f, i) } } } # We now need to make sure that every hunk is the same length if( length(tar.lines.f) != length(cur.lines.f) || length(tar.lines.f) != length(h.cont) ) # nocov start stop( "Logic error: mismatched hunk sizes when aligning words to lines; ", "contact maintainer." ) # nocov end tar.lines.f2 <- tar.lines.f cur.lines.f2 <- cur.lines.f # add padding vector as close to middle of input vector as possible, except # in special cases (only one short line, or first or last hunks) pad_in_middle <- function(vec, pad) c( head(vec, ceiling(length(vec) / 2)), pad, tail(vec, floor(length(vec) / 2)) ) for(i in seq_along(tar.lines.f)) { if(length(tar.lines.f[[i]]) != length(cur.lines.f[[i]])) { tar.long <- length(tar.lines.f[[i]]) > length(cur.lines.f[[i]]) long <- if(tar.long) tar.lines.f[[i]] else cur.lines.f[[i]] short <- if(!tar.long) tar.lines.f[[i]] else cur.lines.f[[i]] long.type <- if(tar.long) tar.end.mix[long] else cur.end.mix[long] short.type <- if(!tar.long) tar.end.mix[short] else cur.end.mix[short] pad <- rep(NA, length(long) - length(short)) short.pad <- if(i == 1L && length(tar.lines.f) > 1L) { c(pad, short) } else if (i == length(tar.lines.f)) { c(short, pad) } else if(h.cont[[i]] || length(short) != 1L) { pad_in_middle(short, pad) } else { if( short.type == "ends" && (long.type[[1L]] %in% c("ends", "neither")) ) { c(pad, short) } else c(short, pad) } if(tar.long) cur.lines.f2[[i]] <- short.pad else tar.lines.f2[[i]] <- short.pad } } # Augment the input vectors by the blanks we added; these blanks are # represented by NAs in our index vector. augment <- function(dat, lines) { lines.u <- unlist(lines) lines.len <- length(lines.u) for(i in names(dat)) { i.vec <- vector(typeof(dat[[i]]), length(lines.u)) i.vec[!is.na(lines.u)] <- dat[[i]] if(i == "word.ind") { i.vec[is.na(lines.u)] <- list(.word.diff.atom) } else if (i == "fill") { # warning: this is also used/subverted for augmenting the original # indices so think before you change it i.vec[is.na(lines.u)] <- TRUE } dat[[i]] <- i.vec } dat } tar.dat.aug <- augment(tar.dat, tar.lines.f2) cur.dat.aug <- augment(cur.dat, cur.lines.f2) # Generate the final vectors to do the diffs on; these should be unique # and matching for the matches, and unique and mismatching for the # mismatches hunk_match <- function(i, l) rep(h.cont[i], length(l[[i]])) tar.match <- unlist(lapply(seq_along(h.cont), hunk_match, l=tar.lines.f2)) cur.match <- unlist(lapply(seq_along(h.cont), hunk_match, l=cur.lines.f2)) pos.nums <- sum(tar.match) if(pos.nums != length(unlist(cur.lines.f2[h.cont]))) { # nocov start stop("Logic Error: pos nums incorrect; contact maintainer") # nocov end } neg.nums <- sum(!tar.match, !cur.match) strings <- make_unique_strings( pos.nums + neg.nums, c(tar.dat.aug$raw, cur.dat.aug$raw) ) strings.pos <- strings[seq.int(pos.nums)] strings.neg <- tail(strings, neg.nums) if(neg.nums + pos.nums != length(strings)) { # nocov start stop("Logic Error: num-string maping failed; contact maintainer") # nocov end } tar.dat.aug$comp[tar.match] <- strings.pos cur.dat.aug$comp[cur.match] <- strings.pos tar.dat.aug$comp[!tar.match] <- head(strings.neg, sum(!tar.match)) cur.dat.aug$comp[!cur.match] <- tail(strings.neg, sum(!cur.match)) list(tar.dat=tar.dat.aug, cur.dat=cur.dat.aug) } # Pull out mismatching words from the word regexec; helper functions reg_pull <- function(ind, reg) { reg.out <- reg[ind] attr(reg.out, "match.length") <- attr(reg, "match.length")[ind] attr(reg.out, "useBytes") <- attr(reg, "useBytes") attr(reg.out, "word.count") <- length(reg) reg.out } # Generate the indices in each row and apply the pulling functions # - reg list produced by `gregexpr` and such # - ends length of each line in words # - mismatch index of mismatching words # reg_apply <- function(reg, ends, mismatch) { if(!length(reg)) { reg } else { use.bytes <- attr(reg[[1L]], "useBytes") # assume useBytes value unchanging regs.fin <- reg buckets <- head(c(0L, ends) + 1L, -1L) mism.lines <- findInterval(mismatch, buckets) mism.lines.u <- unique(mism.lines) mtch.lines.u <- which(!seq_along(ends) %in% mism.lines.u ) # These don't have any mismatches attr(.word.diff.atom, "useBytes") <- use.bytes regs.fin[mtch.lines.u] <- replicate(length(mtch.lines.u), .word.diff.atom, simplify=FALSE) # These do have mismatches, we need to split them up in list elements and # substract the starting index to identify position within each sub-list if(length(mism.lines.u)) { inds.msm <- Map( "-", unname(split(mismatch, mism.lines)), buckets[mism.lines.u] - 1L ) regs.fin[mism.lines.u] <- Map(reg_pull, inds.msm, reg[mism.lines.u]) } regs.fin } } # Modify `tar.dat` and `cur.dat` by generating `regmatches` indices for the # words that are different # # If `diff.mode` is "wrap", then wrapped atomic vector output is unwrapped and # the diff is carried out in the unwrapped form, and then re-assembled. See # `word_to_line_map` for details in how its done. Return values may be longer # than input in this mode. # # `match.quotes` will make "words" starting and ending with quotes; it should # only be used with atomic character vectors or possibly deparsed objects. diff_word2 <- function( tar.dat, cur.dat, tar.ind, cur.ind, etc, match.quotes=FALSE, diff.mode, warn=TRUE ) { stopifnot( is.TF(match.quotes), is.TF(warn) # isTRUE(valid_dat(tar.dat)), isTRUE(valid_dat(cur.dat)) # too expensive ) # Compute the char by char diffs for each line reg <- paste0( # grab leading spaces for each word; these will be stripped before actual # word diff, but we want them to be part of mismatch so they are removed # when we construct the equal strings as that allows better matching b/w # strings with differences removed; could do trailing spaces instead "\\s*(?:", # Some attempt at matching R identifiers; note we explicitly chose not to # match `.` or `..`, etc, since those could easily be punctuation sprintf("%s|", .reg.r.ident), # Not whitespaces that doesn't include quotes "[^ \"]+|", # Quoted phrases as structured in atomic character vectors if(match.quotes) "(?:(?<= )|(?<=^))\"(?:[^\"]|\\\")*?\"(?:(?= )|(?=$))|", # Other quoted phrases we might see in expressions or deparsed chr vecs, # this is a bit lazy currently b/c we're not forcing precise matching b/w # starting and ending delimiters "(?:(?<=[ ([,{])|(?<=^))\"(?:[^\"]|\\\"|\"(?=[^ ]))*?", "\"(?:(?=[ ,)\\]}])|(?=$))|", # Other otherwise 'illegal' quotes that couldn't be matched to one of the # known valid quote structures "\")" ) tar.chr <- tar.dat$trim[tar.ind] cur.chr <- cur.dat$trim[cur.ind] tar.reg <- gregexpr(reg, tar.chr, perl=TRUE) cur.reg <- gregexpr(reg, cur.chr, perl=TRUE) tar.split <- regmatches(tar.chr, tar.reg) cur.split <- regmatches(cur.chr, cur.reg) # Collapse into one line if to do the diff across lines, but record # item counts so we can reconstitute the lines at the end tar.lens <- vapply(tar.split, length, integer(1L)) cur.lens <- vapply(cur.split, length, integer(1L)) tar.unsplit <- unlist(tar.split) cur.unsplit <- unlist(cur.split) if(is.null(tar.unsplit)) tar.unsplit <- character(0L) if(is.null(cur.unsplit)) cur.unsplit <- character(0L) # Remove the leading spaces we grabbed for each word tar.unsplit <- trimws(tar.unsplit, "left") cur.unsplit <- trimws(cur.unsplit, "left") # Run the word diff as a line diff configured in a manner compatible for the # word diff etc@line.limit <- etc@hunk.limit <- etc@context <- -1L etc@mode <- "context" diffs <- char_diff( tar.unsplit, cur.unsplit, etc=etc, diff.mode=diff.mode, warn=warn ) # Need to figure out which elements match, and which ones do not hunks.flat <- diffs$hunks tar.mism <- unlist( lapply(hunks.flat, function(x) if(!x$context) x$A else integer(0L)) ) cur.mism <- abs( unlist(lapply(hunks.flat, function(x) if(!x$context) x$B else integer(0L))) ) # Figure out which line each of these elements came from, and what index # in each of those lines they are; we use the recorded lengths in words of # each line to reconstruct this; also record original line length so we # can compute token ratios tar.ends <- cumsum(tar.lens) cur.ends <- cumsum(cur.lens) tar.dat$word.ind[tar.ind] <- reg_apply(tar.reg, tar.ends, tar.mism) cur.dat$word.ind[cur.ind] <- reg_apply(cur.reg, cur.ends, cur.mism) # If in wrap mode (which is really atomic mode), generate a spoofed # `comp` vector (see word_to_line_map) # # Note that we're only operating on a subset of the data via tar.ind and # cur.ind, these are supposed to be the contiguous block of lines that have # row headers. tar.dat.fin <- tar.dat cur.dat.fin <- cur.dat if(diff.mode == "wrap") { tar.dat.ind <- lapply(tar.dat, '[', tar.ind) cur.dat.ind <- lapply(cur.dat, '[', cur.ind) word.line.mapped <- word_to_line_map( hunks.flat, tar.dat.ind, cur.dat.ind, tar.ends, cur.ends ) # Merge back the mapped data, need to account for possiblity of padding # lines being added. tar.len.old <- length(tar.dat[[1L]]) cur.len.old <- length(cur.dat[[1L]]) tar.ind.lo <- seq_len(head(tar.ind, 1L) - 1L) tar.ind.hi <- seq_len(tar.len.old - tail(tar.ind, 1L)) + tail(tar.ind, 1L) cur.ind.lo <- seq_len(head(cur.ind, 1L) - 1L) cur.ind.hi <- seq_len(cur.len.old - tail(cur.ind, 1L)) + tail(cur.ind, 1L) interleave <- function(idx, new, old, lo, hi) c(old[[idx]][lo], new[[idx]], old[[idx]][hi]) tar.dat.fin <- setNames( lapply( seq_along(tar.dat), interleave, new=word.line.mapped[['tar.dat']], old=tar.dat, lo=tar.ind.lo, hi=tar.ind.hi ), names(tar.dat) ) cur.dat.fin <- setNames( lapply( seq_along(cur.dat), interleave, new=word.line.mapped[['cur.dat']], old=cur.dat, lo=cur.ind.lo, hi=cur.ind.hi ), names(cur.dat) ) } list( tar.dat=tar.dat.fin, cur.dat=cur.dat.fin, hit.diffs.max=diffs$hit.diffs.max ) } # Make unique strings # # Makes gibberish strings that are 16 characters long, are unique, and don't # overlap with `invalid`. This allows us to generate strings we can use to # cause a specific diff outcome. # # n: how long the character vector should be # invalid: what values cannot be contained in the returned values make_unique_strings <- function(n, invalid) { pool <- c( letters, LETTERS, 0:9, "_", ".", "*", "+", "-", "=", "(", ")", "{", "}", "~", "`", "!", "@", "#", "$", "%", "^", "&", ";", ":", "<", ">", "?", ",", "/" ) cols <- 16 # use 16 character samples, should be more than big enough dat <- matrix("", ncol=16, nrow=n) rows <- 1:n safety <- 0 repeat { dat[rows, ] <- matrix(sample(pool, cols * length(rows), replace=TRUE), ncol=cols) dat.chr <- do.call(paste0, split(dat, col(dat))) rows <- which(duplicated(dat.chr) | dat.chr %in% invalid) if(!length(rows)) break # nocov start if(safety <- safety + 1 > 100) stop( "Logic Error: unable to generate unique strings; this should be ", "incredibly rare as we are sampling from 10^31 elements, so try ", "again and if it happens again contact maintainer" ) # nocov end } dat.chr } # Add word diff highlighting word_color <- function(txt, inds, fun) { word.list <- regmatches(txt, inds) word.lens <- vapply(word.list, length, integer(1L)) # remove leading space before coloring words.u <- if(length(word.list)) unlist(word.list) else character(0L) words.u.trim.ind <- regexpr("\\S.*", words.u) words.u.trim <- regmatches(words.u, words.u.trim.ind) # color and re-insert back into space words.c.trim <- fun(words.u.trim) regmatches(words.u, words.u.trim.ind) <- words.c.trim # split back into original lines words.res <- vector("list", length(word.list)) words.res[!!word.lens] <- split( words.u, rep(seq_along(word.lens), times=word.lens) ) words.res[!word.lens] <- list(character(0L)) regmatches(txt, inds) <- words.res txt } diffobj/R/rds.R0000755000176200001440000000171713442554134013007 0ustar liggesusers# Copyright (C) 2019 Brodie Gaslam # # This file is part of "diffobj - Diffs for R Objects" # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Go to for a copy of the license. # Check Whether Input Could Be Reference to RDS File and Load if it Is get_rds <- function(x) { tryCatch( if( (is.chr.1L(x) && Encoding(x) != "bytes" && file_test("-f", x)) || inherits(x, "connection") ) { suppressWarnings(readRDS(x)) } else x, error=function(e) x ) } diffobj/R/system.R0000755000176200001440000000211413442554134013533 0ustar liggesusers# Copyright (C) 2019 Brodie Gaslam # # This file is part of "diffobj - Diffs for R Objects" # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Go to for a copy of the license. #' @include styles.R NULL # nocov start .onLoad <- function(libname, pkgname) { # Scheme defaults are fairly complex... existing.opts <- options() options(.default.opts[setdiff(names(.default.opts), names(existing.opts))]) trimws <<- if(getRversion() < "3.2.0") trimws2 else base::trimws } # Remove DLLs when package is unloaded .onUnload <- function(libpath) { library.dynam.unload("diffobj", libpath) } # nocov end diffobj/R/layout.R0000755000176200001440000001345613442554134013537 0ustar liggesusers# Copyright (C) 2019 Brodie Gaslam # # This file is part of "diffobj - Diffs for R Objects" # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Go to for a copy of the license. # Compute all the different gutter components and report max width gutter_dat <- function(etc) { stopifnot(is(etc, "Settings")) old.opt <- options(crayon.enabled=TRUE) on.exit(options(old.opt)) funs <- etc@style@funs text <- etc@style@text # get every slot except the pad slot; we'll then augment them so they have # all the same number of characters if the style class inherits from # Raw, which should be the case for raw, ansi8 and ansi255. Finally apply # functions; note we assume the provided gutter characters don't contain # ANSI escapes. We're a bit sloppy here with how we pull the relevant stuff slot.nm <- slotNames(text) slots <- slot.nm[grepl("^gutter\\.", slot.nm) & slot.nm != "gutter.pad"] gutt.txt <- vapply(slots, slot, character(1L), object=text) gutt.dat <- if(is(etc@style, "Raw")) format(gutt.txt) else gutt.txt gutt.format.try <- try({ gutt.dat.format <- vapply( slots, function(x) slot(funs, sprintf("%s", x))(gutt.dat[x]), character(1L) ) gutt.dat.format.pad <- funs@gutter(paste0(gutt.dat.format, funs@gutter.pad(text@gutter.pad))) }) if(inherits(gutt.format.try, "try-error")) stop( "Failed attempting to apply gutter formatting functions; if you did not ", "customize them, contact maintainer. See `?StyleFuns`." ) names(gutt.dat.format.pad) <- sub("^gutter\\.", "", names(gutt.dat.format)) gutt.max.w <- max( etc@style@nchar.fun(gutt.dat.format.pad, sgr.supported=etc@sgr.supported) ) gutt.args <- c( list("Gutter"), as.list(gutt.dat.format.pad), list(width=gutt.max.w) ) do.call("new", gutt.args) } # Based on the type of each row in a column, render the correct gutter render_gutters <- function(types, lens, lens.max, etc) { gutter.dat <- etc@gutter Map( function(dat, lens, lens.max) { Map( function(type, len, len.max) { if( type %in% c( "insert", "delete", "match", "guide", "fill", "context.sep" ) ) { c( if(len) slot(gutter.dat, as.character(type)), rep( slot(gutter.dat, paste0(type, ".", "ctd")), max(len - 1L, 0L) ), rep(slot(gutter.dat, "fill"), max(len.max - len, 0L)) ) } else character(len) }, dat, lens, lens.max ) }, types, lens, lens.max ) } render_col <- function(gutter, col, type, etc) { lens <- vapply(col, length, integer(1L)) gutt.ul <- unlist(gutter) col.txt <- paste0(gutt.ul, unlist(col)) type.ul <- unlist(type) es <- etc@style@funs # line formats col.txt[type.ul == "banner.insert"] <- es@banner(es@banner.insert(col.txt[type.ul == "banner.insert"])) col.txt[type.ul == "banner.delete"] <- es@banner(es@banner.delete(col.txt[type.ul == "banner.delete"])) col.txt[type.ul == "insert"] <- es@line(es@line.insert(col.txt[type.ul == "insert"])) col.txt[type.ul == "delete"] <- es@line(es@line.delete(col.txt[type.ul == "delete"])) col.txt[type.ul == "match"] <- es@line(es@line.match(col.txt[type.ul == "match"])) col.txt[type.ul == "guide"] <- es@line(es@line.guide(col.txt[type.ul == "guide"])) col.txt[type.ul == "fill"] <- es@line(es@line.fill(col.txt[type.ul == "fill"])) col.txt[type.ul == "context.sep"] <- es@line(es@context.sep(col.txt[type.ul == "context.sep"])) col.txt[type.ul == "header"] <- es@line(col.txt[type.ul == "header"]) col.txt } render_cols <- function(cols, gutters, types, etc) { Map(render_col, gutters, cols, types, MoreArgs=list(etc=etc)) } render_rows <- function(cols, etc) { col.txt <- do.call(paste, c(cols, list(sep=etc@style@text@pad.col))) etc@style@funs@row(col.txt) } # Create a dummy row so we can compute display width for scaling display in # HTML mode # # @param x a `Diff` object make_dummy_line <- function(x, dummy.text, type) { stopifnot(is.chr.1L(type) && type %in% c("line", "banner")) fns <- x@etc@style@funs txt <- x@etc@style@text line_fun <- slot(fns, type) line_ins_fun <- slot(fns, sprintf("%s.insert", type)) line_del_fun <- slot(fns, sprintf("%s.delete", type)) if(x@etc@mode == "sidebyside") { sprintf( "%s%s%s", line_fun( line_del_fun( sprintf( "%s%s", x@etc@gutter@delete, fns@text(fns@text.delete(dummy.text)) ) ) ), txt@pad.col, line_fun( line_ins_fun( sprintf( "%s%s", x@etc@gutter@insert, fns@text(fns@text.insert(dummy.text)) ) ) ) ) } else { line_fun( line_del_fun( sprintf( "%s%s", x@etc@gutter@delete, fns@text(fns@text.delete(dummy.text)) ) ) ) } } make_dummy_row <- function(x) { cont.meta <- make_dummy_line(x, paste0(rep("a", x@etc@text.width), collapse=""), "line") banner.meta <- make_dummy_line(x, x@etc@style@blank.sub, "banner") fns <- x@etc@style@funs sprintf( "
%s
%s
", "display: none; position: absolute; top: 0px; z-index: -1;", fns@container(fns@row(banner.meta)), fns@container(fns@row(cont.meta)) ) } diffobj/R/capt.R0000755000176200001440000003244213464701604013145 0ustar liggesusers# Copyright (C) 2019 Brodie Gaslam # # This file is part of "diffobj - Diffs for R Objects" # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Go to for a copy of the license. # Capture output of print/show/str; unfortunately doesn't have superb handling # of errors during print/show call, though hopefully these are rare # # x is a quoted call to evaluate capture <- function(x, etc, err) { capt.width <- etc@text.width if(capt.width) { opt.set <- try(width.old <- options(width=capt.width), silent=TRUE) if(inherits(opt.set, "try-error")) { warning( "Unable to set desired width ", capt.width, ", (", conditionMessage(attr(opt.set, "condition")), ");", "proceeding with existing setting." ) } else on.exit(options(width.old)) } # Note, we use `tempfile` for capture as that appears much faster than normal # capture without a file capt.file <- tempfile() on.exit(unlink(capt.file), add=TRUE) res <- try({ capture.output(eval(x, etc@frame), file=capt.file) obj.out <- readLines(capt.file) }) if(inherits(res, "try-error")) err( "Failed attempting to get text representation of object: ", conditionMessage(attr(res, "condition")) ) html_ent_sub(res, etc@style) } # capture normal prints, along with default prints to make sure that if we # do try to wrap an atomic vector print it is very likely to be in a format # we are familiar with and not affected by a non-default print method capt_print <- function(target, current, etc, err, extra){ dots <- extra # What about S4? if(getRversion() >= "3.2.0") { print.match <- try( match.call( get("print", envir=etc@frame, mode='function'), as.call(c(list(quote(print), x=NULL), dots)), envir=etc@frame ) ) } else { # this may be sub-optimal, but match.call does not support the envir arg # prior to this # nocov start print.match <- try( match.call( get("print", envir=etc@frame), as.call(c(list(quote(print), x=NULL), dots)) ) ) # nocov end } if(inherits(print.match, "try-error")) err("Unable to compose `print` call") names(print.match)[[2L]] <- "" tar.call <- cur.call <- print.match if(length(dots)) { if(!is.null(etc@tar.exp)) tar.call[[2L]] <- etc@tar.exp if(!is.null(etc@cur.exp)) cur.call[[2L]] <- etc@cur.exp etc@tar.banner <- deparse(tar.call)[[1L]] etc@cur.banner <- deparse(cur.call)[[1L]] } tar.call.q <- if(is.call(target) || is.symbol(target)) call("quote", target) else target cur.call.q <- if(is.call(current) || is.symbol(current)) call("quote", current) else current if(!is.null(target)) tar.call[[2L]] <- tar.call.q if(!is.null(current)) cur.call[[2L]] <- cur.call.q # If dimensioned object, and in auto-mode, switch to side by side if stuff is # narrow enough to fit if((!is.null(dim(target)) || !is.null(dim(current)))) { cur.capt <- capture(cur.call, etc, err) tar.capt <- capture(tar.call, etc, err) etc <- set_mode(etc, tar.capt, cur.capt) } else { etc <- if(etc@mode == "auto") sideBySide(etc) else etc cur.capt <- capture(cur.call, etc, err) tar.capt <- capture(tar.call, etc, err) } if(isTRUE(etc@guides)) etc@guides <- guidesPrint if(isTRUE(etc@trim)) etc@trim <- trimPrint diff.out <- line_diff(target, current, tar.capt, cur.capt, etc=etc, warn=TRUE) diff.out@capt.mode <- "print" diff.out } # Tries various different `str` settings to get the best possible output capt_str <- function(target, current, etc, err, extra){ # Match original call and managed dots, in particular wrt to the # `max.level` arg dots <- extra frame <- etc@frame line.limit <- etc@line.limit if("object" %in% names(dots)) err("You may not specify `object` as part of `extra`") if(getRversion() < "3.2.0") { # nocov start str.match <- match.call( str_tpl, call=as.call(c(list(quote(str), object=NULL), dots)) ) # nocov end } else { str.match <- match.call( str_tpl, call=as.call(c(list(quote(str), object=NULL), dots)), envir=etc@frame ) } names(str.match)[[2L]] <- "" # Handle auto mode (side by side always for `str`) if(etc@mode == "auto") etc <- sideBySide(etc) # Utility function; defining in body so it has access to `err` eval_try <- function(match.list, index, envir) tryCatch( eval(match.list[[index]], envir=envir), error=function(e) err("Error evaluating `", index, "` arg: ", conditionMessage(e)) ) # Setup / process extra args auto.mode <- FALSE max.level.supplied <- FALSE if( max.level.pos <- match("max.level", names(str.match), nomatch=0L) ) { # max.level specified in call; check for special 'auto' case max.level.eval <- eval_try(str.match, "max.level", etc@frame) if(identical(max.level.eval, "auto")) { auto.mode <- TRUE str.match[["max.level"]] <- NA } else { max.level.supplied <- TRUE } } else { str.match[["max.level"]] <- NA auto.mode <- TRUE max.level.pos <- length(str.match) max.level.supplied <- FALSE } # Was wrap specified in strict width mode? Not sure this is correct any more; # should probably be looking at extra args. wrap <- FALSE if("strict.width" %in% names(str.match)) { res <- eval_try(str.match, "strict.width", etc@frame) wrap <- is.character(res) && length(res) == 1L && !is.na(res) && nzchar(res) && identical(res, substr("wrap", 1L, nchar(res))) } if(auto.mode) { msg <- "Specifying `%s` may cause `str` output level folding to be incorrect" if("comp.str" %in% names(str.match)) warning(sprintf(msg, "comp.str")) if("indent.str" %in% names(str.match)) warning(sprintf(msg, "indent.str")) } # don't want to evaluate target and current more than once, so can't eval # tar.exp/cur.exp, so instead run call with actual object tar.call <- cur.call <- str.match tar.call.q <- if(is.call(target) || is.symbol(target)) call("quote", target) else target cur.call.q <- if(is.call(current) || is.symbol(current)) call("quote", current) else current if(!is.null(target)) tar.call[[2L]] <- tar.call.q if(!is.null(current)) cur.call[[2L]] <- cur.call.q # Run str capt.width <- etc@text.width has.diff <- has.diff.prev <- FALSE # we used to strip_hz_control here, but shouldn't have to since handled by # line_diff tar.capt <- capture(tar.call, etc, err) tar.lvls <- str_levels(tar.capt, wrap=wrap) cur.capt <- capture(cur.call, etc, err) cur.lvls <- str_levels(cur.capt, wrap=wrap) prev.lvl.hi <- lvl <- max.depth <- max(tar.lvls, cur.lvls) prev.lvl.lo <- 0L first.loop <- TRUE safety <- 0L warn <- TRUE if(isTRUE(etc@guides)) etc@guides <- guidesStr if(isTRUE(etc@trim)) etc@trim <- trimStr tar.str <- tar.capt cur.str <- cur.capt diff.obj <- diff.obj.full <- line_diff( target, current, tar.str, cur.str, etc=etc, warn=warn ) if(!max.level.supplied) { repeat{ if((safety <- safety + 1L) > max.depth && !first.loop) # nocov start stop( "Logic Error: exceeded list depth when comparing structures; contact ", "maintainer." ) # nocov end if(!first.loop) { tar.str <- tar.capt[tar.lvls <= lvl] cur.str <- cur.capt[cur.lvls <= lvl] diff.obj <- line_diff( target, current, tar.str, cur.str, etc=etc, warn=warn ) } if(diff.obj@hit.diffs.max) warn <- FALSE has.diff <- suppressWarnings(any(diff.obj)) # If there are no differences reducing levels isn't going to help to # find one; additionally, if not in auto.mode we should not be going # through this process if(first.loop && !has.diff) break first.loop <- FALSE if(line.limit[[1L]] < 1L) break line.len <- diff_line_len( diff.obj@diffs, etc=etc, tar.capt=tar.str, cur.capt=cur.str ) # We need a higher level if we don't have diffs if(!has.diff && prev.lvl.hi - lvl > 1L) { prev.lvl.lo <- lvl lvl <- lvl + as.integer((prev.lvl.hi - lvl) / 2) tar.call[[max.level.pos]] <- lvl cur.call[[max.level.pos]] <- lvl next } else if(!has.diff) { diff.obj <- diff.obj.full lvl <- NULL break } # If we have diffs, need to check whether we should try to reduce lines # to get under line limit if(line.len <= line.limit[[1L]]) { # We fit, nothing else to do break } if(lvl - prev.lvl.lo > 1L) { prev.lvl.hi <- lvl lvl <- lvl - as.integer((lvl - prev.lvl.lo) / 2) tar.call[[max.level.pos]] <- lvl cur.call[[max.level.pos]] <- lvl next } # Couldn't get under limit, so use first run results diff.obj <- diff.obj.full lvl <- NULL break } } else { tar.str <- tar.capt[tar.lvls <= max.level.eval] cur.str <- cur.capt[cur.lvls <= max.level.eval] lvl <- max.level.eval diff.obj <- line_diff(target, current, tar.str, cur.str, etc=etc, warn=warn) } if(auto.mode && !is.null(lvl) && lvl < max.depth) { str.match[[max.level.pos]] <- lvl } else if (!max.level.supplied || is.null(lvl)) { str.match[[max.level.pos]] <- NULL } tar.call <- cur.call <- str.match if(!is.null(etc@tar.exp)) tar.call[[2L]] <- etc@tar.exp if(!is.null(etc@cur.exp)) cur.call[[2L]] <- etc@cur.exp if(is.null(etc@tar.banner)) diff.obj@etc@tar.banner <- deparse(tar.call)[[1L]] if(is.null(etc@cur.banner)) diff.obj@etc@cur.banner <- deparse(cur.call)[[1L]] # Track total differences in fully expanded view so we can report hidden # diffs when folding levels diff.obj@diff.count.full <- count_diffs(diff.obj.full@diffs) diff.obj@capt.mode <- "str" diff.obj } capt_chr <- function(target, current, etc, err, extra){ tar.capt <- if(!is.character(target)) do.call(as.character, c(list(target), extra), quote=TRUE) else target cur.capt <- if(!is.character(current)) do.call(as.character, c(list(current), extra), quote=TRUE) else current if(anyNA(tar.capt)) tar.capt[is.na(tar.capt)] <- "NA" if(anyNA(cur.capt)) cur.capt[is.na(cur.capt)] <- "NA" etc <- set_mode(etc, tar.capt, cur.capt) if(isTRUE(etc@guides)) etc@guides <- guidesChr if(isTRUE(etc@trim)) etc@trim <- trimChr diff.out <- line_diff( target, current, html_ent_sub(tar.capt, etc@style), html_ent_sub(cur.capt, etc@style), etc=etc ) diff.out@capt.mode <- "chr" diff.out } capt_deparse <- function(target, current, etc, err, extra){ dep.try <- try({ tar.capt <- do.call(deparse, c(list(target), extra), quote=TRUE) cur.capt <- do.call(deparse, c(list(current), extra), quote=TRUE) }) if(inherits(dep.try, "try-error")) err("Error attempting to deparse object(s)") etc <- set_mode(etc, tar.capt, cur.capt) if(isTRUE(etc@guides)) etc@guides <- guidesDeparse if(isTRUE(etc@trim)) etc@trim <- trimDeparse diff.out <- line_diff( target, current, html_ent_sub(tar.capt, etc@style), html_ent_sub(cur.capt, etc@style), etc=etc ) diff.out@capt.mode <- "deparse" diff.out } capt_file <- function(target, current, etc, err, extra) { tar.capt <- try(do.call(readLines, c(list(target), extra), quote=TRUE)) if(inherits(tar.capt, "try-error")) err("Unable to read `target` file.") cur.capt <- try(do.call(readLines, c(list(current), extra), quote=TRUE)) if(inherits(cur.capt, "try-error")) err("Unable to read `current` file.") etc <- set_mode(etc, tar.capt, cur.capt) if(isTRUE(etc@guides)) etc@guides <- guidesFile if(isTRUE(etc@trim)) etc@trim <- trimFile diff.out <- line_diff( tar.capt, cur.capt, html_ent_sub(tar.capt, etc@style), html_ent_sub(cur.capt, etc@style), etc=etc ) diff.out@capt.mode <- "file" diff.out } capt_csv <- function(target, current, etc, err, extra){ tar.df <- try(do.call(read.csv, c(list(target), extra), quote=TRUE)) if(inherits(tar.df, "try-error")) err("Unable to read `target` file.") if(!is.data.frame(tar.df)) err("`target` file did not produce a data frame when read") # nocov cur.df <- try(do.call(read.csv, c(list(current), extra), quote=TRUE)) if(inherits(cur.df, "try-error")) err("Unable to read `current` file.") if(!is.data.frame(cur.df)) err("`current` file did not produce a data frame when read") # nocov capt_print(tar.df, cur.df, etc, err, extra) } # Sets mode to "unified" if stuff is too wide to fit side by side without # wrapping otherwise sets it in "sidebyside" set_mode <- function(etc, tar.capt, cur.capt) { stopifnot(is(etc, "Settings"), is.character(tar.capt), is.character(cur.capt)) if(etc@mode == "auto") { if( any( nchar2(cur.capt, sgr.supported=etc@sgr.supported) > etc@text.width.half ) || any( nchar2(tar.capt, sgr.supported=etc@sgr.supported) > etc@text.width.half ) ) { etc@mode <- "unified" } } if(etc@mode == "auto") etc <- sideBySide(etc) etc } diffobj/R/get.R0000755000176200001440000000314313442554134012771 0ustar liggesusers# Copyright (C) 2019 Brodie Gaslam # # This file is part of "diffobj - Diffs for R Objects" # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Go to for a copy of the license. # Retrieves data from the data elements of the Diff object based on the index # values provided in ind. Positive values draw from `tar` elements, and # negative draw from `cur` elements. # # returns a list with the elements. If type is length 1, you will probably # want to unlist the return value get_dat <- function(x, ind, type) { stopifnot( is(x, "Diff"), is.integer(ind), is.chr.1L(type) && type %in% .diff.dat.cols ) # Need to figure out what zero indices are; previously would return # NA_character_, but now since we're getting a whole bunch of different # stuff not sure what the right return value should be, or even if we produce # zero indices anymore get_dat_raw(ind, x@tar.dat[[type]], x@cur.dat[[type]]) } get_dat_raw <- function(ind, tar, cur) { template <- tar[0L] length(template) <- length(ind) template[which(ind < 0L)] <- cur[abs(ind[ind < 0L])] template[which(ind > 0L)] <- tar[abs(ind[ind > 0L])] template } diffobj/R/rdiff.R0000755000176200001440000001253513466133312013306 0ustar liggesusers# Copyright (C) 2019 Brodie Gaslam # # This file is part of "diffobj - Diffs for R Objects" # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Go to for a copy of the license. #' Run Rdiff Directly on R Objects #' #' These functions are here for reference and testing purposes. They are #' wrappers to \code{tools::Rdiff} and rely on an existing system diff utility. #' You should be using \code{\link{ses}} or \code{\link{diffChr}} instead of #' \code{Rdiff_chr} and \code{\link{diffPrint}} instead of \code{Rdiff_obj}. #' See limitations in note. #' #' \code{Rdiff_chr} runs diffs on character vectors or objects coerced to #' character vectors, where each value in the vectors is treated as a line in a #' file. \code{Rdiff_chr} always runs with the \code{useDiff} and \code{Log} #' parameters set to \code{TRUE}. #' #' \code{Rdiff_obj} runs diffs on the \code{print}ed representation of #' the provided objects. For each of \code{from}, \code{to}, will check if they #' are 1 length character vectors referencing an RDS file, and will use the #' contents of that RDS file as the object to compare. #' #' @note These functions will try to use the system \code{diff} utility. This #' will fail in systems that do not have that utility available (e.g. windows #' installation without Rtools). #' @importFrom tools Rdiff #' @export #' @seealso \code{\link{ses}}, \code{\link[=diffPrint]{diff*}} #' @param from character or object coercible to character for \code{Rdiff_chr}, #' any R object with \code{Rdiff_obj}, or a file pointing to an RDS object #' @param to character same as \code{from} #' @param nullPointers passed to \code{tools::Rdiff} #' @param silent TRUE or FALSE, whether to display output to screen #' @param minimal TRUE or FALSE, whether to exclude the lines that show the #' actual differences or only the actual edit script commands #' @return the Rdiff output, invisibly if \code{silent} is FALSE #' Rdiff_chr(letters[1:5], LETTERS[1:5]) #' Rdiff_obj(letters[1:5], LETTERS[1:5]) Rdiff_chr <- function(from, to, silent=FALSE, minimal=FALSE, nullPointers=TRUE) { A <- try(as.character(from)) if(inherits(A, "try-error")) stop("Unable to coerce `target` to character.") B <- try(as.character(to)) if(inherits(B, "try-error")) stop("Unable to coerce `current` to character.") af <- tempfile() bf <- tempfile() writeLines(A, af) writeLines(B, bf) on.exit(unlink(c(af, bf))) Rdiff_run( silent=silent, minimal=minimal, from=af, to=bf, nullPointers=nullPointers ) } #' @export #' @rdname Rdiff_chr Rdiff_obj <- function(from, to, silent=FALSE, minimal=FALSE, nullPointers=TRUE) { dummy.env <- new.env() # used b/c unique object files <- try( vapply( list(from, to), function(x) { if( is.character(x) && length(x) == 1L && !is.na(x) && file_test("-f", x) ) { rdstry <- tryCatch(readRDS(x), error=function(x) dummy.env) if(!identical(rdstry, dummy.env)) x <- rdstry } f <- tempfile() on.exit(unlink(f)) capture.output(if(isS4(x)) show(x) else print(x), file=f) on.exit() f }, character(1L) ) ) if(inherits(files, "try-error")) stop("Unable to store text representation of objects") on.exit(unlink(files)) Rdiff_run( from=files[[1L]], to=files[[2L]], silent=silent, minimal=minimal, nullPointers=nullPointers ) } # Internal use only: BEWARE, will unlink from, to Rdiff_run <- function(from, to, nullPointers, silent, minimal) { stopifnot( isTRUE(silent) || identical(silent, FALSE), isTRUE(minimal) || identical(minimal, FALSE) ) res <- tryCatch( Rdiff( from=from, to=to, useDiff=TRUE, Log=TRUE, nullPointers=nullPointers )$out, warning=function(e) stop( "`tools::Rdiff` returned a warning; this likely means you are running ", "without a `diff` utility accessible to R" ) ) if(!is.character(res)) # nocov start stop("Internal Error: Unexpected tools::Rdiff output, contact maintainer") # nocov end res <- if(minimal) res[!grepl("^[<>-]", res)] else res if(silent) res else { cat(res, sep="\n") invisible(res) } } #' Attempt to Detect Whether diff Utility is Available #' #' Checks whether \code{\link[=Rdiff]{tools::Rdiff}} issues a warning when #' running with \code{useDiff=TRUE} and if it does assumes this is because the #' diff utility is not available. Intended primarily for testing purposes. #' #' @export #' @return TRUE or FALSE #' @param test.with function to test for diff presence with, typically Rdiff #' @examples #' has_Rdiff() has_Rdiff <- function(test.with=tools::Rdiff) { f.a <- tempfile() f.b <- tempfile() on.exit(unlink(c(f.a, f.b))) writeLines(letters[1:3], f.a) writeLines(LETTERS, f.b) tryCatch( { test.with( from=f.a, to=f.b, useDiff=TRUE, Log=TRUE, nullPointers=FALSE ) TRUE }, warning=function(e) FALSE ) } diffobj/R/styles.R0000755000176200001440000015456113442554134013550 0ustar liggesusers# Copyright (C) 2019 Brodie Gaslam # # This file is part of "diffobj - Diffs for R Objects" # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Go to for a copy of the license. #' @include html.R #' @include finalizer.R #' @include pager.R NULL # maybe this shouldn't be an S4 class since the function slot doesn't work # for classed functions (e.g. the ones produced by crayon) #' Functions Used for Styling Diff Components #' #' Except for \code{container} every function specified here should be #' vectorized and apply formatting to each element in a character vectors. The #' functions must accept at least one argument and require no more than one #' argument. The text to be formatted will be passed as a character vector #' as the first argument to each function. #' #' These functions are applied in post processing steps. The \code{diff*} #' methods do not do any of the formatting. Instead, the formatting is done #' only if the user requests to \code{show} the object. Internally, \code{show} #' first converts the object to a character vector using \code{as.character}, #' which applies every formatting function defined here except for #' \code{container}. Then \code{show} applies \code{container} before #' forwarding the result to the screen or pager. #' #' @note the slots are set to class \dQuote{ANY} to allow classed functions #' such as those defined in the \code{crayon} package. Despite this seemingly #' permissive slot definition, only functions are allowed in the slots by #' the validation functions. #' @param container function used primarily by HTML styles to generate an #' outermost \code{DIV} that allows for CSS targeting of its contents #' (see \code{\link{cont_f}} for a function generator appropriate for use #' here) #' @param line function #' @param line.insert function #' @param line.delete function #' @param line.match function #' @param line.guide function formats guide lines (see \code{\link{guides}}) #' @param text function #' @param text.insert function #' @param text.delete function #' @param text.match function #' @param text.guide function formats guide lines (see \code{\link{guides}}) #' @param gutter function #' @param gutter.insert function #' @param gutter.delete function #' @param gutter.match function #' @param gutter.guide function #' @param gutter.pad function #' @param header function to format each hunk header with #' @param banner function to format entire banner #' @param banner.insert function to format insertion banner #' @param banner.delete function to format deletion banner #' @param meta function format meta information lines #' @param context.sep function to format the separator used to visually #' distinguish the A and B hunks in \dQuote{context} \code{mode} #' @return a StyleFuns S4 object #' @seealso \code{\link{Style}} #' @rdname StyleFuns #' @export StyleFuns #' @exportClass StyleFuns StyleFuns <- setClass( "StyleFuns", slots=c( container="ANY", row="ANY", line="ANY", line.insert="ANY", line.delete="ANY", line.match="ANY", line.guide="ANY", line.fill="ANY", text="ANY", text.insert="ANY", text.delete="ANY", text.match="ANY", text.guide="ANY", text.fill="ANY", banner="ANY", banner.insert="ANY", banner.delete="ANY", gutter="ANY", gutter.insert="ANY", gutter.insert.ctd="ANY", gutter.delete="ANY", gutter.delete.ctd="ANY", gutter.match="ANY", gutter.match.ctd="ANY", gutter.guide="ANY", gutter.guide.ctd="ANY", gutter.fill="ANY", gutter.fill.ctd="ANY", gutter.context.sep="ANY", gutter.context.sep.ctd="ANY", gutter.pad="ANY", word.insert="ANY", word.delete="ANY", context.sep="ANY", header="ANY", meta="ANY", trim="ANY" ), prototype=list( container=identity, row=identity, banner=identity, banner.insert=identity, banner.delete=identity, line=identity, line.insert=identity, line.delete=identity, line.match=identity, line.guide=identity, line.fill=identity, text=identity, text.insert=identity, text.delete=identity, text.match=identity, text.guide=identity, text.fill=identity, gutter=identity, gutter.pad=identity, gutter.insert=identity, gutter.insert.ctd=identity, gutter.delete=identity, gutter.delete.ctd=identity, gutter.match=identity, gutter.match.ctd=identity, gutter.guide=identity, gutter.guide.ctd=identity, gutter.fill=identity, gutter.fill.ctd=identity, gutter.context.sep=identity, gutter.context.sep.ctd=identity, word.insert=identity, word.delete=identity, header=identity, context.sep=identity, meta=identity, trim=identity ), validity=function(object){ for(i in slotNames(object)) { if(!is.function(slot(object, i))) return(paste0("Argument `", i, "` should be a function.")) if(has_non_def_formals(tail(formals(slot(object, i)), -1L))) return( paste0( "Argument `", i, "` may not have non-default formals argument after the first." ) ) } TRUE } ) StyleFunsAnsi <- setClass( "StyleFunsAnsi", contains="StyleFuns", prototype=list( word.insert=crayon::green, word.delete=crayon::red, gutter.insert=crayon::green, gutter.insert.ctd=crayon::green, gutter.delete=crayon::red, gutter.delete.ctd=crayon::red, gutter.guide=crayon::silver, gutter.guide.ctd=crayon::silver, gutter.fill=crayon::silver, gutter.fill.ctd=crayon::silver, gutter.context.sep=crayon::silver, gutter.context.sep.ctd=crayon::silver, header=crayon::cyan, meta=crayon::silver, line.guide=crayon::silver, context.sep=crayon::silver, trim=crayon::silver ) ) #' Character Tokens Used in Diffs #' #' Various character tokens are used throughout diffs to provide visual cues. #' For example, gutters will contain characters that denote deletions and #' insertions (\code{<} and \code{>} by default). #' #' @param gutter.insert character(1L) text to use as visual cue to indicate #' whether a diff line is an insertion, defaults to \dQuote{> } #' @param gutter.insert.ctd character(1L) if a diff line is wrapped, the #' visual cue shifts to this character to indicate wrapping occured #' @param gutter.delete character(1L) see \code{gutter.insert} above #' @param gutter.delete.ctd character(1L) see \code{gutter.insert.ctd} above #' @param gutter.match character(1L) see \code{gutter.insert} above #' @param gutter.match.ctd character(1L) see \code{gutter.insert.ctd} above #' @param gutter.guide character(1L) see \code{gutter.insert} above #' @param gutter.guide.ctd character(1L) see \code{gutter.insert.ctd} above #' @param gutter.fill character(1L) see \code{gutter.insert} above #' @param gutter.fill.ctd character(1L) see \code{gutter.insert.ctd} above #' @param gutter.pad character(1L) separator between gutter characters and the #' rest of a line in a diff #' @param pad.col character(1L) separator between columns in side by side mode #' @return a StyleText S4 object #' @seealso \code{\link{Style}} #' @rdname StyleText #' @export StyleText #' @exportClass StyleText StyleText <- setClass( "StyleText", slots=c( gutter.insert="character", gutter.insert.ctd="character", gutter.delete="character", gutter.delete.ctd="character", gutter.match="character", gutter.match.ctd="character", gutter.guide="character", gutter.guide.ctd="character", gutter.fill="character", gutter.fill.ctd="character", gutter.context.sep="character", gutter.context.sep.ctd="character", gutter.pad="character", context.sep="character", pad.col="character", line.break="character" ), prototype=list( gutter.insert=">", gutter.insert.ctd=":", gutter.delete="<", gutter.delete.ctd=":", gutter.match=" ", gutter.match.ctd=" ", gutter.guide="~", gutter.guide.ctd="~", gutter.fill="~", gutter.fill.ctd="~", gutter.context.sep="~", gutter.context.sep.ctd="~", gutter.pad=" ", context.sep="----------", pad.col=" ", line.break="\n" ), validity=function(object){ for(i in slotNames(object)) if(!is.chr.1L(slot(object, i))) return(paste0("Argument `", i, "` must be character(1L) and not NA.")) TRUE } ) #' Styling Information for Summaries #' #' @export #' @rdname StyleSummary #' @slot container function applied to entire summary #' @slot body function applied to everything except the actual map portion of #' the summary #' @slot detail function applied to section showing how many deletions / #' insertions, etc. occurred #' @slot map function applied to the map portion of the summary StyleSummary <- setClass("StyleSummary", slots=c(container="ANY", body="ANY", map="ANY", detail="ANY"), prototype=list( container=function(x) sprintf("\n%s\n", paste0(x, collapse="")), body=identity, detail=function(x) sprintf("\n%s\n", paste0(" ", x, collapse="")), map=function(x) sprintf("\n%s", paste0(" ", x, collapse="\n")) ), validity=function(object) { fun.slots <- c("container", "body", "map", "detail") for(i in fun.slots) { if(!isTRUE(is.one.arg.fun(slot(object, i)))) return( "Slot ", i, " must contain a function that accepts at least one ", "argument and requires no more than one argument." ) } TRUE } ) #' @rdname StyleSummary #' @export StyleSummaryHtml <- setClass("StyleSummaryHtml", contains="StyleSummary", prototype=list( container=function(x) div_f("diffobj-summary")(paste0(x, collapse="")), body=div_f("body"), detail=div_f("detail"), map=div_f("map") ) ) #' Customize Appearance of Diff #' #' S4 objects that expose the formatting controls for \code{Diff} #' objects. Many predefined formats are defined as classes that extend the #' base \code{Style} class. You may fine tune styles by either extending #' the pre-defined classes, or modifying an instance thereof. #' #' @section Pre-defined Classes: #' #' Pre-defined classes are used to populate the \code{\link{PaletteOfStyles}} #' object, which in turn allows the \code{diff*} methods to pick the #' appropriate \code{Style} for each combination of the \code{format}, #' \code{color.mode}, and \code{brightness} parameters when the \code{style} #' parameter is set to \dQuote{auto}. The following classes are pre-defined: #' #' \itemize{ #' \item \code{StyleRaw}: No styles applied #' \item \code{StyleAnsi8NeutralRgb} #' \item \code{StyleAnsi8NeutralYb} #' \item \code{StyleAnsi256LightRgb} #' \item \code{StyleAnsi256LightYb} #' \item \code{StyleAnsi256DarkRgb} #' \item \code{StyleAnsi256DarkYb} #' \item \code{StyleHtmlLightRgb} #' \item \code{StyleHtmlLightYb} #' } #' Each of these classes has an associated constructor function with the #' same name (see examples). Objects instantiated from these classes #' may also be used directly as the value for the \code{style} parameter to the #' \code{diff*} methods. This will override the automatic selection process #' that uses \code{\link{PaletteOfStyles}}. If you wish to tweak an #' auto-selected style rather than explicitly specify one, pass a parameter #' list instead of a \code{Style} objects as the \code{style} parameter to the #' \code{diff*} methods (see examples). #' #' There are predefined classes for most combinations of #' \code{format/color.mode/brightness}, but not all. For example, there are #' only \dQuote{light} \code{brightness} defined for the \dQuote{html} #' \code{format}, and those classes are re-used for all possible #' \code{brightness} values, and the 8 color ANSI neutral classes are used #' for the 256 color neutral selections as well. #' #' To get a preview of what a style looks like just instantiate #' an object; the \code{show} method will output a trivial diff to screen with #' styles applied. Note that for ANSI styles of the dark and light variety #' the show method colors the terminal background and foregrounds in compatible #' colors. In normal usage the terminal background and foreground colors are #' left untouched so you should not expect light styles to look good on dark #' background and vice versa even if they render correctly when showing the #' style object. #' #' @section Style Structure: #' #' Most of the customization is done by specifying functions that operate on #' character vectors and return a modified character vector of the same length. #' The intended use case is to pass \code{crayon} functions such as #' \code{crayon::red}, although you may pass any function of your liking #' that behaves as described. Formatting functions are expected to return their #' inputs formatted in such a way that their \emph{display} width is unchanged. #' If your formatting functions change display width output may not render #' properly, particularly when using \code{mode="sidebyside"}. #' #' The visual representation of the diff has many nested components. The #' functions you specify here will be applied starting with the innermost ones. #' A schematic of the various component that represent an inserted line follows #' (note \dQuote{insert} abbreviated to \dQuote{ins}, and \dQuote{gutter} #' abbreviated to \dQuote{gtr}): #' \preformatted{+- line ---------------------------------------------------+ #' |+- line.ins ---------------------------------------------+| #' ||+- gtr ------------------------++- text ---------------+|| #' |||+- gtr.ins ---++- gtr.pad ---+||+- text.ins ---------+||| #' |||| || |||| +- word.ins -+|||| #' |||| gtr.ins.txt || gtr.pad.txt |||| DIFF | TEXT HERE ||||| #' |||| || |||| +------------+|||| #' |||+-------------++-------------+||+--------------------+||| #' ||+------------------------------++----------------------+|| #' |+--------------------------------------------------------+| #' +----------------------------------------------------------+ #' } #' A similar model applies to deleted and matching lines. The boxes represent #' functions. \code{gutter.insert.txt} represents the text to use in the gutter #' and is not a function. \code{DIFF TEXT HERE} is text from the objects being #' diffed, with the portion that has different words inside the #' \code{word.insert}. \code{gutter.pad} and \code{gutter.pad.txt} are used to #' separate the gutter from the text and usually end up resolving to a space. #' #' Most of the functions defined here default to \code{\link{identity}}, but #' you are given the flexibility to fully format the diff. See #' \code{\link{StyleFuns}} and \code{\link{StyleText}} for a full listing of #' the adjustable elements. #' #' In side-by-side mode there are two \dQuote{lines} per screen line, each with #' the structure described here. #' #' The structure described here may change in the future. #' #' @section HTML Styles: #' #' If you use a \code{Style} that inherits from \code{StyleHtml} the #' diff will be wrapped in HTML tags, styled with CSS, and output to #' \code{getOption("viewer")} if your IDE supports it (e.g. Rstudio), or #' directly to the browser otherwise, assuming that the default #' \code{\link{Pager}} or a correctly configured pager that inherits from #' \code{\link{PagerBrowser}} is in effect. Otherwise, the raw HTML will be #' output to your terminal. #' #' By default HTML output sent to the viewer/browser is a full stand-alone #' webpage with CSS styles to format and color the diff, and JS code to #' handle scaling. The CSS and JS is read from the #' \link[=webfiles]{default files} and injected into the HTML to simplify #' packaging of the output. You can customize the CSS and JS by using the #' \code{css} and \code{js} arguments respectively, but read the rest of this #' documentation section if you plan on doing so. #' #' Should you want to capture the HTML output for use elsewhere, you can do #' so by using \code{as.character} on the return value of the \code{diff*} #' methods. If you want the raw HTML without any of the headers, CSS, and #' JS use \code{html.ouput="diff.only"} when you instantiate the #' \code{StyleHtml} object (see examples), or disable the \code{\link{Pager}}. #' Another option is \code{html.output="diff.w.style"} which will add #' \code{", css.txt) } } if(html.output == "diff.w.style") { tpl <- "%s%s" } else if (html.output == "page") { x.chr <- enc2utf8(x.chr) charset <- '' tpl <- sprintf(" %s\n %%s\n
\n%%s\n
", charset, js ) } else if (html.output == "diff.only") { css <- "" tpl <- "%s%s" } else stop("Internal Error: unexpected html.output; contact maintainer.")# nocov sprintf(tpl, css, paste0(x.chr, collapse="")) } ) diffobj/R/s4.R0000755000176200001440000004273413465660152012554 0ustar liggesusers# Copyright (C) 2019 Brodie Gaslam # This file is part of "diffobj - Diffs for R Objects" # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Go to for a copy of the license. #' @include misc.R #' @include styles.R #' @include pager.R NULL # S4 class definitions setClassUnion("charOrNULL", c("character", "NULL")) #' Dummy Doc File for S4 Methods with Existing Generics #' #' @keywords internal #' @name diffobj_s4method_doc #' @rdname diffobj_s4method_doc NULL #' Controls How Lines Within a Diff Hunk Are Aligned #' #' @slot threshold numeric(1L) between 0 and 1, what proportion of words #' in the lines must match in order to align them. Set to 1 to effectively #' turn aligning off. Defaults to 0.25. #' @slot min.chars integer(1L) positive, minimum number of characters that must #' match across lines in order to align them. This requirement is in addition #' to \code{threshold} and helps minimize spurious alignments. Defaults to #' 3. #' @slot count.alnum.only logical(1L) modifier for \code{min.chars}, whether to #' count alpha numeric characters only. Helps reduce spurious alignment #' caused by meta character sequences such as \dQuote{[[1]]} that would #' otherwise meet the \code{min.chars} limit #' @export AlignThreshold #' @exportClass AlignThreshold #' @examples #' a1 <- AlignThreshold(threshold=0) #' a2 <- AlignThreshold(threshold=1) #' a3 <- AlignThreshold(threshold=0, min.chars=2) #' ## Note how "e f g" is aligned #' diffChr(c("a b c e", "d e f g"), "D e f g", align=a1, pager="off") #' ## But now it is not #' diffChr(c("a b c e", "d e f g"), "D e f g", align=a2, pager="off") #' ## "e f" are not enough chars to align #' diffChr(c("a b c", "d e f"), "D e f", align=a1, pager="off") #' ## Override with min.chars, so now they align #' diffChr(c("a b c", "d e f"), "D e f", align=a3, pager="off") AlignThreshold <- setClass("AlignThreshold", slots=c( threshold="numeric", min.chars="integer", count.alnum.only="logical" ), validity=function(object) { if( length(object@threshold) != 1L || is.na(object@threshold) || !object@threshold %bw% c(0, 1) ) return("Slot `threhold` must be numeric(1L) between 0 and 1") if(!is.int.1L(object@min.chars) || object@min.chars < 0L) return("Slot `min.chars` must be integer(1L) and positive") if(!is.TF(object@count.alnum.only)) return("Slot `count.alnum.only` must be TRUE or FALSE") } ) setMethod( "initialize", "AlignThreshold", function( .Object, threshold=gdo("align.threshold"), min.chars=gdo("align.min.chars"), count.alnum.only=gdo("align.count.alnum.only"), ... ) { if(is.numeric(min.chars)) min.chars <- as.integer(min.chars) callNextMethod( .Object, threshold=threshold, min.chars=min.chars, count.alnum.only=count.alnum.only, ... ) } ) setClass("AutoContext", slots=c( min="integer", max="integer" ), validity=function(object) { if(!is.int.1L(object@max) || object@min < 0L) return("Slot `max` must be integer(1L), positive, and not NA") if(!is.int.1L(object@max)) return("Slot `max` must be integer(1L), and not NA") if(object@max > 0L && object@min > object@max) return("Slot `max` must be negative, or greater than slot `min`") TRUE } ) setClassUnion("doAutoCOrInt", c("AutoContext", "integer")) # pre-computed gutter data GuideLines <- setClass( "GuideLines", slots=c(target="integer", current="integer"), validity=function(object) { vals <- c(object@target, object@current) if(anyNA(vals) || any(vals < 1L)) return("Object may only contain strictly positive integer values") TRUE } ) setClass("StripRowHead", slots=c(target="ANY", current="ANY"), validity=function(object) { if(!isTRUE(err <- is.one.arg.fun(object@target))) return(err) if(!isTRUE(err <- is.one.arg.fun(object@current))) return(err) TRUE } ) setClass("Gutter", slots= c( insert="character", insert.ctd="character", delete="character", delete.ctd="character", match="character", match.ctd="character", guide="character", guide.ctd="character", fill="character", fill.ctd="character", context.sep="character", context.sep.ctd="character", pad="character", width="integer" ) ) setClass("Settings", slots=c( mode="character", # diff output mode context="doAutoCOrInt", line.limit="integer", style="Style", hunk.limit="integer", max.diffs="integer", word.diff="logical", unwrap.atomic="logical", align="AlignThreshold", ignore.white.space="logical", convert.hz.white.space="logical", strip.sgr="logical", sgr.supported="logical", frame="environment", tab.stops="integer", tar.exp="ANY", cur.exp="ANY", tar.banner="charOrNULL", cur.banner="charOrNULL", guides="ANY", guide.lines="GuideLines", trim="ANY", strip.row.head="StripRowHead", disp.width="integer", line.width="integer", text.width="integer", line.width.half="integer", text.width.half="integer", gutter="Gutter", err="function", warn="function" ), prototype=list( disp.width=0L, text.width=0L, line.width=0L, text.width.half=0L, line.width.half=0L, guides=function(obj, obj.as.chr) integer(0L), trim=function(obj, obj.as.chr) cbind(1L, nchar(obj.as.chr)), ignore.white.space=TRUE, convert.hz.white.space=TRUE, word.diff=TRUE, unwrap.atomic=TRUE, strip.sgr=TRUE, sgr.supported=TRUE, err=stop, warn=warning ), validity=function(object){ int.1L.and.pos <- c( "disp.width", "line.width", "text.width", "line.width.half", "text.width.half" ) for(i in int.1L.and.pos) if(!is.int.1L(slot(object, i)) || slot(object, i) < 0L) return(sprintf("Slot `%s` must be integer(1L) and positive", i)) TF <- c( "ignore.white.space", "convert.hz.white.space", "word.diff", "unwrap.atomic", "strip.sgr" ) for(i in TF) if(!is.TF(slot(object, i)) || slot(object, i) < 0L) return(sprintf("Slot `%s` must be TRUE or FALSE", i)) if(!is.TF(object@guides) && !is.function(object@guides)) return("Slot `guides` must be TRUE, FALSE, or a function") if( is.function(object@guides) && !isTRUE(v.g <- is.two.arg.fun(object@guides)) ) return(sprintf("Slot `guides` is not a valid guide function (%s)", v.g)) if(!is.TF(object@trim) && !is.function(object@trim)) return("Slot `trim` must be TRUE, FALSE, or a function") if( is.function(object@trim) && !isTRUE(v.t <- is.two.arg.fun(object@trim)) ) return(sprintf("Slot `trim` is not a valid trim function (%s)", v.t)) TRUE } ) setMethod("initialize", "Settings", function(.Object, ...) { if(is.numeric(.Object@disp.width)) .Object@disp.width <- as.integer(.Object@disp.width) return(callNextMethod(.Object, ...)) } ) setGeneric("sideBySide", function(x, ...) standardGeneric("sideBySide")) setMethod("sideBySide", "Settings", function(x, ...) { x@mode <- "sidebyside" x@text.width <- x@text.width.half x@line.width <- x@line.width.half x } ) .diff.dat.cols <- c( "orig", "raw", "trim", "trim.ind.start", "trim.ind.end", "comp", "eq", "fin", "fill", "word.ind", "tok.rat" ) # Validate the *.dat slots of the Diff objects # # We stopped using this one because it was too expensive computationally. # Saving the code just in case. # valid_dat <- function(x) { # char.cols <- c("orig", "raw", "trim", "eq", "comp", "fin") # list.cols <- c("word.ind") # zerotoone.cols <- "tok.rat" # integer.cols <- c("trim.ind.start", "trim.ind.end") # # if(!is.list(x)) { # "should be a list" # } else if(!identical(names(x), .diff.dat.cols)) { # paste0("should have names ", dep(.diff.dat.cols)) # } else if( # length( # unique( # vapply( # x[c(char.cols, list.cols, zerotoone.cols, integer.cols)], # length, integer(1L) # ) # ) ) != 1L # ) { # "should have equal length components" # } else { # if( # length( # not.char <- which(!vapply(x[char.cols], is.character, logical(1L))) # ) # ){ # sprintf("element `%s` should be character", char.cols[not.char][[1L]]) # } else if ( # length( # not.int <- which(!vapply(x[integer.cols], is.integer, logical(1L))) # ) # ) { # sprintf("element `%s` should be integer", integer.cols[not.int][[1L]]) # } else if ( # length( # not.list <- which(!vapply(x[list.cols], is.list, logical(1L))) # ) # ) { # sprintf("element `%s` should be list", list.cols[not.list][[1L]]) # } else if ( # !all( # vapply( # x$word.ind, # function(y) # is.integer(y) && is.integer(attr(y, "match.length")) && # length(y) == length(attr(y, "match.length")), # logical(1L) # ) ) # ) { # "element `word.ind` is not in expected format" # } else if ( # !is.numeric(x$tok.rat) || anyNA(x$tok.rat) || !all(x$tok.rat %bw% c(0, 1)) # ) { # "element `tok.rat` should be numeric with all values between 0 and 1" # } else if (!is.logical(x$fill) || anyNA(x$fill)) { # "element `fill` should be logical and not contain NAs" # } # else TRUE # } # } #' Diff Result Object #' #' Return value for the \code{\link[=diffPrint]{diff*}} methods. Has #' \code{show}, \code{as.character}, \code{summmary}, \code{[}, \code{head}, #' \code{tail}, and \code{any} methods. #' #' @export setClass("Diff", slots=c( target="ANY", # Actual object tar.dat="list", # see line_diff() for details current="ANY", cur.dat="list", diffs="list", trim.dat="list", # result of trimmaxg sub.index="integer", sub.head="integer", sub.tail="integer", capt.mode="character", # whether in print or str mode hit.diffs.max="logical", diff.count.full="integer", # only really used by diffStr when folding hunk.heads="list", etc="Settings" ), prototype=list( capt.mode="print", trim.dat=list(lines=integer(2L), hunks=integer(2L), diffs=integer(2L)), hit.diffs.max=FALSE, diff.count.full=-1L ), validity=function(object) { # Most of the validation is done by `check_args` if( !is.chr.1L(object@capt.mode) || ! object@capt.mode %in% c("print", "str", "chr", "deparse", "file") ) return("slot `capt.mode` must be either \"print\" or \"str\"") not.list.3 <- !is.list(object@trim.dat) || length(object@trim.dat) != 3L not.names <- !identical(names(object@trim.dat), c("lines", "hunks", "diffs")) not.comp.1 <- !all(vapply(object@trim.dat, is.integer, logical(1L))) not.comp.2 <- !all(vapply(object@trim.dat, length, integer(1L)) == 2L) if(not.list.3) return( paste0( "slot `trim.dat` is not a length 3 list (", typeof(object@trim.dat), ", ", length(object@trim.dat) ) ) if(not.names) return( paste0( "slot `trim.dat` has wrong names", deparse(names(object@trim.dat))[1] ) ) if(not.comp.1) return( paste0( "slot `trim.dat` has non-integer components ", deparse(vapply(object@trim.dat, typeof, character(1L)))[1] ) ) if(not.comp.2) return("slot `trim.dat` has components of length != 2") ## too expensive computationally # if(!isTRUE(tar.dat.val <- valid_dat(object@tar.dat))) # return(paste0("slot `tar.dat` not valid: ", tar.dat.val)) # if(!isTRUE(cur.dat.val <- valid_dat(object@cur.dat))) # return(paste0("slot `cur.dat` not valid: ", cur.dat.val)) if(!is.TF(object@hit.diffs.max)) return("slot `hit.diffs.max` must be TRUE or FALSE") TRUE } ) #' @rdname finalizeHtml setMethod("finalizeHtml", c("Diff"), function(x, x.chr, ...) { style <- x@etc@style html.output <- style@html.output if(html.output == "auto") { html.output <- if(is(style@pager, "PagerBrowser")) "page" else "diff.only" } if(html.output == "page") { x.chr <- c( make_dummy_row(x), sprintf("
%s
", x.chr), sprintf( " ", if(style@scale) "true" else "false" ) ) rez.fun <- if(style@scale) "resize_diff_out_scale" else "resize_diff_out_no_scale" js <- try(readLines(style@js), silent=TRUE) if(inherits(js, "try-error")) { cond <- attr(js, "condition") warning( "Unable to read provided js file \"", style@js, "\" (error: ", paste0(conditionMessage(cond), collapse=""), ")." ) js <- "" } else { js <- paste0( c( js, sprintf( "window.addEventListener('resize', %s, true);\n %s();", rez.fun, rez.fun ) ), collapse="\n" ) } } else js <- "" callNextMethod(x, x.chr, js=js, ...) } ) # Helper fun used by `show` for Diff and DiffSummary objects show_w_pager <- function(txt, pager) { use.pager <- use_pager(pager, attr(txt, "len")) file.keep <- !is.na(pager@file.path) # Finalize and output if(use.pager) { disp.f <- if(!is.na(pager@file.path)) pager@file.path else paste0(tempfile(), ".", pager@file.ext) if(!file.keep) on.exit(add=TRUE, unlink(disp.f)) writeLines(txt, disp.f) if( isTRUE(pager@make.blocking) || (is.na(pager@make.blocking) && !file.keep) ) make_blocking(pager@pager)(disp.f) else pager@pager(disp.f) } else { cat(txt, sep="\n") } } setMethod("show", "Diff", function(object) { txt <- as.character(object) show_w_pager(txt, object@etc@style@pager) invisible(NULL) } ) # Compute what fraction of the lines in target and current actually end up # in the diff; some of the complexity is driven by repeated context hunks setGeneric("lineCoverage", function(x) standardGeneric("lineCoverage")) setMethod("lineCoverage", "Diff", function(x) { lines_in_hunk <- function(z, ind) if(z[[ind]][[1L]]) z[[ind]][[1L]]:z[[ind]][[2L]] hunks.f <- unlist(x@diffs, recursive=FALSE) lines.tar <- length( unique(unlist(lapply(hunks.f, lines_in_hunk, "tar.rng.sub"))) ) lines.cur <- length( unique(unlist(lapply(hunks.f, lines_in_hunk, "cur.rng.sub"))) ) min( 1, (lines.tar + lines.cur) / ( length(x@tar.dat$raw) + length(x@cur.dat$raw)) ) } ) #' Determine if Diff Object Has Differences #' #' @param x a \code{Diff} object #' @param ... unused, for compatibility with generic #' @param na.rm unused, for compatibility with generic #' @return TRUE if there are differences, FALSE if not, FALSE with warning if #' there are no differences but objects are not \code{\link{all.equal}} #' @examples #' any(diffChr(letters, letters)) #' any(diffChr(letters, letters[-c(1, 5, 8)])) setMethod("any", "Diff", function(x, ..., na.rm = FALSE) { dots <- list(...) if(length(dots)) stop("`any` method for `Diff` supports only one argument") res <- any( which( !vapply( unlist(x@diffs, recursive=FALSE), "[[", logical(1L), "context" ) ) ) if(!res && !isTRUE(all.equal(x@target, x@current))) warning("No visible differences, but objects are NOT `all.equal`.") res } ) setClass( "MyersMbaSes", slots=c( a="character", b="character", type="factor", length="integer", offset="integer", diffs="integer" ), prototype=list( type=factor(character(), levels=c("Match", "Insert", "Delete")) ), validity=function(object) { if(!identical(levels(object@type), c("Match", "Insert", "Delete"))) return("Slot `type` levels incorrect") if(any(is.na(c(object@a, object@b)))) return("Slots `a` and `b` may not contain NA values") if(any(is.na(c(object@type, object@length, object@offset)))) return("Slots `type`, `length`, or `offset` may not contain NA values") if(any(c(object@type, object@length, object@offset)) < 0) return( paste0( "Slots `type`, `length`, and `offset` must have values greater ", "than zero" ) ) if(!is.int.1L(object@diffs)) return("Slot `diffs` must be integer(1L) and not NA") TRUE } ) # Run validity on S4 objects # # Intended for use within check_args; unfortunately can't use complete=TRUE # because we are using ANY slots with S3 objects there-in, which causes # the complete check to freak out with "trying to get slot 'package' from..." # # @param x object to test # @param err.tpl a string used with sprintf, must contain two \dQuote{%s} for # respectively \code{arg.name} and the class name # @param arg.name argument the object is supposed to come from # @param err error reporting function valid_object <- function( x, arg.name, err, err.tpl="Argument `%s` is an invalid `%s` object because:" ) { if(isS4(x)) { if(!isTRUE(test <- validObject(x, test=TRUE))) { err( paste( sprintf(err.tpl, arg.name, class(x)[[1L]]), strwrap(test, initial="- ", prefix=" "), collapse="\n" ) ) } } } diffobj/R/myerssimple.R0000755000176200001440000001367013466146340014573 0ustar liggesusers# Copyright (C) 2019 Brodie Gaslam # # This file is part of "diffobj - Diffs for R Objects" # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Go to for a copy of the license. # These are deprecated legacy functions from before we incorporated the # libmba versions of the myers algo # Alternate implementation of Myers algorithm in R, without linear space # modification. Included here mostly for reference purposes and not intended # for use since the MBA myers implemenation should be far superior myers_simple <- function(target, current) { path <- myers_simple_int(target, current) diff_path_to_diff(path, target, current) } myers_simple_int <- function(A, B) { N <- length(A) M <- length(B) MAX <- M + N + 1L OFF <- MAX + 1L # offset to adjust to R indexing Vl <- vector("list", MAX) for(D in seq_len(MAX) - 1L) { Vl[[D + 1L]] <- if(!D) integer(2L * MAX + 1L) else Vl[[D]] for(k in seq(-D, D, by=2L)) { # not sure of precendence for || vs && # k == -D means x == 0 V <- Vl[[D + 1L]] if(k == -D || (k != D && V[k - 1L + OFF] < V[k + 1L + OFF])) { x <- V[k + 1L + OFF] } else { x <- V[k - 1L + OFF] + 1L } y <- x - k # Move on diagonal while (x < N && y < M && A[x + 1L] == B[y + 1L]) { x <- x + 1L y <- y + 1L } # Record last match or end; if a mismatch no longer increment Vl[[D + 1L]][k + OFF] <- x if(x >= N && y >= M) { # Create matrix to hold entire result path; should be longest of # A and B plus recorded differences path.len <- D + max(N, M) res <- matrix(integer(1L), nrow=path.len, ncol=2) res[path.len, ] <- c(x, y) path.len <- path.len - 1L for(d in rev(seq_len(D))) { Vp <- Vl[[d]] break.out <- FALSE repeat { # can't match to zero since that is the initialized value shift.up <- Vp[k + 1L + OFF] == x && x shift.left <- Vp[k - 1L + OFF] == x - 1L && x > 1L if(x <= 0L && y <= 0L) { break } else if(!shift.up && !shift.left) { # must be on snake or about to hit 0,0 x <- max(x - 1L, 0L) y <- max(y - 1L, 0L) } else { if(shift.up) { y <- y - 1L k <- k + 1L } else { x <- x - 1L k <- k - 1L } break.out <- TRUE } res[path.len, ] <- c(x, y) path.len <- path.len - 1L if(break.out) break } } if(any(res < 0L)) { # nocov start stop( "Logic Error: diff generated illegal coords; contact maintainer." ) # nocov end } return(res) } } } stop("Logic Error, should not get here") # nocov } # Translates a diff path produced by the simple Myers Algorithm into the # standard format we use in the rest of the package diff_path_to_diff <- function(path, target, current) { stopifnot( is.character(target), is.character(current), is.matrix(path), is.integer(path), ncol(path) == 2, all(path[, 1L] %in% c(0L, seq_along(target))), all(path[, 2L] %in% c(0L, seq_along(current))) ) # Path specifies 0s as well as duplicate coordinates, which we don't use # in our other formats. For dupes, find first value for each index that is # lined up with a real value in the other column get_dupe <- function(x) { base <- !logical(length(x)) if(!length(y <- which(x != 0L))) base[[1L]] <- FALSE else base[[min(y)]] <- FALSE base } cur.dup <- as.logical(ave(path[, 1L], path[, 2L], FUN=get_dupe)) tar.dup <- as.logical(ave(path[, 2L], path[, 1L], FUN=get_dupe)) path[!path] <- NA_integer_ path[tar.dup, 1L] <- NA_integer_ path[cur.dup, 2L] <- NA_integer_ # Now create the character equivalents of the path matrix tar.path <- target[path[, 1L]] cur.path <- current[path[, 2L]] # Mark the equalities in the path matrix by setting them negative path[which(tar.path == cur.path), ] <- -path[which(tar.path == cur.path), ] # Remaining numbers are the mismatches which we will arbitrarily assign to # each other; to do so we first split our data into groups of matches and # mismatches and do the mapping there-in. We also get rid of non-matching # entries. matched <- ifelse(!is.na(path[, 1]) & path[, 1] < 0L, 1L, 0L) splits <- cumsum(abs(diff(c(0, matched)))) chunks <- split.data.frame(path, splits) res.tar <- res.cur <- vector("list", length(chunks)) mm.count <- 0L # for tracking matched mismatches for(i in seq_along(chunks)) { x <- chunks[[i]] if((neg <- any(x < 0L, na.rm=TRUE)) && !all(x < 0L, na.rm=TRUE)) stop("Internal Error: match group error; contact maintainer") # nocov if(neg) { # Matches, so equal length and set to zero res.tar[[i]] <- res.cur[[i]] <- integer(nrow(x)) } else { # Mismatches tar.mm <- Filter(Negate(is.na), x[, 1L]) cur.mm <- Filter(Negate(is.na), x[, 2L]) x.min.len <- min(length(tar.mm), length(cur.mm)) res.tar[[i]] <- res.cur[[i]] <- seq_len(x.min.len) + mm.count mm.count <- x.min.len + mm.count length(res.tar[[i]]) <- length(tar.mm) length(res.cur[[i]]) <- length(cur.mm) } } if(!length(res.tar)) res.tar <- integer() if(!length(res.cur)) res.cur <- integer() return(list(target=unlist(res.tar), current=unlist(res.cur))) } diffobj/vignettes/0000755000176200001440000000000013466146510013673 5ustar liggesusersdiffobj/vignettes/ansi256brightness.png0000755000176200001440000013706613014331136017666 0ustar liggesusers‰PNG  IHDRT›¬™‚iCCPICC ProfileX ­ywTÍòvïl",9ç%'‰’3KÎ9£²,9KT$Š$ ¢€HðE ¢("ˆ " ¢bEEQ”$_ƒâ{ÏïÜûß·çlϳÕÕ5UõtÏtõÀC GD„ Œ„†EGÚ›ˆ®nîDü@à€ S¢"ômm-Áÿü,Amøy(³iëªý÷&ß( ([ØííE …ø2zJDd4Ø^(ދިij³FB!^ÛÄþ[½¬Þ¿±È–Ž£½85¨dr¤?ô†PNŒ¥øC;ô>°9Ì'0 ‹‡X‡@†2#44|¿…XÂû?ìøÿ&“½ÿÚ$“ýÿâß±À‘ðƆQ!ä}[?þ6¡!10_[AØ"Míá•æ­,8Üb n 󶶘âk0¢?x( ÆÔ âMý J”Ì%`‡ø›ÙÐb^š˜`'ý?XŒ Ñ–>B Œ6süƒ#ÃíÿØG‚ÂB¬7ç´ƒìð5ÛÆy¾QFP}@‚üÍ †\!µ Ž.C?‘ŽØ@gkˆé!î vØôaÓÎhB€Á¦|K'2Æ~Óg(Ÿõ‹4ÞŒê  ¡QmÙG QÈ[÷â„r¥èGS(‡cÑ–>¾†FÃû¢]}Üþøƒˆˆ&mÚÙÔOˆÙšßÐOtžoˆÉ¦\âê¨X‡í±·£#7å0oè± ²ùæ|…>£ßGDÛnædÓŸŸÀC@1ðë ÂAšoŸ‡¿~÷2ˆþÀÈü‘lpÙê ƒ­HAÔ‰ú;Ž´Õë b¡|ý¯ô÷Xà·Õ»5"¼ƒwÅpct0šKØêÁ¯"F £¾=ŽÈ°í'Îgˆ3Åã$·%€½ßHø_d°ÏF Û°íþµ‡}‡Á¾Á>ÆN`ŸgðvËÊŸH÷&Gn{ðײ˜€Ö~gÅf, Ìmë`Ä ×ÊFú}ǰc¸ F F¢Ñ…±)Cévö6½ŽùëÛ¿¹ÜÎû¶Þ¦×Äÿˆñœ^Š^ùÞÛQA&·3ñ­üÛ| –ÅÿÕDE· ûÑ7ÑwÑ×Ð퀈¾î@¢»6ñŸ·²ãÿ÷nö[ †1nëÈ7ÈÏɯmÿú+J6=ØäÎÿhßøh8ÿ€AxľÈ@ÿ€h¢>| ûÍÂ(²;ˆŠò *l>Ó7uX´ßzV£Øü+ €Ï=Í2¨}þ•ùePNw÷eâ'àº@k8%&2ö·=Ìæ h\\€ “"Pš@s`Ø ³B¡×q`?8 Ò@8 @ ¨U üšA;¸n‚Û` ƒÇà9œÓàXK`…BáQt(J%Š’F)¢ÔP:(#”%Êå†òBù£ÂP1¨ý¨T*U‚:‹ªC]B]AÝDÝE ž¡&Qs¨¯¨V„Cä5D±@‘]ˆ?²I@R‘¤©D. mÈMdyŒL ïh€¦E³£Ñ2h5´ÚíŽöCG¢¢3Ñ…èJt#ºrý=žG/cp #ç§)Æ CÁìÅÄdcJ0µ˜6L/æ!f³€ù…¥Ãòb¥±X3¬+Ö‡MÃbk°­Ø>¸v¦±K8Ž'ŽS…kÓ „KÄeãÊqqݸÜî;çÂKãµñ6x2>Ÿ†?…¿€¿ÅOãRÑR P)RS¹S…Q%SRÕS]§¥š¡Z¥f¤¥Ö ¶¡ö¡ÞGK]MÝIý€zšz•†‰FœF›Æ‘&ˆæ0M1M#MÍ šEZZZ!ZuZ;Ú@ÚC´Å´M´wh'i— Ì)‚Á“CÈ!œ'tžéèèÄèôèÜé¢érèêènѽ¢ûIÏB/KoFïCŸD_JßF?Jÿ‰šA”AŸa7CC!C ÆyFjF1FF2ãAÆRÆ+ŒO¿3±0)0Ù0…2e3Õ3ÝešeÆ3‹11û0§2W1ßbžbA³³°PXRXªYúX¦Yq¬â¬f¬A¬Y¬ÿ°±.°1³)±9³Å³•²u±M°£ÙÅØÍØCØsÙ›ÙÇØW8ø8ô9|9289F9~pòpêqúrfr^ä|̹ÂEä2â æ:ÁÕÎõ’Ã-ÅmÇÇ}š»{ž‡•G“‡Â“ÉÓÌ3΋ðJñÚó&òVñò~çãç3á‹à;Åw‹ožŸ_?ˆ?Ÿÿ:ÿœ‹€Ž@ @¾À ÷D6¢>1„XLì%.ò š Æž\rJº(ôR˜FXMØO8_¸GxAD@ÄJd¿HƒÈ¸(µ¨šh€h‘h¿è1q1±t±v±YqNq3ñññtº{%*%Iâ$Õ$ƒ%Ë%‡¥)e©©R©Òˆ´Št t¹ôÈìõa;*w<‘!ÈèËÄÊ4ÈLʲËZÊ&˶Ë~’‘s—;!×/÷K^Y>D¾Zþ¹³‚¹B²B§ÂWE)EŠb©â£t;w&íìØùEIZÉWé´ÒSee+åtååuU•H•F•9UU/Õ2Õ'j¬j¶jÙjwÔ±ê$õ$õkêË*ÑÍŸ5e4ƒ5ë5gµÄµ|µªµ¦´…´ÉÚgµ'tˆ:^:gt&tuɺ•ºoô„õ|ôjôfô%õƒô/è"É“"I­¤ º ц&†™†CFÌFNF%F¯Œ…ŒýŒL”MMºM±¦¦'LŸ˜ñ™QÌêÌÌU͘÷Z,,J,ÞXJYFZvZ!VæV'­^X‹Z‡Y·Û3›“6/mÅm÷Ú^µÃÙÙÚ•Ú½³W°ßoßïÀâ°Ç¡ÞaÉ‘ä˜ëøÜIÂ)ƩǙÁÙÓ¹Îù‡‹¡KžË„«œë×7n·@·w¼»³{ûw#iOeÏ4ϱ]â»âwÝÝͽ;dw׆=ä=-^X/¯z¯5² ¹’üÝÛÌ»Ì{b@)¢|ðÑóÉ÷™óÕöÍóñÓöËó›õ×ö?é? P0hXø%È4¨"èG°Mðùà—‹¡T¡^¡W˜ÂÃzÃùÃãÃG"¤#Ò"&öjì-Ø»iY…ŠÚÕÍ 7σ11Gb&cubKcÆ9ǵÄ3ŇÅî“Ú—±o&Á8á\"&‘’س_pÿáý“ôœ=ˆ:è}°'I8)5iúÉ¡ÚÃ4‡ƒßO–OÎKþ–â’ҙʗz(uêˆÉ‘†4ú´È´'éšéG1GeìÌ8•ñ+Ó'ó^–|VaÖZ6%ûÞ1…cÅÇ6rür†rUrOÇ;>vB÷DmS^BÞÔI«“mùÄüÌüo{ î*VÑÅM[wœ9uüÔZI@ÉãRRéÅ2Þ²Œ²å>壧õN7VðUdU¬œ <óô¬ÉÙ¶J±ÊÂ*\UlÕ»jçêþsjçêj¸k²jÖχŸ¨µ¯í­S­««ç­Ïm@bæ.x^þÇðŸŽF™Æ³Ù/f5¦˜¦÷—¼.5[4÷´¨µ4^½\ÖÊÒšÙ†jÛ×¶ÐÐ>ÑáÖ1rÅüJO§fgëUÙ«ç¯ ^+íbëʽNs=õúƄ߻#ºçoúßœêÙÓóü–ë­G½v½C}}wnß¾Õ¯ßãŽökw5î^¹§v¯}@e mPy°õ¾òýÖ!•¡¶ª:†Õ‡;G´F®êŽÞ|høðö#³G­Œ9=}âùdâ©ÏÓÙg!ϾŒÇޝ>?ôû"ó%ãËÂW¼¯*_K¾¾8¡2Ñ5i89øÆáÍó)ÊÔ‡·QoצSßѽ+œ˜©›Uœ½6g<7üÞãýô‡ˆ«ói™>–}’øtù³ÞçÁ×…é/‘_6¾f/r-žÿ¦ô­ç»í÷WK¡K«?2rý¬]V[î_qY™Y[ï¯K®wþ²øõb#tc#‚IÞÚ  a‹øùðîèÜ`€†þw͵¥·È(¨« ÄMÆ\ àITYÔ´X‚=]ýsF¦$滬\lqìCœ;¸R¹Ÿójóæ_'z ö ‰dˆ.ˆÛKtHñK'5•» @£²sHYZ%[uB£!¨©®å ¢“¦[¡×®?Bš5Ø0b7–514u3 1O²È·‡ƒ“£RS9–v*½úhSFWæ@Öxö̱¥\Üqö¢yÊ'ò ¼ #ŠgŸ*)9_ÚRv£|àôXÅÄ™g+׫±çèjXÏóÔ Ö‰ÕK7È]ØùJ£ÚE&­KÚÍ:-:—u[uÚ´Ûµ:4¯¨wª^U¹¦Ò¥ytÃ¬Ûæ¦KÏž[~½¡}Q·ú“î¤Þ͸—3pbðäý‚¡‚ùÃy#9£S%>Þ;æ÷Äí©Å3Íq©çœ/Ð/>½{Õõºz"c2ìÝ”â[Ö·‹ÓmïöÍhÍüšíž;üžôùÐ3è£öÇe8gBDÆ¿dÕþúq±ø›þ·¹ïÙK2K?|üúybYxùÒŠÖJߪÅêКõÚàºÉz÷/Õ_ ¹ÿ•wºúÏŒ*ÿ1VI¶ì£ÿ î^¾j*¢¿àai‘Ñân×þðO’;§€QôÝÙ£LTIP½«öN}Qh´9tDteôÔõ IÖN†»#LbM˜¥šgZäXæ[Y—Úœ¶=kWeÎáœcÓyç—×·÷sÕžU»Îî®ØSîUJ.ñ>E)ôÉõM÷Kò ˆ òöq µ3 'EhïU‹Ü%-#+GŒçÝÇÀ‘ȾŸõËAæ$¦CL‡™’™SàÖåKk:ëQö ¸ÙÈâÊæ9FÌ‘ÌU>N:açq2 ?¦ ¹0·¨¼¸þTGIé㲩òÅ ä óY¡JÅ*ƒjÇs¾5qç3jËëëo6<º0ûÏúE¦&ÑKͶ-¾—ZsÛjÚ;;F¯Ìv®]Ãw1^ç¼Aì–º©Ô£sˤ׮Ïý¶OØø»)÷Ž  ž½_?tùA×ðí‘£ON<šyüiìÛ“ÕgÈ8þ9áãKÖW¯¹'x'ÞM ¿žyÇÿncf|¶e.ó=ùƒò<Õü³µŸ¢?ë,`î~9úÕèëÆbó7ïï ßÛ—Ü––ÿ)úóò²îrߊÑÊÍUõÕ k¼kGÖ>¯Û®7ýbøåûëÚó†×FÓÿ¢¨[H!ú& À{P™SëÒÈÑJ$é„é…™$˜¥YYUØôØ-9œ9)\aÜxrxKùjùÛúˆß }^¥ã’•Ô’"I³K¿ÛqY&EÖANXnA¾K![Ñ}§Ü·\WÎRqR%ªÎ©µ¨ï×0Ò$h>Ô:¥MÖ×™×½¤£¯®¿Fê6H644ÂÝ3Î2±4¥32Ë5·µ`²µ,°r·æ³ž°9gd'c÷žÕaŸ£–pêu>êbéÊàúЭÐÝ݃Ïc³r—ïn‰ÝöüãNV /z·Râ|Ô|–};ýý5ý×® Ò úÜ’jF6^±k¯ðÞ÷‘-Qû£bbžÇž‹Š×ßǰïuBSbò~ÇÖŽ&Õ:|Ø-Y1…2“zëHEÚþt÷£êœK™cYíÙEÇâsÜsuŽ‹`:±–÷îähþ‚†Ââ¢Ôâ¨SäÛRý2ÅrÑÓœ„3¨3Ëg¿T~¨š®~sn²fâüdíTÝlý§†ïÖq›x/I4«´_vnõk‹m?ÚqúJwçÜ5–.ë!7J»‡z°·ôz“úzú w<ï^ Œ¼ÿâÃðƒQׇ³SŸH>}:~ò…û+‰ drzêÑôÐÌðÜø‡…Oô ;¿z}Ë_YfZõXoØäÿ÷ÙÛæ;«ÓBx®ãüGx-ø €84ØÒA™:@&Mò( r=·ßp  aÝIháÉàç5R°êÔ‚U¼ØkèXpäÃÊò2èOÀ{ð ÖŽ°btB…¢ÒP°2|Œú‚Ð!Òˆ9ˆd" È}ä š ­öB§£›ÐÏ0hŒ,ÆsÖmsX1,[…} k²`Ü%Ü2ž„?†§’¤ÚG5@-@C}ÖPÉ4¯iuiÏЄÂ:5ºjz&úCô_|Æí˜L˜z™ ™o³˜³ ³º²¾a g[c?Æ!Êq›3ˆ‹‘«Û‹‡ÀÓÉÌÇË7Ÿ. +ð“xYp¯¼Ðá‘8Q-1´Ø xYRFrUêžtÉŽP=YÙr·åÏ($*ºîTQâPZQ~¥Ò£Z¯V ~H#LÓKË^ÛTGOWCOE_™¤j iH2²0v5ñ3Ýgv̼Úâºås«U~[C»ûb‡~ÇegY?׳n¯<<)»êvõÒ%çzOú(ûæøÍžúbZŽðÞÛ%³çÿ8Á4ñú•ƒ-‡”w¤R¤‘Ófd)g?Ï9z\ãÄד{‹4NÑ–¼.»vºâLZeTµ_ ¹–\ï}áÊEÖKñ-¯Úl:ú®’ºîu{öü쫾³k@r7¼òÿD~<þåÛÉøi•9á¤/ÅKR+K›óçò¯ ù·ž[ü§‚“ÿ–?ü¯£˜QâGTêê4äÿjò/…˜Aþ3þò¯‰&ÃÓ›Kèq #Ù…ÉÂtb>ae±áØfìœ. 7‚çÃã¯RÑSySµS3PQߦ¡I¥™¡µ m&ðÒ`ML¡{ëß ª ÍŒrŒLrL-ÌjÌÝ,,OXýX°e² °·q8p,rpip½æNçQäy͛ç˷È_+°‡È Ÿp'„ì„Y„ÇDÊD}ÄdÅVÄû%Š%¥t¤Y¤?ìè“9+{HÎKÞ@AB‘AñÇÎ)¥å›*ͪ•jÇÕj„kzk9k[éëêëéèk‘´ ô ŒÌL Lˆ:âC® 3ð<Lj¾ˆžÆðc\1'1ÃX"6;ˆÃ¥àÞâMñT\TéT?¨C¨ßÒxÁ•íM;Gˆ¦CèòáYa…ËØÀäÌŒfne ac}ËVáÄñ‹së4w$¯ßGþÇÝÄ&Áj¡Rá"‘BÑR±*ñF‰’¤æwPËHÉÚÊ%È×)¼ØÉ¬d¥œ£2¢Æ®î¡Q­ùI[['GwR_…”c0kd`\iŠ2£˜÷YJZåZ/Ùî±»ë äXéÌä’ìúÍ=Àãõ.gÈ•ù>ÅÊgÔÏÅÿu oÐÛ¿ÐùðȈ•È#ÑÌ1UqŠñ·Ü?HMâ=t9Ù:eæÈ¡t™¶YsÇŽä ¿šç|r¡ »H¼¸¯Ä» ”WTèžyUy°šx®÷|PC}Óýî_tkz×Õ²ÞšÑÎÒQÒÉõL—Àõònö›™=k½Á}û5î”Ý]°¬¹ÿói8cdè!Ý#³Ç‡ÇZŸ¼{Æ<®ýÜûEÚ˺W·_¿™XyÃ2%öV}ÚäÓ y6d.úý‡ç|LûtôsúBú—ԯɋ¿íûµüÃû§Ç²ÃŠéªöšÂºè/® Â&ÿQ~;?$xüøjccQ |ë'66V+76Ö«`±ß)Ý!¿ÿÏÙÔÞüŸè # 596 411 +Õþâ@IDATxì} @Õ÷ÿvÙåýRâE…D15CPó™¡)hù¤ÌÌ”òW¾¾i™=þjfi__õÕÔL-µò‘ü¦™¢–Šäƒ/ * **à »ì.üïÌîÂ.Ì.³»³0 g"wæÌ½çžûùÌ™3÷Î{í²²²7D@D 9!`ßœ*‹uED@(0øáu€ ˆ"Ðìð©ÆÊœÔäå pòíÖµ¯,ãJh "€ ˆ€¥è´üäéMÚ˜Oë”¦ï š“.7µù¹­ëŽgKMÍF§W]OØ¿jܸ1#ÌËoV¡˜ @D Ù! ü”Ê2RýÄ応+ÐÂ_®Ôî²þÍX¼*³Ä¼f›hè¬Ï<±Œe^~Ö6bBD@f€NðüBaÕ’$ú©ÃG’y|i’-zé…|º1(Ï\:i©º](Ï<8ãÃ#D*M?²`ù{¯OŠúpOºZyúÒIsÖm\ªVq$“¤•Ÿ^7ƒ> š±.]Rü¼¢æ÷D@+  üJ–-ß?3}ÕÎd©ƒ:î‘"%& yó©]gÉGGß.±n–ŸM<«n*Ë íÊ“¸tŒÞ—vzÀ¼O¾Ø¶/åýµ+ï%X•z6%íìÑ¥m\)±_äÛIiYYÛ;®ŠþòxM }ZÔð@DÀJÔ 5î]¦mëµpwì:F‘öܽTÒ‚;µmå¥meÎpàòmi¼¼Èô& uPÿD.‚>¡PêÞBäâ"Ò1¹B÷/èàÑ;’+ oÓ¢ ×ÊÀ„\ š“5¦„êäÄ]D@D€kj"ŽZsE¹ÒgÀÿM›Òwõ¡ÑnÚâæ}±rš«’´î‹W \H £û>…tîÇ÷ïChkmJ ½–޵µ’“šH©N¦Ìþï€qÖîß8«s‹{¿/:¶E¿«3½„jGVkÄD@D€Sôº=iͤ2pÆö™ÖP$jÙ`Õ?¹2Wå½y*rÂÁ­ $&\!= ’‹ [ÀËQk•À?Ž%ÞƒRYÓ•©=©ý•— îÓ£‡¨4åÏ:ÙÁ¥ÍÓäµã™L‰RNXmüED@¸B@7ø ªz~^™Gõ=:“¾H߀³»>Ý‹ŒOéÒkäžT*¨ £?½jv?Y nÕÁOô씕°xLHPÇ™?¦êZYý‘]Úž¹>‚ÒqÚ9´¦…Ië²tYÜì!Ý;†¼›Œ_<è"ˆûˆ"€ !`ÇznO¹T"ˆ]D¢š>M"R’NP‰IVI¥R ²›” #ˆ"€ –"À>øYZæGD@x‚€n·'OLB3D@ë"€ÁϺø¢vD@"€Á‡¤ Iˆ"€ ÖEƒŸuñEíˆ"€ .6Z‹&l¶*_ùM„⾺†Ãìß]-j‹,Ù2ߪüÔ‚MëJ¶s;šæŠ\òŽK¥,ñ½s¨íN>Ñé`[½»7¶¶Ùò«H>™4ò®¢…€ªRÙ@Pa1¦ PQTå8Oðn’xI’c‡c•›w«LÉiy†€49;bdIA r;­R¢ÇñŒÚyÅ#g·E':Kë¼1Nñãÿ=H壕|²É:Á¯RZ{äÌ© ”Gö•-Ê^ñ{Bù;}iéq²snki%(%GþHø0WN©)¾·î÷¿×W:ôìž4¢Ï?{ G¸±GàæNÙgK+nf›Ùùà¸|~P9ù[»TþÙ™QQ „ŠlÅÇA²ë4Õù§åó'Éó•à*|{–°­Ÿ“ŸCô§v²ÄÊ öVbJSPJË.¹”Ÿj‚ÇÉŽ¯È "w—.½Cvfl­PBeö‘ÛA–Ðjä§×eNZ''ÁNÔÁïhRðš%Ρ`æ¥cjšJúÔûÿštë§TiyÊ~øUЕò7çÖ²Idçúöü*KF_ù(YA©ÌyôMPÚ79Uàâóyû˜`±‡‹øé×<;§KoÓwMóŠm¹¸ ~ÊÒ¢äÔä9¿Ÿ KÊøÃ³ÝžN^ìQ¬&ƒ×®~a ¥’ò‘GÝeÇž(Aà1<Ü÷NrâÖGÏÜ\Üuš;1ÙÞ£¥€œfŸ} ˜Ú öõ­Ú4@6?H¶÷ 2ßÄrÒ{U0SøÞ~‡û[ª^»D5éÈÁ±0n!|?¡âNºâ«)•#>sôÓío‘«-®ê0ÞÁáçeEfòÃs2;†å~ü‡höïN&xœò^2ÌÜÕzÿBÅ–r¿KG“ÉÁ¾Ýð– ï<œ°Uš~ðþ”UŸM6.¡”Ç᳦©vj1c¢àöÈÌAW&oÌMÌQ˜’Jd)à²æl›iÊÊ? ù!Nqü® D.ÑËݳÇdlO/>Ô7÷ǵí¦Ú騥ÊüöÉ8Ÿ^"îÖE@÷.U÷¬i’ük“/å%‚ëÊÈ~_y9š£ÙAäçêüì]½œ:z ÝÔÅtÝVúOÈ…ð I ñ— ¦‘R+µ£ŸÃ xòO2•›‡(’@Ñï°8¶«®ëÔÊQûPÜÒ®…¯„Úú8´‹Ô<~</ŠÉ–­†ž»Dƒ‚uµUî­¸'X2”ÛǬÚV5ÏãÒ­“ó'BÜÊÀ´¯Ä.æxœ]K?GßGêêäÓQ©ñ8·ømŠì¼hpÞ•Ô"=΢‹K$‰ Z ‹ JÿZx{ÎòGði»„©& *ôõùƒ½[;Qp˜ƒ«ÚšAßm¸ñ\tÌku(ÖUW[þÆëqÄ‹Ó|-²»dæò–äÓþ³Nþ3Kç'ž›™|ëtQ¹ñgyqQú£¢l½Þ°J…½[°TT‚JçQ)Õñ «(­ÅˆulŽÏ×ÒÓ¬ 2•¿¯-¢(fÿüvÇtcU-$ªò3Uw2+ËuÅàhïKÞ¶B•nË»ä1•è‰^S²ro´<©§`ÉçBòv7ÎpŽùÌëÓ™ö»æç¼<3ïàiYA='ÏL—ffëx@…Â. Œ¨¨•Nf©’fS)©íqôµ‚g“ÊüÒ ;3ÿÕëöü»Â˜•côbU-UòœÒŒôÒl½ñš•Js‡aP¡•Îݲ´&2O©ËQÎÆÔ˜åŽ‹Ó:GëÆÃZeà¡.ƒŸÀ)¸S—Cgôëöº½dgbbH²‘w~×þI޾_ Ã'eSíŽeæ¥^waås½·ß’’¯y¥QY!­¨xBn¾ Ù㊠ìa{=ßÜZþÅÅã@‡¹—ÄK7‰† pð4Ò9"­Ü<¤býy–ž/Ôñ¸Tv>8+¼ývÅÁdú.*§#Ÿ—Ã{‹ ©*‘êÜZÙ‹éêAÀÞ'Ø{ê‚öY­×¼nwegN¯<#ïü¤× ‰Î_V›Ê¢¥½J`e›³Û+Þ’Ÿ¬ñ¸*©T%}B¼SUðX%•#õp£=z÷¥ˆÛKsD#N‡$}jѸ–OûÐÏìÚóú¿ÊëïÝ~-úö»7j¹Xm¸•ÇoX ’:®Üõ0nkÍ‘*{cêKËífží0‰Òø“~¹ÍòÈq"wŸ¨îäO)­°7üüaï$$ϤûL‹»½½¦×t.Žâìå’¸Þýƹ:ÂsO-ü+í-÷[; Ýš{)ï•õæ€ã7‡õzn“Þ{&­Nü­…@‡ñâ/¦Úé\ë¼þ¡%°«¾JÕ/î¼jRæ—ÿºÅnfš#ùääƒ=•_Œ‘w¸$LJ'iT_…Ѩ‘K~rÄö_ j\î‰D¡Q~Gù-ª {œÀ‰ð^ ¡Õ-};G­Çé°)?¾¼pKœÚ8Gh³gá½1o9]úÑÝG Ý=71íqSdÁ0¿ŒM®F›¸¬më mõÓ­¶¬»¤Éø0ªºÈ½Q½ ì5ýœPMäòðË7Ëž?ÚåerÓKj=-âÞ;]·v‘[NµÖ÷½¶žÊë0'-l’á«A­¾Yÿk—••Õ¬ÀÊ#ˆ"€4?ªÛ]ͯêXcD@抿æÊ<Ö@fŒ¿fL>V@抿æÊ<Ö@fŒƒ_•¤B!Õýè¨ÓÃת³åH®PJÈ”Y¸ñ¶lòºMÜ8¶¡Ç±¿ª±³ÏROJ©´ä¢DBñ3-ݪÚ2 ë*R–}rõÁ.úħB¦z×cÞ…ÌãÃáž»V ®«€±tF!Sî&(c¬;£°nåÙr¤*™sñ®fŽyëÑðv¡ÈQ]4¹0Ç(¬[[6µ9Ñã´H˜öËH£°®^¶¡ÇÕÅΨ„ãOÒïfFçʆ9;AYù1{׳½ÚڣɪŠI7—;{Ÿ õ——æ \»:k˜Ê‹sC®?ÆÒ…L&5AcÝ…L•gÍ‘Jvîay;7ÏÊòÿ¤ÜMhÑæh°;“BJÆX:£Ð†f+gD‰QÈk6éÌèqLÖ/c¤ƒQȤ‹5GèqLð‘qü¤…÷Ãn–íïѹùº²bgòÍŠö!ã!¯®±I'/É ¹Vr´g§öU ¥ÀÑÅXà#5*_‘t»eoJVÁ”ž¡= ´*,4Ép6zÊB@LäHRêÍk#+[e…x2‚f¡IŒ:›‰ÐBèLd=Μ˪a9ÒXˆdžªzú٨Ц©üýŽda§.²Ü äB ¡«>F¡6‡þ¯REæ'PD_¾¦¯ìÔiœwõ„"úIR3³×;ûeù‰&eÕ>¥sÌX:£P'SSÞe¬;£“8‚Êò¹·+d[dUžÒNTQ[1céŒÂÚ9›ý1#JŒBf¨Lb=ŽÄz¤Œt0 ™™Äz3ˆ¤õ4¯ äb«JÁ±¯§ý¡›…ó:tÊêÝå;g»ŠJ2ëj]aí‰ê´ê(cV†tΊ;(žóa­9µÉ@^òpäcûÃ]Z@%¥Jh°–›T]fÓØ±¶QxÙ º{{Fú¸‡\-•@Ðr“ (núbË¡cË&zœ¹WSÃqDYˆg Oã†)JjÒ Hwg%´s&-6;½zjUFaMžš=Òòvs§z0ÝJïhW PVVJ5K¶ßÌ#sÎ+Þ»|-è½DP¼x3U›²F™v±tF¡6Gÿe¬;£ ¶Ñy…]ý¼‡¶nýKËú{…š ’™”2–Î(dÊݬeŒ(1 ™`bË&zzleŒt0 ™4²åˆÎ‹Ç¡‡Ýžv$ðe”; ÷MM¿âj¿¥T5¯%Yj½®Ù7÷að$±@ÚRœ^HÖépm£y“§ü%%cQ…Ã~í»½Ð N—Ú6Ÿ™¼ü•kßëd`$¡¥&1jÃRKaÏ‘\Vr¥LÐÅS,ª’Ÿz,§KÍR“l˜ KM·:öl¢Ç™ËUÃq„g*G\xÉyÙ÷žðRD[ײÒ;J‡–Žvà ’=º]Wè#Ô™¢\ÇäüG÷#n«[ž N·É :{’3æW?Ó¹k­õTÅ“.>XôLhm¹V§å&i55‘_ËaÉ‘vd 7ñ®®í£ a²Ü¤&ÂéÕ°:–lÖ˜†Wƒ«½ã=Ž:‰¸ ~$JIËx»T¸6È¿«“½ìÛ»;‹˜…:&ÔÚU)%ª*±£°f½UÉŒ‹wK¼[ýÔIgå•Z¹ ra’Aå¶x‚ @ØrT%WU‘.k¡VŸ?.L²E*8°™ èØ²ÉÒ\.LbY”m$ã¶¡Ç™pMpü¨‚³?ü%·äR•_éøéÓA¡t[QÈÒLeYá{ןLéÖú‚¬±tF¡Yêm/cÝ…,놱Êɉc²,Ùd ûdŒt0 YêDŽXe$÷ÁÏHax @DàöäC•ÐD@DÀ8üŒãƒgD@š ÆG"4Á ×TIq÷·ûóºŽzcPPõÜ5çìÝÿk×þ”¿1oom`fµšì¬ RÈd*pqWç5­ êlMf‡5tuklt¬ BŽêBÍVÂäº ‘ͺ˜XEÒœ8jÆÁ”% ì?6.Ì×Xä“eýsꯔû’îc¦?×Z–"_ëþçÞ¿ÊUä»üz/Âú R]ÿeÇqN©Í£ãØñÃè¢L,H¿)ý[?t¤¶È‘PŽlòŸ¨fÄQ3ïöz¶pÖúvP÷ú”enÛñ[J¹Ø@¥$N³9Å-ZɪM«þ­§ …´XÐñÙ±¯¾1ýÕ~’[³ÔÙL/H¯)ý[t€ÙÛÈ&ÿÙj.5ç–uªŒ7Ý„þ#Ǽںµû¹o×ךh´&²»˜$n1¾µZQpTWçý÷ò¤nRšZ;sl)•qè9²%2Ñãl€­fâq\?EÁõ=»Ž8š8Š~‰F0t`Ý6âå5áàÒº5é•©')m¹¥–ùõïXgªYÖ GfÁ¦ŸÉÊéfìÙ4†ËsVf9bɃ±dÖáˆÃnÏ¢£»Ž ¢F íðð·ï+P©nY¿ùä}cUÂsµ(úsËÞ¿¨˜§}jŸáæ9²GksÄÞBd“=V†RZ›MäÈòìåÖâˆÃ–Ÿë3#F¹“&ßk*Ù»Ö¯§CN×ô屯hã¦T© ŵß’ö«’ý >u`*¨àÏow¥{GMß«f¸' U¦$AŽL@«‘8bo!²É+ÒÝZ×µÑãXÈ•ÑÖïŠ?a@p¥°ëði¥Å vÛX§§ììÎÍ)çÓ§kãJ&S€”t{ÊJ‹e22E-«Y‚ÿÜòKº°k\L8H¥RG±¦ãÓ²‚hBjþAŽj°¨o¯±8ªÏ®šóÈf õí5›ÈQ}ÌÔœohŽ8 ~5• {bw½cž(Ê¥R…Ð¥º¡GÃ!Ô}Qy/q÷o©ôB'Ù|ÒcÔ[¯QQTª¢'ÔÖ£Qê)H–“žN–$T¤îÚœJAåÜ}ú´ç¨økrATn–r¤9Ò·ÐØ²©ÙDŽxÅ‘µ‚Ÿ~%ùy$ð*Nîú¾pÔÏ©?rW<¾%çÐ>šq–´ÕAƒ^{wPmûs/îÛûO>[‹XµËê/HôÜ»ï>W» ª«ÀÖ%õCGjˆÙÍÈ&ÿ‰jFáÄÖ5—#õ¥ù¯éÇûê½&½Ù{ VÙò6cƒA×`ñê0¬Á@n°‚´.¢Á k°‚ªÄàW î ˆ"€48üÔ¡¹@†õDD°u0øÙ:ƒh?"€ ˆ€ÉðoÀKƒM+n2VVÏ —JeJððÐ娔KåJ‘‹ ¨BŽøÏû«Ùä?›È‘8âÑUë­úÓŠkYWŸí:jú  í'xÚ Õ¿ ´‚<ýÃè]ÚR?=œ6µ«~¸Òž2áWž>'$ú€:Cä¼£ßÍ ¥UJÒNˆžNäÞ]Ⱥ#þsÄþêC6ùÏ&rÄ=G|¹›ê{ªî´âëQcÆupYëÎË`ä#´k škœÃ² ¨ùøÐí/_ÏÞ‘ðc¡Œt[HjŠØ£kTTWˆê½ëQ÷¸ãñYÁ7髿§ÏLÊê!JÿÀÀüéôÔ«rÄŽô3z€lòŸM䈎øÞòSçfÞ/R@'ÍMÏ*ïê/d ðo7oþ!‹D­ºD•™Ééùr¥R.Éüët"ôlcyH’¤_HÍ‘*‰ÒüS'ÖC¤+‰|.ûŒ½öe?ü²j1Ììæ¡SSþì"GüçˆýÕ‚lòŸM䈎øÙò«™‘½²øÎïû/i\×#t˜§«ÃmºÞ V]¡úö¡ztyLô|Íá°…§?ʈO®Ž©Õ q»ÎŽ¥ª øãýËzr`6@èÌ«£ªmàÁrÄŽØ_&È&ÿÙDŽ8æˆs{*îïß²ÿ¾ºW/¶ RÉ2Ô,qGù´âþßîW„¾0íyjlˆzËMÚ­^laÌ´1­5ËáiÏqû«TJåR%ˆ=\¸èïÔØ¦”ËU¤íçRk©\*‘‹¿>rGŽô.(r¤gŸÑô8ô8£Û“¶vWä_ðc‡tÃOÎÎ.LUƒrTƒ…íï!›üç92‰#[ ~&U#ˆ"€ ºð}À‹®­¸ ˆ"€p‚?N`D%ˆ"€ ¶„9®®v&뮣ÞTýŠ,xàà( «{2{ÿ¯]ûS üƼ5Þº^¨Â•2¹ÔÞÁÅQÀ†Ì«:0T´±EüãH%­*S‚³‡îõÁ(´;›áˆ}=ùǦÛ•J‰ÔNìâ BS#Ô˜wEŠŒ*pzè’Á(4À&[±õ<އ-?õüåSúi?ŸSÝØýõúï¿ÿ~ó·ëOÖú¬O@jU‡þA%r+OñY)½p&¡cÂÙîÇÿê˜òHªo…YGdU‡  °°îÝÂ&­Kת”æ§î\:#((hgµÈ,õ\gâGWwÊ>“-é.û`RÅít;ŒB‹p°1ިו_l2Ú]•á^PÇìîݳB:æ§j݃1)K¡±É+Ž”îÔñÚ°îׇuLÛ”­¹Ù2 Y’a ™•9âað#@èÎ_^œ°ã|¿^qÓßš2vT{Ý'ûÚiWuc‰jg2㸠%i\±ëÊ~ý2ö8ìï¬ûèc†6ujU‡³)ii—ŽÎL\5gw*%•&¿1rQA‹aJ¾ÍlÆŽTÙŠU Ú/^™!ŠÕ†m”/2 Õ@[ð¯Íqľ®|aÓ€Åùã*âV¶NËh{â°KKô¸Æ¼+*¥¿+üsY›ß³Â~][âï‘ɧ€Qh€Möbëzœu‚_¥´ ÷È™SA)$ì+ª›’š¸•ÞSÓÁcLL„»ªÜÑ7(È«žÏ÷´ùt•q¼¯”$ä°ÞOq®*xtõãäS?jU‡Þ..>Ôªéôª ê°æhRÖš%χ™Ô“íæNÙgK+nfWY®'U”“º8 êa"ûþÓìdûU%äYIhi•Š#sìTJË.¹”ŸÚ=®2;A Ãü*KíÛuuõãâãÚ†b3õþ¿&Ýú)UJ^ÇXºñÄãäª"€™£½|À!pdËçÓKSÈ5Ç(´´ÂV戓g¨ê:*K‹®fem¿[tìG·m·§ƒ—¥r©*H‹G²ózu½FM‰ r¯.¯Qv*•$»p¦£ºô n—º´ôáÀ’:«:€À#4”(-å@»FE»¡Â¾”›Iáì"Ö útðó°³P}#sD5‰«Šœˆ¯Lò$`ZXM*{ƒpdŠÊŠÌ«Oöm/YBG»ÍÞãÕ© z\U9åqyas?|©eW›ñ¸N-fLÌÛ;2s@ç…-gŽlÑ3Phiðnd£žœï?!ó/ÈPÙ–x›€IhÊ•l ­5=ŽË–_þµÉ%¹«èÙïÖˆkº¶ïÍE ž{¡v˜åwé·Kúsy€ÌºbG¢>¨{ʈÁY‘íã²®â¦Yưª÷Õpôs/Z™å´è„àÎlÅWÝeS-o6&GN¡‚nPùÕ$ùÞu²¯ç“ºTÉ•À(äÍሽ¥['ß2¦äIßÀ´[ÁG×øïmñ}•Þ˜l¨;åqîGS‚³²7Ä<ÄMOƒ°)‡Ä-Ê OºÔaú¥Gsú^n«Äòw–É‘‹ûЙp¨oÚ²w>Rp XFF¾0 °iŠØšQ rµù´ÿ¬ìËΛŸxîÏ€Ö¯öñr²ô‡Éâù9—ÿtyûÏ~}ÄêõFXægNÆSsîH9„¨3åqåÒ©®Õ#Á™kÁFÚ0l*óK“çÿ¼Hz&T³²Å‹ƒ90½Q9² \ÐíØó ì[Ÿpó’çæIÝB…lX0žÆŠqÙò8wê²`èàŒ~Ý^·—ìLL I6÷_5b¿ ¹™Iú³àÞ,€-5˹7ÚªŽ½œà؃ü|bÒ£GgI ô°8ò1®ê@ô+¥d{Bº=%¥RÒ¤±t»¹µü‹!ŠÇs/‰—n ààiñóI£sTS%Œì§â*=&;þO²1 -¯Á8bo¤½O°÷Ôí³2Z¯yÝîÊΜ^!yæ¾ó«.´ÑÙ¬¶¤zÇ¡}/+M¦µh\˧},ר)óËJ»´2Ô]°ïÁ‘ž}h6…Õš³cmޏlùië'r÷‰êNþ”Ò {ó.ÒšùËAØzÔ ]wü±#õ¢Þ9jì³ÕiÓaU„ö~êÓ¿®Gä\'&Evêñ’&këoÎ/ãªäÖî¹#£N°†ÎØÔÕ²XÕa¼ø‹©zÙc-•‡?U%} ?™HÙ$ž)XðŠúÖÂ(¤Ò˜¿5G¦[(…Fù}å·@ªr¨ö“Ôð‡M&³E¡{>½7.‚~ùéuâ%n[ Åfh«ŸnµådVzþp$?žõÒ"ºë9Ôeñ/­ChÎ…Ll²—Y™#þÍíYwŽy–B&­P9º¸ÔŒõlÜUˆIJ©L¥ˆô¾ñdO+SJ«:0%mdÿ8ªV‘üÜ\ôºp…–Ag;±¯'ÿØd¶¬£"SÚ{xp×[e;lòŽ#¥\)—‘!y݇qF!3™¬¥VäˆÁ*89;œ3rÔ˜ès]6²É5¢ÜëCŽLÂÔVƒŸI•ÄĈ"€ ˆ€.Üu!èjÅ}D@D€Ç`ðã19h"€ ˆ€uà`Ødž1Ï1Ϫ†]Õ•I&%RVH•U é Øcš¤#ÆhÆW)•V*ɰ =cÏÙ&ü ~ ž¿<.Ì—º"eY'7ÿFÏò¬Æ×¹ûÓžÓ»Tup§Vupÿsïß©Uj†ê¤àb·âÑÖãW×h.ÜÿK¿vP¦Ÿþ+Z3ñƒp^ïˆY-¡Rzîï¤8PPXc6î!øîqªÔ­Y#u<.rY»Ÿ^±ôÎ%O¿­P#9Ïÿ»Y..P•î^DœF8l^ÀêYΆî5xñØ>–^BÖA fŽyq`¯ ÂÈÌÅBaùùû³Û¹µ«:<´ø+RcÕrôЉì5˜Ì%”¤'Exu±4òQ¥yµï´ÇÓ·‹¸òÞÿ£/ßüB—®JÙ#ŸµºÁí’Æ\¤…Æ,Ãsˆ€™ðÚã:Åž¦<Î^\R]>° ·-/í{ÄOw(ïåõŠÎß=¸ÃÔ®•Eìæ­ ˜0P·DŒÉ£…f"ŠÙxWQÝJ’Uòÿºžñ¶[XJ÷–Õ³tÕMgPBÍ_®nº Ý}}陬¥7²z÷ 2˜‡>ÁͤÆËøxyÐóêÊ/©Ú;´ëf<9«³¿¶mü蔡í|C+¨§OGŸØ~šù{{„øG^x’©„®Ö!Œ•˜ˆ¿U’ÿ*øømǯRüº69ùˆÛÑž ¹Pà9’ ùy ÐxœÇÂÐ|z‡ÐØ6¡j’{x™—˜Y9µ+‹àïeo™eÜÞK¹_Õ¡ºv÷/W8w³|j jï<º³Y%\ÖÎ# +sRS¾/‘Ÿ-*KïÔ£³žuÊÌky‰îÁ_Y^’¢àúž]Ç œƒFMD5¢Éc†ƒUÊzÁŽà~U‡jyèqÒÛ«"—yZît-•9W}_ñà¬ü@º× }“g,Ntüî+Ë#z\õÅ·ËÉ­©‘uVuPëx!EՋƃ-Û˾õàX‹C9{ gïÚÒwˆ¯ßŠŠŠótlËO>3DêºáÙv:Bóv‹Žî:.ˆ5´ÃÃß¾ÿ­@¥ºqdýæ“÷ÍÓ…¹ë¬ê ®=®hÑ1ñ»C9{V¸:÷âòÂ2-Pù=Ë‹¢ˆÛÐj(z\£_ãV4€£g(ÚBë¬ê@©–Þ¸t|ÆvöÒE¢ñVu ¬å,‚Ñêž]»ÌÞ÷ð Œòƒ¨@ÇG Çeí‚éHŸ“|ªo®ó†¡Ïç"ʺ>3b”{0iò½¦’ý°k=Y%ÑoÌt²Vn6‰€uVu  à¡Ç)/”Ãè€g¹ô8ר(€(·]²ãŽ+³‚é»aN^t_iÏ AŸç"Ê¢Çñ׳¸ ~êU:u™]\péNÖ¶Ä»Sº¥ô0ëŸ>`²”ó·„¡#ô.F²ªÃæ‰óˆéÓÔQBE/‰¾N¤·ï¬rðÝïÃQËYYžžWêæëã'¨,ÈÎ[Âe”bevò™¹Ÿö{z0È%2;±ØQw&=3*$ ¢³ »ŸÖQZ bwqCAf†¹˜Å8êU¼§Î–§_z²g[Îì).‡SüÍzç§_=®hó*»…û¹X"”ªkeAzi®›K¨Ÿ½ªàɉõ¹Œ´ÿ@ž;@êõiëEƒA"Q Äô8ýK£ qü´°pºªy+U”~Iý_ Òê×üÒ¦7ƪÄs¤‰Y2éÆÅ‹wumdW¯\¯­`\×ÞcI;¯¢èLn%)lñ™3ô8oᲡý_á¢ý§)GìB$Ò–Š¿6‹§«:ðÒãªò‹÷§ÜyÜ“«FΤ¡<®ÅÙ±ä!PuóŒ4ˆß£=.rYÐOšUB8¹6Ðã8‘+%ü›Û“qþòºÕmôUêšd©D©Tª*+•ŽŽF¾æ°´ ÌÔF {\¥\U¥T:Oüpk~ð/ø±ãç/g‡¦B¸A=ŽQ o°ÕàÇÑD@ÛC€£¶Wq´@D ù"€Á¯ùr5GD Ù"`Ñž–É4ǼJ!“U¨]\ŒÏVÝ«:(åR©L)öð°p(´.X´JðПcžQ¨›«ö™8birĨ†KÆÄ&z\-7l8:Kbâˆ1a]!z\]LÔþ?ýUÈðã¬s‡~#߸S›Gÿ “žö5fUòý]êžå#ço¡MöÝ鯇¶³8ÊÓç„D 5Bä¼£ßÍ %Ð…ê4ü/ß×@ŽL¹@ôØD«í†¦@iµ´zñp­[ô8~v{ÖÌ1Š{'.Ý÷î»oDµ–ü}ü²fÁ¦«L»ªX÷»myú²ù[î:›••²aÚ±7W•3c¢Ìküö=gSÒÒ.™¸jÎnõ*NŒB[+y GôÊ&Lˆ{õÕ1É„ä¬VÞ@ެEŒYzkØD«ã†f!Ê}¦ŽÐã8ሟÁšnY³‘IÌÈÊ Éœî.­|œ¡ ¬B{†ñ·:ãYn„JEÀ³½È¼;DÂB™åzE~Qzz¸¸ø„ŽXšNÏ1ŒBËËâHC GÔÊdóñr,ËæÉÊÈ‘‰,×°‰WË MDÒzÉk8Bã‚#v{ê_<â¶}‚à÷ow…†º¥§—uõlãêÒå½™0&dÎÂ…­–/O\vø[s–ѯ%u¤ÌÙ¹êûÛÎn9¾¡zŽyFaݼüðhäÈìK=®®𠦕3¢ÇÕÜ*M‡š§-¿šŠ(Šr‚Ð/ÀÛۃĘûwó¢mWS<Óž<ïZ2„ÆuoÛ6pÀ…”ÛJ¦T&Ë®Ýû ‰|aB(ÀÕê9æ…&«n˜ |Z92›sô¸ºnh6˜Ö͈§s«4j¾·üd9)·ÊZ¿:mYÐáéV°~oÒÃÈàzÈKc­ê ½ytUâ´¤Ÿ¦’éåŸ?‡lwl`‹‡¼NÔ®QQ]!ª÷®GÝãŽÇgÓ JF¡é<[?¯Ö@ŽÌ&=ŽÉ ͆ӊÑãôo•&CÍ÷–ŸƒˆtsÞÏ~H^«©îÝ!c>½ÄšÁždU‡o7oþL0­»Yw¨ ]’ÀÉ`ËÕl)é©L¿|¼‘tµ8òÉ Ò/¤æHÉìžòüSÔó®ä©„Q¨[Y>í«×ˆ¨³òrÄ'–XØ‚WË Y`Ö(IÐãjn•æÀÏ–Ÿ¢\*U]„ èÞ¿ã­¿Ùü7U?a÷¡/T¯éG›Þ«:ˆ‚‡}7ïÏ7„ш‡.Û5ŸƒÆž\7²fU‡]gÇ’˜/gšG³rÕpD”ómå äÈDÆkØDÓB§vCía£ÿÖpDLA£ù°ˆ#þÍíÉ4Ç<ùäV¡±¶ÕGêÝØ«:ÜåJ¸XºÞ—ŽK)åriû¹èÍ1Ï(ÔÉÔ(»L1‚1€Â?›èqúnØØ¬1qÄ`z(Eü ~MÕ;sÌëÁÁË䈗´˜i²i&p ˜ 92 l[ ~&U#ˆ"€ ºð}À‹®­¸ ˆ"€p‚?N`D%ˆ"€ ¶„?[b mEDà ~œÀˆJD@l  ~¶ÄÚŠ ˆ"À ü8• ˆ"€Øül‰-´@D€0øq#*AD°%0øÙ[h+"€ ˆ'`ðãFT‚ ˆ"`K`ð³%¶ÐVD@NÀàÇ Œ¨@DÀ–ÀàgKl¡­ˆ"€ œ €ÁQ "€ ˆ€-!€ÁÏ–ØB[D@8Aƒ'0¢D@[B€Ûà§ÌIOÍ–(m ´@D ù! ü¤é;ƒ4Û¤ÇÓMbòÃÑ#Þ–6?±Æˆ"€ ¶„€^ð¨˜yâRÒþµ‘ËߌþäH¶©Uq ‘ÐÔL˜@DhPj?RvK¿±³Ž.¶km‚„2F~zÝ MƒpƺtZD‹Ó—Nš³nãRúTô‘L9’à ÿ'ïü0hÒÆÓ”ÜD@k"P7øiJkßk¤»GwaúE¾”–‘••±½ãªè/k#šò^âUÙ¡gSÒÎ]ÚÆU“Ñžœ[7iÌ"Ï£ßÅRq7D@D€_Ô”µN·iцkeàVBóIO­B÷/èàÑ;J©tm óGöÝ•² ”ÈqCD@ø‡€Á–ß“üÛ‘­\@™ýßãÖ†OYôíêÕ³^ Etצ¦&^µjTQÃnX—7guÿh­4xˆ ˆ"€4.uƒ_Ź<'õÈœ)[†-æGÞø• îÓ£‡¨4åÏàåhÄâŠ"ˆðü¸ÏOO;¶è­ÉFRâ)D@D ±¨üH`[Þ7$¤ïȵ‘ýJ(1Ë¥ýày‘ë#Ȱ–ާCÁMÏÔZC;Iþ %yOØnáÙï—Ùx¡@/5 ˆ"€ <@À.++‹R©.."6i1 "€ ˆ"ÀkêðRm»‹ _©wD@lZÝž¶]´@D`ƒ?6(aD@&…¿&E'V@D€ üØ „iD@šüšXD@6`ðcƒ¦ADhR`ðkRtbeD@Ø €Á J˜@D I!€Á¯Iщ•AD`ƒ?6(aD@&…¿&E'V@D€ üØ „iD@šüšXD@6`ðcƒ¦ADhR`ðkRtbeD@Ø Àv=?6ºÈbïl’‘4‡¾d™Ð„d{2LHÜ’þø½EµŒ„*‹ò3eÎ+ÏT­ÈÎeM´ÄTö÷‡%ÅÈ»×€¼ÙŠ7[Xó?|-TÀ}¯„AØŒEY‹jâ¶üšñ…€UGD ¹"€Á¯¹2ß õn =^m’°D$î÷ªs?D‚ ¶üœÚ~ÛûÍâQ ªzweSOLÓÀxÌß«`÷øÏp®&!K L×ʼnç|ûtNñ°ªª§×"—\ƒË…¾põ…ÃòžÛ÷c¿}UÏ})æBi“Öa›ÁÏcëà‰ñþŽåö·&M­Vο=(3áë¹0u.ñâ‚üö-¨„-ß5çV !@Ù‰”?æþ¶þùG*°NtíjRû-aG0ü»%¼`^­ÆsUÔßï9@vŠap8´^UÁ;t÷戃p®F„CÆlˆé?­Œ5°ï*‚ µyEb.vŒ w»ì«Sµ8’EZrä﹜œÁd§øŠG8—œZ•ß–fÓûà­aå·¼ÃîœH[7÷˜÷óJô8S &iÿåÿφ;š˜M“|IÈ…ªaùä¯0*¹œì <>ǵøI1,u«3•fa§sUƒÏ-t€ƒ¹/ûýóòû’£% ~~,¸wd^‘Í&DZã©Þðâx˜<ÂÚCY>¤ß1H±·<Ú›x*ÝÉ[ð¿×I:ù8CÅ!×î»wr¥u÷¿"Ÿ ¾wì_Š÷SÖ´'q7“(…S2ðò€·;Âî¶°À º™”½e @6|¶ <[ÁæxªI÷t{¸ÿ ìN‡¸­0e-|‰ëà÷+:ZÁ„nðø,Üבá.W wW€ÕƒžümŠ^Q dク-]ÜJ¿®Ì­“W{P|2äFº{èÖ+kÿz&&8gÝ¿ ›WväÎF3Üê´ ³‡abXíï:A/ÓØûºøBñ¾ÓÓ½\…ë¿~èØ©·N>ž´£Ðíõ¨ãkö}Þ>øï´ËUÕåçOn!~˜{ô¤Ž wë"`uóëJ"Æ@g/\9)wuϰݯ,»]ôØTes·´®˜ªÎvg××ø„/@áù¹ÿ»v­.LÇ„@9|—ß‘3îða ˆò(7X~’˜Ò2Ë”Åpö€~ßÏ­Õ¤ùæ)hñ³àö^ø×ûz·χ0u¸ž8A`€ºoåÀ³4Vß–ÜðeÙãM[Öj<îîS#\òºÍ‚'{禽g²Õ™NË€ü‘m”3 BòW{”ÕçëÝQVÜùo©ßç•e;K¶U¾®N?õ⨶ýþœÕ 2oýë¹ü3:J^½>à=¯²„Ñ–~y¡£³iîrü²¯{héსÝ#Ⱦ)5ßTÔ…oP‹.£Å )¸öIyõI±êöµ ˆr6i'1Ýg r ¨N¨Þ©$ïü ¤–"ä /¹AO¸VA¡ ’‹F¾_BK€óïC “B,‡Ò%@Þüè^=NT±îÞz…oS@çB˜ê§“]ï<X„@2Pì èROäýåhHŠÞÿ„¾ÓE Ūå×*–DÙë{œˆfÓÑ=Î"‚èÌ0ÊzÀËäUpS ÉF"_øB¿$ä]óÐa/ºRx­"}€¼ªÞ:zÒîç&Òåhúõ¡Ÿ…ÊFŸ™T“½:îè# {ûÒ?cúQ^?²uííƒ ¼/´óCç (òx­÷È× LÍùäŽD'½´4<ìÍþtfo~¿± " ®&ý³ƒÈ¹«ËEm6ÃCr6úBRÒ‡ùŸ/è¹õêå©´ÇÅÆ iôÇ+¢ÇZ_¾ÀHxº¯„?+ ÑHØ£s‰_ý¤û1(Ýÿúú`Í ¹ßéë„6G¢6ô„¤¹®~æ½+Ò«áw‚ðÝë> ­¼úáÅåàýjåƒ2Ý×µ4à¡Þ³;gp¤žòG(o#7¬SQL^Þ  „´ÞÔ›JIô¦ÓÝâù#¼&oìS$3g®÷¸¹ã½+·r¯´Žþ1¼=ê왇>3çÊ7îš¼øc ¬@R…Ñ45'‹I6üªÓ˵‘øY½8C[ÁÆÑp>ê Ÿ½óá¿=¡3y6ÀGè„2˜êD^âfŽ?²u5¢¾¸˜F<®Ú½*Z:uØô>r¤]«‡WG÷‘„¤®×#^ß”x%kÓÿ¥?†u£=.æãçbæ¤ v FЮ>õ{¤êà[-gÜQ“n01”Tß8å•ZŽªo”dKÈ¡h×GëþŠYCüóÆéÕ¡wõÛí>"”t—Ùwûü¹?)ÝÊË% Ãur lÎB»¬¬,®êÏ~²%œÞŒ+ÌèÁéÍŒ€Ã“S8½OˆàÄŒÍjÁéÍ,EvœÞŒH˜@D é" 3¬¤éVk† ˆ"€è"Ð8Ýžºà>"Ðl°ð¥û Ía¬8" ‹€®ÇaËOÜGDhð0øÁÀÐ-¨YÀo«•dÍQŸa«µl.v·‡þƒ¨9™pã/¬9êÝú÷áo=ødÎõ6n:t „’øJgô£°nɱÿ‚7hÄG—Áê›yäë_ `Ù _VÙKgQÒ”N1ÖQX·Ö,9ê6>¥Éýn.¬ŒGŽêbÉdt<<¥9ðõÆ…ŒÂšÓÚ½óaÊ@ÍÁñ¥°Ù¸#¬Ü m¾ ´Kg2fozBƺ3 ëÖ%GañðIŒ&÷Œ\X÷&rTK] ×Áï‹!Ì$…àÑú=sâ! €Q¨k…fÿ*òI2à› ´?•1$ѽ¾‚ÈD5óÃ螬Ùg,QX“§Iï1ÖQÈ{Žò!é4ù6Äáð£à•™p~=ƒ>µˆ±tF¡AÍõÄâ]êÅàÞ "{ÃoÂ]F!BéÈWœ›¶Aççê÷¸WVA[ÇX:£Á¤¦(b¬;£¡öì9ʇK§ ñou‡é10þ¸ð ƒ>µˆ±tF¡A6‚Óà÷ë Ìþ§ÈœK/À¿ÿ^$]^¯0›t¯ ¥"Ù7ÿqœZOEMcÛ8x9Î^†¾Æ¾ë M2f€mž³ö]=WÕ%AìÕž¹§> MªQÔüö¦ÿBᛩ ­Û‡eïÀ ¤Ëk2ƒ±I7þyÊã6} âPøû*jÛÆCL$]‚ˆnÆRYh’1Õ¶yÎB@Øs”vÒÔ‡/ó8 M²MêZÍaðSÁà }•A+á· ÓHPSòx0 ëšAK\ˆ1N°h}ðpô?ðŸ? $øzHþ/òá7#Á¯S馘d°x=a1 &q4>~œ=ÀÕöêO³Ø$¥‚³+á¹ }•mWÁ/:Ç 4P˜3qQ'xOÝ>xŽ›HJ:<'Aq¬Ê‡_Œ¿pKM2X¼ž°“8j;Þ Nžà⇠@f±IôÚœ˜Ã/dúRøoŒ Óÿѳ@¢“ž4—*z ßVÁ¨¸"è’¨E¯|A*ø’üK·'¤†ú0•n’I†4Û¨Ür@XsDÊz7Óáö= ¬öO€Ìr“ (núbò€Y ”AtœùâÞbâqdfñºBCG4ɰ¿‚ /Bª†Ž4ˆÙøÏ¡­ Ö,¶´Ç|#A³i‘IM°Í–š#ÐÝG™whkÛÅb–›d@±­‰9lù©«îL5ø²é›œ¦3 ëBEŒ)‡c§¨7ó ¼=.uƒ!¨ø“ý«š<ý¨våÒýà@ ø|,‹3<ž‚±tF¡F}Sÿa¬;£°.¬9¢²ž‚/NQ¿l¾Ïl£ö™7ÆÒ…Ìù›±”FéÝb«Ð÷¸Úº ÷)‡„“Ô‰Ûyе=ôÍ(‰¶áàGöµ3#÷!ý+°èÆã>ù¾š`x<…%&Õ5² H,„5GN'aÍæ¼­A<î{ÃØYb’a­6u†Ãàg®Ð;Ò$ðÚ^©oG²ì,“ÐB‰×`T0Œ¯QãEá6žEÀŠEàTËÆj$Ûׂ§@>ý`b/øy“6e-ÍåL¥›bR-}6h1 ì9ŠžÁOà¿À-žj›D×@IDAT ™À³Ø$z›˜ö¸žùp]“öC´ ¼´W[hŒ ×`xGˆ _ Æ‹ÓŒáìK?q|5F#ùi5xˆ©µ:½ûÃËÏÀ¾oµ)ki¦Ù¬]:íqµ…µ26ÕC‹aÏÑÐxh_ë}¡â–L-6É€^›süBÂÞ5b_/)äÉáÉðSG˜Å ÓÕÍpº x“ú# ìûU?™ ª»7ÏŸÒœêÖ&ªàŠöP?%{,4©¶B[?¶öùw¦ºã¢'ÓIàC¡›d뜘o¿®’°÷>Lˆƒo€W)äW€$<_ª#Ìb.$m#œ ‡~Ó©?âq‡öê'SAu÷æº=AN‡…ÁË*HÓêg :j,4©¶B[?¶öù…ÀPò7…†L?m1€Å&Ðksbn§7›Ûw9œ= yÄo*`Û.€&¡a ºE€œ:U“BýÅØýÓðö¿k„l÷Kg²ÕhãéëÎ(4\Q¶YÇÅÎ'ÖEÎ0–Î(4ªÆOêN¶d†ùAAáÛÁ«’NA>íq;wy[À 4¬>¬x;Áß:ñLýÅXÎ)˜·Òp6CgKgÒÐÄäŒug®8[ŽÚC˜8;ÃCãËÔE0–Î(4l’mžÑõ8nƒ_ÈìÏ w;pt‡RøÓ4ãÝ…,á‹ý^ …¯_‡S,3ÔIÆX:£°NÖ¦)`¬;£eý‘#–@é'ÓuEý3¬Ž4s{¾ý9ô ¢<ξ¾˜ªïÎ(d¥`ć0)6½F}AaÞÆX:£Ð<ý6—‹±îŒB–UCŽX¥ŸL×ã¬üô Ã#D0„€®+JcDŽ[O!uÐõ8?u¨[JD@>"€Á¬ Mˆ"€ VE€ÃÑžVµÓ ʃc>‰ïç wþçU¿^6©€˜Ù‹û’‡KÏl\rÈÐþj¬  óÁåËšIŠˆÓ ª.±Éì°†®nMƒŽuAÈQ]¨ÙJXƒ\W!²Y«HšGÍ8ø@•§öŸJ6ùÂú¿4t@@wQΙíkiÂ҅û•c†¸ŠÉ·OõoõÐò[£º©çá˜8¦àÔÏ_þNebAõ›bk)ꇎÔ9²Z‘MþÕŒ8jæÝžª¢Œ¤Ì\ÃWdØøÉ£ú r2¿¹*´[nfZÆ#ê(z²/­ÔØo=y{¹ª îœúíçí¿],ù<÷ÂKje¦dÌÛ˜ôþÜÙóbŒÖ OÖB ÿ{Ó:•ÞÚ·%¡Ö Žû#G#imŽØˆÇ+C)­Í&zœ!äÙ˭Ň-?§”‹çóv“&_Êëï¿=w.5r»¦/}E7e@@°·7¦Õ~¨°'óhs·14ø½/¢}Ënm_²©f¸'w%Òš Gìm$ŽØˆÇ+h$6ÑãøË‡ÁOøûî_éŠfnûrQXÏÈMJ³±Nϰñ3§Š·/Ú­?aaÞàG1º†…ɪã¢ÌNë&­SPð°÷¦õUåÝwzöì)—\NÓt|ZVP­¢3ß­V‹ÕB¦îacqT×Cô8CÈÔ•7›èqu¹0$ihŽ8 ~zUJ»l|.c½Äwààì×3¸ðruC´ð*•:'ò…‰}üéqž]†NéÒÿüÆ%áƒzŒ¤F¢°†¯ž‚º?åKz íý£§ÄS&(rä‹ÖRñ×䂨Ü,7äH(>r¤o¡±#dS>²‰ñŠ#Öwo}«›Ä‘²¼Ò¾KôDOX«þÈ=8ØOЇ׍ð¦Ý~]õ±º=«P¿#FOØÞ*‹e:aR7þ~ý¥ýºvAÝbL.H¿Ø¦pT?t¤–È‘PlòŸ¨fÄNl]s9R_šG·KÛ÷ÙîË5Bkì5XAÖ0¾qu6t Sî4»fkë[7 ÈØ+È yž¥Á k˜‚t=ƒϯ=4¯)# ëŠfÔÓÖƒŸUÆ,ˆ€%èz‡Ÿ:XbæED@ ~ ‡5–„ ˆ"Àø7à¥Á¦ç :f Š tƒ;êÈÂŶw¿sðà=i£ Güçˆý‚lòŸMäÈ ñ/øÕZlA˺ڗóÎo_õ«ö¼:îÝ@k Z›Ÿ0ËW[úÕ£ÃßÒWÚS&üZ[˜0Ë‹ÎðãæÌu;ΦU¾ºö¯­³úS$•¤Øï©å| €úS¿#G|äˆýÕ‡l¢Ç±¿Z ¤´Á»"ƒWwZqÊ3o9v½œð8Ó`ä#ÙÈÎaCÉJC¬[0@d=b‘;(Ö}ò[x;Á“G>ª¸;'íº¼g÷5çÁk7Κ¹ôÛÙßXøå¬þåW·õ›]|&aÖ';¶.ŸZi w9â?Gì¯d“ÿl"GsÄÏàWk±UyQ~a!¤¥‹|jGo¨5”…w®Ý¹krùN®wR]ƒƒÃçÎŒw¦>«ôåðV ÛøêTxå¼9î ÀQyê²,úWêw䈱'Ù$X¡Ç±¿`˜RÚØ]‘÷^Ä$< »šB¶+>ÀzÃÊÜId]}€lUU9[g„sS|øÂKY99…ŠøPA¾D§»Ø 'Ýgá‘2‚¸ýàAÜűäˆÿ±§Ùä?›Èñ>ø¥]:´ïçd[ýÛÝr÷gFŽ& Ý5òvð‡Ùñ“íÈÖ}nRQ«×¿ú†›tåÞ… ç®$_'}¶ÁáQtdµÜ‰›?i•¹mæ®;Nîî\uæâ‘#þsÄÌ“Ùä?›Èñ>øAfR=áJîß·  tòÐ:,™ö2,,¸1Z‚'7mÚAYqeÍé;2pò ÔšdÙÆ>$|êÞìvÑ1¯ªu ‚#‚óçvœzÇà ë:=õªŠñŸ#=ÂŒ ›üg9â€#ž¾ó«v΀ˆýýJ.]¸=B[¡L’«9Wg pZÎj®Íjífí„ÏørnXîdɽðW†wCaŽåc0ÍXûfϼÝëÿ{ÇgðŒ¨v »\B^ïm9^4«'üýËxõÒÔn½—¹fmÍLÈÿ9bÏ?²É6‘#N8âgð«™‘ÝÙ¯Ã3ýÚ>Óv^ùÃã;·hƒÔ]Áªk Tß>|¼>+âõY´ $}éÄá–?Žˆ‹'ŸÓJîýð=ªwåý¶ Ýøú‚ª*eöŠÙãªmàÁrÄŽØ_&È&ÿÙDŽ8戇ÁOoZñÌCß,¸æ ‚š%î(ŸfZÁÄÅØßôRžœÝÇnKxlûp7ÈÙqP3FS/…é'ßïc÷~ø A>îîîuFnšÚkÓ±¯•|úÈ£šh¾rÄþ*D6ùÏ&rÄ=G¶:±uÃLÎþþ)ë"€ÕŤ–DwšÝZ§Ø6äÄÖÈ&F7 rT/þºg«Á¯ÞJbD€ÿ躢Ö6dð3Ã<Ì‚ð ]kœnÏÉoð ìÂxOƒ°qE?~oQù­à ‹ò7Tævð>Ë¢²áK–)›^²é¶\¥±LÆïe6®l³…ÅO'ÛÂ6¶zÄ}}Öî•Ô—¢1ÏóÿS‡ÆDËFDh’4NËÏ”1!ú9’™œó~ÎzBáG¥9TªrªêÁž :¼·vs(½sI=”!g¢p×᪲+åe öTåíë!‚;Ù5¯v93–[EÁvÓãí\òÏWþôkê6=A!¼ÌIݽˆÙö‘ôw‘‰+“Œ¦¬›×ˆÄ#:¸Áí û¨É(4¢„Í©°XÏVnðçŽ'lÛFšà˜ ø~BéùŸüZãqžU’\e¦1s›½Ø?ò¸¼KJ¸cÓnáί†Wæ\‘äÌã=…þòʼ4UÛb>ìçJqtæö—‡¸»+†‹£ÃíáJÙQ]2…Flcsjÿð@GØq÷›Ä&¥á_ð*çì$Õø¡½Ó¤NízpH¶’¼íYOª¹¯]SIÞîJEL[oWÒ åŽæÚÅ8´Ý9`b¹û“íá‰ÑI—,W^k¿þj–¦7Až¹sIÇ?ò(å­º};9..Ì×-í§Ñ_·¸J%7é ¹yªò\²F›s»WFQálµ"¢&…öçêáJå@ûþ! ânÖy—`lOº€Í°rì§Gà2 µV˜õ;¨{jB´—:ëæÜ¯'þðMHäœ^ÎïÛ³•ÓåuGͶŠ(p€Ê'§ö&k<ήÿ¤v£zpH6éùí ¯£RzxwîÀ˜€WîØT\ë_‡9;#WÇ‘‰ÈöxãèË\¬£òUÂ,wu1›3žØ1fÓηçÇuiåvmÝè5ê¥UÔiù_2¿FîSRÍÝ/@üòô6O“pH¶¼üí«Šo2ðÂÃÝJeÌ@OW±¡$¦ÊÛì|úHœoK*›2}éß>Q=F¡©šõÓ{®(\¡]ëFž¹nYØlú®¶ö“Iq=}Ý.ÿ<¦W‚ùA‘ŸÝž*yY’²R DD‰|wïdn¼•s¾Xnì+öJUšTVF²i²ê#ÉÝÑŒgIä“$%n|úðƼGÅ\h¾“tj××KFð庢à S¾¥túoýlN|˜¸\ RS[ój{’EÚVÁùJ«þÜ^yèTÕÝGÆÌ,Ë„ÛÈw‹œ=œx,¡"_Ê6ˆ 7æ®§Jg3‹Õ¹¢„Cg>š¼qlüѻ‷—ö¢2ÅFü¼zhO§ 2)PÄÃGIV#lÈ3’´½*­Hä+½{ûÆ»§Î—>2æqU™iåÂãzÈ÷0éâèÁ‰+6æ§sãqÿÚµaòèOâ×=]J{\ìÖOVÇwq¢ülÌè•ë²EÁ3'¯¥ú»wv|Oq9åqBuô7³븫7ôìÁíAv~;gŽeöÚ¦›Àµ· ÈïßÛ'³kk'ýµ°¾°ÆÙÃa«;¿ÑJ’žQ,ìãpó­lê©Çâ­hÍŽÕJž¹=s¼˜´¬$'~ù)åã ÿژ•#¶„î_ ÇÁ]ý°ï5ÌŽ<&î«z QrÕYmD4¤Ÿ®›¡“&Ëýéöòš©TÆÝ§à£¡Ð ŒI˜m²ný 'ïÎ:y—=9:÷ùδïÜÉܰ.÷‹Ù¹¿*æiîú™ìhÀ³¤k€º™¾Ùk›nÃz;ƒÙ0nçzïÈð;3ãÕwÅ''Öÿœ2;!áüú =Žãà÷ToèмI<®„Â|¸qÇÂÚƒ½€¨uð\ZÑË’¼}wŸ$YªÔ²üN¤CÈ­G| ­fcΉøË—6Y¦’Îí´ðýßiéêëë.8¿ZÕÊwüqœüÄrxí–Â)Gˆò€·=`Z\,#Ep•.ÞüÔ˜‘Sì#i÷ÎWî£ü¢¡6:–’éÎÉx[/ÒFò~£ŒIÈAá~;4ÄßÝ£•—Ãɤ@2 Ïã/f“OuE˜¨bÀ3­é\÷ÌzÔÔ+ÒNLyœÿÇ•œß—÷kc{œåqÝh<.wcüÕ·¸ð¸ð…s½ÓÂÝ××KðÏ Úã®ìH ØŒ%—gÛED a˜˜ú»­€s ¸diLLßûµŸÓ²ò¥»ù¿~SœÌ™Áõ*¢º }ºÐË«y:ŠÁ^H.þ&a½šêO žwéƒwü]}[¹ ~8ùú³© HŸ”³0 9(UvïµsWÒéµn:p|Wä2øe_ƒ¼"P‰!|0ļÝC8¨;\ /)¥^ÅK¥…ä‰ÃMýì탅Â`.+ÀÖ^bAÉÃk;HòÇÙ¤mÛÉ×Âæ·¦Ü‡·ySøÒ?³[õ‹i¯c ý H:´8Ù‚±ìòÕ“jÇãÆmþÅÞìVÑ1muÓlråq0Ê >qƒÁWAª’-¿N(Žî–¦“¯–Q…hãi€]›0A›ºp¿«Útbʶ¬«Y^q+*%×H,frPöž·v¿:ü»ÞSg·ë;êU…4G¥:“w¹Œyðç~Øõ=$§C•„÷…‹CµBzG "Ïþ¤fžnäÕR”®4@X›àøÐà7¼µ±P]urëµöV~óL¸öžC jݹ3¹ÿH,-Ó»óÚ Ã¶r wòš3ª{;—Éi•Nþ±­ü»–¥¸E¯Vþƒ,ob¶„·€¾p£–gÂ÷ákË䥱¶Ã@ÊâȽ±ê.å•Ô6r¦ýÄ)öÏhïžj¡ºjê}Nþ<ŒƒOvÃè± »Bõ’QhIqa3ºõm‡Èpa«Amߌò™\íy­b=#c½I€wð‹ŒuieI&æ½@Æ÷,ø/=ÈKMÔQ;¹2íŽ Dn)së@y\¹†Í0ÿ™ñí§¼á¡ÏfxœìåqïSç?€ò¸"º¬¶é¦{ÎX;æÛ…þááâAsFP먔UÐÙű±þ±”ljzùÇâ ‰¹À Aª‚ÝeðA l)B›e[ZY–D=ÉK^ˆt¥8ÊÒÄÓŽãÛϘÒ~jm,T—clÌ’9–´™ãÕæ‡Û¯ Ï“Oè,{|ŠfƒQhŽöê<ƒ:­Øù¼XqH¸çÛ3©»¢úy$Üx¬ÿSÄãœZôŒõ ¯Î`âŽ>H&f6”<õ^3gß`÷?,dó÷2HÕ„&Ë,vp†«Ò¨Ÿª£Çj1jE Š£[¹§¨v:½ÑW©¤©Òˆì|‚É›9êm8W[»É៭|FÔ)‹¦þÚ`ÒgÌþÇ?"îeò§áhïzU€™“ˆïDs4àãæl\Ðò­'æ”Á uKÏ¥^Ƙ¾)+ªì[¶(ʃlú#÷²ÂUW%a"‘ªârÍEäìB¾N*»V¨¾¿ÒÅxøMoëá UÅ*݈hºõæ(:þÛÿb]Ý*ïà$þÕ絿½½|Ä"÷y5µ‡¿t?\¯5¦$¨à¨“³¬B£íÝ=+wÿJŸñSUÆ?Um<€ôO×Dç`;_(Vͬ1±×hûþ¤K·Œ~´R“žÍÞÛNÐ:Èpr rÀ(d£ÍPš´÷÷¾/ äèî.Loλ÷žs¿ßwÞy÷¾»¸¢bå$wñ/ko] wpa+224îŒGD*Jÿ­ÖbÓiä{¾ANHQ­ó~cŽ Mä©z3òØý¥;v¨ƒqy ^®œóþÜH×XŸÿPk•£‘ü£3š|‘žÈ'=µ k=Ö¥ä×õÊIîg*¾=SÝ>Ê–]%ÍÖb×ÞÛIŸäìÓІ^öz÷E>ñTkGD“«¡ál÷“=b9þ¨zîi©;H…õÍú›vf€Í™N±®|ŽKêCÍ|¾i«<§™U`ƒLͳ°5õµ=m¹;¶ xTðÅ#Z‚OT þ„µ= £¹ˆg¾OŒ·ÉÙ«8­~/Õ\¤í¬E¯íy¾`, Œ_Øú=*j”ymúM Œ¨Øû…fÙ%ÊeYÀh²tøó.ÓŽï)DymO_ÎëÓü²ï®ÚEË ‚¾ê´äµ= ¨¾Šu³dËOvtÈ¢”Ü›teX 'gž&Ÿ¡ímÓbfBÁF!ðôLJþ£RB¢æB X¼ë3M3°¹¬h1z[jðk1g(ik’4«Õ¶°m½‘Ô¦ ï!cí´ìw)3q¡Ôò3S'dCˆ´5I ŒÕš˜­ãžû–ph: ›´–y5Z ±™‘yp€ ÐóZ~ä»:Usëîê`”I&%b¹$°PÍ#­a‘ M*† ßÕÁPõ5ëîê VKÛ‰CB^4÷‰î®¤BÚTBAŒA€yÁOwW×™!>>¸E7o®WÏvÐÈ•gÖÙÕÛ=kÐÀêÚŒ´uõãë5RÏ×ú(±n_mÞÕmíïWá½#vÇŽ]'å]ÞÞ¾¸ÊÄb!9 Ð4:»:Øš£åqÒ¢‚ëõŸ°Î®œÍYý§jyœ8ã,—²ÇÅ®ï2‘˜€ïó®m_œŠì’v¿ðõè:ᦼ̉íKt^DU©áÿÏ ~YÍ®RÁ¹™ žO"—;ùô劋õE>œM¹«Ã „œ,:ÓA”³ñ¢ 5^y³Vì66ѵ4«††›!ÿîååY»„q='&v‰ß|ÿ·i,7oEñ™Ë§¶T¢½ÇNŽŠß|è7Z†øÒ`.ñl! ÙÕA–yîAÒãDN­†÷åTë‹|bW‡ðA¨“e=NüÛÆŒRÂã¤å>¾NädÑ0Ïåß?°³r÷®'Žq]“×n.Næä­È;“¹qKØãëÉK7—¤‚Ç=[wºVm,üèÛÕÉ/T±…5·*51à ¯/bñãÉ–Õ@ÓV?»âöÒñÄ[ÊÇ“u7Oª I-¼?=’ÅÆ?D™ýOfª„;nŽíæÆ¢¬ xVÀ³ñ‚x Œ¯Ìª¡zW”wA¨15·ª3àqi[¥)k5ág>^Vw;—¶îŽªL”zoÆôpå®âûŸýXߎ¼·ÇF†é®e²‰©ÐühßÕA››g˜¢¨ô ^‚‚)GëÃmk3nßQOó¤f˜Ãüž>âq½œì ~߯S–ãÊŽð*å=QðrÌqߊàJ+Ò÷.WNhöõEÅ ÂT§Úð£iн«ƒZ¥s¿0iQùIݾ3†ÛŠ3ò—Óäq‘ó»øÈ‘ïåäfOµ«ƒºò±+;Dpž9l Õ«Nkø<Î0>Íx•Îà‡wuè膗"vuȼKo¥·rf‹eP¿é3kIû`çªô]%Jï•æwâuâED¸ù÷Aÿª¿6$õþ`·êäñ?—SVÕïõøùíôkNÝcÆÎ¯,þÑû­]¤gç­=@¹d( ƒP¥Úc™0þÜ(K»û³%×´ÖT2*—E- Šr®Þ“"¦KIÖ½²KçÜ;9ÆE¸»:h<®Ç±9œÒ“§‡‚ÇÑ…5Ë¡3øá]øaÈÓØÕ!  ÞF™šÙô”*ÏvïÆA¢»å:=0xW;;$—æYô Ÿ>»úŒwÆKÌÿ½N_Óå; OïÀ¹ …±1#œþœ«ìîßû“/[Õœ<ž<Ž(û órúÃ]¸É—9yîÛñ3f Tsö'ˆ|¦sÅŒW±¨[ rW‡.eÒÆuc!Qñqû:ع#Õê’ÖÀñçñN¨"s)W4†ð¸{» úŽáˆæ*?%Îï!ýÒ½üäiïtDYð8ëß)Æj¤3øá]ð?|tŽAAÄ®èÀycMÑŸ.ÜÛÃãé“ËU:Qïê0‰o'-Î[ðH«=h­E:vVÜÿÑÐ`ýõi|…廾­ßâ¬Cµqí; Z嚊ŽKz°°•üÚß¿~ZMp’ÕTÕ}¦h\‚q’¼?v©¾çäý¸rAxT4*¾Í n-ã*©ê¸êv„PT}\Q•Âßp0§¥—ktî ¼«Ã$'¶´ì§•Ùš²­æqÁù?–ÐÔåéðþzÿ¨‡7’zÄ»:dO—Ø%=Ž-t—]»üþhÂGAÑ“Tð8 ×ÏØÁO »:àã¹1.vèQ£¡.J¯kŽ]ðøm§ùsÑZ†º¨Ps‹ˆÿÕ­_~cÏ,ÜÎã†ke‡•Eôžª S›qœúœ 5Ieg\Ðú§-ã4îê€÷cìãÌF•†ºÔyœvƒÈ×*»:`'Hš†·M-¤e¨‹ŠçàèŽSñ¿:»ýé,\-ÎÃð^ÜÈ>¢Ç~¥Ç‰3NSŸS¡u[ÇiÑü§ ~uÕ¢iW…hëµÜFH5ë®ò»Cš7¨®Q=T‚ÚcþÉÂ[";ðEUußDWº¼¢'ˆ2®“ ›’5ÜÕáé™­·tú;•4ë®òuCÑØß‰÷šsln¤C¬‡Ÿ/®ßÕA<­û1˜ÚÐÔíòÌ\·dð3$ù[7šþ eËryª¨.yø‹¶’ªâ%×t:j´/2ÿ<«–ŽéK̯'XÈ,òÎkÚãlü\žJ«ËOêx\ÍwKn1«2&Y“%MCZßLLÊ ‰[<Ì ~ÆA »:‡¤èAvu G(…14Ï~~Œ©>4'TÔ¿Ÿ-xfÐö8ØÕᙡ*€ `,Ìëö ±hj_'„¦ÿºV¹ ®Šop¸¯ »*#ÃðN#>þ¼/…‰ç®%/9`8©±øèM›éï\´cÅ¡ÐÚ büÑÝ5æI…Ú¹šáœŒ##ÍŽŒÊzÉÈØkà†Ö£ƒTG¤  Áãc¢’0/ø!{6RÜøë]ÍPšè;ê£wbÚâ%ýûêã?n=¦7¬]ú}—ìÅ/vrâàE -xDnN?<5ÚвéÈÒ„¡‹(GÀØõå'Ý”6§|Ÿ·alûñOR¡ëe|Ñ:…š9Iw€_¬W‘׸Dà¨1&Í-Ña¿j‚ÇaF4nØÜô(õëp×ðQiGÌìö”Wܸ§·Ü»{[~éµ#sæüz»šÝé…ÁÁúëYœ—}£Œ1)ÓŸ††+±Ó'EûåîùÔÆfâÉÎñ}KC¡ù'ìütâÈ‘S7r‚§/ݬ,’THƒ2:ŠÐp”yî8q9xðìc¼·FE±¾È‡õGt€O{6x\C7¤mó Ôp×èQi¤ lùÕ°W7Ý8x³1t÷<1çô¾àµöþ\ƒÕTç3˜ŠÚE>£–¾i9.åòÃïã¢\ý©•GäN[7¦®õ˜:tÆô©Ê5æÉ…ÔuÑT‚†£¼ u­ñ¨qúÓhrt…ÀQ@˜ðSÃ&xÁ‡–2¥ ŽÀãèàˆ™-?­Û-ûÊ­ Ôcêç³fÍÑß¡(ý/­ ­l–Yž“S0'Š“‘üÕZ´EοRPTT.broý®¤BZÔY ýÂl¥E瘰pd6¿àqÝÐl0-œÒ¯ÃØ#c‘j”<®±6‰ð8­G¥é”0=ø…w‰ôp¨>´dí–-ë7ýUÄõïVÿÑÏH ¶~,LxcL0çÁlïÈ»÷ý*Ã-*áM:>ú!´cÚ˜1CFNÙS?bB—¤BÓy¶|ލqÝøxÿ]Ý}€#ËO³ð827¤dZŠÓ}Tš *ÓƒŸXŒ×›åwŽ¡õnçŽÐAÝ`Ïð×§O4éí~øŠÖS[ú¨®ªBÈoÈ’„"§B¨¼˜ú`Ï÷×ÿ¼y~BdddlÒûÄóOp›2–LhéÚ™[~xŸ0EiöªaJu¥GæÂÙ|ùÀã¸aóQaX3xœæQi)}W™9àÅÎÑ;*¸<øÌûãüïNƒ&}5WAQtí”zÅ]9Q'…L;Üᆆ;1œÂ¢µJ›»ñÈkañ ÷?]ˆUÉ2ö¬¢aÅÝàèñSñ¿º5æ÷|:‹˜é@&$*͈CÃ6Ç·_L[6ºÑh¨ pÄ®š6BÃ&x\7l<+¥Ðp„‚Ç)Q/U=*ÍcÀ¢aÂ<“d"…mXüXW´ž˜äž±ueîàÄAMœ]×êÃ{³‘´ôß}zyä{/á/„ÕDsтǎ¡íwàIîA|TJu¿/•™isclæFÆÆzðùüÔú5æI…¬– E7àŸÙ:GýV¢.8RCÁè“l‚Çé¸!3¨kÀx µÔµ=}ûMœ½÷‹]Í?ú“ÞÁ<+€£&9Ñ^i°ÉÄXsmO`³1þL“GM2¢íq-5ø5YIH0mW4ÃZk?3̃,€ÓÐö8:ƒÓê ö€ ¤0}´'©Ñ @¨ Á z@ E"Á¯EÒF€ PA‚ô / €@‹D‚_‹¤ Œ@ ‚?*èA^@‰¿I € @~TЃ¼€ -~-’60@€ ü¨ y@Z$üZ$m`4 € øQAò€ ´H øµHÚÀh@*@ð£‚ä@h‘@ðk‘´Ñ€ T0>øÉJòrK„2ƒÊ„¹×s« '1˜.€ V@ Aðþ˜|½ŠL±ð·ñ[þ’]ª— oÆ¿SRÿSóWr~Û†ã…ójà € `Y?T«Wˤ ç¦Gºè½Ž/Ø;ôF½¹l’$7>_›'°'¹"@@Àê4 ~¤óRÇÅ›½ðÛ‹÷5­·ÂSÉÄ?.>þ«ÔðÓݹ¤*@€ X £‚/ þ¿¿­è|lç]Qý½Ç§LZ¾ìngÛ-7÷d¹Hmq~ë7oÜÎünüþI©7±×>~oö©™Í\´âǽ™s_n¯N '€ €@³ `TðCölžK+…ÂbÜ€ûld7o{^ðˆù!êÞR ù¿Q=Ùö.½^Žþø‡h'â¼<"/›ß —âƒÎO Œp€ Ð,?*ñÉ«ÌÍqöDZœøS"‹J©Í‚ (@xFhüXUU?’«TGÝÈMîíã&‘â¢ç“牛Ÿÿqý1’û_®Îfè°÷ AÇþÆã@eDIp€ ÍŠ@£à×m? SxÕ±'÷\ wOoØýó\´öÕîíÆBï§þ;sö+Ý; X«ÕÂÓ©Kˆ:$²{MZ…>µS`ûé)×uÒÀ@@ÀêØ˜§7âì퉮̫ÉñóÑê#S;›Wä@¬Œ€Ùâ„)íÃ?<Øíر¿{ï¼ 5ÆÊ5u€ €€‰Phù =,ˆo‡²‰í&ZÉ@+!`~ð³’ @ †^è.Ê@‡?ÆQ€ X~–FÊ@`üG € `i øYa(@€q@ðc%` €€¥€àgi„¡|@Æ!Áq”€A€ –F‚Ÿ¥†ò@‡?ÆQ€ X~–FÊ@`üG € `i øYa(@€q˜½ŸIMH¤d¢}¿¿E&¦$ë { êü©Îo¬ù솉9šNÞ)´SÓ‰ž§½ ©T×x+øü *ŠHóŽ~Tþü ÃC©Ô}fÁWT²“æ}+0TþÜ Ý 9êºCËO œ€ »+&ð­ÈÛ4kû¦ b{½2ºÿ+^Î|¶s—⣋óó) Þ€2Aù™=w±¹–a‚ÇÞø3hñBüoÆ‘Ããñɯ—(¤<{èâÿˆc‹nÿôÓ_E „ì=¾þ`Nþ¨W{#èS3‰ÇŒìIq¶dT–™”K¸òø‘¯ƒæâG–܃OÞ?W#C²Âk)AûŠ«ˆTÕ§N|=þDµ ±BºJìêîÍey·íúYð£óe2u)pB†½ÁOV~ç‰o'®zÿËÖTß‘§c‚ÝÈ”’Ëž”9sãøcrp2fPÁ†ŸRŠK_öó†7_žä¦}[ÉÓ- ú¦õ²bþYMî…ìO'sà_þ(•¿ðtp˜ '¨¼‡úM9ûîýéÒ¥³?Ÿ›sW†ØîáË_zôêç–dõÝqbݰ>þ˜M[nˆ·3B2å[¨ùæ>‡9Ãf½Ë¹Ñýbg›c/}uód¡Xù’h4µ÷ï¢éï½ó¿—s~Æ]žñg6þ¢g1hÞ£_Çž{”ûÏÖÉdž.íË×î“”^þïÉy2ŽSÄø.kÆ£5eåGß»nS»õó¶L¹ØwtæÎ#•e†=NVô¨$÷Q¹N—·LaïÒê%$“#…–wI*”‰JD TFBÜ÷‡ñÈ”ŸM¾<Éëò[ùœ Û:çn òI«ó>Ê{¬ÅB…­¿g0’)t\ï,M2Dgðc¹‡DÿòÃm¢ÉI‰ö¹›S>á2ðÍOT±ñƒƒ3?8uO'úá¾Ì6+.ýww2êóýÿ\qàк»B.’ŠDµ¸ÛSX%‰´ŸÂ ²ÃO®nø£hÖÝ€¶GJãÏíï÷V¼¿§fÒ§NJâGmîo›F¬Ùô¸ÿYçÀtYöÑ>{Ñ—‹>þ>}÷Ëg‹ª‰‹²»iÉq‡¸ ¼ô"W‹ju¨Nnøa.öž!í¿Œ}*ŠÙ‘h{ióE/n¦o~µgoÜ8tã/·š`SqüÈŠhTúŒ·vîÚðãý:ÿ”ÕÖ kŸðG&a°VfZw¹¹|òe\‹ñ¿œTÈ}»ïíÌþk&D{h¢ ‹ ú:ePÊc7ìM PÜYºôóÞÙ·¯M_sUõ…VòøòÐågÜF¼óiªÂ3Œ´š rÃO%x0!ŽgǸ!3ã†H";ýï8vöî„Wµvõ]šZ™Ê.f¬Hí°vW°;}¿"ê½Y§Úíêb[xüÜì-÷ˆ>Û¿µYµ÷Å0âM 1奜D¶ó&RÛrùDŠNêW$;»º~N„?¹Ö7Wm»ðÆ'óFñYhÑäYK¶þŸÿ'[ýªÿ>T‚S,[¶b‘0ðóeo½QŸþÒ‹‡ñu\ÄrA­½–ó4PbëàMH¼ÕìÛÛÕ¿ø8j’Ý8ú^zÏßvÃÓÒßóß_ÂgOîɳͽºgøá"ÝŽ¯7¡Ðƒÿß^“ Îô"Ù)Mat—´=Ké`>êÆµ-«ž#­¹ÕÇ¥þ=ñz'zo×Ëß¿šâuù]ÏâÛgr±¶v>@XÓ{Ô‡;£õš²)((  ã[‚åÍèÂÜ@9°¼™pr –7cô˜˛у£KåÍ,. € À|ÔZÌ7,@èA yº=é±JZ8……VÚÕ¡…ãæô  íqÐò£S(@ !ÀÀ২‹xN ÌEÀXŽÄµµâ#ë™[«çÕ2cÙ|^ñaB½å<Îx¶ÔÞÏÒDJAUé¹²jÄæ¿ÐF³Œ©°qA2Á½Oý³Eya}!‰>MÌÔ<óÏñþ÷Ñ•Áƒ¢ &$ÕN*llÒ3)!­;©°qõåHV:ñÈ…ªü,ï¬{žvKªTØØ¤ç\BŠ©°1PƲYŸ<® Óþ’ÒA*l\®±Ç5ÆÎ „æàw-çtd^Uß Uç ½î]0¤W€-"’Y%\‹#?¨ &ıúÑ=v­Rñ£ëý‰¸Z3ÎÈ %ÕN*$Ëý ÊHëN*$«¼ñq§„uýÂÏÛ]^¹<í„fvõ!+‘j'ê+ṕ“¢D*$ƒÈx6‰Üàqd6-#¥ƒTHV–ñÇ‘á§_Fgð<¼™W›>hx4ž—©&?™ZZ;e7’6éÄùs7+&Ì[!’ºûD5ûª–¤ç¯½t-ÏÀJMÒ[K½B8²wŽk§špí5Ú×n9^ MÏAÑ$=¥>bŠÐ™À&'xœ97•õ83‘ƒŸ|oÖý•=†t©¹ns<á% äh=’‘ Ém”Jñw>Qä±CªËÛzÄMöÑ^"Y'WÆ?éËùáOœâ®éÈuj'êæ{f‘ÖTHI!EUòÅk¹âêu5òÝ1žä%’ß!&˜¤§ØçA Ç|–IïdR!y]ÀãÈq¡CÚDóÊŠjä4ÈËö׌ü%]âž–Ê·+$dB}˜m½=}eøíŽ.S.ßÄ ’âŠÝïÛ]éÓ)ˆq1z+!%Ón’I¤ú[®: ÆrD`dËêéÛ&ÖÏ7¡ŒJ傟$ÈQ7‰¤ÐçCÇ|ž©ßÞàq–bYoÜ0O¡RHä(˜[l¶Þ,;ÕbƤB²òq$ãvoE|à pÇ –ªWà•)d­µtÿ½“‡ÛˆSþüÃæKi¨¶û‘´ ý# Iµ“ ÉLze¤u'’UÞXŽ”y¹Q#:vû+¢ÕòÜ»¬’j'’™ô\ËHQ"’Ád,›àqdè+#¥ƒTHV¢±)ó‚Ç‘A¨GFcð³Aòškûø`—7ÏqîxÌ#‘lÉ„ä¶8»û$ QZÑÞË*ÁÛuxÕá¬Ýšv˜ìÄ•ú[w¼lé á‘È.õ…˜HòÑžvdÚM0‰ÜÐ,¥ ˆñ‰…¥gV  ™Lpøþ#Äbééa§jR fƒªéàqT´|~ª·7xœå8ÒóD2G¡}B—À3¥¯ôù>º%qø”k‹xOH„z6w³÷ÚÔ¥µæ_gbõ¬Ý1!êuÌ•ìÔÝ›ö,®§*Ú±=]kg®žj°¨šdLÎC£9B¢Òþ—óë±p9Ñ/LÏv”Mª×ñüýc>ç”ooð8‹‘Lïòf¾{ÎSÁý9¢sÏ^Šì:´rã Z2¡þ Éj+dr.‡[¿“Þ˪tä‘ Õ¾]Ovo£?›¾+¤ÚI…úJxÆä¤u'꯸±)Ä2…T¡pf‘7Ìëj'ÖçxVþj/¶dFÀãÌ€ÍÊYHïdR¡~ÃÀãôccÒm£7ø`;òÜØz»äž´¶HÎ[×;BÙ(#i´LP8åï{õïKÌ 0ë ÕN*4«ø–—‰´î¤B#ë TƒdÚ®Øà’1?U›ˆ‘G*4¦LœØ4(ã“‘ÒA*4²LàÈH $Óö8úƒ_eðô! íŠúÒ¿ƒ¦Bà ðü  íq4xy~„š€ ´l øµlþÀz@3xŽƒŸäÖê©Äñk޾ù×zñ¼µo™2ëê[ª™Œz*/­H"VW µ 3M‘vÎgãÜhèW×4èŒV5†ÚX‰Ñ 7.ØlŒ‰E$ÏGzæXW¦*+Cœñó>‹iË7`™°4ç诿Í.2kí¨uË­µ–4Ï#eÅÎ|‘ !=7´ÊlZ‘¼ô⊅[ïªòx÷š7÷ ¥*ié|FN›†W8j!l›Ì'ê9âè9nù÷!ÇËÏ—!Õw³>]¸î¨€ïP­L³*››ççç…39èËØPÞ„"qÕcv¯‘ó–®Z»ôÃ%×ÌQ`º¢†Š[þï& CÀQKâØd>[Ï GÏs˸ ¥†›nœÀf-íÐÁãàÇÓ+uoZœÏ¤Ã°"^‡¡³:¨Ê‹Lˆu]ýo…ªš”¦*2ɪ‘Ø0t8j,Ö lÖ#ÁÜ¿Ï Gt¶ü$ʼnOas7×D“kÚJÌ%Ú evüp O¬ó!Î`ª%ù»Ò*; ìÚtgªYš€#³`ÓÍdaŽt•úlBÇÈkf82’CÉ,ÃÁ¯|Ûâ­ì1IïtË_7{s±\ž±uúÌ_oª\kˆ@iÊœw;Œù`€oÃ+ôü.Ž(iiŽŒ7<Îx¬ô¥´4›àqú7^n)Žhìötòa’Gd(-‘ -ž>¡³ÖÖõå_ÑæM)—KÄbÄá5üˆ—竵Ñh™¢â”Ÿm3fí¬AuãjhÔWW” pd<¨ÍÄ‘ñ‚Çj&6Áã˜ËÁ© uìÞYÙuL9â¹óèŒ&€hnRaêç3–¸~¸ved}ü‘ …bDÌ?–— …öê¸H ¸FŠ$÷Ræ|q–»øƒ~¨ººÚŽÍçÕu|RSÔ vPd¨R5@¦ñÏæâ¨±%ú$àqúi,o.6Áãs¡ObmŽh}´jUŠÇÇò1ÿ ª«%¾º¡§½ÉvÐBåæÁ•ëÒJˆšlÿâêvï¤ Kˆ(ryi)±?ÔØ*6¡HxçÒY1þ¶˜¶xfQ¤ëµ+Gñ×dEÆ„ÓGº`1‘#] ý6uÑa"›À£8ÒzÌëÚõü²÷爷/ž]’´jî¬Å‡äÁ¥äÚwdÝ8K%¡c—$mFþ±¯Wì¿…8áúöRÒÍд"^è¨ääQº¹ˆ_&*j\@K—4 ®!pƒG]T$IDATÔBh6™OÔsÄ,l­¹‰™ækÒ‡/HŒ44ë]“Þì3«)2ÛBÆf´tÖQ¤½Ì®˜·ô…­­2ÖjŠÌ ‘áY¬ui{?†ß{`Þ³Œ€¶+šQÏ–ų̈2d¨  íq4Nu bä@¬‡?ëa š@‚ó‚ŸÕ–gZfˆ‚Š –@y*Ãb³9ŽÄ@ŽXhà'°Ù² Y€#æ?¤ZV|ù«U#0ë6Rn!„·2´Ð±Âøn•›-Xî_›j£96d4 Wf¨_›hcÃåóÝÝù6qK¯ÕYqmg,æ;ŒüªÐ¢•2ÍfàˆùÏ(°É|6#ú9bæTíeÅ ÖÇÌšé„$2äâU?ùœÌµë÷@ÈWN×#KALz EîÎ:Їp”u÷w¦£T)‡OÝÕ]V°Ü+r¶a™‰Q.‰|3h}ú•÷Gs#§lp21š]´”1Ÿ#ã‰6™Ï&pD3GÌ ~ 6[àð]ø<òÂÿ5uX«uäáîáîìŒü𴿸x?eI£WF.¯"fÏ‹sÏ­C±W¦G£Û?¦âßûÿ$FÓ¤£u—~Ž˜È‘ñ4›àqÆß-zR¶°§"»=u•á0P¹uáL|Lº!«œEH±Iiýqg$߯fÆ…2š®¬0ùÓ3&vé>7k÷˜0Œ‚Tú¡XYáìÐ)IKÞGig ð*0 <€#æsdüml2ŸMàˆŽüxæ._•ŒMK_i›ýíºcãÝØB)»üVZþÒ¢mÖż·‹ždÏï9hxlÂ;‘eÜ,RÚÎF‘èØª)ë–¤/ß¡¢'F/§f¡ª“ 1Ÿ#ræÈ¤À&óÙŽèàˆ¡ÝžZNÉvwW.ñlçÕ9Ôõ÷SU8ø©–|&[¦]+ŸO9žž¢x{¿˜¾±hÓ#ÞžunQqqQ(®ß‰‡îÌ~Bp“µhJÖü¢§ÑÜkŠïÈœNOúGÌçH‡0ƒ?€Mæ³ ÑÀÓ[~òòü¬[¥èªó/üS‰ÚøÖ÷ÃK€'Μ¹([wø§‚¹¬,÷µb™L\‘{ìpŠ ¤’Äe×Îd d¸Ð‡.G±.¸"Ρ& 4áç·üPÅÖ…£ù=Ü >´šë"pÄ|ŽŒ¿7€Mæ³ Ñ‘‚…ñ~§N©Y‘]öøú·«¯»àÝ÷?ÿ7@½KRã,º‚Ú8ÙÃô˜È)u?VÞ^?‚_éß½¾Lôþ‰‚ID@µY›¾Ù+&tÇ›EÎÏI‰SÛÀ€àˆù››Ìg8¢™#æ­í)¹µaÎêl1¢ÞlA.Š…r¤ÙâŽðiÉ­E‰«%}§®œˆ§Ôù‡W«6[˜õUb‡ºíðê¯ÑûW&ˆðÄs®›3ýu¶ÉÄb™T*un0‚T,¨!g7gB,] G4q¤½Ò äг¶'°I›f0hlàˆ&Ž´=ŽyÁϸÛÁ:K€g ¤"G8"ÇEKªíŠZbcOé ~Æi6é9SGM¢¯íq-5ø5YIH0mW4ÃZk?3̃,€ÓÐö¸æéKËÎû’i oÏœÜÆ‰;…†46¯$<øS*üþ+•ÜÖË{#熑Ê:…v22¥Õ’Eà ,Ö9²s¬£ÇZ öìm\làè× ›YJÅ€±w2-Ôón/ –Ü0æx+0Á˜dÖLã¦50Ÿé£=­‰ è@xNhž–Ÿ!pkE»Ö•>D(bŒlÚ¼§B¡ÜÎÁŽÃ²1÷~ZѾKx½Ö¨$ßÖ, i¹¤¨©‘Ù±ì¹,Ú^ $B ^¿”ïbѱ:tÔ]~ëÌéÕ•µXÕ¥•zÛ{¹X"´µç±ìÔrIt•Ý\–^t¡1ýgyJH’×€HQ[-Q 6‡¯M©Ð@!Æ\ª jåȉG}z‹1Ú¬’Fzoõw{oáÅ„ÞP?“É«…b;‡ç`ˆ¤[§w¬þ¯Cä;ëƒ7:XxE]„U"‰½›g¯M2%ˆÄB¡HŽÜøêZS*Í‚™%÷ö%î.A(,iê€Pµµò'Õb[6‡Ã6ÄQѾ”GË0G#6Œó§ïÑ¢ŠjeˆåÂÕ&ƒTH‰@H<ÝÔµ¦Xž&»:ºhDÍ}ö´Ù xÓ7̧Î6yEÍÞïãpˆˆ1­cƒô2íû‚Ï—G»×âÇ“Eyyé÷“Žþ©Ôñöº±Ã‚)û½äá¼ðoê¦tÄ Ú—ÛQɵ°ôÁ¡ïO.Ù–³èТ×;ÑwçREG&Bœ°nŸðë"ŸâIƹ É8â£mÄÚ.­ôÞ©ÁI}8)çóòe „ô2i’}²ü+K¿=ô‘'pÖüñïz7©Ð¤b%–^[óVä_*qØ’of/ ä?D§.ùöpê‡+«ãÛ¶Ä (/CãÇLŒ©_5^^yóëíàpˆØ„ic”õTþlð¿v½GÏs9¶âT !ˆ²4(\û§LPøÙšÿîTŠ>›ºp²/åw[É͉ÝGîPéèõYß|¡¼g¥ÿîÜöÝ´”›÷]šªrBm;ší\.@¬¾óÞ i[çYòÒ© ápˆ°¤éBõrä3lÌ«Gþ·³ǪºõA¨ÖBQ´sÿ®ˆbz¿ ‰/Ì™SÐ÷ý¼«œ¦U^)jz,¯VüúöCõNù|z.Ñ‹]QTÂ5äLv?W„J,øpm„[qäë>êìNui1›–{ÇuÊæ”/"CÜeEËûœð¿™Â0«oÜØ¬W^ÇŸª$2 ¿A›L’ƒ‹¿þ©X} G¾½Þø Ú¡´¨‚g#6ÇÃ/Qb(‘)Æ( ³päø¿…‘¡²ËS–ü1hâÔ¶¤BSJ%Mëšpøç¢C\dE[½F,Ø6`Wb8dNöãÖ„7^$V?¦Ä-·Q#»e‚ûWöì˜nsôߊF׌Èë+%}(ÈAv£F¹ñr–c€[×ç3J‹y‰j‹‹RP›µo¹È¥,_÷vîtÜTl~tÿ@_6ÏÃ祹>7Êz°[­84÷úšq!¨Ö<[庚|b،̫yºëâ4JfŒ@QŽeÕçŠwL——B{~¨·c-Ôú|Æ(i:TŠï±Qþîxµ¹®1o_Ë«͑ ›.Ëp ¿È.ýAM0mØDêWå|/Ý›ùúϪ¢>Z7&6XoŸƒ êd•»ÖŸÏ—’úpíQÜ6Â+¼p;vââwÐJi"ièðΫn¾Þþ8þ2úÁÏ¡£‡¶nëFõPkÌ—¤Ÿ®Ò±<¬•{vÐx™ðìûO”=:Ò'PI,°%Ò S^xd÷ºûO¦žÍ𷆨yÙ¹¶Å7¹GÜæ8d5¹W ·o,Z¾ENð_x:(ŒªÇɈo•«·|£ªÎ+ oPÖmŽ*â+±ßRÎÐ/?S©_5uÞhõ*‡T,’?HÞ¸=·8}Ýï7w &J²ãG{h «©«›7,`Ö»y?v¿Ø¯Ò´2pþmûp¨>.äb‚£3׫TE%½jEŽˆTŽîz–UÖ \ü:kK&ÔE¬_²{ýTp/ýç7Vå´'аçwˆ 8¢þþNõÁ§]¡;;ïý3 uÚ6qã6]%ýœ=Æú÷ik_z±ø×ÝUQsZQ½w´6çœhê}ôͱ¬‡çÒ?L*ˆ9bN1 òس;÷éäYåv!õ÷œ‚êÁÁž ®Óò“ãç=vþ‡Êró> Íüe&]‰ÿ0ª‰¶ZSª‰×‘à.«ÂÜù•«Ï\;Ö>v,åo2M鬿Îö8Å-ÛŽ†Šýu¡öRr"Öç ð×;Þp÷š Ì³ç½àGïÌ)Ý0øŸÓÐûÛzUos¥¥=DCÜÞ¯ŒzoxkÞ½+¿}‘z9.1Nï'\ Æ›’•x˜¬úàÓÑÞܼ˜œ5t1sDìœzÆÄúVµ>ùû—%£ƒÚ™b’±i9N㻬Ö”•}ïò¸¹h}×Û‰^¿ EÍšÝWvü—=ë.uIhµ§"+$¼Z¾u<ê#=óu6B^2r$‹‘töNáƒb=Ë[_ܱ,çfÉ :9¢+Dæ·ŽßÔm¿üç””ÙûÃ^™Ö=æEwêŒà–Ÿ][ÂNb¬’°D„ps·å i-ràÚ7‚UüÚÕ¦]âñî€CÔbQHW à†õCÁÝS}&æN¹í©~—ÚjQý½FëWçÞ?´ýÖÊå5^ î §µF1òa͸åçíéJØËwí„ÐÙ éXoeû\¡ >àztYORM[¿¡ÿ¹^xY`×zÎD﯎9+?Â’ Ir›(r  Fqaÿy8î‹£~ qªÏ«[‹èô¦ú‚ ýu·©Ú~ïã)Ó÷{ÏðÒ‹®ªýF ejò¾½]C”M+?¿Öý{W§N!—KÅRÄáÔxj²$ÚàfEhgOÂíý½Ú t5¿¶w8 …ó£¢££Pt¿­ÜßI›}½ºÕŒ]BLÓp,Ü:zPž~ðÎ÷ÓÊDr&l x}˜;ÅȇkNpÔºñNâŒÉ.T …ÁIÅÈÁð ]ŠÐÙ»¾pkq»‹ù"÷I=\~ZÊá7>©¢&œŠbºx4`àÉIOÛ©ŸŠJ£ôì§ó¡Är‰ÿå‡ÛD““ís7§|Â=dî7?5fG$¿u ?^Ñý<Ü·Äm¥Œ|˜þs?ÜÛòMQ¡n%0ÔZ žpý¼z¡{×s Å…¹Å8zÕ™d0›Á‹’Ç3²+…2Þ½âÌÉS(¦nð\(Áƒ±q·§à±P(¤áüÕ ôͺÐöHiü¹ýýÞŠ÷§þ´d¹D8¢’¥¥¸Še²ê梌|øM%ûBâѳ‹Jö4÷Qª¬ºªÆ7(.ÜÇþJʆö=¢•ÁœT¨±ÁŒ³ÚêkÙù…"¹L.}påÊr梺ÙäbH\.$:ËJEb¢CÊj‡½gHûÄ/cŸŠbv$Ú^Ú|Ñ‹›iî7?µÍ<ï oTyåñíðf~>nÒû×½ÁŠSw|;sËxÝ]íà ŸçÑö%”“~_€õæÞÏÃPùæ©m…Éçâò›gþ} Ëe’ÒÃmA½œT‘ …aUîU,¬corÉ 2d\‹ñ¿œTÈ}»ïíÌþk&D{R†ŒÓÇ€ÊÛ™GE×1Gíë‡W‹Ó?ÿfÛÌîâÛQë û©¨¨)’xö kf›óæ±6ƒ•Ý<¤B-#L?•”ݼšñà‰L&—ž;˜Œbë:7äáA®buéã'â+”yeHÔr<;Æ ™7D"Ù™÷ŽóôILʲ'Þ0YÜ„¼í\;€5پ𦫺FiºÎ¤2ùÓŠ  ³á ©McËõ½=ß›·çâ’ϬïÚ©_ÿ§5VRùà­„½õ‰{n=¥ªæ=»__šCÈß^ó= Ý•=b;-bÊK9‰l:XK$Õ2;>1àÓ®CthlZλ„¥®!Ý^V·÷•^ǶÕw yÍÂiéö"É>··‰|û®ŸÞ½#q†H…Ê+fÿ¯æÊ¼O5;o,ûf’ê§à¯‰I?¥¥®h¿%¬Û¾ŸúÌmäð#â"¾Ž‹X.¨µ7ÏãjB¡ÄG¼s9´ùhhäÂÃ?¥ÆVðÆŒ‰Ö~ׯ—uÚ}ry)þê£lû›h³)Éí½—¾ýrÌ_}Nd ÚôÑ0Sr“§­Êîÿú‚úK¯Ÿøs¤ ¸¬ÿÍë¾â!Ÿ¿ ¼råŠß";¥I#hé’–Šª…RÕÄKv›aS»ü’¼í_ÂP^̼Gâ„8”®í`§íárye)Á}OEéùËOüM¨k5}˜ñ>Ä"*¯˜ý¿Ç×ßî®^¦êä‚‘ªjÞÜ6güÇJކþ Ü)úVÿDCºµA2”Μklg³ZD6¬§'·•ñﯜäîÒ*±›Pú”ų¯kQ`kjkoU ÇήÊ·•yÅî>[‹Xlݯ: ªï²wopMíS–KKƒT&üdG]¿ÝU"Qà·OÙtQæ›4áú$Êi2©½³¦ð&ëO`ïd'¾vi¶ bU„r’»“ÏØážÃÅ2¹=›¯¹¥äŠÅˆí5Ò[‹ŽÇ_Ÿ/¼…ìÂë§IèWbì^ï·®÷¨Åañ‚êÚ‘ -‘4ËoòÁ_&ÔJe¸g‰ËÑĘàøýãIsX]Èq6ën´ównÿùû’„÷F)'¹{uˆKìS]+gkߋҲKUÈ5´O¨Æ Qþ•=+Ò‹q¼48!‚¼ÛöÎÿO÷*©œÃåª9¦R.'èÕ§×Gˆ%x0Ì™§~£FQ¾y:JÁ óÚ³4·JÃk&ü¶sãÔžZœ\‘4õå$wרlxá‰DîÀçi‘”Ý.AŽ}_h«…ÑÃc»þ·¿qÚœ‚d‚)xlYäÎåaB< ÉÇâúœ¤Âú‹fýe‡Œºút„DŒNæè¬á(4ñÛ«‰f•¨›Ió¤Ò•7ß/÷Õ¤€†êYv<]·– eø=µßF—oßÄ> ³Zê7ËÁI×$ÊŠlÙlüyŒ4®˜]‡>ý“Šíx­ ‡¯*$váaí£´Sz´›5¼¶€–s{ÜOа$RaÃD&þ¶g9Ø#uËÖÄÜÌLîÐ&ñƒ¤†¦9pøº€Ê…5lÇ6£ûtÔNÔëä^ÚKžÛ³ˆ-žé<ìðÚ(¶V  ³pZËb·±~fÃÙœ³ŠäU×¶}:i§ô:ÚZÎñRNU¤BŠêìÙø‹"Ç"Ñ|7Q¬¨ñÙíÜœÇHË+•ñ:!¥iØ:öz±¯Õ¦Ù©MEÀÎ5tÁ»¡¦æ‚ôÖDÀÎ+ìõ•aÖÔØ¢uµÔà×¢Aol<éN“a‰Õ¶hšIjÓ„mLx 3°Fxy8šÒ"Hí°Úö6™ºC‰H­a€pb¬ ¬‘«¦ÛF{(‚±5‚t€ €@‹F€y-?ò]ŒÙº»:e’I‰jERbÇ'-¤B“Š…Ä€€!Èwu0”C}ͺ»:¨ÕÒv"©­ÅsS\¸:_ïI…´©„‚ƒó‚ÒÙÕAœÿhËn­I+<§w>ôÐŒûÑÅÑ:»:ˆòr'$]ÒhŽˆþéKQšKÆŸÕV®íàiUúˆ.kFâ‰üòò‡'ýY'Œé²af¤¿YÃg·R>—èìê ,<93OÖ¬?£V½Û_=Û¡^Z÷×:»:‹ÿîœü‡FuЈ&õÔü4ïLV2ã‹ûUyƒ÷b Éwf¯ÙV' x|Ô‹ª)læi€\ G€Á#¦ÙÕãï2ö-<°Å†Å–ÿý}iAG_äÃÙ¬³«·Mà·[üñ¤C[zbÒÁ¿»»ë 95“pvÜçCÆwts’ ÷M8¸þxëµÃÝk«Eì7ûnŒ×l®þiÒá5J¡™ÅC6@Àš]x~=þ36;›%:¸}oV€¿¾È‡Ë³Î®<ÏÈ¿ñF[Žƒä·5·÷¥ažr3áÝYþ¾®ŠŠM«6θÚñp´ŸäIcì˜ãQ|Ðã/×$¤‚ ®µd,ó;]ËÁˇíåÃreÉ êÕS=›“uú&r’—OHY_g_ž;«öo„ÆôwןÔè+,nD”—§“×ŵ÷d·B!1œ4ml¿;‹ëÞjèû>…YUtíí`´Y°… @ß®ÈßÆË»W+/n 4 ± ±¥{^€»{€»«·½ϼOììGƒB{ç>íü¹,Ï{èK>¹bÂãxÞ‘ËD;syέ' ÊÍ$¡AÁPèmùÑ¿«ƒ¶û+¥<§pOCûù©[ç¤ðì¿…Ñ]=iz ýœSTöð÷¿*f}§ûj[[µË×öÑù4aV%%ÅW/Þz×µ[Ò‚iÊ…àñæQ:‹ä˜U*dj.èßÕA]“[W΋£z{èNÞT_nŽ“Üìs¹A#ú»ÐäqŠÊiçUÝù!ëᦼt*$+K>œ?~Ü(êóËÀãt€eÒšn#e•ð®[—ÆdNÜ(½)eàà~.Ôï:°$—.Ƀ^äk†©6?ˆ‚“ßÞ5¶ mŸáìÚwmÝ9¦=žáçÁ­ú‰öràt|ÿI½ ôøj%7tZºmñVö˜¤wºå¯›½¹X.ÏØ:}毷 å€k Fïêp>4¦èñÀ^ÕÒÁ™)á£û9Òæ!%‡ÿ©ìö…}²éÇ­|÷ï9Óû‡Pwƒ:ÓlÙ‘í:õ‰ Aèúc¼œ§ú$¿a÷±Ÿv¢>¡³Öv P dmN,³«Q£ê[W²Q«yÝ´«×|»:VTÝÍúu9@ãÒÜÎAí:£v='ÕtýéÆû‹=•ý-•Éß­YÑzìõáátDYð8í;ˆYçt?Õ®Ñã•Ý<Ÿñû†”ÝCÃf—£¼»&^‘5óŒÈ¡s+_¼«Ãý*l_ù¿6í´N B-½´?3`òPšôÕVUæ=r ä²ä’«¯£ˆh¢àZÁþOö§8÷ÜüvkTS[cgçDu EvPd¨ö ï¬ì:¦ñÜy4UÁ‚`CÑzPíêÐ>qQõµó…?l¸øæPï+å]¢t"–ž¬†ÅâSçorB‡éÜxW‡ÍG«x¾ÿ^¤Vû’Î'ˆ!£jÿ<"䥩Á4õUI„%YÕì0o>[!üëÆi4‚¨ˆ¬<ùû¯W8¾|vH'$UÙ9¸P]<ΩÍ{Í·.­»:àÐWQs¹ ¨[Ñ[—ÒôæØÕ›T^²1Í{ŸŽ¡.ª ÿ'é|}Ý:,þ!tѽâ”,»4íõKÄ¥ˆèÔçTÕ<>}ö×— ›ZwuÀ·weîïUhü+ ê¢ìxiŽ]pT“rÑ÷Céꢪ•èÁëÉûê+ØóçÝp;OX–·¢Ëþèû¥rfEЈëÔçTÔëÀÁã´ÀhþSK¿úZÑ´«ƒ›KâÝ XC³îê`çÞzïÁ‰õõ¤á/«uðÞƒAµµOq_.—[×»Ë î¸÷ å„4XE´hÚÕÁεkrb׆unÖ]ìCò/mh…ßìVÝòw‘È2…‚W¿5 Ï·gþbÊ3)XY­‰€%ƒŸyõ ÝÕ¡QQÍ¿«C#“( l•>¨ÓÓD¹L(h Ò]ejþ]™DY`˶·¥mLek +#À¼àg°«ƒq8A*@€`Wzp„Rƒ€MAA]Æ4Ú‡®¢¡@àYD °°JµÀ㨠yŸC´=ަ±SÏ!ŠPe@‹ó‚ŸäÖê©ÄñkNµU¹DX]]ÝäRC·ö-Sf]}«É¤ê¢Í=‘‰ônÀ%à2 ,"6HcíŸdipd$PÖKFÆ&xœõð7FGÆäÃiÀãôżà‡deˆ3~ÞòW;ªÔ•çÜ0=qæìÙ³§.:uÏPXk7,iÞøn•‰,»à ,ãÇ\¾»»;×fä<:" øÚD.—É·‰[zMI…ú˜´ª\‡#aίÊwŽúÿÍݧymidpÔ’fè°‰g:€Çé¸a³óC Ãx\ÃG¥Y10øázp¼üÜÙªa’›?þžÝ÷ÅÉɫƄ—ìÜ~Ò@ô³cóüüˆ5útV‚1 C™ÄY³¦¬[y¢àéÓòÝI© ‹÷Ñý<¦>]P^]]š5?mÑ„mJH…†L³â5 G¼vƒÿC‹—.Õ “×¹½¡}€#+’d´* ›<®¡¢ej8kô¨4z†Žö”⦛j ²LŠCK—.¾¸rÁ­]ÑÑ*ü ŒN¶l“O…°Tú¡}ˆÑ=CbÑ´G¢¤µä…94 Ž_\¼joÄè•‘Ë«¤D)¤B³Š·D& Gl÷6m”óå«3²ð>ƒÂ «Ž ãÓ,W5l‚Çh¹a³ðA¦TÃx14øi¨çuÕ }›¸¬o_³g+‡$½l U¡ÉeÑ3ç.Kç£îÄ•+ÛÌ›¶ùÊ^êËIöÊ “­Ë½wrÝŽ¬Ý9au5 Z´v ¿u|¿ØuHoÝeè(”G!+pd6xàqÝÐl0-œM³ÿÕp„M‘—^þ½ÿ áPà¨Ùy2Î ›àqõˆÕ¹aýÏfÿ«á›§äƒGÌ[ÞLrkÜÕÙb4$iÕ(¼ëªòÀSnÅ2dÏ«oõa¡äÖ¢ÄÕ’¾SWNŒR¥ÁÿÏ?¼zÅþ[ˆ>ë«Ä†„ª3P8Á“ÜERäàìLCg2±X&•Jµ·ë$R°›–¬d‘ ‘€¢#Ò^lIç‚q?èYÞŒŒMð8]74ŽË¥"ãˆDx (:"mc^ðÓ1UïyéÅkÒ‡/HŒlþÑŸz|Î/GMÞÚ®ØdâÆ è ~Ë%“›d¨0K5ɇ¶ÇµÔà×d%! À|´]Ñ k­üÌ0²LC@Ûãè ~L«'Ø€ "ÀôÑž¤Fƒ@ ‚?*èA^@‰¿I € @ÿ’5¿‘0×IEND®B`‚diffobj/vignettes/metacomp.Rmd0000755000176200001440000000751613420351310016142 0ustar liggesusers--- title: "Mean Relative Indifference" subtitle: "A Comparison of R Comparison Functions" author: "Brodie Gaslam" output: rmarkdown::html_vignette: toc: true css: - !expr diffobj::diffobj_css() - styles.css vignette: > %\VignetteIndexEntry{Comparison Functions} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ```{r, echo=FALSE} library(diffobj) old.opt <- options( diffobj.disp.width=80, diffobj.pager="off", diffobj.format="html" ) ``` Most R object comparison functions are good at telling you that objects are different, but less so at conveying _how_ they are different. I wrote `diffobj` to provide an "aha, that's how they are different" comparison. In this vignette I will compare `diffPrint` to `all.equal` and to `testthat::compare`. Disclaimer: I picked the examples here to showcase `diffobj` capabilities, not to carry out a fair and balanced comparison of these comparison functions. Nonetheless, I hope you will find the examples representative of common situations where comparison of R objects is useful. ## Vectors I defined four pairs of numeric vectors for us to compare. I purposefully hid the variable definitions to simulate a comparison of unknown objects. ```{r echo=FALSE} A1 <- 1:10 B1 <- c(1:9, 11) A2 <- 1:20 B2 <- c(20, 1:19) A3 <- 1:20 B3 <- c(20:21, 1:19) ``` ### Stage 1 ```{r} all.equal(A1, B1) ``` The objects are different... At this point I would normally print both `A1` and `B1` to try to figure out how that difference came about since the "mean relative difference" is unhelpful. ```{r} testthat::compare(A1, B1) ``` `testthat::compare` does a better job, but I still feel the need to look at `A1` and `B1`. ```{r, results="asis"} diffPrint(A1, B1) ``` Aha, that's how they are different! ### Stage 2 Let's up the difficulty a little bit: ```{r} testthat::compare(A2, B2) ``` If you look closely you will see that despite a reported 20/20 differences, the two vectors are actually similar, at least in the part visible part of the output. With `diffPrint` it is obvious that `B2` and is the same as `A2`, except that the last value has been moved to the first position: ```{r, results="asis"} diffPrint(A2, B2) ``` ### Stage 3 `testthat::compare` throws in the towel as soon as lengths are unequal: ```{r} testthat::compare(A3, B3) ``` `all.equal` does the same. `diffPrint` is unfazed: ```{r, results="asis"} diffPrint(A3, B3) ``` `diffPrint` also produces useful output for largish vectors: ```{r, results="asis"} A4 <- 1:1e4 B4 <- c(1e4 + 1, A4[-c(4:7, 9e3)]) diffPrint(A4, B4) ``` Do note that the comparison algorithm scales with the square of the number of _differences_, so very large and different vectors will be slow to process. ## Objects R Core and package authors put substantial effort into `print` and `show` methods. `diffPrint` takes advantage of this. Compare: ```{r, R.options=list(max.print=5)} all.equal(iris, iris[-60,]) ``` to: ```{r, results="asis"} diffPrint(iris, iris[-60,]) ``` And: ```{r, R.options=list(max.print=5)} all.equal(lm(hp ~ disp, mtcars), lm(hp ~ cyl, mtcars)) ``` to: ```{r, results="asis"} diffPrint(lm(hp ~ disp, mtcars), lm(hp ~ cyl, mtcars)) ``` In these examples I limited `all.equal` output to five lines for the sake of brevity. Also, since `testthat::compare` reverts to `all.equal` output with more complex objects I omit it from this comparison. ## Parting Thoughts Another candidate comparison function is `compare::compare`. I omitted it from this vignette because it focuses more on similarities than on differences. Additionally, `testthat::compare` and `compare::compare` `print` methods conflict so they cannot be used together. For a more thorough exploration of `diffobj` methods and their features please see the [primary `diffobj` vignette](diffobj.html). ```{r, echo=FALSE} options(old.opt) ``` diffobj/vignettes/embed.Rmd0000755000176200001440000001013213420351310015375 0ustar liggesusers--- title: "Embed Diffs in R Markdown Or Shiny" author: "Brodie Gaslam" output: rmarkdown::html_vignette: toc: true css: - !expr diffobj::diffobj_css() - styles.css vignette: > %\VignetteIndexEntry{Embed Diffs in R Markdown Or Shiny} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ```{r echo=FALSE} library(diffobj) ``` ## Rmarkdown ### Basic Requirements Any R chunks that produce diffs should include the `results='asis'` option, e.g.: ```` ```{r, comment="", results="asis"}`r ''` # R code here ``` ```` ### Embedded CSS This is what a basic code block should look like: ```` ```{r, comment="", results="asis"}`r ''` cat( # output to screen as.character( # convert to diff to character vector diffPrint( # run diff 1:5, 2:6, format="html", # specify html output style=list( html.output="diff.w.style" # configure html style ) ) ) ) ``` ```` Here we use this same code as an actual markdown R code block: ```{r results='asis'} cat( as.character( diffPrint( 1:5, 2:6, format="html", style=list(html.output="diff.w.style") ) ) ) ``` This is an ugly implementation because it produces illegal HTML. The styles are directly embedded in the body of the document, outside of the HEAD tags. Although this is illegal HTML, it seems to work in most browsers. Another problem is that every diff you use in your document will inject the same CSS code over and over. ### External CSS A better option is to provide the CSS directly by modifying the `output` portion of the [YAML header](https://bookdown.org/yihui/rmarkdown/r-package-vignette.html): ``` --- output: rmarkdown::html_vignette: toc: true css: !expr diffobj::diffobj_css() --- ``` In reality you will probably want to specify multiple CSS files, including the original `rmarkdown` one: ``` --- output: rmarkdown::html_vignette: toc: true css: - !expr diffobj::diffobj_css() - !expr system.file("rmarkdown", "templates", "html_vignette", "resources", "vignette.css", package = "rmarkdown") --- ``` Once you set this up then you can use: ```{r results='asis'} cat( as.character( diffPrint( 1:5, 2:6, format="html", style=list(html.output="diff.only") # notice this changed ) ) ) ``` This will omit the CSS, but since we include it via the YAML everything should work as expected. ### Use Options Almost all `diffobj` parameters can be specified via options: ```{r eval=FALSE} options( diffobj.format="html", diffobj.style=list(html.output="diff.only") ) ``` ```{r echo=FALSE} old.opts <- options( diffobj.format="html", diffobj.style=list(html.output="diff.only") ) ``` Then you can just run the diff as normal: ```{r results='asis'} cat(as.character(diffPrint(1:5, 2:6))) ``` ```{r echo=FALSE} options(old.opts) ``` ## Shiny Shiny usage is very similar to `rmarkdown`. In both cases we want to get `diffobj` to produce HTML output to embed in our document. If we are willing to embed the CSS with each diff, we can use: ```{r, eval=FALSE} library(shiny) shinyApp( ui=fluidPage(htmlOutput('diffobj_element')), server=function(input, output) { output$diffobj_element <- renderUI({ HTML( as.character( diffPrint( 1:5, 2:6, format="html", style=list(html.output="diff.w.style") ) ) )}) } ) ``` If we have many diffs, it may be preferable to use options and external style sheet: ```{r, eval=FALSE} options( diffobj.format="html", diffobj.style=list(html.output="diff.only") ) shinyApp( ui=fluidPage( includeCSS(diffobj_css()), htmlOutput('diffobj_element') ), server=function(input, output) { output$diffobj_element <- renderUI({ HTML(as.character(diffPrint(1:5, 2:6,))) }) } ) ``` Unlike with our [rmarkdown example](#external-css), this CSS is included in the body of the HTML document instead of in the header, so it is technically illegal like in our [embedded css example](#embedded-css). diffobj/vignettes/diffobj.Rmd0000755000176200001440000003734713420351310015745 0ustar liggesusers--- title: "diffobj - Diffs for R Objects" author: "Brodie Gaslam" output: rmarkdown::html_vignette: toc: true css: - !expr diffobj::diffobj_css() - styles.css vignette: > %\VignetteIndexEntry{Introduction to Diffobj} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ```{r, echo=FALSE} library(diffobj) old.opt <- options( diffobj.disp.width=80, diffobj.pager="off", diffobj.format="html" ) ``` ## Introduction `diffobj` uses the same comparison mechanism used by `git diff` and `diff` to highlight differences between _rendered_ R objects: ```{r, results="asis"} a <- b <- matrix(1:100, ncol=2) a <- a[-20,] b <- b[-45,] b[c(18, 44)] <- 999 diffPrint(target=a, current=b) ``` `diffobj` comparisons work best when objects have some similarities, or when they are relatively small. The package was originally developed to help diagnose failed unit tests by comparing test results to reference objects in a human-friendly manner. If your terminal supports formatting through ANSI escape sequences, `diffobj` will output colored diffs to the terminal. If not, it will output colored diffs to your IDE viewport if it is supported, or to your browser otherwise. ## Interpreting Diffs ### Shortest Edit Script The output from `diffobj` is a visual representation of the Shortest Edit Script (SES). An SES is the shortest set of deletion and insertion instructions for converting one sequence of elements into another. In our case, the elements are lines of text. We encode the instructions to convert `a` to `b` by deleting lines from `a` (in yellow) and inserting new ones from `b` (in blue). ### Diff Structure The first line of our diff output acts as a legend to the diff by associating the colors and symbols used to represent differences present in each object with the name of the object: ```{r, results="asis", echo=FALSE} diffPrint(target=a, current=b)[1] ``` After the legend come the hunks, which are portions of the objects that have differences with nearby matching lines provided for context: ```{r, results="asis", echo=FALSE} diffPrint(target=a, current=b)[2:10] ``` At the top of the hunk is the hunk header: this tells us that the first displayed hunk (including context lines), starts at line 17 and spans 6 lines for `a` and 7 for `b`. These are display lines, not object row indices, which is why the first row shown of the matrix is row 16. You might have also noticed that the line after the hunk header is out of place: ```{r, results="asis", echo=FALSE} diffPrint(target=a, current=b)[3] ``` This is a special context line that is not technically part of the hunk, but is shown nonetheless because it is useful in helping understand the data. The line is styled differently to highlight that it is not part of the hunk. Since it is not part of the hunk, it is not accounted for in the hunk header. See `?guideLines` for more details. The actual mismatched lines are highlighted in the colors of the legend, with additional visual cues in the gutters: ```{r, results="asis", echo=FALSE} diffPrint(target=a, current=b)[6:9] ``` `diffobj` uses a line by line diff to identify which portions of each of the objects are mismatches, so even if only part of a line mismatches it will be considered different. `diffobj` then runs a word diff within the hunks and further highlights mismatching words. Let's examine the last two lines from the previous hunk more closely: ```{r, results="asis", echo=FALSE} diffPrint(target=a, current=b)[8:9] ``` Here `b` has an extra line so `diffobj` adds an empty line to `a` to maintain the alignment for subsequent matching lines. This additional line is marked with a tilde in the gutter and is shown in a different color to indicate it is not part of the original text. If you look closely at the next matching line you will notice that the `a` and `b` values are not exactly the same. The row indices are different, but `diffobj` excludes row indices from the diff so that rows that are identical otherwise are shown as matching. `diffobj` indicates this is happening by showing the portions of a line that are ignored in the diff in grey. See `?guides` and `?trim` for details and limitations on guideline detection and unsemantic meta data trimming. ### Atomic Vectors Since R can display multiple elements in an atomic vector on the same line, and `diffPrint` is fundamentally a line diff, we use specialized logic when diffing atomic vectors. Consider: ```{r, results="asis"} state.abb2 <- state.abb[-16] state.abb2[37] <- "Pennsylvania" diffPrint(state.abb, state.abb2) ``` Due to the different wrapping frequency no line in the text display of our two vectors matches. Despite this, `diffPrint` only highlights the lines that actually contain differences. The side effect is that lines that only contain matching elements are shown as matching even though the actual lines may be different. You can turn off this behavior in favor of a normal line diff with the `unwrap.atomic` argument to `diffPrint`. Currently this only works for _unnamed_ vectors, and even for them some inputs may produce sub-optimal results. Nested vectors inside lists will not be unwrapped. You can also use `diffChr` (see below) to do a direct element by element comparison. ## Other Diff Functions ### Method Overview `diffobj` defines several S4 generics and default methods to go along with them. Each of them uses a different text representation of the inputs: * `diffPrint`: use the `print`/`show` output and is the one used in the examples so far * `diffStr`: use the output of `str` * `diffObj`: picks between `print`/`show` and `str` depending on which provides the "best" overview of differences * `diffChr`: coerces the inputs to atomic character vectors with `as.character`, and runs the diff on the character vector * `diffFile`: compares the text content of two files * `diffCsv`: loads two CSV files into data frames and compares the data frames with `diffPrint` * `diffDeparse`: deparses and compares the character vectors produced by the deparsing * `ses`: computes the element by element shortest edit script on two character vectors Note the `diff*` functions use lowerCamelCase in keeping with S4 method name convention, whereas the package name itself is all lower case. ### Compare Structure with `diffStr` For complex objects it is often useful to compare structures: ```{r, results="asis"} mdl1 <- lm(Sepal.Length ~ Sepal.Width, iris) mdl2 <- lm(Sepal.Length ~ Sepal.Width + Species, iris) diffStr(mdl1$qr, mdl2$qr, line.limit=15) ``` If you specify a `line.limit` with `diffStr` it will fold nested levels in order to fit under `line.limit` so long as there remain visible differences. If you prefer to see all the differences you can leave `line.limit` unspecified. ### Compare Vectors Elements with `diffChr` Sometimes it is useful to do a direct element by element comparison: ```{r, results="asis"} diffChr(letters[1:3], c("a", "B", "c")) ``` Notice how we are comparing the contents of the vectors with one line per element. ### Why S4? The `diff*` functions are defined as S4 generics with default methods (signature `c("ANY", "ANY")`) so that users can customize behavior for their own objects. For example, a custom method could set many of the default parameters to values more suitable for a particular object. If the objects in question are S3 objects the S3 class will have to be registered with `setOldClass`. ### Return Value All the `diff*` methods return a `Diff` S4 object. It has a `show` method which is responsible for rendering the `Diff` and displaying it to the screen. Because of this you can compute and render diffs in two steps: ```{r, eval=FALSE} x <- diffPrint(letters, LETTERS) x # or equivalently: `show(x)` ``` This may cause the diff to render funny if you change screen widths, etc., between the two steps. There are also `summary`, `any`, and `as.character` methods. The `summary` method provides a high level overview of where the differences are, which can be helpful for large diffs: ```{r, results="asis"} summary(diffStr(mdl1, mdl2)) ``` `any` returns TRUE if there are differences, and `as.character` returns the character representation of the diff. ## Controlling Diffs and Their Appearance ### Parameters The `diff*` family of methods has an extensive set of parameters that allow you to fine tune how the diff is applied and displayed. We will review some of the major ones in this section. For a full description see `?diffPrint`. While the parameter list is extensive, only the objects being compared are required. All the other parameters have default values, and most of them are for advanced use only. The defaults can all be adjusted via the `diffobj.*` options. ### Display Mode There are three built-in display modes that are similar to those found in GNU `diff`: "sidebyside", "unified", and "context". For example, by varying the `mode` parameter with: ```{r, results="asis", eval=FALSE} x <- y <- letters[24:26] y[2] <- "GREMLINS" diffChr(x, y) ``` we get:
mode="sidebyside"mode="unified"mode="context"
```{r, results="asis", echo=FALSE} x <- y <- letters[24:26] y[2] <- "GREMLINS" diffChr(x, y, mode="sidebyside") ``` ```{r, results="asis", echo=FALSE} x <- y <- letters[24:26] y[2] <- "GREMLINS" diffChr(x, y, mode="unified") ``` ```{r, results="asis", echo=FALSE} x <- y <- letters[24:26] y[2] <- "GREMLINS" diffChr(x, y, mode="context") ```
By default `diffobj` will try to use `mode="sidebyside"` if reasonable given display width, and otherwise will switch to `mode="unified"`. You can always force a particular display style by specifying it with the `mode` argument. ### Color Mode The default color mode uses yellow and blue to symbolize deletions and insertions for accessibility to dichromats. If you prefer the more traditional color mode you can specify `color.mode="rgb"` in the parameter list, or use `options(diffobj.color.mode="rgb")`: ```{r, results="asis"} diffChr(x, y, color.mode="rgb") ``` ### Output Formats If your terminal supports it `diffobj` will format the output with ANSI escape sequences. `diffobj` uses Gábor Csárdi's [`crayon`](https://github.com/gaborcsardi/crayon) package to detect ANSI support and to apply ANSI based formatting. If you are using RStudio or another IDE that supports `getOption("viewer")`, `diffobj` will output an HTML/CSS formatted diff to the viewport. In other terminals that do not support ANSI colors, `diffobj` will attempt to output to an HTML/CSS formatted diff to your browser using `browseURL`. You can explicitly specify the output format with the `format` parameter: * `format="raw"` for unformatted diffs * `format="ansi8"` for standard ANSI 8 color formatting * `format="ansi256"` for ANSI 256 color formatting * `format="html"` for HTML/CSS output and styling See [Pagers](#pagers) for more details. ### Brightness The `brightness` parameter allows you to pick a color scheme compatible with the background color of your terminal. The options are: * "light": for use with light tone terminals * "dark": for use with dark tone terminals * "neutral": for use with either light or dark terminals Here are examples of terminal screen renderings for both "rgb" and "yb" `color.mode` for the three `brightness` levels. The examples for "light" and "dark" have the backgrounds forcefully set to a color compatible with the scheme. In actual use the base background and foreground colors are left unchanged, which will look bad if you use "dark" with light colored backgrounds or vice versa. Since we do not know of a good cross platform way of detecting terminal background color the default `brightness` value is "neutral". At this time the only `format` that is affected by this parameter is "ansi256". If you want to specify your own light/dark/neutral schemes you may do so either by specifying a [style](#styles) directly or with [Palette of Styles](#styles). ### Pagers In interactive mode, if the diff output is very long or if your terminal does not support ANSI colors, `diff*` methods will pipe output to a pager. This is done by writing the output to a temporary file and passing the file reference to the pager. The default action is to invoke the pager with `file.show` if your terminal supports ANSI colors and the pager is known to support ANSI colors as well (as of this writing, only `less` is assumed to support ANSI colors), or if not to use `getOption("viewer")` if available (this outputs to the viewport in RStudio), or if not to use `browseURL`. You can fine tune when, how, and if a pager is used with the `pager` parameter. See `?diffPrint` and `?Pager` for more details. ### Styles You can control almost all aspects of the diff output formatting via the `style` parameter. To do so, pass an appropriately configured `Style` object. See `?Style` for more details on how to do this. The default is to auto pick a style based on the values of the `format`, `color.mode`, and `brightness` parameters. This is done by using the computed values for each of those parameters to subset the `PaletteOfStyles` object passed as the `palette.of.styles` parameter. This `PaletteOfStyles` object contains a `Style` object for all the possible permutations of the `style`, `format`, and `color.mode` parameters. See `?PaletteOfStyles`. If you specify the `style` parameter the values of the `format`, `brightness`, and `color.mode` parameters will be ignored. ## Diff Algorithm The primary diff algorithm is Myer's solution to the shortest edit script / longest common sequence problem with the Hirschberg linear space refinement as described in: > E. Myers, "An O(ND) Difference Algorithm and Its Variations", Algorithmica 1, 2 (1986), 251-266. and should be the same algorithm used by GNU diff. The implementation used here is a heavily modified version of Michael B. Allen's diff program from the [`libmba`](http://www.ioplex.com/~miallen/libmba/dl/libmba-0.9.1.tar.gz) `C` library. Any and all bugs in the C code in this package were most likely introduced by yours truly. Please note that the resulting C code is incompatible with the original `libmba` library. ## Performance Considerations ### Diff The diff algorithm scales with the _square_ of the number of _differences_. For reasonably small diffs (< 10K differences), the diff itself is unlikely to be the bottleneck. ### Capture and Processing Capture of inputs for `diffPrint` and `diffStr`, and processing of output for all `diff*` methods will account for most of the execution time unless you have large numbers of differences. This input and output processing scales mostly linearly with the input size. You can improve performance somewhat by using `diffChr` since that skips the capture part, and by turning off `word.diff`: ```{r, eval=FALSE} v1 <- 1:5e4 v2 <- v1[-sample(v1, 100)] diffChr(v1, v2, word.diff=FALSE) ``` will be ~2x as fast as: ```{r, eval=FALSE} diffPrint(v1, v2) ``` *Note*: turning off `word.diff` when using `diffPrint` with unnamed atomic vectors can actually _slow down_ the diff because there may well be fewer element by element differences than line differences as displayed. For example, when comparing `1:1e6` to `2:1e6` there is only one element difference, but every line as displayed is different because of the shift. Using `word.diff=TRUE` (and `unwrap.atomic=TRUE`) allows `diffPrint` to compare element by element rather than line by line. `diffChr` always compares element by element. ### Minimal Diff If you are looking for the fastest possible diff you can use `ses` and completely bypass most input and output processing. Inputs will be coerced to character if they are not character. ```{r} ses(letters[1:5], letters[c(2:3, 5)]) ``` This will be 10-20x faster than `diffChr`, at the cost of less useful output. ```{r, echo=FALSE} options(old.opt) ``` diffobj/vignettes/styles.css0000755000176200001440000001070413420351310015717 0ustar liggesusers/* Styles primarily borrowed from rmarkdown/templates/html_vignette/resources/vignette.css at a time 12/2/2014 when rmarkdown was (and probably still is) under the GPL-3 license */ body { background-color: #fff; margin: 1em auto; max-width: 700px; overflow: visible; padding-left: 2em; padding-right: 2em; font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.5; } #header { text-align: center; } #TOC { clear: both; /*margin: 0 0 10px 10px;*/ padding: 4px; width: 100%; border: 1px solid #CCCCCC; border-radius: 5px; background-color: #f6f6f6; font-size: 13px; line-height: 1.3; } #TOC .toctitle { font-weight: bold; font-size: 15px; margin-left: 5px; } #TOC ul { padding-left: 40px; margin-left: -1.5em; margin-top: 5px; margin-bottom: 5px; } #TOC ul ul { margin-left: -2em; } #TOC li { line-height: 16px; } table { margin: 1em auto; border-width: 1px; border-color: #DDDDDD; border-style: outset; border-collapse: collapse; } table th { border-width: 2px; padding: 5px; border-style: inset; } table td { border-width: 1px; border-style: inset; line-height: 18px; padding: 5px 5px; } table, table th, table td { border-left-style: none; border-right-style: none; } table thead, table tr.even { background-color: #f7f7f7; } p { margin: 1em 0; } blockquote { background-color: #f6f6f6; padding: 0.25em 0.75em; } hr { border-style: solid; border: none; border-top: 1px solid #777; margin: 28px 0; } dl { margin-left: 0; } dl dd { margin-bottom: 13px; margin-left: 13px; } dl dt { font-weight: bold; } ul { margin-top: 0; } ul li { list-style: circle outside; } ul ul { margin-bottom: 0; } h3.subtitle { margin-top: -23px; } pre, code { background-color: #EEE; color: #333; /*border-radius: 3px;*/ } pre { border: 2px solid #EEE; overflow: auto; white-space: pre-wrap; /* Wrap long lines */ /* border-radius: 3px; */ margin: 5px 0px; padding: 5px 10px; font-size: 85%; } pre:not([class]) { color: #353; /*border-radius: 0px 0px 3px 3px;*/ } div.sourceCode pre, div.sourceCode code { background-color: #FAFAFA; } div.sourceCode pre{ /*border-radius: 3px 3px 0px 0px;*/ } div.sourceCode + pre, div.sourceCode + div.diffobj-container { margin-top: -5px; } div.diffobj-container pre.diffobj-content { line-height: 1.3; } /* pre:not([class]) { background-color: #eee; } */ code { font-family: Consolas, Monaco, 'Courier New', monospace; } p > code, li > code, h1 > code, h2 > code, h3 > code, h4 > code, h5 > code, h6 > code { padding: 2px 0px; line-height: 1; font-weight: bold; } div.figure { text-align: center; } img { background-color: #FFFFFF; padding: 2px; border: 1px solid #DDDDDD; border-radius: 3px; border: 1px solid #CCCCCC; margin: 0 5px; } h1 { margin-top: 0; padding-bottom: 3px; font-size: 35px; line-height: 40px; border-bottom: 1px solid #999; } h2 { border-bottom: 1px solid #999; padding-top: 5px; padding-bottom: 2px; font-size: 145%; } h3 { padding-top: 5px; font-size: 120%; } h4 { /*border-bottom: 1px solid #f7f7f7;*/ color: #777; font-size: 105%; } h4.author, h4.date {display: none;} h5, h6 { /*border-bottom: 1px solid #ccc;*/ font-size: 105%; } a { color: #2255dd; font-weight: bold; text-decoration: none; } a:hover { color: #6666ff; } a:visited { color: #800080; } a:visited:hover { color: #BB00BB; } a[href^="http:"] { text-decoration: underline; } a[href^="https:"] { text-decoration: underline; } /* Class described in https://benjeffrey.com/posts/pandoc-syntax-highlighting-css Colours from https://gist.github.com/robsimmons/1172277 */ code > span.kw { color: #555; font-weight: bold; } /* Keyword */ code > span.dt { color: #902000; } /* DataType */ code > span.dv { color: #40a070; } /* DecVal (decimal values) */ code > span.bn { color: #d14; } /* BaseN */ code > span.fl { color: #d14; } /* Float */ code > span.ch { color: #d14; } /* Char */ code > span.st { color: #d14; } /* String */ code > span.co { color: #888888; font-style: italic; } /* Comment */ code > span.ot { color: #007020; } /* OtherToken */ code > span.al { color: #ff0000; font-weight: bold; } /* AlertToken */ code > span.fu { color: #900; font-weight: bold; } /* Function calls */ code > span.er { color: #a61717; background-color: #e3d2d2; } /* ErrorTok */ diffobj/README.md0000755000176200001440000001024113466142510013137 0ustar liggesusers# diffobj - Diffs for R Objects [![](https://travis-ci.org/brodieG/diffobj.svg?branch=master)](https://travis-ci.org/brodieG/diffobj) [![](https://codecov.io/github/brodieG/diffobj/coverage.svg?branch=master)](https://codecov.io/github/brodieG/diffobj?branch=master) [![](http://www.r-pkg.org/badges/version/diffobj)](https://cran.r-project.org/package=diffobj) [![Dependencies direct/recursive](https://tinyverse.netlify.com/badge/diffobj)](https://tinyverse.netlify.com/) Generate a colorized diff of two R objects for an intuitive visualization of their differences. See vignettes for [details](http://htmlpreview.github.io/?https://raw.githubusercontent.com/brodieG/diffobj/master/inst/doc/diffobj.html), and for [comparisons with standard comparison functions](http://htmlpreview.github.io/?https://raw.githubusercontent.com/brodieG/diffobj/master/inst/doc/metacomp.html). ## Output If your terminal supports formatting through ANSI escape sequences, `diffobj` will output colored diffs to the terminal. Otherwise, output will be colored with HTML/CSS and sent to the IDE viewport or to your browser. `diffobj` comes with several built-in color schemes that can be further customized. Some examples: ![Output Examples](https://raw.githubusercontent.com/brodieG/diffobj/master/cliandrstudio.png) ## Installation This package is available on [CRAN](https://cran.r-project.org/package=diffobj). ``` install.packages("diffobj") browseVignettes("diffobj") ``` ## Related Software * [tools::Rdiff](https://stat.ethz.ch/R-manual/R-devel/library/tools/html/Rdiff.html). * [Daff](https://cran.r-project.org/package=daff) diff, patch and merge for data.frames. * [GNU diff](https://www.gnu.org/software/diffutils). ## Acknowledgements * R Core for developing and maintaining such a wonderful language. * CRAN maintainers, for patiently shepherding packages onto CRAN and maintaining the repository, and Uwe Ligges in particular for maintaining [Winbuilder](http://win-builder.r-project.org/). * The users who have reported bugs and possible fixes (see NEWS.md). * [Jim Hester](https://github.com/jimhester) because [covr](https://cran.r-project.org/package=covr) rocks. * [Dirk Eddelbuettel](https://github.com/eddelbuettel) and [Carl Boettiger](https://github.com/cboettig) for the [rocker](https://github.com/rocker-org/rocker) project, and [Gábor Csárdi](https://github.com/gaborcsardi) and the [R-consortium](https://www.r-consortium.org/) for [Rhub](https://github.com/r-hub), without which testing bugs on R-devel and other platforms would be a nightmare. * [Hadley Wickham](https://github.com/hadley/) for [devtools](https://cran.r-project.org/package=devtools) and with [Peter Danenberg](https://github.com/klutometis) for [roxygen2](https://cran.r-project.org/package=roxygen2). * [Yihui Xie](https://github.com/yihui) for [knitr](https://cran.r-project.org/package=knitr) and [J.J. Allaire](https://github.com/jjallaire) etal for [rmarkdown](https://cran.r-project.org/package=rmarkdown), and by extension John MacFarlane for [pandoc](http://pandoc.org/). * Olaf Mersmann for [microbenchmark](https://cran.r-project.org/package=microbenchmark), because microsecond matter, and [Joshua Ulrich](https://github.com/joshuaulrich) for making it lightweight and maintaining it. * [Tomas Kalibera](https://github.com/kalibera) for [rchk](https://github.com/kalibera/rchk) and the accompanying vagrant image, and rcnst to help detect errors in compiled code. * [Winston Chang](https://github.com/wch) for the [r-debug](https://hub.docker.com/r/wch1/r-debug/) docker container, in particular because of the valgrind level 2 instrumented version of R. * All open source developers out there that make their work freely available for others to use. * [Github](https://github.com/), [Travis-CI](https://travis-ci.org/), [Codecov](https://codecov.io/), [Vagrant](https://www.vagrantup.com/), [Docker](https://www.docker.com/), [Ubuntu](https://www.ubuntu.com/), [Brew](https://brew.sh/) for providing infrastructure that greatly simplifies open source development. * [Free Software Foundation](http://fsf.org/) for developing the GPL license and promotion of the free software movement. diffobj/MD50000644000176200001440000005011313466336133012175 0ustar liggesusers618609a86354087737ee3273528099b8 *DESCRIPTION 9061dac61687afcea168f4067b15f1e6 *NAMESPACE 7aab89bd7e6e6c6cdd164c9340fe6637 *NEWS.md 4af31924e5e6cb367a325e3c5d27565a *R/capt.R 6ea8df817528fefed169718c3bad681c *R/check.R be7cd0c640533bd53c815a8a26d60cc0 *R/core.R 0f512f232a04ece0cf0fbb06639d3f45 *R/diff.R d8868faa186fdee67e43deae422ac7df *R/finalizer.R 34f9e857ece12286626af58a6b856c4a *R/get.R de0159d77e5d8a6c17f243b61d8e96e4 *R/guides.R de63fe100e732c27bb371ca8255f2ceb *R/html.R e04e681bcf30477747c34c91848b19a8 *R/hunks.R 8480f36461f12beba4c8f620dee32f98 *R/layout.R b12893804022c8ff99b3121c2492da07 *R/misc.R ffd8227e9d3525f51bcd7b88559dfe44 *R/myerssimple.R 061133d3ce9b1a56481460a2e4dc5897 *R/options.R 1d9244bbe45c15caea26880756a2c443 *R/pager.R fc973690cb745f001c6622d54a8acf74 *R/rdiff.R 74f9edc3db6d1112824edcd25abbc6a5 *R/rds.R 6cc8bd8a38e86301ff82a57469e50b67 *R/s4.R dfe5d83424b8f35848e63ce5f2546251 *R/set.R 5c5c0b912975271172850d5bdfce1c69 *R/styles.R 49e9d094a1c41fe960b12f67917c6680 *R/subset.R b3dc1cdd32b5f024a05a32d13bb1d914 *R/summmary.R 5cbba15be6341f0ec9969327d560c74d *R/system.R d279aa20ca516ca9d32a9d3fd13675dc *R/text.R ed1b1bca5c506e24b80e4ba961f7946f *R/tochar.R 9562552967ad2e42bcf698e43af4813f *R/trim.R 6fd1187bba26d94d4c415ff989167826 *R/word.R 63ed35215b5d9bd48d89a06c618034c4 *README.md c67daadd4a01890e766b400ea9acfc0f *build/vignette.rds 3b1e1b543dbf2d0ad177c893db9ebad8 *inst/COPYRIGHTS 4522b9ebe26e4d989bc7a27f933e3d21 *inst/css/diffobj.css 3578d72a4b947b087850cac48f955b7d *inst/doc/diffobj.R b030819a35d24fb09b3eb8d60f9aa369 *inst/doc/diffobj.Rmd edeaadf6840a0f380fa77698d7dc4bb3 *inst/doc/diffobj.html 55733c252aebea354d72852959091f53 *inst/doc/embed.R 9e93047aa59973c6475724b83fc6c321 *inst/doc/embed.Rmd 270b74e7d66f1d072f9c671d16816894 *inst/doc/embed.html 9655e1bb0b412fc4a0b8b91c6ad8a4b7 *inst/doc/metacomp.R dd768a6e630251f3773bb13c6e2b7804 *inst/doc/metacomp.Rmd ab96a2b21b7db58169e8f2e9959c7971 *inst/doc/metacomp.html b98576fbd80995ecb327f4e3c3c4f7db *inst/script/diffobj.js 31419b3dc6ce97ed3345f193b7656d50 *man/AlignThreshold-class.Rd 93b232974dcc5131b6d2d3614b5e90bc *man/Diff-class.Rd 9c68a7ec84f7a99d8c441910c9419037 *man/Extract_PaletteOfStyles.Rd 5bc9d55578670af2116a2e66d441e0bf *man/Pager.Rd 70f6f6c53819b288f32221a9037ccdd8 *man/PaletteOfStyles-class.Rd b9ab7517451fda5d4d43205dcfc2795d *man/Rdiff_chr.Rd 8ac08b591ab24db5bcefa41937ef3bdf *man/Style.Rd 7432f4de3d7eeac72497bdebce78fa99 *man/StyleFuns.Rd 2c94216969caf6341d74b9ffe80188b7 *man/StyleSummary.Rd 31afdced631cfa0ea225ea80e52f97bb *man/StyleText.Rd 6d815dc4cb5097f23d0b17a2ad3b7058 *man/any-Diff-method.Rd 07cf7499843e07dca9a4094aca6a17d4 *man/as.character-DiffSummary-method.Rd cd1c0b88f407c71f040d6f9d86d89b17 *man/as.character-MyersMbaSes-method.Rd 6a03a14df4a4cee52d28713eb3e6b6d2 *man/auto_context.Rd 42e7cd6e2e963cf61fde825fd0c48b53 *man/console_lines.Rd 64c7ebbdd217b74702e353aa236ede19 *man/diffChr.Rd f14d2b78ca9b9d660793412bce8953d8 *man/diffCsv.Rd cf82b3a238b7ca0ef4fa05948aa345c9 *man/diffDeparse.Rd dd417177744cfa61d7df506db0ffffd0 *man/diffFile.Rd 29916b9909caccb09b666d6a32acbb0c *man/diffObj.Rd edfd18814069c3e6b1fe1068da234843 *man/diffPrint.Rd 5c9a012c92e66b0db8cfc39974d3e7a0 *man/diffStr.Rd d0bf7e1d33dd703aab77640080ed97a4 *man/diff_myers.Rd 246ff7707c0b57375841ee6901cc4522 *man/diffobj-package.Rd fbf810285c555719c8267da1a96dc4d2 *man/diffobj_s4method_doc.Rd 168591e0c6d3f6c40061e5fa5819f724 *man/diffobj_set_def_opts.Rd c7753e1e60b0c231d8793716ac735a0a *man/dimnames-PaletteOfStyles-method.Rd 5b87e9847e8a6d39362bda2be88d4ecd *man/extract-Diff-method.Rd 6b165fc8fa1ab523abb71996bc0e3876 *man/finalizeHtml.Rd f5950a8e6e9dd92fc80cba49b7e6f753 *man/gdo.Rd c28000a3a6efe2d0f20ae6ec96f42dfd *man/guides.Rd e6fe3aeab53db02d9e281856a1895df8 *man/has_Rdiff.Rd d670eeda6aadccea770f06668c898887 *man/make_blocking.Rd 00ba016bdea25ea334451371bd616d3c *man/nchar_html.Rd 4f2341110f17057d5df37855d560c188 *man/pager_is_less.Rd 641f6695b008ea98b037ba7542b653f6 *man/par_frame.Rd ec65dd97f47a4434531dcef7fc2ff12c *man/ses.Rd 59d44aa8cd4780f1c024eac9ab776fd4 *man/show-DiffSummary-method.Rd aa99163a7c9abc473eb8948a162dc998 *man/show-PaletteOfStyles-method.Rd 607c69623555a239f4ab8d3dfad00dda *man/show-Style-method.Rd f6aec61ea0385eb48029f21361b4b36d *man/strip_hz_control.Rd 15311721fee37ae757e44a53b9a71f2f *man/summary-Diff-method.Rd 9597899e306230ba6c6e602acd856348 *man/summary-MyersMbaSes-method.Rd 6b2c1ffebf9f5fab84159dfc666c9554 *man/summary-PaletteOfStyles-method.Rd 089e12b412655819fbd17fba92e02a2c *man/tag_f.Rd d6ab2026fee7c138b5cd2f596eae9210 *man/trim.Rd a781aa55007885ab32eafa432f2e0d4d *man/view_or_browse.Rd 9dd8095f89982d2d197f849c9af145d6 *man/webfiles.Rd 4c217c8c5e4e8ccc64f65f4eb5b202e7 *src/diff.c 172cf29e0d21b9140d241133b36cdb8b *src/diff.h adda75359ceeb291eaeb1903400f87e2 *src/diffobj.c 3a4d96a207313f8d062a150984b5b280 *src/diffobj.h 5b752a841b887c1ad5c862eb6679d9f6 *src/init.c da99a58a47a79bd77444600b897a2cda *tests/run.R 946cfa6f36d73c11f810e608630a76b2 *tests/scaling.R 1b21b8cb7dd03fed7de67aa8a6726d91 *tests/testthat/helper.commonobjects.R a45701a8a943fee86e2f7bfea68c554e *tests/testthat/helper/atomic/100.rds dc3f364393561bd18b20d84286c3b7c7 *tests/testthat/helper/atomic/1000.rds 44c2381df681c732977077f5a46bd25c *tests/testthat/helper/atomic/1100.rds 69f4fa668d1680ac1dccebb25e315e99 *tests/testthat/helper/atomic/1200.rds 0734eca1d4d03680354c5612b93aed88 *tests/testthat/helper/atomic/1250.rds 37c577a82c7968d7997aebe1d3109ca5 *tests/testthat/helper/atomic/1300.rds 34a356139062b436b647f889e3a26a0a *tests/testthat/helper/atomic/1400.rds e05e4ac9e2ac21a5be7ccf5ba4901412 *tests/testthat/helper/atomic/1425.rds 2432d32ed1495f36c39af5fa71db6a64 *tests/testthat/helper/atomic/1450.rds ea6141ad68328e262fb39675bebc751b *tests/testthat/helper/atomic/1500.rds c140a1be8612c04236c9741e044e7f8a *tests/testthat/helper/atomic/1600.rds b6dc4bc12e54f95aa91224f02ee32bb8 *tests/testthat/helper/atomic/1700.rds 764f2019d89135c633fb547ed4e1a2b0 *tests/testthat/helper/atomic/1800.rds 221588368f319815137723249d330fe7 *tests/testthat/helper/atomic/1900.rds c2a2f40ec2fb74e3732c84602b3f88f0 *tests/testthat/helper/atomic/200.rds a74e6e21b9c01c44385949f20db6a701 *tests/testthat/helper/atomic/2000.rds 101109fd4be9f6920e256fa7f4902d42 *tests/testthat/helper/atomic/2100.rds 41fd3b188451f69fd9e1fecb6ca7647d *tests/testthat/helper/atomic/2200.rds f23316310a5b9a7fc25895065a64c1da *tests/testthat/helper/atomic/2300.rds 2f5942ee3eedc6d18123ffcff4756ce7 *tests/testthat/helper/atomic/2400.rds f0e8bb15dd4921aec2d0c224c3646234 *tests/testthat/helper/atomic/2500.rds 3f0cd800db66170eef9945861acd15aa *tests/testthat/helper/atomic/2520.rds fab134028d4d441560b2a6847cae5091 *tests/testthat/helper/atomic/2530.rds 31764b7afd942315de7c9e7436081d93 *tests/testthat/helper/atomic/2540.rds bfafd46faad18c70b7d7b2e6734ea3a3 *tests/testthat/helper/atomic/2600.rds da093f2a3ce05c9b4b76c2bb7a9353a1 *tests/testthat/helper/atomic/2700.rds f4d06103b2b62f64c2b60cfbf4ac4d36 *tests/testthat/helper/atomic/2800.rds 989d1180e3880b221aa73666b60bfd81 *tests/testthat/helper/atomic/2900.rds 7f9785d7bf351f9eee19af77fb04bac2 *tests/testthat/helper/atomic/3000.rds 5d368c9bac8fd272b810a4b15b33fc57 *tests/testthat/helper/atomic/3100.rds 91218647f129ad69cbc6ebd5c7dc172d *tests/testthat/helper/atomic/3200.rds acb225308bc51c408162ad69a43a6863 *tests/testthat/helper/atomic/3300.rds c1694e5b8fdec754a52c2b01e747ed7c *tests/testthat/helper/atomic/3400.rds 6ee314755262cba70a6fe4fd2dda4058 *tests/testthat/helper/atomic/400.rds e5d81a992cfb76c65f2b4676f4cbebf3 *tests/testthat/helper/atomic/500.rds 02e31be899289c97b3a36a6c75ab47a8 *tests/testthat/helper/atomic/600.rds 4fff8918a763c31cd2f3f98e6df53acf *tests/testthat/helper/atomic/700.rds 3e8b6358cb2aa43179319f1bd6228486 *tests/testthat/helper/atomic/800.rds 8357b4e983f364f29582a41bf12ef315 *tests/testthat/helper/atomic/900.rds f6d68e680e822b2808762d45d201b675 *tests/testthat/helper/context/100.rds 5dd71ce40c9679b4c95aaee45c565b37 *tests/testthat/helper/context/100.txt f198a0849fac7b99820ea6694ed26990 *tests/testthat/helper/context/150.rds 41dc9a5fed17f5ee9d909000195c23e6 *tests/testthat/helper/context/200.rds 54ab37eccc5b0e7fe550ebd5467da5af *tests/testthat/helper/context/200.txt f28717f7e31485ece3c0f8e2ce032b3f *tests/testthat/helper/context/300.rds ec95a408b23380dfa98dcd5d44026a0e *tests/testthat/helper/context/400.rds f00958307411ddfe90d8d45be4a6e917 *tests/testthat/helper/context/500.rds 6ea96d1d9b5da34720d14756cb5aada7 *tests/testthat/helper/diffChr/100.rds d41d8cd98f00b204e9800998ecf8427e *tests/testthat/helper/diffChr/100.txt d85efe2ce8b91e2acde6a2b1363211ca *tests/testthat/helper/diffChr/1000.rds ca124f8c0b596d37f7a211162f40f018 *tests/testthat/helper/diffChr/1100.rds f2cbabe3191df5d5e8d74db247af7af3 *tests/testthat/helper/diffChr/1200.rds 805bbf7455cce2d6e79bb5868535d99e *tests/testthat/helper/diffChr/1300.rds 5728b07f03543b472c9e8afe477a4218 *tests/testthat/helper/diffChr/1400.rds 11efd2c5b63c52ebef56eddc809547be *tests/testthat/helper/diffChr/1500.rds 3094895a0ae2a02c3df392b59110131b *tests/testthat/helper/diffChr/200.rds 3f61d4788d843555c01962fe9488f224 *tests/testthat/helper/diffChr/200.txt c28135ec2ad960124c266f2d8e8f1449 *tests/testthat/helper/diffChr/225.rds b858eca5629dab83ad71a0ceb4aeaf28 *tests/testthat/helper/diffChr/250.rds 3d5584a86016db6a893cf63a896bbd3d *tests/testthat/helper/diffChr/300.rds 7eec0dc151eacdc33d6fff179ce36d70 *tests/testthat/helper/diffChr/300.txt a0a3531322d6651912d2b4bfc057fdfb *tests/testthat/helper/diffChr/400.rds 9387a8fc5bcf16bf042f6431a8f3dcc6 *tests/testthat/helper/diffChr/400.txt 14e6ba05956c075bc0dea7a806d1ce11 *tests/testthat/helper/diffChr/500.rds c20342671fef7075a835980cba0f8878 *tests/testthat/helper/diffChr/500.txt a7bff3a7d88f4be8a65fecedd1d6707b *tests/testthat/helper/diffChr/600.rds ad335e2b030a318a911f7ce5b2d81c8c *tests/testthat/helper/diffChr/800.rds da6d129bc1abde1f32243f14060437e2 *tests/testthat/helper/diffChr/900.rds a892016365e770595c37c7cfeef280c4 *tests/testthat/helper/diffDeparse/100.rds a3902e3d7357dc97b3abc4d323046096 *tests/testthat/helper/diffDeparse/200.rds cfd11512ac2c0336d5f7ae6dcd3952b4 *tests/testthat/helper/diffFile/100.rds 125b7770618cee796f75f11fe42b513f *tests/testthat/helper/diffFile/s.o.30dbe0.R 9752f5d324622b136bad0666714cc101 *tests/testthat/helper/diffFile/s.o.3f1f68.R 1483c33e014566ac7c0307b4f875a190 *tests/testthat/helper/diffObj/100.rds 79eb3fb9a4533231c16a88da7c7e67f1 *tests/testthat/helper/diffObj/200.rds 3d2afca44733f3900578b5139c80ac6e *tests/testthat/helper/diffObj/300.rds f28316a1fc903d296643e6730589f6de *tests/testthat/helper/diffObj/400.rds 37b17feb793bf07a25b90bea54db8dd5 *tests/testthat/helper/diffPrint/100.rds 85499e94ba78e61b81abb0a18c518aad *tests/testthat/helper/diffPrint/100.txt 60381bf784f270725eaf5f886df2e40f *tests/testthat/helper/diffPrint/1000.rds c963e95d93e1419329faf4b3e4af8008 *tests/testthat/helper/diffPrint/1100.rds 5e701970a746e1f48a7aabcc27a886e9 *tests/testthat/helper/diffPrint/1200.rds 8f76c594527c84f9c094c41c1e954e15 *tests/testthat/helper/diffPrint/1300.rds 9bd8920b9f7d5e05d30c60d1739e58f5 *tests/testthat/helper/diffPrint/1400.rds 36c801641f202eb148e628da26a0f8bf *tests/testthat/helper/diffPrint/150.rds 90bb138a4f05b20b723cc13de8dfbf2e *tests/testthat/helper/diffPrint/150.txt 07497bf535c796d2b6207f9f0aabb023 *tests/testthat/helper/diffPrint/1500.rds b2de18480bc61e62e2b4d7e14e00963f *tests/testthat/helper/diffPrint/1600.rds e4f1b395cb2c812e12de73aaf57eb689 *tests/testthat/helper/diffPrint/1650.rds 31bec83015b12f4212e0a406c5c69923 *tests/testthat/helper/diffPrint/1700.rds 5778a7ce44ff70aa025f16aecb98a047 *tests/testthat/helper/diffPrint/175.rds 0abeac571d16a77cd414bc57c0d528cc *tests/testthat/helper/diffPrint/175.txt fc035cf2e8ece3effb658e0919d7a9f7 *tests/testthat/helper/diffPrint/1800.rds 5dc5f7ba8f8d8fbc698dc145d0296736 *tests/testthat/helper/diffPrint/1900.rds eec6a82dfed19356cf0d4f8c6b0dcc8b *tests/testthat/helper/diffPrint/200.rds 73d3abb805eaa7da254c6e46a9369b8d *tests/testthat/helper/diffPrint/200.txt 1be79c5818539f20125ac58eba1dcc44 *tests/testthat/helper/diffPrint/2000.rds f6969b4e873c2626cba9655fa0ade92f *tests/testthat/helper/diffPrint/2100.rds f14f24f4387d9ba302727c01c8dca2e6 *tests/testthat/helper/diffPrint/2150.rds 4883764bcc9b13ca249fa1759c2eb0cf *tests/testthat/helper/diffPrint/2200.rds 7acb31232169b734d4fa644446928ab1 *tests/testthat/helper/diffPrint/2250.rds 23e50dc91187ec68ffb8417c962732d2 *tests/testthat/helper/diffPrint/2300.rds f6969b4e873c2626cba9655fa0ade92f *tests/testthat/helper/diffPrint/2350.rds fb6e3e28a0fe590afa16470e491b897d *tests/testthat/helper/diffPrint/2370.rds 90f0c8e624026ae3ee09ff6b36af0a7e *tests/testthat/helper/diffPrint/2380.rds 4a8410c083b3f64cc8fce0ff5d3709a3 *tests/testthat/helper/diffPrint/2383.rds f7adce81326a7bf1b2af4b9db98da099 *tests/testthat/helper/diffPrint/2400.rds f7adce81326a7bf1b2af4b9db98da099 *tests/testthat/helper/diffPrint/2500.rds 88f94687cb1212e341dd409fef89f60e *tests/testthat/helper/diffPrint/2600.rds 51192ba9a08d8130611189b4b7a99717 *tests/testthat/helper/diffPrint/2700.rds 1462ff7687b98218c6dd5a5d79209fc6 *tests/testthat/helper/diffPrint/2800.rds 0fcf1dbaf973aff32c38642dea659515 *tests/testthat/helper/diffPrint/2900.rds 6428bd1ff765feaf9698cc6693cf1ac0 *tests/testthat/helper/diffPrint/300.rds 7266f76fb2b5f81db8f1c18858399827 *tests/testthat/helper/diffPrint/3000.rds a35c08e11e8d7c403e3177c13a2d65f9 *tests/testthat/helper/diffPrint/3100.rds 2c958f4965b14694d30335b9f72807bd *tests/testthat/helper/diffPrint/3200.rds 86794d78ccec9b8dc582ab5a346da9b3 *tests/testthat/helper/diffPrint/3300.rds 4d0208a0b608b7d633f662b2092ed896 *tests/testthat/helper/diffPrint/3400.rds 26bb401980e8c6ddc54688448335eb75 *tests/testthat/helper/diffPrint/400.rds 8ba4fe3b4176287a49fc6ae3f6174ea7 *tests/testthat/helper/diffPrint/500.rds 207c5dd85b0c8df263deae230c68a2b2 *tests/testthat/helper/diffPrint/600.rds f6b17f580422089c84bf2c16cea8579a *tests/testthat/helper/diffPrint/700.rds cf0a10ed0b7939dfbd33366fbfe353c2 *tests/testthat/helper/diffPrint/800.rds 3732b9b1ed11bd4171627347310ee847 *tests/testthat/helper/diffPrint/900.rds e1b09bf3fffdaac6189e86f1c49594b0 *tests/testthat/helper/diffStr/100.rds 8f8c0614f6610391dd23827baa721d2b *tests/testthat/helper/diffStr/100.txt 64fc1bd339dd438d0b4dff9fb26c6dbf *tests/testthat/helper/diffStr/1000.rds d2ca22a1835e41ad7d0b60d1e0b4e252 *tests/testthat/helper/diffStr/1100.rds 0aebb54ccae30a57fa40c9e94d5a0b79 *tests/testthat/helper/diffStr/200.rds 8e82236f5a8dca6179790f610b9162e8 *tests/testthat/helper/diffStr/300.rds afc2ddbcbb06ad13195f6111d352b141 *tests/testthat/helper/diffStr/400.rds f16070308563105cd79766fcbe4731ce *tests/testthat/helper/diffStr/500.rds 3d2afca44733f3900578b5139c80ac6e *tests/testthat/helper/diffStr/600.rds 60a797bb010593ff574c99320941a5c9 *tests/testthat/helper/diffStr/700.rds e816514b74dcc98245ed28dcd05b04e6 *tests/testthat/helper/diffStr/800.rds e816514b74dcc98245ed28dcd05b04e6 *tests/testthat/helper/diffStr/900.rds 81b4d6c860927ce9fe0b389f625f07c8 *tests/testthat/helper/guides/100.rds 9dadb9e30a566f057bb3449ca2e80ea4 *tests/testthat/helper/guides/200.rds 5cc7de0903649ea072c89294f6b8cda2 *tests/testthat/helper/html/100.rds 8b2d8f29e9b5c7e5e4315ec4bc4fa2a3 *tests/testthat/helper/html/200.rds 707b0594698d198b31a37b0d70132b36 *tests/testthat/helper/html/300.rds c55e55e592f08398139031c11f67a826 *tests/testthat/helper/html/350.rds 9cbccce90aea834d543f4d3742e96aa9 *tests/testthat/helper/html/400.rds 0aa5e953e2838d65352d867d456376e0 *tests/testthat/helper/limit/100.rds 90b9ae355f42a20f7839b2629fa05291 *tests/testthat/helper/limit/1000.rds d1933542e8052ee44bb0b2ead7dfb387 *tests/testthat/helper/limit/1100.rds 0df077631f4d4c6b14d1479eb952aff5 *tests/testthat/helper/limit/1200.rds c880da027a5854880a5cc29bb4a0865e *tests/testthat/helper/limit/1300.rds dbe2410e259a09082784d9180d0e19cb *tests/testthat/helper/limit/200.rds a95ecffebee4e50cd16d704cf3e3908b *tests/testthat/helper/limit/300.rds 4aaadb0768c247d2e5b10da4c4c9f537 *tests/testthat/helper/limit/500.rds 602b2ac1e4d2c6b523a9b9cf72ca530d *tests/testthat/helper/limit/600.rds f939bf7d26cef1e59f088084433c10b0 *tests/testthat/helper/limit/700.rds fb49940c1563c0d7e98eaf2d8acf1655 *tests/testthat/helper/limit/800.rds df2834bd7e775c21b6c92e385e31d8f4 *tests/testthat/helper/limit/900.rds 162e195ad74eb4044591f87178a884da *tests/testthat/helper/methods/100.rds a657fc69520a33fd5d0f7a1438b54a73 *tests/testthat/helper/methods/200.rds ae0c8d3d54ce94d5ae3a529d2f07155f *tests/testthat/helper/pager/100.txt 8ee3d97210bc7915cc9b2dc52381f267 *tests/testthat/helper/pager/200.txt d41d8cd98f00b204e9800998ecf8427e *tests/testthat/helper/pager/300.txt d911852c9676e4833578c3bd5f385842 *tests/testthat/helper/style/100.rds 58d7a8816b2b516182879bf23bce19cf *tests/testthat/helper/style/200.rds b02d528d5d7449feba4d502d68963f5d *tests/testthat/helper/style/300.rds e78829dfa9acd41cc85022fbddf477f4 *tests/testthat/helper/style/400.rds 784e2b466f53d531e6bcaf9f7f834065 *tests/testthat/helper/style/500.rds 8ae83e9eaf20da0f0cca68fc184226d5 *tests/testthat/helper/summary/100.rds 98dd5589b90754b1c99677b156856280 *tests/testthat/helper/summary/100.txt 963be56fab7538f72248b353af820d9d *tests/testthat/helper/summary/200.rds 54b7fa3e99c02528b6455cf26004d424 *tests/testthat/helper/summary/300.rds 881a4f2d94f2799a11d97b2982fae9de *tests/testthat/helper/summary/400.rds b8b7c6f80f1df44b42f5be1d379a8d67 *tests/testthat/helper/summary/450.rds bfa07e2f8ba3c984ded34f47cc4f6441 *tests/testthat/helper/summary/500.rds 2022c580b6d6f555a7ed9e4d4c5d7eef *tests/testthat/helper/summary/600.rds 03683967ab30bcbeb1ab014d91d195ef *tests/testthat/helper/summary/700.rds e828109ffc70f1d2767db1fa4b40fbdf *tests/testthat/helper/summary/800.rds 466531c9c8c58319d8a123c24cfcfc65 *tests/testthat/helper/summary/900.rds bffab9bbb20319b1f24862c359640af6 *tests/testthat/helper/trim/100.rds ab2c9cb8084ef78d1a4ab576f6ef3b77 *tests/testthat/helper/trim/200.rds 720332fc5f81d72cc6cbbd4b01c00bb0 *tests/testthat/helper/trim/300.rds 24167a5223c4095addff43e415899e5d *tests/testthat/helper/trim/50.rds a8c919f5a3254792e3881ca0cd9cf8f7 *tests/testthat/testthat.atomic.R b04725a8649b6fe89c62160d5efab180 *tests/testthat/testthat.banner.R 0f1eeccd521ecb775876c8ce57fe2f67 *tests/testthat/testthat.capture.R aa06b5e3648665e1d910381c535b390d *tests/testthat/testthat.check.R 2f0fe79c05fd1654115514d252c8065d *tests/testthat/testthat.context.R 7f0a3355f70ba63fc0e7ca6c24415b45 *tests/testthat/testthat.core.R 5114620a31ebc2c859012cb4413d06a6 *tests/testthat/testthat.diffChr.R 937f2f2c6feb2d8a2f0b36e45656bf46 *tests/testthat/testthat.diffDeparse.R 0b32b2f6b10561082898d3e508828f34 *tests/testthat/testthat.diffObj.R 9ebae3816af1ee7d340cafe2da26714f *tests/testthat/testthat.diffPrint.R b980e8dec45031c5e534be8313c3fa73 *tests/testthat/testthat.diffStr.R 46e348c9ae4a45aeefcce7a0a2a08ec2 *tests/testthat/testthat.file.R 2a48ce1988dc6e328d8a74c954b8c52a *tests/testthat/testthat.guide.R 08e24fcbba40187eb84b71d6036db7ec *tests/testthat/testthat.html.R 29483d0a3d01cc69e3dd0e188d94e89d *tests/testthat/testthat.limit.R 24d78e63ebf532444d173c0665003204 *tests/testthat/testthat.methods.R c3cac5120e020a653baf84bb8a62a88d *tests/testthat/testthat.misc.R 270c58f0bd9121c1ec0e167d380ab5bb *tests/testthat/testthat.pager.R 22c2173bc659345febaa237b93a57b76 *tests/testthat/testthat.rrdiff.R b167459846aaf5d17f7d5ce054148826 *tests/testthat/testthat.s4.R 3a7b47f6fd61ee35864a1c4187a22893 *tests/testthat/testthat.ses.R 3a65958936c4cdd1e549fa4690de9763 *tests/testthat/testthat.style.R 46b7c6f080bfc497d0f655360c7a5f81 *tests/testthat/testthat.subset.R e28e59e7cb5cfaf0d7a25ad99468d319 *tests/testthat/testthat.summary.R c32c7d567aa870c95416e1c81dab6210 *tests/testthat/testthat.text.R 2fbb756985f1ebcf0b6a02c52e7791c4 *tests/testthat/testthat.trim.R be459c11d9cf18fb09350a3fea1e308d *tests/testthat/testthat.warnings.R 498f5f1c22d785221998c067e58cc456 *tests/valgrind/mdl-cur-all.txt 3ebdc9637d25ba9ada7fb03ea757a42e *tests/valgrind/mdl-cur.txt 190d0593d9f845f0afa81a7c23789387 *tests/valgrind/mdl-tar-all.txt 8478afde8a5625a16f8f3118ecec2219 *tests/valgrind/mdl-tar.txt cbc6489a2d892932cc981539af1e3228 *tests/valgrind/tests-valgrind.R b2d7b4d93f4eab544203b2d4160e0583 *vignettes/ansi256brightness.png b030819a35d24fb09b3eb8d60f9aa369 *vignettes/diffobj.Rmd 9e93047aa59973c6475724b83fc6c321 *vignettes/embed.Rmd dd768a6e630251f3773bb13c6e2b7804 *vignettes/metacomp.Rmd 53121e5e594fc9814e2f89ebfeeccf28 *vignettes/styles.css diffobj/build/0000755000176200001440000000000013466146510012762 5ustar liggesusersdiffobj/build/vignette.rds0000644000176200001440000000043713466146510015325 0ustar liggesusers‹…’ÑJÃ0†³µN[7&x|€¾„µ "Êæ…·Y“²h“Œ$£ìÎ×zš&u‚7'ù¿$ÿùä-EMQ4ƒÁ6ZB®:b”ÀzIyU©Í{¶Ô£„‰ £G`.˜%¥;ÇÆïo¤ÕŠîKË•ÄVá¼÷óÇ·÷—ƒs‰Wø‰èª‰Ÿ5^o¹<ø›×wЀhnÀ¦ØKçgNšÍCØ­µgiŸöˆ,†¸Ž-’a^Îý´ÁîwVPK÷²w@¬ÿœÇS(ï|&‰`a¢™‡qÁk.¼r;ˆè%/üv2DÍÙŽIj¼¼xd‡FiÐãF‰VMš-º?ð ¥mÛ¯ÓDeMLH`J‰%Y¥á=¨ïHýð}Ediffobj/DESCRIPTION0000755000176200001440000000251013466336133013374 0ustar liggesusersPackage: diffobj Type: Package Title: Diffs for R Objects Description: Generate a colorized diff of two R objects for an intuitive visualization of their differences. Version: 0.2.3 Authors@R: c( person( "Brodie", "Gaslam", email="brodie.gaslam@yahoo.com", role=c("aut", "cre")), person( "Michael B.", "Allen", email="ioplex@gmail.com", role=c("ctb", "cph"), comment="Original C implementation of Myers Diff Algorithm")) Depends: R (>= 3.1.0) License: GPL (>= 2) LazyData: true URL: https://github.com/brodieG/diffobj BugReports: https://github.com/brodieG/diffobj/issues RoxygenNote: 6.1.1 VignetteBuilder: knitr Encoding: UTF-8 Suggests: knitr, rmarkdown, testthat Collate: 'capt.R' 'options.R' 'pager.R' 'check.R' 'finalizer.R' 'misc.R' 'html.R' 'styles.R' 's4.R' 'core.R' 'diff.R' 'get.R' 'guides.R' 'hunks.R' 'layout.R' 'myerssimple.R' 'rdiff.R' 'rds.R' 'set.R' 'subset.R' 'summmary.R' 'system.R' 'text.R' 'tochar.R' 'trim.R' 'word.R' Imports: crayon (>= 1.3.2), tools, methods, utils, stats NeedsCompilation: yes Packaged: 2019-05-13 01:50:00 UTC; bg Author: Brodie Gaslam [aut, cre], Michael B. Allen [ctb, cph] (Original C implementation of Myers Diff Algorithm) Maintainer: Brodie Gaslam Repository: CRAN Date/Publication: 2019-05-13 18:50:03 UTC diffobj/man/0000755000176200001440000000000013465027156012441 5ustar liggesusersdiffobj/man/PaletteOfStyles-class.Rd0000755000176200001440000001210313201325222017101 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/styles.R \docType{class} \name{PaletteOfStyles-class} \alias{PaletteOfStyles-class} \alias{PaletteOfStyles} \title{Class for Tracking Default Styles by Style Type} \description{ Provides a mechanism for specifying a style based on the style properties along dimensions of format, brightness, and color. This allows a user to request a style that meets a certain description (e.g. a \dQuote{light} scheme in \dQuote{ansi256} format), without having to provide a specific \code{\link{Style}} object. } \section{An Array of Styles}{ A \code{PaletteOfStyles} object is an \dQuote{array} containing either \dQuote{classRepresentation} objects that extend \code{StyleHtml} or are instances of objects that inherit from \code{StyleHtml}. The \code{diff*} methods then pick an object/class from this array based on the values of the \code{format}, \code{brightness}, and \code{color.mode} parameters. For the most part the distinction between actual \code{Style} objects vs \dQuote{classRepresentation} ones is academic, except that with the latter you can control the instantiation by providing a parameter list as the \code{style} argument to the \code{diff*} methods. This is not an option with already instantiated objects. See examples. } \section{Dimensions}{ There are three general orthogonal dimensions of styles that can be used when rendering diffs: the type of format, the \dQuote{brightness} of the output, and whether the colors used are distinguishable if you assume reds and greens are not distinguishable. Defaults for the intersections each of these dimensions are encoded as a three dimensional list. This list is just an atomic vector of type \dQuote{list} with a length 3 \code{dim} attribute. The array/list dimensions are: \itemize{ \item \code{format}: the format type, one of \dQuote{raw}, \dQuote{ansi8}, \dQuote{ansi256}, or \dQuote{html} \item \code{brightness}: whether the colors are bright or not, which allows user to chose a scheme that is compatible with their console, one of: \dQuote{light}, \dQuote{dark}, \dQuote{normal} \item \code{color.mode}: \dQuote{rgb} for full color or \dQuote{yb} for dichromats (yb stands for Yellow Blue). } Each of these dimensions can be specified directly via the corresponding parameters to the \code{diff*} methods. } \section{Methods}{ \code{PaletteOfStyles} objects have The following methods implemented: \itemize{ \item \code{[}, \code{[<-}, \code{[[} \item show \item summary \item dimnames } } \section{Structural Details}{ The array/list is stored in the \code{data} slot of \code{PaletteOfStyles} objects. Subsetting methods are provided so you may operate directly on the S4 object as you would on a regular array. The array/list must be fully populated with objects that are or inherit \code{Style}, or are \dQuote{classRepresentation} objects (i.e. those of the type returned by \code{\link{getClassDef}}) that extend \code{Style}. By default the array is populated only with \dQuote{classRepresentation} objects as that allows the list form of the \code{style} parameter to the \code{diff*} methods. If there is a particular combination of coordinates that does not have a corresponding defined style a reasonable substitution must be provided. For example, this package only defines \dQuote{light} HTML styles, so it simply uses that style for all the possible \code{brightness} values. There is no explicit check that the objects in the list comply with the descriptions implied by their coordinates, although the default object provided by the package does comply for the most part. One check that is carried out is that any element that has a \dQuote{html} value in the \code{format} dimension extends \code{StyleHtml}. While the list may only have the three dimensions described, you can add values to the dimensions provided the values described above are the first ones in each of their corresponding dimensions. For example, if you wanted to allow for styles that would render in \code{grid} graphics, you could generate a default list with a \dQuote{"grid"} value appended to the values of the \code{format} dimension. } \examples{ \dontrun{ ## Look at all "ansi256" styles (assumes compatible terminal) PaletteOfStyles()["ansi256",,] } ## Generate the default style object palette, and replace ## the ansi256 / light / rgb style with our modified one ## which for illustrative purposes is the raw style my.pal <- PaletteOfStyles() my.style <- StyleRaw() # See `?Style` for custom styles my.style@funs@word.delete <- function(x) sprintf("--\%s--", x) my.pal["ansi256", "light", "rgb"] <- list(my.style) # note `list()` ## Output has no format now for format/color.mode/brightness ## we modified ... ## `pager="off"` for CRAN compliance; you may omit in normal use diffPrint( 1:3, 2:5, format="ansi256", color.mode="rgb", brightness="light", palette.of.styles=my.pal, pager="off", disp.width=80 ) ## If so desired, set our new style palette as the default ## one; could also pass directly as argument to `diff*` funs \dontrun{ options(diffobj.palette=defs) } } diffobj/man/gdo.Rd0000755000176200001440000000064713420351310013472 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/options.R \name{gdo} \alias{gdo} \title{Shorthand Function for Accessing diffobj Options} \usage{ gdo(x) } \arguments{ \item{x}{character(1L) name off \code{diffobj} option to retrieve, without the \dQuote{diffobj.} prefix} } \description{ \code{gdo(x)} is equivalent to \code{getOption(sprintf("diffobj.\%s", x))}. } \examples{ gdo("format") } diffobj/man/diffDeparse.Rd0000755000176200001440000004423613466140042015147 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/diff.R \docType{methods} \name{diffDeparse} \alias{diffDeparse} \alias{diffDeparse,ANY-method} \title{Diff Deparsed Objects} \usage{ diffDeparse(target, current, ...) \S4method{diffDeparse}{ANY}(target, current, mode = gdo("mode"), context = gdo("context"), format = gdo("format"), brightness = gdo("brightness"), color.mode = gdo("color.mode"), word.diff = gdo("word.diff"), pager = gdo("pager"), guides = gdo("guides"), trim = gdo("trim"), rds = gdo("rds"), unwrap.atomic = gdo("unwrap.atomic"), max.diffs = gdo("max.diffs"), disp.width = gdo("disp.width"), ignore.white.space = gdo("ignore.white.space"), convert.hz.white.space = gdo("convert.hz.white.space"), tab.stops = gdo("tab.stops"), line.limit = gdo("line.limit"), hunk.limit = gdo("hunk.limit"), align = gdo("align"), style = gdo("style"), palette.of.styles = gdo("palette"), frame = par_frame(), interactive = gdo("interactive"), term.colors = gdo("term.colors"), tar.banner = NULL, cur.banner = NULL, strip.sgr = gdo("strip.sgr"), sgr.supported = gdo("sgr.supported"), extra = list()) } \arguments{ \item{target}{the reference object} \item{current}{the object being compared to \code{target}} \item{...}{unused, for compatibility of methods with generics} \item{mode}{character(1L), one of: \itemize{ \item \dQuote{unified}: diff mode used by \code{git diff} \item \dQuote{sidebyside}: line up the differences side by side \item \dQuote{context}: show the target and current hunks in their entirety; this mode takes up a lot of screen space but makes it easier to see what the objects actually look like \item \dQuote{auto}: default mode; pick one of the above, will favor \dQuote{sidebyside} unless \code{getOption("width")} is less than 80, or in \code{diffPrint} and objects are dimensioned and do not fit side by side, or in \code{diffChr}, \code{diffDeparse}, \code{diffFile} and output does not fit in side by side without wrapping }} \item{context}{integer(1L) how many lines of context are shown on either side of differences (defaults to 2). Set to \code{-1L} to allow as many as there are. Set to \dQuote{auto} to display as many as 10 lines or as few as 1 depending on whether total screen lines fit within the number of lines specified in \code{line.limit}. Alternatively pass the return value of \code{\link{auto_context}} to fine tune the parameters of the auto context calculation.} \item{format}{character(1L), controls the diff output format, one of: \itemize{ \item \dQuote{auto}: to select output format based on terminal capabilities; will attempt to use one of the ANSI formats if they appear to be supported, and if not or if you are in the Rstudio console it will attempt to use HTML and browser output if in interactive mode. \item \dQuote{raw}: plain text \item \dQuote{ansi8}: color and format diffs using basic ANSI escape sequences \item \dQuote{ansi256}: like \dQuote{ansi8}, except using the full range of ANSI formatting options \item \dQuote{html}: color and format using HTML markup; the resulting string is processed with \code{\link{enc2utf8}} when output as a full web page (see docs for \code{html.output} under \code{\link{Style}}). } Defaults to \dQuote{auto}. See \code{palette.of.styles} for details on customization, \code{\link{style}} for full control of output format. See `pager` parameter for more discussion of Rstudio behavior.} \item{brightness}{character, one of \dQuote{light}, \dQuote{dark}, \dQuote{neutral}, useful for adjusting color scheme to light or dark terminals. \dQuote{neutral} by default. See \code{\link{PaletteOfStyles}} for details and limitations. Advanced: you may specify brightness as a function of \code{format}. For example, if you typically wish to use a \dQuote{dark} color scheme, except for when in \dQuote{html} format when you prefer the \dQuote{light} scheme, you may use \code{c("dark", html="light")} as the value for this parameter. This is particularly useful if \code{format} is set to \dQuote{auto} or if you want to specify a default value for this parameter via options. Any names you use should correspond to a \code{format}. You must have one unnamed value which will be used as the default for all \code{format}s that are not explicitly specified.} \item{color.mode}{character, one of \dQuote{rgb} or \dQuote{yb}. Defaults to \dQuote{yb}. \dQuote{yb} stands for \dQuote{Yellow-Blue} for color schemes that rely primarily on those colors to style diffs. Those colors can be easily distinguished by individuals with limited red-green color sensitivity. See \code{\link{PaletteOfStyles}} for details and limitations. Also offers the same advanced usage as the \code{brightness} parameter.} \item{word.diff}{TRUE (default) or FALSE, whether to run a secondary word diff on the in-hunk differences. For atomic vectors setting this to FALSE could make the diff \emph{slower} (see the \code{unwrap.atomic} parameter). For other uses, particularly with \code{\link{diffChr}} setting this to FALSE can substantially improve performance.} \item{pager}{one of \dQuote{auto} (default), \dQuote{on}, \dQuote{off}, a \code{\link{Pager}} object, or a list; controls whether and how a pager is used to display the diff output. If you require a particular pager behavior you must use a \code{\link{Pager}} object, or \dQuote{off} to turn off the pager. All other settings will interact with other parameters such as \code{format}, \code{style}, as well as with your system capabilities in order to select the pager expected to be most useful. \dQuote{auto} and \dQuote{on} are the same, except that in non-interactive mode \dQuote{auto} is equivalent to \dQuote{off}. \dQuote{off} will always send output to the console. If \dQuote{on}, whether the output actually gets routed to the pager depends on the pager \code{threshold} setting (see \code{\link{Pager}}). The default behavior is to use the pager associated with the \code{Style} object. The \code{Style} object is itself is determined by the \code{format} or \code{style} parameters. Depending on your system configuration different styles and corresponding pagers will get selected, unless you specify a \code{Pager} object directly. On a system with a system pager that supports ANSI CSI SGR colors, the pager will only trigger if the output is taller than one window. If the system pager is not known to support ANSI colors then the output will be sent as HTML to the IDE viewer if available or to the web browser if not. Even though Rstudio now supports ANSI CSI SGR at the console output is still formatted as HTML and sent to the IDE viewer. Partly this is for continuity of behavior, but also because the default Rstudio pager does not support ANSI CSI SGR, at least as of this writing. If \code{pager} is a list, then the same as with \dQuote{on}, except that the \code{Pager} object associated with the selected \code{Style} object is re-instantiated with the union of the list elements and the existing settings of that \code{Pager}. The list should contain named elements that correspond to the \code{\link{Pager}} instantiation parameters. The names must be specified in full as partial parameter matching will not be carried out because the pager is re-instantiated with \code{\link{new}}. See \code{\link{Pager}}, \code{\link{Style}}, and \code{\link{PaletteOfStyles}} for more details and for instructions on how to modify the default behavior.} \item{guides}{TRUE (default), FALSE, or a function that accepts at least two arguments and requires no more than two arguments. Guides are additional context lines that are not strictly part of a hunk, but provide important contextual data (e.g. column headers). If TRUE, the context lines are shown in addition to the normal diff output, typically in a different color to indicate they are not part of the hunk. If a function, the function should accept as the first argument the object being diffed, and the second the character representation of the object. The function should return the indices of the elements of the character representation that should be treated as guides. See \code{\link{guides}} for more details.} \item{trim}{TRUE (default), FALSE, or a function that accepts at least two arguments and requires no more than two arguments. Function should compute for each line in captured output what portion of those lines should be diffed. By default, this is used to remove row meta data differences (e.g. \code{[1,]}) so they alone do not show up as differences in the diff. See \code{\link{trim}} for more details.} \item{rds}{TRUE (default) or FALSE, if TRUE will check whether \code{target} and/or \code{current} point to a file that can be read with \code{\link{readRDS}} and if so, loads the R object contained in the file and carries out the diff on the object instead of the original argument. Currently there is no mechanism for specifying additional arguments to \code{readRDS}} \item{unwrap.atomic}{TRUE (default) or FALSE. Relevant primarily for \code{diffPrint}, if TRUE, and \code{word.diff} is also TRUE, and both \code{target} and \code{current} are \emph{unnamed} one-dimension atomics , the vectors are unwrapped and diffed element by element, and then re-wrapped. Since \code{diffPrint} is fundamentally a line diff, the re-wrapped lines are lined up in a manner that is as consistent as possible with the unwrapped diff. Lines that contain the location of the word differences will be paired up. Since the vectors may well be wrapped with different periodicities this will result in lines that are paired up that look like they should not be paired up, though the locations of the differences should be. If is entirely possible that setting this parameter to FALSE will result in a slower diff. This happens if two vectors are actually fairly similar, but their line representations are not. For example, in comparing \code{1:100} to \code{c(100, 1:99)}, there is really only one difference at the \dQuote{word} level, but every screen line is different. \code{diffChr} will also do the unwrapping if it is given a character vector that contains output that looks like the atomic vectors described above. This is a bug, but as the functionality could be useful when diffing e.g. \code{capture.output} data, we now declare it a feature.} \item{max.diffs}{integer(1L), number of \emph{differences} after which we abandon the \code{O(n^2)} diff algorithm in favor of a naive element by element comparison. Set to \code{-1L} to always stick to the original algorithm (defaults to 50000L).} \item{disp.width}{integer(1L) number of display columns to take up; note that in \dQuote{sidebyside} \code{mode} the effective display width is half this number (set to 0L to use default widths which are \code{getOption("width")} for normal styles and \code{80L} for HTML styles. Future versions of \code{diffobj} may change this to larger values for two dimensional objects for better diffs (see details).} \item{ignore.white.space}{TRUE or FALSE, whether to consider differences in horizontal whitespace (i.e. spaces and tabs) as differences (defaults to TRUE).} \item{convert.hz.white.space}{TRUE or FALSE, whether modify input strings that contain tabs and carriage returns in such a way that they display as they would \bold{with} those characters, but without using those characters (defaults to TRUE). The conversion assumes that tab stops are spaced evenly eight characters apart on the terminal. If this is not the case you may specify the tab stops explicitly with \code{tab.stops}.} \item{tab.stops}{integer, what tab stops to use when converting hard tabs to spaces. If not integer will be coerced to integer (defaults to 8L). You may specify more than one tab stop. If display width exceeds that addressable by your tab stops the last tab stop will be repeated.} \item{line.limit}{integer(2L) or integer(1L), if length 1 how many lines of output to show, where \code{-1} means no limit. If length 2, the first value indicates the threshold of screen lines to begin truncating output, and the second the number of lines to truncate to, which should be fewer than the threshold. Note that this parameter is implemented on a best-efforts basis and should not be relied on to produce the exact number of lines requested. In particular do not expect it to work well for for values small enough that the banner portion of the diff would have to be trimmed. If you want a specific number of lines use \code{[} or \code{head} / \code{tail}. One advantage of \code{line.limit} over these other options is that you can combine it with \code{context="auto"} and auto \code{max.level} selection (the latter for \code{diffStr}), which allows the diff to dynamically adjust to make best use of the available display lines. \code{[}, \code{head}, and \code{tail} just subset the text of the output.} \item{hunk.limit}{integer(2L) or integer (1L), how many diff hunks to show. Behaves similarly to \code{line.limit}. How many hunks are in a particular diff is a function of how many differences, and also how much \code{context} is used since context can cause two hunks to bleed into each other and become one.} \item{align}{numeric(1L) between 0 and 1, proportion of words in a line of \code{target} that must be matched in a line of \code{current} in the same hunk for those lines to be paired up when displayed (defaults to 0.25), or an \code{\link{AlignThreshold}} object. Set to \code{1} to turn off alignment which will cause all lines in a hunk from \code{target} to show up first, followed by all lines from \code{current}. Note that in order to be aligned lines must meet the threshold and have at least 3 matching alphanumeric characters (see \code{\link{AlignThreshold}} for details).} \item{style}{\dQuote{auto}, a \code{\link{Style}} object, or a list. \dQuote{auto} by default. If a \code{Style} object, will override the the \code{format}, \code{brightness}, and \code{color.mode} parameters. The \code{Style} object provides full control of diff output styling. If a list, then the same as \dQuote{auto}, except that if the auto-selected \code{Style} requires instantiation (see \code{\link{PaletteOfStyles}}), then the list contents will be used as arguments when instantiating the style object. See \code{\link{Style}} for more details, in particular the examples.} \item{palette.of.styles}{\code{\link{PaletteOfStyles}} object; advanced usage, contains all the \code{\link{Style}} objects or \dQuote{classRepresentation} objects extending \code{\link{Style}} that are selected by specifying the \code{format}, \code{brightness}, and \code{color.mode} parameters. See \code{\link{PaletteOfStyles}} for more details.} \item{frame}{an environment to use as the evaluation frame for the \code{print/show/str}, calls and for \code{diffObj}, the evaluation frame for the \code{diffPrint} / \code{diffStr} calls. Defaults to the return value of \code{\link{par_frame}}.} \item{interactive}{TRUE or FALSE whether the function is being run in interactive mode, defaults to the return value of \code{\link{interactive}}. If in interactive mode, pager will be used if \code{pager} is \dQuote{auto}, and if ANSI styles are not supported and \code{style} is \dQuote{auto}, output will be send to viewer/browser as HTML.} \item{term.colors}{integer(1L) how many ANSI colors are supported by the terminal. This variable is provided for when \code{\link[=num_colors]{crayon::num_colors}} does not properly detect how many ANSI colors are supported by your terminal. Defaults to return value of \code{\link[=num_colors]{crayon::num_colors}} and should be 8 or 256 to allow ANSI colors, or any other number to disallow them. This only impacts output format selection when \code{style} and \code{format} are both set to \dQuote{auto}.} \item{tar.banner}{character(1L), language, or NULL, used to generate the text to display ahead of the diff section representing the target output. If NULL will use the deparsed \code{target} expression, if language, will use the language as it would the \code{target} expression, if character(1L), will use the string with no modifications. The language mode is provided because \code{diffStr} modifies the expression prior to display (e.g. by wrapping it in a call to \code{str}). Note that it is possible in some cases that the substituted value of \code{target} actually is character(1L), but if you provide a character(1L) value here it will be assumed you intend to use that value literally.} \item{cur.banner}{character(1L) like \code{tar.banner}, but for \code{current}} \item{strip.sgr}{TRUE, FALSE, or NULL (default), whether to strip ANSI CSI SGR sequences prior to comparison and for display of diff. If NULL, resolves to TRUE if `style` resolves to an ANSI formatted diff, and FALSE otherwise. The default behavior is to avoid confusing diffs where the original SGR and the SGR added by the diff are mixed together.} \item{sgr.supported}{TRUE, FALSE, or NULL (default), whether to assume the standard output device supports ANSI CSI SGR sequences. If TRUE, strings will be manipulated accounting for the SGR sequences. If NULL, resolves to TRUE if `style` resolves to an ANSI formatted diff, and to `crayon::has_color()` otherwise. This only controls how the strings are manipulated, not whether SGR is added to format the diff, which is controlled by the `style` parameter. This parameter is exposed for the rare cases where you might wish to control string manipulation behavior directly.} \item{extra}{list additional arguments to pass on to the functions used to create text representation of the objects to diff (e.g. \code{print}, \code{str}, etc.)} } \value{ a \code{Diff} object; see \code{\link{diffPrint}}. } \description{ Perform diff on the character vectors produced by \code{\link{deparse}}ing the objects. Each element counts as a line. If an element contains newlines it will be split into elements new lines by the newlines. } \examples{ ## `pager="off"` for CRAN compliance; you may omit in normal use diffDeparse(matrix(1:9, 3), 1:9, pager="off") } \seealso{ \code{\link{diffPrint}} for details on the \code{diff*} functions, \code{\link{diffObj}}, \code{\link{diffStr}}, \code{\link{diffChr}} to compare character vectors directly, \code{\link{ses}} for a minimal and fast diff } diffobj/man/diffCsv.Rd0000755000176200001440000004500113466140042014306 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/diff.R \docType{methods} \name{diffCsv} \alias{diffCsv} \alias{diffCsv,ANY-method} \title{Diff CSV Files} \usage{ diffCsv(target, current, ...) \S4method{diffCsv}{ANY}(target, current, mode = gdo("mode"), context = gdo("context"), format = gdo("format"), brightness = gdo("brightness"), color.mode = gdo("color.mode"), word.diff = gdo("word.diff"), pager = gdo("pager"), guides = gdo("guides"), trim = gdo("trim"), rds = gdo("rds"), unwrap.atomic = gdo("unwrap.atomic"), max.diffs = gdo("max.diffs"), disp.width = gdo("disp.width"), ignore.white.space = gdo("ignore.white.space"), convert.hz.white.space = gdo("convert.hz.white.space"), tab.stops = gdo("tab.stops"), line.limit = gdo("line.limit"), hunk.limit = gdo("hunk.limit"), align = gdo("align"), style = gdo("style"), palette.of.styles = gdo("palette"), frame = par_frame(), interactive = gdo("interactive"), term.colors = gdo("term.colors"), tar.banner = NULL, cur.banner = NULL, strip.sgr = gdo("strip.sgr"), sgr.supported = gdo("sgr.supported"), extra = list()) } \arguments{ \item{target}{character(1L) or file connection with read capability; if character should point to a CSV file} \item{current}{like \code{target}} \item{...}{unused, for compatibility of methods with generics} \item{mode}{character(1L), one of: \itemize{ \item \dQuote{unified}: diff mode used by \code{git diff} \item \dQuote{sidebyside}: line up the differences side by side \item \dQuote{context}: show the target and current hunks in their entirety; this mode takes up a lot of screen space but makes it easier to see what the objects actually look like \item \dQuote{auto}: default mode; pick one of the above, will favor \dQuote{sidebyside} unless \code{getOption("width")} is less than 80, or in \code{diffPrint} and objects are dimensioned and do not fit side by side, or in \code{diffChr}, \code{diffDeparse}, \code{diffFile} and output does not fit in side by side without wrapping }} \item{context}{integer(1L) how many lines of context are shown on either side of differences (defaults to 2). Set to \code{-1L} to allow as many as there are. Set to \dQuote{auto} to display as many as 10 lines or as few as 1 depending on whether total screen lines fit within the number of lines specified in \code{line.limit}. Alternatively pass the return value of \code{\link{auto_context}} to fine tune the parameters of the auto context calculation.} \item{format}{character(1L), controls the diff output format, one of: \itemize{ \item \dQuote{auto}: to select output format based on terminal capabilities; will attempt to use one of the ANSI formats if they appear to be supported, and if not or if you are in the Rstudio console it will attempt to use HTML and browser output if in interactive mode. \item \dQuote{raw}: plain text \item \dQuote{ansi8}: color and format diffs using basic ANSI escape sequences \item \dQuote{ansi256}: like \dQuote{ansi8}, except using the full range of ANSI formatting options \item \dQuote{html}: color and format using HTML markup; the resulting string is processed with \code{\link{enc2utf8}} when output as a full web page (see docs for \code{html.output} under \code{\link{Style}}). } Defaults to \dQuote{auto}. See \code{palette.of.styles} for details on customization, \code{\link{style}} for full control of output format. See `pager` parameter for more discussion of Rstudio behavior.} \item{brightness}{character, one of \dQuote{light}, \dQuote{dark}, \dQuote{neutral}, useful for adjusting color scheme to light or dark terminals. \dQuote{neutral} by default. See \code{\link{PaletteOfStyles}} for details and limitations. Advanced: you may specify brightness as a function of \code{format}. For example, if you typically wish to use a \dQuote{dark} color scheme, except for when in \dQuote{html} format when you prefer the \dQuote{light} scheme, you may use \code{c("dark", html="light")} as the value for this parameter. This is particularly useful if \code{format} is set to \dQuote{auto} or if you want to specify a default value for this parameter via options. Any names you use should correspond to a \code{format}. You must have one unnamed value which will be used as the default for all \code{format}s that are not explicitly specified.} \item{color.mode}{character, one of \dQuote{rgb} or \dQuote{yb}. Defaults to \dQuote{yb}. \dQuote{yb} stands for \dQuote{Yellow-Blue} for color schemes that rely primarily on those colors to style diffs. Those colors can be easily distinguished by individuals with limited red-green color sensitivity. See \code{\link{PaletteOfStyles}} for details and limitations. Also offers the same advanced usage as the \code{brightness} parameter.} \item{word.diff}{TRUE (default) or FALSE, whether to run a secondary word diff on the in-hunk differences. For atomic vectors setting this to FALSE could make the diff \emph{slower} (see the \code{unwrap.atomic} parameter). For other uses, particularly with \code{\link{diffChr}} setting this to FALSE can substantially improve performance.} \item{pager}{one of \dQuote{auto} (default), \dQuote{on}, \dQuote{off}, a \code{\link{Pager}} object, or a list; controls whether and how a pager is used to display the diff output. If you require a particular pager behavior you must use a \code{\link{Pager}} object, or \dQuote{off} to turn off the pager. All other settings will interact with other parameters such as \code{format}, \code{style}, as well as with your system capabilities in order to select the pager expected to be most useful. \dQuote{auto} and \dQuote{on} are the same, except that in non-interactive mode \dQuote{auto} is equivalent to \dQuote{off}. \dQuote{off} will always send output to the console. If \dQuote{on}, whether the output actually gets routed to the pager depends on the pager \code{threshold} setting (see \code{\link{Pager}}). The default behavior is to use the pager associated with the \code{Style} object. The \code{Style} object is itself is determined by the \code{format} or \code{style} parameters. Depending on your system configuration different styles and corresponding pagers will get selected, unless you specify a \code{Pager} object directly. On a system with a system pager that supports ANSI CSI SGR colors, the pager will only trigger if the output is taller than one window. If the system pager is not known to support ANSI colors then the output will be sent as HTML to the IDE viewer if available or to the web browser if not. Even though Rstudio now supports ANSI CSI SGR at the console output is still formatted as HTML and sent to the IDE viewer. Partly this is for continuity of behavior, but also because the default Rstudio pager does not support ANSI CSI SGR, at least as of this writing. If \code{pager} is a list, then the same as with \dQuote{on}, except that the \code{Pager} object associated with the selected \code{Style} object is re-instantiated with the union of the list elements and the existing settings of that \code{Pager}. The list should contain named elements that correspond to the \code{\link{Pager}} instantiation parameters. The names must be specified in full as partial parameter matching will not be carried out because the pager is re-instantiated with \code{\link{new}}. See \code{\link{Pager}}, \code{\link{Style}}, and \code{\link{PaletteOfStyles}} for more details and for instructions on how to modify the default behavior.} \item{guides}{TRUE (default), FALSE, or a function that accepts at least two arguments and requires no more than two arguments. Guides are additional context lines that are not strictly part of a hunk, but provide important contextual data (e.g. column headers). If TRUE, the context lines are shown in addition to the normal diff output, typically in a different color to indicate they are not part of the hunk. If a function, the function should accept as the first argument the object being diffed, and the second the character representation of the object. The function should return the indices of the elements of the character representation that should be treated as guides. See \code{\link{guides}} for more details.} \item{trim}{TRUE (default), FALSE, or a function that accepts at least two arguments and requires no more than two arguments. Function should compute for each line in captured output what portion of those lines should be diffed. By default, this is used to remove row meta data differences (e.g. \code{[1,]}) so they alone do not show up as differences in the diff. See \code{\link{trim}} for more details.} \item{rds}{TRUE (default) or FALSE, if TRUE will check whether \code{target} and/or \code{current} point to a file that can be read with \code{\link{readRDS}} and if so, loads the R object contained in the file and carries out the diff on the object instead of the original argument. Currently there is no mechanism for specifying additional arguments to \code{readRDS}} \item{unwrap.atomic}{TRUE (default) or FALSE. Relevant primarily for \code{diffPrint}, if TRUE, and \code{word.diff} is also TRUE, and both \code{target} and \code{current} are \emph{unnamed} one-dimension atomics , the vectors are unwrapped and diffed element by element, and then re-wrapped. Since \code{diffPrint} is fundamentally a line diff, the re-wrapped lines are lined up in a manner that is as consistent as possible with the unwrapped diff. Lines that contain the location of the word differences will be paired up. Since the vectors may well be wrapped with different periodicities this will result in lines that are paired up that look like they should not be paired up, though the locations of the differences should be. If is entirely possible that setting this parameter to FALSE will result in a slower diff. This happens if two vectors are actually fairly similar, but their line representations are not. For example, in comparing \code{1:100} to \code{c(100, 1:99)}, there is really only one difference at the \dQuote{word} level, but every screen line is different. \code{diffChr} will also do the unwrapping if it is given a character vector that contains output that looks like the atomic vectors described above. This is a bug, but as the functionality could be useful when diffing e.g. \code{capture.output} data, we now declare it a feature.} \item{max.diffs}{integer(1L), number of \emph{differences} after which we abandon the \code{O(n^2)} diff algorithm in favor of a naive element by element comparison. Set to \code{-1L} to always stick to the original algorithm (defaults to 50000L).} \item{disp.width}{integer(1L) number of display columns to take up; note that in \dQuote{sidebyside} \code{mode} the effective display width is half this number (set to 0L to use default widths which are \code{getOption("width")} for normal styles and \code{80L} for HTML styles. Future versions of \code{diffobj} may change this to larger values for two dimensional objects for better diffs (see details).} \item{ignore.white.space}{TRUE or FALSE, whether to consider differences in horizontal whitespace (i.e. spaces and tabs) as differences (defaults to TRUE).} \item{convert.hz.white.space}{TRUE or FALSE, whether modify input strings that contain tabs and carriage returns in such a way that they display as they would \bold{with} those characters, but without using those characters (defaults to TRUE). The conversion assumes that tab stops are spaced evenly eight characters apart on the terminal. If this is not the case you may specify the tab stops explicitly with \code{tab.stops}.} \item{tab.stops}{integer, what tab stops to use when converting hard tabs to spaces. If not integer will be coerced to integer (defaults to 8L). You may specify more than one tab stop. If display width exceeds that addressable by your tab stops the last tab stop will be repeated.} \item{line.limit}{integer(2L) or integer(1L), if length 1 how many lines of output to show, where \code{-1} means no limit. If length 2, the first value indicates the threshold of screen lines to begin truncating output, and the second the number of lines to truncate to, which should be fewer than the threshold. Note that this parameter is implemented on a best-efforts basis and should not be relied on to produce the exact number of lines requested. In particular do not expect it to work well for for values small enough that the banner portion of the diff would have to be trimmed. If you want a specific number of lines use \code{[} or \code{head} / \code{tail}. One advantage of \code{line.limit} over these other options is that you can combine it with \code{context="auto"} and auto \code{max.level} selection (the latter for \code{diffStr}), which allows the diff to dynamically adjust to make best use of the available display lines. \code{[}, \code{head}, and \code{tail} just subset the text of the output.} \item{hunk.limit}{integer(2L) or integer (1L), how many diff hunks to show. Behaves similarly to \code{line.limit}. How many hunks are in a particular diff is a function of how many differences, and also how much \code{context} is used since context can cause two hunks to bleed into each other and become one.} \item{align}{numeric(1L) between 0 and 1, proportion of words in a line of \code{target} that must be matched in a line of \code{current} in the same hunk for those lines to be paired up when displayed (defaults to 0.25), or an \code{\link{AlignThreshold}} object. Set to \code{1} to turn off alignment which will cause all lines in a hunk from \code{target} to show up first, followed by all lines from \code{current}. Note that in order to be aligned lines must meet the threshold and have at least 3 matching alphanumeric characters (see \code{\link{AlignThreshold}} for details).} \item{style}{\dQuote{auto}, a \code{\link{Style}} object, or a list. \dQuote{auto} by default. If a \code{Style} object, will override the the \code{format}, \code{brightness}, and \code{color.mode} parameters. The \code{Style} object provides full control of diff output styling. If a list, then the same as \dQuote{auto}, except that if the auto-selected \code{Style} requires instantiation (see \code{\link{PaletteOfStyles}}), then the list contents will be used as arguments when instantiating the style object. See \code{\link{Style}} for more details, in particular the examples.} \item{palette.of.styles}{\code{\link{PaletteOfStyles}} object; advanced usage, contains all the \code{\link{Style}} objects or \dQuote{classRepresentation} objects extending \code{\link{Style}} that are selected by specifying the \code{format}, \code{brightness}, and \code{color.mode} parameters. See \code{\link{PaletteOfStyles}} for more details.} \item{frame}{an environment to use as the evaluation frame for the \code{print/show/str}, calls and for \code{diffObj}, the evaluation frame for the \code{diffPrint} / \code{diffStr} calls. Defaults to the return value of \code{\link{par_frame}}.} \item{interactive}{TRUE or FALSE whether the function is being run in interactive mode, defaults to the return value of \code{\link{interactive}}. If in interactive mode, pager will be used if \code{pager} is \dQuote{auto}, and if ANSI styles are not supported and \code{style} is \dQuote{auto}, output will be send to viewer/browser as HTML.} \item{term.colors}{integer(1L) how many ANSI colors are supported by the terminal. This variable is provided for when \code{\link[=num_colors]{crayon::num_colors}} does not properly detect how many ANSI colors are supported by your terminal. Defaults to return value of \code{\link[=num_colors]{crayon::num_colors}} and should be 8 or 256 to allow ANSI colors, or any other number to disallow them. This only impacts output format selection when \code{style} and \code{format} are both set to \dQuote{auto}.} \item{tar.banner}{character(1L), language, or NULL, used to generate the text to display ahead of the diff section representing the target output. If NULL will use the deparsed \code{target} expression, if language, will use the language as it would the \code{target} expression, if character(1L), will use the string with no modifications. The language mode is provided because \code{diffStr} modifies the expression prior to display (e.g. by wrapping it in a call to \code{str}). Note that it is possible in some cases that the substituted value of \code{target} actually is character(1L), but if you provide a character(1L) value here it will be assumed you intend to use that value literally.} \item{cur.banner}{character(1L) like \code{tar.banner}, but for \code{current}} \item{strip.sgr}{TRUE, FALSE, or NULL (default), whether to strip ANSI CSI SGR sequences prior to comparison and for display of diff. If NULL, resolves to TRUE if `style` resolves to an ANSI formatted diff, and FALSE otherwise. The default behavior is to avoid confusing diffs where the original SGR and the SGR added by the diff are mixed together.} \item{sgr.supported}{TRUE, FALSE, or NULL (default), whether to assume the standard output device supports ANSI CSI SGR sequences. If TRUE, strings will be manipulated accounting for the SGR sequences. If NULL, resolves to TRUE if `style` resolves to an ANSI formatted diff, and to `crayon::has_color()` otherwise. This only controls how the strings are manipulated, not whether SGR is added to format the diff, which is controlled by the `style` parameter. This parameter is exposed for the rare cases where you might wish to control string manipulation behavior directly.} \item{extra}{list additional arguments to pass on to the functions used to create text representation of the objects to diff (e.g. \code{print}, \code{str}, etc.)} } \value{ a \code{Diff} object; see \code{\link{diffPrint}}. } \description{ Reads CSV files with \code{\link{read.csv}} and passes the resulting data frames onto \code{\link{diffPrint}}. \code{extra} values are passed as arguments are passed to both \code{read.csv} and \code{print}. To the extent you wish to use different \code{extra} arguments for each of those functions you will need to \code{read.csv} the files and pass them to \code{diffPrint} yourself. } \examples{ iris.2 <- iris iris.2$Sepal.Length[5] <- 99 f1 <- tempfile() f2 <- tempfile() write.csv(iris, f1, row.names=FALSE) write.csv(iris.2, f2, row.names=FALSE) ## `pager="off"` for CRAN compliance; you may omit in normal use diffCsv(f1, f2, pager="off") unlink(c(f1, f2)) } \seealso{ \code{\link{diffPrint}} for details on the \code{diff*} functions, \code{\link{diffObj}}, \code{\link{diffStr}}, \code{\link{diffChr}} to compare character vectors directly, \code{\link{ses}} for a minimal and fast diff } diffobj/man/ses.Rd0000755000176200001440000000242413464701604013523 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/core.R \name{ses} \alias{ses} \title{Shortest Edit Script} \usage{ ses(a, b, max.diffs = gdo("max.diffs"), warn = gdo("warn")) } \arguments{ \item{a}{character} \item{b}{character} \item{max.diffs}{integer(1L), number of \emph{differences} after which we abandon the \code{O(n^2)} diff algorithm in favor of a naive element by element comparison. Set to \code{-1L} to always stick to the original algorithm (defaults to 50000L).} \item{warn}{TRUE (default) or FALSE whether to warn if we hit `max.diffs`.} } \value{ character } \description{ Computes shortest edit script to convert \code{a} into \code{b} by removing elements from \code{a} and adding elements from \code{b}. Intended primarily for debugging or for other applications that understand that particular format. See \href{GNU diff docs}{http://www.gnu.org/software/diffutils/manual/diffutils.html#Detailed-Normal} for how to interpret the symbols. } \details{ \code{ses} will be much faster than any of the \code{\link[=diffPrint]{diff*}} methods, particularly for large inputs with limited numbers of differences. NAs are treated as the string \dQuote{NA}. Non-character inputs are coerced to character. } \examples{ ses(letters[1:3], letters[2:4]) } diffobj/man/StyleSummary.Rd0000755000176200001440000000126413201325222015373 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/styles.R \docType{class} \name{StyleSummary-class} \alias{StyleSummary-class} \alias{StyleSummary} \alias{StyleSummaryHtml-class} \alias{StyleSummaryHtml} \title{Styling Information for Summaries} \description{ Styling Information for Summaries } \section{Slots}{ \describe{ \item{\code{container}}{function applied to entire summary} \item{\code{body}}{function applied to everything except the actual map portion of the summary} \item{\code{detail}}{function applied to section showing how many deletions / insertions, etc. occurred} \item{\code{map}}{function applied to the map portion of the summary} }} diffobj/man/AlignThreshold-class.Rd0000755000176200001440000000302213201325222016721 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s4.R \docType{class} \name{AlignThreshold-class} \alias{AlignThreshold-class} \alias{AlignThreshold} \title{Controls How Lines Within a Diff Hunk Are Aligned} \description{ Controls How Lines Within a Diff Hunk Are Aligned } \section{Slots}{ \describe{ \item{\code{threshold}}{numeric(1L) between 0 and 1, what proportion of words in the lines must match in order to align them. Set to 1 to effectively turn aligning off. Defaults to 0.25.} \item{\code{min.chars}}{integer(1L) positive, minimum number of characters that must match across lines in order to align them. This requirement is in addition to \code{threshold} and helps minimize spurious alignments. Defaults to 3.} \item{\code{count.alnum.only}}{logical(1L) modifier for \code{min.chars}, whether to count alpha numeric characters only. Helps reduce spurious alignment caused by meta character sequences such as \dQuote{[[1]]} that would otherwise meet the \code{min.chars} limit} }} \examples{ a1 <- AlignThreshold(threshold=0) a2 <- AlignThreshold(threshold=1) a3 <- AlignThreshold(threshold=0, min.chars=2) ## Note how "e f g" is aligned diffChr(c("a b c e", "d e f g"), "D e f g", align=a1, pager="off") ## But now it is not diffChr(c("a b c e", "d e f g"), "D e f g", align=a2, pager="off") ## "e f" are not enough chars to align diffChr(c("a b c", "d e f"), "D e f", align=a1, pager="off") ## Override with min.chars, so now they align diffChr(c("a b c", "d e f"), "D e f", align=a3, pager="off") } diffobj/man/strip_hz_control.Rd0000755000176200001440000000223613420351310016317 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/text.R \name{strip_hz_control} \alias{strip_hz_control} \title{Replace Horizontal Spacing Control Characters} \usage{ strip_hz_control(txt, stops = 8L, sgr.supported) } \arguments{ \item{txt}{character to covert} \item{stops}{integer, what tab stops to use} \item{sgr.supported}{logical whether the current display device supports ANSI CSI SGR. See \code{\link[=diffPrint]{diff*}}'s \code{sgr.supported} parameter.} } \value{ character, `txt` with horizontal control sequences replaced. } \description{ Removes tabs, newlines, and manipulates the text so that it looks the same as it did with those horizontal control characters embedded. Currently carriage returns are also processed, but in the future they no longer will be. This function is used when the \code{convert.hz.white.space} parameter to the \code{\link[=diffPrint]{diff*}} methods is active. The term \dQuote{strip} is a misnomer that remains for legacy reasons and lazyness. } \details{ This is an internal function with exposed documentation because it is referenced in an external function's documentation. } \keyword{internal} diffobj/man/view_or_browse.Rd0000755000176200001440000000127113201325222015746 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/pager.R \name{view_or_browse} \alias{view_or_browse} \title{Invoke IDE Viewer If Available, browseURL If Not} \usage{ view_or_browse(url) } \arguments{ \item{url}{character(1L) a location containing a file to display} } \value{ the return vaue of \code{getOption("viewer")} if it is a function, or of \code{\link{browseURL}} if the viewer is not available } \description{ Use \code{getOption("viewer")} to view HTML output if it is available as per \href{https://support.rstudio.com/hc/en-us/articles/202133558-Extending-RStudio-with-the-Viewer-Pane}{RStudio}. Fallback to \code{\link{browseURL}} if not available. } diffobj/man/guides.Rd0000755000176200001440000001140513420351310014173 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/guides.R \docType{methods} \name{guides} \alias{guides} \alias{guidesPrint,} \alias{guidesStr,} \alias{guidesChr,} \alias{guidesDeparse} \alias{guidesPrint} \alias{guidesPrint,ANY,character-method} \alias{guidesStr} \alias{guidesStr,ANY,character-method} \alias{guidesChr} \alias{guidesChr,ANY,character-method} \alias{guidesDeparse,ANY,character-method} \alias{guidesFile} \alias{guidesFile,ANY,character-method} \title{Generic Methods to Implement Flexible Guide Line Computations} \usage{ guidesPrint(obj, obj.as.chr) \S4method{guidesPrint}{ANY,character}(obj, obj.as.chr) guidesStr(obj, obj.as.chr) \S4method{guidesStr}{ANY,character}(obj, obj.as.chr) guidesChr(obj, obj.as.chr) \S4method{guidesChr}{ANY,character}(obj, obj.as.chr) guidesDeparse(obj, obj.as.chr) \S4method{guidesDeparse}{ANY,character}(obj, obj.as.chr) guidesFile(obj, obj.as.chr) \S4method{guidesFile}{ANY,character}(obj, obj.as.chr) } \arguments{ \item{obj}{an R object} \item{obj.as.chr}{the character representation of \code{obj} that is used for computing the diffs} } \value{ integer containing values in \code{seq_along(obj.as.chr)} } \description{ Guides are context lines that would normally be omitted from the diff because they are too far from any differences, but provide particularly useful contextual information. Column headers are a common example. Modifying guide finding is an advanced feature intended for package developers that want special treatment for the display output of their objects. } \details{ \code{Diff} detects these important context lines by looking for patterns in the text of the diff, and then displays these lines in addition to the normal diff output. Guides are marked by a tilde in the gutter, and are typically styled differently than normal context lines, by default in grey. Guides may be far from the diff hunk they are juxtaposed to. We eschew the device of putting the guides in the hunk header as \code{git diff} does because often the column alignment of the guide line is meaningful. Guides are detected by the \code{guides*} methods documented here. Each of the \code{diff*} methods (e.g. \code{\link{diffPrint}}) has a corresponding \code{guides*} method (e.g. \code{\link{guidesPrint}}), with the exception of \code{\link{diffCsv}} since that method uses \code{diffPrint} internally. The \code{guides*} methods expect an R object as the first parameter and the captured display representation of the object in a character vector as the second. The function should then identify which elements in the character representation should be treated as guides, and should return the numeric indices for them. The original object is passed as the first argument so that the generic can dispatch on it, and so the methods may adjust their guide finding behavior to data that is easily retrievable from the object, but less so from the character representation thereof. The default method for \code{guidesPrint} has special handling for 2D objects (e.g. data frames, matrices), arrays, time series, tables, lists, and S4 objects that use the default \code{show} method. Guide finding is on a best efforts basis and may fail if your objects contain \dQuote{pathological} display representations. Since the diff will still work with failed \code{guides} finding we consider this an acceptable compromise. Guide finding is more likely to fail with nested recursive structures. \code{guidesStr} highlights top level objects. The default methods for the other \code{guide*} generics do not do anything and exist only as a mechanism for providing custom guide line methods. If you dislike the default handling you can also define your own methods for matrices, arrays, etc., or alternatively you can pass a guide finding function directly via the \code{guides} parameter to the \code{diff*} methods. If you have classed objects with special patterns you can define your own methods for them (see examples), though if your objects are S3 you will need to use \code{\link{setOldClass}} as the \code{guides*} generics are S4. } \note{ The mechanism for identifying guides will almost certainly change in the future to allow for better handling of nested guides, so if you do implement custom guideline methods do so with the understanding that they will likely be deprecated in one of the future releases. } \examples{ ## Roundabout way of suppressing guides for matrices setMethod("guidesPrint", c("matrix", "character"), function(obj, obj.as.chr) integer(0L) ) ## Special guides for "zulu" S3 objects that match lines ## starting in "zulu###" where ### is a nuber setOldClass("zulu") setMethod("guidesPrint", c("zulu", "character"), function(obj, obj.as.chr) { if(length(obj) > 20) grep("^zulu[0-9]*", obj.as.chr) else integer(0L) } ) } diffobj/man/show-DiffSummary-method.Rd0000755000176200001440000000071613201325222017400 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/summmary.R \docType{methods} \name{show,DiffSummary-method} \alias{show,DiffSummary-method} \title{Display DiffSummary Objects} \usage{ \S4method{show}{DiffSummary}(object) } \arguments{ \item{object}{a \code{DiffSummary} object} } \value{ NULL, invisbly show( summary(diffChr(letters, letters[-c(5, 15)], format="raw", pager="off")) ) } \description{ Display DiffSummary Objects } diffobj/man/finalizeHtml.Rd0000755000176200001440000000202513201325222015337 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/finalizer.R, R/s4.R, R/summmary.R \docType{methods} \name{finalizeHtml} \alias{finalizeHtml} \alias{finalizeHtml,ANY-method} \alias{finalizeHtml,Diff-method} \alias{finalizeHtml,DiffSummary-method} \title{Finalizing Methods for HTML Output} \usage{ finalizeHtml(x, ...) \S4method{finalizeHtml}{ANY}(x, x.chr, js, ...) \S4method{finalizeHtml}{Diff}(x, x.chr, ...) \S4method{finalizeHtml}{DiffSummary}(x, x.chr, ...) } \arguments{ \item{x}{object to finalize} \item{...}{arguments to pass on to methods} \item{x.chr}{character text representation of \code{x}, typically generated with the \code{as.character} method for \code{x}} \item{js}{character javascript code to append to HTML representation} } \description{ Used as the \code{finalizer} slot to \code{\link{StyleHtml}} objects to wrap character output prior to output to device. Used primarily by styles that output to HTML to properly configure HTML page structure, including injecting JS, CSS, etc.. } diffobj/man/diffChr.Rd0000755000176200001440000004433113466140042014274 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/diff.R \docType{methods} \name{diffChr} \alias{diffChr} \alias{diffChr,ANY-method} \title{Diff Character Vectors Element By Element} \usage{ diffChr(target, current, ...) \S4method{diffChr}{ANY}(target, current, mode = gdo("mode"), context = gdo("context"), format = gdo("format"), brightness = gdo("brightness"), color.mode = gdo("color.mode"), word.diff = gdo("word.diff"), pager = gdo("pager"), guides = gdo("guides"), trim = gdo("trim"), rds = gdo("rds"), unwrap.atomic = gdo("unwrap.atomic"), max.diffs = gdo("max.diffs"), disp.width = gdo("disp.width"), ignore.white.space = gdo("ignore.white.space"), convert.hz.white.space = gdo("convert.hz.white.space"), tab.stops = gdo("tab.stops"), line.limit = gdo("line.limit"), hunk.limit = gdo("hunk.limit"), align = gdo("align"), style = gdo("style"), palette.of.styles = gdo("palette"), frame = par_frame(), interactive = gdo("interactive"), term.colors = gdo("term.colors"), tar.banner = NULL, cur.banner = NULL, strip.sgr = gdo("strip.sgr"), sgr.supported = gdo("sgr.supported"), extra = list()) } \arguments{ \item{target}{the reference object} \item{current}{the object being compared to \code{target}} \item{...}{unused, for compatibility of methods with generics} \item{mode}{character(1L), one of: \itemize{ \item \dQuote{unified}: diff mode used by \code{git diff} \item \dQuote{sidebyside}: line up the differences side by side \item \dQuote{context}: show the target and current hunks in their entirety; this mode takes up a lot of screen space but makes it easier to see what the objects actually look like \item \dQuote{auto}: default mode; pick one of the above, will favor \dQuote{sidebyside} unless \code{getOption("width")} is less than 80, or in \code{diffPrint} and objects are dimensioned and do not fit side by side, or in \code{diffChr}, \code{diffDeparse}, \code{diffFile} and output does not fit in side by side without wrapping }} \item{context}{integer(1L) how many lines of context are shown on either side of differences (defaults to 2). Set to \code{-1L} to allow as many as there are. Set to \dQuote{auto} to display as many as 10 lines or as few as 1 depending on whether total screen lines fit within the number of lines specified in \code{line.limit}. Alternatively pass the return value of \code{\link{auto_context}} to fine tune the parameters of the auto context calculation.} \item{format}{character(1L), controls the diff output format, one of: \itemize{ \item \dQuote{auto}: to select output format based on terminal capabilities; will attempt to use one of the ANSI formats if they appear to be supported, and if not or if you are in the Rstudio console it will attempt to use HTML and browser output if in interactive mode. \item \dQuote{raw}: plain text \item \dQuote{ansi8}: color and format diffs using basic ANSI escape sequences \item \dQuote{ansi256}: like \dQuote{ansi8}, except using the full range of ANSI formatting options \item \dQuote{html}: color and format using HTML markup; the resulting string is processed with \code{\link{enc2utf8}} when output as a full web page (see docs for \code{html.output} under \code{\link{Style}}). } Defaults to \dQuote{auto}. See \code{palette.of.styles} for details on customization, \code{\link{style}} for full control of output format. See `pager` parameter for more discussion of Rstudio behavior.} \item{brightness}{character, one of \dQuote{light}, \dQuote{dark}, \dQuote{neutral}, useful for adjusting color scheme to light or dark terminals. \dQuote{neutral} by default. See \code{\link{PaletteOfStyles}} for details and limitations. Advanced: you may specify brightness as a function of \code{format}. For example, if you typically wish to use a \dQuote{dark} color scheme, except for when in \dQuote{html} format when you prefer the \dQuote{light} scheme, you may use \code{c("dark", html="light")} as the value for this parameter. This is particularly useful if \code{format} is set to \dQuote{auto} or if you want to specify a default value for this parameter via options. Any names you use should correspond to a \code{format}. You must have one unnamed value which will be used as the default for all \code{format}s that are not explicitly specified.} \item{color.mode}{character, one of \dQuote{rgb} or \dQuote{yb}. Defaults to \dQuote{yb}. \dQuote{yb} stands for \dQuote{Yellow-Blue} for color schemes that rely primarily on those colors to style diffs. Those colors can be easily distinguished by individuals with limited red-green color sensitivity. See \code{\link{PaletteOfStyles}} for details and limitations. Also offers the same advanced usage as the \code{brightness} parameter.} \item{word.diff}{TRUE (default) or FALSE, whether to run a secondary word diff on the in-hunk differences. For atomic vectors setting this to FALSE could make the diff \emph{slower} (see the \code{unwrap.atomic} parameter). For other uses, particularly with \code{\link{diffChr}} setting this to FALSE can substantially improve performance.} \item{pager}{one of \dQuote{auto} (default), \dQuote{on}, \dQuote{off}, a \code{\link{Pager}} object, or a list; controls whether and how a pager is used to display the diff output. If you require a particular pager behavior you must use a \code{\link{Pager}} object, or \dQuote{off} to turn off the pager. All other settings will interact with other parameters such as \code{format}, \code{style}, as well as with your system capabilities in order to select the pager expected to be most useful. \dQuote{auto} and \dQuote{on} are the same, except that in non-interactive mode \dQuote{auto} is equivalent to \dQuote{off}. \dQuote{off} will always send output to the console. If \dQuote{on}, whether the output actually gets routed to the pager depends on the pager \code{threshold} setting (see \code{\link{Pager}}). The default behavior is to use the pager associated with the \code{Style} object. The \code{Style} object is itself is determined by the \code{format} or \code{style} parameters. Depending on your system configuration different styles and corresponding pagers will get selected, unless you specify a \code{Pager} object directly. On a system with a system pager that supports ANSI CSI SGR colors, the pager will only trigger if the output is taller than one window. If the system pager is not known to support ANSI colors then the output will be sent as HTML to the IDE viewer if available or to the web browser if not. Even though Rstudio now supports ANSI CSI SGR at the console output is still formatted as HTML and sent to the IDE viewer. Partly this is for continuity of behavior, but also because the default Rstudio pager does not support ANSI CSI SGR, at least as of this writing. If \code{pager} is a list, then the same as with \dQuote{on}, except that the \code{Pager} object associated with the selected \code{Style} object is re-instantiated with the union of the list elements and the existing settings of that \code{Pager}. The list should contain named elements that correspond to the \code{\link{Pager}} instantiation parameters. The names must be specified in full as partial parameter matching will not be carried out because the pager is re-instantiated with \code{\link{new}}. See \code{\link{Pager}}, \code{\link{Style}}, and \code{\link{PaletteOfStyles}} for more details and for instructions on how to modify the default behavior.} \item{guides}{TRUE (default), FALSE, or a function that accepts at least two arguments and requires no more than two arguments. Guides are additional context lines that are not strictly part of a hunk, but provide important contextual data (e.g. column headers). If TRUE, the context lines are shown in addition to the normal diff output, typically in a different color to indicate they are not part of the hunk. If a function, the function should accept as the first argument the object being diffed, and the second the character representation of the object. The function should return the indices of the elements of the character representation that should be treated as guides. See \code{\link{guides}} for more details.} \item{trim}{TRUE (default), FALSE, or a function that accepts at least two arguments and requires no more than two arguments. Function should compute for each line in captured output what portion of those lines should be diffed. By default, this is used to remove row meta data differences (e.g. \code{[1,]}) so they alone do not show up as differences in the diff. See \code{\link{trim}} for more details.} \item{rds}{TRUE (default) or FALSE, if TRUE will check whether \code{target} and/or \code{current} point to a file that can be read with \code{\link{readRDS}} and if so, loads the R object contained in the file and carries out the diff on the object instead of the original argument. Currently there is no mechanism for specifying additional arguments to \code{readRDS}} \item{unwrap.atomic}{TRUE (default) or FALSE. Relevant primarily for \code{diffPrint}, if TRUE, and \code{word.diff} is also TRUE, and both \code{target} and \code{current} are \emph{unnamed} one-dimension atomics , the vectors are unwrapped and diffed element by element, and then re-wrapped. Since \code{diffPrint} is fundamentally a line diff, the re-wrapped lines are lined up in a manner that is as consistent as possible with the unwrapped diff. Lines that contain the location of the word differences will be paired up. Since the vectors may well be wrapped with different periodicities this will result in lines that are paired up that look like they should not be paired up, though the locations of the differences should be. If is entirely possible that setting this parameter to FALSE will result in a slower diff. This happens if two vectors are actually fairly similar, but their line representations are not. For example, in comparing \code{1:100} to \code{c(100, 1:99)}, there is really only one difference at the \dQuote{word} level, but every screen line is different. \code{diffChr} will also do the unwrapping if it is given a character vector that contains output that looks like the atomic vectors described above. This is a bug, but as the functionality could be useful when diffing e.g. \code{capture.output} data, we now declare it a feature.} \item{max.diffs}{integer(1L), number of \emph{differences} after which we abandon the \code{O(n^2)} diff algorithm in favor of a naive element by element comparison. Set to \code{-1L} to always stick to the original algorithm (defaults to 50000L).} \item{disp.width}{integer(1L) number of display columns to take up; note that in \dQuote{sidebyside} \code{mode} the effective display width is half this number (set to 0L to use default widths which are \code{getOption("width")} for normal styles and \code{80L} for HTML styles. Future versions of \code{diffobj} may change this to larger values for two dimensional objects for better diffs (see details).} \item{ignore.white.space}{TRUE or FALSE, whether to consider differences in horizontal whitespace (i.e. spaces and tabs) as differences (defaults to TRUE).} \item{convert.hz.white.space}{TRUE or FALSE, whether modify input strings that contain tabs and carriage returns in such a way that they display as they would \bold{with} those characters, but without using those characters (defaults to TRUE). The conversion assumes that tab stops are spaced evenly eight characters apart on the terminal. If this is not the case you may specify the tab stops explicitly with \code{tab.stops}.} \item{tab.stops}{integer, what tab stops to use when converting hard tabs to spaces. If not integer will be coerced to integer (defaults to 8L). You may specify more than one tab stop. If display width exceeds that addressable by your tab stops the last tab stop will be repeated.} \item{line.limit}{integer(2L) or integer(1L), if length 1 how many lines of output to show, where \code{-1} means no limit. If length 2, the first value indicates the threshold of screen lines to begin truncating output, and the second the number of lines to truncate to, which should be fewer than the threshold. Note that this parameter is implemented on a best-efforts basis and should not be relied on to produce the exact number of lines requested. In particular do not expect it to work well for for values small enough that the banner portion of the diff would have to be trimmed. If you want a specific number of lines use \code{[} or \code{head} / \code{tail}. One advantage of \code{line.limit} over these other options is that you can combine it with \code{context="auto"} and auto \code{max.level} selection (the latter for \code{diffStr}), which allows the diff to dynamically adjust to make best use of the available display lines. \code{[}, \code{head}, and \code{tail} just subset the text of the output.} \item{hunk.limit}{integer(2L) or integer (1L), how many diff hunks to show. Behaves similarly to \code{line.limit}. How many hunks are in a particular diff is a function of how many differences, and also how much \code{context} is used since context can cause two hunks to bleed into each other and become one.} \item{align}{numeric(1L) between 0 and 1, proportion of words in a line of \code{target} that must be matched in a line of \code{current} in the same hunk for those lines to be paired up when displayed (defaults to 0.25), or an \code{\link{AlignThreshold}} object. Set to \code{1} to turn off alignment which will cause all lines in a hunk from \code{target} to show up first, followed by all lines from \code{current}. Note that in order to be aligned lines must meet the threshold and have at least 3 matching alphanumeric characters (see \code{\link{AlignThreshold}} for details).} \item{style}{\dQuote{auto}, a \code{\link{Style}} object, or a list. \dQuote{auto} by default. If a \code{Style} object, will override the the \code{format}, \code{brightness}, and \code{color.mode} parameters. The \code{Style} object provides full control of diff output styling. If a list, then the same as \dQuote{auto}, except that if the auto-selected \code{Style} requires instantiation (see \code{\link{PaletteOfStyles}}), then the list contents will be used as arguments when instantiating the style object. See \code{\link{Style}} for more details, in particular the examples.} \item{palette.of.styles}{\code{\link{PaletteOfStyles}} object; advanced usage, contains all the \code{\link{Style}} objects or \dQuote{classRepresentation} objects extending \code{\link{Style}} that are selected by specifying the \code{format}, \code{brightness}, and \code{color.mode} parameters. See \code{\link{PaletteOfStyles}} for more details.} \item{frame}{an environment to use as the evaluation frame for the \code{print/show/str}, calls and for \code{diffObj}, the evaluation frame for the \code{diffPrint} / \code{diffStr} calls. Defaults to the return value of \code{\link{par_frame}}.} \item{interactive}{TRUE or FALSE whether the function is being run in interactive mode, defaults to the return value of \code{\link{interactive}}. If in interactive mode, pager will be used if \code{pager} is \dQuote{auto}, and if ANSI styles are not supported and \code{style} is \dQuote{auto}, output will be send to viewer/browser as HTML.} \item{term.colors}{integer(1L) how many ANSI colors are supported by the terminal. This variable is provided for when \code{\link[=num_colors]{crayon::num_colors}} does not properly detect how many ANSI colors are supported by your terminal. Defaults to return value of \code{\link[=num_colors]{crayon::num_colors}} and should be 8 or 256 to allow ANSI colors, or any other number to disallow them. This only impacts output format selection when \code{style} and \code{format} are both set to \dQuote{auto}.} \item{tar.banner}{character(1L), language, or NULL, used to generate the text to display ahead of the diff section representing the target output. If NULL will use the deparsed \code{target} expression, if language, will use the language as it would the \code{target} expression, if character(1L), will use the string with no modifications. The language mode is provided because \code{diffStr} modifies the expression prior to display (e.g. by wrapping it in a call to \code{str}). Note that it is possible in some cases that the substituted value of \code{target} actually is character(1L), but if you provide a character(1L) value here it will be assumed you intend to use that value literally.} \item{cur.banner}{character(1L) like \code{tar.banner}, but for \code{current}} \item{strip.sgr}{TRUE, FALSE, or NULL (default), whether to strip ANSI CSI SGR sequences prior to comparison and for display of diff. If NULL, resolves to TRUE if `style` resolves to an ANSI formatted diff, and FALSE otherwise. The default behavior is to avoid confusing diffs where the original SGR and the SGR added by the diff are mixed together.} \item{sgr.supported}{TRUE, FALSE, or NULL (default), whether to assume the standard output device supports ANSI CSI SGR sequences. If TRUE, strings will be manipulated accounting for the SGR sequences. If NULL, resolves to TRUE if `style` resolves to an ANSI formatted diff, and to `crayon::has_color()` otherwise. This only controls how the strings are manipulated, not whether SGR is added to format the diff, which is controlled by the `style` parameter. This parameter is exposed for the rare cases where you might wish to control string manipulation behavior directly.} \item{extra}{list additional arguments to pass on to the functions used to create text representation of the objects to diff (e.g. \code{print}, \code{str}, etc.)} } \value{ a \code{Diff} object; see \code{\link{diffPrint}}. } \description{ Will perform the diff on the actual string values of the character vectors instead of capturing the printed screen output. Each vector element is treated as a line of text. NA elements are treated as the string \dQuote{NA}. Non character inputs are coerced to character. } \examples{ ## `pager="off"` for CRAN compliance; you may omit in normal use diffChr(LETTERS[1:5], LETTERS[2:6], pager="off") } \seealso{ \code{\link{diffPrint}} for details on the \code{diff*} functions, \code{\link{diffObj}}, \code{\link{diffStr}}, \code{\link{diffDeparse}} to compare deparsed objects, \code{\link{ses}} for a minimal and fast diff } diffobj/man/has_Rdiff.Rd0000755000176200001440000000113213201325222014574 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/rdiff.R \name{has_Rdiff} \alias{has_Rdiff} \title{Attempt to Detect Whether diff Utility is Available} \usage{ has_Rdiff(test.with = tools::Rdiff) } \arguments{ \item{test.with}{function to test for diff presence with, typically Rdiff} } \value{ TRUE or FALSE } \description{ Checks whether \code{\link[=Rdiff]{tools::Rdiff}} issues a warning when running with \code{useDiff=TRUE} and if it does assumes this is because the diff utility is not available. Intended primarily for testing purposes. } \examples{ has_Rdiff() } diffobj/man/nchar_html.Rd0000755000176200001440000000132313420351310015030 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/html.R \name{nchar_html} \alias{nchar_html} \title{Count Text Characters in HTML} \usage{ nchar_html(x, ...) } \arguments{ \item{x}{character} \item{...}{unused for compatibility with internal use} } \value{ integer(length(x)) with number of characters of each element } \description{ Very simple implementation that will fail if there are any \dQuote{>} in the HTML that are not closing tags, and assumes that HTML entities are all one character wide. Also, spaces are counted as one width each because the HTML output is intended to be displayed inside \code{
} tags.
}
\examples{
nchar_html("hello")
}
diffobj/man/summary-Diff-method.Rd0000755000176200001440000000271213420351310016535 0ustar  liggesusers% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/summmary.R
\docType{methods}
\name{summary,Diff-method}
\alias{summary,Diff-method}
\title{Summary Method for Diff Objects}
\usage{
\S4method{summary}{Diff}(object, scale.threshold = 0.1,
  max.lines = 50L, width = getOption("width"), ...)
}
\arguments{
\item{object}{at \code{Diff} object}

\item{scale.threshold}{numeric(1L) between 0 and 1, how much distortion to
allow when creating the summary map, where 0 is none and 1 is as much as
needed to fit under \code{max.lines}, defaults to 0.1}

\item{max.lines}{integer(1L) how many lines to allow for the summary map,
defaults to 50}

\item{width}{integer(1L) how many columns wide the output should be, defaults
to \code{getOption("width")}}

\item{...}{unused, for compatibility with generic}
}
\value{
a \code{DiffSummary} object
## `pager="off"` for CRAN compliance; you may omit in normal use
summary(diffChr(letters, letters[-c(5, 15)], format="raw", pager="off"))
}
\description{
Provides high level count of insertions, deletions, and matches, as well as a
\dQuote{map} of where the differences are.
}
\details{
Sequences of single operations (e.g. "DDDDD") are compressed provided that
compressing them does not distort the relative size of the sequence relative
to the longest such sequence in the map by more than \code{scale.threshold}.
Since length 1 sequences cannot be further compressed \code{scale.threshold}
does not apply to them.
}
diffobj/man/as.character-MyersMbaSes-method.Rd0000755000176200001440000000103713201325222020717 0ustar  liggesusers% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/core.R
\docType{methods}
\name{as.character,MyersMbaSes-method}
\alias{as.character,MyersMbaSes-method}
\title{Generate a character representation of Shortest Edit Sequence}
\usage{
\S4method{as.character}{MyersMbaSes}(x, ...)
}
\arguments{
\item{x}{S4 object of class \code{MyersMbaSes}}

\item{...}{unused}
}
\value{
character vector
}
\description{
Generate a character representation of Shortest Edit Sequence
}
\seealso{
\code{\link{ses}}
}
\keyword{internal}
diffobj/man/as.character-DiffSummary-method.Rd0000755000176200001440000000124113201325222020750 0ustar  liggesusers% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/summmary.R
\docType{methods}
\name{as.character,DiffSummary-method}
\alias{as.character,DiffSummary-method}
\title{Generate Character Representation of DiffSummary Object}
\usage{
\S4method{as.character}{DiffSummary}(x, ...)
}
\arguments{
\item{x}{a \code{DiffSummary} object}

\item{...}{not used, for compatibility with generic}
}
\value{
the summary as a character vector intended to be \code{cat}ed to
  terminal
}
\description{
Generate Character Representation of DiffSummary Object
}
\examples{
as.character(
  summary(diffChr(letters, letters[-c(5, 15)], format="raw", pager="off"))
)
}
diffobj/man/diffobj-package.Rd0000755000176200001440000000053513201325222015711 0ustar  liggesusers% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/diff.R
\docType{package}
\name{diffobj-package}
\alias{diffobj-package}
\title{Diffs for R Objects}
\description{
Generate a colorized diff of two R objects for an intuitive visualization of
their differences.  See `vignette(package="diffobj", "diffobj")` for details.
}
diffobj/man/Diff-class.Rd0000755000176200001440000000054513201325222014671 0ustar  liggesusers% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/s4.R
\docType{class}
\name{Diff-class}
\alias{Diff-class}
\title{Diff Result Object}
\description{
Return value for the \code{\link[=diffPrint]{diff*}} methods.  Has
\code{show}, \code{as.character}, \code{summmary}, \code{[}, \code{head},
\code{tail}, and \code{any} methods.
}
diffobj/man/StyleText.Rd0000755000176200001440000000275313201325222014666 0ustar  liggesusers% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/styles.R
\docType{class}
\name{StyleText-class}
\alias{StyleText-class}
\alias{StyleText}
\title{Character Tokens Used in Diffs}
\arguments{
\item{gutter.insert}{character(1L) text to use as visual cue to indicate
whether a diff line is an insertion, defaults to \dQuote{> }}

\item{gutter.insert.ctd}{character(1L) if a diff line is wrapped, the
visual cue shifts to this character to indicate wrapping occured}

\item{gutter.delete}{character(1L) see \code{gutter.insert} above}

\item{gutter.delete.ctd}{character(1L) see \code{gutter.insert.ctd} above}

\item{gutter.match}{character(1L) see \code{gutter.insert} above}

\item{gutter.match.ctd}{character(1L) see \code{gutter.insert.ctd} above}

\item{gutter.guide}{character(1L) see \code{gutter.insert} above}

\item{gutter.guide.ctd}{character(1L) see \code{gutter.insert.ctd} above}

\item{gutter.fill}{character(1L) see \code{gutter.insert} above}

\item{gutter.fill.ctd}{character(1L) see \code{gutter.insert.ctd} above}

\item{gutter.pad}{character(1L) separator between gutter characters and the
rest of a line in a diff}

\item{pad.col}{character(1L) separator between columns in side by side mode}
}
\value{
a StyleText S4 object
}
\description{
Various character tokens are used throughout diffs to provide visual cues.
For example, gutters will contain characters that denote deletions and
insertions (\code{<} and \code{>} by default).
}
\seealso{
\code{\link{Style}}
}
diffobj/man/diffFile.Rd0000755000176200001440000004431713466140042014443 0ustar  liggesusers% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/diff.R
\docType{methods}
\name{diffFile}
\alias{diffFile}
\alias{diffFile,ANY-method}
\title{Diff Files}
\usage{
diffFile(target, current, ...)

\S4method{diffFile}{ANY}(target, current, mode = gdo("mode"),
  context = gdo("context"), format = gdo("format"),
  brightness = gdo("brightness"), color.mode = gdo("color.mode"),
  word.diff = gdo("word.diff"), pager = gdo("pager"),
  guides = gdo("guides"), trim = gdo("trim"), rds = gdo("rds"),
  unwrap.atomic = gdo("unwrap.atomic"), max.diffs = gdo("max.diffs"),
  disp.width = gdo("disp.width"),
  ignore.white.space = gdo("ignore.white.space"),
  convert.hz.white.space = gdo("convert.hz.white.space"),
  tab.stops = gdo("tab.stops"), line.limit = gdo("line.limit"),
  hunk.limit = gdo("hunk.limit"), align = gdo("align"),
  style = gdo("style"), palette.of.styles = gdo("palette"),
  frame = par_frame(), interactive = gdo("interactive"),
  term.colors = gdo("term.colors"), tar.banner = NULL,
  cur.banner = NULL, strip.sgr = gdo("strip.sgr"),
  sgr.supported = gdo("sgr.supported"), extra = list())
}
\arguments{
\item{target}{character(1L) or file connection with read capability; if
character should point to a text file}

\item{current}{like \code{target}}

\item{...}{unused, for compatibility of methods with generics}

\item{mode}{character(1L), one of:
\itemize{
  \item \dQuote{unified}: diff mode used by \code{git diff}
  \item \dQuote{sidebyside}: line up the differences side by side
  \item \dQuote{context}: show the target and current hunks in their
    entirety; this mode takes up a lot of screen space but makes it easier
    to see what the objects actually look like
  \item \dQuote{auto}: default mode; pick one of the above, will favor
    \dQuote{sidebyside} unless \code{getOption("width")} is less than 80,
    or in \code{diffPrint} and objects are dimensioned and do not fit side
    by side, or in \code{diffChr}, \code{diffDeparse}, \code{diffFile} and
    output does not fit in side by side without wrapping
}}

\item{context}{integer(1L) how many lines of context are shown on either side
of differences (defaults to 2).  Set to \code{-1L} to allow as many as
there are.  Set to \dQuote{auto}  to display as many as 10 lines or as few
as 1 depending on whether total screen lines fit within the number of lines
specified in \code{line.limit}.  Alternatively pass the return value of
\code{\link{auto_context}} to fine tune the parameters of the auto context
calculation.}

\item{format}{character(1L), controls the diff output format, one of:
\itemize{
  \item \dQuote{auto}: to select output format based on terminal
    capabilities; will attempt to use one of the ANSI formats if they
    appear to be supported, and if not or if you are in the Rstudio console
    it will attempt to use HTML and browser output if in interactive mode.
  \item \dQuote{raw}: plain text
  \item \dQuote{ansi8}: color and format diffs using basic ANSI escape
    sequences
  \item \dQuote{ansi256}: like \dQuote{ansi8}, except using the full range
    of ANSI formatting options
  \item \dQuote{html}: color and format using HTML markup; the resulting
    string is processed with \code{\link{enc2utf8}} when output as a full
    web page (see docs for \code{html.output} under \code{\link{Style}}).
}
Defaults to \dQuote{auto}.  See \code{palette.of.styles} for details
on customization, \code{\link{style}} for full control of output format.
See `pager` parameter for more discussion of Rstudio behavior.}

\item{brightness}{character, one of \dQuote{light}, \dQuote{dark},
\dQuote{neutral}, useful for adjusting color scheme to light or dark
terminals.  \dQuote{neutral} by default.  See \code{\link{PaletteOfStyles}}
for details and limitations.  Advanced: you may specify brightness as a
function of \code{format}.  For example, if you typically wish to use a
\dQuote{dark} color scheme, except for when in \dQuote{html} format when
you prefer the \dQuote{light} scheme, you may use
\code{c("dark", html="light")} as the value for this parameter.  This is
particularly useful if \code{format} is set to \dQuote{auto} or if you
want to specify a default value for this parameter via options.  Any names
you use should correspond to a \code{format}.  You must have one unnamed
value which will be used as the default for all \code{format}s that are
not explicitly specified.}

\item{color.mode}{character, one of \dQuote{rgb} or \dQuote{yb}.
Defaults to \dQuote{yb}.  \dQuote{yb} stands for \dQuote{Yellow-Blue} for
color schemes that rely primarily on those colors to style diffs.
Those colors can be easily distinguished by individuals with
limited red-green color sensitivity.  See \code{\link{PaletteOfStyles}} for
details and limitations.  Also offers the same advanced usage as the
\code{brightness} parameter.}

\item{word.diff}{TRUE (default) or FALSE, whether to run a secondary word
diff on the in-hunk differences.  For atomic vectors setting this to
FALSE could make the diff \emph{slower} (see the \code{unwrap.atomic}
parameter).  For other uses, particularly with \code{\link{diffChr}}
setting this to FALSE can substantially improve performance.}

\item{pager}{one of \dQuote{auto} (default), \dQuote{on},
  \dQuote{off}, a \code{\link{Pager}} object, or a list; controls whether and
  how a pager is used to display the diff output.  If you require a
  particular pager behavior you must use a \code{\link{Pager}}
  object, or \dQuote{off} to turn off the pager.  All other settings will
  interact with other parameters such as \code{format}, \code{style}, as well
  as with your system capabilities in order to select the pager expected to
  be most useful.

  \dQuote{auto} and \dQuote{on} are the same, except that in non-interactive
  mode \dQuote{auto} is equivalent to \dQuote{off}.  \dQuote{off} will always
  send output to the console.  If \dQuote{on}, whether the output
  actually gets routed to the pager depends on the pager \code{threshold}
  setting (see \code{\link{Pager}}).  The default behavior is to use the
  pager associated with the \code{Style} object.  The \code{Style} object is
  itself is determined by the \code{format} or \code{style} parameters.

  Depending on your system configuration different styles and corresponding
  pagers will get selected, unless you specify a \code{Pager} object
  directly.  On a system with a system pager that supports ANSI CSI SGR
  colors, the pager will only trigger if the output is taller than one
  window.  If the system pager is not known to support ANSI colors then the
  output will be sent as HTML to the IDE viewer if available or to the web
  browser if not.  Even though Rstudio now supports ANSI CSI SGR at the
  console output is still formatted as HTML and sent to the IDE viewer.
  Partly this is for continuity of behavior, but also because the default
  Rstudio pager does not support ANSI CSI SGR, at least as of this writing.

  If \code{pager} is a list, then the same as with \dQuote{on}, except that
  the \code{Pager} object associated with the selected \code{Style} object is
  re-instantiated with the union of the list elements and the existing
  settings of that \code{Pager}.  The list should contain named elements that
  correspond to the \code{\link{Pager}} instantiation parameters.  The names
  must be specified in full as partial parameter matching will not be carried
  out because the pager is re-instantiated with \code{\link{new}}.

  See \code{\link{Pager}}, \code{\link{Style}}, and
  \code{\link{PaletteOfStyles}} for more details and for instructions on how
  to modify the default behavior.}

\item{guides}{TRUE (default), FALSE, or a function that accepts at least two
arguments and requires no more than two arguments.  Guides
are additional context lines that are not strictly part of a hunk, but
provide important contextual data (e.g. column headers).  If TRUE, the
context lines are shown in addition to the normal diff output, typically
in a different color to indicate they are not part of the hunk.  If a
function, the function should accept as the first argument the object
being diffed, and the second the character representation of the object.
The function should return the indices of the elements of the
character representation that should be treated as guides.  See
\code{\link{guides}} for more details.}

\item{trim}{TRUE (default), FALSE, or a function that accepts at least two
arguments and requires no more than two arguments.  Function should compute
for each line in captured output what portion of those lines should be
diffed.  By default, this is used to remove row meta data differences
(e.g. \code{[1,]}) so they alone do not show up as differences in the
diff.  See \code{\link{trim}} for more details.}

\item{rds}{TRUE (default) or FALSE, if TRUE will check whether
\code{target} and/or \code{current} point to a file that can be read with
\code{\link{readRDS}} and if so, loads the R object contained in the file
and carries out the diff on the object instead of the original argument.
Currently there is no mechanism for specifying additional arguments to
\code{readRDS}}

\item{unwrap.atomic}{TRUE (default) or FALSE.  Relevant primarily for
\code{diffPrint}, if TRUE, and \code{word.diff} is also TRUE, and both
\code{target} and \code{current} are \emph{unnamed} one-dimension atomics ,
the vectors are unwrapped and diffed element by element, and then
re-wrapped.  Since \code{diffPrint} is fundamentally a line diff, the
re-wrapped lines are lined up in a manner that is as consistent as possible
with the unwrapped diff.  Lines that contain the location of the word
differences will be paired up.  Since the vectors may well be wrapped with
different periodicities this will result in lines that are paired up that
look like they should not be paired up, though the locations of the
differences should be.  If is entirely possible that setting this parameter
to FALSE will result in a slower diff.  This happens if two vectors are
actually fairly similar, but their line representations are not.  For
example, in comparing \code{1:100} to \code{c(100, 1:99)}, there is really
only one difference at the \dQuote{word} level, but every screen line is
different.  \code{diffChr} will also do the unwrapping if it is given a
character vector that contains output that looks like the atomic vectors
described above.  This is a bug, but as the functionality could be useful
when diffing e.g. \code{capture.output} data, we now declare it a feature.}

\item{max.diffs}{integer(1L), number of \emph{differences} after which we
abandon the \code{O(n^2)} diff algorithm in favor of a naive element by
element comparison. Set to \code{-1L} to always stick to the original
algorithm (defaults to 50000L).}

\item{disp.width}{integer(1L) number of display columns to take up; note that
in \dQuote{sidebyside} \code{mode} the effective display width is half this
number (set to 0L to use default widths which are \code{getOption("width")}
for normal styles and \code{80L} for HTML styles.  Future versions of
\code{diffobj} may change this to larger values for two dimensional objects
for better diffs (see details).}

\item{ignore.white.space}{TRUE or FALSE, whether to consider differences in
horizontal whitespace (i.e. spaces and tabs) as differences (defaults to
TRUE).}

\item{convert.hz.white.space}{TRUE or FALSE, whether modify input strings
that contain tabs and carriage returns in such a way that they display as
they would \bold{with} those characters, but without using those
characters (defaults to TRUE).  The conversion assumes that tab stops are
spaced evenly eight characters apart on the terminal.  If this is not the
case you may specify the tab stops explicitly with \code{tab.stops}.}

\item{tab.stops}{integer, what tab stops to use when converting hard tabs to
spaces.  If not integer will be coerced to integer (defaults to 8L).  You
may specify more than one tab stop.  If display width exceeds that
addressable by your tab stops the last tab stop will be repeated.}

\item{line.limit}{integer(2L) or integer(1L), if length 1 how many lines of
output to show, where \code{-1} means no limit.  If length 2, the first
value indicates the threshold of screen lines to begin truncating output,
and the second the number of lines to truncate to, which should be fewer
than the threshold.  Note that this parameter is implemented on a
best-efforts basis and should not be relied on to produce the exact
number of lines requested.  In particular do not expect it to work well for
for values small enough that the banner portion of the diff would have to
be trimmed.  If you want a specific number of lines use \code{[} or
\code{head} / \code{tail}.  One advantage of \code{line.limit} over these
other options is that you can combine it with \code{context="auto"} and
auto \code{max.level} selection (the latter for \code{diffStr}), which
allows the diff to dynamically adjust to make best use of the available
display lines.  \code{[}, \code{head}, and \code{tail} just subset the text
of the output.}

\item{hunk.limit}{integer(2L) or integer (1L), how many diff hunks to show.
Behaves similarly to \code{line.limit}.  How many hunks are in a
particular diff is a function of how many differences, and also how much
\code{context} is used since context can cause two hunks to bleed into
each other and become one.}

\item{align}{numeric(1L) between 0 and 1, proportion of
words in a line of \code{target} that must be matched in a line of
\code{current} in the same hunk for those lines to be paired up when
displayed (defaults to 0.25), or an \code{\link{AlignThreshold}} object.
Set to \code{1} to turn off alignment which will cause all lines in a hunk
from \code{target} to show up first, followed by all lines from
\code{current}.  Note that in order to be aligned lines must meet the
threshold and have at least 3 matching alphanumeric characters (see
\code{\link{AlignThreshold}} for details).}

\item{style}{\dQuote{auto}, a \code{\link{Style}} object, or a list.
\dQuote{auto} by default.  If a \code{Style} object, will override the
the \code{format}, \code{brightness}, and \code{color.mode} parameters.
The \code{Style} object provides full control of diff output styling.
If a list, then the same as \dQuote{auto}, except that if the auto-selected
\code{Style} requires instantiation (see \code{\link{PaletteOfStyles}}),
then the list contents will be used as arguments when instantiating the
style object.  See \code{\link{Style}} for more details, in particular the
examples.}

\item{palette.of.styles}{\code{\link{PaletteOfStyles}} object; advanced
usage, contains all the \code{\link{Style}} objects or
\dQuote{classRepresentation} objects extending \code{\link{Style}} that are
selected by specifying the \code{format}, \code{brightness}, and
\code{color.mode} parameters.  See \code{\link{PaletteOfStyles}} for more
details.}

\item{frame}{an environment to use as the evaluation frame for the
\code{print/show/str}, calls and for \code{diffObj}, the evaluation frame
for the \code{diffPrint} / \code{diffStr} calls.  Defaults to the return
value of \code{\link{par_frame}}.}

\item{interactive}{TRUE or FALSE whether the function is being run in
interactive mode, defaults to the return value of
\code{\link{interactive}}.  If in interactive mode, pager will be used if
\code{pager} is \dQuote{auto}, and if ANSI styles are not supported and
\code{style} is \dQuote{auto}, output will be send to viewer/browser as
HTML.}

\item{term.colors}{integer(1L) how many ANSI colors are supported by the
terminal.  This variable is provided for when
\code{\link[=num_colors]{crayon::num_colors}} does not properly detect how
many ANSI colors are supported by your terminal. Defaults to return value
of \code{\link[=num_colors]{crayon::num_colors}} and should be 8 or 256 to
allow ANSI colors, or any other number to disallow them.  This only
impacts output format selection when \code{style} and \code{format} are
both set to \dQuote{auto}.}

\item{tar.banner}{character(1L), language, or NULL, used to generate the
text to display ahead of the diff section representing the target output.
If NULL will use the deparsed \code{target} expression, if language, will
use the language as it would the \code{target} expression, if
character(1L), will use the string with no modifications.  The language
mode is provided because \code{diffStr} modifies the expression prior to
display (e.g. by wrapping it in a call to \code{str}).  Note that it is
possible in some cases that the substituted value of \code{target} actually
is character(1L), but if you provide a character(1L) value here it will be
assumed you intend to use that value literally.}

\item{cur.banner}{character(1L) like \code{tar.banner}, but for
\code{current}}

\item{strip.sgr}{TRUE, FALSE, or NULL (default), whether to strip ANSI CSI
SGR sequences prior to comparison and for display of diff.  If NULL,
resolves to TRUE if `style` resolves to an ANSI formatted diff, and
FALSE otherwise.  The default behavior is to avoid confusing diffs where
the original SGR and the SGR added by the diff are mixed together.}

\item{sgr.supported}{TRUE, FALSE, or NULL (default), whether to assume the
standard output device supports ANSI CSI SGR sequences.  If TRUE, strings
will be manipulated accounting for the SGR sequences.  If NULL,
resolves to TRUE if `style` resolves to an ANSI formatted diff, and
to `crayon::has_color()` otherwise.  This only controls how the strings are
manipulated, not whether SGR is added to format the diff, which is
controlled by the `style` parameter.  This parameter is exposed for the
rare cases where you might wish to control string manipulation behavior
directly.}

\item{extra}{list additional arguments to pass on to the functions used to
create text representation of the objects to diff (e.g. \code{print},
\code{str}, etc.)}
}
\value{
a \code{Diff} object; see \code{\link{diffPrint}}.
}
\description{
Reads text files with \code{\link{readLines}} and performs a diff on the
resulting character vectors.
}
\examples{
\dontrun{
url.base <- "https://raw.githubusercontent.com/wch/r-source"
f1 <- file.path(url.base, "29f013d1570e1df5dc047fb7ee304ff57c99ea68/README")
f2 <- file.path(url.base, "daf0b5f6c728bd3dbcd0a3c976a7be9beee731d9/README")
diffFile(f1, f2)
}
}
\seealso{
\code{\link{diffPrint}} for details on the \code{diff*} functions,
  \code{\link{diffObj}}, \code{\link{diffStr}},
  \code{\link{diffChr}} to compare character vectors directly,
  \code{\link{ses}} for a minimal and fast diff
}
diffobj/man/Extract_PaletteOfStyles.Rd0000755000176200001440000000303013201325222017467 0ustar  liggesusers% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/styles.R
\docType{methods}
\name{[<-,PaletteOfStyles-method}
\alias{[<-,PaletteOfStyles-method}
\alias{[,PaletteOfStyles,ANY,ANY,ANY-method}
\alias{[[,PaletteOfStyles-method}
\title{Extract/Replace a Style Class or Object from PaletteOfStyles}
\usage{
\S4method{[}{PaletteOfStyles}(x, i, j, ...) <- value

\S4method{[}{PaletteOfStyles,ANY,ANY,ANY}(x, i, j, ..., drop = FALSE)

\S4method{[[}{PaletteOfStyles}(x, i, j, ..., exact = TRUE)
}
\arguments{
\item{x}{a \code{\link{PaletteOfStyles}} object}

\item{i}{numeric, or character corresponding to a valid style \code{format}}

\item{j}{numeric, or character corresponding to a valid style
\code{brightness}}

\item{...}{pass a numeric or character corresponding to a valid
\code{color.mode}}

\item{value}{a \emph{list} of \code{\link{Style}} class or
\code{\link{Style}} objects}

\item{drop}{TRUE or FALSE, whether to drop dimensions, defaults to FALSE,
which is different than generic}

\item{exact}{passed on to generic}
}
\value{
a \code{\link{Style}} \code{ClassRepresentation} object or
   \code{\link{Style}} object for \code{[[}, and a list of the same for
   \code{[}
}
\description{
Extract/Replace a Style Class or Object from PaletteOfStyles
}
\examples{
pal <- PaletteOfStyles()
pal[["ansi256", "light", "rgb"]]
pal["ansi256", "light", ]
pal["ansi256", "light", "rgb"] <- list(StyleAnsi8NeutralRgb())
}
\seealso{
\code{\link{diffPrint}} for explanations of \code{format},
  \code{brightness}, and \code{color.mode}
}
diffobj/man/summary-MyersMbaSes-method.Rd0000755000176200001440000000132313201325222020054 0ustar  liggesusers% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/core.R
\docType{methods}
\name{summary,MyersMbaSes-method}
\alias{summary,MyersMbaSes-method}
\title{Summary Method for Shortest Edit Path}
\usage{
\S4method{summary}{MyersMbaSes}(object, with.match = FALSE, ...)
}
\arguments{
\item{object}{the \code{diff_myers} object to display}

\item{with.match}{logical(1L) whether to show what text the edit command
refers to}

\item{...}{forwarded to the data frame print method used to actually display
the data}
}
\value{
whatever the data frame print method returns
}
\description{
Displays the data required to generate the shortest edit path for comparison
between two strings.
}
\keyword{internal}
diffobj/man/diff_myers.Rd0000755000176200001440000000234713325666164015073 0ustar  liggesusers% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/core.R
\name{diff_myers}
\alias{diff_myers}
\title{Diff two character vectors}
\usage{
diff_myers(a, b, max.diffs = 0L, warn = FALSE)
}
\arguments{
\item{a}{character}

\item{b}{character}

\item{max.diffs}{integer(1L) how many differences before giving up; set to
zero to allow as many as there are}

\item{warn}{TRUE or FALSE, whether to warn if we hit `max.diffs`.}
}
\value{
list
}
\description{
Implementation of Myer's Diff algorithm with linear space refinement
originally implemented by Mike B. Allen as part of
\href{libmba}{http://www.ioplex.com/~miallen/libmba/}
version 0.9.1.  This implementation is a heavily modified version of the
original C code and is not compatible with the \code{libmba} library.
The C code is simplified by using fixed size arrays instead of variable
ones for tracking the longest reaching paths and for recording the shortest
edit scripts.  Additionally all error handling and memory allocation calls
have been moved to the internal R functions designed to handle those things.
A failover result is provided in the case where max diffs allowed is
exceeded.  Ability to provide custom comparison functions is removed.
}
\keyword{internal}
diffobj/man/diffobj_set_def_opts.Rd0000755000176200001440000000075313420351310017060 0ustar  liggesusers% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/options.R
\name{diffobj_set_def_opts}
\alias{diffobj_set_def_opts}
\title{Set All diffobj Options to Defaults}
\usage{
diffobj_set_def_opts()
}
\value{
list for use with \code{options} that contains values of
  \code{diffob} options before they were forced to defaults
}
\description{
Used primarily for testing to ensure all options are set to default values.
}
\examples{
\dontrun{
  diffobj_set_def_opts()
}
}
diffobj/man/dimnames-PaletteOfStyles-method.Rd0000755000176200001440000000073413201325222021056 0ustar  liggesusers% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/styles.R
\docType{methods}
\name{dimnames,PaletteOfStyles-method}
\alias{dimnames,PaletteOfStyles-method}
\title{Retrieve Dimnames for PaletteOfStyles Objects}
\usage{
\S4method{dimnames}{PaletteOfStyles}(x)
}
\arguments{
\item{x}{a \code{\link{PaletteOfStyles}} object}
}
\value{
list the dimension names
dimnames(PaletteOfStyles())
}
\description{
Retrieve Dimnames for PaletteOfStyles Objects
}
diffobj/man/diffStr.Rd0000755000176200001440000004513113466140042014327 0ustar  liggesusers% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/diff.R
\docType{methods}
\name{diffStr}
\alias{diffStr}
\alias{diffStr,ANY-method}
\title{Diff Object Structures}
\usage{
diffStr(target, current, ...)

\S4method{diffStr}{ANY}(target, current, mode = gdo("mode"),
  context = gdo("context"), format = gdo("format"),
  brightness = gdo("brightness"), color.mode = gdo("color.mode"),
  word.diff = gdo("word.diff"), pager = gdo("pager"),
  guides = gdo("guides"), trim = gdo("trim"), rds = gdo("rds"),
  unwrap.atomic = gdo("unwrap.atomic"), max.diffs = gdo("max.diffs"),
  disp.width = gdo("disp.width"),
  ignore.white.space = gdo("ignore.white.space"),
  convert.hz.white.space = gdo("convert.hz.white.space"),
  tab.stops = gdo("tab.stops"), line.limit = gdo("line.limit"),
  hunk.limit = gdo("hunk.limit"), align = gdo("align"),
  style = gdo("style"), palette.of.styles = gdo("palette"),
  frame = par_frame(), interactive = gdo("interactive"),
  term.colors = gdo("term.colors"), tar.banner = NULL,
  cur.banner = NULL, strip.sgr = gdo("strip.sgr"),
  sgr.supported = gdo("sgr.supported"), extra = list())
}
\arguments{
\item{target}{the reference object}

\item{current}{the object being compared to \code{target}}

\item{...}{unused, for compatibility of methods with generics}

\item{mode}{character(1L), one of:
\itemize{
  \item \dQuote{unified}: diff mode used by \code{git diff}
  \item \dQuote{sidebyside}: line up the differences side by side
  \item \dQuote{context}: show the target and current hunks in their
    entirety; this mode takes up a lot of screen space but makes it easier
    to see what the objects actually look like
  \item \dQuote{auto}: default mode; pick one of the above, will favor
    \dQuote{sidebyside} unless \code{getOption("width")} is less than 80,
    or in \code{diffPrint} and objects are dimensioned and do not fit side
    by side, or in \code{diffChr}, \code{diffDeparse}, \code{diffFile} and
    output does not fit in side by side without wrapping
}}

\item{context}{integer(1L) how many lines of context are shown on either side
of differences (defaults to 2).  Set to \code{-1L} to allow as many as
there are.  Set to \dQuote{auto}  to display as many as 10 lines or as few
as 1 depending on whether total screen lines fit within the number of lines
specified in \code{line.limit}.  Alternatively pass the return value of
\code{\link{auto_context}} to fine tune the parameters of the auto context
calculation.}

\item{format}{character(1L), controls the diff output format, one of:
\itemize{
  \item \dQuote{auto}: to select output format based on terminal
    capabilities; will attempt to use one of the ANSI formats if they
    appear to be supported, and if not or if you are in the Rstudio console
    it will attempt to use HTML and browser output if in interactive mode.
  \item \dQuote{raw}: plain text
  \item \dQuote{ansi8}: color and format diffs using basic ANSI escape
    sequences
  \item \dQuote{ansi256}: like \dQuote{ansi8}, except using the full range
    of ANSI formatting options
  \item \dQuote{html}: color and format using HTML markup; the resulting
    string is processed with \code{\link{enc2utf8}} when output as a full
    web page (see docs for \code{html.output} under \code{\link{Style}}).
}
Defaults to \dQuote{auto}.  See \code{palette.of.styles} for details
on customization, \code{\link{style}} for full control of output format.
See `pager` parameter for more discussion of Rstudio behavior.}

\item{brightness}{character, one of \dQuote{light}, \dQuote{dark},
\dQuote{neutral}, useful for adjusting color scheme to light or dark
terminals.  \dQuote{neutral} by default.  See \code{\link{PaletteOfStyles}}
for details and limitations.  Advanced: you may specify brightness as a
function of \code{format}.  For example, if you typically wish to use a
\dQuote{dark} color scheme, except for when in \dQuote{html} format when
you prefer the \dQuote{light} scheme, you may use
\code{c("dark", html="light")} as the value for this parameter.  This is
particularly useful if \code{format} is set to \dQuote{auto} or if you
want to specify a default value for this parameter via options.  Any names
you use should correspond to a \code{format}.  You must have one unnamed
value which will be used as the default for all \code{format}s that are
not explicitly specified.}

\item{color.mode}{character, one of \dQuote{rgb} or \dQuote{yb}.
Defaults to \dQuote{yb}.  \dQuote{yb} stands for \dQuote{Yellow-Blue} for
color schemes that rely primarily on those colors to style diffs.
Those colors can be easily distinguished by individuals with
limited red-green color sensitivity.  See \code{\link{PaletteOfStyles}} for
details and limitations.  Also offers the same advanced usage as the
\code{brightness} parameter.}

\item{word.diff}{TRUE (default) or FALSE, whether to run a secondary word
diff on the in-hunk differences.  For atomic vectors setting this to
FALSE could make the diff \emph{slower} (see the \code{unwrap.atomic}
parameter).  For other uses, particularly with \code{\link{diffChr}}
setting this to FALSE can substantially improve performance.}

\item{pager}{one of \dQuote{auto} (default), \dQuote{on},
  \dQuote{off}, a \code{\link{Pager}} object, or a list; controls whether and
  how a pager is used to display the diff output.  If you require a
  particular pager behavior you must use a \code{\link{Pager}}
  object, or \dQuote{off} to turn off the pager.  All other settings will
  interact with other parameters such as \code{format}, \code{style}, as well
  as with your system capabilities in order to select the pager expected to
  be most useful.

  \dQuote{auto} and \dQuote{on} are the same, except that in non-interactive
  mode \dQuote{auto} is equivalent to \dQuote{off}.  \dQuote{off} will always
  send output to the console.  If \dQuote{on}, whether the output
  actually gets routed to the pager depends on the pager \code{threshold}
  setting (see \code{\link{Pager}}).  The default behavior is to use the
  pager associated with the \code{Style} object.  The \code{Style} object is
  itself is determined by the \code{format} or \code{style} parameters.

  Depending on your system configuration different styles and corresponding
  pagers will get selected, unless you specify a \code{Pager} object
  directly.  On a system with a system pager that supports ANSI CSI SGR
  colors, the pager will only trigger if the output is taller than one
  window.  If the system pager is not known to support ANSI colors then the
  output will be sent as HTML to the IDE viewer if available or to the web
  browser if not.  Even though Rstudio now supports ANSI CSI SGR at the
  console output is still formatted as HTML and sent to the IDE viewer.
  Partly this is for continuity of behavior, but also because the default
  Rstudio pager does not support ANSI CSI SGR, at least as of this writing.

  If \code{pager} is a list, then the same as with \dQuote{on}, except that
  the \code{Pager} object associated with the selected \code{Style} object is
  re-instantiated with the union of the list elements and the existing
  settings of that \code{Pager}.  The list should contain named elements that
  correspond to the \code{\link{Pager}} instantiation parameters.  The names
  must be specified in full as partial parameter matching will not be carried
  out because the pager is re-instantiated with \code{\link{new}}.

  See \code{\link{Pager}}, \code{\link{Style}}, and
  \code{\link{PaletteOfStyles}} for more details and for instructions on how
  to modify the default behavior.}

\item{guides}{TRUE (default), FALSE, or a function that accepts at least two
arguments and requires no more than two arguments.  Guides
are additional context lines that are not strictly part of a hunk, but
provide important contextual data (e.g. column headers).  If TRUE, the
context lines are shown in addition to the normal diff output, typically
in a different color to indicate they are not part of the hunk.  If a
function, the function should accept as the first argument the object
being diffed, and the second the character representation of the object.
The function should return the indices of the elements of the
character representation that should be treated as guides.  See
\code{\link{guides}} for more details.}

\item{trim}{TRUE (default), FALSE, or a function that accepts at least two
arguments and requires no more than two arguments.  Function should compute
for each line in captured output what portion of those lines should be
diffed.  By default, this is used to remove row meta data differences
(e.g. \code{[1,]}) so they alone do not show up as differences in the
diff.  See \code{\link{trim}} for more details.}

\item{rds}{TRUE (default) or FALSE, if TRUE will check whether
\code{target} and/or \code{current} point to a file that can be read with
\code{\link{readRDS}} and if so, loads the R object contained in the file
and carries out the diff on the object instead of the original argument.
Currently there is no mechanism for specifying additional arguments to
\code{readRDS}}

\item{unwrap.atomic}{TRUE (default) or FALSE.  Relevant primarily for
\code{diffPrint}, if TRUE, and \code{word.diff} is also TRUE, and both
\code{target} and \code{current} are \emph{unnamed} one-dimension atomics ,
the vectors are unwrapped and diffed element by element, and then
re-wrapped.  Since \code{diffPrint} is fundamentally a line diff, the
re-wrapped lines are lined up in a manner that is as consistent as possible
with the unwrapped diff.  Lines that contain the location of the word
differences will be paired up.  Since the vectors may well be wrapped with
different periodicities this will result in lines that are paired up that
look like they should not be paired up, though the locations of the
differences should be.  If is entirely possible that setting this parameter
to FALSE will result in a slower diff.  This happens if two vectors are
actually fairly similar, but their line representations are not.  For
example, in comparing \code{1:100} to \code{c(100, 1:99)}, there is really
only one difference at the \dQuote{word} level, but every screen line is
different.  \code{diffChr} will also do the unwrapping if it is given a
character vector that contains output that looks like the atomic vectors
described above.  This is a bug, but as the functionality could be useful
when diffing e.g. \code{capture.output} data, we now declare it a feature.}

\item{max.diffs}{integer(1L), number of \emph{differences} after which we
abandon the \code{O(n^2)} diff algorithm in favor of a naive element by
element comparison. Set to \code{-1L} to always stick to the original
algorithm (defaults to 50000L).}

\item{disp.width}{integer(1L) number of display columns to take up; note that
in \dQuote{sidebyside} \code{mode} the effective display width is half this
number (set to 0L to use default widths which are \code{getOption("width")}
for normal styles and \code{80L} for HTML styles.  Future versions of
\code{diffobj} may change this to larger values for two dimensional objects
for better diffs (see details).}

\item{ignore.white.space}{TRUE or FALSE, whether to consider differences in
horizontal whitespace (i.e. spaces and tabs) as differences (defaults to
TRUE).}

\item{convert.hz.white.space}{TRUE or FALSE, whether modify input strings
that contain tabs and carriage returns in such a way that they display as
they would \bold{with} those characters, but without using those
characters (defaults to TRUE).  The conversion assumes that tab stops are
spaced evenly eight characters apart on the terminal.  If this is not the
case you may specify the tab stops explicitly with \code{tab.stops}.}

\item{tab.stops}{integer, what tab stops to use when converting hard tabs to
spaces.  If not integer will be coerced to integer (defaults to 8L).  You
may specify more than one tab stop.  If display width exceeds that
addressable by your tab stops the last tab stop will be repeated.}

\item{line.limit}{integer(2L) or integer(1L), if length 1 how many lines of
output to show, where \code{-1} means no limit.  If length 2, the first
value indicates the threshold of screen lines to begin truncating output,
and the second the number of lines to truncate to, which should be fewer
than the threshold.  Note that this parameter is implemented on a
best-efforts basis and should not be relied on to produce the exact
number of lines requested.  In particular do not expect it to work well for
for values small enough that the banner portion of the diff would have to
be trimmed.  If you want a specific number of lines use \code{[} or
\code{head} / \code{tail}.  One advantage of \code{line.limit} over these
other options is that you can combine it with \code{context="auto"} and
auto \code{max.level} selection (the latter for \code{diffStr}), which
allows the diff to dynamically adjust to make best use of the available
display lines.  \code{[}, \code{head}, and \code{tail} just subset the text
of the output.}

\item{hunk.limit}{integer(2L) or integer (1L), how many diff hunks to show.
Behaves similarly to \code{line.limit}.  How many hunks are in a
particular diff is a function of how many differences, and also how much
\code{context} is used since context can cause two hunks to bleed into
each other and become one.}

\item{align}{numeric(1L) between 0 and 1, proportion of
words in a line of \code{target} that must be matched in a line of
\code{current} in the same hunk for those lines to be paired up when
displayed (defaults to 0.25), or an \code{\link{AlignThreshold}} object.
Set to \code{1} to turn off alignment which will cause all lines in a hunk
from \code{target} to show up first, followed by all lines from
\code{current}.  Note that in order to be aligned lines must meet the
threshold and have at least 3 matching alphanumeric characters (see
\code{\link{AlignThreshold}} for details).}

\item{style}{\dQuote{auto}, a \code{\link{Style}} object, or a list.
\dQuote{auto} by default.  If a \code{Style} object, will override the
the \code{format}, \code{brightness}, and \code{color.mode} parameters.
The \code{Style} object provides full control of diff output styling.
If a list, then the same as \dQuote{auto}, except that if the auto-selected
\code{Style} requires instantiation (see \code{\link{PaletteOfStyles}}),
then the list contents will be used as arguments when instantiating the
style object.  See \code{\link{Style}} for more details, in particular the
examples.}

\item{palette.of.styles}{\code{\link{PaletteOfStyles}} object; advanced
usage, contains all the \code{\link{Style}} objects or
\dQuote{classRepresentation} objects extending \code{\link{Style}} that are
selected by specifying the \code{format}, \code{brightness}, and
\code{color.mode} parameters.  See \code{\link{PaletteOfStyles}} for more
details.}

\item{frame}{an environment to use as the evaluation frame for the
\code{print/show/str}, calls and for \code{diffObj}, the evaluation frame
for the \code{diffPrint} / \code{diffStr} calls.  Defaults to the return
value of \code{\link{par_frame}}.}

\item{interactive}{TRUE or FALSE whether the function is being run in
interactive mode, defaults to the return value of
\code{\link{interactive}}.  If in interactive mode, pager will be used if
\code{pager} is \dQuote{auto}, and if ANSI styles are not supported and
\code{style} is \dQuote{auto}, output will be send to viewer/browser as
HTML.}

\item{term.colors}{integer(1L) how many ANSI colors are supported by the
terminal.  This variable is provided for when
\code{\link[=num_colors]{crayon::num_colors}} does not properly detect how
many ANSI colors are supported by your terminal. Defaults to return value
of \code{\link[=num_colors]{crayon::num_colors}} and should be 8 or 256 to
allow ANSI colors, or any other number to disallow them.  This only
impacts output format selection when \code{style} and \code{format} are
both set to \dQuote{auto}.}

\item{tar.banner}{character(1L), language, or NULL, used to generate the
text to display ahead of the diff section representing the target output.
If NULL will use the deparsed \code{target} expression, if language, will
use the language as it would the \code{target} expression, if
character(1L), will use the string with no modifications.  The language
mode is provided because \code{diffStr} modifies the expression prior to
display (e.g. by wrapping it in a call to \code{str}).  Note that it is
possible in some cases that the substituted value of \code{target} actually
is character(1L), but if you provide a character(1L) value here it will be
assumed you intend to use that value literally.}

\item{cur.banner}{character(1L) like \code{tar.banner}, but for
\code{current}}

\item{strip.sgr}{TRUE, FALSE, or NULL (default), whether to strip ANSI CSI
SGR sequences prior to comparison and for display of diff.  If NULL,
resolves to TRUE if `style` resolves to an ANSI formatted diff, and
FALSE otherwise.  The default behavior is to avoid confusing diffs where
the original SGR and the SGR added by the diff are mixed together.}

\item{sgr.supported}{TRUE, FALSE, or NULL (default), whether to assume the
standard output device supports ANSI CSI SGR sequences.  If TRUE, strings
will be manipulated accounting for the SGR sequences.  If NULL,
resolves to TRUE if `style` resolves to an ANSI formatted diff, and
to `crayon::has_color()` otherwise.  This only controls how the strings are
manipulated, not whether SGR is added to format the diff, which is
controlled by the `style` parameter.  This parameter is exposed for the
rare cases where you might wish to control string manipulation behavior
directly.}

\item{extra}{list additional arguments to pass on to the functions used to
create text representation of the objects to diff (e.g. \code{print},
\code{str}, etc.)}
}
\value{
a \code{Diff} object; see \code{\link{diffPrint}}.
}
\description{
Compares the \code{str} output of \code{target} and \code{current}.  If
the \code{max.level} parameter to \code{str} is left unspecified, will
attempt to find the largest \code{max.level} that fits within
\code{line.limit} and shows at least one difference.
}
\details{
Due to the seemingly inconsistent nature of \code{max.level} when used with
objects with nested attributes, and also due to the relative slowness of
\code{str}, this function simulates the effect of \code{max.level} by hiding
nested lines instead of repeatedly calling \code{str} with varying values of
\code{max.level}.
}
\examples{
## `pager="off"` for CRAN compliance; you may omit in normal use
with(mtcars, diffStr(lm(mpg ~ hp)$qr, lm(mpg ~ disp)$qr, pager="off"))
}
\seealso{
\code{\link{diffPrint}} for details on the \code{diff*} functions,
  \code{\link{diffObj}}, \code{\link{diffStr}},
  \code{\link{diffChr}} to compare character vectors directly,
  \code{\link{diffDeparse}} to compare deparsed objects,
  \code{\link{ses}} for a minimal and fast diff
}
diffobj/man/Style.Rd0000755000176200001440000003445613420351310014026 0ustar  liggesusers% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/styles.R
\docType{class}
\name{Style-class}
\alias{Style-class}
\alias{Style}
\alias{StyleRaw-class}
\alias{StyleRaw}
\alias{StyleAnsi-class}
\alias{StyleAnsi}
\alias{StyleAnsi8NeutralRgb-class}
\alias{StyleAnsi8NeutralRgb}
\alias{StyleAnsi8NeutralYb-class}
\alias{StyleAnsi8NeutralYb}
\alias{StyleAnsi256LightRgb-class}
\alias{StyleAnsi256LightRgb}
\alias{StyleAnsi256LightYb-class}
\alias{StyleAnsi256LightYb}
\alias{StyleAnsi256DarkRgb-class}
\alias{StyleAnsi256DarkRgb}
\alias{StyleAnsi256DarkYb-class}
\alias{StyleAnsi256DarkYb}
\alias{StyleHtml-class}
\alias{StyleHtml}
\alias{StyleHtmlLightRgb-class}
\alias{StyleHtmlLightRgb}
\alias{StyleHtmlLightYb-class}
\alias{StyleHtmlLightYb}
\title{Customize Appearance of Diff}
\arguments{
\item{funs}{a \code{\link{StyleFuns}} object that contains all the functions
represented above}

\item{text}{a \code{\link{StyleText}} object that contains the non-content
text used by the diff (e.g. \code{gutter.insert.txt})}

\item{summary}{a \code{\link{StyleSummary}} object that contains formatting
functions and other meta data for rendering summaries}

\item{wrap}{TRUE or FALSE, whether the text should be hard wrapped to fit in
the console}

\item{pad}{TRUE or FALSE, whether text should be right padded}

\item{pager}{what type of \code{\link{Pager}} to use}

\item{nchar.fun}{function to use to count characters; intended mostly for
internal use (used only for gutters as of version 0.2.0).}

\item{wrap}{TRUE or FALSE, whether text should be hard wrapped at
\code{disp.width}}

\item{na.sub}{what character value to substitute for NA elements; NA elements
are generated when lining up side by side diffs by adding padding rows; by
default the text styles replace these with a blank character string, and
the HTML styles leave them as NA for the HTML formatting functions to deal
with}

\item{blank}{sub what character value to replace blanks with; needed in
particular for HTML rendering (uses \code{" "}) to prevent lines from
collapsing}

\item{disp.width}{how many columns the text representation of the objects to
diff is allowed to take up before it is hard wrapped (assuming \code{wrap}
is TRUE).  See param \code{disp.width} for \code{\link{diffPrint}}.}

\item{finalizer}{function that accepts at least two parameters and requires
no more than two parameters, will receive as the first parameter the
the object to render (either a \code{Diff} or a \code{DiffSummary}
object), and the text representation of that object as the second
argument.  This allows final modifications to the character output so that
it is displayed correctly by the pager.  For example, \code{StyleHtml}
objects use it to generate HTML headers if the \code{Diff} is destined to
be displayed in a browser.  The object themselves are passed along to
provide information about the paging device and other contextual data to
the function.}

\item{html.output}{(\code{StyleHtml} objects only) one of:
\itemize{
  \item \dQuote{page}: Include all HTML/CSS/JS required to create a
    stand-alone web page with the diff; in this mode the diff string will
    be re-encoded with \code{\link{enc2utf8}} and the HTML page encoding
    will be declared as UTF-8.
  \item \dQuote{diff.w.style}: The CSS and HTML, but without any of the
    outer tags that would make it a proper HTML page (i.e. no
    \code{/} tags or the like) and without the JS; note that
    technically this is illegal HTML since we have \code{