<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Pierre Donat-Bouillud</title><link>https://www.pdonatbouillud.com/authors/admin/</link><atom:link href="https://www.pdonatbouillud.com/authors/admin/index.xml" rel="self" type="application/rss+xml"/><description>Pierre Donat-Bouillud</description><generator>Wowchemy (https://wowchemy.com)</generator><language>en-us</language><copyright>© Pierre Donat-Bouillud 2026</copyright><image><url>https://www.pdonatbouillud.com/authors/admin/avatar_huf5219ca642ad4724664ab37b8155f575_166250_270x270_fill_q75_lanczos_center.jpg</url><title>Pierre Donat-Bouillud</title><link>https://www.pdonatbouillud.com/authors/admin/</link></image><item><title>R4R: Reproducibility for R</title><link>https://www.pdonatbouillud.com/project/reproducibility-for-r/</link><pubDate>Wed, 30 Jul 2025 22:04:18 -0700</pubDate><guid>https://www.pdonatbouillud.com/project/reproducibility-for-r/</guid><description>&lt;p>Ensuring reproducibility is a fundamental challenge in computational research. Reproducing results often requires reconstructing
complex software environments involving data files, external tools,
system libraries, and language-specific packages. While various
tools aim to simplify this process, they often rely on user-provided
metadata, overlook system dependencies, or produce unnecessarily
large environments.&lt;/p>
&lt;p>R Markdown notebooks are a popular format for data analysis.
In this project, we focus on the reproducibility of computational notebooks in R. We aim to automate the creation of minimal, user-inspectable, self-contained execution, distributable environments through dynamic program analysis techniques.&lt;/p>
&lt;p>We created a tool, &lt;code>r4r&lt;/code>, that captures all runtime dependencies of a data analysis pipeline and produces a Docker image capable of reproducing the original execution. Although designed with first-class support for the R programming language, r4r also includes a generic fallback mechanism applicable to other languages.&lt;/p>
&lt;p>This project is financed by the &lt;a href="https://cordis.europa.eu/project/id/101081989" target="_blank" rel="noopener">ERC PoC grant 101081989&lt;/a> and spans from January 2024 to June 2025.&lt;/p></description></item><item><title>Data Bugs</title><link>https://www.pdonatbouillud.com/project/data-bugs/</link><pubDate>Thu, 04 May 2023 17:47:54 +0200</pubDate><guid>https://www.pdonatbouillud.com/project/data-bugs/</guid><description>&lt;p>The goal of this project is to develop new programming language technologies to
assist data scientists, using the R language for empirical validation of our novel ideas.&lt;/p>
&lt;p>The project is financed by the &lt;a href="https://opjak.cz/vyzvy/vyzva-c-02_22_010-msca-fellowships-cz/" target="_blank" rel="noopener">operational program Jan Amos Comenius&lt;/a> and spans from March, 2023, to March, 2025.&lt;/p></description></item><item><title>Fuzzing R</title><link>https://www.pdonatbouillud.com/project/fuzzing-r/</link><pubDate>Mon, 17 Apr 2023 16:38:11 +0200</pubDate><guid>https://www.pdonatbouillud.com/project/fuzzing-r/</guid><description>&lt;p>R is a vector-oriented, dynamic language, mostly used in data-science. It is difficult to statically analyze, and so we mainly use dynamic techniques to gather insights about R programs. One instance of them is &lt;em>fuzzing&lt;/em>.&lt;/p>
&lt;p>We do not use fuzzing in R only to find bugs&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> but also to dynamically infer type signatures, or other interesting properties of R programs.&lt;/p>
&lt;section class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1" role="doc-endnote">
&lt;p>Although we also have found plenty of them! &lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/section></description></item><item><title>Finding NA in serialized R values</title><link>https://www.pdonatbouillud.com/post/find-na-serialized-sexp/</link><pubDate>Tue, 04 Apr 2023 15:30:43 +0200</pubDate><guid>https://www.pdonatbouillud.com/post/find-na-serialized-sexp/</guid><description>&lt;p>In &lt;a href="https://www.pdonatbouillud.com/post/testing-cpp-code-r-packages/">a previous post&lt;/a>, I explained how to test a C++ function, &lt;code>find_na&lt;/code>, used in a &lt;a href="https://github.com/PRL-PRG/sxpdb/" target="_blank" rel="noopener">database for R values&lt;/a>. &lt;code>find_na&lt;/code> checks if &lt;code>NA&lt;/code> is present or not in a previously serialized R value. By not unserializing the value, it avoids unnecessary allocations.&lt;/p>
&lt;h1 id="serialization-of-r-values">Serialization of R values&lt;/h1>
&lt;p>Native R serialization functions are defined in &lt;a href="https://svn.r-project.org/R/trunk/src/main/serialize.c" target="_blank" rel="noopener">&lt;code>serialize.c&lt;/code>&lt;/a> in the R codebase. Three versions of the serialization are available; I use the latest one. The serialization starts with a header indicating how atomic values are serialized, including the character encoding, and then recursively traverses the R value.&lt;/p>
&lt;p>&lt;code>R_InitOutPStream&lt;/code> first creates a stream in which to write the serialized value and then &lt;code>R_serialize&lt;/code> performs the actual serialization:&lt;/p>
&lt;pre>&lt;code class="language-cpp"> WriteBuffer write_buffer(buf);
R_outpstream_st out;
R_InitOutPStream(&amp;amp;out,
reinterpret_cast&amp;lt;R_pstream_data_t&amp;gt;(&amp;amp;write_buffer),
R_pstream_binary_format,
3, //version of the serialization
append_byte, append_buf,
refhook_write, R_NilValue);
R_Serialize(val, &amp;amp;out);
&lt;/code>&lt;/pre>
&lt;ul>
&lt;li>&lt;code>R_pstream_data_t&lt;/code> is an alias for &lt;code>void*&lt;/code>. It is passed as an argument to callbacks &lt;code>append_byte&lt;/code> and &lt;code>append_buf&lt;/code>;&lt;/li>
&lt;li>&lt;code>R_pstream_binary_format&lt;/code> indicates that atomic values should be serialized as binary. Among other formats are &lt;em>XDR&lt;/em>, with &lt;code>R_pstream_xdr_formatR_pstream_xdr_format&lt;/code> or ASCII, with &lt;code>R_pstream_ascii_format&lt;/code>;&lt;/li>
&lt;li>&lt;code>refhook_write&lt;/code> provides a way to customize the serialization of some values. In &lt;em>sxpdb&lt;/em>, we use it to disable serialization for environments.&lt;/li>
&lt;/ul>
&lt;pre>&lt;code class="language-cpp">SEXP Serializer::refhook_write(SEXP val, SEXP data) {
if(TYPEOF(val) != ENVSXP) {
return R_NilValue;//it means that R will serialize the value as usual
}
// We just do not serialize the environment and return a blank string
return R_BlankScalarString;
}
&lt;/code>&lt;/pre>
&lt;ul>
&lt;li>The &lt;code>R_NilValue&lt;/code> passed last to &lt;code>R_InitOutPStream&lt;/code> is the second argument of &lt;code>refhook_write&lt;/code>.&lt;/li>
&lt;/ul>
&lt;h1 id="partially-unserializing">Partially unserializing&lt;/h1>
&lt;p>The database stores the serialized values, stripped of the header with the version of the serialization and the encoding.&lt;/p>
&lt;p>Function &lt;code>unserialize_view&lt;/code> takes a serialized value — a buffer of bytes — from the database and returns a &lt;code>sexp_view_t&lt;/code>, which holds pre-computed metadata on the serialized view.&lt;/p>
&lt;pre>&lt;code class="language-cpp">const sexp_view_t Serializer::unserialize_view(
const std::vector&amp;lt;std::byte&amp;gt;&amp;amp; buf);
struct sexp_view_t {
SEXPTYPE type = ANYSXP;
const void* data = nullptr;
size_t length = 0;
size_t element_size = 0;
};
&lt;/code>&lt;/pre>
&lt;p>For vector types, the serialized value then comprises flags, the length of the vector, and the actual data in the vector:&lt;/p>
&lt;p>&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1121px" viewBox="-0.5 -0.5 1121 41" style="max-width:100%;max-height:41px;">&lt;defs/>&lt;g>&lt;rect x="0" y="0" width="280" height="40" fill-opacity="0.5" fill="#ff6666" stroke="rgb(0, 0, 0)" stroke-opacity="0.5" pointer-events="all"/>&lt;g transform="translate(-0.5 -0.5)">&lt;switch>&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 20px; margin-left: 140px;">&lt;div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); ">&lt;div style="display: inline-block; font-size: 28px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">&lt;div style="font-size: 28px;">header&lt;/div>&lt;/div>&lt;/div>&lt;/div>&lt;/foreignObject>&lt;text x="140" y="28" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="28px" text-anchor="middle">header&lt;/text>&lt;/switch>&lt;/g>&lt;rect x="280" y="0" width="80" height="40" fill="#d5e8d4" stroke="#82b366" pointer-events="all"/>&lt;g transform="translate(-0.5 -0.5)">&lt;switch>&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 20px; margin-left: 320px;">&lt;div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); ">&lt;div style="display: inline-block; font-size: 28px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">flags&lt;/div>&lt;/div>&lt;/div>&lt;/foreignObject>&lt;text x="320" y="28" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="28px" text-anchor="middle">flags&lt;/text>&lt;/switch>&lt;/g>&lt;rect x="360" y="0" width="240" height="40" fill="#fff2cc" stroke="#d6b656" pointer-events="all"/>&lt;g transform="translate(-0.5 -0.5)">&lt;switch>&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 20px; margin-left: 480px;">&lt;div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); ">&lt;div style="display: inline-block; font-size: 28px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">length (1-3 words)&lt;/div>&lt;/div>&lt;/div>&lt;/foreignObject>&lt;text x="480" y="28" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="28px" text-anchor="middle">length (1-3 words)&lt;/text>&lt;/switch>&lt;/g>&lt;rect x="600" y="0" width="520" height="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/>&lt;g transform="translate(-0.5 -0.5)">&lt;switch>&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 20px; margin-left: 860px;">&lt;div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); ">&lt;div style="display: inline-block; font-size: 28px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">data&lt;/div>&lt;/div>&lt;/div>&lt;/foreignObject>&lt;text x="860" y="28" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="28px" text-anchor="middle">data&lt;/text>&lt;/switch>&lt;/g>&lt;/g>&lt;switch>&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>&lt;a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display&lt;/text>&lt;/a>&lt;/switch>&lt;/svg>&lt;/p>
&lt;p>The flags include the type and the presence of attributes of the value:&lt;/p>
&lt;pre>&lt;code class="language-cpp">int flags = 0;
std::memcpy(&amp;amp;flags, data, sizeof(int));
sexp_view.type = flags &amp;amp; 255;
bool has_attr = flags &amp;amp; (1 &amp;lt;&amp;lt; 9);
&lt;/code>&lt;/pre>
&lt;p>The function then stores the actual value data in the &lt;code>data&lt;/code> field.&lt;/p>
&lt;p>For vector values (character, logical, integer, real, complex), &lt;code>length&lt;/code> in &lt;code>sexp_view_t&lt;/code> stores the number of elements in the vector. &lt;code>element_size&lt;/code> represents the number of bytes of an element in the vector. For character vectors, that one does not make sense: in R, character vectors of type &lt;code>STRSXP&lt;/code> are actually vectors of atomic strings, of type &lt;code>CHARSXP&lt;/code>, which have variable length.&lt;/p>
&lt;h1 id="looking-for-na">Looking for &lt;code>NA&lt;/code>&lt;/h1>
&lt;p>For logical and integer vectors, we can simply look for the magic &lt;code>NA&lt;/code> value in the &lt;code>data&lt;/code> field, after casting it to an integer, as &lt;code>v&lt;/code>:&lt;/p>
&lt;pre>&lt;code class="language-cpp">std::find(v, v + length, NA_LOGICAL) != v + length;
&lt;/code>&lt;/pre>
&lt;p>For real vectors, &lt;code>NA&lt;/code> is a more complicated beast. There is an existing protocol for missing values defined by the floating point standard (&lt;a href="http://en.wikipedia.org/wiki/IEEE_floating_point" target="_blank" rel="noopener">IEEE 754&lt;/a>): representing them with &lt;code>NaN&lt;/code>. In R, &lt;code>NA&lt;/code> is &lt;code>NaN&lt;/code> with a special bit pattern: the lowest word is 1954, the year Ross Ihaka, one of the creator of the R Language, was born.&lt;/p>
&lt;pre>&lt;code class="language-c">static double R_ValueOfNA(void)
{
/* The gcc shipping with Fedora 9 gets this wrong without
* the volatile declaration. Thanks to Marc Schwartz. */
volatile ieee_double x;
x.word[hw] = 0x7ff00000;
x.word[lw] = 1954;
return x.value;
}
&lt;/code>&lt;/pre>
&lt;p>The C R API provides &lt;code>R_IsNA&lt;/code> to simplify the check:&lt;/p>
&lt;pre>&lt;code class="language-cpp">std::find_if(v, v + length, [](double d) -&amp;gt; bool {
return R_IsNA(d) ;}) != v + length;
&lt;/code>&lt;/pre>
&lt;p>Complex vectors are &lt;code>NA&lt;/code> if the real &lt;strong>or&lt;/strong> the imaginary part is &lt;code>NA&lt;/code>:&lt;/p>
&lt;pre>&lt;code class="language-cpp">std::find_if(v, v + length, [](const Rcomplex&amp;amp; c) -&amp;gt; bool {
return ISNAN(c.r) || ISNAN(c.i);}) != v + length;
&lt;/code>&lt;/pre>
&lt;p>Character vectors are again more complex. Each &lt;code>CHARSXP&lt;/code> element of the vector is again a valid R value and so is serialized with the flags and then the length of the element. &lt;code>NA&lt;/code> is the element with length &lt;code>-1&lt;/code>:&lt;/p>
&lt;pre>&lt;code class="language-cpp">SEXPTYPE type = ANYSXP;
int size = 0;
for(size_t i = 0; i &amp;lt; length; i++) {
std::memcpy(&amp;amp;type, data, sizeof(int));
type &amp;amp;= 255;// this would also store the encoding...
assert(type == CHARSXP);
data += sizeof(int);
std::memcpy(&amp;amp;size, data, sizeof(int));
data += sizeof(int);
if(size == -1) {// this is NA_STRING
return true;
}
assert(size &amp;gt;= 0);
//else we jump to the next CHARSXP
data += size;
}
return false;
&lt;/code>&lt;/pre>
&lt;p>With that, the search of &lt;code>NA&lt;/code> is $O(1)$ in memory. Note that I do not deal with non-vector types, such as environments (not stored in the database) or lists.&lt;/p></description></item><item><title>Testing C++ Code in R Packages</title><link>https://www.pdonatbouillud.com/post/testing-cpp-code-r-packages/</link><pubDate>Tue, 21 Mar 2023 16:02:00 +0100</pubDate><guid>https://www.pdonatbouillud.com/post/testing-cpp-code-r-packages/</guid><description>&lt;p>When you write a package in R, you probably write tests. A popular package for unit tests with R is &lt;a href="https://testthat.r-lib.org/" target="_blank" rel="noopener">&lt;em>testthat&lt;/em>&lt;/a>,&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> with the final invocation:&lt;/p>
&lt;pre>&lt;code class="language-R">devtools::test()
&lt;/code>&lt;/pre>
&lt;p>If your package also includes native code, let&amp;rsquo;s say, C++, you can test it by testing the R API that exercises the native functions.
But what if native functions do not map exactly to the R API, R functions use a combination of the native functions, or you want to check the results of native functions that do not map easily to R types or would require to write tedious converters?&lt;/p>
&lt;p>In &lt;a href="https://github.com/PRL-PRG/sxpdb/" target="_blank" rel="noopener">sxpdb&lt;/a>, a database to efficiently store R values when instrumenting the R interpreter with &lt;a href="https://github.com/PRL-PRG/R-dyntrace" target="_blank" rel="noopener">R-dyntrace&lt;/a>, I have a &lt;code>find_na&lt;/code> function that tests for the presence of &lt;code>NA&lt;/code> in a serialized R value in an efficient way. It does no try to deserialize the value, so does not allocate new memory, but traverses the serialized value in search of &lt;code>NA&lt;/code>.&lt;/p>
&lt;p>I suspected this function to be defective after problems in search indexes that use this function. It does not make sense to create a specific R function to call it as it only processes serialized R values that are not - and should not - be exposes to the R API. Thus I chose to test it directly on the C++ side.
I could use another C++ testing library, such as &lt;a href="https://google.github.io/googletest/" target="_blank" rel="noopener">&lt;em>GoogleTest&lt;/em>&lt;/a> but then I will have two separate test suites to run. How inconvenient, no more just running &lt;code>devtools::test()&lt;/code>!&lt;/p>
&lt;p>Fortunately, &lt;em>testthat&lt;/em> can integrate with &lt;a href="https://github.com/catchorg/Catch2" target="_blank" rel="noopener">&lt;em>catch2&lt;/em>&lt;/a>, another popular C++ unit test library.
Using &lt;em>catch2&lt;/em> with &lt;em>testthat&lt;/em> is easy with &lt;a href="https://testthat.r-lib.org/reference/use_catch.html" target="_blank" rel="noopener">&lt;code>use_catch&lt;/code>&lt;/a>, which sets up all the infrastructure.
Let&amp;rsquo;s see how it looks like in practice with &lt;code>sxpdb&lt;/code>.&lt;/p>
&lt;h1 id="_catch2_-within-_testthat_">&lt;em>catch2&lt;/em> within &lt;em>testthat&lt;/em>&lt;/h1>
&lt;p>I used &lt;code>use_catch&lt;/code> to build the scaffolding. For instance, it created a file &lt;code>tests/testthat/test-cpp.R&lt;/code> for &lt;code>testthat&lt;/code> to know about the C++ tests, and a file &lt;code>src/test-example.cpp&lt;/code> where to write the actual C++ tests. I renamed it to &lt;code>tests.cpp&lt;/code>.&lt;/p>
&lt;p>I do not use dynamic registration in &lt;code>sxpdb&lt;/code> for performance reason, and to hide the symbols I do not want to be part of the public API. The documentation of &lt;code>use_catch&lt;/code> is less clear about what to do in that case. If you do nothing, the compilation will fail as the linker cannot find the entry point of the tests, function &lt;code>run_testthat_tests&lt;/code>.&lt;/p>
&lt;div class="alert alert-note">
&lt;div>
&lt;p>If you do not use dynamic symbol lookup in your package, in your &lt;code>init.c&lt;/code> file, you have:&lt;/p>
&lt;pre>&lt;code class="language-C">void R_init_sxpdb(DllInfo* dll) {
R_registerRoutines(dll, NULL, callMethods, NULL, NULL);
R_useDynamicSymbols(dll, FALSE);// FALSE here
}
&lt;/code>&lt;/pre>
&lt;p>Disabling dynamic registration hides symbols not explicitly registered in &lt;code>callMethods&lt;/code> in &lt;code>init.c&lt;/code>&lt;/p>
&lt;p>More information in the &lt;a href="https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Registering-native-routines">official documentation&lt;/a>&lt;/p>
&lt;/div>
&lt;/div>
&lt;p>&lt;em>testthat&lt;/em> expects to see symbol &lt;code>run_testthat_tests&lt;/code> in your package. You have to add it to &lt;code>callMethods&lt;/code> explicitly and make it visible in the &lt;code>init.c&lt;/code> file:&lt;/p>
&lt;pre>&lt;code class="language-C">extern SEXP run_testthat_tests(SEXP use_xml_sxp);
static const R_CallMethodDef callMethods[] = {
{&amp;quot;run_testthat_tests&amp;quot;, (DL_FUNC) &amp;amp;run_testthat_tests, 1},
{NULL, NULL, 0} // Must have at the end
};
&lt;/code>&lt;/pre>
&lt;h1 id="writing-your-tests">Writing your tests&lt;/h1>
&lt;p>Adding a test is as easy as creating a context and using the same-looking function as in &lt;code>testthat&lt;/code>:&lt;/p>
&lt;pre>&lt;code class="language-C">context(&amp;quot;Index tests&amp;quot;) {
test_that(&amp;quot;find_na on sexp_view&amp;quot;) {
Serializer ser(64);
// Scalar NA real
SEXP scalar_na = PROTECT(Rf_ScalarReal(NA_REAL));
sexp_view_t sexp_view = Serializer::unserialize_view(ser.serialize(scalar_na));
UNPROTECT(1);
expect_true(find_na(sexp_view));
...
&lt;/code>&lt;/pre>
&lt;p>The C++ tests now appear as any other contexts:&lt;/p>
&lt;pre>&lt;code class="language-bash">&amp;gt; devtools::test()
ℹ Loading sxpdb
ℹ Testing sxpdb
✔ | F W S OK | Context
✔ | 4 | Index tests
✔ | 25 | db [0.5s]
✔ | 7 | merge [0.2s]
✔ | 24 | query [0.6s]
══ Results ══════════════════════════════
Duration: 1.7 s
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 60 ]
&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>And actually, that &lt;code>find_na&lt;/code> worked perfectly and the bug originated from elsewhere, but it was fun to play with &lt;code>catch2&lt;/code>!&lt;/p>
&lt;section class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1" role="doc-endnote">
&lt;p>Other R unit testing packages are &lt;a href="https://github.com/brodieG/unitizer" target="_blank" rel="noopener">unitizeR&lt;/a>, &lt;a href="https://github.com/markvanderloo/tinytest" target="_blank" rel="noopener">tinytest&lt;/a> from the tinyverse, &lt;a href="https://cran.r-project.org/web/packages/RUnit/index.html" target="_blank" rel="noopener">RUnit&lt;/a> &lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/section></description></item><item><title>Eval in R</title><link>https://www.pdonatbouillud.com/project/eval-in-r/</link><pubDate>Mon, 30 Aug 2021 11:54:26 +0200</pubDate><guid>https://www.pdonatbouillud.com/project/eval-in-r/</guid><description>&lt;p>R has an omnipotent function, the &lt;code>eval&lt;/code> function. In addition to evaluate any arbitrary R expression, it can also specify the environment in which the expression will be evaluated. The environment corresponds to the local scope, to a parent scope, of a caller function, or to a custom-built environment.&lt;/p>
&lt;p>As &lt;code>eval&lt;/code> unleashes its power, how do people use &lt;code>eval&lt;/code>? Is it dangerous? Is it somehow analyzable? Can it be replaced automatically by more specialized functions?&lt;/p></description></item><item><title>Rhythm Quantization</title><link>https://www.pdonatbouillud.com/project/rythm-quantization/</link><pubDate>Sun, 12 Apr 2020 17:08:30 +0200</pubDate><guid>https://www.pdonatbouillud.com/project/rythm-quantization/</guid><description>&lt;p>We represent rhythms as trees. Rhythm trees can be changed into other rhythm trees using &lt;em>rewriting rules&lt;/em>. It makes it possible to define an equivalence relation between rythms. Some of the rewriting rules simplify the rhythms and are used to quantify them.&lt;/p>
&lt;figure id="figure-examples-of-rythms-with-slurs-and-dots-and-their-tree-representation">
&lt;a data-fancybox="" href="https://www.pdonatbouillud.com/project/rythm-quantization/slurs_huab565c6698300604706897a4197fe6bc_78978_2000x2000_fit_lanczos_2.png" data-caption="Examples of rythms with slurs and dots and their tree representation.">
&lt;img data-src="https://www.pdonatbouillud.com/project/rythm-quantization/slurs_huab565c6698300604706897a4197fe6bc_78978_2000x2000_fit_lanczos_2.png" class="lazyload" alt="" width="800" height="274">
&lt;/a>
&lt;figcaption>
Examples of rythms with slurs and dots and their tree representation.
&lt;/figcaption>
&lt;/figure>
&lt;figure id="figure-rewrite-sequence-starting-from-the-tree-d-of-previous-figure">
&lt;a data-fancybox="" href="https://www.pdonatbouillud.com/project/rythm-quantization/rewrite_hua099fa2e32025ec7176914fdf8ccebf9_88571_2000x2000_fit_lanczos_2.png" data-caption="Rewrite sequence starting from the tree (d) of previous figure.">
&lt;img data-src="https://www.pdonatbouillud.com/project/rythm-quantization/rewrite_hua099fa2e32025ec7176914fdf8ccebf9_88571_2000x2000_fit_lanczos_2.png" class="lazyload" alt="" width="800" height="235">
&lt;/a>
&lt;figcaption>
Rewrite sequence starting from the tree (d) of previous figure.
&lt;/figcaption>
&lt;/figure></description></item></channel></rss>