Huon on the internet2021-10-24T21:57:55+00:00https://huonw.github.io/Huon Wilsondbau.pp@gmail.comhttps://huonw.github.io/blog/2021/10/nsw-covid-qrMechanical sympathy for QR codes: making NSW check-in better2021-10-13T00:00:00+00:002021-10-13T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>Governments here in Australia have been telling us to keep distance from each other. Surprisingly, the same government has simultaneously put out posters that required people to get close, unnecessarily. They contain QR codes for contact-tracing check-ins that are small and dense, meaning they’re hard to scan. How could they be better?</p>
<p>Here’s how:</p>
<figure class="image ">
<div class="image-positioner">
<a href="qr-poster-comparison-three.png" style="text-decoration: none">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1893" height="890" viewBox="-12 -12 1893 890" role="img" aria-label="[description]">
<desc>Three pages placed horizontally. They all contain a 'we're covid safe' logo, text like 'please check in before entering our premises.' and a QR code. The left-most one labelled 'original' in red has a very small QR and dense code; the centre one labelled 'works now' in green has a much larger QR code that's slightly less dense; the right-most one labelled 'with planning' in orange also has a large QR code, that is much less dense.</desc>
<filter id="blur-text" width="130%" height="130%">
<feOffset result="offOut" in="SourceAlpha" dx="4" dy="8" />
<feColormatrix result="lightened" in="offOut" values="0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 1 0" />
<feGaussianBlur result="blurOut" in="lightened" stdDeviation="12" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
<g transform="translate(0 0)" data-width="619">
<g transform="translate(0 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="" />
<use href="#poster-page-original" xlink:href="#poster-page-original" class="" />
</g>
<use href="#qr-code-v16-H-original" xlink:href="#qr-code-v16-H-original" class=" qr-poster-original" />
</g>
<g transform="translate(619 0)" data-width="619">
<g transform="translate(0 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="" />
</g>
<use href="#qr-code-v11-Q-works-in-app" xlink:href="#qr-code-v11-Q-works-in-app" class=" qr-poster-new" />
</g>
<g transform="translate(1238 0)" data-width="619">
<g transform="translate(0 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="" />
</g>
<use href="#qr-code-v05-Q-optimized-offline" xlink:href="#qr-code-v05-Q-optimized-offline" class=" qr-poster-new" />
</g>
<text class="hovering-text-poster bad" dx="0" filter="url(#blur-text)">original</text>
<text class="hovering-text-poster good" dx="619" filter="url(#blur-text)">works now</text>
<text class="hovering-text-poster okay" dx="1238" filter="url(#blur-text)">optimised</text>
</svg>
</a>
</div>
</figure>
<p>The central one labelled “works now” could be rolled out right now. The existing native app <strong>can understand that poster</strong>, so if the NSW government switched to issuing something like that, life is magically easier for everyone. The “optimised” poster shows what could’ve been, if the QR code was optimised to the extreme, when the feature was initially deployed into the app.</p>
<p>In this article, we’ll walk through how to get to that point, touching on:</p>
<ul>
<li>the contents of the current check-in QR codes and how they function</li>
<li>the workings of QR codes in general (error correction, versions and encoding modes)</li>
<li>a walk-through of optimising the data stored in a QR code</li>
<li>the security of these QR codes</li>
</ul>
<p>Let’s learn some general lessons for using/designing QR codes by looking at this specific example.</p>
<aside data-icon="⚠️">
There's been a lot written about whether these QR codes/contact tracing is government overreach and about the governmental response to the pandemic more generally, and I am not adding to that here: these QR codes serve as an easy example of the technology.
</aside>
<h2 id="what-are-you-writing-about">What are you writing about?</h2>
<p>Almost every shopfront here in NSW and even across Australia has an A4 page with a QR code hidden in it. Customers entering need to scan to register their presence for contact tracing, which (if they’ve installed it) pushes them into the Service NSW app. The design is clever in some ways, but seemingly not so clever in other ways.</p>
<figure class="image has-caption">
<div class="image-positioner">
<a href="qr-poster-original.pdf" style="text-decoration: none">
<input class="poster-switch-checkbox" type="checkbox" id="checkbox-0" checked="" />
<label class="poster-switch-label" for="checkbox-0">
Switch to <strong>
<span class="poster-switch-posters">poster</span>
<span class="poster-switch-codes">code only</span>
</strong>
</label>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1376" height="890" viewBox="-12 -12 1376 890" role="img" aria-label="[description]">
<desc>An image with two panels. On the left, a page as above with text like 'please check in before entering our premises'. On the right, a screenshot of a mobile app: the page is titled 'COVID Safe Check-in', there's an info box saying 'Checking in to Test NSW Government QR code' </desc>
<filter id="blur-text" width="130%" height="130%">
<feOffset result="offOut" in="SourceAlpha" dx="4" dy="8" />
<feColormatrix result="lightened" in="offOut" values="0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 1 0" />
<feGaussianBlur result="blurOut" in="lightened" stdDeviation="12" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
<g transform="translate(0 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-original" xlink:href="#poster-page-original" class="switchable-poster" />
</g>
<use href="#qr-code-v16-H-original" xlink:href="#qr-code-v16-H-original" class="switchable-qr qr-poster-original" />
</g>
<image x="866" height="842" width="474" xlink:href="service-nsw-scan.png" href="service-nsw-scan.png" />
</svg>
</a>
</div>
<figcaption><p>The check-in poster and the view of the Service NSW app after scanning it. <a href="qr-poster-original.pdf">Tap to view the poster alone (PDF)</a>.</p>
</figcaption>
</figure>
<p>The QR code encodes a rather long URL: <a href="https://www.service.nsw.gov.au/campaign/service-nsw-mobile-app?data=eyJ0IjoiY292aWQxOV9idXNpbmVzcyIsImJpZCI6IjEyMTMyMSIsImJuYW1lIjoiVGVzdCBOU1cgR292ZXJubWVudCBRUiBjb2RlIiwiYmFkZHJlc3MiOiJCdXNpbmVzcyBhZGRyZXNzIGdvZXMgaGVyZSAifQ=="><code class="language-plaintext break-all highlighter-rouge">https://www.service.nsw.gov.au/campaign/service-nsw-mobile-app?data=eyJ0IjoiY292aWQxOV9idXNpbmVzcyIsImJpZCI6IjEyMTMyMSIsImJuYW1lIjoiVGVzdCBOU1cgR292ZXJubWVudCBRUiBjb2RlIiwiYmFkZHJlc3MiOiJCdXNpbmVzcyBhZGRyZXNzIGdvZXMgaGVyZSAifQ==</code></a>.</p>
<p>To fit all this in, it has to be dense: it has 81 modules (little squares) along each side, meaning it’s version 16.</p>
<aside data-icon="ℹ️">
QR codes come in 40 different sizes, called <strong>versions</strong>, starting with only 21 modules along each side at version 1 and increasing by four for each version: 25 for version 2, 29 for version 3, …, up to 177 for version 40.
</aside>
<p>The <code class="language-plaintext highlighter-rouge">eyJ0…fQ==</code> value of the <code class="language-plaintext highlighter-rouge">data</code> parameter looks like it might be encoded with <a href="https://en.wikipedia.org/wiki/Base64">base64</a>, and indeed, if we try decoding it under that assumption, we find some structured JSON:</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="code"><pre><span class="p">{</span><span class="w">
</span><span class="nl">"t"</span><span class="p">:</span><span class="w"> </span><span class="s2">"covid19_business"</span><span class="p">,</span><span class="w">
</span><span class="nl">"bid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"121321"</span><span class="p">,</span><span class="w">
</span><span class="nl">"bname"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Test NSW Government QR code"</span><span class="p">,</span><span class="w">
</span><span class="nl">"baddress"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Business address goes here "</span><span class="w">
</span><span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Putting those keys into words, we can guess that each QR code tells us:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">t</code>: it’s a COVID-19 check-in code</li>
<li><code class="language-plaintext highlighter-rouge">bid</code>: an identifier for the business</li>
<li><code class="language-plaintext highlighter-rouge">bname</code>: the business name</li>
<li><code class="language-plaintext highlighter-rouge">baddress</code>: the business address (this is a test QR code, but in real QR codes it looks something like “123 Street, Suburb NSW”)</li>
</ul>
<h2 id="evaluation">Evaluation</h2>
<p>These QR code posters have some great attributes:</p>
<ol>
<li>They <strong>exist</strong> at all: the contact tracing situation was a mess until the Service NSW codes were rolled out and made mandatory. Some venues would require hand written entries, others would require entering personal information into random websites (of varying ease-of-use). Now, it’s a single app that remembers your details.</li>
<li><strong>Using a URL</strong> (rather than just raw data) means they can be usefully interpreted by any scanner, such as a phone’s camera, rather than requiring scanning with a specific app.</li>
<li>There’s a native app that could theoretically be used to do an <strong>offline check-in</strong> when there’s no internet access, for later syncing… although this doesn’t actually work in practice, with the current app. The QR code contains enough info to still <strong>display a confirmation</strong> to the user, so they know they scanned what they expected.</li>
<li>The use of <strong>high error correction</strong> means they’re theoretically resilient to ‘damage’, like reflections off lamination or windows, or even dirt and holes.</li>
<li>The poster <strong>explains</strong> how to scan the code (it seems unthinkable now, but these codes were introduced into a world where QR codes were <em>not</em> instantly recognisable).</li>
</ol>
<figure class="image has-caption">
<div class="image-positioner">
<a href="service-nsw-scan-header.png">
<img src="service-nsw-scan-header.png" alt="An image containing a crop of only the top region of the Service NSW app screenshot above. It focuses on the page title 'COVID Safe Check-in' and a box that says 'Checking in to Test NSW Government QR code' and 'Not the right venue? scan again'." width="800" height="392" />
</a>
</div>
<figcaption><p>The Service NSW app shows the venue name when checking in, even offline.</p>
</figcaption>
</figure>
<p>On the other hand, they could be better:</p>
<ol>
<li>The QR code is physically tiny.</li>
<li>Using the highest level of error correction might be taking things a little far.</li>
<li>The URL could be formatted better to take advantage of how QR codes are constructed.</li>
<li>There’s unnecessary data stored in the URL.</li>
<li>The URL could be much shorter overall.</li>
</ol>
<p>Let’s go through these, and see what difference they can make. We’ll ignore the realities of actually implementing these in the context of a real and existing app/infrastructure, which can change the appropriate technical decisions dramatically.</p>
<h2 id="bigger-qr-code">Bigger QR code</h2>
<p>The QR code is small in the poster. When printed at the default A4<sup id="fnref:a4" role="doc-noteref"><a href="#fn:a4" class="footnote" rel="footnote">0</a></sup>, it’s only ~5cm (2 inches) on each side, which means about <strong>less than 5%</strong> of the total page area is QR code, but that’s what people need to interact with.</p>
<p>Making it larger would make it much easier to scan, and doesn’t change anything about the QR code itself, so will definitely continue to work with the existing app. I imagine that most people in NSW are familiar with how to use the codes now, so the instructions could be de-emphasised.</p>
<p>I did some really quick shuffling and resizing of the page elements, without editing or reflowing text (just deleting the blue box around the code<sup id="fnref:quiet-zone" role="doc-noteref"><a href="#fn:quiet-zone" class="footnote" rel="footnote">1</a></sup>), and it’s definitely possible to have the code be bigger. It could be even larger still with a little more editing elbow-grease applied.</p>
<figure class="image ">
<div class="image-positioner">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1274" height="890" viewBox="-12 -12 1274 890" role="img" aria-label="[description]">
<desc>Two pages placed horizontally. As above, they both contain a 'we're covid safe' logo, text like 'please check in before entering our premises.' and a QR code. The left-most one labelled 'original' has a very small QR and dense code; the right one labelled 'rearranged' has a much larger QR code with the surrounding text rearranged slightly.</desc>
<filter id="blur-text" width="130%" height="130%">
<feOffset result="offOut" in="SourceAlpha" dx="4" dy="8" />
<feColormatrix result="lightened" in="offOut" values="0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 1 0" />
<feGaussianBlur result="blurOut" in="lightened" stdDeviation="12" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
<g transform="translate(0 0)" data-width="619">
<g transform="translate(0 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="" />
<use href="#poster-page-original" xlink:href="#poster-page-original" class="" />
</g>
<use href="#qr-code-v16-H-original" xlink:href="#qr-code-v16-H-original" class=" qr-poster-original" />
</g>
<g transform="translate(619 0)" data-width="619">
<g transform="translate(0 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="" />
</g>
<use href="#qr-code-v16-H-original" xlink:href="#qr-code-v16-H-original" class=" qr-poster-new" />
</g>
<text class="hovering-text-poster bad" dx="0" filter="url(#blur-text)">original</text>
<text class="hovering-text-poster good" dx="619" filter="url(#blur-text)">rearranged</text>
</svg>
</div>
</figure>
<p>When printed on an A4 page, the code in the new version is 16cm on each side, more than three times larger, and so can probably be scanned from <strong>three times further away</strong>. It now consumes ~40% of the page area.</p>
<p>(After publishing, <a href="https://news.ycombinator.com/item?id=28847827">several</a> <a href="https://twitter.com/nickzoic/status/1448429172242587649">people</a> pointed out that truly huge QR codes seem to cause difficulties with scanning in practice in some cases. One would need to actually test the design in practice to validate a particular size.)</p>
<h2 id="better-qr-code">Better QR code</h2>
<p>We’ve done the easiest step of making the QR larger, let’s now make the QR code within the overall poster better. Here’s the sequence of changes we’ll apply, moving from left to right and top to bottom. The codes get simpler, with larger modules (little squares), and thus become easier to scan.</p>
<figure class="image ">
<div class="image-positioner">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2634" height="890" viewBox="-12 -12 2634 890" role="img" aria-label="[description]">
<desc>Three QR codes placed horizontally. They get less dense from left to right, and are labelled: 'original' in red, 'error correction Q' in orange, 'without address' in orange.</desc>
<filter id="blur-text" width="130%" height="130%">
<feOffset result="offOut" in="SourceAlpha" dx="4" dy="8" />
<feColormatrix result="lightened" in="offOut" values="0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 1 0" />
<feGaussianBlur result="blurOut" in="lightened" stdDeviation="12" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
<g transform="translate(0 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-original" xlink:href="#poster-page-original" class="switchable-poster" />
</g>
<use href="#qr-code-v16-H-original" xlink:href="#qr-code-v16-H-original" class="switchable-qr qr-poster-original" />
</g>
<g transform="translate(866 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v13-Q-original" xlink:href="#qr-code-v13-Q-original" class="switchable-qr qr-poster-new" />
</g>
<g transform="translate(1732 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v11-Q-works-in-app" xlink:href="#qr-code-v11-Q-works-in-app" class="switchable-qr qr-poster-new" />
</g>
<text class="hovering-text-qr bad" dx="0" filter="url(#blur-text)">original</text>
<text class="hovering-text-qr okay" dx="866" filter="url(#blur-text)">error correction Q</text>
<text class="hovering-text-qr okay" dx="1732" filter="url(#blur-text)">remove address</text>
</svg>
</div>
</figure>
<figure class="image ">
<div class="image-positioner">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2634" height="890" viewBox="-12 -12 2634 890" role="img" aria-label="[description]">
<desc>Three QR codes placed horizontally. They get less dense from left to right, and are labelled: 'better encoding' in orange, 'short path' in green, 'remove name' in orange, </desc>
<filter id="blur-text" width="130%" height="130%">
<feOffset result="offOut" in="SourceAlpha" dx="4" dy="8" />
<feColormatrix result="lightened" in="offOut" values="0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 1 0" />
<feGaussianBlur result="blurOut" in="lightened" stdDeviation="12" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
<g transform="translate(0 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v07-Q-path-encoding" xlink:href="#qr-code-v07-Q-path-encoding" class="switchable-qr qr-poster-new" />
</g>
<g transform="translate(866 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v05-Q-short-path" xlink:href="#qr-code-v05-Q-short-path" class="switchable-qr qr-poster-new" />
</g>
<g transform="translate(1732 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v02-Q-optimized-tiny" xlink:href="#qr-code-v02-Q-optimized-tiny" class="switchable-qr qr-poster-new" />
</g>
<text class="hovering-text-qr okay" dx="0" filter="url(#blur-text)">better encoding</text>
<text class="hovering-text-qr good" dx="866" filter="url(#blur-text)">short path</text>
<text class="hovering-text-qr okay" dx="1732" filter="url(#blur-text)">remove name</text>
</svg>
</div>
</figure>
<p>We’ll see how the “short path” QR code in green retains all of the valuable properties we identified above, including simple offline check-in support. The “remove name” code is even simpler, and is what is possible if we compromise offline check-ins nicely.</p>
<h3 id="less-error-correction">Less error correction</h3>
<p>The QR code uses the highest error correction (EC) level: H. As explored in <a href="/blog/2021/09/qr-error-correction/"><em>QR error correction helps and hinders scanning</em></a>, there’s four levels of error correction, and they correspond to how many modules can be corrupted and still have the QR code scannable. They also influence how much data can be stored:</p>
<table>
<thead>
<tr>
<th>EC level</th>
<th style="text-align: right">max damage</th>
<th style="text-align: right">data storage (vs. L)</th>
</tr>
</thead>
<tbody>
<tr>
<td>H (high)</td>
<td style="text-align: right">30%</td>
<td style="text-align: right">43%</td>
</tr>
<tr>
<td>Q (quartile)</td>
<td style="text-align: right">25%</td>
<td style="text-align: right">57%</td>
</tr>
<tr>
<td>M (medium)</td>
<td style="text-align: right">15%</td>
<td style="text-align: right">79%</td>
</tr>
<tr>
<td>L (low)</td>
<td style="text-align: right">7%</td>
<td style="text-align: right">100%</td>
</tr>
</tbody>
</table>
<p>I imagine that most codes are placed in relatively non-hostile environments (indoors, or at least under cover), so the use of EC level H could be reduced, or made configurable. Dropping down one level, to Q, reduces the version from 16 to 13, making each module (small square) larger—17% more area—and thus the overall QR code easier to scan.</p>
<p>Dropping further makes the modules larger, although the biggest win comes from moving from H to Q, and each lower level is less resilient.</p>
<table>
<thead>
<tr>
<th>EC level</th>
<th style="text-align: right">version</th>
<th style="text-align: right">module size (vs. H)</th>
<th style="text-align: right">vs. previous</th>
</tr>
</thead>
<tbody>
<tr>
<td>H</td>
<td style="text-align: right">16</td>
<td style="text-align: right">-</td>
<td style="text-align: right">-</td>
</tr>
<tr>
<td>Q</td>
<td style="text-align: right">13</td>
<td style="text-align: right">+17%</td>
<td style="text-align: right">+17%</td>
</tr>
<tr>
<td>M</td>
<td style="text-align: right">11</td>
<td style="text-align: right">+33%</td>
<td style="text-align: right">+13%</td>
</tr>
<tr>
<td>L</td>
<td style="text-align: right">9</td>
<td style="text-align: right">+53%</td>
<td style="text-align: right">+15%</td>
</tr>
</tbody>
</table>
<p>This also doesn’t change anything about the data that is encoded, and so should continue to work with the existing app.</p>
<p>We do want these codes to be reasonably resilient to damage, so let’s <strong>choose Q</strong>.</p>
<figure class="image ">
<div class="image-positioner">
<input class="poster-switch-checkbox" type="checkbox" id="checkbox-1" />
<label class="poster-switch-label" for="checkbox-1">
Switch to <strong>
<span class="poster-switch-posters">posters</span>
<span class="poster-switch-codes">codes only</span>
</strong>
</label>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="3500" height="890" viewBox="-12 -12 3500 890" role="img" aria-label="[description]">
<desc>Four QR codes placed horizontally. They get less dense from left to right, and are labelled: H in red, Q in green, M in orange and L in orange.</desc>
<filter id="blur-text" width="130%" height="130%">
<feOffset result="offOut" in="SourceAlpha" dx="4" dy="8" />
<feColormatrix result="lightened" in="offOut" values="0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 1 0" />
<feGaussianBlur result="blurOut" in="lightened" stdDeviation="12" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
<g transform="translate(0 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v16-H-original" xlink:href="#qr-code-v16-H-original" class="switchable-qr qr-poster-new" />
</g>
<g transform="translate(866 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v13-Q-original" xlink:href="#qr-code-v13-Q-original" class="switchable-qr qr-poster-new" />
</g>
<g transform="translate(1732 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v11-M-original" xlink:href="#qr-code-v11-M-original" class="switchable-qr qr-poster-new" />
</g>
<g transform="translate(2598 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v09-L-original" xlink:href="#qr-code-v09-L-original" class="switchable-qr qr-poster-new" />
</g>
<text class="hovering-text-qr bad big" dx="0" filter="url(#blur-text)">H</text>
<text class="hovering-text-qr good big" dx="866" filter="url(#blur-text)">Q</text>
<text class="hovering-text-qr okay big" dx="1732" filter="url(#blur-text)">M</text>
<text class="hovering-text-qr okay big" dx="2598" filter="url(#blur-text)">L</text>
</svg>
</div>
</figure>
<h3 id="structure-of-the-url">Structure of the URL</h3>
<p>We’ve done the easiest parts, now we need to actually think about the data encoded in the QR code. Less data means a smaller version, which means larger modules and thus easier scanning. This QR code stores a URL that contains a large base64 JSON snippet. We can optimise in a few ways, some of which seem to work with existing apps, and some which don’t. We can look at the parts of the URL independently:</p>
<ol>
<li><strong>scheme</strong>: <code class="language-plaintext highlighter-rouge">https://</code></li>
<li><strong>domain</strong>: <code class="language-plaintext highlighter-rouge">www.service.nsw.gov.au</code></li>
<li><strong>path</strong>: <code class="language-plaintext highlighter-rouge">campaign/service-nsw-mobile-app</code></li>
<li><strong>query</strong>: <code class="language-plaintext break-all highlighter-rouge">data=eyJ0IjoiY292aWQxOV9idXNpbmVzcyIsImJpZCI6IjEyMTMyMSIsImJuYW1lIjoiVGVzdCBOU1cgR292ZXJubWVudCBRUiBjb2RlIiwiYmFkZHJlc3MiOiJCdXNpbmVzcyBhZGRyZXNzIGdvZXMgaGVyZSAifQ==</code></li>
</ol>
<p>The simplest part to look at is the scheme, because there’s two options <code class="language-plaintext highlighter-rouge">https://</code> and old-school <code class="language-plaintext highlighter-rouge">http://</code>: we could save a character but it doesn’t seem worth dropping the encryption. Let’s leave it, and <strong>choose <code class="language-plaintext highlighter-rouge">https://</code></strong>.</p>
<h3 id="less-data-remove-unnecessary-information">Less data: remove unnecessary information</h3>
<p>The lowest hanging fruit for actual change is in 4, the query. This query is currently a single parameter <code class="language-plaintext highlighter-rouge">data</code> with value base64-encoded JSON value <code class="language-plaintext highlighter-rouge">eyJ0…fQ==</code> . The JSON includes a <code class="language-plaintext highlighter-rouge">baddress</code> field… I <strong>cannot find any place</strong> in the app or website that displays this, and indeed removing it seems to function just fine. The resulting encoded JSON is <code class="language-plaintext break-all highlighter-rouge">eyJ0IjoiY292aWQxOV9idXNpbmVzcyIsImJpZCI6IjEyMTMyMSIsImJuYW1lIjoiVGVzdCBOU1cgR292ZXJubWVudCBRUiBjb2RlIn0=</code>, which is 104 characters, down from 160.</p>
<table>
<thead>
<tr>
<th>JSON value</th>
<th style="text-align: right">URL length</th>
<th style="text-align: right">version at Q</th>
</tr>
</thead>
<tbody>
<tr>
<td>Original with <code class="language-plaintext highlighter-rouge">baddress</code></td>
<td style="text-align: right">228</td>
<td style="text-align: right">13</td>
</tr>
<tr>
<td>New without <code class="language-plaintext highlighter-rouge">baddress</code></td>
<td style="text-align: right">172</td>
<td style="text-align: right">11</td>
</tr>
</tbody>
</table>
<p>Sounds good. Let’s <strong>choose to remove the <code class="language-plaintext highlighter-rouge">baddress</code> field</strong>.</p>
<figure class="image ">
<div class="image-positioner">
<input class="poster-switch-checkbox" type="checkbox" id="checkbox-2" />
<label class="poster-switch-label" for="checkbox-2">
Switch to <strong>
<span class="poster-switch-posters">posters</span>
<span class="poster-switch-codes">codes only</span>
</strong>
</label>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2634" height="890" viewBox="-12 -12 2634 890" role="img" aria-label="[description]">
<desc>Three QR codes placed horizontally. The left two labelled 'original' and 'previous' have denser patterns than the right one labelled 'remove address'.</desc>
<filter id="blur-text" width="130%" height="130%">
<feOffset result="offOut" in="SourceAlpha" dx="4" dy="8" />
<feColormatrix result="lightened" in="offOut" values="0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 1 0" />
<feGaussianBlur result="blurOut" in="lightened" stdDeviation="12" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
<g transform="translate(0 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-original" xlink:href="#poster-page-original" class="switchable-poster" />
</g>
<use href="#qr-code-v16-H-original" xlink:href="#qr-code-v16-H-original" class="switchable-qr qr-poster-original" />
</g>
<g transform="translate(866 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v13-Q-original" xlink:href="#qr-code-v13-Q-original" class="switchable-qr qr-poster-new" />
</g>
<g transform="translate(1732 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v11-Q-works-in-app" xlink:href="#qr-code-v11-Q-works-in-app" class="switchable-qr qr-poster-new" />
</g>
<text class="hovering-text-qr bad" dx="0" filter="url(#blur-text)">original</text>
<text class="hovering-text-qr okay" dx="866" filter="url(#blur-text)">previous</text>
<text class="hovering-text-qr good" dx="1732" filter="url(#blur-text)">remove address</text>
</svg>
</div>
</figure>
<h3 id="modes-upper-casing">Modes: upper-casing</h3>
<p>QR codes encode the data in one of <a href="https://en.wikipedia.org/wiki/QR_code#Storage">four modes</a>, based on the characters that the data contains:</p>
<table>
<thead>
<tr>
<th>Mode</th>
<th>Permitted characters</th>
<th style="text-align: right">Bits per character</th>
</tr>
</thead>
<tbody>
<tr>
<td>Numeric</td>
<td>0123456789</td>
<td style="text-align: right">3.33</td>
</tr>
<tr>
<td>Alphanumeric</td>
<td>0–9, A–Z, space, $%*+-./:</td>
<td style="text-align: right">5.5</td>
</tr>
<tr>
<td>Binary</td>
<td>any byte</td>
<td style="text-align: right">8</td>
</tr>
<tr>
<td>Kanji</td>
<td><a href="https://en.wikipedia.org/wiki/JIS_X_0208">Shift JIS X 0208</a></td>
<td style="text-align: right">13</td>
</tr>
</tbody>
</table>
<p>Lower bits per character is better: it means we can fit more characters in a given space. Most data in URL QR codes will use the Binary mode, because there will typically be lowercase letters, but we can do better using the Alphanumeric one: a lot of components of a URL can be upper case without causing issues or changing the behaviour. For instance, the domain name is case-insensitive, and a lot of servers treat the path case-insensitively too. Base64 encoding is case-sensitive so we cannot upper-case.</p>
<p>Fortunately, QR codes can encode the data <strong>in multiple segments with different modes</strong><sup id="fnref:optimal" role="doc-noteref"><a href="#fn:optimal" class="footnote" rel="footnote">2</a></sup>, so we can still benefit from this: the first part can be encoded in Alphanumeric, while the second part with the base64 value can be Binary.</p>
<table>
<thead>
<tr>
<th>url</th>
<th style="text-align: right">works with app</th>
<th style="text-align: right">L</th>
<th style="text-align: right">Q</th>
</tr>
</thead>
<tbody>
<tr>
<td>Original:<br /><code class="language-plaintext break-all highlighter-rouge">https://www.service.nsw.gov.au/campaign/service-nsw-mobile-app?data=eyJ…</code></td>
<td style="text-align: right">yes</td>
<td style="text-align: right">8</td>
<td style="text-align: right">11</td>
</tr>
<tr>
<td>Upper-case domain and scheme:<br /><code class="language-plaintext break-all highlighter-rouge">HTTPS://WWW.SERVICE.NSW.GOV.AU/campaign/service-nsw-mobile-app?data=eyJ…</code></td>
<td style="text-align: right">yes</td>
<td style="text-align: right">8</td>
<td style="text-align: right">11</td>
</tr>
<tr>
<td>Only data lower-case:<br /><code class="language-plaintext break-all highlighter-rouge">HTTPS://WWW.SERVICE.NSW.GOV.AU/CAMPAIGN/SERVICE-NSW-MOBILE-APP?DATA=eyJ…</code></td>
<td style="text-align: right">no</td>
<td style="text-align: right">7</td>
<td style="text-align: right">11</td>
</tr>
</tbody>
</table>
<p>The second option—upper-casing only the domain and scheme—is as far as we get and still (seemingly) <strong>work with existing app installations</strong>.</p>
<p>The upper-casing of that option doesn’t happen to make a difference for the data we have here, although it does reduce the number of bits required total, so it may make a difference for some businesses where their name/ID happens to push them just above a version boundary.</p>
<p>The last option—upper-casing everything except for the base64 value—also doesn’t make much difference at our chosen error correction level Q, but it does at others like L.</p>
<p>Since we’re at the limit, we’re now going off into the world of imagination, where bureaucracy is made up and existing users don’t matter. So let’s <strong>lock in the last one</strong>.</p>
<h3 id="less-data-encode-better">Less data: encode better</h3>
<p>Our URL now looks like: <code class="language-plaintext break-all highlighter-rouge">HTTPS://WWW.SERVICE.NSW.GOV.AU/CAMPAIGN/SERVICE-NSW-MOBILE-APP?DATA=eyJ0IjoiY292aWQxOV9idXNpbmVzcyIsImJpZCI6IjEyMTMyMSIsImJuYW1lIjoiVGVzdCBOU1cgR292ZXJubWVudCBRUiBjb2RlIn0=</code>.</p>
<p>The base64 JSON value <code class="language-plaintext highlighter-rouge">eyJ0…In0=</code> looks like it’s still worth thinking about: it now contains less data, but it’s still takes 70% of the storage in the QR code (880 bits out of 1256 total). It’s stored in the URL in a <code class="language-plaintext highlighter-rouge">?DATA=</code> query parameter, and the data it contains is:</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre><span class="p">{</span><span class="w">
</span><span class="nl">"t"</span><span class="p">:</span><span class="w"> </span><span class="s2">"covid19_business"</span><span class="p">,</span><span class="w">
</span><span class="nl">"bid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"121321"</span><span class="p">,</span><span class="w">
</span><span class="nl">"bname"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Test NSW Government QR code"</span><span class="p">,</span><span class="w">
</span><span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Unfortunately there’s a lot of overhead from JSON (all the <code class="language-plaintext highlighter-rouge">{":,</code>s), and then even more overhead from base64.</p>
<p>We can do better, because we’ve got simple textual data and simple values (plain strings). This means that we could pass the values in the URL directly, either via a query parameters, or in the path directly, which saves us a little overhead of <code class="language-plaintext highlighter-rouge">?N=…</code>. We’ve already got the <code class="language-plaintext highlighter-rouge">/C</code> in the URL to indicate a check-in, so we can probably drop the <code class="language-plaintext highlighter-rouge">"t": "covid19_business"</code>, and our scheme here is very hand-crafted, so we can optimise the parameters down as much as we like.</p>
<table>
<thead>
<tr>
<th>encoding</th>
<th style="text-align: right">length</th>
<th style="text-align: right">Q</th>
</tr>
</thead>
<tbody>
<tr>
<td>Original, JSON + Base64:<br /><code class="language-plaintext break-all highlighter-rouge">...?DATA=eyJ0…</code></td>
<td style="text-align: right">172</td>
<td style="text-align: right">11</td>
</tr>
<tr>
<td>URL parameters:<br /><code class="language-plaintext break-all highlighter-rouge">...?T=COVID19_BUSINESS&BID=121321&BNAME=name…</code></td>
<td style="text-align: right">126</td>
<td style="text-align: right">8</td>
</tr>
<tr>
<td>Better URL parameters:<br /><code class="language-plaintext break-all highlighter-rouge">...?I=121321&N=name…</code></td>
<td style="text-align: right">101</td>
<td style="text-align: right">7</td>
</tr>
<tr>
<td>ID in path:<br /><code class="language-plaintext break-all highlighter-rouge">.../121321&N=name…</code></td>
<td style="text-align: right">99</td>
<td style="text-align: right">7</td>
</tr>
<tr>
<td>Everything in path:<br /><code class="language-plaintext break-all highlighter-rouge">.../121321/name…</code></td>
<td style="text-align: right">67</td>
<td style="text-align: right">7</td>
</tr>
</tbody>
</table>
<p>This makes a huge difference. Our URL is now down to 97 characters (from 228 originally): <code class="language-plaintext break-all highlighter-rouge">HTTPS://WWW.SERVICE.NSW.GOV.AU/CAMPAIGN/SERVICE-NSW-MOBILE-APP/121321/Test+NSW+Government+QR+code</code>.</p>
<p>Let’s lock that last option in, <strong>choosing to encode things efficiently in the path</strong>.</p>
<figure class="image ">
<div class="image-positioner">
<input class="poster-switch-checkbox" type="checkbox" id="checkbox-4" />
<label class="poster-switch-label" for="checkbox-4">
Switch to <strong>
<span class="poster-switch-posters">posters</span>
<span class="poster-switch-codes">codes only</span>
</strong>
</label>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2634" height="890" viewBox="-12 -12 2634 890" role="img" aria-label="[description]">
<desc>Three QR codes placed horizontally. The left two labelled 'original' and 'previous' have denser patterns than the right one labelled 'better encoding'.</desc>
<filter id="blur-text" width="130%" height="130%">
<feOffset result="offOut" in="SourceAlpha" dx="4" dy="8" />
<feColormatrix result="lightened" in="offOut" values="0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 1 0" />
<feGaussianBlur result="blurOut" in="lightened" stdDeviation="12" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
<g transform="translate(0 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-original" xlink:href="#poster-page-original" class="switchable-poster" />
</g>
<use href="#qr-code-v16-H-original" xlink:href="#qr-code-v16-H-original" class="switchable-qr qr-poster-original" />
</g>
<g transform="translate(866 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v11-Q-works-in-app" xlink:href="#qr-code-v11-Q-works-in-app" class="switchable-qr qr-poster-new" />
</g>
<g transform="translate(1732 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v07-Q-path-encoding" xlink:href="#qr-code-v07-Q-path-encoding" class="switchable-qr qr-poster-new" />
</g>
<text class="hovering-text-qr bad" dx="0" filter="url(#blur-text)">original</text>
<text class="hovering-text-qr okay" dx="866" filter="url(#blur-text)">previous</text>
<text class="hovering-text-qr good" dx="1732" filter="url(#blur-text)">better encoding</text>
</svg>
</div>
</figure>
<p>Look at how much simpler that QR code is.</p>
<h3 id="less-data-a-better-path">Less data: a better path</h3>
<p>Next, the initial <code class="language-plaintext highlighter-rouge">CAMPAIGN/SERVICE-NSW-MOBILE-APP</code> components of the path are rather long… do we really need to spell out “campaign” and “mobile app”? At least it’s not “mobile telephone application”!</p>
<p>Let’s just cut that down. There’s two nice options here: no path at all, or a very short one.</p>
<table>
<thead>
<tr>
<th>path</th>
<th style="text-align: right">length</th>
<th style="text-align: right">Q</th>
</tr>
</thead>
<tbody>
<tr>
<td>Original <code class="language-plaintext break-all highlighter-rouge">CAMPAIGN/SERVICE-NSW-MOBILE-APP</code></td>
<td style="text-align: right">97</td>
<td style="text-align: right">7</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">C</code></td>
<td style="text-align: right">67</td>
<td style="text-align: right">5</td>
</tr>
<tr>
<td>Empty</td>
<td style="text-align: right">65</td>
<td style="text-align: right">5</td>
</tr>
</tbody>
</table>
<p>There’s not much difference here, so it feels better to include the <code class="language-plaintext highlighter-rouge">C</code> to distinguish when a link is for a check-in. If the path is empty, the URL looks like <code class="language-plaintext break-all highlighter-rouge">HTTPS://WWW.SERVICE.NSW.GOV.AU/121321/...</code>, which means the front page of <code class="language-plaintext break-all highlighter-rouge">HTTPS://WWW.SERVICE.NSW.GOV.AU</code> needs to be detecting whether the path looks like a check-in ID and redirect to the appropriate page (when loaded in a web browser), and that sounds annoying and would require relatively unusual code.</p>
<p>Let’s choose this <strong>short single-character path</strong>.</p>
<figure class="image ">
<div class="image-positioner">
<input class="poster-switch-checkbox" type="checkbox" id="checkbox-5" />
<label class="poster-switch-label" for="checkbox-5">
Switch to <strong>
<span class="poster-switch-posters">posters</span>
<span class="poster-switch-codes">codes only</span>
</strong>
</label>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2634" height="890" viewBox="-12 -12 2634 890" role="img" aria-label="[description]">
<desc>Three QR codes placed horizontally. The left two labelled 'original' and 'previous' have denser patterns than the right one labelled 'shorter path'.</desc>
<filter id="blur-text" width="130%" height="130%">
<feOffset result="offOut" in="SourceAlpha" dx="4" dy="8" />
<feColormatrix result="lightened" in="offOut" values="0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 1 0" />
<feGaussianBlur result="blurOut" in="lightened" stdDeviation="12" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
<g transform="translate(0 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-original" xlink:href="#poster-page-original" class="switchable-poster" />
</g>
<use href="#qr-code-v16-H-original" xlink:href="#qr-code-v16-H-original" class="switchable-qr qr-poster-original" />
</g>
<g transform="translate(866 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v07-Q-path-encoding" xlink:href="#qr-code-v07-Q-path-encoding" class="switchable-qr qr-poster-new" />
</g>
<g transform="translate(1732 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v05-Q-short-path" xlink:href="#qr-code-v05-Q-short-path" class="switchable-qr qr-poster-new" />
</g>
<text class="hovering-text-qr bad" dx="0" filter="url(#blur-text)">original</text>
<text class="hovering-text-qr okay" dx="866" filter="url(#blur-text)">previous</text>
<text class="hovering-text-qr good" dx="1732" filter="url(#blur-text)">shorter path</text>
</svg>
</div>
</figure>
<h3 id="less-data-better-domain">Less data: better domain</h3>
<p>The domain being used is long: <code class="language-plaintext break-all highlighter-rouge">WWW.SERVICE.NSW.GOV.AU</code>. We definitely don’t need to be spelling that all out. There’s a variety of options here: shortenings of the current URL like <code class="language-plaintext break-all highlighter-rouge">Q.SERVICE.NSW.GOV.AU</code> or <code class="language-plaintext highlighter-rouge">S.NSW.GOV.AU</code>; or something really short, like <code class="language-plaintext highlighter-rouge">NSW.AU</code> (this domain doesn’t exist, but any 6 character domain would be equivalent). The best choice probably depends on the bureaucracy and dev/sys-ops requirements with the relevant organisations.</p>
<table>
<thead>
<tr>
<th>domain</th>
<th style="text-align: right">length</th>
<th style="text-align: right">L</th>
<th style="text-align: right">Q</th>
</tr>
</thead>
<tbody>
<tr>
<td>Original<br /><code class="language-plaintext break-all highlighter-rouge">WWW.SERVICE.NSW.GOV.AU</code></td>
<td style="text-align: right">67</td>
<td style="text-align: right">4</td>
<td style="text-align: right">5</td>
</tr>
<tr>
<td><code class="language-plaintext break-all highlighter-rouge">Q.SERVICE.NSW.GOV.AU</code></td>
<td style="text-align: right">65</td>
<td style="text-align: right">4</td>
<td style="text-align: right">5</td>
</tr>
<tr>
<td><code class="language-plaintext break-all highlighter-rouge">S.NSW.GOV.AU</code></td>
<td style="text-align: right">57</td>
<td style="text-align: right">3</td>
<td style="text-align: right">5</td>
</tr>
<tr>
<td><code class="language-plaintext break-all highlighter-rouge">NSW.AU</code></td>
<td style="text-align: right">51</td>
<td style="text-align: right">3</td>
<td style="text-align: right">4</td>
</tr>
</tbody>
</table>
<p>Most of these choices don’t a difference at level Q, but they do reduce the overall data (and thus may make a difference in some cases) and reduce the version at level L. Let’s <strong>choose <code class="language-plaintext highlighter-rouge">S.NSW.GOV.AU</code></strong>, because being scoped within the <code class="language-plaintext highlighter-rouge">GOV.AU</code> second-level domain seems more trustworthy: <code class="language-plaintext break-all highlighter-rouge">HTTPS://S.NSW.GOV.AU/C/121321/Test+NSW+Government+QR+code</code>.</p>
<p>This 57-character URL still contains all the same useful information as the original 228-character one. Here’s the four pieces of data that were in the JSON snippet:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">"t": "covid19_business"</code> ⇒ <code class="language-plaintext highlighter-rouge">/C/</code> in the path</li>
<li><code class="language-plaintext highlighter-rouge">"bid": "121321"</code> ⇒ first path parameter</li>
<li><code class="language-plaintext highlighter-rouge">"bname": "Test NSW Government QR code"</code> ⇒ second path parameter</li>
<li><code class="language-plaintext highlighter-rouge">"baddress": "…"</code> ⇒ removed, because it is not used</li>
</ul>
<figure class="image ">
<div class="image-positioner">
<input class="poster-switch-checkbox" type="checkbox" id="checkbox-6" />
<label class="poster-switch-label" for="checkbox-6">
Switch to <strong>
<span class="poster-switch-posters">posters</span>
<span class="poster-switch-codes">codes only</span>
</strong>
</label>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2634" height="890" viewBox="-12 -12 2634 890" role="img" aria-label="[description]">
<desc>Three QR codes placed horizontally. The left one labelled 'original' has denser patterns than the right two labelled 'previous' and 'better domain'.</desc>
<filter id="blur-text" width="130%" height="130%">
<feOffset result="offOut" in="SourceAlpha" dx="4" dy="8" />
<feColormatrix result="lightened" in="offOut" values="0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 1 0" />
<feGaussianBlur result="blurOut" in="lightened" stdDeviation="12" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
<g transform="translate(0 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-original" xlink:href="#poster-page-original" class="switchable-poster" />
</g>
<use href="#qr-code-v16-H-original" xlink:href="#qr-code-v16-H-original" class="switchable-qr qr-poster-original" />
</g>
<g transform="translate(866 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v05-Q-short-path" xlink:href="#qr-code-v05-Q-short-path" class="switchable-qr qr-poster-new" />
</g>
<g transform="translate(1732 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v05-Q-optimized-offline" xlink:href="#qr-code-v05-Q-optimized-offline" class="switchable-qr qr-poster-new" />
</g>
<text class="hovering-text-qr bad" dx="0" filter="url(#blur-text)">original</text>
<text class="hovering-text-qr okay" dx="866" filter="url(#blur-text)">previous</text>
<text class="hovering-text-qr good" dx="1732" filter="url(#blur-text)">better domain</text>
</svg>
</div>
</figure>
<p>These final two QR codes look similar because they’re both version 5; the density and size of modules hasn’t changed, but their exact arrangement has.</p>
<h3 id="reducing-offline-support">Reducing offline support</h3>
<p>We’re pretty much at the limit of what is possible when the URL includes the business name to enable offline check-ins<sup id="fnref:compression" role="doc-noteref"><a href="#fn:compression" class="footnote" rel="footnote">3</a></sup>. What if we drop the requirement to encode this? For instance, the app could say “you’re checking into a business” rather than show the name for confirmation, or even have the app stores its own database mapping IDs to business names<sup id="fnref:database" role="doc-noteref"><a href="#fn:database" class="footnote" rel="footnote">4</a></sup>.</p>
<p>If we do that, the URL can be 29 characters: <code class="language-plaintext break-all highlighter-rouge">HTTPS://S.NSW.GOV.AU/C/121321</code>. This fits in a version 2 QR code!</p>
<figure class="image ">
<div class="image-positioner">
<input class="poster-switch-checkbox" type="checkbox" id="checkbox-7" />
<label class="poster-switch-label" for="checkbox-7">
Switch to <strong>
<span class="poster-switch-posters">posters</span>
<span class="poster-switch-codes">codes only</span>
</strong>
</label>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2634" height="890" viewBox="-12 -12 2634 890" role="img" aria-label="[description]">
<desc>Three QR codes placed horizontally. The left two labelled 'original' and 'previous' have denser patterns than the right one labelled 'without name'.</desc>
<filter id="blur-text" width="130%" height="130%">
<feOffset result="offOut" in="SourceAlpha" dx="4" dy="8" />
<feColormatrix result="lightened" in="offOut" values="0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 1 0" />
<feGaussianBlur result="blurOut" in="lightened" stdDeviation="12" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
<g transform="translate(0 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-original" xlink:href="#poster-page-original" class="switchable-poster" />
</g>
<use href="#qr-code-v16-H-original" xlink:href="#qr-code-v16-H-original" class="switchable-qr qr-poster-original" />
</g>
<g transform="translate(866 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v05-Q-optimized-offline" xlink:href="#qr-code-v05-Q-optimized-offline" class="switchable-qr qr-poster-new" />
</g>
<g transform="translate(1732 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v02-Q-optimized-tiny" xlink:href="#qr-code-v02-Q-optimized-tiny" class="switchable-qr qr-poster-new" />
</g>
<text class="hovering-text-qr bad" dx="0" filter="url(#blur-text)">original</text>
<text class="hovering-text-qr okay" dx="866" filter="url(#blur-text)">previous</text>
<text class="hovering-text-qr good" dx="1732" filter="url(#blur-text)">without name</text>
</svg>
</div>
</figure>
<p>(@dhsysusbsjsi on the orange site <a href="https://news.ycombinator.com/item?id=28691747">points out</a> that one can even get to version 1, with some further tweaks, but these do require dropping error correction further, to L or M.)</p>
<h2 id="is-this-hacking-do-the-changes-lose-security">Is this hacking? Do the changes lose security?</h2>
<p><strong>No</strong>. The current NSW QR codes are <strong>inherently insecure</strong>, and none of the changes we’ve walk through make them more or less secure.</p>
<p>There’s lots of people who will have the skills to pull them apart and generate new ones with different values for the business ID, name or address. The base64 encoding is <strong>not encryption</strong>, because there’s no secret key/password required to encode or decode it. Base64 is a very common way to store and communicate data on the internet, to the point that some people would likely even instantly recognise the three character prefix <code class="language-plaintext highlighter-rouge">eyJ</code> as “this is base64’d JSON”.</p>
<p>One way to make these secure<sup id="fnref:threat-model" role="doc-noteref"><a href="#fn:threat-model" class="footnote" rel="footnote">5</a></sup> would be to <strong>cryptographically sign</strong> the URLs, adding extra information that is computed from the data using a secret key held by Service NSW. When loading the URL for a check-in, the app validates that the signature matches. If someone tries to tamper with the data, they’ll need to update the signature, but without knowing the key, they’ll need a <em>very</em> lucky guess.</p>
<p>For instance, a 200 bit <a href="https://en.wikipedia.org/wiki/BLS_digital_signature">BLS signature</a> may give sufficient security. To get a sense of the overhead this might impose, we can pretend we’ve generated a signature of this size somehow and add it to the URL (this <strong>isn’t</strong> a real cryptographic protocol, don’t take my word for it). First, encode it as a 61 digit number to benefit from the Numeric QR mode, and, then, add it as an extra query parameter <code class="language-plaintext highlighter-rouge">…?…&S=16069…</code>. This does make the URLs longer, and thus requires higher versions, but our optimisations have made room for this, meaning that the QR codes are still easier to scan while having better security:</p>
<table>
<thead>
<tr>
<th>QR code</th>
<th style="text-align: right">unsigned version</th>
<th style="text-align: right">signed version</th>
</tr>
</thead>
<tbody>
<tr>
<td>original</td>
<td style="text-align: right">16</td>
<td style="text-align: right">17</td>
</tr>
<tr>
<td>optimised for app</td>
<td style="text-align: right">11</td>
<td style="text-align: right">12</td>
</tr>
<tr>
<td>fully optimised</td>
<td style="text-align: right">5</td>
<td style="text-align: right">7</td>
</tr>
</tbody>
</table>
<figure class="image ">
<div class="image-positioner">
<input class="poster-switch-checkbox" type="checkbox" id="checkbox-8" />
<label class="poster-switch-label" for="checkbox-8">
Switch to <strong>
<span class="poster-switch-posters">posters</span>
<span class="poster-switch-codes">codes only</span>
</strong>
</label>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2634" height="890" viewBox="-12 -12 2634 890" role="img" aria-label="[description]">
<desc>Three QR codes placed horizontally. The left two labelled are labelled 'original' and 'insecure', while the right one is labelled 'signed'. The right one is slightly denser than the center, but much less dense than the first.</desc>
<filter id="blur-text" width="130%" height="130%">
<feOffset result="offOut" in="SourceAlpha" dx="4" dy="8" />
<feColormatrix result="lightened" in="offOut" values="0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 1 0" />
<feGaussianBlur result="blurOut" in="lightened" stdDeviation="12" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
<g transform="translate(0 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-original" xlink:href="#poster-page-original" class="switchable-poster" />
</g>
<use href="#qr-code-v16-H-original" xlink:href="#qr-code-v16-H-original" class="switchable-qr qr-poster-original" />
</g>
<g transform="translate(866 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v05-Q-optimized-offline" xlink:href="#qr-code-v05-Q-optimized-offline" class="switchable-qr qr-poster-new" />
</g>
<g transform="translate(1732 0)" data-width="866">
<g transform="translate(123.5 0)">
<use href="#poster-page-background" xlink:href="#poster-page-background" class="switchable-poster" />
<use href="#poster-page-new" xlink:href="#poster-page-new" class="switchable-poster" />
</g>
<use href="#qr-code-v07-Q-signed" xlink:href="#qr-code-v07-Q-signed" class="switchable-qr qr-poster-new" />
</g>
<text class="hovering-text-qr bad" dx="0" filter="url(#blur-text)">original, unsigned</text>
<text class="hovering-text-qr okay" dx="866" filter="url(#blur-text)">unsigned</text>
<text class="hovering-text-qr good" dx="1732" filter="url(#blur-text)">signed</text>
</svg>
</div>
</figure>
<h2 id="other-states">Other states</h2>
<p>Every other state in Australia has a similar check-in process, including apps and QR codes on posters. Most of these use simpler QR codes than NSW, but they make other trade-offs:</p>
<ul>
<li>South Australia and Victoria include the venue name, and thus potentially nicely support offline check-ins too. However, SA definitely requires internet to check-in, and I’m unsure about Victoria.</li>
<li>Victoria and Western Australia seem to have real security in their QR codes (for instance, WA uses a <a href="https://jwt.io/">JSON Web Token</a>, which includes a cryptographic signature, however, <a href="https://twitter.com/JamesHenstridge/status/1448516012379238404">it was pointed out</a> that the choice of a symmetric signature makes the value questionable).</li>
<li>ACT, Queensland, Northern Territory and Tasmania all have essentially the same app and QR codes, and use efficient URLs (with a lot of Numeric data), but have some seemingly unnecessary redundancy.</li>
<li>Most states use error correction level Q, but Victoria and Western Australia drop one level to M: it’s probably not a coincidence these two states also have the longest URLs, similar to or longer than the NSW URLs.</li>
</ul>
<h2 id="what-did-we-learn">What did we learn?</h2>
<p>Throughout this article we’ve stepped through a process of optimising QR codes for the real world, by looking at the check-in posters used here in NSW. We turned those posters into something that would be much easier to use, and in the process touched on:</p>
<ul>
<li>the contents of the current check-in QR codes and how they function</li>
<li>the workings of QR codes in general (error correction, versions and encoding modes)</li>
<li>a walk-through of optimising the data stored in a QR code, moving from version 16 to version 5 <strong>without compromising functionality</strong></li>
<li>the security of these QR codes, observing how it’s possible to <strong>improve security</strong> and have codes that are still easier to scan</li>
</ul>
<p>We didn’t consider the realities of actually implementing these, within an existing app and/or support infrastructure. The appropriate technical trade-off can change dramatically.</p>
<section id="external-links" class="no-print">
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Mechanical%20sympathy%20for%20QR%20codes:%20making%20NSW%20check-in%20better&url=https://huonw.github.io/blog/2021/10/nsw-covid-qr/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2021/10/nsw-covid-qr/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2021/10/nsw-covid-qr/&title=Mechanical%20sympathy%20for%20QR%20codes:%20making%20NSW%20check-in%20better" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<svg height="0" viewBox="-20 -20 1270 882" width="0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin-top: 0">
<defs>
<rect id="poster-page-background" width="594.960022" height="841.919983" fill="#fff" />
<g id="poster-page-new">
<use xlink:href="#covid-safe-logo" x="64.124374" y="30" />
<use xlink:href="#poster-check-in" x="67.414962" y="-121.92558" />
<use xlink:href="#poster-community" x="66.460506" y="-126.12689" />
<use xlink:href="#poster-easy" transform="matrix(0.73782341,0,0,0.73821084,83.57544,253.53852)" />
<use xlink:href="#poster-privacy" x="0" y="62.846437" />
</g>
<g id="poster-page-original">
<use xlink:href="#covid-safe-logo" x="243.852" y="24.012" />
<use xlink:href="#poster-check-in" />
<use xlink:href="#poster-community" />
<path d="m280.002122 385.999217h233.995414v234.00062h-233.995414zm0 0" fill="none" stroke="#01a0d5" stroke-width="16" transform="matrix(.750315 0 0 .750315 0 .00002)" />
<use xlink:href="#poster-easy" />
<use xlink:href="#poster-privacy" />
</g>
<!-- try to order these nicely for progressive rendering of the first image -->
<g id="qr-code-v05-Q-optimized-offline" transform="scale(27.02702702)">
<rect fill="#fff" height="37" width="37" /><path d="m18 26v1h1v-1zm0 9v1h1v-1zm-10-35v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-1 32v1h1v-1zm-29-27v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm22 0v1h1v-1zm-30 5v1h1v-1zm10 4v1h1v-1zm23 2v1h1v-1zm-29-27v1h1v-1zm30 1v1h1v-1zm-32 29v1h1v-1zm23 2v1h1v-1zm-22-28v1h1v-1zm31 15v1h1v-1zm-12 7v1h1v-1zm-19 5v1h1v-1zm11 0v1h1v-1zm12-26v1h1v-1zm-11 9v1h1v-1zm3-4v1h1v-1zm-3 13v1h1v-1zm3 5v1h1v-1zm1-26v1h1v-1zm11 0v1h1v-1zm-30-4v1h1v-1zm11 0v1h1v-1zm19 13v1h1v-1zm-24 21v1h1v-1zm23 2v1h1v-1zm-18-27v1h1v-1zm22-3v1h1v-1zm0 9v1h1v-1zm-23 7v1h1v-1zm15-2v1h1v-1zm8 13v1h1v-1zm-8-4v1h1v-1zm-23 7v1h1v-1zm32-20v1h1v-1zm-31-6v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm22 6v1h1v-1zm-21-23v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm22 6v1h1v-1zm-8-4v1h1v-1zm0 9v1h1v-1zm-10-23v1h1v-1zm10 32v1h1v-1zm12-26v1h1v-1zm0 9v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm0 9v1h1v-1zm24-33v1h1v-1zm-9 31v1h1v-1zm-3-15v1h1v-1zm0 9v1h1v-1zm-19-4v1h1v-1zm33 6v1h1v-1zm-10-23v1h1v-1zm-9 19v1h1v-1zm11 0v1h1v-1zm-10-23v1h1v-1zm-9 28v1h1v-1zm20-22v1h1v-1zm-23 16v1h1v-1zm15-2v1h1v-1zm-15 11v1h1v-1zm15-2v1h1v-1zm-11-28v1h1v-1zm15-2v1h1v-1zm8 13v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm-8 5v1h1v-1zm20-22v1h1v-1zm12 2v1h1v-1zm-31-6v1h1v-1zm29 36v1h1v-1zm-29-27v1h1v-1zm0 9v1h1v-1zm33-3v1h1v-1zm-10-14v1h1v-1zm-21 26v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm-23 7v1h1v-1zm1-26v1h1v-1zm23 11v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm15 7v1h1v-1zm12-35v1h1v-1zm0 9v1h1v-1zm-31-6v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm30 1v1h1v-1zm-30 8v1h1v-1zm30 1v1h1v-1zm-29-27v1h1v-1zm21 32v1h1v-1zm12-35v1h1v-1zm-20 31v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm0 9v1h1v-1zm-31-6v1h1v-1zm12 2v1h1v-1zm-8 5v1h1v-1zm-3-24v1h1v-1zm15-2v1h1v-1zm-15 11v1h1v-1zm23 11v1h1v-1zm-23 7v1h1v-1zm27-28v1h1v-1zm-8-4v1h1v-1zm-5 34v1h1v-1zm-10-23v1h1v-1zm15-2v1h1v-1zm7 8v1h1v-1zm-8-4v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-3 13v1h1v-1zm23-26v1h1v-1zm-8-4v1h1v-1zm-23 7v1h1v-1zm31 15v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm-3-24v1h1v-1zm15 7v1h1v-1zm-15 11v1h1v-1zm14 2v1h1v-1zm0 9v1h1v-1zm-10-23v1h1v-1zm2 19v1h1v-1zm-10-23v1h1v-1zm30 1v1h1v-1zm-20 31v1h1v-1zm-8-4v1h1v-1zm9-22v1h1v-1zm11 0v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-31-6v1h1v-1zm12 2v1h1v-1zm23-26v1h1v-1zm-20 31v1h1v-1zm20-22v1h1v-1zm-19-4v1h1v-1zm0 9v1h1v-1zm11 0v1h1v-1zm-23 7v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm-23 7v1h1v-1zm1-26v1h1v-1zm15-2v1h1v-1zm6 34v1h1v-1zm-7-21v1h1v-1zm0 9v1h1v-1zm0 9v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm-19-4v1h1v-1zm9-22v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm-3-24v1h1v-1zm12 2v1h1v-1zm22 0v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-15 11v1h1v-1zm34 2v1h1v-1zm1-26v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-23 7v1h1v-1zm4-2v1h1v-1zm10 13v1h1v-1zm0 9v1h1v-1zm1-35v1h1v-1zm10 35v1h1v-1zm-30-4v1h1v-1zm11 0v1h1v-1zm1-26v1h1v-1zm11 9v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm-28 18v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm-8-4v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm-8-4v1h1v-1zm-11-10v1h1v-1zm22 6v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-16-17v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm1-26v1h1v-1zm-20 31v1h1v-1zm12-35v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm-27-8v1h1v-1zm27 26v1h1v-1zm-19-4v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm-3-24v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm-20 31v1h1v-1zm-3-24v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-34 7v1h1v-1zm14 2v1h1v-1zm0 9v1h1v-1zm11 0v1h1v-1zm-21-23v1h1v-1zm22-3v1h1v-1zm-9 31v1h1v-1zm9-22v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-30-4v1h1v-1zm27 17v1h1v-1zm-19-4v1h1v-1zm22 0v1h1v-1zm-3 13v1h1v-1zm-27-17v1h1v-1zm8 13v1h1v-1zm3-4v1h1v-1zm-3 13v1h1v-1zm12-35v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm31-22v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm11-3v1h1v-1zm-30 8v1h1v-1zm30 1v1h1v-1zm-10-23v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm6 34v1h1v-1zm-19-4v1h1v-1zm23 1v1h1v-1zm-8-4v1h1v-1zm-10-26v1h1v-1zm22 0v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm3-4v1h1v-1zm-30-4v1h1v-1zm8 13v1h1v-1zm22 0v1h1v-1zm-30-4v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm-20 31v1h1v-1zm12-35v1h1v-1zm19 13v1h1v-1zm-8 5v1h1v-1zm-19-4v1h1v-1zm30 1v1h1v-1zm-12 7v1h1v-1zm12 2v1h1v-1zm-31 3v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm15-30v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm-18-21v1h1v-1zm-8-4v1h1v-1zm30 13v1h1v-1zm-30-4v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm11 9v1h1v-1zm1-26v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm6 34v1h1v-1zm23 2v1h1v-1zm-8-4v1h1v-1zm9-22v1h1v-1zm-31 3v1h1v-1zm19 13v1h1v-1zm12 2v1h1v-1zm-31-6v1h1v-1zm11 9v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm-3 13v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm1-26v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-1 32v1h1v-1zm-10-23v1h1v-1zm11 0v1h1v-1zm-30 5v1h1v-1zm11 0v1h1v-1zm22-3v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm15-2v1h1v-1zm-3-24v1h1v-1zm-20 31v1h1v-1zm23 2v1h1v-1zm9-22v1h1v-1zm-31-6v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm8 13v1h1v-1zm-8 5v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm9-4v1h1v-1zm3 5v1h1v-1zm-9 19v1h1v-1zm-21-23v1h1v-1zm6 34v1h1v-1zm15-2v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm15-2v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm-31-6v1h1v-1zm32-20v1h1v-1zm-31 3v1h1v-1zm19 13v1h1v-1zm12 2v1h1v-1zm-20-6v1h1v-1zm-11 18v1h1v-1zm11 0v1h1v-1zm22-3v1h1v-1zm-21-23v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm18 36v1h1v-1zm-10-23v1h1v-1zm2 19v1h1v-1zm-21-23v1h1v-1zm11 9v1h1v-1zm3-7v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm15-2v1h1v-1zm12-26v1h1v-1zm-23 7v1h1v-1zm-8-4v1h1v-1zm31 15v1h1v-1zm-31-6v1h1v-1zm23 11v1h1v-1zm9-22v1h1v-1zm-32 29v1h1v-1zm30 1v1h1v-1zm-7 1v1h1v-1zm-3-24v1h1v-1zm-8 5v1h1v-1zm0 9v1h1v-1zm22-3v1h1v-1zm-19-4v1h1v-1zm0 9v1h1v-1zm1-23v1h1v-1zm-9 19v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-3-24v1h1v-1zm23 2v1h1v-1zm0 9v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm15-2v1h1v-1zm-11-28v1h1v-1zm0 9v1h1v-1zm-8-4v1h1v-1zm23 2v1h1v-1zm-15 11v1h1v-1zm-8 5v1h1v-1zm30 1v1h1v-1zm-10-23v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-1 32v1h1v-1zm1-23v1h1v-1zm22-3v1h1v-1zm0 9v1h1v-1zm0 9v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-22-28v1h1v-1zm3 33v1h1v-1zm12-35v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm4-30v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm0 9v1h1v-1zm-8-4v1h1v-1zm30 1v1h1v-1zm-10-14v1h1v-1zm-9 19v1h1v-1zm19 13v1h1v-1zm-31-6v1h1v-1zm12 2v1h1v-1zm1-23v1h1v-1zm10 23v1h1v-1zm-23 7v1h1v-1zm35-33v1h1v-1zm-20 31v1h1v-1zm20-22v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-8-4v1h1v-1zm-19-4v1h1v-1zm8 13v1h1v-1zm0 9v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm20-22v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-8-4v1h1v-1zm0 9v1h1v-1zm-11-19v1h1v-1zm-8-4v1h1v-1zm18 36v1h1v-1zm11 0v1h1v-1zm-29-27v1h1v-1zm30 1v1h1v-1zm0 9v1h1v-1zm3-4v1h1v-1zm-11 0v1h1v-1zm8 13v1h1v-1zm-16-17v1h1v-1zm-15 11v1h1v-1zm12 2v1h1v-1zm22 9v1h1v-1zm-19-4v1h1v-1zm20-13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-22-28v1h1v-1zm15-2v1h1v-1zm6 34v1h1v-1zm-21-14v1h1v-1zm6-2v1h1v-1zm0 9v1h1v-1zm-10-23v1h1v-1zm2 19v1h1v-1zm8 13v1h1v-1zm12-35v1h1v-1zm8 13v1h1v-1zm-28 18v1h1v-1zm-3-24v1h1v-1zm23 2v1h1v-1zm8 13v1h1v-1zm-16-17v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm8 13v1h1v-1zm4-30v1h1v-1zm-20 31v1h1v-1zm20-22v1h1v-1zm-19-4v1h1v-1zm-8-4v1h1v-1zm-4 11v1h1v-1zm23 2v1h1v-1zm-22-10v1h1v-1zm2 19v1h1v-1zm13-21v1h1v-1zm9 30v1h1v-1zm-30-4v1h1v-1zm20-22v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm-8-4v1h1v-1zm-11 9v1h1v-1zm20-22v1h1v-1zm-20 31v1h1v-1zm11 0v1h1v-1zm-19 5v1h1v-1zm9-22v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-15 11v1h1v-1zm34 2v1h1v-1zm-19-4v1h1v-1zm20-22v1h1v-1zm-31-6v1h1v-1zm12 2v1h1v-1zm11 9v1h1v-1zm-19-4v1h1v-1zm-4 11v1h1v-1zm1-17v1h1v-1zm2 19v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-22 9v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm-11 9v1h1v-1zm20-22v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm-3 13v1h1v-1zm-16-17v1h1v-1zm16 26v1h1v-1zm-16-17v1h1v-1zm19 13v1h1v-1zm9-22v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm-34 7v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm14 2v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm3-4v1h1v-1zm9-22v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-19-4v1h1v-1zm8 13v1h1v-1zm0 9v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-10-26v1h1v-1zm3-4v1h1v-1zm-3 22v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-8-4v1h1v-1zm0 9v1h1v-1zm20-22v1h1v-1zm10 32v1h1v-1zm-33-16v1h1v-1zm14 11v1h1v-1zm11 0v1h1v-1zm4-30v1h1v-1zm0 9v1h1v-1zm-3 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm11 0v1h1v-1zm-30-4v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm9-22v1h1v-1zm-20 31v1h1v-1zm11 0v1h1v-1zm12-26v1h1v-1zm-19-4v1h1v-1zm8 13v1h1v-1zm22-3v1h1v-1zm-30 8v1h1v-1zm30 1v1h1v-1zm-12 7v1h1v-1zm12 2v1h1v-1zm-33-25v1h1v-1zm2 28v1h1v-1zm12-17v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm-3 13v1h1v-1zm12-26v1h1v-1zm-19-4v1h1v-1zm22 0v1h1v-1zm-3 13v1h1v-1zm-27-17v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm-30-4v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm22-3v1h1v-1zm-30 8v1h1v-1zm30 1v1h1v-1zm-31 3v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm4-30v1h1v-1zm-19 5v1h1v-1z" />
</g>
<g id="qr-code-v11-Q-works-in-app" transform="scale(16.39344262)">
<rect fill="#fff" height="61" width="61" /><path fill="#000" d="m26 21v1h1v-1zm24-15v1h1v-1zm-33 52v1h1v-1zm25-56v1h1v-1zm-13 30v1h1v-1zm-29-27v1h1v-1zm11 9v1h1v-1zm41 1v1h1v-1zm-46 33v1h1v-1zm27-10v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm45 8v1h1v-1zm-21-23v1h1v-1zm11 9v1h1v-1zm-2 39v1h1v-1zm1-35v1h1v-1zm-30 8v1h1v-1zm-13 30v1h1v-1zm25-56v1h1v-1zm-1 41v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm35-6v1h1v-1zm-5 34v1h1v-1zm-29-27v1h1v-1zm32 32v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm33 34v1h1v-1zm-31 12v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-10-14v1h1v-1zm-5 34v1h1v-1zm4-30v1h1v-1zm23 20v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm20-22v1h1v-1zm37 4v1h1v-1zm-42 3v1h1v-1zm11 9v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm45 8v1h1v-1zm-10-14v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-5 34v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm32-11v1h1v-1zm17 52v1h1v-1zm-27-17v1h1v-1zm-21-23v1h1v-1zm52 10v1h1v-1zm-29-27v1h1v-1zm33 34v1h1v-1zm-54-8v1h1v-1zm4 7v1h1v-1zm31-13v1h1v-1zm-34 7v1h1v-1zm48 13v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-5 34v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm3 11v1h1v-1zm38 26v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm52 47v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-2 39v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm44 43v1h1v-1zm-3-6v1h1v-1zm-27-17v1h1v-1zm-8-4v1h1v-1zm39-9v1h1v-1zm-54 29v1h1v-1zm4-30v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-49-5v1h1v-1zm40 36v1h1v-1zm-20-58v1h1v-1zm-23 16v1h1v-1zm24 28v1h1v-1zm12 11v1h1v-1zm-42 3v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-1 41v1h1v-1zm-21-23v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-21 26v1h1v-1zm24-15v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm31 24v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-9 37v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-23 16v1h1v-1zm12-26v1h1v-1zm11 46v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-42 3v1h1v-1zm39-9v1h1v-1zm-16-8v1h1v-1zm-1 41v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-54 29v1h1v-1zm25-56v1h1v-1zm-13 30v1h1v-1zm31 24v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm4-30v1h1v-1zm-11 27v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-43 38v1h1v-1zm32-11v1h1v-1zm4-30v1h1v-1zm-50-1v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-3 31v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-1-45v1h1v-1zm-33 52v1h1v-1zm24-15v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm32-11v1h1v-1zm-8-4v1h1v-1zm6 43v1h1v-1zm-48-40v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-5 34v1h1v-1zm25-56v1h1v-1zm-8-4v1h1v-1zm10 50v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm-22 18v1h1v-1zm12-26v1h1v-1zm-28-19v1h1v-1zm19 50v1h1v-1zm-1 4v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm18 54v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm-11 27v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm3 5v1h1v-1zm24-15v1h1v-1zm-34 7v1h1v-1zm33-3v1h1v-1zm-35 42v1h1v-1zm4-30v1h1v-1zm15 16v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm12-26v1h1v-1zm22 24v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-20-58v1h1v-1zm3 5v1h1v-1zm-1 41v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm31-13v1h1v-1zm-34 7v1h1v-1zm12 11v1h1v-1zm-10 35v1h1v-1zm-21-23v1h1v-1zm14 20v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-42 3v1h1v-1zm46-33v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm31 24v1h1v-1zm12-26v1h1v-1zm11 9v1h1v-1zm-54 29v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-42 40v1h1v-1zm-4-34v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm12 10v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-5 34v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-19 23v1h1v-1zm19 13v1h1v-1zm-35-21v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm12 11v1h1v-1zm-42 3v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm31-13v1h1v-1zm-54 29v1h1v-1zm4-30v1h1v-1zm15 16v1h1v-1zm-5 34v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm10-36v1h1v-1zm-1 41v1h1v-1zm4-30v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm2 46v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm24-21v1h1v-1zm-33 52v1h1v-1zm20-22v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-28 18v1h1v-1zm-21-23v1h1v-1zm52 47v1h1v-1zm-42 3v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-30 14v1h1v-1zm33-3v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm24 28v1h1v-1zm9 5v1h1v-1zm-8-4v1h1v-1zm-27-17v1h1v-1zm47-5v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm33 34v1h1v-1zm12-26v1h1v-1zm-29-27v1h1v-1zm40 36v1h1v-1zm-42 3v1h1v-1zm-10-14v1h1v-1zm13 19v1h1v-1zm40-7v1h1v-1zm-50-1v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm40-44v1h1v-1zm-39 45v1h1v-1zm-8-4v1h1v-1zm52 10v1h1v-1zm-21-23v1h1v-1zm-30-23v1h1v-1zm33 34v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm-7-39v1h1v-1zm-27 46v1h1v-1zm-7-39v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-31 49v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-42 3v1h1v-1zm33 34v1h1v-1zm3 5v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm43-2v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-35 42v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm-18-12v1h1v-1zm-9 31v1h1v-1zm52 10v1h1v-1zm1-35v1h1v-1zm-54 29v1h1v-1zm43-2v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-51 34v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm18 48v1h1v-1zm12-26v1h1v-1zm-30 14v1h1v-1zm41 1v1h1v-1zm19 13v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-50-1v1h1v-1zm-1-45v1h1v-1zm33 34v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-54 29v1h1v-1zm12-26v1h1v-1zm27-10v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm52 10v1h1v-1zm-11 27v1h1v-1zm-7-39v1h1v-1zm-9 31v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm4-30v1h1v-1zm41 38v1h1v-1zm-21-23v1h1v-1zm-23 16v1h1v-1zm13 19v1h1v-1zm20-22v1h1v-1zm-21-23v1h1v-1zm-9 31v1h1v-1zm42 2v1h1v-1zm-56-44v1h1v-1zm41 1v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-53-6v1h1v-1zm19 13v1h1v-1zm15 16v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm3 11v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm4-30v1h1v-1zm23 20v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-17-53v1h1v-1zm33 34v1h1v-1zm-42 3v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm31-13v1h1v-1zm-27 19v1h1v-1zm19 13v1h1v-1zm4-30v1h1v-1zm-42 3v1h1v-1zm45 8v1h1v-1zm-43 38v1h1v-1zm39-9v1h1v-1zm-37-31v1h1v-1zm41 38v1h1v-1zm-54-8v1h1v-1zm12 11v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm38-37v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm30 28v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-30 14v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-43 38v1h1v-1zm37 4v1h1v-1zm12-26v1h1v-1zm-15 20v1h1v-1zm12-26v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-11 27v1h1v-1zm-16-8v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-38 10v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm39-9v1h1v-1zm-54 29v1h1v-1zm33-3v1h1v-1zm-9 31v1h1v-1zm24-15v1h1v-1zm-42 3v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm58-22v1h1v-1zm-13 30v1h1v-1zm-30 8v1h1v-1zm1-35v1h1v-1zm32 32v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm53 12v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm32-48v1h1v-1zm-39 45v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm4-30v1h1v-1zm33 34v1h1v-1zm20-22v1h1v-1zm-49-5v1h1v-1zm12-26v1h1v-1zm-13 30v1h1v-1zm11 9v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-11 27v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm41 37v1h1v-1zm1-35v1h1v-1zm-49-5v1h1v-1zm40 36v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm-9 31v1h1v-1zm40-7v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm11 15v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-31 12v1h1v-1zm-21-23v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm31 24v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm12-26v1h1v-1zm-28 18v1h1v-1zm40 36v1h1v-1zm-20-58v1h1v-1zm32 32v1h1v-1zm-55-16v1h1v-1zm33 34v1h1v-1zm12-26v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm20-22v1h1v-1zm-32 47v1h1v-1zm4-30v1h1v-1zm20 15v1h1v-1zm4-30v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm32 32v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm2 46v1h1v-1zm21-57v1h1v-1zm10 44v1h1v-1zm-7-39v1h1v-1zm-27 46v1h1v-1zm-7-39v1h1v-1zm11 9v1h1v-1zm20 15v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm11 9v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-16-8v1h1v-1zm19 13v1h1v-1zm30 28v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm-22 18v1h1v-1zm3 5v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm23-17v1h1v-1zm1-35v1h1v-1zm2 46v1h1v-1zm31-13v1h1v-1zm-8-4v1h1v-1zm-29-27v1h1v-1zm10 50v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm-5 7v1h1v-1zm11 9v1h1v-1zm12-26v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm-31 49v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-7-39v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm32-11v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-26-16v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm19-14v1h1v-1zm-23 16v1h1v-1zm1-35v1h1v-1zm32 32v1h1v-1zm-34 7v1h1v-1zm27-10v1h1v-1zm12-26v1h1v-1zm20-22v1h1v-1zm-50-1v1h1v-1zm11 46v1h1v-1zm20-22v1h1v-1zm-16-8v1h1v-1zm6 43v1h1v-1zm25-56v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-1 41v1h1v-1zm11 9v1h1v-1zm-7-39v1h1v-1zm30 22v1h1v-1zm-7-39v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm52 47v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-40 49v1h1v-1zm25-56v1h1v-1zm-33 52v1h1v-1zm47-5v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm23 20v1h1v-1zm-54-8v1h1v-1zm31 24v1h1v-1zm-8-4v1h1v-1zm14 20v1h1v-1zm12-26v1h1v-1zm-29-27v1h1v-1zm-5 34v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-30 8v1h1v-1zm-1 4v1h1v-1zm15 16v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm53 12v1h1v-1zm-49-5v1h1v-1zm20-22v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm31-13v1h1v-1zm-13 30v1h1v-1zm-8-4v1h1v-1zm-29-27v1h1v-1zm10 50v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm39-9v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-1 41v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-21 26v1h1v-1zm23 20v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-42 3v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-23 16v1h1v-1zm15 16v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm30 28v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm10 44v1h1v-1zm-7-39v1h1v-1zm-3 31v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-34 7v1h1v-1zm4-30v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-42 3v1h1v-1zm19 13v1h1v-1zm-20-58v1h1v-1zm13 19v1h1v-1zm-1 35v1h1v-1zm-10-8v1h1v-1zm46-33v1h1v-1zm-15 20v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm41 1v1h1v-1zm-17-53v1h1v-1zm-22 18v1h1v-1zm24 28v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-41-32v1h1v-1zm11 9v1h1v-1zm12 11v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-2 39v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-16-8v1h1v-1zm10 50v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm40 36v1h1v-1zm-34 7v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm23 20v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm23 20v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm12-26v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-27-17v1h1v-1zm30 28v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-53-6v1h1v-1zm19 13v1h1v-1zm-15-43v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm41 38v1h1v-1zm-21-23v1h1v-1zm12-26v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm39-9v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm11 15v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm-27-17v1h1v-1zm-21-23v1h1v-1zm6 43v1h1v-1zm4-30v1h1v-1zm32-11v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm12-26v1h1v-1zm11 9v1h1v-1zm-5 7v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm3 5v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-23 53v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-2 39v1h1v-1zm-37-31v1h1v-1zm-1 41v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm-49-5v1h1v-1zm30 28v1h1v-1zm3 5v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-1 41v1h1v-1zm46-33v1h1v-1zm-4 29v1h1v-1zm-27-17v1h1v-1zm1 1v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm-4-34v1h1v-1zm20-22v1h1v-1zm15 16v1h1v-1zm-5 34v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm41 38v1h1v-1zm-42 3v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm32-48v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm44 43v1h1v-1zm-21-23v1h1v-1zm-23 16v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-29-27v1h1v-1zm-1 41v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-23 16v1h1v-1zm1-35v1h1v-1zm35-6v1h1v-1zm-35 42v1h1v-1zm33-3v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm52 47v1h1v-1zm-22-19v1h1v-1zm-28 18v1h1v-1zm47-5v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm12 11v1h1v-1zm-54-8v1h1v-1zm41 38v1h1v-1zm-18-18v1h1v-1zm-16-3v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm12 11v1h1v-1zm3 5v1h1v-1zm1-35v1h1v-1zm-12 25v1h1v-1zm1-35v1h1v-1zm15 16v1h1v-1zm-13 30v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-28 18v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm11 46v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm-34 7v1h1v-1zm4-30v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-9-49v1h1v-1zm-21 26v1h1v-1zm-11 27v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm39-9v1h1v-1zm-7-39v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm12 11v1h1v-1zm33-3v1h1v-1zm-30 8v1h1v-1zm20-22v1h1v-1zm-10 35v1h1v-1zm-21-23v1h1v-1zm33 34v1h1v-1zm20-22v1h1v-1zm-50-1v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm18 48v1h1v-1zm-7-39v1h1v-1zm-11 27v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm32 32v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm24-15v1h1v-1zm12-26v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-7-39v1h1v-1zm-34 7v1h1v-1zm10 44v1h1v-1zm1-35v1h1v-1zm20-22v1h1v-1zm15 16v1h1v-1zm-54 29v1h1v-1zm60 14v1h1v-1zm-49-5v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm11 15v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm23 20v1h1v-1zm-8-41v1h1v-1zm12 11v1h1v-1zm-34 7v1h1v-1zm52 10v1h1v-1zm-30 8v1h1v-1zm11 15v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-30-23v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm-42 3v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm11 9v1h1v-1zm-30 14v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm32 32v1h1v-1zm-34 7v1h1v-1zm24-15v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm10 44v1h1v-1zm1-35v1h1v-1zm-54 29v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm41 37v1h1v-1zm-48-40v1h1v-1zm11 9v1h1v-1zm-5 34v1h1v-1zm27-10v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm4-30v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm56 17v1h1v-1zm-31 12v1h1v-1zm-21-23v1h1v-1zm11 9v1h1v-1zm11 15v1h1v-1zm31-13v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm43-2v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm6 43v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm-1 41v1h1v-1zm31-13v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm2 46v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm12-26v1h1v-1zm14 47v1h1v-1zm-48-40v1h1v-1zm56 17v1h1v-1zm-42 3v1h1v-1zm11 9v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm-34 7v1h1v-1zm35-6v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-27-17v1h1v-1zm14 47v1h1v-1zm-37-31v1h1v-1zm20-22v1h1v-1zm-9 37v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm3 5v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-29-27v1h1v-1zm18 54v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm31-13v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm10 50v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-29-27v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm46-33v1h1v-1zm12 11v1h1v-1zm-50-1v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm3 42v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm25-56v1h1v-1zm-2 39v1h1v-1zm20-22v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-30 14v1h1v-1zm-9 31v1h1v-1zm47-5v1h1v-1zm-8-4v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-9 37v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-18-18v1h1v-1zm-28 23v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-23 16v1h1v-1zm12 11v1h1v-1zm46-33v1h1v-1zm-35 42v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-30-23v1h1v-1zm52 47v1h1v-1zm-4-34v1h1v-1zm-54 29v1h1v-1zm39-9v1h1v-1zm-37-31v1h1v-1zm-1 4v1h1v-1zm11 46v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-9 37v1h1v-1zm20-22v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-7-39v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm3 5v1h1v-1zm1-35v1h1v-1zm11 46v1h1v-1zm12-26v1h1v-1zm-54 29v1h1v-1zm31-13v1h1v-1zm-27-17v1h1v-1zm47-5v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-9 37v1h1v-1zm12-26v1h1v-1zm-9 31v1h1v-1zm-20-58v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm-9 31v1h1v-1zm40-7v1h1v-1zm-8-4v1h1v-1zm4-30v1h1v-1zm-15 20v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm-26-52v1h1v-1zm19 13v1h1v-1zm11 15v1h1v-1zm-30 8v1h1v-1zm53-25v1h1v-1zm-35 42v1h1v-1zm-8-4v1h1v-1zm32-11v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm-9 31v1h1v-1zm-7-39v1h1v-1zm43-2v1h1v-1zm-31 12v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm33 34v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-11 27v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm2 46v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm31-13v1h1v-1zm-34 7v1h1v-1zm-4-34v1h1v-1zm40-7v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm11 15v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-7-39v1h1v-1zm11 9v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm23 20v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm11 9v1h1v-1zm41 1v1h1v-1zm-48-40v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm12 11v1h1v-1zm-13 30v1h1v-1zm-29-27v1h1v-1zm60 14v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-46 33v1h1v-1zm38-37v1h1v-1zm-19 23v1h1v-1zm31-13v1h1v-1zm-53-6v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-4-34v1h1v-1zm41 38v1h1v-1zm-42 3v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm12-26v1h1v-1zm-49-5v1h1v-1zm-5 34v1h1v-1zm24-21v1h1v-1zm11 15v1h1v-1zm11 9v1h1v-1zm-27-17v1h1v-1zm-15 20v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm10 50v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-13 30v1h1v-1zm23 20v1h1v-1zm-42 3v1h1v-1zm4-30v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm44 43v1h1v-1zm-20-58v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm-13 30v1h1v-1zm-30 8v1h1v-1zm20-22v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm3 11v1h1v-1zm38 26v1h1v-1zm-27-17v1h1v-1zm1-35v1h1v-1zm-23 16v1h1v-1zm27-10v1h1v-1zm19 13v1h1v-1zm-13 30v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm10 50v1h1v-1zm-16-8v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm4 7v1h1v-1zm-54-8v1h1v-1zm53 12v1h1v-1zm-30 8v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm4-30v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm52 47v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-48-40v1h1v-1zm32 32v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm12 11v1h1v-1zm23-17v1h1v-1zm-31 12v1h1v-1zm1-35v1h1v-1zm48 13v1h1v-1zm-21-23v1h1v-1zm-33 52v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-30 14v1h1v-1zm3 5v1h1v-1zm39-9v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm33 34v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm-29-27v1h1v-1zm6 43v1h1v-1zm12-26v1h1v-1zm15 16v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm47-5v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm12 11v1h1v-1zm-43 38v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm20-22v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm12 54v1h1v-1zm40-7v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-42 3v1h1v-1zm11 46v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm-29-27v1h1v-1zm20-22v1h1v-1zm10 50v1h1v-1zm4-30v1h1v-1zm-11-10v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm23 20v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm23-11v1h1v-1zm-37-31v1h1v-1zm40 36v1h1v-1zm-22-19v1h1v-1zm-9 31v1h1v-1zm32-11v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-9 31v1h1v-1zm-26-52v1h1v-1zm47-5v1h1v-1zm-28 18v1h1v-1zm-22-19v1h1v-1zm33 34v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm10 44v1h1v-1zm-53-6v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm-10-14v1h1v-1zm33 34v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm3 11v1h1v-1zm19 13v1h1v-1zm4-30v1h1v-1zm-54 29v1h1v-1zm-1-45v1h1v-1zm48 13v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-22 18v1h1v-1zm12-26v1h1v-1zm40 36v1h1v-1zm-49-5v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm10 50v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm11 9v1h1v-1zm-7-39v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm20 15v1h1v-1zm12-26v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm30 28v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm20-22v1h1v-1zm27-10v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm30 28v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-50-1v1h1v-1zm31 24v1h1v-1zm1-35v1h1v-1zm-2 39v1h1v-1zm24-21v1h1v-1zm-53-6v1h1v-1zm11 9v1h1v-1zm45 8v1h1v-1zm-50-1v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-23 16v1h1v-1zm46-33v1h1v-1zm-23 53v1h1v-1zm-8-4v1h1v-1zm4-30v1h1v-1zm20-22v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-53-6v1h1v-1zm19 13v1h1v-1zm-7-3v1h1v-1zm13 19v1h1v-1zm27 17v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-30 8v1h1v-1zm41 38v1h1v-1zm-1 4v1h1v-1zm-49-5v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm12-26v1h1v-1zm-23 16v1h1v-1zm24-15v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-13 30v1h1v-1zm11 9v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm4 7v1h1v-1zm-42 3v1h1v-1zm-8-4v1h1v-1zm38 26v1h1v-1zm-41-32v1h1v-1zm15 16v1h1v-1zm20-22v1h1v-1zm-16-8v1h1v-1zm14 47v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm45 8v1h1v-1zm-27 46v1h1v-1zm-15-43v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-23 16v1h1v-1zm1-35v1h1v-1zm52 47v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm-5 34v1h1v-1zm15 16v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm39-9v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm23 20v1h1v-1zm-23 16v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm-34 7v1h1v-1zm44 43v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm58-22v1h1v-1zm-35 42v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-23 16v1h1v-1zm53 12v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm20-22v1h1v-1zm3 42v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-1 41v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm-1-45v1h1v-1zm12 54v1h1v-1zm40-7v1h1v-1zm-28-19v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-42 3v1h1v-1zm33 34v1h1v-1zm-30 8v1h1v-1zm41 1v1h1v-1zm-9-49v1h1v-1zm-33 52v1h1v-1zm25-56v1h1v-1zm18 54v1h1v-1zm-4-34v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27 19v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-34 7v1h1v-1zm24-15v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm-42 3v1h1v-1zm11 9v1h1v-1zm11 9v1h1v-1zm12 11v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm22 24v1h1v-1zm-7-39v1h1v-1zm-8-4v1h1v-1zm18 48v1h1v-1zm-53-6v1h1v-1zm46-33v1h1v-1zm-35 42v1h1v-1zm-7-39v1h1v-1zm31 24v1h1v-1zm20-22v1h1v-1zm-16-8v1h1v-1zm-27 46v1h1v-1zm-7-39v1h1v-1zm-8-4v1h1v-1zm41 37v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm20 15v1h1v-1zm-8-41v1h1v-1zm12 11v1h1v-1zm11 9v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm2 46v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm-29-27v1h1v-1zm-1 4v1h1v-1zm12 11v1h1v-1zm19 13v1h1v-1zm3 5v1h1v-1zm1-35v1h1v-1zm-54 29v1h1v-1zm12-26v1h1v-1z" />
</g>
<g id="qr-code-v16-H-original" transform="scale(12.34567901)">
<rect fill="#fff" height="81" width="81" /><path fill="#000" d="m26 21v1h1v-1zm41 38v1h1v-1zm-49-42v1h1v-1zm32-11v1h1v-1zm13 19v1h1v-1zm-46 33v1h1v-1zm25-56v1h1v-1zm-14 65v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-34 7v1h1v-1zm57 13v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm32-11v1h1v-1zm-33 52v1h1v-1zm19 13v1h1v-1zm47-68v1h1v-1zm-55 64v1h1v-1zm46-33v1h1v-1zm-21-23v1h1v-1zm-33 52v1h1v-1zm-9-49v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-29-27v1h1v-1zm61 16v1h1v-1zm-74 14v1h1v-1zm65 17v1h1v-1zm1-35v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm-27-17v1h1v-1zm47-5v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm4-30v1h1v-1zm60 51v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-1-45v1h1v-1zm-28 18v1h1v-1zm41 1v1h1v-1zm-35 42v1h1v-1zm25-56v1h1v-1zm-21 26v1h1v-1zm-21-23v1h1v-1zm-13 67v1h1v-1zm65-20v1h1v-1zm-15-43v1h1v-1zm-54 29v1h1v-1zm38 26v1h1v-1zm-26-52v1h1v-1zm39-9v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-8-4v1h1v-1zm-35 79v1h1v-1zm33-3v1h1v-1zm-21-23v1h1v-1zm52 10v1h1v-1zm1-35v1h1v-1zm-49-5v1h1v-1zm20-22v1h1v-1zm-14 65v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-5 34v1h1v-1zm16-19v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm1-35v1h1v-1zm-55 64v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm6-69v1h1v-1zm40 36v1h1v-1zm-7-39v1h1v-1zm-23 53v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm41 1v1h1v-1zm19 13v1h1v-1zm-36-66v1h1v-1zm11 9v1h1v-1zm-14 65v1h1v-1zm-20-58v1h1v-1zm52 47v1h1v-1zm-28-62v1h1v-1zm40 36v1h1v-1zm-62 25v1h1v-1zm11 9v1h1v-1zm47-68v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-34 44v1h1v-1zm46-33v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm-54 29v1h1v-1zm41 1v1h1v-1zm-48-40v1h1v-1zm68 18v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm52 47v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-53-6v1h1v-1zm17-60v1h1v-1zm41 1v1h1v-1zm-46 33v1h1v-1zm31-13v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm-54 29v1h1v-1zm58-22v1h1v-1zm-55 64v1h1v-1zm74-51v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-5 34v1h1v-1zm-30 8v1h1v-1zm1-35v1h1v-1zm-27-17v1h1v-1zm46 30v1h1v-1zm-27-17v1h1v-1zm40 36v1h1v-1zm8 4v1h1v-1zm-28-62v1h1v-1zm-47 68v1h1v-1zm45 8v1h1v-1zm-40-73v1h1v-1zm3 42v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm19 13v1h1v-1zm-72-19v1h1v-1zm11 9v1h1v-1zm25-56v1h1v-1zm-14 65v1h1v-1zm24-15v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-27-17v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-27-17v1h1v-1zm56 44v1h1v-1zm-71-24v1h1v-1zm58-22v1h1v-1zm-42 3v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm59 49v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-73 16v1h1v-1zm19 13v1h1v-1zm-20-58v1h1v-1zm52 47v1h1v-1zm3 5v1h1v-1zm12-26v1h1v-1zm-7-39v1h1v-1zm-27 46v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm21-57v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm10 50v1h1v-1zm25-56v1h1v-1zm-55 64v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-6 69v1h1v-1zm-48-40v1h1v-1zm60 14v1h1v-1zm1-35v1h1v-1zm-74 51v1h1v-1zm77-46v1h1v-1zm-42 40v1h1v-1zm39-9v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm-7-39v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-7-39v1h1v-1zm31-13v1h1v-1zm3 42v1h1v-1zm-21 26v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-49-5v1h1v-1zm19 13v1h1v-1zm41 1v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm25-56v1h1v-1zm-13 67v1h1v-1zm3 5v1h1v-1zm1-35v1h1v-1zm31-13v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm-8-4v1h1v-1zm61 16v1h1v-1zm-40-73v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm40 73v1h1v-1zm-42 3v1h1v-1zm-10-14v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm41 1v1h1v-1zm-20-58v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm52 47v1h1v-1zm-40-73v1h1v-1zm-10 72v1h1v-1zm47-5v1h1v-1zm-27-17v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm12-26v1h1v-1zm-73 16v1h1v-1zm19 13v1h1v-1zm6-69v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-53-6v1h1v-1zm65-20v1h1v-1zm-54 29v1h1v-1zm20 15v1h1v-1zm33-3v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-14 65v1h1v-1zm-30 8v1h1v-1zm2-70v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm-7-39v1h1v-1zm52 47v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-39-71v1h1v-1zm12 54v1h1v-1zm47-5v1h1v-1zm-27-17v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-55 64v1h1v-1zm6-69v1h1v-1zm-14 65v1h1v-1zm20-22v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm20 15v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-7-39v1h1v-1zm-27 46v1h1v-1zm32 32v1h1v-1zm-47-75v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm60 51v1h1v-1zm-49-42v1h1v-1zm33 34v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm6-69v1h1v-1zm-33 52v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-21-23v1h1v-1zm32 32v1h1v-1zm1-35v1h1v-1zm-49-5v1h1v-1zm68 18v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-20 22v1h1v-1zm-34 7v1h1v-1zm64 21v1h1v-1zm-21-23v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-7-39v1h1v-1zm18 48v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm66-55v1h1v-1zm-13 67v1h1v-1zm12-26v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-14 65v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm-5 34v1h1v-1zm17-60v1h1v-1zm52 47v1h1v-1zm-54 29v1h1v-1zm31-13v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm47-5v1h1v-1zm-62 25v1h1v-1zm66-55v1h1v-1zm-54 29v1h1v-1zm12 11v1h1v-1zm33-3v1h1v-1zm-21-23v1h1v-1zm-28-19v1h1v-1zm19 50v1h1v-1zm1-35v1h1v-1zm41 1v1h1v-1zm-9 31v1h1v-1zm-1-45v1h1v-1zm-33 52v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-29-27v1h1v-1zm1-35v1h1v-1zm60 51v1h1v-1zm-68-55v1h1v-1zm60 51v1h1v-1zm-62 25v1h1v-1zm20-22v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm68 18v1h1v-1zm-8-4v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm18 54v1h1v-1zm-29-27v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm13 19v1h1v-1zm-46 33v1h1v-1zm25-56v1h1v-1zm-54 29v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-33 52v1h1v-1zm-9-49v1h1v-1zm-8-4v1h1v-1zm41 38v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm6-69v1h1v-1zm-13 30v1h1v-1zm31-13v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-46 33v1h1v-1zm38-37v1h1v-1zm19 50v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm1-35v1h1v-1zm39 71v1h1v-1zm-20-58v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-5 34v1h1v-1zm-49-5v1h1v-1zm1-35v1h1v-1zm63 56v1h1v-1zm-40-73v1h1v-1zm33 34v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm1-35v1h1v-1zm-16 55v1h1v-1zm-27-17v1h1v-1zm20-22v1h1v-1zm11 46v1h1v-1zm-54-8v1h1v-1zm25-56v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm32 69v1h1v-1zm12-26v1h1v-1zm1-35v1h1v-1zm-55 64v1h1v-1zm25-56v1h1v-1zm41 1v1h1v-1zm-46 33v1h1v-1zm25-56v1h1v-1zm13 19v1h1v-1zm-46 33v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-20-58v1h1v-1zm-2 76v1h1v-1zm5-71v1h1v-1zm41 38v1h1v-1zm11 9v1h1v-1zm-7-39v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-42 3v1h1v-1zm-8-4v1h1v-1zm60 51v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-34 7v1h1v-1zm6-69v1h1v-1zm-14 65v1h1v-1zm25-56v1h1v-1zm-5 34v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm-42 40v1h1v-1zm46-33v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm33-3v1h1v-1zm-67-53v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-43 38v1h1v-1zm58-22v1h1v-1zm14 47v1h1v-1zm-48-40v1h1v-1zm12-26v1h1v-1zm-14 65v1h1v-1zm6-69v1h1v-1zm-8-4v1h1v-1zm14 47v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm52 47v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm19 13v1h1v-1zm47-68v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-1 41v1h1v-1zm19 13v1h1v-1zm6-69v1h1v-1zm-14 65v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-74-29v1h1v-1zm4-30v1h1v-1zm32 69v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm17-60v1h1v-1zm14 47v1h1v-1zm-27-17v1h1v-1zm39-9v1h1v-1zm-27-17v1h1v-1zm11 9v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm12 11v1h1v-1zm-8-4v1h1v-1zm-55 64v1h1v-1zm74-51v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm7 8v1h1v-1zm-49-5v1h1v-1zm20-22v1h1v-1zm32 32v1h1v-1zm-42 40v1h1v-1zm39-9v1h1v-1zm-27-17v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-7-39v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm25-56v1h1v-1zm-5 34v1h1v-1zm-28 18v1h1v-1zm-1-45v1h1v-1zm33-3v1h1v-1zm11 46v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm19 13v1h1v-1zm6-69v1h1v-1zm-55 64v1h1v-1zm32-48v1h1v-1zm-50-1v1h1v-1zm33 34v1h1v-1zm-17-53v1h1v-1zm19 13v1h1v-1zm-33 52v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm46-33v1h1v-1zm-67 10v1h1v-1zm12 54v1h1v-1zm12-26v1h1v-1zm20 15v1h1v-1zm12-26v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-55 64v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-28 18v1h1v-1zm2-70v1h1v-1zm59 49v1h1v-1zm-48-40v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-22 18v1h1v-1zm46-33v1h1v-1zm-6 69v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-49-5v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-46 33v1h1v-1zm25-56v1h1v-1zm40 36v1h1v-1zm-53-6v1h1v-1zm11 9v1h1v-1zm20 15v1h1v-1zm33-3v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-54 29v1h1v-1zm65-20v1h1v-1zm-73 16v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-2 76v1h1v-1zm-8-4v1h1v-1zm-1-45v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-30 8v1h1v-1zm33-3v1h1v-1zm-67 10v1h1v-1zm13 19v1h1v-1zm20 15v1h1v-1zm-29-64v1h1v-1zm41 38v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm21 23v1h1v-1zm-29-27v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-16 55v1h1v-1zm1-35v1h1v-1zm12-26v1h1v-1zm-9 31v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm74-51v1h1v-1zm-33 52v1h1v-1zm19 13v1h1v-1zm-28-62v1h1v-1zm12 54v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm44 43v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm47-68v1h1v-1zm19 13v1h1v-1zm-73 16v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm20 15v1h1v-1zm-8-41v1h1v-1zm12 11v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm-14 65v1h1v-1zm24-21v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm10 50v1h1v-1zm19 13v1h1v-1zm-39-71v1h1v-1zm-9 31v1h1v-1zm41 1v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm-20 22v1h1v-1zm-34 7v1h1v-1zm6-69v1h1v-1zm-14 65v1h1v-1zm74-51v1h1v-1zm-73 16v1h1v-1zm11 9v1h1v-1zm20 15v1h1v-1zm33-3v1h1v-1zm-21-23v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-20-58v1h1v-1zm12 54v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm6-69v1h1v-1zm-27 46v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm18 48v1h1v-1zm61 16v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm12-26v1h1v-1zm20-22v1h1v-1zm-75 6v1h1v-1zm32 69v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm-14 65v1h1v-1zm24-21v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-13 30v1h1v-1zm25-56v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm32 69v1h1v-1zm-29-64v1h1v-1zm41 38v1h1v-1zm-54 29v1h1v-1zm12-26v1h1v-1zm46-33v1h1v-1zm-64 15v1h1v-1zm41 1v1h1v-1zm-46 33v1h1v-1zm57 13v1h1v-1zm-1 4v1h1v-1zm-37-31v1h1v-1zm20-22v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-47 68v1h1v-1zm12-26v1h1v-1zm46-33v1h1v-1zm-42 3v1h1v-1zm41 38v1h1v-1zm-29-27v1h1v-1zm40 36v1h1v-1zm-20-58v1h1v-1zm13 19v1h1v-1zm-16 55v1h1v-1zm-7-39v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm-55 64v1h1v-1zm58-22v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm39-9v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm47-68v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm12 11v1h1v-1zm40 36v1h1v-1zm-7-39v1h1v-1zm-21-23v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-46 33v1h1v-1zm-29-27v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm52 47v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm14 47v1h1v-1zm-48-40v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm52 47v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-28 18v1h1v-1zm-5 34v1h1v-1zm-10-14v1h1v-1zm46-33v1h1v-1zm20-22v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm19 50v1h1v-1zm-8-4v1h1v-1zm-40-73v1h1v-1zm33 34v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm47-68v1h1v-1zm-55 64v1h1v-1zm1-35v1h1v-1zm38 26v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-31 49v1h1v-1zm46-33v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm-5 34v1h1v-1zm25-56v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm32 69v1h1v-1zm12-26v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-27-17v1h1v-1zm46-33v1h1v-1zm-66 55v1h1v-1zm32-48v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm1-35v1h1v-1zm-31 49v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm32 32v1h1v-1zm-20-58v1h1v-1zm-54 29v1h1v-1zm19-14v1h1v-1zm-5 34v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm47-68v1h1v-1zm-75 6v1h1v-1zm-2 39v1h1v-1zm12 11v1h1v-1zm46-33v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm41 1v1h1v-1zm1-35v1h1v-1zm-49-5v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-7-39v1h1v-1zm-35 42v1h1v-1zm4 7v1h1v-1zm68 18v1h1v-1zm-8-4v1h1v-1zm6-69v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-20-58v1h1v-1zm13 19v1h1v-1zm-46 33v1h1v-1zm25-56v1h1v-1zm41 1v1h1v-1zm-55 64v1h1v-1zm1-35v1h1v-1zm24-21v1h1v-1zm-33 52v1h1v-1zm20-22v1h1v-1zm36-41v1h1v-1zm-54 29v1h1v-1zm-9 31v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm24-15v1h1v-1zm-54 29v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm27 17v1h1v-1zm-50-1v1h1v-1zm-21-23v1h1v-1zm24 28v1h1v-1zm-30 14v1h1v-1zm74-51v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm66-18v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-55 64v1h1v-1zm33-3v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm68 18v1h1v-1zm-49-5v1h1v-1zm20-22v1h1v-1zm-43 38v1h1v-1zm33 34v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-49-5v1h1v-1zm33-3v1h1v-1zm-34 7v1h1v-1zm65 17v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm59 49v1h1v-1zm6-69v1h1v-1zm-14 65v1h1v-1zm6-69v1h1v-1zm-54 29v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-27-17v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-25 56v1h1v-1zm24-15v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-72-19v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm12 11v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm40 36v1h1v-1zm-49-5v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-16-8v1h1v-1zm-34 7v1h1v-1zm33 34v1h1v-1zm12-26v1h1v-1zm12-26v1h1v-1zm-55 64v1h1v-1zm65-20v1h1v-1zm12-26v1h1v-1zm-53-6v1h1v-1zm-9 31v1h1v-1zm20 15v1h1v-1zm20-22v1h1v-1zm3 5v1h1v-1zm-53-6v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm-9 31v1h1v-1zm19 13v1h1v-1zm3 11v1h1v-1zm1-35v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-49-5v1h1v-1zm68 18v1h1v-1zm-47 5v1h1v-1zm-1-45v1h1v-1zm41 1v1h1v-1zm-28 18v1h1v-1zm-21-23v1h1v-1zm60 14v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-34 44v1h1v-1zm46-70v1h1v-1zm-54 29v1h1v-1zm41 38v1h1v-1zm-62-61v1h1v-1zm32 69v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm-16 55v1h1v-1zm1-35v1h1v-1zm-27-17v1h1v-1zm39-9v1h1v-1zm-28 18v1h1v-1zm60 51v1h1v-1zm-40-73v1h1v-1zm-8-4v1h1v-1zm52 47v1h1v-1zm-54 29v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-20-58v1h1v-1zm-28 18v1h1v-1zm40 36v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-21-23v1h1v-1zm-33 52v1h1v-1zm20-22v1h1v-1zm-21-23v1h1v-1zm33 34v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-54 29v1h1v-1zm41 1v1h1v-1zm6-69v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm60 51v1h1v-1zm-49-42v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-14 65v1h1v-1zm6-69v1h1v-1zm-34 7v1h1v-1zm40 36v1h1v-1zm-54 29v1h1v-1zm6-69v1h1v-1zm40 36v1h1v-1zm23 20v1h1v-1zm-42 3v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm40-44v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-55 64v1h1v-1zm74-51v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-53-6v1h1v-1zm11 9v1h1v-1zm41 38v1h1v-1zm-21-23v1h1v-1zm-28-19v1h1v-1zm41 38v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-20-58v1h1v-1zm12 54v1h1v-1zm-21-23v1h1v-1zm-7-39v1h1v-1zm6 43v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm52 47v1h1v-1zm-23 16v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm-8-4v1h1v-1zm74-51v1h1v-1zm-54 29v1h1v-1zm-8-4v1h1v-1zm60 51v1h1v-1zm-28-62v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm41 38v1h1v-1zm11 9v1h1v-1zm-53-6v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm66-55v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-46 33v1h1v-1zm66-55v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm-35 79v1h1v-1zm33-3v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm-10-14v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-5 34v1h1v-1zm-49-5v1h1v-1zm74-51v1h1v-1zm-73 16v1h1v-1zm19 13v1h1v-1zm46-33v1h1v-1zm-25 56v1h1v-1zm-29-27v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm41 38v1h1v-1zm19 13v1h1v-1zm-61-10v1h1v-1zm19 13v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm39 71v1h1v-1zm-28-62v1h1v-1zm33-3v1h1v-1zm19 50v1h1v-1zm4-30v1h1v-1zm-34 7v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm53 12v1h1v-1zm-21-23v1h1v-1zm-14 65v1h1v-1zm6-69v1h1v-1zm-14 65v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-36-66v1h1v-1zm40 36v1h1v-1zm-30 14v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-27-17v1h1v-1zm-15 20v1h1v-1zm44 43v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-53-6v1h1v-1zm25-56v1h1v-1zm-33 52v1h1v-1zm25-56v1h1v-1zm41 1v1h1v-1zm-76 41v1h1v-1zm1-35v1h1v-1zm65-20v1h1v-1zm-33 52v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm65 17v1h1v-1zm-40-73v1h1v-1zm41 38v1h1v-1zm11 9v1h1v-1zm-54 29v1h1v-1zm47-68v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm-27-17v1h1v-1zm46 30v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-27-17v1h1v-1zm29 63v1h1v-1zm-40-73v1h1v-1zm65 17v1h1v-1zm-54 29v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-33 52v1h1v-1zm25-56v1h1v-1zm41 1v1h1v-1zm-35 42v1h1v-1zm-17-53v1h1v-1zm-9 37v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-26-52v1h1v-1zm38 26v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm65 17v1h1v-1zm-33 52v1h1v-1zm-29-27v1h1v-1zm20-22v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-42 3v1h1v-1zm-8-4v1h1v-1zm60 51v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm-27-17v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm46-33v1h1v-1zm-67 10v1h1v-1zm40 36v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20 15v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm-5 34v1h1v-1zm-49-5v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm67 53v1h1v-1zm-48-40v1h1v-1zm-2 39v1h1v-1zm24-15v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm6-69v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-73 16v1h1v-1zm66-55v1h1v-1zm-27 46v1h1v-1zm12-26v1h1v-1zm-28 18v1h1v-1zm31 24v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-74 51v1h1v-1zm12-26v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm67 53v1h1v-1zm-48-40v1h1v-1zm-27 46v1h1v-1zm4-30v1h1v-1zm41 38v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm18 48v1h1v-1zm-53-6v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm-29-27v1h1v-1zm32 69v1h1v-1zm-40-73v1h1v-1zm-2 76v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm30 28v1h1v-1zm-9 31v1h1v-1zm-20-58v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm31 24v1h1v-1zm-28-19v1h1v-1zm12 11v1h1v-1zm-15 20v1h1v-1zm-27-17v1h1v-1zm74-51v1h1v-1zm-55 64v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm30 28v1h1v-1zm19 13v1h1v-1zm6-69v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-8-4v1h1v-1zm-20-58v1h1v-1zm-14 65v1h1v-1zm6-69v1h1v-1zm19 13v1h1v-1zm11 15v1h1v-1zm-9 31v1h1v-1zm-28-62v1h1v-1zm40 36v1h1v-1zm19 13v1h1v-1zm-26-52v1h1v-1zm-54 29v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm45 8v1h1v-1zm-75 6v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-14 65v1h1v-1zm1-35v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-28 18v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm20-22v1h1v-1zm-21-23v1h1v-1zm-21 63v1h1v-1zm11 9v1h1v-1zm-40-73v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm6-69v1h1v-1zm41 1v1h1v-1zm18 48v1h1v-1zm-53-6v1h1v-1zm65-20v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm-21-23v1h1v-1zm44 43v1h1v-1zm-42 3v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm20-22v1h1v-1zm-48-40v1h1v-1zm-8-4v1h1v-1zm68 18v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-2 76v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm74-51v1h1v-1zm-9 31v1h1v-1zm-1 41v1h1v-1zm-49-42v1h1v-1zm20 15v1h1v-1zm41 1v1h1v-1zm-9 31v1h1v-1zm-20-58v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-53-6v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-27-17v1h1v-1zm11 9v1h1v-1zm60 51v1h1v-1zm-8-4v1h1v-1zm-40-73v1h1v-1zm37 67v1h1v-1zm-7-39v1h1v-1zm-49-5v1h1v-1zm68 18v1h1v-1zm-28 18v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm-54 29v1h1v-1zm6-69v1h1v-1zm19 13v1h1v-1zm-33 52v1h1v-1zm-1-45v1h1v-1zm13 19v1h1v-1zm32-11v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm40 36v1h1v-1zm-1-45v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-73 16v1h1v-1zm11 9v1h1v-1zm61 16v1h1v-1zm-49-42v1h1v-1zm41 38v1h1v-1zm-29-27v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm8 4v1h1v-1zm-28-62v1h1v-1zm-9 31v1h1v-1zm-5 34v1h1v-1zm-2-73v1h1v-1zm68 18v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-31 49v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm40-44v1h1v-1zm-27-17v1h1v-1zm18 48v1h1v-1zm-54 29v1h1v-1zm74-51v1h1v-1zm-27-17v1h1v-1zm-46 33v1h1v-1zm65-20v1h1v-1zm-46 33v1h1v-1zm-8-4v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm41 38v1h1v-1zm19 13v1h1v-1zm-28-62v1h1v-1zm-22 61v1h1v-1zm20-22v1h1v-1zm-9-49v1h1v-1zm-28 18v1h1v-1zm60 51v1h1v-1zm-11-10v1h1v-1zm-56-44v1h1v-1zm41 1v1h1v-1zm27 17v1h1v-1zm-28 18v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm52 47v1h1v-1zm1-35v1h1v-1zm-29-27v1h1v-1zm19 13v1h1v-1zm13 19v1h1v-1zm-67 10v1h1v-1zm46-33v1h1v-1zm-25 56v1h1v-1zm45-78v1h1v-1zm-22 61v1h1v-1zm-20-58v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm60 51v1h1v-1zm-74 14v1h1v-1zm4 7v1h1v-1zm39-9v1h1v-1zm-27-17v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm66-55v1h1v-1zm-28 18v1h1v-1zm12-26v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm32 69v1h1v-1zm20-22v1h1v-1zm-21-23v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-13 30v1h1v-1zm25-56v1h1v-1zm-14 65v1h1v-1zm6-69v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-54 29v1h1v-1zm17-60v1h1v-1zm41 38v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-53-6v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm46 30v1h1v-1zm-26-52v1h1v-1zm18 48v1h1v-1zm-47-75v1h1v-1zm40 36v1h1v-1zm12-26v1h1v-1zm11 46v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm12-26v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-25 56v1h1v-1zm-29-27v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm52 47v1h1v-1zm-15-43v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm-55 64v1h1v-1zm39-9v1h1v-1zm-38 10v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm6 43v1h1v-1zm6-69v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm18 54v1h1v-1zm4-30v1h1v-1zm1-35v1h1v-1zm-55 64v1h1v-1zm66-55v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-29-27v1h1v-1zm-13 30v1h1v-1zm25-56v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-55 64v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-55 64v1h1v-1zm53 12v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm-54 29v1h1v-1zm74-51v1h1v-1zm-21-23v1h1v-1zm-33 52v1h1v-1zm11 9v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm19 50v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm11 46v1h1v-1zm46-33v1h1v-1zm-5 34v1h1v-1zm19 13v1h1v-1zm-56-44v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm12-26v1h1v-1zm-67 10v1h1v-1zm56 17v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-26-52v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm47-5v1h1v-1zm21 23v1h1v-1zm-2-73v1h1v-1zm-27 46v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm58-22v1h1v-1zm-10 35v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm13 19v1h1v-1zm40-7v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-62 25v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-16-8v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm12 11v1h1v-1zm46-33v1h1v-1zm-6 69v1h1v-1zm-29-27v1h1v-1zm46-33v1h1v-1zm-5 34v1h1v-1zm-48-40v1h1v-1zm30 22v1h1v-1zm-41-32v1h1v-1zm52 47v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm-72-19v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm-20-58v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm32 69v1h1v-1zm-75 6v1h1v-1zm20-22v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm20-22v1h1v-1zm40 36v1h1v-1zm12-26v1h1v-1zm-21-23v1h1v-1zm-54 29v1h1v-1zm33 34v1h1v-1zm-29-64v1h1v-1zm60 51v1h1v-1zm-27-17v1h1v-1zm38 26v1h1v-1zm1-35v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm19 13v1h1v-1zm-53-6v1h1v-1zm25-56v1h1v-1zm40 36v1h1v-1zm-73 16v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm41 38v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm-27-17v1h1v-1zm74-51v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-55 64v1h1v-1zm1-35v1h1v-1zm48 40v1h1v-1zm-29-27v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm48 40v1h1v-1zm-29-27v1h1v-1zm-13 61v1h1v-1zm4-24v1h1v-1zm19 13v1h1v-1zm17-60v1h1v-1zm-5 34v1h1v-1zm11 9v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm6-69v1h1v-1zm40 36v1h1v-1zm-54 29v1h1v-1zm12-26v1h1v-1zm31 24v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm47-68v1h1v-1zm-55 64v1h1v-1zm1-35v1h1v-1zm46-33v1h1v-1zm-42 3v1h1v-1zm-12 62v1h1v-1zm41 1v1h1v-1zm25-56v1h1v-1zm-33 52v1h1v-1zm-1-45v1h1v-1zm13 19v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-49-5v1h1v-1zm19 13v1h1v-1zm41 1v1h1v-1zm-74 51v1h1v-1zm53-74v1h1v-1zm13 19v1h1v-1zm-46 33v1h1v-1zm23 20v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-62 25v1h1v-1zm6-69v1h1v-1zm68 18v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm65-20v1h1v-1zm-72-19v1h1v-1zm-1 35v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm60 51v1h1v-1zm-49-42v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm-1-45v1h1v-1zm-54 29v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm52 10v1h1v-1zm-8-4v1h1v-1zm-29-27v1h1v-1zm-2 76v1h1v-1zm13-61v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm20-22v1h1v-1zm-55 64v1h1v-1zm46-33v1h1v-1zm1-35v1h1v-1zm-55 64v1h1v-1zm1-35v1h1v-1zm44 43v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm32 69v1h1v-1zm-40-73v1h1v-1zm52 47v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-20-58v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm-54 29v1h1v-1zm11 9v1h1v-1zm20 15v1h1v-1zm33-3v1h1v-1zm-30 8v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm7-41v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm-34 7v1h1v-1zm40 36v1h1v-1zm25-56v1h1v-1zm-72-19v1h1v-1zm39 71v1h1v-1zm-20-58v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm60 51v1h1v-1zm-68-55v1h1v-1zm60 51v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-48-40v1h1v-1zm60 14v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-54 29v1h1v-1zm12-26v1h1v-1zm11 46v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm-35 42v1h1v-1zm20-22v1h1v-1zm46-33v1h1v-1zm-53-6v1h1v-1zm11 9v1h1v-1zm33 34v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm18 54v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-73 16v1h1v-1zm65-20v1h1v-1zm-54 29v1h1v-1zm53 12v1h1v-1zm-54 29v1h1v-1zm5-71v1h1v-1zm20 15v1h1v-1zm32 32v1h1v-1zm-54 29v1h1v-1zm-1-45v1h1v-1zm13 19v1h1v-1zm27 17v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm11 46v1h1v-1zm-40-73v1h1v-1zm60 51v1h1v-1zm-4-34v1h1v-1zm-27 46v1h1v-1zm-27-17v1h1v-1zm39-9v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm47-5v1h1v-1zm-70 21v1h1v-1zm20-22v1h1v-1zm12 11v1h1v-1zm3 5v1h1v-1zm41 1v1h1v-1zm-46 33v1h1v-1zm17-60v1h1v-1zm-48 72v1h1v-1zm46-33v1h1v-1zm-21-23v1h1v-1zm41 38v1h1v-1zm-49-42v1h1v-1zm41 38v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-61-10v1h1v-1zm38 26v1h1v-1zm1-35v1h1v-1zm-27-17v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm20-22v1h1v-1zm-55 64v1h1v-1zm5-65v1h1v-1zm52 47v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-53-6v1h1v-1zm19 13v1h1v-1zm-2-73v1h1v-1zm19 13v1h1v-1zm-5 34v1h1v-1zm-49-5v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm52 47v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-61-10v1h1v-1zm18 48v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm65 17v1h1v-1zm-54 29v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm46-33v1h1v-1zm-35 42v1h1v-1zm33-3v1h1v-1zm-67 10v1h1v-1zm46-33v1h1v-1zm-27-17v1h1v-1zm47-5v1h1v-1zm-33 52v1h1v-1zm-37-31v1h1v-1zm60 51v1h1v-1zm5-65v1h1v-1zm-55 64v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-27-17v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm12 11v1h1v-1zm-54 29v1h1v-1zm5-71v1h1v-1zm41 38v1h1v-1zm-13 30v1h1v-1zm24-21v1h1v-1zm-7-39v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-48-40v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-2 76v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-74 51v1h1v-1zm25-56v1h1v-1zm-13 30v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-49-5v1h1v-1zm1-35v1h1v-1zm32 32v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm66-18v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-42 3v1h1v-1zm3 42v1h1v-1zm60 14v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-53-6v1h1v-1zm19 13v1h1v-1zm47-68v1h1v-1zm-63 60v1h1v-1zm20-22v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm11 46v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm74-51v1h1v-1zm-27 46v1h1v-1zm-27-17v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-42 3v1h1v-1zm41 38v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm6-69v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm40 36v1h1v-1zm-53-6v1h1v-1zm6-69v1h1v-1zm19 13v1h1v-1zm40 36v1h1v-1zm-53-6v1h1v-1zm35-6v1h1v-1zm-54 29v1h1v-1zm66-55v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm-16-8v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm58-22v1h1v-1zm-35 42v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-54 29v1h1v-1zm6-69v1h1v-1zm37 67v1h1v-1zm-40-73v1h1v-1zm32 69v1h1v-1zm39-9v1h1v-1zm-28 18v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-53-6v1h1v-1zm46-33v1h1v-1zm19 13v1h1v-1zm-53-6v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-10-14v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm-35 79v1h1v-1zm32-48v1h1v-1zm-28 18v1h1v-1zm68 18v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-8-4v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm-75 6v1h1v-1zm46-33v1h1v-1zm-13 67v1h1v-1zm-29-64v1h1v-1zm60 51v1h1v-1zm-27-17v1h1v-1zm38 26v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-53-6v1h1v-1zm10 44v1h1v-1zm12-26v1h1v-1zm32-11v1h1v-1zm-8-4v1h1v-1zm-54 29v1h1v-1zm74-51v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-14 65v1h1v-1zm-20-58v1h1v-1zm-28 18v1h1v-1zm47-5v1h1v-1zm-27-17v1h1v-1zm48 40v1h1v-1zm-71-24v1h1v-1zm44 43v1h1v-1zm1-35v1h1v-1zm10 44v1h1v-1zm-39-71v1h1v-1zm41 1v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-54 29v1h1v-1zm12-26v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm20 15v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm40 36v1h1v-1zm25-56v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm52 47v1h1v-1zm-42 3v1h1v-1zm19 13v1h1v-1zm-39-71v1h1v-1zm12 54v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm39-9v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-21-23v1h1v-1zm-33 52v1h1v-1zm20 15v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm1-35v1h1v-1zm30 22v1h1v-1zm-72-19v1h1v-1zm19 13v1h1v-1zm-9 31v1h1v-1zm61 16v1h1v-1zm-68-55v1h1v-1zm64 21v1h1v-1zm-55-16v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm11 46v1h1v-1zm-40-73v1h1v-1zm-13 30v1h1v-1zm12 11v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm39-9v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm46-33v1h1v-1zm-21-23v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm39-9v1h1v-1zm-8-4v1h1v-1zm32 69v1h1v-1zm-22-19v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-55 64v1h1v-1zm74-51v1h1v-1zm-27-17v1h1v-1zm-46 33v1h1v-1zm19 13v1h1v-1zm24-15v1h1v-1zm-28-19v1h1v-1zm20 15v1h1v-1zm40 36v1h1v-1zm-20-58v1h1v-1zm12 54v1h1v-1zm-20-58v1h1v-1zm-14 65v1h1v-1zm19 13v1h1v-1zm6-69v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm60 51v1h1v-1zm-68-55v1h1v-1zm19 13v1h1v-1zm41 38v1h1v-1zm4-30v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm6-69v1h1v-1zm40 36v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm12 11v1h1v-1zm32 32v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-75 6v1h1v-1zm12-26v1h1v-1zm65-20v1h1v-1zm-13 67v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm-40-73v1h1v-1zm-13 67v1h1v-1zm25-56v1h1v-1zm-33 52v1h1v-1zm19 13v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm11 9v1h1v-1zm41 1v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm-10-8v1h1v-1zm40 36v1h1v-1zm-30 8v1h1v-1zm-7-39v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-43 44v1h1v-1zm46-33v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-54 29v1h1v-1zm31-13v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm24-21v1h1v-1zm-13 67v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm66-55v1h1v-1zm-54 29v1h1v-1zm39-9v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm47-68v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm-34 7v1h1v-1zm24-15v1h1v-1zm40 36v1h1v-1zm-61-10v1h1v-1zm65-20v1h1v-1zm-8-4v1h1v-1zm-67 10v1h1v-1zm46-33v1h1v-1zm-33 52v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm33 34v1h1v-1zm20-22v1h1v-1zm-74 14v1h1v-1zm58-22v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm40 36v1h1v-1zm-20-58v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm10 50v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-21-23v1h1v-1zm-33 52v1h1v-1zm20-22v1h1v-1zm24-21v1h1v-1zm-5 34v1h1v-1zm-48-40v1h1v-1zm52 10v1h1v-1zm-54 29v1h1v-1zm65 17v1h1v-1zm-53-6v1h1v-1zm46-33v1h1v-1zm20-22v1h1v-1zm-55 64v1h1v-1zm41 1v1h1v-1zm6-69v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm19 13v1h1v-1zm-26-52v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm52 47v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm8 4v1h1v-1zm-28-62v1h1v-1zm11 9v1h1v-1zm-33 52v1h1v-1zm25-56v1h1v-1zm20-22v1h1v-1zm-13 30v1h1v-1zm-49-5v1h1v-1zm60 51v1h1v-1zm-40-73v1h1v-1zm44 43v1h1v-1zm1-35v1h1v-1zm-55 64v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-42 3v1h1v-1zm66-18v1h1v-1zm-33 52v1h1v-1zm25-56v1h1v-1zm-5 34v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm40 36v1h1v-1zm-20-58v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm32 69v1h1v-1zm-40-73v1h1v-1zm52 47v1h1v-1zm-54 29v1h1v-1zm74-51v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm20 15v1h1v-1zm-33 52v1h1v-1zm25-56v1h1v-1zm-35 42v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm59 49v1h1v-1zm1-35v1h1v-1zm-49-5v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-14 65v1h1v-1zm-20-58v1h1v-1zm52 47v1h1v-1zm19 13v1h1v-1zm-35-21v1h1v-1zm-21-23v1h1v-1zm59 49v1h1v-1zm-72-19v1h1v-1zm65-20v1h1v-1zm1-35v1h1v-1zm-55 64v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm-21-23v1h1v-1zm64 21v1h1v-1zm1-35v1h1v-1zm-55 64v1h1v-1zm20-22v1h1v-1zm46-33v1h1v-1zm-74 51v1h1v-1zm19 13v1h1v-1zm49 5v1h1v-1zm-29-27v1h1v-1zm-27-17v1h1v-1zm60 14v1h1v-1zm-48-40v1h1v-1zm19 13v1h1v-1zm-1 41v1h1v-1zm19 13v1h1v-1zm-2-73v1h1v-1zm14 47v1h1v-1zm-27-17v1h1v-1zm-21-23v1h1v-1zm-14 65v1h1v-1zm6-69v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm19 13v1h1v-1zm-73 16v1h1v-1zm32-48v1h1v-1zm-9 31v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm74-51v1h1v-1zm-63 60v1h1v-1zm41 1v1h1v-1zm-39-71v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm68 18v1h1v-1zm-48-40v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-10 72v1h1v-1zm-20-58v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm-62 25v1h1v-1zm11 9v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm12 54v1h1v-1zm28 19v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-61-10v1h1v-1zm19 13v1h1v-1zm-16-8v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm20-22v1h1v-1zm24 65v1h1v-1zm-40-73v1h1v-1zm-34 7v1h1v-1zm32 69v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-28-62v1h1v-1zm40 36v1h1v-1zm-34 44v1h1v-1zm46-70v1h1v-1zm-75 6v1h1v-1zm41 38v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm65-20v1h1v-1zm-72-19v1h1v-1zm10 44v1h1v-1zm2-70v1h1v-1zm19 13v1h1v-1zm24 65v1h1v-1zm-55-16v1h1v-1zm58-59v1h1v-1zm-14 65v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm-28 18v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm-19 23v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-27-17v1h1v-1zm38 26v1h1v-1zm-73 16v1h1v-1zm47-68v1h1v-1zm18 48v1h1v-1zm-54 29v1h1v-1zm47-68v1h1v-1zm19 13v1h1v-1zm-73 16v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm60 51v1h1v-1zm-31 12v1h1v-1zm6-69v1h1v-1zm19 13v1h1v-1zm-73 16v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-33 52v1h1v-1zm-2-73v1h1v-1zm68 18v1h1v-1zm-21-23v1h1v-1zm24 65v1h1v-1zm-34 7v1h1v-1zm-40-73v1h1v-1zm32 69v1h1v-1zm1-35v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm1-35v1h1v-1zm-2 70v1h1v-1zm12-26v1h1v-1zm-53-6v1h1v-1zm46-33v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm65-20v1h1v-1zm-54 29v1h1v-1zm41 38v1h1v-1zm-62-61v1h1v-1zm33 34v1h1v-1zm31-13v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm-26-52v1h1v-1zm18 48v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm32 69v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm46-33v1h1v-1zm-34 44v1h1v-1zm-9 31v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm10 44v1h1v-1zm1-35v1h1v-1zm20-22v1h1v-1zm-55 64v1h1v-1zm39-72v1h1v-1zm6 80v1h1v-1zm-8-4v1h1v-1zm-29-64v1h1v-1zm41 38v1h1v-1zm-54 29v1h1v-1zm25-56v1h1v-1zm-33 52v1h1v-1zm66-55v1h1v-1zm-67 10v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-29-27v1h1v-1zm1-35v1h1v-1zm32 32v1h1v-1zm20-22v1h1v-1zm-12 62v1h1v-1zm-7-39v1h1v-1zm-8-4v1h1v-1zm14 47v1h1v-1zm-29-27v1h1v-1zm46-33v1h1v-1zm10 50v1h1v-1zm1-35v1h1v-1zm-21-23v1h1v-1zm12 54v1h1v-1zm-20-58v1h1v-1zm-14 65v1h1v-1zm6-69v1h1v-1zm19 13v1h1v-1zm-33 52v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-48-40v1h1v-1zm19 13v1h1v-1zm41 1v1h1v-1zm11 46v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm31-13v1h1v-1zm-8-4v1h1v-1zm-48-40v1h1v-1zm68 18v1h1v-1zm-28 18v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm32-11v1h1v-1zm3 5v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm52 10v1h1v-1zm25-56v1h1v-1zm-54 29v1h1v-1zm41 38v1h1v-1zm-49-42v1h1v-1zm41 38v1h1v-1zm20-22v1h1v-1zm-16-8v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm19 50v1h1v-1zm33-3v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm-1-45v1h1v-1zm-14 65v1h1v-1zm-49-5v1h1v-1zm74-51v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-53-6v1h1v-1zm40 36v1h1v-1zm12 11v1h1v-1zm-54 29v1h1v-1zm65-20v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-8 59v1h1v-1zm-27-17v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm6-69v1h1v-1zm-5 34v1h1v-1zm3 42v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-42 3v1h1v-1zm40 36v1h1v-1zm-20-58v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-6 69v1h1v-1zm25-56v1h1v-1zm-61-10v1h1v-1zm71 60v1h1v-1zm-7-39v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-35 42v1h1v-1zm12-26v1h1v-1zm-7-39v1h1v-1zm-30 8v1h1v-1zm61 16v1h1v-1zm-74 14v1h1v-1zm66-18v1h1v-1zm-13 30v1h1v-1zm19 13v1h1v-1zm-48-40v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-27-17v1h1v-1zm59 49v1h1v-1zm-20-58v1h1v-1zm-50-1v1h1v-1zm-8-4v1h1v-1zm32-11v1h1v-1zm-9 31v1h1v-1zm-5 34v1h1v-1zm25-56v1h1v-1zm-14 65v1h1v-1zm47-68v1h1v-1zm-54 29v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-29-27v1h1v-1zm-13 30v1h1v-1zm58-22v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-55 64v1h1v-1zm-7-39v1h1v-1zm60 14v1h1v-1zm-48-40v1h1v-1zm19 13v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm2 46v1h1v-1zm65-20v1h1v-1zm-29-27v1h1v-1zm-5 34v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-21-23v1h1v-1zm32 69v1h1v-1zm13-61v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-74 51v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm41 1v1h1v-1zm-48-40v1h1v-1zm20-22v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm40 73v1h1v-1zm-29-27v1h1v-1zm46-33v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-13 30v1h1v-1zm46-33v1h1v-1zm-21-23v1h1v-1zm10 50v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-47 5v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm-15 20v1h1v-1zm46-33v1h1v-1zm-13 67v1h1v-1zm-21-23v1h1v-1zm-8-41v1h1v-1zm41 38v1h1v-1zm-30 8v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm19 13v1h1v-1zm-67-53v1h1v-1zm59 49v1h1v-1zm-39-71v1h1v-1zm11 9v1h1v-1zm-14 65v1h1v-1zm-20-58v1h1v-1zm12 54v1h1v-1zm40-7v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm-8-4v1h1v-1zm66-55v1h1v-1zm-27 46v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-49-5v1h1v-1zm-1 4v1h1v-1zm11 46v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm74-51v1h1v-1zm-43 38v1h1v-1zm41 1v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-67 10v1h1v-1zm4-30v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-35-21v1h1v-1zm-27 46v1h1v-1zm6-69v1h1v-1zm59 49v1h1v-1zm-72-19v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-16-8v1h1v-1zm20-22v1h1v-1zm31 24v1h1v-1zm13 19v1h1v-1zm-34 7v1h1v-1zm-29-27v1h1v-1zm19 13v1h1v-1zm41 1v1h1v-1zm-28 18v1h1v-1zm7-41v1h1v-1zm-27-17v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-1 41v1h1v-1zm-9 31v1h1v-1zm-20-58v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm8 4v1h1v-1zm4-30v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm-21-23v1h1v-1zm32 69v1h1v-1zm1-72v1h1v-1zm-9 31v1h1v-1zm41 38v1h1v-1zm-29-27v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm21-57v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm-10-8v1h1v-1zm40 36v1h1v-1zm-49-5v1h1v-1zm68 18v1h1v-1zm-8-4v1h1v-1zm-27-17v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-20-58v1h1v-1zm-33 52v1h1v-1zm25-56v1h1v-1zm40 36v1h1v-1zm-54 29v1h1v-1zm66-55v1h1v-1zm-75 6v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-53-6v1h1v-1zm65-20v1h1v-1zm-54 29v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm52 47v1h1v-1zm-15 20v1h1v-1zm6-69v1h1v-1zm-54 29v1h1v-1zm-8-4v1h1v-1zm68 18v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm1-35v1h1v-1zm-49-5v1h1v-1zm19 13v1h1v-1zm41 1v1h1v-1zm-74 51v1h1v-1zm-1-45v1h1v-1zm45 8v1h1v-1zm10 44v1h1v-1zm2-70v1h1v-1zm-49-5v1h1v-1zm68 18v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm-1 41v1h1v-1zm19 13v1h1v-1zm6-69v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-27-17v1h1v-1zm68 18v1h1v-1zm-21-23v1h1v-1zm-28 18v1h1v-1zm18 54v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm10 44v1h1v-1zm20-22v1h1v-1zm1-35v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-53-6v1h1v-1zm65-20v1h1v-1zm-73 16v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm60 51v1h1v-1zm1-35v1h1v-1zm-49-42v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-53-6v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm60 51v1h1v-1zm-68-55v1h1v-1zm19 13v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm-27-17v1h1v-1zm47-5v1h1v-1zm-27-17v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm-27 46v1h1v-1zm4-30v1h1v-1zm20 15v1h1v-1zm-9 31v1h1v-1zm1-72v1h1v-1zm19 50v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm-35 42v1h1v-1zm47-68v1h1v-1zm19 13v1h1v-1zm-33 52v1h1v-1zm-29-27v1h1v-1zm41 38v1h1v-1zm-48-77v1h1v-1zm52 47v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-28-62v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-54 29v1h1v-1zm12-26v1h1v-1zm19 50v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-49-5v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm1-35v1h1v-1zm-36 77v1h1v-1zm41 1v1h1v-1zm-48-40v1h1v-1zm19 13v1h1v-1zm-15-43v1h1v-1z" />
</g>
<image id="covid-safe-logo" height="108.7" preserveAspectRatio="none" width="108.7" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAEYCAYAAACHjumMAAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw1AUhU9TpSoVBzuoCGaoThZERRy1CkWoEGqFVh1MXvojNGlIUlwcBdeCgz+LVQcXZ10dXAVB8AfEzc1J0UVKvC8ptIjxwuN9nHfP4b37AKFWYprVNgZoum2mEnExk10RQ6/oRD8EBDAkM8uYlaQkfOvrnvqo7mI8y7/vz+pWcxYDAiLxDDNMm3ideGrTNjjvE0dYUVaJz4lHTbog8SPXFY/fOBdcFnhmxEyn5ogjxGKhhZUWZkVTI54kjqqaTvlCxmOV8xZnrVRhjXvyF4Zz+vIS12kNIoEFLEKCCAUVbKAEGzHadVIspOg87uMfcP0SuRRybYCRYx5laJBdP/gf/J6tlZ8Y95LCcaD9xXE+hoHQLlCvOs73sePUT4DgM3ClN/3lGjD9SXq1qUWPgJ5t4OK6qSl7wOUO0PdkyKbsSkFaQj4PvJ/RN2WB3luga9WbW+Mcpw9AmmaVvAEODoGRAmWv+by7o3Vu//Y05vcDFsJygkx8thgAABm/elRYdFJhdyBwcm9maWxlIHR5cGUgZXhpZgAAaIHt2kuS5LqSJNA5V9FLIAkCIJcD4iPSO6jl9zFG3k+9uvI+9XpQg8qQzIj0cCdBg5maqhq2+R//d23/x59yndd25XqXp5Tdn+u5nrP54d7/+DN/ff957div79/f/hy//j22v/zF6XvyPf28WK9fv02/Xv/t/eX37y70F7848t98IP1+m/PPN67t1+vnfv6nFT338eeH+nmcX3/XGvda8+fp2lWEofw81M8ttt8u442vKKXvY8VX9Tf7uX5fj697b3s/rn3sfX999eM5ziPt67iOsR3tWMc8hu/96NZ4nfOsvp9nP9P32p3q+Zw97elIV3wd66zpSSPd6Uz9nCmla0vn72s5vvs+3/26hxz7OLz1PFzs8JG/+7X9ozf8M19r9V2MjsPTH79iZV3nGftwRBhT/OttNuRYv/YtfwH+7ev3P9ufNjbZwfyF+faAbX9/LvHm44/cSl8CJO/Lvv/k11FH7Nr5Zcnl3tlijmQL9nKkfJRjr+dZj+NK522DmpWf6TpfO3DkfA6LPK+Uir255ZF7+0w9vvee+fx5XanYn5xKqvbmSc1mXVeWP/W65VDLKV8555JrvvOT21ZSuUoupdQSNddqqlfNtdRa7/rUdqf7uvNd7nrf93O353ySksxPeepzP8/Tmnu2a2u5+XTzjtbe803v9ea3vPW93+dtXfr0q+deeu13f3ob50jjGnmUUcc9ntHmMaXSNq+ZZ5l13vOZbcm1lda18iqrrns9q/2+a7929b98/Qu7dvzatfPbqXhf/X3XvFprXOi7xBE4k2PP7Nh5HXa8xg5I6DP2bL+P6zpj52LP9udUFfm0yBx7M469bUexhdc8zryO3/fuj537p/dtE+t/tG/nP7NzW2zd/4edO7eZ/mbf/mLXRiBh/3bspwojpntSfbmddzvfOfq2+nvFf25v/ne+b//uBf73Qv/uhd5Uc3pblWzneY/enl5mOfc1c2657/N6+n70J837fJ/+ytKtr7qv81lTY9mXBtVeuTfXsc9nnPma58pPnde9xl57mr3NdLtuX1DyyDNJzEfWbVOar8d1pXVJfZwnInFFVe7rbems8nM+uU716mq9vfke9Z3pLMphzv5OdZjvrSqMst53Pc9KT7+k/F2i4/nE+Sgfqz3bW0aR+CO9/uw/6fzua/8jl7dfP+wrXfm4lGV62m+pr+X/09+3v/+GU/VBg6eWlgMS0lvX3vr7QoQ5DoBy1zlXLmtre3veLJ519PvuaMBe55vLez13LV0J69ivArdDaUUJq9/2zlzau8SoAK0FhTYRyK89q7OPG7rN7637m1pfV3tSrvH/t4KZy81zSb/isporHM9va9/+3tOnXkUOLEf4/vw9j1VuoT7aWPduWWDkup5llbW3G3EqF3wq2SJWP9/7aBaInLTWc5MBQW5qOluv+zWf/gBhdEp29Gez213mTbu7jlrXe6/rPVt7jluOPvJol8F3pMfI5RlTxDztHfgNbo+r+GzxaC/0t4i916djdvWQp/tolwSodi0/s97ZDqWgD0dx7z1b6oL9ba6u3aikeY7tFgdJe16XqD/PBTHvYs/XniKP2wNvtQkA/q43Zdt3DYvy0NmzlL7f7q9UtpLTneuTL69We75SWr26ns+f78ig/3yuaQGvyB/pPofO4O/qHcp3tahKU9pa7fI/0q2qOKEXOc8yLPwaJc0W6bjLAhVcytg984v+CuMtQOsRbSxn3Vu5hBA2YL/Xikgeb7WrHhN4HOec5z3f967HUDtTWef3GLrU9XqMpGsN+iFXfe2IyEy3qqJ7TA9SpHcfWSPz7rLW8wrDBTFOzaoc45q7XD7q22vL7yzV0s/N8uuayavHoStjxVnR1o+Kv3XvGmGpaUTHa/cY83mTjjuz/SoITdXsxGyv26GE1Fa3O3tZotWj0J76tgeCAUSd8ZzjloE2Zxz4/93mPq68MkYBVfrt122LojoSLN0DNOXKEqCuEJ5+lolR2u+nvE0Xrs0LPWiV3Gz5eOJ3E2oVmLip41rVpBy+67qmhLmE8yrpLkPGAwRFcfaCwdvfZIWPeh6CZ6FK8W0wtKetvCWYwXv1ARIeGzJ3N7WNGNaUgI/yrMFNDstGcYe43MeJilUgPPYmeL3nrTzuuiiydSskIH+dXaJIycfDX3RfbcPn1wStz8oK7g02U8tIcwnAQ42cwB/QWNxlK6+xrlFzlG1av1jG7fpuWl7sJgf6D/WCa8nXNy14uGDfuwOcLdoS5v7BF/jQL1DUoq7Q71OlVeGbMuqAoUAIxlzvPkZs14osX/2IXCqbyAp6PtXsnANYJHvs7vfd9ALr9GPTXJKiIl+OBKkluHLL09IyQvjkUsfmnStuLMCBJvrbPQV6yKQeqwxQtfn9N3w9Eir2F3C6/c0LWm3kRXoHWlZ+etX1B8S2t0UGe14k7ZGJvWiEFTBtd21n3Hnl+WoYykQ0JVvR6uWBlyWfp1cR92rXdZQX9X3JwnEqj4WNLsXbt4AvpZ/VjT995fcUfhWovU4ssiS8/TmeVPLck44+JFBUHsowusLTva6zDcGeKnNvin1/pdmcgZk5VwFXDJkefVTgIJyeMp9be5jC1PZVe81N3WAWLr9N5WLfx3vZudg7jxT9ElLeSWrj++pC3xitlTVRXJeYDdCfL9ocMBYJNzeoOfL9TNW6n/2BM2mM8vSujqTpexU5KC/LcnsdADMq+4SXrxhF39qPMWDQVmJdp+v6IH6+0o5c0636hOKkia7yUIBQKUcQQdUN15TPKbmfiVhZ9z2PbQrsnXcM7djhpoTVX/EzxsDIlTpp9ENt71jvua5zBHzRQ9BXkfoU3dFSmdya67V0ZZtwtTbul23AS7jIoXx7LqtAuwq081P0LZ0GDVSzL8HUst1/daF3GyHKzuGlF9sglWQg9b48zxlcI4356juaQoU2yRo940sivQdZhhRmHfN4uhgpsDxeu9Q05mAi6UOg44kdTGWcVUtGGWUTDJujamOv3D4kjF5TFmtGjM4sVhVTGLXKyrqUq/SUyN57w8ovN2mi012CVUiKDEUOEbzGA5e18bvmzRZwY17QBZVp+uPOgRvqBUzpChdk1esBOKYlHJ0se9YYlhvaSRcNu+A5tpCXyFpztYewnEeAwk0Xuo96unGeQOjWZXeK3a2HFPEcryZ8t6xoZNB1MxAiLhRcsDot33J0GQ+XxKWod0oTj3miFPGp9Dw17qGocaPo92c4c7Nt0fKi39CBSxVpI88K6tylK+8J2EJtHU144dpzCP71DORfNxyRkjfrCRXatN6JcHac1AreAWEk4EAmtSOtW09livjEna9vT1sgbI/o4Gj6Fc1deslb4OLD3aIeInKCUe4RgACLiGRNjvYlhTs9UI4HHQ5wQPBag16xjNkgYt4qpnNHh7qyNhT3pHRPze7Cl+6r08E32NSIAnFrn70yyFYqnv0IZL+FSFVsa8DVUy+oKLKnC7xHsscBM3awDxoQKYJbrPsKasPGaWvP0Q1rQy9yl/Z9a4zAgtOUnlC0tit5uMgpDOAXY0iBctk2CatfS91bk0N2IgpfE1NSIWp0BiQcOlXekdasx2udQwTtPYPiPoOH0fqvDPM5FEohrNSni+G6OVASTmvZbCtlFfzV45Bq1v7BLEgZ+v95vQVxnLt8fdRhuTos187sRzzzPmB3y5u1X7v3XQSLIoL97XKnJrePjHuXAmf1aAuHsvPRGRC3IkpLq2KQhWUhvNupDpeSWO+V1pBmsIfHogjsVXBRSHTk+o4LWArTHhT2hJMSDkPSWfOk64ZHi4BprFhCt8vpDS4YWY9sPwAXdqCXq39kq59BOfpFJCih9xRcK2YA3ZsUJpt+emkIoLv8IWHCvJGs118oQaAHCygONc3WudaGWleLwmCsX/cFOdwa3N+C3IGXiKsQscCN7KVtohD3jNL1PVx0pGuFn7Mxe1CMdMNZtGyk4Og1RER3YX7zqvxGO/7I8zfkTD/VRSAqN0chLi3uPU6YHT01SRg07oUYdLASh2rBq6hMkQnYxgnRew+hwpt26rLlVDszgCQ6wEZsqSaVOHbkgdxQFdrcRD8nmdJChVf1Cm20JGRdh401HnrXCiQdPurq212qjq6dN9QcGMIfOYkqZAvqVIzKw2ytjEzKHqf+tE1dG8UaxD+yrcg2slVtR+cUN/s7YvOIPeXRMnZSg0Zw2BURcJxQf8aDVcAdcC29UM7e10YLglOXGpjgWUisVlRm5BmDLFRlLsN/AswQ2cYZoAqtff9yscYrHV3YrjN9nfHgYpAzaPtwSxSGy4551SvYNWX0RikkJgDQiDbfWlgVIKQND9fnNuUmtbxyWIUP1YIvX+vqQZEL6LWN8ptSvdNuP6nbDswPwkkFQ0OF2YJJbEKgApsOjSh9nLb3LpOZtjc6hCQcUTqq8Q7kJC14i/IKb0DnyISoXqR6G6c+1DSmZVkH0/HVHjw4pAGrD1JN5T7RKvutsfSgFWzUSOcaZFiOqclZaJE7GEoNNoCLHFGdemvHXmzZFSxmTup2hU3+vAYfYYnMFxL4oQ8u50jAYtPodt2EsirQCKMlazgbQyKN66nDeoXXpUM3UrdiT0YjcDgWLR5rv3AEjxZ+wghnha+kgZ69hg8lW23T1SO2bxZrMf90OXSwMPwY5j1gDQAAnpE2LhgUij5E1LGWfSI6sg6hFNTEoE0Uv+HZn3rW17E6nRRGWvoQbTtRYGg+eNcI1EQROurdouAJGFaIoHmIM2Q+S+dk/ZDzwRVm5AX2N/CwQ2a7koJcwfzNUth4zJXyrzck7nH/evr7AMwHTUXlPUIJXhl6ByuoHZyvgMqPDuceOp+bFz6MnPIh7YB/xPsLTctfxNqf/WwhYOthlBViE3e5oy+4L8LCWOgoaTpxTr4RA9BtpFldHHYKJNF6mEWndVMF5u/B4k7pC6gL8jePm4iAOx/pAzFF9w9tqtgXnlpJUWHuQ51VYF4+B+3MLMiPjGKxfi8aORqyrqNOtMrqB8hytK+uUSh7urUZWo/KhFDyUdp3ky5lgXbdwbGYqLdLEmkorO3LEUleCQ8B4TCCa9jUm+GRW/80Iw1/GVb8raH2Xww2Uolmvk91coR5KoLEMdU3X44IMI+fhu585BSNL/QDJwiYmF20PhkPVyB3uoKaAjhEkZTQAIpa3mQvKX5He8Kv+Tk9P/b8fBQBTsZ9MSMpr1bQgm6in3xDLhTuW2I/mKn4K4+tfm9DKrNmD17Rv5AN+OUKPwIRuV2Jx4mIKuqP+mGILzXZyHPmwqWz7Gs7w9ux2OjMTCdexbwEijqjSll0ZH0LeUNpsive0EMaarcjpOjBQCrhah9tg4zToCThisdP3nLJXFSFTaSrmEUZzIB7hoEKwS4ZAYOvpfjzhIiSxgOPTb84YhMWFJaoYcVpZUQ1FoBYj1DEPeT5GwNdDADdpg/at8aOe4ZKFZyNh1JAAwWFlT7h0e/5jrDIrQB3yUcCMgsNgSeoCTVHnJTD7BixsZkjaNS1US21kM2xEp3l82503ejxnLMv00NFKhDtgImIKtGtJVQ+Ibc0i/EZ0RtkNVCbWK3eicycWsTnJoPIEymEsLGvwwMzOUswJpbmi0dIpoJ0sNrNyM9N/AkPEib5VfScEDUG7YgAtYkJ5ejtCA31HH4LzR0ah/LFoAzRhA2ItnsjFRS80ng8AsAL/FKS8finOxabLxNFglEECMAt8hqDPDylG47rshVMCjaNBrVD1lEGen/O3rAPbrv0BF2INH+MFFmPNwgHmuRRWowm7AuF5m99thTU6v3mmUTLxSxEN3KYhGcNXisn8LdHE93TKYNgBkl7hq0h0DkYnLlLYLZ9CRn8we1iGTEEIABRoGHYVjMB8jksYDMOiywBvBKtx0wPMVW1OmAMD/hniwULfkcKVGZ4ys4HHkJKkMfrvRS62UOKVm+1YYtHs/nag1wbXzsSXcsrdjEkT7lT1NYIRfHuRao7J0ETsVej/NRsp1GQTycicmhzFBEsQ4qNLcrz9cJUBBritw1IEn71JjHICpVEdN/Jd55cABtQIzzzCV8IQo4QZtsR75uKlDu9u+G3fmYJvgeO94dw04Chk2Ag41e4KxwixrS/FCnfjmxgjf3E/C9CHlscs9kzKFj0biCh2rvTE4CJJgvqMYOeYrstbccXKnoP9f+Ct1BIMoffUBQTFU++wUUqBSbaDbWJg0RX5mOu0G64NpMlTKr4OKD98Oi33zpUE7+PX6f4n6jpFzwcvV3aBg5e4JsUUawKc5OdjDcWNSLMcmBmMvZ6MA42ucLmwA4nUVClOMhAcudI3PAQCHNkmicbg+1nA/MwsUgTySyBM2hHlJQPgLijA8kHavr76Qpy+dfft99fIGlwpOs8wFDMGIvpYfTJj+CO2CJcS2/icTArJTthUqItEQEedDMMCLiM0y7hmxvuX+FeMx2jB1EumA3YOQ1NcjaDAVsGggYD7329WsxFb2kd6LHswnhjKHk62cSIHWiCRvJwRvWM8JMvbFE/JBuQVBqDcWaUmJFEyatRR0puqYR/TqthrCPGQO4bMSvaj9JkPtLezPXAtcmeJsnUKw+YLBkyTTstodptP0MJvKtbUgWLB9tRPxCawUkM4bRhAoaEQmtAQghCHN9LqhzIhK97H5va7iHeUBQKGisK2YkgkyQwmdlhFBdDGzQ+HGDp7F/Ph53TeQtj5Zb0yCNeGN8b94GmmDJae0Y+kiI2yX338fWSNw4T3NFOS3QqbCqQ8viQWUUnp8ZMltQj554hRJjDW11M7jrf4KBCQJRRjMExAcuBSpG0g045poEQ/lvwCYxi06277IgZyBEeZ4k46q0heXIccYjCzCGvW4gGVHlGJjwxjuYriWoL0x6HXOu8gl+kMBoi1B2RQMQMJUNDBV0GrmAJETSssgFBWUOSpJg9stNCwBj5DO6MK6cwjUM9gbQjZosTHMty2MPI4Y6nsBv7JZzy/UVv8hk2gV1gRB1hQ3/+pXMvsC5c8bAfwwnVXvbPlgyddkpr6uYTC1Qfn+cNvQqJwBHuFCuiDtSzCkrRaCEBxiOzZJreFA46qKp/8cnXyMuAkHf7xmmTDXBx5jCh8US2Cg3ChcTY6BU2N1UHkolb+/HlGEl/fhh2X5FY6wqqsMaWU5yqwSxqnGCjPtGaEnFROTGpQJ1HyCk4OOZPRrH26Yy7B+VGShKGYuBL9dEGNpAFY9wETPnLUDowK65g58kangAXNg7mGF3RudfXpvbQSGl+6+P6mWaY/cQ5AFnsj+jeOhF2e5NWD+2QwhS3zeHT/unT4Tv9+vDYzWk1VSCK6MYxBh3SnCq4QA1vC0wQiGG3kT6cMesCezncEo5D/WIGED4ngmbmN3xSSxh13Sf8KLa2YsUDTpa4USSIenhpgDRKMUZsn4nOXlKEPDljsWANIbnY9UZNwqE4z9DQ2qs+esSppxzTLtoskJQ3ewXLjabRfvJKjfZw2O/AQ2NDPdnsLWqJ46BfeFCQR+tLXQTu1zBr/nZy4E/DKfR/06kM8szZf5td/cVxlBaDMOrYRLYHAcUzzx7y9g17BQKgPptjE0gMYzXHoQIQzdMxVCUaaKv5GelnOI/Q9sQqYxBiVBxuTLS3FvMAv7wq4Wf8x8XjTHNF3q85QxVExABeXuGxYkySOWrC4QrNlGNscvPqnO1ytIDoF8YNcWfZG4W28ItBFxxzRqoGuZChNbKELDNbipEtBYL5HSEKuSeWUszXOUezbzGf8awaTnjpZ/jOKiRG3vZMrVLcfA4MisvsygjweUCMGiakdkEHl7BOOBFeqXw1IwUJ4ujcCC4fNnrMQ50b0cEYBTFkG5SDUzNRA9ovJhFTU/ZlkLux7TyOMA29CTo67fh+gkJG1NDD4bUbzZ9xNDC6nD4DYhr3Sw9D9smhHgYMcWwcFT72CBOjAXc7UL4Tk4bXSgS3zyHneNCqXdoeIbkfkBt5bSYdlpeM3NjF3iPfapwo4n8zp6V0VIXA1yBy4fum9+c4EVwN599EUjbQ+Dl8VbTr3dyHnAtEo4AQOetSjECQkOAgzTgZS3+jAl4eQeM0Mc7KCJJFY6t/0pehafjlKIUDOSYCx6O9YVYxxMYXaW1Ob8Shhxsa0G9kCJtSTMWfsMH1IMpX7TybvY1P9Din4LgMu8nt3fS52CPArbk7FHVmxnkE1L2ELXFY5nfIAKclecyBjA7dIsxEU27DNCcHcnhgMRHwMwSJg1MIIHZOs1KHpnFsUTlsaE5VxvnIxfU0F6HCII+xYyoI14zRU2yb7GRZhKHNwLnjDLJxYJxKcoYDBB0cTUaoCx+xdt5IiTbhweJAiYhGZclVdBxPdmBkxflD7WwCwBajJYwjWCa2Fm6esgSWMWLfsB3JegQ3Cf8UInkIgldMPt59x0GwmHW7LXulRe3aMnyAhTttzB32aKHXaHtGrcOO32xphEV+hKym1s2qIUI4Jc7e0HEwDAsC/MR3zj8np5DkDycdrWI4hOcfZ6iMqGycmRXOs2C59uPwQgmv9Byhi4wdJHrMlrhShFl4Z89H9Flj4Y+HmG3Hr/b3SYD/9HK8WOJ0LJcSU45mGQc45/qmQHuk0qcgEYUVGwxMRoy9V0xAiVvxBt0wIQZbz2dVrzjP8I4f0KclviMxuv/j+JlxuqOoxvjsmBVHoiyeJo+HibGQynanJ1hZHC9wAYfzzP3sa9QATfXG0SRFSwn/4Ly8/UMY0Hn/0tG67V8+i/ePLpRYX//AXPu737f/7gf/90L/My+kiPXF7f8BfZGYAjrY4vwAAB6NSURBVHja7V1diFVXlj5PRvJQDD6YUTLQ8yCEBEIJ/dAIifUgHR+EUgsRXzSMohjbToTuIMS/F7VpGFJDaCI2UgpNoOuee6/OQ9UEJmZ6EiLiDKEx1Ig+dKNWC9a992SmNFVlSjNrnaprbt0659y9ztnnZ5/9Lfjyo7du7bP3Xt9Za+211nYciD1S91YS1jkVb8AZ9XbRfx9xKs0zTrV5nuA6tdZnjtu64VRbtwj3CA3CI8Ic4Snhh0U8XfyzR4ufuef/TI1+lr+j2nL97+Tv5t/Bv2vhd66j71+JhYBAzCWRF0iR+wk7ScGPk6JfIqX+kpT+fgdB5I37REQ8pkv+GF0eK42Zxw6BQApDJqtJOTfTv4+Ssn5CuFkgEokH13+GT4h4jhLpbPafEQKBZCCj3npSuHd8q6TamjCeTNQx4Vs7/OwVmgMIBKKFUF4npTpM7kSFFOyBRYTSCw/8OeG54TmCQCBKLk8fKcwQWSjnSIlug0iUcdufM5fmjucQAoE8J5WXCQfojXyZFGUGZJEYM/5c1mhOeW4hEAtJZS3hICnCGAghZfAc12iuec4hkBKTygo/H6TGOSKteSh/5pj3575Ga1BtrcCGhJRDKt4Gx20M06aehJIXBrQWzWF/bSAQA62VFwn76I15FcpceFwlq2afv2YQSMGJ5RUCpd637kJxjQOtmV/a8Ao2MqRobtBGslZGaJM+g6IaD1rD5oi/phBIzsSyhYilDqUs7QlU3V9jCCRjYtlGm28cSmgNxilOsw0bH5I2sQwid8VqcE7NIBQBoptYNhGx1KBgwKLrVKNg8CYoBiTpqdB62kwXoFRAMJoX/D0CgQiJ5SXCadpE01AioAem/b3CewYCUSCX/Zb1WgF09ayp0d6BQEKIZYDcoStQFCAhrvh7CQJZJJZVi+7QLJQD0ITZhT3VXAUFs/t0iJo7ta5BIYCUcM2pTA1B0eyzWrgfyzAUAMjotGnYGUE/Gluslu206Nex6YGMcZ2sme1QwLJKtdVHVstZFCQCuRZS8h6stdA3uGQu0Zt03w5qh4Di1DbxnoSUglwOoZscUNCueoegoOYSyxrCR9jIQMEDwLxH10BhzSKXN8gl+hSbFzAEn/p7FmLEKdFuWrA72LSAYbhDzeF3Q4ELfVLkHaOFmsNmBQzFHFkyx6DIRUycq3ofY4MCJYnL8F5GYl5ByKWf4i1oBgWUC67f1KofCp4vuWyixfgCGxIoKb5A57z8yGUHLcA32IRAyfGNM/pwBxQ+S3Ebe3G5GWDVpXC85yEZSM37JU14E5sOsAxNf+9DUj2Gfp8m+jE2G2ApHlNo4H0QQTrk8gFN8PfYZIDl+J5I5gMQgl5yOYE2CwCwpO3DCRADyAUAQDIFd4tALgAQTjJwlxIEdBFzAYDeMRkEfmMcReO0CABUT5dwhC1KokOeCwBI82SQjKeU/o8MXQCIm/GLsoJQctmE2iIA0FC7hALJZeTSj6poANBahd0PYvFPi6ixDvq5AID+fjK4TdK3XtCJDgDS6oxnea7LMWwCAEgR1vb45Q7qaNANAGljju7E3m2bW/SGg6tFACH+efCkEjBXy3DHnnuXanSLXRWXogEy/Ow3f/rh7t+/0RPjP92N+QoO+n5KQd81NlgvuM4VkFsvW08qEcyOX1cwX+FB34/KTi6HsMhAHHzzj5uVCObl87cwX9Ekc6is5PImPeAkFhiQYsevXSVy+eMbBzFfvTHp62LJkun6CONYXCAOmDjgHmmNx4z7OlmifJezWFggDtjlUSEXRt8f7mHO1F2ls+Ugl4q3HV3pgLjY+4uLSuTCQWDMl7AbXmVqu+lxF7qUvnUdiwnExVevDSkRzFsnxjBfclw3u16p7g1jEYG0c1++enUI8xXfVRo21TUawuIBWeS+wD1KiMrUkGF1Rq1VNPBrWDwgi9yXn/3mPzFfyXCNXKVVJp0ancaiAVnkvsA90uYqnTYl7jJAA57FggFZ5L6c3PVbzJcezFI/3wETkuquYLGArHJfUBqgETXS3WKTS2M/FgrIKvcFldNpkIy3v6iu0Us0wAksEpBV7gtKA1LBBAV8XyoiwSCwC2SW+wL3yKaAb91bTwObxsIAWeW+oHI6VUz7Ol2g/roXsChAlrkvcI9SD/heKIr1sgkLAmSZ+8IkhMrpTDJ8C3BDJC5NAzLOfUFpQGZWTC1ncvEGsRBA1rkvKA3I1IoZzDOpbgyLAGSZ+4LSgMytmLG8kuq2YQGArHNf4B7lYsVsy8N6QY9dIPPcF7hHuVgx41n3etmCiQeyzn2Be5SrFbMly5OjOiYdyDr35cg//Q7zlZ8VU8/KetmICQeyzn1BaUAhrJiNWWTtjmCygaxzX1AaUAgrZiTtrN1XHFxBAuSQ+4LSgELgmc8BKVovZzDJQNa5L3CPClVpfSYt6+VF+gV3McFA1rkvv//5rzBfRYFLHDBCXJBCYt0+TDCQR+4L3KOiBXub+9JIrLuKyQWyzn3hI2zMV+FwVffR9IYiPeDJiZkfPn/4/XPUJ58Efm7rteklnxv40/8t+8xPxr9d8pnOzw3fmV32d1E4ReNqf2+3vP1fj/w/7//3/+35Pfw8pya+88fWPV7pmMLmxpTcF5QGFPbIeoPO4G6hroB9+8ajZQocpIx/efR0yWdG/jq77DPv/fnxsu/6u3/1/L9jBZVI5/eHEQyTl0ROdpBWnDH95fFTo3NfUBpQWDdJ05WzbnMFfeFkkR6OCcB78myJIjFRdH6GLYVu4Z9pk0cbX387v+QzlyfnYitzGgTT/WxlIBjV3BeUBhQ62DtJwd4VOoK7u4r4gN2Kxv/f+fcX/zoXqHCdbhJbPd2yZ5EIgn4HExRbRWEYvp0OwXQSo3RM3fNiUu4L3KOig7hBQ92RW8SHC3KTOq2TbvcoyMpgpY/6jm5l5riIZHwX6Xe1ZfCr6VCC6XTv+Pfz7wkjqO4xBbl9ZSkNePVf/htKXGy4SXNf1tKXzBfx4YLcJBUrIcoa6HSPkhJMtxXVJpFeBNNG97O1f7fpBKOa+wL3yAjMk5u0Nol7dLDIDxjmJoW5R51uEpNMlHsU9P0cz4g6semM/3SOofN0SYVggly3tgUkHVMQeZmQ+7L30EUosBnB3oNJ3KNCt8TsdpPa1km3e1SfXEo4fNTLR9hR7lGcgGonifBYPn/4ZBlpBREMkwf/OYOf6WtvPpSEpGMqEsGo5r6gNMAojMV1j14u+sMFuUl8rNvt9nQrNf9Mt5UT5GrEJZgoxAnydlpAJhOMqnuEymnDMEJcEcM9OmDCw9X/Fu0O7QkJjnYTU9sFiXSRHqm5SDoJ5lSPPJgFSyl8TN1WmQnBXZQGGOcmHYjjHl024eF6KWxbwYJOZjqVVCXGIw3yJiWYbvfK5CCvau4L3CMjcVnqHvXRD82Y8HBBblKQ8gUFdXspaVYE03ZjumNFQYmBJhKMJPcFldNGYobcpD5JacCQSQ/YmW8S5faExS+C3KO0ktqiCCaILLtriaRjYsA9AjJIuhuSEMw5kx4uSGmD3J6g5Lww9yittPxex9RBtVFbOwhQOibV4HMRgruonDYZ3jlJa4bbJj1c0Js/yHVQ/VyeBBNmpcQtwMybYCS5LygNMLo26bZq/OV1Ex/wwzszS9yCoNYMC60evlvyuTD3aKE1woyoNYJKPCSoXUN3nCWohUS74FE6prxrkSS5L6icNhyjxB29rZfmYUwWgNIAIMZx9WGV4+kKJgvIOrgL96gUqKjEXx5govKLJbELxUFdDkgzti6WERQlYS6t3BdUTpciDvOgV2vM9WV7aI5nvEvxC46PcI1PUBuH9hEzHwnzZ8PiN2mdgHFtVFh7ie5gL4+RE/CKVAKQNPclzD1iUm3XaPVC1HyofkcUmPhNJPkc4jDro+Iv75TFEmCiiHPi0nki9CEpfhqKzOPjmqmwBEH1DnpzhSUaHbkvkqznqCRIndIuzfALZsm6BOksi8O8E+UeXTL9AZlYkipuN9F098VNAh3EEodo+OJ4VfT94V5mwd2o0oAiEkyQMOEElXVYmg9zKYpgJkx2hZJYLCpEk8RaYBNbxQ1KIlFEKImHJO3FIsl9iaqcNoVgOvcIiIY4JCT/ZbWpD8W9XXRbBWHm8daIvJkw8KbLYnzt0oIgIuQcEx1Krzv3Jao0wDSC0fUyKkH7htVBAd7NJj7MnoC+umlL9w0G0S7Rd4XZ4Kp3ETH+4fz/5OoemUwwSV5GpUCNuGQ5wTSPmmi55CUqm+fdgLqiLEmmOwB5ZO/vlBWfYzFpB3d7VU6bTDBRLTcsCPQeDYq/fGJazCUrtyPsDRVlBgfdy5S1dJcJcPBW+ej4tXiZtZJYz1snxkpPMFaSjEtcEpDBe9Okh0g7YNpbeZ+EEgz/ed7jC3PnJAQgrQ3SkftSRoLp9TIqIW52B3hfMOkB3k4Qd+HF/tpbKEzkBk9xTp44ByK6P81c7LHxuNi14ixeVjD+b+53E5ewuje3JNh7ctdvcy0NKAvBsHCSp2WB3hc6A7z9Zbde2keIYYlRfjf/b+d7KuvbPczdoCtHVBWkV9LWezFzfLpdJdVgr7Q/iyS4q2IdZU0w/0FWaVgmLxM9v5CSuOWSw4ESZPT2dzaY2llm64UtCtWMSyaaoE2kevQotV74ezleI4k9xSHYzvIHSbBX1U2S5L6oVk5nTTCq7Ud5j8RZg6jmZiXscLez8wTpeFmtl1MxsnC7lViVoKTWS9x8CR5L0N1Jqm6dJNirmhMjyX1Rdb2KSjDttQ5r1apK9CU/STpuXImA9MoPlQ5zvU6pJGYtN4OSSJLAX9AFc73enp0kqRrsZTdJpXRAV+6LKQQTd81Nu+pXT8mA2/rShEG/J8wrSfq2kBay9Yrh6N5oQT2GVefj5yfHtZUOSIK74z/dncoLJS+C8dddYE1a4yYxp3RYMPdNGLTkxCfrqL3UPdJ1bCmxYrrdJNVgby83SXL0Lbk1wBSCkVrWkpibwQRzv00uK00ZtCSCn3VykySrWGef3FOCUgS+RjduZm+YmyTJfZFeqmYKwUj35tu2JN6NeCs5B2adKQMu8ltCoug6LnCLkzHcHZN6jbrIJS0dkLhH0iJKkwhGEvDVuf6FBnML5cAMmFIaIJGsxycJ9ul8g0XdWKkS9FZ1b8JKByTBXemlaiYRjOQFY02gtzI14DijjV0gmOSQvMF0m8hJ5mXvLy7GzomR5L5EuVkgmNLmwuziNplHQDD2Eowk2NudvyLJfYlzawAIxvhcmCOcZHemjASTda9UiYukM2U8qYvk96wh4ohTOiBxj3pVToNgSkkwZygHpnkeQd5sc3R6FUumpYRhp1eSAsi2myQJ7sa9VM0kgqn/7QmCvMuT7c7zMbVbxjKBUxobdes+ptaZbCUhtigFUg32tptESXJf4l6qZhLBSPbmoDWd7jyXs3g/M2XAkrdEkjKBLFw4XTUpko0dlRukGuxlN+lVwfF2kjunkWhnPD5jgrlhyoClpQJJk+2kcRyJsuvINJZWlkdlD0uCvZzun7Z7VNZSgaxffDln895gF+mWSW0ypcWOcYO97YpqScXzKWGD7yT3LUnbNqgQmmqwVwJp0yrTCEba1N2eI2oft5hg7pk0aGkHOr6ULKnyqlZVx2k2dTJmOwlpuwYVa04S7E2jNMA0golzY4Q17RoWcI8JpmHUTQJfyW8SCLsrSHrHksoNinFacEpIJs4FbhKzXBK81Vk5bRLB8LjirLNV7tECGkwwj0waNLs8cdoWtltmBhFE+5J1lU3T6/Y+acBP8r1xe/1KYlGSzF7dpQF5E0z7RRQEfrHxiyDJ7aEWXmHyiAlmzrSBv5fwviF2L3ijMOI20/6QclnC4jvSJkTdRMMbnXNlGCMJGn7HeWtKgr1pukdla/ptofXyg88t9I+nJg4+zXuok5rU0m5zaUnc6zJ+/9avEpNL0utny0QwFl8l+9QxdfB5X7zWa9NwrCTP8SUpSdAR7E3qHpWJYCzKe1kOkwcfN96R1RtJ2tJSpyTNZP43Qa6LjsrpMhKMyjU3NhDMU5MfIGsl5k0jeSPlQTI6yiQk3e50lQaUiWCk19GU2UWaM/1B4hzdZkEueYxPV6W25GoTHZXTZSKYqGuFbQzyPirDw6R9H3TSTRP3Hh3JyZjuN2acnJgkpQGmEwxbLZYl0ikdUzfK9FBxb93LwipoWzM6T8B0jy9psFeXe2QSwXy+eOUsCGVZLVLDuFIBCdHwJfdJNg3fR5xW46qFxLnZwo6vDWlOTNzKaZMIhvcV5zrxHsu6sZmJpQK3yvyQ7JrwRuCLy8MIh60A/jvOa2GlzTI41x4fkw1bNmFH22yV8TPw+LL07/mCNr5RQBW6s7Z5blQQtWaq3xEFJjvEVeIUOxrUrkHnxm2ngBf1DVT08QGAAm4Y1XAKAADjGk41XUxEMiujE5gXAHge5HWNavqdt1vF8Q+V4kP+e46ncLEiV+Fm6eYwyXG8oBeQBAZk0/TbkGtL8ixH0HGszL1ksjjKVD2i52CyDuLjPsl+57+M8W7A0fzzLoQ5wJ5G3gL415YYcvFaHuCWDPpzJr5PjWiktVk6SgryqmoPOpaO01FQl1hfcxRMMEeMuTo2a3dI2pIyjkWjO2YjbUilo/E4CAYEEw6+OrbiDWAisiWXztTyrZpM67jKldSaAsGAYMItmKkBx6l76zAZ6bpFafRs0TXusNseQTAgmMQYJW4hglmJychng+q8wiJJ/VWSYC8IBgQTcUy90vGl2rqPCclWWXS2UZRewKYz2AuCAcGEkMt957m4rS9hvcg3ZzvfJU7z8K0ajzWTKnmSI2sQDAgmBF92EEzzku0TIrECovp+8J9zjkaU4nECXtHcurixIBAMCCbEgrn0I8FUmsdtnxDJVSOq1kdQkynd11fEvStJV7BXlWD4ZE4lw1gVQe6lhGDQvyX1HJjjPxLMaGOn7RMi6c0ijZ3w59uKqDP3RfcbO47SqRJM0tMq3fMBgkkZNW/njwRT9/pBMOoE82FMF0f3pk4a3NVBAiAYICQHpr+TYF4AwcjySPj2xbw3qWpgWRKAlgZ7QTBAIEaIU5ZItXXT5gmJex0tn8Cw8oTde51mEaaqSC6Bkx5Zg2CAgADvTWeZVFuf2F41rSu/heuM0m7TwFXMEsVWDWJLj6xNJZj3/vxdasFmEAxxyXKCaR61fWLSuPYkDetGokh7Fo9PJQS6R3DkqkowTLy6euMWJRM77Lgc8RfikmXiNjbbPjFsdaRbGqCnglr1aLr7SDwNayPrPJio8goQTGECvJuXE0zdW43JkeXDxJWTCfuwqFpa3cooiTOpxihAMCCYgADvaidQqq0JTFA2JBO3FklyNN39/RxbUQ32srUFggHBxMCEEyooGVjytldVxiQkIw0Ef/2tWq+asGZSuoO9IBgQTHiJwPJA7zuYpOhUf90iSdrj4+akgVrZbYkzIBgQjLAHDHFIqNS99Zik4I3LJxhpKZOqFSOpO4r6TlUriK0YEAwIRhjgXe9ESrX1ABMV3VKTrQDeWLqUS8VSkChPr0ZWPHZdwV4QDAimwz164PQUt1XBZMkIh4+3LyrclxQmlyfntAZ32fLodc2GrvokUwmGrxkJujhPClztuwSV3gRTbR7GRCXLCJbGbVRaOKSRBKjDipEk2vHbPimiWmWgVCD3+Mvh3gRT917HZLX8plFJkuIkOSe9CGbrtekf8pSoBlmoRQI6mny/7ihJtXXb5ok6uRijYMXfk6BTmXK1cw+CyatrnMqRNQgGWMRtR1nc5jmbLRddKf6qeTRRF6Dl2QZSJRANggEW4J1TJ5jRxpCtx9FRpCC5X/pdgYsUFeTV1RJThxUDggEiOtgNqRNM3eujH5qxjVwkLg03nGIS6Szb50S4hRMlGSlEuWF5BndVlNJUglE5aZNA5x1XBmKG6o/6HJG4rcs2HTXnqchh7pfulphJJYgkTCWYLObGIlx2xFJtHrBlgtgayUui3nwS0mv3SokDSc0VW2kgGBBMl3t0QE4wde9lWC/pSlRFtaRmKCpIrLt6vPvIGgQDgvG5Ipa4rTFb4i9ZHwWz1RB1MiWJ4+xJeOmXhMy6j6xBMNYTzJgTW6rNgzZNFmeKpt2ioa2k3a5GkmCljnR1CcF23gIJgrGcYGrewfgEU/fW0pfMo0WDzo34pGdOjaQYUdfphSTzuNMlA8FYTTDzPkckErfl2tyiQVd8holFNfdC8jt1NROXdLvrPLIGwVhNMK6TWEYbu2xPImJ3Jk57BlZY/jkJCUiOpnVvakmwt/27QTAWE0zN25WcYOreCvqySWQrLiUcTqjjRDsmEFZMdlX43/z/bPlExVgAoAS9X5gTVjhaxG0OY1IBAOioPRp2tEllagMmFACAjtaYGxyt4rauYmIBACBcdbRLtbkPEwsAAAV39+knmLr3In35XUwwAFiNu1Q5/aKTilSaZzDBAGB1cPeMk5rUvVfolzzDJAOAlXjmc0Cq4rZGMNEAYKX1MuKkLpWpjZhoALDyaHqjk4m4rTomHABsOjkinc9MKlNbMOkAYJX1ssXJVNzWOCYeAKzAuJO5VKa2YeIBwIrEum1OLmJJS00AsBhjTm5SmRrEAgBAmdsyNAadXMVt1bAQAFDKk6Oak7tUpjZhMQCghKh7m5xCiNu6gAUBgFJl7V5wCiN1bz0NahqLAgClwLSv04WSSvM0FgYAygDS5cJJ3XuJBjeBxQEAozHh63IhpdrYjwUCAKOT6vY7hRa3dQULBQAm5ryQ7hZe6g8HaLCzWDAAMAqz5BoNOEaIi4AvACCwm17AdxUN+hoWDQCMwDUyClY5RkllaggLBwBG9HoZcowUXDkLAAUP7DaHHWOl7q2lh7iOhQSAQuK6r6NGS2Vqu4OrTgCgaHjm62YpxG2exYICQKFOjc46pZFqq496S6CHLwAUA+O+TpZK6t6b9FCTWFwAyBWTvi6WUqrNQ1hgAMjzSJp0sNTiNj/CQgNALkfSHzmll7q3huIxn2LBASBTsM6tcayQuvcGPewdLDoAZII7vs5ZJZWp3fTgc1h8AEgVc3T1yG7HSqk0j2EDAECq+S7HHKvFbXyMTQAAqQR1P3asF66FqOHyNgDQ3J2OdWotCGaBZPppMr7AxgAALfjC1ynIEpLhGyK/weYAgET4pjg3MhaOZB7uoAm6i00CALFwl8hlB4gkOui7lyaqic0CACI0fd2BqNQsNX5JE/YYmwYAlPDY1xmIKEfmfZq477F5ACASpCOkK5BYJPMBuuEBQERXuirpCCQRyZwAyQBAILmcAEGAZAAA5GKIu4SYDICYC9yiVAO/OF0CLD4tQkA3iyNs5MkA9uW54Cg602Q8ZPwC9mToIokul7IC1C4BNtQWIf0/H5LxCyRRhQ2UuSoahYs5k0w/+skApezngpYLRQn8UmMd7t6FjQmUpROd8ZfSl/MY+5iDRuKAuZhDD93inzDxbQW4EgUwDXf8mzYgRsRl+N4lXO4GmBJv+dS+e4vMj8uswTW1gAHxlo/8204hphINXfZdbU1iMwMFw2T5L6K3x2V6kxZ0HJsaKIhLNO7vSUipXKY+emOcRdsHINc2C66/B/ugkKU9yp7aTgt8HZsdyBjX/b0HscJl4sS8YWx6IKNA7jAS5+y0ZoZoA1yDEgAp4Zq/xyAWi9tcRbGZ07QZZqEQgCbM0r46TVbLKigYpO02DdDGuALlABKeEF3x9xIEEnza1NhPG2UCygIIMeHUvP1QIIiKNfOSb+JWW9NQHKAHpn0Xm/cMBCIkmvVENBegRECIO3TB3yMQSEKi2eQ3AIJSAe1mUJUpdJqD6D5xagzSBhuDkllLLGNELINQBEjageBtqG2yrHaoMrUNGx+SrVSmttDmq0MJS0ssdX+NIZCciWYjBYNHUEhZloLE1oi/phBIwYLBrxDRnMGlcIZeblahteM1hEAKTjQvUpxmH23aq1DcwrtBV2m99vlrBoEY6D5t8KtpXXTVKxCpcDe5YX9tIJBSiNtcQVbNLtrcLm3yeSh65pj3536U1qDurcCGhJTZhVpLZHPQz6uA4qefu1L3DqIfC8RWsnmZyOYAKcJlUogZkEJizPhzWfcO+HMLgUCek00fZQsPkTt1jhTlNshC2UqhuaI5G6W54zmEQCAqhPPwdVKYw6RAFcIDkMlzQnngz0mN5obnCAKBaJDK1HoinHfobX3Jsp41E/4z1+jZ6w9RvQyBZORSrSbS2UxHrkdJAT+ht/rNEpDJTf9Zat5Rp9LY7D8jBAIpDOm8QKTTT7GcnUQ8x/03v9v6knC/QC7OfX9MPDYe4x9prPWH/f7YIRCIoVJtriRFXkcENLCYD3LET4+vNs8TXFL6zwg3iARuEe7Rfzfo348Ic4SnHSTxdPHP+O8a/mf5Z/hn+Tv4u/g7F1Lvj/i/i39n/cE6+v+VWAh75P8Bk+fHJ5ES0I0AAAAASUVORK5CYII=" />
<path id="a" d="m10.96875-6.734375-2.203125 6.734375h-1.671875l-1.484375-4.5625-1.53125 4.5625h-1.65625l-2.203125-6.734375h1.609375l1.515625 4.734375 1.578125-4.734375h1.453125l1.53125 4.78125 1.5625-4.78125zm0 0" /><path id="b" d="m5.75-2.5625c0 .023438-.007812.15625-.015625.40625h-3.921875c.070312.3125.238281.5625.5.75.257812.1875.585938.28125.984375.28125.269531 0 .507813-.035156.71875-.109375.207031-.082031.398437-.210937.578125-.390625l.796875.859375c-.492187.5625-1.203125.84375-2.140625.84375-.574219 0-1.089844-.1132812-1.546875-.34375-.449219-.226563-.792969-.539063-1.03125-.9375-.242187-.40625-.359375-.863281-.359375-1.375 0-.507813.117188-.96875.359375-1.375.238281-.40625.566406-.722656.984375-.953125.414062-.226562.882812-.34375 1.40625-.34375.507812 0 .96875.109375 1.375.328125s.722656.53125.953125.9375c.238281.40625.359375.882813.359375 1.421875zm-2.671875-1.546875c-.34375 0-.632813.101563-.859375.296875-.230469.1875-.371094.449219-.421875.78125h2.546875c-.054688-.332031-.195312-.59375-.421875-.78125-.230469-.195312-.511719-.296875-.84375-.296875zm0 0" /><path id="c" d="m2.125-4.484375c.175781-.25.414062-.4375.71875-.5625.3125-.132813.664062-.203125 1.0625-.203125v1.390625c-.167969-.007813-.28125-.015625-.34375-.015625-.429688 0-.765625.121094-1.015625.359375-.242187.242187-.359375.601563-.359375 1.078125v2.4375h-1.5v-5.171875h1.4375zm0 0" /><path id="d" d="m2.4375.078125c-.4375 0-.859375-.0546875-1.265625-.15625-.40625-.101563-.734375-.238281-.984375-.40625l.5-1.078125c.226562.15625.503906.28125.828125.375.332031.085938.65625.125.96875.125.632813 0 .953125-.15625.953125-.46875 0-.144531-.089844-.25-.265625-.3125-.167969-.0625-.429687-.117188-.78125-.171875-.429687-.0625-.78125-.132813-1.0625-.21875-.273437-.082031-.511719-.226563-.71875-.4375-.199219-.207031-.296875-.507813-.296875-.90625 0-.320313.09375-.609375.28125-.859375.195312-.257812.472656-.457031.828125-.59375.363281-.144531.789063-.21875 1.28125-.21875.363281 0 .726563.042969 1.09375.125.363281.085938.664063.195312.90625.328125l-.5 1.0625c-.460937-.25-.960937-.375-1.5-.375-.3125 0-.554687.046875-.71875.140625-.15625.085938-.234375.199219-.234375.34375 0 .15625.082031.265625.25.328125.175781.0625.453125.125.828125.1875.425781.074219.773437.152344 1.046875.234375.269531.074219.503906.214844.703125.421875.195313.210937.296875.507813.296875.890625 0 .324219-.101562.609375-.296875.859375-.1875.242187-.46875.433594-.84375.578125-.367187.1328125-.796875.203125-1.296875.203125zm0 0" /><path id="e" d="m3.75-5.25c.476562 0 .914062.117188 1.3125.34375.394531.21875.703125.53125.921875.9375.226563.398438.34375.859375.34375 1.390625s-.117187.996094-.34375 1.390625c-.21875.398438-.527344.710938-.921875.9375-.398438.21875-.835938.328125-1.3125.328125-.65625 0-1.179688-.207031-1.5625-.625v2.40625h-1.5v-7.03125h1.4375v.59375c.375-.445313.914062-.671875 1.625-.671875zm-.265625 4.09375c.382813 0 .703125-.128906.953125-.390625.25-.257813.375-.601563.375-1.03125 0-.4375-.125-.785156-.375-1.046875-.25-.257812-.570312-.390625-.953125-.390625-.386719 0-.703125.132813-.953125.390625-.242188.261719-.359375.609375-.359375 1.046875 0 .429687.117187.773437.359375 1.03125.25.261719.566406.390625.953125.390625zm0 0" /><path id="f" d="m3.1875.078125c-.554688 0-1.046875-.1132812-1.484375-.34375-.4375-.226563-.78125-.539063-1.03125-.9375-.242187-.40625-.359375-.863281-.359375-1.375 0-.519531.117188-.976563.359375-1.375.25-.40625.59375-.722656 1.03125-.953125.4375-.226562.929687-.34375 1.484375-.34375.539062 0 1.015625.117188 1.421875.34375.414063.230469.710937.554688.890625.96875l-1.15625.625c-.273438-.46875-.65625-.703125-1.15625-.703125-.398438 0-.726562.132813-.984375.390625-.25.25-.375.601562-.375 1.046875 0 .4375.125.789063.375 1.046875.257813.25.585937.375.984375.375.507812 0 .894531-.234375 1.15625-.703125l1.15625.625c-.179688.417969-.476562.742187-.890625.96875-.40625.2304688-.882813.34375-1.421875.34375zm0 0" /><path id="g" d="m4.03125-.25c-.148438.105469-.328125.1875-.546875.25-.21875.0507812-.445313.078125-.671875.078125-.617188 0-1.09375-.15625-1.4375-.46875-.335938-.3125-.5-.773437-.5-1.390625v-2.125h-.796875v-1.140625h.796875v-1.265625h1.5v1.265625h1.28125v1.140625h-1.28125v2.109375c0 .21875.050781.390625.15625.515625.113281.117188.273438.171875.484375.171875.238281 0 .441406-.066406.609375-.203125zm0 0" /><path id="h" d="m5.828125-5.171875-2.34375 5.484375c-.242187.59375-.539063 1.007812-.890625 1.25-.34375.25-.761719.375-1.25.375-.273438 0-.539062-.042969-.796875-.125-.261719-.085938-.4765625-.199219-.640625-.34375l.546875-1.0625c.113281.101562.242187.179688.390625.234375.15625.0625.304688.09375.453125.09375.207031 0 .375-.054687.5-.15625.125-.09375.238281-.257813.34375-.484375l.015625-.0625-2.234375-5.203125h1.546875l1.453125 3.515625 1.453125-3.515625zm0 0" /><path id="i" d="m3.15625.078125c-.554688 0-1.046875-.1132812-1.484375-.34375-.429687-.226563-.761719-.539063-1-.9375-.242187-.40625-.359375-.863281-.359375-1.375 0-.519531.117188-.976563.359375-1.375.238281-.40625.570313-.722656 1-.953125.4375-.226562.929687-.34375 1.484375-.34375.539062 0 1.023438.117188 1.453125.34375.425781.230469.757813.546875 1 .953125.25.398437.375.855469.375 1.375 0 .511719-.125.96875-.375 1.375-.242187.398437-.574219.710937-1 .9375-.429687.2304688-.914063.34375-1.453125.34375zm0-1.234375c.382812 0 .695312-.128906.9375-.390625.25-.257813.375-.601563.375-1.03125 0-.4375-.125-.785156-.375-1.046875-.242188-.257812-.554688-.390625-.9375-.390625-.386719 0-.703125.132813-.953125.390625-.25.261719-.375.609375-.375 1.046875 0 .429687.125.773437.375 1.03125.25.261719.566406.390625.953125.390625zm0 0" /><path id="j" d="m5.90625-5.171875v5.171875h-1.421875v-.609375c-.199219.21875-.4375.390625-.71875.515625-.273437.1132812-.570313.171875-.890625.171875-.679688 0-1.21875-.191406-1.625-.578125-.398438-.394531-.59375-.976562-.59375-1.75v-2.921875h1.5v2.703125c0 .835938.347656 1.25 1.046875 1.25.363281 0 .65625-.113281.875-.34375.21875-.238281.328125-.585938.328125-1.046875v-2.5625zm0 0" /><path id="k" d="m.6875-5.171875h1.5v5.171875h-1.5zm.75-.71875c-.273438 0-.496094-.078125-.671875-.234375-.167969-.164062-.25-.367188-.25-.609375 0-.226563.082031-.421875.25-.578125.175781-.164062.398437-.25.671875-.25.28125 0 .503906.078125.671875.234375.175781.15625.265625.351563.265625.578125 0 .25-.089844.460938-.265625.625-.167969.15625-.390625.234375-.671875.234375zm0 0" /><path id="l" d="m5.828125-5.171875-2.1875 5.171875h-1.546875l-2.171875-5.171875h1.546875l1.4375 3.515625 1.46875-3.515625zm0 0" /><path id="m" d="m2.78125-5.25c.800781 0 1.414062.195312 1.84375.578125.425781.375.640625.949219.640625 1.71875v2.953125h-1.40625v-.640625c-.28125.480469-.808594.71875-1.578125.71875-.398438 0-.742188-.0703125-1.03125-.203125-.292969-.132812-.515625-.316406-.671875-.546875-.148437-.238281-.21875-.507813-.21875-.8125 0-.476563.175781-.851563.53125-1.125.363281-.28125.925781-.421875 1.6875-.421875h1.1875c0-.332031-.101563-.585938-.296875-.765625-.199219-.175781-.496094-.265625-.890625-.265625-.28125 0-.558594.046875-.828125.140625-.261719.085937-.484375.199219-.671875.34375l-.53125-1.046875c.28125-.195312.617187-.347656 1.015625-.453125.394531-.113281.800781-.171875 1.21875-.171875zm-.125 4.3125c.257812 0 .488281-.054688.6875-.171875.195312-.113281.335938-.289063.421875-.53125v-.515625h-1.03125c-.617187 0-.921875.199219-.921875.59375 0 .199219.070312.355469.21875.46875.15625.105469.363281.15625.625.15625zm0 0" /><path id="n" d="m6.453125-4.203125-1.421875 4.203125h-.46875l-1.21875-3.578125-1.25 3.578125h-.453125l-1.421875-4.203125h.453125l1.21875 3.609375 1.25-3.609375h.421875l1.234375 3.640625 1.234375-3.640625zm0 0" /><path id="o" d="m2.203125-3.1875c.394531 0 .707031.121094.9375.359375.238281.230469.359375.5625.359375 1v1.828125h-.421875v-1.78125c0-.332031-.085937-.582031-.25-.75-.167969-.175781-.402344-.265625-.703125-.265625-.34375 0-.617188.105469-.8125.3125-.199219.199219-.296875.476563-.296875.828125v1.65625h-.421875v-4.453125h.421875v1.84375c.113281-.175781.273437-.316406.484375-.421875.207031-.101562.441406-.15625.703125-.15625zm0 0" /><path id="p" d="m3.34375-1.453125h-2.640625c.019531.335937.144531.605469.375.8125.226563.199219.515625.296875.859375.296875.195312 0 .378906-.035156.546875-.109375.164063-.070313.304687-.175781.421875-.3125l.25.28125c-.148438.167969-.324219.296875-.53125.390625-.210938.0859375-.4375.125-.6875.125-.324219 0-.609375-.0664062-.859375-.203125-.25-.144531-.449219-.335937-.59375-.578125-.136719-.238281-.203125-.515625-.203125-.828125s.0625-.585937.1875-.828125c.132812-.238281.316406-.425781.546875-.5625.238281-.144531.503906-.21875.796875-.21875.289062 0 .550781.074219.78125.21875.238281.136719.421875.324219.546875.5625.132813.242188.203125.515625.203125.828125zm-1.53125-1.359375c-.304688 0-.558594.101562-.765625.296875-.199219.1875-.3125.4375-.34375.75h2.234375c-.03125-.3125-.152344-.5625-.359375-.75-.199219-.195313-.453125-.296875-.765625-.296875zm0 0" /><path id="q" d="m2.203125-3.1875c.394531 0 .707031.121094.9375.359375.238281.230469.359375.5625.359375 1v1.828125h-.421875v-1.78125c0-.332031-.085937-.582031-.25-.75-.167969-.175781-.402344-.265625-.703125-.265625-.34375 0-.617188.105469-.8125.3125-.199219.199219-.296875.476563-.296875.828125v1.65625h-.421875v-3.15625h.40625v.578125c.113281-.1875.273438-.332031.484375-.4375.207031-.113281.445313-.171875.71875-.171875zm0 0" /><path id="r" d="m3.25-3.15625-1.578125 3.53125c-.136719.300781-.289063.507812-.453125.625-.167969.125-.367188.1875-.59375.1875-.15625 0-.304688-.027344-.4375-.078125-.125-.042969-.2382812-.109375-.34375-.203125l.203125-.3125c.15625.15625.347656.234375.578125.234375.144531 0 .265625-.042969.359375-.125.101563-.074219.203125-.210937.296875-.40625l.140625-.296875-1.421875-3.15625h.453125l1.1875 2.6875 1.1875-2.6875zm0 0" /><path id="s" d="m1.890625.03125c-.3125 0-.589844-.0664062-.828125-.203125-.242188-.144531-.433594-.335937-.578125-.578125-.136719-.238281-.203125-.515625-.203125-.828125s.066406-.585937.203125-.828125c.144531-.238281.335937-.425781.578125-.5625.238281-.144531.515625-.21875.828125-.21875.300781 0 .570313.074219.8125.21875.25.136719.441406.324219.578125.5625.132812.242188.203125.515625.203125.828125s-.070313.589844-.203125.828125c-.136719.242188-.328125.433594-.578125.578125-.242187.1367188-.511719.203125-.8125.203125zm0-.375c.21875 0 .414063-.050781.59375-.15625.175781-.101562.3125-.25.40625-.4375.101563-.1875.15625-.398438.15625-.640625 0-.238281-.054687-.453125-.15625-.640625-.09375-.1875-.230469-.332031-.40625-.4375-.179687-.101562-.375-.15625-.59375-.15625-.230469 0-.433594.054688-.609375.15625-.179688.105469-.320312.25-.421875.4375-.105469.1875-.15625.402344-.15625.640625 0 .242187.050781.453125.15625.640625.101563.1875.242187.335938.421875.4375.175781.105469.378906.15625.609375.15625zm0 0" /><path id="t" d="m3.4375-3.15625v3.15625h-.40625v-.578125c-.117188.199219-.273438.351563-.46875.453125-.1875.1054688-.414062.15625-.671875.15625-.40625 0-.730469-.1132812-.96875-.34375-.242187-.226562-.359375-.566406-.359375-1.015625v-1.828125h.421875v1.78125c0 .335938.082031.589844.25.765625.164063.167969.398437.25.703125.25.332031 0 .59375-.097656.78125-.296875.195312-.195312.296875-.476562.296875-.84375v-1.65625zm0 0" /><path id="u" d="m1.4375.03125c-.25 0-.496094-.03515625-.734375-.109375-.230469-.070313-.414063-.164063-.546875-.28125l.1875-.34375c.132812.105469.300781.195313.5.265625.195312.0625.40625.09375.625.09375.289062 0 .503906-.039062.640625-.125.132813-.09375.203125-.222656.203125-.390625 0-.113281-.039062-.203125-.109375-.265625-.074219-.070312-.171875-.125-.296875-.15625-.117188-.03125-.265625-.0625-.453125-.09375-.261719-.050781-.46875-.101562-.625-.15625-.15625-.050781-.292969-.132812-.40625-.25-.105469-.125-.15625-.289062-.15625-.5 0-.257812.109375-.472656.328125-.640625.226562-.175781.535156-.265625.921875-.265625.207031 0 .410156.03125.609375.09375.207031.054688.378906.121094.515625.203125l-.1875.34375c-.261719-.175781-.574219-.265625-.9375-.265625-.273437 0-.476563.046875-.609375.140625-.136719.09375-.203125.21875-.203125.375 0 .125.035156.226563.109375.296875.082031.074219.179688.125.296875.15625.113281.03125.269531.070312.46875.109375.257813.042969.460937.089844.609375.140625.15625.042969.285156.125.390625.25.101563.117188.15625.273438.15625.46875 0 .28125-.117187.507812-.34375.671875-.230469.15625-.546875.234375-.953125.234375zm0 0" /><path id="v" d="m1.90625.03125c-.3125 0-.59375-.0664062-.84375-.203125-.242188-.132813-.433594-.320313-.578125-.5625-.136719-.25-.203125-.53125-.203125-.84375s.066406-.585937.203125-.828125c.144531-.238281.335937-.425781.578125-.5625.25-.144531.53125-.21875.84375-.21875.269531 0 .507812.058594.71875.171875.21875.105469.390625.257813.515625.453125l-.328125.21875c-.105469-.15625-.234375-.269531-.390625-.34375-.15625-.082031-.328125-.125-.515625-.125-.230469 0-.4375.054688-.625.15625-.179688.105469-.320312.25-.421875.4375-.105469.1875-.15625.402344-.15625.640625 0 .242187.050781.453125.15625.640625.101563.1875.242187.335938.421875.4375.1875.105469.394531.15625.625.15625.1875 0 .359375-.035156.515625-.109375.15625-.082031.285156-.203125.390625-.359375l.328125.21875c-.125.210938-.296875.367188-.515625.46875-.21875.1054688-.460938.15625-.71875.15625zm0 0" /><path id="w" d="m1.703125-3.1875c.40625 0 .71875.105469.9375.3125.21875.210938.328125.515625.328125.921875v1.953125h-.40625v-.484375c-.09375.15625-.234375.28125-.421875.375s-.40625.140625-.65625.140625c-.355469 0-.636719-.0820312-.84375-.25-.210937-.164062-.3125-.390625-.3125-.671875 0-.269531.09375-.484375.28125-.640625.195313-.164062.507813-.25.9375-.25h1v-.1875c0-.269531-.078125-.476562-.234375-.625-.148438-.144531-.367188-.21875-.65625-.21875-.199219 0-.390625.039062-.578125.109375-.1875.0625-.34375.152344-.46875.265625l-.203125-.3125c.164062-.132812.359375-.238281.578125-.3125.226563-.082031.46875-.125.71875-.125zm-.15625 2.875c.238281 0 .441406-.050781.609375-.15625.175781-.113281.304688-.273438.390625-.484375v-.515625h-.984375c-.542969 0-.8125.1875-.8125.5625 0 .1875.066406.335938.203125.4375.144531.105469.34375.15625.59375.15625zm0 0" /><path id="x" d="m2.25-.1875c-.074219.074219-.171875.1328125-.296875.171875-.117187.03125-.234375.046875-.359375.046875-.304688 0-.53125-.078125-.6875-.234375-.15625-.164063-.234375-.394531-.234375-.6875v-1.90625h-.5625v-.359375h.5625v-.6875h.421875v.6875h.953125v.359375h-.953125v1.890625c0 .1875.046875.328125.140625.421875s.226563.140625.40625.140625c.082031 0 .164063-.007812.25-.03125.082031-.03125.15625-.070312.21875-.125zm0 0" /><path id="y" d="m4.90625.359375c-.125.15625-.277344.269531-.453125.34375-.167969.082031-.355469.125-.5625.125-.25 0-.484375-.0625-.703125-.1875-.21875-.117187-.46875-.320313-.75-.609375-.398438-.0117188-.761719-.109375-1.09375-.296875-.324219-.1875-.578125-.4375-.765625-.75-.179687-.320313-.265625-.679687-.265625-1.078125 0-.40625.09375-.769531.28125-1.09375.1875-.320312.445312-.578125.78125-.765625.34375-.1875.722656-.28125 1.140625-.28125.414063 0 .789063.09375 1.125.28125.332031.179687.59375.433594.78125.765625.195313.324219.296875.6875.296875 1.09375 0 .34375-.074219.667969-.21875.96875-.148438.292969-.351562.539062-.609375.734375-.261719.1875-.5625.3203125-.90625.390625.3125.320312.617187.484375.921875.484375.3125 0 .578125-.132813.796875-.390625zm-4.15625-2.453125c0 .324219.078125.621094.234375.890625.15625.261719.363281.46875.625.625.269531.148437.570313.21875.90625.21875.332031 0 .632813-.070313.90625-.21875.269531-.15625.476563-.363281.625-.625.15625-.269531.234375-.566406.234375-.890625 0-.332031-.078125-.628906-.234375-.890625-.148437-.269531-.355469-.476563-.625-.625-.273437-.15625-.574219-.234375-.90625-.234375-.335937 0-.636719.078125-.90625.234375-.261719.148437-.46875.355469-.625.625-.15625.261719-.234375.558594-.234375.890625zm0 0" /><path id="z" d="m3.546875 0-.953125-1.34375c-.105469.011719-.21875.015625-.34375.015625h-1.125v1.328125h-.4375v-4.203125h1.5625c.539062 0 .960938.132813 1.265625.390625.300781.25.453125.601562.453125 1.046875 0 .335937-.085938.617187-.25.84375-.167969.21875-.402344.382813-.703125.484375l1.015625 1.4375zm-1.296875-1.703125c.414062 0 .734375-.09375.953125-.28125s.328125-.445313.328125-.78125c0-.332031-.109375-.585937-.328125-.765625-.21875-.1875-.539063-.28125-.953125-.28125h-1.125v2.109375zm0 0" /><path id="A" d="m3.46875-4.453125v4.453125h-.40625v-.625c-.125.210938-.292969.371094-.5.484375-.210938.1171875-.449219.171875-.71875.171875-.292969 0-.558594-.0664062-.796875-.203125-.242187-.132813-.429687-.320313-.5625-.5625-.136719-.25-.203125-.53125-.203125-.84375s.066406-.585937.203125-.828125c.132813-.25.320313-.441406.5625-.578125.238281-.132813.503906-.203125.796875-.203125.257812 0 .488281.058594.6875.171875.207031.105469.378906.257813.515625.453125v-1.890625zm-1.578125 4.109375c.21875 0 .414063-.050781.59375-.15625.175781-.101562.3125-.25.40625-.4375.101563-.1875.15625-.398438.15625-.640625 0-.238281-.054687-.453125-.15625-.640625-.09375-.1875-.230469-.332031-.40625-.4375-.179687-.101562-.375-.15625-.59375-.15625-.230469 0-.433594.054688-.609375.15625-.179688.105469-.320312.25-.421875.4375-.105469.1875-.15625.402344-.15625.640625 0 .242187.050781.453125.15625.640625.101563.1875.242187.335938.421875.4375.175781.105469.378906.15625.609375.15625zm0 0" /><path id="B" d="m.640625-.625c.09375 0 .164063.03125.21875.09375.0625.0625.09375.140625.09375.234375 0 .042969-.007813.09375-.015625.15625-.011719.0546875-.03125.1171875-.0625.1875l-.25.828125h-.28125l.203125-.875c-.074219-.0195312-.132813-.0546875-.171875-.109375-.03125-.050781-.046875-.113281-.046875-.1875 0-.09375.023437-.171875.078125-.234375.0625-.0625.140625-.09375.234375-.09375zm0 0" /><path id="C" d="m.59375-4.453125h.421875v4.453125h-.421875zm0 0" /><path id="D" d="m2.21875-3.1875c.289062 0 .554688.070312.796875.203125.25.136719.441406.328125.578125.578125.132812.242188.203125.515625.203125.828125s-.070313.59375-.203125.84375c-.136719.242187-.324219.429687-.5625.5625-.242188.1367188-.511719.203125-.8125.203125-.25 0-.480469-.0507812-.6875-.15625-.210938-.101562-.382812-.257812-.515625-.46875v1.75h-.421875v-4.3125h.40625v.625c.125-.207031.289062-.367188.5-.484375.21875-.113281.457031-.171875.71875-.171875zm-.03125 2.84375c.21875 0 .414062-.050781.59375-.15625.1875-.101562.328125-.25.421875-.4375.101563-.1875.15625-.398438.15625-.640625 0-.238281-.054687-.453125-.15625-.640625-.09375-.1875-.234375-.332031-.421875-.4375-.179688-.101562-.375-.15625-.59375-.15625s-.421875.054688-.609375.15625c-.179687.105469-.320313.25-.421875.4375-.09375.1875-.140625.402344-.140625.640625 0 .242187.046875.453125.140625.640625.101562.1875.242188.335938.421875.4375.1875.105469.390625.15625.609375.15625zm0 0" /><path id="E" d="m1-2.53125c.101562-.21875.253906-.378906.453125-.484375.195313-.113281.441406-.171875.734375-.171875v.421875h-.109375c-.335937 0-.59375.105469-.78125.3125-.1875.199219-.28125.480469-.28125.84375v1.609375h-.421875v-3.15625h.40625zm0 0" /><path id="F" d="m.59375-3.15625h.421875v3.15625h-.421875zm.21875-.6875c-.09375 0-.171875-.03125-.234375-.09375-.054687-.0625-.078125-.132812-.078125-.21875 0-.070312.023438-.140625.078125-.203125.0625-.0625.140625-.09375.234375-.09375.082031 0 .15625.03125.21875.09375.0625.054687.09375.121094.09375.203125 0 .085938-.03125.15625-.09375.21875s-.136719.09375-.21875.09375zm0 0" /><path id="G" d="m1.640625-4.125c-.1875 0-.328125.054688-.421875.15625-.09375.09375-.140625.242188-.140625.4375v.375h.96875v.359375h-.953125v2.796875h-.421875v-2.796875h-.5625v-.359375h.5625v-.390625c0-.289063.078125-.519531.234375-.6875.164062-.164063.40625-.25.71875-.25.113281 0 .226562.023437.34375.0625.113281.03125.207031.078125.28125.140625l-.140625.3125c-.125-.101562-.28125-.15625-.46875-.15625zm0 0" /><path id="H" d="m4.53125-3.1875c.394531 0 .703125.117188.921875.34375.226563.230469.34375.570312.34375 1.015625v1.828125h-.421875v-1.78125c0-.332031-.078125-.582031-.234375-.75-.15625-.175781-.382813-.265625-.671875-.265625-.324219 0-.585938.105469-.78125.3125-.1875.199219-.28125.476563-.28125.828125v1.65625h-.421875v-1.78125c0-.332031-.078125-.582031-.234375-.75-.15625-.175781-.382812-.265625-.671875-.265625-.324219 0-.585937.105469-.78125.3125-.1875.199219-.28125.476563-.28125.828125v1.65625h-.421875v-3.15625h.40625v.578125c.113281-.195313.269531-.347656.46875-.453125.195312-.101562.425781-.15625.6875-.15625.269531 0 .5.058594.6875.171875.195312.117187.34375.28125.4375.5.113281-.207031.28125-.367187.5-.484375.21875-.125.46875-.1875.75-.1875zm0 0" /><path id="I" d="m1.84375.03125c-.3125 0-.617188-.046875-.90625-.140625-.28125-.101563-.5-.238281-.65625-.40625l.171875-.34375c.15625.15625.359375.28125.609375.375s.507812.140625.78125.140625c.375 0 .65625-.066406.84375-.203125.1875-.132813.28125-.316406.28125-.546875 0-.164062-.054688-.296875-.15625-.390625-.105469-.101563-.230469-.179687-.375-.234375-.148438-.050781-.351562-.109375-.609375-.171875-.324219-.082031-.578125-.160156-.765625-.234375-.1875-.070312-.351562-.179688-.484375-.328125-.125-.15625-.1875-.363281-.1875-.625 0-.21875.050781-.410156.15625-.578125.113281-.175781.285156-.316406.515625-.421875.238281-.101563.53125-.15625.875-.15625.238281 0 .472656.039063.703125.109375.226563.0625.425781.152344.59375.265625l-.15625.34375c-.179687-.113281-.367187-.195313-.5625-.25-.199219-.0625-.390625-.09375-.578125-.09375-.375 0-.65625.074219-.84375.21875-.179688.136719-.265625.320313-.265625.546875 0 .167969.046875.304688.140625.40625.101562.105469.234375.183594.390625.234375.15625.054687.363281.109375.625.171875.300781.074219.546875.152344.734375.234375.195312.074219.359375.183594.484375.328125.132813.148438.203125.351562.203125.609375 0 .210937-.058594.402344-.171875.578125-.117187.179688-.292969.320312-.53125.421875-.230469.09375-.515625.140625-.859375.140625zm0 0" /><path id="J" d="m3.25-3.15625-1.40625 3.15625h-.4375l-1.40625-3.15625h.453125l1.171875 2.6875 1.203125-2.6875zm0 0" /><path id="K" d="m4.1875-4.203125v4.203125h-.359375l-2.703125-3.40625v3.40625h-.4375v-4.203125h.359375l2.703125 3.40625v-3.40625zm0 0" /><path id="L" d="m2.515625.03125c-.417969 0-.796875-.0859375-1.140625-.265625-.335938-.1875-.59375-.441406-.78125-.765625-.1875-.332031-.28125-.695312-.28125-1.09375 0-.40625.09375-.769531.28125-1.09375.1875-.332031.445312-.585938.78125-.765625.34375-.1875.722656-.28125 1.140625-.28125.3125 0 .597656.054687.859375.15625.269531.105469.492188.257813.671875.453125l-.28125.28125c-.324219-.332031-.734375-.5-1.234375-.5-.335938 0-.636719.078125-.90625.234375-.273438.148437-.484375.355469-.640625.625-.15625.261719-.234375.558594-.234375.890625 0 .324219.078125.621094.234375.890625.15625.261719.367187.46875.640625.625.269531.148437.570312.21875.90625.21875.5 0 .910156-.164063 1.234375-.5l.28125.28125c-.179687.199219-.402344.351563-.671875.453125-.273438.1054688-.558594.15625-.859375.15625zm0 0" /><path id="M" d="m.6875-4.203125h.4375v4.203125h-.4375zm0 0" /><path id="N" d="m.6875-4.203125h1.703125c.445313 0 .835937.089844 1.171875.265625.34375.179688.609375.429688.796875.75.1875.3125.28125.679688.28125 1.09375 0 .40625-.09375.773438-.28125 1.09375-.1875.3125-.453125.558594-.796875.734375-.335938.1796875-.726562.265625-1.171875.265625h-1.703125zm1.6875 3.8125c.363281 0 .679688-.070313.953125-.21875.28125-.144531.492187-.34375.640625-.59375.15625-.257813.234375-.554687.234375-.890625 0-.34375-.078125-.640625-.234375-.890625-.148438-.257813-.359375-.460937-.640625-.609375-.273437-.144531-.589844-.21875-.953125-.21875h-1.25v3.421875zm0 0" /><path id="O" d="m2.21875-3.1875c.289062 0 .554688.070312.796875.203125.25.136719.441406.328125.578125.578125.132812.242188.203125.515625.203125.828125s-.070313.59375-.203125.84375c-.136719.242187-.328125.429687-.578125.5625-.242187.1367188-.507813.203125-.796875.203125-.261719 0-.5-.0546875-.71875-.171875-.210938-.113281-.375-.273437-.5-.484375v.625h-.40625v-4.453125h.421875v1.890625c.132813-.195312.304687-.347656.515625-.453125.207031-.113281.4375-.171875.6875-.171875zm-.03125 2.84375c.21875 0 .414062-.050781.59375-.15625.1875-.101562.328125-.25.421875-.4375.101563-.1875.15625-.398438.15625-.640625 0-.238281-.054687-.453125-.15625-.640625-.09375-.1875-.234375-.332031-.421875-.4375-.179688-.101562-.375-.15625-.59375-.15625s-.421875.054688-.609375.15625c-.179687.105469-.320313.25-.421875.4375-.09375.1875-.140625.402344-.140625.640625 0 .242187.046875.453125.140625.640625.101562.1875.242188.335938.421875.4375.1875.105469.390625.15625.609375.15625zm0 0" /><path id="P" d="m3.515625-3.15625v2.765625c0 .539063-.132813.9375-.390625 1.1875-.261719.257813-.65625.390625-1.1875.390625-.292969 0-.570312-.042969-.828125-.125-.261719-.085938-.476563-.203125-.640625-.359375l.21875-.3125c.144531.132813.328125.238281.546875.3125.21875.070313.445313.109375.6875.109375.40625 0 .703125-.09375.890625-.28125.1875-.179688.28125-.4648438.28125-.859375v-.40625c-.136719.199219-.3125.351563-.53125.453125-.210938.105469-.445312.15625-.703125.15625-.292969 0-.5625-.0625-.8125-.1875-.242187-.132812-.429687-.316406-.5625-.546875-.136719-.238281-.203125-.503906-.203125-.796875 0-.289062.066406-.550781.203125-.78125.132813-.238281.320313-.421875.5625-.546875.238281-.132813.507813-.203125.8125-.203125.269531 0 .507813.058594.71875.171875.21875.105469.394531.261719.53125.46875v-.609375zm-1.609375 2.65625c.226562 0 .429688-.046875.609375-.140625.1875-.101563.332031-.238281.4375-.40625.101563-.175781.15625-.378906.15625-.609375 0-.21875-.054687-.414062-.15625-.59375-.105469-.175781-.25-.3125-.4375-.40625-.179687-.101562-.382813-.15625-.609375-.15625-.230469 0-.4375.054688-.625.15625-.179688.09375-.320312.230469-.421875.40625-.105469.179688-.15625.375-.15625.59375 0 .230469.050781.433594.15625.609375.101563.167969.242187.304687.421875.40625.1875.09375.394531.140625.625.140625zm0 0" /><path id="Q" d="m.640625.03125c-.09375 0-.171875-.03125-.234375-.09375s-.09375-.140625-.09375-.234375.03125-.171875.09375-.234375.140625-.09375.234375-.09375c.082031 0 .15625.03125.21875.09375s.09375.140625.09375.234375-.03125.171875-.09375.234375-.136719.09375-.21875.09375zm0 0" /><path id="R" d="m1.5-3.8125h-1.46875v-.390625h3.390625v.390625h-1.484375v3.8125h-.4375zm0 0" /><path id="S" d="m5.21875-3.15625-1.1875 3.15625h-.40625l-.984375-2.59375-1 2.59375h-.40625l-1.1875-3.15625h.40625l1 2.6875 1-2.6875h.375l1 2.6875 1.015625-2.6875zm0 0" /><path id="T" d="m1.765625-1.578125-.75.671875v.90625h-.421875v-4.453125h.421875v3.015625l1.890625-1.71875h.53125l-1.359375 1.296875 1.484375 1.859375h-.53125zm0 0" /><path id="U" d="m3.1875-.390625v.390625h-2.953125v-.3125l1.75-1.703125c.21875-.21875.363281-.40625.4375-.5625.082031-.15625.125-.3125.125-.46875 0-.25-.089844-.441406-.265625-.578125-.167969-.144531-.40625-.21875-.71875-.21875-.492188 0-.875.15625-1.15625.46875l-.296875-.265625c.164063-.1875.375-.332031.625-.4375.25-.101563.535156-.15625.859375-.15625.425781 0 .765625.105469 1.015625.3125.25.199219.375.46875.375.8125 0 .21875-.046875.433594-.140625.640625-.09375.199219-.277344.429688-.546875.6875l-1.421875 1.390625zm0 0" /><path id="V" d="m2.71875-2.203125c.257812.09375.460938.230469.609375.40625.144531.167969.21875.375.21875.625s-.070313.464844-.203125.640625c-.136719.179688-.328125.320312-.578125.421875-.242187.09375-.527344.140625-.859375.140625-.5 0-.898438-.1015625-1.1875-.3125-.292969-.21875-.4375-.515625-.4375-.890625 0-.25.066406-.457031.203125-.625.144531-.175781.347656-.3125.609375-.40625-.210938-.082031-.371094-.203125-.484375-.359375-.117187-.15625-.171875-.34375-.171875-.5625 0-.34375.128906-.613281.390625-.8125.269531-.195312.628906-.296875 1.078125-.296875.300781 0 .5625.046875.78125.140625.21875.085938.390625.210938.515625.375.125.167969.1875.367188.1875.59375 0 .21875-.058594.40625-.171875.5625-.117188.15625-.28125.277344-.5.359375zm-1.84375-.90625c0 .230469.085938.414063.265625.546875.1875.136719.441406.203125.765625.203125.320312 0 .578125-.066406.765625-.203125.1875-.132812.28125-.316406.28125-.546875 0-.238281-.101563-.425781-.296875-.5625-.1875-.132813-.4375-.203125-.75-.203125-.324219 0-.578125.070312-.765625.203125-.179687.136719-.265625.324219-.265625.5625zm1.03125 2.78125c.375 0 .664062-.070313.875-.21875.207031-.15625.3125-.363281.3125-.625 0-.257813-.105469-.460937-.3125-.609375-.210938-.15625-.5-.234375-.875-.234375-.367188 0-.65625.078125-.875.234375-.210938.148438-.3125.351562-.3125.609375 0 .261719.101562.46875.3125.625.207031.148437.5.21875.875.21875zm0 0" /><path id="W" d="m3.46875-3.15625v4.3125h-.421875v-1.75c-.136719.210938-.308594.367188-.515625.46875-.199219.1054688-.429688.15625-.6875.15625-.304688 0-.574219-.0664062-.8125-.203125-.230469-.132813-.414062-.320313-.546875-.5625-.136719-.25-.203125-.53125-.203125-.84375s.066406-.585937.203125-.828125c.132813-.25.320313-.441406.5625-.578125.238281-.132813.503906-.203125.796875-.203125.269531 0 .507812.058594.71875.171875.207031.117187.375.277344.5.484375v-.625zm-1.578125 2.8125c.21875 0 .414063-.050781.59375-.15625.175781-.101562.3125-.25.40625-.4375.101563-.1875.15625-.398438.15625-.640625 0-.238281-.054687-.453125-.15625-.640625-.09375-.1875-.230469-.332031-.40625-.4375-.179687-.101562-.375-.15625-.59375-.15625-.230469 0-.433594.054688-.609375.15625-.179688.105469-.320312.25-.421875.4375-.105469.1875-.15625.402344-.15625.640625 0 .242187.050781.453125.15625.640625.101563.1875.242187.335938.421875.4375.175781.105469.378906.15625.609375.15625zm0 0" /><path id="X" d="m2.515625.03125c-.417969 0-.796875-.0859375-1.140625-.265625-.335938-.1875-.59375-.441406-.78125-.765625-.1875-.332031-.28125-.695312-.28125-1.09375 0-.40625.09375-.769531.28125-1.09375.1875-.320312.445312-.578125.78125-.765625.34375-.1875.722656-.28125 1.140625-.28125.414063 0 .789063.09375 1.125.28125.332031.179687.59375.433594.78125.765625.195313.324219.296875.6875.296875 1.09375 0 .398438-.101562.761719-.296875 1.09375-.1875.324219-.449219.578125-.78125.765625-.335937.1796875-.710937.265625-1.125.265625zm0-.390625c.332031 0 .632813-.070313.90625-.21875.269531-.15625.476563-.363281.625-.625.15625-.269531.234375-.566406.234375-.890625 0-.332031-.078125-.628906-.234375-.890625-.148437-.269531-.355469-.476563-.625-.625-.273437-.15625-.574219-.234375-.90625-.234375-.335937 0-.636719.078125-.90625.234375-.261719.148437-.46875.355469-.625.625-.15625.261719-.234375.558594-.234375.890625 0 .324219.078125.621094.234375.890625.15625.261719.363281.46875.625.625.269531.148437.570313.21875.90625.21875zm0 0" /><path id="Y" d="m4.171875-4.203125-1.859375 4.203125h-.4375l-1.859375-4.203125h.484375l1.609375 3.65625 1.609375-3.65625zm0 0" /><path id="Z" d="m.359375-1.8125h1.578125v.375h-1.578125zm0 0" /><path id="aa" d="m1.46875-4.203125v4.203125h-.421875v-3.8125h-1v-.390625zm0 0" /><path id="ab" d="m1.625-4.234375c.550781 0 .972656.183594 1.265625.546875.300781.367188.453125.882812.453125 1.546875 0 .46875-.085938.867187-.25 1.1875-.15625.324219-.382812.570313-.671875.734375-.28125.1679688-.617187.25-1 .25-.417969 0-.75-.0703125-1-.21875l.171875-.359375c.207031.136719.484375.203125.828125.203125.457031 0 .816406-.144531 1.078125-.4375.269531-.289062.40625-.71875.40625-1.28125 0-.101562-.007812-.21875-.015625-.34375-.105469.230469-.273437.40625-.5.53125-.230469.125-.492187.1875-.78125.1875-.28125 0-.53125-.050781-.75-.15625-.210937-.101562-.375-.25-.5-.4375-.117187-.1875-.171875-.410156-.171875-.671875 0-.25.0625-.472656.1875-.671875.125-.195312.296875-.347656.515625-.453125.21875-.101563.460937-.15625.734375-.15625zm.046875 2.1875c.207031 0 .390625-.039063.546875-.125.164062-.082031.296875-.191406.390625-.328125.09375-.144531.140625-.300781.140625-.46875 0-.15625-.046875-.300781-.140625-.4375-.085937-.144531-.210937-.257812-.375-.34375-.167969-.082031-.367187-.125-.59375-.125-.304687 0-.554687.085938-.75.25-.1875.167969-.28125.390625-.28125.671875 0 .273437.09375.492187.28125.65625.195313.167969.457031.25.78125.25zm0 0" /><path id="ac" d="m2.25-4.203125c.539062 0 .960938.132813 1.265625.390625.300781.25.453125.601562.453125 1.046875 0 .449219-.152344.804687-.453125 1.0625-.304687.25-.726563.375-1.265625.375h-1.125v1.328125h-.4375v-4.203125zm0 2.484375c.414062 0 .734375-.085938.953125-.265625.21875-.1875.328125-.445313.328125-.78125 0-.332031-.109375-.585937-.328125-.765625-.21875-.1875-.539063-.28125-.953125-.28125h-1.125v2.09375zm0 0" /><path id="ad" d="m17.8125-10.921875-3.578125 10.921875h-2.71875l-2.40625-7.40625-2.46875 7.40625h-2.703125l-3.59375-10.921875h2.625l2.46875 7.671875 2.5625-7.671875h2.34375l2.5 7.734375 2.546875-7.734375zm0 0" /><path id="ae" d="m9.34375-4.171875c0 .03125-.015625.25-.046875.65625h-6.34375c.113281.523437.382813.933594.8125 1.234375.425781.304688.957031.453125 1.59375.453125.4375 0 .820313-.0625 1.15625-.1875.34375-.132813.660156-.34375.953125-.625l1.28125 1.40625c-.78125.90625-1.933594 1.359375-3.453125 1.359375-.949219 0-1.789063-.1796875-2.515625-.546875-.730469-.375-1.292969-.890625-1.6875-1.546875-.398438-.65625-.59375-1.398438-.59375-2.234375 0-.820313.191406-1.5625.578125-2.21875.394531-.664063.929687-1.179687 1.609375-1.546875.6875-.363281 1.453125-.546875 2.296875-.546875.820313 0 1.5625.179687 2.21875.53125.664063.34375 1.1875.851563 1.5625 1.515625.382813.65625.578125 1.421875.578125 2.296875zm-4.34375-2.515625c-.554688 0-1.015625.15625-1.390625.46875s-.605469.742188-.6875 1.28125h4.140625c-.085938-.53125-.3125-.953125-.6875-1.265625-.375-.320313-.835938-.484375-1.375-.484375zm0 0" /><path id="af" d="m2.0625-11.703125c.4375 0 .796875.140625 1.078125.421875.28125.273438.421875.632812.421875 1.078125 0 .210937-.027344.417969-.078125.625-.054687.210937-.164063.523437-.328125.9375l-.90625 2.234375h-1.546875l.671875-2.453125c-.261719-.113281-.464844-.285156-.609375-.515625-.148437-.226562-.21875-.503906-.21875-.828125 0-.445313.140625-.804687.421875-1.078125.289062-.28125.65625-.421875 1.09375-.421875zm0 0" /><path id="ag" d="m3.453125-7.296875c.289063-.394531.679687-.695313 1.171875-.90625.5-.207031 1.070312-.3125 1.71875-.3125v2.234375c-.273438-.019531-.453125-.03125-.546875-.03125-.699219 0-1.246094.199219-1.640625.59375-.398438.386719-.59375.96875-.59375 1.75v3.96875h-2.4375v-8.390625h2.328125zm0 0" /><path id="ah" d="m6.234375-8.515625c1.03125 0 1.863281.3125 2.5.9375.644531.617187.96875 1.539063.96875 2.765625v4.8125h-2.421875v-4.4375c0-.664062-.148438-1.160156-.4375-1.484375-.292969-.332031-.714844-.5-1.265625-.5-.617187 0-1.105469.195313-1.46875.578125-.367187.375-.546875.9375-.546875 1.6875v4.15625h-2.4375v-11.578125h2.4375v4.046875c.320312-.3125.710938-.550781 1.171875-.71875.457031-.175781.957031-.265625 1.5-.265625zm0 0" /><path id="ai" d="m1.125-11.578125h2.4375v11.578125h-2.4375zm0 0" /><path id="aj" d="m6.09375-8.515625c.78125 0 1.488281.179687 2.125.53125.632812.355469 1.132812.859375 1.5 1.515625.363281.648438.546875 1.402344.546875 2.265625 0 .867187-.183594 1.625-.546875 2.28125-.367188.648437-.867188 1.152344-1.5 1.515625-.636719.3554688-1.34375.53125-2.125.53125-1.074219 0-1.917969-.335938-2.53125-1.015625v3.921875h-2.4375v-11.421875h2.328125v.953125c.601563-.71875 1.484375-1.078125 2.640625-1.078125zm-.421875 6.640625c.625 0 1.132813-.207031 1.53125-.625.394531-.425781.59375-.992188.59375-1.703125 0-.707031-.199219-1.269531-.59375-1.6875-.398437-.425781-.90625-.640625-1.53125-.640625s-1.140625.214844-1.546875.640625c-.398438.417969-.59375.980469-.59375 1.6875 0 .710937.195312 1.277344.59375 1.703125.40625.417969.921875.625 1.546875.625zm0 0" /><path id="ak" d="m1.125-8.390625h2.4375v8.390625h-2.4375zm1.21875-1.171875c-.449219 0-.8125-.128906-1.09375-.390625-.28125-.257813-.421875-.582031-.421875-.96875 0-.382813.140625-.707031.421875-.96875.28125-.257813.644531-.390625 1.09375-.390625.445312 0 .8125.125 1.09375.375s.421875.5625.421875.9375c0 .40625-.140625.746094-.421875 1.015625-.28125.261719-.648438.390625-1.09375.390625zm0 0" /><path id="al" d="m6.234375-8.515625c1.03125 0 1.863281.3125 2.5.9375.644531.617187.96875 1.539063.96875 2.765625v4.8125h-2.421875v-4.4375c0-.664062-.148438-1.160156-.4375-1.484375-.292969-.332031-.714844-.5-1.265625-.5-.617187 0-1.105469.195313-1.46875.578125-.367187.375-.546875.9375-.546875 1.6875v4.15625h-2.4375v-8.390625h2.328125v.96875c.320313-.34375.722656-.609375 1.203125-.796875.476562-.195312 1.003906-.296875 1.578125-.296875zm0 0" /><path id="am" d="m9.78125-8.390625v6.984375c0 1.539062-.402344 2.6875-1.203125 3.4375-.792969.75-1.960937 1.125-3.5 1.125-.8125 0-1.585937-.101562-2.3125-.296875-.730469-.199219-1.335937-.484375-1.8125-.859375l.96875-1.75c.351563.289062.800781.519531 1.34375.6875.539063.175781 1.082031.265625 1.625.265625.84375 0 1.460937-.195313 1.859375-.578125.40625-.375.609375-.945312.609375-1.71875v-.359375c-.636719.699219-1.523437 1.046875-2.65625 1.046875-.773437 0-1.480469-.164062-2.125-.5-.636719-.34375-1.140625-.820312-1.515625-1.4375-.375-.613281-.5625-1.320312-.5625-2.125 0-.800781.1875-1.507812.5625-2.125.375-.613281.878906-1.085938 1.515625-1.421875.644531-.332031 1.351563-.5 2.125-.5 1.21875 0 2.144531.398437 2.78125 1.1875v-1.0625zm-4.59375 5.984375c.644531 0 1.171875-.1875 1.578125-.5625.414063-.382812.625-.882812.625-1.5 0-.613281-.210937-1.109375-.625-1.484375-.40625-.382813-.933594-.578125-1.578125-.578125-.648438 0-1.179688.195312-1.59375.578125-.417969.375-.625.871094-.625 1.484375 0 .617188.207031 1.117188.625 1.5.414062.375.945312.5625 1.59375.5625zm0 0" /><path id="an" d="m4.734375-3.296875-1.171875 1.15625v2.140625h-2.4375v-11.578125h2.4375v6.546875l3.5625-3.359375h2.890625l-3.484375 3.546875 3.796875 4.84375h-2.9375zm0 0" /><path id="ao" d="m5.125.125c-.886719 0-1.683594-.1796875-2.390625-.546875-.699219-.375-1.246094-.890625-1.640625-1.546875-.398438-.65625-.59375-1.398438-.59375-2.234375 0-.832031.195312-1.578125.59375-2.234375.394531-.65625.941406-1.164062 1.640625-1.53125.707031-.363281 1.503906-.546875 2.390625-.546875.882812 0 1.671875.183594 2.359375.546875.695313.367188 1.242187.875 1.640625 1.53125.394531.65625.59375 1.402344.59375 2.234375 0 .835937-.199219 1.578125-.59375 2.234375-.398438.65625-.945312 1.171875-1.640625 1.546875-.6875.3671875-1.476563.546875-2.359375.546875zm0-2c.625 0 1.132812-.207031 1.53125-.625.40625-.425781.609375-.992188.609375-1.703125 0-.707031-.203125-1.269531-.609375-1.6875-.398438-.425781-.90625-.640625-1.53125-.640625s-1.140625.214844-1.546875.640625c-.40625.417969-.609375.980469-.609375 1.6875 0 .710937.203125 1.277344.609375 1.703125.40625.417969.921875.625 1.546875.625zm0 0" /><path id="ap" d="m9.578125-8.390625v8.390625h-2.296875v-1c-.324219.367188-.710938.648438-1.15625.84375-.449219.1875-.933594.28125-1.453125.28125-1.105469 0-1.980469-.316406-2.625-.953125-.648437-.632813-.96875-1.578125-.96875-2.828125v-4.734375h2.4375v4.375c0 1.355469.566406 2.03125 1.703125 2.03125.582031 0 1.050781-.1875 1.40625-.5625.351562-.382813.53125-.953125.53125-1.703125v-4.140625zm0 0" /><path id="aq" d="m5.1875.125c-.898438 0-1.703125-.1796875-2.421875-.546875-.710937-.375-1.265625-.890625-1.671875-1.546875-.398438-.65625-.59375-1.398438-.59375-2.234375 0-.832031.195312-1.578125.59375-2.234375.40625-.65625.960938-1.164062 1.671875-1.53125.71875-.363281 1.523437-.546875 2.421875-.546875.882812 0 1.65625.183594 2.3125.546875.65625.367188 1.132812.890625 1.4375 1.578125l-1.875 1.015625c-.4375-.769531-1.070312-1.15625-1.890625-1.15625-.636719 0-1.164063.210938-1.578125.625-.417969.417969-.625.984375-.625 1.703125s.207031 1.289063.625 1.703125c.414062.417969.941406.625 1.578125.625.832031 0 1.460937-.382812 1.890625-1.15625l1.875 1.03125c-.304688.667969-.78125 1.1875-1.4375 1.5625s-1.429688.5625-2.3125.5625zm0 0" /><path id="ar" d="m11.875-8.515625c1.050781 0 1.882812.308594 2.5.921875.625.617188.9375 1.542969.9375 2.78125v4.8125h-2.4375v-4.4375c0-.664062-.140625-1.160156-.421875-1.484375-.273437-.332031-.664063-.5-1.171875-.5-.574219 0-1.027344.1875-1.359375.5625-.335937.367187-.5.914063-.5 1.640625v4.21875h-2.421875v-4.4375c0-1.320312-.53125-1.984375-1.59375-1.984375-.5625 0-1.011719.1875-1.34375.5625-.335938.367187-.5.914063-.5 1.640625v4.21875h-2.4375v-8.390625h2.328125v.953125c.3125-.34375.691406-.609375 1.140625-.796875.457031-.1875.957031-.28125 1.5-.28125.59375 0 1.125.121094 1.59375.359375.476562.230469.863281.5625 1.15625 1 .34375-.425781.773438-.757812 1.296875-1 .53125-.238281 1.109375-.359375 1.734375-.359375zm0 0" /><path id="as" d="m6.546875-.40625c-.242187.179688-.539063.3125-.890625.40625-.34375.0820312-.710938.125-1.09375.125-1 0-1.777344-.253906-2.328125-.765625-.542969-.507813-.8125-1.257813-.8125-2.25v-3.453125h-1.296875v-1.859375h1.296875v-2.046875h2.4375v2.046875h2.09375v1.859375h-2.09375v3.421875c0 .355469.085937.632813.265625.828125.1875.1875.445312.28125.78125.28125.382812 0 .710938-.101562.984375-.3125zm0 0" /><path id="at" d="m9.453125-8.390625-3.78125 8.90625c-.386719.96875-.867187 1.648437-1.4375 2.046875-.5625.394531-1.246094.59375-2.046875.59375-.4375 0-.871094-.070312-1.296875-.203125-.429687-.136719-.777344-.324219-1.046875-.5625l.890625-1.734375c.1875.164062.398437.296875.640625.390625.25.09375.492188.140625.734375.140625.332031 0 .601563-.085938.8125-.25.207031-.15625.394531-.421875.5625-.796875l.03125-.078125-3.640625-8.453125h2.515625l2.359375 5.6875 2.375-5.6875zm0 0" /><path id="au" d="m6.53125.1875c-1.117188 0-2.125-.2382812-3.03125-.71875-.898438-.488281-1.605469-1.160156-2.125-2.015625-.511719-.863281-.765625-1.835937-.765625-2.921875 0-1.082031.253906-2.050781.765625-2.90625.519531-.851562 1.226562-1.519531 2.125-2 .90625-.488281 1.921875-.734375 3.046875-.734375.9375 0 1.785156.167969 2.546875.5.769531.335937 1.414062.8125 1.9375 1.4375l-1.625 1.484375c-.742188-.84375-1.652344-1.265625-2.734375-1.265625-.679687 0-1.28125.152344-1.8125.453125-.53125.292969-.949219.703125-1.25 1.234375-.292969.523437-.4375 1.121094-.4375 1.796875 0 .679688.144531 1.28125.4375 1.8125.300781.53125.71875.949219 1.25 1.25.53125.292969 1.132813.4375 1.8125.4375 1.082031 0 1.992187-.429688 2.734375-1.296875l1.625 1.5c-.523438.636719-1.167969 1.121094-1.9375 1.453125-.773438.3320312-1.625.5-2.5625.5zm0 0" /><path id="av" d="m6.59375.1875c-1.136719 0-2.164062-.2421875-3.078125-.734375-.90625-.488281-1.621094-1.160156-2.140625-2.015625-.511719-.863281-.765625-1.832031-.765625-2.90625 0-1.070312.253906-2.035156.765625-2.890625.519531-.851563 1.234375-1.523437 2.140625-2.015625.914063-.488281 1.941406-.734375 3.078125-.734375 1.125 0 2.140625.246094 3.046875.734375.90625.492188 1.617187 1.164062 2.140625 2.015625.519531.855469.78125 1.820313.78125 2.890625 0 1.074219-.261719 2.042969-.78125 2.90625-.523438.855469-1.234375 1.527344-2.140625 2.015625-.90625.4921875-1.921875.734375-3.046875.734375zm0-2.15625c.644531 0 1.222656-.144531 1.734375-.4375.519531-.300781.925781-.71875 1.21875-1.25.300781-.53125.453125-1.132812.453125-1.8125 0-.675781-.152344-1.273438-.453125-1.796875-.292969-.53125-.699219-.941406-1.21875-1.234375-.511719-.300781-1.089844-.453125-1.734375-.453125-.648438 0-1.230469.152344-1.75.453125-.523438.292969-.933594.703125-1.234375 1.234375-.292969.523437-.4375 1.121094-.4375 1.796875 0 .679688.144531 1.28125.4375 1.8125.300781.53125.710937.949219 1.234375 1.25.519531.292969 1.101562.4375 1.75.4375zm0 0" /><path id="aw" d="m11.796875-10.921875-4.71875 10.921875h-2.5l-4.71875-10.921875h2.734375l3.328125 7.796875 3.359375-7.796875zm0 0" /><path id="ax" d="m1.296875-10.921875h2.53125v10.921875h-2.53125zm0 0" /><path id="ay" d="m1.296875-10.921875h4.96875c1.175781 0 2.21875.230469 3.125.6875.914063.449219 1.625 1.085937 2.125 1.90625.507813.8125.765625 1.765625.765625 2.859375s-.257812 2.054688-.765625 2.875c-.5.824219-1.210937 1.464844-2.125 1.921875-.90625.449219-1.949219.671875-3.125.671875h-4.96875zm4.84375 8.84375c1.082031 0 1.945313-.300781 2.59375-.90625.65625-.613281.984375-1.441406.984375-2.484375 0-1.039062-.328125-1.863281-.984375-2.46875-.648437-.601562-1.511719-.90625-2.59375-.90625h-2.3125v6.765625zm0 0" /><path id="az" d="m3.953125.125c-.699219 0-1.382813-.0859375-2.046875-.25-.667969-.175781-1.199219-.394531-1.59375-.65625l.8125-1.75c.375.242188.828125.4375 1.359375.59375.53125.148438 1.050781.21875 1.5625.21875 1.03125 0 1.546875-.253906 1.546875-.765625 0-.238281-.140625-.410156-.421875-.515625-.28125-.101562-.714844-.191406-1.296875-.265625-.6875-.101563-1.257812-.222656-1.703125-.359375-.449219-.132812-.839844-.375-1.171875-.71875-.324219-.34375-.484375-.832031-.484375-1.46875 0-.53125.148437-1 .453125-1.40625.3125-.40625.757812-.722656 1.34375-.953125.59375-.226563 1.289062-.34375 2.09375-.34375.59375 0 1.179688.070313 1.765625.203125.59375.125 1.082031.304688 1.46875.53125l-.8125 1.71875c-.742187-.414062-1.546875-.625-2.421875-.625-.523438 0-.914062.074219-1.171875.21875-.261719.148438-.390625.335938-.390625.5625 0 .261719.140625.445312.421875.546875.28125.105469.726563.203125 1.34375.296875.6875.117188 1.25.242188 1.6875.375.4375.125.8125.359375 1.125.703125.320313.34375.484375.824219.484375 1.4375 0 .523437-.15625.984375-.46875 1.390625-.304688.40625-.757812.726562-1.359375.953125-.59375.21875-1.304687.328125-2.125.328125zm0 0" /><path id="aA" d="m4.515625-8.515625c1.300781 0 2.296875.308594 2.984375.921875.695312.617188 1.046875 1.546875 1.046875 2.796875v4.796875h-2.265625v-1.046875c-.460938.78125-1.3125 1.171875-2.5625 1.171875-.648438 0-1.210938-.109375-1.6875-.328125-.46875-.21875-.828125-.519531-1.078125-.90625-.25-.382813-.375-.820313-.375-1.3125 0-.78125.289063-1.394531.875-1.84375.59375-.445313 1.503906-.671875 2.734375-.671875h1.9375c0-.53125-.164062-.9375-.484375-1.21875-.324219-.289062-.808594-.4375-1.453125-.4375-.449219 0-.890625.074219-1.328125.21875-.429687.136719-.792969.324219-1.09375.5625l-.875-1.703125c.457031-.3125 1.003906-.554687 1.640625-.734375.644531-.175781 1.304688-.265625 1.984375-.265625zm-.1875 7c.414063 0 .785156-.09375 1.109375-.28125.320312-.195313.550781-.484375.6875-.859375v-.859375h-1.671875c-1 0-1.5.328125-1.5.984375 0 .3125.117187.5625.359375.75.25.179688.585938.265625 1.015625.265625zm0 0" /><path id="aB" d="m3.796875-8.203125h2.15625v1.859375h-2.09375v6.34375h-2.4375v-6.34375h-1.296875v-1.859375h1.296875v-.375c0-.957031.28125-1.71875.84375-2.28125.570313-.5625 1.375-.84375 2.40625-.84375.363281 0 .707031.042969 1.03125.125.332031.074219.609375.183594.828125.328125l-.640625 1.765625c-.28125-.195313-.609375-.296875-.984375-.296875-.742188 0-1.109375.40625-1.109375 1.21875zm0 0" /><path id="aC" d="m6.09375-8.515625c.78125 0 1.488281.179687 2.125.53125.632812.355469 1.132812.859375 1.5 1.515625.363281.648438.546875 1.402344.546875 2.265625 0 .867187-.183594 1.625-.546875 2.28125-.367188.648437-.867188 1.152344-1.5 1.515625-.636719.3554688-1.34375.53125-2.125.53125-1.15625 0-2.039062-.363281-2.640625-1.09375v.96875h-2.328125v-11.578125h2.4375v4.0625c.613281-.664063 1.457031-1 2.53125-1zm-.421875 6.640625c.625 0 1.132813-.207031 1.53125-.625.394531-.425781.59375-.992188.59375-1.703125 0-.707031-.199219-1.269531-.59375-1.6875-.398437-.425781-.90625-.640625-1.53125-.640625s-1.140625.214844-1.546875.640625c-.398438.417969-.59375.980469-.59375 1.6875 0 .710937.195312 1.277344.59375 1.703125.40625.417969.921875.625 1.546875.625zm0 0" /><path id="aD" d="m9.671875-11.578125v11.578125h-2.3125v-.96875c-.605469.730469-1.480469 1.09375-2.625 1.09375-.792969 0-1.511719-.1757812-2.15625-.53125-.636719-.351562-1.136719-.859375-1.5-1.515625-.367187-.65625-.546875-1.414063-.546875-2.28125 0-.863281.179688-1.625.546875-2.28125.363281-.65625.863281-1.15625 1.5-1.5.644531-.351563 1.363281-.53125 2.15625-.53125 1.070313 0 1.910156.335937 2.515625 1v-4.0625zm-4.515625 9.703125c.613281 0 1.125-.207031 1.53125-.625.40625-.425781.609375-.992188.609375-1.703125 0-.707031-.203125-1.269531-.609375-1.6875-.40625-.425781-.917969-.640625-1.53125-.640625-.625 0-1.140625.214844-1.546875.640625-.40625.417969-.609375.980469-.609375 1.6875 0 .710937.203125 1.277344.609375 1.703125.40625.417969.921875.625 1.546875.625zm0 0" /><path id="aE" d="m2.046875.125c-.429687 0-.789063-.1445312-1.078125-.4375-.292969-.289062-.4375-.65625-.4375-1.09375 0-.445312.144531-.804688.4375-1.078125.289062-.28125.648438-.421875 1.078125-.421875.425781 0 .785156.140625 1.078125.421875.289062.273437.4375.632813.4375 1.078125 0 .4375-.148438.804688-.4375 1.09375-.292969.2929688-.652344.4375-1.078125.4375zm0 0" /><path id="aF" d="m2.046875-5.484375c-.429687 0-.789063-.144531-1.078125-.4375-.292969-.289063-.4375-.65625-.4375-1.09375 0-.445313.144531-.804687.4375-1.078125.289062-.28125.648438-.421875 1.078125-.421875.425781 0 .785156.140625 1.078125.421875.289062.273438.4375.632812.4375 1.078125 0 .4375-.148438.804687-.4375 1.09375-.292969.292969-.652344.4375-1.078125.4375zm0 5.609375c-.429687 0-.789063-.1445312-1.078125-.4375-.292969-.289062-.4375-.65625-.4375-1.09375 0-.445312.144531-.804688.4375-1.078125.289062-.28125.648438-.421875 1.078125-.421875.425781 0 .785156.140625 1.078125.421875.289062.273437.4375.632813.4375 1.078125 0 .4375-.148438.804688-.4375 1.09375-.292969.2929688-.652344.4375-1.078125.4375zm0 0" /><path id="aG" d="m10.8125-19.609375c1.738281 0 3.242188.292969 4.515625.875 1.28125.574219 2.265625 1.390625 2.953125 2.453125.695312 1.0625 1.046875 2.324219 1.046875 3.78125 0 1.4375-.351563 2.699219-1.046875 3.78125-.6875 1.074219-1.671875 1.898438-2.953125 2.46875-1.273437.5625-2.777344.84375-4.515625.84375h-3.953125v5.40625h-4.53125v-19.609375zm-.25 10.5c1.363281 0 2.398438-.289063 3.109375-.875.707031-.59375 1.0625-1.429687 1.0625-2.515625 0-1.09375-.355469-1.929688-1.0625-2.515625-.710937-.59375-1.746094-.890625-3.109375-.890625h-3.703125v6.796875zm0 0" /><path id="aH" d="m2.015625-20.78125h4.375v20.78125h-4.375zm0 0" /><path id="aI" d="m16.78125-7.484375c0 .0625-.027344.460937-.078125 1.1875h-11.40625c.207031.929687.691406 1.664063 1.453125 2.203125.769531.542969 1.722656.8125 2.859375.8125.78125 0 1.472656-.113281 2.078125-.34375.613281-.238281 1.179688-.609375 1.703125-1.109375l2.328125 2.515625c-1.417969 1.625-3.492188 2.4375-6.21875 2.4375-1.699219 0-3.203125-.328125-4.515625-.984375-1.304687-.664063-2.3125-1.585937-3.03125-2.765625-.710937-1.175781-1.0625-2.507812-1.0625-4 0-1.476562.347656-2.8125 1.046875-4 .707031-1.1875 1.671875-2.109375 2.890625-2.765625 1.226563-.664063 2.597656-1 4.109375-1 1.476562 0 2.8125.320313 4 .953125 1.195312.636719 2.132812 1.546875 2.8125 2.734375.6875 1.1875 1.03125 2.5625 1.03125 4.125zm-7.8125-4.5c-.992188 0-1.824219.28125-2.5.84375-.667969.554687-1.078125 1.3125-1.234375 2.28125h7.421875c-.148438-.945313-.558594-1.703125-1.234375-2.265625-.667969-.570312-1.484375-.859375-2.453125-.859375zm0 0" /><path id="aJ" d="m8.09375-15.296875c2.332031 0 4.125.558594 5.375 1.671875 1.257812 1.105469 1.890625 2.78125 1.890625 5.03125v8.59375h-4.09375v-1.875c-.824219 1.398438-2.355469 2.09375-4.59375 2.09375-1.15625 0-2.164063-.1953125-3.015625-.578125-.855469-.394531-1.507812-.9375-1.953125-1.625-.449219-.695313-.671875-1.484375-.671875-2.359375 0-1.394531.523438-2.492188 1.578125-3.296875 1.0625-.8125 2.695313-1.21875 4.90625-1.21875h3.46875c0-.945313-.292969-1.675781-.875-2.1875-.574219-.519531-1.4375-.78125-2.59375-.78125-.8125 0-1.605469.132813-2.375.390625-.773437.25-1.429687.589844-1.96875 1.015625l-1.578125-3.046875c.820312-.582031 1.804688-1.03125 2.953125-1.34375 1.15625-.320312 2.335937-.484375 3.546875-.484375zm-.328125 12.578125c.738281 0 1.398437-.171875 1.984375-.515625.582031-.34375.992188-.851563 1.234375-1.53125v-1.53125h-3c-1.792969 0-2.6875.589844-2.6875 1.765625 0 .554688.21875.996094.65625 1.328125.4375.324219 1.039063.484375 1.8125.484375zm0 0" /><path id="aK" d="m7.09375.21875c-1.261719 0-2.492188-.1523438-3.6875-.453125-1.1875-.3125-2.136719-.703125-2.84375-1.171875l1.453125-3.125c.675781.429688 1.488281.777344 2.4375 1.046875.957031.273437 1.890625.40625 2.796875.40625 1.851562 0 2.78125-.457031 2.78125-1.375 0-.425781-.257812-.734375-.765625-.921875-.5-.1875-1.273437-.347656-2.3125-.484375-1.242187-.1875-2.261719-.398437-3.0625-.640625-.804687-.238281-1.5-.664062-2.09375-1.28125-.585937-.625-.875-1.503906-.875-2.640625 0-.945313.273437-1.789063.828125-2.53125.550781-.738281 1.351562-1.3125 2.40625-1.71875 1.0625-.414063 2.3125-.625 3.75-.625 1.0625 0 2.117188.121094 3.171875.359375 1.050781.230469 1.925781.546875 2.625.953125l-1.453125 3.109375c-1.335938-.738281-2.78125-1.109375-4.34375-1.109375-.9375 0-1.640625.132813-2.109375.390625-.46875.261719-.703125.59375-.703125 1 0 .46875.25.796875.75.984375.507812.1875 1.316406.367187 2.421875.53125 1.226563.210937 2.234375.433594 3.015625.671875.789062.230469 1.472656.652344 2.046875 1.265625.582031.617187.875 1.476563.875 2.578125 0 .929688-.28125 1.757812-.84375 2.484375-.5625.730469-1.382813 1.296875-2.453125 1.703125-1.074219.3945312-2.34375.59375-3.8125.59375zm0 0" /><path id="aL" d="m9.296875.21875c-1.605469 0-3.046875-.328125-4.328125-.984375-1.28125-.664063-2.28125-1.585937-3-2.765625-.71875-1.175781-1.078125-2.507812-1.078125-4 0-1.5.359375-2.835938 1.078125-4.015625.71875-1.175781 1.71875-2.09375 3-2.75 1.28125-.664063 2.722656-1 4.328125-1 1.59375 0 2.976563.335937 4.15625 1 1.1875.65625 2.050781 1.605469 2.59375 2.84375l-3.390625 1.8125c-.78125-1.375-1.914062-2.0625-3.390625-2.0625-1.136719 0-2.078125.375-2.828125 1.125-.742188.742187-1.109375 1.757813-1.109375 3.046875 0 1.28125.367187 2.296875 1.109375 3.046875.75.75 1.691406 1.125 2.828125 1.125 1.5 0 2.628906-.691406 3.390625-2.078125l3.390625 1.84375c-.542969 1.199219-1.40625 2.136719-2.59375 2.8125-1.179687.667969-2.5625 1-4.15625 1zm0 0" /><path id="aM" d="m11.171875-15.296875c1.875 0 3.382813.5625 4.53125 1.6875 1.144531 1.117187 1.71875 2.777344 1.71875 4.984375v8.625h-4.359375v-7.953125c0-1.195313-.265625-2.085937-.796875-2.671875-.523437-.59375-1.277344-.890625-2.265625-.890625-1.105469 0-1.984375.34375-2.640625 1.03125-.648437.679687-.96875 1.6875-.96875 3.03125v7.453125h-4.375v-20.78125h4.375v7.28125c.570313-.582031 1.269531-1.023438 2.09375-1.328125.820313-.3125 1.71875-.46875 2.6875-.46875zm0 0" /><path id="aN" d="m8.484375-5.90625-2.09375 2.0625v3.84375h-4.375v-20.78125h4.375v11.765625l6.390625-6.0625h5.203125l-6.28125 6.390625 6.84375 8.6875h-5.296875zm0 0" /><path id="aO" d="m2.015625-15.078125h4.375v15.078125h-4.375zm2.1875-2.09375c-.804687 0-1.460937-.234375-1.96875-.703125-.5-.46875-.75-1.046875-.75-1.734375s.25-1.265625.75-1.734375c.507813-.46875 1.164063-.703125 1.96875-.703125.800781 0 1.453125.226563 1.953125.671875.507812.449219.765625 1.011719.765625 1.6875 0 .71875-.257813 1.320312-.765625 1.796875-.5.480469-1.152344.71875-1.953125.71875zm0 0" /><path id="aP" d="m11.171875-15.296875c1.875 0 3.382813.5625 4.53125 1.6875 1.144531 1.117187 1.71875 2.777344 1.71875 4.984375v8.625h-4.359375v-7.953125c0-1.195313-.265625-2.085937-.796875-2.671875-.523437-.59375-1.277344-.890625-2.265625-.890625-1.105469 0-1.984375.34375-2.640625 1.03125-.648437.679687-.96875 1.6875-.96875 3.03125v7.453125h-4.375v-15.078125h4.171875v1.765625c.582031-.632812 1.300781-1.125 2.15625-1.46875.863281-.34375 1.804688-.515625 2.828125-.515625zm0 0" /><path id="aQ" d="m10.921875-15.296875c1.40625 0 2.679687.324219 3.828125.96875 1.144531.648437 2.046875 1.554687 2.703125 2.71875.65625 1.167969.984375 2.527344.984375 4.078125 0 1.554688-.328125 2.914062-.984375 4.078125-.65625 1.167969-1.558594 2.074219-2.703125 2.71875-1.148438.6367188-2.421875.953125-3.828125.953125-2.074219 0-3.652344-.648438-4.734375-1.953125v1.734375h-4.171875v-20.78125h4.375v7.3125c1.101563-1.21875 2.613281-1.828125 4.53125-1.828125zm-.75 11.9375c1.113281 0 2.03125-.375 2.75-1.125.71875-.757813 1.078125-1.773437 1.078125-3.046875 0-1.269531-.359375-2.28125-1.078125-3.03125-.71875-.757812-1.636719-1.140625-2.75-1.140625-1.125 0-2.046875.382813-2.765625 1.140625-.71875.75-1.078125 1.761719-1.078125 3.03125 0 1.273438.359375 2.289062 1.078125 3.046875.71875.75 1.640625 1.125 2.765625 1.125zm0 0" /><path id="aR" d="m6.8125-14.734375h3.859375v3.359375h-3.75v11.375h-4.375v-11.375h-2.328125v-3.359375h2.328125v-.671875c0-1.71875.507813-3.082031 1.53125-4.09375 1.019531-1.007812 2.453125-1.515625 4.296875-1.515625.65625 0 1.273438.074219 1.859375.21875.59375.136719 1.082031.335937 1.46875.59375l-1.140625 3.171875c-.5-.351562-1.089844-.53125-1.765625-.53125-1.324219 0-1.984375.730469-1.984375 2.1875zm0 0" /><path id="aS" d="m9.1875.21875c-1.585938 0-3.007812-.328125-4.265625-.984375-1.261719-.664063-2.25-1.585937-2.96875-2.765625-.710937-1.175781-1.0625-2.507812-1.0625-4 0-1.5.351563-2.835938 1.0625-4.015625.71875-1.175781 1.707031-2.09375 2.96875-2.75 1.257813-.664063 2.679687-1 4.265625-1 1.59375 0 3.015625.335937 4.265625 1 1.25.65625 2.226563 1.574219 2.9375 2.75.707031 1.179687 1.0625 2.515625 1.0625 4.015625 0 1.492188-.355469 2.824219-1.0625 4-.710937 1.179688-1.6875 2.101562-2.9375 2.765625-1.25.65625-2.671875.984375-4.265625.984375zm0-3.578125c1.125 0 2.046875-.375 2.765625-1.125.71875-.757813 1.078125-1.773437 1.078125-3.046875 0-1.269531-.359375-2.28125-1.078125-3.03125-.71875-.757812-1.640625-1.140625-2.765625-1.140625-1.117188 0-2.039062.382813-2.765625 1.140625-.730469.75-1.09375 1.761719-1.09375 3.03125 0 1.273438.363281 2.289062 1.09375 3.046875.726563.75 1.648437 1.125 2.765625 1.125zm0 0" /><path id="aT" d="m6.1875-13.078125c.519531-.726563 1.222656-1.28125 2.109375-1.65625.894531-.375 1.921875-.5625 3.078125-.5625v4.03125c-.492188-.03125-.820312-.046875-.984375-.046875-1.25 0-2.230469.351562-2.9375 1.046875-.710937.699219-1.0625 1.75-1.0625 3.15625v7.109375h-4.375v-15.078125h4.171875zm0 0" /><path id="aU" d="m11.734375-.734375c-.429687.324219-.953125.5625-1.578125.71875s-1.28125.234375-1.96875.234375c-1.804688 0-3.195312-.453125-4.171875-1.359375-.980469-.914063-1.46875-2.265625-1.46875-4.046875v-6.1875h-2.328125v-3.359375h2.328125v-3.671875h4.375v3.671875h3.75v3.359375h-3.75v6.140625c0 .636719.160156 1.125.484375 1.46875.332031.34375.796875.515625 1.390625.515625.6875 0 1.273437-.1875 1.765625-.5625zm0 0" /><path id="aV" d="m17.5625-15.078125v12.5625c0 2.757813-.71875 4.8125-2.15625 6.15625s-3.539062 2.015625-6.296875 2.015625c-1.460937 0-2.84375-.179688-4.15625-.53125-1.304687-.355469-2.386719-.867188-3.25-1.53125l1.75-3.140625c.632813.519531 1.4375.929687 2.40625 1.234375.96875.3125 1.9375.46875 2.90625.46875 1.507813 0 2.625-.34375 3.34375-1.03125.726563-.679688 1.09375-1.707031 1.09375-3.09375v-.640625c-1.148437 1.25-2.734375 1.875-4.765625 1.875-1.386719 0-2.65625-.300781-3.8125-.90625-1.148438-.601563-2.058594-1.457031-2.734375-2.5625-.667969-1.101563-1-2.375-1-3.8125s.332031-2.703125 1-3.796875c.675781-1.101562 1.585937-1.957031 2.734375-2.5625 1.15625-.613281 2.425781-.921875 3.8125-.921875 2.175781 0 3.835938.71875 4.984375 2.15625v-1.9375zm-8.265625 10.765625c1.15625 0 2.101563-.335938 2.84375-1.015625.738281-.6875 1.109375-1.582031 1.109375-2.6875 0-1.101563-.371094-1.992187-1.109375-2.671875-.742187-.675781-1.6875-1.015625-2.84375-1.015625s-2.109375.339844-2.859375 1.015625c-.742188.679688-1.109375 1.570312-1.109375 2.671875 0 1.105469.367187 2 1.109375 2.6875.75.679687 1.703125 1.015625 2.859375 1.015625zm0 0" /><path id="aW" d="m17.203125-15.078125v15.078125h-4.140625v-1.796875c-.585938.65625-1.277344 1.15625-2.078125 1.5-.804687.34375-1.671875.515625-2.609375.515625-1.980469 0-3.546875-.566406-4.703125-1.703125-1.15625-1.132813-1.734375-2.828125-1.734375-5.078125v-8.515625h4.359375v7.875c0 2.429687 1.019531 3.640625 3.0625 3.640625 1.039063 0 1.878906-.335938 2.515625-1.015625.632812-.6875.953125-1.703125.953125-3.046875v-7.453125zm0 0" /><path id="aX" d="m10.921875-15.296875c1.40625 0 2.679687.324219 3.828125.96875 1.144531.648437 2.046875 1.554687 2.703125 2.71875.65625 1.167969.984375 2.527344.984375 4.078125 0 1.554688-.328125 2.914062-.984375 4.078125-.65625 1.167969-1.558594 2.074219-2.703125 2.71875-1.148438.6367188-2.421875.953125-3.828125.953125-1.917969 0-3.429687-.601562-4.53125-1.8125v7.03125h-4.375v-20.515625h4.171875v1.75c1.082031-1.3125 2.660156-1.96875 4.734375-1.96875zm-.75 11.9375c1.113281 0 2.03125-.375 2.75-1.125.71875-.757813 1.078125-1.773437 1.078125-3.046875 0-1.269531-.359375-2.28125-1.078125-3.03125-.71875-.757812-1.636719-1.140625-2.75-1.140625-1.125 0-2.046875.382813-2.765625 1.140625-.71875.75-1.078125 1.761719-1.078125 3.03125 0 1.273438.359375 2.289062 1.078125 3.046875.71875.75 1.640625 1.125 2.765625 1.125zm0 0" /><path id="aY" d="m21.3125-15.296875c1.894531 0 3.394531.558594 4.5 1.671875 1.113281 1.105469 1.671875 2.773438 1.671875 5v8.625h-4.375v-7.953125c0-1.195313-.25-2.085937-.75-2.671875-.492187-.59375-1.195313-.890625-2.109375-.890625-1.023438 0-1.835938.335937-2.4375 1-.59375.65625-.890625 1.640625-.890625 2.953125v7.5625h-4.375v-7.953125c0-2.375-.953125-3.5625-2.859375-3.5625-1 0-1.804688.335937-2.40625 1-.59375.65625-.890625 1.640625-.890625 2.953125v7.5625h-4.375v-15.078125h4.171875v1.75c.5625-.632813 1.25-1.117187 2.0625-1.453125.8125-.34375 1.703125-.515625 2.671875-.515625 1.0625 0 2.019531.214844 2.875.640625.863281.417969 1.5625 1.027344 2.09375 1.828125.613281-.78125 1.390625-1.382813 2.328125-1.8125.945312-.4375 1.976562-.65625 3.09375-.65625zm0 0" /><path id="aZ" d="m3.671875.21875c-.773437 0-1.417969-.2578125-1.9375-.78125-.523437-.519531-.78125-1.171875-.78125-1.953125 0-.800781.257813-1.445313.78125-1.9375.519531-.5 1.164063-.75 1.9375-.75.757813 0 1.398437.25 1.921875.75.53125.492187.796875 1.136719.796875 1.9375 0 .78125-.265625 1.433594-.796875 1.953125-.523438.5234375-1.164062.78125-1.921875.78125zm0 0" /><path id="a0" d="m4.046875.078125c-.679687 0-1.335937-.109375-1.96875-.328125-.625-.21875-1.109375-.507812-1.453125-.875l.375-.75c.332031.335938.773438.605469 1.328125.8125.5625.210938 1.132813.3125 1.71875.3125.832031 0 1.453125-.148438 1.859375-.453125.414062-.300781.625-.695313.625-1.1875 0-.363281-.117188-.65625-.34375-.875-.21875-.226563-.492188-.398437-.8125-.515625-.324219-.125-.777344-.253906-1.359375-.390625-.699219-.175781-1.257813-.34375-1.671875-.5-.40625-.164063-.761719-.414063-1.0625-.75-.292969-.332031-.4375-.785156-.4375-1.359375 0-.46875.117188-.894531.359375-1.28125.25-.382812.628906-.691406 1.140625-.921875.507812-.226563 1.144531-.34375 1.90625-.34375.53125 0 1.046875.074219 1.546875.21875.507813.148437.945313.351563 1.3125.609375l-.328125.78125c-.386719-.257812-.796875-.453125-1.234375-.578125s-.871094-.1875-1.296875-.1875c-.8125 0-1.421875.15625-1.828125.46875s-.609375.714844-.609375 1.203125c0 .367188.109375.664062.328125.890625.226563.230469.507813.402344.84375.515625.34375.117188.800781.246094 1.375.390625.675781.167969 1.222656.335937 1.640625.5.414062.15625.769531.402344 1.0625.734375.289062.335938.4375.78125.4375 1.34375 0 .460938-.125.882812-.375 1.265625-.25.386719-.636719.695313-1.15625.921875-.523438.21875-1.164062.328125-1.921875.328125zm0 0" /><path id="a1" d="m4.1875.0625c-.6875 0-1.304688-.1445312-1.84375-.4375-.542969-.300781-.96875-.722656-1.28125-1.265625-.304688-.539063-.453125-1.148437-.453125-1.828125 0-.675781.148437-1.28125.453125-1.8125.3125-.539062.738281-.960938 1.28125-1.265625.539062-.300781 1.15625-.453125 1.84375-.453125.59375 0 1.125.121094 1.59375.359375.476562.230469.851562.570313 1.125 1.015625l-.703125.46875c-.230469-.34375-.523437-.597656-.875-.765625-.34375-.164063-.726563-.25-1.140625-.25-.5 0-.953125.117187-1.359375.34375-.398437.21875-.710937.539063-.9375.953125-.21875.40625-.328125.875-.328125 1.40625s.109375 1.007812.328125 1.421875c.226563.40625.539063.726563.9375.953125.40625.21875.859375.328125 1.359375.328125.414062 0 .796875-.082031 1.140625-.25.351563-.164063.644531-.421875.875-.765625l.703125.46875c-.273438.449219-.648438.792969-1.125 1.03125-.480469.2304688-1.011719.34375-1.59375.34375zm0 0" /><path id="a2" d="m3.734375-7c.90625 0 1.597656.230469 2.078125.6875.488281.449219.734375 1.121094.734375 2.015625v4.296875h-.890625v-1.078125c-.21875.355469-.53125.636719-.9375.84375-.40625.1992188-.890625.296875-1.453125.296875-.78125 0-1.402344-.179688-1.859375-.546875-.460938-.375-.6875-.863281-.6875-1.46875 0-.59375.210938-1.066406.640625-1.421875.425781-.363281 1.109375-.546875 2.046875-.546875h2.203125v-.421875c0-.59375-.167969-1.046875-.5-1.359375-.335937-.3125-.824219-.46875-1.46875-.46875-.4375 0-.859375.074219-1.265625.21875-.40625.136719-.757812.335937-1.046875.59375l-.421875-.703125c.351562-.300781.773438-.53125 1.265625-.6875.5-.164062 1.019531-.25 1.5625-.25zm-.328125 6.328125c.53125 0 .984375-.117187 1.359375-.359375.375-.25.65625-.601562.84375-1.0625v-1.125h-2.171875c-1.1875 0-1.78125.414062-1.78125 1.234375 0 .40625.148438.730469.453125.96875.3125.230469.742187.34375 1.296875.34375zm0 0" /><path id="a3" d="m4.84375-7c.875 0 1.566406.257812 2.078125.765625.519531.5.78125 1.234375.78125 2.203125v4.03125h-.9375v-3.9375c0-.71875-.183594-1.265625-.546875-1.640625-.367188-.382813-.882812-.578125-1.546875-.578125-.75 0-1.34375.226562-1.78125.671875-.429687.449219-.640625 1.0625-.640625 1.84375v3.640625h-.9375v-6.9375h.890625v1.265625c.257813-.414063.613281-.738281 1.0625-.96875.457031-.238281.984375-.359375 1.578125-.359375zm0 0" /><path id="a4" d="m4.96875-.421875c-.179688.15625-.398438.277344-.65625.359375-.261719.0820312-.53125.125-.8125.125-.65625 0-1.164062-.171875-1.515625-.515625-.34375-.351563-.515625-.851563-.515625-1.5v-4.203125h-1.25v-.78125h1.25v-1.53125h.9375v1.53125h2.109375v.78125h-2.109375v4.15625c0 .40625.101562.71875.3125.9375.207031.21875.503906.328125.890625.328125.1875 0 .367187-.03125.546875-.09375.1875-.0625.347656-.148437.484375-.265625zm0 0" /><path id="a5" d="m4.84375-7c.875 0 1.566406.257812 2.078125.765625.519531.5.78125 1.234375.78125 2.203125v4.03125h-.9375v-3.9375c0-.71875-.183594-1.265625-.546875-1.640625-.367188-.382813-.882812-.578125-1.546875-.578125-.75 0-1.34375.226562-1.78125.671875-.429687.449219-.640625 1.0625-.640625 1.84375v3.640625h-.9375v-9.796875h.9375v4.0625c.25-.40625.597656-.71875 1.046875-.9375.445313-.21875.960937-.328125 1.546875-.328125zm0 0" /><path id="a6" d="m7.359375-3.1875h-5.8125c.050781.730469.328125 1.320312.828125 1.765625.5.4375 1.132812.65625 1.90625.65625.425781 0 .816406-.078125 1.171875-.234375.363281-.15625.679687-.378906.953125-.671875l.53125.609375c-.3125.367188-.703125.648438-1.171875.84375-.460937.1875-.964844.28125-1.515625.28125-.710938 0-1.339844-.1484375-1.890625-.453125-.554687-.300781-.984375-.71875-1.296875-1.25-.304688-.539063-.453125-1.148437-.453125-1.828125 0-.675781.144531-1.28125.4375-1.8125.289063-.539062.691406-.960938 1.203125-1.265625.519531-.300781 1.101562-.453125 1.75-.453125.644531 0 1.222656.152344 1.734375.453125.507813.304687.910156.71875 1.203125 1.25.289062.53125.4375 1.140625.4375 1.828125zm-3.359375-3c-.667969 0-1.230469.214844-1.6875.640625-.449219.429687-.703125.984375-.765625 1.671875h4.921875c-.0625-.6875-.324219-1.242188-.78125-1.671875-.449219-.425781-1.011719-.640625-1.6875-.640625zm0 0" /><path id="a7" d="m10.8125.78125c-.28125.34375-.617188.601562-1 .78125-.375.175781-.792969.265625-1.25.265625-.542969 0-1.058594-.136719-1.546875-.40625-.492187-.261719-1.039063-.710937-1.640625-1.34375-.886719-.0234375-1.6875-.238281-2.40625-.65625-.710938-.414063-1.265625-.972656-1.671875-1.671875-.40625-.707031-.609375-1.5-.609375-2.375 0-.882812.207031-1.679688.625-2.390625.414062-.71875.992188-1.28125 1.734375-1.6875.75-.414063 1.582031-.625 2.5-.625.914063 0 1.738281.203125 2.46875.609375.738281.40625 1.316406.96875 1.734375 1.6875.425781.71875.640625 1.523438.640625 2.40625 0 .78125-.164063 1.496094-.484375 2.140625-.324219.648437-.773438 1.1875-1.34375 1.625-.574219.429687-1.234375.710937-1.984375.84375.675781.71875 1.34375 1.078125 2 1.078125.6875 0 1.273437-.28125 1.765625-.84375zm-9.15625-5.40625c0 .730469.164062 1.386719.5 1.96875.34375.574219.8125 1.027344 1.40625 1.359375.59375.335937 1.253906.5 1.984375.5.726563 0 1.382813-.164063 1.96875-.5.59375-.332031 1.054687-.785156 1.390625-1.359375.332031-.582031.5-1.238281.5-1.96875 0-.71875-.167969-1.367188-.5-1.953125-.335938-.582031-.796875-1.039063-1.390625-1.375-.585937-.332031-1.242187-.5-1.96875-.5-.730469 0-1.390625.167969-1.984375.5-.59375.335937-1.0625.792969-1.40625 1.375-.335938.585937-.5 1.234375-.5 1.953125zm0 0" /><path id="a8" d="m7.8125 0-2.109375-2.96875c-.242187.023438-.484375.03125-.734375.03125h-2.484375v2.9375h-.984375v-9.25h3.46875c1.175781 0 2.097656.289062 2.765625.859375.664063.5625 1 1.335937 1 2.3125 0 .730469-.183594 1.34375-.546875 1.84375-.367188.492187-.886719.84375-1.5625 1.0625l2.265625 3.171875zm-2.875-3.765625c.914062 0 1.613281-.203125 2.09375-.609375.488281-.40625.734375-.972656.734375-1.703125 0-.75-.246094-1.320313-.734375-1.71875-.480469-.394531-1.179688-.59375-2.09375-.59375h-2.453125v4.625zm0 0" /><path id="a9" d="m4.140625.0625c-.667969 0-1.273437-.1484375-1.8125-.453125-.53125-.300781-.953125-.71875-1.265625-1.25-.304688-.539063-.453125-1.148437-.453125-1.828125 0-.675781.148437-1.28125.453125-1.8125.3125-.539062.734375-.960938 1.265625-1.265625.539063-.300781 1.144531-.453125 1.8125-.453125.675781 0 1.28125.152344 1.8125.453125.539063.304687.960937.726563 1.265625 1.265625.300781.53125.453125 1.136719.453125 1.8125 0 .679688-.152344 1.289062-.453125 1.828125-.304688.53125-.726562.949219-1.265625 1.25-.53125.3046875-1.136719.453125-1.8125.453125zm0-.828125c.5 0 .941406-.109375 1.328125-.328125.394531-.226562.703125-.550781.921875-.96875.21875-.414062.328125-.882812.328125-1.40625 0-.53125-.109375-1-.328125-1.40625-.21875-.414062-.527344-.734375-.921875-.953125-.386719-.226563-.828125-.34375-1.328125-.34375-.492187 0-.933594.117187-1.328125.34375-.386719.21875-.695312.539063-.921875.953125-.21875.40625-.328125.875-.328125 1.40625 0 .523438.109375.992188.328125 1.40625.226563.417969.535156.742188.921875.96875.394531.21875.835938.328125 1.328125.328125zm0 0" /><path id="ba" d="m7.640625-9.796875v9.796875h-.890625v-1.375c-.28125.46875-.65625.828125-1.125 1.078125-.460938.2421875-.980469.359375-1.5625.359375-.648438 0-1.234375-.1445312-1.765625-.4375-.53125-.300781-.949219-.71875-1.25-1.25-.292969-.539062-.4375-1.15625-.4375-1.84375s.144531-1.296875.4375-1.828125c.300781-.539063.71875-.957031 1.25-1.25.53125-.300781 1.117187-.453125 1.765625-.453125.5625 0 1.066406.121094 1.515625.359375.457031.230469.832031.570313 1.125 1.015625v-4.171875zm-3.5 9.03125c.488281 0 .925781-.109375 1.3125-.328125.394531-.226562.703125-.550781.921875-.96875.226562-.414062.34375-.882812.34375-1.40625 0-.53125-.117188-1-.34375-1.40625-.21875-.414062-.527344-.734375-.921875-.953125-.386719-.226563-.824219-.34375-1.3125-.34375-.492187 0-.933594.117187-1.328125.34375-.386719.21875-.695312.539063-.921875.953125-.21875.40625-.328125.875-.328125 1.40625 0 .523438.109375.992188.328125 1.40625.226563.417969.535156.742188.921875.96875.394531.21875.835938.328125 1.328125.328125zm0 0" /><path id="bb" d="m11.5-6.9375-2.625 6.9375h-.890625l-2.1875-5.6875-2.1875 5.6875h-.890625l-2.609375-6.9375h.890625l2.1875 5.890625 2.21875-5.890625h.8125l2.21875 5.890625 2.203125-5.890625zm0 0" /><path id="bc" d="m1.3125-6.9375h.9375v6.9375h-.9375zm.46875-1.53125c-.1875 0-.351562-.0625-.484375-.1875-.125-.132812-.1875-.296875-.1875-.484375 0-.175781.0625-.328125.1875-.453125.132813-.132812.296875-.203125.484375-.203125.195312 0 .359375.070313.484375.203125.132813.125.203125.273438.203125.4375 0 .199219-.070312.367188-.203125.5-.125.125-.289063.1875-.484375.1875zm0 0" /><path id="bd" d="m7.140625-6.9375-3.46875 7.765625c-.28125.65625-.609375 1.117187-.984375 1.390625-.367188.269531-.808594.40625-1.328125.40625-.335937 0-.648437-.054688-.9375-.15625-.292969-.105469-.539063-.261719-.75-.46875l.4375-.703125c.351563.351563.773437.53125 1.265625.53125.3125 0 .578125-.089844.796875-.265625.226563-.179688.441406-.480469.640625-.90625l.296875-.671875-3.09375-6.921875h.96875l2.625 5.890625 2.609375-5.890625zm0 0" /><path id="be" d="m7.5625-6.9375v6.9375h-.890625v-1.265625c-.25.417969-.59375.746094-1.03125.984375-.429687.2304688-.917969.34375-1.46875.34375-.90625 0-1.625-.25-2.15625-.75-.523437-.507812-.78125-1.253906-.78125-2.234375v-4.015625h.9375v3.921875c0 .730469.179687 1.289063.546875 1.671875.363281.375.878906.5625 1.546875.5625.726563 0 1.300781-.21875 1.71875-.65625.425781-.445312.640625-1.066406.640625-1.859375v-3.640625zm0 0" /><path id="bf" d="m2.203125-5.578125c.21875-.46875.546875-.820313.984375-1.0625.4375-.238281.976562-.359375 1.625-.359375v.90625h-.234375c-.730469 0-1.304687.226562-1.71875.671875-.40625.449219-.609375 1.078125-.609375 1.890625v3.53125h-.9375v-6.9375h.890625zm0 0" /><path id="bg" d="m3.171875.0625c-.5625 0-1.105469-.078125-1.625-.234375-.511719-.164063-.914063-.375-1.203125-.625l.421875-.734375c.289063.230469.65625.417969 1.09375.5625.4375.148438.894531.21875 1.375.21875.632813 0 1.101563-.097656 1.40625-.296875.300781-.195313.453125-.476563.453125-.84375 0-.25-.085938-.445313-.25-.59375-.167969-.144531-.382812-.253906-.640625-.328125-.25-.082031-.589844-.15625-1.015625-.21875-.5625-.101562-1.015625-.210938-1.359375-.328125-.335937-.113281-.625-.300781-.875-.5625-.242187-.257813-.359375-.625-.359375-1.09375 0-.582031.238281-1.054687.71875-1.421875.488281-.375 1.164062-.5625 2.03125-.5625.445312 0 .894531.0625 1.34375.1875.445312.117188.816406.273438 1.109375.46875l-.40625.75c-.574219-.394531-1.257813-.59375-2.046875-.59375-.605469 0-1.058594.105469-1.359375.3125-.304687.210938-.453125.484375-.453125.828125 0 .273437.082031.484375.25.640625.175781.15625.390625.273438.640625.34375.257813.074219.613281.152344 1.0625.234375.550781.105469.992187.210937 1.328125.3125.34375.105469.628906.289063.859375.546875.238281.25.359375.601562.359375 1.046875 0 .617187-.257812 1.101563-.765625 1.453125-.5.355469-1.199219.53125-2.09375.53125zm0 0" /><path id="bh" d="m9.96875-7c.875 0 1.554688.25 2.046875.75.5.5.75 1.242188.75 2.21875v4.03125h-.9375v-3.9375c0-.71875-.179687-1.265625-.53125-1.640625-.34375-.382813-.828125-.578125-1.453125-.578125-.730469 0-1.304688.226562-1.71875.671875-.40625.449219-.609375 1.0625-.609375 1.84375v3.640625h-.9375v-3.9375c0-.71875-.179687-1.265625-.53125-1.640625-.34375-.382813-.839844-.578125-1.484375-.578125-.710938 0-1.273438.226562-1.6875.671875-.417969.449219-.625 1.0625-.625 1.84375v3.640625h-.9375v-6.9375h.890625v1.265625c.25-.425781.59375-.753906 1.03125-.984375.4375-.226562.941406-.34375 1.515625-.34375.582031 0 1.085938.125 1.515625.375.425781.242188.742187.605469.953125 1.09375.257812-.457031.628906-.816406 1.109375-1.078125.476563-.257813 1.023437-.390625 1.640625-.390625zm0 0" /><path id="bi" d="m4.890625-7c.644531 0 1.234375.152344 1.765625.453125.53125.292969.941406.710937 1.234375 1.25.300781.53125.453125 1.140625.453125 1.828125 0 .699219-.152344 1.320312-.453125 1.859375-.292969.53125-.703125.945313-1.234375 1.234375-.523438.2929688-1.109375.4375-1.765625.4375-.5625 0-1.074219-.1132812-1.53125-.34375-.449219-.238281-.820313-.582031-1.109375-1.03125v3.875h-.9375v-9.5h.890625v1.359375c.28125-.457031.648437-.804687 1.109375-1.046875.46875-.25.992188-.375 1.578125-.375zm-.078125 6.234375c.488281 0 .929688-.109375 1.328125-.328125.394531-.226562.703125-.550781.921875-.96875.226562-.414062.34375-.882812.34375-1.40625 0-.53125-.117188-1-.34375-1.40625-.21875-.414062-.527344-.734375-.921875-.953125-.398437-.226563-.839844-.34375-1.328125-.34375-.492188 0-.933594.117187-1.328125.34375-.386719.21875-.695313.539063-.921875.953125-.21875.40625-.328125.875-.328125 1.40625 0 .523438.109375.992188.328125 1.40625.226562.417969.535156.742188.921875.96875.394531.21875.835937.328125 1.328125.328125zm0 0" /><path id="bj" d="m1.40625.0625c-.199219 0-.367188-.06640625-.5-.203125-.136719-.144531-.203125-.316406-.203125-.515625 0-.207031.066406-.378906.203125-.515625.132812-.132813.300781-.203125.5-.203125.1875 0 .347656.070312.484375.203125.144531.136719.21875.308594.21875.515625 0 .199219-.074219.371094-.21875.515625-.136719.13671875-.296875.203125-.484375.203125zm0 0" /><path id="bk" d="m2.484375-8.390625v3.625h4.78125v.84375h-4.78125v3.921875h-.984375v-9.25h6.34375v.859375zm0 0" /><path id="bl" d="m1.3125-9.796875h.9375v9.796875h-.9375zm0 0" /><path id="bm" d="m7.140625-6.9375-3.09375 6.9375h-.953125l-3.078125-6.9375h.96875l2.609375 5.90625 2.625-5.90625zm0 0" /><path id="bn" d="m9.234375-9.25v9.25h-.8125l-5.9375-7.5v7.5h-.984375v-9.25h.8125l5.953125 7.5v-7.5zm0 0" /><path id="bo" d="m14.1875-9.25-3.109375 9.25h-1.03125l-2.703125-7.875-2.71875 7.875h-1.015625l-3.125-9.25h1l2.671875 7.953125 2.765625-7.953125h.90625l2.71875 8 2.703125-8zm0 0" /><path id="bp" d="m4.890625-7c.644531 0 1.234375.152344 1.765625.453125.53125.292969.941406.710937 1.234375 1.25.300781.53125.453125 1.140625.453125 1.828125s-.152344 1.304688-.453125 1.84375c-.292969.53125-.703125.949219-1.234375 1.25-.53125.2929688-1.121094.4375-1.765625.4375-.585937 0-1.109375-.1171875-1.578125-.359375-.460938-.25-.828125-.609375-1.109375-1.078125v1.375h-.890625v-9.796875h.9375v4.171875c.289062-.445312.660156-.785156 1.109375-1.015625.457031-.238281.96875-.359375 1.53125-.359375zm-.078125 6.234375c.488281 0 .929688-.109375 1.328125-.328125.394531-.226562.703125-.550781.921875-.96875.226562-.414062.34375-.882812.34375-1.40625 0-.53125-.117188-1-.34375-1.40625-.21875-.414062-.527344-.734375-.921875-.953125-.398437-.226563-.839844-.34375-1.328125-.34375-.492188 0-.933594.117187-1.328125.34375-.386719.21875-.695313.539063-.921875.953125-.21875.40625-.328125.875-.328125 1.40625 0 .523438.109375.992188.328125 1.40625.226562.417969.535156.742188.921875.96875.394531.21875.835937.328125 1.328125.328125zm0 0" /><path id="bq" d="m3.609375-9.078125c-.40625 0-.714844.117187-.921875.34375-.210938.21875-.3125.542969-.3125.96875v.828125h2.140625v.78125h-2.109375v6.15625h-.9375v-6.15625h-1.25v-.78125h1.25v-.859375c0-.632813.179688-1.132813.546875-1.5.363281-.375.878906-.5625 1.546875-.5625.269531 0 .523438.039063.765625.109375.25.074219.457031.183594.625.328125l-.3125.6875c-.28125-.226563-.625-.34375-1.03125-.34375zm0 0" /><path id="br" d="m1.40625-9.859375c.207031 0 .375.070313.5.203125.132812.136719.203125.304688.203125.5 0 .117188-.015625.230469-.046875.34375-.03125.117188-.074219.257812-.125.421875l-.5625 1.8125h-.640625l.46875-1.890625c-.148437-.050781-.265625-.132812-.359375-.25-.085938-.125-.125-.269531-.125-.4375 0-.207031.0625-.375.1875-.5.132812-.132812.300781-.203125.5-.203125zm0 0" /><path id="bs" d="m7.734375-6.9375v6.09375c0 1.175781-.292969 2.046875-.875 2.609375-.574219.570313-1.4375.859375-2.59375.859375-.648437 0-1.257813-.09375-1.828125-.28125-.574219-.1875-1.042969-.449219-1.40625-.78125l.46875-.71875c.332031.300781.738281.535156 1.21875.703125.488281.164063.992188.25 1.515625.25.882813 0 1.53125-.210937 1.9375-.625.414063-.40625.625-1.039063.625-1.890625v-.890625c-.292969.4375-.671875.773437-1.140625 1-.46875.230469-.992188.34375-1.5625.34375-.65625 0-1.25-.140625-1.78125-.421875-.53125-.289062-.949219-.691406-1.25-1.203125-.304688-.519531-.453125-1.101563-.453125-1.75 0-.65625.148437-1.238281.453125-1.75.300781-.507813.71875-.90625 1.25-1.1875s1.125-.421875 1.78125-.421875c.59375 0 1.125.121094 1.59375.359375.476562.242187.863281.585937 1.15625 1.03125v-1.328125zm-3.546875 5.84375c.5 0 .953125-.109375 1.359375-.328125s.71875-.519531.9375-.90625c.226563-.382813.34375-.820313.34375-1.3125 0-.5-.117187-.9375-.34375-1.3125-.21875-.382813-.53125-.679687-.9375-.890625-.398437-.21875-.851563-.328125-1.359375-.328125-.5 0-.953125.105469-1.359375.3125-.398437.210937-.710937.507813-.9375.890625-.21875.386719-.328125.828125-.328125 1.328125 0 .492187.109375.929687.328125 1.3125.226563.386719.539063.6875.9375.90625.40625.21875.859375.328125 1.359375.328125zm0 0" /><path id="bt" d="m3.78125-9.25v9.25h-1.71875v-7.796875h-1.953125v-1.453125zm0 0" /><path id="bu" d="m7.28125-1.453125v1.453125h-6.8125v-1.15625l3.65625-3.484375c.414062-.394531.695312-.738281.84375-1.03125.144531-.289063.21875-.582031.21875-.875 0-.4375-.148438-.765625-.4375-.984375-.292969-.226562-.71875-.34375-1.28125-.34375-.9375 0-1.65625.320312-2.15625.953125l-1.203125-.921875c.351563-.476562.835937-.851562 1.453125-1.125.613281-.269531 1.300781-.40625 2.0625-.40625 1 0 1.796875.242188 2.390625.71875.601563.46875.90625 1.117188.90625 1.9375 0 .5-.109375.976562-.328125 1.421875-.210938.4375-.609375.9375-1.203125 1.5l-2.46875 2.34375zm0 0" /><path id="bv" d="m4.515625-5.421875c.851563.105469 1.503906.398437 1.953125.875.445312.46875.671875 1.0625.671875 1.78125 0 .53125-.136719 1.023437-.40625 1.46875-.273437.4375-.683594.789063-1.234375 1.046875-.554688.25-1.226562.375-2.015625.375-.65625 0-1.292969-.09375-1.90625-.28125-.617187-.1875-1.136719-.445312-1.5625-.78125l.734375-1.328125c.332031.28125.738281.507813 1.21875.671875.476562.15625.972656.234375 1.484375.234375.613281 0 1.09375-.125 1.4375-.375s.515625-.59375.515625-1.03125c0-.425781-.167969-.757813-.5-1-.324219-.238281-.824219-.359375-1.5-.359375h-.84375v-1.1875l2.09375-2.484375h-4.1875v-1.453125h6.296875v1.15625zm0 0" />
<g id="poster-privacy"><use x="36.015129" xlink:href="#a" y="688.039044" /><use x="46.65639" xlink:href="#b" y="688.039044" /><use x="55.434469" xlink:href="#c" y="688.039044" /><use x="59.487368" xlink:href="#b" y="688.039044" /><use x="65.547512" xlink:href="#d" y="688.039044" /><use x="70.647253" xlink:href="#e" y="688.039044" /><use x="77.274026" xlink:href="#b" y="688.039044" /><use x="83.33417" xlink:href="#f" y="688.039044" /><use x="89.086968" xlink:href="#g" y="688.039044" /><use x="95.982653" xlink:href="#h" y="688.039044" /><use x="101.581795" xlink:href="#i" y="688.039044" /><use x="107.872428" xlink:href="#j" y="688.039044" /><use x="114.470395" xlink:href="#c" y="688.039044" /><use x="121.327669" xlink:href="#e" y="688.039044" /><use x="127.954442" xlink:href="#c" y="688.039044" /><use x="132.055358" xlink:href="#k" y="688.039044" /><use x="134.946171" xlink:href="#l" y="688.039044" /><use x="140.516497" xlink:href="#m" y="688.039044" /><use x="146.499807" xlink:href="#f" y="688.039044" /><use x="152.098949" xlink:href="#h" y="688.039044" /><use x="36.015129" xlink:href="#n" y="709.04787" /><use x="42.683919" xlink:href="#o" y="709.04787" /><use x="46.759613" xlink:href="#p" y="709.04787" /><use x="50.385126" xlink:href="#q" y="709.04787" /><use x="56.021477" xlink:href="#r" y="709.04787" /><use x="59.184797" xlink:href="#s" y="709.04787" /><use x="62.948371" xlink:href="#t" y="709.04787" /><use x="68.560714" xlink:href="#u" y="709.04787" /><use x="71.495937" xlink:href="#v" y="709.04787" /><use x="74.875355" xlink:href="#w" y="709.04787" /><use x="78.416833" xlink:href="#q" y="709.04787" /><use x="84.053184" xlink:href="#x" y="709.04787" /><use x="86.490197" xlink:href="#o" y="709.04787" /><use x="90.565891" xlink:href="#p" y="709.04787" /><use x="95.764059" xlink:href="#y" y="709.04787" /><use x="100.800167" xlink:href="#z" y="709.04787" /><use x="106.712643" xlink:href="#v" y="709.04787" /><use x="110.038033" xlink:href="#s" y="709.04787" /><use x="113.801608" xlink:href="#A" y="709.04787" /><use x="117.871314" xlink:href="#p" y="709.04787" /><use x="121.496826" xlink:href="#B" y="709.04787" /><use x="124.342016" xlink:href="#x" y="709.04787" /><use x="126.779029" xlink:href="#o" y="709.04787" /><use x="130.854723" xlink:href="#p" y="709.04787" /><use x="136.05289" xlink:href="#s" y="709.04787" /><use x="139.816465" xlink:href="#q" y="709.04787" /><use x="143.880161" xlink:href="#C" y="709.04787" /><use x="145.494834" xlink:href="#r" y="709.04787" /><use x="150.320854" xlink:href="#D" y="709.04787" /><use x="154.390561" xlink:href="#p" y="709.04787" /><use x="158.016073" xlink:href="#E" y="709.04787" /><use x="160.399059" xlink:href="#u" y="709.04787" /><use x="163.334282" xlink:href="#s" y="709.04787" /><use x="167.097857" xlink:href="#q" y="709.04787" /><use x="171.161553" xlink:href="#w" y="709.04787" /><use x="174.70303" xlink:href="#C" y="709.04787" /><use x="177.890358" xlink:href="#F" y="709.04787" /><use x="179.505031" xlink:href="#q" y="709.04787" /><use x="183.568727" xlink:href="#G" y="709.04787" /><use x="185.549547" xlink:href="#s" y="709.04787" /><use x="189.313121" xlink:href="#E" y="709.04787" /><use x="191.672099" xlink:href="#H" y="709.04787" /><use x="198.040767" xlink:href="#w" y="709.04787" /><use x="201.582245" xlink:href="#x" y="709.04787" /><use x="204.019258" xlink:href="#F" y="709.04787" /><use x="205.63393" xlink:href="#s" y="709.04787" /><use x="209.397505" xlink:href="#q" y="709.04787" /><use x="215.033856" xlink:href="#u" y="709.04787" /><use x="217.96908" xlink:href="#p" y="709.04787" /><use x="221.594592" xlink:href="#q" y="709.04787" /><use x="225.658287" xlink:href="#x" y="709.04787" /><use x="229.667956" xlink:href="#x" y="709.04787" /><use x="231.990915" xlink:href="#s" y="709.04787" /><use x="237.327156" xlink:href="#I" y="709.04787" /><use x="241.018694" xlink:href="#p" y="709.04787" /><use x="244.644195" xlink:href="#E" y="709.04787" /><use x="247.141246" xlink:href="#J" y="709.04787" /><use x="250.394611" xlink:href="#F" y="709.04787" /><use x="252.009273" xlink:href="#v" y="709.04787" /><use x="255.334675" xlink:href="#p" y="709.04787" /><use x="260.532842" xlink:href="#K" y="709.04787" /><use x="265.412891" xlink:href="#I" y="709.04787" /><use x="269.104451" xlink:href="#n" y="709.04787" /><use x="277.34592" xlink:href="#F" y="709.04787" /><use x="278.960581" xlink:href="#u" y="709.04787" /><use x="283.468472" xlink:href="#r" y="709.04787" /><use x="286.631803" xlink:href="#s" y="709.04787" /><use x="290.395377" xlink:href="#t" y="709.04787" /><use x="294.435053" xlink:href="#E" y="709.04787" /><use x="298.414737" xlink:href="#L" y="709.04787" /><use x="302.622483" xlink:href="#t" y="709.04787" /><use x="306.662159" xlink:href="#u" y="709.04787" /><use x="309.597383" xlink:href="#x" y="709.04787" /><use x="311.920342" xlink:href="#s" y="709.04787" /><use x="315.683916" xlink:href="#H" y="709.04787" /><use x="322.064595" xlink:href="#p" y="709.04787" /><use x="325.690096" xlink:href="#E" y="709.04787" /><use x="329.669779" xlink:href="#M" y="709.04787" /><use x="331.48253" xlink:href="#N" y="709.04787" /><use x="336.320538" xlink:href="#B" y="709.04787" /><use x="339.165727" xlink:href="#x" y="709.04787" /><use x="341.60274" xlink:href="#o" y="709.04787" /><use x="345.678434" xlink:href="#p" y="709.04787" /><use x="350.866138" xlink:href="#C" y="709.04787" /><use x="352.480811" xlink:href="#s" y="709.04787" /><use x="356.244386" xlink:href="#v" y="709.04787" /><use x="359.623803" xlink:href="#w" y="709.04787" /><use x="363.165281" xlink:href="#x" y="709.04787" /><use x="365.602294" xlink:href="#F" y="709.04787" /><use x="367.216967" xlink:href="#s" y="709.04787" /><use x="370.980541" xlink:href="#q" y="709.04787" /><use x="376.616892" xlink:href="#s" y="709.04787" /><use x="380.380467" xlink:href="#G" y="709.04787" /><use x="383.987969" xlink:href="#x" y="709.04787" /><use x="386.424983" xlink:href="#o" y="709.04787" /><use x="390.500677" xlink:href="#p" y="709.04787" /><use x="395.698844" xlink:href="#O" y="709.04787" /><use x="399.768551" xlink:href="#t" y="709.04787" /><use x="403.808238" xlink:href="#u" y="709.04787" /><use x="406.743462" xlink:href="#F" y="709.04787" /><use x="408.358135" xlink:href="#q" y="709.04787" /><use x="412.433829" xlink:href="#p" y="709.04787" /><use x="416.059341" xlink:href="#u" y="709.04787" /><use x="418.994565" xlink:href="#u" y="709.04787" /><use x="423.502443" xlink:href="#r" y="709.04787" /><use x="426.665763" xlink:href="#s" y="709.04787" /><use x="430.429338" xlink:href="#t" y="709.04787" /><use x="436.041681" xlink:href="#w" y="709.04787" /><use x="439.583158" xlink:href="#E" y="709.04787" /><use x="441.912128" xlink:href="#p" y="709.04787" /><use x="36.015129" xlink:href="#J" y="715.800706" /><use x="39.268494" xlink:href="#F" y="715.800706" /><use x="40.883167" xlink:href="#u" y="715.800706" /><use x="43.818391" xlink:href="#F" y="715.800706" /><use x="45.433064" xlink:href="#x" y="715.800706" /><use x="47.870077" xlink:href="#F" y="715.800706" /><use x="49.48475" xlink:href="#q" y="715.800706" /><use x="53.560444" xlink:href="#P" y="715.800706" /><use x="57.678167" xlink:href="#B" y="715.800706" /><use x="60.523356" xlink:href="#x" y="715.800706" /><use x="62.960369" xlink:href="#F" y="715.800706" /><use x="64.575042" xlink:href="#H" y="715.800706" /><use x="70.955709" xlink:href="#p" y="715.800706" /><use x="76.153877" xlink:href="#w" y="715.800706" /><use x="79.695354" xlink:href="#q" y="715.800706" /><use x="83.771048" xlink:href="#A" y="715.800706" /><use x="89.41341" xlink:href="#x" y="715.800706" /><use x="91.850423" xlink:href="#o" y="715.800706" /><use x="95.926118" xlink:href="#p" y="715.800706" /><use x="101.124285" xlink:href="#A" y="715.800706" /><use x="105.193992" xlink:href="#w" y="715.800706" /><use x="108.735469" xlink:href="#x" y="715.800706" /><use x="111.058428" xlink:href="#p" y="715.800706" /><use x="116.256596" xlink:href="#s" y="715.800706" /><use x="120.02017" xlink:href="#G" y="715.800706" /><use x="123.627673" xlink:href="#r" y="715.800706" /><use x="126.790993" xlink:href="#s" y="715.800706" /><use x="130.554567" xlink:href="#t" y="715.800706" /><use x="134.594255" xlink:href="#E" y="715.800706" /><use x="138.573916" xlink:href="#J" y="715.800706" /><use x="141.827281" xlink:href="#F" y="715.800706" /><use x="143.441954" xlink:href="#u" y="715.800706" /><use x="146.377177" xlink:href="#F" y="715.800706" /><use x="147.99185" xlink:href="#x" y="715.800706" /><use x="150.488879" xlink:href="#Q" y="715.800706" /><use x="153.334068" xlink:href="#R" y="715.800706" /><use x="156.779512" xlink:href="#o" y="715.800706" /><use x="160.855206" xlink:href="#p" y="715.800706" /><use x="166.053373" xlink:href="#O" y="715.800706" /><use x="170.12308" xlink:href="#t" y="715.800706" /><use x="174.162767" xlink:href="#u" y="715.800706" /><use x="177.097991" xlink:href="#F" y="715.800706" /><use x="178.712664" xlink:href="#q" y="715.800706" /><use x="182.788358" xlink:href="#p" y="715.800706" /><use x="186.41387" xlink:href="#u" y="715.800706" /><use x="189.349094" xlink:href="#u" y="715.800706" /><use x="193.856972" xlink:href="#S" y="715.800706" /><use x="199.133187" xlink:href="#F" y="715.800706" /><use x="200.74786" xlink:href="#C" y="715.800706" /><use x="202.362533" xlink:href="#C" y="715.800706" /><use x="205.549861" xlink:href="#q" y="715.800706" /><use x="209.625555" xlink:href="#s" y="715.800706" /><use x="213.38913" xlink:href="#x" y="715.800706" /><use x="217.398798" xlink:href="#u" y="715.800706" /><use x="220.334022" xlink:href="#p" y="715.800706" /><use x="223.959534" xlink:href="#p" y="715.800706" /><use x="229.157701" xlink:href="#s" y="715.800706" /><use x="232.921276" xlink:href="#E" y="715.800706" /><use x="236.9003" xlink:href="#v" y="715.800706" /><use x="240.22569" xlink:href="#s" y="715.800706" /><use x="243.989265" xlink:href="#C" y="715.800706" /><use x="245.603937" xlink:href="#C" y="715.800706" /><use x="247.21861" xlink:href="#p" y="715.800706" /><use x="250.844122" xlink:href="#v" y="715.800706" /><use x="254.259547" xlink:href="#x" y="715.800706" /><use x="258.269216" xlink:href="#x" y="715.800706" /><use x="260.706229" xlink:href="#o" y="715.800706" /><use x="264.769925" xlink:href="#F" y="715.800706" /><use x="266.384597" xlink:href="#u" y="715.800706" /><use x="270.892476" xlink:href="#F" y="715.800706" /><use x="272.507149" xlink:href="#q" y="715.800706" /><use x="276.570845" xlink:href="#G" y="715.800706" /><use x="278.551665" xlink:href="#s" y="715.800706" /><use x="282.31524" xlink:href="#E" y="715.800706" /><use x="284.674217" xlink:href="#H" y="715.800706" /><use x="291.042886" xlink:href="#w" y="715.800706" /><use x="294.584363" xlink:href="#x" y="715.800706" /><use x="297.021376" xlink:href="#F" y="715.800706" /><use x="298.636049" xlink:href="#s" y="715.800706" /><use x="302.399623" xlink:href="#q" y="715.800706" /><use x="308.035974" xlink:href="#w" y="715.800706" /><use x="311.577452" xlink:href="#q" y="715.800706" /><use x="315.653146" xlink:href="#A" y="715.800706" /><use x="321.295508" xlink:href="#F" y="715.800706" /><use x="322.910181" xlink:href="#x" y="715.800706" /><use x="326.919849" xlink:href="#S" y="715.800706" /><use x="332.196064" xlink:href="#F" y="715.800706" /><use x="333.810737" xlink:href="#C" y="715.800706" /><use x="335.42541" xlink:href="#C" y="715.800706" /><use x="338.612738" xlink:href="#s" y="715.800706" /><use x="342.376312" xlink:href="#q" y="715.800706" /><use x="346.440008" xlink:href="#C" y="715.800706" /><use x="348.054681" xlink:href="#r" y="715.800706" /><use x="352.880702" xlink:href="#O" y="715.800706" /><use x="356.950408" xlink:href="#p" y="715.800706" /><use x="362.148576" xlink:href="#t" y="715.800706" /><use x="366.188263" xlink:href="#u" y="715.800706" /><use x="369.123487" xlink:href="#p" y="715.800706" /><use x="372.748999" xlink:href="#A" y="715.800706" /><use x="378.391361" xlink:href="#G" y="715.800706" /><use x="380.372181" xlink:href="#s" y="715.800706" /><use x="384.135755" xlink:href="#E" y="715.800706" /><use x="388.115416" xlink:href="#v" y="715.800706" /><use x="391.440807" xlink:href="#s" y="715.800706" /><use x="395.204381" xlink:href="#q" y="715.800706" /><use x="399.268077" xlink:href="#x" y="715.800706" /><use x="401.70509" xlink:href="#w" y="715.800706" /><use x="405.258566" xlink:href="#v" y="715.800706" /><use x="408.67399" xlink:href="#x" y="715.800706" /><use x="412.683659" xlink:href="#x" y="715.800706" /><use x="415.120672" xlink:href="#E" y="715.800706" /><use x="417.47365" xlink:href="#w" y="715.800706" /><use x="421.027126" xlink:href="#v" y="715.800706" /><use x="424.406544" xlink:href="#F" y="715.800706" /><use x="426.021217" xlink:href="#q" y="715.800706" /><use x="430.096911" xlink:href="#P" y="715.800706" /><use x="434.214646" xlink:href="#Q" y="715.800706" /><use x="437.059835" xlink:href="#n" y="715.800706" /><use x="443.398496" xlink:href="#p" y="715.800706" /><use x="36.015129" xlink:href="#s" y="722.553543" /><use x="39.778703" xlink:href="#q" y="722.553543" /><use x="43.842399" xlink:href="#C" y="722.553543" /><use x="45.457072" xlink:href="#r" y="722.553543" /><use x="50.283093" xlink:href="#T" y="722.553543" /><use x="53.764543" xlink:href="#p" y="722.553543" /><use x="57.390055" xlink:href="#p" y="722.553543" /><use x="61.015568" xlink:href="#D" y="722.553543" /><use x="66.65793" xlink:href="#x" y="722.553543" /><use x="69.094943" xlink:href="#o" y="722.553543" /><use x="73.158638" xlink:href="#F" y="722.553543" /><use x="74.773311" xlink:href="#u" y="722.553543" /><use x="79.28119" xlink:href="#F" y="722.553543" /><use x="80.895863" xlink:href="#q" y="722.553543" /><use x="84.959559" xlink:href="#G" y="722.553543" /><use x="86.940379" xlink:href="#s" y="722.553543" /><use x="90.703953" xlink:href="#E" y="722.553543" /><use x="93.062931" xlink:href="#H" y="722.553543" /><use x="99.4316" xlink:href="#w" y="722.553543" /><use x="102.973077" xlink:href="#x" y="722.553543" /><use x="105.41009" xlink:href="#F" y="722.553543" /><use x="107.024763" xlink:href="#s" y="722.553543" /><use x="110.788337" xlink:href="#q" y="722.553543" /><use x="116.424688" xlink:href="#G" y="722.553543" /><use x="118.405508" xlink:href="#s" y="722.553543" /><use x="122.169083" xlink:href="#E" y="722.553543" /><use x="126.148744" xlink:href="#U" y="722.553543" /><use x="129.558169" xlink:href="#V" y="722.553543" /><use x="134.951224" xlink:href="#A" y="722.553543" /><use x="139.02093" xlink:href="#w" y="722.553543" /><use x="142.502381" xlink:href="#r" y="722.553543" /><use x="145.719728" xlink:href="#u" y="722.553543" /><use x="150.227607" xlink:href="#t" y="722.553543" /><use x="154.267295" xlink:href="#q" y="722.553543" /><use x="158.330991" xlink:href="#C" y="722.553543" /><use x="159.945663" xlink:href="#p" y="722.553543" /><use x="163.571176" xlink:href="#u" y="722.553543" /><use x="166.506399" xlink:href="#u" y="722.553543" /><use x="171.014278" xlink:href="#E" y="722.553543" /><use x="173.343248" xlink:href="#p" y="722.553543" /><use x="176.96876" xlink:href="#W" y="722.553543" /><use x="181.038466" xlink:href="#t" y="722.553543" /><use x="185.078154" xlink:href="#F" y="722.553543" /><use x="186.692827" xlink:href="#E" y="722.553543" /><use x="189.021796" xlink:href="#p" y="722.553543" /><use x="192.647309" xlink:href="#A" y="722.553543" /><use x="198.28967" xlink:href="#G" y="722.553543" /><use x="200.270491" xlink:href="#s" y="722.553543" /><use x="204.034065" xlink:href="#E" y="722.553543" /><use x="208.013726" xlink:href="#v" y="722.553543" /><use x="211.339117" xlink:href="#s" y="722.553543" /><use x="215.102691" xlink:href="#q" y="722.553543" /><use x="219.166387" xlink:href="#x" y="722.553543" /><use x="221.6034" xlink:href="#w" y="722.553543" /><use x="225.156875" xlink:href="#v" y="722.553543" /><use x="228.5723" xlink:href="#x" y="722.553543" /><use x="232.581969" xlink:href="#x" y="722.553543" /><use x="235.018982" xlink:href="#E" y="722.553543" /><use x="237.37196" xlink:href="#w" y="722.553543" /><use x="240.925435" xlink:href="#v" y="722.553543" /><use x="244.304853" xlink:href="#F" y="722.553543" /><use x="245.919526" xlink:href="#q" y="722.553543" /><use x="249.995221" xlink:href="#P" y="722.553543" /><use x="255.685599" xlink:href="#E" y="722.553543" /><use x="258.014569" xlink:href="#p" y="722.553543" /><use x="261.640081" xlink:href="#C" y="722.553543" /><use x="263.254754" xlink:href="#w" y="722.553543" /><use x="266.796231" xlink:href="#x" y="722.553543" /><use x="269.11919" xlink:href="#p" y="722.553543" /><use x="272.744702" xlink:href="#A" y="722.553543" /><use x="278.387064" xlink:href="#x" y="722.553543" /><use x="280.710023" xlink:href="#s" y="722.553543" /><use x="286.046253" xlink:href="#w" y="722.553543" /><use x="291.160386" xlink:href="#L" y="722.553543" /><use x="295.386152" xlink:href="#X" y="722.553543" /><use x="300.296197" xlink:href="#Y" y="722.553543" /><use x="304.395911" xlink:href="#M" y="722.553543" /><use x="306.208661" xlink:href="#N" y="722.553543" /><use x="311.21475" xlink:href="#Z" y="722.553543" /><use x="313.405669" xlink:href="#aa" y="722.553543" /><use x="315.572568" xlink:href="#ab" y="722.553543" /><use x="320.800755" xlink:href="#o" y="722.553543" /><use x="324.876449" xlink:href="#s" y="722.553543" /><use x="328.640023" xlink:href="#x" y="722.553543" /><use x="331.077037" xlink:href="#u" y="722.553543" /><use x="334.01226" xlink:href="#D" y="722.553543" /><use x="338.081978" xlink:href="#s" y="722.553543" /><use x="341.845552" xlink:href="#x" y="722.553543" /><use x="344.342581" xlink:href="#Q" y="722.553543" /><use x="347.187793" xlink:href="#ac" y="722.553543" /><use x="351.497594" xlink:href="#C" y="722.553543" /><use x="353.112279" xlink:href="#p" y="722.553543" /><use x="356.647745" xlink:href="#w" y="722.553543" /><use x="360.189211" xlink:href="#u" y="722.553543" /><use x="363.124434" xlink:href="#p" y="722.553543" /><use x="368.322602" xlink:href="#E" y="722.553543" /><use x="370.651583" xlink:href="#p" y="722.553543" /><use x="374.277084" xlink:href="#G" y="722.553543" /><use x="376.257904" xlink:href="#p" y="722.553543" /><use x="379.883427" xlink:href="#E" y="722.553543" /><use x="383.863111" xlink:href="#x" y="722.553543" /><use x="386.18607" xlink:href="#s" y="722.553543" /><use x="391.522289" xlink:href="#x" y="722.553543" /><use x="393.959302" xlink:href="#o" y="722.553543" /><use x="398.034996" xlink:href="#p" y="722.553543" /><use x="403.233186" xlink:href="#v" y="722.553543" /><use x="406.558588" xlink:href="#s" y="722.553543" /><use x="410.322163" xlink:href="#C" y="722.553543" /><use x="411.936824" xlink:href="#C" y="722.553543" /><use x="413.551485" xlink:href="#p" y="722.553543" /><use x="417.176986" xlink:href="#v" y="722.553543" /><use x="420.592422" xlink:href="#x" y="722.553543" /><use x="423.029435" xlink:href="#F" y="722.553543" /><use x="424.644097" xlink:href="#s" y="722.553543" /><use x="428.407671" xlink:href="#q" y="722.553543" /><use x="434.139404" xlink:href="#q" y="722.553543" /><use x="438.215098" xlink:href="#s" y="722.553543" /><use x="441.978673" xlink:href="#x" y="722.553543" /><use x="444.415686" xlink:href="#F" y="722.553543" /><use x="446.030358" xlink:href="#v" y="722.553543" /><use x="449.355749" xlink:href="#p" y="722.553543" /><use x="36.015129" xlink:href="#F" y="729.30638" /><use x="37.629802" xlink:href="#q" y="729.30638" /><use x="43.266153" xlink:href="#x" y="729.30638" /><use x="45.703166" xlink:href="#o" y="729.30638" /><use x="49.77886" xlink:href="#p" y="729.30638" /><use x="54.977028" xlink:href="#w" y="729.30638" /><use x="58.518505" xlink:href="#D" y="729.30638" /><use x="62.588212" xlink:href="#D" y="729.30638" /><use x="68.230573" xlink:href="#G" y="729.30638" /><use x="70.211394" xlink:href="#s" y="729.30638" /><use x="73.974968" xlink:href="#E" y="729.30638" /><use x="77.954629" xlink:href="#H" y="729.30638" /><use x="84.335296" xlink:href="#s" y="729.30638" /><use x="88.09887" xlink:href="#E" y="729.30638" /><use x="90.42784" xlink:href="#p" y="729.30638" /><use x="95.626008" xlink:href="#F" y="729.30638" /><use x="97.240681" xlink:href="#q" y="729.30638" /><use x="101.304376" xlink:href="#G" y="729.30638" /><use x="103.285197" xlink:href="#s" y="729.30638" /><use x="107.048771" xlink:href="#E" y="729.30638" /><use x="109.407748" xlink:href="#H" y="729.30638" /><use x="115.776417" xlink:href="#w" y="729.30638" /><use x="119.317894" xlink:href="#x" y="729.30638" /><use x="121.754907" xlink:href="#F" y="729.30638" /><use x="123.36958" xlink:href="#s" y="729.30638" /><use x="127.133155" xlink:href="#q" y="729.30638" /><use x="131.19685" xlink:href="#Q" y="729.30638" /><use x="36.015129" xlink:href="#R" y="748.064259" /><use x="39.460573" xlink:href="#o" y="748.064259" /><use x="43.524268" xlink:href="#F" y="748.064259" /><use x="45.138941" xlink:href="#u" y="748.064259" /><use x="49.64682" xlink:href="#y" y="748.064259" /><use x="54.682928" xlink:href="#z" y="748.064259" /><use x="60.595404" xlink:href="#v" y="748.064259" /><use x="63.920795" xlink:href="#s" y="748.064259" /><use x="67.684369" xlink:href="#A" y="748.064259" /><use x="71.754076" xlink:href="#p" y="748.064259" /><use x="76.952243" xlink:href="#F" y="748.064259" /><use x="78.566916" xlink:href="#u" y="748.064259" /><use x="83.074795" xlink:href="#G" y="748.064259" /><use x="85.055615" xlink:href="#s" y="748.064259" /><use x="88.81919" xlink:href="#E" y="748.064259" /><use x="143.706115" xlink:href="#B" y="748.064259" /><use x="146.551304" xlink:href="#C" y="748.064259" /><use x="148.165977" xlink:href="#s" y="748.064259" /><use x="151.929551" xlink:href="#v" y="748.064259" /><use x="155.308969" xlink:href="#w" y="748.064259" /><use x="158.850446" xlink:href="#x" y="748.064259" /><use x="161.173405" xlink:href="#p" y="748.064259" /><use x="164.798917" xlink:href="#A" y="748.064259" /><use x="170.438784" xlink:href="#w" y="748.064259" /><use x="173.980261" xlink:href="#x" y="748.064259" /><use x="263.591668" xlink:href="#Q" y="748.064259" /></g>
<g id="poster-community"><use x="152.32571" xlink:href="#ad" y="243.10214" /><use x="169.617764" xlink:href="#ae" y="243.10214" /><use x="179.481095" xlink:href="#af" y="243.10214" /><use x="183.570002" xlink:href="#ag" y="243.10214" /><use x="190.155959" xlink:href="#ae" y="243.10214" /><use x="204.42034" xlink:href="#ah" y="243.10214" /><use x="215.29809" xlink:href="#ae" y="243.10214" /><use x="225.145816" xlink:href="#ai" y="243.10214" /><use x="229.843381" xlink:href="#aj" y="243.10214" /><use x="240.611896" xlink:href="#ak" y="243.10214" /><use x="245.309461" xlink:href="#al" y="243.10214" /><use x="256.18721" xlink:href="#am" y="243.10214" /><use x="271.528452" xlink:href="#an" y="243.10214" /><use x="281.469819" xlink:href="#ae" y="243.10214" /><use x="291.317545" xlink:href="#ae" y="243.10214" /><use x="301.165272" xlink:href="#aj" y="243.10214" /><use x="316.350442" xlink:href="#ao" y="243.10214" /><use x="326.57273" xlink:href="#ap" y="243.10214" /><use x="337.294432" xlink:href="#ag" y="243.10214" /><use x="348.43751" xlink:href="#aq" y="243.10214" /><use x="357.551705" xlink:href="#ao" y="243.10214" /><use x="367.773994" xlink:href="#ar" y="243.10214" /><use x="384.145258" xlink:href="#ar" y="243.10214" /><use x="400.516546" xlink:href="#ap" y="243.10214" /><use x="411.238259" xlink:href="#al" y="243.10214" /><use x="422.022391" xlink:href="#ak" y="243.10214" /><use x="426.719967" xlink:href="#as" y="243.10214" /><use x="433.352727" xlink:href="#at" y="243.10214" /><use x="132.325121" xlink:href="#au" y="261.860019" /><use x="143.530618" xlink:href="#av" y="261.860019" /><use x="156.343587" xlink:href="#aw" y="261.860019" /><use x="167.751971" xlink:href="#ax" y="261.860019" /><use x="172.870912" xlink:href="#ay" y="261.860019" /><use x="190.17857" xlink:href="#az" y="261.860019" /><use x="198.465651" xlink:href="#aA" y="261.860019" /><use x="208.094887" xlink:href="#aB" y="261.860019" /><use x="214.02536" xlink:href="#ae" y="261.860019" /><use x="228.289741" xlink:href="#aC" y="261.860019" /><use x="238.824149" xlink:href="#at" y="261.860019" /><use x="252.573513" xlink:href="#ag" y="261.860019" /><use x="259.15947" xlink:href="#ae" y="261.860019" /><use x="269.007197" xlink:href="#aq" y="261.860019" /><use x="278.121403" xlink:href="#ao" y="261.860019" /><use x="288.343692" xlink:href="#ag" y="261.860019" /><use x="294.92965" xlink:href="#aD" y="261.860019" /><use x="305.729375" xlink:href="#ak" y="261.860019" /><use x="310.42694" xlink:href="#al" y="261.860019" /><use x="321.304689" xlink:href="#am" y="261.860019" /><use x="336.645919" xlink:href="#aq" y="261.860019" /><use x="345.760115" xlink:href="#ao" y="261.860019" /><use x="355.982404" xlink:href="#al" y="261.860019" /><use x="366.766513" xlink:href="#as" y="261.860019" /><use x="373.555345" xlink:href="#aA" y="261.860019" /><use x="383.278209" xlink:href="#aq" y="261.860019" /><use x="392.626512" xlink:href="#as" y="261.860019" /><use x="403.83201" xlink:href="#aD" y="261.860019" /><use x="414.631735" xlink:href="#ae" y="261.860019" /><use x="424.479462" xlink:href="#as" y="261.860019" /><use x="431.268294" xlink:href="#aA" y="261.860019" /><use x="440.897529" xlink:href="#ak" y="261.860019" /><use x="445.595082" xlink:href="#ai" y="261.860019" /><use x="450.292658" xlink:href="#az" y="261.860019" /><use x="458.579739" xlink:href="#aE" y="261.860019" /></g><path d="m280.002122 385.999217h233.995414v234.00062h-233.995414zm0 0" fill="none" stroke="#01a0d5" stroke-width="16" transform="matrix(.750315 0 0 .750315 0 .00002)" />
<g id="poster-check-in" fill="#01a0d5"><use height="100%" width="100%" x="131.68031" xlink:href="#aG" y="174.82346" /><use height="100%" width="100%" x="152.18309" xlink:href="#aH" y="174.82346" /><use height="100%" width="100%" x="160.61388" xlink:href="#aI" y="174.82346" /><use height="100%" width="100%" x="177.86757" xlink:href="#aJ" y="174.82346" /><use height="100%" width="100%" x="195.14929" xlink:href="#aK" y="174.82346" /><use height="100%" width="100%" x="210.0222" xlink:href="#aI" y="174.82346" /><use height="100%" width="100%" x="235.62265" xlink:href="#aL" y="174.82346" /><use height="100%" width="100%" x="252.06409" xlink:href="#aM" y="174.82346" /><use height="100%" width="100%" x="271.58655" xlink:href="#aI" y="174.82346" /><use height="100%" width="100%" x="289.26038" xlink:href="#aL" y="174.82346" /><use height="100%" width="100%" x="305.70181" xlink:href="#aN" y="174.82346" /><use height="100%" width="100%" x="332.11453" xlink:href="#aO" y="174.82346" /><use height="100%" width="100%" x="340.54532" xlink:href="#aP" y="174.82346" /><use height="100%" width="100%" x="367.82635" xlink:href="#aQ" y="174.82346" /><use height="100%" width="100%" x="387.15274" xlink:href="#aI" y="174.82346" /><use height="100%" width="100%" x="404.8266" xlink:href="#aR" y="174.82346" /><use height="100%" width="100%" x="415.47009" xlink:href="#aS" y="174.82346" /><use height="100%" width="100%" x="433.81616" xlink:href="#aT" y="174.82346" /><use height="100%" width="100%" x="445.63605" xlink:href="#aI" y="174.82346" /><use height="100%" width="100%" x="131.08241" xlink:href="#aI" y="204.83606" /><use height="100%" width="100%" x="148.75626" xlink:href="#aP" y="204.83606" /><use height="100%" width="100%" x="168.11066" xlink:href="#aU" y="204.83606" /><use height="100%" width="100%" x="179.81851" xlink:href="#aI" y="204.83606" /><use height="100%" width="100%" x="197.49236" xlink:href="#aT" y="204.83606" /><use height="100%" width="100%" x="209.4523" xlink:href="#aO" y="204.83606" /><use height="100%" width="100%" x="217.88309" xlink:href="#aP" y="204.83606" /><use height="100%" width="100%" x="237.40553" xlink:href="#aV" y="204.83606" /><use height="100%" width="100%" x="264.93863" xlink:href="#aS" y="204.83606" /><use height="100%" width="100%" x="283.2847" xlink:href="#aW" y="204.83606" /><use height="100%" width="100%" x="302.52707" xlink:href="#aT" y="204.83606" /><use height="100%" width="100%" x="322.52567" xlink:href="#aX" y="204.83606" /><use height="100%" width="100%" x="341.85205" xlink:href="#aT" y="204.83606" /><use height="100%" width="100%" x="353.67194" xlink:href="#aI" y="204.83606" /><use height="100%" width="100%" x="371.34579" xlink:href="#aY" y="204.83606" /><use height="100%" width="100%" x="400.72754" xlink:href="#aO" y="204.83606" /><use height="100%" width="100%" x="409.15833" xlink:href="#aK" y="204.83606" /><use height="100%" width="100%" x="424.03122" xlink:href="#aI" y="204.83606" /><use height="100%" width="100%" x="441.70508" xlink:href="#aK" y="204.83606" /><use height="100%" width="100%" x="456.57797" xlink:href="#aZ" y="204.83606" /></g>
<g id="poster-easy"><use x="215.481142" xlink:href="#ax" y="504.211824" /><use x="220.600083" xlink:href="#as" y="504.211824" /><use x="227.388927" xlink:href="#af" y="504.211824" /><use x="231.196911" xlink:href="#az" y="504.211824" /><use x="243.900646" xlink:href="#ae" y="504.211824" /><use x="253.514266" xlink:href="#aA" y="504.211824" /><use x="263.143501" xlink:href="#az" y="504.211824" /><use x="271.274511" xlink:href="#at" y="504.211824" /><use x="285.023874" xlink:href="#as" y="504.211824" /><use x="291.547401" xlink:href="#ao" y="504.211824" /><use x="306.186344" xlink:href="#aq" y="504.211824" /><use x="315.347376" xlink:href="#ah" y="504.211824" /><use x="326.225126" xlink:href="#ae" y="504.211824" /><use x="336.072853" xlink:href="#aq" y="504.211824" /><use x="345.233886" xlink:href="#an" y="504.211824" /><use x="359.950864" xlink:href="#ak" y="504.211824" /><use x="364.648428" xlink:href="#al" y="504.211824" /><use x="375.432549" xlink:href="#aF" y="504.211824" /><use x="126.615687" xlink:href="#a0" y="538.726323" /><use x="134.737091" xlink:href="#a1" y="538.726323" /><use x="142.171813" xlink:href="#a2" y="538.726323" /><use x="149.963077" xlink:href="#a3" y="538.726323" /><use x="162.363072" xlink:href="#a4" y="538.726323" /><use x="167.724524" xlink:href="#a5" y="538.726323" /><use x="176.691074" xlink:href="#a6" y="538.726323" /><use x="188.127061" xlink:href="#a7" y="538.726323" /><use x="199.206506" xlink:href="#a8" y="538.726323" /><use x="212.213958" xlink:href="#a1" y="538.726323" /><use x="219.529829" xlink:href="#a9" y="538.726323" /><use x="227.809697" xlink:href="#ba" y="538.726323" /><use x="236.763047" xlink:href="#a6" y="538.726323" /><use x="248.199033" xlink:href="#bb" y="538.726323" /><use x="259.806708" xlink:href="#bc" y="538.726323" /><use x="263.358993" xlink:href="#a4" y="538.726323" /><use x="268.720444" xlink:href="#a5" y="538.726323" /><use x="281.12044" xlink:href="#bd" y="538.726323" /><use x="288.079758" xlink:href="#a9" y="538.726323" /><use x="296.359626" xlink:href="#be" y="538.726323" /><use x="305.24695" xlink:href="#bf" y="538.726323" /><use x="314.002211" xlink:href="#bg" y="538.726323" /><use x="320.459735" xlink:href="#bh" y="538.726323" /><use x="334.470808" xlink:href="#a2" y="538.726323" /><use x="342.26206" xlink:href="#bf" y="538.726323" /><use x="347.755564" xlink:href="#a4" y="538.726323" /><use x="353.117015" xlink:href="#bi" y="538.726323" /><use x="362.070354" xlink:href="#a5" y="538.726323" /><use x="371.036904" xlink:href="#a9" y="538.726323" /><use x="379.316772" xlink:href="#a3" y="538.726323" /><use x="388.283323" xlink:href="#a6" y="538.726323" /><use x="399.719332" xlink:href="#a1" y="538.726323" /><use x="407.154066" xlink:href="#a2" y="538.726323" /><use x="414.945318" xlink:href="#bh" y="538.726323" /><use x="428.982815" xlink:href="#a6" y="538.726323" /><use x="436.958944" xlink:href="#bf" y="538.726323" /><use x="442.135496" xlink:href="#a2" y="538.726323" /><use x="453.386606" xlink:href="#a9" y="538.726323" /><use x="461.666474" xlink:href="#bf" y="538.726323" /><use x="470.421747" xlink:href="#a7" y="538.726323" /><use x="481.501192" xlink:href="#a8" y="538.726323" /><use x="126.615687" xlink:href="#a1" y="554.482942" /><use x="133.931558" xlink:href="#a9" y="554.482942" /><use x="142.211426" xlink:href="#ba" y="554.482942" /><use x="151.164776" xlink:href="#a6" y="554.482942" /><use x="162.600763" xlink:href="#bf" y="554.482942" /><use x="167.724501" xlink:href="#a6" y="554.482942" /><use x="175.502553" xlink:href="#a2" y="554.482942" /><use x="183.320217" xlink:href="#ba" y="554.482942" /><use x="192.273567" xlink:href="#a6" y="554.482942" /><use x="200.249707" xlink:href="#bf" y="554.482942" /><use x="205.413059" xlink:href="#bj" y="554.482942" /><path d="m90.414062 535.726562v-.75c0-.816406.074219-1.621093.222657-2.417968.148437-.796875.371093-1.570313.660156-2.320313.292969-.753906.652344-1.464843 1.074219-2.140625.425781-.675781.910156-1.300781 1.449218-1.878906.539063-.574219 1.125-1.085938 1.761719-1.539062.632813-.449219 1.304688-.832032 2.011719-1.144532.703125-.308594 1.429688-.542968 2.179688-.703125.75-.160156 1.503906-.238281 2.269531-.238281.761719 0 1.519531.078125 2.269531.238281.75.160157 1.476562.394531 2.179688.703125.707031.3125 1.375.695313 2.011718 1.144532.636719.453124 1.222656.964843 1.761719 1.539062.539063.578125 1.023437 1.203125 1.445313 1.878906.425781.675782.785156 1.386719 1.078124 2.140625.289063.75.511719 1.523438.660157 2.320313.148437.796875.222656 1.601562.222656 2.417968v.75c0 .8125-.074219 1.617188-.222656 2.414063-.148438.796875-.371094 1.570313-.660157 2.320313-.292968.753906-.652343 1.464843-1.078124 2.140624-.421876.675782-.90625 1.304688-1.445313 1.878907s-1.125 1.085937-1.761719 1.539062c-.636718.449219-1.304687.832031-2.011718 1.144531-.703126.308594-1.429688.542969-2.179688.703126-.75.160156-1.507812.238281-2.269531.238281-.765625 0-1.519531-.078125-2.269531-.238281-.75-.160157-1.476563-.394532-2.179688-.703126-.707031-.3125-1.378906-.695312-2.011719-1.144531-.636719-.453125-1.222656-.964843-1.761719-1.539062-.539062-.574219-1.023437-1.203125-1.449218-1.878907-.421875-.675781-.78125-1.386718-1.074219-2.140624-.289063-.75-.511719-1.523438-.660156-2.320313-.148438-.796875-.222657-1.601563-.222657-2.414063zm0 0" fill="#fff" /><path d="m89.289062 535.726562v-.75c0-.886718.082032-1.765624.242188-2.636718.164062-.871094.40625-1.714844.726562-2.535156.320313-.816407.714844-1.597657 1.179688-2.332032.464844-.738281.992188-1.421875 1.585938-2.046875.59375-.628906 1.238281-1.1875 1.933593-1.679687.695313-.492188 1.429688-.910156 2.203125-1.25.773438-.339844 1.574219-.59375 2.394532-.765625.820312-.175781 1.652343-.261719 2.488281-.261719.835937 0 1.667969.085938 2.488281.261719.820312.171875 1.617188.425781 2.394531.765625.773438.339844 1.507813.757812 2.203125 1.25.695313.492187 1.339844 1.050781 1.933594 1.679687.59375.625 1.121094 1.308594 1.585938 2.046875.464843.734375.859374 1.515625 1.179687 2.332032.320313.820312.5625 1.664062.726563 2.535156.160156.871094.242187 1.75.242187 2.636718v.75c0 .886719-.082031 1.761719-.242187 2.632813-.164063.871094-.40625 1.714844-.726563 2.535156-.320313.816407-.714844 1.597657-1.179687 2.332031-.464844.738282-.992188 1.421876-1.585938 2.046876-.59375.628906-1.238281 1.1875-1.933594 1.679687-.695312.492187-1.429687.910156-2.203125 1.25-.777343.339844-1.574219.59375-2.394531.769531-.820312.171875-1.652344.257813-2.488281.257813-.835938 0-1.667969-.085938-2.488281-.257813-.820313-.175781-1.621094-.429687-2.394532-.769531-.773437-.339844-1.507812-.757813-2.203125-1.25-.695312-.492187-1.339843-1.050781-1.933593-1.679687-.59375-.625-1.121094-1.308594-1.585938-2.046876-.464844-.734374-.859375-1.515624-1.179688-2.332031-.320312-.820312-.5625-1.664062-.726562-2.535156-.160156-.871094-.242188-1.746094-.242188-2.632813zm2.25 0v-.75c0-.742187.066407-1.472656.203126-2.199218.132812-.722656.332031-1.425782.597656-2.109375.261718-.683594.585937-1.332031.96875-1.945313.382812-.617187.820312-1.183594 1.304687-1.707031.488281-.523437 1.019531-.988281 1.59375-1.398437.574219-.410157 1.179688-.757813 1.816407-1.039063.636718-.285156 1.292968-.496094 1.96875-.640625.679687-.144531 1.359374-.21875 2.050781-.21875.691406 0 1.371093.074219 2.050781.21875.675781.144531 1.332031.355469 1.96875.640625.636719.28125 1.242188.628906 1.816406 1.039063.574219.410156 1.105469.875 1.589844 1.398437.488281.523437.925781 1.089844 1.308594 1.707031.382812.613282.707031 1.261719.96875 1.945313.265625.683593.464844 1.386719.597656 2.109375.136719.726562.203125 1.457031.203125 2.199218v.75c0 .738282-.066406 1.46875-.203125 2.195313-.132812.722656-.332031 1.425781-.597656 2.109375-.261719.683594-.585938 1.332031-.96875 1.945312-.382813.617188-.820313 1.183594-1.308594 1.707032-.484375.523437-1.015625.988281-1.589844 1.398437-.574218.410157-1.179687.757813-1.816406 1.039063-.636719.285156-1.292969.5-1.96875.640625-.679688.144531-1.359375.21875-2.050781.21875-.691407 0-1.371094-.074219-2.050781-.21875-.675782-.140625-1.332032-.355469-1.96875-.640625-.636719-.28125-1.242188-.628906-1.816407-1.039063-.574219-.410156-1.105469-.875-1.59375-1.398437-.484375-.523438-.921875-1.089844-1.304687-1.707032-.382813-.613281-.707032-1.261718-.96875-1.945312-.265625-.683594-.464844-1.386719-.597656-2.109375-.136719-.726563-.203126-1.457031-.203126-2.195313zm0 0" fill="#01a0d5" fill-rule="evenodd" /><use fill="#ababab" x="99.487104" xlink:href="#bt" y="540.226953" /><use x="126.615687" xlink:href="#bk" y="588.247125" /><use x="134.737091" xlink:href="#a9" y="588.247125" /><use x="143.01696" xlink:href="#bl" y="588.247125" /><use x="146.569245" xlink:href="#bl" y="588.247125" /><use x="150.121529" xlink:href="#a9" y="588.247125" /><use x="158.203309" xlink:href="#bb" y="588.247125" /><use x="173.27083" xlink:href="#a4" y="588.247125" /><use x="178.632281" xlink:href="#a5" y="588.247125" /><use x="187.598832" xlink:href="#a6" y="588.247125" /><use x="199.034819" xlink:href="#bi" y="588.247125" /><use x="207.988168" xlink:href="#bf" y="588.247125" /><use x="213.111907" xlink:href="#a9" y="588.247125" /><use x="221.391775" xlink:href="#bh" y="588.247125" /><use x="235.40286" xlink:href="#bi" y="588.247125" /><use x="244.356209" xlink:href="#a4" y="588.247125" /><use x="249.717661" xlink:href="#bg" y="588.247125" /><use x="259.63502" xlink:href="#a9" y="588.247125" /><use x="267.914888" xlink:href="#a3" y="588.247125" /><use x="280.314884" xlink:href="#a4" y="588.247125" /><use x="285.676336" xlink:href="#a5" y="588.247125" /><use x="294.642886" xlink:href="#a6" y="588.247125" /><use x="306.078873" xlink:href="#a0" y="588.247125" /><use x="314.200277" xlink:href="#a6" y="588.247125" /><use x="322.176429" xlink:href="#bf" y="588.247125" /><use x="327.669932" xlink:href="#bm" y="588.247125" /><use x="334.827327" xlink:href="#bc" y="588.247125" /><use x="338.3796" xlink:href="#a1" y="588.247125" /><use x="345.695471" xlink:href="#a6" y="588.247125" /><use x="357.131481" xlink:href="#bn" y="588.247125" /><use x="367.867573" xlink:href="#a0" y="588.247125" /><use x="375.988966" xlink:href="#bo" y="588.247125" /><use x="394.120179" xlink:href="#a2" y="588.247125" /><use x="401.911431" xlink:href="#bi" y="588.247125" /><use x="410.864792" xlink:href="#bi" y="588.247125" /><use x="423.278011" xlink:href="#a9" y="588.247125" /><use x="431.557879" xlink:href="#bf" y="588.247125" /><use x="440.313152" xlink:href="#a9" y="588.247125" /><use x="448.59302" xlink:href="#a3" y="588.247125" /><use x="460.993027" xlink:href="#a4" y="588.247125" /><use x="466.354479" xlink:href="#a5" y="588.247125" /><use x="475.321029" xlink:href="#a6" y="588.247125" /><use x="126.615687" xlink:href="#a0" y="604.003744" /><use x="134.737091" xlink:href="#a6" y="604.003744" /><use x="142.713232" xlink:href="#bf" y="604.003744" /><use x="148.206724" xlink:href="#bm" y="604.003744" /><use x="155.36413" xlink:href="#bc" y="604.003744" /><use x="158.916415" xlink:href="#a1" y="604.003744" /><use x="166.232286" xlink:href="#a6" y="604.003744" /><use x="177.668273" xlink:href="#bn" y="604.003744" /><use x="188.404377" xlink:href="#a0" y="604.003744" /><use x="196.525781" xlink:href="#bo" y="604.003744" /><use x="214.656982" xlink:href="#bb" y="604.003744" /><use x="226.066567" xlink:href="#a6" y="604.003744" /><use x="234.042708" xlink:href="#bp" y="604.003744" /><use x="242.996057" xlink:href="#bq" y="604.003744" /><use x="247.353876" xlink:href="#a9" y="604.003744" /><use x="255.633744" xlink:href="#bf" y="604.003744" /><use x="260.823508" xlink:href="#bh" y="604.003744" /><use x="274.834593" xlink:href="#bj" y="604.003744" /><path d="m90.414062 585.246094v-.75c0-.8125.074219-1.617188.222657-2.414063.148437-.800781.371093-1.574219.660156-2.324219.292969-.75.652344-1.464843 1.074219-2.140624.425781-.675782.910156-1.300782 1.449218-1.875.539063-.574219 1.125-1.089844 1.761719-1.539063.632813-.453125 1.304688-.835937 2.011719-1.144531.703125-.3125 1.429688-.546875 2.179688-.707032.75-.15625 1.503906-.234374 2.269531-.234374.761719 0 1.519531.078124 2.269531.234374.75.160157 1.476562.394532 2.179688.707032.707031.308594 1.375.691406 2.011718 1.144531.636719.449219 1.222656.964844 1.761719 1.539063.539063.574218 1.023437 1.199218 1.445313 1.875.425781.675781.785156 1.390624 1.078124 2.140624.289063.75.511719 1.523438.660157 2.324219.148437.796875.222656 1.601563.222656 2.414063v.75c0 .8125-.074219 1.617187-.222656 2.414062-.148438.796875-.371094 1.574219-.660157 2.324219-.292968.75-.652343 1.464844-1.078124 2.140625-.421876.675781-.90625 1.300781-1.445313 1.875s-1.125 1.089844-1.761719 1.539062c-.636718.453126-1.304687.832032-2.011718 1.144532-.703126.3125-1.429688.546875-2.179688.703125-.75.160156-1.507812.238281-2.269531.238281-.765625 0-1.519531-.078125-2.269531-.238281-.75-.15625-1.476563-.390625-2.179688-.703125-.707031-.3125-1.378906-.691406-2.011719-1.144532-.636719-.449218-1.222656-.964843-1.761719-1.539062-.539062-.574219-1.023437-1.199219-1.449218-1.875-.421875-.675781-.78125-1.390625-1.074219-2.140625-.289063-.75-.511719-1.527344-.660156-2.324219-.148438-.796875-.222657-1.601562-.222657-2.414062zm0 0" fill="#fff" /><path d="m89.289062 585.246094v-.75c0-.886719.082032-1.765625.242188-2.636719.164062-.867187.40625-1.714844.726562-2.53125.320313-.820313.714844-1.597656 1.179688-2.335937.464844-.738282.992188-1.417969 1.585938-2.046876.59375-.625 1.238281-1.1875 1.933593-1.679687.695313-.492187 1.429688-.910156 2.203125-1.246094.773438-.339843 1.574219-.597656 2.394532-.769531.820312-.171875 1.652343-.261719 2.488281-.261719.835937 0 1.667969.089844 2.488281.261719s1.617188.429688 2.394531.769531c.773438.335938 1.507813.753907 2.203125 1.246094.695313.492187 1.339844 1.054687 1.933594 1.679687.59375.628907 1.121094 1.308594 1.585938 2.046876.464843.738281.859374 1.515624 1.179687 2.335937.320313.816406.5625 1.664063.726563 2.53125.160156.871094.242187 1.75.242187 2.636719v.75c0 .886718-.082031 1.765625-.242187 2.632812-.164063.871094-.40625 1.714844-.726563 2.535156-.320313.820313-.714844 1.597657-1.179687 2.335938-.464844.738281-.992188 1.417969-1.585938 2.046875-.59375.625-1.238281 1.1875-1.933594 1.679687-.695312.492188-1.429687.90625-2.203125 1.246094-.777343.339844-1.574219.597656-2.394531.769532-.820312.171874-1.652344.257812-2.488281.257812-.835938 0-1.667969-.085938-2.488281-.257812-.820313-.171876-1.621094-.429688-2.394532-.769532-.773437-.339844-1.507812-.753906-2.203125-1.246094-.695312-.492187-1.339843-1.054687-1.933593-1.679687-.59375-.628906-1.121094-1.308594-1.585938-2.046875s-.859375-1.515625-1.179688-2.335938c-.320312-.820312-.5625-1.664062-.726562-2.535156-.160156-.867187-.242188-1.746094-.242188-2.632812zm2.25 0v-.75c0-.738282.066407-1.472656.203126-2.195313.132812-.726562.332031-1.429687.597656-2.113281.261718-.683594.585937-1.332031.96875-1.945312.382812-.613282.820312-1.183594 1.304687-1.703126.488281-.523437 1.019531-.992187 1.59375-1.402343.574219-.410157 1.179688-.757813 1.816407-1.039063.636718-.28125 1.292968-.496094 1.96875-.640625.679687-.144531 1.359374-.214843 2.050781-.214843.691406 0 1.371093.070312 2.050781.214843.675781.144531 1.332031.359375 1.96875.640625s1.242188.628906 1.816406 1.039063c.574219.410156 1.105469.878906 1.589844 1.402343.488281.519532.925781 1.089844 1.308594 1.703126.382812.613281.707031 1.261718.96875 1.945312.265625.683594.464844 1.386719.597656 2.113281.136719.722657.203125 1.457031.203125 2.195313v.75c0 .738281-.066406 1.46875-.203125 2.195312-.132812.726563-.332031 1.429688-.597656 2.113282-.261719.679687-.585938 1.328124-.96875 1.945312-.382813.613281-.820313 1.179688-1.308594 1.703125-.484375.523437-1.015625.988281-1.589844 1.402344-.574218.410156-1.179687.753906-1.816406 1.039062-.636719.28125-1.292969.496094-1.96875.640625-.679688.144532-1.359375.214844-2.050781.214844-.691407 0-1.371094-.070312-2.050781-.214844-.675782-.144531-1.332032-.359375-1.96875-.640625-.636719-.285156-1.242188-.628906-1.816407-1.039062-.574219-.414063-1.105469-.878907-1.59375-1.402344-.484375-.523437-.921875-1.089844-1.304687-1.703125-.382813-.617188-.707032-1.265625-.96875-1.945312-.265625-.683594-.464844-1.386719-.597656-2.113282-.136719-.726562-.203126-1.457031-.203126-2.195312zm0 0" fill="#01a0d5" fill-rule="evenodd" /><use fill="#ababab" x="98.162329" xlink:href="#bu" y="589.747755" /><use x="126.615687" xlink:href="#a0" y="637.767927" /><use x="134.737091" xlink:href="#a5" y="637.767927" /><use x="143.703642" xlink:href="#a9" y="637.767927" /><use x="151.785421" xlink:href="#bb" y="637.767927" /><use x="166.852942" xlink:href="#a2" y="637.767927" /><use x="178.104052" xlink:href="#bg" y="637.767927" /><use x="184.561565" xlink:href="#a4" y="637.767927" /><use x="189.923017" xlink:href="#a2" y="637.767927" /><use x="197.714279" xlink:href="#bq" y="637.767927" /><use x="202.190948" xlink:href="#bq" y="637.767927" /><use x="210.127464" xlink:href="#bh" y="637.767927" /><use x="224.16495" xlink:href="#a6" y="637.767927" /><use x="232.141091" xlink:href="#bh" y="637.767927" /><use x="246.152176" xlink:href="#bp" y="637.767927" /><use x="255.105525" xlink:href="#a6" y="637.767927" /><use x="263.081666" xlink:href="#bf" y="637.767927" /><use x="271.836926" xlink:href="#a4" y="637.767927" /><use x="277.198378" xlink:href="#a5" y="637.767927" /><use x="286.138527" xlink:href="#a2" y="637.767927" /><use x="293.929791" xlink:href="#a4" y="637.767927" /><use x="302.751089" xlink:href="#bd" y="637.767927" /><use x="309.710407" xlink:href="#a9" y="637.767927" /><use x="317.990275" xlink:href="#be" y="637.767927" /><use x="326.877599" xlink:href="#br" y="637.767927" /><use x="329.914855" xlink:href="#bm" y="637.767927" /><use x="336.874161" xlink:href="#a6" y="637.767927" /><use x="348.310148" xlink:href="#bg" y="637.767927" /><use x="354.767649" xlink:href="#bc" y="637.767927" /><use x="358.319922" xlink:href="#bs" y="637.767927" /><use x="367.378934" xlink:href="#a3" y="637.767927" /><use x="376.345484" xlink:href="#a6" y="637.767927" /><use x="384.321613" xlink:href="#ba" y="637.767927" /><use x="396.734832" xlink:href="#bc" y="637.767927" /><use x="400.287106" xlink:href="#a3" y="637.767927" /><use x="409.227255" xlink:href="#bj" y="637.767927" /><path d="m90.414062 634.765625v-.75c0-.8125.074219-1.617187.222657-2.414063.148437-.796874.371093-1.570312.660156-2.324218.292969-.75.652344-1.464844 1.074219-2.140625.425781-.675781.910156-1.300781 1.449218-1.875.539063-.574219 1.125-1.085938 1.761719-1.539063.632813-.453125 1.304688-.832031 2.011719-1.144531.703125-.3125 1.429688-.546875 2.179688-.703125.75-.160156 1.503906-.238281 2.269531-.238281.761719 0 1.519531.078125 2.269531.238281.75.15625 1.476562.390625 2.179688.703125.707031.3125 1.375.691406 2.011718 1.144531.636719.453125 1.222656.964844 1.761719 1.539063s1.023437 1.199219 1.445313 1.875c.425781.675781.785156 1.390625 1.078124 2.140625.289063.753906.511719 1.527344.660157 2.324218.148437.796876.222656 1.601563.222656 2.414063v.75c0 .8125-.074219 1.617187-.222656 2.417969-.148438.796875-.371094 1.570312-.660157 2.320312-.292968.75-.652343 1.464844-1.078124 2.140625-.421876.675781-.90625 1.300781-1.445313 1.875s-1.125 1.089844-1.761719 1.539063c-.636718.453125-1.304687.835937-2.011718 1.144531-.703126.3125-1.429688.546875-2.179688.707031-.75.15625-1.507812.238282-2.269531.238282-.765625 0-1.519531-.082032-2.269531-.238282-.75-.160156-1.476563-.394531-2.179688-.707031-.707031-.308594-1.378906-.691406-2.011719-1.144531-.636719-.449219-1.222656-.964844-1.761719-1.539063-.539062-.574219-1.023437-1.199219-1.449218-1.875-.421875-.675781-.78125-1.390625-1.074219-2.140625-.289063-.75-.511719-1.523437-.660156-2.320312-.148438-.800782-.222657-1.605469-.222657-2.417969zm0 0" fill="#fff" /><path d="m89.289062 634.765625v-.75c0-.886719.082032-1.765625.242188-2.632813.164062-.871093.40625-1.714843.726562-2.535156.320313-.820312.714844-1.597656 1.179688-2.335937.464844-.734375.992188-1.417969 1.585938-2.046875.59375-.625 1.238281-1.183594 1.933593-1.679688.695313-.492187 1.429688-.90625 2.203125-1.246094.773438-.339843 1.574219-.597656 2.394532-.769531.820312-.171875 1.652343-.257812 2.488281-.257812.835937 0 1.667969.085937 2.488281.257812s1.617188.429688 2.394531.769531c.773438.339844 1.507813.753907 2.203125 1.246094.695313.496094 1.339844 1.054688 1.933594 1.679688.59375.628906 1.121094 1.3125 1.585938 2.046875.464843.738281.859374 1.515625 1.179687 2.335937.320313.820313.5625 1.664063.726563 2.535156.160156.867188.242187 1.746094.242187 2.632813v.75c0 .886719-.082031 1.765625-.242187 2.636719-.164063.867187-.40625 1.714844-.726563 2.53125-.320313.820312-.714844 1.597656-1.179687 2.335937-.464844.738281-.992188 1.421875-1.585938 2.046875-.59375.628906-1.238281 1.1875-1.933594 1.679688-.695312.492187-1.429687.910156-2.203125 1.246094-.777343.339843-1.574219.597656-2.394531.769531-.820312.175781-1.652344.261719-2.488281.261719-.835938 0-1.667969-.085938-2.488281-.261719-.820313-.171875-1.621094-.429688-2.394532-.769531-.773437-.335938-1.507812-.753907-2.203125-1.246094-.695312-.492188-1.339843-1.050782-1.933593-1.679688-.59375-.625-1.121094-1.308594-1.585938-2.046875s-.859375-1.515625-1.179688-2.335937c-.320312-.816406-.5625-1.664063-.726562-2.53125-.160156-.871094-.242188-1.75-.242188-2.636719zm2.25 0v-.75c0-.738281.066407-1.46875.203126-2.195313.132812-.722656.332031-1.429687.597656-2.109374.261718-.683594.585937-1.332032.96875-1.949219.382812-.613281.820312-1.179688 1.304687-1.703125.488281-.523438 1.019531-.988282 1.59375-1.398438.574219-.414062 1.179688-.757812 1.816407-1.042968.636718-.28125 1.292968-.496094 1.96875-.640626.679687-.144531 1.359374-.214843 2.050781-.214843.691406 0 1.371093.070312 2.050781.214843.675781.144532 1.332031.359376 1.96875.640626.636719.285156 1.242188.628906 1.816406 1.042968.574219.410156 1.105469.875 1.589844 1.398438.488281.523437.925781 1.089844 1.308594 1.703125.382812.617187.707031 1.265625.96875 1.949219.265625.679687.464844 1.386718.597656 2.109374.136719.726563.203125 1.457032.203125 2.195313v.75c0 .738281-.066406 1.472656-.203125 2.195313-.132812.726562-.332031 1.429687-.597656 2.113281-.261719.683593-.585938 1.332031-.96875 1.945312-.382813.613281-.820313 1.183594-1.308594 1.707031-.484375.519532-1.015625.988282-1.589844 1.398438-.574218.410156-1.179687.757812-1.816406 1.039062-.636719.285157-1.292969.496094-1.96875.640626-.679688.144531-1.359375.214843-2.050781.214843-.691407 0-1.371094-.070312-2.050781-.214843-.675782-.144532-1.332032-.355469-1.96875-.640626-.636719-.28125-1.242188-.628906-1.816407-1.039062s-1.105469-.878906-1.59375-1.398438c-.484375-.523437-.921875-1.09375-1.304687-1.707031-.382813-.613281-.707032-1.261719-.96875-1.945312-.265625-.683594-.464844-1.386719-.597656-2.113281-.136719-.722657-.203126-1.457032-.203126-2.195313zm0 0" fill="#01a0d5" fill-rule="evenodd" /><use fill="#ababab" x="98.162329" xlink:href="#bv" y="639.268557" /></g>
<g id="qr-code-v13-Q-original" transform="scale(14.4927536232)">
<rect fill="#fff" height="69" width="69" /><path d="m67 59v1h1v-1zm-8-4v1h1v-1zm-9-49v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm-14 65v1h1v-1zm-20-58v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-5 34v1h1v-1zm15 16v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm53 12v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm11 9v1h1v-1zm-34 7v1h1v-1zm32 32v1h1v-1zm1-35v1h1v-1zm15 16v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm11 46v1h1v-1zm20-22v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm-5 34v1h1v-1zm19 13v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm-37-31v1h1v-1zm-1 41v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm11 46v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm52 47v1h1v-1zm-7-39v1h1v-1zm-35 42v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-7-39v1h1v-1zm-31 49v1h1v-1zm32-48v1h1v-1zm11 9v1h1v-1zm-50-1v1h1v-1zm52 47v1h1v-1zm-20-58v1h1v-1zm-10 35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm66-18v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-42 3v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm56 17v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-27 46v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-49-5v1h1v-1zm-1 4v1h1v-1zm65 17v1h1v-1zm1-35v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm33-3v1h1v-1zm-49-5v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm32 32v1h1v-1zm-21-23v1h1v-1zm-42 3v1h1v-1zm1-35v1h1v-1zm57 13v1h1v-1zm-5 34v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm-28 18v1h1v-1zm51 2v1h1v-1zm-42 3v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm58-22v1h1v-1zm-54-8v1h1v-1zm12 11v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm40 36v1h1v-1zm-34 7v1h1v-1zm25-56v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm52 47v1h1v-1zm-28-19v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm39-9v1h1v-1zm-7-39v1h1v-1zm-28 18v1h1v-1zm47-5v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm-42 3v1h1v-1zm66-18v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm41 1v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm11 9v1h1v-1zm-21 26v1h1v-1zm32-11v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-62 25v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm31 24v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-9 37v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-49-5v1h1v-1zm40 36v1h1v-1zm12-26v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm13-61v1h1v-1zm11 46v1h1v-1zm20-22v1h1v-1zm-62 25v1h1v-1zm19 13v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm10 50v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-30 8v1h1v-1zm-13 30v1h1v-1zm25-56v1h1v-1zm40 36v1h1v-1zm-22 18v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm11 9v1h1v-1zm30 28v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-3 31v1h1v-1zm-28 18v1h1v-1zm21-57v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm-61-10v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm44 43v1h1v-1zm-42 3v1h1v-1zm-21-23v1h1v-1zm60 14v1h1v-1zm-48-40v1h1v-1zm-8-4v1h1v-1zm60 14v1h1v-1zm-21-23v1h1v-1zm-33 52v1h1v-1zm24-15v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm-7-39v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm33 34v1h1v-1zm-29-64v1h1v-1zm-22 18v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-13 30v1h1v-1zm25-56v1h1v-1zm-8-4v1h1v-1zm18 54v1h1v-1zm19 13v1h1v-1zm-26-52v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm-28 18v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm19 50v1h1v-1zm33-3v1h1v-1zm-49-42v1h1v-1zm19 50v1h1v-1zm-1 4v1h1v-1zm-21-23v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm18 54v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-35-21v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm12 11v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm12-26v1h1v-1zm6 43v1h1v-1zm-48-40v1h1v-1zm60 51v1h1v-1zm-49-42v1h1v-1zm-1 41v1h1v-1zm46-33v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm31-13v1h1v-1zm-54 29v1h1v-1zm15 16v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-5 34v1h1v-1zm-49-5v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm10 50v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-7-39v1h1v-1zm-35 42v1h1v-1zm25-56v1h1v-1zm33 34v1h1v-1zm-23 16v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm12-26v1h1v-1zm-62 25v1h1v-1zm20-22v1h1v-1zm45 8v1h1v-1zm-30 8v1h1v-1zm-5 34v1h1v-1zm25-56v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-28-62v1h1v-1zm3 5v1h1v-1zm41 38v1h1v-1zm4-30v1h1v-1zm-27 46v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-50-1v1h1v-1zm60 51v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-46 33v1h1v-1zm12-26v1h1v-1zm24-21v1h1v-1zm-34 7v1h1v-1zm33-3v1h1v-1zm11 46v1h1v-1zm-53-6v1h1v-1zm3 5v1h1v-1zm13-61v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm53 12v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-37-31v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm52 47v1h1v-1zm12-26v1h1v-1zm-34 7v1h1v-1zm-28 18v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm12 11v1h1v-1zm-1 4v1h1v-1zm-30 8v1h1v-1zm-7-39v1h1v-1zm40 36v1h1v-1zm-55-16v1h1v-1zm4-30v1h1v-1zm52 47v1h1v-1zm-58-5v1h1v-1zm39-9v1h1v-1zm-29-27v1h1v-1zm-13 30v1h1v-1zm46-33v1h1v-1zm-34 44v1h1v-1zm46-33v1h1v-1zm-2 39v1h1v-1zm-21-23v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm19 13v1h1v-1zm-5 7v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm19 13v1h1v-1zm17-60v1h1v-1zm-34 7v1h1v-1zm41 1v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-23 53v1h1v-1zm-16-8v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm18 54v1h1v-1zm12-26v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-20-58v1h1v-1zm32 32v1h1v-1zm-55-16v1h1v-1zm56 17v1h1v-1zm-8-4v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-26-52v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm47-5v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-1 41v1h1v-1zm-21-23v1h1v-1zm4-30v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm32 32v1h1v-1zm-53-6v1h1v-1zm-10-14v1h1v-1zm13 19v1h1v-1zm20 15v1h1v-1zm23-17v1h1v-1zm1-35v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-15 20v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-31 49v1h1v-1zm1-35v1h1v-1zm12 54v1h1v-1zm20-22v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm-9-49v1h1v-1zm33 34v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-49-5v1h1v-1zm33-3v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm4-30v1h1v-1zm11 46v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm60 14v1h1v-1zm-49-5v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm44 43v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm65-20v1h1v-1zm-61-10v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm12-26v1h1v-1zm20 15v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm10 50v1h1v-1zm-28-62v1h1v-1zm12 54v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-62 25v1h1v-1zm65-20v1h1v-1zm-42 3v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-16-8v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm18 48v1h1v-1zm12-26v1h1v-1zm-9 37v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm-29-27v1h1v-1zm40 36v1h1v-1zm20-22v1h1v-1zm-62 25v1h1v-1zm20-22v1h1v-1zm-21-23v1h1v-1zm52 47v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-1 41v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm60 14v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm12-26v1h1v-1zm-23 53v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm21-57v1h1v-1zm-8-4v1h1v-1zm-55 64v1h1v-1zm20-22v1h1v-1zm45 8v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-2 39v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-30 8v1h1v-1zm12-26v1h1v-1zm-9 37v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-29-27v1h1v-1zm40 36v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-53-6v1h1v-1zm19 13v1h1v-1zm3 42v1h1v-1zm13-61v1h1v-1zm-14 65v1h1v-1zm25-56v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm41 38v1h1v-1zm-49-42v1h1v-1zm41 38v1h1v-1zm-42 3v1h1v-1zm24-52v1h1v-1zm-35 42v1h1v-1zm38-37v1h1v-1zm-34 7v1h1v-1zm11 46v1h1v-1zm33-3v1h1v-1zm-21-23v1h1v-1zm11 9v1h1v-1zm-7-39v1h1v-1zm6 43v1h1v-1zm12-26v1h1v-1zm-29-27v1h1v-1zm33 34v1h1v-1zm-42 3v1h1v-1zm46-33v1h1v-1zm-8 59v1h1v-1zm-27-17v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-31 49v1h1v-1zm11 9v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-62 25v1h1v-1zm20-22v1h1v-1zm-9 37v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-30 8v1h1v-1zm41 38v1h1v-1zm-54-8v1h1v-1zm12 11v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm39-9v1h1v-1zm-54 29v1h1v-1zm38-37v1h1v-1zm-34 7v1h1v-1zm44 43v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm-5 34v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm4-30v1h1v-1zm12 11v1h1v-1zm-43 38v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm4 7v1h1v-1zm53-25v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm-46 33v1h1v-1zm25-56v1h1v-1zm-2 39v1h1v-1zm12-26v1h1v-1zm11 46v1h1v-1zm-22-19v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm10 50v1h1v-1zm25-56v1h1v-1zm-54 29v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm-9 31v1h1v-1zm-20-58v1h1v-1zm52 47v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm-23 16v1h1v-1zm46-33v1h1v-1zm20 15v1h1v-1zm-62-12v1h1v-1zm41 38v1h1v-1zm-29-27v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm59 49v1h1v-1zm-20-58v1h1v-1zm-8-4v1h1v-1zm-14 65v1h1v-1zm32-11v1h1v-1zm4-30v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm24-52v1h1v-1zm31 24v1h1v-1zm-54 29v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-9 37v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm-7-39v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm12 11v1h1v-1zm12-26v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm11 15v1h1v-1zm31-13v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-53-6v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm12 11v1h1v-1zm23-17v1h1v-1zm1-35v1h1v-1zm-51 34v1h1v-1zm19 13v1h1v-1zm-26-52v1h1v-1zm39-9v1h1v-1zm-39 45v1h1v-1zm20-22v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm16-19v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm11 9v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm31 24v1h1v-1zm-34 7v1h1v-1zm13 19v1h1v-1zm27 17v1h1v-1zm-7-39v1h1v-1zm-21-23v1h1v-1zm12 54v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-42 3v1h1v-1zm-21-23v1h1v-1zm33 34v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm20-22v1h1v-1zm-55 64v1h1v-1zm24-52v1h1v-1zm3 5v1h1v-1zm20 15v1h1v-1zm12-26v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-35 42v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm18 54v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm-27-17v1h1v-1zm31-13v1h1v-1zm-54 29v1h1v-1zm12-26v1h1v-1zm20 15v1h1v-1zm-28-19v1h1v-1zm32-11v1h1v-1zm11 9v1h1v-1zm-53-6v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-9 31v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm33 34v1h1v-1zm-42 3v1h1v-1zm39-9v1h1v-1zm-27-17v1h1v-1zm20-22v1h1v-1zm-49-5v1h1v-1zm6 43v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm15 16v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm19 13v1h1v-1zm-53-6v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm45 8v1h1v-1zm-3-6v1h1v-1zm-54 29v1h1v-1zm38-37v1h1v-1zm12-26v1h1v-1zm-15 57v1h1v-1zm12-26v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-55 64v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm46-33v1h1v-1zm-53-6v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm41 38v1h1v-1zm-23 16v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm11 46v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm32-11v1h1v-1zm-22 61v1h1v-1zm-21-23v1h1v-1zm12-26v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-30 8v1h1v-1zm41 38v1h1v-1zm4-30v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm19-50v1h1v-1zm29 63v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm-5 34v1h1v-1zm-29-27v1h1v-1zm40 36v1h1v-1zm25-56v1h1v-1zm-54 29v1h1v-1zm3 11v1h1v-1zm19 13v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-30-23v1h1v-1zm11 46v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm65-14v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm20-22v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm11 46v1h1v-1zm41 1v1h1v-1zm-30 8v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-1-45v1h1v-1zm-54 29v1h1v-1zm4-30v1h1v-1zm44 43v1h1v-1zm12-26v1h1v-1zm-53-6v1h1v-1zm19 13v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm-54-8v1h1v-1zm11 46v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm10 50v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm25-56v1h1v-1zm-33 52v1h1v-1zm39-9v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-1-45v1h1v-1zm13 19v1h1v-1zm31 24v1h1v-1zm-53-6v1h1v-1zm11 9v1h1v-1zm-7-39v1h1v-1zm18 48v1h1v-1zm-26-52v1h1v-1zm46 30v1h1v-1zm-8-4v1h1v-1zm11 15v1h1v-1zm-49-5v1h1v-1zm33-3v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm20-22v1h1v-1zm-49-5v1h1v-1zm52 47v1h1v-1zm-50-1v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm46-33v1h1v-1zm12 11v1h1v-1zm-13 30v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-49-5v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-20-58v1h1v-1zm-33 52v1h1v-1zm12-26v1h1v-1zm3 5v1h1v-1zm20 15v1h1v-1zm12-26v1h1v-1zm-54 29v1h1v-1zm11 9v1h1v-1zm13-61v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm-11 27v1h1v-1zm33-3v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm33 34v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-16-8v1h1v-1zm31 24v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm46-33v1h1v-1zm-62 25v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-55-16v1h1v-1zm33 34v1h1v-1zm-29-64v1h1v-1zm41 38v1h1v-1zm19 13v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm-9 31v1h1v-1zm12-26v1h1v-1zm-54 29v1h1v-1zm-1-45v1h1v-1zm13 19v1h1v-1zm11 9v1h1v-1zm12 11v1h1v-1zm12-26v1h1v-1zm-34 7v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-29-27v1h1v-1zm12-26v1h1v-1zm-1 41v1h1v-1zm-21-23v1h1v-1zm32 32v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm24-15v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm10 44v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm30 28v1h1v-1zm12-26v1h1v-1zm-49-5v1h1v-1zm40 36v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-54 29v1h1v-1zm66-55v1h1v-1zm-54 29v1h1v-1zm31 24v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-55 64v1h1v-1zm46-33v1h1v-1zm19 13v1h1v-1zm-61-10v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm33 34v1h1v-1zm-34 7v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm11 9v1h1v-1zm-27-17v1h1v-1zm60 14v1h1v-1zm-11 27v1h1v-1zm-7-39v1h1v-1zm18 48v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm-54 29v1h1v-1zm4-30v1h1v-1zm41 38v1h1v-1zm-21-23v1h1v-1zm12-26v1h1v-1zm-33 52v1h1v-1zm25-56v1h1v-1zm-34 7v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-1-45v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm30 28v1h1v-1zm-26-52v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-53-6v1h1v-1zm65-20v1h1v-1zm-54 29v1h1v-1zm32-11v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm4-30v1h1v-1zm18 54v1h1v-1zm-27-17v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm4-30v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm31 24v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm-5 34v1h1v-1zm25-56v1h1v-1zm-55 64v1h1v-1zm12-26v1h1v-1zm53 12v1h1v-1zm-50-1v1h1v-1zm19 13v1h1v-1zm-28-62v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-23 53v1h1v-1zm33-3v1h1v-1zm-21-23v1h1v-1zm12-26v1h1v-1zm11 9v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-5 34v1h1v-1zm-37-31v1h1v-1zm53 12v1h1v-1zm-29-27v1h1v-1zm-10 35v1h1v-1zm-23 16v1h1v-1zm12 11v1h1v-1zm4-30v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm38-37v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm12 11v1h1v-1zm-9 31v1h1v-1zm41 1v1h1v-1zm1-35v1h1v-1zm-30 8v1h1v-1zm-33 52v1h1v-1zm47-5v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-21-23v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm4-30v1h1v-1zm-16 55v1h1v-1zm-26-52v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-55 64v1h1v-1zm47-68v1h1v-1zm-42 3v1h1v-1zm43-2v1h1v-1zm-33 52v1h1v-1zm19 13v1h1v-1zm-29-27v1h1v-1zm46-33v1h1v-1zm-33 52v1h1v-1zm-20-58v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm12 11v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm-15-43v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-34 7v1h1v-1zm11 46v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm45-29v1h1v-1zm3 42v1h1v-1zm-34 7v1h1v-1zm-7-39v1h1v-1zm-9 31v1h1v-1zm47-5v1h1v-1zm-27-17v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm18 54v1h1v-1zm-30 8v1h1v-1zm47-68v1h1v-1zm-14 65v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-16 55v1h1v-1zm37 4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-54 29v1h1v-1zm25-56v1h1v-1zm41 1v1h1v-1zm-34 7v1h1v-1zm-19 23v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm33-3v1h1v-1zm-21-23v1h1v-1zm-41-32v1h1v-1zm36 39v1h1v-1zm19 13v1h1v-1zm-53-6v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm19 13v1h1v-1zm-20-58v1h1v-1zm41 1v1h1v-1zm-28 18v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm11 46v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-2 39v1h1v-1zm-48-40v1h1v-1zm39-9v1h1v-1zm-54 29v1h1v-1zm4-30v1h1v-1zm60 51v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-21-23v1h1v-1zm-22 61v1h1v-1zm1-35v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-1 41v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-33 52v1h1v-1zm25-56v1h1v-1zm40 36v1h1v-1zm-42 3v1h1v-1zm-21-23v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm18 48v1h1v-1zm-30 14v1h1v-1zm33-3v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm13 19v1h1v-1zm-34 44v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm19 13v1h1v-1zm-26-52v1h1v-1zm19 13v1h1v-1zm-43 38v1h1v-1zm21-57v1h1v-1zm-28 18v1h1v-1zm31 24v1h1v-1zm20-22v1h1v-1zm-62 25v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-22-19v1h1v-1zm33 34v1h1v-1zm12-26v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-27-17v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm46-33v1h1v-1zm-41-32v1h1v-1zm31 24v1h1v-1zm-28-19v1h1v-1zm31 24v1h1v-1zm-42 3v1h1v-1zm41 1v1h1v-1zm-19 23v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-53-6v1h1v-1zm19 13v1h1v-1zm15 16v1h1v-1zm20-22v1h1v-1zm1-35v1h1v-1zm-55 64v1h1v-1zm46-33v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-1 41v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm32 32v1h1v-1zm20-22v1h1v-1zm-62 25v1h1v-1zm64 21v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm41 38v1h1v-1zm-50-1v1h1v-1zm46-33v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm11 46v1h1v-1zm-22-19v1h1v-1zm-7-39v1h1v-1zm18 48v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-22 18v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm-10 35v1h1v-1zm-21-23v1h1v-1zm12-26v1h1v-1zm44 43v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm-42 3v1h1v-1zm-8-4v1h1v-1zm11 46v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-5 34v1h1v-1zm-8-4v1h1v-1zm25-56v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm32 32v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm41 38v1h1v-1zm-42 3v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-7-39v1h1v-1zm-29-27v1h1v-1zm52 10v1h1v-1zm-31 49v1h1v-1zm41 1v1h1v-1zm-9-49v1h1v-1zm-33 52v1h1v-1zm25-56v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-53-6v1h1v-1zm19 13v1h1v-1zm44 43v1h1v-1zm-50-1v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-15-43v1h1v-1zm-34 7v1h1v-1zm3 42v1h1v-1zm20-22v1h1v-1zm12-26v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-27-17v1h1v-1zm14 47v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm44 43v1h1v-1zm-53-6v1h1v-1zm38 26v1h1v-1zm-27-17v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm31 24v1h1v-1zm-49-42v1h1v-1zm41 38v1h1v-1zm-42 3v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm12-26v1h1v-1zm-50-1v1h1v-1zm52 47v1h1v-1zm-8-4v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-35 42v1h1v-1zm-8-4v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm4-30v1h1v-1zm19 50v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-62-55v1h1v-1zm36 39v1h1v-1zm19 13v1h1v-1zm-17-53v1h1v-1zm-33 52v1h1v-1zm-9-49v1h1v-1zm60 51v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm1-35v1h1v-1zm-55 64v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm-27-17v1h1v-1zm-23 16v1h1v-1zm41 38v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm32 32v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm23 20v1h1v-1zm12-26v1h1v-1zm-62 25v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm39-9v1h1v-1zm12 11v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-35 42v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm-2 39v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm44 43v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-35 42v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-28-62v1h1v-1zm41 1v1h1v-1zm-42 3v1h1v-1zm19 50v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm29 63v1h1v-1zm-27-17v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-31 12v1h1v-1zm11 9v1h1v-1zm32-11v1h1v-1zm-54 29v1h1v-1zm12-26v1h1v-1zm46-33v1h1v-1zm-55 64v1h1v-1zm1-35v1h1v-1zm4-30v1h1v-1zm37 67v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-20-58v1h1v-1zm32 32v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm19 50v1h1v-1zm-28-19v1h1v-1zm20 15v1h1v-1zm20-22v1h1v-1zm-28-19v1h1v-1zm12 11v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm-26-52v1h1v-1zm19 13v1h1v-1zm11 15v1h1v-1zm-21-23v1h1v-1zm12 54v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm25-56v1h1v-1zm-33 52v1h1v-1zm20 15v1h1v-1zm-8-41v1h1v-1zm12 11v1h1v-1zm19 13v1h1v-1zm4-30v1h1v-1zm-54 29v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm33 34v1h1v-1zm-9-49v1h1v-1zm-54 29v1h1v-1zm41 1v1h1v-1zm19 13v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm33 34v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm24-15v1h1v-1zm-8-4v1h1v-1zm6 43v1h1v-1zm-8-4v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-46 33v1h1v-1zm25-56v1h1v-1zm-8-4v1h1v-1zm18 54v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-49-5v1h1v-1zm40 36v1h1v-1zm-53-6v1h1v-1zm19 13v1h1v-1zm24-15v1h1v-1zm-28-19v1h1v-1zm20 15v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-54 29v1h1v-1zm-7-39v1h1v-1zm18 48v1h1v-1zm45 8v1h1v-1zm-34 7v1h1v-1zm-17-53v1h1v-1zm41 1v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm52 47v1h1v-1zm-20-58v1h1v-1zm-43 38v1h1v-1zm65-20v1h1v-1zm-53-6v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm19 13v1h1v-1zm4-30v1h1v-1zm-21 63v1h1v-1zm-1-45v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm12-26v1h1v-1zm-18-12v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm46-33v1h1v-1zm-50-1v1h1v-1zm20 15v1h1v-1zm-11-10v1h1v-1zm-16-8v1h1v-1zm-5 34v1h1v-1zm57 13v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm11 9v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm20 15v1h1v-1zm32 32v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm-29-27v1h1v-1zm46-33v1h1v-1zm-33 52v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20 15v1h1v-1zm-49-42v1h1v-1zm-13 30v1h1v-1zm12 11v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm60 51v1h1v-1zm-29-27v1h1v-1zm1-35v1h1v-1zm-14 65v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm25-56v1h1v-1zm-35 42v1h1v-1zm20-22v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm12 11v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-30-23v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm57 13v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm33-3v1h1v-1zm-29-27v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm14 47v1h1v-1zm-25-57v1h1v-1zm52 47v1h1v-1zm-53-6v1h1v-1zm19 13v1h1v-1zm19 13v1h1v-1zm-35-21v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm-42 3v1h1v-1zm12 11v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-31 12v1h1v-1zm-20-58v1h1v-1zm-8-4v1h1v-1zm60 51v1h1v-1zm-50-1v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm58-22v1h1v-1zm6 43v1h1v-1zm-37-31v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm44 43v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-21-23v1h1v-1zm-33 52v1h1v-1zm39-9v1h1v-1zm-7-39v1h1v-1zm-30 8v1h1v-1zm41 1v1h1v-1zm-31 49v1h1v-1zm-8-4v1h1v-1zm39-9v1h1v-1zm-27-17v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm41 38v1h1v-1zm12-26v1h1v-1zm-49-5v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-35 42v1h1v-1zm27-10v1h1v-1zm12-26v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-42 40v1h1v-1zm46-33v1h1v-1zm-13 30v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm10 44v1h1v-1zm-62-55v1h1v-1zm44 43v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm52 47v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm18 54v1h1v-1zm-27-17v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm10 44v1h1v-1zm12 11v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm66-55v1h1v-1zm-55 64v1h1v-1zm21-57v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-23 16v1h1v-1zm33 34v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-24 51v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-35 42v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm19 13v1h1v-1zm-26-52v1h1v-1zm47-5v1h1v-1zm-50-1v1h1v-1zm33 34v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-7-39v1h1v-1zm18 48v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-43 38v1h1v-1zm53 12v1h1v-1zm-62-61v1h1v-1zm13 19v1h1v-1zm28 19v1h1v-1zm-8-4v1h1v-1zm-23 16v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm18 48v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm30 28v1h1v-1zm-27-17v1h1v-1zm-29-27v1h1v-1zm60 14v1h1v-1zm-54 29v1h1v-1zm-8-4v1h1v-1zm66-55v1h1v-1zm-67 10v1h1v-1zm44 43v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-50-1v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm3 5v1h1v-1zm12 11v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-30 8v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-30 14v1h1v-1zm11 9v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-53-6v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-42 3v1h1v-1zm-8-4v1h1v-1zm41 38v1h1v-1zm-21-23v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm52 47v1h1v-1zm-34 7v1h1v-1zm31-13v1h1v-1zm-27-17v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-34 7v1h1v-1zm-7-39v1h1v-1zm31 24v1h1v-1zm33-3v1h1v-1zm-30 8v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm11 9v1h1v-1zm12-26v1h1v-1zm33 34v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm12-26v1h1v-1zm31 24v1h1v-1zm-28 18v1h1v-1zm41 1v1h1v-1zm-34 7v1h1v-1zm-10-14v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm53 12v1h1v-1zm-8-4v1h1v-1zm4-30v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm-7-39v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-46 33v1h1v-1zm56 17v1h1v-1zm-29-27v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm-41-32v1h1v-1zm11 9v1h1v-1zm52 47v1h1v-1zm1-35v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-43 38v1h1v-1zm20-22v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm-10-14v1h1v-1zm45 8v1h1v-1zm-5 34v1h1v-1zm-30 8v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-17-53v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm60 51v1h1v-1zm-28-62v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm32 32v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm-50-1v1h1v-1zm19 13v1h1v-1zm-22-19v1h1v-1zm36 39v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-5 34v1h1v-1zm11 9v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm-1-45v1h1v-1zm-8-4v1h1v-1zm53 12v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-16 55v1h1v-1zm12-26v1h1v-1zm-54 29v1h1v-1zm47-68v1h1v-1zm-42 3v1h1v-1zm44 43v1h1v-1zm-21-23v1h1v-1zm11 9v1h1v-1zm-13 30v1h1v-1zm25-56v1h1v-1zm-33 52v1h1v-1zm11 9v1h1v-1zm-20-58v1h1v-1zm52 47v1h1v-1zm-8-4v1h1v-1zm-41-32v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm39-9v1h1v-1zm-8-4v1h1v-1zm12 11v1h1v-1zm-13 30v1h1v-1zm-30 8v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm39-9v1h1v-1zm-42 3v1h1v-1zm52 47v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm-7-39v1h1v-1zm-54 29v1h1v-1zm25-56v1h1v-1zm41 1v1h1v-1zm-46 33v1h1v-1zm-8-4v1h1v-1zm27 17v1h1v-1zm-28 18v1h1v-1zm12-26v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm31 24v1h1v-1zm-53-6v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-28 18v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-37-31v1h1v-1zm19 13v1h1v-1zm-1 41v1h1v-1zm4-30v1h1v-1zm11 9v1h1v-1zm-53-6v1h1v-1zm46-33v1h1v-1zm-15 20v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm20-22v1h1v-1zm-9 68v1h1v-1zm20-22v1h1v-1zm-50-1v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-9 31v1h1v-1zm12-26v1h1v-1zm30 28v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-34 7v1h1v-1zm-9-49v1h1v-1zm33-3v1h1v-1zm19 50v1h1v-1zm-28-19v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-61-10v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-29-27v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm47-5v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-30 14v1h1v-1zm40 36v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-20-58v1h1v-1zm-31 12v1h1v-1zm45 8v1h1v-1zm-35 42v1h1v-1zm47-68v1h1v-1zm-35 42v1h1v-1zm-8-4v1h1v-1zm22 24v1h1v-1zm25-56v1h1v-1zm-53-6v1h1v-1zm-9 31v1h1v-1zm40 36v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm12-26v1h1v-1zm-67 10v1h1v-1zm33 34v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-34 7v1h1v-1zm-7-39v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-16-8v1h1v-1zm19 13v1h1v-1zm32-11v1h1v-1zm-8-4v1h1v-1zm-54 29v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm47-5v1h1v-1zm-28 18v1h1v-1zm11 15v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm-26-52v1h1v-1zm19 13v1h1v-1zm-35 42v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm33 34v1h1v-1zm-21-23v1h1v-1zm-8-41v1h1v-1zm20 15v1h1v-1zm-35 42v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-27 19v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm1-35v1h1v-1zm-55 64v1h1v-1zm58-59v1h1v-1zm-42 3v1h1v-1zm19 50v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm40 36v1h1v-1zm-20-58v1h1v-1zm-9 31v1h1v-1zm-7-39v1h1v-1zm18 54v1h1v-1zm19 13v1h1v-1zm-28-62v1h1v-1zm32 32v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-39 45v1h1v-1zm20-22v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-54 29v1h1v-1zm12-26v1h1v-1z" />
</g>
<g id="qr-code-v11-M-original" transform="scale(16.39344262)">
<rect fill="#fff" height="61" width="61" /><path d="m59 55v1h1v-1zm-9-49v1h1v-1zm-33 52v1h1v-1zm12-26v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-46 33v1h1v-1zm27-10v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm27-10v1h1v-1zm-13 30v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-43 38v1h1v-1zm25-56v1h1v-1zm-9 37v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm-8-4v1h1v-1zm32-11v1h1v-1zm3 5v1h1v-1zm-5 34v1h1v-1zm-29-27v1h1v-1zm32 32v1h1v-1zm12-26v1h1v-1zm-21-23v1h1v-1zm-9 37v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm-29-27v1h1v-1zm-5 34v1h1v-1zm38-37v1h1v-1zm-34 7v1h1v-1zm11 46v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm37 4v1h1v-1zm-42 3v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-43 38v1h1v-1zm4 7v1h1v-1zm12-26v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-35-21v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm32-11v1h1v-1zm-21 26v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm12 11v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm14 20v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm18 54v1h1v-1zm-15-43v1h1v-1zm19 13v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-42 3v1h1v-1zm44 43v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm-10 35v1h1v-1zm20-22v1h1v-1zm20-22v1h1v-1zm3 42v1h1v-1zm-42 3v1h1v-1zm39-9v1h1v-1zm-8-4v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm58-22v1h1v-1zm-13 30v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm-34 7v1h1v-1zm36 39v1h1v-1zm-42 3v1h1v-1zm39-9v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm12 11v1h1v-1zm4-30v1h1v-1zm11 9v1h1v-1zm-2 39v1h1v-1zm-18-12v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm31-13v1h1v-1zm20-22v1h1v-1zm-31 49v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-10-14v1h1v-1zm-27-17v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm10 50v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm4-30v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm23 20v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm47-5v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm2 46v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm46-33v1h1v-1zm-21-23v1h1v-1zm-2 39v1h1v-1zm20 15v1h1v-1zm-28-19v1h1v-1zm20 15v1h1v-1zm12-26v1h1v-1zm-42 3v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-19 23v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm32 32v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm-8-4v1h1v-1zm24-15v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm-43 38v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm43-2v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm1-35v1h1v-1zm-30 8v1h1v-1zm-13 30v1h1v-1zm24-15v1h1v-1zm11 9v1h1v-1zm-7-39v1h1v-1zm18 48v1h1v-1zm-53-6v1h1v-1zm46-33v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm32-11v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm-5 34v1h1v-1zm25-56v1h1v-1zm2 46v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-31 12v1h1v-1zm1-35v1h1v-1zm3 5v1h1v-1zm19 50v1h1v-1zm-1 4v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-15-43v1h1v-1zm19 13v1h1v-1zm-1 41v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-23 16v1h1v-1zm-8-4v1h1v-1zm-11 27v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-7-39v1h1v-1zm3 5v1h1v-1zm24-15v1h1v-1zm-33 52v1h1v-1zm-1-45v1h1v-1zm32 32v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm38-37v1h1v-1zm-34 7v1h1v-1zm31 24v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-35 42v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm37 4v1h1v-1zm-23 16v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-18-18v1h1v-1zm-9 37v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm-54 29v1h1v-1zm32-11v1h1v-1zm3 5v1h1v-1zm-5 34v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-21-23v1h1v-1zm32 32v1h1v-1zm-21-23v1h1v-1zm-17 33v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm-4-34v1h1v-1zm31 24v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm37 4v1h1v-1zm-42 3v1h1v-1zm19 13v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-31 49v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm31-13v1h1v-1zm-27-17v1h1v-1zm-15 20v1h1v-1zm-8-4v1h1v-1zm11 15v1h1v-1zm11 9v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm11 15v1h1v-1zm-30 8v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm4-30v1h1v-1zm44 43v1h1v-1zm-29-27v1h1v-1zm-13 30v1h1v-1zm39-9v1h1v-1zm-27-17v1h1v-1zm11 9v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm46-33v1h1v-1zm-34 44v1h1v-1zm46-33v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm53 12v1h1v-1zm-8-4v1h1v-1zm-29-27v1h1v-1zm-13 30v1h1v-1zm36-47v1h1v-1zm-33 52v1h1v-1zm-1-45v1h1v-1zm41 1v1h1v-1zm-28 18v1h1v-1zm-21-23v1h1v-1zm52 47v1h1v-1zm-30-23v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-42 3v1h1v-1zm12 11v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm20-22v1h1v-1zm15 16v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm33 34v1h1v-1zm12-26v1h1v-1zm-10-14v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-23 16v1h1v-1zm13 19v1h1v-1zm40-7v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm40-44v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-30 14v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-34 7v1h1v-1zm13 19v1h1v-1zm-9-49v1h1v-1zm37 4v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm11 46v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm40 36v1h1v-1zm-9-49v1h1v-1zm-34 7v1h1v-1zm33 34v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm-42 3v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm12 11v1h1v-1zm-8-41v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-10-8v1h1v-1zm12 54v1h1v-1zm21-57v1h1v-1zm-9 31v1h1v-1zm-21-23v1h1v-1zm10 13v1h1v-1zm31 24v1h1v-1zm-15-43v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-26-52v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm3 11v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-49-5v1h1v-1zm6 43v1h1v-1zm-8-4v1h1v-1zm-1-45v1h1v-1zm33 34v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm4-30v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm11 15v1h1v-1zm11 9v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm18 54v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm31-13v1h1v-1zm1-35v1h1v-1zm-54 29v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm3 5v1h1v-1zm41 38v1h1v-1zm-21-23v1h1v-1zm-31 12v1h1v-1zm41 1v1h1v-1zm-30 8v1h1v-1zm42-34v1h1v-1zm-49-5v1h1v-1zm18 54v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-48-40v1h1v-1zm6 43v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20 15v1h1v-1zm23-17v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-35 42v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm3 11v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm23 20v1h1v-1zm1-35v1h1v-1zm10 44v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-5 34v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-17-53v1h1v-1zm33 34v1h1v-1zm-23 16v1h1v-1zm-7-39v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-39 45v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm37 4v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm11 46v1h1v-1zm-54-8v1h1v-1zm12 11v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-35 42v1h1v-1zm38-37v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm23 20v1h1v-1zm-1 4v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm58-22v1h1v-1zm-50-1v1h1v-1zm39-9v1h1v-1zm-43 38v1h1v-1zm37 4v1h1v-1zm-1-45v1h1v-1zm13 19v1h1v-1zm-29-27v1h1v-1zm-5 34v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-19 23v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-18-12v1h1v-1zm31-13v1h1v-1zm-54 29v1h1v-1zm4-30v1h1v-1zm44 43v1h1v-1zm-34 7v1h1v-1zm4-30v1h1v-1zm-23 16v1h1v-1zm4-30v1h1v-1zm11 46v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-43 38v1h1v-1zm3 11v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm39-9v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm21-57v1h1v-1zm19 13v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm-31 49v1h1v-1zm31-13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-27 46v1h1v-1zm4-30v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-49-5v1h1v-1zm33-3v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm-9 31v1h1v-1zm-18-12v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm11 15v1h1v-1zm-29-27v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-21 26v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm40-7v1h1v-1zm-28-19v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm19 13v1h1v-1zm3 11v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-49-5v1h1v-1zm33-3v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-42 3v1h1v-1zm-9-49v1h1v-1zm52 47v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-42 3v1h1v-1zm-8-4v1h1v-1zm30 22v1h1v-1zm24-15v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm32 32v1h1v-1zm-22 18v1h1v-1zm12-26v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm20-22v1h1v-1zm-51 34v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm36-41v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm22 24v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm-7-39v1h1v-1zm31 24v1h1v-1zm-8-4v1h1v-1zm-23 16v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm30 28v1h1v-1zm-8-4v1h1v-1zm-27-17v1h1v-1zm-21-23v1h1v-1zm6 43v1h1v-1zm4-30v1h1v-1zm23 20v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm11 15v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm11 9v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm18 54v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm4-30v1h1v-1zm-35 42v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-9 37v1h1v-1zm-9-49v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm-11 27v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-51 34v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm32-11v1h1v-1zm-33 52v1h1v-1zm23-17v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-18-12v1h1v-1zm11 9v1h1v-1zm-34 7v1h1v-1zm4-30v1h1v-1zm-8-4v1h1v-1zm35-6v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm32 32v1h1v-1zm-34 7v1h1v-1zm25-56v1h1v-1zm-8-4v1h1v-1zm41 38v1h1v-1zm-50-1v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm39-9v1h1v-1zm-31 49v1h1v-1zm12-26v1h1v-1zm3 5v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm12-26v1h1v-1zm-1 41v1h1v-1zm46-33v1h1v-1zm-35 42v1h1v-1zm31-13v1h1v-1zm-15-43v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm11 46v1h1v-1zm12-26v1h1v-1zm-23 16v1h1v-1zm38-37v1h1v-1zm-42 3v1h1v-1zm10 50v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm47-5v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm31 24v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm6 16v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm7 8v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm12 11v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm53 12v1h1v-1zm-16-8v1h1v-1zm10-36v1h1v-1zm-21 26v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm52 47v1h1v-1zm-41-32v1h1v-1zm19 13v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm12 11v1h1v-1zm-13 30v1h1v-1zm12-26v1h1v-1zm-49-5v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-33 52v1h1v-1zm12-26v1h1v-1zm3 5v1h1v-1zm20 15v1h1v-1zm-42 3v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm39-9v1h1v-1zm-42 3v1h1v-1zm23 20v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-5 7v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm-27 46v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm-7-39v1h1v-1zm31 24v1h1v-1zm-42 3v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-30 8v1h1v-1zm33-3v1h1v-1zm-55-16v1h1v-1zm4-30v1h1v-1zm33 34v1h1v-1zm20-22v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-35 42v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm35-6v1h1v-1zm-54 29v1h1v-1zm60 14v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-1-45v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm10 50v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-3 31v1h1v-1zm-28-19v1h1v-1zm40-7v1h1v-1zm-9 31v1h1v-1zm-7-39v1h1v-1zm-34 7v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm18 54v1h1v-1zm-8-4v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm32 32v1h1v-1zm-21-23v1h1v-1zm-34 7v1h1v-1zm33 34v1h1v-1zm4-30v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm3 5v1h1v-1zm24-15v1h1v-1zm-33 52v1h1v-1zm-21-23v1h1v-1zm60 14v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm24-15v1h1v-1zm4-30v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm37 4v1h1v-1zm-17-53v1h1v-1zm18 17v1h1v-1zm-21-23v1h1v-1zm-9 31v1h1v-1zm11 15v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm-21-23v1h1v-1zm6 43v1h1v-1zm46-33v1h1v-1zm-53-6v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm24-15v1h1v-1zm-35 42v1h1v-1zm15-21v1h1v-1zm12-26v1h1v-1zm-30 14v1h1v-1zm10 13v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm11 46v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm-7-39v1h1v-1zm44 43v1h1v-1zm-50-1v1h1v-1zm20-22v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm60 14v1h1v-1zm-39 45v1h1v-1zm20-22v1h1v-1zm-16-8v1h1v-1zm12-26v1h1v-1zm14 47v1h1v-1zm-29-27v1h1v-1zm-9 37v1h1v-1zm23-17v1h1v-1zm19 13v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-26-16v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm16-56v1h1v-1zm12 11v1h1v-1zm3 5v1h1v-1zm6 43v1h1v-1zm12-26v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm-1 41v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm-31 49v1h1v-1zm33-3v1h1v-1zm2-40v1h1v-1zm-5 34v1h1v-1zm-28 18v1h1v-1zm-20-58v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm10 50v1h1v-1zm4-30v1h1v-1zm-22-19v1h1v-1zm15 16v1h1v-1zm20-22v1h1v-1zm-35 42v1h1v-1zm38-37v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm52 47v1h1v-1zm-41-32v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm47-5v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm12 11v1h1v-1zm-50-1v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-15-7v1h1v-1zm-5 34v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm32-48v1h1v-1zm-31 49v1h1v-1zm-7-39v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-41-32v1h1v-1zm44 43v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-43 38v1h1v-1zm-1-45v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-23 53v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-16-8v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-1 41v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm40 36v1h1v-1zm-9-49v1h1v-1zm-13 30v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-39 45v1h1v-1zm32-48v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm45 8v1h1v-1zm-10-14v1h1v-1zm-31 12v1h1v-1zm37 4v1h1v-1zm12-26v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm-1 35v1h1v-1zm-7-39v1h1v-1zm-10-8v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm41 1v1h1v-1zm-23 16v1h1v-1zm16-19v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-24 51v1h1v-1zm1-35v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm-3 31v1h1v-1zm46-33v1h1v-1zm-54 29v1h1v-1zm41 1v1h1v-1zm19 13v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm-34 7v1h1v-1zm52 47v1h1v-1zm-16-8v1h1v-1zm-30-23v1h1v-1zm11 9v1h1v-1zm12-26v1h1v-1zm20 15v1h1v-1zm-23 16v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-9 31v1h1v-1zm23-11v1h1v-1zm-28 18v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-43 38v1h1v-1zm12-26v1h1v-1zm20 15v1h1v-1zm12-26v1h1v-1zm-51 34v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-9 37v1h1v-1zm20-22v1h1v-1zm-29-27v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-54 29v1h1v-1zm51 2v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm24-15v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm-11 27v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm31-13v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm4-30v1h1v-1zm23 20v1h1v-1zm-13 30v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm11 9v1h1v-1zm-1 41v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm32-11v1h1v-1zm3 5v1h1v-1zm20-22v1h1v-1zm-33 52v1h1v-1zm31-13v1h1v-1zm-29-27v1h1v-1zm-9 37v1h1v-1zm-8-4v1h1v-1zm39-9v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm-19 23v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-16-8v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-5 34v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm22 24v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm20-22v1h1v-1zm-42 3v1h1v-1zm15 16v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm-27 46v1h1v-1zm19-50v1h1v-1zm-15 20v1h1v-1zm-8-4v1h1v-1zm11 15v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm39-9v1h1v-1zm-8-4v1h1v-1zm12 11v1h1v-1zm-42 3v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm-32 53v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-49-5v1h1v-1zm1-35v1h1v-1zm-2 39v1h1v-1zm46-33v1h1v-1zm-30 14v1h1v-1zm19 13v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm44 43v1h1v-1zm-42 3v1h1v-1zm25-56v1h1v-1zm11 9v1h1v-1zm-34 7v1h1v-1zm22 24v1h1v-1zm-28 18v1h1v-1zm47-5v1h1v-1zm-27-17v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm37 4v1h1v-1zm-29-27v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-13 30v1h1v-1zm27-10v1h1v-1zm-42 3v1h1v-1zm-8-4v1h1v-1zm47-5v1h1v-1zm-8-4v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm12 11v1h1v-1zm11 9v1h1v-1zm-7-39v1h1v-1zm-8-4v1h1v-1zm17 52v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm36 39v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm12-26v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm-23 53v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm32 32v1h1v-1zm-21-23v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm44 43v1h1v-1zm-22-19v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm23 20v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm21 59v1h1v-1zm1-35v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm24-15v1h1v-1zm4-30v1h1v-1zm-54 29v1h1v-1zm30 22v1h1v-1zm-26-52v1h1v-1zm19 13v1h1v-1zm11 15v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-29-27v1h1v-1zm13 19v1h1v-1zm-15 20v1h1v-1zm4-30v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm-42 3v1h1v-1zm1-35v1h1v-1zm10 44v1h1v-1zm21-57v1h1v-1zm15 16v1h1v-1zm-50-1v1h1v-1zm6 43v1h1v-1zm46-33v1h1v-1zm-21-23v1h1v-1zm-34 7v1h1v-1zm44 43v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm24-52v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm12 11v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm14 20v1h1v-1zm20-22v1h1v-1zm-29-27v1h1v-1zm-13 30v1h1v-1zm12-26v1h1v-1zm11 9v1h1v-1zm33 34v1h1v-1zm-1-45v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-9 37v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm12 11v1h1v-1zm3 5v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm40 36v1h1v-1zm-29-27v1h1v-1zm24-15v1h1v-1zm-13 30v1h1v-1zm19 13v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-46 33v1h1v-1zm27-10v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-13 30v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm4-30v1h1v-1zm20 15v1h1v-1zm-10 35v1h1v-1zm32-48v1h1v-1zm-54 29v1h1v-1zm15 16v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-22-19v1h1v-1zm-8-4v1h1v-1zm20 15v1h1v-1zm-21 26v1h1v-1zm24-21v1h1v-1zm-13 30v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm41 38v1h1v-1zm-54-8v1h1v-1zm12 11v1h1v-1zm31-13v1h1v-1zm12-26v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm15 16v1h1v-1zm12-26v1h1v-1zm-35 42v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm4 7v1h1v-1zm-42 3v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm46 30v1h1v-1zm-27-17v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm-7-39v1h1v-1zm-34 7v1h1v-1zm23 20v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm-34 7v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm-1 41v1h1v-1zm19 13v1h1v-1zm-16-8v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm53 12v1h1v-1zm-49-5v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-30-23v1h1v-1zm-8-4v1h1v-1zm52 47v1h1v-1zm-42 3v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-42 3v1h1v-1zm12 11v1h1v-1zm46-33v1h1v-1zm-42 3v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm-34 7v1h1v-1zm36 39v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm25-56v1h1v-1zm-33 52v1h1v-1zm25-56v1h1v-1zm14 47v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-30 14v1h1v-1zm11 9v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-9 37v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm6 43v1h1v-1zm-8-4v1h1v-1zm35-6v1h1v-1zm-34 7v1h1v-1zm-7-39v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm47-5v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm12 11v1h1v-1zm46-33v1h1v-1zm-50-1v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm44 43v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm52 47v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm45 8v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm11 46v1h1v-1zm12-26v1h1v-1zm-54 29v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm14 20v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm3 5v1h1v-1zm12 11v1h1v-1zm-15 20v1h1v-1zm33-3v1h1v-1zm-49-5v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm47-5v1h1v-1zm-50-1v1h1v-1zm44 43v1h1v-1zm1-35v1h1v-1zm10 44v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-21-23v1h1v-1zm33 34v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm39-9v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm-18-12v1h1v-1zm40 36v1h1v-1zm-20-58v1h1v-1zm-28 18v1h1v-1zm41 1v1h1v-1zm11 9v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm-21-23v1h1v-1zm56 17v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm3 5v1h1v-1zm12 11v1h1v-1zm-34 7v1h1v-1zm33-3v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm-19 23v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm12 11v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-27-17v1h1v-1zm33 33v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-42 3v1h1v-1zm23 20v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm6 43v1h1v-1zm-48-40v1h1v-1zm11 9v1h1v-1zm12-26v1h1v-1zm18 54v1h1v-1zm-48-40v1h1v-1zm41 1v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm38-37v1h1v-1zm-34 7v1h1v-1zm27-10v1h1v-1zm-5 34v1h1v-1zm24-21v1h1v-1zm-53-6v1h1v-1zm52 10v1h1v-1zm-38 10v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm46-33v1h1v-1zm-23 53v1h1v-1zm4-30v1h1v-1zm23-17v1h1v-1zm-53-6v1h1v-1zm19 13v1h1v-1zm37 4v1h1v-1zm-23 16v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm20-22v1h1v-1zm-30 8v1h1v-1zm41 38v1h1v-1zm-54-8v1h1v-1zm53 12v1h1v-1zm-29-27v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm15 16v1h1v-1zm12-26v1h1v-1zm14 47v1h1v-1zm-16-8v1h1v-1zm24-15v1h1v-1zm-23 16v1h1v-1zm-27-17v1h1v-1zm38 26v1h1v-1zm-41-32v1h1v-1zm15 16v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-23 16v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-1 41v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm4-30v1h1v-1zm-30-23v1h1v-1zm52 47v1h1v-1zm-49-5v1h1v-1zm43-39v1h1v-1zm-33 52v1h1v-1zm20-22v1h1v-1zm-29-27v1h1v-1zm44 43v1h1v-1zm-34 7v1h1v-1zm-16-8v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-54 29v1h1v-1zm46-33v1h1v-1zm12 11v1h1v-1zm-54-8v1h1v-1zm44 43v1h1v-1zm-20-58v1h1v-1zm-23 16v1h1v-1zm44 43v1h1v-1zm-50-1v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm31-13v1h1v-1zm-42 3v1h1v-1zm58-22v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm-7-39v1h1v-1zm11 9v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm53 12v1h1v-1zm-16-8v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm20-22v1h1v-1zm3 42v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-54 29v1h1v-1zm45 8v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm6 43v1h1v-1zm12-26v1h1v-1zm-21-23v1h1v-1zm52 47v1h1v-1zm-16-8v1h1v-1zm-42 3v1h1v-1zm39-9v1h1v-1zm-8-4v1h1v-1zm-27-17v1h1v-1zm31-13v1h1v-1zm-22 18v1h1v-1zm40 36v1h1v-1zm-49-5v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-33 52v1h1v-1zm35-6v1h1v-1zm20-22v1h1v-1zm-16-8v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm22 24v1h1v-1zm-29-27v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm-34 7v1h1v-1zm49-22v1h1v-1zm-35 42v1h1v-1zm-7-39v1h1v-1zm18 48v1h1v-1zm-7-39v1h1v-1zm11 9v1h1v-1zm12 11v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-35 42v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm11 15v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm-8-4v1h1v-1zm18 48v1h1v-1zm-7-39v1h1v-1zm-23 16v1h1v-1zm-8-4v1h1v-1zm20 15v1h1v-1zm20-22v1h1v-1zm-16-8v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm60 14v1h1v-1zm-30 8v1h1v-1zm-5 7v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-54 29v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm24-15v1h1v-1zm11 9v1h1v-1zm-31 12v1h1v-1zm-7-39v1h1v-1zm9 5v1h1v-1zm40 36v1h1v-1zm-16-8v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-3 31v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm-53-6v1h1v-1zm46-33v1h1v-1zm-27 46v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1z" />
</g>
<g id="qr-code-v09-L-original" transform="scale(18.8679245283)">
<rect fill="#fff" height="53" width="53" /><path d="m18 17v1h1v-1zm32-11v1h1v-1zm-42 3v1h1v-1zm40 36v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-11 27v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm24-15v1h1v-1zm-2 39v1h1v-1zm-21-23v1h1v-1zm3 5v1h1v-1zm12-26v1h1v-1zm-1 41v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm20 15v1h1v-1zm3 5v1h1v-1zm10 13v1h1v-1zm-38 10v1h1v-1zm39-9v1h1v-1zm-8-4v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-46 33v1h1v-1zm4-30v1h1v-1zm23 20v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm7 8v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm22 24v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-7-3v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm46-33v1h1v-1zm-8-4v1h1v-1zm-5 34v1h1v-1zm-29-27v1h1v-1zm24-15v1h1v-1zm-21 26v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm23-17v1h1v-1zm-11 27v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm27-10v1h1v-1zm-13 30v1h1v-1zm23-17v1h1v-1zm-42 3v1h1v-1zm15 16v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm3 42v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-13 30v1h1v-1zm31-13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-38 10v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-30-23v1h1v-1zm33 34v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-1 4v1h1v-1zm-21-23v1h1v-1zm33 33v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm23 20v1h1v-1zm12-26v1h1v-1zm-27-17v1h1v-1zm41 37v1h1v-1zm-42 3v1h1v-1zm12-26v1h1v-1zm3 5v1h1v-1zm1 1v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm48 13v1h1v-1zm1-35v1h1v-1zm-23 16v1h1v-1zm23 20v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-42 3v1h1v-1zm45 8v1h1v-1zm3 5v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm9 5v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm19 13v1h1v-1zm-6-38v1h1v-1zm-8-4v1h1v-1zm6 43v1h1v-1zm16-19v1h1v-1zm11 9v1h1v-1zm-7-39v1h1v-1zm-35 42v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-11 27v1h1v-1zm33-3v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-9 37v1h1v-1zm3 5v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm22 24v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm20 15v1h1v-1zm-8-41v1h1v-1zm-8-4v1h1v-1zm20 15v1h1v-1zm-23 16v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-16-8v1h1v-1zm19 13v1h1v-1zm11 15v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm31 24v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm24-15v1h1v-1zm-34 7v1h1v-1zm40 36v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-11 27v1h1v-1zm-7-39v1h1v-1zm11 9v1h1v-1zm-43 38v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm10 50v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-21-23v1h1v-1zm3 5v1h1v-1zm-1 41v1h1v-1zm23-17v1h1v-1zm-8-4v1h1v-1zm-30-23v1h1v-1zm23 20v1h1v-1zm10 13v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-9 37v1h1v-1zm39-9v1h1v-1zm-27-17v1h1v-1zm-15 20v1h1v-1zm38-37v1h1v-1zm-42 3v1h1v-1zm31 24v1h1v-1zm18 17v1h1v-1zm-8-4v1h1v-1zm-26-16v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm-42 3v1h1v-1zm24-15v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-16-8v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm11 15v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-19 23v1h1v-1zm-8-4v1h1v-1zm39-9v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm4-30v1h1v-1zm22 24v1h1v-1zm-28 18v1h1v-1zm39-9v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm12 11v1h1v-1zm3 5v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-35 42v1h1v-1zm37 4v1h1v-1zm-21-23v1h1v-1zm-21 26v1h1v-1zm44-43v1h1v-1zm-34 7v1h1v-1zm13 19v1h1v-1zm20-22v1h1v-1zm-30 14v1h1v-1zm34-8v1h1v-1zm-42 3v1h1v-1zm4 7v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm6 43v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm-9 31v1h1v-1zm-10-8v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm0 36v1h1v-1zm20-22v1h1v-1zm13 19v1h1v-1zm-34 7v1h1v-1zm-10-14v1h1v-1zm45 8v1h1v-1zm-50-1v1h1v-1zm39-9v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-11 27v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm12-26v1h1v-1zm-38 10v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-7-39v1h1v-1zm-23 16v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm11 46v1h1v-1zm-42 3v1h1v-1zm32-48v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-1 41v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm-34 7v1h1v-1zm3 5v1h1v-1zm12-26v1h1v-1zm-22 18v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27 19v1h1v-1zm41 1v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm-23 16v1h1v-1zm12 11v1h1v-1zm4-30v1h1v-1zm-27 46v1h1v-1zm-15-43v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-11 27v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-22-19v1h1v-1zm-21-23v1h1v-1zm33 34v1h1v-1zm12-26v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-42 3v1h1v-1zm20-22v1h1v-1zm3 5v1h1v-1zm-22 18v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm14 20v1h1v-1zm12-26v1h1v-1zm-15 20v1h1v-1zm-4-34v1h1v-1zm20 15v1h1v-1zm-31 12v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm14 20v1h1v-1zm19 13v1h1v-1zm-48-40v1h1v-1zm40 36v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20 15v1h1v-1zm12-26v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm32 32v1h1v-1zm-41 4v1h1v-1zm38-37v1h1v-1zm-34 7v1h1v-1zm23 20v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm14 20v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-11 27v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-22-19v1h1v-1zm-6 42v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-30 8v1h1v-1zm-13 30v1h1v-1zm12 11v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm19-14v1h1v-1zm12-26v1h1v-1zm-5 34v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-7-3v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-19 23v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-13 30v1h1v-1zm19 13v1h1v-1zm-37-31v1h1v-1zm22 24v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm3 11v1h1v-1zm-44-7v1h1v-1zm7-19v1h1v-1zm19 13v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-3 31v1h1v-1zm-8-41v1h1v-1zm41 38v1h1v-1zm-29-27v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm2 46v1h1v-1zm-42 3v1h1v-1zm32-48v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm-22 18v1h1v-1zm11 9v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm3 11v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-18-12v1h1v-1zm-9 31v1h1v-1zm47-5v1h1v-1zm-27-17v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm37 4v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-21 26v1h1v-1zm20-22v1h1v-1zm3 5v1h1v-1zm20 15v1h1v-1zm-42 3v1h1v-1zm3 5v1h1v-1zm20-22v1h1v-1zm-27-17v1h1v-1zm47-5v1h1v-1zm-8-4v1h1v-1zm2 46v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm22 24v1h1v-1zm4-30v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm30 22v1h1v-1zm3 11v1h1v-1zm-1-45v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm10 50v1h1v-1zm12-26v1h1v-1zm-31 12v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm23-11v1h1v-1zm-9 31v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm31 24v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-27 46v1h1v-1zm33-3v1h1v-1zm-48-40v1h1v-1zm19 13v1h1v-1zm22 24v1h1v-1zm-8-4v1h1v-1zm-11-10v1h1v-1zm-8-4v1h1v-1zm31 24v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-22-19v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-26-16v1h1v-1zm41 1v1h1v-1zm3 5v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm35-6v1h1v-1zm-8-4v1h1v-1zm-22-19v1h1v-1zm4 6v1h1v-1zm14 20v1h1v-1zm-31 12v1h1v-1zm12-26v1h1v-1zm32-11v1h1v-1zm-43 38v1h1v-1zm46-33v1h1v-1zm-2 39v1h1v-1zm-29-27v1h1v-1zm3 11v1h1v-1zm19 13v1h1v-1zm12-26v1h1v-1zm-27-17v1h1v-1zm9 5v1h1v-1zm3 5v1h1v-1zm-5 34v1h1v-1zm-29-27v1h1v-1zm23-17v1h1v-1zm-8-4v1h1v-1zm-9 37v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1 1v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm20-22v1h1v-1zm-5 7v1h1v-1zm11 9v1h1v-1zm19 13v1h1v-1zm-41-32v1h1v-1zm3 42v1h1v-1zm20-22v1h1v-1zm-23 16v1h1v-1zm4-30v1h1v-1zm27-10v1h1v-1zm-5 34v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-42 3v1h1v-1zm12 11v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-18-13v1h1v-1zm13 19v1h1v-1zm-34 7v1h1v-1zm23 20v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm38 26v1h1v-1zm-18-12v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm31-13v1h1v-1zm-50-1v1h1v-1zm11 15v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm45 8v1h1v-1zm-29-27v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-13 30v1h1v-1zm3 5v1h1v-1zm9 5v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-22 18v1h1v-1zm3 5v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-5 7v1h1v-1zm-21-23v1h1v-1zm-13 30v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-11 27v1h1v-1zm12-26v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-19-14v1h1v-1zm33 34v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm11 9v1h1v-1zm-22 18v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-8-4v1h1v-1zm31-13v1h1v-1zm-22 18v1h1v-1zm33-3v1h1v-1zm-30 8v1h1v-1zm10 13v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm28 19v1h1v-1zm-4-34v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm2 46v1h1v-1zm-21-23v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm13 19v1h1v-1zm32-11v1h1v-1zm-28 17v1h1v-1zm-6-38v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-13 30v1h1v-1zm24-15v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm-35 42v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm22 24v1h1v-1zm-48-40v1h1v-1zm19 13v1h1v-1zm11 9v1h1v-1zm4-24v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm23-17v1h1v-1zm11 46v1h1v-1zm-11-10v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm12-26v1h1v-1zm-9 37v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm11 9v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm12 11v1h1v-1zm15-21v1h1v-1zm-27 46v1h1v-1zm-4-34v1h1v-1zm-9 37v1h1v-1zm-1-45v1h1v-1zm-8-4v1h1v-1zm40 36v1h1v-1zm1 1v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm-27 46v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm14 20v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-18-12v1h1v-1zm12-26v1h1v-1zm11 9v1h1v-1zm-42 3v1h1v-1zm15 16v1h1v-1zm12-26v1h1v-1zm-5 34v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm11 9v1h1v-1zm33-3v1h1v-1zm-21-23v1h1v-1zm-9 37v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm31-13v1h1v-1zm-31 49v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-30 14v1h1v-1zm4-30v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-34 7v1h1v-1zm3 42v1h1v-1zm12-26v1h1v-1zm-15 20v1h1v-1zm46-33v1h1v-1zm-8-4v1h1v-1zm-5 34v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-49-5v1h1v-1zm11 15v1h1v-1zm30 22v1h1v-1zm-15-43v1h1v-1zm-27 46v1h1v-1zm4-30v1h1v-1zm24-15v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-1 40v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-19 23v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-23 16v1h1v-1zm1-35v1h1v-1zm2 46v1h1v-1zm31-13v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm11 15v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-42 3v1h1v-1zm41 38v1h1v-1zm-29-27v1h1v-1zm33-3v1h1v-1zm-10-14v1h1v-1zm-1 4v1h1v-1zm-9 31v1h1v-1zm-10-8v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm-28 18v1h1v-1zm39-9v1h1v-1zm-27 19v1h1v-1zm10 13v1h1v-1zm16-19v1h1v-1zm-15 20v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-11 27v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm12-26v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm37 4v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-7-39v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm11 15v1h1v-1zm-21-23v1h1v-1zm-16-8v1h1v-1zm-3-6v1h1v-1zm33 34v1h1v-1zm11 9v1h1v-1zm-30-23v1h1v-1zm11 9v1h1v-1zm16-19v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm19 13v1h1v-1zm4-24v1h1v-1zm-30 8v1h1v-1zm20-22v1h1v-1zm-2 39v1h1v-1zm4-30v1h1v-1zm20 15v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-43 38v1h1v-1zm1-35v1h1v-1zm18 48v1h1v-1zm1-35v1h1v-1zm-16-8v1h1v-1zm19 13v1h1v-1zm14 20v1h1v-1zm-34 7v1h1v-1zm24-15v1h1v-1zm4-30v1h1v-1zm11 9v1h1v-1zm-23 16v1h1v-1zm-8-4v1h1v-1zm24-15v1h1v-1zm6 43v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm30 28v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm32-11v1h1v-1zm-28-19v1h1v-1zm20 15v1h1v-1zm11 9v1h1v-1zm-21 26v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-29-27v1h1v-1zm10 50v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm-42 3v1h1v-1zm20-22v1h1v-1zm12 11v1h1v-1zm11 9v1h1v-1zm-42 3v1h1v-1zm46-33v1h1v-1zm-34 7v1h1v-1zm6 16v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm-3 31v1h1v-1zm-7-39v1h1v-1zm-9 31v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-42 3v1h1v-1zm40 36v1h1v-1zm-29-27v1h1v-1zm3 11v1h1v-1zm11 9v1h1v-1zm20-22v1h1v-1zm-42 3v1h1v-1zm15 16v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm3 11v1h1v-1zm11 9v1h1v-1zm23-17v1h1v-1zm-38 10v1h1v-1zm20-22v1h1v-1zm23-17v1h1v-1zm-27 19v1h1v-1zm-22-19v1h1v-1zm40 36v1h1v-1zm-42 3v1h1v-1zm16-19v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm38 26v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm36 39v1h1v-1zm-9-49v1h1v-1zm-20 27v1h1v-1zm11 9v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-23 16v1h1v-1zm12 11v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-2 39v1h1v-1zm-7-3v1h1v-1zm12-26v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm-3 31v1h1v-1zm-8-5v1h1v-1zm1-35v1h1v-1zm44 43v1h1v-1zm-8-4v1h1v-1zm-3-6v1h1v-1zm12-26v1h1v-1zm-49-5v1h1v-1zm33-3v1h1v-1zm-34 7v1h1v-1zm12 11v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm30 28v1h1v-1zm-8-4v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-42 3v1h1v-1zm19 13v1h1v-1zm4-30v1h1v-1zm-11 27v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm45 8v1h1v-1zm-29-27v1h1v-1zm11 9v1h1v-1zm-13 30v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm12 11v1h1v-1zm-42 3v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm-7-39v1h1v-1zm19 13v1h1v-1zm-10-8v1h1v-1zm24 28v1h1v-1zm-21-23v1h1v-1zm30 28v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-23 16v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm11 9v1h1v-1zm-30 14v1h1v-1zm23-17v1h1v-1zm-1 4v1h1v-1zm12 11v1h1v-1zm12-26v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-31 12v1h1v-1zm32-11v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm11 15v1h1v-1zm3 5v1h1v-1zm-15 20v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm20 15v1h1v-1zm-28-19v1h1v-1zm20 15v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-43 38v1h1v-1zm41 1v1h1v-1zm-48-40v1h1v-1zm18 48v1h1v-1zm12-26v1h1v-1zm3 11v1h1v-1zm-8-4v1h1v-1zm-23 16v1h1v-1zm20-22v1h1v-1zm12 11v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm-31 12v1h1v-1zm12-26v1h1v-1zm-22 18v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm-30 8v1h1v-1zm41 1v1h1v-1zm-7-3v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm-8-4v1h1v-1zm4-30v1h1v-1zm-1 41v1h1v-1zm-10-14v1h1v-1zm20-22v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-5 34v1h1v-1zm24-21v1h1v-1zm-9 37v1h1v-1zm20-22v1h1v-1zm3 5v1h1v-1zm-22-19v1h1v-1zm-9 37v1h1v-1zm-10-14v1h1v-1zm46-33v1h1v-1zm-2 39v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-13 30v1h1v-1zm24-15v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm12-26v1h1v-1zm-27 46v1h1v-1zm38-37v1h1v-1zm-11 27v1h1v-1zm-8-4v1h1v-1zm-23 16v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm-13 3v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm23 20v1h1v-1zm12-26v1h1v-1zm11 9v1h1v-1zm-1 4v1h1v-1zm-37-31v1h1v-1zm-1 41v1h1v-1zm23-17v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-42 3v1h1v-1zm12 11v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-42 3v1h1v-1zm-8-4v1h1v-1zm30 28v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-21-23v1h1v-1zm-2 39v1h1v-1zm46-33v1h1v-1zm-38 10v1h1v-1zm31-13v1h1v-1zm-34 7v1h1v-1zm15 16v1h1v-1zm12-26v1h1v-1zm-13 30v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-27 19v1h1v-1zm20-22v1h1v-1zm11 9v1h1v-1zm-38 10v1h1v-1zm41 1v1h1v-1zm-30 13v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm23-17v1h1v-1zm-34 44v1h1v-1zm33-3v1h1v-1zm3 5v1h1v-1zm-21-23v1h1v-1zm-23 16v1h1v-1zm1-35v1h1v-1zm36 39v1h1v-1zm-42 3v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-31 49v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm46-33v1h1v-1zm-42 3v1h1v-1zm33 34v1h1v-1zm4-30v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-9 31v1h1v-1zm-26-16v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-23 16v1h1v-1zm45 8v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm17 52v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm31 24v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-22-19v1h1v-1zm33 34v1h1v-1zm-21-23v1h1v-1zm3 5v1h1v-1zm-13 30v1h1v-1zm23-17v1h1v-1zm-21-23v1h1v-1zm41 38v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-42 3v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm10 50v1h1v-1zm-37-31v1h1v-1zm18 17v1h1v-1zm-21-23v1h1v-1zm45 8v1h1v-1zm-9 31v1h1v-1zm12-26v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-6-38v1h1v-1zm19 13v1h1v-1zm3 11v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm46-33v1h1v-1zm-35 42v1h1v-1zm4-30v1h1v-1zm32-11v1h1v-1zm-8-4v1h1v-1zm6 43v1h1v-1zm-21-23v1h1v-1zm-27-17v1h1v-1zm11 9v1h1v-1zm19 13v1h1v-1zm4-24v1h1v-1zm18 48v1h1v-1zm-27-17v1h1v-1zm-21-23v1h1v-1zm40 36v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm32-11v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-13 30v1h1v-1zm-18-18v1h1v-1zm11 15v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm-31 12v1h1v-1zm20-22v1h1v-1zm11 46v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-5 7v1h1v-1zm-8-4v1h1v-1zm39-9v1h1v-1zm-8-4v1h1v-1zm-29-27v1h1v-1zm41 1v1h1v-1zm-46 33v1h1v-1zm35-6v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-42 3v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm22 24v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm-19 23v1h1v-1zm19 13v1h1v-1zm-7-39v1h1v-1zm-35 42v1h1v-1zm46-33v1h1v-1zm-8-4v1h1v-1zm1 1v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm40 36v1h1v-1zm4-30v1h1v-1zm-19 23v1h1v-1zm-22-19v1h1v-1zm35-6v1h1v-1zm-16-8v1h1v-1zm-5 34v1h1v-1zm24-21v1h1v-1zm-34 7v1h1v-1zm41 1v1h1v-1zm-28 18v1h1v-1zm20-22v1h1v-1zm-21-23v1h1v-1zm-8-4v1h1v-1zm-1 41v1h1v-1zm11 9v1h1v-1zm12-26v1h1v-1zm12-26v1h1v-1zm-42 3v1h1v-1zm11 46v1h1v-1zm-8-4v1h1v-1zm41 1v1h1v-1zm2-40v1h1v-1zm-13 30v1h1v-1zm-29-27v1h1v-1zm3 11v1h1v-1zm19 13v1h1v-1zm11 9v1h1v-1zm-7-39v1h1v-1zm-27 19v1h1v-1zm20-22v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm19-14v1h1v-1zm14 47v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm-22-19v1h1v-1zm31 24v1h1v-1zm-16-8v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm-23 16v1h1v-1zm16-19v1h1v-1zm-2 39v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-38 10v1h1v-1zm19 13v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm3 11v1h1v-1zm-21-23v1h1v-1zm-11-10v1h1v-1zm33-3v1h1v-1zm11 46v1h1v-1zm-8-4v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm47-5v1h1v-1zm-28 18v1h1v-1zm-22-19v1h1v-1zm33 34v1h1v-1zm-21-23v1h1v-1zm23-17v1h1v-1zm-10 35v1h1v-1zm-21-23v1h1v-1zm33 34v1h1v-1zm-30-23v1h1v-1zm4 6v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-2 39v1h1v-1zm-21-23v1h1v-1zm45 8v1h1v-1zm-37-31v1h1v-1zm41 1v1h1v-1zm-1 35v1h1v-1zm-34 7v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm11 9v1h1v-1zm11 9v1h1v-1zm12 11v1h1v-1zm-42 3v1h1v-1zm20-22v1h1v-1zm-9 31v1h1v-1zm20-22v1h1v-1zm3 11v1h1v-1zm12-26v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm-35 42v1h1v-1zm4-30v1h1v-1zm43-2v1h1v-1zm-42 3v1h1v-1zm22 18v1h1v-1zm3 11v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm-15 20v1h1v-1zm4-30v1h1v-1zm0 36v1h1v-1zm-8-4v1h1v-1zm-18-12v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm-10-14v1h1v-1zm-13 30v1h1v-1zm24-15v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-34 7v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1z" />
</g>
<g id="qr-code-v07-Q-path-encoding" transform="scale(22.2222222222)">
<rect fill="#fff" height="45" width="45" /><path d="m7 17v1h1v-1zm11 0v1h1v-1zm8 22v1h1v-1zm1-35v1h1v-1zm-20 31v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm23 2v1h1v-1zm-24 42v1h1v-1zm-10-35v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm29 36v1h1v-1zm11 0v1h1v-1zm-40-27v1h1v-1zm11 0v1h1v-1zm30 10v1h1v-1zm-8 5v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-37-40v1h1v-1zm-2 30v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm1-35v1h1v-1zm34 2v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-3 13v1h1v-1zm-31-6v1h1v-1zm0 9v1h1v-1zm35-33v1h1v-1zm-24 33v1h1v-1zm23 2v1h1v-1zm-34 7v1h1v-1zm35-33v1h1v-1zm-24 33v1h1v-1zm4-39v1h1v-1zm-3 13v1h1v-1zm23 2v1h1v-1zm-20-6v1h1v-1zm-3 13v1h1v-1zm2 19v1h1v-1zm23-17v1h1v-1zm-21-23v1h1v-1zm-19-4v1h1v-1zm6 34v1h1v-1zm24-21v1h1v-1zm-9 19v1h1v-1zm19 4v1h1v-1zm-29-27v1h1v-1zm30 1v1h1v-1zm-35 33v1h1v-1zm27-28v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-31-6v1h1v-1zm23 2v1h1v-1zm-31-6v1h1v-1zm23 2v1h1v-1zm19 13v1h1v-1zm-30-41v1h1v-1zm-11 9v1h1v-1zm34 2v1h1v-1zm-34 7v1h1v-1zm0 9v1h1v-1zm11 9v1h1v-1zm1-35v1h1v-1zm23 2v1h1v-1zm-2 30v1h1v-1zm-18-27v1h1v-1zm18 36v1h1v-1zm-10-23v1h1v-1zm-17 24v1h1v-1zm31-22v1h1v-1zm0 9v1h1v-1zm-19-4v1h1v-1zm-10-23v1h1v-1zm-5 34v1h1v-1zm23 2v1h1v-1zm-8-4v1h1v-1zm23-35v1h1v-1zm-4 39v1h1v-1zm-7-30v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-39-19v1h1v-1zm23 2v1h1v-1zm-15 11v1h1v-1zm15-2v1h1v-1zm0 9v1h1v-1zm9-22v1h1v-1zm-32 29v1h1v-1zm12-35v1h1v-1zm18 36v1h1v-1zm-10-23v1h1v-1zm3 24v1h1v-1zm-11-28v1h1v-1zm-11 9v1h1v-1zm23-17v1h1v-1zm2 19v1h1v-1zm-10-23v1h1v-1zm18 36v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-10-23v1h1v-1zm11-3v1h1v-1zm-20 31v1h1v-1zm20-22v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-15 11v1h1v-1zm23 11v1h1v-1zm12-35v1h1v-1zm-1 35v1h1v-1zm-34 7v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-11-37v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm16 17v1h1v-1zm-31-6v1h1v-1zm23 2v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm34 2v1h1v-1zm-19-4v1h1v-1zm-15 11v1h1v-1zm0 9v1h1v-1zm12-35v1h1v-1zm12 2v1h1v-1zm-32 29v1h1v-1zm1-35v1h1v-1zm40 36v1h1v-1zm-21-23v1h1v-1zm2 19v1h1v-1zm-21-23v1h1v-1zm32 32v1h1v-1zm-32-23v1h1v-1zm13 19v1h1v-1zm20-22v1h1v-1zm0 9v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-31-6v1h1v-1zm23 2v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm-3-24v1h1v-1zm34 2v1h1v-1zm-31 31v1h1v-1zm12-35v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-34 7v1h1v-1zm15-2v1h1v-1zm20-22v1h1v-1zm-20 31v1h1v-1zm23-35v1h1v-1zm-42 3v1h1v-1zm23 2v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm-13 21v1h1v-1zm5-25v1h1v-1zm-13 30v1h1v-1zm-10-23v1h1v-1zm30 1v1h1v-1zm11 9v1h1v-1zm-21-23v1h1v-1zm-9 19v1h1v-1zm-10-23v1h1v-1zm11 0v1h1v-1zm18 36v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-12 4v1h1v-1zm39 4v1h1v-1zm-15-34v1h1v-1zm8 22v1h1v-1zm-27-17v1h1v-1zm8 13v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm0 9v1h1v-1zm-3-24v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm34 2v1h1v-1zm-19-4v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-34 7v1h1v-1zm33-3v1h1v-1zm-6-25v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm6 34v1h1v-1zm-6-25v1h1v-1zm-13 30v1h1v-1zm20-22v1h1v-1zm11 0v1h1v-1zm-30-4v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-39 18v1h1v-1zm20-22v1h1v-1zm8 13v1h1v-1zm-16-17v1h1v-1zm27 17v1h1v-1zm-11 9v1h1v-1zm-31-6v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-18-39v1h1v-1zm11 9v1h1v-1zm8 13v1h1v-1zm-31-6v1h1v-1zm31 15v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm1-35v1h1v-1zm-23-2v1h1v-1zm34 2v1h1v-1zm-35 42v1h1v-1zm24-33v1h1v-1zm-2 30v1h1v-1zm-21-23v1h1v-1zm36 21v1h1v-1zm-21-23v1h1v-1zm18 8v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm-10-23v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm30 1v1h1v-1zm-12 35v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-42-6v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm16 26v1h1v-1zm11 0v1h1v-1zm-42-6v1h1v-1zm35-33v1h1v-1zm-23 35v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-23 7v1h1v-1zm4-2v1h1v-1zm-3-24v1h1v-1zm34 2v1h1v-1zm-19-4v1h1v-1zm-2 30v1h1v-1zm23 2v1h1v-1zm-12 7v1h1v-1zm9-22v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm-18-39v1h1v-1zm22 0v1h1v-1zm-3 13v1h1v-1zm-28 18v1h1v-1zm9-22v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm27 17v1h1v-1zm-30-4v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm1-26v1h1v-1zm-20 31v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm-12 7v1h1v-1zm23 2v1h1v-1zm-19-4v1h1v-1zm18 17v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm34 2v1h1v-1zm-42-6v1h1v-1zm31-22v1h1v-1zm-31 31v1h1v-1zm12-35v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm8 13v1h1v-1zm-16-17v1h1v-1zm27 17v1h1v-1zm-8-4v1h1v-1zm-3 13v1h1v-1zm-16-17v1h1v-1zm27 17v1h1v-1zm-27-8v1h1v-1zm8 13v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm16 17v1h1v-1zm3-4v1h1v-1zm-31 22v1h1v-1zm30 1v1h1v-1zm-18-27v1h1v-1zm-15 11v1h1v-1zm36 21v1h1v-1zm-21-23v1h1v-1zm0 9v1h1v-1zm22-3v1h1v-1zm-29-27v1h1v-1zm29 36v1h1v-1zm-33-25v1h1v-1zm13 28v1h1v-1zm24-33v1h1v-1zm-15 11v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm-20 31v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm12-35v1h1v-1zm-39 18v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm-20 31v1h1v-1zm12-35v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-27-17v1h1v-1zm-1 26v1h1v-1zm9-13v1h1v-1zm-16-17v1h1v-1zm27 17v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm20-22v1h1v-1zm-11 0v1h1v-1zm-28 18v1h1v-1zm-3-24v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm9-22v1h1v-1zm22 0v1h1v-1zm-1 23v1h1v-1zm-18-27v1h1v-1zm-15 11v1h1v-1zm37-5v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm11 0v1h1v-1zm-21-23v1h1v-1zm33 25v1h1v-1zm-23 7v1h1v-1zm12-35v1h1v-1zm-1 35v1h1v-1zm-8-4v1h1v-1zm20 6v1h1v-1zm-11-28v1h1v-1zm-19-4v1h1v-1zm22 0v1h1v-1zm-30-4v1h1v-1zm8 13v1h1v-1zm34 2v1h1v-1zm-15 11v1h1v-1zm-27-17v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm12-35v1h1v-1zm10 35v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm-30-4v1h1v-1zm12-35v1h1v-1zm-1 35v1h1v-1zm9-22v1h1v-1zm11 0v1h1v-1zm10 23v1h1v-1zm-29-27v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm0 9v1h1v-1zm30 1v1h1v-1zm-10-14v1h1v-1zm13 19v1h1v-1zm-15 11v1h1v-1zm-21-23v1h1v-1zm36 21v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm11 0v1h1v-1zm12 2v1h1v-1zm-12 7v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm0 9v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm-19 5v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm22 0v1h1v-1zm-30-4v1h1v-1zm41 1v1h1v-1zm-21-23v1h1v-1zm11 0v1h1v-1zm-20 31v1h1v-1zm30 1v1h1v-1zm-21-23v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm32 32v1h1v-1zm-19-4v1h1v-1zm9-22v1h1v-1zm-32 29v1h1v-1zm20-22v1h1v-1zm15-2v1h1v-1zm-15 11v1h1v-1zm-19-4v1h1v-1zm34 2v1h1v-1zm-15 11v1h1v-1zm-19-4v1h1v-1zm12-35v1h1v-1zm23 2v1h1v-1zm-24 33v1h1v-1zm12-26v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm12 2v1h1v-1zm-31-6v1h1v-1zm8 13v1h1v-1zm12-35v1h1v-1zm-1 35v1h1v-1zm-8-4v1h1v-1zm24-33v1h1v-1zm-23 7v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm10 23v1h1v-1zm-29-27v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm10 14v1h1v-1zm-29-18v1h1v-1zm30 1v1h1v-1zm-20 22v1h1v-1zm12-17v1h1v-1zm11 0v1h1v-1zm-32-23v1h1v-1zm11 0v1h1v-1zm18 36v1h1v-1zm-37-40v1h1v-1zm6 34v1h1v-1zm34 2v1h1v-1zm-19-4v1h1v-1zm9-22v1h1v-1zm3 5v1h1v-1zm-15 11v1h1v-1zm-19-4v1h1v-1zm34 2v1h1v-1zm-34 7v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm12-35v1h1v-1zm22 37v1h1v-1zm-22-28v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm-3 13v1h1v-1zm27-28v1h1v-1zm-2 30v1h1v-1zm-32-23v1h1v-1zm22 0v1h1v-1zm-19-4v1h1v-1zm-5 34v1h1v-1zm34 2v1h1v-1zm-40-27v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-31-6v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm8 13v1h1v-1zm-39-19v1h1v-1zm8 13v1h1v-1zm12-35v1h1v-1zm11 37v1h1v-1zm-19-32v1h1v-1zm0 9v1h1v-1zm0 9v1h1v-1zm23 2v1h1v-1zm-34 7v1h1v-1zm23-35v1h1v-1zm12 2v1h1v-1zm-24 33v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-7-30v1h1v-1zm11 9v1h1v-1zm9-22v1h1v-1zm13 19v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-10-23v1h1v-1zm-30-4v1h1v-1zm11 0v1h1v-1zm30 1v1h1v-1zm-35 33v1h1v-1zm34 2v1h1v-1zm1-26v1h1v-1zm-20 31v1h1v-1zm23-35v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-31-6v1h1v-1zm31 15v1h1v-1zm-39-19v1h1v-1zm8 13v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm24-33v1h1v-1zm-32 29v1h1v-1zm1-35v1h1v-1zm19 13v1h1v-1zm12 2v1h1v-1zm-31-6v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm22 6v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-21-23v1h1v-1zm11 0v1h1v-1zm-1 32v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm4-39v1h1v-1zm-3 13v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-42-6v1h1v-1zm23 2v1h1v-1zm-18 0v1h1v-1zm3 20v1h1v-1zm23 2v1h1v-1zm-31-6v1h1v-1zm23 2v1h1v-1zm-15 11v1h1v-1zm12-35v1h1v-1zm12 2v1h1v-1zm-9 31v1h1v-1zm-3-24v1h1v-1zm-20 31v1h1v-1zm12-35v1h1v-1zm23 2v1h1v-1zm-15 11v1h1v-1zm2 19v1h1v-1zm-10-14v1h1v-1zm2 19v1h1v-1zm20-22v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm-18-27v1h1v-1zm-9 19v1h1v-1zm27 17v1h1v-1zm-30-32v1h1v-1zm23 2v1h1v-1zm-20 31v1h1v-1zm20-22v1h1v-1zm11 0v1h1v-1zm-34 7v1h1v-1zm15-2v1h1v-1zm8 13v1h1v-1zm12-35v1h1v-1zm-1 35v1h1v-1zm-19-4v1h1v-1zm-15 11v1h1v-1zm35-33v1h1v-1zm-31-6v1h1v-1zm23 2v1h1v-1zm8 13v1h1v-1zm-16-17v1h1v-1zm-15 11v1h1v-1zm0 9v1h1v-1zm34 2v1h1v-1zm-42-6v1h1v-1zm23 2v1h1v-1zm-15 11v1h1v-1zm-8-4v1h1v-1zm30 1v1h1v-1zm11 0v1h1v-1zm-21-23v1h1v-1zm-19-4v1h1v-1zm40 36v1h1v-1zm-40-27v1h1v-1zm11 0v1h1v-1zm-1 32v1h1v-1zm23-35v1h1v-1zm-12 35v1h1v-1zm-8-4v1h1v-1zm1-26v1h1v-1zm11 0v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm0 9v1h1v-1zm-23 7v1h1v-1zm1-35v1h1v-1zm11 37v1h1v-1zm-11-28v1h1v-1zm23 2v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm-15 11v1h1v-1zm15-2v1h1v-1zm-15 11v1h1v-1zm35-33v1h1v-1zm-31-6v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-5 34v1h1v-1zm-6-25v1h1v-1zm-8-4v1h1v-1zm6 34v1h1v-1zm-21-23v1h1v-1zm15-2v1h1v-1zm18-1v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-21-23v1h1v-1zm-9 19v1h1v-1zm11 0v1h1v-1zm19 13v1h1v-1zm-39 9v1h1v-1zm-3-15v1h1v-1zm23 2v1h1v-1zm-10-23v1h1v-1zm21 23v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm12-35v1h1v-1zm-19 5v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-31-6v1h1v-1zm23 2v1h1v-1zm-19-4v1h1v-1zm-4 11v1h1v-1zm31 15v1h1v-1zm4-39v1h1v-1zm-31 31v1h1v-1zm31-22v1h1v-1zm-34 7v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm21 23v1h1v-1zm-32-33v1h1v-1zm34 2v1h1v-1zm-5 34v1h1v-1zm-37-40v1h1v-1zm18 36v1h1v-1zm-10-23v1h1v-1zm2 19v1h1v-1zm11 0v1h1v-1zm-21-23v1h1v-1zm41 1v1h1v-1zm-12 26v1h1v-1zm12-17v1h1v-1zm-30-4v1h1v-1zm19 13v1h1v-1zm11 0v1h1v-1zm-42-6v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm1-35v1h1v-1zm-20 31v1h1v-1zm20-22v1h1v-1zm0 9v1h1v-1zm-27-17v1h1v-1zm29 36v1h1v-1zm-33-25v1h1v-1zm4-2v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm-3-24v1h1v-1zm34 2v1h1v-1zm-31 31v1h1v-1zm18-1v1h1v-1zm13-21v1h1v-1zm2 19v1h1v-1zm-21-23v1h1v-1zm6 34v1h1v-1zm-21-23v1h1v-1zm24 19v1h1v-1zm12 2v1h1v-1zm-42 3v1h1v-1zm20-22v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-27-17v1h1v-1zm11 0v1h1v-1zm-21-23v1h1v-1zm2 19v1h1v-1zm16 17v1h1v-1zm3-4v1h1v-1zm-10-35v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm-3-24v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm19 4v1h1v-1zm-34 7v1h1v-1zm34 2v1h1v-1zm-3 13v1h1v-1zm-28 9v1h1v-1zm-3-15v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-15 11v1h1v-1zm16-37v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm2-6v1h1v-1zm-21 2v1h1v-1zm-8-4v1h1v-1zm0 9v1h1v-1zm-3-15v1h1v-1zm15-2v1h1v-1zm-13 30v1h1v-1zm22 0v1h1v-1zm12 2v1h1v-1zm-34 7v1h1v-1zm12-35v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm30 4v1h1v-1zm-7-30v1h1v-1zm20-13v1h1v-1zm-31 31v1h1v-1zm8 13v1h1v-1zm23-35v1h1v-1zm-39 18v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm7 39v1h1v-1zm1-26v1h1v-1zm-16-17v1h1v-1zm27 17v1h1v-1zm-42-6v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm16 17v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm19 4v1h1v-1zm-42 3v1h1v-1zm35-24v1h1v-1zm-31-6v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-19-4v1h1v-1zm6 34v1h1v-1zm27-28v1h1v-1zm-15 11v1h1v-1zm-21-23v1h1v-1zm2 19v1h1v-1zm34 2v1h1v-1zm-15 11v1h1v-1zm-27-8v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm12-35v1h1v-1zm10 35v1h1v-1zm-19-4v1h1v-1zm-11 9v1h1v-1zm23-35v1h1v-1zm-3 13v1h1v-1zm-16-17v1h1v-1zm27 17v1h1v-1zm-19-4v1h1v-1zm27 17v1h1v-1zm-24-22v1h1v-1zm8 14v1h1v-1zm8 13v1h1v-1zm-27-17v1h1v-1zm8 13v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm2 19v1h1v-1zm-11 3v1h1v-1zm1-26v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm12-35v1h1v-1zm18 36v1h1v-1zm-33-25v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm37-5v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-29-27v1h1v-1zm17 34v1h1v-1zm12 2v1h1v-1zm-31-6v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm-10-35v1h1v-1zm11 9v1h1v-1zm11 0v1h1v-1zm12 2v1h1v-1zm-15 11v1h1v-1zm-19-4v1h1v-1zm34 2v1h1v-1zm-42-6v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm22 0v1h1v-1zm12 2v1h1v-1zm-12 7v1h1v-1zm-10-35v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-27-17v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm-31 22v1h1v-1zm12-26v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm-27-8v1h1v-1zm8 13v1h1v-1zm31-22v1h1v-1zm-39 18v1h1v-1zm20-22v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm12-35v1h1v-1zm6 34v1h1v-1zm-21-23v1h1v-1zm36 21v1h1v-1zm-3 4v1h1v-1zm-18-27v1h1v-1zm-15 11v1h1v-1zm36 21v1h1v-1zm1-35v1h1v-1zm0 18v1h1v-1zm-31-6v1h1v-1zm0 9v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm12-35v1h1v-1zm-20 31v1h1v-1zm1-35v1h1v-1zm34 2v1h1v-1zm-12 7v1h1v-1zm12 2v1h1v-1zm-15 11v1h1v-1zm-27-17v1h1v-1zm8 13v1h1v-1zm34 2v1h1v-1zm-15 11v1h1v-1zm3-4v1h1v-1zm9-22v1h1v-1zm-28 18v1h1v-1zm-3 13v1h1v-1zm23-35v1h1v-1zm-11 0v1h1v-1zm19 13v1h1v-1zm-39 18v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm11 0v1h1v-1zm-1 26v1h1v-1zm-30 5v1h1v-1zm41 1v1h1v-1zm-37-40v1h1v-1zm40 36v1h1v-1zm-32-23v1h1v-1zm11 0v1h1v-1zm11-3v1h1v-1zm-9 31v1h1v-1zm-21-23v1h1v-1zm18 8v1h1v-1zm-21-23v1h1v-1zm36 21v1h1v-1zm-21-23v1h1v-1zm6 34v1h1v-1zm15-2v1h1v-1zm1-35v1h1v-1zm-24 42v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm-8 5v1h1v-1zm8 13v1h1v-1zm-8-4v1h1v-1zm-3 13v1h1v-1zm3-4v1h1v-1zm8 13v1h1v-1zm1-35v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-27-17v1h1v-1zm11 0v1h1v-1zm16 26v1h1v-1zm-16-17v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm11 9v1h1v-1zm-19-4v1h1v-1zm9-22v1h1v-1zm13 19v1h1v-1zm-10-23v1h1v-1zm10 32v1h1v-1zm11 0v1h1v-1zm-32-23v1h1v-1zm11 0v1h1v-1zm21 32v1h1v-1zm-10-35v1h1v-1zm-30 8v1h1v-1zm18-1v1h1v-1zm12 2v1h1v-1zm3-4v1h1v-1zm-15 11v1h1v-1zm12 2v1h1v-1zm-31-6v1h1v-1zm19 13v1h1v-1zm12 11v1h1v-1zm-31-6v1h1v-1zm12-35v1h1v-1zm11 0v1h1v-1zm12 2v1h1v-1zm-24 33v1h1v-1zm23 2v1h1v-1zm-11-28v1h1v-1zm-8-4v1h1v-1z" />
</g>
<g id="qr-code-v05-Q-short-path" transform="scale(27.02702702)">
<rect fill="#fff" height="37" width="37" /><path d="m15 21v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm0 9v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm3-4v1h1v-1zm-3 13v1h1v-1zm2 19v1h1v-1zm-10-23v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm0 9v1h1v-1zm33-3v1h1v-1zm-23 7v1h1v-1zm15-2v1h1v-1zm-21-23v1h1v-1zm30 1v1h1v-1zm-24 33v1h1v-1zm-8-4v1h1v-1zm23 2v1h1v-1zm-3-24v1h1v-1zm-19-4v1h1v-1zm31 15v1h1v-1zm-20-6v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm1-26v1h1v-1zm11 9v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-10-17v1h1v-1zm22 0v1h1v-1zm-30-4v1h1v-1zm11 0v1h1v-1zm-5 34v1h1v-1zm23 2v1h1v-1zm-29-27v1h1v-1zm33-3v1h1v-1zm0 9v1h1v-1zm-23 7v1h1v-1zm15-2v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm12-35v1h1v-1zm8 13v1h1v-1zm12 2v1h1v-1zm-31-6v1h1v-1zm31 15v1h1v-1zm-31-6v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm12-17v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm3-7v1h1v-1zm0 9v1h1v-1zm-8-4v1h1v-1zm12-8v1h1v-1zm-23 7v1h1v-1zm15 7v1h1v-1zm9-22v1h1v-1zm-20 3v1h1v-1zm-11 9v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm22-3v1h1v-1zm-10-23v1h1v-1zm-9 19v1h1v-1zm11 0v1h1v-1zm-10-23v1h1v-1zm-11 9v1h1v-1zm2 19v1h1v-1zm20-22v1h1v-1zm-23 16v1h1v-1zm0 9v1h1v-1zm4-30v1h1v-1zm-8 14v1h1v-1zm23 2v1h1v-1zm-15 11v1h1v-1zm24-24v1h1v-1zm-31-6v1h1v-1zm29 36v1h1v-1zm-8-4v1h1v-1zm-10-23v1h1v-1zm22 24v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-23 7v1h1v-1zm4-2v1h1v-1zm-3-24v1h1v-1zm23 11v1h1v-1zm-23 7v1h1v-1zm15-2v1h1v-1zm12-26v1h1v-1zm-23 7v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm-8-4v1h1v-1zm0 9v1h1v-1zm30 1v1h1v-1zm-10-23v1h1v-1zm2 19v1h1v-1zm11 0v1h1v-1zm-32-23v1h1v-1zm11 0v1h1v-1zm-1 32v1h1v-1zm23-35v1h1v-1zm0 9v1h1v-1zm-8-4v1h1v-1zm-19 5v1h1v-1zm-4 11v1h1v-1zm23 2v1h1v-1zm-19-4v1h1v-1zm8 13v1h1v-1zm-11-28v1h1v-1zm0 9v1h1v-1zm15-2v1h1v-1zm-15 20v1h1v-1zm4-30v1h1v-1zm23 2v1h1v-1zm-13 30v1h1v-1zm11 0v1h1v-1zm-21-23v1h1v-1zm22-3v1h1v-1zm-8 5v1h1v-1zm-11 9v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm23-26v1h1v-1zm-20 31v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-31-6v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-3-24v1h1v-1zm3 33v1h1v-1zm20-22v1h1v-1zm-23 7v1h1v-1zm14 2v1h1v-1zm-10-14v1h1v-1zm2 19v1h1v-1zm11 0v1h1v-1zm-21-23v1h1v-1zm30 1v1h1v-1zm-30 8v1h1v-1zm30 1v1h1v-1zm-28 18v1h1v-1zm9-22v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-31-6v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-31-6v1h1v-1zm34 2v1h1v-1zm-22 9v1h1v-1zm23-35v1h1v-1zm-1 35v1h1v-1zm1-26v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm-4 11v1h1v-1zm12 2v1h1v-1zm-12 7v1h1v-1zm1-26v1h1v-1zm15-2v1h1v-1zm-13 30v1h1v-1zm11 0v1h1v-1zm1-17v1h1v-1zm-8 5v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-19 5v1h1v-1zm9-22v1h1v-1zm22 0v1h1v-1zm-3 13v1h1v-1zm-19-4v1h1v-1zm3-4v1h1v-1zm-15 11v1h1v-1zm12 2v1h1v-1zm22 0v1h1v-1zm1-26v1h1v-1zm-20 31v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm-19-4v1h1v-1zm0 9v1h1v-1zm18 17v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm1-35v1h1v-1zm-20 31v1h1v-1zm12-26v1h1v-1zm0 9v1h1v-1zm8 13v1h1v-1zm-16-17v1h1v-1zm27 17v1h1v-1zm-8-4v1h1v-1zm9-22v1h1v-1zm-8 5v1h1v-1zm8 13v1h1v-1zm-16-17v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm11 0v1h1v-1zm-19 5v1h1v-1zm-1 4v1h1v-1zm11 0v1h1v-1zm-18-21v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-27-17v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-8-4v1h1v-1zm-11 9v1h1v-1zm23-35v1h1v-1zm-12 35v1h1v-1zm9-22v1h1v-1zm3-4v1h1v-1zm-3 13v1h1v-1zm-16-17v1h1v-1zm27 17v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm8 13v1h1v-1zm20-22v1h1v-1zm-28 18v1h1v-1zm-3-24v1h1v-1zm23 2v1h1v-1zm-20 31v1h1v-1zm31-22v1h1v-1zm-19 5v1h1v-1zm-1 4v1h1v-1zm0 9v1h1v-1zm11 0v1h1v-1zm-21-23v1h1v-1zm2 19v1h1v-1zm1-17v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm-11 9v1h1v-1zm22 0v1h1v-1zm-19-4v1h1v-1zm-3 13v1h1v-1zm12-35v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm11 0v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm-3 13v1h1v-1zm3-4v1h1v-1zm-11 9v1h1v-1zm22 6v1h1v-1zm-10-23v1h1v-1zm11 0v1h1v-1zm-13 30v1h1v-1zm-19-4v1h1v-1zm12-17v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm3-4v1h1v-1zm-3 13v1h1v-1zm3-4v1h1v-1zm9-22v1h1v-1zm3-4v1h1v-1zm-22 9v1h1v-1zm22 0v1h1v-1zm-22 9v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-30-4v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-11 9v1h1v-1zm12-35v1h1v-1zm0 9v1h1v-1zm11 9v1h1v-1zm-1 4v1h1v-1zm12 2v1h1v-1zm-12 7v1h1v-1zm-19 5v1h1v-1zm12-35v1h1v-1zm-1 35v1h1v-1zm1-26v1h1v-1zm3-4v1h1v-1zm-3 22v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-27 1v1h1v-1zm19 13v1h1v-1zm0 9v1h1v-1zm-7-21v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm6 34v1h1v-1zm-6-25v1h1v-1zm18 8v1h1v-1zm12 2v1h1v-1zm-31-6v1h1v-1zm31 15v1h1v-1zm-20-6v1h1v-1zm8 13v1h1v-1zm-7-21v1h1v-1zm3-4v1h1v-1zm-11 9v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm1-26v1h1v-1zm22 0v1h1v-1zm0 9v1h1v-1zm-30 5v1h1v-1zm11 0v1h1v-1zm22 6v1h1v-1zm-8-4v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-3-24v1h1v-1zm12 2v1h1v-1zm-31 3v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm4-21v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm-19 5v1h1v-1zm20-22v1h1v-1zm3 5v1h1v-1zm-30-4v1h1v-1zm6 34v1h1v-1zm15-2v1h1v-1zm4-30v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm24-33v1h1v-1zm-9 31v1h1v-1zm-3-24v1h1v-1zm12 2v1h1v-1zm-31-6v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm0 9v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm4-30v1h1v-1zm18 36v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-21-23v1h1v-1zm11 0v1h1v-1zm-12 13v1h1v-1zm0 9v1h1v-1zm23 2v1h1v-1zm-8-4v1h1v-1zm-11-28v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-31-6v1h1v-1zm23 2v1h1v-1zm0 9v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm-8-4v1h1v-1zm9-22v1h1v-1zm-32 29v1h1v-1zm30 1v1h1v-1zm-7 1v1h1v-1zm-3-24v1h1v-1zm0 9v1h1v-1zm-19-4v1h1v-1zm0 9v1h1v-1zm33-3v1h1v-1zm-19-4v1h1v-1zm0 18v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-3-24v1h1v-1zm23 11v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm15-2v1h1v-1zm-19-23v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm8 13v1h1v-1zm22-3v1h1v-1zm-10-23v1h1v-1zm-9 19v1h1v-1zm-10-23v1h1v-1zm32 32v1h1v-1zm-21-23v1h1v-1zm3 2v1h1v-1zm-12 7v1h1v-1zm12 2v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-22-28v1h1v-1zm15-2v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm0 9v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm4-30v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm15-2v1h1v-1zm-23 7v1h1v-1zm30 1v1h1v-1zm-7 1v1h1v-1zm7 8v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-22 9v1h1v-1zm1-23v1h1v-1zm2 19v1h1v-1zm-15 11v1h1v-1zm35-33v1h1v-1zm0 9v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm12-26v1h1v-1zm-15 11v1h1v-1zm15-2v1h1v-1zm-19-14v1h1v-1zm8 13v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm30 1v1h1v-1zm0 9v1h1v-1zm-8-4v1h1v-1zm-11 0v1h1v-1zm19 13v1h1v-1zm-16-17v1h1v-1zm-15 11v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm11 9v1h1v-1zm-19-4v1h1v-1zm1-17v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm-4 11v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm-22-28v1h1v-1zm3 33v1h1v-1zm12-26v1h1v-1zm-15 11v1h1v-1zm14 2v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm-18-27v1h1v-1zm2 19v1h1v-1zm8 13v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm-20 31v1h1v-1zm19 4v1h1v-1zm-22-28v1h1v-1zm12 2v1h1v-1zm3-4v1h1v-1zm-15 11v1h1v-1zm12 2v1h1v-1zm22 0v1h1v-1zm-19-4v1h1v-1zm16 17v1h1v-1zm-8-4v1h1v-1zm12-26v1h1v-1zm-8 5v1h1v-1zm8 13v1h1v-1zm-31-6v1h1v-1zm4-2v1h1v-1zm-4 11v1h1v-1zm1-17v1h1v-1zm2 19v1h1v-1zm-8 5v1h1v-1zm31-22v1h1v-1zm0 9v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-19 5v1h1v-1zm11 0v1h1v-1zm-20 31v1h1v-1zm20-22v1h1v-1zm8 13v1h1v-1zm-31-6v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-15 11v1h1v-1zm15-2v1h1v-1zm20-22v1h1v-1zm-31-6v1h1v-1zm12 2v1h1v-1zm-12 7v1h1v-1zm0 9v1h1v-1zm4-2v1h1v-1zm-3-15v1h1v-1zm13 19v1h1v-1zm-11 9v1h1v-1zm-8-4v1h1v-1zm20-22v1h1v-1zm-20 31v1h1v-1zm11 0v1h1v-1zm12-26v1h1v-1zm-19-4v1h1v-1zm8 22v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm-28 18v1h1v-1zm8 13v1h1v-1zm23-35v1h1v-1zm-12 35v1h1v-1zm9-22v1h1v-1zm2 19v1h1v-1zm-33-16v1h1v-1zm34 2v1h1v-1zm-19-4v1h1v-1zm-15 11v1h1v-1zm15-2v1h1v-1zm-1 4v1h1v-1zm11 0v1h1v-1zm-17-25v1h1v-1zm6 34v1h1v-1zm3-4v1h1v-1zm-10-26v1h1v-1zm0 9v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-30-4v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm19 13v1h1v-1zm1-26v1h1v-1zm0 9v1h1v-1zm0 9v1h1v-1zm-16-17v1h1v-1zm19 13v1h1v-1zm0 9v1h1v-1zm-10-26v1h1v-1zm21 32v1h1v-1zm-33-25v1h1v-1zm15-2v1h1v-1zm-15 11v1h1v-1zm25 2v1h1v-1zm-19-4v1h1v-1zm8 13v1h1v-1zm3-4v1h1v-1zm9-22v1h1v-1zm3-4v1h1v-1zm-22 9v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-16-17v1h1v-1zm19 13v1h1v-1zm-30-4v1h1v-1zm20-22v1h1v-1zm-20 31v1h1v-1zm11 0v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-27-17v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-19 5v1h1v-1zm18 8v1h1v-1zm12 2v1h1v-1zm-33-25v1h1v-1zm2 28v1h1v-1zm11 0v1h1v-1zm1-26v1h1v-1zm11 9v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm-9 31v1h1v-1zm-10-26v1h1v-1zm22 0v1h1v-1zm-30-4v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm8 13v1h1v-1zm-27-17v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm-30-4v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm11 0v1h1v-1zm2 19v1h1v-1zm-21-23v1h1v-1zm21 32v1h1v-1zm1-26v1h1v-1zm-30 8v1h1v-1zm30 1v1h1v-1zm0 9v1h1v-1zm-31-6v1h1v-1zm11 9v1h1v-1zm12-26v1h1v-1z" />
</g>
<g id="qr-code-v02-Q-optimized-tiny" transform="scale(40)">
<rect fill="#fff" height="25" width="25" /><path d="m19 0v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm0 9v1h1v-1zm0 9v1h1v-1zm11 0v1h1v-1zm-7-21v1h1v-1zm18 8v1h1v-1zm-19-4v1h1v-1zm0 9v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm12-17v1h1v-1zm3-4v1h1v-1zm0 9v1h1v-1zm0 9v1h1v-1zm-18-21v1h1v-1zm10 22v1h1v-1zm4-21v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm12-8v1h1v-1zm-8 14v1h1v-1zm11 0v1h1v-1zm-7-12v1h1v-1zm-1 4v1h1v-1zm-8 14v1h1v-1zm20-22v1h1v-1zm0 18v1h1v-1zm-8 5v1h1v-1zm4-21v1h1v-1zm-11 9v1h1v-1zm-1 13v1h1v-1zm4-21v1h1v-1zm0 9v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm12-17v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm3 2v1h1v-1zm-11-10v1h1v-1zm15-2v1h1v-1zm-15 11v1h1v-1zm15-2v1h1v-1zm-11-10v1h1v-1zm-8-4v1h1v-1zm0 18v1h1v-1zm1-17v1h1v-1zm11 0v1h1v-1zm3 11v1h1v-1zm-8 5v1h1v-1zm-3-15v1h1v-1zm15-2v1h1v-1zm-15 11v1h1v-1zm0 9v1h1v-1zm15-2v1h1v-1zm-11-19v1h1v-1zm-8 14v1h1v-1zm22-3v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm-7 6v1h1v-1zm-3-24v1h1v-1zm0 18v1h1v-1zm14 2v1h1v-1zm-10-14v1h1v-1zm-8-4v1h1v-1zm0 9v1h1v-1zm11-3v1h1v-1zm11 9v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm15-2v1h1v-1zm-7-21v1h1v-1zm-3 3v1h1v-1zm15-2v1h1v-1zm-1 13v1h1v-1zm3 5v1h1v-1zm1-17v1h1v-1zm-23 7v1h1v-1zm12 2v1h1v-1zm0 9v1h1v-1zm3-4v1h1v-1zm-7-3v1h1v-1zm11-4v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-3-6v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm-8 1v1h1v-1zm0 9v1h1v-1zm-8-4v1h1v-1zm12-17v1h1v-1zm8 13v1h1v-1zm-16-8v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm0 9v1h1v-1zm-3-24v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm15-2v1h1v-1zm-1 4v1h1v-1zm-10-14v1h1v-1zm14 2v1h1v-1zm-19-4v1h1v-1zm8 13v1h1v-1zm-8 5v1h1v-1zm20-22v1h1v-1zm0 9v1h1v-1zm0 9v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm20-4v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm14 2v1h1v-1zm0 9v1h1v-1zm3-4v1h1v-1zm-10-17v1h1v-1zm0 9v1h1v-1zm12-8v1h1v-1zm3-4v1h1v-1zm-3 13v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm-1 4v1h1v-1zm-7-12v1h1v-1zm3-4v1h1v-1zm0 9v1h1v-1zm-10-8v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm0 9v1h1v-1zm11 0v1h1v-1zm1-17v1h1v-1zm-8-4v1h1v-1zm0 9v1h1v-1zm18 8v1h1v-1zm-19-4v1h1v-1zm0 9v1h1v-1zm12-17v1h1v-1zm3-4v1h1v-1zm0 9v1h1v-1zm-10 1v1h1v-1zm11 0v1h1v-1zm-19 5v1h1v-1zm11 0v1h1v-1zm-1 4v1h1v-1zm12-17v1h1v-1zm-8 5v1h1v-1zm0 9v1h1v-1zm1-17v1h1v-1zm3 5v1h1v-1zm-3 13v1h1v-1zm3-4v1h1v-1zm1-8v1h1v-1zm-19-4v1h1v-1zm10 13v1h1v-1zm0 9v1h1v-1zm-8-4v1h1v-1zm1-17v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm0 9v1h1v-1zm12-17v1h1v-1zm3-4v1h1v-1zm0 18v1h1v-1zm3-7v1h1v-1zm-15 11v1h1v-1zm-4-16v1h1v-1zm0 9v1h1v-1zm0 9v1h1v-1zm20-22v1h1v-1zm0 9v1h1v-1zm-8-4v1h1v-1zm4-3v1h1v-1zm-12 4v1h1v-1zm15-2v1h1v-1zm0 9v1h1v-1zm0 9v1h1v-1zm-11-19v1h1v-1zm12 1v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm3 11v1h1v-1zm0 9v1h1v-1zm-11-19v1h1v-1zm0 9v1h1v-1zm0 9v1h1v-1zm4-12v1h1v-1zm0 9v1h1v-1zm4-12v1h1v-1zm-5-2v1h1v-1zm-4 11v1h1v-1zm4-2v1h1v-1zm-4 11v1h1v-1zm1-8v1h1v-1zm4-12v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-8-4v1h1v-1zm0 9v1h1v-1zm22 6v1h1v-1zm-8-4v1h1v-1zm1-8v1h1v-1zm-8 5v1h1v-1zm-3-15v1h1v-1zm15-2v1h1v-1zm0 9v1h1v-1zm-15 11v1h1v-1zm6-2v1h1v-1zm-10-14v1h1v-1zm11-3v1h1v-1zm11 0v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-8-4v1h1v-1zm0 9v1h1v-1zm0 9v1h1v-1zm1-8v1h1v-1zm-8 5v1h1v-1zm-3-15v1h1v-1zm15-2v1h1v-1zm-1 13v1h1v-1zm-8-4v1h1v-1zm11 9v1h1v-1zm-19-4v1h1v-1zm20-13v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm12 11v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm-11-19v1h1v-1zm4 7v1h1v-1zm-4 11v1h1v-1zm1-17v1h1v-1zm13 19v1h1v-1zm1-17v1h1v-1zm0 9v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-16-17v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm0 9v1h1v-1zm20-22v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-8-4v1h1v-1zm-11-1v1h1v-1zm3-7v1h1v-1zm11 9v1h1v-1zm-19 5v1h1v-1zm12-17v1h1v-1zm8 13v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-10-8v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm14 11v1h1v-1zm-15-16v1h1v-1zm11 9v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm-16 1v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-19 5v1h1v-1zm-3-15v1h1v-1zm6 7v1h1v-1zm11 9v1h1v-1zm-10-17v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm9-13v1h1v-1zm-8-4v1h1v-1zm10 13v1h1v-1zm0 9v1h1v-1zm-19-4v1h1v-1zm12-8v1h1v-1zm3-4v1h1v-1z" />
</g>
<g id="qr-code-v07-Q-signed" transform="scale(22.2222222222)">
<rect fill="#fff" height="45" width="45" /><path d="m26 21v1h1v-1zm-8 5v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm-10-35v1h1v-1zm22 0v1h1v-1zm12 2v1h1v-1zm-13 30v1h1v-1zm-11 12v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm12 2v1h1v-1zm-42-6v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-1 23v1h1v-1zm-29-27v1h1v-1zm11 0v1h1v-1zm30 1v1h1v-1zm-1 26v1h1v-1zm-29-18v1h1v-1zm30 1v1h1v-1zm3-4v1h1v-1zm-3 22v1h1v-1zm-37-40v1h1v-1zm29 36v1h1v-1zm11 0v1h1v-1zm-42-6v1h1v-1zm20-22v1h1v-1zm12 2v1h1v-1zm-32 29v1h1v-1zm1-35v1h1v-1zm34 2v1h1v-1zm-15 11v1h1v-1zm12 2v1h1v-1zm3-4v1h1v-1zm-34 7v1h1v-1zm34 2v1h1v-1zm1-26v1h1v-1zm-35 42v1h1v-1zm12-35v1h1v-1zm23 2v1h1v-1zm-24 33v1h1v-1zm1-26v1h1v-1zm23 2v1h1v-1zm-31-6v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-9 19v1h1v-1zm12-17v1h1v-1zm-10-23v1h1v-1zm11 0v1h1v-1zm-30-4v1h1v-1zm11 0v1h1v-1zm19 13v1h1v-1zm-9 19v1h1v-1zm-15 2v1h1v-1zm5-25v1h1v-1zm30 1v1h1v-1zm-1 26v1h1v-1zm-19 5v1h1v-1zm-15 2v1h1v-1zm35-24v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-8-4v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-42-6v1h1v-1zm31 15v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-11-37v1h1v-1zm0 9v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm0 9v1h1v-1zm0 9v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm12 2v1h1v-1zm-2 30v1h1v-1zm-18-27v1h1v-1zm10 32v1h1v-1zm-10-23v1h1v-1zm11 6v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm8 13v1h1v-1zm-8-4v1h1v-1zm-10-23v1h1v-1zm-5 34v1h1v-1zm34 2v1h1v-1zm-19-4v1h1v-1zm23-35v1h1v-1zm-44 42v1h1v-1zm10-35v1h1v-1zm23 2v1h1v-1zm11 9v1h1v-1zm-3 13v1h1v-1zm-16-17v1h1v-1zm-15 20v1h1v-1zm12-35v1h1v-1zm-20 31v1h1v-1zm23 2v1h1v-1zm-15 11v1h1v-1zm-8-4v1h1v-1zm23 2v1h1v-1zm-22-28v1h1v-1zm11 0v1h1v-1zm23 2v1h1v-1zm-34 7v1h1v-1zm11 9v1h1v-1zm22-3v1h1v-1zm-10-23v1h1v-1zm2 19v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm-10-23v1h1v-1zm11 0v1h1v-1zm-9 19v1h1v-1zm20-13v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm1-35v1h1v-1zm-20 31v1h1v-1zm-15 11v1h1v-1zm34 2v1h1v-1zm-19-4v1h1v-1zm-11-37v1h1v-1zm34 2v1h1v-1zm-19-4v1h1v-1zm-15 11v1h1v-1zm34 2v1h1v-1zm-34 7v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-34 7v1h1v-1zm12-26v1h1v-1zm12 2v1h1v-1zm-32 29v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-9 19v1h1v-1zm11 0v1h1v-1zm-21-23v1h1v-1zm11 0v1h1v-1zm18 27v1h1v-1zm11 0v1h1v-1zm-30 5v1h1v-1zm-10-23v1h1v-1zm11 0v1h1v-1zm21 23v1h1v-1zm1-26v1h1v-1zm0 9v1h1v-1zm-31 3v1h1v-1zm31 15v1h1v-1zm-19-4v1h1v-1zm12-35v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm-3-24v1h1v-1zm34 2v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm-34 7v1h1v-1zm15-2v1h1v-1zm20-22v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-31-6v1h1v-1zm34 2v1h1v-1zm-42-6v1h1v-1zm18 36v1h1v-1zm-18-18v1h1v-1zm41 1v1h1v-1zm-41 8v1h1v-1zm30 1v1h1v-1zm11 0v1h1v-1zm-21-23v1h1v-1zm2 19v1h1v-1zm11 0v1h1v-1zm-32-23v1h1v-1zm11 0v1h1v-1zm-1 32v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-12 4v1h1v-1zm12 5v1h1v-1zm12-35v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-27-17v1h1v-1zm-4 11v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm8 13v1h1v-1zm-11-37v1h1v-1zm23 2v1h1v-1zm-1 35v1h1v-1zm-22-28v1h1v-1zm34 2v1h1v-1zm-11 9v1h1v-1zm13 19v1h1v-1zm-36-12v1h1v-1zm15-2v1h1v-1zm2 10v1h1v-1zm8 0v1h1v-1zm13-36v1h1v-1zm-19-4v1h1v-1zm-5 34v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm1-26v1h1v-1zm11 0v1h1v-1zm-30-4v1h1v-1zm30 13v1h1v-1zm-39 18v1h1v-1zm9-22v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm27 17v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-16-17v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-31-6v1h1v-1zm12 2v1h1v-1zm-8 5v1h1v-1zm8 13v1h1v-1zm12-35v1h1v-1zm-23-2v1h1v-1zm34 2v1h1v-1zm-12 35v1h1v-1zm-23 7v1h1v-1zm24-33v1h1v-1zm-23 7v1h1v-1zm15-2v1h1v-1zm18 8v1h1v-1zm-19-4v1h1v-1zm-10-14v1h1v-1zm2 19v1h1v-1zm-10-23v1h1v-1zm18 36v1h1v-1zm11 0v1h1v-1zm-29-27v1h1v-1zm30 1v1h1v-1zm-9 31v1h1v-1zm-10-35v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm3-4v1h1v-1zm-3 13v1h1v-1zm-16-17v1h1v-1zm-15 11v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm27 17v1h1v-1zm-30-4v1h1v-1zm23-35v1h1v-1zm-1 35v1h1v-1zm-19-4v1h1v-1zm-3 13v1h1v-1zm23-35v1h1v-1zm-20 31v1h1v-1zm12-35v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-8-4v1h1v-1zm0 9v1h1v-1zm-3-24v1h1v-1zm34 2v1h1v-1zm2 19v1h1v-1zm-23 16v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm9-22v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm3-4v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm1-35v1h1v-1zm22 0v1h1v-1zm-31 31v1h1v-1zm31-22v1h1v-1zm-19-4v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm27 17v1h1v-1zm-8-4v1h1v-1zm-19-4v1h1v-1zm20-22v1h1v-1zm-20 31v1h1v-1zm20-22v1h1v-1zm-9 9v1h1v-1zm15-2v1h1v-1zm-12 7v1h1v-1zm12 2v1h1v-1zm-12 7v1h1v-1zm1-35v1h1v-1zm-31 31v1h1v-1zm11 0v1h1v-1zm20-22v1h1v-1zm-31 31v1h1v-1zm12-35v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm8 13v1h1v-1zm-27-17v1h1v-1zm8 13v1h1v-1zm3-4v1h1v-1zm-3 13v1h1v-1zm-16-17v1h1v-1zm27 17v1h1v-1zm1-26v1h1v-1zm-20 31v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm27 17v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm-31 22v1h1v-1zm12-26v1h1v-1zm18 27v1h1v-1zm-10-14v1h1v-1zm11 0v1h1v-1zm2 19v1h1v-1zm-21-23v1h1v-1zm-15 11v1h1v-1zm8-32v1h1v-1zm6 34v1h1v-1zm23 2v1h1v-1zm-29-27v1h1v-1zm17 34v1h1v-1zm-8-4v1h1v-1zm24-33v1h1v-1zm-15 11v1h1v-1zm-8-4v1h1v-1zm23 2v1h1v-1zm-34 7v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm-12 35v1h1v-1zm-8-4v1h1v-1zm23 2v1h1v-1zm-3-24v1h1v-1zm-39 18v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm23-35v1h1v-1zm-12 35v1h1v-1zm19 4v1h1v-1zm-7-30v1h1v-1zm-3 13v1h1v-1zm-16-17v1h1v-1zm27 17v1h1v-1zm-11 9v1h1v-1zm-16-17v1h1v-1zm19 13v1h1v-1zm9-22v1h1v-1zm11 0v1h1v-1zm-39 18v1h1v-1zm-3-24v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm-3-24v1h1v-1zm12 2v1h1v-1zm22 0v1h1v-1zm-19-4v1h1v-1zm18 27v1h1v-1zm-33-16v1h1v-1zm37 4v1h1v-1zm-23 7v1h1v-1zm-10-23v1h1v-1zm13 19v1h1v-1zm8 13v1h1v-1zm12 2v1h1v-1zm-11-28v1h1v-1zm-19-4v1h1v-1zm34 2v1h1v-1zm-15 11v1h1v-1zm-27-17v1h1v-1zm8 13v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-27-17v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm12-35v1h1v-1zm10 35v1h1v-1zm9-22v1h1v-1zm-39 18v1h1v-1zm20-22v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm11 0v1h1v-1zm30 1v1h1v-1zm-21-23v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-8 5v1h1v-1zm20-22v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm6 34v1h1v-1zm-21-23v1h1v-1zm36 21v1h1v-1zm-3 4v1h1v-1zm-31 3v1h1v-1zm17-1v1h1v-1zm-13-20v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm12 2v1h1v-1zm-31-6v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm-22 9v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm31 6v1h1v-1zm-34 7v1h1v-1zm22 0v1h1v-1zm-3 4v1h1v-1zm-16-8v1h1v-1zm8 13v1h1v-1zm22-3v1h1v-1zm-21-23v1h1v-1zm-9 31v1h1v-1zm12-35v1h1v-1zm18 36v1h1v-1zm-21-23v1h1v-1zm11 0v1h1v-1zm2 19v1h1v-1zm-10-23v1h1v-1zm21 23v1h1v-1zm-40-18v1h1v-1zm30 1v1h1v-1zm-32 29v1h1v-1zm20-22v1h1v-1zm15-2v1h1v-1zm-15 11v1h1v-1zm12 2v1h1v-1zm-20-6v1h1v-1zm23 2v1h1v-1zm-15 11v1h1v-1zm12 2v1h1v-1zm-31-6v1h1v-1zm35-33v1h1v-1zm-24 33v1h1v-1zm1-26v1h1v-1zm11 0v1h1v-1zm12 2v1h1v-1zm-20-6v1h1v-1zm0 9v1h1v-1zm-11 9v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-8-4v1h1v-1zm35-33v1h1v-1zm-24 42v1h1v-1zm24-33v1h1v-1zm-15 11v1h1v-1zm-27-17v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm21 23v1h1v-1zm-29-27v1h1v-1zm19 13v1h1v-1zm-9 19v1h1v-1zm20-22v1h1v-1zm0 9v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-21-23v1h1v-1zm2 19v1h1v-1zm16 17v1h1v-1zm-37-40v1h1v-1zm6 34v1h1v-1zm15 7v1h1v-1zm-3-24v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm23 11v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-15 11v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm23 2v1h1v-1zm-19-41v1h1v-1zm-3 13v1h1v-1zm23 2v1h1v-1zm-20-6v1h1v-1zm-3 13v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm12-26v1h1v-1zm23 2v1h1v-1zm-34 7v1h1v-1zm11 0v1h1v-1zm-13 30v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-40-27v1h1v-1zm41 1v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-34 7v1h1v-1zm23 2v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-3 13v1h1v-1zm-39-19v1h1v-1zm8 13v1h1v-1zm23 2v1h1v-1zm-8-4v1h1v-1zm9-22v1h1v-1zm-31 3v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm15-2v1h1v-1zm-34 7v1h1v-1zm35-33v1h1v-1zm-35 42v1h1v-1zm12-35v1h1v-1zm11 0v1h1v-1zm-12 35v1h1v-1zm4-39v1h1v-1zm18 36v1h1v-1zm-9-40v1h1v-1zm2 19v1h1v-1zm11 0v1h1v-1zm-11 9v1h1v-1zm1-23v1h1v-1zm-30-4v1h1v-1zm41 1v1h1v-1zm-35 33v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-19 5v1h1v-1zm23-35v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm-11 9v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-42-6v1h1v-1zm23 2v1h1v-1zm-15 11v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm12 2v1h1v-1zm-31-6v1h1v-1zm11 0v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm22 6v1h1v-1zm-21-23v1h1v-1zm11 0v1h1v-1zm10 32v1h1v-1zm-10-23v1h1v-1zm-9 19v1h1v-1zm-10-23v1h1v-1zm10 32v1h1v-1zm12-26v1h1v-1zm-23 7v1h1v-1zm34 2v1h1v-1zm-19-4v1h1v-1zm8 13v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-7-39v1h1v-1zm11 0v1h1v-1zm-34 7v1h1v-1zm23 2v1h1v-1zm8 13v1h1v-1zm-39-19v1h1v-1zm5 2v1h1v-1zm26 13v1h1v-1zm-8-4v1h1v-1zm19 13v1h1v-1zm-10-26v1h1v-1zm-2 30v1h1v-1zm-10-23v1h1v-1zm-20 31v1h1v-1zm35-33v1h1v-1zm-15 11v1h1v-1zm2 19v1h1v-1zm11 0v1h1v-1zm-21-23v1h1v-1zm0 9v1h1v-1zm2 19v1h1v-1zm20-22v1h1v-1zm-18-18v1h1v-1zm18 36v1h1v-1zm-8-4v1h1v-1zm-19-4v1h1v-1zm-3-24v1h1v-1zm3 33v1h1v-1zm31-22v1h1v-1zm-34 7v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm-20 31v1h1v-1zm-15 11v1h1v-1zm35-33v1h1v-1zm0 9v1h1v-1zm-39-10v1h1v-1zm8 13v1h1v-1zm33 6v1h1v-1zm-21-23v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm18 36v1h1v-1zm-8-4v1h1v-1zm11 0v1h1v-1zm-21-23v1h1v-1zm29 27v1h1v-1zm-30 5v1h1v-1zm22 0v1h1v-1zm-19-4v1h1v-1zm20-13v1h1v-1zm-31-6v1h1v-1zm12 11v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm-4 11v1h1v-1zm1-35v1h1v-1zm0 9v1h1v-1zm34 11v1h1v-1zm-34 7v1h1v-1zm15-2v1h1v-1zm-15 11v1h1v-1zm35-33v1h1v-1zm-31-6v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-5 34v1h1v-1zm5-25v1h1v-1zm-19-4v1h1v-1zm-5 34v1h1v-1zm11 0v1h1v-1zm-6-25v1h1v-1zm18-1v1h1v-1zm-41 8v1h1v-1zm41 1v1h1v-1zm-39 18v1h1v-1zm20-22v1h1v-1zm8 13v1h1v-1zm-31-6v1h1v-1zm12 2v1h1v-1zm1-23v1h1v-1zm2 19v1h1v-1zm8 4v1h1v-1zm0 9v1h1v-1zm12-35v1h1v-1zm-1 35v1h1v-1zm-3 4v1h1v-1zm11 0v1h1v-1zm-26-34v1h1v-1zm19 13v1h1v-1zm-27-17v1h1v-1zm-4 11v1h1v-1zm31 15v1h1v-1zm-31-6v1h1v-1zm12 2v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm19 13v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm-34 7v1h1v-1zm36 21v1h1v-1zm-21-23v1h1v-1zm18 17v1h1v-1zm-19-4v1h1v-1zm-10-23v1h1v-1zm34 2v1h1v-1zm-5 34v1h1v-1zm-37-40v1h1v-1zm23 2v1h1v-1zm-15 11v1h1v-1zm2 19v1h1v-1zm8 4v1h1v-1zm23-26v1h1v-1zm-20 31v1h1v-1zm20-22v1h1v-1zm-19-4v1h1v-1zm-8-4v1h1v-1zm-15 11v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm-8-4v1h1v-1zm19 4v1h1v-1zm-34 7v1h1v-1zm12 2v1h1v-1zm22 0v1h1v-1zm-3 4v1h1v-1zm-19 5v1h1v-1zm23-35v1h1v-1zm-12 35v1h1v-1zm11 0v1h1v-1zm1-26v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-27-17v1h1v-1zm29 36v1h1v-1zm-21-23v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-22-28v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm21 32v1h1v-1zm-21-23v1h1v-1zm-15 11v1h1v-1zm24 19v1h1v-1zm9-22v1h1v-1zm-39 27v1h1v-1zm20-22v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm19 13v1h1v-1zm-16-17v1h1v-1zm-21-23v1h1v-1zm30 1v1h1v-1zm-28 18v1h1v-1zm8 13v1h1v-1zm12-35v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-39 18v1h1v-1zm19 4v1h1v-1zm-22-28v1h1v-1zm12 2v1h1v-1zm22 0v1h1v-1zm-19-4v1h1v-1zm16 17v1h1v-1zm-19-4v1h1v-1zm22 0v1h1v-1zm8 4v1h1v-1zm-11 9v1h1v-1zm-28 9v1h1v-1zm20-13v1h1v-1zm-23 7v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-34 7v1h1v-1zm15-2v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-31-6v1h1v-1zm23 2v1h1v-1zm-19-4v1h1v-1zm-4 20v1h1v-1zm1-26v1h1v-1zm2 19v1h1v-1zm13-21v1h1v-1zm6 34v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm20-22v1h1v-1zm-39 18v1h1v-1zm8 13v1h1v-1zm12-35v1h1v-1zm-1 35v1h1v-1zm-19-4v1h1v-1zm23-26v1h1v-1zm-11 24v1h1v-1zm19-11v1h1v-1zm0 9v1h1v-1zm12-35v1h1v-1zm-20 31v1h1v-1zm8 13v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-16-17v1h1v-1zm27 17v1h1v-1zm-42-6v1h1v-1zm23 2v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm16 17v1h1v-1zm-31-6v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm-23 7v1h1v-1zm15-2v1h1v-1zm20-22v1h1v-1zm2 19v1h1v-1zm-33-25v1h1v-1zm23 2v1h1v-1zm-5 40v1h1v-1zm15-8v1h1v-1zm-33-25v1h1v-1zm12 2v1h1v-1zm11 0v1h1v-1zm-13 30v1h1v-1zm11 0v1h1v-1zm1-26v1h1v-1zm-21-14v1h1v-1zm2 19v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm12 2v1h1v-1zm-15 11v1h1v-1zm-19-4v1h1v-1zm22 0v1h1v-1zm12 2v1h1v-1zm-42-6v1h1v-1zm8 13v1h1v-1zm23-35v1h1v-1zm-20 31v1h1v-1zm20-22v1h1v-1zm8 13v1h1v-1zm-27-17v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-16-17v1h1v-1zm35 30v1h1v-1zm-27-17v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-16-17v1h1v-1zm19 13v1h1v-1zm8 13v1h1v-1zm1-35v1h1v-1zm-28 18v1h1v-1zm19 13v1h1v-1zm9-22v1h1v-1zm2 19v1h1v-1zm-11 12v1h1v-1zm1-35v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm30 1v1h1v-1zm-33-25v1h1v-1zm23 2v1h1v-1zm-23 7v1h1v-1zm37-5v1h1v-1zm-23 7v1h1v-1zm23 2v1h1v-1zm-29-27v1h1v-1zm29 36v1h1v-1zm-33-25v1h1v-1zm13 28v1h1v-1zm-10-35v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm8 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm-28 18v1h1v-1zm31 6v1h1v-1zm-22-28v1h1v-1zm10 35v1h1v-1zm9-22v1h1v-1zm-27-17v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm-31 22v1h1v-1zm12-26v1h1v-1zm8 13v1h1v-1zm-8-4v1h1v-1zm0 9v1h1v-1zm31-22v1h1v-1zm-39 18v1h1v-1zm30 1v1h1v-1zm-10-23v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm30 1v1h1v-1zm3 5v1h1v-1zm1-35v1h1v-1zm-23 16v1h1v-1zm23 2v1h1v-1zm-31-6v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm11 0v1h1v-1zm8 13v1h1v-1zm1-35v1h1v-1zm-9 31v1h1v-1zm-10-35v1h1v-1zm34 2v1h1v-1zm0 9v1h1v-1zm-42-6v1h1v-1zm8 13v1h1v-1zm11 0v1h1v-1zm11 0v1h1v-1zm-19-4v1h1v-1zm31 6v1h1v-1zm-34 7v1h1v-1zm22 0v1h1v-1zm9-22v1h1v-1zm-31 31v1h1v-1zm12-35v1h1v-1zm-1 35v1h1v-1zm20-22v1h1v-1zm-9 22v1h1v-1zm-10-26v1h1v-1zm11 0v1h1v-1zm-31 31v1h1v-1zm11 0v1h1v-1zm30 1v1h1v-1zm-37-40v1h1v-1zm29 36v1h1v-1zm-21-23v1h1v-1zm22-3v1h1v-1zm-9 31v1h1v-1zm-21-23v1h1v-1zm30 1v1h1v-1zm3-4v1h1v-1zm-36-12v1h1v-1zm21 32v1h1v-1zm12 2v1h1v-1zm-20-6v1h1v-1zm24-33v1h1v-1zm-1 35v1h1v-1zm-22-19v1h1v-1zm0 9v1h1v-1zm11 0v1h1v-1zm0 9v1h1v-1zm1-35v1h1v-1zm-1 44v1h1v-1zm-7-39v1h1v-1zm8 13v1h1v-1zm-27-17v1h1v-1zm19 13v1h1v-1zm8 13v1h1v-1zm-27-17v1h1v-1zm11 0v1h1v-1zm-3 13v1h1v-1zm-8-4v1h1v-1zm30 13v1h1v-1zm-30-4v1h1v-1zm11 0v1h1v-1zm9-22v1h1v-1zm13 19v1h1v-1zm-21-23v1h1v-1zm11 0v1h1v-1zm18 36v1h1v-1zm-8-4v1h1v-1zm-21-23v1h1v-1zm11 0v1h1v-1zm2 19v1h1v-1zm19 13v1h1v-1zm-19-4v1h1v-1zm-21-23v1h1v-1zm33-3v1h1v-1zm-15 11v1h1v-1zm12 2v1h1v-1zm-31-6v1h1v-1zm19 13v1h1v-1zm-8-4v1h1v-1zm23 2v1h1v-1zm-15 11v1h1v-1zm-19-4v1h1v-1zm35-33v1h1v-1zm-24 33v1h1v-1zm23 2v1h1v-1zm-11-28v1h1v-1zm-8-4v1h1v-1z" />
</g>
</defs>
</svg>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:a4" role="doc-endnote">
<p>The page is nominally A4, but I’ve seen some instances where the scaling has been mixed up, and the “poster” is printed at A5 or smaller (on an A4 page). This is really hard to scan, especially when placed behind (reflective) glass in an outdoor shop window. <a href="#fnref:a4" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:quiet-zone" role="doc-endnote">
<p>Deleting the blue box and particularly the region of white within it sacrifices the “quiet zone”: QR codes are meant to be surrounded by a zone of simple background four modules wide. However, this ends up being a lot of overhead, and <a href="https://qrworld.wordpress.com/2011/08/09/the-quiet-zone/">doesn’t seem to be necessary in practice</a>. <a href="#fnref:quiet-zone" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:optimal" role="doc-endnote">
<p>Choosing the best segmentation is an interesting optimisation problem, because each segment has some overhead via a header, so it’s better to encode something like <code class="language-plaintext highlighter-rouge">A1b</code> as one Binary segment to only pay for the header once, rather than three segments (<code class="language-plaintext highlighter-rouge">A</code> Alphanumeric, <code class="language-plaintext highlighter-rouge">1</code> Numeric, <code class="language-plaintext highlighter-rouge">b</code> Binary) which requires three headers. <a href="https://www.nayuki.io/page/optimal-text-segmentation-for-qr-codes"><em>Optimal text segmentation for QR codes</em></a> explores this and even provides a live demo. <a href="#fnref:optimal" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:compression" role="doc-endnote">
<p>There’s probably a lot of redundancy in the business names, like many that include “Pty Ltd”, “Cafe” or even “Australia”. One could potentially see some benefits by compressing them (and then encode the resulting binary output with base64 or similar again), especially using a compression method that can use a custom dictionary, to capture all ‘unusual’ commonalities among these business names. <a href="#fnref:compression" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:database" role="doc-endnote">
<p>Having the app manage a database brings a whole variety of exciting issues since presumably it’ll need to handle updates, and it may be relatively large. These can be solved, but it’s not nearly as easy as just grabbing the name out of the URL itself. <a href="#fnref:database" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:threat-model" role="doc-endnote">
<p>When talking about security, one should have an understanding of what the attack is being secured against. In this case, we’re securing against creating a QR code with a changed business ID or name, to deceive people into thinking they’re checking into venue A, when they’re actually checking into venue B. This may not actually be a particular concern. <a href="#fnref:threat-model" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2021/09/qr-error-correctionQR error correction helps and hinders scanning2021-09-28T00:00:00+00:002021-09-28T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>Error correction sounds good. It means fewer errors, right? When it comes to QR codes, that’ll mean easier scanning for people, surely? It seems like that’s not the whole story.</p>
<p>I wondered about this, and couldn’t find an answer, so I did some exploration, and found there’s two factors in tension: the error correction on one hand, and the resulting data density on the other:</p>
<ol>
<li>For fixed data, like a particular URL, it’s <strong>easier</strong> to read a QR code with <strong>lower</strong> error correction, but only when there’s minimal damage to the code (like reflections and dirt).</li>
<li>Error correction works as advertised when there’s damage: <strong>higher</strong> error correction means <strong>more</strong> codes can be read.</li>
</ol>
<p>The rest of this article explores what this means.</p>
<h2 id="quick-intro-to-quick-response">Quick intro to quick response</h2>
<p>QR (quick response) codes are now extremely widespread in Australia, because they’re used for COVID contact tracing check-ins and placed in every shop window, but they’re somewhat magic. Before diving into the details, Wikipedia says <a href="https://en.wikipedia.org/wiki/QR_code">a whole lot about QR codes</a>; the summary is: a QR code is a pattern of black and white squares that encodes some data (often a URL), that cameras can read.</p>
<p>The article even has diagrams. Here’s one with a whole lot of detail, highlighting a bunch of key concepts going into a QR code, however there’s two that are most important when considering how easy it is to scan a QR code: <strong>version</strong> and <strong>error correction</strong>.</p>
<figure class="image has-caption">
<div class="image-positioner">
<a href="https://commons.wikimedia.org/wiki/File:QR_Code_Structure_Example_3.svg">
<img src="1024px-QR_Code_Structure_Example_3.svg.png" alt="A graphic containing a QR code, with highlighted areas. These areas are labelled as: 1 version information, 2 format information, 3 data and error correction key, 4 required patterns, 4.1 position, 4.2 alignment, 4.3 timing, 5 quiet zone" width="1024" height="574" />
</a>
</div>
<figcaption><p>A diagram of the functional parts of a QR code. <a href="https://commons.wikimedia.org/wiki/File:QR_Code_Structure_Example_3.svg">source</a></p>
</figcaption>
</figure>
<p>What are these two factors?</p>
<ul>
<li>
<p><strong>error correction</strong> (EC): a measure of redundancy in the QR code, translating into how much “damage” it can tolerate and still be readable. This is how images and logos can be directly embedded into QR codes, without any special consideration. This is <strong>chosen</strong> when creating the QR code. The number of modules that can be damaged for each level is:</p>
<table>
<thead>
<tr>
<th>EC</th>
<th style="text-align: right">max damage</th>
</tr>
</thead>
<tbody>
<tr>
<td>L (low)</td>
<td style="text-align: right">7%</td>
</tr>
<tr>
<td>M (medium)</td>
<td style="text-align: right">15%</td>
</tr>
<tr>
<td>Q (quartile)</td>
<td style="text-align: right">25%</td>
</tr>
<tr>
<td>H (high)</td>
<td style="text-align: right">30%</td>
</tr>
</tbody>
</table>
</li>
<li>
<p><strong>version</strong>: the number of little squares (modules) along each side of the big square. This is normally <strong>computed</strong> automatically when creating the QR code (from the data and error correction).</p>
<table>
<thead>
<tr>
<th style="text-align: right">version</th>
<th style="text-align: right">modules</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: right">1</td>
<td style="text-align: right">21</td>
</tr>
<tr>
<td style="text-align: right">2</td>
<td style="text-align: right">25</td>
</tr>
<tr>
<td style="text-align: right">…</td>
<td style="text-align: right">…</td>
</tr>
<tr>
<td style="text-align: right"><em>n</em></td>
<td style="text-align: right">4<em>n</em> + 17</td>
</tr>
<tr>
<td style="text-align: right">…</td>
<td style="text-align: right">…</td>
</tr>
<tr>
<td style="text-align: right">39</td>
<td style="text-align: right">173</td>
</tr>
<tr>
<td style="text-align: right">40</td>
<td style="text-align: right">177</td>
</tr>
</tbody>
</table>
</li>
</ul>
<figure class="image has-caption">
<div class="image-positioner">
<a href="v1-v18-v40.png">
<img src="v1-v18-v40.png" alt="An image showing three QR codes placed horizontally, the left most one consists of only a very large modules, while they're barely distinguishable in the right most one." width="1110" height="378" />
</a>
</div>
<figcaption><p>Three QR codes, using versions 1, 18 and 40 (from left to right).</p>
</figcaption>
</figure>
<p>Together, the version and error correction dictate how much data a given QR code can store<sup id="fnref:modes" role="doc-noteref"><a href="#fn:modes" class="footnote" rel="footnote">0</a></sup>:</p>
<figure class="image plotly-plot has-caption">
<div class="image-positioner">
<div style="width: 100%; height: 300px" class="lazily-filled" data-plotly-data="eJzNVk1v20YQ/SsEe3ALjIGd/V7fgiBACrSHNmgvRQ4raWVtQ5EGSTtSDf33vqEUtwXiHIIArgGR1HLnvTfzZlZ+bDd5zu3NH49tV/vS3jy20y7f4aHdPbTUfqybedfe2BO1+zx+KKPsWA/dgIf2u+0qrZRv5eWwkRjBmBDW5718/QmPB4AzaTJkyZGnQJESsSJmYk1siC2xI/bEgTgSJ9KKNGI0aUPaknakPelAOpJOZBQZJgNIQ8aScWQ8mUAmkklk1Xtqj0IaZI8zFICpgG6EB58kuGAIAoKPD2RBYV0kB2YXPXlrKUBLwN6I9aRFsoJWlSCbg2gP+CrBbD3kO4hjL5cAWo4WGSYHKuWhmmMUUkkH2rXzePIJmUaFS3IGoufjUvdpnecZhT7RV3my3lprv+DJzy/qiZUoq8mjrvZsCyBRPLAIAbAdWCIiBQ/bLARZB1sUtHqFSEveQzO0hpBgT6CE55SgVDmkwdDGnJCN9uAwAU9WGJx5skgvhNgsoZyi5Cd2LApA/c3sCJHXfvO8Hb+8qB0sONhm0fKKgiX0PisQAIBhBAcgLl0rTSvQgASpQWFNsmRhpY2YMpTSYQo8EL2HMVAbEB+VAySyAWbCPlYCzJIUYxneyyXKIAmb1ZIqfGAHZ9n7bzcWSv7s8z68fUkf5AyRaCMVxQGE80fGI0XpY+lgsLrFDEbRtXBiInB2AB0fQUNxLYyyAgsDHCQ70LlkMDFIRkCBF2BYwGhE3CO+JzncpP5K6q+wmxkQrIUXJ9xn6o+lLh+H+1nqux368z3va3eUbcdpLvvr+0rNdb6768r1eYWu3pXboTS//XhFza/DapgHat6W7qHMdZ2peTXW3FEz5X66nspYt9RcvZL45rVY2LzZD39WhD7BXFZg21T/gkC+GH9bexEEVShTuzrfuvYGVzSCwqau3JZ+s2yqc7c00lwOCGjfvJYGGcZa+jnPdeilt8BwzP16t/QRdM/Dvl1sY3TM04uubOdzCwnFbngoY5dXpft/FOl0Eq2HOn0u69/LOEmu2AOaD3dj2dbDP+uNlHg3fPzPu20dJ8lXFi8DlccxHy9LD7mblmHCz+gyQGYZmrQMipPhkLGwMgrLEIy5vy3nAPX+JAV/Ruu7eRjLppF/WprvV8e5TD980j3db8/ammX9X6qf3vQDzo1P9lxkH5r7vm5rwRl9+hu9UFWV"></div>
</div>
<figcaption><p>The number of bytes a QR code can store, at each version and EC level. (This can be read horizontally too: for a given number of bytes, what version is required?)</p>
</figcaption>
</figure>
<p>(The plots in this article are interactive: hover/tap for more info, tap or double tap the legend to toggle series’ visibility, tap-and-drag to zoom, and hit the ‘home’ icon to reset.)</p>
<p>That plot indicates a QR code using EC level H can store <strong>less than half</strong> the data of one using level L: the closest they get is at version 3 (53 vs. 24 bytes). The other levels have less overhead, but it’s still a cost that’s paid:</p>
<table>
<thead>
<tr>
<th>EC</th>
<th style="text-align: right">max bytes (version 40)</th>
<th style="text-align: right">storage relative to L (mean)</th>
</tr>
</thead>
<tbody>
<tr>
<td>L</td>
<td style="text-align: right">2953</td>
<td style="text-align: right">100%</td>
</tr>
<tr>
<td>M</td>
<td style="text-align: right">2331</td>
<td style="text-align: right">79%</td>
</tr>
<tr>
<td>Q</td>
<td style="text-align: right">1663</td>
<td style="text-align: right">57%</td>
</tr>
<tr>
<td>H</td>
<td style="text-align: right">1273</td>
<td style="text-align: right">43%</td>
</tr>
</tbody>
</table>
<p>That’s some hefty overhead: presumably it is useful for something… I did some experiments.</p>
<h2 id="experiments">Experiments</h2>
<p>I set up a little Rust program that ran <a href="http://zbar.sourceforge.net/">ZBar</a> against an exhaustive set of different QR configurations: all 40 versions and all 4 EC levels. For each of these 160 configurations:</p>
<ol>
<li>scale the QR code to given size</li>
<li>place it into a <a href="https://www.flickr.com/photos/_mia/5612839179">background image</a>, to simulate “real world” conditions</li>
<li>pass the resulting image into ZBar and check if it scans successfully</li>
<li>repeat at different sizes to find the smallest size at which the QR code scans successfully, or determine that it couldn’t be scanned at any size</li>
</ol>
<figure class="image has-caption">
<div class="image-positioner">
<a href="qr-example-7-h-full.png">
<img src="qr-example-7-h-800.jpg" alt="An photo of a shopfront window with some text and reflection of the person. There is a large obviously-CGI QR code overlaid on part of the shopfront." width="800" height="400" />
</a>
</div>
<figcaption><p>An excerpt of an example of the generated image: the code is EC level H and version 7, and has been scaled to 700 pixels on each size. Tap for full size.</p>
</figcaption>
</figure>
<h2 id="error-correction-makes-scanning-more-reliable">Error correction makes scanning more reliable</h2>
<p>Error correction sounds useful for scanning: if the QR code is further away or smaller, then there’s more likely to be blurriness or other “damage”, and so higher error-correction will make it easier to read… right?</p>
<p>A camera generally doesn’t care about the actual distance, as it just “thinks” in the pixels it sees. Thus, it’s the size of the QR code within the overall image that matters. I call this the <em>field of view</em>. Example, for the full sized version of Figure 4:</p>
<table>
<thead>
<tr>
<th> </th>
<th style="text-align: right">size (px)</th>
<th style="text-align: right">total (px)</th>
<th style="text-align: right">field of view</th>
</tr>
</thead>
<tbody>
<tr>
<td>full image</td>
<td style="text-align: right">2592 × 1944</td>
<td style="text-align: right">5.04m</td>
<td style="text-align: right">100%</td>
</tr>
<tr>
<td>QR code</td>
<td style="text-align: right">700 × 700</td>
<td style="text-align: right">0.49m</td>
<td style="text-align: right">9.7%</td>
</tr>
</tbody>
</table>
<p>Under the reasonable assumption that the camera is close to <a href="https://en.wikipedia.org/wiki/Rectilinear_lens">rectilinear</a>, this gives a measure of how close the user has to be that scales smoothly with the physical size of the QR code: if the device can read a 10cm QR code from 1m away, it’ll be able to read a 1m code from 10m. Similarly, fields of view can be used to predict distance: given two codes, where the first needs 4% field of view and the second only needs 1%, the second can likely be scanned from about twice as far<sup id="fnref:quadratic" role="doc-noteref"><a href="#fn:quadratic" class="footnote" rel="footnote">1</a></sup>.</p>
<p>Error correction definitely allows scanning a given QR code from further away. At version 40, a level H QR code only requires 1.7% field of view, while a level L one requires 2.4%.</p>
<figure class="image plotly-plot has-caption">
<div class="image-positioner">
<div style="width: 100%; height: 300px" class="lazily-filled" data-plotly-data="eJzNl9tu20YQhl+FYGG4BdbCng+6C4IALlBfOEV7E+SCliibDS0KEn1QDb17Z/4VqaRRkaQJYBvQaD174C6//WdGT+W86qty+u6pvOnu63WzXHTltNx8aFalKNv6ul7Or9fd3Yqcv5Hntlp/qNfl9KmcdW1HjfKnxVW6kp76ulU1a/ptOZUTv6Oh3bym/jxjQ/3L6rbeL7O56R7y4uV0UbWbWpSPtAkltDDCCie8CCKKJJQUSgmlhTJCWaGcUF6oIFQUKgkthaY5WmgjtBXaCe2FDkJHoZMwUhglDC1phLHCOGG8MEGYKEwSVr4XJW32nZxIK8h4NpGM4g8MuzS3NI+g55AJbBIZWpUMDzHsMzw18GDn2PAMz/9Gnhbj0Ercm+zYohNN+HAT2jS16GPZODr7JLCf3Y4ONLH0FiaJTopRtISe0Dk0TaST9NsVv9zNrOp7AkTsmmXNnB6aeX9TTu1uJ76C8cVxxrOFtfYbGF+8UMb8xmXYM5ZMUcUR9IExWjzW6IExuwy7LE+wamzxIoGXHD5xwB24KzJHPDKh5Zk0fxitGWlzy/NViHGPPAaGrNH6cXwvj/MNUc38/Ov5Xr48vuYTvhDyQc1MUKVP+YaRrxrYGjuwNTzYjrwB2LHxPMPDp0fGTNcMopXMjM/JD1McK/Ytw8YfcOuBMmTu/Y+jfH6csuQ/+/WUz18yZTco+EDZDZQh6kwZJo6U7Uh5VPExyAjjY0A/QM7/EOMBMR+Tr9I+hhvEb9a4zVGbubL5H4D7+nbVVj0PP3naTid6d3I0JQ8rfJ6QxzVHxDz2X6n4OWlCTDIgNCNkKmBVoKYsVAsCKoFoTr4OQk5mzMEBLDVggiGCvXPc6ZHXvQfLTBlBGgkY41JgIUpkV5WhImhrkDOZJlTs0OslQ/YIzSFaiJh7Ey2s6TRsVURedkjR5nPu3wT54hjkMSN/EfLFc0NGYRX8R5A1KiwoFfAUtKYiSi1cAY3IrCFEnRClNUotn6MzUi9KLgt1Os2wnU9QLzS8Z57zM/dGlG0xIhlb1iZIyoASDPw1vw8Ch3Dt2G8R12WAnLOMeWZwPDCanKSZs1T2OzlfHuM8ZuYvcr58Zs6IstLmCI3ADKXJFA60Lch7eCBgnctrBxuhZiRrA4/BfbEgb8HTIbs79LoEaeOJPiFQ2xyuQRvhI0n2JJdpB+DO0RoSR67Wnj3GsJStRE2GjOysQ8JOkDv3hgDqNn0n6fNjpMfs/EXS589MOpfSAJ3FjR82MgFxLqjNWFYrhFqNVK1RSmsPhadcYKMMA3I4LIRvQTn/mNJA7YAatRcKbdRrIbKNmB5jgq7tPj1LMOftkEWA1pC4QQluEPQx2EnE9oiojsos6CMV945cbbXt7nqmtuiW+bu6bdotD9tuiPzZXSOKs2q1auuz7BGnv9fXXV388eupKN52V13fieK8bu/rvplVoni1bqpWFJtquTnbUFW3EMXpK55fvOaLUby57f5qaOq4zN7DpVnzN21Q4Z5U6+tmyRuiXRHm8ip/tVTiiZKul9wNdxCDmr7F5evrR5pQvnlN97fs1k297Ku+6Wil8oaesK2WsxvcTtp3392WuAKKbt/Y0daLPl9HfgQU0FZXdfsyXhJVU7TXx2Zz7NR/UrHLZ6Ux9JgPq3W9aB4P/mJf/X7St2jWGz4vO/firNbrart33VOFDGFaEiSL0UCACaJzLDSWmGVZQVDranld5wny/Y5f+H/s9fJtsWjqdl50i+K+qR+Kn09+GTa+uVvkzZ18tOPRuewozgxo9lt+LO6WDa03pxf0D07LbKM="></div>
</div>
<figcaption><p>The field of view required to scan a QR code of a given version, at each EC level (lower is better).</p>
</figcaption>
</figure>
<p>This seems to all come back to the size of the individual modules (the small squares that make up the overall QR code):</p>
<figure class="image plotly-plot has-caption">
<div class="image-positioner">
<div style="width: 100%; height: 300px" class="lazily-filled" data-plotly-data="eJzVWV1vW7kR/SuCikVagDZIznBI+m2xWCAFmoe0aF8W+6DYV4kaWdeQlMTewP+9Z+Q7o7RVnAQJkN0gsAlefgxnzpw5pN/Prxb7xfzil/fzV+PbYbvaLMf5xXz3enUzD/P18HLYXL3cjm9u0Pk39Fwvtq+H7fzi/fxyXI9ozP+0fNFfRMG38WZxudrfzS/iudxj6Hg14PvDjB2+bxbXw7TM7tX47mHx+cVysd4NYX4LI1LIgQKHEiTU0EIPKYaUQsohUUgcUglJQqohtZB6yDFkzMkhU8gccglZQq4ht5B7oBgoBcKSFIgDlUASqAZqgXrg+GuYw9hf+LzWVqRFapmL1DScxRLKOSWOjUqXnlKrvUzdpWZuqTeOtVUpMnX3kqhFSTWJYCHrJhHptUvJqTZqh145L1xz4tYjvuReD731nHshTr2KzmGZekuWmIhzrFhDsm3Xs1SJ0ohimkyWc6YYOaWSsxDVMvWmiD1ip9gaczYbsFHurXXG9JbEx0YqKUptODrZKaTWXKnq6JJStbPFmqhTyQUr9wfL2nnnXuEtKvBZrbYu5dypc2YpnSc/lPPKmbpQynBeLMld2WJOCAkO0yubd1LtOeWKI6QW29RL6m2SVjqiMnkSfohca6lwMHZzG2BUjo1hWU9SzA/M2KqIRgPO71NvxJDaYgcy4Dm3F04rIhiHXWv33VrBl4Sz5Rzz1NsQcSCiSS/Ahvms4ASZGvoq8TQScYiVKKcI9EU7b8KRatFQJsQt23kVq6mUmHJLzcZmwCXhWBwxVmxdRAWwFmbJsUua4tMAW65AYCpAuJkgmMokibBjt82ABQ0klq5Apjksw6mtUu4wtnc6mhCRQoQwYIatq9FR/xIc1x4Aiazb390oEewuF/s9yAQ8s9oMyinvVlf7V/MLvr8Pn8FHz07z0eWSceTP56Nnfwg+4nPAATlIuSZgm42OSBOYEaHGJZPQY3TEiIfyWYmlFEWKpRbQqKuAHoALWwLIL4dEQfhq9Dw8zUetAT8xwq5W8dPyBVArGvlOAKETKzDNjCWo1N6cQFNG2iZsFYGtR+hIk7NjLtWEL5F5OltTS6skQJmoGfnFhPQtIBICB06JDA+DYjC/qOeQUpMJ2Ic5irpUwGs2theOoDjkXAXRGLQ7OAdr5JZhW3NeRhdSG0wFovEgYfeckS0tqSPdZ2ANJAWmRCUbW6EizCguYLaEVDP3ILngeTgN+Rktx+HL0pGiSF9Yw+4faoBN494IJtt2uTSwUwMRC/fePBjAxGE8iFDMEzXlhITGbuDa7OcoiBniiDIDrmFbAuwZWcuioCSRbQcn4AygAviI3cUdHpCW4VIUAyDFLAY88B8mJvWelUnArwEh8ASR02WMtWoxBagaTuT4qYewg9s6/MruY1RvQL7HBDKzsWDqhJjAlIzCxd+Wlp6fpqXa0qVcfT4tPf+d0RKdo8Ag7Qq3Cu3BzTgFQqMAgqirjDM6ME/xEggIEWpAFZaY6j2h+Ki6AHmgPtdiy2bgN2KrDA4hZwnQFyoMCA1QTqhKLiSq1kPoGQFcDWxfQkrAAhgTuEJCofUIKyGTYCwkUVNd17utCzijgoKkAW9UPaMlgV4hUl/gR7OcSRmpjJ0gRthOjEILkoEaQAnF0Zx/WF0WkeNFc8eWrTgZiKIfNNljpKT1BClTlToK9W71BMoEckga3FlQMS1COH2DrIM+ILDTlLgY3ZGCFXNAIEcBB2WJ+bGD0ZXNzeSmijJjQwUGprnfmFV05oqknI7dkIxqKqpJbgIyMgexSh1IqKIqyomUoIwbAAUNFL1SQSUilkVUOOKoH5VarGWxo2iAz8Hy4nwQVYSKUhAoq1qY4Z4DxQBwShcG7IryBwVGKt6TAxPM2FB7VPHBeUcyP0VKWsKLKngl6miOR8FA8FF0gRgg4Buz0tPTrBT1H38+Kz39I7MSaSYU1BjgD/Uke2VEIiLYDISj1LRHaQlKAAoDVzX4rTlaCYoa4EPokA/lSDUnaEmzQ/MAYiD1XLwynqIlBazea2AqrgNH0aeXsXRQNaht0Y9xkpYStiGVbDhEda45zUqQGLAzsd4Icbd9RCzhFMh6sCJ0AESXdX4DVkLiq3eUBPX+Y9UAkqqoHCK9oSTxnIEDWClbytGVX8ZKsSOSEbf8qnzoGyoNCxKyoXawEwVomVRGQZSCBx5lJTANWJQ1Hui3cHBWRd1xs8RR2KtEUYUCRaLyvOcjMeEeAIoGtUXwhwUaNsAXUJJYoiWrCKrVoQ0hGtWHXsGAFYik1NTLxGayXjmhviuOIiSO7qYyMokqVRWr7rmvpqb9cH2zXux1+A/v7y7O6f7k85It8P+PS76kc5OO/Z9npe9GQwfhb5xP3nKZbukI9Zpd8xZvNX9+OT7EsD1mpHq8eXvLNXIWa5H99obYuxB1fyHK9j4BWWatZjOOKVvYW/6OJC7Xxa9iIt7y1xfQprX8lcU4RpCFZrDf0fwbZIG12Fv1+BzhLbJnhyje6h+B5Zdg8NkpDPqDwicx+Oz7YlCcp6R4g71lLC+SvOXjxPSNOIGJE6o0n9F8hlcN6dlbZoBXtei1MB5f0Vx0p+It2x+azVu+CvkM8hkfviJ6y/f3Ycdi7A443omrO6C6A6o7oH5QnN0Ad0BtPsMdUB8c8HUgfH4KhH59/CQIn39PEOozjFeN6k8z3QV48nsGeauYJLBcZ33fthLoaImOAr8PuNiPHowUveUXtuSCPIm3ml9LfJXs7/bZkXS8kmQPPbkBlP3Cy97yVzhqfkVxlXaUney4Zc9IdqD5m4sNKmTHKcVbflkp/qIzKdivw+DTUxj0y8InMfj0+2JQxHAk1RVvnxrVb1w1mW41gjnctq1V/LXQYVn9flG7rdcc0i27OGbXs8WTwO/uzTVw67Zej7Zez8cUsfV68QuBeCpVV37HZPHnsOiEGl2PRD6+dHoCGWrj8W3CsZqcnz/MJRe4xZbzP2/YnyRU/X4MhehaL+7GN3vF1nLcPPxeXK/Wdzrsbgd4nr1ZhdnZ4uZmPZw99IQn/xhejsPsn399EmZ/H1+M+zHMng7rt8N+dbkIsx+3q8U6zHaLze5sh6vwMsye/KjzZz8pfGc/X4//XmGqLzP16H129RsMTAc0L7YvVxs1CFYBjPMXD7/WuBeHOZIg3lumHAat9utDiuyHW0yY//wTkmw+blfDZr/Yr0asNH+FHe4Wm8tXhxyC3fvxen4AakKO+If1sNw/JI1ucUjT9eLFsP59OAlCHrbernanTv2vYbvTs2IMtnl9sx2Wq9tj/2x6Mvivb8vVdqfn1c6JQhbb7eJu6nq7WO8O9MGgDaUMOtBEP1BDUTpQImBN/kPabxebl8PDhPjrvTr8I7Y+G6/e4LjL1bC+mo3L2dvV8G725x/+osYPtzfjBpFbjtvrhQ6+Gd8drjVqvX1UQ9fr+bTlZPp+/G3YjnML3NR7O3uzWWGnK7jvPyGcaRk="></div>
</div>
<figcaption><p>The field of view of an individual module required to scan a QR code of a given version, at each EC level (lower is better).</p>
</figcaption>
</figure>
<p>The lines of best fit look pretty flat, and quantitative measures like the parameter confidence intervals agree (except for level L, the 95% confidence intervals on the slope coefficient include 0). In other words: a QR code is scannable if its modules are sufficiently large, and that size threshold doesn’t change much as the version changes.</p>
<p>On the other hand, the size threshold <em>does</em> change with EC level: level L modules need to be 25-35% larger than level H ones, matching the intuition about the benefits of higher error correction.</p>
<p>Cool, hypothesis confirmed: error correction makes scanning more reliable…</p>
<h2 id="error-correction-makes-scanning-less-reliable">Error correction makes scanning less reliable</h2>
<p>Not so fast! When creating a QR code, versions and module sizes usually don’t matter, it’s the data that matters: one will usually choose the data and error correction, and then have the version computed automatically, as the smallest that works.</p>
<p>The overhead of higher error correction matters a lot.</p>
<figure class="image has-caption">
<div class="image-positioner">
<a href="v3l-v6h.png">
<img src="v3l-v6h.png" alt="" width="716" height="364" />
</a>
</div>
<figcaption><p>This post’s URL encoded at EC level L (left) and EC level H. At level L, the data fits in a version 3 code, while level H requires version 6, resulting in smaller modules (half the area/field of view).</p>
</figcaption>
</figure>
<p>If we change from looking at the version to the actual data stored, the ordering reverses, and EC level L works best!</p>
<figure class="image plotly-plot has-caption">
<div class="image-positioner">
<div style="width: 100%; height: 300px" class="lazily-filled" data-plotly-data="eJztWdtu3MgR/ZUBA8MbgBZY1Xe9LRYLOED84F3kaZGHkcSxGY+GgmZ8UQz9e+qcJqmxPb5sdhHkwQbY4lSzm6zqU+dUt983V+vDujn/7X3zcnzT3w67zdicN/tXw03TNtv+Rb+7enE7vr4x49/Ncr2+fdXfNufvm8txO9pN85fNRbnoovWNN+vL4XDXnHdn8d4eHa96668j9ta/W1/30zT7l+PbOnlzvllv933bvLOPkNC6rg1dm0IrnV32S+ynlK5VDa2mrnVqVwytt98+hDbY75BDG70NswmSPZvNXhRzdDa4M4uIDRWMFwwWjybgkYgm4UXZ4032Ou2sV8UmVWcTqLdeDdGaWMyW7cvUnvtn25izv3VnnW+tiWiyNYKLDUyKO8UTqmgSmmKNc2jwiIPNYWjCw/bx1mBExM+MYTnPdwW9xS939vVn4qzRjDu7PJpQrEmwwxxyq2de7a44u8MDNoWeuWKNZvPkcHeDxdlfrg8HW2Bb+2HXY53fDleHl825v79vvwEjz05j5HLjvf8dGHn2BYxgLVpbKluP3FWY6ASThGVDv122es4BKl3rzeYDgGWX/Y72fLQlTmZPNoZwsftSCDtrBKAA6kQjIAPcYAYJboEMXgooCIZKAVo6wINfYK/+CB5YrC5N8OgAAMkLRh7gwTs863SGB0wOJo8BXpY7TJIw5XzlGSkJXRkQ4CsL7yJAgguocAtQcIdswMiKlpyAD+XdnweN56ehkbJcxqtvh8bzL0KjMog3WrDlBRFMAAkPAGFGAyTI4plP7LJ7b3afARSbwGISBUCZeCUAbwYWsxf7Wwg+8IbgvYIQgqNE0eHwNg+IeIAqgNBspgdMuA8wQd54IA+supQPMZEWTMiMB+dnPDg87BeMEBQBTcSISJsuuAAi3MwRHdZZPHBBcJT5zqGJDxDRGRlklRj/PGQ8PY2MDv/8tyPj6eeRARi0WOgOzGErX8kDSyOQGSxRIDyq1MwcYiNsoLN+lzHULrv3NjqYHWQQCvjEJsSkNl+CDEGC7G+23yXMUAmLFOF1iveaHp1CRJgZ4gERYUYESaMigk1eEOEXRCwscQoQVJhFax4AUX8YHmY44AMBu0leHKUFHOKroAADaP4LMBz665vt+oDHH72/Oz9z9yeLjXmCT0uNZcoFDXj2oyIDC89VPxKLrooFlD6Svysf6MQJcVr0buIFP3HDvPg6cUSceaKr9YevwhIy3jQJjK/cEc2WzAa9IDjAJxmfY5dOQMHnAY9mK2YrZitmq8BhDYOv7vDZHb67Qwe9oTv0hw7BI1G6iQ44VSWsshI64JjAM3HUtsx4LHSFDvgncFDgoQTqKzrgpMBLgZsCPyUymOiAqwJfqzjCW4G7An8FDkvWudDKjH9mcYcGHYUVWKSccmXQYG3gucJzhecKzxWeKzxXeK5cy7qY6OBycj3hea3j4LnCc4XnCs/VUw64/uiA5wrPFZ5rIBP4ufyD5wrPFZ5rJGRYF6IDnis8V3iu8FzhucJzhecKzzUTZeiA5wrPFZ5rORYIpD+FoGOh2FHtu1irTCVDkB0qNZACxNeKwi/FRWG1KY5SQkHxrDdiLUVpYRnpWLC6SieeVEKmcGQOV9zCKcqSxNMQKDuJNFMwZSBjBVJT8LWYrdRDCx6PLJSjYoJI3YqBllSlitRECkwsnVMtjFkP12Ins9QhX9bamFVSDryPJLTM0rjD+CJV72rFTEtkTZTdxHYIszDMwjCjVAL3IczCMAvDLAwz2JGkGEmS7CwwaK3Cq4CSKJWaaRigrgaSqFBZYUGYBRWeMMrCKE9FGaJsLSt2T5lGlIVRFkZZGGVhlIVRtjYtqh5Z7UWWeogySjxMgCgLoyyMsjDK0HQqe8CU/JhEqUeUrRVMkPmpCLMwzMIwC8MsDLMwzFL3JIiyMMpqUVZG2VqrKpRRVkZZGWVllG1fItiUIMrKKCujrIiyNQUTIMrKKFvrMQGirIyyMsrKKFurmABRVkRZGWVllG0v1BXuiE4I1+9RqWenVGrZ7HxVpZ59V6nvKvU5lfpICrhDcLU+pCCwfusyi0JyqpB2LUAsGGknYQoZflIBNtQJpZZo5jEEqdJJPZcg5ZNwHUm57i64UfHcm070T4Xwqe5FMVkQ0jzFKFCwArk2kI9jp8uOhKQeedIRE6WgkOxZ9iZqTKIMJT6fUq1PyfxUvExV5Lw51n1vJOfDXKgchd9dKECFAlRIn+SrjpVt50n0kdSf5YjuI+m+1sEkfTKekBWVxKl1v8SdE8thJUVrcUdEX0j05HjqhaOmOG7IvWAybChA8uR+GkjOgQQeSOAzxxdyPJm+eG7OSObkeA6N1K1IZUt0M5GWE6l7ZnfaeTiQO5K8cvfvK7Fzl8dTgErjVLXi6pFRIbFHEjt4tetI3kI6d6BXHJ9Ym/SPEevzU8S6HBV8lViffyfW/zWxfkhXWmtWVqssyTqWkFKPNpiYwswWprCyblSebyhZQUkrEy9p3duSl2o9Wve4ZCTeRhISKSCQQwLnCiwtQypH9EPOIQNGUmgkPSYSbGLNmlhBT3WmHJENDfmIZkg5JLpCpuQJIvMB3KJHpWRlFTliFVrIOSYc5BNyS911s5Qs5A0ShiepMPNcruShR+SRyRu1NgzkDXIImQu1NxiDBMFiL7LyjCzQSGuJST6zBC3kkEwSymSxqfAjx2WSpLHsH0vyp6eSfDn1+WqSP/2e5J8k+UdZyKIh1D0jiwNhzvE2Hu0QmRTKukKJeNcdlQKRO8S6B6Syel83ftR27tUC91eBKV23ecyn6OvWjvnE/KPYp3oAxXTN/JrMqiUzIXNhQvHEs5AMpr0a06dzdYdG+a7/+0GRnGSaZ1hKXVNXN2FMrrIcaTrP3VfiDouZ52mvZ1tVfCn/gWlSN1WRahq17qSYONz8TJlDgU41l8qJM/N7M23Xd+PrA7C+GXf17/p62N7hsbu9pcuT10O7erK+udn2T6qlffxr/2LsV//42+N29ct4MR7GdvW0377pD8Plul39eDust+1qv97tn+z722HTrh7/iPGrn5BOq5+vx38NNnSZZrLgoHT4t32gMLvWty+GHT7IvsoQ1VzUP9vm3FpLyu5+zlw+NBy2TNlD/84GND//ZEnfjLdDvzusD8NoMzUv7Q13693lS+a0ffdhvG4ITbGcXTq2/eZQkxivIG1s1xf99v8jSPf3+NZ3w/6U178extv+aoX/PV39cHF36Pd/RRjsla/2rzebwXxqVrRPp9If9OxGY717xOgz0z//ZbUZ+u3Vatys3gz929UPjz6d/9GXpmY0J+J8t3q9G2y+K/PpP8vdtZk="></div>
</div>
<figcaption><p>The field of view required to scan a QR code containing a given amount of data, at each EC level (lower is better).</p>
</figcaption>
</figure>
<p>If one is trying to “send” 1000 bytes via QR code, using level H requires a field of view of 1.4%, while level L only needs half that (approximately), at 0.8%. This means a device can be further away and still read the L code successfully.</p>
<p>This doesn’t seem to match the results in Figures 5 and 6. What’s going on? The level L code uses version 22 with 105<sup>2</sup> ≈ 11k modules, while level H requires version 36 with 161<sup>2</sup> ≈ 26k modules. This ratio (2.35) is much larger than the ratio between the module sizes (~1.3), so, sure, level H allows the individual modules to be smaller, but it requires using so many more that the overall QR code is larger than the level L ones<sup id="fnref:consistency" role="doc-noteref"><a href="#fn:consistency" class="footnote" rel="footnote">2</a></sup>.</p>
<p>This seems to apply consistently across versions, as might be expected from Figure 3: a QR code at level L can store twice as much data as one at level H, for the same version. The data storage overhead of error correction doesn’t seem be worth the apparently dubious benefits.</p>
<p>Nice. <em>This</em> hypothesis—error correction makes scanning less reliable—is correct, right?</p>
<h2 id="error-correction-really-does-make-scanning-more-reliable">Error correction really does make scanning more reliable</h2>
<p>There is more to the story. Those measures had no noise introduced to the QR codes. They’re the easy cases. What happens if we turn up the noise?</p>
<p>I generated 10 different noise patterns, and thresholded them at 8 different levels, and overlaid this noise across each of the QR codes, for a total of 160 × 80 = 12800 configurations. This is meant to simulate reflections and dirt that could make it harder for a device to read the code.</p>
<figure class="image has-caption">
<div class="image-positioner">
<a href="qr-example-7-h-noise-together.jpg">
<img src="qr-example-7-h-noise-together.jpg" alt="Three QR codes overlaid; from left to right: 1, the QR code displayed in Figure 4; 2, the same QR code with some white and black regions of damage; 3, the same QR code with larger and more regions of damage." width="800" height="286" />
</a>
</div>
<figcaption><p>Three levels of damage to the example QR code in Figure 4: no damage (<a href="qr-example-7-h-full.png">full</a>), some regions of white and black noise (<a href="qr-example-7-h-noise-less-full.png">full</a>), even more and larger regions of noise (<a href="qr-example-7-h-noise-full.png">full</a>).</p>
</figcaption>
</figure>
<p>Across all of these experiments, <strong>60% more</strong> EC level H QR codes could be scanned successfully than level L ones:</p>
<table>
<thead>
<tr>
<th>EC</th>
<th style="text-align: right">number successfully scanned</th>
</tr>
</thead>
<tbody>
<tr>
<td>L</td>
<td style="text-align: right">1257</td>
</tr>
<tr>
<td>M</td>
<td style="text-align: right">1600</td>
</tr>
<tr>
<td>Q</td>
<td style="text-align: right">1839</td>
</tr>
<tr>
<td>H</td>
<td style="text-align: right">2001</td>
</tr>
</tbody>
</table>
<p>This is driven by resilience to damage: the QR code at level H is more likely to be able to be read when there’s significant damage. The following plot has a point for each successful scan, against the percentage of pixels damaged.</p>
<figure class="image plotly-plot has-caption">
<div class="image-positioner">
<div style="width: 100%; height: 300px" class="lazily-filled" data-plotly-data="eJztnWuPHVd2nv8KwUAYBy619/0iDAYwHAMOkHxIDH8a+EOTakpttdgE2bKpCPzvqfU+a9fpPodzS2ZGM0ZrgC6eqtq3tdf1XWvX/PTy6+uH65df/fqnl6/uP767v3378OHlVy+v7+5ebi/f3N7dvb6/u3+/33n/zavrvwmb/vdf92ff3v/7zfvbt2/u92cfvrt9t9/6t9uHh5v93bi9vLt9e/Pyq59efr71p+3l99fvv7N3T6/8l2D/FXv49vr7vfXLf9r71Ize3e9zCtvLj/s8w1XoW4xXdW7lqsQt1qs4t9iu4n57XrW61asxtpiuZttiuYr7v/NVHvZK2/LVnHa3JvsdpprHLcWruXcVrnK2t9P+9lXJdiOULYWroUZ7J+OqFWvTsnqZNs6wIefeZKjbetXztje0wcKwqfZufdRqXY+tXfWqLor9LnXrV3oraM6dn/vjvatuN7Otcl7lvrcsDDhswvu07VneBw9azUhGAPv3Tp1uk59l0yT7Va6aXrEFzH120ZZS1UOzKRlNs425d5y12D72jpP+maKmMvZ2ydaa2z6frvntb9nSonW537hKeV91i3uPse4jDBG/pH0GbSzKTv3dV2gUqqJy1Y19LfvidWOnabrqbe8i2vSGtmWf+d54Vhus73+Ghp17L8WIE6pm1DbNdX/fOGJcJbFBtg6LTXyfYjOSFiMYm5E0ea1m3/diQ9sWFpHANqh1e5zL/r6RRGTfKb5PJSVbQrIRm23c/pb1t4+0/9VMxQz21MYo1qbbRuybRK9Rg1ajxd61MVy1V7vRZ39p2KBRDJ2KDcr25H1PbK37AsSJbV/Pvn2SjrAz+s4lzdmy2sS7TWTvLnWxSrM3I6vNYkXboGr3iglON5JOE5Zsk+3GSiZGmsHOz+KSahMw4tk6gw1e8hrcpLDZ2MGIbOwn5rDWcY1dxSzW2TAujptmOsSMwWQpm5ztLKCxk3GaCfxVTs7zRROKMEKyNU3tZrK5ZNuWbB0PCC8emZIOm9iwrnapz7btgzf3n0OTDWK2bGSWhMBGQZRqki5jYklJMypon4u1D5qTbUiwiYq3kwluFXsVox4qS3KzT2R/dWeDkiBLspGy5qzdizZmEw2CJHFITG0O1cRkv1ukkqS0gkm80TDaTGp29aOJJ1E92YvBdiHZplZf6BBNNYjRWEtJ0hM+qyYO7dZLNnU0IJfTpBg/7C/5KrV0EwObhdipm9YZfdEj25/ZEY1gC802VpUmkAapEuKg3aiPqFPFSiZ/1qoZQyMTQdsiPjHRqbZEI1mHLZIRTyOZYtREtJhuxK6PJmKq2Ng4mqqTcMVyRpu+JhNtGcMYVdMwnR1caUKPwjyGJpzZim5Pgolu7ktvzgSNMnIkGmmELjMiZrJmOzVsf4M1laHBPFQZLoZOpprX/EwGJJShuxrdH5v+ExmCGFIvVdv/JnaKzXZeYlpsHibAzShklEUzxWOOxhXJ9lZqM9pS1VIiOGyRCZG0SUb1n2Q7JfciRZY6qfurMyJNNrUs0bBOtT7uRtTZWMNrS7VbVfxs2qK09VSya79aZRgpfxjWpA6TITuxD+CKkAnbpjrlzM5Cviaimemf7HyUVE/rVy6I6NJg72bSFdCN6jua2MhSivGi3csmrpqSMeyu6qe9K8JFzJPMH8KfJWdFZjYhCVra/iY8Is+hGCtM+2krK6YvYAXb37E/abJSozBqYo+LiYGtES8F82cbxMQLKpb3zX5GqeiImcmIuCS+6ak5LPgqJlBs49hcncyO7A7Trl3qL0sVjiut0HS0Pa8mVEPboZ0KRt/kXgsaw8jUzGRhYKKWJkNQOkY5s4lmF2xa7REPwjM7OaR8o9in2p/pPCs+QbVoZ2WpY8WsoOO6/MkqQlecjCSlmkwQpFlPuqbacjVyaoeSwpmV8ZzIV3a9UJaRycVlWwKW3SViLVM94EddmT3NDTssnyKZmA3Tu1EabOCdGLXEaclILLmXATSXRtwoHWqrky1rppekDjG3U7Oty3wm8Ye8w8zbuIxrRdZaXlSU+YGtjdWLiJ4wDNlmhUm0LpL53ENuRzNmHVJeVXZFzG7aYVFba6ziHdm6jL0S5eQ2NnFZtT6dOL26eXfVERt7H12T1Yo66KbOdvY7pmsECRPZlUTI/cg2kX26mqIpmiDVbCHItCUuXRaM3NW2Lcv2S7NLCw4bq0jANslAdwmSQTAuNP8wGb8Fk0/zLpskQK5SgoYSRDFvjxjo6aZY7o6CkqwtlhAiQ1OOQbV700OYJElqErUxMfrVKDPcE5TPMxr71N2VM2aVHkoSR2OJbASq6OaCVzRgyyZ2CTxM1tLEA0+3m2IdUqxd1kOuvdyHeaxgKdFh3AuNpwxwkRocxTVKQ512Y4hurDKlUWTSRFFxjoxvjThVjZBM1iJLsiS/A9FVGNNEKe2bhEMWv8tvc+dMxE/4QXIYI5GK67FUYNBdI8slaQfvNKIuCZO9NKdvUcZxlzczG6w9tDr5vFEWXi5PlBUX16NIhunlJqmU61eM4tkCropUHqqP+AWX2Y24C4oaZrTtVLdNreV2RikC2yWRk5DCVHM30S+ERiK5ESxUApxm6xpX2mQz8QNNKx3ZidiKxJIIaZgs1ug8z2CugJoC6YiDI06YzkxF5CwmN9JXtaEH3Z9s4AhTCmaXKOlaxYwQtlYX1OgBoYXoVVG2ZBFJih6SyLvI8HdM7hkUEw16lbsnFy1C2THd8YruTCkyzb6YSLgwPWQwZ2xftfZlululdpL3YkSaUrOY1Uj01ZdPbVxlA1eJgr0qqWnqxAyP1E5PCN9wBWTTbphRrSAYZykGVHg4m3OWDKq8KbkL3UPrKnhkaRn5RtKguNSmIBLxX0cdmA/X5BMEd1fNp3FLX91yiMfE5yJaLKjLalMaZtma+LDK9HV7HhQ+74o8y22RxAZ7Dwss4CIYWeTgVtf1zSYxJJynACJfaSNNj0mpVEIDGVmiH1zcKIlcEfiJfaamAogAE07RvS/HocnUAGvhTRfiqGw3E3tV5WM2D3xsVQpHu60+OkmnOXyhLERCCwxE0IKRivvVWZKbk4NGEaccHVrBJbo0kXE04aapWqKn2LCSFaduSDSyQs/Z8Ap88glDWw60xJc/zGWoNneC0WB0H25FEy5NYPeiQoUIeCROzcODlilFIREhwGk4MqGz1TIGOGkBF2vORftk5l+ebTBli7egGEabPpbfI/e3AMZNYQlFZqYT/RYcHXmA5j3NkzlS6AfHV4e5jLAZyKXX5eFLITcPRLPkANuWXeKqccPUfhZuBo8bfeAKL7GK5LhddNM51KP2PzqC2qUGBACZtBUmWIdbN4GDQyIXwQ6iwtF4AAq2aW4mOlGGrbq52+7BXFSPsp8xg+8Jx8mykifxgjEF4VX45FhLwH+LghJYS7J5CZntYoRo7ra5UJkIYnhMkjRhj8WDFI3oZ10ptFAooYHRbOohuYMuvBGmGsKCs2/7yG7rEnhYSm5B3Xwj3EZiU5rmERWjVsfgVDypuAKjLgYyChRgnO4OdwP2sSfJFj6rR8QrThoHXKjgR15WIr4ejg7PiL9UDh80yo8ciseiwlrwwmxK08PuZC5BFKnkFofT/hR2QBZ8OqKpZQib7QOs3ccUm6VybFCcqzV4ThMoZrhVUiQZ4DTJb1TUumKjXg8W64rENZuGaRGfjelgsq+BKJMXJvHnaQ026hJ6bHp+sh3y2rWE2ti3mIiDtRPGn6U6k1VHus1/LNKfvf7r9vLhx3eWzXh1//Hlp+3nT7P0EV+3rx+lWf7X59IseBlDCkYYAckWgfkkW4R8mPERFwdilcquy3mZkvhdu7hBPWVULE9jnNdJj0QX7SF2l3tPRiKh1TJZFZuO6UrxeV+hVvMwWwBclwJNhQ2Xx/E4aaKUQSGQQw+BYMr0ZEUFaXqsHRwrloutSD8rZKzyUgUVpOkMM/BsU91WugHQIj1KhgC3DU/bJKWLFDvIIQhD6I2c4EnEesp5dHDU2B9lOgZNWAXaaToi2TFkw904Uhyy+VjEU4qjnsA54SyWIXO/xA38WJQJIEbKZijmHiKJVEfR8HKvTnmMShpD9niaV1eJc0Z37yTiwQjFukxaFAiHGfzNSQvA9E6+guVdJCykuk4Ji9+RqkgaMVbXhhepCuneCKCi9ZgakEVvoM9m9RoYnTtM4cgP1L6dEhVulVeKgoxEcM7ux9Cw6BwrohSIJwCpTvykKGA+ynZE/G86tT6EnAvSrMfahY4qwSe3LR5yXMFLD6A92EaJPx5nIFZGIB0ZiAr4MpbJ6245g+f3Vgqi4zto1Dbx5h1VrzABQ0/ctgmQKq2qjGUu50mGgJoSpcKBCwshVtwLw1aPsTJusWcWMN6PF5wAIZQiPOUUxPG+3sdJhXiQpm++Q0qu/obEwimlkElTk1JI/Qi1V04h4wM2ENWxOdxcHmU1hAJVEgnKIbj/tDIHhqsIQlaaMxD3icEVegFgBjRnWUMnwUoFR1W71jwqe5QpkKE7MgVHjiAQhsrQt3zMJOGmO+jeiaUkDebNrxRG9kxrVN58SSypYOXUlM6a6cgR1MWxnTyfdpi9Cv0AAk85gkcwfD0YUngO8dznswTkPuSmCl0TFKgYeBzZgbmyAz2T8p1AxNHVzWSv2uJK0Thk7Fh1nk0L2s0HLG8TNuEyfbDSABCdTNJvzwIAVssxju6HCcE7MgCeBlwZgA5elPOjNECEMx6lAc4zAImen6QAMlF1KKcUgOCbtlIAeTtLAawENY418L5QgZSe5gDSgZ0JJQ4FbpWnYrOFklJ0yTV9A/bHbVkAdV6w/6hHjCNYIccD9i/xgP3TAQB2s7sKApo2QVOtj0F/1pxBhhboH0HOHuH9YkdF5POUoo0HZFYPvF9etvIWXXMT9ys07v2YqyBJBcnC+4T7E4pLhYoth4uSi0rxlPcJ6xcpwVSyF5FI6EpdeTqpsOEAdRoHwG9TFAtRDhNO5GxeHvMY4Acx99qYAAAaxoIcPKiLRzAlFb8AfXcNKa6AuYpA5rGyFAGL56ntae2GBYjJkXz0qUwjkVwkPyNgsC00TwytqoC5JmlwgHTOkZwqBiNKOS38Xn8KursvcMAB5HqqlJhK05hAtw5uX0w4EQBHYg/w0gQR2L462OhMUReipg5jXZi75/mJpubKX5+w+lhIWAvqzpOEKT6WE1LeYWseZMfpSH3C6ZjWeJUpJQI3kPrsuyJoMiCALT5F6fvyLKPnHHBdxWPpwOenu+aP8XnTmNiv4o6+B7vm1zo+X5cwlOL4vKQ9m31c+Hw+Zg1EXA6ESStTaQfc6vDYBCmAXUmrelIigumpmmwugEri6ik10KyIW5cATSxL576GwG1SxYtiypQ5ngEomg4YTHZkHLIuVkhxwfFFqG+Wj53GAlq7CGziX2y47vtzQPBkwZWsRHcLx83HVDPIknbH4XesR/XQxfEMzXU0Tw8l8tyTmLs8gdodroMxNyV1FAgtXhdBterk7mQmrHwKqRt/nxD1BHhFjmAecQPpySeAuvu9golR/2mJvxATgWJxW8hVFYDi6Jgp6rF56WEzak6prNLWxsj3jtYGvIrctlArueAyWZT2CGBSUqZ5dCZOUSKqe8j2CFSlJqlDkSJV5ihuBqWYXu+oGrdVuxQDAV+nNgngHKdMkHlrK5oUyLZSUAuzHajBRPACaCsaylmY1UEqAR4CWCdgWUHL2YsHUC74uLvbNp3d8vDKrEjKTMhjmU9g/QRaNTyjQU2TvF5Iqsis4msW44i2GM8zGidYfGWbxMHxyEOoGEk69gRfsn8IemkeCUUSlVFIKwA/aFr0SQzfTd/TuB3Iq3GR/KtiRoApqrEbgGgr9/KdCfwx2RnZEIC1uNBjD80zKQXSenE6eLzyNnkljApxUzaTmPFPnRWnODkCNE4pvwXiTQxbwcssR4LTSys3r0UiZq50Q+ieiB7kgcs9NmMRjoSXMGLI230zzG5TuOKgsGDHFD2FcNICffkeirGnNIaX+ylVmQ9UOANYNddHQepBWeYKtqJIqju8S01sW2oPxdA9XZFlkns+RCw1+olywRekqYmryAdUROU/c3paSdWHLSNkmSofxeKrklW2D9+qAOVTwox/JZyj1wPHVphKtru6tg6O6jff+0ROCxA7s7ski4NXUWjAal5pAjTyulsx+Qmmjs2Tk17VpbSBY9GPUWp1J91CpNxW8hf+HGA8gdKqtnKlHvgoBST7kQlGHJIu7kUbCQ09AxWch0MzqHv1gAM3dzwhqwKuuAD1itucCIgywpaUa+gsuGNoqd30gDwmvGoh0OLitgovVi64R69L9FRAzMgu5TYJI6AsWMiOOsuYkRWR8yOopisTdaToo6q38QuXJ+XRQCSiiCsoHuMvD2x+/aaU0h6Bzf/zs2CzbHp32CbjEA1wPSkdQLkgjlowe18FD+59iMpAKqATKq1XaJ6oQKhgniozn8VTc6qKb64JnY2L29soNSFfMoNDGRd2r0rwDgUwT0yUS8TEySiCnToIakeiFJXOBWpxlKHJzh5Z1OLugrzc6RD4xII3DByVVN2rBN1xkKRXz8arxF2unKDRTqVyA/wCAgDKLNageJ9xGQ2PAxoCphS/jHGg3jqTEPaqadW0aNtiglbu1jWHhCsPVNwtmmpJ2MiFAwvidhy4OtzZV2FM9DMVDv76jDhAsMDe5PaxrTqjxzBvBVlmTOqbD9RLNSVK6HrFefcsgJapfLXKatCAB3gLxGo2tSbH/SMKcqGJDcAvPsFqgUSJ4cRQGlrR10JpveJYRVAF+7QqURb0uiWnmpBnBWxeuhTcAWYjToXf7olQfX8QBPwV6GqR2CECCCHlqxgP3ezaNbnHEQBbBFupRq0deOuUnY9ez+TFMifS9IIZe4KrCpFQEb7CzvEYTXWLCqqrYEo2cfMqBykMzxLKHg5SN6onoWgzbl7XIuMg31dlrUPCuj2pvdbIdS2NimGNZXN3I9+9/tgRHqeAYre0LICA2bYCwiLcZ7CghUoqaFkDC3OReZKLn9LCmLNbDC99yi6Ek6g9wYSK8CoJdSisDDCo9ADe9JCAVP2Bkmo+lBA6XpPgTNUnybGiONoRpkAlypEa6mAJAX/P41Zkr3vRiptGi6I3L/ScXlVcyS4RcMhdrMsHphYmyCt0lBb8KGXiIivZmkDT4msp4n7CUeMJ15xeuqo5+vNGtZ9QzIJ2OmJiajKFKgjRJpRKbjxUGSQPPsGJAzFW9gLPIaK1K9E/oHY5/IqOhkjU78hQUHJEGD1VqiOYVvWDqq6SE6eQMHiVRCIMF3CN0+Kwo6D+QaZVxY6MXxGdCO1UBCxgfHSQ+4ru7hWkbTluHd9SKsEsp73S0Oceeg3VVmwnSFGAQXWanuClsRAt8bWqIhWlxQwku9A529Z+jH8Mk1cRqp9iQ1XLbbdetGdzO4GGldLTdvi4ua1ZFCFxk1eESDpSmKZrvKHFqn40HPibErFkoGyXxlHyRZRbMhM8AwYbTK3EHpiVh8knuCpTFLrAQHBAwiCOBcxF3RMEqLAeNX5kfFRx0k5OshdbyNZ0V9YC5Zd9A1PzIjIZ+gpABkjqeMBwlMxLh7JXe00xg0IF4e01rUpEsY9DeBUEUHCXpGFVqVK2rnSywrzk/tcCwNZ2uw+gbIKbDUc7NHu5253hKySUvIYFBjdjT9mjfEBHQUXsbdE8CdRKC4RpmWqZLokMZZFAkEjPK3QWx+RV7AvWFlEcJ6jNq6h6xvNxfgFaE9Hlxh8AoA5tKhSQJywLJ8s6gSc1TPY35FY2qrNTxBYUDgdI5GVQhQmPgVDPR1rNy6kUwBYiGimFshbftCvsx/RiUcd1BEU2ChusaG96YYKm56WyKtejEGosFlSxfJc/VRYWkVblWZoHbtM3LwTT0Qk/45QQxujFv2mtS2EguTf5esnjxaPE0g9zCsaq26kwccEbOjk6sVKOI45OqwFAooy6gF7SmQUKeHFKypsfF6jOjXI6ir3nFX/hkEe5H3ESAR7wxSr9SgQ91OMpXtAwpPfqIp5UkmDofljFvpYpB0WwnmgitgJVKnBe5xSsJpyNdlR4LHYAnfbAxdmhrol0Vw0DrFgnN7L7A45tdqz6IN7CK1KaSeGvDg0oMU5E7CcQFQWNxyVYHgdLIEI+wnaphrHCMJlNTV96Mrtv4UVayibJqCk6GmUFmPPwblPEj/Y01CDbIgs5bQv8KNa4OhUguiVwXF0JS2llOb8qf4un+GrgTprBSCTKAZbyShILarNTJn6kUz5EwXp2NN/w2jvnnXCYGbCOfMynbKvydW5eanBAR45EqcpZGRox0vSCELFJRl/Kc+yJvgZnumJ8jBjJmZR500FzyZ+nKxZUlJZ7ojmJOSZeTOWgkCpFlDuO3gDtrpaarxSXQgZth/xcyGP91qG6Xl//7DiQCw7yGM9JNE+1fNkBBAJe2lVPkIP3yb2qfrRQIVImM5bm5jWUoguIUcVJHgBoqkSXhGl/5JKgrwvIZfPK6LJor40TyACRxiLSgAJ+zvUvEeJ582q+Co8hnv/xOYjH82lSStFPpWTAC9m/KmTcCKpwRGiM5zqrY/yyq15VU0y7OpSje3Iox3Jhgpc2+zlBuXOCUMgkYe+L68SycnZIDyo9S+FxKFSIhe7D5QUXX+ZHxeV+yCnLDCtXogNacoEXHlFwmuQRB9fFkZy7Dm3r9DNJNzlAKobqCwpBaXqpkh9LL2j64EU2wsDylo8D9pmUuJAkubbBHbPstV+bp4b1bgekCCBh8twUbwX/mEWDZOMUxQt6BXko6HL807I51pAcydH5/0rvksbSyK8mUDnqCooH3W39aWkBbKeWsv0DG+pHqKSNtfCYj+i6LLiAU14T5mi4mhlyexUVKZ2jKLm7YBaSOuEUuElbCnxRcrp43UviX6foXxq4Ehgp4TO9TCpyNmj4AeWA1VbuSplVcimJTtchTj9sU1BwxyG6CuEyOyhXM/sIzbMdlDRU2FAHyUZfjqfqGXUGpvsXH4RzFz/eELd0JGIDR44pQZX5wlagJuUjcxBflRpzxWHJI3avRkh9RfrdNz94NbZ/J8VFifLuCaoWcLjka2kPObXW1hgik9xT5fqG+yLRU4gN8qy8KoQRFqneG/r3OH80fP2kS4n4GxzhbKEqA3ijbX4wmr4TUk1v1cFa6v2L87Lronw6U6h6g7qivkPdL1DE971C4JXEVyq3EiIVfB6BdALHCavRs/4Jl7aqz7S9UoL4rpF/BXBGEhBlxabMd665CeYECUr8jG4H7UHxoFVl/AW1gY8rPCmD0rqXXRxoy+Q9O9qUT+zgqZI4knOgc5ty5jfyTt0X52DpPPR89OWrgrUQxI3jDHjAhyPhTC1DdSvg+9tc7FfWbC4XTZZB1l/eyKomiAwb8PBcc2t2Sk1Pt+dO/5SXQBbZIy+EkOdT4fmxorvZli8iY9W9KrF64KOCj7wc7HogcSIu40ibF74Oc6QYZTL43kJeG9fcLlZiDcl+68sGRC/rq56CnstPyR5UOnCBR+WfrQnAC0qEjHaMtgyc4h9ZxEQsX+CjccxTUyxer7PKaOfh7SvedaC4Lp2t9LM00nD9LJ+NnGJFSKujlsepUqFPAiwUr0SUpJfuCZnNuKDuukDWSjmDfM/gO1Wujl2QRyEuLjgllTRydk0QUVLjlEysTh7Vqukkq5vkwtpPIhX8GwZ+hk5AuNRH8MLCTECnuAeWrld5iQCQjdu16mcRFDPkbdUbX8Q/kgwpCmtTD8xaRlazUPTdEP2MfpXqEgdlj4yiF16OtaHNdVHm5eycFvCpNEt5NrIaZF7z5odXqfNrHs+4MIG1xIUoRXebq7ZVfr92zBIdR/jtC5KF5+MKFXfCOUKiqsoxfXUI2RG8JmcBq+PCWCCzGwGpPOmh6h8YiI6TlkVwwShr9zK80PxLMAkU58m2ZXcL2+YHGstR8NBWAkGorqofEdO5QGOB1Gof0h8zgPj4tw83Hx/WzbvrVzd3Fhq8+uYz33TTK/dv95s+mN96uPn+3d31g03ol69+9cVPfvPjw6df/t2rX734j9uHb1/88O7Fw/2LL376+OmLF19ff3/9zc0v9xfeX//ql3/H9dTZPp2vft2nwVFDH1Ub2bT0KCZlo+pvMx9mDDvAMab9nUF/7VMp2yz2/qz8NQx+NgMAZ9e/1WpOHaMNQSdSg+Un9kviV+ZZ5dKE4oeuT7kFjRZj0LPI991i4ibtYlGD2AIXfg2ezcRF7VLQr8ToidFTUrlaorNUKhf1kiqvVJp3fvXMhVcGnU2/NH2ATtSJmYFy1Js5cWGgdSlcKu2YfKbPDM1K0LMS/aLplkQFEJMvuXLRPAuzLpVXKq80OuuBC88GbzL5Ih6INXBJiUvmotFrDlx4lnmz8AvS1aJJ1MpNFlYZvTJsZW/r4MLoldEbpGvKee2XwqVxUYPGxjVm1jK/mERj9MaiW+MXG9c6r3RGGPnJhQaTX/BLD4FL46LN6Rxj6mxqhxV7okHmJnPpMFGHr3uLXLjZNV5n9M7ofTIePD+QlQElBrw0GH0w+kiFi5oPKDGypjsKv9iVATOM6r9o3uiTKQ02Z7A5A9YYTHAwwQFdJnSZ0GVCl8kHGScTRDPs4seb7NhxyVxozqxn5iYsPKHgRDpm4U32dsLe6Jv9wivw2bqwsAnXzVY/d/E3mSdiMeGQCSUmjHJc2pMLc+njcxcIMsKTC11Dz+PCBAcjIA8TWs9BL/DEcYlPLulzl/xbLowHg/1el/Z7Xf71hImF/ydM7OKV3/FBU3OrNnIAOTz9jxM4QR5me/pIsOTlLUB1kVQFqunpK8o8mp9w3lKfRdSXLfplC5ULnbcACsDdTOczVyRyvpYkn12hrUXdly3meQvbaZxzhW/nLfLlGNmRuKyE5sUY+XIMj0+e3lY4tXHuoHgY3i5fkVP25G70MMVCw4s+qxzmfNagCssT8nHZQLDARQOVE1nwdd6gcWbtrAElXjrhe9lAyPJFAyVcDF49b9AB/88adMorLV102UDHRS4aUBU9dFIlnT0dJHQND1ZdyzjjSnuDfIzcMtWAnPcxVaijRIAlLi+envc4rygkwkaFs5Vz7vLyFt8M8/z7uZBy9OPyFsex/LO/8XKkc7H1Wr6onL0kNV6OdC65kY9wWe3V5l7i5UjpcqTk37DlPKOVy16+cTFS4jyQf7/FSsTP38iXIxmkbgcodI5O52Iv37gYyYIdOx+04U6dSy6fObq8pWJile3zgarLNy5GKnxQcfph73N51gGmi5Es5rbvT/CFungu03rjYqTKR5/1xZ/gn6FqHCbXV6mearnkZ6Z0Xl9fANbXdNqZaltfkLZPMMTOp6P0oZ+L3vTNOKsisI8g67ta+szU+Wu4TVf6Gol+Zn1i7fI1fW1aH0oQmKCPHVz0hkdgvZ2Lrr7roT8XfZssRxXRn8svHwPR9/bOGsltS/pc5bkE82UYfVn4spFypnwM9bKRPidxMZLqTZTBPJfgrE9r25+LkfSBPDO96VyCMx+NSefGN22KZ5Ks77kEZ7ulPxcjyf7KAJ9LsN4f9udipCyP6M8IGpy+UPJnBQ3kJ3e5rQ4diMhDkS5xyFAkNhSIDbnCY8r+iMWmIhd8/5kBE/RUXv1suq/oDTfaneIAHBD8ixJEv4GAJxD6BRz7gIcdcJgjAEBEtUeipwUjELtFgRkxEmREOot0FvH994BJFw/5CYZSABXAEiZmlgj5U3ZQgQaEMYlQLDFCImJJBCCJmCEx+URAkAgB0gQjcGwhOZoA4FD0Zia8y4Q4mdgm03UmmsmMkKFnJq4rrKEE/wVw4LiDAw4Qq0CsQmBWCHQLCEwh6C4E3aU77sDN4bgDv1hRITgpxBOVSVSiykoAWVnmgTvwjOjwABwcaaAB0XaFrBVKVOZSCW0rw1Zioka03aJfwAgYdkEM2X8BKsAorQJUAHc0otHGXBrxZyO+PuAHfhEPNhjzAByACtjbjvXoUL4DvfTsvxxp4Bf70JlEB67qcEFvvAIJOpzcmUuHIB2CdKaEPJ/ACJ5BrA6xHKHo7FiHIwfCNYKjF6AQMNHZZUEaDnAAMeCbHAAHb7L9x4UGEOT8Up9cGN2BEcTwwEfSb7nk/78La0Aa/5BL+0Mv/Xdfxl/y5U+GGHz221zPiMGjFs+IwUWDZ8TgGTF4Rgye3HpGDJ4Rg2fE4D8tYnA6Zv5nRQxEUkIZ0qNd8W9XsEhSdCigHor/cO7JYZK0HIpjyFUOxeOrLAFsQRiCAtgpwZwK4Z5gC44q6P+iQqG8J7tWVQLqISA7gczuqlEg7AoEvKE64sArxFuBQCsQYQUC3jC8mgHkgAA7YgOjG6jkoATPcJUjkMEqcSC6jAS1kbg+knSMxHCRgSIrOkAJhyEceKCMoXkZA28yTy9jyATDOfrF6xeAE1bhAs/Iwh6IA///ewT7R/0CUb6jCg4nEM2uigU6K8AXBXoWJliIngvzLOAWhYxpYYRKyFmxihWFXAkWKwFhJQSshICVsOuoX+AmQXQtfpMGkLwC1lTAmsrMVlGDgwvMs7IrFfTjKHjw8geHIcZnLgTfdZQnFxqMJ/USwxvMxxei9Tod2kh/lEv+Y17KX8Kl/qyX9ode+h/lMv7KL38yrOCzn1Z5xgoetXjGCi4aPGMFz1jBM1bw5NYzVvCMFTxjBf9psYLTeeU/J1bQFOY2les2hX1NfjTl2ORIu2JSxxMUS3aFb9RckwElAdrVT1d01hWHUXtNfpOE5lD6lUprEolD8fco1DAIYWggD4/+UtUwOCIhPEFpzinVQZXzLKpeUD9T/Tj+QNi2jkUAAYTsFQw88/MQRDCRtGpE9CNR9FGzwDOC8EjoGbufh3BcgJvrPITDA5QZkEhNABCJhGhiLkfpAq+U8uTSfu/LeHKZf+CFVPtvv8Tni1/S8+Wv7JJ/nkv5K7/8yeCBz36W4xkeeNTiGR64aPAMDzzDA8/wwJNbz/DAMzzwDA/8fPDA/vPu+sf7Hx7M6Xlz/5br9fe3dz/aN81+/LCH71/+cLu9+PL63bu7my+5s/3in2++ub958S///Rfbi/99/+r+4X578U83d/9+83D7+np78ffvb6/vthcfrt9++PLDzfvbN9uLX/y9tX/xD+ZXvfjH7+//7XZvenTjd3an6sPt/9knFwve2De3b21C+6zS7mO94nInh8tcuv2lu5tvbt5+rZduH+7k3gESvPzHfzB/7f797c3bh+uHW0ET3+4j/Hj99vW3cu72eT/cf2+39L22j8eDu5s3hnp8ZIin4MfPT6RPn2wRH28/fHbV79/vbV/fv39/81qL/mRwz8ebr99fv/1mf/Hh/Q83n2ytv6H9fxP88uJvvpC3641+/aVEKO/c8uHb+//48O72u5sP9LXfsV/f339tPIUD/bfXr9/ffzAEiDffXr/bn73+4f2Hnbp+8+Hb29ffvb3Z3/oq+a2vrz98u7/39f3Deml54a/url9/t9/cCffdhx/e7OvZb37xktk8ufn2fnfxP/Fgscab67sPN76NPs+PC1D6+vbDw77t+70v46dP/xf8EF6R"></div>
</div>
<figcaption><p>The amount of damage sustained by successfully scanned QR codes. The hover information displays the number of points to the left of the line (number of successful scans up to and including that amount of damage).</p>
</figcaption>
</figure>
<p>Under these experimental conditions, level L codes become much less readable above ~6% damage, level M above ~12%, level Q above ~18% and level H above ~20%. This correlates with the resilience I quoted in the table above.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I’ve learnt that the four different QR error correction levels influence both <strong>data volume</strong> and <strong>ability to scan</strong>. For a given QR version, increasing error correction allows storing less data, but makes scanning easier and more reliable. However, for storing a fixed amount of data, like a particular URL, the version is computed automatically, and the easier scanning can <strong>trade off</strong> against the data volume. This ties back to the size of the individual modules (little squares): there is a minimum certain size before a code is scannable, and so a denser code needs to be larger.</p>
<h2 id="experimental-caveats">Experimental caveats</h2>
<p>The results I’ve talked about here are all empirical, and there’s a bunch of reasons that they’re not perfect:</p>
<ol>
<li>I tested only one method of reading QR codes, <a href="http://zbar.sourceforge.net/">ZBar</a>, although some quick experiments with my iPhone seem to correlate with the results here.</li>
<li>I also tested only one background image, so the behaviour may differ greatly with QR codes contained in different surrounds.</li>
<li>The QR codes are generated to be perfectly rectangular and aligned to the image pixel grid, which is unlikely to happen in the real world.</li>
<li>The noise generated is random Perlin noise, which isn’t likely to be what occurs in the real world.</li>
<li>The overlaying of the noise carefully skips damaging the position patterns (the big squares in three corners), because the performance is catastrophically worse if they can be damaged too.</li>
</ol>
<section id="external-links" class="no-print">
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=QR%20error%20correction%20helps%20and%20hinders%20scanning&url=https://huonw.github.io/blog/2021/09/qr-error-correction/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2021/09/qr-error-correction/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2021/09/qr-error-correction/&title=QR%20error%20correction%20helps%20and%20hinders%20scanning" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<script id="plotly-template" type="application/zlib">
eJztWutu4ygUfhf/2kpoZPC9r1JVI2KTxCoxWYy3TaO8+56DISGXdkeabppRLNWqgz/O+TgX7tuo4YZHj9toxnX0+LSNhNZK/3zDolpJBYWRXsz+SnJi/x6iHXGYzaeYFdcvQiNEtp04hbIkJfZJ2UNEote2McvoMf6RQc01N0boDqvMWylXqoHqkfpHaMk3AO7bdyigMbwp2Tat2WBNtoOqZrNGLLZl90zw/1pJ17BvJjQSQVY1/BDGcuL8re1RrugaZHXZmiRa6Lbx316XrRFQFuJ92artlL4E7g3X5mMNO7TVzVBxRnN2siZbKq3WUqBPnpzjbMBuIzVYYd5hULutXz4ij9+kAE+W43uPfhxM3zbCfXVyWEDjoNtSUZ1Rg74iDyukr7kENk9P8Q+QbSOWQLRmD9EzwbKcFWkWV2WcVIxmeeUwKaEZSalDUZalVUyrPC7KNKFJMaLSAkKfZIVHlSVLMlallBZ5WsT5iCooYSUpmEOxQFuJ2kdUVZAkJqVHoZJQ6QiioA5RlUdZPdler0NlMWEFqTz7NFCGyh2qAPaMlKVDZWH7WJY5Q8QJYTkpvMbsWCFzKOAFxsi9JT4wKrixyEnueeXVsUpvekZoXJAicbAiMClq9jBwUZKRynuyDJsIij0sIzQHicxbttzrG63rcTkB/4HXvdojnUmWeBxYDZpNy7GtdB9VaHTsB1kMH54PWeDCPkiBoB+7bkIeqUdCS8HNiq+nnJxy8p5y0od9kAILOSXBlAR3mAQQ+DYN2t6ohearkyn/70/hD5KP9LBmSrgp4e4q4YLQP0mFaW00ZcR9Z0S4TFqJfplcc3hwdJxepLDmulZKN71lcbTv9T+zOdA5cLCMWjHufA1GwfC8aEGa0YM4wAGAQEhqHLJPxvHrUvccAj7OoVc25TcbwEWT+xWs+7+ZV7AF4EoWQt0CMaQRspI3QUqGnGDOPFNvt8DLMQm4Xdq0/xZqh037sOA23OmpBOxwicP15hbYeSqW3aDnvBbTtHCaFt7TtNCHPaaA4TPpEkBI2ft9iPNTSIgYfNLYnoSdnlSOZ2iYbUvBmzG9L8mhZULsg5HwqRzHdeS3e0Ys30BeIZp3nTLctKprxJwP0ljeXGv1GirLC/Aa6MIYgPo4s0Op3bCaCY1p2hvd1iZyaenPGa/ULYVdwTbqxd+D6EzL5dQxTB3DtTuGQ/St2m7opxCcQvC6Iej7w1eOk8QxDkADBdKF7VtHs1FoA7NeH0tKZAss4tSXIMOiDCCUVU5OtS8CUpTFpDpIpiAEbEDRPB4UE/fsUTGIQZV0X5HGgCghYmNr12iuOvPp7SO7DtxGs8XZVRX+cnZVRfKuOR6Fj+4C9Uv1itV6v1MxFnTN/vcwG7rWnAyuMEaP5wszYcdnLtsFDFqRFHOz/+oOImqpetHjELnma6F/nvNeS2WC4gs03aIJh+3FAK9+nP3VCzvYKsTu92Pc0GovB32mWPMGerSvU7fD9YwYJyxvXuyM1y8LrYZPPXVJN5b5+cDHTA7iQydf5Eeid6HVuSCgvfmj2L7/QWwxJpaQGuFEFCe+H81DD1NeP6cECWrNa3eyh9f69qvl4B7elyTL1TKv/tKk2+fa+d7sb8vHV+Om4Aa6TjWfR48UvfDfyXQLdHa7fwEhtg6y
</script>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:modes" role="doc-endnote">
<p>There’s also the concept of the <em>mode</em>: Numeric, Alphanumeric, Binary or Kanji. However, they all store an (approximately) equal number of bits, so I’ll just use the Binary mode, because I’m far more used to talking about bytes than bits.</p>
<p>Details: each mode defines an input character set and then an efficient way to encode those characters. For example, for Numeric, the permitted characters are 0, 1, …, 9, and they’re encoded with 3 digits in 10 bits. The Binary mode is probably familiar: one input byte is encoded into 8 bits in the QR code. To convert numbers in this post into bits, multiply by 8. To convert the number of bits to the number of input characters in each mode, divide by the appropriate coefficient (Numeric: 10/3; Alphanumeric: 11/2, Binary: 8, Kanji: 13). <a href="#fnref:modes" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:quadratic" role="doc-endnote">
<p>For fixed field of view, the distance scales linearly with the side length of the QR code and the field of view is proportional to the area, which scales (close to) quadratically with the side length, so the permitted distance is approximately inversely proportional to the square root of the required field of view: quadrupling the required field of view requires being half the distance (approximately). <a href="#fnref:quadratic" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:consistency" role="doc-endnote">
<p>The various numbers flying around here match up: level L modules need to be ~30% larger than level H ones, and the level H QR code requires 2.35× more modules. This predicts that the level H code would have to be 2.35/1.3 = 1.81 times larger than level L one, and that’s pretty close to what we observed: 1.4/0.8 = 1.75. <a href="#fnref:consistency" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2021/08/home-cooked-appThe joy of cooking (an app)2021-08-30T00:00:00+00:002021-08-30T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>Taking shortcuts? Leaving edge-cases unconsidered and unhandled? Is this engineering?! My approach to programming and software engineering has been shaped by years of building <a href="https://rust-lang.org/">open source</a> <a href="https://swift.org/">compilers</a> and <a href="https://stellargraph.readthedocs.io/en/stable/README.html">libraries</a>, where those edge cases matter, reliability is crucial and flexibility is important. It’s been a breath of fresh air to take a step back and stop thinking about every detail, and instead write code that works for a specific set of people: me and my wife.</p>
<p>Early in 2020 (while fires raged, in the before-times), I read <a href="https://www.robinsloan.com/notes/home-cooked-app/">Robin Sloan’s <em>An app can be a home-cooked meal</em></a> and the idea of code designed for a small group has been rattling around in my head ever since. I can feel the truth in Sloan’s writing:</p>
<blockquote>
<p>And, when you free programming from the requirement to be general and professional and scalable, it becomes a different activity altogether, just as cooking at home is really nothing like cooking in a commercial kitchen. I can report to you: not only is this different activity rewarding in almost exactly the same way that cooking for someone you love is rewarding, there’s another feeling, one that persists as you use the app together. I have struggled with words for this, but/and I think it might be the crux of the whole thing:</p>
<p>This messaging app I built for, and with, my family, it won’t change unless we want it to change. There will be no sudden redesign, no flood of ads, no pivot to chase a userbase inscrutable to us. It might go away at some point, but that will be our decision, too. What is this feeling? Independence? Security? Sovereignty?</p>
<p>Is it simply… the feeling of being home?</p>
</blockquote>
<h2 id="the-project">The project</h2>
<p>I got married last October, and I wanted to have an anticipatory countdown, somehow. We both happen to have Garmin watches, which support custom watch faces<sup id="fnref:monkeysee" role="doc-noteref"><a href="#fn:monkeysee" class="footnote" rel="footnote">0</a></sup>. I created a simple but fun little app: it shows the time, date and the battery, but also has a photo and a message.</p>
<figure class="image has-caption">
<div class="image-positioner">
<a href="screenshots.png">
<img src="screenshots.png" alt="Two images side-by-side. On the left, a digital rendering of a watch face at 17:24 on 27 December, with the text Happy Birthday, Sterling! across the top. On the right, a photo of the same watch face on a real device" />
</a>
</div>
<figcaption><p>A screenshot of the watch face from Garmin’s simulator (left) and on my watch (right), on the birthday of <a href="https://www.instagram.com/corgi_tales/">Sterling</a> (pictured, excited about a tennis ball).</p>
</figcaption>
</figure>
<p>The photo and message changes each day, usually randomly. However, on some special days, it chooses a particular message or photo, like the countdown across the 10 days leading up to our wedding. The photos are synced, so that both of us get the same photo each day<sup id="fnref:photo-syncing" role="doc-noteref"><a href="#fn:photo-syncing" class="footnote" rel="footnote">1</a></sup>. The random messages aren’t synced: instead I wrote some for my wife’s watch and she for mine. It’s nice to wake up each day and see what unexpected combination of photo and message I get.</p>
<h2 id="engineering">“Engineering”</h2>
<p>If I was to create a gallery/message app <em>properly</em>, there’s a whole lot of work required beyond the core “show the watch face” code:</p>
<ul>
<li>upload photos and messages for random selection</li>
<li>associate particular photos and/or messages with particular days (with the possibility for recurrence each month or year)</li>
<li>potentially, a service to sync the photo order/selection across multiple devices (or, just being careful to put the same photos in the same order on each one)</li>
<li>message templating, because some messages should say “Happy 1st anniversary” on the first instance, “Happy 2nd anniversary” on the next</li>
<li>wrapping text nicely, to avoid it overflowing off the edge of the screen, like the example above</li>
<li>workflow for publishing to an app store, including signing and updating</li>
</ul>
<p>Instead, I didn’t do it properly, because it’s an app to run on two and only two devices. I can break all the rules: I can hardcode content, I include private information<sup id="fnref:private" role="doc-noteref"><a href="#fn:private" class="footnote" rel="footnote">2</a></sup>, I can make assumptions about devices. For instance:</p>
<ul>
<li>the app directly embeds all 25 background images<sup id="fnref:size" role="doc-noteref"><a href="#fn:size" class="footnote" rel="footnote">3</a></sup></li>
<li>our two devices are different models, so the messages for each of us are conditionalised based on the device qualifiers using build system functionality</li>
<li>those two models are the only ones supported, so a minimal CI that builds for those devices is all that’s needed</li>
<li>the app can be easily sideloaded: plug the watch into a computer and copy the compiled <code class="language-plaintext highlighter-rouge">.prg</code> file onto it (I think this is a critical component of “home cooking” code: one must have sufficient control over the devices to run arbitrary code, without expensive or complicated processes managed by external corporations)</li>
</ul>
<h2 id="open-source">Open source?</h2>
<p>The modern software world is <a href="https://www.fordfoundation.org/work/learning/research-reports/roads-and-bridges-the-unseen-labor-behind-our-digital-infrastructure/">built on the shoulders of free and open source software</a>. This project is not even 700 lines of code, and it’s still no different: I wrote it in Eclipse, stored it in git (via Emacs and <a href="https://magit.vc/">Magit</a>), cribbed the toolchain setup for CI from <a href="https://github.com/jokarls/donkeybrains">code published by @jokarls</a>, and that’s ignoring my little Python script for resizing and dithering the images appropriately, and of course skipping over whatever is used by Garmin (and the computer upon which I wrote it, and the internet services I used…).</p>
<p>Unfortunately, I think open sourcing this sort of code removes a large chunk of the freedom that’s so glorious. Just as publishing a recipe changes how it is phrased, so too does publishing the source code. I’m comfortable sharing code freely, whatever its state, but I’m not comfortable sharing the private messages I’d write to my wife, or the photos included: without the pressure of openness, I could just place these wherever is convenient.</p>
<p>I’d also prefer to keep these personal project free from the unfortunate negative aspects of published code: my experience with open source is somewhat tainted by the responsibilities of maintainership and the associated guilt, burnout and occasional instances of unpleasant user entitlement.</p>
<p>I’ll give back from these sort of “cooking” projects through blog posts, contributions to the tools/libraries used and potentially splitting out small parts, instead of open sourcing the projects themselves.</p>
<h2 id="like-cooking">Like cooking</h2>
<p>I don’t generally like cooking that much, except for when I’m cooking with my wife. I get the same immediate and personal sense of fulfillment writing code for (and, in this case, with) her. “Eating” the app on my wrist for months is just icing on top.</p>
<section id="external-links" class="no-print">
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=The%20joy%20of%20cooking%20(an%20app)&url=https://huonw.github.io/blog/2021/08/home-cooked-app/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2021/08/home-cooked-app/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2021/08/home-cooked-app/&title=The%20joy%20of%20cooking%20(an%20app)" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:monkeysee" role="doc-endnote">
<p>The programming language/environment for the Garmin devices is called “Monkey C”, so they took the opportunity to call the compiler “Monkey Do” 🥁 <a href="#fnref:monkeysee" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:photo-syncing" role="doc-endnote">
<p>The photo for any particular day is chosen by hashing the <a href="https://en.wikipedia.org/wiki/Unix_time">Unix time</a> of midnight on that day and using this as a “random” index into the list of photos. All these factors (reference time, hashing algorithm, and order of photos) are the same on both watches, so the chosen photo always matches on the two devices. <a href="#fnref:photo-syncing" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:private" role="doc-endnote">
<p>I still wouldn’t (and don’t) include credentials or other valuable personal information, but I can at least feel free to include sickly sweet messages for my wife. <a href="#fnref:private" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:size" role="doc-endnote">
<p>Unsurprisingly, an app containing 25 images ends up as a large artefact, even when they’re reduced to the limited fidelity the devices can display (4 or 8 bits per pixel and circles with diameters of 208 or 240 pixels). There doesn’t seem to be much documentation, but I can report that watch faces of the following sizes seem to work in practice: 718KB for a Garmin Forerunner 45 (FR45), 1.78MB for a Garmin Forerunner 245 Music (FR245M). <a href="#fnref:size" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2020/04/worktrees-and-pyenvGit worktrees and pyenv: developing Python libraries faster2020-04-15T00:00:00+00:002020-04-15T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>It’s glorious to work on one task at a time, to be able to take it from start to finish completely, and then move onto the next one. Sounds great, but reality is messier than that and context switches are common. Doing so can be annoying and inefficient, but <em>not</em> switching means tasks and colleagues are delayed. I combine <a href="https://git-scm.com/docs/git-worktree">git worktrees</a> and <a href="https://github.com/pyenv/pyenv">pyenv</a> (and plugins) to reduce the overhead of a context switch and keep development and collaboration as smooth as possible when working on a Python library.</p>
<p>Reducing the pain of a context switch allows me to work on many tasks concurrently: usually one or two large features with a few medium ones and a variety of small ones. My day typically involves working on the larger features for hours consecutively, and then either getting blocked or needing some time to think subconsciously, and thus pick up one of the smaller tasks.</p>
<h2 id="context">Context</h2>
<p>This post talks about how I use these tools in practice, in my work on the <a href="https://github.com/stellargraph/stellargraph">StellarGraph library</a> for <a href="https://medium.com/stellargraph/knowing-your-neighbours-machine-learning-on-graphs-9b7c3d0d5896?source=collection_home---6------7-----------------------">machine learning on graphs</a>. StellarGraph is a Python library built using TensorFlow 2, Keras and other common data science tools.</p>
<p>We take a fairly standard approach to developing and distributing the code, which means promptness and collaboration are important:</p>
<ul>
<li><a href="https://github.com/stellargraph/stellargraph/tree/develop/demos">extensive demos</a> and documentation</li>
<li>supporting multiple versions of Python (3.6 and 3.7)</li>
<li>all changes happen via pull request (with <a href="https://github.com/stellargraph/stellargraph/pulse/monthly">70-100 per month</a>)
<ul>
<li>code review is required: a pull request cannot be merged without at least one approving review</li>
<li>testing is also required: a pull request cannot be merged without passing <a href="https://buildkite.com/stellar/stellargraph-public/">continuous integration</a>, including <a href="https://medium.com/stellargraph/better-notebooks-through-ci-automatically-testing-documentation-for-graph-machine-learning-5789e277e597">validating the demos</a> (which all adds up to take a few minutes)</li>
</ul>
</li>
</ul>
<p>The <a href="https://www.stellargraph.io/">StellarGraph team</a> is a project of CSIRO’s Data61 that brings together engineers, data scientists, UX designers and researchers. I mostly fit into the first of those boxes and part of my role as an engineer on a mixed team is being a force multiplier: optimising or otherwise making the code more reliable, and improving the tooling & processes listed above (and others), all so that others with less engineering experience or more research interest can use their strengths best.</p>
<p>This means my day is often broken up with:</p>
<ul>
<li>reviewing others’ pull requests, so they’re not waiting for approvals and feedback; this sometimes involves checking out the pull request’s code locally to run it</li>
<li>responding to code reviews on my own pull requests, to try to keep the “review cycle” as tight as possible; this usually involves editing the code in response</li>
<li>doing small fixes for annoying <a href="https://en.wikipedia.org/wiki/Paper_cut_bug">paper cuts</a></li>
<li>validating requirements and expectations for larger or more complicated features and bug fixes; this often involves doing a proof-of-concept implementation as a draft pull request</li>
</ul>
<p>All of these involve working with the repository’s code on my local machine, and all of them are typically short tasks, ranging from minutes to hours, not days like a new feature.</p>
<p>In addition to all of these code tasks, when I do finish a feature or bug fix, I then have to wait for code review from one of my colleagues: this is usually fast (at most a day or two), but, if I was trying to avoid context switching, it’s a long time to do no other work.</p>
<h2 id="sequential-development">Sequential development</h2>
<p>Suppose I’m working on feature A, but then notice paper cut B that would take a minute or two to fix, maybe a typo in documentation or a minor bug. This is something like the third situation above.</p>
<p>One approach is to just do the fix in the changes for feature A, so that the eventual pull request and code that lands combines both A and B. This is suboptimal, as it leads to:</p>
<ul>
<li><strong>Slower code reviews</strong>: each review is larger, and it’s unclear what change was for what purpose so more detangling is required.</li>
<li><strong>Less useful git history</strong>: if the combined A+B change appears in a <code class="language-plaintext highlighter-rouge">git blame</code> investigation, it’s just like looking at it in a code review except the author likely can’t answer questions about it, because they no longer remember (I certainly forget these sort of details quickly).</li>
<li><strong>Slower landing</strong> of the useful small fixes: the fix for B gets caught up and delayed by the lengthier review of the A’s pull request.</li>
</ul>
<p>A better approach is to do this work on a separate branch, and thus in a separate pull request. Doing this concurrent work with a single copy of a repository is frustrating, because the book-keeping work required to do the fix dwarfs the actual effort to fix:</p>
<ol>
<li>Save the state of my work-in-progress work on A’s branch <code class="language-plaintext highlighter-rouge">feature/A</code>, which means both:
<ul>
<li>the actual changes to files on disk (either with a “WIP” commit or <a href="https://www.git-scm.com/docs/git-stash">stash</a>, taking care to manage any relevant temporary or new files)</li>
<li>any context that’s not directly stored in version control (such as where I’m editing in each file, or what I’m planning to do next)</li>
</ul>
</li>
<li>Create and switch a new branch <code class="language-plaintext highlighter-rouge">bugfix/B</code>, do the fix for B, create a pull request for it</li>
<li>Restore the A work, by switching back to the <code class="language-plaintext highlighter-rouge">feature/A</code> branch and then reapplying/remembering any other state from step 1</li>
</ol>
<p>The actual fix, step 2, is much easier and even faster than either of steps 1 or 3, which require remembering things about the code base while also remembering things about git (hard work!).</p>
<h2 id="concurrent-development">Concurrent development</h2>
<p>The list is much simpler if I have multiple parallel copies of the source code, each of which can be changed independently:</p>
<ol>
<li>Change from the directory that has <code class="language-plaintext highlighter-rouge">feature/A</code> checked out to one that doesn’t have any work-in-progress work (or create a new copy)</li>
<li>Create a new branch <code class="language-plaintext highlighter-rouge">bugfix/B</code>, do the fix for B, create a pull request for it</li>
<li>Change back to the directory for <code class="language-plaintext highlighter-rouge">feature/A</code>, picking up where I left off (including things like the exact position for each file in my editor)</li>
</ol>
<p>The whole process takes about as long as step 2 itself because there’s little book-keeping. It doesn’t directly fix the “remember what I’m planning to do next” problem from the first version, but it does dramatically improve it: I find there’s minimal context switching from a “work on the code” mindset to a “manage git” one, and there’s less time to forget or get distracted because the whole process is faster.</p>
<p>Using separate directories has other benefits over switching branches too: it makes it easier for incremental compilation (and other tooling) to reduce work, although this doesn’t apply to Python so much<sup id="fnref:incremental-compilation" role="doc-noteref"><a href="#fn:incremental-compilation" class="footnote" rel="footnote">0</a></sup>; and, I find it’s easier for me to find my code<sup id="fnref:branches" role="doc-noteref"><a href="#fn:branches" class="footnote" rel="footnote">1</a></sup>.</p>
<p>One way to implement this is to clone the repository multiple times into separate directories, like <code class="language-plaintext highlighter-rouge">...-1</code>, <code class="language-plaintext highlighter-rouge">...-2</code> and so on:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre>git clone <span class="s1">'git@github.com:stellargraph/stellargraph.git'</span> stellargraph-1
git clone <span class="s1">'git@github.com:stellargraph/stellargraph.git'</span> stellargraph-2
git clone <span class="s1">'git@github.com:stellargraph/stellargraph.git'</span> stellargraph-3
</pre></td></tr></tbody></table></code></pre></figure>
<p>I used this effectively for many years for a few different projects.</p>
<figure class="image has-caption">
<div class="image-positioner">
<a href="many_copies.png">
<img src="many_copies.png" alt="a diagram showing four repositories: one labelled remote repository with the .git subdirectories of three local repositories pointing to it, labelled 1, 2, 3" />
</a>
</div>
<figcaption><p>A stylised view of the structure of multiple clones of a single repository shows they are isolated from each other.</p>
</figcaption>
</figure>
<p>However, each copy is isolated, which leads to some problems in practice:</p>
<ul>
<li>They each require the full git state, which is potentially large.</li>
<li>They can end up with inconsistent state, especially about remote branches: <code class="language-plaintext highlighter-rouge">origin/master</code> in <code class="language-plaintext highlighter-rouge">stellargraph-1</code> may be different to <code class="language-plaintext highlighter-rouge">origin/master</code> in <code class="language-plaintext highlighter-rouge">stellargraph-2</code>, depending on when <code class="language-plaintext highlighter-rouge">git pull</code> or <code class="language-plaintext highlighter-rouge">git fetch</code> was run.</li>
<li>There can be confusion about “where was that branch”, whenever I do switch away from a branch, because, by default, each branch only exists in the copy where it was created.</li>
</ul>
<h2 id="concurrent-development-with-worktrees">Concurrent development with worktrees</h2>
<p>A better way is to use <a href="https://git-scm.com/docs/git-worktree">multiple worktrees</a>. These behave almost identically to multiple independent clones, except they share a single <code class="language-plaintext highlighter-rouge">.git</code> directory with all its metadata. Therefore, each one has access to all branches (local or remote) and the state of each of those branches is the same in every copy.</p>
<p>A worktree can be created in a single command<sup id="fnref:magit" role="doc-noteref"><a href="#fn:magit" class="footnote" rel="footnote">2</a></sup>, similar to a <code class="language-plaintext highlighter-rouge">git clone</code>:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre><span class="nb">cd</span> /path/to/stellargraph-1
<span class="c"># now inside the repository that needs multiple copies</span>
git worktree add ../stellargraph-2 <span class="s1">'some-existing-branch'</span>
git worktree add ../stellargraph-3 <span class="nt">-b</span> <span class="s1">'some-new-branch'</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The first argument to <code class="language-plaintext highlighter-rouge">add</code> is the (sibling) directory for the new worktree, and the second optional one is the name of the branch to checkout there (to start with). Each of these <code class="language-plaintext highlighter-rouge">stellargraph-...</code> directories acts like a normal git repository, and all the usual operations like creating or switching branches, committing and stashing work just fine.</p>
<figure class="image has-caption">
<div class="image-positioner">
<a href="worktrees.png">
<img src="worktrees.png" alt="a diagram showing the same four repositories as before, except now the remote repository has an arrow only from the .git directory of the local repository 1, while 2 and 3 point to that .git directory" />
</a>
</div>
<figcaption><p>The stylised view of worktrees shows that they all share a single main <code class="language-plaintext highlighter-rouge">.git</code> folder.</p>
</figcaption>
</figure>
<p>I use a fixed set of worktrees, numbered from 1 to 5 (at the moment).</p>
<p>An alternative (that I’ve never tried) is to have a worktree for individual branches. If we’re starting a new feature in a new branch <code class="language-plaintext highlighter-rouge">foo</code>, the system might look something like the following:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre>git worktree add ../stellargraph-foo <span class="nt">-b</span> foo
<span class="nb">cd</span> ../stellargraph-foo
<span class="c"># edit code...</span>
git push
<span class="c"># (repeat from 4 until finished)</span>
<span class="c"># finished, so the worktree isn't needed any more</span>
<span class="nb">cd</span> ..
<span class="nb">rm </span>stellargraph-foo
</pre></td></tr></tbody></table></code></pre></figure>
<p>I’ve used worktrees on all sorts of projects, including Scala, C++ and Rust. Python brings some special challenges.</p>
<h2 id="parallel-python-dependency-management">Parallel Python dependency management</h2>
<p>Multiple copies of a source tree is great, but also leads to some confusing circumstances if the state is mixed up. It can be easy to edit a file in the wrong directory, and then not understand why it’s not applying. I know I’ve spent time tracking down incorrect-location edits whenever there’s multiple copies of anything (functions, files, directories, …)!</p>
<figure class="image has-caption">
<div class="image-positioner">
<a href="https://xkcd.com/754/">
<img src="dependencies.png" alt="an xkcd cartoon showing a stylised screenshot of a college course description. Course: CPSC 432. Description: intermediate compiler design, with a focus on dependency resolution. Prereqs: CPSC 432" />
</a>
</div>
<figcaption><p>Dependency management is hard enough, without having to do 5 times at once</p>
</figcaption>
</figure>
<p>Python’s common approach to dependencies makes this worse: code needs to be run in the correct <a href="https://packaging.python.org/tutorials/installing-packages/#creating-virtual-environments">virtual environment</a>, which is, by default, managed separately to the current directory. Typically, each checkout would have its own virtual environment, in case there’s differences in the exact set of dependencies used, and to allow each copy to install its exact on-disk version of StellarGraph <a href="https://stackoverflow.com/a/35064498/1256624">with <code class="language-plaintext highlighter-rouge">--editable</code></a>.</p>
<p>A sequence in a terminal might look like:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre><span class="nb">cd</span> .../stellargraph-1
<span class="nb">.</span> venv/bin/activate
<span class="c"># do work, like running tests:</span>
pytest tests/
<span class="c"># finished there, let's move</span>
<span class="nb">cd</span> ../stellargraph-2
<span class="c"># run tests again</span>
pytest tests/
</pre></td></tr></tbody></table></code></pre></figure>
<p>Unfortunately, I’ve made a mistake there: the <code class="language-plaintext highlighter-rouge">pytest</code> run on line 9 is running with the virtual environment from the <code class="language-plaintext highlighter-rouge">stellargraph-1</code> worktree, not properly reflecting the state of the <code class="language-plaintext highlighter-rouge">stellargraph-2</code> source tree. I needed to run <code class="language-plaintext highlighter-rouge">. venv/bin/activate</code> again after the <code class="language-plaintext highlighter-rouge">cd</code> on line 7, using the <code class="language-plaintext highlighter-rouge">venv</code> directory in <code class="language-plaintext highlighter-rouge">stellargraph-2</code>.</p>
<h2 id="pyenv-to-the-rescue">pyenv to the rescue</h2>
<p>I solve this by using <a href="https://github.com/pyenv/pyenv">pyenv</a> and <a href="https://github.com/pyenv/pyenv-virtualenv">pyenv-virtualenv</a>. Instead of creating a virtual environment in each worktree (such as in a <code class="language-plaintext highlighter-rouge">venv</code> subdirectory), I create a pyenv virtual environment for each one and associate them using <code class="language-plaintext highlighter-rouge">pyenv local</code>. For example, for the <code class="language-plaintext highlighter-rouge">stellargraph-1</code> worktree, I ran something like:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
</pre></td><td class="code"><pre>pyenv virtualenv 3.6.10 <span class="s1">'stellargraph-1'</span>
pyenv <span class="nb">local</span> <span class="s1">'stellargraph-1'</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>After doing this, any command that uses <code class="language-plaintext highlighter-rouge">python</code> or <code class="language-plaintext highlighter-rouge">pip</code> (like <code class="language-plaintext highlighter-rouge">pip install --editable .</code> or <code class="language-plaintext highlighter-rouge">pytest</code>) anywhere within the <code class="language-plaintext highlighter-rouge">stellargraph-1</code> worktree will automatically use the <code class="language-plaintext highlighter-rouge">stellargraph-1</code> virtual environment<sup id="fnref:other-libraries" role="doc-noteref"><a href="#fn:other-libraries" class="footnote" rel="footnote">3</a></sup>: I don’t need to manually activate or deactivate the virtual environments, cutting out a major piece of human error.</p>
<p>Using pyenv for version management has other benefits too:</p>
<ul>
<li><a href="https://github.com/aiguofer/pyenv-jupyter-kernel">pyenv-jupyter-kernel</a> allows for automatically registering each virtual environments with <a href="https://jupyter.org/">Jupyter</a>, making it easy to test features and bug fixes in a notebook, and, more importantly, work on <a href="https://github.com/stellargraph/stellargraph/tree/develop/demos">StellarGraph’s demos</a>.</li>
<li>virtual environments can be easily created for different versions of Python (replace <code class="language-plaintext highlighter-rouge">3.6.10</code> with <code class="language-plaintext highlighter-rouge">3.7.7</code>). For instance, I have <code class="language-plaintext highlighter-rouge">stellargraph-1-3_7</code> to also test/compare with Python 3.7 in the <code class="language-plaintext highlighter-rouge">stellargraph-1</code> worktree (I do not use these ones with <code class="language-plaintext highlighter-rouge">pyenv local</code>, just <code class="language-plaintext highlighter-rouge">pyenv shell</code>, which does require manual activation and deactivation).</li>
</ul>
<h2 id="wrapping-up">Wrapping up</h2>
<p>I use git worktrees to have parallel copies of the StellarGraph Python library that share git metadata, and pyenv & pyenv-virtualenv to manage the Python virtual environments for each. This reduces the overhead of switching contexts, and so makes it easier for me to do more work and collaborate efficiently, by fitting small fixes into the gaps around big features.</p>
<section id="external-links" class="no-print">
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Git%20worktrees%20and%20pyenv:%20developing%20Python%20libraries%20faster%20%23rustlang&url=https://huonw.github.io/blog/2020/04/worktrees-and-pyenv/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2020/04/worktrees-and-pyenv/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2020/04/worktrees-and-pyenv/&title=Git%20worktrees%20and%20pyenv:%20developing%20Python%20libraries%20faster" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:incremental-compilation" role="doc-endnote">
<p>Switching branches will update the contents of files on disk, which looks like a modification of those files, even if all of those changes were undone when restoring the original files in step 3. Tools that monitor for modifications to reduce work will get confused by the “switch, …, switch back” and spuriously do more work: separate directories avoids this entirely</p>
<p>Many Python projects (including StellarGraph) don’t need to worrying about to refreshing/rebuilding state associated a source tree, because they often don’t have any compiled artifacts or other generated state associated with a branch. The code runs directly from the source files, without any compilation or preprocessing.</p>
<p>In a compiled languages like Scala or C++, I’ve found this benefit much more important: builds are much faster when they can be incremental, just updating the artifacts associated with the (small number of) source files that I actually changed. If switching branches and then back spuriously modifies-then-unmodifies a lot of files (or a file with a lot of dependents), the first build after doing that may be much slower than an incremental build for the real changes, without any branch switching. <a href="#fnref:incremental-compilation" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:branches" role="doc-endnote">
<p>I always forget to delete branches after finishing with them, so I end up with dozens (or hundreds…) of local branches associated with closed and merge pull requests. These interfere with tab completion, requiring me to remember a significant amount of the name for <code class="language-plaintext highlighter-rouge">git checkout ...</code>. I find it’s easier to remember which of a handful of checkouts holds the relevant code, and even when I do forget, a brute-force approach of checking every copy is fast (skipping ahead, this is even faster with worktrees, with <code class="language-plaintext highlighter-rouge">git worktree list</code>). In some ways, this benefit is treating a symptom, not the disease… but it works for me. <a href="#fnref:branches" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:magit" role="doc-endnote">
<p>I usually use git via <a href="https://magit.vc/">magit</a> in Emacs, where these creation commands are <code class="language-plaintext highlighter-rouge">% b</code> or <code class="language-plaintext highlighter-rouge">% c</code> in the magit status buffer, and jumping between worktrees is <code class="language-plaintext highlighter-rouge">% g</code> (<a href="https://magit.vc/manual/magit.html#Worktree">relevant docs</a>). <a href="#fnref:magit" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:other-libraries" role="doc-endnote">
<p>Using pyenv for virtual environments is great for working on multiple separate libraries/code bases, even without multiple copies. The process is exactly the same: <code class="language-plaintext highlighter-rouge">pyenv virtualenv ...</code> to initialise the environment, and <code class="language-plaintext highlighter-rouge">pyenv local</code> to associate a directory with the virtual environment. <a href="#fnref:other-libraries" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2016/04/myths-and-legends-about-integer-overflow-in-rustMyths and Legends about Integer Overflow in Rust2016-04-29T00:00:00+00:002016-04-29T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>The primitive integer types supported by CPUs are finite
approximations to the infinite set of integers we’re all used to. This
approximation breaks down and some computations will give results that
don’t match real integers, like <code class="language-plaintext highlighter-rouge">255_u8 + 1 == 0</code>. Often, this
mismatch is something the programmer didn’t think about, and thus can
easily result in bugs.</p>
<p>Rust is a programming language designed to protect against bugs; it
does focus on outlawing the most insidious class of them—memory
unsafety—but it also likes to assist the programmer in avoiding
others: <a href="/blog/2016/04/memory-leaks-are-memory-safe/#not-all-is-lost">memory leaks</a>, <a href="https://doc.rust-lang.org/std/result/#results-must-be-used">ignoring errors</a>, and, in this case,
<a href="https://en.wikipedia.org/wiki/Integer_overflow">integer overflow</a>.</p>
<h2 id="overflow-in-rust">Overflow in Rust</h2>
<p>The status of detecting and avoiding overflow in Rust changed several
times in the lead up to the 1.0.0 release last year. That fluid
situation means there’s still quite a bit of confusion about exactly
how overflow is handled and mitigated, and what the consequences are.</p>
<p>Before 1.0.0-alpha, overflow was handled by wrapping, giving the
result one would expect from a two’s complement representation (as
most modern CPUs use). However, this was thought to be suboptimal:
unexpected and unintended overflow is a common source of bugs. It is
particularly bad in C and C++ due to signed overflow being undefined,
and the lack of protection against memory safety violations—overflow
can easily cascade into memory corruption—but it is still
problematic in more defensive languages like Rust: there are numerous
examples of overflows, they’ve cropped up in many video games (in
<a href="http://www.gamasutra.com/blogs/MaxWoolf/20130508/191959/Diablo_III_Economy_Broken_by_an_Integer_Overflow_Bug.php">their economies</a>, in <a href="http://www.codeproject.com/Articles/802368/Integer-Overflow-in-Hearthstone">health bars</a>, and more),
<a href="http://googleresearch.blogspot.com.au/2006/06/extra-extra-read-all-about-it-nearly.html">binary search</a> and even <a href="http://www.nytimes.com/2015/05/01/business/faa-orders-fix-for-possible-power-loss-in-boeing-787.html?_r=0">aircraft</a>. More prosaically,
code like <code class="language-plaintext highlighter-rouge">max(x - y, z)</code> turns up semiregularly, and it can give
wildly wrong results when the numbers are unsigned and <code class="language-plaintext highlighter-rouge">x - y</code>
overflows through 0. Thus, there was a push to make Rust more
defensive about integer overflows.</p>
<p>The current status in Rust was decided in <a href="https://github.com/rust-lang/rfcs/pull/560">RFC 560</a>:</p>
<ul>
<li>in debug mode, arithmetic (<code class="language-plaintext highlighter-rouge">+</code>, <code class="language-plaintext highlighter-rouge">-</code>, etc.) on signed and unsigned primitive integers
is <strong>checked for overflow</strong>, panicking if it occurs, and,</li>
<li>in release mode, overflow is not checked and is <strong>specified to wrap
as two’s complement</strong>.</li>
</ul>
<p>These<sup id="fnref:unconditional" role="doc-noteref"><a href="#fn:unconditional" class="footnote" rel="footnote">0</a></sup> overflow checks can be manually disabled or
enabled independently of the compilation mode both globally and at a
per-operation level.</p>
<p>By checking for overflow in some modes, overflow bugs in Rust code are
hopefully found earlier. Furthermore, code that actually wants
wrapping behaviour is explicit about this requirement, meaning fewer
false positives for both future static analyses and for code that
enables overflow checking in all modes.</p>
<h2 id="myth-overflow-is-undefined">Myth: overflow is undefined</h2>
<p>One way to allow compilers to catch overflow is to make it
<em>undefined</em>, that is, there’s absolutely no guarantees about behaviour
when overflow occurs and hence it is legal to panic instead of trying
to return something. However, Rust’s core goal is ensuring memory
safety, and leaving things <em>undefined</em> —in the sense of C undefined
behaviour—is in direct contradiction to this. For one, a variable
that is undefined does not have to have a consistent value from use to
use:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="code"><pre><span class="c">// pseudo-Rust</span>
<span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="n">undefined</span><span class="p">;</span>
<span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span>
<span class="k">let</span> <span class="n">z</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span>
<span class="nd">assert_eq!</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">);</span> <span class="c">// this could fail</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>This has disastrous consequences for things that rely on checking a
value for safety, like indexing an array with bounds checks <code class="language-plaintext highlighter-rouge">foo[x]</code>:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre><span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="n">undefined</span><span class="p">;</span>
<span class="c">// let y = foo[x]; is equivalent to</span>
<span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="k">if</span> <span class="n">x</span> <span class="o"><</span> <span class="n">foo</span><span class="nf">.len</span><span class="p">()</span> <span class="p">{</span>
<span class="k">unsafe</span> <span class="p">{</span> <span class="o">*</span><span class="n">foo</span><span class="nf">.get_unchecked</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nd">panic!</span><span class="p">(</span><span class="s">"index out of bounds"</span><span class="p">)</span>
<span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>If the value of <code class="language-plaintext highlighter-rouge">x</code> isn’t consistent from the <code class="language-plaintext highlighter-rouge">x < foo.len()</code>
comparison to the actual access of the array, there’s no guarantee the
access will be in-bounds: the comparison might be <code class="language-plaintext highlighter-rouge">0 < foo.len()</code>,
while the index might be <code class="language-plaintext highlighter-rouge">foo.get_unchecked(123456789)</code>. Problematic!</p>
<p>Therefore, unlike signed integers in C, integer overflow cannot be
undefined in Rust. In other words, compilers must assume that overflow
may happen (unless they can prove otherwise). This has a
possibly unintuitive consequence that <code class="language-plaintext highlighter-rouge">x + 1 > x</code> is not always true,
something C compilers <em>do</em> assume is true if <code class="language-plaintext highlighter-rouge">x</code> is signed.</p>
<p>“But what about performance?” I hear you ask. It is true that
undefined behaviour drives optimisations by allowing the compiler to
make assumptions, and hence removing this ability could impact
speed. Overflow of signed integers being undefined is particularly
useful in C because such integers are often used as the induction
variables on loops, and hence the ability to make assumptions allows
more precise analysis of loop trip counts: <code class="language-plaintext highlighter-rouge">for (int i = 0; i < n;
i++)</code> will repeat <code class="language-plaintext highlighter-rouge">n</code> times, as <code class="language-plaintext highlighter-rouge">n</code> can be assumed to not be
negative. Rust sidesteps much of this by using unsigned integers for
indexing (<code class="language-plaintext highlighter-rouge">0..n</code> will always be <code class="language-plaintext highlighter-rouge">n</code> steps), and also by allowing easy
custom iterators, which can be used to loop directly over data
structures like <code class="language-plaintext highlighter-rouge">for x in some_array { ... }</code>. These iterators can
exploit guarantees about the data structures internally without having
to expose undefined behaviour to the user.</p>
<p>Another thing Rust misses compared to C is optimising <code class="language-plaintext highlighter-rouge">x * 2 / 2</code> to
just <code class="language-plaintext highlighter-rouge">x</code>, when <code class="language-plaintext highlighter-rouge">x</code> is signed. In this case, there’s no built-in
feature for getting the optimisation (beyond just writing <code class="language-plaintext highlighter-rouge">x</code> instead
of the complicated arithmetic of course), however in my experience,
expressions like that most often occur with <code class="language-plaintext highlighter-rouge">x</code> known at compile time,
and hence the whole expression can be constant-folded.</p>
<h2 id="myth-overflow-is-unspecified">Myth: overflow is unspecified</h2>
<p>Similar to leaving the result of overflow undefined, it could be left
just unspecified, meaning the compiler must assume it could happen,
but is allowed to make the operation return any particular result (or
not return at all). Indeed, <a href="https://github.com/nikomatsakis/rfcs/blob/630dd70a51c0c7e166be78cd3bc8f1247664db28/text/0000-integer-overflow.md#semantics-of-overflow-with-the-built-in-types">the first version</a> of
<a href="https://github.com/rust-lang/rfcs/pull/560">RFC 560</a> for checking integer overflow, proposed:</p>
<blockquote>
<p>Change this to define them, on overflow, as either returning an
unspecified result, or task panic, depending on whether the overflow
is checked.</p>
<p>[…]</p>
<ul>
<li>In theory, the implementation returns an unspecified result. In practice, however, this will most likely be the same as the wraparound result. Implementations should avoid needlessly exacerbating program errors with additional unpredictability or surprising behavior.</li>
<li>Most importantly: this is not undefined behavior in the C sense. Only the result of the operation is left unspecified, as opposed to the entire program’s meaning, as in C. The programmer would not be allowed to rely on a specific, or any, result being returned on overflow, but the compiler would also not be allowed to assume that overflow won’t happen and optimize based on this assumption.</li>
</ul>
</blockquote>
<p>There was a lot of discussion about the RFC and about the
“unspecified” result of arithmetic, meaning that <code class="language-plaintext highlighter-rouge">127_i8 + 1</code> could
theoretically return <code class="language-plaintext highlighter-rouge">-128</code> (per two’s complement) or <code class="language-plaintext highlighter-rouge">0</code> or <code class="language-plaintext highlighter-rouge">127</code>, or
anything else. This idea took hold in the community… and then it was
changed.</p>
<p>With strong encouragement from a few people, the RFC was tightened up
to actually specify the result: arithmetic on primitives that
overflows either doesn’t return (e.g. it panics), or returns the
wrapped result one would expect from two’s complement. <a href="https://github.com/rust-lang/rfcs/blob/master/text/0560-integer-overflow.md#arithmetic-operations-with-error-conditions">It</a> now says:</p>
<blockquote>
<p>The operations +, -, *, can underflow and overflow. When checking is
enabled this will panic. When checking is disabled this will two’s
complement wrap.</p>
</blockquote>
<p>Specifying the result is a defensive measure: errors are more likely
to cancel out when overflow isn’t caught. An expression like <code class="language-plaintext highlighter-rouge">x - y +
z</code> is evaluated like <code class="language-plaintext highlighter-rouge">(x - y) + z</code> and hence the subtraction could
overflow (e.g. <code class="language-plaintext highlighter-rouge">x = 0</code> and <code class="language-plaintext highlighter-rouge">y = 1</code> both unsigned), but as long as <code class="language-plaintext highlighter-rouge">z</code>
is large enough (<code class="language-plaintext highlighter-rouge">z >= 1</code> in that example), the result will be what
one expects from true integers.</p>
<p>The change happened towards the end of the 160 comment long RFC
discussion and so it was easy for people to miss, making it easy for
people to still think the result is unspecified.</p>
<h2 id="myth-the-programmer-has-no-control-of-overflow-handling">Myth: the programmer has no control of overflow handling</h2>
<p>One of the main objections to adding overflow checking was the
existance of programs/algorithms that <em>want</em> two’s complement
overflow, such as hashing algorithms, certain data structures (ring
buffers, particularly) and even image codecs. For these algorithms,
using <code class="language-plaintext highlighter-rouge">+</code> in debug mode would be incorrect: the code would panic even
though it was executing as intended. Additionally, some more
security-minded domains wish to have overflow checks on in all modes
by default.</p>
<p>The RFC and the standard library provide <em>four</em>
sets of methods beyond the pure operators:</p>
<ul>
<li><a href="http://doc.rust-lang.org/std/primitive.i32.html#method.wrapping_add"><code class="language-plaintext highlighter-rouge">wrapping_add</code></a>, <a href="http://doc.rust-lang.org/std/primitive.i32.html#method.wrapping_sub"><code class="language-plaintext highlighter-rouge">wrapping_sub</code></a>, …</li>
<li><a href="http://doc.rust-lang.org/std/primitive.i32.html#method.saturating_add"><code class="language-plaintext highlighter-rouge">saturating_add</code></a>, <a href="http://doc.rust-lang.org/std/primitive.i32.html#method.saturating_sub"><code class="language-plaintext highlighter-rouge">saturating_sub</code></a>, …</li>
<li><a href="http://doc.rust-lang.org/std/primitive.i32.html#method.overflowing_add"><code class="language-plaintext highlighter-rouge">overflowing_add</code></a>, <a href="http://doc.rust-lang.org/std/primitive.i32.html#method.overflowing_sub"><code class="language-plaintext highlighter-rouge">overflowing_sub</code></a>, ..</li>
<li><a href="http://doc.rust-lang.org/std/primitive.i32.html#method.checked_add"><code class="language-plaintext highlighter-rouge">checked_add</code></a>, <a href="http://doc.rust-lang.org/std/primitive.i32.html#method.checked_sub"><code class="language-plaintext highlighter-rouge">checked_sub</code></a>, …</li>
</ul>
<p>These should cover all bases of “don’t want overflow to panic in some
modes”:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">wrapping_...</code> returns the straight two’s complement result,</li>
<li><code class="language-plaintext highlighter-rouge">saturating_...</code> returns the largest/smallest value (as appropriate) of
the type when overflow occurs,</li>
<li><code class="language-plaintext highlighter-rouge">overflowing_...</code> returns the two’s
complement result along with a boolean indicating if overflow occured,
and</li>
<li><code class="language-plaintext highlighter-rouge">checked_...</code> returns an <code class="language-plaintext highlighter-rouge">Option</code> that’s <code class="language-plaintext highlighter-rouge">None</code> when overflow
occurs.</li>
</ul>
<p>All of these can be implemented in terms of <code class="language-plaintext highlighter-rouge">overflowing_...</code>, but the
standard library is trying to make it easy for programmers to do the
right thing in the most common cases.</p>
<p>Code that truly wants two’s complement wrapping can be written like
<code class="language-plaintext highlighter-rouge">x.wrapping_sub(y).wrapping_add(z)</code>. This works, but clearly can get a
little verbose, verbosity that can be reduced in some cases via the
standard library’s <a href="http://doc.rust-lang.org/std/num/struct.Wrapping.html"><code class="language-plaintext highlighter-rouge">Wrapping</code></a> wrapper type.</p>
<p>The current state isn’t necessarily the final state of overflow
checking: the RFC even mentioned some <a href="https://github.com/rust-lang/rfcs/blob/master/text/0560-integer-overflow.md#alternatives-and-possible-future-directions">future directions</a>. Rust
could introduce operators like Swift’s wrapping <code class="language-plaintext highlighter-rouge">&+</code> in future,
something that was not done initially because Rust tries to be
conservative and reasonably minimal, as well as hypothetically having
scoped disabling of overflow checking (e.g. a single function could be
explicitly marked, and its internals would thus be unchecked in all
modes). There’s interest in the latter particularly, from some of
Rust’s keenest (potential) users <a href="https://github.com/rust-lang/cargo/issues/2262">Servo</a> and <a href="https://wiki.mozilla.org/Oxidation#Rust_.2F_Cargo_nice-to-haves">Gecko</a>.</p>
<p>For code that wants overflow checking everywhere, one can either use
<code class="language-plaintext highlighter-rouge">checked_add</code> pervasively (annoying!), or explicitly enable
them. Although they are tied to debug assertions by default, overflow
checks can be turned on by passing <code class="language-plaintext highlighter-rouge">-C debug-assertions=on</code> to rustc,
or setting the <code class="language-plaintext highlighter-rouge">debug-assertions</code> field of a
<a href="http://doc.crates.io/manifest.html#the-profile-sections">cargo profile</a>. There’s also work on having them able to be
activated independently of other debug assertions (rustc currently has
the unstable <code class="language-plaintext highlighter-rouge">-Z force-overflow-checks</code> flag).</p>
<h2 id="myth-the-approach-to-overflow-checks-makes-code-slow">Myth: the approach to overflow checks makes code slow</h2>
<p>Rust aims to be as fast as possible, and the design of the current
overflow checking approach took various performance considerations
seriously. Performance is one of the main motivations for checks being
disabled in release builds by default, and indeed means that there’s
no speed penalty to the way in which Rust helps mitigate/flag
integer overflow bugs during development.</p>
<p>It’s an unfortunate reality that checking for overflow requires more
code and more instructions:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre><span class="nd">#[no_mangle]</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">unchecked</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">i32</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-></span> <span class="nb">i32</span> <span class="p">{</span>
<span class="n">x</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="n">y</span><span class="p">)</span>
<span class="p">}</span>
<span class="nd">#[no_mangle]</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">checked</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">i32</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-></span> <span class="nb">i32</span> <span class="p">{</span>
<span class="n">x</span> <span class="o">+</span> <span class="n">y</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>With <code class="language-plaintext highlighter-rouge">-O -Z force-overflow-checks</code>, on x86<sup id="fnref:arm" role="doc-noteref"><a href="#fn:arm" class="footnote" rel="footnote">1</a></sup>, this compiles to (with some
editing for clarity):</p>
<figure class="highlight"><pre><code class="language-asm" data-lang="asm"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="code"><pre>unchecked:
leal (%rdi,%rsi), %eax
retq
checked:
pushq %rax
addl %esi, %edi
jo .overflow_occurred
movl %edi, %eax
popq %rcx
retq
.overflow_occurred:
leaq panic_loc2994(%rip), %rdi
callq _ZN9panicking5panic20h4265c0105caa1121SaME@PLT
</pre></td></tr></tbody></table></code></pre></figure>
<p>It is definitely annoying that there are all<sup id="fnref:extra" role="doc-noteref"><a href="#fn:extra" class="footnote" rel="footnote">2</a></sup> those extra
instructions, as is the fact that implementations are forced to use
<code class="language-plaintext highlighter-rouge">add</code> rather than having the option to use <code class="language-plaintext highlighter-rouge">lea</code><sup id="fnref:lea" role="doc-noteref"><a href="#fn:lea" class="footnote" rel="footnote">3</a></sup>. However, an
even bigger performance hit is how overflow checks inhibit other
optimisations, both because the checks themselves serialise code
(inhibiting things like loop unrolling/reordering and vectorisation)
and because the panic/stack unwinding forces the compiler to
<a href="http://danluu.com/integer-overflow/">be more conservative</a>.</p>
<p>All these considerations explain why overflow checks are not enabled
in release mode, where usually getting the highest performance
possible is desirable.</p>
<p>That said, even when the checks are enabled in release mode, the
performance hit can be reduced like with bounds checked arrays. For
one, compilers can do range analysis/inductive proofs to deduce that
certain overflow checks are sure to never fail; indeed,
<a href="http://blog.regehr.org/archives/1384">significant</a> <a href="https://github.com/apple/swift/blob/16b3d6c8d5b2d610cdfd72898f6ab384e632b69b/lib/SILOptimizer/Transforms/RedundantOverflowCheckRemoval.cpp">effort</a> has been <a href="https://github.com/llvm-mirror/llvm/blob/8b47c17a53d683f313eaaa93c4a53de26d8fcba5/lib/Transforms/InstCombine/InstCombineAddSub.cpp#L893-L987">devoted</a> to <a href="https://github.com/gcc-mirror/gcc/blob/fd3211e13bbbb6882f477aa75a36eb0ccdec485f/gcc/tree-vrp.c#L9792-L9884">the topic</a>. Additionally,
the significant pain caused by using panics can be reduced by
application authors <a href="https://github.com/rust-lang/rfcs/blob/master/text/1513-less-unwinding.md">converting panics into aborts</a>, if it’s
appropriate for their domain.</p>
<p>The integer overflow RFC gives itself some room for optimisation too:
it <a href="https://github.com/rust-lang/rfcs/blob/master/text/0560-integer-overflow.md#delayed-panics">allows “delayed panics”</a>, meaning a Rust implementation
is allowed to perform a sequence of operations like <code class="language-plaintext highlighter-rouge">a + b + c + d</code>
and only panic once at the end if any intermediate overflow occurred,
instead of having to separately check for overflow (and panic) in <code class="language-plaintext highlighter-rouge">tmp
= a + b</code> and then in <code class="language-plaintext highlighter-rouge">tmp + c</code> etc. No known implementation actually
does this yet, but they could.</p>
<h2 id="myth-the-checks-find-no-bugs">Myth: the checks find no bugs</h2>
<p>All the design/discussion/implementation of this scheme for handling
integer overflow would be wasted if it didn’t actually find any bugs
in practice. I personally have had quite a few bugs found nearly as I
write them, with expressions like <code class="language-plaintext highlighter-rouge">cmp::max(x - y, z)</code> (they never hit
the internet, so no links for them), especially when combined with
testing infrastructure like <a href="https://crates.io/crates/quickcheck"><code class="language-plaintext highlighter-rouge">quickcheck</code></a>.</p>
<p>The overflow checks have found bugs through out the ecosystem; for instance, (not exhaustive!)</p>
<ul>
<li><a href="https://github.com/rust-lang/rust/pull/22532#issuecomment-75168901">the standard library</a></li>
<li><a href="https://github.com/rust-lang/rust/pull/31281">the compiler</a></li>
<li><a href="https://github.com/rust-lang/rust/pull/23127">the built-in benchmark harness</a></li>
<li><a href="https://github.com/servo/servo/issues/6040">Servo</a></li>
<li><a href="https://github.com/PistonDevelopers/image/pull/412"><code class="language-plaintext highlighter-rouge">image</code></a></li>
<li><a href="https://github.com/servo/rust-url/issues/124"><code class="language-plaintext highlighter-rouge">url</code></a></li>
<li><a href="https://github.com/servo/webrender/pull/243"><code class="language-plaintext highlighter-rouge">webrender</code></a></li>
</ul>
<p>Beyond Rust, there’s a lot of evidence for the dangers of integer overflow and
desire for detecting/protecting against them. It was on the
<a href="http://cwe.mitre.org/top25/">CWE/SANS list of top 25 errors in 2011</a>, languages like Swift
will unconditionally check for overflow, and others like Python 3 and
Haskell will avoid overflow entirely by default, via arbitrary
precision integers. Furthermore, in C, several compilers have options
to both make signed overflow defined as two’s complement wrapping
(<code class="language-plaintext highlighter-rouge">-fwrapv</code>) and to catch it when it does happen
(<code class="language-plaintext highlighter-rouge">-fsanitize=signed-integer-overflow</code>).</p>
<p><em>Thanks to <a href="https://github.com/ubsan">Nicole Mazzuca</a>, <a href="https://github.com/Aatch">James Miller</a>,
<a href="https://github.com/tsion">Scott Olson</a>, and 👻👻👻 for reading and giving feedback on
this post.</em></p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="https://users.rust-lang.org/t/myths-and-legends-about-integer-overflow-in-rust/5612">users</a></li>
<li class="external-link"><a href="https://www.reddit.com/r/rust/comments/4gz93u/myths_and_legends_about_integer_overflow_in_rust/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Myths%20and%20Legends%20about%20Integer%20Overflow%20in%20Rust%20%23rustlang&url=https://huonw.github.io/blog/2016/04/myths-and-legends-about-integer-overflow-in-rust/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2016/04/myths-and-legends-about-integer-overflow-in-rust/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2016/04/myths-and-legends-about-integer-overflow-in-rust/&title=Myths%20and%20Legends%20about%20Integer%20Overflow%20in%20Rust" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:unconditional" role="doc-endnote">
<p>There are some unconditional and uncontrollable
overflow checks for arithmetic: <code class="language-plaintext highlighter-rouge">x / 0</code>, and <a href="http://blog.regehr.org/archives/887"><code class="language-plaintext highlighter-rouge">MIN / -1</code></a> (for
signed integer types), and similarly for <code class="language-plaintext highlighter-rouge">%</code>. These computations
are actually undefined behaviour in C and LLVM (which is the
historical reason for why rustc has them unconditional), although,
it seems to me that Rust could theoretically consider the
latter a normal overflow and return <code class="language-plaintext highlighter-rouge">MIN</code> when the checks are off. <a href="#fnref:unconditional" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:arm" role="doc-endnote">
<p>On 32-bit ARM, LLVM <a href="https://llvm.org/bugs/show_bug.cgi?id=27571">currently decides</a> to emit a
chain of redundant comparisons and register manipulations, so the
penalty is even higher! <a href="#fnref:arm" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:extra" role="doc-endnote">
<p>There’s more instructions in the function version than there
would be when <code class="language-plaintext highlighter-rouge">checked</code> is inlined (as it should be): the
<code class="language-plaintext highlighter-rouge">pushq</code>/<code class="language-plaintext highlighter-rouge">pop</code>/<code class="language-plaintext highlighter-rouge">movl</code> register management wouldn’t be
necessary. Also, even without inlining I believe the
<code class="language-plaintext highlighter-rouge">pushq</code>/<code class="language-plaintext highlighter-rouge">popq</code> stack management isn’t necessary, but
unfortunately the published Rust binaries <s>don't use a new
enough version of LLVM to get its new <a href="http://reviews.llvm.org/D9210">"shrink wrapping"
optimisation pass</a></s> use a version of LLVM that
contains <a href="https://llvm.org/bugs/show_bug.cgi?id=25614">a bug in its “shrink-wrapping” pass</a>
(thanks for <a href="https://users.rust-lang.org/t/myths-and-legends-about-integer-overflow-in-rust/5612/2?u=huon">the correction</a>, Eli Friedman). <a href="#fnref:extra" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:lea" role="doc-endnote">
<p>On x86, it can be extremely useful to be able to use <code class="language-plaintext highlighter-rouge">lea</code>
(load effective address) for arithmetic: it can do relatively
complicated computations, and is usually computed in a different
part of the CPU and its pipeline than <code class="language-plaintext highlighter-rouge">add</code>, allowing exploiting
more instruction-level parallelism. The x86 ISA allows
dereferencing complicated pointer computations: the most general
form is <code class="language-plaintext highlighter-rouge">A(r1, r2, B)</code> (in AT&T syntax), which is equal to <code class="language-plaintext highlighter-rouge">r1 +
B * r2 + A</code> for registers <code class="language-plaintext highlighter-rouge">r1</code> and <code class="language-plaintext highlighter-rouge">r2</code> and constants <code class="language-plaintext highlighter-rouge">A</code> and
<code class="language-plaintext highlighter-rouge">B</code>. Normally these are used directly in memory instructions like
<code class="language-plaintext highlighter-rouge">mov</code> (e.g. <code class="language-plaintext highlighter-rouge">let y = array_of_u32[x];</code> could compile to something
along the lines of <code class="language-plaintext highlighter-rouge">mov (array_of_u32.as_ptr(), x, 4), y</code> ,
because each element is of size 4), but <code class="language-plaintext highlighter-rouge">lea</code> allows just doing
the arithmetic without hitting memory. All-in-all, being able to
use <code class="language-plaintext highlighter-rouge">lea</code> for arithmetic is quite nice. The downside is of course
<code class="language-plaintext highlighter-rouge">lea</code> doesn’t integrate directly with overflow detection: it
doesn’t set the CPU flags that signal it. <a href="#fnref:lea" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2016/04/memory-leaks-are-memory-safeMemory Leaks are Memory Safe2016-04-04T00:00:00+00:002016-04-04T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p><a href="https://en.wikipedia.org/wiki/Memory_safety"><em>Memory unsafety</em></a> and <a href="https://en.wikipedia.org/wiki/Memory_leak"><em>memory leaks</em></a> are arguably the two
categories of bugs that have received the most attention for
prevention and mitigation. As their names suggest, they are in the
same part of “bug space”, however they are in some ways diametric
opposites, and solving one does not solve the other. The widespread
use of memory-safe managed languages hammers this point home: they
avoid some memory unsafety by presenting a “leak everything” model to
programmers.</p>
<p>Put simply: <strong>memory unsafety is doing something with invalid data,
a memory leak is <em>not</em> doing something with valid data</strong>. In table
form:</p>
<table>
<thead>
<tr>
<th> </th>
<th>Valid data</th>
<th>Invalid data</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Used</strong></td>
<td>👍</td>
<td>Memory unsafety</td>
</tr>
<tr>
<td><strong>Not used</strong></td>
<td>Memory leak</td>
<td>👍</td>
</tr>
</tbody>
</table>
<p>The best programs lie in the 👍 cells only: they manipulate valid
things, and don’t manipulate invalid ones. Passable programs might
also have some valid data that they don’t use (leak memory), but bad
ones will try to use invalid data.</p>
<p>When a language, such as Rust, advertises itself as memory <em>safe</em>, it
isn’t saying anything about whether memory <em>leaks</em> are impossible.</p>
<h2 id="consequences">Consequences</h2>
<p>The most important difference between memory unsafety and memory leaks
in practice is the scope of their possible results, with one easily
very serious, and the other usually just annoying.</p>
<p>Memory safety is a key building block in any other form of
safety/program correctness. If a program is not memory safe, there are
very few guarantees about its behaviour, due to the possibility of
memory corruption. A malicious party interacting with a memory unsafe
program may be able to exploit the unsafety to
<a href="https://en.wikipedia.org/wiki/Heartbleed">read private keys straight out of a server’s memory</a> or
to execute arbitrary code on someone else’s computer.</p>
<p>On the other hand, a memory leak will generally, at worst, lead to a
denial-of-service, where a useful program is killed due to using too
much memory (and, as it grows to this stage, the computer may be
rendered essentially inoperable due to memory pressure). This also can
be caused by a malicious attacker, but the damage is usually very much
more controlled. Of course, a denial-of-service can be extremely
annoying, and there are places where this is a critical problem, but
memory unsafety would generally be equally problematic—more likely,
more problematic. (Additionally, given memory unsafety’s inherent lack
of control, a problem there could easily lead to a denial-of-service
similar/identical to that which a memory leak can cause.)</p>
<p>Given this, most programming languages choose to tolerate memory leaks
(they allow data not be deallocated/cleaned up after the last time it
is used), but not memory unsafety. That is, most “memory safe
languages” guarantee all programs written in them have no
unsafety<sup id="fnref:optin" role="doc-noteref"><a href="#fn:optin" class="footnote" rel="footnote">0</a></sup>, and they only try—usually, try hard—to help
programmers avoid leaks, but without making a hard promise.</p>
<h2 id="delete-free"><code class="language-plaintext highlighter-rouge">delete free</code></h2>
<p>There are a few different ways to get memory unsafety, but there’s one
category (from the Wikipedia article) that stands out when we’re
discussing memory management:</p>
<blockquote>
<ul>
<li><strong>Dynamic memory errors</strong> - incorrect management of dynamic memory and pointers:
<ul>
<li><strong>Dangling pointer</strong> - a pointer storing the address of an object that has been deleted.</li>
<li><strong>Double free</strong> - repeated calls to free may prematurely free a new object at the same address. If the exact address has not been reused, other corruption may occur, especially in allocators that use free lists.</li>
<li><strong>Invalid free</strong> - passing an invalid address to free can corrupt the heap.</li>
<li><strong>Null pointer accesses</strong> will cause an exception or program termination in most environments, but can cause corruption in operating system kernels or systems without memory protection, or when use of the null pointer involves a large or negative offset.</li>
</ul>
</li>
</ul>
</blockquote>
<p>In that list, only null pointer accesses aren’t caused by deallocating
memory—calling the <code class="language-plaintext highlighter-rouge">free</code> function to mark an allocation as
unused/return it to the operating system—incorrectly. And thus, one
way to be guaranteed to avoid three quarters of those possibilities is
to just never call <code class="language-plaintext highlighter-rouge">free</code>: if memory is never released, it is
impossible to suffer from the problems caused by releasing it. In
terms of the table above, removing <code class="language-plaintext highlighter-rouge">free</code> is removing the “Invalid
data” column: all data is always valid.</p>
<p>Of course, just disallowing <code class="language-plaintext highlighter-rouge">free</code> has some downsides<sup id="fnref:lockfree" role="doc-noteref"><a href="#fn:lockfree" class="footnote" rel="footnote">1</a></sup>, particularly
making it very annoying to write programs that don’t eventually use
all available memory. However, computers are infallible in ways humans
are not, so maybe we could allow them to call <code class="language-plaintext highlighter-rouge">free</code>…</p>
<h2 id="optimising-leaks">Optimising leaks</h2>
<p>A large fraction of modern code is written in languages designed to be
memory safe, languages like Java, Javascript, Python and Ruby. They
have no explicit <code class="language-plaintext highlighter-rouge">free</code>, and so automatically manage memory (hence
“managed language”) via a <em>garbage collector</em> built into the runtime
systems shipped with the languages’ compilers and interpreters.</p>
<p>At its core<sup id="fnref:layers" role="doc-noteref"><a href="#fn:layers" class="footnote" rel="footnote">2</a></sup>, garbage collection is a way to make it feasible
to expose a programming model where all allocations leak. Letting a
garbage collector manage every allocation theoretically allows
programs (and programmers) to pretend that memory is infinite, not
needing to carefully track when memory isn’t needed any more: programs
do whatever they want, and the GC will automatically and dynamically
free chunks of memory that are guaranteed to be unneeded, ensuring the
program’s memory use remains under control. Almost all garbage
collectors determine neededness conservatively by finding things no longer
accessible from the main program (the garbage collector itself needs
to keep track of/have access to all allocations).</p>
<p>In practice, the programmer has to think about non-infinite
memory and its consequences a little more often than never, but memory
unsafety concerns <em>are</em> removed, as desired. High-performance code
often has to chose particular coding patterns to work-around
deficiencies with garbage collectors (such as object pools to avoid
touching the GC in tight loops), and one can accidentally create
<a href="https://en.wikipedia.org/wiki/Lapsed_listener_problem">chains of references</a> that keep large trees of data
unnecessarily alive.</p>
<p>However, even in the face of practical concerns, the point stands:
without <code class="language-plaintext highlighter-rouge">free</code>, there’s no scope for some types of memory unsafety.</p>
<!--(Due credit: I think I first heard an idea along the lines "GC is an
optimisation for leaking" from
[Alexis Beingessner](https://twitter.com/Gankro).)-->
<h2 id="less-leaky-abstractions">Less leaky abstractions</h2>
<p>Given my status, I’d be remiss to mention an alternative to the
leak-everything managed paradigm: instead using a technique that
crosses out the whole “Invalid data” column, one can be more precise
and cross out only the “Memory unsafety” cell. The <a href="https://www.rust-lang.org/">Rust programming language</a> does this.</p>
<p>Rust doesn’t have C-style manual memory management, but rather
RAII/scope-based resource management similar to C++, allowing types to
have destructors for automatic clean-up. It does not literally have a
<code class="language-plaintext highlighter-rouge">free</code> function users must remember to call (removing most of the
“manual”), but the <a href="http://doc.rust-lang.org/std/mem/fn.drop.html"><code class="language-plaintext highlighter-rouge">drop</code></a> function serves the role of explicit
<code class="language-plaintext highlighter-rouge">free</code>, allowing one to explicitly cause the destructor to be run on a
value, thus invalidating it. In contrast to both C and C++, the
language prevents use of such data at compile time to avoid memory
unsafety.</p>
<p>However, a programming model that’s not “leak everything” doesn’t mean
it is “leak nothing”: the revised table for Rust (and anything
similar) still has its memory leak cell.</p>
<table>
<thead>
<tr>
<th> </th>
<th>Valid data</th>
<th>Invalid data</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Used</strong></td>
<td>👍</td>
<td>Impossible</td>
</tr>
<tr>
<td><strong>Not used</strong></td>
<td>Memory leak</td>
<td>👍</td>
</tr>
</tbody>
</table>
<p>I’m not including this section because I think it’s a great promotion
of Rust (being allowed to have invalid data that one can’t use doesn’t
exactly sound world-shaking<sup id="fnref:moves" role="doc-noteref"><a href="#fn:moves" class="footnote" rel="footnote">3</a></sup>…), but because that is the hole
which this article is filling. The similarity of the phrases “memory
leak” and “memory safety” regularly tricks people who have read “Rust
is memory safe” into thinking Rust is (just) preventing memory leaks,
leading to legitimate doubts about what Rust offers instead of, say,
modern C++ in the space of low-level systems languages. <strong>Rust
disallows memory unsafety, but memory leaks are possible</strong>.</p>
<h3 id="stdmemforget"><code class="language-plaintext highlighter-rouge">std::mem::forget</code></h3>
<p>Finally, returning to the title, Rust has the <a href="https://doc.rust-lang.org/std/mem/fn.forget.html"><code class="language-plaintext highlighter-rouge">forget</code></a>
function, which throws away a value without actually running the
destructor while still marking it invalid as if freed normally, thus
possibly leaking memory. For a long time, this was marked as <code class="language-plaintext highlighter-rouge">unsafe</code>,
that is, Rust was implicitly including memory leaks as something the
programmer must opt-in to, like the risk of memory unsafety. However,
this was not correct in practice, as things like reference cycles and
thread deadlock could cause memory to leak. Rust <a href="https://github.com/rust-lang/rfcs/blob/master/text/1066-safe-mem-forget.md">decided</a> to
make <code class="language-plaintext highlighter-rouge">forget</code> safe, focusing its guarantees on just preventing memory
unsafety and instead making only best-effort attempts
towards preventing memory leaks (like essentially all other
languages, memory safe and otherwise).</p>
<h3 id="not-all-is-lost">Not all is lost!</h3>
<p>Like modern C++, the efforts Rust makes are pretty good, with
RAII/scope-based resource management (specifically destructors) being
a powerful tool for managing memory and <a href="http://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html#locks">beyond</a> (and
<a href="http://blog.skylight.io/rust-means-never-having-to-close-a-socket/">beyonder</a>), especially when combined with Rust’s
move-by-default semantics. The point about not being a guarantee is
that (a) it’s not trivial to make a useful <em>formal</em> definition of
memory leak (at the very least, usefulness varies depending on the
context), and (b) there are relatively rare edge-cases that seem to be
impossible to statically prevent without non-trivial cost. The
<a href="http://doc.rust-lang.org/stable/nomicon/leaking.html">wash-up</a> in Rust’s standard library is all values have to be
<em>memory safe</em> to leak, but they can still consider being leaked
incorrect. In other words, one may get unwanted behaviour if a value
is leaked, but the consequences will be more far controlled than a
segfault or memory corruption.</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="https://users.rust-lang.org/t/memory-leaks-are-memory-safe/5288?u=huon">users</a></li>
<li class="external-link"><a href="https://www.reddit.com/r/rust/comments/4dgvvh/memory_leaks_are_memory_safe_huon_on_the_internet/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Memory%20Leaks%20are%20Memory%20Safe%20%23rustlang&url=https://huonw.github.io/blog/2016/04/memory-leaks-are-memory-safe/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2016/04/memory-leaks-are-memory-safe/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2016/04/memory-leaks-are-memory-safe/&title=Memory%20Leaks%20are%20Memory%20Safe" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:optin" role="doc-endnote">
<p>More specifically, programming languages will guarantee that
one can only get unsafety by explicitly opting in to it, in some
form, such as via Python’s <code class="language-plaintext highlighter-rouge">ctypes</code> module, or Rust’s <code class="language-plaintext highlighter-rouge">unsafe</code>
keyword. <a href="#fnref:optin" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:lockfree" role="doc-endnote">
<p>It also has some upsides beyond just less memory
unsafety: if one is OK without <code class="language-plaintext highlighter-rouge">free</code>, it becomes much easier to
write programs where the lifetime of data is unclear, which makes
many concurrent algorithms easier to write. That said, there are
schemes for writing such code when manual <code class="language-plaintext highlighter-rouge">free</code>s are required,
such as <a href="https://en.wikipedia.org/wiki/Hazard_pointer">hazard pointers</a> and the simpler
<a href="http://aturon.github.io/blog/2015/08/27/epoch/">epoch-based memory reclamation</a>. <a href="#fnref:lockfree" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:layers" role="doc-endnote">
<p>It’s worth noting that the detailed knowledge of memory
layout required for a top-flight garbage collectors lends itself
to other tricks, such as allocations usually being a cheap pointer
bump with a generational GC, and the ability for a moving GC to
shift data around, improving cache locality (especially useful
given the generally pointer-heavy nature of most managed
languages). However, these tricks are orthogonal to both memory
safety and memory leaks. <a href="#fnref:layers" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:moves" role="doc-endnote">
<p>It’s pretty useful, in that it allows move semantics to
work, but that’s an article for another time, perhaps. <a href="#fnref:moves" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2015/10/rreverse-debuggingRreverrse Debugging2015-10-27T00:00:00+00:002015-10-27T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>Imagine being able to step forward and <em>backwards</em> as code runs in
your debugger. Imagine being able to do an test run multiple times
with exactly the same sequence of instructions and values, right down
to memory addresses and IO. Imagine being able to run an executable
thousands of times and then do all that in the one execution that
triggers the rare bug that’s draining you of life…</p>
<p>The <a href="http://rr-project.org/">rr tool</a> is amazing.</p>
<h2 id="a-debugger-skeptic">A Debugger Skeptic</h2>
<p>I’ve never been a huge user of debuggers. Being able to diagnose my
code line-by-line, statement-by-statement sounded theoretically good
to me, but I’ve never really “clicked” with it in practice.</p>
<p>Of course, there’s an element of a vicious cycle: I only use debuggers
(generally gdb for native code, and pdb for Python) occasionally and
so I’m not an expert, so things end up being annoying, slow and
require searching the internet a lot, and this discourages me from
using them. And, for anything non-trivial, circumstances often mean
that my attempts to use a debugger are an exercise in pulling teeth
and are slower than just staring at the code harder and/or adding more
logging.</p>
<p>There’s also a large element of just never having a tool I liked using
(which is what this whole post is about): I don’t do that much in
managed languages like Java or C#, and I’ve not used Visual Studio in
any detail, all of which I’ve heard rumours about being top-notch.</p>
<p>The thing I struggle with most is the disconnect between when problems
are detected/manifest, and the fundamental cause. A typical “tricky”
bug might only occur occasionally: an assertion of
internal-consistency inside a library triggers inside a function in a
somewhat non-deterministic way, meaning it may take hundreds or even
thousands of calls before it triggers. And, even when it does, the
assertion is usually just detecting some flow-on consequence, and the
actual problem is usually some unknown distance before the
assertion. All this means I struggle to wrangle the debugger into the
right position to catch the bug, without having to walk through too
many perfectly fine executions. I’m sure a gdb-empress could handle
this with ease, but I don’t have those skills, so just sticking some
<code class="language-plaintext highlighter-rouge">println!</code>s in my code and reading the log backwards from the error is
easier.</p>
<p>Recently I’ve been trying to get better at using debuggers, to break
my vicious cycle. I’ve been poking around in my Python code for my
Masters’ with pdb, and I’ve of course been poking around in Rust code
with gdb. This has been an uphill struggle: I have to consciously
decide to actually open a debugger and then work out exactly where I
need to be. There was a boost when I worked out that
<a href="https://github.com/rust-lang/rust/blob/0152a93bb41ba360b41dd62451c2472fc5978d0c/src/etc/rust-gdb">the <code class="language-plaintext highlighter-rouge">rust-gdb</code> wrapper</a> was made to be used (and is
conveniently installed by default, even with
<a href="https://github.com/brson/multirust"><code class="language-plaintext highlighter-rouge">multirust</code></a>), but, still: not my favourite activity.</p>
<p>And then, <a href="https://github.com/mozilla/rr/releases/tag/4.0.0">rr 4.0</a> was <a href="http://robert.ocallahan.org/2015/10/rr-40-released-with-reverse-execution.html">released</a>…</p>
<h2 id="on-a-rrrroll">On a rrrroll</h2>
<p>rr is everything I never knew I wanted from a debugger. I can barely
stay out of it: I’m slicing and dicing bugs in Rust code like never
before. Instead of having to plan out the best places to put
breakpoints for what I suspect the issue <em>might</em> be, I just run the
code, and then break whereever/whenever I want to later.</p>
<p>The workflow is simple: make a recording of an execution, and then
replay it in a debugger, with the ability to do go anywhere you want
any time.</p>
<p>With Rust/Cargo, my command line will often look like:</p>
<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre><span class="nv">$ </span>cargo build <span class="nt">--example</span> foo
... things compiling ...
<span class="nv">$ </span>rr ./target/debug/examples/foo
... rr & program output ...
<span class="nv">$ </span>rr replay <span class="nt">-d</span> rust-gdb
... rr & gdb start-up ...
<span class="o">(</span>gdb<span class="o">)</span> <span class="nb">break </span>rust_panic
...
<span class="o">(</span>gdb<span class="o">)</span> <span class="k">continue</span>
... program output ...
<span class="o">(</span>gdb<span class="o">)</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>(The <code class="language-plaintext highlighter-rouge">-d</code> flag was <a href="https://github.com/mozilla/rr/pull/1551">added</a> just after 4.0 was released. I asked
a question about swapping in different gdbs, and was prompted to
file an issue, which was fixed in just a few hours! Anyway, this means
getting the best Rust experience requires building from rr
<a href="https://github.com/mozilla/rr">from source</a> at the moment.)</p>
<h3 id="recording">Recording</h3>
<p>The recording step with <code class="language-plaintext highlighter-rouge">rr</code> is simple: tell it your binary and it’ll
run it, saving the state it needs for replaying as the program
executes.</p>
<p>This <em>could</em> be implemented as just saving the entire state of the
machine (memory and registers) between each step, but this would be
super-slow, and it won’t be nearly as nice to use as rr. The worst
overhead I’ve noticed is the rr’d program taking <s>14× longer</s>, but it’s
a pretty dumb program, and I only came up with it for this blog post<sup id="fnref:worst-case" role="doc-noteref"><a href="#fn:worst-case" class="footnote" rel="footnote">0</a></sup>:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre><span class="c">// hammer.rs</span>
<span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fs</span><span class="p">::</span><span class="n">File</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">io</span><span class="p">::</span><span class="nn">prelude</span><span class="p">::</span><span class="o">*</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">for</span> <span class="mi">_</span> <span class="n">in</span> <span class="mi">0</span><span class="o">..</span><span class="mi">100000</span> <span class="p">{</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">buffer</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">;</span> <span class="mi">1000</span><span class="p">];</span>
<span class="nn">File</span><span class="p">::</span><span class="nf">open</span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">()</span><span class="nf">.read</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="n">buffer</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>It takes <s>4.2s</s> 0.5s (this case <a href="https://github.com/mozilla/rr/commit/9bc7077e0a031a200aca7baf785dad17eba9f941">was optimised</a>) to be
recorded with rr, but only 0.3s to run normally. (Compiled with
<code class="language-plaintext highlighter-rouge">rustc -g hammer.rs</code>, with Rust 1.3.0.) Of course, most programs will
being doing more than reading from a file a <em>lot</em>, and the overhead is
much smaller for more typical work-loads. For instance, I’ve been
doing some work with Aatch’s big-integer library, <a href="https://github.com/Aatch/ramp">ramp</a>, and
<a href="https://github.com/Aatch/ramp/blob/7fac34b95600562a1272995568fff331eb533894/examples/factorial.rs">the <code class="language-plaintext highlighter-rouge">factorial</code> example</a> (compiled in debug mode) takes
1.8s to run under rr, and 1.5s normally.</p>
<h3 id="determinism">Determinism</h3>
<p>The replaying is where the magic really kicks in for me. The <code class="language-plaintext highlighter-rouge">rr
replay</code> command brings up an instance of gdb with the trace loaded
and ready to go.</p>
<p>The recording means that the “execution” of a replay is now
deterministic (and completely so, as far as I can tell), so you can be
sure about a lot of things. Reading random data is fine:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre><span class="c">// urandom.rs</span>
<span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fs</span><span class="p">::</span><span class="n">File</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">io</span><span class="p">::</span><span class="nn">prelude</span><span class="p">::</span><span class="o">*</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">buffer</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">;</span> <span class="mi">5</span><span class="p">];</span>
<span class="nn">File</span><span class="p">::</span><span class="nf">open</span><span class="p">(</span><span class="s">"/dev/urandom"</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">()</span><span class="nf">.read</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="n">buffer</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{:?}"</span><span class="p">,</span> <span class="n">buffer</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Recording and playing it back gives the same result each time:</p>
<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre><span class="nv">$ </span>rr ./urandom
rr: Saving the execution of <span class="sb">`</span>./urandom<span class="s1">' to trace directory `/home/huon/.rr/urandom-2'</span><span class="nb">.</span>
<span class="o">[</span>45, 45, 28, 0, 236]
<span class="nv">$ </span>~/projects/mozilla/rr/obj/bin/rr replay <span class="nt">-d</span> rust-gdb ~/.rr/urandom-2
GNU gdb <span class="o">(</span>Ubuntu 7.10-1ubuntu2<span class="o">)</span> 7.10
...
<span class="o">(</span>gdb<span class="o">)</span> c
Continuing.
<span class="o">[</span>45, 45, 28, 0, 236]
</pre></td></tr></tbody></table></code></pre></figure>
<p>Even the memory addresses of allocations, the stack and everything
else are deterministic, so watches and watchpoints can be placed on
exact bytes in memory and they’ll do the right thing on every “run” of
the program. (I’ve not had a reason to use it yet, but seems awesome
for debugging memory corruption: find an execution that exhibits
corruption of some string or something, and place a watch point on
those bytes to find the locations that modify the string.)</p>
<h3 id="back-to-the-future">Back to the future</h3>
<p>The thing I’ve really fallen in love with is reverse debugging:
instead of just being able to let the program’s execution progress
forward in various forms (<code class="language-plaintext highlighter-rouge">step</code>, <code class="language-plaintext highlighter-rouge">next</code>, <code class="language-plaintext highlighter-rouge">continue</code> etc.), you can do
the same in reverse.</p>
<p>My strategy is:</p>
<ul>
<li>find out some code has a bug (oh no!),</li>
<li>record executions with <code class="language-plaintext highlighter-rouge">rr</code> until the bug exhibits how I want<sup id="fnref:rare" role="doc-noteref"><a href="#fn:rare" class="footnote" rel="footnote">1</a></sup>,</li>
<li>replay the execution until there’s a sure-fire indicator it occurred
(for instance, in Rust code, breaking on <code class="language-plaintext highlighter-rouge">rust_panic</code> will stop
execution at any panic, including assertion failures),</li>
<li>work backwards from that point, diving in and out of functions and
stepping forward and backwards over lines/instructions/anything.</li>
</ul>
<p>The keys here are the <code class="language-plaintext highlighter-rouge">r</code>/<code class="language-plaintext highlighter-rouge">reverse-</code>-prefixed gdb commands: <code class="language-plaintext highlighter-rouge">rn</code>
(<code class="language-plaintext highlighter-rouge">reverse-next</code>), <code class="language-plaintext highlighter-rouge">rs</code> (<code class="language-plaintext highlighter-rouge">reverse-step</code>), <code class="language-plaintext highlighter-rouge">rc</code> (<code class="language-plaintext highlighter-rouge">reverse-continue</code>) and
so on. These do what their non-<code class="language-plaintext highlighter-rouge">reverse</code> conterparts do, except
backwards: <code class="language-plaintext highlighter-rouge">next</code> goes from line 9 to line 10, <code class="language-plaintext highlighter-rouge">reverse-next</code> goes
from line 10 to line 9.</p>
<p>It just feels so great to start up the debugger, break on
<code class="language-plaintext highlighter-rouge">rust_panic</code>, examine the state of the world, and then break on some
<em>previous</em> function call and jump back to it with
<code class="language-plaintext highlighter-rouge">reverse-continue</code>. I can track down problems without having to fiddle
with setting up how to actually start the debugger in the right place.</p>
<h3 id="removing-the-rose-coloured-glasses">Removing the rose-coloured glasses</h3>
<p>I’m probably sounding breathlessly enthusiastic, as if record/replay
and reverse execution was some amazing new functionally. It’s not.</p>
<p>Other environments/languages/tools have the functionality, but it’s
the first time I’ve had the luck to use it. There’s even actually
<a href="https://www.sourceware.org/gdb/wiki/ProcessRecord">recording</a> and <a href="https://www.sourceware.org/gdb/wiki/ReverseDebug">reverse execution</a> in gdb itself,
but it is (apparently) quite slow, and seems to be less reliable: I
tried <code class="language-plaintext highlighter-rouge">target record</code> on an Rust executable, and the recording
immediately failed in glibc’s <code class="language-plaintext highlighter-rouge">memset</code> (it couldn’t handle an AVX2
instruction).</p>
<p>Also, rr of course has its own limitations:</p>
<ul>
<li>it doesn’t literally fix your bugs for you,</li>
<li>it only runs on Linux and only x86 & x86-64 (although there’s a
<a href="https://github.com/mozilla/rr/issues/1373">bug about ARM</a>),</li>
<li>programs run on a single-core, so concurrency is possible but not
parallelism (and hence programs may be much slower due to that, and
some bugs for which rr might be nice to have are harder/impossible
to trigger),</li>
<li>modifying variables/memory is useless: any changes are ignored and
overwritten, since the executions are recorded and fixed,</li>
<li>it doesn’t support all syscalls, but I’ve not encountered one that
isn’t supported personally, and the reason is they’re implemented on
an on-demand basis; things that have been founded to be needed for
debugging Firefox etc.,</li>
<li>the time overhead is fairly low, but the memory overhead is quite
large: the factorial example from before went from 3MB (as measured
by GNU time) to 82MB. I’m sure that a large part of this is a
constant overhead, because it doesn’t seem like it’d be a great tool
for debugging large applications (for which it is designed, and
<a href="https://github.com/mozilla/rr/wiki/Testimonials">used</a>) with 30× increase in memory use.</li>
</ul>
<p>(There’s a few more technical things mentioned in the limitations on
<a href="http://rr-project.org/">rr’s website</a>.)</p>
<h2 id="summary">Summary</h2>
<p>Reverse debugging has converted me from “meh debuggers” to “💜 rr”:
being able to dance around freely—<em>frolic</em>, even—in code
pleases me like no other tool. It is language agnostic, I’m led to
believe that anything that works in GDB itself will work with rr: I
believe it was designed with C/C++ applications like Firefox in mind,
but it works flawlessly with Rust, and I’m sure other languages too.</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="https://users.rust-lang.org/t/rreverrse-debugging/3418">users</a></li>
<li class="external-link"><a href="https://www.reddit.com/r/rust/comments/3qf6j9/rreverrse_debugging/">/r/rust</a></li>
<li class="external-link"><a href="https://www.reddit.com/r/programming/comments/3qf6kq/rreverrse_debugging/">/r/programming</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Rreverrse%20Debugging%20%23rustlang&url=https://huonw.github.io/blog/2015/10/rreverse-debugging/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/10/rreverse-debugging/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/10/rreverse-debugging/&title=Rreverrse%20Debugging" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:worst-case" role="doc-endnote">
<p>I know pretty much nothing about the internals of rr or
Linux, so I have no idea if this is actually triggering a
particularly bad case; but I do know that rr will shim in/save the
result of syscalls into the operating system, so doing a lot of
them (as opening/reading/closing a file will do) is probably slow. <a href="#fnref:worst-case" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:rare" role="doc-endnote">
<p>The rr developers point out this means tracking down rare
bugs is made easier: run rr on a test-case in a loop until
the bug triggers, and then you can dissect it at leisure,
instead of just hoping to catch it by chance in a traditional
debugger. (Again not something I’ve particularly needed to
use yet, so I don’t speak from experience: the rarest bug
I’ve had to tackle recently occurred in about a quarter of
executions.) <a href="#fnref:rare" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2015/10/simple_parallel-revisiting-knnsimple_parallel 0.3: Revisiting k-NN2015-10-24T00:00:00+00:002015-10-24T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>I recently released version 0.3 of my
<a href="https://crates.io/crates/simple_parallel"><code class="language-plaintext highlighter-rouge">simple_parallel</code></a> crate, which builds on
<a href="https://crates.io/crates/crossbeam">Aaron Turon’s <code class="language-plaintext highlighter-rouge">crossbeam</code></a> to resolve
<a href="https://users.rust-lang.org/t/simple-parallel-now-partially-compiles-on-stable/1536">the stability and safety difficulties</a>: the crate now works
with Rust 1.3.0 stable, and offers safe data-parallel <code class="language-plaintext highlighter-rouge">for</code> loops and
<code class="language-plaintext highlighter-rouge">map</code>s.</p>
<p>I still don’t recommend it for general use, but I think it’s a neat
demonstration of what Rust’s type system allows, and hopefully
inspiration for something awesome.</p>
<div class="centered-libs">
<div class="lib-info" style="display: inline-block">
<a class="lib-link" href="http://huonw.github.io/simple_parallel/simple_parallel">simple_parallel</a>
<a href="https://crates.io/crates/simple_parallel"><img class="lib-badge inline-image" src="https://img.shields.io/crates/v/simple_parallel.svg" alt="simple_parallel on crates.io" /></a>
</div>
</div>
<h2 id="simple_parallel-in-16-lines"><code class="language-plaintext highlighter-rouge">simple_parallel</code> in 16 lines</h2>
<p>Taster: safely setting the values in an array stored directly on the
stack of a parent thread, in parallel, with 4 threads.</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="code"><pre><span class="c">// (add `simple_parallel = "0.3"` to your Cargo.toml)</span>
<span class="k">extern</span> <span class="n">crate</span> <span class="n">simple_parallel</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">pool</span> <span class="o">=</span> <span class="nn">simple_parallel</span><span class="p">::</span><span class="nn">Pool</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="mi">4</span><span class="p">);</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">stack_array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">;</span> <span class="mi">10</span><span class="p">];</span>
<span class="k">let</span> <span class="n">large_complicated_thing</span> <span class="o">=</span> <span class="nd">vec!</span><span class="p">[</span><span class="mi">4</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">];</span>
<span class="n">pool</span><span class="nf">.for_</span><span class="p">(</span><span class="n">stack_array</span><span class="nf">.iter_mut</span><span class="p">()</span><span class="nf">.enumerate</span><span class="p">(),</span> <span class="p">|(</span><span class="n">i</span><span class="p">,</span> <span class="n">elem</span><span class="p">)|</span> <span class="p">{</span>
<span class="o">*</span><span class="n">elem</span> <span class="o">=</span> <span class="n">large_complicated_thing</span><span class="p">[</span><span class="n">i</span> <span class="o">%</span> <span class="mi">4</span><span class="p">]</span>
<span class="p">});</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{:?}"</span><span class="p">,</span> <span class="o">&</span><span class="n">stack_array</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>This is the same as writing <code class="language-plaintext highlighter-rouge">for (i, elem) in
stack_array.iter_mut().enumerate() { ... }</code>, and the output is:
<code class="language-plaintext highlighter-rouge">[4, 3, 2, 1, 4, 3, 2, 1, 4, 3]</code>.</p>
<p>It is a rather complicated way to initialise an array with those
values, but it demonstrates some nice properties:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">stack_array</code> is <strong>allocated directly on the stack of the main thread</strong>;
it doesn’t need to be pushed to the heap. Lifetimes and
<code class="language-plaintext highlighter-rouge">simple_parallel</code>’s API ensures that the subthreads can’t hold
references to it for too long statically (no GC necessary).</li>
<li>the <code class="language-plaintext highlighter-rouge">large_complicated_thing</code> <code class="language-plaintext highlighter-rouge">Vec</code> is safely shared between all
threads, no copies necessary. Each thread gets a <code class="language-plaintext highlighter-rouge">&Vec<_></code>
reference, and they all point to the same <code class="language-plaintext highlighter-rouge">large_complicated_thing</code>
value stored on the main thread’s stack. Again lifetimes ensure that
the references won’t be dangling, but more interestingly the vector
can be read without needing to copy or lock: <strong>zero-overhead
immutable shared data</strong>.</li>
<li>the <code class="language-plaintext highlighter-rouge">iter_mut</code> method creates an iterator over <code class="language-plaintext highlighter-rouge">&mut</code> references to
the elements of <code class="language-plaintext highlighter-rouge">stack_array</code>. The closure is called on each of them
in parallel, and the references are
disjoint/not-aliasing<sup id="fnref:cache-line" role="doc-noteref"><a href="#fn:cache-line" class="footnote" rel="footnote">0</a></sup>, meaning each call is
manipulating a different section of memory. <strong>No atomics or locks
are needed</strong> to mutate what the iterator feeds the closure.</li>
<li>the <code class="language-plaintext highlighter-rouge">simple_parallel</code> APIs all consume (nearly) arbitrary iterators:
I can take the slice iterator and create a new (lazy) iterator via
<code class="language-plaintext highlighter-rouge">enumerate</code>, pairing the output of the slice iterator with
indices. (There are of course restrictions about the thread-safety
properties of the iterator and its elements, necessary to get points
above safely.)</li>
</ul>
<p>Some of this is driven by <code class="language-plaintext highlighter-rouge">simple_parallel</code>, some of it is
<code class="language-plaintext highlighter-rouge">crossbeam</code>, but most of it is the power of Rust’s type system: it
<a href="/blog/2015/02/some-notes-on-send-and-sync/">comes together just right</a> to ensure<sup id="fnref:not-proved" role="doc-noteref"><a href="#fn:not-proved" class="footnote" rel="footnote">1</a></sup>
<a href="http://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html">concurrency can be done fearlessly</a>.</p>
<p>It’s not perfect, it’s not even <em>great</em>—there’s
unnecessary overhead and it doesn’t offer many operations—but
has been useful for me (e.g. speeding up processing some pictures just
required replacing <code class="language-plaintext highlighter-rouge">for photo in photos</code> with <code class="language-plaintext highlighter-rouge">pool.for_(photos,
|photo|</code>) and serves as a neat little exploration into Rust’s type
system. I’m confident we’ll see better libraries from better
programmers that allow for some magical things.</p>
<h2 id="k-nn"><em>k</em>-NN</h2>
<p>The very first post on this blog was
<a href="/blog/2014/06/comparing-knn-in-rust/"><em>Comparing k-NN in Rust</em></a>, which ended with parallelising
the task of validating a <em>k</em>-nearest neighbour (<em>k</em>-NN) classifier,
using the safe-but-crude tools Rust-circa-0.11 offered at the time. We
now live in a promised land, with <a href="http://blog.rust-lang.org/2015/05/15/Rust-1.0.html">language stability</a>,
<a href="https://crates.io/">Cargo & thousands of crates</a>, and
<a href="https://github.com/rust-lang/rfcs/blob/master/text/0458-send-improvements.md"><code class="language-plaintext highlighter-rouge">Send</code> without <code class="language-plaintext highlighter-rouge">'static</code></a>, so there’s shiny new
safe-and-less-crude tools!</p>
<p>The example above disguised the role of <code class="language-plaintext highlighter-rouge">crossbeam</code>, but the
<code class="language-plaintext highlighter-rouge">simple_parallel</code>/<code class="language-plaintext highlighter-rouge">crossbeam</code> combo means it’s easy to process a
stream in parallel, sharing data from parent threads with no overhead
at all. The <em>k</em>-NN code loads <a href="https://github.com/c4fsharp/Dojo-Digits-Recognizer/tree/1eb4297a49dbd82a952c1523f5413519b8f1d62a/Dojo">two files</a> into <code class="language-plaintext highlighter-rouge">Vec</code>s of
784-dimensional “pixels” via <code class="language-plaintext highlighter-rouge">slurp_file</code>—one of 5000 pixels of training
data and one of 500 samples to test the classifier against—and then
uses the <code class="language-plaintext highlighter-rouge">classify</code> function to predict a label for each of the
validation samples based on the training ones, finally printing how
many were predicted correctly.</p>
<p>The full code is available at <a href="https://github.com/huonw/revisiting-knn">huonw/revisiting-knn</a>, updated
from 0.11.0 (which wasn’t too hard at all<sup id="fnref:update" role="doc-noteref"><a href="#fn:update" class="footnote" rel="footnote">2</a></sup>), so I’m just going
to focus on the interesting bit: <code class="language-plaintext highlighter-rouge">main</code>. The sequential version is
short:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">training_set</span> <span class="o">=</span> <span class="nf">slurp_file</span><span class="p">(</span><span class="s">"trainingsample.csv"</span><span class="p">);</span>
<span class="k">let</span> <span class="n">validation_sample</span> <span class="o">=</span> <span class="nf">slurp_file</span><span class="p">(</span><span class="s">"validationsample.csv"</span><span class="p">);</span>
<span class="k">let</span> <span class="n">num_correct</span> <span class="o">=</span> <span class="n">validation_sample</span><span class="nf">.iter</span><span class="p">()</span>
<span class="nf">.filter</span><span class="p">(|</span><span class="n">x</span><span class="p">|</span> <span class="nf">classify</span><span class="p">(</span><span class="o">&</span><span class="n">training_set</span><span class="p">,</span> <span class="o">&</span><span class="n">x</span><span class="py">.pixels</span><span class="p">)</span> <span class="o">==</span> <span class="n">x</span><span class="py">.label</span><span class="p">)</span>
<span class="nf">.count</span><span class="p">();</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"Percentage correct: {:.1}%"</span><span class="p">,</span>
<span class="n">num_correct</span> <span class="k">as</span> <span class="nb">f64</span> <span class="o">/</span> <span class="n">validation_sample</span><span class="nf">.len</span><span class="p">()</span> <span class="k">as</span> <span class="nb">f64</span> <span class="o">*</span> <span class="mf">100.0</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>All the work is happening in the <code class="language-plaintext highlighter-rouge">filter</code> call: the <code class="language-plaintext highlighter-rouge">classify</code>
function is the expensive one: each call does 5000 784-dimensional
<a href="https://github.com/huonw/revisiting-knn/blob/219ad78a9b15554b10d08c4e626e11e09256b8dd/src/main.rs#L44">vector distance calculations</a>. <code class="language-plaintext highlighter-rouge">perf</code>’s instruction level
profiling tells me that nearly 95% of the time is spent in
<a href="https://github.com/huonw/revisiting-knn/blob/219ad78a9b15554b10d08c4e626e11e09256b8dd/src/main.rs#L36">the loop</a><sup id="fnref:simd" role="doc-noteref"><a href="#fn:simd" class="footnote" rel="footnote">3</a></sup> for that calculation (which actually gets inlined all
the way into <code class="language-plaintext highlighter-rouge">main</code> itself).</p>
<p>The parallel <code class="language-plaintext highlighter-rouge">main</code> isn’t much longer:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="code"><pre><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="c">// load files</span>
<span class="k">let</span> <span class="n">training_set</span> <span class="o">=</span> <span class="nf">slurp_file</span><span class="p">(</span><span class="s">"trainingsample.csv"</span><span class="p">);</span>
<span class="k">let</span> <span class="n">validation_sample</span> <span class="o">=</span> <span class="nf">slurp_file</span><span class="p">(</span><span class="s">"validationsample.csv"</span><span class="p">);</span>
<span class="c">// create a thread pool</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">pool</span> <span class="o">=</span> <span class="nn">simple_parallel</span><span class="p">::</span><span class="nn">Pool</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="mi">4</span><span class="p">);</span>
<span class="nn">crossbeam</span><span class="p">::</span><span class="nf">scope</span><span class="p">(|</span><span class="n">scope</span><span class="p">|</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">num_correct</span> <span class="o">=</span>
<span class="n">pool</span><span class="nf">.unordered_map</span><span class="p">(</span><span class="n">scope</span><span class="p">,</span> <span class="o">&</span><span class="n">validation_sample</span><span class="p">,</span> <span class="p">|</span><span class="n">x</span><span class="p">|</span> <span class="p">{</span>
<span class="c">// is it classified right? (in parallel)</span>
<span class="nf">classify</span><span class="p">(</span><span class="o">&</span><span class="n">training_set</span><span class="p">,</span> <span class="o">&</span><span class="n">x</span><span class="py">.pixels</span><span class="p">)</span> <span class="o">==</span> <span class="n">x</span><span class="py">.label</span>
<span class="p">})</span>
<span class="nf">.filter</span><span class="p">(|</span><span class="n">t</span><span class="p">|</span> <span class="n">t</span><span class="na">.1</span><span class="p">)</span>
<span class="nf">.count</span><span class="p">();</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"Percentage correct: {:.1}%"</span><span class="p">,</span>
<span class="n">num_correct</span> <span class="k">as</span> <span class="nb">f64</span> <span class="o">/</span> <span class="n">validation_sample</span><span class="nf">.len</span><span class="p">()</span> <span class="k">as</span> <span class="nb">f64</span> <span class="o">*</span> <span class="mf">100.0</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The <a href="http://huonw.github.io/simple_parallel/simple_parallel/pool/struct.Pool.html#method.unordered_map"><code class="language-plaintext highlighter-rouge">unordered_map</code></a> function is a bit more complicated than
<code class="language-plaintext highlighter-rouge">for_</code> above: this takes an iterator over <code class="language-plaintext highlighter-rouge">A</code>s, and a function from
<code class="language-plaintext highlighter-rouge">A</code> to any type <code class="language-plaintext highlighter-rouge">B</code>, and returns an iterator of <code class="language-plaintext highlighter-rouge">(usize, B)</code>s, in some
random order<sup id="fnref:order" role="doc-noteref"><a href="#fn:order" class="footnote" rel="footnote">4</a></sup>. For above, <code class="language-plaintext highlighter-rouge">B</code> is a <code class="language-plaintext highlighter-rouge">bool</code>: whether the
predicted classification was correct or not.</p>
<p>The hardest part of parallelising that was working out where to put
the <code class="language-plaintext highlighter-rouge">crossbeam::scope</code>: as I wrote it above, or <code class="language-plaintext highlighter-rouge">let num_correct =
crossbeam::scope(...);</code>. It was correct and ran in parallel the first
time!</p>
<p>Interestingly, this code benefits greatly from being able to share
stacks: firstly, each <code class="language-plaintext highlighter-rouge">x</code> in the loop is just a pointer into the
<code class="language-plaintext highlighter-rouge">validation_sample</code> <code class="language-plaintext highlighter-rouge">Vec</code>, it can point right to the large chunk of
memory (for a 784D vector) owned by the main thread without having to
copy. Secondly, and even better, all the parallel <code class="language-plaintext highlighter-rouge">classify</code> calls can
read share the one huge <code class="language-plaintext highlighter-rouge">training_set</code> array without needing to copy
it, which is 5000 of those high-dimensional vectors.</p>
<h3 id="numbers">Numbers</h3>
<p>Discussing parallelism means nothing without proving it is doing
something useful: making things run faster. The sequential code took
1.86s, and the parallel version slashed that to 0.69s, 2.7× faster.</p>
<p>I’m running 1.3.0 stable, and compiled the <a href="https://github.com/huonw/revisiting-knn">the code</a> with
<code class="language-plaintext highlighter-rouge">cargo build --release --features sequential</code> and <code class="language-plaintext highlighter-rouge">cargo build
--release</code> for the two <code class="language-plaintext highlighter-rouge">main</code> functions above, the rest of the code
stayed the same. I measured the time to run with <code class="language-plaintext highlighter-rouge">perf stat -r 5
...</code>.</p>
<p>I’m on a different/faster computer to the <a href="/blog/2014/06/comparing-knn-in-rust/">previous post</a>,
and the original Rust code no longer compiles. However, the OCaml code
still does: with the same compiler, it takes approximately 8.5 seconds
on this one, about 1.6-1.7× faster. The sequential Rust code is
nearly 2× faster than the old version—meaning the
compiler/standard library has likely improved—and the parallel
version even more… but this computer has more cores so the
comparison isn’t so interesting.</p>
<h2 id="crossbeamscope"><code class="language-plaintext highlighter-rouge">crossbeam::scope</code></h2>
<p>The key trick that allowed me to get the APIs to be safe is
<a href="http://aturon.github.io/crossbeam-doc/crossbeam/fn.scope.html">this <code class="language-plaintext highlighter-rouge">scope</code> function</a>. It was somewhat infamously
<a href="https://github.com/rust-lang/rust/issues/24292">realised</a> that destructors cannot be relied upon for
scope-based memory safety: it is possible to leave a scope without
running a destructor, in safe code, e.g. get the value stuck in a
reference cycle of <code class="language-plaintext highlighter-rouge">Rc</code>s. This means that if a library ever hands away
an instance of something with a destructor, it has to be sure that
things won’t go completely wrong if that destructor never executes.</p>
<p>The alternative used in <code class="language-plaintext highlighter-rouge">crossbeam</code> was
<a href="https://github.com/aturon/rfcs/blob/75db90de40849d7cd28e334388ffa74b9e7a9bcf/text/0000-scoped-take-2.md">described in a Rust RFC</a> (that never landed in Rust itself)
written by Aaron, <code class="language-plaintext highlighter-rouge">crossbeam</code>’s author. The approach is to be a
control freak: never let anyone else control your value, only hand off
<code class="language-plaintext highlighter-rouge">&</code> references to it, so what runs when is in your power, and yours
alone. This is what <code class="language-plaintext highlighter-rouge">scope</code> does,</p>
<p>Its signature is:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
</pre></td><td class="code"><pre><span class="k">pub</span> <span class="k">fn</span> <span class="n">scope</span><span class="o"><</span><span class="nv">'a</span><span class="p">,</span> <span class="n">F</span><span class="p">,</span> <span class="n">R</span><span class="o">></span><span class="p">(</span><span class="n">f</span><span class="p">:</span> <span class="n">F</span><span class="p">)</span> <span class="k">-></span> <span class="n">R</span>
<span class="k">where</span> <span class="n">F</span><span class="p">:</span> <span class="nf">FnOnce</span><span class="p">(</span><span class="o">&</span><span class="n">Scope</span><span class="o"><</span><span class="nv">'a</span><span class="o">></span><span class="p">)</span> <span class="k">-></span> <span class="n">R</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>That is, <code class="language-plaintext highlighter-rouge">scope</code> takes one argument, which is a closure, and then
passes a reference to a <a href="http://aturon.github.io/crossbeam-doc/crossbeam/struct.Scope.html"><code class="language-plaintext highlighter-rouge">Scope</code></a> to that closure. This
<code class="language-plaintext highlighter-rouge">Scope</code> object allows for spawning threads/deferring functions, with
the guarantee that the thread will exit/the function will run before
<code class="language-plaintext highlighter-rouge">scope</code> returns.</p>
<p>Only the iterator <em>adapters</em> like <code class="language-plaintext highlighter-rouge">unordered_map</code> (rather than
consumers like <code class="language-plaintext highlighter-rouge">for_</code>) in <code class="language-plaintext highlighter-rouge">simple_parallel</code> need to think about this
externally: they act asynchronously, and so return an object that
can’t live too long and needs to control the threads spawned to do the
parallel processing, where as the consumers can just block. By taking
a <code class="language-plaintext highlighter-rouge">Scope</code> argument, these iterator adapters can defer functions that
do the thread control, giving the nice iterator APIs with a fairly
minimal usage overhead (wrapping the calls in a closure and passing an
extra argument).</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="https://users.rust-lang.org/t/simple-parallel-0-3-revisiting-k-nn/3383">users</a></li>
<li class="external-link"><a href="https://www.reddit.com/r/rust/comments/3px8y9/simple_parallel_03_revisiting_knn/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=simple_parallel%200.3:%20Revisiting%20k-NN%20%23rustlang&url=https://huonw.github.io/blog/2015/10/simple_parallel-revisiting-knn/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/10/simple_parallel-revisiting-knn/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/10/simple_parallel-revisiting-knn/&title=simple_parallel%200.3:%20Revisiting%20k-NN" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:cache-line" role="doc-endnote">
<p>The pointers are disjoint, so safety/semantically
everything is cool, but in the real world there are
performance concerns, like false sharing: the values
are all adjacent in memory and so probably share a
cache-line. In practice, this shouldn’t be a problem:
either the time to compute each value will be
significant, so the false sharing hit is irrelevant, or
one doesn’t need to/shouldn’t parallelise at the level
of individual elements. <a href="#fnref:cache-line" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:not-proved" role="doc-endnote">
<p>Strictly speaking, “ensure” isn’t quite right: there’s
not a formal proof that <code class="language-plaintext highlighter-rouge">Send</code>/<code class="language-plaintext highlighter-rouge">Sync</code>/… all do have
this guarantee, but there is work on
<a href="https://www.ralfj.de/blog/2015/10/12/formalizing-rust.html">formalisations of Rust</a> that will either
tackle this directly, or form important groundwork. <a href="#fnref:not-proved" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:update" role="doc-endnote">
<p>This code is pretty simple, so was barely affected by
language/library changes: some import paths changed, some
imports became necessary and others could be dropped, and
all the <code class="language-plaintext highlighter-rouge">.as_slice()</code> calls disappeared. <a href="#fnref:update" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:simd" role="doc-endnote">
<p>The code is actually slower than strictly necessary: if the
<code class="language-plaintext highlighter-rouge">fold</code> is separated into <code class="language-plaintext highlighter-rouge">.map(|(&a, &b)| a - b).fold(0, |s,
d| s + d * d)</code>, it is autovectorised by LLVM to use SIMD
instructions and runs twice as fast. However, I decided
against doing this in the spirit of Rust 1.3 v. Rust 0.11
comparisons: IIRC the old code didn’t get SIMD-ified. <a href="#fnref:simd" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:order" role="doc-endnote">
<p>There’s also <a href="http://huonw.github.io/simple_parallel/simple_parallel/pool/struct.Pool.html#method.map">the plain old <code class="language-plaintext highlighter-rouge">map</code> function</a>, which is
careful to return the elements in the same order as they
were in the original iterator. This is a bit more expensive. <a href="#fnref:order" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2015/08/simd-in-rustSIMD in Rust2015-08-24T00:00:00+00:002015-08-24T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>A new scheme for SIMD in <a href="https://www.rust-lang.org/">Rust</a> is available in the latest nightly
compilers, fresh off the builders (get it while it’s hot!).</p>
<p>For the last two months, I’ve been interning at Mozilla Research,
working on improving the state of <a href="https://en.wikipedia.org/wiki/SIMD">SIMD</a> parallelism in <a href="https://www.rust-lang.org/">Rust</a>:
exposing more CPU instructions in the compiler, and an
<a href="https://github.com/huonw/simd">in-progress library</a> that provides a mostly-safe but
low-level interface to that core functionality.</p>
<p>It’s still simple to use; the following are kernels for rendering
<a href="https://en.wikipedia.org/wiki/Mandelbrot_set">the Mandelbrot set</a>, one scalar, one with explicit
vectorisation, and both similar:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">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
</pre></td><td class="code"><pre><span class="c">// Scalar!</span>
<span class="c">// compute the escape time for the point `c_x + i c_y`</span>
<span class="k">fn</span> <span class="nf">mandelbrot_naive</span><span class="p">(</span><span class="n">c_x</span><span class="p">:</span> <span class="nb">f32</span><span class="p">,</span> <span class="n">c_y</span><span class="p">:</span> <span class="nb">f32</span><span class="p">,</span> <span class="n">max_iter</span><span class="p">:</span> <span class="nb">u32</span><span class="p">)</span> <span class="k">-></span> <span class="nb">u32</span> <span class="p">{</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">x</span> <span class="o">=</span> <span class="n">c_x</span><span class="p">;</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">y</span> <span class="o">=</span> <span class="n">c_y</span><span class="p">;</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">while</span> <span class="n">count</span> <span class="o"><</span> <span class="n">max_iter</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">xy</span> <span class="o">=</span> <span class="n">x</span> <span class="o">*</span> <span class="n">y</span><span class="p">;</span>
<span class="k">let</span> <span class="n">xx</span> <span class="o">=</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span><span class="p">;</span>
<span class="k">let</span> <span class="n">yy</span> <span class="o">=</span> <span class="n">y</span> <span class="o">*</span> <span class="n">y</span><span class="p">;</span>
<span class="k">let</span> <span class="n">sum</span> <span class="o">=</span> <span class="n">xx</span> <span class="o">+</span> <span class="n">yy</span><span class="p">;</span>
<span class="k">if</span> <span class="n">sum</span> <span class="o">></span> <span class="mf">4.0</span> <span class="p">{</span>
<span class="k">break</span>
<span class="p">}</span>
<span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">xx</span> <span class="o">-</span> <span class="n">yy</span> <span class="o">+</span> <span class="n">c_x</span><span class="p">;</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">xy</span> <span class="o">*</span> <span class="mf">2.0</span> <span class="o">+</span> <span class="n">c_y</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">count</span>
<span class="p">}</span>
<span class="c">// SIMD!</span>
<span class="c">// compute the escape time for the four point `c_x + i c_y` at once</span>
<span class="k">fn</span> <span class="nf">mandelbrot_vector</span><span class="p">(</span><span class="n">c_x</span><span class="p">:</span> <span class="n">f32x4</span><span class="p">,</span> <span class="n">c_y</span><span class="p">:</span> <span class="n">f32x4</span><span class="p">,</span> <span class="n">max_iter</span><span class="p">:</span> <span class="nb">u32</span><span class="p">)</span> <span class="k">-></span> <span class="n">u32x4</span> <span class="p">{</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">x</span> <span class="o">=</span> <span class="n">c_x</span><span class="p">;</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">y</span> <span class="o">=</span> <span class="n">c_y</span><span class="p">;</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">count</span> <span class="o">=</span> <span class="nn">u32x4</span><span class="p">::</span><span class="nf">splat</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="k">for</span> <span class="mi">_</span> <span class="n">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">max_iter</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">xy</span> <span class="o">=</span> <span class="n">x</span> <span class="o">*</span> <span class="n">y</span><span class="p">;</span>
<span class="k">let</span> <span class="n">xx</span> <span class="o">=</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span><span class="p">;</span>
<span class="k">let</span> <span class="n">yy</span> <span class="o">=</span> <span class="n">y</span> <span class="o">*</span> <span class="n">y</span><span class="p">;</span>
<span class="k">let</span> <span class="n">sum</span> <span class="o">=</span> <span class="n">xx</span> <span class="o">+</span> <span class="n">yy</span><span class="p">;</span>
<span class="k">let</span> <span class="n">mask</span> <span class="o">=</span> <span class="n">sum</span><span class="nf">.lt</span><span class="p">(</span><span class="nn">f32x4</span><span class="p">::</span><span class="nf">splat</span><span class="p">(</span><span class="mf">4.0</span><span class="p">));</span>
<span class="k">if</span> <span class="o">!</span><span class="n">mask</span><span class="nf">.any</span><span class="p">()</span> <span class="p">{</span> <span class="k">break</span> <span class="p">}</span>
<span class="n">count</span> <span class="o">=</span> <span class="n">count</span> <span class="o">+</span> <span class="n">mask</span><span class="nf">.to_i</span><span class="p">()</span><span class="nf">.select</span><span class="p">(</span><span class="nn">u32x4</span><span class="p">::</span><span class="nf">splat</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span>
<span class="nn">u32x4</span><span class="p">::</span><span class="nf">splat</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">xx</span> <span class="o">-</span> <span class="n">yy</span> <span class="o">+</span> <span class="n">c_x</span><span class="p">;</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">xy</span> <span class="o">+</span> <span class="n">xy</span> <span class="o">+</span> <span class="n">c_y</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">count</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<figure class="image has-caption">
<div class="image-positioner">
<a href="mandel.png">
<img src="mandel.png" alt="a rendering of the Mandelbrot set" />
</a>
</div>
<figcaption><p>The result of both Mandelbrot kernels, running in a loop with some printing code.</p>
</figcaption>
</figure>
<h2 id="simd">SIMD?</h2>
<p>SIMD (Single Instruction Multiple Data) is a way to get data
parallelism on a lot of modern hardware: CPUs have instructions that
will operate on vectors of multiple values in a single call, e.g. most
x86 CPUs offer the <code class="language-plaintext highlighter-rouge">addps</code> and <code class="language-plaintext highlighter-rouge">mulps</code> instructions to do four
single-precision floating point additions (multiplications,
respectively) in parallel, by operating on two 128-bit registers.</p>
<figure class="image ">
<div class="image-positioner">
<a href="vector.png">
<img src="vector.png" alt="a diagram of a vector (x + y) * z, where each operand contains four values" />
</a>
</div>
</figure>
<p>Rust is a good fit for many of the headline examples of such
applications, which are traditionally written in C/C++ (or Fortran,
for the last),</p>
<ul>
<li>media codecs (audio, video, images)</li>
<li>games/things that do a lot of 3D graphics manipulation</li>
<li>numerical software (data processing (big or not), simulations etc.)</li>
</ul>
<p>Improved support for explicit SIMD enables Rust to reliably push
hardware to its limit in these areas, and more.</p>
<h2 id="common-vs-platform-specific">Common vs. Platform-specific</h2>
<p>There’s a common core of operations that is widely supported, however,
there is a variety of useful instructions that aren’t found
everywhere. My <code class="language-plaintext highlighter-rouge">simd</code> crate tries to provide a consistent API for the
cross-platform functionality, and then opt-ins for platform specific
things. The cross-platform API of <code class="language-plaintext highlighter-rouge">simd</code> is based on
<a href="https://01.org/node/1495">work on SIMD in JavaScript</a>: they’ve already done the hard
yards to abstract out a basic set of operations that generally work
efficiently, or are extremely useful, everywhere. It definitely
doesn’t cover everything, but it does go a long way.</p>
<p>A limited-but-increasing amount of the platform-specific functionality
is exposed via submodules of the <code class="language-plaintext highlighter-rouge">simd</code> crate, allowing one to opt-in
to non-portability in a manner similar to <a href="http://doc.rust-lang.org/std/os/"><code class="language-plaintext highlighter-rouge">std::os</code></a>. For
example, the SSSE3 instruction <code class="language-plaintext highlighter-rouge">pshufb</code> is exposed as the
<code class="language-plaintext highlighter-rouge">shuffle_bytes</code> method on traits in <code class="language-plaintext highlighter-rouge">simd::x86::ssse3</code>:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre><span class="k">pub</span> <span class="k">fn</span> <span class="nf">shuffle</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">u8x16</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">u8x16</span><span class="p">)</span> <span class="k">-></span> <span class="n">u8x16</span> <span class="p">{</span>
<span class="k">use</span> <span class="nn">simd</span><span class="p">::</span><span class="nn">x86</span><span class="p">::</span><span class="nn">ssse3</span><span class="p">::</span><span class="o">*</span><span class="p">;</span>
<span class="n">x</span><span class="nf">.shuffle_bytes</span><span class="p">(</span><span class="n">y</span><span class="p">)</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Caveat Programmor: even the cross-platform API isn’t entirely portable
at the moment, it requires <em>some</em> level of SIMD support in hardware,
and requires the <code class="language-plaintext highlighter-rouge">simd</code> library to have support for the platform. The
major example of this is ARM and i686 (i.e. x86) CPUs aren’t
guaranteed to have SIMD instructions, so one must opt-in via the <code class="language-plaintext highlighter-rouge">-C
target-feature=+neon</code> (respectively <code class="language-plaintext highlighter-rouge">-C target-feature=+sse2</code>)
argument to the compiler<sup id="fnref:cargo" role="doc-noteref"><a href="#fn:cargo" class="footnote" rel="footnote">0</a></sup>. Also, I haven’t worked on PowerPC or
MIPS, focusing only on x86, ARM and AArch64 (ARMv8-A’s 64-bit
architecture).</p>
<h2 id="benchmarks">Benchmarks</h2>
<p>Does this help? Yes! SIMD code in Rust can be several times faster
than scalar code, and is comparable and even ahead of using the
intrinsics in Clang. GCC seems to still have a significant edge over
Rust/LLVM (<code class="language-plaintext highlighter-rouge">rustc</code>’s optimiser) in some cases, but not in others.</p>
<p>I wrote a collection of microbenchmarks comparing the new SIMD
functionality to scalar Rust code, and to similar implementations in
C/C++:</p>
<ul>
<li>operations on 4×4 <code class="language-plaintext highlighter-rouge">f32</code> matrices (only SIMD Rust vs. scalar Rust)
<ul>
<li>inversion</li>
<li>multiplication</li>
<li>transposition</li>
</ul>
</li>
<li>the Mandelbrot example above</li>
<li>some benchmarks from <a href="http://benchmarksgame.alioth.debian.org/">the Benchmark Game</a>, where the fastest
programs use explicit SIMD:
<ul>
<li><a href="http://benchmarksgame.alioth.debian.org/u64/performance.php?test=nbody">nbody</a> (fastest on single-core x86-64 is C)</li>
<li><a href="http://benchmarksgame.alioth.debian.org/u64/performance.php?test=fannkuchredux">fannkuch-redux</a> (C++)</li>
<li><a href="http://benchmarksgame.alioth.debian.org/u64/performance.php?test=spectralnorm">spectral-norm</a> (C)</li>
</ul>
</li>
</ul>
<p>I adapted the inverse and multiply comparative benchmarks from
<a href="https://github.com/tc39/ecmascript_simd/tree/7270420f2f4bd337985307fc46a627c94bd1059e/src/benchmarks">ecmascript_js</a> into Rust microbenchmarks, and
translated the fastest benchmark game programs into Rust. I used the
original implementations of those fastest programs for comparison and
the fastest Rust programs (none of which use explicit SIMD) as a
baseline, after disabling threading. All of this Rust code is
included as <a href="https://github.com/huonw/simd/tree/master/examples">examples</a> or <a href="https://github.com/huonw/simd/tree/master/benches">benchmarks</a> in
the <code class="language-plaintext highlighter-rouge">simd</code> repository.</p>
<p>The Rust code is quite portable. The matrix operations use only the
cross-platform API and so work on x86, ARM and AArch64 right now. The
nbody and spectral-norm benchmarks require <code class="language-plaintext highlighter-rouge">f64</code>s (aka <code class="language-plaintext highlighter-rouge">double</code>s)
which are only supported by x86-64 & AArch64 hardware. The
fannkuch-redux benchmark requires conditionally defining a single
operation based on the platform, which takes only a few lines on each
listed platform.</p>
<figure class="image has-caption">
<div class="image-positioner">
<a href="chart-x86-64.png">
<img src="chart-x86-64.png" alt="A chart for x86-64 of the speed-up over scalar code for vectorised code compiled with Rust, Clang and GCC on 3 benchmarks (nbody, spectral-norm, fannkuch-redux), and Rust only for 4 more (matrix inverse, matrix multiply, matrix transpose, mandelbrot). The speed-up ranges from about 1.2 for nbody to about 3 for matrix multiply and mandelbrot." />
</a>
</div>
<figcaption><p>Benchmarks on x86-64 (Intel i7-4900MQ). The Rust code was compiled with <code class="language-plaintext highlighter-rouge">-C opt-level=3</code>, the C/C++ with <code class="language-plaintext highlighter-rouge">-O3</code>. SSSE3 was enabled for fannkuch-redux only.</p>
</figcaption>
</figure>
<figure class="image has-caption">
<div class="image-positioner">
<a href="chart-aarch64.png">
<img src="chart-aarch64.png" alt="A chart for AArch64 of the speed-up over scalar code for vectorised code compiled with Rust for the same 7 benchmarks (nbody, spectral-norm, fannkuch-redux, matrix inverse, matrix multiply, matrix transpose, mandelbrot). The speed-up ranges from 1 for spectral-norm to about 3.2 for matrix multiply and mandelbrot" />
</a>
</div>
<figcaption><p>Benchmarks on AArch64 (Google Nexus 9). The Rust code was compiled with <code class="language-plaintext highlighter-rouge">-C opt-level=3</code>.</p>
</figcaption>
</figure>
<figure class="image has-caption">
<div class="image-positioner">
<a href="chart-arm.png">
<img src="chart-arm.png" alt="A chart for ARM of the speed-up over scalar code for vectorised code compiled with Rust for a subset of 5 benchmarks (fannkuch-redux, matrix inverse, matrix multiply, matrix transpose, mandelbrot). The speed-up ranges from 1.8 for fannkuch-redux to 4 for matrix multiply" />
</a>
</div>
<figcaption><p>Benchmarks on ARM (Google Nexus 5). The Rust code was compiled with <code class="language-plaintext highlighter-rouge">-C opt-level=3 -C target-feature=+neon</code>.</p>
</figcaption>
</figure>
<p>Compiler versions:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">rustc 1.4.0-dev (02e97342c 2015-08-17)</code></li>
<li><code class="language-plaintext highlighter-rouge">Ubuntu clang version 3.6.0-2ubuntu1 (tags/RELEASE_360/final) (based on LLVM 3.6.0)</code></li>
<li><code class="language-plaintext highlighter-rouge">gcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2</code></li>
</ul>
<h2 id="explicit-simd-in-the-compiler">Explicit SIMD in the Compiler</h2>
<p>My changes are a large incremental improvement over the old SIMD
support in Rust. Previously, the only explicit SIMD supported by the
compiler was declaring types as SIMD vectors with the <code class="language-plaintext highlighter-rouge">#[simd]</code>
attribute, which automatically allowed the use of built-in operators
like <code class="language-plaintext highlighter-rouge">+</code> and <code class="language-plaintext highlighter-rouge">*</code> and (sometimes) <code class="language-plaintext highlighter-rouge">==</code>. This was the full extent, and
the only way to get anything beyond that was by relying on the
optimiser, using inline assembly or <a href="http://huonw.github.io/llvmint/llvmint/">horrible hacks</a>. Now,
there are a large number of intrinsics defined by the compiler as
foreign functions with a special ABI. The <code class="language-plaintext highlighter-rouge">simd</code> crate imports them to
implement its functionality, e.g.:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="code"><pre><span class="nd">#[repr(simd)]</span>
<span class="k">struct</span> <span class="nf">u8x16</span><span class="p">(</span><span class="nb">u8</span><span class="p">,</span> <span class="nb">u8</span><span class="p">,</span> <span class="nb">u8</span><span class="p">,</span> <span class="nb">u8</span><span class="p">,</span> <span class="nb">u8</span><span class="p">,</span> <span class="nb">u8</span><span class="p">,</span> <span class="nb">u8</span><span class="p">,</span> <span class="nb">u8</span><span class="p">,</span>
<span class="nb">u8</span><span class="p">,</span> <span class="nb">u8</span><span class="p">,</span> <span class="nb">u8</span><span class="p">,</span> <span class="nb">u8</span><span class="p">,</span> <span class="nb">u8</span><span class="p">,</span> <span class="nb">u8</span><span class="p">,</span> <span class="nb">u8</span><span class="p">,</span> <span class="nb">u8</span><span class="p">);</span>
<span class="k">extern</span> <span class="s">"platform-intrinsic"</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">x86_mm_shuffle_epi8</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">u8x16</span><span class="p">,</span> <span class="n">idx</span><span class="p">:</span> <span class="n">u8x16</span><span class="p">)</span> <span class="k">-></span> <span class="n">u8x16</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>These intrinsics follow the pattern of
<code class="language-plaintext highlighter-rouge"><architecture>_<vendor's-name></code>, relying on the definitions that CPU
vendors give.</p>
<p>Speaking of the optimiser, <code class="language-plaintext highlighter-rouge">rustc</code> uses LLVM, which is industrial
strength, and supports a lot of autovectorisation<sup id="fnref:ispc" role="doc-noteref"><a href="#fn:ispc" class="footnote" rel="footnote">1</a></sup>: compiling
scalar code into code that uses SIMD intrinsics. The power of this can
be seen above in the spectral-norm benchmark on AArch64, where the
optimiser managed to make the scalar code just as efficient as the
vector code. However, this is limited: small code changes can disturb
vectorisation optimisations, and, more significantly, vectorisation
may require changing the semantics slightly, and, synthesising
complicated vector instructions is hard for a compiler<sup id="fnref:soa" role="doc-noteref"><a href="#fn:soa" class="footnote" rel="footnote">2</a></sup>.</p>
<p>The new compiler intrinsics brings Rust essentially on par with the
SIMD extensions in C and C++ compilers like GCC, Clang and ICC. It
requires some more manual effort to use it directly, with manual
<code class="language-plaintext highlighter-rouge">extern</code> imports required, but libraries like <code class="language-plaintext highlighter-rouge">simd</code> avoid that issue.</p>
<h2 id="the-future">The Future</h2>
<p>The goal for the immediate future is nailing down the design of the
low-level functionality discussed in <a href="https://github.com/rust-lang/rfcs/pull/1199">RFC 1199</a>, and making
sure that the initial implementation in <a href="https://github.com/rust-lang/rust/pull/27169">#27169</a> matches
that. An unfortunate missing piece is nice support for rearranging
vectors via shuffles and swizzles: I believe it requires
<a href="https://github.com/rust-lang/rfcs/pull/1062">RFC 1062</a> or some other way to have values in generics<sup id="fnref:shuffle" role="doc-noteref"><a href="#fn:shuffle" class="footnote" rel="footnote">3</a></sup>.</p>
<p>My long-term goal is to have <code class="language-plaintext highlighter-rouge">simd</code> provide the common cross-platform
API, with extension traits giving access to all of the
platform-specific instructions. All of this will have as thin an
interface to as possible: the vast majority of functions correspond to
one instruction. (One question I haven’t resolved is what to call
these functions: I could name them directly after the instruction they
correspond to (<code class="language-plaintext highlighter-rouge">pshufb</code>), or after the equivalent C intrinsic
(<code class="language-plaintext highlighter-rouge">_mm_shuffle_epi8</code>), or something approximately descriptive
(<code class="language-plaintext highlighter-rouge">shuffle_bytes</code>). Thoughts?)</p>
<p>I’m not personally planning to build long-vector functionality on top
of the improved short-vector functionality, but it is certainly
something that would be very cool to see.</p>
<p>One major missing piece of this new SIMD support in Rust is how to
handle run-time determination of SIMD functionality: it’s relatively
common to have an algorithm with multiple implementations for
different levels of SIMD support, with a run-time switch to choose the
fastest one supported by the CPU being used. For example, x86-64 CPUs
are only guaranteed to support SSE2, but there’s things beyond that
with more functionality and even longer SIMD vectors (Intel’s new
AVX512 has 512-bit vectors). I’ve got some vague ideas in this area,
but nothing concrete yet.</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="https://users.rust-lang.org/t/improved-simd-in-rust/2604">users</a></li>
<li class="external-link"><a href="https://www.reddit.com/r/rust/comments/3i85lg/simd_in_rust/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=SIMD%20in%20Rust%20%23rustlang&url=https://huonw.github.io/blog/2015/08/simd-in-rust/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/08/simd-in-rust/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/08/simd-in-rust/&title=SIMD%20in%20Rust" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:cargo" role="doc-endnote">
<p>Passing the <code class="language-plaintext highlighter-rouge">-C target-feature</code> flag to a whole compilation
with <code class="language-plaintext highlighter-rouge">cargo</code> is somewhat annoying at the moment. It requires
a custom target spec or intercepting how <code class="language-plaintext highlighter-rouge">rustc</code> is called
via the <code class="language-plaintext highlighter-rouge">RUSTC</code> environment variable (for my own
experiments, I’m doing the latter: pointing the variable to
a shell script that runs <code class="language-plaintext highlighter-rouge">rustc -C target-feature='...'
"$@"</code>). This is covered by <a href="https://github.com/rust-lang/cargo/issues/1137">#1137</a>. <a href="#fnref:cargo" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:ispc" role="doc-endnote">
<p>LLVM by itself isn’t as powerful as <a href="https://ispc.github.io/">Intel’s ISPC</a>,
which has its own C variant, but I could imagine a Rust
version too, to push the limits of compiler-driven
autovectorisation (among other things) here too. <a href="#fnref:ispc" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:soa" role="doc-endnote">
<p>Another thing compilers can’t easily do is layout
optimisations. A major one is an AoS (array-of-structs) to SoA
(struct-of-arrays) transformation, which is particularly
important for SIMD, both explicit and autovectorised. <a href="#fnref:soa" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:shuffle" role="doc-endnote">
<p>Fortunately, not all is lost: I’ve always seen the
optimiser synthesise a real shuffle from a manual series
of <code class="language-plaintext highlighter-rouge">extract</code>s and a <code class="language-plaintext highlighter-rouge">new</code>. It’s annoying and isn’t
<em>strictly</em> guaranteed, but it seems to be pretty reliable. <a href="#fnref:shuffle" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2015/07/what-is-simdWhat is SIMD?2015-07-10T00:00:00+00:002015-07-10T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>I’m currently in San Francisco doing an internship at Mozilla
Research, working on creating functionality for SIMD in the Rust
programming language.</p>
<p>(This post is designed to describe what I’m doing for people not
necessarily familiar with programming or SIMD.)</p>
<h2 id="what-is-simd">What is SIMD?</h2>
<p>SIMD stands for “Single Instruction, Multiple Data”, and refers to
functionality that a lot of computer hardware has for operating on
multiple numbers at once. (Essentially 100% of all desktops and
laptops and many modern phones have support for it.)</p>
<p>At a high-level, computers only operate on numbers. A program running
on the computer will be executing sequences of instructions like</p>
<ul>
<li>add <em>x</em> and <em>y</em></li>
<li>multiply the result and <em>z</em>,</li>
<li>store that number to location <em>p</em>,</li>
<li>repeat (until it’s been done 100 times).</li>
</ul>
<p>Usually each instruction is a simple operation. If boxes are numbers,
the first two steps above (which are run 100 times) might look like:</p>
<figure class="image ">
<div class="image-positioner">
<a href="scalar2.png">
<img src="scalar2.png" alt="a diagram of a scalar (x + y) * z, where each operand contains one value" />
</a>
</div>
</figure>
<p>Each operation is working with just two numbers… but imagine if we
could instead do the same operation to many pairs at the same time:
things would go faster.</p>
<p>This is exactly what SIMD offers. The operation instead looks like:</p>
<figure class="image ">
<div class="image-positioner">
<a href="vector.png">
<img src="vector.png" alt="a diagram of a vector (x + y) * z, where each operand contains four values" />
</a>
</div>
</figure>
<p>If we’re doing 4 things at once, hopefully it is about 4 times faster!
(We’d only have to repeat that 25 times to do the whole sequence
above.)</p>
<p>Doing arithmetic in parallel is definitely useful, but one of the more
interesting parts of SIMD is how it allows doing relatively
complicated rearragements of data efficiently. SIMD vectors can be
shuffled, swizzled, blended, permuted…</p>
<figure class="image ">
<div class="image-positioner">
<a href="swizzle.png">
<img src="swizzle.png" alt="a diagram of a vector swizzle from x y z w to y w z x" />
</a>
</div>
</figure>
<p>These shuffles allow super-linear speed-ups: even if the computer can
only do 4 things at once, shuffles mean some operations can become 5
or more times faster!</p>
<h2 id="why-care-about-simd">Why care about SIMD?</h2>
<p>The SIMD operations allow a pile of common operations to be performed
much faster, allowing software to be snappier, smoother and use less
battery.</p>
<p>There are a few common uses for SIMD:</p>
<ul>
<li>rendering graphics/text to your screen, which often involves doing
the exactly same relatively simple arithmetic operations a lot (your
browser is probably using SIMD to display this page).</li>
<li>decoding compressed pictures, videos and audio, where being faster
means using less battery: very important in the age of the
laptop/tablet/phone.</li>
<li>scientific computing will often need to do some heavy-weight
number-crunching, and being able to do that four (or eight, or
sixteen) times faster can be very nice.</li>
</ul>
<h2 id="what-does-creating-simd-support-mean">What does “creating SIMD support” mean?</h2>
<p>AKA, what am I doing with my days?</p>
<p>First, an introduction to what “programming” means. A programmer
generally spends their time either thinking, or editing text
documents: source code. The source code is written by the programmer
to describe all the things that the computer needs to do. A program (a
compiler) will then convert that source code into the sequence of
instructions the computer needs to execute (machine code).</p>
<p>(A compiler is itself a computer program and so has its own source
code, which was converted to machine code by a different compiler
which also has its own source code, which was converted to machine
code by another one… all the way back to the first programs, written
directly in machine code by humans: no compiler to help.)</p>
<p>There are a lot of different programming languages in the world: each
one has its own compilers that understand source code written in that
language, and each of those languages has its own specialties. Some
languages are simple and relatively easy to learn at the expense of
being slower or less flexible, while others are more complicated but
offer more control, flexibility and/or speed. The Rust programming
language is relatively new one being created by Mozilla, and fits into
the latter category.</p>
<p>Currently, the compiler for Rust doesn’t have good support for
generating the SIMD instructions, so part of my work is making
modifications to the Rust compiler’s own source code so that it does
this better. However, the interface this exposes is very low-level: it
has complete control, but requires a lot of effort to use. So part of
my project is also building a collection of Rust code (a library) that
wraps up the raw functionality into something nicer and easier to use.</p>
<p>Over the past few days I’ve made some big steps. I posted
<a href="https://internals.rust-lang.org/t/pre-rfc-simd-groundwork/2343">my first design document</a>
for initial public feedback, and then
<a href="https://github.com/rust-lang/rfcs/pull/1199">a revised form as an official proposal</a>,
but I also made some progress with the actual code.</p>
<figure class="image ">
<div class="image-positioner">
<a href="mandelbrot.png">
<img src="mandelbrot.png" alt="a screenshot of an ASCII art rendering of the Mandelbrot set" />
</a>
</div>
</figure>
<p>It’s a (rather basic) rendering of the
<a href="https://en.wikipedia.org/wiki/Mandelbrot_set">Mandelbrot set</a>, using
a sketch of my nicer library.</p>
<p>Somewhat unusually for SIMD, I can run it on many different types of
computers without having to change the code at all: that screenshot is
when running on my computer, but I can run it on my phone, a little
box similar to a Raspberry Pi, and in a variety of emulators… and
they all give exactly the same output. (It runs approximately 3×
faster than the non-SIMD version.)</p>
<p>It’s a simple example, but it’s a nice first step.</p>
https://huonw.github.io/blog/2015/06/travis-cargo-0.1.3travis-cargo 0.1.3: --no-sudo2015-06-30T00:00:00+00:002015-06-30T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>I just pushed <a href="https://github.com/huonw/travis-cargo">travis-cargo</a>
version 0.1.3, which adds a <code class="language-plaintext highlighter-rouge">--no-sudo</code> command to
<a href="/blog/2015/05/travis-on-the-train-part-2/">the <code class="language-plaintext highlighter-rouge">coveralls</code> and <code class="language-plaintext highlighter-rouge">coverage</code> subcommands</a> to allow
recording/uploading test coverage without needing <code class="language-plaintext highlighter-rouge">sudo</code>.</p>
<p>This is based<sup id="fnref:others" role="doc-noteref"><a href="#fn:others" class="footnote" rel="footnote">0</a></sup> on <a href="https://github.com/seanmonstar">@seanmonstar</a>’s
investigation/<a href="https://github.com/seanmonstar/httparse/blob/48da357b84397cc512265c8f6a99c89a7f513351/.travis.yml">implementation</a>
of sudo-less <code class="language-plaintext highlighter-rouge">kcov</code>. It works because Travis’s
<a href="http://docs.travis-ci.com/user/apt/"><code class="language-plaintext highlighter-rouge">apt</code> addon</a> has
<a href="https://github.com/travis-ci/apt-package-whitelist">whitelisted</a>
everything necessary. Unfortunately, it does require these to be
installed explicitly, which seems to be something <code class="language-plaintext highlighter-rouge">travis-cargo</code> can’t
do itself: they seem to have to be explicitly listed in the manifest.</p>
<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="code"><pre><span class="na">addons</span><span class="pi">:</span>
<span class="na">apt</span><span class="pi">:</span>
<span class="na">packages</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">libcurl4-openssl-dev</span>
<span class="pi">-</span> <span class="s">libelf-dev</span>
<span class="pi">-</span> <span class="s">libdw-dev</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>This allows leveraging Travis’ container-based infrastructure, with
<a href="http://blog.travis-ci.com/2014-12-17-faster-builds-with-container-based-infrastructure/">all its benefits</a>,
especially around speed and latency. If you switch<sup id="fnref:false" role="doc-noteref"><a href="#fn:false" class="footnote" rel="footnote">1</a></sup>, you can check that
your builds are using it by looking for the following paragraph in the
build-logs:</p>
<figure class="highlight"><pre><code class="language-text" data-lang="text"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre>This job is running on container-based infrastructure, which does not allow use of 'sudo', setuid and setguid executables.
If you require sudo, add 'sudo: required' to your .travis.yml
See http://docs.travis-ci.com/user/workers/container-based-infrastructure/ for details.
</pre></td></tr></tbody></table></code></pre></figure>
<p><code class="language-plaintext highlighter-rouge">--no-sudo</code> is opt-in because it’d be a breaking change to do anything
else, and because adding <code class="language-plaintext highlighter-rouge">sudo: required</code> is simpler than the whole
<code class="language-plaintext highlighter-rouge">addons: apt: ...</code> thing.</p>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:others" role="doc-endnote">
<p><a href="https://github.com/jonas-schievink">@jonas-schievink</a> had
a
<a href="https://github.com/huonw/travis-cargo/issues/7">similar observation a month ago</a>
but I only noticed after already implementing it just today:
sorry Jonas! <a href="#fnref:others" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:false" role="doc-endnote">
<p>I think I have also sometimes found it necessary to also add
<code class="language-plaintext highlighter-rouge">sudo: false</code> to forcibly opt-in to the container-based
infrastructure. <a href="#fnref:false" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2015/06/announcing-primalAnnouncing Primal: Putting Raw Power Into Prime Numbers2015-06-08T00:00:00+00:002015-06-08T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>Over the past few weeks I’ve been working on improving my
<code class="language-plaintext highlighter-rouge">slow_primes</code> library, culminating in needing rename and the 0.2
release of <code class="language-plaintext highlighter-rouge">primal</code>, a Rust crate for computing properties of prime
numbers with <a href="http://primesieve.org/">state-of-the-art algorithms</a>, while still
maintaining an idiomatic and easy-to-use interface.</p>
<div class="centered-libs">
<div class="lib-info" style="display: inline-block">
<a class="lib-link" href="http://huonw.github.io/primal/primal">primal</a>
<a href="https://crates.io/crates/primal"><img class="lib-badge inline-image" src="https://img.shields.io/crates/v/primal.svg" alt="primal on crates.io" /></a>
</div>
</div>
<p>My computer takes a quarter of a second and less than 3MB of RAM to
tell me that there 50,847,534 primes below 1 billion:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="code"><pre><span class="k">extern</span> <span class="n">crate</span> <span class="n">primal</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"there are {} primes below 1 billion"</span><span class="p">,</span>
<span class="nn">primal</span><span class="p">::</span><span class="nn">StreamingSieve</span><span class="p">::</span><span class="nf">prime_pi</span><span class="p">(</span><span class="mi">1_000_000_000</span><span class="p">));</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Some of the first programming I did was <a href="http://projecteuler.net">Project Euler</a>: a site
that has little mathematical problems designed to be solved with the
assistance of a computer. Beyond just the challenge of solving the
problem, there’s the challenge of writing code to go as fast as
possible: Project Euler will always hold a little place in my heart
for making me learn a <em>lot</em> about writing fast code. That’s where I
first tried to write fast prime sieves: I recall being pretty happy
when I could sieve up to a “big” number like 1 million quickly; now
I’m pretty happy that I can sieve up to 1 billion in (probably) less
time, and certainly less memory.</p>
<h2 id="migrating-from-slow_primesprimal-01">Migrating from <code class="language-plaintext highlighter-rouge">slow_primes</code>/<code class="language-plaintext highlighter-rouge">primal</code> 0.1</h2>
<ul>
<li>rename <code class="language-plaintext highlighter-rouge">slow_primes</code> to <code class="language-plaintext highlighter-rouge">primal</code> or bump the version in your
<code class="language-plaintext highlighter-rouge">Cargo.toml</code>,</li>
<li>replace <code class="language-plaintext highlighter-rouge">Primes</code> with <a href="http://huonw.github.io/primal/primal/struct.Sieve.html"><code class="language-plaintext highlighter-rouge">Sieve</code></a>: <code class="language-plaintext highlighter-rouge">Primes::sieve(100)</code>
→ <code class="language-plaintext highlighter-rouge">Sieve::new(100)</code>,</li>
<li>call <a href="http://huonw.github.io/primal/primal/struct.Sieve.html#method.primes_from"><code class="language-plaintext highlighter-rouge">primes_from</code></a> instead of <code class="language-plaintext highlighter-rouge">primes()</code>:
<code class="language-plaintext highlighter-rouge">sieve.primes()</code> → <code class="language-plaintext highlighter-rouge">sieve.primes_from(0)</code>,</li>
<li>revel in the improved sieving performance.</li>
</ul>
<h2 id="highlights">Highlights</h2>
<p><code class="language-plaintext highlighter-rouge">primal</code> is pretty great, if I do say so myself:</p>
<ul>
<li>the documentation is complete: every function has some text
describing it, all have explicit examples (except <code class="language-plaintext highlighter-rouge">Sieve::new</code>), and
there’s <a href="http://huonw.github.io/primal/primal/#examples">a nice introductory walk-through</a> tackling three
classes of problems.</li>
<li>it includes a highly optimised segmented, wheel <a href="http://huonw.github.io/primal/primal/struct.Sieve.html">sieve</a>: <code class="language-plaintext highlighter-rouge">Sieve</code>. It
is essentially a Rust port of Kim Walisch’s awesome <a href="http://primesieve.org/">primesieve</a>,
which is apparently the fastest sieve in the world. The version in
<code class="language-plaintext highlighter-rouge">primal</code> is really fast, certainly the fastest sieve I can find in
Rust, but there’s still room to improve: the Rust version is
currently around 5-20% slower than primesieve (single-threaded), and
lacks some of the fancier features that it adds on top.</li>
<li>
<p>there’s some reasonably deep mathematics:
<a href="http://huonw.github.io/primal/primal/fn.estimate_nth_prime.html"><code class="language-plaintext highlighter-rouge">estimate_nth_prime(n)</code></a> and <a href="http://huonw.github.io/primal/primal/fn.estimate_prime_pi.html"><code class="language-plaintext highlighter-rouge">estimate_prime_pi(k)</code></a>
provide fast (<em>O</em>(1)) but precise bounds for the <em>n</em>th prime, and
the number of primes below <em>k</em>:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">estimate_prime_pi(1_000_000_000)</code> returns a lower-bound of
50,785,736 (0.12% below the true value) and an upper-bound of
50,865,514 (0.04% above),</li>
<li><code class="language-plaintext highlighter-rouge">estimate_nth_prime(50_847_535)</code> returns 999,873,297 and
1,000,273,162, which are 0.01% below and 0.03% above
(respectively) the true value of 1,000,000,007.</li>
</ul>
</li>
</ul>
<p>However, my favourite feature is <a href="http://huonw.github.io/primal/primal/struct.Primes.html"><code class="language-plaintext highlighter-rouge">Primes</code></a>: which is an fast
lazy iterator over <em>all</em> primes, with no upper-bound (other than integer
size). In a flash of inspiration, I realised it would be possible to
do this reasonably efficiently with a simple wrapper around the core
sieve. It can be used a bit like:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="code"><pre><span class="k">extern</span> <span class="n">crate</span> <span class="n">primal</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"the 50,847,535th prime is {}"</span><span class="p">,</span>
<span class="nn">primal</span><span class="p">::</span><span class="nn">Primes</span><span class="p">::</span><span class="nf">all</span><span class="p">()</span><span class="nf">.nth</span><span class="p">(</span><span class="mi">50_847_535</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">());</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<div class="join"></div>
<p>It’s not the most efficient way to get the nth prime, but it still
only takes <s>0.9</s> 0.52 seconds<sup id="fnref:update" role="doc-noteref"><a href="#fn:update" class="footnote" rel="footnote">0</a></sup> to print 1,000,000,007. (A better way is
<a href="http://huonw.github.io/primal/primal/struct.StreamingSieve.html#method.nth_prime"><code class="language-plaintext highlighter-rouge">StreamingSieve::nth_prime</code></a>, which takes ~0.22s for the
same task.)</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="https://users.rust-lang.org/t/primal-putting-raw-power-into-prime-numbers/1747">users</a></li>
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/39134l/announcing_primal_putting_raw_power_into_prime/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Announcing%20Primal:%20Putting%20Raw%20Power%20Into%20Prime%20Numbers%20%23rustlang&url=https://huonw.github.io/blog/2015/06/announcing-primal/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/06/announcing-primal/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/06/announcing-primal/&title=Announcing%20Primal:%20Putting%20Raw%20Power%20Into%20Prime%20Numbers" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:update" role="doc-endnote">
<p>I realised the original implementation was inefficient, and
using far more memory than necessary: fixing that made a
huge difference. <a href="#fnref:update" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2015/05/defaulting-to-thread-safetyDefaulting to Thread-Safety: Closures and Concurrency2015-05-26T00:00:00+00:002015-05-26T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>Rust has some powerful tricks to model properties of aggregate types
via unsafe traits with default and negative implementations. These
features motivated by offering flexible concurrency/parallelism, and
allow powerful closure-based APIs without losing any thread-safety (or
memory-safety) guarantees at all.</p>
<p>I realised that <a href="/blog/2015/05/finding-closure-in-rust/">my recent post</a> on the low-level details of
closures missed an important aspect: how they interact with
threading. This post builds on the struct desugaring in that one, the
general concepts in <a href="http://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html"><em>Fearless Concurrency with Rust</em></a> and
the discussion of “markers” in
<a href="http://blog.rust-lang.org/2015/05/11/traits.html"><em>Abstraction without overhead</em></a>. (I suppose I should
link <a href="/blog/2015/02/some-notes-on-send-and-sync/"><em>Some notes on Send and Sync</em></a> too.)</p>
<h2 id="threads">Threads</h2>
<p>Spawning a thread in Rust is easy:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="n">thread</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">s</span> <span class="o">=</span> <span class="s">"from the parent"</span><span class="p">;</span>
<span class="nn">thread</span><span class="p">::</span><span class="nf">spawn</span><span class="p">(</span><span class="k">move</span> <span class="p">||</span> <span class="p">{</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"child prints a string {}"</span><span class="p">,</span> <span class="n">s</span><span class="p">);</span>
<span class="p">});</span>
<span class="nn">thread</span><span class="p">::</span><span class="nf">sleep_ms</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>As one might hope, this prints <code class="language-plaintext highlighter-rouge">child prints a string from the
parent</code>. Like C, an binary exits once the main thread is finished, so
I’ve inserted a sleep to (usually) ensure that the child thread is
spawned and prints before the main thread dies. (It could also call
<a href="http://doc.rust-lang.org/std/thread/struct.JoinHandle.html#method.join"><code class="language-plaintext highlighter-rouge">join</code></a> on the return value of <code class="language-plaintext highlighter-rouge">spawn</code> to block, but there’s an
example below that the <code class="language-plaintext highlighter-rouge">join</code> strategy complicates, so <code class="language-plaintext highlighter-rouge">sleep_ms</code> it
is.)</p>
<p>It’s one of Rust’s key guarantees that this is ensured to be
thread-safe, statically. The standard library ensures there’s no way to
pass references between threads that get invalidated and even there’s
no way to mutate things without using atomics or locks (among other
assurances).</p>
<p>This all works automatically with closures without too much magic,
despite closures being very magic… how?</p>
<h2 id="trait-bounds">Trait bounds</h2>
<p>The signature of <a href="http://doc.rust-lang.org/std/thread/fn.spawn.html"><code class="language-plaintext highlighter-rouge">spawn</code></a> is</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="code"><pre><span class="k">pub</span> <span class="k">fn</span> <span class="n">spawn</span><span class="o"><</span><span class="n">F</span><span class="p">,</span> <span class="n">T</span><span class="o">></span><span class="p">(</span><span class="n">f</span><span class="p">:</span> <span class="n">F</span><span class="p">)</span> <span class="k">-></span> <span class="n">JoinHandle</span><span class="o"><</span><span class="n">T</span><span class="o">></span>
<span class="k">where</span>
<span class="n">F</span><span class="p">:</span> <span class="nb">Send</span> <span class="o">+</span> <span class="nv">'static</span> <span class="o">+</span> <span class="nf">FnOnce</span><span class="p">()</span> <span class="k">-></span> <span class="n">T</span><span class="p">,</span>
<span class="n">T</span><span class="p">:</span> <span class="nb">Send</span> <span class="o">+</span> <span class="nv">'static</span>
</pre></td></tr></tbody></table></code></pre></figure>
<div class="join"></div>
<p>In words, <code class="language-plaintext highlighter-rouge">spawn</code> is a generic function with one argument and two type
parameters:</p>
<ul>
<li>the type <code class="language-plaintext highlighter-rouge">F</code> can be any function/closure that returns a <code class="language-plaintext highlighter-rouge">T</code>
(<code class="language-plaintext highlighter-rouge">FnOnce() -> T</code>), can be safely transferred between threads
(<code class="language-plaintext highlighter-rouge">Send</code>) and contains no short-lived references (<code class="language-plaintext highlighter-rouge">'static</code>),</li>
<li>the type <code class="language-plaintext highlighter-rouge">T</code> can be any type at all, as long as it can be transferred
between threads and doesn’t contain short-lived references.</li>
</ul>
<p>The <a href="http://doc.rust-lang.org/std/thread/struct.JoinHandle.html"><code class="language-plaintext highlighter-rouge">JoinHandle<T></code></a> allows for retrieving the <code class="language-plaintext highlighter-rouge">T</code> that <code class="language-plaintext highlighter-rouge">f</code>
returns, via its <a href="http://doc.rust-lang.org/std/thread/struct.JoinHandle.html#method.join"><code class="language-plaintext highlighter-rouge">join</code> method</a>.</p>
<p>Why each bound on <code class="language-plaintext highlighter-rouge">F</code>? Well, <code class="language-plaintext highlighter-rouge">f</code> needs to be a function/callable of some
sort, so we need one of the <code class="language-plaintext highlighter-rouge">Fn*</code> closure traits, and <code class="language-plaintext highlighter-rouge">spawn</code> will
just run the closure on a new thread, and run it exactly once, so
using <a href="http://doc.rust-lang.org/std/ops/trait.FnOnce.html"><code class="language-plaintext highlighter-rouge">FnOnce</code></a> gives callers of <code class="language-plaintext highlighter-rouge">spawn</code> the most power.</p>
<p>The closure is run on a new thread independent of the parent. There’s
absolutely no guarantees or relationships between how long the new
thread runs for, and how long the stack frames of the parent last, so
the new thread cannot have any references to the parent’s stack. This
is the <code class="language-plaintext highlighter-rouge">'static</code> requirement: only data that can live for arbitrary
long can be passed via the new closure.</p>
<p>The last bound is <a href="http://doc.rust-lang.org/std/marker/trait.Send.html"><code class="language-plaintext highlighter-rouge">Send</code></a>: the closure is executed on a new thread, so
it definitely needs to be safe to transfer to that new thread. This is
what <code class="language-plaintext highlighter-rouge">Send</code> guarantees.</p>
<p>The return type <code class="language-plaintext highlighter-rouge">T</code> can be anything, as long as it can be transferred
from the child thread back to the parent (<code class="language-plaintext highlighter-rouge">Send</code>) and as long as it is
also independent of any stack frames (<code class="language-plaintext highlighter-rouge">'static</code>).</p>
<p>This all sounds fine and dandy. But there’s one hitch: the <code class="language-plaintext highlighter-rouge">Send</code>
trait is defined entirely<sup id="fnref:almost" role="doc-noteref"><a href="#fn:almost" class="footnote" rel="footnote">0</a></sup> in the standard library. The
compiler doesn’t know anything about it. But, somehow, even the purely
compiler-internal types constructed for closures can implement it.</p>
<h2 id="witchcraft">Witchcraft?</h2>
<p>The main protagonist here is OIBIT.</p>
<p>Ok, fine, it’s really default impls, but OIBIT is more fun to say.</p>
<p>Previously, <code class="language-plaintext highlighter-rouge">Send</code> was a super-special compiler built-in trait with
powers unseen by human eyes. Then, everything changed when the
<a href="https://github.com/rust-lang/rfcs/blob/master/text/0019-opt-in-builtin-traits.md">opt-in built-in traits (OIBIT)</a> nation attacked. This
introduced the concept of <a href="https://github.com/rust-lang/rfcs/blob/master/text/0019-opt-in-builtin-traits.md#default-and-negative-impls">a “default impl”</a> for marker
traits (traits that don’t contain any items). A <em>default
implementation</em> for <code class="language-plaintext highlighter-rouge">Trait</code> means that <code class="language-plaintext highlighter-rouge">Trait</code> will automatically be
implemented by aggregate types where all (if any) the other types they
contain also implement <code class="language-plaintext highlighter-rouge">Trait</code>.</p>
<p>Syntactically, it looks like:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
</pre></td><td class="code"><pre><span class="k">impl</span> <span class="n">Trait</span> <span class="k">for</span> <span class="o">..</span> <span class="p">{}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>In terms of functionality, suppose <code class="language-plaintext highlighter-rouge">u8</code> and <code class="language-plaintext highlighter-rouge">String</code> implement <code class="language-plaintext highlighter-rouge">Trait</code>
but <code class="language-plaintext highlighter-rouge">i16</code> doesn’t,</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="code"><pre><span class="c">// implement `Trait`</span>
<span class="k">enum</span> <span class="n">Good</span> <span class="p">{</span> <span class="n">A</span><span class="p">,</span> <span class="n">B</span> <span class="p">}</span>
<span class="k">struct</span> <span class="n">Excellent</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">Wonderful</span> <span class="p">{</span> <span class="n">x</span><span class="p">:</span> <span class="nb">u8</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">String</span> <span class="p">}</span>
<span class="k">type</span> <span class="n">Splendid</span> <span class="o">=</span> <span class="nb">Option</span><span class="o"><</span><span class="nb">u8</span><span class="o">></span><span class="p">;</span>
<span class="k">type</span> <span class="n">Brilliant</span> <span class="o">=</span> <span class="p">(</span><span class="nb">u8</span><span class="p">,</span> <span class="nb">String</span><span class="p">);</span>
<span class="c">// don't implement `Trait`</span>
<span class="k">enum</span> <span class="n">Bad</span> <span class="p">{</span>
<span class="nf">X</span><span class="p">(</span><span class="nb">i16</span><span class="p">),</span>
<span class="nf">Y</span><span class="p">(</span><span class="nb">u8</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">struct</span> <span class="n">Poor</span> <span class="p">{</span> <span class="n">x</span><span class="p">:</span> <span class="nb">i16</span> <span class="p">}</span>
<span class="k">type</span> <span class="n">Subpar</span> <span class="o">=</span> <span class="nb">Option</span><span class="o"><</span><span class="nb">i16</span><span class="o">></span><span class="p">;</span>
<span class="k">type</span> <span class="n">Underwhelming</span> <span class="o">=</span> <span class="p">(</span><span class="nb">String</span><span class="p">,</span> <span class="nb">i16</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>This rule applies to closures too: if a closure only captures things
that implement <code class="language-plaintext highlighter-rouge">Trait</code>, it is a struct similar to <code class="language-plaintext highlighter-rouge">Wonderful</code>, so the
(implicit) closure type implements <code class="language-plaintext highlighter-rouge">Trait</code> too.</p>
<p>The <code class="language-plaintext highlighter-rouge">Send</code> trait has one of these nifty default implementations, so
benefits from all that machinery, and it’s how closures can be used
with <code class="language-plaintext highlighter-rouge">Send</code>. To demonstrate specifically:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
</pre></td><td class="code"><pre><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">rc</span><span class="p">::</span><span class="nb">Rc</span><span class="p">;</span>
<span class="c">// can only be used with `Send` types.</span>
<span class="k">fn</span> <span class="n">check_send</span><span class="o"><</span><span class="n">T</span><span class="p">:</span> <span class="nb">Send</span><span class="o">></span><span class="p">(</span><span class="mi">_</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">let</span> <span class="n">vec</span><span class="p">:</span> <span class="nb">Vec</span><span class="o"><</span><span class="nb">Option</span><span class="o"><</span><span class="nb">String</span><span class="o">>></span> <span class="o">=</span> <span class="nd">vec!</span><span class="p">[</span><span class="nb">None</span><span class="p">,</span> <span class="nb">None</span><span class="p">,</span> <span class="nb">None</span><span class="p">];</span>
<span class="k">let</span> <span class="n">f</span> <span class="o">=</span> <span class="k">move</span> <span class="p">||</span> <span class="p">{</span>
<span class="k">let</span> <span class="mi">_</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">vec</span><span class="p">);</span> <span class="c">// make sure they're captured</span>
<span class="p">};</span>
<span class="nf">check_send</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>
<span class="k">let</span> <span class="n">pointer</span><span class="p">:</span> <span class="nb">Rc</span><span class="o"><</span><span class="nb">i32</span><span class="o">></span> <span class="o">=</span> <span class="nn">Rc</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="k">let</span> <span class="n">g</span> <span class="o">=</span> <span class="k">move</span> <span class="p">||</span> <span class="p">{</span>
<span class="k">let</span> <span class="mi">_</span> <span class="o">=</span> <span class="n">pointer</span><span class="p">;</span> <span class="c">// make sure it is captured</span>
<span class="p">};</span>
<span class="nf">check_send</span><span class="p">(</span><span class="n">g</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>This fails to compile, but in a particular way:</p>
<figure class="highlight"><pre><code class="language-text" data-lang="text"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="code"><pre>...:18:1: 18:11 error: the trait `core::marker::Send` is not implemented for the type `alloc::rc::Rc<i32>` [E0277]
...:18 check_send(g);
^~~~~~~~~~
...:18:1: 18:11 note: `alloc::rc::Rc<i32>` cannot be sent between threads safely
...:18 check_send(g);
^~~~~~~~~~
</pre></td></tr></tbody></table></code></pre></figure>
<p>Both <code class="language-plaintext highlighter-rouge">i32</code> and <code class="language-plaintext highlighter-rouge">Vec<Option<String>></code> are <code class="language-plaintext highlighter-rouge">Send</code> so the <code class="language-plaintext highlighter-rouge">f</code> closure is
<code class="language-plaintext highlighter-rouge">Send</code> and <code class="language-plaintext highlighter-rouge">check_send(f)</code> compiles fine. On the other hand,
<code class="language-plaintext highlighter-rouge">Rc</code> is not <code class="language-plaintext highlighter-rouge">Send</code>,
so <code class="language-plaintext highlighter-rouge">g</code> doesn’t get the default implementation.</p>
<p>The <a href="http://doc.rust-lang.org/std/rc/"><code class="language-plaintext highlighter-rouge">Rc</code> type</a> is a reference counted pointer where reference count
adjustments happen non-atomically. This means that manipulating them
from multiple threads will be a data-race: undefined behaviour. An
<code class="language-plaintext highlighter-rouge">Rc</code> handle can’t transfer between threads due to this risk, and this
is statically guaranteed since the type doesn’t implement <code class="language-plaintext highlighter-rouge">Send</code>. (The
<a href="http://doc.rust-lang.org/std/sync/struct.Arc.html"><code class="language-plaintext highlighter-rouge">Arc</code> type</a> is a thread-safe version, using atomic reference
counting.)</p>
<h3 id="opting-out-or-in">Opting out or in</h3>
<p>It’s nice that <code class="language-plaintext highlighter-rouge">Send</code> is automatically implemented for a type when the
contents are entirely <code class="language-plaintext highlighter-rouge">Send</code>, but this isn’t always perfect: it is
possible to use <code class="language-plaintext highlighter-rouge">unsafe</code> code make a thread-unsafe type, even if it
is composed entirely of primitives. Similarly, it is possible to have
a type composed of non-<code class="language-plaintext highlighter-rouge">Send</code> internals which is actually thread-safe,
by imposing extra constraints on how it manipulates data.</p>
<p>Hence, two important parts of the OIBIT proposal are making it
possible to fill in a “gap” that the default impl doesn’t cover, and
also forcibly opt out of the default implementation. The former is
so interesting: itcan be performed with normal <code class="language-plaintext highlighter-rouge">impl</code>, as one would
write for the trait if it didn’t have default implementation.</p>
<p>On the other hand, the opt-out is new and different, it is done via
<em>negative implementations</em>: to opt out of <code class="language-plaintext highlighter-rouge">Trait</code>, implement <code class="language-plaintext highlighter-rouge">!Trait</code>.
For example, the thread-unsafe <code class="language-plaintext highlighter-rouge">Rc<T></code> type has</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
</pre></td><td class="code"><pre><span class="k">impl</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="o">!</span><span class="nb">Send</span> <span class="k">for</span> <span class="nb">Rc</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="p">{}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Having to opt-out might seem error-prone, but the only way a type with
individually-thread-safe contents can be thread unsafe in aggregate is
if there is some <code class="language-plaintext highlighter-rouge">unsafe</code> code somewhere. Considering thread-safety is
an important part of writing <code class="language-plaintext highlighter-rouge">unsafe</code> code in Rust, and if you’re
building safe abstractions, you need to be careful to have the right
trait implementations.</p>
<p>However, it’s not as scary as it sounds. Safety is still the
(sometimes conservative) default in the <em>vast</em> majority<sup id="fnref:vast" role="doc-noteref"><a href="#fn:vast" class="footnote" rel="footnote">1</a></sup> of
low-level cases. The standard library has negative implementations of
<code class="language-plaintext highlighter-rouge">Send</code> for the raw pointer types <code class="language-plaintext highlighter-rouge">*const T</code> and <code class="language-plaintext highlighter-rouge">*mut T</code>, so data
structures containing them need to explicitly opt <em>in</em> if they are in
fact thread-safe. (<code class="language-plaintext highlighter-rouge">Rc</code> actually falls into this category, so the
implementation above isn’t strictly required.)</p>
<p>There’s one slight hole here: the type of a closure is unnameable, so
there’s no way to opt-out of a defaulted trait like <code class="language-plaintext highlighter-rouge">Send</code>. That is, a
closure could capture only <code class="language-plaintext highlighter-rouge">Send</code> types, but use <code class="language-plaintext highlighter-rouge">unsafe</code> code to be
thread unsafe. This is somewhat unfortunate, but closures are not
designed to be the abstraction boundary for safety. I’d personally try
to wrap the unsafety up into a real type with the correct trait
(un)implementations to help the compiler help me as much as
possible. Also, as I said above, this rarely happens, due to the
negative implementations in the standard library; I imagine the the
most common way that isn’t necessarily caught by that is calling
non-reentrant FFI function, but wrapping C libraries in a safe
interface is <a href="http://blog.rust-lang.org/2015/04/24/Rust-Once-Run-Everywhere.html#safe-abstractions">a nice idea</a>.</p>
<p>Anyway, returning to the opt in mechanism, what is stopping us from wrapping a
thread-unsafe type (non-<code class="language-plaintext highlighter-rouge">Send</code>) in a new struct and implementing
<code class="language-plaintext highlighter-rouge">Send</code> for it? Something like</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="code"><pre><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">rc</span><span class="p">::</span><span class="nb">Rc</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">Trick</span> <span class="p">{</span>
<span class="n">x</span><span class="p">:</span> <span class="nb">Rc</span><span class="o"><</span><span class="nb">i32</span><span class="o">></span>
<span class="p">}</span>
<span class="k">impl</span> <span class="nb">Send</span> <span class="k">for</span> <span class="n">Trick</span> <span class="p">{}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>If this worked (spoilers: it doesn’t), we’ve “asserted” that <code class="language-plaintext highlighter-rouge">Trick</code>
is thread-safe despite it containing a thread-unsafe type, <em>without</em>
mentioning <code class="language-plaintext highlighter-rouge">unsafe</code> anywhere. This would introduce a very real risk of
undefined behaviour via data races.</p>
<h3 id="unsafe-traits">Unsafe traits</h3>
<p>Fortunately, the compiler spits out an error:</p>
<figure class="highlight"><pre><code class="language-text" data-lang="text"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre>...:6:1: 6:23 error: the trait `core::marker::Send` requires an `unsafe impl` declaration [E0200]
...:6 impl Send for Trick {}
^~~~~~~~~~~~~~~~~~~~~~
</pre></td></tr></tbody></table></code></pre></figure>
<p>Ah! So it’s telling us that <code class="language-plaintext highlighter-rouge">unsafe</code> is in fact required. This version
of the <code class="language-plaintext highlighter-rouge">impl</code> compiles:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
</pre></td><td class="code"><pre><span class="k">unsafe</span> <span class="k">impl</span> <span class="nb">Send</span> <span class="k">for</span> <span class="n">Trick</span> <span class="p">{}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>This is because <code class="language-plaintext highlighter-rouge">Send</code> is <a href="https://github.com/rust-lang/rust/blob/7cb9914fceaeaa6a39add43d3da15bb6e1d191f6/src/libcore/marker.rs#L38">declared</a> as <code class="language-plaintext highlighter-rouge">unsafe</code>:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
</pre></td><td class="code"><pre><span class="k">pub</span> <span class="k">unsafe</span> <span class="k">trait</span> <span class="nb">Send</span> <span class="p">{</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>An <em>unsafe trait</em> is unsafe to implement, but not to use. It is
designed to allow representing classes of types with absolute
guarantees, that users of the trait can rely on even if it risks
memory unsafety: it is up to the implementers to ensure they satisfy
the requirements. It’s not unsafe to <em>not</em> implement an unsafe trait,
so negative implementations don’t need <code class="language-plaintext highlighter-rouge">unsafe</code>.</p>
<p>The guarantee for <code class="language-plaintext highlighter-rouge">Send</code> is thread-safety: a type should only
implement <code class="language-plaintext highlighter-rouge">Send</code> if it is absolutely sure.</p>
<h3>⚠⚠⚠</h3>
<p>Unsafe traits are great and libraries should definitely use them where
they make sense. However, the OIBIT functionality (default and
negative implementations) are still unstable and hence only usable
with a nightly compiler behind the <code class="language-plaintext highlighter-rouge">optin_builtin_traits</code>
feature. There’s some details around them that are unclear from the
RFC and even the implementation, especially how they interact with
primitives, so I could imagine some tweaks/breaking changes in future.</p>
<p>Unfortunately, this means that the only way to opt-out of a defaulted
trait with a stable compiler is to store a non-implementing type. The
slickest way is to use <a href="http://doc.rust-lang.org/std/marker/struct.PhantomData.html"><code class="language-plaintext highlighter-rouge">PhantomData<T></code></a>, which is a
zero-sized type (so no runtime effect) that behaves as if it stores
its type argument. For opting out of <code class="language-plaintext highlighter-rouge">Send</code>, a field of type
<code class="language-plaintext highlighter-rouge">PhantomData<*const ()></code> works. (However, as discussed above, this is
rarely needed for <code class="language-plaintext highlighter-rouge">Send</code> and <code class="language-plaintext highlighter-rouge">Sync</code>, which are the only two defaulted
traits one can possibly use on a stable compiler.)</p>
<figure class="image ">
<div class="image-positioner">
<a href="https://www.flickr.com/photos/tm-tm/2634203419/">
<img src="unstable-small.jpg" alt="a photo of a sign saying: danger, unstable cliff" />
</a>
</div>
</figure>
<h2 id="calling-concurrently">Calling Concurrently</h2>
<p>We’ve been focusing on <code class="language-plaintext highlighter-rouge">Send</code> above, but there’s another trait that’s
important for thread-safety: <a href="http://doc.rust-lang.org/std/marker/trait.Sync.html"><code class="language-plaintext highlighter-rouge">Sync</code></a>. This trait represents
values that are safe to be <em>accessed</em> by multiple threads at once,
that is, sharing.</p>
<p>It is sometimes useful to call a single function on multiple threads,
like a parallel map, so it would be pretty great if this was
possible…</p>
<p>Just like <code class="language-plaintext highlighter-rouge">Send</code>, <code class="language-plaintext highlighter-rouge">Sync</code> is a defaulted trait, and so works well with
closures too. A closure that only captures thread-shareable values
(like a string) is also thread-shareable:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="code"><pre><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="nb">Arc</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="n">thread</span><span class="p">;</span>
<span class="k">fn</span> <span class="n">upto</span><span class="o"><</span><span class="n">F</span><span class="o">></span><span class="p">(</span><span class="n">n</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span> <span class="n">func</span><span class="p">:</span> <span class="n">F</span><span class="p">)</span>
<span class="k">where</span> <span class="n">F</span><span class="p">:</span> <span class="nb">Send</span> <span class="o">+</span> <span class="nv">'static</span> <span class="o">+</span> <span class="nf">Fn</span><span class="p">(</span><span class="nb">usize</span><span class="p">)</span> <span class="o">+</span> <span class="n">Sync</span>
<span class="p">{</span>
<span class="k">let</span> <span class="n">func</span> <span class="o">=</span> <span class="nn">Arc</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">func</span><span class="p">);</span>
<span class="k">for</span> <span class="n">i</span> <span class="n">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">n</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">f</span> <span class="o">=</span> <span class="n">func</span><span class="nf">.clone</span><span class="p">();</span>
<span class="nn">thread</span><span class="p">::</span><span class="nf">spawn</span><span class="p">(</span><span class="k">move</span> <span class="p">||</span> <span class="nf">f</span><span class="p">(</span><span class="n">i</span><span class="p">));</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">message</span> <span class="o">=</span> <span class="s">"hello"</span><span class="p">;</span>
<span class="nf">upto</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="p">|</span><span class="n">i</span><span class="p">|</span> <span class="nd">println!</span><span class="p">(</span><span class="s">"thread #{}: {}"</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">message</span><span class="p">));</span>
<span class="c">// as above, don't let `main` finish</span>
<span class="nn">thread</span><span class="p">::</span><span class="nf">sleep_ms</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<div class="join"></div>
<p>The output sometimes looks like:</p>
<figure class="highlight"><pre><code class="language-text" data-lang="text"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre>thread #0: hello
thread #7: hello
thread #2: hello
thread #9: hello
thread #1: hello
thread #4: hello
thread #3: hello
thread #6: hello
thread #8: hello
thread #5: hello
</pre></td></tr></tbody></table></code></pre></figure>
<p>The <code class="language-plaintext highlighter-rouge">Send</code> and <code class="language-plaintext highlighter-rouge">'static</code> bounds are “boring”, they’re just required
due to the implementation details of using <code class="language-plaintext highlighter-rouge">spawn</code> and <code class="language-plaintext highlighter-rouge">Arc</code>, it’s the
<code class="language-plaintext highlighter-rouge">Fn</code> and <code class="language-plaintext highlighter-rouge">Sync</code> bounds that are fundamental to this behaviour.</p>
<p>We’re calling the function from multiple threads at once, which means
accessing the closure’s environment concurrently, so the <code class="language-plaintext highlighter-rouge">Sync</code> bound
is necessary to guarantee safety. Also, by nature, sharing across
threads means we’ve only got access to the closure via an <code class="language-plaintext highlighter-rouge">&</code>
reference, so we need to be able to call it via that sort of reference
and <a href="http://doc.rust-lang.org/std/ops/trait.Fn.html"><code class="language-plaintext highlighter-rouge">Fn</code></a> is exactly the right trait for
it. (<a href="/blog/2015/05/finding-closure-in-rust/"><em>Finding Closure in Rust</em></a> looks at the three closure
traits more closely.)</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="https://users.rust-lang.org/t/defaulting-to-thread-safety-closures-and-concurrency/1583">users</a></li>
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/37e6w2/defaulting_to_threadsafety_closures_and/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Defaulting%20to%20Thread-Safety:%20Closures%20and%20Concurrency%20%23rustlang&url=https://huonw.github.io/blog/2015/05/defaulting-to-thread-safety/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/05/defaulting-to-thread-safety/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/05/defaulting-to-thread-safety/&title=Defaulting%20to%20Thread-Safety:%20Closures%20and%20Concurrency" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:almost" role="doc-endnote">
<p>They’re currently still “lang-items” (i.e. known to the
compiler), but Niko Matsakis tells me “it’s because compiler
needs refactoring”, i.e. an implementation detail. <a href="#fnref:almost" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:vast" role="doc-endnote">
<p>There’s only 3 negative implementations (approximated by
matches for <code class="language-plaintext highlighter-rouge">/\bimpl.*!/</code>) in my <code class="language-plaintext highlighter-rouge">projects</code> folder, which
contains 100-200 Rust libraries over 1.3 million lines,
written by me and others, mainly others. All three are for
<code class="language-plaintext highlighter-rouge">Send</code>’s sibling <a href="http://doc.rust-lang.org/std/marker/trait.Sync.html"><code class="language-plaintext highlighter-rouge">Sync</code></a> which uses exactly the same
default/negative impl set-up, and, in fact, one of them is on
a type that is already not <code class="language-plaintext highlighter-rouge">Sync</code> (it has non-<code class="language-plaintext highlighter-rouge">Sync</code>
contents).</p>
<p>(That said, there’s always the possibility that there <em>should</em> be
a lot more negative impls, and there’s a large number of broken
libraries in the ecosystem… however, this would surprise me greatly.) <a href="#fnref:vast" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2015/05/rust-1.0-in-numbersRust 1.0 in Numbers2015-05-20T00:00:00+00:002015-05-20T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p><a href="http://blog.rust-lang.org/2015/05/15/Rust-1.0.html">Rust 1.0</a> was
made with<sup id="fnref:methodology" role="doc-noteref"><a href="#fn:methodology" class="footnote" rel="footnote">0</a></sup>…</p>
<style>
.number { text-align: right; }
table { max-width: 600px; border-spacing: 0 0.4em; border-collapse: separate; }
td { vertical-align: top; }
</style>
<table><tr><td class="number">1</td><td class="thing">stable release
</td></tr><tr><td class="number">2</td><td class="thing">‘Rust’ compilers
</td></tr><tr><td class="number">3</td><td class="thing"><a href="https://github.com/graydon/bors">bots</a> <a href="https://github.com/nrc/highfive">on</a> <a href="https://github.com/barosl/homu">Github</a>
</td></tr><tr><td class="number">4</td><td class="thing">years of <a href="https://en.wikipedia.org/wiki/Bootstrapping_%28compilers%29">bootstrapping</a> (<a href="http://github.com/rust-lang/rust/commit/7b95b5c033435e0b8902c3f3afb14c80e04fee83"><code class="language-plaintext highlighter-rouge">7b95b5c</code></a> is the innocuous commit of the first snapshot)
</td></tr><tr><td class="number">5</td><td class="thing"><a href="http://github.com/rust-lang/rust/commit/c01efc669f09508b55eced32d3c88702578a7c3e">years of git</a>
</td></tr><tr><td class="number">~9</td><td class="thing">years total
</td></tr><tr><td class="number">12</td><td class="thing"><code class="language-plaintext highlighter-rouge">0.x</code> releases
</td></tr><tr><td class="number">~28</td><td class="thing"><a href="https://users.rust-lang.org/t/a-list-of-rust-1-0-launch-meetups/1171">launch meetups</a>
</td></tr><tr><td class="number">~53</td><td class="thing">irc channels starting with <code class="language-plaintext highlighter-rouge">#rust</code> on irc.mozilla.org
</td></tr><tr><td class="number">80</td><td class="thing"><a href="http://this-week-in-rust.org/">TWiRs</a>
</td></tr><tr><td class="number">156</td><td class="thing">people with project-flair on <a href="http://reddit.com/r/rust">/r/rust</a>
</td></tr><tr><td class="number">~174</td><td class="thing"><a href="https://github.com/rust-lang/rfcs/">RFCs</a>
</td></tr><tr><td class="number">302</td><td class="thing">comments on the most-commented pull request, <a href="https://github.com/rust-lang/rust/pull/12081">#12081</a>
</td></tr><tr><td class="number">312</td><td class="thing"><a href="https://github.com/rust-lang/rust/blob/1.0.0/src/snapshots.txt">compiler snapshots</a> for bootstrapping
</td></tr><tr><td class="number">363</td><td class="thing">minutes spent hitting Github’s API to get the comment numbers here
</td></tr><tr><td class="number">365</td><td class="thing">revisions to <a href="https://en.wikipedia.org/wiki/Rust_%28programming_language%29">the Wikipedia page</a>
</td></tr><tr><td class="number">406</td><td class="thing">comments on the most (or maybe, least) popular RFC, <a href="https://github.com/rust-lang/rfcs/pull/517">#517</a>
</td></tr><tr><td class="number">632</td><td class="thing">tweets from <a href="https://twitter.com/rustlang">@rustlang</a>
</td></tr><tr><td class="number">~656</td><td class="thing">gigabytes of Rust downloaded from static.rust-lang.org on May 15 and 16
</td></tr><tr><td class="number">905</td><td class="thing">users on <a href="https://users.rust-lang.org">users.rust-lang.org</a>
</td></tr><tr><td class="number">1,015</td><td class="thing">contributors (according to <code class="language-plaintext highlighter-rouge">AUTHORS.txt</code>)
</td></tr><tr><td class="number">1,034</td><td class="thing">contributors (according to <code class="language-plaintext highlighter-rouge">git shortlog -sn</code>)
</td></tr><tr><td class="number">1,286</td><td class="thing">users on <a href="https://internals.rust-lang.org">internals.rust-lang.org</a>
</td></tr><tr><td class="number">2,076</td><td class="thing">crates on <a href="https://crates.io">crates.io</a>
</td></tr><tr><td class="number">2,096</td><td class="thing">questions <a href="http://stackoverflow.com/questions/tagged/rust">tagged <code class="language-plaintext highlighter-rouge">[rust]</code> on Stack Overflow</a>
</td></tr><tr><td class="number">~2,200</td><td class="thing">forks of <a href="https://github.com/rust-lang/rust">rust-lang/rust</a>
</td></tr><tr><td class="number">5,552</td><td class="thing"><code class="language-plaintext highlighter-rouge">*.rs</code> files
</td></tr><tr><td class="number">6,262</td><td class="thing">merge commits from <a href="https://github.com/bors">@bors</a>
</td></tr><tr><td class="number">~7,000</td><td class="thing"><a href="https://github.com/rust-lang/rust/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Amerged+">merged pull requests</a> to rust-lang/rust (according to Github)
</td></tr><tr><td class="number">~7,617</td><td class="thing"><a href="https://github.com/search?utf8=%E2%9C%93&q=language%3Arust&type=Repositories&ref=searchresults">repositories in Rust on Github</a>
</td></tr><tr><td class="number">10,246</td><td class="thing">successful <code class="language-plaintext highlighter-rouge">cargo publish</code>s to crates.io
</td></tr><tr><td class="number">10,500</td><td class="thing">subscribers to /r/rust
</td></tr><tr><td class="number">~11,000</td><td class="thing"><a href="https://github.com/rust-lang/rust/stargazers">stars</a> on Github
</td></tr><tr><td class="number">~11,600</td><td class="thing"><a href="https://github.com/rust-lang/rust/issues?q=is%3Aclosed+is%3Aissue">closed issues</a>
</td></tr><tr><td class="number">11,669</td><td class="thing"><a href="https://github.com/brson/rust-dev-archives">emails</a> sent to the (now unused) rust-dev mailing list
</td></tr><tr><td class="number">~11,900</td><td class="thing"><a href="https://github.com/rust-lang/rust/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aclosed">total pull requests</a>
</td></tr><tr><td class="number">12,012</td><td class="thing">views of the release announcement on release day from Hacker News, the largest single source
</td></tr><tr><td class="number">~16,078</td><td class="thing">comments on RFCs
</td></tr><tr><td class="number">~18,518</td><td class="thing">comments on commits
</td></tr><tr><td class="number">22,293</td><td class="thing">words in the largest single page of documentation, <a href="https://doc.rust-lang.org/reference.html">the reference</a> (according <code class="language-plaintext highlighter-rouge">wc -w</code>)
</td></tr><tr><td class="number">22,693</td><td class="thing">views of rust-lang/rust on release day
</td></tr><tr><td class="number">41,373</td><td class="thing">commits
</td></tr><tr><td class="number">45,710</td><td class="thing">total pageviews of the release announcement on release day
</td></tr><tr><td class="number">~50,936</td><td class="thing">comments on issues in rust-lang/rust
</td></tr><tr><td class="number">58,163</td><td class="thing">backticks <code>`</code>
</td></tr><tr><td class="number">61,563</td><td class="thing">lines of not-Rust (according to <code class="language-plaintext highlighter-rouge">cloc</code>, not including git submodules)
</td></tr><tr><td class="number">~65,665</td><td class="thing">comments on pull requests (we’re a talkative bunch!)
</td></tr><tr><td class="number">67,629</td><td class="thing">words in <a href="https://doc.rust-lang.org/book">the Book</a>
</td></tr><tr><td class="number">~106,000</td><td class="thing">results returned by a Google search for ‘rustlang’
</td></tr><tr><td class="number">169,525</td><td class="thing">lines of Rust in <code class="language-plaintext highlighter-rouge">src/test</code>
</td></tr><tr><td class="number">182,267</td><td class="thing">lines added/removed by the single largest commit <code class="language-plaintext highlighter-rouge">5f066e0</code> (according to <code class="language-plaintext highlighter-rouge">git show --shortstat</code>)
</td></tr><tr><td class="number">332,721</td><td class="thing">lines of Rust in the libraries & compiler
</td></tr><tr><td class="number">~1,000,000</td><td class="thing">messages sent to <code class="language-plaintext highlighter-rouge">#rust</code> over the year before the release (according to my IRC logs)
</td></tr><tr><td class="number">2,070,539</td><td class="thing">downloads from crates.io
</td></tr><tr><td class="number">3,575,149</td><td class="thing">total deleted lines over the whole history (according to <code class="language-plaintext highlighter-rouge">git log --shortstat</code>)
</td></tr><tr><td class="number">4,263,472</td><td class="thing">total added lines
</td></tr><tr><td class="number">27,466,060</td><td class="thing">bytes of files in the 1.0 source tree
</td></tr><tr><td class="number">∞</td><td class="thing">code breakage in the lead up
</td></tr></table>
<p>Happy 1.0!</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="https://users.rust-lang.org/t/rust-1-0-in-numbers/1476">users</a></li>
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/36m7xa/rust_10_in_numbers/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Rust%201.0%20in%20Numbers%20%23rustlang&url=https://huonw.github.io/blog/2015/05/rust-1.0-in-numbers/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/05/rust-1.0-in-numbers/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/05/rust-1.0-in-numbers/&title=Rust%201.0%20in%20Numbers" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:methodology" role="doc-endnote">
<p>I’ve tried to get the numbers that apply leading up to
the 1.0 release exactly, e.g. using the exact tag of
the release for code related things, and generally
trying to show the value as it was on/before
May 15. I’ve put a ~ on particularly dubious numbers
and those for which I can’t get the true 1.0 value,
but most probably have too many significant figures
anyway: this definitely isn’t meant to be at all
scientific. <a href="#fnref:methodology" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2015/05/finding-closure-in-rustFinding Closure in Rust2015-05-08T00:00:00+00:002015-05-08T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>Have you ever used an <a href="http://doc.rust-lang.org/std/iter/trait.Iterator.html">iterator adapter</a> in <a href="http://rust-lang.org">Rust</a>?
Called a method on <a href="http://doc.rust-lang.org/std/option/enum.Option.html"><code class="language-plaintext highlighter-rouge">Option</code></a>? <a href="http://doc.rust-lang.org/std/thread/fn.spawn.html">Spawned</a> a thread?
You’ve almost certainly used a <a href="https://en.wikipedia.org/wiki/Closure_%28computer_programming%29">closure</a>. The design in Rust may seem
a little complicated, but it slides right into Rust’s normal ownership
model so let’s reinvent it from scratch.</p>
<p>The new design was introduced in <a href="https://github.com/rust-lang/rfcs/blob/master/text/0114-closures.md">RFC 114</a>, moving Rust to a
model for closures similar to C++11’s. The design builds on Rust’s
standard trait system to allow for allocation-less
statically-dispatched closures, but also giving the choice to opt-in
to type-erasure and dynamic dispatch and the benefits that brings. It
incorporates elements of inference that “just work” by ensuring that
ownership works out.</p>
<blockquote>
<p>Steve Klabnik has written
<a href="http://doc.rust-lang.org/book/closures.html">some docs on Rust’s closures</a> for the official
documentation. I’ve explicitly avoided reading it so far because
I’ve always wanted to write this, and I think it’s better to give a
totally independent explanation while I have the chance. If
something is confusing here, maybe they help clarify.</p>
</blockquote>
<h2 id="whats-a-closure">What’s a closure?</h2>
<p>In a sentence: a closure is a function that can directly use variables
from the scope in which it is defined. This is often described as the
closure <em>closing over</em> or <em>capturing</em> variables (the
<em>captures</em>). Collectively, the variables are called the <em>environment</em>.</p>
<p>Syntactically, a closure in Rust is an anonymous function<sup id="fnref:anon" role="doc-noteref"><a href="#fn:anon" class="footnote" rel="footnote">0</a></sup> value
defined a little like Ruby, with pipes: <code class="language-plaintext highlighter-rouge">|arguments...| body</code>. For
example, <code class="language-plaintext highlighter-rouge">|a, b| a + b</code> defines a closure that takes two arguments and
returns their sum. It’s just like a normal function declaration, with
more inference:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="code"><pre><span class="c">// function:</span>
<span class="k">fn</span> <span class="nf">foo</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="nb">i32</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-></span> <span class="nb">i32</span> <span class="p">{</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span> <span class="p">}</span>
<span class="c">// closure:</span>
<span class="p">|</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">|</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Just like a normal function, they can be called with parentheses:
<code class="language-plaintext highlighter-rouge">closure(arguments...)</code>.</p>
<p>To illustrate the capturing, this code snippet calls
<a href="http://doc.rust-lang.org/std/option/enum.Option.html#method.map"><code class="language-plaintext highlighter-rouge">map</code></a> on an <code class="language-plaintext highlighter-rouge">Option<i32></code>, which will call a closure on
the <code class="language-plaintext highlighter-rouge">i32</code> (if it exists) and create a new <code class="language-plaintext highlighter-rouge">Option</code> containing the
return value of the call.</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="code"><pre><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">option</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>
<span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
<span class="c">// explicit types:</span>
<span class="k">let</span> <span class="n">new</span><span class="p">:</span> <span class="nb">Option</span><span class="o"><</span><span class="nb">i32</span><span class="o">></span> <span class="o">=</span> <span class="n">option</span><span class="nf">.map</span><span class="p">(|</span><span class="n">val</span><span class="p">:</span> <span class="nb">i32</span><span class="p">|</span> <span class="k">-></span> <span class="nb">i32</span> <span class="p">{</span> <span class="n">val</span> <span class="o">+</span> <span class="n">x</span> <span class="p">});</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{:?}"</span><span class="p">,</span> <span class="n">new</span><span class="p">);</span> <span class="c">// Some(5)</span>
<span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
<span class="c">// inferred:</span>
<span class="k">let</span> <span class="n">new2</span> <span class="o">=</span> <span class="n">option</span><span class="nf">.map</span><span class="p">(|</span><span class="n">val</span><span class="p">|</span> <span class="n">val</span> <span class="o">*</span> <span class="n">y</span><span class="p">);</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{:?}"</span><span class="p">,</span> <span class="n">new2</span><span class="p">);</span> <span class="c">// Some(20)</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The closures are capturing the <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">y</code> variables, allowing them to
be used while mapping. (To be more convincing, imagine they were only
known at runtime, so that one couldn’t just write <code class="language-plaintext highlighter-rouge">val + 3</code> inside the
closure.)</p>
<h2 id="back-to-basics">Back to basics</h2>
<p>Now that we have the semantics in mind, take a step back and riddle me
this: how would one implement that sort of generic <code class="language-plaintext highlighter-rouge">map</code> if Rust
didn’t have closures?</p>
<p>The functionality of <code class="language-plaintext highlighter-rouge">Option::map</code> we’re trying to duplicate is (equivalently):</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="code"><pre><span class="k">fn</span> <span class="n">map</span><span class="o"><</span><span class="n">X</span><span class="p">,</span> <span class="n">Y</span><span class="o">></span><span class="p">(</span><span class="n">option</span><span class="p">:</span> <span class="nb">Option</span><span class="o"><</span><span class="n">X</span><span class="o">></span><span class="p">,</span> <span class="n">transformer</span><span class="p">:</span> <span class="o">...</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Option</span><span class="o"><</span><span class="n">Y</span><span class="o">></span> <span class="p">{</span>
<span class="k">match</span> <span class="n">option</span> <span class="p">{</span>
<span class="nf">Some</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">=></span> <span class="nf">Some</span><span class="p">(</span><span class="nf">transformer</span><span class="p">(</span><span class="n">x</span><span class="p">)),</span> <span class="c">// (closure syntax for now)</span>
<span class="nb">None</span> <span class="k">=></span> <span class="nb">None</span><span class="p">,</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>We need to fill in the <code class="language-plaintext highlighter-rouge">...</code> with something that transforms an <code class="language-plaintext highlighter-rouge">X</code> into
a <code class="language-plaintext highlighter-rouge">Y</code>. The biggest constraint for perfectly replacing <code class="language-plaintext highlighter-rouge">Option::map</code> is
that it needs to be generic in some way, so that it works with
absolutely any way we wish to do the transformation. In Rust, that
calls for a generic bounded by a trait.</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre><span class="k">fn</span> <span class="n">map</span><span class="o"><</span><span class="n">X</span><span class="p">,</span> <span class="n">Y</span><span class="p">,</span> <span class="n">T</span><span class="o">></span><span class="p">(</span><span class="n">option</span><span class="p">:</span> <span class="nb">Option</span><span class="o"><</span><span class="n">X</span><span class="o">></span><span class="p">,</span> <span class="n">transform</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Option</span><span class="o"><</span><span class="n">Y</span><span class="o">></span>
<span class="k">where</span> <span class="n">T</span><span class="p">:</span> <span class="cm">/* the trait */</span>
<span class="p">{</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>This trait needs to have a method that converts some specific type
into another. Hence there’ll have to be form of type parameters to
allow the exact types to be specified in generic bounds like
<code class="language-plaintext highlighter-rouge">map</code>. There’s two choices: generics in the trait definition (“input
type parameters”) and associated types (“output type parameters”). The
quoted names hint at the choices we should take: the type that gets
input into the transformation should be a generic in the trait, and
the type that is output by the transformation should be an associated
type.<sup id="fnref:assoc-vs-not" role="doc-noteref"><a href="#fn:assoc-vs-not" class="footnote" rel="footnote">1</a></sup></p>
<p>So, our trait looks something like:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Transform</span><span class="o"><</span><span class="n">Input</span><span class="o">></span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Output</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">transform</span><span class="p">(</span><span class="cm">/* self?? */</span><span class="p">,</span> <span class="n">input</span><span class="p">:</span> <span class="n">Input</span><span class="p">)</span> <span class="k">-></span> <span class="nn">Self</span><span class="p">::</span><span class="n">Output</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The last question is what sort of <code class="language-plaintext highlighter-rouge">self</code> (if any) the method should
take?</p>
<p>The transformation should be able to incorporate arbitrary information
beyond what is contained in <code class="language-plaintext highlighter-rouge">Input</code>. Without any <code class="language-plaintext highlighter-rouge">self</code> argument, the
method would look like <code class="language-plaintext highlighter-rouge">fn transform(input: Input) -> Self::Output</code>
and the operation could only depend on <code class="language-plaintext highlighter-rouge">Input</code> and global
variables (ick). So we do need one.</p>
<p>The most obvious options are by-reference <code class="language-plaintext highlighter-rouge">&self</code>,
by-mutable-reference <code class="language-plaintext highlighter-rouge">&mut self</code>, or by-value <code class="language-plaintext highlighter-rouge">self</code>. We want to allow
the users of <code class="language-plaintext highlighter-rouge">map</code> to have as much power as possible while still
enabling <code class="language-plaintext highlighter-rouge">map</code> to type-check. At a high-level <code class="language-plaintext highlighter-rouge">self</code> gives
<em>implementers</em> (i.e. the types users define to implement the trait)
the most flexibility, with <code class="language-plaintext highlighter-rouge">&mut self</code> next and <code class="language-plaintext highlighter-rouge">&self</code> the least
flexible. Conversely, <code class="language-plaintext highlighter-rouge">&self</code> gives <em>consumers</em> of the trait
(i.e. functions with generics bounded by the trait) the most
flexibility, and <code class="language-plaintext highlighter-rouge">self</code> the least.</p>
<table>
<thead>
<tr>
<th style="text-align: right"> </th>
<th><strong>Implementer</strong></th>
<th><strong>Consumer</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: right"><code class="language-plaintext highlighter-rouge">self</code></td>
<td>Can move out and mutate</td>
<td>Can only call method once</td>
</tr>
<tr>
<td style="text-align: right"><code>&mut self</code></td>
<td>Can’t move out, can mutate</td>
<td>Can call many times, only with unique access</td>
</tr>
<tr>
<td style="text-align: right"><code class="language-plaintext highlighter-rouge">&self</code></td>
<td>Can’t move out or mutate</td>
<td>Can call many times, with no restrictions</td>
</tr>
</tbody>
</table>
<div class="join"></div>
<p>(“Move out” and “mutate” in the implementer column are referring to data stored inside <code class="language-plaintext highlighter-rouge">self</code>.)</p>
<p>Choosing between them is a balance, we usually want to chose the
highest row of the table that still allows the consumers to do what
they need to do, as that allows the external implementers to do as
much as possible.</p>
<p>Starting at the top of that table: we can try <code class="language-plaintext highlighter-rouge">self</code>. This gives <code class="language-plaintext highlighter-rouge">fn
transform(self, input: Input) -> Self::Output</code>. The by-value <code class="language-plaintext highlighter-rouge">self</code>
will consume ownership, and hence <code class="language-plaintext highlighter-rouge">transform</code> can only be called
once. Fortunately, <code class="language-plaintext highlighter-rouge">map</code> only needs to do the transformation once, so
by-value <code class="language-plaintext highlighter-rouge">self</code> works perfectly.</p>
<p>In summary, our <code class="language-plaintext highlighter-rouge">map</code> and its trait look like:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Transform</span><span class="o"><</span><span class="n">Input</span><span class="o">></span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Output</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">transform</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">input</span><span class="p">:</span> <span class="n">Input</span><span class="p">)</span> <span class="k">-></span> <span class="nn">Self</span><span class="p">::</span><span class="n">Output</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="n">map</span><span class="o"><</span><span class="n">X</span><span class="p">,</span> <span class="n">Y</span><span class="p">,</span> <span class="n">T</span><span class="o">></span><span class="p">(</span><span class="n">option</span><span class="p">:</span> <span class="nb">Option</span><span class="o"><</span><span class="n">X</span><span class="o">></span><span class="p">,</span> <span class="n">transform</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Option</span><span class="o"><</span><span class="n">Y</span><span class="o">></span>
<span class="k">where</span> <span class="n">T</span><span class="p">:</span> <span class="n">Transform</span><span class="o"><</span><span class="n">X</span><span class="p">,</span> <span class="n">Output</span> <span class="o">=</span> <span class="n">Y</span><span class="o">></span>
<span class="p">{</span>
<span class="k">match</span> <span class="n">option</span> <span class="p">{</span>
<span class="nf">Some</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">=></span> <span class="nf">Some</span><span class="p">(</span><span class="n">transform</span><span class="nf">.transform</span><span class="p">(</span><span class="n">x</span><span class="p">)),</span>
<span class="nb">None</span> <span class="k">=></span> <span class="nb">None</span><span class="p">,</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The example from before can then be reimplemented rather verbosely, by
creating structs and implementing <code class="language-plaintext highlighter-rouge">Transform</code> to do the appropriate
conversion for that struct.</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">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
</pre></td><td class="code"><pre><span class="c">// replacement for |val| val + x</span>
<span class="k">struct</span> <span class="n">Adder</span> <span class="p">{</span> <span class="n">x</span><span class="p">:</span> <span class="nb">i32</span> <span class="p">}</span>
<span class="k">impl</span> <span class="n">Transform</span><span class="o"><</span><span class="nb">i32</span><span class="o">></span> <span class="k">for</span> <span class="n">Adder</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Output</span> <span class="o">=</span> <span class="nb">i32</span><span class="p">;</span>
<span class="c">// ignoring the `fn ... self`, this looks similar to |val| val + x</span>
<span class="k">fn</span> <span class="nf">transform</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">val</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-></span> <span class="nb">i32</span> <span class="p">{</span>
<span class="n">val</span> <span class="o">+</span> <span class="k">self</span><span class="py">.x</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c">// replacement for |val| val * y</span>
<span class="k">struct</span> <span class="n">Multiplier</span> <span class="p">{</span> <span class="n">y</span><span class="p">:</span> <span class="nb">i32</span> <span class="p">}</span>
<span class="k">impl</span> <span class="n">Transform</span><span class="o"><</span><span class="nb">i32</span><span class="o">></span> <span class="k">for</span> <span class="n">Multiplier</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Output</span> <span class="o">=</span> <span class="nb">i32</span><span class="p">;</span>
<span class="c">// looks similar to |val| val * y</span>
<span class="k">fn</span> <span class="nf">transform</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">val</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-></span> <span class="nb">i32</span> <span class="p">{</span>
<span class="n">val</span> <span class="o">*</span> <span class="k">self</span><span class="py">.y</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">option</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>
<span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
<span class="k">let</span> <span class="n">new</span><span class="p">:</span> <span class="nb">Option</span><span class="o"><</span><span class="nb">i32</span><span class="o">></span> <span class="o">=</span> <span class="nf">map</span><span class="p">(</span><span class="n">option</span><span class="p">,</span> <span class="n">Adder</span> <span class="p">{</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="p">});</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{:?}"</span><span class="p">,</span> <span class="n">new</span><span class="p">);</span> <span class="c">// Some(5)</span>
<span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
<span class="k">let</span> <span class="n">new2</span> <span class="o">=</span> <span class="nf">map</span><span class="p">(</span><span class="n">option</span><span class="p">,</span> <span class="n">Multiplier</span> <span class="p">{</span> <span class="n">y</span><span class="p">:</span> <span class="n">y</span> <span class="p">});</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{:?}"</span><span class="p">,</span> <span class="n">new2</span><span class="p">);</span> <span class="c">// Some(20)</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>We’ve manually implemented something that seems to have the same
semantics as Rust closures, using traits and some structs to store and
manipulate the captures. In fact, the struct has some uncanny
similarities to the “environment” of a closure: it stores a pile of
variables that need to be used in the body of <code class="language-plaintext highlighter-rouge">transform</code>.</p>
<h2 id="how-do-real-closures-work">How do real closures work?</h2>
<p>Just like that, plus a little more flexibility and syntactic
sugar. The real definition of <code class="language-plaintext highlighter-rouge">Option::map</code> is:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre><span class="k">impl</span><span class="o"><</span><span class="n">X</span><span class="o">></span> <span class="nb">Option</span><span class="o"><</span><span class="n">X</span><span class="o">></span> <span class="p">{</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="n">map</span><span class="o"><</span><span class="n">Y</span><span class="p">,</span> <span class="n">F</span><span class="p">:</span> <span class="nf">FnOnce</span><span class="p">(</span><span class="n">X</span><span class="p">)</span> <span class="k">-></span> <span class="n">Y</span><span class="o">></span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">f</span><span class="p">:</span> <span class="n">F</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Option</span><span class="o"><</span><span class="n">Y</span><span class="o">></span> <span class="p">{</span>
<span class="k">match</span> <span class="k">self</span> <span class="p">{</span>
<span class="nf">Some</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">=></span> <span class="nf">Some</span><span class="p">(</span><span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">)),</span>
<span class="nb">None</span> <span class="k">=></span> <span class="nb">None</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p><code class="language-plaintext highlighter-rouge">FnOnce(X) -> Y</code> is another name for our <code class="language-plaintext highlighter-rouge">Transform<X, Output = Y></code>
bound, and, <code class="language-plaintext highlighter-rouge">f(x)</code> for <code class="language-plaintext highlighter-rouge">transform.transform(x)</code>.</p>
<p>There are three traits for closures, all of which provide the
<code class="language-plaintext highlighter-rouge">...(...)</code> call syntax (one could regard them as different kinds of
<code class="language-plaintext highlighter-rouge">operator()</code> in C++). They differ only by the <code class="language-plaintext highlighter-rouge">self</code> type of the call
method, and they cover all of the <code class="language-plaintext highlighter-rouge">self</code> options listed above.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">&self</code> is <a href="http://doc.rust-lang.org/std/ops/trait.Fn.html"><code class="language-plaintext highlighter-rouge">Fn</code></a></li>
<li><code class="language-plaintext highlighter-rouge">&mut self</code> is <a href="http://doc.rust-lang.org/std/ops/trait.FnMut.html"><code class="language-plaintext highlighter-rouge">FnMut</code></a></li>
<li><code class="language-plaintext highlighter-rouge">self</code> is <a href="http://doc.rust-lang.org/std/ops/trait.FnMut.html"><code class="language-plaintext highlighter-rouge">FnOnce</code></a></li>
</ul>
<p>These traits are covering exactly the three core ways to handle data
in Rust, so having each of them meshes perfectly with Rust’s
type-system.</p>
<p>When you write <code class="language-plaintext highlighter-rouge">|args...| code...</code> the compiler will implicitly define
a unique new struct type storing the captured variables, and then
implement one of those traits using the closure’s body, rewriting any
mentions of captured variables to go via the closure’s
environment. The struct type doesn’t have a user visible name, it is
purely internal to the compiler. When the program hits the closure
definition at runtime, it fills in an instance of struct and passes
that instance into whatever it needs to (like we did with our <code class="language-plaintext highlighter-rouge">map</code>
above).</p>
<p>There’s two questions left:</p>
<ol>
<li>how are variables captured? (what type are the fields of the environment struct?)</li>
<li>which trait is used? (what type of <code class="language-plaintext highlighter-rouge">self</code> is used?)</li>
</ol>
<p>The compiler answers both by using some local rules to choose the
version that will give the most flexibility. The local rules are
designed to be able to be checked only knowing the definition
the closure, and the types of any variables it captures.<sup id="fnref:i-think" role="doc-noteref"><a href="#fn:i-think" class="footnote" rel="footnote">2</a></sup></p>
<p>By “flexibility” I mean the compiler chooses the option that (it
thinks) will compile, but imposes the least on the programmer.</p>
<h3 id="structs-and-captures">Structs and captures</h3>
<p>If you’re familiar with closures in C++11, you may recall the <code class="language-plaintext highlighter-rouge">[=]</code>
and <code class="language-plaintext highlighter-rouge">[&]</code> capture lists: capture variables by-value<sup id="fnref:copy" role="doc-noteref"><a href="#fn:copy" class="footnote" rel="footnote">3</a></sup> and
by-reference respectively. Rust has similar capability: variables can
be captured by-value—the variable is moved into the closure
environment—or by-reference—a reference to the variable is stored
in the closure environment.</p>
<p>By default, the compiler looks at the closure body to see how captured
variables are used, and uses that to infers how variables should be
captured:</p>
<ul>
<li>if a captured variable is only ever used through a shared reference,
it is captured by <code class="language-plaintext highlighter-rouge">&</code> reference,</li>
<li>if it used through a mutable reference (including assignment), it is
captured by <code class="language-plaintext highlighter-rouge">&mut</code> reference,</li>
<li>if it is moved, it is forced to be captured by-value. (NB. using a
<a href="http://doc.rust-lang.org/std/marker/trait.Copy.html"><code class="language-plaintext highlighter-rouge">Copy</code></a> type
by-value only needs a <code class="language-plaintext highlighter-rouge">&</code> reference, so this rule only applies to
non-<code class="language-plaintext highlighter-rouge">Copy</code> ones.)</li>
</ul>
<p>The algorithm seems a little non-trivial, but it matches exactly the
mental model of a practiced Rust programmer, using ownership/borrows
as precisely as it can. In fact, if a closure is “non-escaping”, that
is, never leaves the stack frame in which it is created, I believe
this algorithm is perfect: code will compile without needing any
annotations about captures.</p>
<p>To summarise, the compiler will capture variables in the way that is
least restrictive in terms of continued use outside the closure (<code class="language-plaintext highlighter-rouge">&</code>
is preferred, then <code class="language-plaintext highlighter-rouge">&mut</code> and lastly by-value), and that still works
for all their uses within the closure. This analysis happens on a
per-variable basis, e.g.:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
</pre></td><td class="code"><pre><span class="k">struct</span> <span class="n">T</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="k">fn</span> <span class="nf">by_value</span><span class="p">(</span><span class="mi">_</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">fn</span> <span class="nf">by_mut</span><span class="p">(</span><span class="mi">_</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="n">T</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">fn</span> <span class="nf">by_ref</span><span class="p">(</span><span class="mi">_</span><span class="p">:</span> <span class="o">&</span><span class="n">T</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="n">T</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">y</span><span class="p">:</span> <span class="n">T</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">z</span><span class="p">:</span> <span class="n">T</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="k">let</span> <span class="n">closure</span> <span class="o">=</span> <span class="p">||</span> <span class="p">{</span>
<span class="nf">by_ref</span><span class="p">(</span><span class="o">&</span><span class="n">x</span><span class="p">);</span>
<span class="nf">by_ref</span><span class="p">(</span><span class="o">&</span><span class="n">y</span><span class="p">);</span>
<span class="nf">by_ref</span><span class="p">(</span><span class="o">&</span><span class="n">z</span><span class="p">);</span>
<span class="c">// forces `y` and `z` to be at least captured by `&mut` reference</span>
<span class="nf">by_mut</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="n">y</span><span class="p">);</span>
<span class="nf">by_mut</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="n">z</span><span class="p">);</span>
<span class="c">// forces `z` to be captured by value</span>
<span class="nf">by_value</span><span class="p">(</span><span class="n">z</span><span class="p">);</span>
<span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>To focus on the flexibility: since <code class="language-plaintext highlighter-rouge">x</code> is only captured by shared
reference, it is legal for it be used while <code class="language-plaintext highlighter-rouge">closure</code> exists, and
since <code class="language-plaintext highlighter-rouge">y</code> is borrowed (by mutable reference) it can be used once
<code class="language-plaintext highlighter-rouge">closure</code> goes out of scope, but <code class="language-plaintext highlighter-rouge">z</code> cannot be used at all, even once
<code class="language-plaintext highlighter-rouge">closure</code> is gone, since it has been moved into the <code class="language-plaintext highlighter-rouge">closure</code> value.</p>
<p>The compiler would create code that looks a bit like:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="code"><pre><span class="k">struct</span> <span class="n">Environment</span><span class="o"><</span><span class="nv">'x</span><span class="p">,</span> <span class="nv">'y</span><span class="o">></span> <span class="p">{</span>
<span class="n">x</span><span class="p">:</span> <span class="o">&</span><span class="nv">'x</span> <span class="n">T</span><span class="p">,</span>
<span class="n">y</span><span class="p">:</span> <span class="o">&</span><span class="nv">'y</span> <span class="k">mut</span> <span class="n">T</span><span class="p">,</span>
<span class="n">z</span><span class="p">:</span> <span class="n">T</span>
<span class="p">}</span>
<span class="cm">/* impl of FnOnce for Environment */</span>
<span class="k">let</span> <span class="n">closure</span> <span class="o">=</span> <span class="n">Environment</span> <span class="p">{</span>
<span class="n">x</span><span class="p">:</span> <span class="o">&</span><span class="n">x</span><span class="p">,</span>
<span class="n">y</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="n">y</span><span class="p">,</span>
<span class="n">z</span><span class="p">:</span> <span class="n">z</span>
<span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The struct desugaring allows the full power of Rust’s type system is
brought to bear on ensuring it isn’t possible to accidentally get a
dangling reference or use freed memory or trigger any other memory
safety violation by misusing a closure. If there is problematic code,
the compiler will point it out.</p>
<h3 id="move-and-escape"><code class="language-plaintext highlighter-rouge">move</code> and escape</h3>
<p>I stated above that the inference is perfect for non-escaping
closures… which implies that it is not perfect for “escaping” ones.</p>
<p>If a closure is escaping, that is, if it might leave the stack frame
where it is created, it must not contain any references to values
inside that stack frame, since those references would be dangling when
the closure is used outside that frame: very bad. Fortunately the
compiler will emit an error if there’s a risk of that, but returning
closures can be useful and so should be possible; for example<sup id="fnref:trait-object" role="doc-noteref"><a href="#fn:trait-object" class="footnote" rel="footnote">4</a></sup>:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre><span class="c">/// Returns a closure that will add `x` to its argument.</span>
<span class="k">fn</span> <span class="nf">make_adder</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Box</span><span class="o"><</span><span class="nf">Fn</span><span class="p">(</span><span class="nb">i32</span><span class="p">)</span> <span class="k">-></span> <span class="nb">i32</span><span class="o">></span> <span class="p">{</span>
<span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(|</span><span class="n">y</span><span class="p">|</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">f</span> <span class="o">=</span> <span class="nf">make_adder</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="nf">f</span><span class="p">(</span><span class="mi">1</span><span class="p">));</span> <span class="c">// 4</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="nf">f</span><span class="p">(</span><span class="mi">10</span><span class="p">));</span> <span class="c">// 13</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<div class="join"></div>
<p>Looks good, except… it doesn’t actually compile:</p>
<figure class="highlight"><pre><code class="language-text" data-lang="text"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="code"><pre>...:3:14: 3:23 error: closure may outlive the current function, but it borrows `x`, which is owned by the current function [E0373]
...:3 Box::new(|y| x + y)
^~~~~~~~~
...:3:18: 3:19 note: `x` is borrowed here
...:3 Box::new(|y| x + y)
^
</pre></td></tr></tbody></table></code></pre></figure>
<p>The problem is clearer when everything is written as explicit structs:
<code class="language-plaintext highlighter-rouge">x</code> only needs to be captured by reference to be used with <code class="language-plaintext highlighter-rouge">+</code>, so the
compiler is inferring that the code can look like:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre><span class="k">struct</span> <span class="n">Closure</span><span class="o"><</span><span class="nv">'a</span><span class="o">></span> <span class="p">{</span>
<span class="n">x</span><span class="p">:</span> <span class="o">&</span><span class="nv">'a</span> <span class="nb">i32</span>
<span class="p">}</span>
<span class="cm">/* impl of Fn for Closure */</span>
<span class="k">fn</span> <span class="nf">make_adder</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Box</span><span class="o"><</span><span class="nf">Fn</span><span class="p">(</span><span class="nb">i32</span><span class="p">)</span> <span class="k">-></span> <span class="nb">i32</span><span class="o">></span> <span class="p">{</span>
<span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">Closure</span> <span class="p">{</span> <span class="n">x</span><span class="p">:</span> <span class="o">&</span><span class="n">x</span> <span class="p">})</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p><code class="language-plaintext highlighter-rouge">x</code> goes out of scope at the end of <code class="language-plaintext highlighter-rouge">make_adder</code> so it is illegal to
return something that holds a reference to it.</p>
<p>So how do we fix it? Wouldn’t it be nice if the compiler could tell
us…</p>
<p>Well, actually, I omitted the last two lines of the error message above:</p>
<figure class="highlight"><pre><code class="language-text" data-lang="text"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
</pre></td><td class="code"><pre>...:3:14: 3:23 help: to force the closure to take ownership of `x` (and any other referenced variables), use the `move` keyword, as shown:
...: Box::new(move |y| x + y)
</pre></td></tr></tbody></table></code></pre></figure>
<p>A new keyword! The <code class="language-plaintext highlighter-rouge">move</code> keyword can be placed in front of a closure
declaration, and overrides the inference to capture all variables by
value. Going back to the previous section, if the code used <code class="language-plaintext highlighter-rouge">let
closure = move || { /* same code */ }</code> the environment struct would
look like:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre><span class="k">struct</span> <span class="n">Environment</span> <span class="p">{</span>
<span class="n">x</span><span class="p">:</span> <span class="n">T</span><span class="p">,</span>
<span class="n">y</span><span class="p">:</span> <span class="n">T</span><span class="p">,</span>
<span class="n">z</span><span class="p">:</span> <span class="n">T</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Capturing entirely by value is also strictly more general than
capturing by reference: the reference types are first-class in Rust,
so “capture by reference” is the same as “capture a reference by
value”. Thus, unlike C++, there’s little fundamental distinction
between capture by reference and by value, and the analysis Rust does
is not actually <em>necessary</em>: it just makes programmers’ lives easier.</p>
<p>To demonstrate, the following code will have the same behaviour and
same environment as the first version, by capturing references using
<code class="language-plaintext highlighter-rouge">move</code>:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="code"><pre><span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="n">T</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">y</span><span class="p">:</span> <span class="n">T</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">z</span><span class="p">:</span> <span class="n">T</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="k">let</span> <span class="n">x_ref</span><span class="p">:</span> <span class="o">&</span><span class="n">T</span> <span class="o">=</span> <span class="o">&</span><span class="n">x</span><span class="p">;</span>
<span class="k">let</span> <span class="n">y_mut</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="n">T</span> <span class="o">=</span> <span class="o">&</span><span class="k">mut</span> <span class="n">y</span><span class="p">;</span>
<span class="k">let</span> <span class="n">closure</span> <span class="o">=</span> <span class="k">move</span> <span class="p">||</span> <span class="p">{</span>
<span class="nf">by_ref</span><span class="p">(</span><span class="n">x_ref</span><span class="p">);</span>
<span class="nf">by_ref</span><span class="p">(</span><span class="o">&*</span><span class="n">y_mut</span><span class="p">);</span>
<span class="nf">by_ref</span><span class="p">(</span><span class="o">&</span><span class="n">z</span><span class="p">);</span>
<span class="nf">by_mut</span><span class="p">(</span><span class="n">y_mut</span><span class="p">);</span>
<span class="nf">by_mut</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="n">z</span><span class="p">);</span>
<span class="nf">by_value</span><span class="p">(</span><span class="n">z</span><span class="p">);</span>
<span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The set of variables that are captured is exactly those that are used
in the body of the closure, there’s no fine-grained capture lists like
in C++11. The <code class="language-plaintext highlighter-rouge">[=]</code> capture list exists as the <code class="language-plaintext highlighter-rouge">move</code> keyword, but
that is all.</p>
<p>We can now solve the original problem of returning from <code class="language-plaintext highlighter-rouge">make_adder</code>:
by writing <code class="language-plaintext highlighter-rouge">move</code> we force the compiler to avoid any
implicit/additional references, ensuring that the closure isn’t tied
to the stack frame of its birth. If we take the compiler’s suggestion
and write <code class="language-plaintext highlighter-rouge">Box::new(move |y| x + y)</code>, the code inside the compiler
will look more like:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre><span class="k">struct</span> <span class="n">Closure</span> <span class="p">{</span>
<span class="n">x</span><span class="p">:</span> <span class="nb">i32</span>
<span class="p">}</span>
<span class="cm">/* impl of Fn for Closure */</span>
<span class="k">fn</span> <span class="nf">make_adder</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Box</span><span class="o"><</span><span class="nf">Fn</span><span class="p">(</span><span class="nb">i32</span><span class="p">)</span> <span class="k">-></span> <span class="nb">i32</span><span class="o">></span> <span class="p">{</span>
<span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">Closure</span> <span class="p">{</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="p">})</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>It is clear that the compiler doesn’t infer when <code class="language-plaintext highlighter-rouge">move</code> is required
(or else we wouldn’t need to write it), but the fact that the <code class="language-plaintext highlighter-rouge">help</code>
message exists suggests that the compiler does know enough to infer
when <code class="language-plaintext highlighter-rouge">move</code> is necessary or not… in some cases. Unfortunately, doing
so in general in a reliable way (a <code class="language-plaintext highlighter-rouge">help</code> message can be
heuristic/best-effort, but inference built into the language cannot
be), would require more than just an analysis of the internals of the
closure body: it would require more complicated machinery to look at
how/where the closure value is used.</p>
<h3 id="traits">Traits</h3>
<p>The actual “function” bit of closures are handled by the traits
mentioned above. The implicit struct types will also have implicit
implementations of some of those traits, exactly those traits that
will actually work for the type.</p>
<p>Let’s start with an example: for the <code class="language-plaintext highlighter-rouge">make_adder</code> example, the <code class="language-plaintext highlighter-rouge">Fn</code>
trait is implemented for the implicit closure struct:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="code"><pre><span class="c">// (this is just illustrative, see the footnote for the gory details)</span>
<span class="k">impl</span> <span class="nf">Fn</span><span class="p">(</span><span class="nb">i32</span><span class="p">)</span> <span class="k">-></span> <span class="nb">i32</span> <span class="k">for</span> <span class="n">Closure</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">call</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-></span> <span class="nb">i32</span> <span class="p">{</span>
<span class="c">// |y| x + y</span>
<span class="k">self</span><span class="py">.x</span> <span class="o">+</span> <span class="n">y</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>In reality, there are also implicit implementations<sup id="fnref:invalid" role="doc-noteref"><a href="#fn:invalid" class="footnote" rel="footnote">5</a></sup> of
<code class="language-plaintext highlighter-rouge">FnMut</code> and <code class="language-plaintext highlighter-rouge">FnOnce</code> for <code class="language-plaintext highlighter-rouge">Closure</code>, but <code class="language-plaintext highlighter-rouge">Fn</code> is the “fundamental” one
for this closure.</p>
<p>There’s three traits, and so seven non-empty sets of traits that <em>could</em><sup id="fnref:inherit" role="doc-noteref"><a href="#fn:inherit" class="footnote" rel="footnote">6</a></sup> possibly be
implemented… but there’s actually only three interesting
configurations:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">Fn</code>, <code class="language-plaintext highlighter-rouge">FnMut</code> and <code class="language-plaintext highlighter-rouge">FnOnce</code>,</li>
<li><code class="language-plaintext highlighter-rouge">FnMut</code> and <code class="language-plaintext highlighter-rouge">FnOnce</code>,</li>
<li>only <code class="language-plaintext highlighter-rouge">FnOnce</code>.</li>
</ul>
<p>Why? Well, the three closure traits are actually three nested sets:
every closure that implements <code class="language-plaintext highlighter-rouge">Fn</code> can also implement <code class="language-plaintext highlighter-rouge">FnMut</code> (if
<code class="language-plaintext highlighter-rouge">&self</code> works, <code class="language-plaintext highlighter-rouge">&mut self</code> also works; proof: <code class="language-plaintext highlighter-rouge">&*self</code>), and similarly
every closure implementing <code class="language-plaintext highlighter-rouge">FnMut</code> can also implement <code class="language-plaintext highlighter-rouge">FnOnce</code>. This
hierarchy is enforced at the type level,
e.g. <a href="http://doc.rust-lang.org/std/ops/trait.FnMut.html"><code class="language-plaintext highlighter-rouge">FnMut</code></a>
has declaration:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre><span class="k">pub</span> <span class="k">trait</span> <span class="n">FnMut</span><span class="o"><</span><span class="n">Args</span><span class="o">></span><span class="p">:</span> <span class="n">FnOnce</span><span class="o"><</span><span class="n">Args</span><span class="o">></span> <span class="p">{</span>
<span class="o">...</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<div class="join"></div>
<p>In words: anything that implements <code class="language-plaintext highlighter-rouge">FnMut</code> <em>must</em> also implement
<code class="language-plaintext highlighter-rouge">FnOnce</code>.</p>
<p>There’s no subtlety required when inferring what traits to implement
as the compiler can and will just implement <em>every</em> trait for which
the implementation is legal. This is in-keeping with the “offer
maximum flexibility” rule that was used for the inference of the
capture types, since more traits means more options. The subset nature
of the <code class="language-plaintext highlighter-rouge">Fn*</code> traits means that following this rule will always result
in one of the three sets listed above being implemented.</p>
<p>As an example, this code demonstrates a closure for which an
implementation of <code class="language-plaintext highlighter-rouge">Fn</code> is illegal but both <code class="language-plaintext highlighter-rouge">FnMut</code> and <code class="language-plaintext highlighter-rouge">FnOnce</code> are
fine.</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="code"><pre><span class="k">let</span> <span class="k">mut</span> <span class="n">v</span> <span class="o">=</span> <span class="nd">vec!</span><span class="p">[];</span>
<span class="c">// nice form</span>
<span class="k">let</span> <span class="n">closure</span> <span class="o">=</span> <span class="p">||</span> <span class="n">v</span><span class="nf">.push</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="c">// explicit form</span>
<span class="k">struct</span> <span class="n">Environment</span><span class="o"><</span><span class="nv">'v</span><span class="o">></span> <span class="p">{</span>
<span class="n">v</span><span class="p">:</span> <span class="o">&</span><span class="nv">'v</span> <span class="k">mut</span> <span class="nb">Vec</span><span class="o"><</span><span class="nb">i32</span><span class="o">></span>
<span class="p">}</span>
<span class="c">// let's try implementing `Fn`</span>
<span class="k">impl</span><span class="o"><</span><span class="nv">'v</span><span class="o">></span> <span class="nf">Fn</span><span class="p">()</span> <span class="k">for</span> <span class="n">Environment</span><span class="o"><</span><span class="nv">'v</span><span class="o">></span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">call</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="py">.v</span><span class="nf">.push</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c">// error: cannot borrow data mutably</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">let</span> <span class="n">closure</span> <span class="o">=</span> <span class="n">Environment</span> <span class="p">{</span> <span class="n">v</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="n">v</span> <span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>It is illegal to mutate via a <code class="language-plaintext highlighter-rouge">& &mut ...</code>, and <code class="language-plaintext highlighter-rouge">&self</code> is creating
that outer shared reference. If it was <code class="language-plaintext highlighter-rouge">&mut self</code> or <code class="language-plaintext highlighter-rouge">self</code>, it would
be fine: the former is more flexible, so the compiler implements
<code class="language-plaintext highlighter-rouge">FnMut</code> for <code class="language-plaintext highlighter-rouge">closure</code> (and also <code class="language-plaintext highlighter-rouge">FnOnce</code>).</p>
<p>Similarly, if <code class="language-plaintext highlighter-rouge">closure</code> was to be <code class="language-plaintext highlighter-rouge">|| drop(v);</code>—that is, move out of
<code class="language-plaintext highlighter-rouge">v</code>—it would be illegal to implement either <code class="language-plaintext highlighter-rouge">Fn</code> or <code class="language-plaintext highlighter-rouge">FnMut</code>, since
the <code class="language-plaintext highlighter-rouge">&self</code> (respectively <code class="language-plaintext highlighter-rouge">&mut self</code>) means that the method would be
trying to steal ownership out of borrowed data: criminal.</p>
<h2 id="flexibility">Flexibility</h2>
<p>One of Rust’s goals is to leave choice in the hands of the programmer,
allowing their code to be efficient, with abstractions compiling away
and just leaving fast machine code. The design of closures to use
unique struct types and traits/generics is key to this.</p>
<p>Since each closure has its own type, there’s no compulsory need for
heap allocation when using closures: as demonstrated above, the
captures can just be placed directly into the struct value. This is a
property Rust shares with C++11, allowing closures to be used in
essentially any environment, including bare-metal environments.</p>
<p>The unique types does mean that one can’t use different closures
together automatically, e.g. one can’t create a vector of several
distinct closures. They may have different sizes and require different
invocations (different closures correspond to different internal code,
so a different function to call). Fortunately, the use of traits to
abstract over the closure types means one can opt-in to these features
and their benefits “on demand”, via <a href="/blog/2015/01/peeking-inside-trait-objects/">trait objects</a>: returning
the <code class="language-plaintext highlighter-rouge">Box<Fn(i32) -> i32></code> above used a trait object.</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre><span class="k">let</span> <span class="n">text</span> <span class="o">=</span> <span class="s">"second"</span><span class="p">;</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">closures</span><span class="p">:</span> <span class="nb">Vec</span><span class="o"><</span><span class="nb">Box</span><span class="o"><</span><span class="nf">Fn</span><span class="p">()</span><span class="o">>></span> <span class="o">=</span> <span class="nd">vec!</span><span class="p">[];</span>
<span class="n">closures</span><span class="nf">.push</span><span class="p">(</span><span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(||</span> <span class="nd">println!</span><span class="p">(</span><span class="s">"first"</span><span class="p">)));</span>
<span class="n">closures</span><span class="nf">.push</span><span class="p">(</span><span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(||</span> <span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">text</span><span class="p">)));</span>
<span class="n">closures</span><span class="nf">.push</span><span class="p">(</span><span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(||</span> <span class="nd">println!</span><span class="p">(</span><span class="s">"third"</span><span class="p">)));</span>
<span class="k">for</span> <span class="n">f</span> <span class="n">in</span> <span class="o">&</span><span class="n">closures</span> <span class="p">{</span>
<span class="nf">f</span><span class="p">();</span> <span class="c">// first / second / third</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>An additional benefit to the approach of unique types and generics
means that, by default, the compiler has full information about what
closure calls are doing at each call site, and so has the choice to
perform key optimisations like inlining. For example, the following
snippets compile to the same code,</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="code"><pre><span class="n">x</span><span class="nf">.map</span><span class="p">(|</span><span class="n">z</span><span class="p">|</span> <span class="n">z</span> <span class="o">+</span> <span class="mi">3</span><span class="p">)</span>
<span class="k">match</span> <span class="n">x</span> <span class="p">{</span>
<span class="nf">Some</span><span class="p">(</span><span class="n">z</span><span class="p">)</span> <span class="k">=></span> <span class="nf">Some</span><span class="p">(</span><span class="n">z</span> <span class="o">+</span> <span class="mi">3</span><span class="p">),</span>
<span class="nb">None</span> <span class="k">=></span> <span class="nb">None</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<div class="join"></div>
<p>(When I tested it by placing them into separate functions in a single
binary, the compiler actually optimised the second function to a
direct call to the first.)</p>
<p>This is all due to how Rust implements generics via monomorphisation,
where generic functions are compiled for each way their type
parameters are chosen, explicitly substituting the generic type with a
concrete one. Unfortunately, this isn’t always an optimisation, as it
can result in code bloat, where there are many similar copies of a
single function, which is again something that trait objects can
tackle: by using a trait object instead, one can use dynamically
dispatched closures to ensure there’s only one copy of a function,
even if it is used with many different closures.</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
</pre></td><td class="code"><pre><span class="k">fn</span> <span class="n">generic_closure</span><span class="o"><</span><span class="n">F</span><span class="p">:</span> <span class="nf">Fn</span><span class="p">(</span><span class="nb">i32</span><span class="p">)</span><span class="o">></span><span class="p">(</span><span class="n">f</span><span class="p">:</span> <span class="n">F</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">f</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="nf">f</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="nf">generic_closure</span><span class="p">(|</span><span class="n">x</span><span class="p">|</span> <span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">x</span><span class="p">));</span> <span class="c">// A</span>
<span class="nf">generic_closure</span><span class="p">(|</span><span class="n">x</span><span class="p">|</span> <span class="p">{</span> <span class="c">// B</span>
<span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">2</span><span class="p">;</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">y</span><span class="p">);</span>
<span class="p">});</span>
<span class="k">fn</span> <span class="nf">closure_object</span><span class="p">(</span><span class="n">f</span><span class="p">:</span> <span class="o">&</span><span class="nf">Fn</span><span class="p">(</span><span class="nb">i32</span><span class="p">))</span> <span class="p">{</span>
<span class="nf">f</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="nf">f</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="nf">closure_object</span><span class="p">(</span><span class="o">&</span><span class="p">|</span><span class="n">x</span><span class="p">|</span> <span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">x</span><span class="p">));</span>
<span class="nf">closure_object</span><span class="p">(</span><span class="o">&</span><span class="p">|</span><span class="n">x</span><span class="p">|</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">2</span><span class="p">;</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">y</span><span class="p">);</span>
<span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The final binary will have two copies of <code class="language-plaintext highlighter-rouge">generic_closure</code>, one for
<code class="language-plaintext highlighter-rouge">A</code> and one for <code class="language-plaintext highlighter-rouge">B</code>, but only one copy of <code class="language-plaintext highlighter-rouge">closure_object</code>. In fact,
there are implementations of the <code class="language-plaintext highlighter-rouge">Fn*</code> traits for pointers, so one can
even use a trait object directly with <code class="language-plaintext highlighter-rouge">generic_closure</code>,
e.g. <code class="language-plaintext highlighter-rouge">generic_closure((&|x| { ... }) as &Fn(_))</code>: so users of
higher-order functions can choose which trade-off they want themselves.</p>
<p>All of this flexibility falls directly out of using traits<sup id="fnref:stdfunction" role="doc-noteref"><a href="#fn:stdfunction" class="footnote" rel="footnote">7</a></sup> for
closures, and the separate parts are independent and very
compositional.</p>
<p>The power closures offer allow one to build high-level, “fluent” APIs
without losing performance compared to writing out the details by
hand. The prime example of this is
<a href="http://doc.rust-lang.org/std/iter">iterators</a>: one can write long
chains of calls to adapters like <code class="language-plaintext highlighter-rouge">map</code> and <code class="language-plaintext highlighter-rouge">filter</code> which get
optimised down to efficient C-like code. (For example, I wrote
<a href="/blog/2014/06/comparing-knn-in-rust/">a post</a> that demonstrates this, and the situation has only
improved since then: the closure design described here was implemented
months later.)</p>
<h2 id="in-closing">In closing</h2>
<p>Rust’s C++11-inspired closures are powerful tools that allow for
high-level and efficient code to be build, marrying two properties
often in contention. The moving parts of Rust’s closures are built
directly from the normal type system with traits, structs and
generics, which allows them to automatically gain features like heap
allocation and dynamic dispatch, but doesn’t require them.</p>
<p>(Thanks to Steve Klabnik and Aaron Turon for providing feedback on a
draft, and many commenters on <a href="http://www.reddit.com/r/rust/comments/359tj5/finding_closure_in_rust/">/r/rust</a>
and on IRC for finding inaccuracies and improvements.)</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="https://users.rust-lang.org/t/finding-closure-in-rust/1285">users</a></li>
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/359tj5/finding_closure_in_rust/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Finding%20Closure%20in%20Rust%20%23rustlang&url=https://huonw.github.io/blog/2015/05/finding-closure-in-rust/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/05/finding-closure-in-rust/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/05/finding-closure-in-rust/&title=Finding%20Closure%20in%20Rust" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:anon" role="doc-endnote">
<p>The Rust <code class="language-plaintext highlighter-rouge">|...| ...</code> syntax is more than just a closure: it’s
an <a href="http://en.wikipedia.org/wiki/Anonymous_function">anonymous function</a>. In general, it’s possible to have things
that are closures but aren’t anonymous (e.g. in Python,
functions declared with <code class="language-plaintext highlighter-rouge">def foo():</code> are closures too, they
can refer to variables in any scopes in which the <code class="language-plaintext highlighter-rouge">def foo</code>
is contained). The anonymity refers to the fact that the
closure expression is a value, it’s possible to just use it
directly and there’s no separate <code class="language-plaintext highlighter-rouge">fn foo() { ... }</code> with the
function value referred to via <code class="language-plaintext highlighter-rouge">foo</code>. <a href="#fnref:anon" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:assoc-vs-not" role="doc-endnote">
<p>This choice is saying that transformers can be
overloaded by the starting type, but the ending type
is entirely determined by the pair of the transform
and the starting type. Using an associated type for
the return value is more restrictive (no overloading
on return type only) but it gives the compiler a much
easier time when inferring types. Using an associated
type for the input value too would be too
restrictive: it is very useful for the output type to
depend on the input type, e.g. a transformation <code class="language-plaintext highlighter-rouge">&'a
[i32]</code> to <code class="language-plaintext highlighter-rouge">&'a i32</code> (by e.g. indexing) has the two
types connected via the generic lifetime <code class="language-plaintext highlighter-rouge">'a</code>. <a href="#fnref:assoc-vs-not" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:i-think" role="doc-endnote">
<p>This statement isn’t precisely true in practice,
e.g. <code class="language-plaintext highlighter-rouge">rustc</code> will emit different errors if closures are
misused in certain equivalent-but-non-identical
ways. However, I believe these are just improved
diagnostics, not a fundamental language thing… however,
I’m not sure. <a href="#fnref:i-think" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:copy" role="doc-endnote">
<p>“By-value” in C++, including <code class="language-plaintext highlighter-rouge">[=]</code>, is really “by-copy” (with
some copy-elision rules to sometimes elide copies in certain
cases), whereas in Rust it is always “by-move”, more similar
to rvalue references in C++. <a href="#fnref:copy" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:trait-object" role="doc-endnote">
<p>Since closure types are unique and unnameable, the
only way to return one is via a trait object, at
least until Rust gets something like the “abstract
return types” of <a href="https://github.com/rust-lang/rfcs/pull/105">RFC 105</a>, something much
desired for handling closures. This is a little like
an interface-checked version of C++11’s
<code class="language-plaintext highlighter-rouge">decltype(auto)</code>, which, I believe, was also partly
motivated by closures with unnameable types. <a href="#fnref:trait-object" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:invalid" role="doc-endnote">
<p>I wrote an invalid <code class="language-plaintext highlighter-rouge">Fn</code> implementation because the real
version is ugly and much less clear, and doesn’t work with
stable compilers at the moment. But since you asked, here
is what’s required:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#![feature(unboxed_closures, core)]
impl Fn<(i32,)> for Closure {
extern "rust-call" fn call(&self, (y,): (i32,)) -> i32 {
self.x + y
}
}
impl FnMut<(i32,)> for Closure {
extern "rust-call" fn call_mut(&mut self, args: (i32,)) -> i32 {
self.call(args)
}
}
impl FnOnce<(i32,)> for Closure {
type Output = i32;
extern "rust-call" fn call_once(self, args: (i32,)) -> i32 {
self.call(args)
}
}
</code></pre></div> </div>
<p>Just looking at that, one might be able to guess at a few of the
reasons that manual implementations of the function traits aren’t
stabilised for general use. The only way to create types
implementing those traits with the 1.0 compiler is with a closure
expression. <a href="#fnref:invalid" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:inherit" role="doc-endnote">
<p>I’m ignoring the inheritance, which means that certain
sets are actually statically illegal, i.e., without other
constraints there are seven possibilities. <a href="#fnref:inherit" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:stdfunction" role="doc-endnote">
<p>C++ has a similar choice, with <code class="language-plaintext highlighter-rouge">std::function</code> able to
provide type erasure/dynamic dispatch for closure
types, although it requires separate definition as a
library type, and requires allocations. The Rust trait
objects are a simple building block in the language,
and don’t require allocations (e.g. <code class="language-plaintext highlighter-rouge">&Fn()</code> is a trait
object that can be created out of a pointer to the
stack). <a href="#fnref:stdfunction" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2015/05/where-self-meets-sized-revisiting-object-safetyWhere Self Meets Sized: Revisiting Object Safety2015-05-06T00:00:00+00:002015-05-06T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>The concept of object safety in Rust was recently refined to be more
flexible in an important way: the checks can be disabled for specific
methods by using <code class="language-plaintext highlighter-rouge">where</code> clauses to restrict them to only work when
<code class="language-plaintext highlighter-rouge">Self: Sized</code>.</p>
<p>This post is a rather belated fourth entry in my series on trait
objects and object safety:
<a href="/blog/2015/01/peeking-inside-trait-objects/"><em>Peeking inside Trait Objects</em></a>,
<a href="/blog/2015/01/the-sized-trait/"><em>The Sized Trait</em></a> and <a href="/blog/2015/01/object-safety/"><em>Object Safety</em></a>. It’s
been long enough that a refresher is definitely in order, although this
isn’t complete coverage of the details.</p>
<details class="post-series">
<summary class="post-series-title">Other posts in this series on trait objects</summary>
<ol class="post-series-list">
<li class=""><a href="/blog/2015/01/peeking-inside-trait-objects/">Peeking inside Trait Objects</a></li>
<li class=""><a href="/blog/2015/01/the-sized-trait/">The Sized Trait</a></li>
<li class=""><a href="/blog/2015/01/object-safety/">Object Safety</a></li>
<li class="current"><a href="/blog/2015/05/where-self-meets-sized-revisiting-object-safety/">Where Self Meets Sized: Revisting Object Safety</a></li>
</ol>
</details>
<h2 id="recap">Recap</h2>
<p>Rust offers open sets of types, type erasure and dynamic dispatch via
<a href="/blog/2015/01/peeking-inside-trait-objects/">trait objects</a>. However, to ensure a uniform handling
of trait objects and non-trait objects in generic code, there are
certain restrictions about exactly which traits can be used to create
objects: this is <a href="/blog/2015/01/object-safety/">object safety</a>.</p>
<p>A trait is object safe only if the compiler can automatically
implement it for itself, by implementing each method as a dynamic
function call through the vtable stored in a trait object.</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Foo</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">method_a</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">u8</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">method_b</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">f32</span><span class="p">)</span> <span class="k">-></span> <span class="nb">String</span><span class="p">;</span>
<span class="p">}</span>
<span class="c">// automatically inserted by the compiler</span>
<span class="k">impl</span><span class="o"><</span><span class="nv">'a</span><span class="o">></span> <span class="n">Foo</span> <span class="k">for</span> <span class="n">Foo</span><span class="o">+</span><span class="nv">'a</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">method_a</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">u8</span> <span class="p">{</span>
<span class="c">// dynamic dispatch to `method_a` of erased type</span>
<span class="k">self</span><span class="nf">.method_a</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">method_b</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">f32</span><span class="p">)</span> <span class="k">-></span> <span class="nb">String</span> <span class="p">{</span>
<span class="c">// as above</span>
<span class="k">self</span><span class="nf">.method_b</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Without the object safety rules one can write functions with type
signatures satisfied by trait objects, where the internals make it
impossible to actually use with trait objects. However, Rust tries to ensure
that this can’t happen—code should only need to know the signatures
of anything it calls, not the internals—and hence object safety.</p>
<p>These rules outlaw creating trait objects of, for example, traits with
generic methods:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Bar</span> <span class="p">{</span>
<span class="k">fn</span> <span class="n">bad</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="n">T</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="n">Bar</span> <span class="k">for</span> <span class="nb">u8</span> <span class="p">{</span>
<span class="k">fn</span> <span class="n">bad</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="mi">_</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="p">{}</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="o">&</span><span class="mi">1_u8</span> <span class="k">as</span> <span class="o">&</span><span class="n">Bar</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/*
...:10:5: 10:7 error: cannot convert to a trait object because trait `Bar` is not object-safe [E0038]
...:10 &1 as &Bar;
^~
...:10:5: 10:7 note: method `bad` has generic type parameters
...:10 &1 as &Bar;
^~
*/</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Trait object values always appear behind a pointer, like <code class="language-plaintext highlighter-rouge">&SomeTrait</code>
or <code class="language-plaintext highlighter-rouge">Box<AnotherTrait></code>, since the trait value “<code class="language-plaintext highlighter-rouge">SomeTrait</code>” itself
doesn’t have size known at compile time. This property is captured via
the <a href="/blog/2015/01/the-sized-trait/"><code class="language-plaintext highlighter-rouge">Sized</code> trait</a>, which is implemented for types like <code class="language-plaintext highlighter-rouge">i32</code>,
or simple <code class="language-plaintext highlighter-rouge">struct</code>s and <code class="language-plaintext highlighter-rouge">enum</code>s, but not for unsized slices <code class="language-plaintext highlighter-rouge">[T]</code>, or
the plain trait types <code class="language-plaintext highlighter-rouge">SomeTrait</code>.</p>
<h2 id="iterating-on-the-design">Iterating on the design</h2>
<p>One impact of introducing object safety was that the design of several
traits had to change. The most noticeable ones were <code class="language-plaintext highlighter-rouge">Iterator</code>, and
the IO traits <code class="language-plaintext highlighter-rouge">Read</code> and <code class="language-plaintext highlighter-rouge">Write</code> (although they were probably <code class="language-plaintext highlighter-rouge">Reader</code>
and <code class="language-plaintext highlighter-rouge">Writer</code> at that point).</p>
<p>Focusing on the former, before object safety it was defined
something<sup id="fnref:associated-type" role="doc-noteref"><a href="#fn:associated-type" class="footnote" rel="footnote">0</a></sup> like:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Iterator</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Item</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">next</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Option</span><span class="o"><</span><span class="nn">Self</span><span class="p">::</span><span class="n">Item</span><span class="o">></span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">size_hint</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="p">(</span><span class="nb">usize</span><span class="p">,</span> <span class="nb">Option</span><span class="o"><</span><span class="nb">usize</span><span class="o">></span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="c">// ... methods methods methods ...</span>
<span class="k">fn</span> <span class="n">zip</span><span class="o"><</span><span class="n">U</span><span class="o">></span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">other</span><span class="p">:</span> <span class="n">U</span><span class="p">)</span> <span class="k">-></span> <span class="n">Zip</span><span class="o"><</span><span class="n">Self</span><span class="p">,</span> <span class="nn">U</span><span class="p">::</span><span class="n">IntoIter</span><span class="o">></span>
<span class="k">where</span> <span class="n">U</span><span class="p">:</span> <span class="n">IntoIterator</span>
<span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="k">fn</span> <span class="n">map</span><span class="o"><</span><span class="n">B</span><span class="p">,</span> <span class="n">F</span><span class="o">></span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">f</span><span class="p">:</span> <span class="n">F</span><span class="p">)</span> <span class="k">-></span> <span class="n">Map</span><span class="o"><</span><span class="n">Self</span><span class="p">,</span> <span class="n">F</span><span class="o">></span>
<span class="k">where</span> <span class="n">F</span><span class="p">:</span> <span class="nf">FnMut</span><span class="p">(</span><span class="nn">Self</span><span class="p">::</span><span class="n">Item</span><span class="p">)</span> <span class="k">-></span> <span class="n">B</span>
<span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="c">// etc</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The above <code class="language-plaintext highlighter-rouge">Iterator</code> isn’t object safe: it has generic methods, and so
it isn’t possible to implement <code class="language-plaintext highlighter-rouge">Iterator</code> for <code class="language-plaintext highlighter-rouge">Iterator</code> itself. This
is unfortunate, since it is very useful to be able to create and use
<code class="language-plaintext highlighter-rouge">Iterator</code> trait objects, so it <em>had</em> to be made object safe.</p>
<p>The solution at the time was extension traits: define a new trait
<code class="language-plaintext highlighter-rouge">IteratorExt</code> that incorporated all the object unsafe methods, and use
a blanket implementation to implement it for all <code class="language-plaintext highlighter-rouge">Iterator</code>s “from the
outside”.</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Iterator</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Item</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">next</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Option</span><span class="o"><</span><span class="nn">Self</span><span class="p">::</span><span class="n">Item</span><span class="o">></span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">size_hint</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="p">(</span><span class="nb">usize</span><span class="p">,</span> <span class="nb">Option</span><span class="o"><</span><span class="nb">usize</span><span class="o">></span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">trait</span> <span class="n">IteratorExt</span><span class="p">:</span> <span class="n">Sized</span> <span class="o">+</span> <span class="n">Iterator</span> <span class="p">{</span>
<span class="c">// ... methods methods methods ...</span>
<span class="k">fn</span> <span class="n">zip</span><span class="o"><</span><span class="n">U</span><span class="o">></span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">other</span><span class="p">:</span> <span class="n">U</span><span class="p">)</span> <span class="k">-></span> <span class="n">Zip</span><span class="o"><</span><span class="n">Self</span><span class="p">,</span> <span class="nn">U</span><span class="p">::</span><span class="n">IntoIter</span><span class="o">></span>
<span class="k">where</span> <span class="n">U</span><span class="p">:</span> <span class="n">IntoIterator</span>
<span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="k">fn</span> <span class="n">map</span><span class="o"><</span><span class="n">B</span><span class="p">,</span> <span class="n">F</span><span class="o">></span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">f</span><span class="p">:</span> <span class="n">F</span><span class="p">)</span> <span class="k">-></span> <span class="n">Map</span><span class="o"><</span><span class="n">Self</span><span class="p">,</span> <span class="n">F</span><span class="o">></span>
<span class="k">where</span> <span class="n">F</span><span class="p">:</span> <span class="nf">FnMut</span><span class="p">(</span><span class="nn">Self</span><span class="p">::</span><span class="n">Item</span><span class="p">)</span> <span class="k">-></span> <span class="n">B</span>
<span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="c">// etc</span>
<span class="p">}</span>
<span class="c">// blanket impl, for all iterators</span>
<span class="k">impl</span><span class="o"><</span><span class="n">I</span><span class="p">:</span> <span class="n">Iterator</span><span class="o">></span> <span class="n">IteratorExt</span> <span class="k">for</span> <span class="n">I</span> <span class="p">{}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The <code class="language-plaintext highlighter-rouge">next</code> and <code class="language-plaintext highlighter-rouge">size_hint</code> methods are object safe, so this version of
<code class="language-plaintext highlighter-rouge">Iterator</code> can create trait objects: <code class="language-plaintext highlighter-rouge">Box<Iterator<Item = u8>></code> is a
legal iterator over bytes. It works because the methods of
<code class="language-plaintext highlighter-rouge">IteratorExt</code> are no longer part of <code class="language-plaintext highlighter-rouge">Iterator</code> and so they’re not
involved in any object considerations for it.</p>
<p>Fortunately, those methods aren’t lost on trait objects, because there
are implementations like the following, allowing the blanket
implementation of <code class="language-plaintext highlighter-rouge">IteratorExt</code> to kick in:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="code"><pre><span class="c">// make Box<...> an Iterator by deferring to the contents</span>
<span class="k">impl</span><span class="o"><</span><span class="n">I</span><span class="p">:</span> <span class="n">Iterator</span> <span class="o">+</span> <span class="o">?</span><span class="n">Sized</span><span class="o">></span> <span class="n">Iterator</span> <span class="k">for</span> <span class="nb">Box</span><span class="o"><</span><span class="n">I</span><span class="o">></span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Item</span> <span class="o">=</span> <span class="nn">I</span><span class="p">::</span><span class="n">Item</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">next</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Option</span><span class="o"><</span><span class="nn">I</span><span class="p">::</span><span class="n">Item</span><span class="o">></span> <span class="p">{</span>
<span class="p">(</span><span class="o">**</span><span class="k">self</span><span class="p">)</span><span class="nf">.next</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">size_hint</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="p">(</span><span class="nb">usize</span><span class="p">,</span> <span class="nb">Option</span><span class="o"><</span><span class="nb">usize</span><span class="o">></span><span class="p">)</span> <span class="p">{</span>
<span class="p">(</span><span class="o">**</span><span class="k">self</span><span class="p">)</span><span class="nf">.size_hint</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<div class="join"></div>
<p>(The <code class="language-plaintext highlighter-rouge">?Sized</code> ensures this applies to <code class="language-plaintext highlighter-rouge">Box<Iterator<...>></code> trait
objects as well as simply <code class="language-plaintext highlighter-rouge">Box<SomeType></code> where <code class="language-plaintext highlighter-rouge">SomeType</code> is a normal
type that implements <code class="language-plaintext highlighter-rouge">Iterator</code>.)</p>
<p>This approach has some benefits, like clarifying the separation
between the “core” methods (<code class="language-plaintext highlighter-rouge">next</code> and <code class="language-plaintext highlighter-rouge">size_hint</code>) and the
helpers. However, it has several downsides, especially for cases that
aren’t <code class="language-plaintext highlighter-rouge">Iterator</code>:</p>
<ul>
<li>extra traits in the documentation,</li>
<li>users will have to import those extra traits</li>
<li>it only works with default-able methods,</li>
<li>the defaults can’t be overridden, e.g. there’s no way for a specific
type to slot in a more efficient way to implement a method</li>
</ul>
<p>All-in-all, it was a wet blanket on libraries. Fortunately, not all
was lost: let’s meet our saviour.</p>
<h2 id="its-a-bird-its-a-plane">It’s a bird… it’s a plane…</h2>
<p>It’s a <a href="https://github.com/rust-lang/rfcs/blob/master/text/0135-where.md"><code class="language-plaintext highlighter-rouge">where</code> clause</a>!</p>
<p><code class="language-plaintext highlighter-rouge">where</code> clauses allow predicating functions/methods/types on
essentially arbitrary trait relationships, not just the plain <code class="language-plaintext highlighter-rouge"><T:
SomeTrait></code>, where the left-hand side has to be a generic type
declared right then and there. For example, one can use
<a href="http://doc.rust-lang.org/std/convert/trait.From.html"><code class="language-plaintext highlighter-rouge">From</code></a> to
convert <em>to</em> types with a <code class="language-plaintext highlighter-rouge">where</code> clause.</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre><span class="k">fn</span> <span class="n">convert_to_string</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="k">-></span> <span class="nb">String</span>
<span class="k">where</span> <span class="nb">String</span><span class="p">:</span> <span class="n">From</span><span class="o"><</span><span class="n">T</span><span class="o">></span>
<span class="p">{</span>
<span class="nn">String</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The important realisation was that <code class="language-plaintext highlighter-rouge">where</code> allows placing restrictions
on <code class="language-plaintext highlighter-rouge">Self</code> directly on methods, so that certain methods only exist for
some implementing types. This was used to great effect to collapse
piles of traits into a single one, for example in <code class="language-plaintext highlighter-rouge">std::iter</code>.
<a href="http://doc.rust-lang.org/0.12.0/std/iter/#traits">Rust 0.12.0 had</a> a
swathe of extra <code class="language-plaintext highlighter-rouge">Iterator</code> traits: <code class="language-plaintext highlighter-rouge">Additive...</code>, <code class="language-plaintext highlighter-rouge">Cloneable...</code>,
<code class="language-plaintext highlighter-rouge">Multiplicative...</code>, <code class="language-plaintext highlighter-rouge">MutableDoubleEnded...</code>, <code class="language-plaintext highlighter-rouge">Ord...</code>.</p>
<p>Each of these were designed to define a few extra methods that
required specific restrictions on the element type of the iterator,
for example, <code class="language-plaintext highlighter-rouge">OrdIterator</code> needed <code class="language-plaintext highlighter-rouge">Ord</code> elements:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">OrdIterator</span><span class="p">:</span> <span class="n">Iterator</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">max</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Option</span><span class="o"><</span><span class="nn">Self</span><span class="p">::</span><span class="n">Item</span><span class="o">></span><span class="p">;</span>
<span class="c">// ...</span>
<span class="p">}</span>
<span class="k">impl</span><span class="o"><</span><span class="n">A</span><span class="p">:</span> <span class="nb">Ord</span><span class="p">,</span> <span class="n">I</span><span class="p">:</span> <span class="n">Iterator</span><span class="o"><</span><span class="n">Item</span> <span class="o">=</span> <span class="n">A</span><span class="o">>></span> <span class="n">OrdIterator</span> <span class="k">for</span> <span class="n">I</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">max</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Option</span><span class="o"><</span><span class="n">A</span><span class="o">></span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="c">// ...</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The <a href="http://doc.rust-lang.org/std/iter">current <code class="language-plaintext highlighter-rouge">std::iter</code></a> is much
cleaner: all the traits above have been merged into <code class="language-plaintext highlighter-rouge">Iterator</code> itself
with <code class="language-plaintext highlighter-rouge">where</code> clauses, e.g.
<a href="http://doc.rust-lang.org/nightly/std/iter/trait.Iterator.html#method.max"><code class="language-plaintext highlighter-rouge">max</code></a>:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Iterator</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Item</span><span class="p">;</span>
<span class="c">// ...</span>
<span class="k">fn</span> <span class="nf">max</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Option</span><span class="o"><</span><span class="nn">Self</span><span class="p">::</span><span class="n">Item</span><span class="o">></span>
<span class="k">where</span> <span class="nn">Self</span><span class="p">::</span><span class="n">Item</span><span class="p">:</span> <span class="nb">Ord</span>
<span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="c">// ...</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Notably, there’s no restriction on <code class="language-plaintext highlighter-rouge">Item</code> for general <code class="language-plaintext highlighter-rouge">Iterator</code>s,
only on <code class="language-plaintext highlighter-rouge">max</code>, so iterators retain full flexibility while still
gaining a <code class="language-plaintext highlighter-rouge">max</code> method that only works when it should:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre><span class="k">struct</span> <span class="n">NotOrd</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="p">(</span><span class="mi">0</span><span class="o">..</span><span class="mi">10</span><span class="p">)</span><span class="nf">.max</span><span class="p">();</span> <span class="c">// ok</span>
<span class="p">(</span><span class="mi">0</span><span class="o">..</span><span class="mi">10</span><span class="p">)</span><span class="nf">.map</span><span class="p">(|</span><span class="mi">_</span><span class="p">|</span> <span class="n">NotOrd</span><span class="p">)</span><span class="nf">.max</span><span class="p">();</span>
<span class="p">}</span>
<span class="cm">/*
...:5:29: 5:34 error: the trait `core::cmp::Ord` is not implemented for the type `NotOrd` [E0277]
...:5 (0..10).map(|_| NotOrd).max();
^~~~~
*/</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>This approach works fine for normal traits like <code class="language-plaintext highlighter-rouge">Ord</code>, and also works
equally well for “special” traits like <code class="language-plaintext highlighter-rouge">Sized</code>:
<a href="http://stackoverflow.com/a/27820018/1256624">it is possible</a> to
restrict methods to only work when <code class="language-plaintext highlighter-rouge">Self</code> has a statically known size
with <code class="language-plaintext highlighter-rouge">where Self: Sized</code>. Initially this had no interaction with
object safety, it would just influence what exactly that method could
do.</p>
<h2 id="putting-it-together">Putting it together</h2>
<p>The piece that interacts with object safety is <a href="https://github.com/rust-lang/rfcs/pull/817">RFC 817</a>, which
made <code class="language-plaintext highlighter-rouge">where Self: Sized</code> special: the compiler now understands that
methods tagged with that cannot ever be used on a trait object, even
in generic code. This means it is perfectly correct to completely
ignores any methods with that <code class="language-plaintext highlighter-rouge">where</code> clause when checking object
safety.</p>
<p>The bad example from the start can be written to compile:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Bar</span> <span class="p">{</span>
<span class="k">fn</span> <span class="n">bad</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span>
<span class="k">where</span> <span class="n">Self</span><span class="p">:</span> <span class="n">Sized</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="n">Bar</span> <span class="k">for</span> <span class="nb">u8</span> <span class="p">{</span>
<span class="k">fn</span> <span class="n">bad</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="mi">_</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span>
<span class="k">where</span> <span class="n">Self</span><span class="p">:</span> <span class="n">Sized</span>
<span class="p">{}</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="o">&</span><span class="mi">1_u8</span> <span class="k">as</span> <span class="o">&</span><span class="n">Bar</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<div class="join"></div>
<p>And also adjusted to not compile: try calling <code class="language-plaintext highlighter-rouge">(&1_u8 as
&Bar).bad("foo")</code> in <code class="language-plaintext highlighter-rouge">main</code> and the compiler spits out an error,</p>
<figure class="highlight"><pre><code class="language-text" data-lang="text"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="code"><pre>...:13:21: 13:31 error: the trait `core::marker::Sized` is not implemented for the type `Bar` [E0277]
...:13 (&1_u8 as &Bar).bad("foo")
^~~~~~~~~~
...:13:21: 13:31 note: `Bar` does not have a constant size known at compile-time
...:13 (&1_u8 as &Bar).bad("foo")
^~~~~~~~~~
</pre></td></tr></tbody></table></code></pre></figure>
<p>Importantly, this solves the <code class="language-plaintext highlighter-rouge">Iterator</code> problem: there’s no longer a
need to split methods into extension traits to ensure object safety,
one can instead just guard the bad ones. <code class="language-plaintext highlighter-rouge">Iterator</code> now looks like:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Iterator</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Item</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">next</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Option</span><span class="o"><</span><span class="nn">Self</span><span class="p">::</span><span class="n">Item</span><span class="o">></span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">size_hint</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="p">(</span><span class="nb">usize</span><span class="p">,</span> <span class="nb">Option</span><span class="o"><</span><span class="nb">usize</span><span class="o">></span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="c">// ... methods methods methods ...</span>
<span class="k">fn</span> <span class="n">zip</span><span class="o"><</span><span class="n">U</span><span class="o">></span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">other</span><span class="p">:</span> <span class="n">U</span><span class="p">)</span> <span class="k">-></span> <span class="n">Zip</span><span class="o"><</span><span class="n">Self</span><span class="p">,</span> <span class="nn">U</span><span class="p">::</span><span class="n">IntoIter</span><span class="o">></span>
<span class="k">where</span> <span class="n">Self</span><span class="p">:</span> <span class="n">Sized</span><span class="p">,</span> <span class="n">U</span><span class="p">:</span> <span class="n">IntoIterator</span>
<span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="k">fn</span> <span class="n">map</span><span class="o"><</span><span class="n">B</span><span class="p">,</span> <span class="n">F</span><span class="o">></span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">f</span><span class="p">:</span> <span class="n">F</span><span class="p">)</span> <span class="k">-></span> <span class="n">Map</span><span class="o"><</span><span class="n">Self</span><span class="p">,</span> <span class="n">F</span><span class="o">></span>
<span class="k">where</span> <span class="n">Self</span><span class="p">:</span> <span class="n">Sized</span><span class="p">,</span> <span class="n">F</span><span class="p">:</span> <span class="nf">FnMut</span><span class="p">(</span><span class="nn">Self</span><span class="p">::</span><span class="n">Item</span><span class="p">)</span> <span class="k">-></span> <span class="n">B</span>
<span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="c">// etc</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<div class="join"></div>
<p>(Along with <code class="language-plaintext highlighter-rouge">max</code> and the other <code class="language-plaintext highlighter-rouge">where</code>-reliant methods from the other
<code class="language-plaintext highlighter-rouge">*Iterator</code> traits mentioned above.)</p>
<p>The extra flexibility this <code class="language-plaintext highlighter-rouge">where</code> clauses offer is immensely helpful
for designing that perfect API. Of course, just adding <code class="language-plaintext highlighter-rouge">where Self:
Sized</code> isn’t a complete solution or the only trick: the current
<code class="language-plaintext highlighter-rouge">Iterator</code> still has the same sort of implementations of <code class="language-plaintext highlighter-rouge">Iterator</code>
for <code class="language-plaintext highlighter-rouge">Box<I></code> where <code class="language-plaintext highlighter-rouge">I: Iterator + ?Sized</code>, and traits using the
<code class="language-plaintext highlighter-rouge">where</code> technique may want to adopt others that <code class="language-plaintext highlighter-rouge">Iterator</code> does.</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="https://users.rust-lang.org/t/where-self-meets-sized-revisting-object-safety/1249">users</a></li>
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/351pil/where_self_meets_sized_revisiting_object_safety/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Where%20Self%20Meets%20Sized:%20Revisiting%20Object%20Safety%20%23rustlang&url=https://huonw.github.io/blog/2015/05/where-self-meets-sized-revisiting-object-safety/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/05/where-self-meets-sized-revisiting-object-safety/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/05/where-self-meets-sized-revisiting-object-safety/&title=Where%20Self%20Meets%20Sized:%20Revisiting%20Object%20Safety" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:associated-type" role="doc-endnote">
<p>I’m using an associated type for <code class="language-plaintext highlighter-rouge">Item</code> here, but
I believe it was probably still a generic
parameter <code class="language-plaintext highlighter-rouge">trait Iterator<Item> { ...</code> at this
point, and the <code class="language-plaintext highlighter-rouge">IntoIterator</code> trait didn’t
exist. However it doesn’t matter: the exact same
problems existed, just with different syntax. <a href="#fnref:associated-type" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2015/05/travis-on-the-train-part-2Travis on the train, part 22015-05-01T00:00:00+00:002015-05-01T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>After announcing <a href="https://github.com/huonw/travis-cargo">travis-cargo</a> a few days ago in
<a href="/blog/2015/04/helping-travis-catch-the-rustc-train/"><em>Helping Travis catch the rustc train</em></a>, I got some
great hints/contributions from <a href="https://github.com/jansegre">Jan Segre</a> and had a fun little
time automating <a href="https://users.rust-lang.org/t/tutorial-how-to-collect-test-coverages-for-rust-project/650/2?u=huon">code coverage collection</a> via
<a href="http://coveralls.io">coveralls.io</a>. Unfortunately, <strong>this is a breaking
change</strong> for existing users of travis-cargo, but the migration is
easy.</p>
<p>(If you’re wondering what travis-cargo is, see <a href="/blog/2015/04/helping-travis-catch-the-rustc-train/">the linked post</a>.)</p>
<p>Version 0.1 of travis-cargo is now available on PyPI<sup id="fnref:pypi" role="doc-noteref"><a href="#fn:pypi" class="footnote" rel="footnote">0</a></sup> and should be
installed via that medium. This works on Travis:</p>
<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
</pre></td><td class="code"><pre>pip <span class="nb">install</span> <span class="s1">'travis-cargo<0.2'</span> <span class="nt">--user</span> <span class="o">&&</span> <span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="nv">$HOME</span>/.local/bin:<span class="nv">$PATH</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The explicit <code class="language-plaintext highlighter-rouge"><0.2</code> requirement will reduce the risk of builds
breaking due to upstream changes in future, as I will make every
effort to follow semantic versioning and bump versions for any further
breaking changes.</p>
<h2 id="breaking-change"><code class="language-plaintext highlighter-rouge">[breaking-change]</code></h2>
<p>Part of migrating to PyPI requires renaming the <code class="language-plaintext highlighter-rouge">travis-cargo.py</code>
script to <code class="language-plaintext highlighter-rouge">travis_cargo.py</code> and making it no longer a
directly-executable script. <strong>This means that a direct <code class="language-plaintext highlighter-rouge">git clone</code> and
alias as I suggested previously will not work</strong>.</p>
<p>From now, the only supported way to install travis-cargo is via <code class="language-plaintext highlighter-rouge">pip</code>
as demonstrated above, and the recommended format to use with <code class="language-plaintext highlighter-rouge">pip</code> is
the PyPI package, with a version requirement. The example installation
listed above will install a <code class="language-plaintext highlighter-rouge">travis-cargo</code> binary to <code class="language-plaintext highlighter-rouge">~/.local/bin</code>
and then ensure that that it is available to the shell by name.</p>
<p>However, that’s not the only breaking change: the <code class="language-plaintext highlighter-rouge">cargo</code> subcommand
has been removed since the default binary is now much longer, and
looks more like the real <code class="language-plaintext highlighter-rouge">cargo</code> program. Cargo subcommands passed to
<code class="language-plaintext highlighter-rouge">travis-cargo</code> directly work as one would expect. Migration:</p>
<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="code"><pre><span class="c"># before</span>
./tc cargo build
<span class="c"># after</span>
travis-cargo build
</pre></td></tr></tbody></table></code></pre></figure>
<p>The <code class="language-plaintext highlighter-rouge">doc-upload</code> subcommand and the <code class="language-plaintext highlighter-rouge">--only</code> and <code class="language-plaintext highlighter-rouge">-q</code> arguments have
not been changed.</p>
<p>I’ve opened pull requests on all users of travis-cargo that I found
via Github’s search. I apologise if I missed you, fortunately
<a href="https://github.com/huonw/order-stat/commit/251a80999b5d727224523a33847479d23048d7ab">the migration</a>
is straight-forward.</p>
<h2 id="covering-your-coverage-needs">Covering your coverage needs</h2>
<p>The new and improved travis-cargo includes <code class="language-plaintext highlighter-rouge">travis-cargo coveralls</code>,
which will use the awesome <a href="https://github.com/SimonKagstrom/kcov">kcov</a> along with Rust’s debuginfo
support to record the test coverage of your libraries, and then upload
them to <a href="http://coveralls.io">coveralls.io</a>.</p>
<figure class="image has-caption">
<div class="image-positioner">
<a href="coveralls.png">
<img src="coveralls.png" alt="A screenshot of the coveralls.io inteface showing the coverage for 4 files: 75% for lib.rs with 6 out of 8 lines covered, 97.62% for mom.rs with 41 out of 42 lines covered, 100% for quickselect with 50 out of 50 lines covered and 100% for floyd_rivest.rs with 71 out of 71 lines covered." />
</a>
</div>
<figcaption><p>order-stat’s test coverage: being a little library makes it easy to get high numbers.</p>
</figcaption>
</figure>
<p>kcov combines with Coveralls to make supporting this ridiculously
easy: simply register/activate coveralls.io and add <code class="language-plaintext highlighter-rouge">sudo: required</code> &
<code class="language-plaintext highlighter-rouge">travis-cargo coveralls</code> to your <code class="language-plaintext highlighter-rouge">.travis.yml</code>. (The <code class="language-plaintext highlighter-rouge">sudo</code>
requirement is necessary to install kcov; it unfortunately makes
Travis much slower to start builds.)</p>
<p><code class="language-plaintext highlighter-rouge">travis-cargo coveralls</code> should successfully handle both in-crate and
external tests, so the Coveralls dashboard will display the total
coverage across all real tests (doc-tests are figments of our
imaginations). At the moment, Cargo doesn’t provide reliable
programmatic access to everything it knows, so <code class="language-plaintext highlighter-rouge">travis-cargo
coveralls</code> first searches the output of <code class="language-plaintext highlighter-rouge">cargo test</code> for the binaries
it runs, and then executes those binaries under kcov. For this to work
best, the <code class="language-plaintext highlighter-rouge">test</code> <a href="http://doc.crates.io/manifest.html#the-[profile.*]-sections">profile</a> must have <code class="language-plaintext highlighter-rouge">debug = true</code> (which is
the default).</p>
<h2 id="travis-cargo-by-example-the-sequel">Travis-cargo by example: the sequel</h2>
<p>order-stat’s <a href="https://github.com/huonw/order-stat/blob/251a809/.travis.yml"><code class="language-plaintext highlighter-rouge">.travis.yml</code></a> now looks like (<a href="https://github.com/huonw/order-stat/commit/251a80999b5d727224523a33847479d23048d7ab">diff for the change</a>):</p>
<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="code"><pre><span class="na">language</span><span class="pi">:</span> <span class="s">rust</span>
<span class="na">sudo</span><span class="pi">:</span> <span class="s">required</span>
<span class="na">rust</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">nightly</span>
<span class="pi">-</span> <span class="s">beta</span>
<span class="na">before_script</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH</span>
<span class="na">script</span><span class="pi">:</span>
<span class="pi">-</span> <span class="pi">|</span>
<span class="s">travis-cargo build &&</span>
<span class="s">travis-cargo test &&</span>
<span class="s">travis-cargo bench &&</span>
<span class="s">travis-cargo doc</span>
<span class="na">after_success</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">travis-cargo --only beta doc-upload</span>
<span class="pi">-</span> <span class="s">travis-cargo coveralls</span>
<span class="c1"># ...</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>travis-cargo’s <a href="https://github.com/huonw/travis-cargo#readme">README</a> contains more information.</p>
<p>(Thanks to <a href="https://github.com/SimonKagstrom">Simon Kågström</a> for
assistance with getting kcov working properly, lifthrasiir and
jscheivink for <a href="https://users.rust-lang.org/t/tutorial-how-to-collect-test-coverages-for-rust-project/650?u=huon">their tutorials</a> on collecting
coverage, and Jan Segre for the assistance with packaging and
feedback.)</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="https://users.rust-lang.org/t/travis-on-the-train-part-2/1196">users</a></li>
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/34i7sr/travis_on_the_train_part_2/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Travis%20on%20the%20train,%20part%202%20%23rustlang&url=https://huonw.github.io/blog/2015/05/travis-on-the-train-part-2/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/05/travis-on-the-train-part-2/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/05/travis-on-the-train-part-2/&title=Travis%20on%20the%20train,%20part%202" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:pypi" role="doc-endnote">
<p>Incidentally, I found publishing Python packages on PyPI much
more difficult than publishing Rust packages on crates.io.
Great job on getting cargo and crates.io so streamlined, Alex
Crichton, Yehuda Katz and everyone who has contributed! <a href="#fnref:pypi" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2015/04/helping-travis-catch-the-rustc-trainHelping Travis catch the rustc train2015-04-28T00:00:00+00:002015-04-28T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>I’ve been putting off configuring my continuous integration settings
to match the Rust train model: it involves non-trivial branching on
the configuration, and duplicating that over a pile of repos is not
something I looked forward to. So, instead, I wrote
<a href="https://github.com/huonw/travis-cargo">travis-cargo</a> to make things
easier.</p>
<h2 id="branching-on-configuration">Branching on configuration?</h2>
<p>One approach to developing Rust libraries once 1.0 is released will be
to test by building with the latest stable compiler, to ensure that
code is behaving as one hopes. I’m sure this will work well enough, but it’s missing an important part of the release model</p>
<p>Rust is adopting <a href="http://blog.rust-lang.org/2014/10/30/Stability.html">“trains”</a> for releases, where features migrate
from unstable to beta and then on to stable. That beta period is a big
motivation for the approach: before releases become stable, there is 6
weeks of development for people to test their code and help ensure
backwards compatibility isn’t accidentally broken. If there is a
regression, it will be assessed and presumably fixed before the stable
release. There is effort on going to incorporate this sort of checking
into <a href="https://crates.io/">crates.io</a>—brson has been posting regular
<a href="https://internals.rust-lang.org/t/regression-report-beta-2015-04-03-vs-nightly-2015-04-24/1967">regression reports</a> recently—but this doesn’t cover
all cases: at the very least, not all code is on crates.io.</p>
<p>The easiest way to get code to be tested is to have continuous
integration infrastructure like <a href="https://travis-ci.org/">Travis CI</a>
running tests against all configurations of interest: for a lot of
code this is likely to mean running builds against the most recent
stable release, the current beta, and the current nightly, and I
forsee that some libraries may wish to compile against slightly older
versions too. And having three builds for everything is just the start.</p>
<p>The nightly compilers offer unstable features that are fully
disallowed on the beta and stable channels, and libraries may wish to
offer or use functionality that is only enabled with unstable
compilers. At the moment, a big example is
<a href="http://doc.rust-lang.org/nightly/book/benchmark-tests.html">microbenchmarks with <code class="language-plaintext highlighter-rouge">#[bench]</code></a>, running them requires
importing the feature-gated <code class="language-plaintext highlighter-rouge">test</code> crate: I configure my libraries to
have an <code class="language-plaintext highlighter-rouge">unstable</code> <a href="http://doc.crates.io/manifest.html#the-[features]-section">feature</a> that has to be activated for
nightly-only features like benchmarks to be compiled in, so that the
library and its tests can be run with stable and beta compilers.</p>
<p>Unfortunately, all this means it is a little tricky to configure CI
optimally so that nightly builders build with unstable features, and
stable builds don’t. That said, It’s not <em>that</em> tricky, for example, using
<a href="http://docs.travis-ci.com/user/build-configuration/#The-Build-Matrix">Travis’ Build Matrix</a> functionality.</p>
<p>The final nail in the coffin is that I and others use CI to upload
rendered documentation for successful builds of the master branch:
<a href="http://www.hoverbear.org/2015/03/07/rust-travis-github-pages/">method I usually use</a> involves a big chunk of commands,
and requires manually inserting the library name. Having a separate
script allows me to use hoverbear’s code but abstracted out in a
DRY-er way. (The script even calls <code class="language-plaintext highlighter-rouge">cargo</code> directly to extract the
true library name, straight from the horse’s mouth.)</p>
<h2 id="travis-cargo-by-example">Travis-cargo by example</h2>
<blockquote>
<p><em>Update 2015-05-01</em>: the details of the code here has now changed
but the explanation is still the same, see
<a href="/blog/2015/05/travis-on-the-train-part-2/"><em>Travis on the train, part 2</em></a> for the up-to-date version of
the code.</p>
</blockquote>
<div class="lib-info" style="">
<a class="lib-link" href="http://huonw.github.io/order-stat/order_stat">order-stat</a>
<a href="https://crates.io/crates/order-stat"><img class="lib-badge inline-image" src="https://img.shields.io/crates/v/order-stat.svg" alt="order-stat on crates.io" /></a>
</div>
<p>The interesting bit of the Travis configuration I’m now using for,
say,
<a href="https://github.com/huonw/order-stat/blob/eb6fd83/.travis.yml"><code class="language-plaintext highlighter-rouge">order-stat</code></a>,
looks like:</p>
<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">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
</pre></td><td class="code"><pre><span class="na">language</span><span class="pi">:</span> <span class="s">rust</span>
<span class="c1"># run builds for both the nightly and beta branch</span>
<span class="na">rust</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">nightly</span>
<span class="pi">-</span> <span class="s">beta</span>
<span class="c1"># load travis-cargo</span>
<span class="na">before_script</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">git clone --depth 1 https://github.com/huonw/travis-cargo</span>
<span class="c1"># make a short alias (`alias` itself doesn't work)</span>
<span class="pi">-</span> <span class="s">ln -s ./travis-cargo/travis-cargo.py tc</span>
<span class="c1"># the main build</span>
<span class="na">script</span><span class="pi">:</span>
<span class="pi">-</span> <span class="pi">|</span>
<span class="s">./tc cargo build &&</span>
<span class="s">./tc cargo test &&</span>
<span class="s">./tc cargo bench &&</span>
<span class="s">./tc cargo doc</span>
<span class="na">after_success</span><span class="pi">:</span>
<span class="c1"># upload the documentation from the build with beta (automatically only actually</span>
<span class="c1"># runs on the master branch)</span>
<span class="pi">-</span> <span class="s">./tc --only beta doc-upload</span>
<span class="c1"># ...</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>which makes builds look like (look at the two jobs!):</p>
<p><a href="travis.png"><img src="travis.png" alt="" /></a></p>
<p>And, it’s exactly the same configuration that I’m using for my other
libraries, other than the chunk of <code class="language-plaintext highlighter-rouge">secure</code> nonsense at the end (the
encrypted Github token).</p>
<p><code class="language-plaintext highlighter-rouge">tc cargo ...</code> just runs cargo, but implicitly adds <code class="language-plaintext highlighter-rouge">--feature
unstable</code> when running the <code class="language-plaintext highlighter-rouge">rust: nightly</code> configuration (and
<code class="language-plaintext highlighter-rouge">--verbose</code> by default for all configurations). The command <code class="language-plaintext highlighter-rouge">tc
doc-upload</code> pushes the docs rendered by <code class="language-plaintext highlighter-rouge">cargo doc</code> to the main repo,
but with multiple configuration this may result in “races”/displaying
documentation for an undesired or inconsistent configuration: the
<code class="language-plaintext highlighter-rouge">--only beta</code> argument ensures that it only runs in the <code class="language-plaintext highlighter-rouge">beta</code>
configuration.</p>
<p>On the point of uploading docs: I imagine/hope there will be some sort
of crates.io-based doc hosting in future which makes manually hosting
docs less imperative. However, that day isn’t here yet, and that host
will presumably only have docs for released crates, being able to
easily host documentation for the cutting-edge master branch still
seems handy.</p>
<p>Once we have a stable release, I intend to add <code class="language-plaintext highlighter-rouge">- stable</code> to <code class="language-plaintext highlighter-rouge">rust:</code>,
and switch <code class="language-plaintext highlighter-rouge">doc-upload</code> to <code class="language-plaintext highlighter-rouge">--only stable</code>; I believe these should be
the only changes necessary to get reliable testing against all three
channels.</p>
<p>Also, the manual passing of an unstable flag may become moot in
future, if the
<a href="https://internals.rust-lang.org/t/setting-cfg-nightly-on-nightly-by-default/1893">compiler itself supports a <code class="language-plaintext highlighter-rouge">nightly</code> <code class="language-plaintext highlighter-rouge">cfg</code> setting</a>,
further reducing the necessity for this script.</p>
<p>The <a href="https://github.com/huonw/travis-cargo">travis-cargo repo</a>
contains a README with more details, and the script itself.</p>
<p>(Thanks to hoverbear for their original code, and bluss for
<a href="https://users.rust-lang.org/t/psa-1-0-0-beta-2-is-out/1019/13">their similar shell script</a>
for inspiration.)</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="https://users.rust-lang.org/t/helping-travis-catch-the-rustc-train/1167">users</a></li>
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/344o0e/helping_travis_catch_the_rustc_train/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Helping%20Travis%20catch%20the%20rustc%20train%20%23rustlang&url=https://huonw.github.io/blog/2015/04/helping-travis-catch-the-rustc-train/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/04/helping-travis-catch-the-rustc-train/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/04/helping-travis-catch-the-rustc-train/&title=Helping%20Travis%20catch%20the%20rustc%20train" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
https://huonw.github.io/blog/2015/04/little-librariesLittle libraries2015-04-27T00:00:00+00:002015-04-27T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>I’ve been having a lot of fun recently solving “little” problems in
Rust. I have a long term project to make something for displaying my
(GPS-tagged) photos nicely and, along the way, I’ve discovered and
filled in a few gaps by creating focused crates for small tasks.</p>
<figure class="image has-caption">
<div class="image-positioner">
<a href="whole-world.jpg">
<img src="whole-world.jpg" alt="A screenshot of a world map, with dots and coloured lines joined into a time series" title="Lots of places to go yet." />
</a>
</div>
<figcaption><p>My travels over the last few years, as displayed by the current web interface (served to the browser via Rust, of course).</p>
</figcaption>
</figure>
<p>Once an idea is formed, <code class="language-plaintext highlighter-rouge">cargo</code> means it only takes an hour or two to go
from zero to a focused crate with a chunk of code &
<a href="http://doc.rust-lang.org/book/documentation.html">documentation</a>, published on <a href="http://crates.io">crates.io</a>
and a nice feeling of completion.</p>
<p>The best part is that it makes sense to do this: <code class="language-plaintext highlighter-rouge">cargo</code> makes it so
easy to reuse code in a reliable way that publishing such little
crates is worth the effort: it takes a single line in the package
manifest to use them, and package versions only change when you
ask<sup id="fnref:lock" role="doc-noteref"><a href="#fn:lock" class="footnote" rel="footnote">0</a></sup>, so your code won’t implicitly break if upstream changes.
Rust<sup id="fnref:unique" role="doc-noteref"><a href="#fn:unique" class="footnote" rel="footnote">1</a></sup> is improving on many traditional systems languages in
this respect by encouraging a good package ecosystem from the
beginning, whereas languages like C or C++ don’t have a canonical
process and so it can take non-trivial effort to integrate third party
code.</p>
<p>Of course, not everything can be a small library: for the GPS photo
project, I needed to be able to read EXIF data (<code class="language-plaintext highlighter-rouge">rexiv2</code>), interact
with a database (<code class="language-plaintext highlighter-rouge">rusqlite</code>) and display a web interface (<code class="language-plaintext highlighter-rouge">iron</code>,
<code class="language-plaintext highlighter-rouge">nickel</code>). Nonetheless, the ease with which <code class="language-plaintext highlighter-rouge">cargo</code> manages
dependencies is still massively useful for “big” libraries like those,
for both the developers and the users.</p>
<div class="centered-libs">
<div class="lib-info" style="display: inline-block">
<a class="lib-link" href="https://felixcrux.com/files/doc/rexiv2/">rexiv2</a>
<a href="https://crates.io/crates/rexiv2"><img class="lib-badge inline-image" src="https://img.shields.io/crates/v/rexiv2.svg" alt="rexiv2 on crates.io" /></a>
</div>
<div class="lib-info" style="display: inline-block">
<a class="lib-link" href="http://www.rust-ci.org/jgallagher/rusqlite/doc/rusqlite/">rusqlite</a>
<a href="https://crates.io/crates/rusqlite"><img class="lib-badge inline-image" src="https://img.shields.io/crates/v/rusqlite.svg" alt="rusqlite on crates.io" /></a>
</div>
<div class="lib-info" style="display: inline-block">
<a class="lib-link" href="http://ironframework.io/doc/iron/">iron</a>
<a href="https://crates.io/crates/iron"><img class="lib-badge inline-image" src="https://img.shields.io/crates/v/iron.svg" alt="iron on crates.io" /></a>
</div>
<div class="lib-info" style="display: inline-block">
<a class="lib-link" href="http://docs.nickel.rs/nickel/">nickel</a>
<a href="https://crates.io/crates/nickel"><img class="lib-badge inline-image" src="https://img.shields.io/crates/v/nickel.svg" alt="nickel on crates.io" /></a>
</div>
</div>
<h2 id="the-process">The process</h2>
<p>I have a “process” with three parts:</p>
<ul>
<li>write the code</li>
<li>infrastructure: register <a href="https://travis-ci.org">travis CI</a> to
upload documentation to be visible at <code class="language-plaintext highlighter-rouge">$user.github.io/$repo</code> (I
also add some <a href="/blog/2015/03/rust-infrastructure-can-be-your-infrastructure/">project management tools</a>),</li>
<li>crates.io: add
<a href="http://doc.crates.io/manifest.html#package-metadata">metadata</a>
and
<a href="http://doc.crates.io/crates-io.html#publishing-crates">publish the crate</a></li>
</ul>
<p>It’s pretty simple, especially with some automation for the
infrastructure step. Other than writing the code itself, the most
complicated part of the whole process is uploading the documentation
to be readable online: everything else is essentially a single <code class="language-plaintext highlighter-rouge">cargo
...</code> invocation. My usual configuration for Travis looks something
like:</p>
<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="code"><pre><span class="na">language</span><span class="pi">:</span> <span class="s">rust</span>
<span class="na">sudo</span><span class="pi">:</span> <span class="no">false</span>
<span class="na">script</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">cargo build --verbose && cargo test --verbose && cargo doc --verbose</span>
<span class="na">after_success</span><span class="pi">:</span>
<span class="pi">-</span> <span class="pi">|</span>
<span class="s">[ $TRAVIS_BRANCH = master ] &&</span>
<span class="s">[ $TRAVIS_PULL_REQUEST = false ] &&</span>
<span class="s">echo '<meta http-equiv=refresh content=0;url=[CRATE NAME]/index.html>' > target/doc/index.html &&</span>
<span class="s">git clone https://github.com/davisp/ghp-import &&</span>
<span class="s">./ghp-import/ghp-import -n target/doc &&</span>
<span class="s">git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages</span>
<span class="na">env</span><span class="pi">:</span>
<span class="na">global</span><span class="pi">:</span>
<span class="na">secure</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Khw6CgS[...]jlPRac="</span>
</pre></td></tr></tbody></table></code></pre></figure>
<blockquote>
<p><em>Update 2015-04-28</em>: see
<a href="/blog/2015/04/helping-travis-catch-the-rustc-train/"><em>Helping Travis catch the rustc train</em></a> for a neater
way to do this and more.</p>
</blockquote>
<p>The <code class="language-plaintext highlighter-rouge">after_success</code> section manages publishing the documentation that
<code class="language-plaintext highlighter-rouge">cargo doc</code> renders, via
<a href="https://github.com/davisp/ghp-import"><code class="language-plaintext highlighter-rouge">ghp-import</code></a>. It is inspired
by
<a href="http://www.hoverbear.org/2015/03/07/rust-travis-github-pages/">Andrew Hobden’s instructions</a>,
modified to avoid needing <code class="language-plaintext highlighter-rouge">sudo</code> to be able to get the faster/more
responsive Travis builds. Hopefully this will get even easier in
future, with some sort of <code class="language-plaintext highlighter-rouge">rustdoc</code> as a service. (See also Keegan
McAllister’s
<a href="https://github.com/kmcallister/travis-doc-upload">more secure variant</a>.)</p>
<p>(I believe one can have a <code class="language-plaintext highlighter-rouge">.travis.yml</code> that compiles on both nightly
and beta/stable by adding something like <code class="language-plaintext highlighter-rouge">rust:
["nightly", "1.0.0-beta3"]</code>, but I’ve not yet investigated.)</p>
<h2 id="cogset"><code class="language-plaintext highlighter-rouge">cogset</code></h2>
<div class="lib-info" style="">
<a class="lib-link" href="http://huonw.github.io/cogset/cogset">cogset</a>
<a href="https://crates.io/crates/cogset"><img class="lib-badge inline-image" src="https://img.shields.io/crates/v/cogset.svg" alt="cogset on crates.io" /></a>
</div>
<p>The first problem that I encountered was wanting to group clusters of
photos that are close in space-time, so that I could highlight the
segments where I’m actually walking around some interesting place and
skip the boring bits of travelling in between.</p>
<figure class="image has-caption">
<div class="image-positioner">
<a href="scotland-circles.jpg">
<img src="scotland-circles.jpg" alt="A screenshot of a map of a part of Scotland, with coloured dots and lines joined into a time-series, and circles around clusters of those" title="Climbing Arthur's Seat was great." />
</a>
</div>
<figcaption><p>A few days in Scotland: Linlithgow (small circle, in the middle), Falkirk, back to Linlithgow (large) and ending in Edinburgh.</p>
</figcaption>
</figure>
<p>This is exactly what
<a href="https://en.wikipedia.org/wiki/Cluster_analysis">clustering</a> is
designed to do… but unfortunately I couldn’t find any
pre-implemented algorithms for doing this in Rust.</p>
<p>A bit of research indicated that I probably wanted a density-based
clustering algorithm, such as
<a href="https://en.wikipedia.org/wiki/DBSCAN">DBSCAN</a> or
<a href="https://en.wikipedia.org/wiki/OPTICS_algorithm">OPTICS</a>. These are
quite simple to use: feed in a collection of points with the ability
to find all the points that are less than ε units away from a given
point and DBSCAN efficiently iterates over those points finding
“dense” clusters, like the circles in the image above.</p>
<p>A few hours of working through the Wikipedia pseudocode, testing
and documenting gave me the
<a href="http://huonw.github.io/cogset/cogset/"><code class="language-plaintext highlighter-rouge">cogset</code></a> crate, with its
generic
<a href="http://huonw.github.io/cogset/cogset/struct.Dbscan.html"><code class="language-plaintext highlighter-rouge">Dbscan</code></a>
type. This slotted straight into my project by just adding <code class="language-plaintext highlighter-rouge">cogset =
"0.1"</code> to the <code class="language-plaintext highlighter-rouge">[dependencies]</code> section.</p>
<p>I particularly like how <code class="language-plaintext highlighter-rouge">Dbscan</code> is generic over the index/database of
points, letting someone calling <code class="language-plaintext highlighter-rouge">Dbscan</code> use a
<a href="http://huonw.github.io/cogset/cogset/struct.BruteScan.html">naive index</a>
if performance of the DBSCAN computation isn’t a problem, or an
efficient
<a href="https://en.wikipedia.org/wiki/Spatial_database#Spatial_index">spatial index</a>
if it is.</p>
<h2 id="tz-search"><code class="language-plaintext highlighter-rouge">tz-search</code></h2>
<div class="lib-info" style="">
<a class="lib-link" href="http://huonw.github.io/tz-search/tz_search">tz-search</a>
<a href="https://crates.io/crates/tz-search"><img class="lib-badge inline-image" src="https://img.shields.io/crates/v/tz-search.svg" alt="tz-search on crates.io" /></a>
</div>
<p>I discovered that my Nexus 5 records some timestamps incorrectly:
panoramas use the local time as the GPS time, resulting in some
confusing artifacts on the map. Fortunately both the local time and
the GPS location are recorded correctly, so it is possible to compute
the true GPS time by deducing the UTC offset of the local time and
hence computing the true GPS time from the local time.</p>
<p>This requires knowing the timezone of a point on the Earth’s surface,
again something for which I couldn’t find a good Rust library. Looking
into this problem turned up
<a href="https://github.com/bradfitz/latlong"><code class="language-plaintext highlighter-rouge">latlong</code></a>, a very neat little
Go library for doing exactly this task: mapping a latitude/longitude
pair to the name of the timezone it is in.</p>
<p>Porting it directly<sup id="fnref:pattern-matching" role="doc-noteref"><a href="#fn:pattern-matching" class="footnote" rel="footnote">2</a></sup> was straight-forward, resulting in
<a href="http://huonw.github.io/tz-search/tz_search/"><code class="language-plaintext highlighter-rouge">tz-search</code></a>, which offers:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
</pre></td><td class="code"><pre><span class="k">let</span> <span class="n">timezone</span> <span class="o">=</span> <span class="nn">tz_search</span><span class="p">::</span><span class="nf">lookup</span><span class="p">(</span><span class="o">-</span><span class="mf">33.8885</span><span class="p">,</span> <span class="mf">151.1908</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">timezone</span><span class="p">);</span> <span class="c">// Australia/Sydney</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Of course, just knowing the name of the timezone isn’t good enough,
but it’s the first step of getting the UTC offset (I haven’t
investigated the remaining ones yet…).</p>
<h2 id="order-stat"><code class="language-plaintext highlighter-rouge">order-stat</code></h2>
<div class="lib-info" style="">
<a class="lib-link" href="http://huonw.github.io/order-stat/order_stat">order-stat</a>
<a href="https://crates.io/crates/order-stat"><img class="lib-badge inline-image" src="https://img.shields.io/crates/v/order-stat.svg" alt="order-stat on crates.io" /></a>
</div>
<p>The most recent “little library” I published is <code class="language-plaintext highlighter-rouge">order-stat</code>, for
computing order statistics (the <code class="language-plaintext highlighter-rouge">k</code>th smallest/largest element of a
set). The motivation is expanding <code class="language-plaintext highlighter-rouge">cogset</code> slightly, with
<a href="https://en.wikipedia.org/wiki/OPTICS_algorithm">the OPTICS algorithm</a>,
which requires the ability to do exactly that.</p>
<p>Poking about, I found a few
<a href="https://en.wikipedia.org/wiki/Selection_algorithm">selection algorithms</a>,
notably <a href="https://en.wikipedia.org/wiki/Quickselect">quickselect</a> and
<a href="https://en.wikipedia.org/wiki/Floyd–Rivest_algorithm">Floyd–Rivest</a>,
as well as the obvious one of doing a full sort and indexing to
retrieve the <code class="language-plaintext highlighter-rouge">k</code>th element. All offer the same interface, so decision
between them is only guided by performance.</p>
<table>
<thead>
<tr>
<th>Algorithm</th>
<th style="text-align: right">Huge (ms)</th>
<th style="text-align: right">Large (µs)</th>
<th style="text-align: right">Small (ns)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Sort</td>
<td style="text-align: right">7.2</td>
<td style="text-align: right">87.8</td>
<td style="text-align: right">264</td>
</tr>
<tr>
<td>Quickselect</td>
<td style="text-align: right">1.1</td>
<td style="text-align: right">9.7</td>
<td style="text-align: right">83</td>
</tr>
<tr>
<td>Floyd–Rivest</td>
<td style="text-align: right"><strong>0.48</strong></td>
<td style="text-align: right"><strong>2.4</strong></td>
<td style="text-align: right"><strong>72</strong></td>
</tr>
</tbody>
</table>
<p>Hence, Floyd–Rivest has the honour of being
<a href="http://huonw.github.io/order-stat/order_stat/fn.kth.html">the star</a>
of this little library.</p>
<p>Somewhat against the idea of a little library, <code class="language-plaintext highlighter-rouge">order-stat</code>
<a href="http://huonw.github.io/order-stat/order_stat/fn.median_of_medians.html">includes</a>
a
<a href="https://en.wikipedia.org/wiki/Median_of_medians">medians-of-medians</a>
implementation too (in “little”’s defense, it’s related to order
statistics and medians).</p>
<div class="lib-info" style="">
<a class="lib-link" href="https://github.com/BurntSushi/quickcheck#readme">quickcheck</a>
<a href="https://crates.io/crates/quickcheck"><img class="lib-badge inline-image" src="https://img.shields.io/crates/v/quickcheck.svg" alt="quickcheck on crates.io" /></a>
</div>
<p>I should plug the <code class="language-plaintext highlighter-rouge">quickcheck</code> crate: it discovered a few subtle bugs
in my Floyd–Rivest implementation, stemming from me incorrectly
translating the copy-happy original version (presumably designed for
GC’d languages where any value can be duplicated cheaply) to a Rust
version that avoids clones and respects ownership.</p>
<p>(As a bonus, neither algorithm does any allocations!)</p>
<h2 id="write-your-own">Write your own!</h2>
<p>I’m sure there’s little tasks that you’ve solved in your own projects
that others might want to solve too, so unleashing them on the world
as a focused library on crates.io would be great! With 1.0 fast
approaching, the Rust ecosystem has a level of stability never seen
before, and maintaining a pile of little libraries is getting easier
and easier.</p>
<p>(For best results, include <a href="http://doc.rust-lang.org/book/documentation.html">documentation</a> and fill out as
much crates.io
<a href="http://doc.crates.io/manifest.html#package-metadata">metadata</a> as you
can.)</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="http://users.rust-lang.org/t/little-libraries/1159">users</a></li>
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/340dtb/little_libraries/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Little%20libraries%20%23rustlang&url=https://huonw.github.io/blog/2015/04/little-libraries/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/04/little-libraries/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/04/little-libraries/&title=Little%20libraries" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:lock" role="doc-endnote">
<p>This locking even holds true when using git dependencies:
once you compile once with Cargo versions of dependencies are
locked until you explicitly opt-in to a version upgrade with
<code class="language-plaintext highlighter-rouge">cargo update</code>. See
<a href="http://doc.crates.io/guide.html#cargo.toml-vs-cargo.lock">“Cargo.toml vs Cargo.lock”</a>
for more details. <a href="#fnref:lock" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:unique" role="doc-endnote">
<p>This post isn’t meant to imply that Rust is the only
language (or even systems language) with this property. I
just think it’s cool that Rust has it. <a href="#fnref:unique" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:pattern-matching" role="doc-endnote">
<p>It wasn’t quite a one-to-one port: I opted to use
<a href="https://github.com/huonw/tz-search/blob/229617cd23dc413957c5b02d027c7ce4bb2be3d0/src/lib.rs#L120-L124">an <code class="language-plaintext highlighter-rouge">enum</code></a> and <a href="https://github.com/huonw/tz-search/blob/229617cd23dc413957c5b02d027c7ce4bb2be3d0/src/lib.rs#L260-L284">pattern matching</a>
to model the different internal encodings,
instead of a trait object which would be the true
translation of the <a href="https://github.com/bradfitz/latlong/blob/7d3ff04aa2b06b9db6947f7d99a4bb3cc66570bc/latlong.go#L157-L159"><code class="language-plaintext highlighter-rouge">interface</code></a> used
in the original library. <a href="#fnref:pattern-matching" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2015/03/rust-infrastructure-can-be-your-infrastructureRust infrastructure can be your infrastructure2015-03-17T00:00:00+00:002015-03-17T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p><a href="http://rust-lang.org">Rust</a> is a reasonably large project:
<a href="https://github.com/rust-lang/rust">the compiler and standard libraries</a>
are over 350kloc, built across nearly 40000 commits by the hands of
around 900 contributors. Not only that: there are more than 30 other
repositories in
<a href="https://github.com/rust-lang">the rust-lang GitHub organisation</a> that
shouldn’t fall by the wayside, and, for rust-lang/rust alone, there
are often more than 100 pull requests landing and a dozen new
contributors in a single week.</p>
<blockquote>
<p><em>Update 2015-06-16</em>: Homu is now available online: <a href="http://homu.io/">homu.io</a></p>
</blockquote>
<p>It is sometimes chaotic… <em>often</em> chaotic… with
<a href="http://blog.rust-lang.org/2015/02/13/Final-1.0-timeline.html">1.0 quickly approaching</a>,
and there are definitely places where the core team (and the rest of
the community) sometimes can’t keep up, but getting code into
rust-lang/rust is rarely one<sup id="fnref:rollups" role="doc-noteref"><a href="#fn:rollups" class="footnote" rel="footnote">0</a></sup>, due to two critical robots:</p>
<ul>
<li>an integration bot/pull request manager, <a href="https://github.com/barosl/homu">Barosl Lee’s Homu</a></li>
<li>a review assigner,
<a href="https://github.com/nrc/highfive">Josh Matthews and Nick Cameron’s Highfive</a></li>
</ul>
<p>The former ensures the master tree is essentially always passing
tests, on all first-class platforms: it won’t let a patch land until
it does. The latter makes sure that pull requests don’t slip through
the cracks, there’s someone with power watching out for each one.</p>
<p>I’ve been wanting to experiment with using the bots for my own code,
and yesterday I finally got around to it, and (with a small bit of
assistance from the tool authors) got it all setup in a short time. I
can say from experience that it isn’t too hard to configure these
tools to run on your own repos on GitHub, and
they’re not Rust-specific: they work with any repository. All you need
is a public-facing server!</p>
<p><em>Update 2015-03-19</em>: Homu and Highfive have been
<a href="http://huon.me:54857/queue/all">deployed</a> to the
<a href="https://github.com/contain-rs">contain-rs</a> organisation, as
<a href="https://github.com/FlashCat">@FlashCat</a>.</p>
<p>Also, Manish
<a href="http://www.reddit.com/r/rust/comments/2zf8ur/rust_infrastructure_can_be_your_infrastructure/cpitcam">points out</a>
that there is actually an open
<a href="https://careers.mozilla.org/en-US/position/oymA0fwe">Operations Engineer</a>
position for Mozilla Research, to manage the infrastructure of Rust
and Servo.</p>
<h2 id="homu-not-rocket-science">Homu: “Not rocket science”</h2>
<p>Homu is a reimplementation/extension of the original
<a href="https://github.com/graydon/bors">bors</a> bot. Bors was implemented by
<a href="http://graydon.livejournal.com/186550.html">Graydon Hoare (Rust’s original designer) in 2013 (or so)</a>
to apply Ben Elliston’s “not rocket science” rule to Rust:</p>
<blockquote>
<p>The Not Rocket Science Rule Of Software Engineering:</p>
<p>automatically maintain a repository of code that always passes all the tests</p>
</blockquote>
<p>The <a href="http://buildbot.rust-lang.org/homu/">core idea</a> is simple: the
correct way for anyone (including core developers) to land code into
rust-lang/rust is to submit a pull request to that repo. Someone on
the reviewers whitelist will review the code and, once it looks good,
write a “review approved” comment:
<a href="https://github.com/rust-lang/rust/pull/23415#issuecomment-81877665"><code class="language-plaintext highlighter-rouge">@bors r+</code></a><sup id="fnref:bors" role="doc-noteref"><a href="#fn:bors" class="footnote" rel="footnote">1</a></sup>. Homu
will take the pull request, merge it with master into a new branch,
and submit that branch to a testing backend. If the tests pass, Homu
fast-forwards to the merge commit and starts on the next patch in
<a href="http://buildbot.rust-lang.org/homu/queue/rust">its queue</a>.</p>
<p>The process of code review and landing gated on testing works wonders
for Rust: only really subtly—or transiently—broken patches get
into master, other forms of brokenness are eliminated before touching
mainline at all and backing out/reverting of patches once landed is
rarely needed.<sup id="fnref:no-free-lunch" role="doc-noteref"><a href="#fn:no-free-lunch" class="footnote" rel="footnote">2</a></sup></p>
<p>Having tests always passing sounds great, right? It’s even better
because it’s not hard to use Homu with your own repos: just follow the
<a href="https://github.com/barosl/homu#usage">usage instructions</a> (I could
only get the git version to work). It supports two testing backends at
the moment, <a href="http://buildbot.net/">Buildbot</a> and
<a href="https://travis-ci.org/">Travis CI</a>.</p>
<p>Barosl tells me that he <s>is planning</s> has released
<a href="http://homu.io">Homu-as-a-service</a>, so <s>it is likely to get even
simpler in future</s> adding it to your project is easy and we live in
the future.</p>
<p>Even if you don’t need the test handling, Homu is still useful: the
queue pages are nice pull request summary panels, especially since
they can be combined to display pull requests across multiple repos,
e.g. the
<a href="http://buildbot.rust-lang.org/homu/">rust-lang Homu instance</a> manages
two repositories, <code class="language-plaintext highlighter-rouge">cargo</code> and <code class="language-plaintext highlighter-rouge">rust</code>, and there’s a variety of ways to
digest that:</p>
<ul>
<li><a href="http://buildbot.rust-lang.org/homu/queue/cargo">…/queue/cargo</a></li>
<li><a href="http://buildbot.rust-lang.org/homu/queue/cargo+rust">…/queue/cargo+rust</a></li>
<li><a href="http://buildbot.rust-lang.org/homu/queue/all">…/queue/all</a></li>
</ul>
<p>I easily lose track of pull requests against my own repos, so I’ve
registered a lot of my repos with my Homu instance, and the <code class="language-plaintext highlighter-rouge">/all</code>
endpoint shows me everything I want to know.</p>
<p>Which brings me on to highfive:</p>
<h2 id="highfive-welcome-you-should-hear-from-huonw-soon">Highfive: “welcome! You should hear from @huonw soon.”</h2>
<p>As I said, I easily lose track of pull requests against my own
repos. My GitHub news feed and emails is very busy with all
development and discussion in rust-lang/rust, so small pull requests
in other repositories can get drowned out and lost. Fortunately, Rust
(and Servo) itself had this problem and has made progress on it
already: <a href="https://github.com/nrc/highfive">Nick Cameron</a> built on
<a href="https://github.com/jdm/highfive">Josh Matthew’s script</a> for greeting
new contributors to create
<a href="https://github.com/rust-highfive">@rust-highfive</a>, a bot that still
<a href="https://github.com/rust-lang/rust/pull/23364#issuecomment-80426067">says hi</a>,
but also manages
<a href="https://github.com/rust-lang/rust/pull/23430#event-256365835">assigning potential reviewers to PRs</a>.</p>
<p>The bot randomly chooses a person (out of a small per-repo whitelist)
and uses GitHub’s assignment feature to make the pull request their
responsibility. The theory is that, for people who don’t know who
should review the code, the randomly chosen reviewer will either do
the review themselves or will know someone more appropriate, so things
rarely get left in limbo for weeks or months, especially not small
patches like edits to documentation.</p>
<p>Of course, in single-person projects like my own, finding other
reviewers doesn’t make so much sense, the goal is to be friendly, and
have pull requests automatically assigned to me, so that they show up
in the list that GitHub can show.</p>
<h3 id="setting-it-up">Setting it up</h3>
<p>Highfive is currently mainly designed for use as an internal rust-lang
tool, and so isn’t as well documented as Homu which was written to be
more generic from the start. I’ll write down a bit of docs here, but
they’re just an overview, so don’t be afraid to look at/edit the
source if you do wish to deploy it: it is easy to customize. Of
course, as an internal tool, it is designed to cater just for the
needs of rust-lang and support/patches not needed for that use case
may not be accepted upstream.</p>
<p>Highfive lives as CGI script <code class="language-plaintext highlighter-rouge">newpr.py</code>, using a basic configuration
file to authenticate with GitHub and JSON files to control the
possible reviewers on a per repo basis. The script selects a person
out of a set of eligible reviewers it determines by looking at the
JSON files, and the directory that has the most code changes in the
pull request.</p>
<p>Highfive also supports pinging reviewers on IRC to… encourage them
to make progress on the PR: the upstream repo is currently
<a href="https://github.com/nrc/highfive/blob/7c5b73babfd0881d1c676b8f0f7dbbeed5a392ba/highfive/newpr.py#L109-L114">hard-coded</a>
to <code class="language-plaintext highlighter-rouge">#rust-bots</code> on <code class="language-plaintext highlighter-rouge">irc.mozilla.org</code>. (It’s an internal tool!) <strong>Any deployment should
disable/change this.</strong></p>
<p>It interacts with GitHub via
<a href="https://help.github.com/articles/about-webhooks/">webhooks</a>: to add
Highfive to a repository, create a webhook under the repo’s
settings pointing to whereever <code class="language-plaintext highlighter-rouge">newpr.py</code> is exposed to the internet,
with the <code class="language-plaintext highlighter-rouge">application/x-www-form-urlencoded</code> content type.</p>
<h4 id="configuration-files">Configuration files</h4>
<p>The GitHub file should just be called <code class="language-plaintext highlighter-rouge">config</code>, and looks like:</p>
<figure class="highlight"><pre><code class="language-ini" data-lang="ini"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre><span class="nn">[github]</span>
<span class="py">user</span> <span class="p">=</span> <span class="s"><user name of the account to use for the bot></span>
<span class="py">token</span> <span class="p">=</span> <span class="s"><api token generated for that account></span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The API token must be protected, i.e. be careful to ensure that file
it isn’t accessible over the internet, and keep it out of git
(<a href="https://help.github.com/articles/remove-sensitive-data/">just deleting it after committing it isn’t enough</a>:
it will still appear in the history). I’m not sure of the exact
permissions the token should have, the instance for rust-lang uses
<code class="language-plaintext highlighter-rouge">notifications</code>, <code class="language-plaintext highlighter-rouge">read:org</code>, <code class="language-plaintext highlighter-rouge">repo</code> and <code class="language-plaintext highlighter-rouge">user</code> but I suspect just
<code class="language-plaintext highlighter-rouge">repo</code> is enough.</p>
<p>There are two sorts of JSON repo configuration files: <code class="language-plaintext highlighter-rouge"><name>.json</code>
defines the set of reviewers chosen in the repo specifically called
<code class="language-plaintext highlighter-rouge"><name></code>, while <code class="language-plaintext highlighter-rouge">_global.json</code> defines groups of reviewers in scope in
all other config files. E.g. For rust-lang,
<a href="https://github.com/nrc/highfive/blob/7c5b73babfd0881d1c676b8f0f7dbbeed5a392ba/highfive/configs/_global.json"><code class="language-plaintext highlighter-rouge">_global.json</code></a>
looks like:</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="code"><pre><span class="p">{</span><span class="w">
</span><span class="nl">"groups"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"core"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"@brson"</span><span class="p">,</span><span class="w"> </span><span class="s2">"@pcwalton"</span><span class="p">,</span><span class="w"> </span><span class="s2">"@nikomatsakis"</span><span class="p">,</span><span class="w"> </span><span class="s2">"@alexcrichton"</span><span class="p">,</span><span class="w"> </span><span class="s2">"@huonw"</span><span class="p">],</span><span class="w">
</span><span class="nl">"crates"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"@huonw"</span><span class="p">,</span><span class="w"> </span><span class="s2">"@alexcrichton"</span><span class="p">],</span><span class="w">
</span><span class="nl">"doc"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"@steveklabnik"</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>And
<a href="https://github.com/nrc/highfive/blob/7c5b73babfd0881d1c676b8f0f7dbbeed5a392ba/highfive/configs/rust.json"><code class="language-plaintext highlighter-rouge">rust.json</code></a>
looks like:</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="code"><pre><span class="p">{</span><span class="w">
</span><span class="nl">"groups"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"all"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"core"</span><span class="p">],</span><span class="w">
</span><span class="nl">"compiler"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"@pnkfelix"</span><span class="p">,</span><span class="w"> </span><span class="s2">"@nick29581"</span><span class="p">,</span><span class="w"> </span><span class="s2">"@eddyb"</span><span class="p">,</span><span class="w"> </span><span class="s2">"@Aatch"</span><span class="p">],</span><span class="w">
</span><span class="nl">"syntax"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"@pnkfelix"</span><span class="p">,</span><span class="w"> </span><span class="s2">"@nick29581"</span><span class="p">,</span><span class="w"> </span><span class="s2">"@sfackler"</span><span class="p">,</span><span class="w"> </span><span class="s2">"@kmc"</span><span class="p">],</span><span class="w">
</span><span class="nl">"libs"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"@aturon"</span><span class="p">]</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"dirs"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"doc"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"doc"</span><span class="p">],</span><span class="w">
</span><span class="nl">"liballoc"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"libs"</span><span class="p">],</span><span class="w">
</span><span class="nl">"libarena"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"libs"</span><span class="p">],</span><span class="w">
</span><span class="nl">"libbacktrace"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
</span><span class="nl">"libcollections"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"libs"</span><span class="p">,</span><span class="w"> </span><span class="s2">"@Gankro"</span><span class="p">],</span><span class="w">
</span><span class="err">...</span><span class="w">
</span><span class="nl">"librustc"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"compiler"</span><span class="p">],</span><span class="w">
</span><span class="err">...</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>As one might guess, <code class="language-plaintext highlighter-rouge">groups</code> defines groups of reviewers, in terms of
GitHub handles and other groups. The <code class="language-plaintext highlighter-rouge">dirs</code> dictionary lists
directories under <code class="language-plaintext highlighter-rouge">src</code> in the repo, Highfive will determine which
directory contains the most changed files in the patch, and select
those groups/users, so that people with particular areas of expertise
get to review those patches (heuristically).</p>
<p>The people in the <code class="language-plaintext highlighter-rouge">all</code> group are considered eligible for any review,
and so are added to the pool no matter what. As such the <code class="language-plaintext highlighter-rouge">dirs</code> field
is completely optional, if it is missing (or any directories are
missing) Highfive will select from only the <code class="language-plaintext highlighter-rouge">all</code> group. I’ve
used this to
<a href="https://github.com/huonw/highfive/tree/8bf65450821629fd5815fe7bbc3aab3ae69a449a/highfive/configs">configure my Highfive</a>
to assign me for everything (makes sense…).</p>
<h2 id="batch-configuration">Batch configuration</h2>
<p>It’s pretty annoying to use GitHub’s web interface to manually add to
each repo your robot collaborator, and then add the webhooks necessary
for Homu and Highfive, so I wrote
<a href="https://github.com/huonw/repo-admin">a few Python scripts</a> to
help. The code has some examples of using them to set-up these two pieces of
infrastructure.</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="http://users.rust-lang.org/t/rust-infrastructure-can-be-your-infrastructure/688">users</a></li>
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/2zf8ur/rust_infrastructure_can_be_your_infrastructure/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Rust%20infrastructure%20can%20be%20your%20infrastructure%20%23rustlang&url=https://huonw.github.io/blog/2015/03/rust-infrastructure-can-be-your-infrastructure/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/03/rust-infrastructure-can-be-your-infrastructure/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/03/rust-infrastructure-can-be-your-infrastructure/&title=Rust%20infrastructure%20can%20be%20your%20infrastructure" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:rollups" role="doc-endnote">
<p>I’m being… optimistic. There’s so many patches submitted
that we only keep up via regular “rollups”: landing
several pull requests in a single batch (usually created
by the hard-working
<a href="https://github.com/Manishearth">Manish Goregaokar</a>). This
is a curse and a blessing of the design of the testing
infrastructure: serialized testing means things take a
while to land, but testing everything (including rollups)
before landing ensures that the master branch still passes
tests. <a href="#fnref:rollups" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:bors" role="doc-endnote">
<p>Why <code class="language-plaintext highlighter-rouge">@bors</code> if its now Homu? The Homu back-end listens for
mentions of the account it is registered to use, and
rust-lang still uses <a href="https://github.com/bors">@bors</a>. <a href="#fnref:bors" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:no-free-lunch" role="doc-endnote">
<p>Unfortunately, there’s no free lunch, and the test
guarantees come at a cost of scalability, as landing
patches to master is serialised
(<a href="#fn:rollups">see above too</a>). <a href="#fnref:no-free-lunch" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2015/02/some-notes-on-send-and-syncSome notes on Send and Sync2015-02-20T00:00:00+00:002015-02-20T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>If you’ve been in the <code class="language-plaintext highlighter-rouge">#rust-internals</code> IRC channel recently, you
may’ve caught a madman raving about how much they like Rust:</p>
<figure class="highlight"><pre><code class="language-irc" data-lang="irc"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre>...
[15:50:03] <huon> I love this language
...
[20:02:07] <huon> did you know: Rust is awesome.
...
</pre></td></tr></tbody></table></code></pre></figure>
<p>I was (and still am) losing my mind over how well <code class="language-plaintext highlighter-rouge">Sync</code> and <code class="language-plaintext highlighter-rouge">Send</code>
interact with everything, especially now that
<a href="https://github.com/rust-lang/rust/pull/22319">the implementation</a> for
<a href="https://github.com/rust-lang/rfcs/pull/458">RFC 458</a> has landed.</p>
<p>I’m aiming to write down a few edge cases and slight subtleties here
that Aaron Turon, Niko Matsakis and I have realised; so that we (and
others) don’t have to keep rediscovering them. So, unfortunately, this
short(ish) article isn’t aiming to describe entirely why I’m so keen
on them, but…</p>
<h2 id="the-traits">The traits</h2>
<p>… I’m sure I can write something.</p>
<p>Rust aims to be a language with really good support for concurrency;
and there’s two parts to it: ownership & lifetimes, and the traits
<a href="http://doc.rust-lang.org/nightly/std/marker/trait.Send.html"><code class="language-plaintext highlighter-rouge">Send</code></a> & <a href="http://doc.rust-lang.org/nightly/std/marker/trait.Sync.html"><code class="language-plaintext highlighter-rouge">Sync</code></a> (the docs for these aren’t great at
the moment, especially since the latest set of improvements landed so
recently).</p>
<p>These traits capture and control the two most common ways a piece of
data be accessed and thrown around by threads, dictating whether it is
safe to transfer ownership or pass a reference into another
thread.</p>
<p>The traits are “marker traits”, meaning they have no methods and don’t
inherently provide any functionality. They serve as markers of certain
invariants that types implementing them are expected to fulfill
(they’re <code class="language-plaintext highlighter-rouge">unsafe</code> to implement manually for this reason: the
programmer has to ensure the invariants are upheld). Specifically, the
working definitions we have from them now are:</p>
<ul>
<li>If <code class="language-plaintext highlighter-rouge">T: Send</code>, then passing by-value a value of type <code class="language-plaintext highlighter-rouge">T</code> into
another thread will not lead to data races (or other unsafety)</li>
<li>If <code class="language-plaintext highlighter-rouge">T: Sync</code>, then passing a reference <code class="language-plaintext highlighter-rouge">&T</code> to a value of type <code class="language-plaintext highlighter-rouge">T</code>
into another thread will not lead to data races (or other unsafety)
(aka, <code class="language-plaintext highlighter-rouge">T: Sync</code> implies <code class="language-plaintext highlighter-rouge">&T: Send</code>)</li>
</ul>
<p>That is, <code class="language-plaintext highlighter-rouge">Sync</code> is related to how a type works when shared across
multiple threads at once, and <code class="language-plaintext highlighter-rouge">Send</code> talks about how a type behaves as
it crosses a task boundary. (These definitions are pretty vague, but
the core team is definitely very interested in firming up and
formalising them to be able to prove useful concrete things.)</p>
<p>These two traits enable a lot of useful concurrency and parallel
patterns to be expressed while guaranteeing memory safety. Basic
examples include message passing and shared memory, both immutable &
mutable (e.g. with enforced atomic instructions, or protected by
locks). But more advanced things are easily possible too, with safety
falling automatically out of the type system and the design of the
standard library, e.g. manipulating (reading and writing) data stored
directly on another thread’s stack and mutating disjoint pieces of a
vector in parallel with no locking necessary.</p>
<p>(It’s worth mentioning that Rust only guarantees memory safety and,
particularly, freedom from data races, it doesn’t guarantee freedom
from other concurrence/parallelism issues, such as dead locks, and
non-data-race race conditions.)</p>
<p>I have a very basic little library, <a href="http://huonw.github.io/simple_parallel/simple_parallel/"><code class="language-plaintext highlighter-rouge">simple_parallel</code></a>
(<a href="https://github.com/huonw/simple_parallel">source</a>), that is trying to experiment with these
ideas. The examples there show off a few of the things mentioned
above, and compile today. I think its pretty cool what Rust can do.</p>
<p>Anyway, there’ll probably be lots more said about this later by me and
by others. Now, I’ll stop distracting myself and get on to the actual
notes I wanted to write down.</p>
<h2 id="sync--copy--send"><code class="language-plaintext highlighter-rouge">Sync + Copy</code> ⇒ <code class="language-plaintext highlighter-rouge">Send</code></h2>
<p>That is, if a type <code class="language-plaintext highlighter-rouge">T</code> implements both <code class="language-plaintext highlighter-rouge">Sync</code> and <code class="language-plaintext highlighter-rouge">Copy</code>, then it can
also implement <code class="language-plaintext highlighter-rouge">Send</code> (conversely, a type is only allowed to be both
<code class="language-plaintext highlighter-rouge">Sync</code> and <code class="language-plaintext highlighter-rouge">Copy</code> if it is also <code class="language-plaintext highlighter-rouge">Send</code>).</p>
<p>Proof:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="code"><pre><span class="c">// we start with some `T` on the main thread</span>
<span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="n">T</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="nn">thread</span><span class="p">::</span><span class="nf">scoped</span><span class="p">(||</span> <span class="p">{</span>
<span class="c">// and transfer a reference to a subthread (safe, since T: Sync)</span>
<span class="k">let</span> <span class="n">y</span><span class="p">:</span> <span class="o">&</span><span class="n">T</span> <span class="o">=</span> <span class="o">&</span><span class="n">x</span><span class="p">;</span>
<span class="c">// now use `T: Copy` to duplicate the data out, meaning we've</span>
<span class="c">// transferred `x` by-value into this new thread</span>
<span class="k">let</span> <span class="n">z</span><span class="p">:</span> <span class="n">T</span> <span class="o">=</span> <span class="o">*</span><span class="n">y</span><span class="p">;</span>
<span class="p">})</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The transfer happened only using <code class="language-plaintext highlighter-rouge">Sync + Copy</code> and so must be safe (if
it wasn’t safe <code class="language-plaintext highlighter-rouge">T</code> isn’t allowed to implement <code class="language-plaintext highlighter-rouge">Sync</code>), hence it is
legal for <code class="language-plaintext highlighter-rouge">T</code> to implement <code class="language-plaintext highlighter-rouge">Send</code>.</p>
<p>This might not seem so interesting, since it <em>is</em> just a specific case
of the definition of <code class="language-plaintext highlighter-rouge">Sync</code> (“can copy out of <code class="language-plaintext highlighter-rouge">&</code>” is a fundamental
property of our <code class="language-plaintext highlighter-rouge">T: Copy</code> type, and so has to be considered when
considering the thread safety of <code class="language-plaintext highlighter-rouge">&T</code>), but it is a little
subtle.</p>
<p>Also, needing to consider this case at all probably won’t come up for
many types; one is most likely to encounter types that are not <code class="language-plaintext highlighter-rouge">Send</code>
when storing pointers to shared memory—such as <code class="language-plaintext highlighter-rouge">Rc</code>—and
most such types are not <code class="language-plaintext highlighter-rouge">Copy</code> since they have to manage their
memory—such as <code class="language-plaintext highlighter-rouge">Rc</code> again. The two most prominent examples are
<code class="language-plaintext highlighter-rouge">&</code> (which has safety ensured by <code class="language-plaintext highlighter-rouge">Sync</code> and static analysis in the
compiler: lifetimes) and a hypothetical <code class="language-plaintext highlighter-rouge">Gc<T></code> pointer. I guess we’ll
just have to take care for <code class="language-plaintext highlighter-rouge">Gc</code>.</p>
<h2 id="mut-t-send-when-t-send"><code class="language-plaintext highlighter-rouge">&mut T: Send</code> when <code class="language-plaintext highlighter-rouge">T: Send</code></h2>
<p>We want to work out when it is safe to transfer a mutable reference
<code class="language-plaintext highlighter-rouge">&mut T</code> between threads. For the shared reference <code class="language-plaintext highlighter-rouge">&T</code> it is easy:
replacing <code class="language-plaintext highlighter-rouge">&T</code> with <code class="language-plaintext highlighter-rouge">U</code> in the definition of <code class="language-plaintext highlighter-rouge">Sync</code> gives the
definition of <code class="language-plaintext highlighter-rouge">Send</code> (up to alpha renaming), so <code class="language-plaintext highlighter-rouge">&T: Send</code> when <code class="language-plaintext highlighter-rouge">T:
Sync</code>, which can be
<a href="https://github.com/rust-lang/rust/blob/522d09dfecbeca1595f25ac58c6d0178bbd21d7d/src/libcore/marker.rs#L388">expressed in code</a>
as</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
</pre></td><td class="code"><pre><span class="k">unsafe</span> <span class="k">impl</span><span class="o"><</span><span class="nv">'a</span><span class="p">,</span> <span class="n">T</span><span class="p">:</span> <span class="n">Sync</span><span class="o">></span> <span class="nb">Send</span> <span class="k">for</span> <span class="o">&</span><span class="nv">'a</span> <span class="n">T</span> <span class="p">{</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>For <code class="language-plaintext highlighter-rouge">&mut</code>, you might suspect that thread-safety might depend on
<code class="language-plaintext highlighter-rouge">Sync</code> in some way since that trait is so important for the other
reference type, but, you’d be wrong. It is another example of the
dramatic semantic difference between <code class="language-plaintext highlighter-rouge">&mut</code> and <code class="language-plaintext highlighter-rouge">&</code> despite the
syntactic similarities.</p>
<p>The mutable reference type has the guarantee that it is globally
unaliased, so if a thread has access to a piece of data via a <code class="language-plaintext highlighter-rouge">&mut</code>,
then it is the only thread in the whole program that can legally read
from/write to that data. In particular, there’s no sharing and there
cannot be several threads concurrently accessing that memory at the
same time, hence the sharing-related guarantees of <code class="language-plaintext highlighter-rouge">Sync</code> don’t
guarantee thread-safety for <code class="language-plaintext highlighter-rouge">&mut</code>. (RFC 458 describes this as <code class="language-plaintext highlighter-rouge">&mut</code>
linearizing access to its contents.)</p>
<p>Thinking about this, it seems like transferring a <code class="language-plaintext highlighter-rouge">&mut T</code> between
threads might <em>almost</em> be safe for any <code class="language-plaintext highlighter-rouge">T</code>. The thinking might be that
<code class="language-plaintext highlighter-rouge">Send</code> describes “passing by-value” between threads, and this passing
changes fundamental properties of the program, such as where/when
destructors are run; on the other hand, passing a <code class="language-plaintext highlighter-rouge">&mut T</code> is passing
by reference so doesn’t change such things, and a <code class="language-plaintext highlighter-rouge">&mut</code> has unique
access, so the whole set-up is basically the same as running on the
original thread. For example, passing a <code class="language-plaintext highlighter-rouge">&mut T</code> around doesn’t change
where/when the destructor of the <code class="language-plaintext highlighter-rouge">T</code> is run: it is still run when it
goes out of scope, on the main thread.</p>
<p>Unfortunately…</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="code"><pre><span class="c">// we start with some `T` on the main thread</span>
<span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="n">T</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="c">// wrap it up</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">packet</span><span class="p">:</span> <span class="nb">Option</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
<span class="nn">thread</span><span class="p">::</span><span class="nf">scoped</span><span class="p">(||</span> <span class="p">{</span>
<span class="c">// and transfer just a mutable reference to the other thread</span>
<span class="k">let</span> <span class="n">y</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nb">Option</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="o">=</span> <span class="o">&</span><span class="k">mut</span> <span class="n">packet</span><span class="p">;</span>
<span class="c">// and then steal the `T` out!</span>
<span class="k">let</span> <span class="n">z</span><span class="p">:</span> <span class="n">T</span> <span class="o">=</span> <span class="n">y</span><span class="nf">.take</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>
<span class="p">})</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>That code transfers the <code class="language-plaintext highlighter-rouge">T</code> between two threads by just transferring a
<code class="language-plaintext highlighter-rouge">&mut Option<T></code>. Hence, if it is illegal to transfer a <code class="language-plaintext highlighter-rouge">T</code> between
threads, by-value, it must also be illegal to transfer <code class="language-plaintext highlighter-rouge">&mut
Option<T></code> because we can use that to construct a <code class="language-plaintext highlighter-rouge">T</code> transfer. In
pseudo-code, if <code class="language-plaintext highlighter-rouge">T: !Send</code>, then <code class="language-plaintext highlighter-rouge">&mut Option<T>: !Send</code> (using
“negative bounds”, meaning <code class="language-plaintext highlighter-rouge">T</code> does not implement <code class="language-plaintext highlighter-rouge">Send</code>).</p>
<p>Of course, this isn’t a failing of <code class="language-plaintext highlighter-rouge">Option</code> itself, its just a type
that makes for a direct example. One could also create one <code class="language-plaintext highlighter-rouge">T</code> in each
thread, and use <code class="language-plaintext highlighter-rouge">std::mem::swap</code> to exchange their places, causing
both <code class="language-plaintext highlighter-rouge">T</code>s to transfer by-value between threads (double the
unsafety!!).</p>
<p>The general rule is that transferring a <code class="language-plaintext highlighter-rouge">&mut T</code> between threads is
guaranteed to be safe if <code class="language-plaintext highlighter-rouge">T: Send</code>, so <code class="language-plaintext highlighter-rouge">&mut T</code> behaves <em>very</em> much
like <code class="language-plaintext highlighter-rouge">T</code> with relation to concurrency. (It is <em>theoretically</em> possible
to have types for which sending a <code class="language-plaintext highlighter-rouge">&mut T</code> is safe, but sending a
plain <code class="language-plaintext highlighter-rouge">T</code> is not, meaning <code class="language-plaintext highlighter-rouge">&mut T: Send</code> but not <code class="language-plaintext highlighter-rouge">T: Send</code>, so the
relationship is not “if and only if”, as wrongerontheinternet pointed
out on /r/rust.)</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="http://users.rust-lang.org/t/some-notes-on-send-and-sync/400">users</a></li>
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/2wjwcl/some_notes_on_send_and_sync/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Some%20notes%20on%20Send%20and%20Sync%20%23rustlang&url=https://huonw.github.io/blog/2015/02/some-notes-on-send-and-sync/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/02/some-notes-on-send-and-sync/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/02/some-notes-on-send-and-sync/&title=Some%20notes%20on%20Send%20and%20Sync" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
https://huonw.github.io/blog/2015/02/rust-sydney-1Rust Sydney's first meetup: trip report2015-02-14T00:00:00+00:002015-02-14T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p><a href="http://meetup.com/Rust-Sydney">Rust Sydney</a> had its first event last
Monday! The first Rust gathering I know of in Sydney, in Australia,
or in the whole southern hemisphere.</p>
<p><img src="logo.png" alt="Logo" title="The logo; with apologies to the Rust logo and the Sydney Harbour Bridge" /></p>
<p>I had fun meeting all sorts of people interested in Rust: some who’d
dived in deep already, some who’d only recently started playing with
it, and some who’d been following for a while but had unfortunately
not got time to actually use it (hopefully soon!).</p>
<p>Thanks to everyone who came and, to
<a href="http://envoyat.com">Envoy Advanced Technologies</a> for providing the
space and drinks, free of charge.</p>
<p>I started off with a brief talk, covering where Rust is now (and
<a href="https://github.com/rust-lang/rust/pull/22319">will be soon</a>), built
around the classic haiku:</p>
<blockquote>
<p>a systems language<br />
pursuing the trifecta<br />
safe, concurrent, fast</p>
</blockquote>
<p><a href="http://huonw.github.io/rust-sydney-feb15/">My slides are online</a>,
although there’s no recording so they may not be so interesting. But
you <em>can</em> get the human part of the experience by imagining this
picture of me is moving.</p>
<p><img src="awkward-me.jpg" alt="Me in front of my slides" title="Me, standing awkwardly as I begin" /></p>
<p>(I was fortunate enough to schedule the meetup when Servo developer
<a href="https://twitter.com/SimonSapin">Simon Sapin</a> happened to be in
Sydney; he took all three photos in this article.)</p>
<p>I wrapped up and handed over to Steve Klabnik: he was in town for a
conference and was the reason I was prompted into organising
something, so Rust Sydney could start with a bang. We got a reprisal
of his FOSDEM talk
<a href="https://fosdem.org/2015/schedule/event/the_story_of_rust/">“The story of Rust”</a>
which was a really interesting overview of Rust since beginning as
Graydon Hoare’s personal project around 2007, up until the
<a href="http://blog.rust-lang.org/2015/02/13/Final-1.0-timeline.html">impending 1.0</a>
release. <a href="http://www.steveklabnik.com/fosdem2015/">His slides</a> (use
left/right arrow keys to navigate) are also online, and hopefully the
recording from FOSDEM will appear in the near future.</p>
<p><img src="steve.jpg" alt="Steve Klabnik in front of his slides" title="Steve, starting off" /></p>
<p>It was pleasantly surprising to see how many people were interested
enough in Rust to come to a meetup, at least 30. More than I had
expected and I didn’t even do much advertising. It was awesome!</p>
<p><img src="crowd-small.jpg" alt="The crowd" title="Intent listeners" /></p>
<p>I’ll be trying to organise more events, so be sure sign up to
<a href="http://meetup.com/Rust-Sydney">Rust Sydney</a> to keep track (although I
will post to <a href="http://users.rust-lang.org">users.r-l.o</a> and
<a href="http://reddit.com/r/rust">/r/rust</a> too). I hope to see you there.</p>
<p>(I have a <a href="https://github.com/huonw/rust-sydney/"><code class="language-plaintext highlighter-rouge">rust-sydney</code></a>
repository for collecting related things.)</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="http://users.rust-lang.org/t/rust-sydneys-first-meetup-trip-report/341">users</a></li>
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/2vvat9/rust_sydneys_first_meetup_trip_report/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Rust%20Sydney's%20first%20meetup:%20trip%20report%20%23rustlang&url=https://huonw.github.io/blog/2015/02/rust-sydney-1/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/02/rust-sydney-1/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/02/rust-sydney-1/&title=Rust%20Sydney's%20first%20meetup:%20trip%20report" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
https://huonw.github.io/blog/2015/01/object-safetyObject Safety2015-01-13T00:00:00+00:002015-01-13T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>A trait object in <a href="http://rust-lang.org">Rust</a><sup id="fnref:version" role="doc-noteref"><a href="#fn:version" class="footnote" rel="footnote">0</a></sup> can only be
constructed out of traits that satisfy certain restrictions, which are
collectively called “object safety”. This object safety can appear to
be a needless restriction at first, I’ll try to give a deeper
understanding into why it exists and related compiler behaviour.</p>
<p>This is the second (and a half) in a short series of articles on trait
objects. The first
one—<a href="/blog/2015/01/peeking-inside-trait-objects/">Peeking inside Trait Objects</a>—set the scene by
looking into the low-level implementation details of trait objects,
and the
first-and-a-half-th—<a href="/blog/2015/01/the-sized-trait/">an interlude about <code class="language-plaintext highlighter-rouge">Sized</code></a>—looked
at the special <code class="language-plaintext highlighter-rouge">Sized</code> trait. I strongly recommended at least glancing
over it to be familiar with trait objects, vtables and <code class="language-plaintext highlighter-rouge">Sized</code>,
since this post builds on those concepts.</p>
<details class="post-series">
<summary class="post-series-title">Other posts in this series on trait objects</summary>
<ol class="post-series-list">
<li class=""><a href="/blog/2015/01/peeking-inside-trait-objects/">Peeking inside Trait Objects</a></li>
<li class=""><a href="/blog/2015/01/the-sized-trait/">The Sized Trait</a></li>
<li class="current"><a href="/blog/2015/01/object-safety/">Object Safety</a></li>
<li class=""><a href="/blog/2015/05/where-self-meets-sized-revisiting-object-safety/">Where Self Meets Sized: Revisting Object Safety</a></li>
</ol>
</details>
<h2 id="motivation">Motivation</h2>
<p>The notion of object safety was introduced in <a href="https://github.com/rust-lang/rfcs/blob/master/text/0255-object-safety.md">RFC 255</a>, with
the motivation that one should be able to use the dynamic trait object
types <code class="language-plaintext highlighter-rouge">Foo</code> (as a type) in more places where a “static” <code class="language-plaintext highlighter-rouge">Foo</code> (as a
trait) generic is expected. In a sense, it is bringing the two uses of
traits—static dispatch and dynamic dispatch—closer together,
reducing special handling in the language.</p>
<p>The high-level behaviour/restriction imposed by that RFC is: a trait
object—<code class="language-plaintext highlighter-rouge">&Foo</code>, <code class="language-plaintext highlighter-rouge">&mut Foo</code>, etc.—can only be made out of a trait
<code class="language-plaintext highlighter-rouge">Foo</code> if <code class="language-plaintext highlighter-rouge">Foo</code> is object safe. This section will focus on borrowed <code class="language-plaintext highlighter-rouge">&</code>
trait objects, but what is said applies to any.</p>
<p>Let’s look at an example of the things object safety enables: if we
have a trait <code class="language-plaintext highlighter-rouge">Foo</code> and a function like</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
</pre></td><td class="code"><pre><span class="k">fn</span> <span class="n">func</span><span class="o"><</span><span class="n">T</span><span class="p">:</span> <span class="n">Foo</span> <span class="o">+</span> <span class="o">?</span><span class="n">Sized</span><span class="o">></span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="o">&</span><span class="n">T</span><span class="p">)</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>It would be nice to be able to call it like <code class="language-plaintext highlighter-rouge">func(object)</code> where
<code class="language-plaintext highlighter-rouge">object: &Foo</code>; that is, take <code class="language-plaintext highlighter-rouge">T</code> to be the dynamically sized type
<code class="language-plaintext highlighter-rouge">Foo</code>. As you might guess from the context, it is not possible to
do this without some notion of object safety: the arbitrary piece of
code <code class="language-plaintext highlighter-rouge">...</code> can do bad (uncontrolled) things.</p>
<p>Take it on faith (for a few paragraphs) that calling a generic method
is one example of something that can’t be done on a trait object. So,
let’s define a trait and a function like:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Bad</span> <span class="p">{</span>
<span class="k">fn</span> <span class="n">generic_method</span><span class="o"><</span><span class="n">A</span><span class="o">></span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="n">A</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="n">func</span><span class="o"><</span><span class="n">T</span><span class="p">:</span> <span class="n">Bad</span> <span class="o">+</span> <span class="o">?</span><span class="n">Sized</span><span class="o">></span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="o">&</span><span class="n">T</span><span class="p">)</span> <span class="p">{</span>
<span class="n">x</span><span class="nf">.generic_method</span><span class="p">(</span><span class="s">"foo"</span><span class="p">);</span> <span class="c">// A = &str</span>
<span class="n">x</span><span class="nf">.generic_method</span><span class="p">(</span><span class="mi">1_u8</span><span class="p">);</span> <span class="c">// A = u8</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The function <code class="language-plaintext highlighter-rouge">func</code> <em>can’t</em> be called like <code class="language-plaintext highlighter-rouge">foo(obj)</code> where <code class="language-plaintext highlighter-rouge">obj</code> is a
trait object <code class="language-plaintext highlighter-rouge">&Bad</code> because the generic method calls are
illegal. There’s a possible approaches here, like</p>
<ol>
<li>have signatures like <code class="language-plaintext highlighter-rouge"><T: Foo + ?Sized>(x: &T)</code> not work with <code class="language-plaintext highlighter-rouge">T =
Foo</code> by default, for any trait <code class="language-plaintext highlighter-rouge">Foo</code>,</li>
<li>check the body of the function to see if it is legal to have <code class="language-plaintext highlighter-rouge">T =
Bad</code> when we ask for that, or</li>
<li>ensure that we can never pass a <code class="language-plaintext highlighter-rouge">&Bad</code> into <code class="language-plaintext highlighter-rouge">func</code>.</li>
</ol>
<p>Approach 1 is what existed before object safety, and is what object
safety was designed to solve. Approach 2 violates Rust’s goal of
needing to know only the signatures of any function/method called to
type-check a program. That is, if one satisfies the signature one can
call it, unlike C++, there’s no need to type-check internal code of
each the actual instantiation of a generic because the signatures
guarantee that the internals will be legal.</p>
<p>Approach 3 is the one that Rust takes via object safety, by ensuring
that it is impossible to ever encounter a scenario in which a function
with signature <code class="language-plaintext highlighter-rouge">fn func<T: Foo + ?Sized>(x: &T)</code> that does bad things,
could have <code class="language-plaintext highlighter-rouge">T == Foo</code>. That is, make it so that the only way that a
<code class="language-plaintext highlighter-rouge">&Foo</code> can be created is if there’s no way that <code class="language-plaintext highlighter-rouge">func</code> can misbehave.</p>
<p>Object safety and those sort of function signatures apply particularly
to UFCS (uniform function call syntax), which allows one to call
methods as normal, generic function scoped under the type/trait in
which they are defined, for example, the UFCS function
<code class="language-plaintext highlighter-rouge">Bad::generic_method</code> from the trait above effectively has signature:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
</pre></td><td class="code"><pre><span class="k">fn</span> <span class="nn">Bad</span><span class="p">::</span><span class="n">generic_method</span><span class="o"><</span><span class="n">Self</span><span class="p">:</span> <span class="n">Bad</span> <span class="o">+</span> <span class="o">?</span><span class="n">Sized</span><span class="p">,</span> <span class="n">A</span><span class="o">></span><span class="p">(</span><span class="k">self</span><span class="p">:</span> <span class="o">&</span><span class="n">Self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="n">A</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>If <code class="language-plaintext highlighter-rouge">fn method(&self)</code> comes from a trait <code class="language-plaintext highlighter-rouge">Foo</code>, <code class="language-plaintext highlighter-rouge">x.method()</code> can
always be rewritten to <code class="language-plaintext highlighter-rouge">Foo::method(x)</code> (modulo auto-deref and
auto-ref, which possibly add an <code class="language-plaintext highlighter-rouge">&</code> and/or some number of <code class="language-plaintext highlighter-rouge">*</code>s),
however, without object safety, it may not be possible to write
<code class="language-plaintext highlighter-rouge">trait_object.method()</code> as <code class="language-plaintext highlighter-rouge">Foo::method(trait_object)</code>. Object safety
guarantees this transformation is always valid—making UFCS and
method calls essentially equivalent—by outlawing creating a trait
object in situations where it would be invalid.</p>
<h2 id="how-it-works">How it works</h2>
<p>After <a href="https://github.com/rust-lang/rfcs/blob/master/text/0546-Self-not-sized-by-default.md">RFC 546</a> and <a href="https://github.com/rust-lang/rust/pull/20341">PR 20341</a>, making trait objects
automatically work with those sort of generic functions is achieved by
effectively having the compiler implicitly create an implementation of
<code class="language-plaintext highlighter-rouge">Foo</code> (as a trait) for <code class="language-plaintext highlighter-rouge">Foo</code> (as a type). Each method of the trait is
implemented to call into the corresponding method in the vtable. In
the explicit notation of <a href="/blog/2015/01/peeking-inside-trait-objects/">my previous post</a>, the
situation might look something like:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Foo</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">method1</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">);</span>
<span class="k">fn</span> <span class="nf">method2</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">i32</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="k">-></span> <span class="nb">usize</span><span class="p">;</span>
<span class="p">}</span>
<span class="c">// autogenerated impl</span>
<span class="k">impl</span><span class="o"><</span><span class="nv">'a</span><span class="o">></span> <span class="n">Foo</span> <span class="k">for</span> <span class="n">Foo</span><span class="o">+</span><span class="nv">'a</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">method1</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
<span class="c">// `self` is an `&Foo` trait object.</span>
<span class="c">// load the right function pointer and call it with the opaque data pointer</span>
<span class="p">(</span><span class="k">self</span><span class="py">.vtable.method1</span><span class="p">)(</span><span class="k">self</span><span class="py">.data</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">method2</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">i32</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="k">-></span> <span class="nb">usize</span> <span class="p">{</span>
<span class="c">// `self` is an `&mut Foo` trait object</span>
<span class="c">// as above, passing along the other arguments</span>
<span class="p">(</span><span class="k">self</span><span class="py">.vtable.method2</span><span class="p">)(</span><span class="k">self</span><span class="py">.data</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>To be clear: the <code class="language-plaintext highlighter-rouge">.vtable</code> and <code class="language-plaintext highlighter-rouge">.data</code> notation doesn’t work directly
on trait objects, so that code has no hope of compiling, I am just
being explicit about actual behaviour.</p>
<h2 id="object-safety">Object safety</h2>
<p>The rules for object safety were set-out in that initial
<a href="https://github.com/rust-lang/rfcs/blob/master/text/0255-object-safety.md">RFC 255</a>, with two missed cases identified and resolved in
<a href="https://github.com/rust-lang/rfcs/issues/428">RFC 428</a> and <a href="https://github.com/rust-lang/rfcs/blob/master/text/0546-Self-not-sized-by-default.md">RFC 546</a>. At the time of writing, the
possible ways to be object-unsafe are described
<a href="https://github.com/rust-lang/rust/blob/2127e0d56d85ff48aafce90ab762650e46370b63/src/librustc/middle/traits/object_safety.rs#L30-L52">by two enums</a>:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
</pre></td><td class="code"><pre><span class="k">pub</span> <span class="k">enum</span> <span class="n">ObjectSafetyViolation</span><span class="o"><</span><span class="nv">'tcx</span><span class="o">></span> <span class="p">{</span>
<span class="c">/// Self : Sized declared on the trait</span>
<span class="n">SizedSelf</span><span class="p">,</span>
<span class="c">/// Method has someting illegal</span>
<span class="nf">Method</span><span class="p">(</span><span class="nb">Rc</span><span class="o"><</span><span class="nn">ty</span><span class="p">::</span><span class="n">Method</span><span class="o"><</span><span class="nv">'tcx</span><span class="o">>></span><span class="p">,</span> <span class="n">MethodViolationCode</span><span class="p">),</span>
<span class="p">}</span>
<span class="c">/// Reasons a method might not be object-safe.</span>
<span class="nd">#[derive(Copy,Clone,Show)]</span>
<span class="k">pub</span> <span class="k">enum</span> <span class="n">MethodViolationCode</span> <span class="p">{</span>
<span class="c">/// e.g., `fn(self)`</span>
<span class="n">ByValueSelf</span><span class="p">,</span>
<span class="c">/// e.g., `fn foo()`</span>
<span class="n">StaticMethod</span><span class="p">,</span>
<span class="c">/// e.g., `fn foo(&self, x: Self)` or `fn foo(&self) -> Self`</span>
<span class="n">ReferencesSelf</span><span class="p">,</span>
<span class="c">/// e.g., `fn foo<A>()`</span>
<span class="n">Generic</span><span class="p">,</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Let’s go through each case.</p>
<p><em>Update 2015-05-06</em>: <a href="https://github.com/rust-lang/rfcs/pull/817">RFC 817</a> added more precise control
over object safety via <code class="language-plaintext highlighter-rouge">where</code> clauses, see
<a href="/blog/2015/05/where-self-meets-sized-revisiting-object-safety/"><em>Where Self Meets Sized: Revisiting Object Safety</em></a>.</p>
<h3 id="sized-self">Sized <code class="language-plaintext highlighter-rouge">Self</code></h3>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Foo</span><span class="p">:</span> <span class="n">Sized</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">method</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The trait <code class="language-plaintext highlighter-rouge">Foo</code> inherits from <code class="language-plaintext highlighter-rouge">Sized</code>, requiring the <code class="language-plaintext highlighter-rouge">Self</code> type to be
sized, and hence writing <code class="language-plaintext highlighter-rouge">impl Foo for Foo</code> is illegal: the type <code class="language-plaintext highlighter-rouge">Foo</code>
is not sized and doesn’t implement <code class="language-plaintext highlighter-rouge">Sized</code>. Traits default to <code class="language-plaintext highlighter-rouge">Self</code>
being possibly-unsized—effectively a bound <code class="language-plaintext highlighter-rouge">Self: ?Sized</code>—to make
more traits object safe by default.</p>
<h3 id="by-value-self">By-value <code class="language-plaintext highlighter-rouge">self</code></h3>
<p><em>Update 2015-05-06</em>: this is no longer object unsafe, but it is
impossible to call such methods on possibly-unsized types, including
trait objects. That is, one can define traits with <code class="language-plaintext highlighter-rouge">self</code> methods,
but one is statically disallowed from call those methods on trait
objects (and on generics that could be trait objects).</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Foo</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">method</span><span class="p">(</span><span class="k">self</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>At the moment<sup id="fnref:change-is-in-the-air" role="doc-noteref"><a href="#fn:change-is-in-the-air" class="footnote" rel="footnote">1</a></sup>, it’s not possible to use trait
objects by-value anywhere, due to the lack of sizedness. If one were
to write an <code class="language-plaintext highlighter-rouge">impl Foo for Foo</code>, the signature of <code class="language-plaintext highlighter-rouge">method</code> would mean
<code class="language-plaintext highlighter-rouge">self</code> has type <code class="language-plaintext highlighter-rouge">Foo</code>: a by-value unsized type, illegal!</p>
<h3 id="static-method">Static method</h3>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Foo</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">func</span><span class="p">()</span> <span class="k">-></span> <span class="nb">i32</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>There’s no way to provide a sensible implementation of <code class="language-plaintext highlighter-rouge">func</code> as a
static method on the type <code class="language-plaintext highlighter-rouge">Foo</code>:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre><span class="k">impl</span><span class="o"><</span><span class="nv">'a</span><span class="o">></span> <span class="n">Foo</span> <span class="k">for</span> <span class="n">Foo</span><span class="o">+</span><span class="nv">'a</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">func</span><span class="p">()</span> <span class="k">-></span> <span class="nb">i32</span> <span class="p">{</span>
<span class="c">// what goes here??</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The compiler can’t just conjure up some <code class="language-plaintext highlighter-rouge">i32</code>—the chosen value may
make no sense in context—and it can’t call some other type’s
<code class="language-plaintext highlighter-rouge">Foo::func</code> method—which type would it choose? The whole scenario
makes no sense.</p>
<h3 id="references-self">References <code class="language-plaintext highlighter-rouge">Self</code></h3>
<p>There’s two fundamental ways in which this can happen, as an argument
or as a return value, in either case a reference to the <code class="language-plaintext highlighter-rouge">Self</code> type
means that it must match the type of the <code class="language-plaintext highlighter-rouge">self</code> value, the true type
of which is unknown at compile time. For example:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Foo</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">method</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">other</span><span class="p">:</span> <span class="o">&</span><span class="n">Self</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The types of the two arguments have to match, but this can’t be
guaranteed with a trait object: the erased types of two separate
<code class="language-plaintext highlighter-rouge">&Foo</code> values may not match:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre><span class="k">impl</span><span class="o"><</span><span class="nv">'a</span><span class="o">></span> <span class="n">Foo</span> <span class="k">for</span> <span class="n">Foo</span><span class="o">+</span><span class="nv">'a</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">method</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">other</span><span class="p">:</span> <span class="o">&</span><span class="p">(</span><span class="n">Foo</span><span class="o">+</span><span class="nv">'a</span><span class="p">))</span>
<span class="p">(</span><span class="k">self</span><span class="py">.vtable.method</span><span class="p">)(</span><span class="k">self</span><span class="py">.data</span><span class="p">,</span> <span class="cm">/* what goes here? */</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>(Using the explicit-but-invalid notation as above.)</p>
<p>One can’t use <code class="language-plaintext highlighter-rouge">other.data</code> because the <code class="language-plaintext highlighter-rouge">method</code> entry of <code class="language-plaintext highlighter-rouge">self.vtable</code>
is assuming that both pointers point to the same, specific type
(whatever type the vtable is specialised for), but there’s absolutely
no guarantee <code class="language-plaintext highlighter-rouge">other.data</code> points to matching data. There’s also not
necessarily a (reliable) way to detect a mismatch, and no way the
compiler can know a correct way to handle a mismatch even if it can be
detected.</p>
<h3 id="generic-method">Generic method</h3>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Foo</span> <span class="p">{</span>
<span class="k">fn</span> <span class="n">method</span><span class="o"><</span><span class="n">A</span><span class="o">></span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">a</span><span class="p">:</span> <span class="n">A</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>As discussed briefly in <a href="/blog/2015/01/peeking-inside-trait-objects/">the first post</a>, generic
functions in Rust are monomorphised, that is, a copy of the function
is created for each type used as a generic parameter. An attempted
implementation might look like</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre><span class="k">impl</span><span class="o"><</span><span class="nv">'a</span><span class="o">></span> <span class="n">Foo</span> <span class="k">for</span> <span class="n">Foo</span><span class="o">+</span><span class="nv">'a</span> <span class="p">{</span>
<span class="k">fn</span> <span class="n">method</span><span class="o"><</span><span class="n">A</span><span class="o">></span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">a</span><span class="p">:</span> <span class="n">A</span><span class="p">)</span> <span class="p">{</span>
<span class="p">(</span><span class="k">self</span><span class="py">.vtable</span><span class="err">.</span><span class="cm">/* ... huh ???*/</span><span class="p">)(</span><span class="k">self</span><span class="py">.data</span><span class="p">,</span> <span class="n">a</span><span class="p">:</span> <span class="n">A</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The vtable is a static struct of function pointers, somehow we have to
select a function pointer from it that will work with the arbitrary
type <code class="language-plaintext highlighter-rouge">A</code>. To have any hope of doing this, one would have<sup id="fnref:alternative" role="doc-noteref"><a href="#fn:alternative" class="footnote" rel="footnote">2</a></sup>
to pregenerate code for every type that could possibly be used for <code class="language-plaintext highlighter-rouge">A</code>
and then fill in the <code class="language-plaintext highlighter-rouge">huh</code> above to select the right one. This would
be effectively implicitly adding a whole series of methods to the
trait:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Foo</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">method_u8</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">);</span> <span class="c">// A = u8</span>
<span class="k">fn</span> <span class="nf">method_i8</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">);</span> <span class="c">// A = i8</span>
<span class="k">fn</span> <span class="nf">method_String</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">);</span> <span class="c">// A = String</span>
<span class="k">fn</span> <span class="nf">method_unit</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">);</span> <span class="c">// A = ()</span>
<span class="c">// ...</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>and each one would need an entry in the vtable struct. If it is even
possible, this would be some serious bloat, especially as I imagine
most possibilities wouldn’t be used.</p>
<p>For the more fundamental question of “is it possible”, the answer is
rarely: it only works if the number of possible types that can be used
with the generic parameters is finite and completely known, so that a
complete list can be written. I think the only circumstance in which
this occurs is if all the parameters have to be bounded by some
private trait (the example above fails, since <code class="language-plaintext highlighter-rouge">A</code> is unbounded and so
can be used with every type ever, including ones that aren’t even
defined in scope).</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/2s2okp/object_safety/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Object%20Safety%20%23rustlang&url=https://huonw.github.io/blog/2015/01/object-safety/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/01/object-safety/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/01/object-safety/&title=Object%20Safety" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:version" role="doc-endnote">
<p><a href="/blog/2015/01/peeking-inside-trait-objects/">As usual</a>, this post is designed to reflect
the state of Rust at version <code class="language-plaintext highlighter-rouge">rustc 1.0.0-nightly
(44a287e6e 2015-01-08 17:03:40 -0800)</code>. <a href="#fnref:version" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:change-is-in-the-air" role="doc-endnote">
<p>There is desire to remove/relax this
restriction for function parameters, and
especially <code class="language-plaintext highlighter-rouge">self</code>, to allow them to be
unsized types. Niko’s <a href="http://smallcultfollowing.com/babysteps/blog/2014/11/26/purging-proc/">“Purging proc”</a>
describes the problem and the necessity for
<a href="http://doc.rust-lang.org/nightly/std/thunk/trait.Invoke.html">the <code class="language-plaintext highlighter-rouge">Invoke</code> trait</a> as a work-around
for the <code class="language-plaintext highlighter-rouge">FnOnce</code> trait. <a href="#fnref:change-is-in-the-air" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:alternative" role="doc-endnote">
<p>Strictly speaking I suppose one could do some type of
runtime codegen/JITing, but that’s not really
something Rust wants to build into the language, as it
would require Rust programs to essentially carry
around a compiler. <a href="#fnref:alternative" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2015/01/the-sized-traitThe Sized Trait2015-01-12T00:00:00+00:002015-01-12T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>An important piece in my story about trait objects in
<a href="http://rust-lang.org">Rust</a><sup id="fnref:version" role="doc-noteref"><a href="#fn:version" class="footnote" rel="footnote">0</a></sup> is <a href="https://doc.rust-lang.org/nightly/std/marker/trait.Sized.html">the <code class="language-plaintext highlighter-rouge">Sized</code> trait</a>,
so I’m slotting in this short post between
<a href="/blog/2015/01/peeking-inside-trait-objects/">my discussion of low-level details</a> and
<a href="/blog/2015/01/object-safety/">the post on “object safety”</a>.</p>
<details class="post-series">
<summary class="post-series-title">Other posts in this series on trait objects</summary>
<ol class="post-series-list">
<li class=""><a href="/blog/2015/01/peeking-inside-trait-objects/">Peeking inside Trait Objects</a></li>
<li class="current"><a href="/blog/2015/01/the-sized-trait/">The Sized Trait</a></li>
<li class=""><a href="/blog/2015/01/object-safety/">Object Safety</a></li>
<li class=""><a href="/blog/2015/05/where-self-meets-sized-revisiting-object-safety/">Where Self Meets Sized: Revisting Object Safety</a></li>
</ol>
</details>
<p><code class="language-plaintext highlighter-rouge">Sized</code> is a (very) special compiler built-in trait that is
automatically implemented or not based on the sizedness of a type. A
type is considered sized if the precise size of a value of type is
known and fixed at compile time once the real types of the type
parameters are known (i.e. after completing monomorphisation). For
example,</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">u8</code> is one byte,</li>
<li><code class="language-plaintext highlighter-rouge">Vec<T></code> is either 12 or 24 bytes (platforms with 32 and 64 bit
pointers respectively), independent of <code class="language-plaintext highlighter-rouge">T</code>,</li>
<li>pointers like <code class="language-plaintext highlighter-rouge">&T</code> are sized too, on 64-bit platforms <code class="language-plaintext highlighter-rouge">&T</code> is either
8 or 16 bytes, for sized <code class="language-plaintext highlighter-rouge">T</code> and unsized <code class="language-plaintext highlighter-rouge">T</code> respectively. This may
seems like the size isn’t known, but the sizedness of <code class="language-plaintext highlighter-rouge">T</code> is always
known at compile time, so the precise one of those options is also
known.</li>
</ul>
<p>Types for which the size is not known are called
<a href="http://smallcultfollowing.com/babysteps/blog/2014/01/05/dst-take-5/">dynamically sized types (DSTs)</a>, and there’s two classes of
examples in current Rust<sup id="fnref:virtual" role="doc-noteref"><a href="#fn:virtual" class="footnote" rel="footnote">1</a></sup>: <code class="language-plaintext highlighter-rouge">[T]</code> and <code class="language-plaintext highlighter-rouge">Trait</code>. A slice<sup id="fnref:str" role="doc-noteref"><a href="#fn:str" class="footnote" rel="footnote">2</a></sup>
<code class="language-plaintext highlighter-rouge">[T]</code> is unsized because it represents an unknown-at-compile-time
number of <code class="language-plaintext highlighter-rouge">T</code>s contiguous in memory. A <code class="language-plaintext highlighter-rouge">Trait</code> is unsized because it
represents a value of any type that implements <code class="language-plaintext highlighter-rouge">Trait</code> and these have
wildly different sizes; I discussed this
<a href="/blog/2015/01/peeking-inside-trait-objects/#why-pointers">in the previous post too</a>. Unsized values must always
appear behind a pointer at runtime, like <code class="language-plaintext highlighter-rouge">&[T]</code> or <code class="language-plaintext highlighter-rouge">Box<Trait></code>, and
have the information required to compute their size and other relevant
properties (the length for <code class="language-plaintext highlighter-rouge">[T]</code>, the vtable for <code class="language-plaintext highlighter-rouge">Trait</code>) stored next
to that pointer.</p>
<p>Sized types are more flexible, since the compiler knows how to
manipulate them directly: passing them directly into functions, moving
them about in memory. Putting an unsized type behind a pointer
effectively makes it sized. A <code class="language-plaintext highlighter-rouge">Box</code> trait object, like <code class="language-plaintext highlighter-rouge">Box<Trait></code>,
is the closest one can get to handling a trait object as a normal
value; the <code class="language-plaintext highlighter-rouge">Box</code> ensures sizedness (at the expense of an allocation)
without fundamentally changing the ownership semantics of a normal
value.</p>
<h2 id="sized"><code class="language-plaintext highlighter-rouge">?Sized</code></h2>
<p>The <code class="language-plaintext highlighter-rouge">Sized</code> trait gets some special syntax for use in bounds, at the
moment: <code class="language-plaintext highlighter-rouge">?Sized</code>. Such a bound is necessary because <code class="language-plaintext highlighter-rouge">Sized</code> is
special: it is a default bound for type parameters in most positions,
and so one needs some way to opt-in to a parameter not necessarily
being sized.</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre><span class="k">fn</span> <span class="n">foo</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">()</span> <span class="p">{}</span> <span class="c">// can only be used with sized T</span>
<span class="k">fn</span> <span class="n">bar</span><span class="o"><</span><span class="n">T</span><span class="p">:</span> <span class="o">?</span><span class="n">Sized</span><span class="o">></span><span class="p">()</span> <span class="p">{}</span> <span class="c">// can be used with both sized and unsized T</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>This bound is particularly special because adding the <code class="language-plaintext highlighter-rouge">?Sized</code> bound
to a parameter <code class="language-plaintext highlighter-rouge">T</code> increases the number of types that can be used for
<code class="language-plaintext highlighter-rouge">T</code>, whereas every other trait bound reduces it.</p>
<p>This unusual decision was chosen because of the increased flexibility
of sized types, and some data (which I now can’t find in the issue
tracker) which indicated that most type parameters needed to be
sized. That is, not having these defaults would result in many
instances of <code class="language-plaintext highlighter-rouge">T: Sized</code> bounds in the standard library and elsewhere.</p>
<p>However, I believe this data did not consider using some form of
inference (like lifetime elision) to try to guess when sizedness was
likely to be needed, and some data Niko has been collecting apparently
implies that such inference may make removing this special case
significantly more palatable. (It may or may not be so palatable so as
to be worth the breaking change…)</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/2s2gee/the_sized_trait/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=The%20Sized%20Trait%20%23rustlang&url=https://huonw.github.io/blog/2015/01/the-sized-trait/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/01/the-sized-trait/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/01/the-sized-trait/&title=The%20Sized%20Trait" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:version" role="doc-endnote">
<p>Per the <a href="/blog/2015/01/peeking-inside-trait-objects/">previous post</a>, this post is
designed to reflect the state of Rust at version: <code class="language-plaintext highlighter-rouge">rustc
1.0.0-nightly (44a287e6e 2015-01-08 17:03:40 -0800)</code>. <a href="#fnref:version" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:virtual" role="doc-endnote">
<p>There is the possibility that Rust will gain some form of
<a href="http://discuss.rust-lang.org/t/summary-of-efficient-inheritance-rfcs/494">“inheritance”</a>, and Niko points out to me that
<code class="language-plaintext highlighter-rouge">Sized</code> may play an important role there too: certain
types (e.g. “base classes” in an conventional inheritance
scheme) make sense to be unsized. <a href="#fnref:virtual" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:str" role="doc-endnote">
<p>The unsized string type <code class="language-plaintext highlighter-rouge">str</code> is usually considered a slice,
since it is just a <code class="language-plaintext highlighter-rouge">[u8]</code> with the guarantee that the bytes
are valid UTF-8. <a href="#fnref:str" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2015/01/peeking-inside-trait-objectsPeeking inside Trait Objects2015-01-10T00:00:00+00:002015-01-10T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>One of the most powerful parts of
<a href="http://rust-lang.org">the Rust programming language</a><sup id="fnref:version" role="doc-noteref"><a href="#fn:version" class="footnote" rel="footnote">0</a></sup> is the
<a href="http://doc.rust-lang.org/nightly/book/traits.html">trait system</a>. They form the basis of the generic system and
polymorphic functions and types. There’s an interesting use of traits,
as so-called “trait objects”, that allows for dynamic polymorphism and
heterogeneous uses of types, which I’m going to look at in more detail
over a short series of posts.</p>
<p><em>Update 2015-02-19</em>: A lot of this document has been copied into
<a href="http://doc.rust-lang.org/nightly/book/trait-objects.html">the main book</a>, with improvements and updates.</p>
<p>This post will set the scene, with an introduction to the internals of
a trait object; the remaining posts will look at the <code class="language-plaintext highlighter-rouge">Sized</code> trait and
“object safety” in detail (a lot of people have encountered trouble
with somewhat abstruse compiler errors about this recently).</p>
<details class="post-series">
<summary class="post-series-title">Other posts in this series on trait objects</summary>
<ol class="post-series-list">
<li class="current"><a href="/blog/2015/01/peeking-inside-trait-objects/">Peeking inside Trait Objects</a></li>
<li class=""><a href="/blog/2015/01/the-sized-trait/">The Sized Trait</a></li>
<li class=""><a href="/blog/2015/01/object-safety/">Object Safety</a></li>
<li class=""><a href="/blog/2015/05/where-self-meets-sized-revisiting-object-safety/">Where Self Meets Sized: Revisting Object Safety</a></li>
</ol>
</details>
<h2 id="traits">Traits</h2>
<p>A simple example of a <code class="language-plaintext highlighter-rouge">trait</code> is this <code class="language-plaintext highlighter-rouge">Foo</code>. It has one method that is
expected to return a <code class="language-plaintext highlighter-rouge">String</code>, and, in the real world, there would be
some expectation about what the string would mean, but this is just a
blog, so you’re free to make up your own favourite meaning.</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre><span class="k">trait</span> <span class="n">Foo</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">method</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">String</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>This can then be implemented for certain types, stating that these
types satisfy whatever behaviours the trait is trying to summarise and
allow polymorphism over. For example, bytes and strings are <code class="language-plaintext highlighter-rouge">Foo</code>,
apparently:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="code"><pre><span class="k">impl</span> <span class="n">Foo</span> <span class="k">for</span> <span class="nb">u8</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">method</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">String</span> <span class="p">{</span> <span class="nd">format!</span><span class="p">(</span><span class="s">"u8: {}"</span><span class="p">,</span> <span class="o">*</span><span class="k">self</span><span class="p">)</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="n">Foo</span> <span class="k">for</span> <span class="nb">String</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">method</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">String</span> <span class="p">{</span> <span class="nd">format!</span><span class="p">(</span><span class="s">"string: {}"</span><span class="p">,</span> <span class="o">*</span><span class="k">self</span><span class="p">)</span> <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>There’s two basic ways to use traits to be polymorphic:</p>
<p>The first and most common are generic functions like <code class="language-plaintext highlighter-rouge">fn func<T:
Foo>(x: &T)</code>. These are implemented via monomorphisation, the compiler
creates a specialised version of the generic function for every type
used with it. This has some upsides—static dispatching of any
method calls<sup id="fnref:upsides" role="doc-noteref"><a href="#fn:upsides" class="footnote" rel="footnote">1</a></sup>, allowing for inlining and hence usually higher
performance—and some downsides—causing code bloat due to
many copies of the same function existing in the binary, one for each
type<sup id="fnref:one-per-type" role="doc-noteref"><a href="#fn:one-per-type" class="footnote" rel="footnote">2</a></sup>.</p>
<p>Fortunately, there’s second option if that trade-off is inappropriate,
or if being required to know every type everywhere is impossible or
undesirable.</p>
<h2 id="trait-objects">Trait objects</h2>
<p>Trait objects, like <code class="language-plaintext highlighter-rouge">&Foo</code> or <code class="language-plaintext highlighter-rouge">Box<Foo></code>, are normal values that store
a value of <em>any</em> type that implements the given trait, where the
precise type can only be known at runtime. The methods of the trait
can be called on a trait object via a special record of function
pointers (created and managed by the compiler).</p>
<p>A function that takes a trait object—say <code class="language-plaintext highlighter-rouge">fn func(x: &Foo)</code>—is not
specialised to each of the types that implements <code class="language-plaintext highlighter-rouge">Foo</code>: only one copy
is generated, often (but not always) resulting in less code
bloat. However, this comes at the cost of requiring slower virtual
function calls, and effectively inhibiting any chance of inlining and
related optimisations from occurring.</p>
<p>Trait objects are both simple and complicated: their core
representation and layout is quite straight-forward, but there are
some curly error messages and surprising behaviours to discover.</p>
<h2 id="obtaining-a-trait-object">Obtaining a trait object</h2>
<p>There’s two similar ways to get a trait object value: casts and
coercions. If <code class="language-plaintext highlighter-rouge">T</code> is a type that implements a trait <code class="language-plaintext highlighter-rouge">Foo</code> (e.g. <code class="language-plaintext highlighter-rouge">u8</code>
for the <code class="language-plaintext highlighter-rouge">Foo</code> above), then the two ways to get a <code class="language-plaintext highlighter-rouge">Foo</code> trait object
out of a pointer to <code class="language-plaintext highlighter-rouge">T</code> look like:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre><span class="k">let</span> <span class="n">ref_to_t</span><span class="p">:</span> <span class="o">&</span><span class="n">T</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="c">// `as` keyword for casting</span>
<span class="k">let</span> <span class="n">cast</span> <span class="o">=</span> <span class="n">ref_to_t</span> <span class="k">as</span> <span class="o">&</span><span class="n">Foo</span><span class="p">;</span>
<span class="c">// using a `&T` in a place that has a known type of `&Foo` will implicitly coerce:</span>
<span class="k">let</span> <span class="n">coerce</span><span class="p">:</span> <span class="o">&</span><span class="n">Foo</span> <span class="o">=</span> <span class="n">ref_to_t</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">also_coerce</span><span class="p">(</span><span class="mi">_u</span><span class="n">nused</span><span class="p">:</span> <span class="o">&</span><span class="n">Foo</span><span class="p">)</span> <span class="p">{}</span>
<span class="nf">also_coerce</span><span class="p">(</span><span class="n">ref_to_t</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>These trait object coercions and casts also work for pointers like
<code class="language-plaintext highlighter-rouge">&mut T</code> to <code class="language-plaintext highlighter-rouge">&mut Foo</code> and <code class="language-plaintext highlighter-rouge">Box<T></code> to <code class="language-plaintext highlighter-rouge">Box<Foo></code>, but that’s all at
the moment. Other than some bugs, coercions and casts are identical.</p>
<p>This operation can be seen as “erasing” the compiler’s knowledge about
the specific type of the pointer, and hence trait objects are
sometimes referred to “type erasure”.</p>
<h2 id="representation">Representation</h2>
<p>Let’s start simple, with the runtime representation of a trait
object. The <code class="language-plaintext highlighter-rouge">std::raw</code> module contains structs with layouts that are
the same as the complicated build-in types,
<a href="http://doc.rust-lang.org/nightly/std/raw/struct.TraitObject.html">including trait objects</a>:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="code"><pre><span class="k">pub</span> <span class="k">struct</span> <span class="n">TraitObject</span> <span class="p">{</span>
<span class="k">pub</span> <span class="n">data</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="p">(),</span>
<span class="k">pub</span> <span class="n">vtable</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="p">(),</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>That is, a trait object like <code class="language-plaintext highlighter-rouge">&Foo</code> consists of a “data” pointer and a
“vtable” pointer.</p>
<p>The data pointer addresses the data (of some unknown type <code class="language-plaintext highlighter-rouge">T</code>) that
the trait object is storing, and the vtable pointer points to the
<a href="https://en.wikipedia.org/wiki/Virtual_method_table">vtable</a> (“virtual method table”) corresponding to the implementation
of <code class="language-plaintext highlighter-rouge">Foo</code> for <code class="language-plaintext highlighter-rouge">T</code>.</p>
<p>A vtable is essentially a struct of function pointers, pointing to the
concrete piece of machine code for each method in the
implementation. A method call like <code class="language-plaintext highlighter-rouge">trait_object.method()</code> will
retrieve the correct pointer out of the vtable and then do a dynamic
call of it. For example:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">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
</pre></td><td class="code"><pre><span class="k">struct</span> <span class="n">FooVtable</span> <span class="p">{</span>
<span class="n">destructor</span><span class="p">:</span> <span class="k">fn</span><span class="p">(</span><span class="o">*</span><span class="k">mut</span> <span class="p">()),</span>
<span class="n">size</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span>
<span class="n">align</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span>
<span class="n">method</span><span class="p">:</span> <span class="k">fn</span><span class="p">(</span><span class="o">*</span><span class="k">const</span> <span class="p">())</span> <span class="k">-></span> <span class="nb">String</span><span class="p">,</span>
<span class="p">}</span>
<span class="c">// u8:</span>
<span class="k">fn</span> <span class="nf">call_method_on_u8</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="o">*</span><span class="k">const</span> <span class="p">())</span> <span class="k">-></span> <span class="nb">String</span> <span class="p">{</span>
<span class="c">// the compiler guarantees that this function is only called</span>
<span class="c">// with `x` pointing to a u8</span>
<span class="k">let</span> <span class="n">byte</span><span class="p">:</span> <span class="o">&</span><span class="nb">u8</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="o">&*</span><span class="p">(</span><span class="n">x</span> <span class="k">as</span> <span class="o">*</span><span class="k">const</span> <span class="nb">u8</span><span class="p">)</span> <span class="p">};</span>
<span class="n">byte</span><span class="nf">.method</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">static</span> <span class="n">Foo_for_u8_vtable</span><span class="p">:</span> <span class="n">FooVtable</span> <span class="o">=</span> <span class="n">FooVtable</span> <span class="p">{</span>
<span class="n">destructor</span><span class="p">:</span> <span class="cm">/* compiler magic */</span><span class="p">,</span>
<span class="n">size</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="n">align</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="c">// cast to a function pointer</span>
<span class="n">method</span><span class="p">:</span> <span class="n">call_method_on_u8</span> <span class="k">as</span> <span class="k">fn</span><span class="p">(</span><span class="o">*</span><span class="k">const</span> <span class="p">())</span> <span class="k">-></span> <span class="nb">String</span><span class="p">,</span>
<span class="p">};</span>
<span class="c">// String:</span>
<span class="k">fn</span> <span class="nf">call_method_on_String</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="o">*</span><span class="k">const</span> <span class="p">())</span> <span class="k">-></span> <span class="nb">String</span> <span class="p">{</span>
<span class="c">// the compiler guarantees that this function is only called</span>
<span class="c">// with `x` pointing to a String</span>
<span class="k">let</span> <span class="n">string</span><span class="p">:</span> <span class="o">&</span><span class="nb">String</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="o">&*</span><span class="p">(</span><span class="n">x</span> <span class="k">as</span> <span class="o">*</span><span class="k">const</span> <span class="nb">String</span><span class="p">)</span> <span class="p">};</span>
<span class="n">string</span><span class="nf">.method</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">static</span> <span class="n">Foo_for_String_vtable</span><span class="p">:</span> <span class="n">FooVtable</span> <span class="o">=</span> <span class="n">FooVtable</span> <span class="p">{</span>
<span class="n">destructor</span><span class="p">:</span> <span class="cm">/* compiler magic */</span><span class="p">,</span>
<span class="c">// values for a 64-bit computer, halve them for 32-bit ones</span>
<span class="n">size</span><span class="p">:</span> <span class="mi">24</span><span class="p">,</span>
<span class="n">align</span><span class="p">:</span> <span class="mi">8</span><span class="p">,</span>
<span class="n">method</span><span class="p">:</span> <span class="n">call_method_on_String</span> <span class="k">as</span> <span class="k">fn</span><span class="p">(</span><span class="o">*</span><span class="k">const</span> <span class="p">())</span> <span class="k">-></span> <span class="nb">String</span><span class="p">,</span>
<span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>(The <code class="language-plaintext highlighter-rouge">call_method_on_...</code> functions could also be UFCS: <code class="language-plaintext highlighter-rouge"><... as
Foo>::method</code>, but that’s somewhat less clear.)</p>
<p>The <code class="language-plaintext highlighter-rouge">destructor</code> field in each vtable points to a function that will
clean up any resources of the vtable’s type, for <code class="language-plaintext highlighter-rouge">u8</code> it is trivial,
but for <code class="language-plaintext highlighter-rouge">String</code> it will free the memory. This is necessary for owning
trait objects like <code class="language-plaintext highlighter-rouge">Box<Foo></code>, which need to clean-up both the <code class="language-plaintext highlighter-rouge">Box</code>
allocation and as well as the internal type when they go out of
scope. The <code class="language-plaintext highlighter-rouge">size</code> and <code class="language-plaintext highlighter-rouge">align</code> fields store the size of the erased
type, and its alignment requirements; these are essentially unused at
the moment since the information is embedded in the destructor, but
will be used in future, as trait objects are progressively made more
flexible.</p>
<p>Suppose we’ve got some values that implement <code class="language-plaintext highlighter-rouge">Foo</code>, the explicit form
of construction and use of <code class="language-plaintext highlighter-rouge">Foo</code> trait objects might look a bit like
(ignoring the type mismatches: they’re all just pointers anyway):</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
</pre></td><td class="code"><pre><span class="k">let</span> <span class="n">a</span><span class="p">:</span> <span class="nb">String</span> <span class="o">=</span> <span class="s">"foo"</span><span class="nf">.to_string</span><span class="p">();</span>
<span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="nb">u8</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="c">// let b: &Foo = &a;</span>
<span class="k">let</span> <span class="n">b</span> <span class="o">=</span> <span class="n">TraitObject</span> <span class="p">{</span>
<span class="c">// store the data</span>
<span class="n">data</span><span class="p">:</span> <span class="o">&</span><span class="n">a</span><span class="p">,</span>
<span class="c">// store the methods</span>
<span class="n">vtable</span><span class="p">:</span> <span class="o">&</span><span class="n">Foo_for_String_vtable</span>
<span class="p">};</span>
<span class="c">// let y: &Foo = x;</span>
<span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="n">TraitObject</span> <span class="p">{</span>
<span class="c">// store the data</span>
<span class="n">data</span><span class="p">:</span> <span class="o">&</span><span class="n">x</span><span class="p">,</span>
<span class="c">// store the methods</span>
<span class="n">vtable</span><span class="p">:</span> <span class="o">&</span><span class="n">Foo_for_u8_vtable</span>
<span class="p">};</span>
<span class="c">// b.method();</span>
<span class="p">(</span><span class="n">b</span><span class="py">.vtable.method</span><span class="p">)(</span><span class="n">b</span><span class="py">.data</span><span class="p">);</span>
<span class="c">// y.method();</span>
<span class="p">(</span><span class="n">y</span><span class="py">.vtable.method</span><span class="p">)(</span><span class="n">y</span><span class="py">.data</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>If <code class="language-plaintext highlighter-rouge">b</code> or <code class="language-plaintext highlighter-rouge">y</code> were owning trait objects (<code class="language-plaintext highlighter-rouge">Box<Foo></code>), there would be a
<code class="language-plaintext highlighter-rouge">(b.vtable.destructor)(b.data)</code> (respectively <code class="language-plaintext highlighter-rouge">y</code>) call when they went
out of scope.</p>
<h3 id="why-pointers">Why pointers?</h3>
<p>The use of language like “fat pointer” implies that a trait object is
always a pointer of some form, but why? I wrote above that</p>
<blockquote>
<p>[Trait objects] are normal values and can store a value of <em>any</em> type
that implements the given trait, where the precise type can only
be known at runtime.</p>
</blockquote>
<p>Rust does not put things behind a pointer by default, unlike many
managed languages, so types can have different sizes. Knowing the size
of the value at compile time is important for things like passing it
as an argument to a function, moving it about on the stack and
allocating (and deallocating) space on the heap to store it.</p>
<p>For <code class="language-plaintext highlighter-rouge">Foo</code>, we would need to have a value that could be at least either
a <code class="language-plaintext highlighter-rouge">String</code> (24 bytes) or a <code class="language-plaintext highlighter-rouge">u8</code> (1 byte), as well as any other type
for which dependent crates may implement <code class="language-plaintext highlighter-rouge">Foo</code> (any number of bytes at
all). There’s no way to guarantee that this last point can work if the
values are stored without a pointer, because those other types can be
arbitrarily large.</p>
<p>Putting the value behind a pointer means the size of the value is not
relevant when we are tossing a trait object around, only the size of
the pointer itself.</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/2rutqb/peeking_inside_trait_objects/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Peeking%20inside%20Trait%20Objects%20%23rustlang&url=https://huonw.github.io/blog/2015/01/peeking-inside-trait-objects/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/01/peeking-inside-trait-objects/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/01/peeking-inside-trait-objects/&title=Peeking%20inside%20Trait%20Objects" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:version" role="doc-endnote">
<p>It’s generally good practice for Rust posts to mention
their version due to language instability, but this post
and the series won’t have much real runnable code and the
concepts described are pretty stable… but habits die
hard: <code class="language-plaintext highlighter-rouge">rustc 1.0.0-nightly (44a287e6e 2015-01-08 17:03:40 -0800)</code>.</p>
<p>This should also be the 1.0.0-alpha release (speaking of which,
the language instability should be starting to settle down now). <a href="#fnref:version" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:upsides" role="doc-endnote">
<p>Static dispatching isn’t <em>guaranteed</em> to be an upside:
compilers aren’t perfect and may “optimise” code to become
slower. For example, functions inlined too eagerly will
bloating the instruction cache (cache rules everything
around us). This is part of the reason that <code class="language-plaintext highlighter-rouge">#[inline]</code>
and <code class="language-plaintext highlighter-rouge">#[inline(always)]</code> that should be used carefully, and
one reason why using a trait object—with its dynamic
dispatch—is sometimes more efficient.</p>
<p>However, the common case is that it is more efficient to use
static dispatch, and one can always have a thin
statically-dispatched wrapper function that does a dynamic, but
not vice versa, meaning static calls are more flexible. The
standard library tries to be statically dispatched where possible
for this reason. <a href="#fnref:upsides" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:one-per-type" role="doc-endnote">
<p>There’s no guarantee that there will actually be a
copy of the function for each type that implements
the trait, or even for each type that is used with
the function, since the compiler is free to combine
copies if it can tell that sharing the code would not
change semantics. But, in general, this optimisation
doesn’t trigger. <a href="#fnref:one-per-type" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2015/01/crates.io-crate-graphcrates.io crate graph2015-01-04T00:00:00+00:002015-01-04T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p><a href="http://rust-lang.org/">Rust</a> is a systems programming language that
comes with an awesome package manager <a href="http://doc.crates.io">Cargo</a>,
which hooks into the <a href="https://crates.io">crates.io</a> registry as one of
its possible sources of packages. The packages can have dependency
relationships between each other, making the database into a natural
directed graph.</p>
<p>Rust as we have it today is still relatively new, Cargo is even newer,
and crates.io is newer still, so
<a href="http://www.modulecounts.com">the package ecosystem is small</a>: at the
time of writing, only 681 crates exist on crates.io (compared to 115
thousand for node.js’ <a href="https://npmjs.org">npm</a>). I’m sure this will
quickly pick up with as Rust
<a href="http://blog.rust-lang.org/2014/12/12/1.0-Timeline.html">moves to 1.0</a>
and beyond, but at the moment the network of crates and their
dependencies is still easily small enough to be handled globally with
simple means like the graphviz suite of tools and naive Rust
programs. Which is exactly what I did.</p>
<h2 id="the-graphs">The graphs</h2>
<p>The “full” graph of the ecosystem, as rendered by graphviz’s <code class="language-plaintext highlighter-rouge">fdp</code>, is
busy, very busy:</p>
<p><a href="most-packages.svg"><img src="most-packages-preview.png" alt="Most packages" /></a></p>
<p>Click for the rest of the much larger graph as an SVG with clickable
package names, sized according to the number of dependent
packages. You may wish to zoom out to get your bearings.</p>
<p>That’s not even the complete package graph: development dependencies
are completely ignored (they can cause cycles), and any crates with no
dependencies and no dependent crates are not shown, since they’re not
yet interacting with the ecosystem at all; but even so, the graph is
fairly useless.</p>
<p>The suck is mainly due to the most popular crates like
<a href="https://crates.io/crates/time/reverse_dependencies"><code class="language-plaintext highlighter-rouge">time</code></a> and
<a href="https://crates.io/crates/rustc-serialize/reverse_dependencies"><code class="language-plaintext highlighter-rouge">rustc-serialize</code></a>, which
pull most clusters into the very center of the graph. Eliminating them
(specifically, crates with 15 or more dependent crates) gives a more
reasonable graph.</p>
<p><a href="fewer-packages.svg"><img src="fewer-packages-preview.png" alt="Fewer packages" /></a></p>
<p>(Click for bigger.)</p>
<p>That graph makes it clearer that there’s a few distinct clusters. The
left has a lot of web-development functionality, clustered around
<a href="https://crates.io/crates/hyper"><code class="language-plaintext highlighter-rouge">hyper</code></a>,
<a href="https://crates.io/crates/conduit"><code class="language-plaintext highlighter-rouge">conduit</code></a> and
<a href="https://crates.io/crates/openssl"><code class="language-plaintext highlighter-rouge">openssl</code></a>. The right has a lot of
game-development and computer graphics libraries, with
<a href="https://crates.io/keywords/piston">many components</a> (that’s not all
of them) from <a href="http://piston.rs">Piston</a> and many from
<a href="https://github.com/SiegeLord/RustAllegro">RustAllegro</a>. Spread around
are smaller clusters, like
<a href="https://crates.io/crates/epsilonz">epsilonz</a>, and a variety of
numerical projects (of which some
<a href="https://crates.io/crates/num/reverse_dependencies">use <code class="language-plaintext highlighter-rouge">num</code></a>,
and <a href="https://crates.io/crates/matrix">others</a> do not).</p>
<p>It’s not a cluster so much, but there are a lot of examples of some
crate <code class="language-plaintext highlighter-rouge">$foo</code> depending on <code class="language-plaintext highlighter-rouge">$foo-sys</code>: people following
<a href="http://doc.crates.io/build-script.html#*-sys-packages">the convention for publishing FFI bindings</a>.</p>
<h2 id="collecting-the-data">Collecting the data</h2>
<p>crates.io uses
<a href="https://github.com/rust-lang/crates.io-index">a git repo</a> for
distributing information about the registered crates. Each one gets a
file containing a series of JSON objects (one per line) looking a lot
like:</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="code"><pre><span class="p">{</span><span class="w">
</span><span class="nl">"cksum"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"665e3764d2f654d77382ec6ed40a2faf5a114a6e41e2d1c307ff97916924ec64"</span><span class="p">,</span><span class="w">
</span><span class="nl">"deps"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"default_features"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"kind"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"normal"</span><span class="p">,</span><span class="w">
</span><span class="nl">"name"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"num"</span><span class="p">,</span><span class="w">
</span><span class="nl">"optional"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
</span><span class="nl">"target"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
</span><span class="nl">"req"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"~0"</span><span class="p">,</span><span class="w">
</span><span class="nl">"features"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">""</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"vers"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"0.1.5"</span><span class="p">,</span><span class="w">
</span><span class="nl">"yanked"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
</span><span class="nl">"name"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"slow_primes"</span><span class="p">,</span><span class="w">
</span><span class="nl">"features"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span><span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>That’s the info for version 0.1.5 of
<a href="https://crates.io/crates/slow_primes"><code class="language-plaintext highlighter-rouge">slow_primes</code></a>; it contains the
key piece of information<sup id="fnref:index-works" role="doc-noteref"><a href="#fn:index-works" class="footnote" rel="footnote">0</a></sup> that we need: the dependencies, in the <code class="language-plaintext highlighter-rouge">deps</code>
field. The simplistic analysis I’m doing here means that the only
facts of interest are the <code class="language-plaintext highlighter-rouge">name</code> of the dependency and whether it is a
dev-dependency (<code class="language-plaintext highlighter-rouge">kind == "dev"</code>).</p>
<p>The fixed format of the JSON makes it ameniable to
<code class="language-plaintext highlighter-rouge">#[derive(RustcDecodable)]</code>, an attribute that will automatically
create deserialization code that does the right thing:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="code"><pre><span class="nd">#[derive(RustcDecodable)]</span>
<span class="k">struct</span> <span class="n">CrateInfo</span> <span class="p">{</span>
<span class="n">name</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span>
<span class="n">vers</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span>
<span class="n">deps</span><span class="p">:</span> <span class="nb">Vec</span><span class="o"><</span><span class="n">DepInfo</span><span class="o">></span><span class="p">,</span>
<span class="n">cksum</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span>
<span class="n">features</span><span class="p">:</span> <span class="n">HashMap</span><span class="o"><</span><span class="nb">String</span><span class="p">,</span> <span class="nb">Vec</span><span class="o"><</span><span class="nb">String</span><span class="o">>></span><span class="p">,</span>
<span class="n">yanked</span><span class="p">:</span> <span class="nb">bool</span><span class="p">,</span>
<span class="p">}</span>
<span class="nd">#[derive(RustcDecodable)]</span>
<span class="k">struct</span> <span class="n">DepInfo</span> <span class="p">{</span>
<span class="n">name</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span>
<span class="n">req</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span>
<span class="n">features</span><span class="p">:</span> <span class="nb">Vec</span><span class="o"><</span><span class="nb">String</span><span class="o">></span><span class="p">,</span>
<span class="n">optional</span><span class="p">:</span> <span class="nb">bool</span><span class="p">,</span>
<span class="n">default_features</span><span class="p">:</span> <span class="nb">bool</span><span class="p">,</span>
<span class="n">target</span><span class="p">:</span> <span class="nb">Option</span><span class="o"><</span><span class="nb">String</span><span class="o">></span><span class="p">,</span>
<span class="n">kind</span><span class="p">:</span> <span class="nb">Option</span><span class="o"><</span><span class="nb">String</span><span class="o">></span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>The graph is based on the most recent version of each package, so I
just take the last line in each crate’s file, run it through
<a href="http://doc.rust-lang.org/rustc-serialize/rustc-serialize/json/fn.decode.html"><code class="language-plaintext highlighter-rouge">json::decode</code></a>
and get back a <code class="language-plaintext highlighter-rouge">CrateInfo</code>. A few tens of lines later, the code knows
about every crate and about every dependency link and can print it all
out to a graphviz DOT file. (Unfortunately the neat
<a href="http://doc.rust-lang.org/nightly/graphviz"><code class="language-plaintext highlighter-rouge">graphviz</code></a> library
doesn’t offer the flexibility I wanted for setting arbitrary
attributes, so I had to resort to manual printing.)</p>
<p>There is one trip-up: a crate can depend on another crate multiple
times, with different configurations (most commonly, differing
<code class="language-plaintext highlighter-rouge">target</code>s), so some deduplication is required to avoid double counting
and cluttering the graph with multiple lines. Other than that, the
details of the implementation aren’t very interesting, but the code is
publicly available at
<a href="http://github.com/huonw/crates.io-graph">github.com/huonw/crates.io-graph</a>.</p>
<p>Thanks to cmr, acrichto, FreeFall and tomaka in #cargo for
help/suggestions/copy-editing/catching bugs.</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/2rawmg/cratesio_crate_graph/">/r/rust</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=crates.io%20crate%20graph%20%23rustlang&url=https://huonw.github.io/blog/2015/01/crates.io-crate-graph/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2015/01/crates.io-crate-graph/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2015/01/crates.io-crate-graph/&title=crates.io%20crate%20graph" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:index-works" role="doc-endnote">
<p>Before FreeFall pointed this out, I was considering
downloading everything on crates.io to construct the
graph, which would’ve been pretty fun too! <a href="#fnref:index-works" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2014/07/what-does-rusts-unsafe-meanWhat does Rust's “unsafe” mean?2014-07-24T00:00:00+00:002014-07-24T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p><a href="http://rust-lang.org/">Rust</a> is an in-development<sup id="fnref:version" role="doc-noteref"><a href="#fn:version" class="footnote" rel="footnote">0</a></sup> systems
programming language with a strong focus on no-overhead memory
safety. This is achieved through a powerful type system (with
similarities to Haskell), and careful tracking of ownership and
pointers, guaranteeing safety. However, this is too restrictive for a
low-level systems language, an escape hatch is occasionally
required. Enter the <code class="language-plaintext highlighter-rouge">unsafe</code> keyword.</p>
<h2 id="poking-holes-in-memory-safety">Poking holes in memory safety</h2>
<p>Rust aims to be memory safe, so that, by default, code cannot crash
(or be exploited) due to dangling pointers or iterator
invalidation. However, there are things that cannot fit into the type
system, for example, it is not possible to get the raw interactions
with the operating system and system libraries (like memory allocators
and thread spawning) to be truly safe. Detailed human knowledge about
how to use them safely is required to be encoded at some point, and
this is not easily checkable by a compiler: mistakes can be made.</p>
<p>Other memory safe languages (e.g. managed ones like Python or Haskell)
have all this knowledge encoded in the implementations of their
underlying virtual machines/runtime systems, usually written in
C. Rust doesn’t have a heavy-weight VM or runtime, but still needs to
provide (preferably safe) interfaces in some manner.</p>
<p>Rust fills these holes with the <code class="language-plaintext highlighter-rouge">unsafe</code> keyword, which opts in to
possibly dangerous behaviour; like calling into the operating system
and external libraries via
<a href="http://doc.rust-lang.org/master/guide-ffi.html">the foreign function interface (FFI)</a>,
or handling possibly-invalid machine pointers directly.</p>
<p>Rust uses <code class="language-plaintext highlighter-rouge">unsafe</code> to build all the abstractions seen in the standard
library: the vast majority of it is written in Rust, including
fundamental types like
<a href="http://doc.rust-lang.org/master/std/rc/struct.Rc.html">the reference counted <code class="language-plaintext highlighter-rouge">Rc</code></a>,
<a href="http://doc.rust-lang.org/master/std/vec/struct.Vec.html">the dynamic vector <code class="language-plaintext highlighter-rouge">Vec</code></a>,
and
<a href="http://doc.rust-lang.org/master/std/collections/hashmap/struct.HashMap.html"><code class="language-plaintext highlighter-rouge">HashMap</code></a>,
with only
<a href="https://github.com/rust-lang/rust/tree/82ec1aef293ddc5c6373bd7f5ec323fafbdf7901/src/rt">a few small C shims</a>
and some external non-Rust libraries like jemalloc and libuv.</p>
<h2 id="unsafe"><code class="language-plaintext highlighter-rouge">unsafe</code></h2>
<p>There are two ways in which one can opt-in to these possibly dangerous
behaviours: with an <code class="language-plaintext highlighter-rouge">unsafe</code> block, or with an <code class="language-plaintext highlighter-rouge">unsafe</code> function.</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="code"><pre><span class="c">// calling some C functions imported via FFI:</span>
<span class="k">unsafe</span> <span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">some_c_function</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">bar</span><span class="p">()</span> <span class="p">{</span>
<span class="k">unsafe</span> <span class="p">{</span>
<span class="nf">another_c_function</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">baz</span><span class="p">()</span> <span class="p">{</span>
<span class="c">// illegal, not inside an `unsafe` context</span>
<span class="c">// yet_another_c_function();</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Being inside an <code class="language-plaintext highlighter-rouge">unsafe</code> context allows one to (not necessarily
complete):</p>
<ol>
<li>call functions marked <code class="language-plaintext highlighter-rouge">unsafe</code> (this includes FFI functions)</li>
<li>dereference raw pointers (the <code class="language-plaintext highlighter-rouge">*const</code> and <code class="language-plaintext highlighter-rouge">*mut</code> types), which can
possibly be <code class="language-plaintext highlighter-rouge">NULL</code>, or otherwise invalid</li>
<li>access a mutable global variable</li>
<li>use inline assembly</li>
</ol>
<p>All of these can easily cause large problems. For example, a shared
reference <code class="language-plaintext highlighter-rouge">&T</code> is a machine pointer, but it <em>must</em> always point to a
valid value of type <code class="language-plaintext highlighter-rouge">T</code>; all four of the above can cause this to be
violated:</p>
<ol>
<li>
<p>There is an <code class="language-plaintext highlighter-rouge">unsafe</code> function
<a href="http://doc.rust-lang.org/master/std/mem/fn.transmute.html"><code class="language-plaintext highlighter-rouge">std::mem::transmute</code></a>
which takes the bytes of its argument and pretends they are of any
type one wants, thus, one can create an invalid <code class="language-plaintext highlighter-rouge">&</code> pointer by
reinterpreting an integer: <code class="language-plaintext highlighter-rouge">transmute::<uint, &Vec<int>>(0)</code>.</p>
</li>
<li>
<p>A raw pointer <code class="language-plaintext highlighter-rouge">p: *const T</code> can legally be <code class="language-plaintext highlighter-rouge">NULL</code>. The
“rereferencing” operation <code class="language-plaintext highlighter-rouge">&*p</code> creates a reference <code class="language-plaintext highlighter-rouge">&T</code> pointing to
<code class="language-plaintext highlighter-rouge">p</code>s data, a no-op at runtime, since <code class="language-plaintext highlighter-rouge">*const T</code> and <code class="language-plaintext highlighter-rouge">&T</code> are both
just a single pointer under the hood. If <code class="language-plaintext highlighter-rouge">p</code> is <code class="language-plaintext highlighter-rouge">NULL</code> this allows
one to create a <code class="language-plaintext highlighter-rouge">NULL</code> <code class="language-plaintext highlighter-rouge">&T</code>: invalid!</p>
</li>
<li>
<p>If one has <code class="language-plaintext highlighter-rouge">static mut X: Option<i64> = Some(1234);</code>, one can use
pattern matching to get a reference <code class="language-plaintext highlighter-rouge">r: &i64</code> pointing to the <code class="language-plaintext highlighter-rouge">1234</code>
integer, but another thread can overwrite <code class="language-plaintext highlighter-rouge">X</code> with <code class="language-plaintext highlighter-rouge">None</code>, leaving
<code class="language-plaintext highlighter-rouge">r</code> dangling.</p>
</li>
<li>
<p>Inline assembly can set arbitrary registers to arbitrary values,
including setting a register meant to be holding a <code class="language-plaintext highlighter-rouge">&T</code> to zero.</p>
</li>
</ol>
<h2 id="what-does-unsafe-really-mean">What does <code class="language-plaintext highlighter-rouge">unsafe</code> really mean?</h2>
<p>An <code class="language-plaintext highlighter-rouge">unsafe</code> context is the programmer telling the compiler that the
code is guaranteed to be safe due to invariants impossible to express
in the type system, and that it satisfies
<a href="http://doc.rust-lang.org/nightly/reference.html#behavior-considered-undefined">the invariants that Rust itself imposes</a>.</p>
<p>These invariants are assumed to never be broken, even inside <code class="language-plaintext highlighter-rouge">unsafe</code>
code blocks, and the compiler compiles and optimises with this
assumption. Thus, breaking any of those invariants is
<a href="https://en.wikipedia.org/wiki/Undefined_behaviour">undefined behaviour</a><sup id="fnref:ub-llvm" role="doc-noteref"><a href="#fn:ub-llvm" class="footnote" rel="footnote">1</a></sup>
and can leave a program doing “anything”, even making
<a href="http://www.catb.org/jargon/html/N/nasal-demons.html">demons fly out your nose</a>.</p>
<p>That is, an <code class="language-plaintext highlighter-rouge">unsafe</code> context is not a free pass to mutate anything and
everything, nor is it a free pass to mangle pointers and alias
references: all the normal rules of Rust still apply, the compiler is
just giving the programmer more power, at the expense of leaving it up
to the programmer to ensure everything is safe.</p>
<p>A non-<code class="language-plaintext highlighter-rouge">unsafe</code> function using <code class="language-plaintext highlighter-rouge">unsafe</code> internally <em>should</em> be
implemented to be safe to call; that is, there is no circumstance or
set of arguments that can make the function violate
<a href="http://doc.rust-lang.org/nightly/reference.html#behavior-considered-undefined">any invariants</a>. If
there are such circumstances, it should be marked <code class="language-plaintext highlighter-rouge">unsafe</code>.</p>
<p>This rule is most important for public, exported functions; private
functions are guaranteed to only be called in a limited set of
configurations (since all calls are in the crate/module in which it is
defined), so the author has more flexibility about what sort of safety
guarantees they give. However, marking possibly-dangerous things
<code class="language-plaintext highlighter-rouge">unsafe</code> helps the compiler help the programmer do the right thing, so
is encouraged even for private items.</p>
<h3 id="case-study-vec">Case study: <code class="language-plaintext highlighter-rouge">Vec</code></h3>
<p>The <code class="language-plaintext highlighter-rouge">Vec<T></code> type is
<a href="https://github.com/rust-lang/rust/blob/82ec1aef293ddc5c6373bd7f5ec323fafbdf7901/src/libcollections/vec.rs#L55-L59">defined</a>
as:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre><span class="k">pub</span> <span class="k">struct</span> <span class="nb">Vec</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="p">{</span>
<span class="n">len</span><span class="p">:</span> <span class="nb">uint</span><span class="p">,</span>
<span class="n">cap</span><span class="p">:</span> <span class="nb">uint</span><span class="p">,</span>
<span class="n">ptr</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="n">T</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>There are (at least) two invariants here:</p>
<ol>
<li><code class="language-plaintext highlighter-rouge">ptr</code> holds an allocation with enough space for <code class="language-plaintext highlighter-rouge">cap</code> values of type <code class="language-plaintext highlighter-rouge">T</code></li>
<li>That allocation holds <code class="language-plaintext highlighter-rouge">len</code> valid values of type <code class="language-plaintext highlighter-rouge">T</code> (i.e. the
first <code class="language-plaintext highlighter-rouge">len</code> out of <code class="language-plaintext highlighter-rouge">cap</code> of the <code class="language-plaintext highlighter-rouge">T</code>s are valid, implying <code class="language-plaintext highlighter-rouge">len <=
cap</code>)</li>
</ol>
<p>It’s not feasible to express these in Rust’s type system, so they are
guaranteed by a careful implementation. The implementation is then
forced to use <code class="language-plaintext highlighter-rouge">unsafe</code> to assuage the compiler’s doubts about certain
operations. The compiler does not and cannot understand the invariants
stated above, and so cannot be sure that
<a href="https://github.com/rust-lang/rust/blob/82ec1aef293ddc5c6373bd7f5ec323fafbdf7901/src/libcollections/vec.rs#L1430">creating a slice view into the vector</a>
is safe. It is implemented like so:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre><span class="k">fn</span> <span class="n">as_slice</span><span class="o"><</span><span class="nv">'a</span><span class="o">></span><span class="p">(</span><span class="o">&</span><span class="nv">'a</span> <span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="o">&</span><span class="nv">'a</span> <span class="p">[</span><span class="n">T</span><span class="p">]</span> <span class="p">{</span>
<span class="k">unsafe</span> <span class="p">{</span> <span class="nn">mem</span><span class="p">::</span><span class="nf">transmute</span><span class="p">(</span><span class="n">Slice</span> <span class="p">{</span> <span class="n">data</span><span class="p">:</span> <span class="k">self</span><span class="nf">.as_ptr</span><span class="p">(),</span> <span class="n">len</span><span class="p">:</span> <span class="k">self</span><span class="py">.len</span> <span class="p">})</span> <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>And you can see that it could easily be unsafe e.g. if one
accidentally wrote <code class="language-plaintext highlighter-rouge">self.cap</code> instead of <code class="language-plaintext highlighter-rouge">self.len</code>, the resulting
slice would be too long and the last elements of it would be
uninitialised data. The compiler can’t verify that this is correct,
and so assumes the worst, disallowing it without the explicit opt-in.</p>
<p>Another thing to note is these <code class="language-plaintext highlighter-rouge">Vec</code> invariants are required to always
hold or else <code class="language-plaintext highlighter-rouge">Vec</code> will be allowing incorrect behaviour to happen via
the safe methods it exposes (e.g. if someone could increase <code class="language-plaintext highlighter-rouge">len</code>
without initialising the elements appropriately, the <code class="language-plaintext highlighter-rouge">as_slice</code> method
above would be broken).</p>
<p>Unfortunately, it’s not possible to get the Rust compiler to directly
enforce them, so the <code class="language-plaintext highlighter-rouge">Vec</code> API has to be careful to guarantee that
they can’t be violated; part of this is keeping the fields private, so
they cannot be directly changed, another part is being careful to mark
the <code class="language-plaintext highlighter-rouge">unsafe</code> parts of the API as <code class="language-plaintext highlighter-rouge">unsafe</code>, e.g.
<a href="http://doc.rust-lang.org/master/collections/vec/struct.Vec.html#method.set_len">the <code class="language-plaintext highlighter-rouge">set_len</code> method</a>
can directly change the <code class="language-plaintext highlighter-rouge">len</code> field.</p>
<h3 id="case-study-malloc">Case study: <code class="language-plaintext highlighter-rouge">malloc</code></h3>
<p>The C function <code class="language-plaintext highlighter-rouge">malloc</code> is described by my man page as the following:</p>
<blockquote>
<p>The <code class="language-plaintext highlighter-rouge">malloc()</code> function allocates size bytes and returns a pointer to the
allocated memory. The memory is not initialized. If size is 0, then
<code class="language-plaintext highlighter-rouge">malloc()</code> returns either <code class="language-plaintext highlighter-rouge">NULL</code>, or a unique pointer value that can later
be successfully passed to <code class="language-plaintext highlighter-rouge">free()</code>.</p>
<p>The <code class="language-plaintext highlighter-rouge">malloc()</code> and <code class="language-plaintext highlighter-rouge">calloc()</code> functions return a pointer to the allocated
memory, which is suitably aligned for any built-in type. On error,
these functions return <code class="language-plaintext highlighter-rouge">NULL</code>. […].</p>
</blockquote>
<p>The <code class="language-plaintext highlighter-rouge">libc</code> crate predefines most of the common symbols from the <code class="language-plaintext highlighter-rouge">libc</code>
on various platforms, including <code class="language-plaintext highlighter-rouge">libc::malloc</code>. Let’s write a safe
program that creates memory for, stores and prints an 8-byte <code class="language-plaintext highlighter-rouge">i64</code>
integer, carefully justifying why we know more than the compiler, and
thus why each <code class="language-plaintext highlighter-rouge">unsafe</code> is safe (in a perfect world all <code class="language-plaintext highlighter-rouge">unsafe</code> blocks
would be justified/proved correct).</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">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
</pre></td><td class="code"><pre><span class="k">extern</span> <span class="n">crate</span> <span class="n">libc</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="n">ptr</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">pointer</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">i64</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span>
<span class="c">// rustc doesn't know what `malloc` does, and so doesn't know</span>
<span class="c">// that calling it with argument 8 is always safe; but we do,</span>
<span class="c">// so we override the compiler's concern with</span>
<span class="c">// `unsafe`. (`malloc` returns a `*mut libc::c_void` so we</span>
<span class="c">// need to cast it to the type we want.)</span>
<span class="nn">libc</span><span class="p">::</span><span class="nf">malloc</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span> <span class="k">as</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">i64</span>
<span class="p">};</span>
<span class="c">// we know that the only failure condition is the pointer being</span>
<span class="c">// NULL, in any other circumstance the pointer points to a valid</span>
<span class="c">// memory allocation of at least 8 bytes.</span>
<span class="k">if</span> <span class="n">pointer</span><span class="nf">.is_null</span><span class="p">()</span> <span class="p">{</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"could not allocate"</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="c">// here, the only thing missing is initialisation, the memory</span>
<span class="c">// is valid but uninitialised, so lets fix that. Since it is</span>
<span class="c">// not initialised, we have to be careful to avoid running</span>
<span class="c">// destructors on the old memory; via `std::ptr::write`.</span>
<span class="k">unsafe</span> <span class="p">{</span>
<span class="c">// allocation is valid, and the memory is uninitialised,</span>
<span class="c">// so this is safe and correct.</span>
<span class="nn">ptr</span><span class="p">::</span><span class="nf">write</span><span class="p">(</span><span class="n">pointer</span><span class="p">,</span> <span class="mi">1234i64</span><span class="p">);</span>
<span class="p">}</span>
<span class="c">// now `pointer` is looking at initialised, valid memory, so</span>
<span class="c">// it is valid to read from it, and to obtain a reference to</span>
<span class="c">// it.</span>
<span class="k">let</span> <span class="n">data</span><span class="p">:</span> <span class="o">&</span><span class="nb">i64</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="o">&*</span><span class="n">pointer</span> <span class="p">};</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"The data is {}"</span><span class="p">,</span> <span class="o">*</span><span class="n">data</span><span class="p">);</span>
<span class="c">// prints: The data is 1234</span>
<span class="p">}</span>
<span class="c">// (leaking memory is not `unsafe`.)</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>(Keen eyes will note that <code class="language-plaintext highlighter-rouge">i64</code> doesn’t have a destructor and so the
<a href="http://doc.rust-lang.org/master/std/ptr/fn.write.html"><code class="language-plaintext highlighter-rouge">ptr::write</code></a>
call isn’t strictly required, but it’s good practice.)</p>
<h2 id="faq-why-isnt-unsafe-viral">FAQ: Why isn’t <code class="language-plaintext highlighter-rouge">unsafe</code> viral?</h2>
<p>One <em>might</em> expect a function containing an <code class="language-plaintext highlighter-rouge">unsafe</code> block to be
<code class="language-plaintext highlighter-rouge">unsafe</code> to call, that is, <code class="language-plaintext highlighter-rouge">unsafe</code>ty infects everything it touches,
similar to how Haskell forces one to mark all impure calculations with
the <code class="language-plaintext highlighter-rouge">IO</code> type.</p>
<p>However, this is not the case, <code class="language-plaintext highlighter-rouge">unsafe</code> is just an implementation
detail; if a safe function uses <code class="language-plaintext highlighter-rouge">unsafe</code> internally, it just means the
author has been forced to step around the type system, but still
exposes a safe interface.</p>
<p>More pragmatically, if <code class="language-plaintext highlighter-rouge">unsafe</code> were viral, every Rust program ever
would be entirely <code class="language-plaintext highlighter-rouge">unsafe</code>, since the whole standard library is
written in Rust, built on top of <code class="language-plaintext highlighter-rouge">unsafe</code> internals.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The <code class="language-plaintext highlighter-rouge">unsafe</code> marker is a way to step around Rust’s type system; by
telling <code class="language-plaintext highlighter-rouge">rustc</code> that there are external conditions/invariants that
guarantee correctness: the compiler steps back and locally leaves the
programmer to verify that
<a href="http://doc.rust-lang.org/reference.html#behavior-considered-undefined">various properties</a>
hold. This allows Rust to write very low-level code like C, but still
be memory safe by default, by forcing programmers to opt-in to the
risky behaviour.</p>
<p>The
<a href="http://doc.rust-lang.org/master/guide-unsafe.html">“Writing Safe Unsafe and Low-Level Code”</a>
provides guidance and tips about using <code class="language-plaintext highlighter-rouge">unsafe</code> correctly.</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/2bhwgc/what_does_rusts_unsafe_mean/">/r/rust</a></li>
<li class="external-link"><a href="http://www.reddit.com/r/programming/comments/2bhwhl/what_does_rusts_unsafe_mean/">/r/programming</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=What%20does%20Rust's%20%E2%80%9Cunsafe%E2%80%9D%20mean?%20%23rustlang&url=https://huonw.github.io/blog/2014/07/what-does-rusts-unsafe-mean/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2014/07/what-does-rusts-unsafe-mean/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2014/07/what-does-rusts-unsafe-mean/&title=What%20does%20Rust's%20%E2%80%9Cunsafe%E2%80%9D%20mean?" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
<div class="footnotes" role="doc-endnotes">
<ol start="0">
<li id="fn:version" role="doc-endnote">
<p>The code in this post compiles with <code class="language-plaintext highlighter-rouge">rustc 0.12.0-pre-nightly (aa0e35bc6 2014-07-22 00:26:21 +0000)</code>. <a href="#fnref:version" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:ub-llvm" role="doc-endnote">
<p><a href="http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html">“What Every C Programmer Should Know About Undefined Behaviour”</a>
is a series of articles highlighting how insidious undefined behaviour
can be, leading to subtly (or not so subtly) broken programs. <a href="#fnref:ub-llvm" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
https://huonw.github.io/blog/2014/06/error-handling-in-rust-knn-case-studyError handling in Rust: a k-NN case study2014-06-11T00:00:00+00:002014-06-11T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>After posting
<a href="/blog/2014/06/comparing-knn-in-rust/">a Rust translation of some <em>k</em>-nearest neighbour code</a>,
I got a <a href="https://news.ycombinator.com/item?id=7875378">few</a>
<a href="https://news.ycombinator.com/item?id=7872878">comments</a> asking “how
would you handle errors if you wanted to?”. This is the perfect chance
to briefly demonstrate a few idioms.</p>
<p>See my <a href="/blog/2014/06/comparing-knn-in-rust/">previous post</a> for context and the original
code; like with the code in that post, this code compiles with <code class="language-plaintext highlighter-rouge">rustc
0.11.0-pre-nightly (e55f64f 2014-06-09 01:11:58 -0700)</code></p>
<h2 id="what-type-of-error-handling-to-use">What type of error handling to use?</h2>
<p>The “canonical” way is to use type system, with types like
<a href="http://doc.rust-lang.org/master/std/result/type.Result.html"><code class="language-plaintext highlighter-rouge">Result<A, B></code></a>, which can either be an <code class="language-plaintext highlighter-rouge">Ok</code> containing a
value of type <code class="language-plaintext highlighter-rouge">A</code> or an <code class="language-plaintext highlighter-rouge">Err</code> containing a <code class="language-plaintext highlighter-rouge">B</code> (isomorphic to
Haskell’s <code class="language-plaintext highlighter-rouge">Either</code>), and <a href="http://doc.rust-lang.org/master/std/option/type.Option.html"><code class="language-plaintext highlighter-rouge">Option<T></code></a>, which is either a
<code class="language-plaintext highlighter-rouge">Some</code> containing a <code class="language-plaintext highlighter-rouge">T</code>, or just <code class="language-plaintext highlighter-rouge">None</code> containing no data.</p>
<p>The standard library uses <code class="language-plaintext highlighter-rouge">Result</code> and <code class="language-plaintext highlighter-rouge">Option</code> pervasively, meaning
you can essentially be guaranteed to handle all errors (and
theoretically never crash) as long as you avoid calling
<a href="http://doc.rust-lang.org/master/core/result/type.Result.html#method.unwrap"><code class="language-plaintext highlighter-rouge">unwrap</code></a> and the small number of similar methods. For
example, <a href="http://doc.rust-lang.org/master/std/io/index.html#error-handling">almost all IO actions</a> return an
<a href="http://doc.rust-lang.org/master/std/io/type.IoResult.html"><code class="language-plaintext highlighter-rouge">IoResult<...></code></a>, which defines the possible errors via the
<a href="http://doc.rust-lang.org/master/std/io/struct.IoError.html"><code class="language-plaintext highlighter-rouge">IoError</code></a> type and its contained <code class="language-plaintext highlighter-rouge">IoErrorKind</code> enum.</p>
<p>An approximation of monadic short-circuiting is provided for <code class="language-plaintext highlighter-rouge">Result</code>
by
<a href="http://doc.rust-lang.org/master/std/result/#the-try!-macro">the <code class="language-plaintext highlighter-rouge">try!</code> macro</a>,
which just returns immediately if an error occurs (that is, if
variable with type <code class="language-plaintext highlighter-rouge">Result</code> is an <code class="language-plaintext highlighter-rouge">Err</code>), propagating it upwards for
the caller to handle. However, <code class="language-plaintext highlighter-rouge">try!</code> isn’t the only strategy, and
it’s easy to define custom handlers, as I do below.</p>
<p>Before you ask: Rust lacks conventional exceptions (since these are
hard to make memory safe without a garbage collector, as I understand
it); in safe code/by default, unwinding can only be stopped at task
boundaries.</p>
<h2 id="the-bullet-proof-code">The bullet-proof code</h2>
<p>The <code class="language-plaintext highlighter-rouge">try!</code> macro and <code class="language-plaintext highlighter-rouge">IoError</code> form my inspiration for the code to
handle errors in <code class="language-plaintext highlighter-rouge">slurp_file</code>: define an enum with the various failure
conditions, and <a href="http://doc.rust-lang.org/master/guide-macros.html">define a short custom macro</a> that either
“unwrap”s or short-circuits to return the appropriate error marker.</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
</pre></td><td class="code"><pre><span class="nd">#![feature(macro_rules)]</span>
<span class="c">// the possible things that can go wrong</span>
<span class="k">enum</span> <span class="n">SlurpError</span> <span class="p">{</span>
<span class="c">// The input was malformed (could have line/column number info too)</span>
<span class="n">InvalidInput</span><span class="p">,</span>
<span class="c">// Some piece of IO failed (e.g. couldn't open a file)</span>
<span class="nf">FailedIo</span><span class="p">(</span><span class="n">IoError</span><span class="p">)</span>
<span class="p">}</span>
<span class="c">// short-circuiting macro: "unwrap" the value, an error will return</span>
<span class="c">// from the surrounding function propagating that error upwards.</span>
<span class="c">//</span>
<span class="c">// macro_rules! macros take a sequence of tokens/AST non-terminals and</span>
<span class="c">// attempt to pattern match on them, taking the first branch that</span>
<span class="c">// fits, and then effectively replace the macro invocation with the</span>
<span class="c">// right-hand side of that branch.</span>
<span class="nd">macro_rules!</span> <span class="n">try_slurp</span> <span class="p">{</span>
<span class="c">// `$...` is a special variable, a "macro argument", this `value`</span>
<span class="c">// is an expression, e.g. `1 + 2`, `foo()`, `if ... { ... } else {</span>
<span class="c">// ... }`. Hence, to match, this arm needs to be passed an</span>
<span class="c">// expression, a comma, then a literal `FailedIo`.</span>
<span class="c">// e.g. `try_slurp!(foo(), FailedIo)`</span>
<span class="p">(</span><span class="nv">$value</span><span class="p">:</span> <span class="n">expr</span><span class="p">,</span> <span class="n">FailedIo</span><span class="p">)</span> <span class="k">=></span> <span class="p">{</span>
<span class="c">// The macro expands to this code if the pattern matches. The</span>
<span class="c">// `$value` expression is `match`d as a `Result<..., IoError>`</span>
<span class="c">// (passing in something else will be a type error, after the</span>
<span class="c">// macro expands). Success is unwrapped, and a failure is</span>
<span class="c">// returned from the function/closure in which the macro is</span>
<span class="c">// called.</span>
<span class="k">match</span> <span class="nv">$value</span> <span class="p">{</span>
<span class="nf">Ok</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">=></span> <span class="n">x</span><span class="p">,</span>
<span class="nf">Err</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="k">=></span> <span class="k">return</span> <span class="nf">Err</span><span class="p">(</span><span class="nf">FailedIo</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="c">// similarly here, this branch takes an arbitrary expression and</span>
<span class="c">// then has to match exactly `InvalidInput`</span>
<span class="p">(</span><span class="nv">$value</span><span class="p">:</span> <span class="n">expr</span><span class="p">,</span> <span class="n">InvalidInput</span><span class="p">)</span> <span class="k">=></span> <span class="p">{</span>
<span class="c">// as above, except handling `$value` as an `Option<...>`.</span>
<span class="k">match</span> <span class="nv">$value</span> <span class="p">{</span>
<span class="nf">Some</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">=></span> <span class="n">x</span><span class="p">,</span>
<span class="nb">None</span> <span class="k">=></span> <span class="k">return</span> <span class="nf">Err</span><span class="p">(</span><span class="n">InvalidInput</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">slurp_file</span><span class="p">(</span><span class="n">file</span><span class="p">:</span> <span class="o">&</span><span class="n">Path</span><span class="p">)</span> <span class="k">-></span> <span class="n">Result</span><span class="o"><</span><span class="nb">Vec</span><span class="o"><</span><span class="n">LabelPixel</span><span class="o">></span><span class="p">,</span> <span class="n">SlurpError</span><span class="o">></span> <span class="p">{</span>
<span class="k">use</span> <span class="nn">std</span><span class="p">::{</span><span class="n">result</span><span class="p">,</span> <span class="n">option</span><span class="p">};</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">file</span> <span class="o">=</span> <span class="nn">BufferedReader</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nd">try_slurp!</span><span class="p">(</span><span class="nn">File</span><span class="p">::</span><span class="nf">open</span><span class="p">(</span><span class="n">file</span><span class="p">),</span> <span class="n">FailedIo</span><span class="p">));</span>
<span class="k">let</span> <span class="n">lines</span> <span class="o">=</span> <span class="n">file</span><span class="nf">.lines</span><span class="p">()</span>
<span class="nf">.skip</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="nf">.map</span><span class="p">(|</span><span class="n">line</span><span class="p">|</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">line</span> <span class="o">=</span> <span class="nd">try_slurp!</span><span class="p">(</span><span class="n">line</span><span class="p">,</span> <span class="n">FailedIo</span><span class="p">);</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">splits</span> <span class="o">=</span> <span class="n">line</span><span class="nf">.as_slice</span><span class="p">()</span><span class="nf">.trim</span><span class="p">()</span><span class="nf">.split</span><span class="p">(</span><span class="sc">','</span><span class="p">)</span><span class="nf">.map</span><span class="p">(|</span><span class="n">x</span><span class="p">|</span> <span class="nf">from_str</span><span class="p">(</span><span class="n">x</span><span class="p">));</span>
<span class="c">// .and_then is flattening Option<Option<int>> to Option<int>.</span>
<span class="k">let</span> <span class="n">label</span> <span class="o">=</span> <span class="nd">try_slurp!</span><span class="p">(</span><span class="n">splits</span><span class="nf">.next</span><span class="p">()</span><span class="nf">.and_then</span><span class="p">(|</span><span class="n">x</span><span class="p">|</span> <span class="n">x</span><span class="p">),</span> <span class="n">InvalidInput</span><span class="p">);</span>
<span class="k">let</span> <span class="n">pixels</span> <span class="o">=</span> <span class="nd">try_slurp!</span><span class="p">(</span><span class="nn">option</span><span class="p">::</span><span class="nf">collect</span><span class="p">(</span><span class="n">splits</span><span class="p">),</span> <span class="n">InvalidInput</span><span class="p">);</span>
<span class="nf">Ok</span><span class="p">(</span><span class="n">LabelPixel</span> <span class="p">{</span>
<span class="n">label</span><span class="p">:</span> <span class="n">label</span><span class="p">,</span>
<span class="n">pixels</span><span class="p">:</span> <span class="n">pixels</span>
<span class="p">})</span>
<span class="p">});</span>
<span class="nn">result</span><span class="p">::</span><span class="nf">collect</span><span class="p">(</span><span class="n">lines</span><span class="p">)</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<blockquote>
<p>vbhit provides <a href="http://www.reddit.com/r/rust/comments/27tuu5/error_handling_in_rust_a_knn_case_study/ci4nvpi.">a nice alternative implementation</a> that
avoids defining the macro.</p>
</blockquote>
<p>The return value can then be pattern-matched where-ever <code class="language-plaintext highlighter-rouge">slurp_file</code>
is called, and the error propagated upwards there, or handled
appropriately e.g. the <code class="language-plaintext highlighter-rouge">slurp_file</code> calls in <code class="language-plaintext highlighter-rouge">main</code> could be changed
to something like:</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre><span class="k">let</span> <span class="n">training_set</span> <span class="o">=</span> <span class="k">match</span> <span class="nf">slurp_file</span><span class="p">(</span><span class="o">&</span><span class="nn">Path</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="s">"trainingsample.csv"</span><span class="p">))</span> <span class="p">{</span>
<span class="nf">Ok</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="k">=></span> <span class="n">data</span><span class="p">,</span>
<span class="nf">Err</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="k">=></span> <span class="p">{</span>
<span class="k">match</span> <span class="n">e</span> <span class="p">{</span>
<span class="nf">FailedIo</span><span class="p">(</span><span class="n">io</span><span class="p">)</span> <span class="k">=></span> <span class="nd">println!</span><span class="p">(</span><span class="s">"Couldn't read file: {}"</span><span class="p">,</span> <span class="n">io</span><span class="p">),</span>
<span class="n">InvalidInput</span> <span class="k">=></span> <span class="nd">println!</span><span class="p">(</span><span class="s">"Invalid file format"</span><span class="p">)</span>
<span class="p">}</span>
<span class="nn">std</span><span class="p">::</span><span class="nn">os</span><span class="p">::</span><span class="nf">set_exit_status</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></figure>
<h2 id="collect">“collect”?</h2>
<p>The two <code class="language-plaintext highlighter-rouge">collect</code> functions
(<a href="http://doc.rust-lang.org/master/std/result/fn.collect.html"><code class="language-plaintext highlighter-rouge">result</code></a>
and
<a href="http://doc.rust-lang.org/master/std/option/fn.collect.html"><code class="language-plaintext highlighter-rouge">option</code></a>)
are useful helpers, which take an <code class="language-plaintext highlighter-rouge">Iterator<Result<T, X>></code> and return
something like <code class="language-plaintext highlighter-rouge">Result<Vec<T>, X></code> or <code class="language-plaintext highlighter-rouge">Result<HashSet<T>, X></code>
(respectively <code class="language-plaintext highlighter-rouge">Option</code>). It’s not a coincidence that these functions
share the same name as the
<a href="http://doc.rust-lang.org/master/std/iter/trait.Iterator.html#tymethod.collect"><code class="language-plaintext highlighter-rouge">Iterator.collect</code></a>
I used last time: all of them allow collecting to the same set of
generic container types.</p>
<p>A Haskeller might notice that they are actually just special cases of
<a href="http://hackage.haskell.org/package/base-4.7.0.0/docs/Prelude.html#v:sequence">the monadic <code class="language-plaintext highlighter-rouge">sequence</code></a>; there is a possibility that this
could be handled generically in future (with higher kinded types),
but, unfortunately, there is no guarantee that a Monad trait will work
nicely due to Rust’s affine types and various low-level details.</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/27tuu5/error_handling_in_rust_a_knn_case_study/">/r/rust</a></li>
<li class="external-link"><a href="http://www.reddit.com/r/programming/comments/27tuw8/error_handling_in_rust_a_knn_case_study/">/r/programming</a></li>
<li class="external-link"><a href="https://news.ycombinator.com/item?id=7875793">Hacker News</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Error%20handling%20in%20Rust:%20a%20k-NN%20case%20study%20%23rustlang&url=https://huonw.github.io/blog/2014/06/error-handling-in-rust-knn-case-study/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2014/06/error-handling-in-rust-knn-case-study/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2014/06/error-handling-in-rust-knn-case-study/&title=Error%20handling%20in%20Rust:%20a%20k-NN%20case%20study" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>
https://huonw.github.io/blog/2014/06/comparing-knn-in-rustComparing k-NN in Rust2014-06-10T00:00:00+00:002014-06-10T00:00:00+00:00Huon Wilsonhttps://huonw.github.io/<p>In my voyages around the internet, I came across <a href="http://philtomson.github.io/blog/2014/05/29/comparing-a-machine-learning-algorithm-implemented-in-f-number-and-ocaml/">a pair</a> of
<a href="http://philtomson.github.io/blog/2014/05/30/stop-the-presses-ocaml-wins/">blog posts</a> which compare the implementation of a
<em>k</em>-nearest neighbour (<em>k</em>-NN) classifier in F# and OCaml. I couldn’t
resist writing the code into <a href="http://rust-lang.org/">Rust</a> to see how it fared.</p>
<p>Rust is a memory-safe systems language under heavy development; this
code compiles with <a href="http://www.rust-lang.org/install.html">the latest nightly</a> (as of 2014-06-10 12:00 UTC),
specifically <code class="language-plaintext highlighter-rouge">rustc 0.11.0-pre-nightly (e55f64f 2014-06-09 01:11:58
-0700)</code>.</p>
<h2 id="code">Code</h2>
<p>The Rust code is a nearly-direct translation of the original F# code,
the only change was changing <code class="language-plaintext highlighter-rouge">distance</code> to compute the squared
distance, that is, <code class="language-plaintext highlighter-rouge">a*a + b*b + ...</code> (square root is strictly
increasing, yo).</p>
<p>For clarity, all errors are ignored (that’s the <code class="language-plaintext highlighter-rouge">.unwrap()</code> calls):
the input is assumed to be valid and IO is assumed to succeed. I wrote
<a href="/blog/2014/06/error-handling-in-rust-knn-case-study/">a follow-up post describing how one would handle errors</a>. Also,
I made no effort to remove/reduce/streamline allocations.</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">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
51
52
53
54
</pre></td><td class="code"><pre><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">io</span><span class="p">::{</span><span class="n">File</span><span class="p">,</span> <span class="n">BufferedReader</span><span class="p">};</span>
<span class="k">struct</span> <span class="n">LabelPixel</span> <span class="p">{</span>
<span class="n">label</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
<span class="n">pixels</span><span class="p">:</span> <span class="nb">Vec</span><span class="o"><</span><span class="nb">int</span><span class="o">></span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">slurp_file</span><span class="p">(</span><span class="n">file</span><span class="p">:</span> <span class="o">&</span><span class="n">Path</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Vec</span><span class="o"><</span><span class="n">LabelPixel</span><span class="o">></span> <span class="p">{</span>
<span class="nn">BufferedReader</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nn">File</span><span class="p">::</span><span class="nf">open</span><span class="p">(</span><span class="n">file</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">())</span>
<span class="nf">.lines</span><span class="p">()</span>
<span class="nf">.skip</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="nf">.map</span><span class="p">(|</span><span class="n">line</span><span class="p">|</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="nf">.unwrap</span><span class="p">();</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">iter</span> <span class="o">=</span> <span class="n">line</span><span class="nf">.as_slice</span><span class="p">()</span><span class="nf">.trim</span><span class="p">()</span>
<span class="nf">.split</span><span class="p">(</span><span class="sc">','</span><span class="p">)</span>
<span class="nf">.map</span><span class="p">(|</span><span class="n">x</span><span class="p">|</span> <span class="nf">from_str</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">());</span>
<span class="n">LabelPixel</span> <span class="p">{</span>
<span class="n">label</span><span class="p">:</span> <span class="n">iter</span><span class="nf">.next</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">(),</span>
<span class="n">pixels</span><span class="p">:</span> <span class="n">iter</span><span class="nf">.collect</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">})</span>
<span class="nf">.collect</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">distance_sqr</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="o">&</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> <span class="n">y</span><span class="p">:</span> <span class="o">&</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="k">-></span> <span class="nb">int</span> <span class="p">{</span>
<span class="c">// run through the two vectors, summing up the squares of the differences</span>
<span class="n">x</span><span class="nf">.iter</span><span class="p">()</span>
<span class="nf">.zip</span><span class="p">(</span><span class="n">y</span><span class="nf">.iter</span><span class="p">())</span>
<span class="nf">.fold</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="p">|</span><span class="n">s</span><span class="p">,</span> <span class="p">(</span><span class="o">&</span><span class="n">a</span><span class="p">,</span> <span class="o">&</span><span class="n">b</span><span class="p">)|</span> <span class="n">s</span> <span class="o">+</span> <span class="p">(</span><span class="n">a</span> <span class="o">-</span> <span class="n">b</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">a</span> <span class="o">-</span> <span class="n">b</span><span class="p">))</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">classify</span><span class="p">(</span><span class="n">training</span><span class="p">:</span> <span class="o">&</span><span class="p">[</span><span class="n">LabelPixel</span><span class="p">],</span> <span class="n">pixels</span><span class="p">:</span> <span class="o">&</span><span class="p">[</span><span class="nb">int</span><span class="p">])</span> <span class="k">-></span> <span class="nb">int</span> <span class="p">{</span>
<span class="n">training</span>
<span class="nf">.iter</span><span class="p">()</span>
<span class="c">// find element of `training` with the smallest distance_sqr to `pixel`</span>
<span class="nf">.min_by</span><span class="p">(|</span><span class="n">p</span><span class="p">|</span> <span class="nf">distance_sqr</span><span class="p">(</span><span class="n">p</span><span class="py">.pixels</span><span class="nf">.as_slice</span><span class="p">(),</span> <span class="n">pixels</span><span class="p">))</span><span class="nf">.unwrap</span><span class="p">()</span>
<span class="py">.label</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">training_set</span> <span class="o">=</span> <span class="nf">slurp_file</span><span class="p">(</span><span class="o">&</span><span class="nn">Path</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="s">"trainingsample.csv"</span><span class="p">));</span>
<span class="k">let</span> <span class="n">validation_sample</span> <span class="o">=</span> <span class="nf">slurp_file</span><span class="p">(</span><span class="o">&</span><span class="nn">Path</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="s">"validationsample.csv"</span><span class="p">));</span>
<span class="k">let</span> <span class="n">num_correct</span> <span class="o">=</span> <span class="n">validation_sample</span><span class="nf">.iter</span><span class="p">()</span>
<span class="nf">.filter</span><span class="p">(|</span><span class="n">x</span><span class="p">|</span> <span class="p">{</span>
<span class="nf">classify</span><span class="p">(</span><span class="n">training_set</span><span class="nf">.as_slice</span><span class="p">(),</span> <span class="n">x</span><span class="py">.pixels</span><span class="nf">.as_slice</span><span class="p">())</span> <span class="o">==</span> <span class="n">x</span><span class="py">.label</span>
<span class="p">})</span>
<span class="nf">.count</span><span class="p">();</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"Percentage correct: {}%"</span><span class="p">,</span>
<span class="n">num_correct</span> <span class="k">as</span> <span class="nb">f64</span> <span class="o">/</span> <span class="n">validation_sample</span><span class="nf">.len</span><span class="p">()</span> <span class="k">as</span> <span class="nb">f64</span> <span class="o">*</span> <span class="mf">100.0</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>(Prints <code class="language-plaintext highlighter-rouge">Percentage correct: 94.4%</code>, matching the OCaml.)</p>
<h2 id="hows-it-compare">How’s it compare?</h2>
<p>I don’t have an F# compiler, so I’ll only compare against the fastest
OCaml solution (from <a href="http://philtomson.github.io/blog/2014/05/30/stop-the-presses-ocaml-wins/">the follow-up post</a>), after making the
same modification to <code class="language-plaintext highlighter-rouge">distance</code>.</p>
<p>The Rust was compiled with <code class="language-plaintext highlighter-rouge">rustc -O</code>, and the OCaml with <code class="language-plaintext highlighter-rouge">ocamlopt
str.cmxa</code> (as recommended), using version 4.01.0. I ran each 3 times
(times in seconds) on <a href="https://github.com/c4fsharp/Dojo-Digits-Recognizer/tree/1eb4297a49dbd82a952c1523f5413519b8f1d62a/Dojo">these CSV files</a>.</p>
<table>
<thead>
<tr>
<th style="text-align: right">Lang</th>
<th style="text-align: right">1</th>
<th style="text-align: right">2</th>
<th style="text-align: right">3</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: right">Rust</td>
<td style="text-align: right">3.56</td>
<td style="text-align: right">3.46</td>
<td style="text-align: right">3.86</td>
</tr>
<tr>
<td style="text-align: right">OCaml</td>
<td style="text-align: right">13.9</td>
<td style="text-align: right">14.7</td>
<td style="text-align: right">14.1</td>
</tr>
</tbody>
</table>
<p>So the Rust code is about 3.5–4× faster than the
OCaml.</p>
<p>It’s worth noting that the Rust code is entirely safe and built
directly (and mostly minimally) using the abstractions provided by the
standard library. The speed is mainly due to the magic of
<a href="http://doc.rust-lang.org/master/std/iter/">Rust’s (lazy) iterators</a>
which provide very efficient sequential access to elements of
vectors/slices, as well as a variety of efficient
<a href="http://doc.rust-lang.org/master/guide-container.html#iterator-adaptors">adaptors</a>
implementing various useful algorithms. These may look high-level and
hard to optimise, but they are very transparent to the compiler,
resulting in fast machine code.</p>
<blockquote>
<p><em>Updated 2014-06-11</em>: the Rust code is not as fast as it could be,
due to bugs like
<a href="https://github.com/mozilla/rust/issues/11751">#11751</a>, caused by
LLVM being unable to understand that <code class="language-plaintext highlighter-rouge">&</code> pointers are never
null. benh wrote a <a href="https://gist.github.com/huonw/7b7473ac3981fead07ab">short slice-zip iterator</a> that may
make its way into the standard library: he even
<a href="https://news.ycombinator.com/item?id=7875969">used it</a> to make the
code 3 times faster.</p>
</blockquote>
<p>In comparison, the OCaml code has had to manually write a few
functions (for folding and for reading lines from a file), and
contains two possibly-concerning pieces of code:</p>
<figure class="highlight"><pre><code class="language-ocaml" data-lang="ocaml"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
</pre></td><td class="code"><pre><span class="k">let</span> <span class="n">v1</span> <span class="o">=</span> <span class="n">unsafe_get</span> <span class="n">a1</span> <span class="n">i</span> <span class="k">in</span>
<span class="k">let</span> <span class="n">v2</span> <span class="o">=</span> <span class="n">unsafe_get</span> <span class="n">a2</span> <span class="n">i</span> <span class="k">in</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>It might be interesting to compare against
<a href="http://leonardo-m.livejournal.com/111598.html">this D code</a>, but I
can’t get it to compile right.</p>
<h2 id="what-about-parallelism">What about parallelism?</h2>
<p>I’m glad you asked! Rust is designed to be good for concurrency, using
<a href="http://doc.rust-lang.org/master/std/kinds/trait.Share.html">the type system</a>
to guarantee that code is threadsafe. As I said before, Rust is under
heavy development, and currently lacks a data parallelism library (so
there’s no parallel-map to just call directly yet), but it’s easy
enough to use
<a href="http://doc.rust-lang.org/master/sync/struct.Future.html">the built-in futures</a>
for this.</p>
<p>The code can be made parallel simply by replacing the <code class="language-plaintext highlighter-rouge">main</code> function
with the following.</p>
<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">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
</pre></td><td class="code"><pre><span class="c">// how many chunks should the validation sample be divided into? (==</span>
<span class="c">// how many futures to create.)</span>
<span class="k">static</span> <span class="n">NUM_CHUNKS</span><span class="p">:</span> <span class="nb">uint</span> <span class="o">=</span> <span class="mi">32</span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">use</span> <span class="nn">sync</span><span class="p">::{</span><span class="nb">Arc</span><span class="p">,</span> <span class="n">Future</span><span class="p">};</span>
<span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="n">cmp</span><span class="p">;</span>
<span class="c">// "atomic reference counted": guaranteed thread-safe shared</span>
<span class="c">// memory. The type signature and API of `Arc` guarantees that</span>
<span class="c">// concurrent access to the contents will be safe, due to the `Share`</span>
<span class="c">// trait.</span>
<span class="k">let</span> <span class="n">training_set</span> <span class="o">=</span> <span class="nn">Arc</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nf">slurp_file</span><span class="p">(</span><span class="o">&</span><span class="nn">Path</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="s">"trainingsample.csv"</span><span class="p">)));</span>
<span class="k">let</span> <span class="n">validation_sample</span> <span class="o">=</span> <span class="nn">Arc</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nf">slurp_file</span><span class="p">(</span><span class="o">&</span><span class="nn">Path</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="s">"validationsample.csv"</span><span class="p">)));</span>
<span class="k">let</span> <span class="n">chunk_size</span> <span class="o">=</span> <span class="p">(</span><span class="n">validation_sample</span><span class="nf">.len</span><span class="p">()</span> <span class="o">+</span> <span class="n">NUM_CHUNKS</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">/</span> <span class="n">NUM_CHUNKS</span><span class="p">;</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">futures</span> <span class="o">=</span> <span class="nf">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">NUM_CHUNKS</span><span class="p">)</span><span class="nf">.map</span><span class="p">(|</span><span class="n">i</span><span class="p">|</span> <span class="p">{</span>
<span class="c">// create new "copies" (just incrementing the reference</span>
<span class="c">// counts) for our new future to handle.</span>
<span class="k">let</span> <span class="n">ts</span> <span class="o">=</span> <span class="n">training_set</span><span class="nf">.clone</span><span class="p">();</span>
<span class="k">let</span> <span class="n">vs</span> <span class="o">=</span> <span class="n">validation_sample</span><span class="nf">.clone</span><span class="p">();</span>
<span class="nn">Future</span><span class="p">::</span><span class="nf">spawn</span><span class="p">(</span><span class="nf">proc</span><span class="p">()</span> <span class="p">{</span>
<span class="c">// compute the region of the vector we are handling...</span>
<span class="k">let</span> <span class="n">lo</span> <span class="o">=</span> <span class="n">i</span> <span class="o">*</span> <span class="n">chunk_size</span><span class="p">;</span>
<span class="k">let</span> <span class="n">hi</span> <span class="o">=</span> <span class="nn">cmp</span><span class="p">::</span><span class="nf">min</span><span class="p">(</span><span class="n">lo</span> <span class="o">+</span> <span class="n">chunk_size</span><span class="p">,</span> <span class="n">vs</span><span class="nf">.len</span><span class="p">());</span>
<span class="c">// ... and then handle that region.</span>
<span class="n">vs</span><span class="nf">.slice</span><span class="p">(</span><span class="n">lo</span><span class="p">,</span> <span class="n">hi</span><span class="p">)</span>
<span class="nf">.iter</span><span class="p">()</span>
<span class="nf">.filter</span><span class="p">(|</span><span class="n">x</span><span class="p">|</span> <span class="p">{</span>
<span class="nf">classify</span><span class="p">(</span><span class="n">ts</span><span class="nf">.as_slice</span><span class="p">(),</span> <span class="n">x</span><span class="py">.pixels</span><span class="nf">.as_slice</span><span class="p">())</span> <span class="o">==</span> <span class="n">x</span><span class="py">.label</span>
<span class="p">})</span>
<span class="nf">.count</span><span class="p">()</span>
<span class="p">})</span>
<span class="p">})</span><span class="py">.collect</span><span class="p">::</span><span class="o"><</span><span class="nb">Vec</span><span class="o"><</span><span class="n">Future</span><span class="o"><</span><span class="nb">uint</span><span class="o">>>></span><span class="p">();</span>
<span class="c">// run through the futures (waiting for each to complete) and sum the results</span>
<span class="k">let</span> <span class="n">num_correct</span> <span class="o">=</span> <span class="n">futures</span><span class="nf">.mut_iter</span><span class="p">()</span><span class="nf">.map</span><span class="p">(|</span><span class="n">f</span><span class="p">|</span> <span class="n">f</span><span class="nf">.get</span><span class="p">())</span><span class="nf">.fold</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="p">|</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">|</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span><span class="p">);</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"Percentage correct: {}%"</span><span class="p">,</span>
<span class="n">num_correct</span> <span class="k">as</span> <span class="nb">f64</span> <span class="o">/</span> <span class="n">validation_sample</span><span class="nf">.len</span><span class="p">()</span> <span class="k">as</span> <span class="nb">f64</span> <span class="o">*</span> <span class="mf">100.0</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>(Also prints <code class="language-plaintext highlighter-rouge">Percentage correct: 94.4%</code>.)</p>
<p>This gives a nice speed up, approximately halving the time required:
the real time is now stable around 1.81 seconds (6.25 s of user time)
on my machine.</p>
<section id="external-links" class="no-print">
<div id="comments">
<span class="external-label">Comments:</span>
<ul class="external-list">
<li class="external-link"><a href="http://www.reddit.com/r/rust/comments/27s7ei/comparing_knn_in_rust/">/r/rust</a></li>
<li class="external-link"><a href="http://www.reddit.com/r/programming/comments/27s7g6/comparing_knn_in_rust/">/r/programming</a></li>
<li class="external-link"><a href="https://news.ycombinator.com/item?id=7872398">Hacker News</a></li>
</ul>
</div>
<div id="external-page">
<span class="external-label">Share this on</span>
<ul class="external-list">
<li class="external-link"><a href="https://twitter.com/intent/tweet?text=Comparing%20k-NN%20in%20Rust%20%23rustlang&url=https://huonw.github.io/blog/2014/06/comparing-knn-in-rust/&via=huon_w" target="_blank" rel="nofollow noopener" title="Share on Twitter">Twitter</a></li>
<li class="external-link"><a href="https://facebook.com/sharer.php?u=https://huonw.github.io/blog/2014/06/comparing-knn-in-rust/" rel="nofollow noopener" target="_blank" title="Share on Facebook">Facebook</a></li>
<li class="external-link"><a href="https://www.reddit.com/submit?url=https://huonw.github.io/blog/2014/06/comparing-knn-in-rust/&title=Comparing%20k-NN%20in%20Rust" rel="nofollow noopener" target="_blank" title="Share on Reddit">Reddit</a></li>
</ul>
</div>
</section>