From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, URIBL_BLOCKED,USER_AGENT_SANE_1 autolearn=no autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6E8B5C388F7 for ; Tue, 10 Nov 2020 10:46:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 12AA22076E for ; Tue, 10 Nov 2020 10:46:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726698AbgKJKqi (ORCPT ); Tue, 10 Nov 2020 05:46:38 -0500 Received: from foss.arm.com ([217.140.110.172]:53700 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726462AbgKJKqi (ORCPT ); Tue, 10 Nov 2020 05:46:38 -0500 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id F383E11D4; Tue, 10 Nov 2020 02:46:36 -0800 (PST) Received: from arm.com (usa-sjc-imap-foss1.foss.arm.com [10.121.207.14]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id BE3B13F6CF; Tue, 10 Nov 2020 02:46:35 -0800 (PST) Date: Tue, 10 Nov 2020 10:46:32 +0000 From: Dave Martin To: Li Qiang Cc: alexandre.torgue@st.com, catalin.marinas@arm.com, linux-crypto@vger.kernel.org, mcoquelin.stm32@gmail.com, will@kernel.org, davem@davemloft.net, linux-arm-kernel@lists.infradead.org, herbert@gondor.apana.org.au Subject: Re: [PATCH 0/1] arm64: Accelerate Adler32 using arm64 SVE instructions. Message-ID: <20201110104629.GJ6882@arm.com> References: <20201103121506.1533-1-liqiang64@huawei.com> <20201105165301.GH6882@arm.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: User-Agent: Mutt/1.5.23 (2014-03-12) Precedence: bulk List-ID: X-Mailing-List: linux-crypto@vger.kernel.org On Mon, Nov 09, 2020 at 11:43:35AM +0800, Li Qiang wrote: > Hi Dave, > > I carefully read the ideas you provided and the sample code you gave me.:) > > 在 2020/11/6 0:53, Dave Martin 写道: > > On Tue, Nov 03, 2020 at 08:15:05PM +0800, l00374334 wrote: > >> From: liqiang > >> > >> Dear all, > >> > >> Thank you for taking the precious time to read this email! > >> > >> Let me introduce the implementation ideas of my code here. > >> > >> In the process of using the compression library libz, I found that the adler32 > >> checksum always occupies a higher hot spot, so I paid attention to this algorithm. > >> After getting in touch with the SVE instruction set of armv8, I realized that > >> SVE can effectively accelerate adler32, so I made some attempts and got correct > >> and better performance results. I very much hope that this modification can be > >> applied to the kernel. > >> > >> Below is my analysis process: > >> > >> Adler32 algorithm > >> ================= > >> > >> Reference: https://en.wikipedia.org/wiki/Adler-32 > >> > >> Assume that the buf of the Adler32 checksum to be calculated is D and the length is n: > >> > >> A = 1 + D1 + D2 + ... + Dn (mod 65521) > >> > >> B = (1 + D1) + (1 + D1 + D2) + ... + (1 + D1 + D2 + ... + Dn) (mod 65521) > >> = n×D1 + (n−1)×D2 + (n−2)×D3 + ... + Dn + n (mod 65521) > >> > >> Adler-32(D) = B × 65536 + A > >> > >> In C, an inefficient but straightforward implementation is: > >> > >> const uint32_t MOD_ADLER = 65521; > >> > >> uint32_t adler32(unsigned char *data, size_t len) > >> { > >> uint32_t a = 1, b = 0; > >> size_t index; > >> > >> // Process each byte of the data in order > >> for (index = 0; index < len; ++index) > >> { > >> a = (a + data[index]) % MOD_ADLER; > >> b = (b + a) % MOD_ADLER; > >> } > >> > >> return (b << 16) | a; > >> } > >> > >> SVE vector method > >> ================= > >> > >> Step 1. Determine the block size: > >> Use addvl instruction to get SVE bit width. > >> Assuming the SVE bit width is x here. > >> > >> Step 2. Start to calculate the first block: > >> The calculation formula is: > >> A1 = 1 + D1 + D2 + ... + Dx (mod 65521) > >> B1 = x*D1 + (x-1)*D2 + ... + Dx + x (mod 65521) > >> > >> Step 3. Calculate the follow block: > >> The calculation formula of A2 is very simple, just add up: > >> A2 = A1 + Dx+1 + Dx+2 + ... + D2x (mod 65521) > >> > >> The calculation formula of B2 is more complicated, because > >> the result is related to the length of buf. When calculating > >> the B1 block, it is actually assumed that the length is the > >> block length x. Now when calculating B2, the length is expanded > >> to 2x, so B2 becomes: > >> B2 = 2x*D1 + (2x-1)*D2 + ... + (x+1)*Dx + x*D(x+1) + ... + D2x + 2x > >> = x*D1 + x*D1 + x*D2 + (x-1)*D2 + ... + x*Dx + Dx + x*1 + x + [x*D(x+1) + (x-1)*D(x+2) + ... + D2x] > >> ^^^^ ~~~~ ^^^^ ~~~~~~~~ ^^^^ ~~ ^^^ ~ +++++++++++++++++++++++++++++++++++++ > >> Through the above polynomial transformation: > >> Symbol "^" represents the ; > >> Symbol "~" represents the ; > >> Symbol "+" represents the next block. > >> > >> So we can get the method of calculating the next block from > >> the previous block(Assume that the first byte number of the > >> new block starts from 1): > >> An+1 = An + D1 + D2 + ... + Dx (mod 65521) > >> Bn+1 = Bn + x*An + x*D1 + (x-1)*D2 + ... + Dx (mod 65521) > > > > Putting aside people's concerns for the moment, I think this may be > > formulated in a slightly more convenient way: > > > > If > > X0, X1, ... are the data bytes > > An is 1 + Sum [i=0 .. n-1] Xi > > Bn is n + Sum [i=0 .. n-1] (n-i)Xi > > = Sum [i=1 .. n] Ai > > Yes, this can be calculated from the original expression. > > > > > (i.e., An, Bn are the accumulations for the first n bytes here, not the > > first n blocks) > > > > then > > > > A[n+v] - An = Sum[i=n .. n+v-1] Xi > > > > B[n+v] - Bn = v + (Sum [i=0 .. n+v-1] (n+v-i) Xi) > > - Sum [i=0 .. n-1] (n-i)Xi > > > > = v + (Sum [i=n .. n+v-1] (n+v-i) Xi) > > + (Sum [i=0 .. n-1] (n+v-i) Xi) > > - Sum [i=0 .. n-1] (n-i)Xi > > > > = v + (Sum [i=n .. n+v-1] (n+v-i) Xi) > > + Sum [i=0 .. n-1] ((n+v-i) - (n-i)) Xi > > > > = v + (Sum [i=n .. n+v-1] (n+v-i) Xi) > > + vSum [i=0 .. n-1] Xi > > > > = v + v(An - 1) + Sum [i=n .. n+v-1] (n+v-i) Xi > > > > = vAn + Sum [i=n .. n+v-1] (n+v-i) Xi > > > > = vAn + vSum [i=n .. n+v-1] Xi > > + Sum [i=n .. n+v-1] (n-i) Xi > > > > = vAn + vSum [i=n .. n+v-1] Xi > > + Sum [i=n .. n+v-1] (n-i) Xi > > > > = vA[n+v] + Sum [i=n .. n+v-1] (n-i) Xi > > > > Let j = i - n; then: > > > > B[n+v] - Bn = vA[n+v] - Sum [j=0 .. v-1] j X[j+n] > > > > Which gives us a multiplier j that increases with the X[] index. > > My original approach is to multiply the byte sequence with the **decreasing** > sequence. I think the increasing sequence here is more friendly to the trailing > bytes of the loop.:-) > > > > > > > I think this gives a core loop along the following lines. I don't know > > whether this is correct, or whether it works -- but feel free to take > > ideas from it if it helps. > > > > Accumulators are 32 bits. This provides for a fair number of iterations > > without overflow, but large input data will still require splitting into > > chunks, with modulo reduction steps in between. There are rather a lot > > of serial dependencies in the core loop, but since the operations > > involved are relatively cheap, this is probably not a significant issue > > in practice: the load-to-use dependency is probably the bigger concern. > > Pipelined loop unrolling could address these if necessary. > > > > The horizontal reductions (UADDV) still probably don't need to be done > > until after the last chunk. > > > > > > Beware: I wasn't careful with the initial values for Bn / An, so some > > additional adjustments might be needed... > > > > --8<-- > > > > ptrue pP.s > > ptrue pA.s > > > > mov zA.s, #0 // accumulator for An > > mov zB.s, #0 // accumulator for Bn > > index zJ.s, #0, #1 // zJ.s = [0, 1, .. V-1] > > > > mov zV.s, #0 > > incw zV.s // zV.s = [V, V, .. V] > > When I actually tested this code, I found that the byte length of the > test must be equal to the vector length divided by 4 (that is, an integer > multiple of the number of words in the SVE) and the result is correct. > > And I think one of the reasons is that the value stored in zV.s needs to be > changed in the last cycle. It should be changed to the actual number of > bytes remaining in the last cycle. :) Ah yes, you're quite right about this. In my derivation above, v will need to be smaller than the vector length when processing the final, partial block (if any). > > > > > // where V is number of elements per block > > // = the number of 32-bit elements that fit in a Z-register > > > > add xLimit, xX, xLen > > b start > > > > loop: > > ld1b zX.s, pP/z, [xX] > > incw xX > > In order to verify my conjecture, I added a bit of code to here, which is to > subtract the corresponding value from zV in the last loop. But it is correct > only when the number of bytes is less than one cycle. Test cases that exceed > one complete cycle will also fail. > > So I guess before calculating the last cycle, zA should be summed first, > because before the end of the cycle, zA and zB are scattered in the elements > of the vector, if the last cycle calculates zB, only part of zA is summed > ( Because pP/m does not count inactive elements), it should be incomplete. > > This is just my guess and has not yet been verified.:) I think zA shouldn't be wrong, since that only accumulates active elements anyway. I think it's the bogus zV multiplier involved in the update to zB that is the problem. > > > > add zA.s, pP/m, zA.s, zX.s // zA.s += zX.s > > > > msb zX.s, pP/m, zJ.s, zB.s // zX.s := zB.s - zX.s * zJ.s > > > > movprfx zB, zA > > mad zB.s, pP/m, zV.s, zX.s // zB.s := zX.s + zA.s * V > > start: > > whilelo pP.s, xX, xLimit > > b.first loop There are a few options, I guess. One way is to use CNTP inside the loop to get the number of active elements, instead of just setting zV in advance. This approach may add a slight overhead, but it is worth experimenting with it. If the overhead is neglibible, then this approach has the example of being simple to understand. Alternatively, you could do an adjustment after the loop ends to subtract the appropriate amounts from zB. Unfortunately, pP has already been overwritten by the time the loop ends. If could be backed up into another P-register before overwriting it: this should be pretty low- overhead. A final option would be to change the b.first to a b.last, so that the loop ends after the final full block. Then copy-paste the loop body to execute once more, but using CNTP to get the element count to multiply by, as described above. This makes the code a bit larger, but it probably the best-performance option. You may be able to rotate the loop so that it breaks out after updating zA (which IIUC doesn't need to be done in a special way for the partial block). This would mean you only have to specialise the zB update code. > > > > // Collect the partial sums together: > > > > uaddv d0, pA, z0.s > > uaddv d1, pA, z1.s > > > > // Finally, add 1 to d0, and xLen to d1, and do modulo reduction. > > > > -->8-- > > > > [...] > > > > Cheers > > ---Dave > > . > > > > The code you sent me provides a correct way to deal with trailing bytes, > I need to spend some more time to debug the problem encountered above. > Thank you! > > Cheers.^_^ I was cheekily leaving the testing and debugging for you to do :) Part of my reason for interest in this is that if we enable SVE in the kernel, it would be good to have some clean examples for people to follow -- even if not this algo. SVE is a bit different to use compared with fixed-length vector ISAs, so worked examples are always useful. Cheers ---Dave From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI, SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_SANE_1 autolearn=no autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 632D7C388F7 for ; Tue, 10 Nov 2020 10:47:12 +0000 (UTC) Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id C5DCF205CB for ; Tue, 10 Nov 2020 10:47:11 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="070xhbz+" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org C5DCF205CB Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=arm.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=merlin.20170209; h=Sender:Content-Transfer-Encoding: Content-Type:Cc:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:In-Reply-To:MIME-Version:References:Message-ID: Subject:To:From:Date:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=w43VivBtOCFBZJNpJJtdG9/BpyuGKfb8E9XFi50shrE=; b=070xhbz+qbaIqWEMV911Vq5Rm Y10vIZvtgNF8uYiyDZzxzi+3z1kZkrGheEZTeDTySF7pAWDPrWKoUL+91LqwYNtYxqqH/dZWe1+x/ Uy6RpC8gV2EcsbZ3Xz0wSvSTKRZlX87giL7E7GIEjiz1Z7uw4yUHG12J4lvUCcWbQBDL0dcA7WmCE l8B7qGuO8ZGyMf9GYViwB5uSMmZ+CAnR6Av2JQ/98p6b/xcZRTMjMf54A75+0S1GpGAW7vTqzmAFS FSP9l2q2CqeEfMNhOFApRZWGAmAYf/8iYrz/0EXhhq+lcyT8VXPklyuJ9oQMB5OYrydQjiUsGvzLV YMO79DLsA==; Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1kcRAh-0006Ta-AJ; Tue, 10 Nov 2020 10:46:43 +0000 Received: from foss.arm.com ([217.140.110.172]) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1kcRAd-0006S7-D5 for linux-arm-kernel@lists.infradead.org; Tue, 10 Nov 2020 10:46:40 +0000 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id F383E11D4; Tue, 10 Nov 2020 02:46:36 -0800 (PST) Received: from arm.com (usa-sjc-imap-foss1.foss.arm.com [10.121.207.14]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id BE3B13F6CF; Tue, 10 Nov 2020 02:46:35 -0800 (PST) Date: Tue, 10 Nov 2020 10:46:32 +0000 From: Dave Martin To: Li Qiang Subject: Re: [PATCH 0/1] arm64: Accelerate Adler32 using arm64 SVE instructions. Message-ID: <20201110104629.GJ6882@arm.com> References: <20201103121506.1533-1-liqiang64@huawei.com> <20201105165301.GH6882@arm.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.23 (2014-03-12) X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20201110_054639_563591_02ECE598 X-CRM114-Status: GOOD ( 59.58 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: alexandre.torgue@st.com, catalin.marinas@arm.com, linux-crypto@vger.kernel.org, mcoquelin.stm32@gmail.com, will@kernel.org, davem@davemloft.net, linux-arm-kernel@lists.infradead.org, herbert@gondor.apana.org.au Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org T24gTW9uLCBOb3YgMDksIDIwMjAgYXQgMTE6NDM6MzVBTSArMDgwMCwgTGkgUWlhbmcgd3JvdGU6 Cj4gSGkgRGF2ZSwKPiAKPiBJIGNhcmVmdWxseSByZWFkIHRoZSBpZGVhcyB5b3UgcHJvdmlkZWQg YW5kIHRoZSBzYW1wbGUgY29kZSB5b3UgZ2F2ZSBtZS46KQo+IAo+IOWcqCAyMDIwLzExLzYgMDo1 MywgRGF2ZSBNYXJ0aW4g5YaZ6YGTOgo+ID4gT24gVHVlLCBOb3YgMDMsIDIwMjAgYXQgMDg6MTU6 MDVQTSArMDgwMCwgbDAwMzc0MzM0IHdyb3RlOgo+ID4+IEZyb206IGxpcWlhbmcgPGxpcWlhbmc2 NEBodWF3ZWkuY29tPgo+ID4+Cj4gPj4gRGVhciBhbGwsCj4gPj4KPiA+PiBUaGFuayB5b3UgZm9y IHRha2luZyB0aGUgcHJlY2lvdXMgdGltZSB0byByZWFkIHRoaXMgZW1haWwhCj4gPj4KPiA+PiBM ZXQgbWUgaW50cm9kdWNlIHRoZSBpbXBsZW1lbnRhdGlvbiBpZGVhcyBvZiBteSBjb2RlIGhlcmUu Cj4gPj4KPiA+PiBJbiB0aGUgcHJvY2VzcyBvZiB1c2luZyB0aGUgY29tcHJlc3Npb24gbGlicmFy eSBsaWJ6LCBJIGZvdW5kIHRoYXQgdGhlIGFkbGVyMzIKPiA+PiBjaGVja3N1bSBhbHdheXMgb2Nj dXBpZXMgYSBoaWdoZXIgaG90IHNwb3QsIHNvIEkgcGFpZCBhdHRlbnRpb24gdG8gdGhpcyBhbGdv cml0aG0uCj4gPj4gQWZ0ZXIgZ2V0dGluZyBpbiB0b3VjaCB3aXRoIHRoZSBTVkUgaW5zdHJ1Y3Rp b24gc2V0IG9mIGFybXY4LCBJIHJlYWxpemVkIHRoYXQKPiA+PiBTVkUgY2FuIGVmZmVjdGl2ZWx5 IGFjY2VsZXJhdGUgYWRsZXIzMiwgc28gSSBtYWRlIHNvbWUgYXR0ZW1wdHMgYW5kIGdvdCBjb3Jy ZWN0Cj4gPj4gYW5kIGJldHRlciBwZXJmb3JtYW5jZSByZXN1bHRzLiBJIHZlcnkgbXVjaCBob3Bl IHRoYXQgdGhpcyBtb2RpZmljYXRpb24gY2FuIGJlCj4gPj4gYXBwbGllZCB0byB0aGUga2VybmVs Lgo+ID4+Cj4gPj4gQmVsb3cgaXMgbXkgYW5hbHlzaXMgcHJvY2VzczoKPiA+Pgo+ID4+IEFkbGVy MzIgYWxnb3JpdGhtCj4gPj4gPT09PT09PT09PT09PT09PT0KPiA+Pgo+ID4+IFJlZmVyZW5jZTog aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQWRsZXItMzIKPiA+Pgo+ID4+IEFzc3VtZSB0 aGF0IHRoZSBidWYgb2YgdGhlIEFkbGVyMzIgY2hlY2tzdW0gdG8gYmUgY2FsY3VsYXRlZCBpcyBE IGFuZCB0aGUgbGVuZ3RoIGlzIG46Cj4gPj4KPiA+PiAgICAgICAgIEEgPSAxICsgRDEgKyBEMiAr IC4uLiArIERuIChtb2QgNjU1MjEpCj4gPj4KPiA+PiAgICAgICAgIEIgPSAoMSArIEQxKSArICgx ICsgRDEgKyBEMikgKyAuLi4gKyAoMSArIEQxICsgRDIgKyAuLi4gKyBEbikgKG1vZCA2NTUyMSkK PiA+PiAgICAgICAgICAgPSBuw5dEMSArIChu4oiSMSnDl0QyICsgKG7iiJIyKcOXRDMgKyAuLi4g KyBEbiArIG4gKG1vZCA2NTUyMSkKPiA+Pgo+ID4+ICAgICAgICAgQWRsZXItMzIoRCkgPSBCIMOX IDY1NTM2ICsgQQo+ID4+Cj4gPj4gSW4gQywgYW4gaW5lZmZpY2llbnQgYnV0IHN0cmFpZ2h0Zm9y d2FyZCBpbXBsZW1lbnRhdGlvbiBpczoKPiA+Pgo+ID4+ICAgICAgICAgY29uc3QgdWludDMyX3Qg TU9EX0FETEVSID0gNjU1MjE7Cj4gPj4KPiA+PiAgICAgICAgIHVpbnQzMl90IGFkbGVyMzIodW5z aWduZWQgY2hhciAqZGF0YSwgc2l6ZV90IGxlbikKPiA+PiAgICAgICAgIHsKPiA+PiAgICAgICAg ICAgICAgICAgdWludDMyX3QgYSA9IDEsIGIgPSAwOwo+ID4+ICAgICAgICAgICAgICAgICBzaXpl X3QgaW5kZXg7Cj4gPj4KPiA+PiAgICAgICAgICAgICAgICAgLy8gUHJvY2VzcyBlYWNoIGJ5dGUg b2YgdGhlIGRhdGEgaW4gb3JkZXIKPiA+PiAgICAgICAgICAgICAgICAgZm9yIChpbmRleCA9IDA7 IGluZGV4IDwgbGVuOyArK2luZGV4KQo+ID4+ICAgICAgICAgICAgICAgICB7Cj4gPj4gICAgICAg ICAgICAgICAgICAgICAgICAgYSA9IChhICsgZGF0YVtpbmRleF0pICUgTU9EX0FETEVSOwo+ID4+ ICAgICAgICAgICAgICAgICAgICAgICAgIGIgPSAoYiArIGEpICUgTU9EX0FETEVSOwo+ID4+ICAg ICAgICAgICAgICAgICB9Cj4gPj4KPiA+PiAgICAgICAgICAgICAgICAgcmV0dXJuIChiIDw8IDE2 KSB8IGE7Cj4gPj4gICAgICAgICB9Cj4gPj4KPiA+PiBTVkUgdmVjdG9yIG1ldGhvZAo+ID4+ID09 PT09PT09PT09PT09PT09Cj4gPj4KPiA+PiBTdGVwIDEuIERldGVybWluZSB0aGUgYmxvY2sgc2l6 ZToKPiA+PiAgICAgICAgIFVzZSBhZGR2bCBpbnN0cnVjdGlvbiB0byBnZXQgU1ZFIGJpdCB3aWR0 aC4KPiA+PiAgICAgICAgIEFzc3VtaW5nIHRoZSBTVkUgYml0IHdpZHRoIGlzIHggaGVyZS4KPiA+ Pgo+ID4+IFN0ZXAgMi4gU3RhcnQgdG8gY2FsY3VsYXRlIHRoZSBmaXJzdCBibG9jazoKPiA+PiAg ICAgICAgIFRoZSBjYWxjdWxhdGlvbiBmb3JtdWxhIGlzOgo+ID4+ICAgICAgICAgICAgICAgICBB MSA9IDEgKyBEMSArIEQyICsgLi4uICsgRHggKG1vZCA2NTUyMSkKPiA+PiAgICAgICAgICAgICAg ICAgQjEgPSB4KkQxICsgKHgtMSkqRDIgKyAuLi4gKyBEeCArIHggKG1vZCA2NTUyMSkKPiA+Pgo+ ID4+IFN0ZXAgMy4gQ2FsY3VsYXRlIHRoZSBmb2xsb3cgYmxvY2s6Cj4gPj4gICAgICAgICBUaGUg Y2FsY3VsYXRpb24gZm9ybXVsYSBvZiBBMiBpcyB2ZXJ5IHNpbXBsZSwganVzdCBhZGQgdXA6Cj4g Pj4gICAgICAgICAgICAgICAgIEEyID0gQTEgKyBEeCsxICsgRHgrMiArIC4uLiArIEQyeCAobW9k IDY1NTIxKQo+ID4+Cj4gPj4gICAgICAgICBUaGUgY2FsY3VsYXRpb24gZm9ybXVsYSBvZiBCMiBp cyBtb3JlIGNvbXBsaWNhdGVkLCBiZWNhdXNlCj4gPj4gICAgICAgICB0aGUgcmVzdWx0IGlzIHJl bGF0ZWQgdG8gdGhlIGxlbmd0aCBvZiBidWYuIFdoZW4gY2FsY3VsYXRpbmcKPiA+PiAgICAgICAg IHRoZSBCMSBibG9jaywgaXQgaXMgYWN0dWFsbHkgYXNzdW1lZCB0aGF0IHRoZSBsZW5ndGggaXMg dGhlCj4gPj4gICAgICAgICBibG9jayBsZW5ndGggeC4gTm93IHdoZW4gY2FsY3VsYXRpbmcgQjIs IHRoZSBsZW5ndGggaXMgZXhwYW5kZWQKPiA+PiAgICAgICAgIHRvIDJ4LCBzbyBCMiBiZWNvbWVz Ogo+ID4+ICAgICAgICAgICAgICAgICBCMiA9IDJ4KkQxICsgKDJ4LTEpKkQyICAgICAgICAgICAg ICsgLi4uICsgKHgrMSkqRHggKyB4KkQoeCsxKSArIC4uLiArIEQyeCArIDJ4Cj4gPj4gICAgICAg ICAgICAgICAgICAgID0geCpEMSArIHgqRDEgKyB4KkQyICsgKHgtMSkqRDIgKyAuLi4gKyB4KkR4 ICsgRHggKyB4KjEgKyB4ICsgW3gqRCh4KzEpICsgKHgtMSkqRCh4KzIpICsgLi4uICsgRDJ4XQo+ ID4+ICAgICAgICAgICAgICAgICAgICAgIF5eXl4gICB+fn5+ICAgXl5eXiAgIH5+fn5+fn5+ICAg ICAgICAgXl5eXiAgIH5+ICAgXl5eICAgfiAgICsrKysrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysKPiA+PiAgICAgICAgIFRocm91Z2ggdGhlIGFib3ZlIHBvbHlub21pYWwgdHJhbnNm b3JtYXRpb246Cj4gPj4gICAgICAgICAgICAgICAgIFN5bWJvbCAiXiIgcmVwcmVzZW50cyB0aGUg PHggKiBBMT47Cj4gPj4gICAgICAgICAgICAgICAgIFN5bWJvbCAifiIgcmVwcmVzZW50cyB0aGUg PEIxPjsKPiA+PiAgICAgICAgICAgICAgICAgU3ltYm9sICIrIiByZXByZXNlbnRzIHRoZSBuZXh0 IGJsb2NrLgo+ID4+Cj4gPj4gICAgICAgICBTbyB3ZSBjYW4gZ2V0IHRoZSBtZXRob2Qgb2YgY2Fs Y3VsYXRpbmcgdGhlIG5leHQgYmxvY2sgZnJvbQo+ID4+ICAgICAgICAgdGhlIHByZXZpb3VzIGJs b2NrKEFzc3VtZSB0aGF0IHRoZSBmaXJzdCBieXRlIG51bWJlciBvZiB0aGUKPiA+PiAgICAgICAg IG5ldyBibG9jayBzdGFydHMgZnJvbSAxKToKPiA+PiAgICAgICAgICAgICAgICAgQW4rMSA9IEFu ICsgRDEgKyBEMiArIC4uLiArIER4IChtb2QgNjU1MjEpCj4gPj4gICAgICAgICAgICAgICAgIEJu KzEgPSBCbiArIHgqQW4gKyB4KkQxICsgKHgtMSkqRDIgKyAuLi4gKyBEeCAobW9kIDY1NTIxKQo+ ID4gCj4gPiBQdXR0aW5nIGFzaWRlIHBlb3BsZSdzIGNvbmNlcm5zIGZvciB0aGUgbW9tZW50LCBJ IHRoaW5rIHRoaXMgbWF5IGJlCj4gPiBmb3JtdWxhdGVkIGluIGEgc2xpZ2h0bHkgbW9yZSBjb252 ZW5pZW50IHdheToKPiA+IAo+ID4gSWYKPiA+IAlYMCwgWDEsIC4uLiBhcmUgdGhlIGRhdGEgYnl0 ZXMKPiA+IAlBbiBpcyAxICsgU3VtIFtpPTAgLi4gbi0xXSBYaQo+ID4gCUJuIGlzIG4gKyBTdW0g W2k9MCAuLiBuLTFdIChuLWkpWGkKPiA+IAkJPSBTdW0gW2k9MSAuLiBuXSBBaQo+IAo+IFllcywg dGhpcyBjYW4gYmUgY2FsY3VsYXRlZCBmcm9tIHRoZSBvcmlnaW5hbCBleHByZXNzaW9uLgo+IAo+ ID4gCj4gPiAoaS5lLiwgQW4sIEJuIGFyZSB0aGUgYWNjdW11bGF0aW9ucyBmb3IgdGhlIGZpcnN0 IG4gYnl0ZXMgaGVyZSwgbm90IHRoZQo+ID4gZmlyc3QgbiBibG9ja3MpCj4gPiAKPiA+IHRoZW4K PiA+IAo+ID4gCUFbbit2XSAtIEFuID0gU3VtW2k9biAuLiBuK3YtMV0gWGkKPiA+IAo+ID4gCUJb bit2XSAtIEJuID0gdiArIChTdW0gW2k9MCAuLiBuK3YtMV0gKG4rdi1pKSBYaSkKPiA+IAkJCS0g U3VtIFtpPTAgLi4gbi0xXSAobi1pKVhpCj4gPiAKPiA+IAkJPSB2ICsgKFN1bSBbaT1uIC4uIG4r di0xXSAobit2LWkpIFhpKQo+ID4gCQkJKyAoU3VtIFtpPTAgLi4gbi0xXSAobit2LWkpIFhpKQo+ ID4gCQkJLSBTdW0gW2k9MCAuLiBuLTFdIChuLWkpWGkKPiA+IAo+ID4gCQk9IHYgKyAoU3VtIFtp PW4gLi4gbit2LTFdIChuK3YtaSkgWGkpCj4gPiAJCQkrIFN1bSBbaT0wIC4uIG4tMV0gKChuK3Yt aSkgLSAobi1pKSkgWGkKPiA+IAo+ID4gCQk9IHYgKyAoU3VtIFtpPW4gLi4gbit2LTFdIChuK3Yt aSkgWGkpCj4gPiAJCQkrIHZTdW0gW2k9MCAuLiBuLTFdIFhpCj4gPiAKPiA+IAkJPSB2ICsgdihB biAtIDEpICsgU3VtIFtpPW4gLi4gbit2LTFdIChuK3YtaSkgWGkKPiA+IAo+ID4gCQk9IHZBbiAr IFN1bSBbaT1uIC4uIG4rdi0xXSAobit2LWkpIFhpCj4gPiAKPiA+IAkJPSB2QW4gKyB2U3VtIFtp PW4gLi4gbit2LTFdIFhpCj4gPiAJCQkrIFN1bSBbaT1uIC4uIG4rdi0xXSAobi1pKSBYaQo+ID4g Cj4gPiAJCT0gdkFuICsgdlN1bSBbaT1uIC4uIG4rdi0xXSBYaQo+ID4gCQkJKyBTdW0gW2k9biAu LiBuK3YtMV0gKG4taSkgWGkKPiA+IAo+ID4gCQk9IHZBW24rdl0gKyBTdW0gW2k9biAuLiBuK3Yt MV0gKG4taSkgWGkKPiA+IAo+ID4gTGV0IGogPSBpIC0gbjsgdGhlbjoKPiA+IAo+ID4gCUJbbit2 XSAtIEJuID0gdkFbbit2XSAtIFN1bSBbaj0wIC4uIHYtMV0gaiBYW2orbl0KPiA+IAo+ID4gV2hp Y2ggZ2l2ZXMgdXMgYSBtdWx0aXBsaWVyIGogdGhhdCBpbmNyZWFzZXMgd2l0aCB0aGUgWFtdIGlu ZGV4Lgo+IAo+IE15IG9yaWdpbmFsIGFwcHJvYWNoIGlzIHRvIG11bHRpcGx5IHRoZSBieXRlIHNl cXVlbmNlIHdpdGggdGhlICoqZGVjcmVhc2luZyoqCj4gc2VxdWVuY2UuIEkgdGhpbmsgdGhlIGlu Y3JlYXNpbmcgc2VxdWVuY2UgaGVyZSBpcyBtb3JlIGZyaWVuZGx5IHRvIHRoZSB0cmFpbGluZwo+ IGJ5dGVzIG9mIHRoZSBsb29wLjotKQo+IAo+ID4gCj4gPiAKPiA+IEkgdGhpbmsgdGhpcyBnaXZl cyBhIGNvcmUgbG9vcCBhbG9uZyB0aGUgZm9sbG93aW5nIGxpbmVzLiAgSSBkb24ndCBrbm93Cj4g PiB3aGV0aGVyIHRoaXMgaXMgY29ycmVjdCwgb3Igd2hldGhlciBpdCB3b3JrcyAtLSBidXQgZmVl bCBmcmVlIHRvIHRha2UKPiA+IGlkZWFzIGZyb20gaXQgaWYgaXQgaGVscHMuCj4gPiAKPiA+IEFj Y3VtdWxhdG9ycyBhcmUgMzIgYml0cy4gIFRoaXMgcHJvdmlkZXMgZm9yIGEgZmFpciBudW1iZXIg b2YgaXRlcmF0aW9ucwo+ID4gd2l0aG91dCBvdmVyZmxvdywgYnV0IGxhcmdlIGlucHV0IGRhdGEg d2lsbCBzdGlsbCByZXF1aXJlIHNwbGl0dGluZyBpbnRvCj4gPiBjaHVua3MsIHdpdGggbW9kdWxv IHJlZHVjdGlvbiBzdGVwcyBpbiBiZXR3ZWVuLiAgVGhlcmUgYXJlIHJhdGhlciBhIGxvdAo+ID4g b2Ygc2VyaWFsIGRlcGVuZGVuY2llcyBpbiB0aGUgY29yZSBsb29wLCBidXQgc2luY2UgdGhlIG9w ZXJhdGlvbnMKPiA+IGludm9sdmVkIGFyZSByZWxhdGl2ZWx5IGNoZWFwLCB0aGlzIGlzIHByb2Jh Ymx5IG5vdCBhIHNpZ25pZmljYW50IGlzc3VlCj4gPiBpbiBwcmFjdGljZTogdGhlIGxvYWQtdG8t dXNlIGRlcGVuZGVuY3kgaXMgcHJvYmFibHkgdGhlIGJpZ2dlciBjb25jZXJuLgo+ID4gUGlwZWxp bmVkIGxvb3AgdW5yb2xsaW5nIGNvdWxkIGFkZHJlc3MgdGhlc2UgaWYgbmVjZXNzYXJ5Lgo+ID4g Cj4gPiBUaGUgaG9yaXpvbnRhbCByZWR1Y3Rpb25zIChVQUREVikgc3RpbGwgcHJvYmFibHkgZG9u J3QgbmVlZCB0byBiZSBkb25lCj4gPiB1bnRpbCBhZnRlciB0aGUgbGFzdCBjaHVuay4KPiA+IAo+ ID4gCj4gPiBCZXdhcmU6IEkgd2Fzbid0IGNhcmVmdWwgd2l0aCB0aGUgaW5pdGlhbCB2YWx1ZXMg Zm9yIEJuIC8gQW4sIHNvIHNvbWUKPiA+IGFkZGl0aW9uYWwgYWRqdXN0bWVudHMgbWlnaHQgYmUg bmVlZGVkLi4uCj4gPiAKPiA+IC0tODwtLQo+ID4gCj4gPiAJcHRydWUgICBwUC5zCj4gPiAJcHRy dWUgICBwQS5zCj4gPiAKPiA+IAltb3YgICAgIHpBLnMsICMwICAgICAgICAgICAgICAgIC8vIGFj Y3VtdWxhdG9yIGZvciBBbgo+ID4gCW1vdiAgICAgekIucywgIzAgICAgICAgICAgICAgICAgLy8g YWNjdW11bGF0b3IgZm9yIEJuCj4gPiAJaW5kZXggICB6Si5zLCAjMCwgIzEgICAgICAgICAgICAv LyB6Si5zID0gWzAsIDEsIC4uIFYtMV0KPiA+IAo+ID4gCW1vdiAgICAgelYucywgIzAKPiA+IAlp bmN3ICAgIHpWLnMgICAgICAgICAgICAgICAgICAgIC8vIHpWLnMgPSBbViwgViwgLi4gVl0KPiAK PiBXaGVuIEkgYWN0dWFsbHkgdGVzdGVkIHRoaXMgY29kZSwgSSBmb3VuZCB0aGF0IHRoZSBieXRl IGxlbmd0aCBvZiB0aGUKPiB0ZXN0IG11c3QgYmUgZXF1YWwgdG8gdGhlIHZlY3RvciBsZW5ndGgg ZGl2aWRlZCBieSA0ICh0aGF0IGlzLCBhbiBpbnRlZ2VyCj4gbXVsdGlwbGUgb2YgdGhlIG51bWJl ciBvZiB3b3JkcyBpbiB0aGUgU1ZFKSBhbmQgdGhlIHJlc3VsdCBpcyBjb3JyZWN0Lgo+Cj4gQW5k IEkgdGhpbmsgb25lIG9mIHRoZSByZWFzb25zIGlzIHRoYXQgdGhlIHZhbHVlIHN0b3JlZCBpbiB6 Vi5zIG5lZWRzIHRvIGJlCj4gY2hhbmdlZCBpbiB0aGUgbGFzdCBjeWNsZS4gSXQgc2hvdWxkIGJl IGNoYW5nZWQgdG8gdGhlIGFjdHVhbCBudW1iZXIgb2YKPiBieXRlcyByZW1haW5pbmcgaW4gdGhl IGxhc3QgY3ljbGUuIDopCgpBaCB5ZXMsIHlvdSdyZSBxdWl0ZSByaWdodCBhYm91dCB0aGlzLiAg SW4gbXkgZGVyaXZhdGlvbiBhYm92ZSwgdiB3aWxsCm5lZWQgdG8gYmUgc21hbGxlciB0aGFuIHRo ZSB2ZWN0b3IgbGVuZ3RoIHdoZW4gcHJvY2Vzc2luZyB0aGUgZmluYWwsCnBhcnRpYWwgYmxvY2sg KGlmIGFueSkuCgo+IAo+ID4gCj4gPiAvLyB3aGVyZSBWIGlzIG51bWJlciBvZiBlbGVtZW50cyBw ZXIgYmxvY2sKPiA+IC8vICAgICAgPSB0aGUgbnVtYmVyIG9mIDMyLWJpdCBlbGVtZW50cyB0aGF0 IGZpdCBpbiBhIFotcmVnaXN0ZXIKPiA+IAo+ID4gCWFkZCAgICAgeExpbWl0LCB4WCwgeExlbgo+ ID4gCWIgICAgICAgc3RhcnQKPiA+IAo+ID4gbG9vcDogICAKPiA+IAlsZDFiICAgIHpYLnMsIHBQ L3osIFt4WF0KPiA+IAlpbmN3ICAgIHhYCj4gCj4gSW4gb3JkZXIgdG8gdmVyaWZ5IG15IGNvbmpl Y3R1cmUsIEkgYWRkZWQgYSBiaXQgb2YgY29kZSB0byBoZXJlLCB3aGljaCBpcyB0bwo+IHN1YnRy YWN0IHRoZSBjb3JyZXNwb25kaW5nIHZhbHVlIGZyb20gelYgaW4gdGhlIGxhc3QgbG9vcC4gQnV0 IGl0IGlzIGNvcnJlY3QKPiBvbmx5IHdoZW4gdGhlIG51bWJlciBvZiBieXRlcyBpcyBsZXNzIHRo YW4gb25lIGN5Y2xlLiBUZXN0IGNhc2VzIHRoYXQgZXhjZWVkCj4gb25lIGNvbXBsZXRlIGN5Y2xl IHdpbGwgYWxzbyBmYWlsLgo+IAo+IFNvIEkgZ3Vlc3MgYmVmb3JlIGNhbGN1bGF0aW5nIHRoZSBs YXN0IGN5Y2xlLCB6QSBzaG91bGQgYmUgc3VtbWVkIGZpcnN0LAo+IGJlY2F1c2UgYmVmb3JlIHRo ZSBlbmQgb2YgdGhlIGN5Y2xlLCB6QSBhbmQgekIgYXJlIHNjYXR0ZXJlZCBpbiB0aGUgZWxlbWVu dHMKPiBvZiB0aGUgdmVjdG9yLCBpZiB0aGUgbGFzdCBjeWNsZSBjYWxjdWxhdGVzIHpCLCBvbmx5 IHBhcnQgb2YgekEgaXMgc3VtbWVkCj4gKCBCZWNhdXNlIHBQL20gZG9lcyBub3QgY291bnQgaW5h Y3RpdmUgZWxlbWVudHMpLCBpdCBzaG91bGQgYmUgaW5jb21wbGV0ZS4KPiAKPiBUaGlzIGlzIGp1 c3QgbXkgZ3Vlc3MgYW5kIGhhcyBub3QgeWV0IGJlZW4gdmVyaWZpZWQuOikKCkkgdGhpbmsgekEg c2hvdWxkbid0IGJlIHdyb25nLCBzaW5jZSB0aGF0IG9ubHkgYWNjdW11bGF0ZXMgYWN0aXZlCmVs ZW1lbnRzIGFueXdheS4gIEkgdGhpbmsgaXQncyB0aGUgYm9ndXMgelYgbXVsdGlwbGllciBpbnZv bHZlZCBpbiB0aGUKdXBkYXRlIHRvIHpCIHRoYXQgaXMgdGhlIHByb2JsZW0uCgo+ID4gCj4gPiAJ YWRkICAgICB6QS5zLCBwUC9tLCB6QS5zLCB6WC5zICAgICAgICAvLyB6QS5zICs9IHpYLnMKPiA+ IAo+ID4gCW1zYiAgICAgelgucywgcFAvbSwgekoucywgekIucyAgICAgICAgLy8gelgucyA6PSB6 Qi5zIC0gelgucyAqIHpKLnMKPiA+IAo+ID4gCW1vdnByZnggekIsIHpBCj4gPiAJbWFkICAgICB6 Qi5zLCBwUC9tLCB6Vi5zLCB6WC5zICAgICAgICAvLyB6Qi5zIDo9IHpYLnMgKyB6QS5zICogVgo+ ID4gc3RhcnQ6ICAKPiA+IAl3aGlsZWxvIHBQLnMsIHhYLCB4TGltaXQKPiA+IAliLmZpcnN0IGxv b3AKClRoZXJlIGFyZSBhIGZldyBvcHRpb25zLCBJIGd1ZXNzLgoKT25lIHdheSBpcyB0byB1c2Ug Q05UUCBpbnNpZGUgdGhlIGxvb3AgdG8gZ2V0IHRoZSBudW1iZXIgb2YgYWN0aXZlCmVsZW1lbnRz LCBpbnN0ZWFkIG9mIGp1c3Qgc2V0dGluZyB6ViBpbiBhZHZhbmNlLiAgVGhpcyBhcHByb2FjaCBt YXkgYWRkCmEgc2xpZ2h0IG92ZXJoZWFkLCBidXQgaXQgaXMgd29ydGggZXhwZXJpbWVudGluZyB3 aXRoIGl0LiAgSWYgdGhlCm92ZXJoZWFkIGlzIG5lZ2xpYmlibGUsIHRoZW4gdGhpcyBhcHByb2Fj aCBoYXMgdGhlIGV4YW1wbGUgb2YgYmVpbmcKc2ltcGxlIHRvIHVuZGVyc3RhbmQuCgpBbHRlcm5h dGl2ZWx5LCB5b3UgY291bGQgZG8gYW4gYWRqdXN0bWVudCBhZnRlciB0aGUgbG9vcCBlbmRzIHRv CnN1YnRyYWN0IHRoZSBhcHByb3ByaWF0ZSBhbW91bnRzIGZyb20gekIuICBVbmZvcnR1bmF0ZWx5 LCBwUCBoYXMgYWxyZWFkeQpiZWVuIG92ZXJ3cml0dGVuIGJ5IHRoZSB0aW1lIHRoZSBsb29wIGVu ZHMuICBJZiBjb3VsZCBiZSBiYWNrZWQgdXAgaW50bwphbm90aGVyIFAtcmVnaXN0ZXIgYmVmb3Jl IG92ZXJ3cml0aW5nIGl0OiB0aGlzIHNob3VsZCBiZSBwcmV0dHkgbG93LQpvdmVyaGVhZC4KCkEg ZmluYWwgb3B0aW9uIHdvdWxkIGJlIHRvIGNoYW5nZSB0aGUgYi5maXJzdCB0byBhIGIubGFzdCwg c28gdGhhdCB0aGUKbG9vcCBlbmRzIGFmdGVyIHRoZSBmaW5hbCBmdWxsIGJsb2NrLiAgVGhlbiBj b3B5LXBhc3RlIHRoZSBsb29wIGJvZHkgdG8KZXhlY3V0ZSBvbmNlIG1vcmUsIGJ1dCB1c2luZyBD TlRQIHRvIGdldCB0aGUgZWxlbWVudCBjb3VudCB0byBtdWx0aXBseQpieSwgYXMgZGVzY3JpYmVk IGFib3ZlLiAgVGhpcyBtYWtlcyB0aGUgY29kZSBhIGJpdCBsYXJnZXIsIGJ1dCBpdApwcm9iYWJs eSB0aGUgYmVzdC1wZXJmb3JtYW5jZSBvcHRpb24uICBZb3UgbWF5IGJlIGFibGUgdG8gcm90YXRl IHRoZQpsb29wIHNvIHRoYXQgaXQgYnJlYWtzIG91dCBhZnRlciB1cGRhdGluZyB6QSAod2hpY2gg SUlVQyBkb2Vzbid0IG5lZWQgdG8KYmUgZG9uZSBpbiBhIHNwZWNpYWwgd2F5IGZvciB0aGUgcGFy dGlhbCBibG9jaykuICBUaGlzIHdvdWxkIG1lYW4geW91Cm9ubHkgaGF2ZSB0byBzcGVjaWFsaXNl IHRoZSB6QiB1cGRhdGUgY29kZS4KCj4gPiAKPiA+IC8vIENvbGxlY3QgdGhlIHBhcnRpYWwgc3Vt cyB0b2dldGhlcjoKPiA+IAo+ID4gCXVhZGR2ICAgZDAsIHBBLCB6MC5zCj4gPiAJdWFkZHYgICBk MSwgcEEsIHoxLnMKPiA+IAo+ID4gLy8gRmluYWxseSwgYWRkIDEgdG8gZDAsIGFuZCB4TGVuIHRv IGQxLCBhbmQgZG8gbW9kdWxvIHJlZHVjdGlvbi4KPiA+IAo+ID4gLS0+OC0tCj4gPiAKPiA+IFsu Li5dCj4gPiAKPiA+IENoZWVycwo+ID4gLS0tRGF2ZQo+ID4gLgo+ID4gCj4gCj4gVGhlIGNvZGUg eW91IHNlbnQgbWUgcHJvdmlkZXMgYSBjb3JyZWN0IHdheSB0byBkZWFsIHdpdGggdHJhaWxpbmcg Ynl0ZXMsCj4gSSBuZWVkIHRvIHNwZW5kIHNvbWUgbW9yZSB0aW1lIHRvIGRlYnVnIHRoZSBwcm9i bGVtIGVuY291bnRlcmVkIGFib3ZlLgo+IFRoYW5rIHlvdSEKPiAKPiBDaGVlcnMuXl9eCgpJIHdh cyBjaGVla2lseSBsZWF2aW5nIHRoZSB0ZXN0aW5nIGFuZCBkZWJ1Z2dpbmcgZm9yIHlvdSB0byBk byA6KQoKUGFydCBvZiBteSByZWFzb24gZm9yIGludGVyZXN0IGluIHRoaXMgaXMgdGhhdCBpZiB3 ZSBlbmFibGUgU1ZFIGluIHRoZQprZXJuZWwsIGl0IHdvdWxkIGJlIGdvb2QgdG8gaGF2ZSBzb21l IGNsZWFuIGV4YW1wbGVzIGZvciBwZW9wbGUgdG8KZm9sbG93IC0tIGV2ZW4gaWYgbm90IHRoaXMg YWxnby4gIFNWRSBpcyBhIGJpdCBkaWZmZXJlbnQgdG8gdXNlCmNvbXBhcmVkIHdpdGggZml4ZWQt bGVuZ3RoIHZlY3RvciBJU0FzLCBzbyB3b3JrZWQgZXhhbXBsZXMgYXJlIGFsd2F5cwp1c2VmdWwu CgpDaGVlcnMKLS0tRGF2ZQoKX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f X19fX19fX18KbGludXgtYXJtLWtlcm5lbCBtYWlsaW5nIGxpc3QKbGludXgtYXJtLWtlcm5lbEBs aXN0cy5pbmZyYWRlYWQub3JnCmh0dHA6Ly9saXN0cy5pbmZyYWRlYWQub3JnL21haWxtYW4vbGlz dGluZm8vbGludXgtYXJtLWtlcm5lbAo=