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 B6B6FC4742C for ; Mon, 16 Nov 2020 15:56:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7D01D207BC for ; Mon, 16 Nov 2020 15:56:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730631AbgKPP4p (ORCPT ); Mon, 16 Nov 2020 10:56:45 -0500 Received: from foss.arm.com ([217.140.110.172]:42210 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731570AbgKPP4p (ORCPT ); Mon, 16 Nov 2020 10:56:45 -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 C590631B; Mon, 16 Nov 2020 07:56:43 -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 236663F70D; Mon, 16 Nov 2020 07:56:42 -0800 (PST) Date: Mon, 16 Nov 2020 15:56:38 +0000 From: Dave Martin To: Li Qiang Cc: alexandre.torgue@st.com, catalin.marinas@arm.com, gaoguijin@huawei.com, colordev.jiang@huawei.com, luchunhua@huawei.com, linux-crypto@vger.kernel.org, mcoquelin.stm32@gmail.com, liliang889@huawei.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: <20201116155636.GZ6882@arm.com> References: <20201103121506.1533-1-liqiang64@huawei.com> <20201105165301.GH6882@arm.com> <20201110104629.GJ6882@arm.com> <89a9bdcc-b96e-2f2d-6c52-ca44e0e3472c@huawei.com> <20201110160708.GL6882@arm.com> <484ad2c8-3905-fc98-237c-f7eb4045edbc@huawei.com> <20201112111745.GS6882@arm.com> <72514954-ea04-6aa3-73d8-bb0fc39b6de2@huawei.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <72514954-ea04-6aa3-73d8-bb0fc39b6de2@huawei.com> User-Agent: Mutt/1.5.23 (2014-03-12) Precedence: bulk List-ID: X-Mailing-List: linux-crypto@vger.kernel.org On Sat, Nov 14, 2020 at 03:31:56PM +0800, Li Qiang wrote: > > > 在 2020/11/12 19:17, Dave Martin 写道: > > On Thu, Nov 12, 2020 at 03:20:53PM +0800, Li Qiang wrote: > >> > >> > >> 在 2020/11/11 0:07, Dave Martin 写道: > >>>>>>> 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 > >>>> I found the bug I encountered earlier, that is, the calculation of zB here > >>>> needs to use pA with all elements activated. The reason is the same as my > >>>> previous guess, because all elements of zA should be involved when calculating zB. > >>>> Because the original calculation formula is like this. > >>>> > >>>> For example: > >>>> In the last loop: > >>>> left byte is: 3 | 4 | \0 | > >>>> zA.s is: 100 | 200 | 100 | 200 (sum = 600) > >>>> pP.s is: 1 | 1 | 0 | 0 (Only activate the first 2 channels) > >>>> > >>>> At this time, if the calculation of zB only takes the first 2 active elements, the data > >>>> is incomplete, because according to the description of the original algorithm, zB is always > >>>> based on the sum of all the accumulated bytes. > >>> Yes, you're quite right here: zX is partial: only the elements pP are > >>> valid; but all elements of zA and zB are always valid. I was focusing > >>> too much on the handling of the partial input block. > >>> > >>>> Here we can simply change the prediction register used in the two sentences related to > >>>> zB to the one that is all true (it is pA in our code), like this: > >>>> msb zX.s, pA/m, zJ.s, zB.s // zX.s := zB.s - zX.s * zJ.s > >>> Are you sure about this? In a final partial block, the trailing > >>> elements of zX.s beyond pP will be leftover junk from the last > >>> iteration. > >> > >> Yes, I have verified this code and it is correct. The reason is that if pP is used here, > >> the inactive elements of zB will be ignored in zX, which will cause data loss.(I think it is > > > > Yes, you're quite right. I was forgetting about the /z (zeroing) > > semantics for the ld1b. This means that we get away with not > > inactivating those elements in the msb instruction, since zeros > > multiplied by the elements of zJ remain zero. > > > >> because the zB data is covered by the multiplication and addition results of zX, zA, and zV > >> using movprfx and mad. Have I got that right?) :) > > > > Yes, I think so. > > > >> On the other hand zX uses the prediction register pP/z when loading data, the value of the > >> inactive element is 0, the inactive element in zX will not affect the final result, the inactive > >> element in zB will be directly assigned to the inactive element of zX element. > >> > >> Then in the next instruction, it will be added to zB along with zX. > > > > Yes. > > > > This might be part of the reason why the architects decided that SVE > > loads zero the inactive elements instead of leaving them unchanged. > > I think so, it is a useful feature in this scenario. > > > > >> > >>> > >>> This might require a bit more thought in order to get the final block > >>> handling correct. > >>> > >>>> trailloop: // Last cycle entrance > >>>> cntp x6, p1, p0.s // Get active element count of last cycle > >>>> cpy zV.s, p1/m, w6 // Set zV to the actual value. > >>> Note that you can also write "mov" here, but I'm not sure which alias is > >>> preferred> > >>>> loop: // Core loop entrance > >>>> ld1b zX.s, p0/z, [x1] > >>>> incw x1 > >>>> > >>>> add zA.s, p0/m, zA.s, zX.s // The calculation of zA still needs to use p0 > >>>> msb zX.s, p1/m, zJ.s, zB.s // Change p register here > >>>> movprfx zB, zA > >>>> mad zB.s, p1/m, zV.s, zX.s // Change p register here > >>> As discussed above, are you sure this is correct now? > > > > I think we've agreed that it is correct. > > > > Thinking about the code flow, I think the initialisation of zV is > > slightly wrong: for very short data, the first block may be shorter than > > VL. > > > > I think this is easily solved by getting rid of the constant > > initialisation for zV and removing the branch that skips the CNTP code > > when entering the loop for the first time. That way, zV gets > > initialised to the correct thing on entering the loop, irrespective of > > whether we have a whole vector on the first iteration. > > The problem you are worried about is valuable. This problem must be considered when > the loop is unrolled. However, the code in my email a few days ago did not have this > problem, and I have also tested use cases with a length less than VL. :) > > The reason is that when the length of the test case is less than VL, 'b.last' is > false and 'b.first' is true, and then it will still jump directly to trailloop to update zV. > > b start > trailloop: > cntp x6, p1, p0.s > mov zV.s, p1/m, w6 > loop: > ... > start: > whilelo p0.s, x1, xLimit > b.last loop > b.first trailloop Yes, that makes sense. I rotated the loop so that it didn't need a separate entry point, but the logic is similar otherwise. > > > > Note, to squeeze maximum performance out of this, you still probably > > want to unroll the loop a few times so that you can schedule useful work > > in between each load and the computations that depend on it. > > Yes, I tried to expand the loop on the basis of the previous code, but the effect was > not very satisfactory(the performance is only about 2 times that of the C version), > so I reconsidered the way of implementation, based on the formula you derived earlier. > > > > >>>> start: > >>>> whilelo p0.s, x1, xLimit > >>>> b.last loop // The condition for the core loop to continue is that b.last is true > >>>> b.first trailloop // If b.last is false and b.first is true, it means the last cycle > >>>> > >>>> uaddv d0, p1, zA.s > >>>> uaddv d1, p1, zB.s > >>>> > >>>> mov x12, v0.2d[0] > >>>> mov x13, v1.2d[0] > >>> The "2" here seems not to be required by the syntax, although it's > >>> harmless. > >> > >> Yes I deleted it. > >> > >>> > >>>> add x10, x10, x12 > >>>> add x11, x11, x13 > >>>> add x11, x11, x2 > >>> If x10 and x11 are used as accmulators by the caller, I guess this works. > >> > >> X10 and X11 are part A and part B of the initial value of adler32 passed in by the caller. > > > > Right, that makes sense. > > > >> > >>> > >>>> mod65521 10, 14, 12 > >>>> mod65521 11, 14, 12 > > > > Note, can you replace these with udiv? > > > > While using mul might be slightly cheaper to achieve this, it makes the > > code more complex and will have a negligible impact on the overall > > cost... > > > > So, does something like this work: > > > > mov x3, #65521 > > udiv x4, x10, x3 > > udiv x5, x11, x3 > > msub x10, x4, x3, x10 > > msub x11, x5, x3, x11 > > Yes, the reason for doing this here is that I initially performed modulo division > in the loop body, so that we can never overflow the data, so I considered optimizing > the division to multiplication.If we only do a modular division at the end, we don't > need to implement the code like this. > > > > >>>> lsl x11, x11, #16 > >>>> orr x0, x10, x11 > >>>> ret > >>>> -->8-- > >>>> > >>>> After this modification, The test results are correct when the data length is less than about 8 Kbyte, > >>>> part A will still be correct after 8K, and an overflow error will occur in part B. This is because A > >>>> only accumulates all the bytes, and the accumulative acceleration of B expands faster, because the > >>>> accumulative formula of B is: > >>>> B = (1 + D1) + (1 + D1 + D2) + ... + (1 + D1 + D2 + ... + Dn) (mod 65521) > >>>> = n×D1 + (n−1)×D2 + (n−2)×D3 + ... + Dn + n (mod 65521) > >>>> > >>>> If we take the average value of Dx to 128 and n to 8192: > >>>> B = (1 + 2 + ... + 8129) * 128 + 8192 > >>>> = 4,295,499,776 (32bit overflow) > >>>> > >>>> So I think the 32-bit accumulator is still not enough for part B here. :) > >>>> > >>>> -- > >>>> Best regards, > >>>> Li Qiang > >>> That makes sense. I hadn't tried to calculate the actual bound. > >>> > >>> It may be worth trying this with 64-bit accumulators. This will > >>> probably slow things down, but it depends on the relative computation / > >>> memory throughput exhibited by the hardware. > >> > >> If a 64-bit wide vector register is used, for most scenes where the amount of data is not particularly large, > >> is it wasted more vector resources? > > > > Yes :) > > > > Depending on the algorithm, this might be a better tradeoff if it meant > > that the data could be processed in larger chunks. I suspect that the > > tradeoff is unfavourable in this particluar case though -- but I haven't > > tried it. > > > >> Maybe we can also try to use 16-bit wide vector registers to load data and calculations, > >> and accumulate them into the scalar register xn before overflow, just like my original patch, > >> but I can try to use ascending order to change the processing of the last loop Be more elegant. > > > > Perhaps. But I assumed that 16-bit elements would overflow much too > > fast to be practical. Just multiplying zX by zJ once can produce > > element values up to 0xfe01 if VL is 256 bytes. > > > > Did you have some idea for how to make this work? > > > > It may be possible to do 16-bit multiplies with 32-bit accumulation. > > SVE2 has some NEON-style mixed-width multiply-accumulate instructions > > that can achieve this sort of thing directly, but in SVE(1) I think > > you would have to break the multiply-accumulates up. Say: > > > > mul zX.h, pP/m, zX.h, zJ.h > > sub zX.s, pP/m, zB.s, zX.s > > > > whilelo pP.s should generate a predicate with odd .h elements inactive, > > and ld1b zX.s, pP/z, ... will make sure those elements are all zeroed in > > xX.h. > > > > I'm not sure this would be faster than a single 32-bit msb, since > > multiply-accumulates are likely to be heavily optimised in the hardware, > > but you could try it. > > I adopted a part of the patch I originally submitted, combined with the formula you gave > to calculate B[n+v] using an increasing sequence, and then modify the code again. The code is at the end. > > > > >>> > >>> I think the code can't be bulletproof without breaking the input into > >>> chunks anyway, though. > >> > >> I don't quite understand what it means here. Does it mean that the input bytes are read into the vector in > >> blocks for calculation (this is how it is done now) or the intermediate results are stored in different elements > >> of the vector in blocks during the calculation process? :-) > > > > I mean, you limit the number of iterations of the core loop so that > > overflow doesn't happen. You're already doing this; I just wanted to > > make the point that 64-bit accumulators probably don't solve this > > problem, even though it will take a very large number of iterations to > > cause an overflow. > > > > Unless Adler32 specifies the maximum length of the input data not to > > exceed some value, it could go on forever -- so overflow becomes > > inevitable. > > Yes, I understand that if we do not perform the modulo division in time, data overflow will happen sooner or later. > So my initial patch added modulo division to the loop body and optimized it.:) > > As you said, if we don’t want to perform modular division in the loop body, then we need to block the input and > perform the modular division in time before the data will never overflow (Need to consider the worst case, > that is, all data is 0xff). > > --8<-- > ... > adler_A .req x10 > adler_B .req x11 > > .macro adler32_core > ld1b zX.h, p0/z, [x1] // load bytes > inch x1 > > uaddv d0, p0, zX.h > mul zX.h, p0/m, zX.h, zJ.h // Sum [j=0 .. v-1] j*X[j+n] > mov x9, v0.d[0] > uaddv d1, p0, zX.h > add adler_A, adler_A, x9 // A[n+v] = An + Sum [j=0 ... v-1] X[j] > mov x9, v1.d[0] > madd adler_B, x7, adler_A, adler_B // Bn + v*A[n+v] > sub adler_B, adler_B, x9 // B[n+v] = Bn + v*A[n+v] - Sum [j=0 .. v-1] j*X[j+n] > .endm If this has best performance, I find that quite surprising. Those uaddv instructions will stop the vector lanes flowing independently inside the loop, so if an individual element load is slow arriving then everything will have to wait. A decent hardware prefetcher may tend to hide that issue for sequential memory access, though: i.e., if the hardware does a decent job of fetching data before the actual loads are issued, the data may appear to arrive with minimal delay. The effect might be a lot worse for algorithms that have less predictable memory access patterns. Possibly you do win some additional performance due to processing twice as many elements at once, here. > > .macro adler32_loop > whilelo p0.h, x1, xLimit > cntp x7, p0, p0.h // x7 is used to store 'v' > adler32_core > .endm > > ENTRY(XXX) > ... > add xLimit, x1, x2 > > loop: > adler32_loop > adler32_loop > adler32_loop > adler32_loop > cmp x7, #0 > b.ne loop > > mov x3, #65521 > udiv x4, x10, x3 > udiv x5, x11, x3 > msub x10, x4, x3, x10 > msub x11, x5, x3, x11 > ... > -->8-- > > > I tested 500Mbyte random data, the result is completely correct, longer data volume did not test, > I think we can also wrap a layer of data block control code, that is, after the data volume > exceeds a certain threshold, the control code The input data is segmented by threshold, and > the initial adler32 of the subsequent segment is the result of the previous segment calculation. > > Like: > adler32 = 1; > if (len <= MAX_BLOCK_LEN) > adler32 = adler32_sve(adler32, buf, len); > else { > int i = 0; > while (i < len) { > int block_len = (len - i > MAX_BLOCK_LEN) ? MAX_BLOCK_LEN : (len - i); > adler32 = adler32_sve(adler32, &buf[i], block_len); > i += block_len; > } > } > return adler32; > > In this way, we don't have to worry about when the data overflows and when to modulo in the core algorithm code. Yes, something like that ought to work. 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 0FB54C4742C for ; Mon, 16 Nov 2020 15:58: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 88F4320A8B for ; Mon, 16 Nov 2020 15:58: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="L2uvUUZh" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 88F4320A8B 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=Xye3ZS+TDApxCvoKmRx5zzE4UcC9yoUHMAm1Ndp4ajc=; b=L2uvUUZhqH8IxxG5RyIZAMAB/ xrVpRj/s5FBJ/o2qy9MmxZhrrO6t4bL71NgYM4IYGKxHf/b9HG5/KlL6HCTuvB+F/sj4Uh1PwL7/E oIE5p5Ye5S4mtaSY16AP1OzQ8ytFpd8v8tyK2RIcrEvO9sJuAJXXQI7/fNdaZK6o+pyRnyM37NOmS SAoFN8G8fVfPuapSQvSAn9/Mk1SH3Tm5rzj5d8Y7JqR+yiP+L+Pvo1+5POIvEhp1PNJqEZs7sKNSh cdUJMMjNvdwHxDk1lob5k5W2zvQ1TYYBkL1pcyUpBsJh2GpS7IGcuFQ9dqQ471imH6PsFk7s7HrDC 1K9yQ05rA==; Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1kegs4-0003Kv-VJ; Mon, 16 Nov 2020 15:56:49 +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 1kegs1-0003Jk-8P for linux-arm-kernel@lists.infradead.org; Mon, 16 Nov 2020 15:56:47 +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 C590631B; Mon, 16 Nov 2020 07:56:43 -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 236663F70D; Mon, 16 Nov 2020 07:56:42 -0800 (PST) Date: Mon, 16 Nov 2020 15:56:38 +0000 From: Dave Martin To: Li Qiang Subject: Re: [PATCH 0/1] arm64: Accelerate Adler32 using arm64 SVE instructions. Message-ID: <20201116155636.GZ6882@arm.com> References: <20201103121506.1533-1-liqiang64@huawei.com> <20201105165301.GH6882@arm.com> <20201110104629.GJ6882@arm.com> <89a9bdcc-b96e-2f2d-6c52-ca44e0e3472c@huawei.com> <20201110160708.GL6882@arm.com> <484ad2c8-3905-fc98-237c-f7eb4045edbc@huawei.com> <20201112111745.GS6882@arm.com> <72514954-ea04-6aa3-73d8-bb0fc39b6de2@huawei.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <72514954-ea04-6aa3-73d8-bb0fc39b6de2@huawei.com> 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-20201116_105645_441990_E49E0B9F X-CRM114-Status: GOOD ( 66.87 ) 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, gaoguijin@huawei.com, colordev.jiang@huawei.com, luchunhua@huawei.com, linux-crypto@vger.kernel.org, mcoquelin.stm32@gmail.com, liliang889@huawei.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 T24gU2F0LCBOb3YgMTQsIDIwMjAgYXQgMDM6MzE6NTZQTSArMDgwMCwgTGkgUWlhbmcgd3JvdGU6 Cj4gCj4gCj4g5ZyoIDIwMjAvMTEvMTIgMTk6MTcsIERhdmUgTWFydGluIOWGmemBkzoKPiA+IE9u IFRodSwgTm92IDEyLCAyMDIwIGF0IDAzOjIwOjUzUE0gKzA4MDAsIExpIFFpYW5nIHdyb3RlOgo+ ID4+Cj4gPj4KPiA+PiDlnKggMjAyMC8xMS8xMSAwOjA3LCBEYXZlIE1hcnRpbiDlhpnpgZM6Cj4g Pj4+Pj4+PiAJYWRkICAgICB6QS5zLCBwUC9tLCB6QS5zLCB6WC5zICAgICAgICAvLyB6QS5zICs9 IHpYLnMKPiA+Pj4+Pj4+Cj4gPj4+Pj4+PiAJbXNiICAgICB6WC5zLCBwUC9tLCB6Si5zLCB6Qi5z ICAgICAgICAvLyB6WC5zIDo9IHpCLnMgLSB6WC5zICogekoucwo+ID4+Pj4+Pj4KPiA+Pj4+Pj4+ IAltb3ZwcmZ4IHpCLCB6QQo+ID4+Pj4+Pj4gCW1hZCAgICAgekIucywgcFAvbSwgelYucywgelgu cyAgICAgICAgLy8gekIucyA6PSB6WC5zICsgekEucyAqIFYKPiA+Pj4+IEkgZm91bmQgdGhlIGJ1 ZyBJIGVuY291bnRlcmVkIGVhcmxpZXIsIHRoYXQgaXMsIHRoZSBjYWxjdWxhdGlvbiBvZiB6QiBo ZXJlCj4gPj4+PiBuZWVkcyB0byB1c2UgcEEgd2l0aCBhbGwgZWxlbWVudHMgYWN0aXZhdGVkLiBU aGUgcmVhc29uIGlzIHRoZSBzYW1lIGFzIG15Cj4gPj4+PiBwcmV2aW91cyBndWVzcywgYmVjYXVz ZSBhbGwgZWxlbWVudHMgb2YgekEgc2hvdWxkIGJlIGludm9sdmVkIHdoZW4gY2FsY3VsYXRpbmcg ekIuCj4gPj4+PiBCZWNhdXNlIHRoZSBvcmlnaW5hbCBjYWxjdWxhdGlvbiBmb3JtdWxhIGlzIGxp a2UgdGhpcy4KPiA+Pj4+Cj4gPj4+PiBGb3IgZXhhbXBsZToKPiA+Pj4+IEluIHRoZSBsYXN0IGxv b3A6Cj4gPj4+PiAJbGVmdCBieXRlIGlzOgkgIDMgfCAgIDQgfCAgXDAgfAo+ID4+Pj4gCXpBLnMg aXM6CTEwMCB8IDIwMCB8IDEwMCB8IDIwMCAoc3VtID0gNjAwKQo+ID4+Pj4gCXBQLnMgaXM6CSAg MSB8ICAgMSB8ICAgMCB8ICAgMCAoT25seSBhY3RpdmF0ZSB0aGUgZmlyc3QgMiBjaGFubmVscykK PiA+Pj4+Cj4gPj4+PiBBdCB0aGlzIHRpbWUsIGlmIHRoZSBjYWxjdWxhdGlvbiBvZiB6QiBvbmx5 IHRha2VzIHRoZSBmaXJzdCAyIGFjdGl2ZSBlbGVtZW50cywgdGhlIGRhdGEKPiA+Pj4+IGlzIGlu Y29tcGxldGUsIGJlY2F1c2UgYWNjb3JkaW5nIHRvIHRoZSBkZXNjcmlwdGlvbiBvZiB0aGUgb3Jp Z2luYWwgYWxnb3JpdGhtLCB6QiBpcyBhbHdheXMKPiA+Pj4+IGJhc2VkIG9uIHRoZSBzdW0gb2Yg YWxsIHRoZSBhY2N1bXVsYXRlZCBieXRlcy4KPiA+Pj4gWWVzLCB5b3UncmUgcXVpdGUgcmlnaHQg aGVyZTogelggaXMgcGFydGlhbDogb25seSB0aGUgZWxlbWVudHMgcFAgYXJlCj4gPj4+IHZhbGlk OyBidXQgYWxsIGVsZW1lbnRzIG9mIHpBIGFuZCB6QiBhcmUgYWx3YXlzIHZhbGlkLiAgSSB3YXMg Zm9jdXNpbmcKPiA+Pj4gdG9vIG11Y2ggb24gdGhlIGhhbmRsaW5nIG9mIHRoZSBwYXJ0aWFsIGlu cHV0IGJsb2NrLgo+ID4+Pgo+ID4+Pj4gSGVyZSB3ZSBjYW4gc2ltcGx5IGNoYW5nZSB0aGUgcHJl ZGljdGlvbiByZWdpc3RlciB1c2VkIGluIHRoZSB0d28gc2VudGVuY2VzIHJlbGF0ZWQgdG8KPiA+ Pj4+IHpCIHRvIHRoZSBvbmUgdGhhdCBpcyBhbGwgdHJ1ZSAoaXQgaXMgcEEgaW4gb3VyIGNvZGUp LCBsaWtlIHRoaXM6Cj4gPj4+PiAJbXNiICAgICB6WC5zLCBwQS9tLCB6Si5zLCB6Qi5zICAgICAg ICAvLyB6WC5zIDo9IHpCLnMgLSB6WC5zICogekoucwo+ID4+PiBBcmUgeW91IHN1cmUgYWJvdXQg dGhpcz8gIEluIGEgZmluYWwgcGFydGlhbCBibG9jaywgdGhlIHRyYWlsaW5nCj4gPj4+IGVsZW1l bnRzIG9mIHpYLnMgYmV5b25kIHBQIHdpbGwgYmUgbGVmdG92ZXIganVuayBmcm9tIHRoZSBsYXN0 Cj4gPj4+IGl0ZXJhdGlvbi4KPiA+Pgo+ID4+IFllcywgSSBoYXZlIHZlcmlmaWVkIHRoaXMgY29k ZSBhbmQgaXQgaXMgY29ycmVjdC4gVGhlIHJlYXNvbiBpcyB0aGF0IGlmIHBQIGlzIHVzZWQgaGVy ZSwKPiA+PiB0aGUgaW5hY3RpdmUgZWxlbWVudHMgb2YgekIgd2lsbCBiZSBpZ25vcmVkIGluIHpY LCB3aGljaCB3aWxsIGNhdXNlIGRhdGEgbG9zcy4oSSB0aGluayBpdCBpcwo+ID4gCj4gPiBZZXMs IHlvdSdyZSBxdWl0ZSByaWdodC4gIEkgd2FzIGZvcmdldHRpbmcgYWJvdXQgdGhlIC96ICh6ZXJv aW5nKQo+ID4gc2VtYW50aWNzIGZvciB0aGUgbGQxYi4gIFRoaXMgbWVhbnMgdGhhdCB3ZSBnZXQg YXdheSB3aXRoIG5vdAo+ID4gaW5hY3RpdmF0aW5nIHRob3NlIGVsZW1lbnRzIGluIHRoZSBtc2Ig aW5zdHJ1Y3Rpb24sIHNpbmNlIHplcm9zCj4gPiBtdWx0aXBsaWVkIGJ5IHRoZSBlbGVtZW50cyBv ZiB6SiByZW1haW4gemVyby4KPiA+IAo+ID4+IGJlY2F1c2UgdGhlIHpCIGRhdGEgaXMgY292ZXJl ZCBieSB0aGUgbXVsdGlwbGljYXRpb24gYW5kIGFkZGl0aW9uIHJlc3VsdHMgb2YgelgsIHpBLCBh bmQgelYKPiA+PiB1c2luZyBtb3ZwcmZ4IGFuZCBtYWQuIEhhdmUgSSBnb3QgdGhhdCByaWdodD8p IDopCj4gPiAKPiA+IFllcywgSSB0aGluayBzby4KPiA+IAo+ID4+IE9uIHRoZSBvdGhlciBoYW5k IHpYIHVzZXMgdGhlIHByZWRpY3Rpb24gcmVnaXN0ZXIgcFAveiB3aGVuIGxvYWRpbmcgZGF0YSwg dGhlIHZhbHVlIG9mIHRoZQo+ID4+IGluYWN0aXZlIGVsZW1lbnQgaXMgMCwgdGhlIGluYWN0aXZl IGVsZW1lbnQgaW4gelggd2lsbCBub3QgYWZmZWN0IHRoZSBmaW5hbCByZXN1bHQsIHRoZSBpbmFj dGl2ZQo+ID4+IGVsZW1lbnQgaW4gekIgd2lsbCBiZSBkaXJlY3RseSBhc3NpZ25lZCB0byB0aGUg aW5hY3RpdmUgZWxlbWVudCBvZiB6WCBlbGVtZW50Lgo+ID4+Cj4gPj4gVGhlbiBpbiB0aGUgbmV4 dCBpbnN0cnVjdGlvbiwgaXQgd2lsbCBiZSBhZGRlZCB0byB6QiBhbG9uZyB3aXRoIHpYLgo+ID4g Cj4gPiBZZXMuCj4gPiAKPiA+IFRoaXMgbWlnaHQgYmUgcGFydCBvZiB0aGUgcmVhc29uIHdoeSB0 aGUgYXJjaGl0ZWN0cyBkZWNpZGVkIHRoYXQgU1ZFCj4gPiBsb2FkcyB6ZXJvIHRoZSBpbmFjdGl2 ZSBlbGVtZW50cyBpbnN0ZWFkIG9mIGxlYXZpbmcgdGhlbSB1bmNoYW5nZWQuCj4gCj4gSSB0aGlu ayBzbywgaXQgaXMgYSB1c2VmdWwgZmVhdHVyZSBpbiB0aGlzIHNjZW5hcmlvLgo+IAo+ID4gCj4g Pj4KPiA+Pj4KPiA+Pj4gVGhpcyBtaWdodCByZXF1aXJlIGEgYml0IG1vcmUgdGhvdWdodCBpbiBv cmRlciB0byBnZXQgdGhlIGZpbmFsIGJsb2NrCj4gPj4+IGhhbmRsaW5nIGNvcnJlY3QuCj4gPj4+ Cj4gPj4+PiB0cmFpbGxvb3A6CQkJLy8gTGFzdCBjeWNsZSBlbnRyYW5jZQo+ID4+Pj4gICAgICAg ICBjbnRwICAgIHg2LCBwMSwgcDAucwkvLyBHZXQgYWN0aXZlIGVsZW1lbnQgY291bnQgb2YgbGFz dCBjeWNsZQo+ID4+Pj4gICAgICAgICBjcHkgICAgIHpWLnMsIHAxL20sIHc2CS8vIFNldCB6ViB0 byB0aGUgYWN0dWFsIHZhbHVlLgo+ID4+PiBOb3RlIHRoYXQgeW91IGNhbiBhbHNvIHdyaXRlICJt b3YiIGhlcmUsIGJ1dCBJJ20gbm90IHN1cmUgd2hpY2ggYWxpYXMgaXMKPiA+Pj4gcHJlZmVycmVk Pgo+ID4+Pj4gbG9vcDoJCQkJLy8gQ29yZSBsb29wIGVudHJhbmNlCj4gPj4+PiAgICAgICAgIGxk MWIgICAgelgucywgcDAveiwgW3gxXQo+ID4+Pj4gICAgICAgICBpbmN3ICAgIHgxCj4gPj4+Pgo+ ID4+Pj4gICAgICAgICBhZGQgICAgIHpBLnMsIHAwL20sIHpBLnMsIHpYLnMJLy8gVGhlIGNhbGN1 bGF0aW9uIG9mIHpBIHN0aWxsIG5lZWRzIHRvIHVzZSBwMAo+ID4+Pj4gICAgICAgICBtc2IgICAg IHpYLnMsIHAxL20sIHpKLnMsIHpCLnMJLy8gQ2hhbmdlIHAgcmVnaXN0ZXIgaGVyZQo+ID4+Pj4g ICAgICAgICBtb3ZwcmZ4IHpCLCB6QQo+ID4+Pj4gICAgICAgICBtYWQgICAgIHpCLnMsIHAxL20s IHpWLnMsIHpYLnMJLy8gQ2hhbmdlIHAgcmVnaXN0ZXIgaGVyZQo+ID4+PiBBcyBkaXNjdXNzZWQg YWJvdmUsIGFyZSB5b3Ugc3VyZSB0aGlzIGlzIGNvcnJlY3Qgbm93Pwo+ID4gCj4gPiBJIHRoaW5r IHdlJ3ZlIGFncmVlZCB0aGF0IGl0IGlzIGNvcnJlY3QuCj4gPiAKPiA+IFRoaW5raW5nIGFib3V0 IHRoZSBjb2RlIGZsb3csIEkgdGhpbmsgdGhlIGluaXRpYWxpc2F0aW9uIG9mIHpWIGlzCj4gPiBz bGlnaHRseSB3cm9uZzogZm9yIHZlcnkgc2hvcnQgZGF0YSwgdGhlIGZpcnN0IGJsb2NrIG1heSBi ZSBzaG9ydGVyIHRoYW4KPiA+IFZMLgo+ID4gCj4gPiBJIHRoaW5rIHRoaXMgaXMgZWFzaWx5IHNv bHZlZCBieSBnZXR0aW5nIHJpZCBvZiB0aGUgY29uc3RhbnQKPiA+IGluaXRpYWxpc2F0aW9uIGZv ciB6ViBhbmQgcmVtb3ZpbmcgdGhlIGJyYW5jaCB0aGF0IHNraXBzIHRoZSBDTlRQIGNvZGUKPiA+ IHdoZW4gZW50ZXJpbmcgdGhlIGxvb3AgZm9yIHRoZSBmaXJzdCB0aW1lLiAgVGhhdCB3YXksIHpW IGdldHMKPiA+IGluaXRpYWxpc2VkIHRvIHRoZSBjb3JyZWN0IHRoaW5nIG9uIGVudGVyaW5nIHRo ZSBsb29wLCBpcnJlc3BlY3RpdmUgb2YKPiA+IHdoZXRoZXIgd2UgaGF2ZSBhIHdob2xlIHZlY3Rv ciBvbiB0aGUgZmlyc3QgaXRlcmF0aW9uLgo+IAo+IFRoZSBwcm9ibGVtIHlvdSBhcmUgd29ycmll ZCBhYm91dCBpcyB2YWx1YWJsZS4gVGhpcyBwcm9ibGVtIG11c3QgYmUgY29uc2lkZXJlZCB3aGVu Cj4gdGhlIGxvb3AgaXMgdW5yb2xsZWQuIEhvd2V2ZXIsIHRoZSBjb2RlIGluIG15IGVtYWlsIGEg ZmV3IGRheXMgYWdvIGRpZCBub3QgaGF2ZSB0aGlzCj4gcHJvYmxlbSwgYW5kIEkgaGF2ZSBhbHNv IHRlc3RlZCB1c2UgY2FzZXMgd2l0aCBhIGxlbmd0aCBsZXNzIHRoYW4gVkwuIDopCj4gCj4gVGhl IHJlYXNvbiBpcyB0aGF0IHdoZW4gdGhlIGxlbmd0aCBvZiB0aGUgdGVzdCBjYXNlIGlzIGxlc3Mg dGhhbiBWTCwgJ2IubGFzdCcgaXMKPiBmYWxzZSBhbmQgJ2IuZmlyc3QnIGlzIHRydWUsIGFuZCB0 aGVuIGl0IHdpbGwgc3RpbGwganVtcCBkaXJlY3RseSB0byB0cmFpbGxvb3AgdG8gdXBkYXRlIHpW Lgo+IAo+IAliCXN0YXJ0Cj4gdHJhaWxsb29wOgo+IAljbnRwCXg2LCBwMSwgcDAucwo+IAltb3YJ elYucywgcDEvbSwgdzYKPiBsb29wOgo+IAkuLi4KPiBzdGFydDoKPiAJd2hpbGVsbwlwMC5zLCB4 MSwgeExpbWl0Cj4gCWIubGFzdAlsb29wCj4gCWIuZmlyc3QJdHJhaWxsb29wCgpZZXMsIHRoYXQg bWFrZXMgc2Vuc2UuICBJIHJvdGF0ZWQgdGhlIGxvb3Agc28gdGhhdCBpdCBkaWRuJ3QgbmVlZCBh CnNlcGFyYXRlIGVudHJ5IHBvaW50LCBidXQgdGhlIGxvZ2ljIGlzIHNpbWlsYXIgb3RoZXJ3aXNl LgoKPiA+IAo+ID4gTm90ZSwgdG8gc3F1ZWV6ZSBtYXhpbXVtIHBlcmZvcm1hbmNlIG91dCBvZiB0 aGlzLCB5b3Ugc3RpbGwgcHJvYmFibHkKPiA+IHdhbnQgdG8gdW5yb2xsIHRoZSBsb29wIGEgZmV3 IHRpbWVzIHNvIHRoYXQgeW91IGNhbiBzY2hlZHVsZSB1c2VmdWwgd29yawo+ID4gaW4gYmV0d2Vl biBlYWNoIGxvYWQgYW5kIHRoZSBjb21wdXRhdGlvbnMgdGhhdCBkZXBlbmQgb24gaXQuCj4gCj4g WWVzLCBJIHRyaWVkIHRvIGV4cGFuZCB0aGUgbG9vcCBvbiB0aGUgYmFzaXMgb2YgdGhlIHByZXZp b3VzIGNvZGUsIGJ1dCB0aGUgZWZmZWN0IHdhcwo+IG5vdCB2ZXJ5IHNhdGlzZmFjdG9yeSh0aGUg cGVyZm9ybWFuY2UgaXMgb25seSBhYm91dCAyIHRpbWVzIHRoYXQgb2YgdGhlIEMgdmVyc2lvbiks Cj4gc28gSSByZWNvbnNpZGVyZWQgdGhlIHdheSBvZiBpbXBsZW1lbnRhdGlvbiwgYmFzZWQgb24g dGhlIGZvcm11bGEgeW91IGRlcml2ZWQgZWFybGllci4KPiAKPiA+IAo+ID4+Pj4gc3RhcnQ6Cj4g Pj4+PiAgICAgICAgIHdoaWxlbG8gcDAucywgeDEsIHhMaW1pdAo+ID4+Pj4gICAgICAgICBiLmxh c3QgIGxvb3AJCS8vIFRoZSBjb25kaXRpb24gZm9yIHRoZSBjb3JlIGxvb3AgdG8gY29udGludWUg aXMgdGhhdCBiLmxhc3QgaXMgdHJ1ZQo+ID4+Pj4gICAgICAgICBiLmZpcnN0IHRyYWlsbG9vcAkv LyBJZiBiLmxhc3QgaXMgZmFsc2UgYW5kIGIuZmlyc3QgaXMgdHJ1ZSwgaXQgbWVhbnMgdGhlIGxh c3QgY3ljbGUKPiA+Pj4+Cj4gPj4+PiAgICAgICAgIHVhZGR2ICAgZDAsIHAxLCB6QS5zCj4gPj4+ PiAgICAgICAgIHVhZGR2ICAgZDEsIHAxLCB6Qi5zCj4gPj4+Pgo+ID4+Pj4gICAgICAgICBtb3Yg ICAgIHgxMiwgdjAuMmRbMF0KPiA+Pj4+ICAgICAgICAgbW92ICAgICB4MTMsIHYxLjJkWzBdCj4g Pj4+IFRoZSAiMiIgaGVyZSBzZWVtcyBub3QgdG8gYmUgcmVxdWlyZWQgYnkgdGhlIHN5bnRheCwg YWx0aG91Z2ggaXQncwo+ID4+PiBoYXJtbGVzcy4KPiA+Pgo+ID4+IFllcyBJIGRlbGV0ZWQgaXQu Cj4gPj4KPiA+Pj4KPiA+Pj4+ICAgICAgICAgYWRkICAgICB4MTAsIHgxMCwgeDEyCj4gPj4+PiAg ICAgICAgIGFkZCAgICAgeDExLCB4MTEsIHgxMwo+ID4+Pj4gICAgICAgICBhZGQgICAgIHgxMSwg eDExLCB4Mgo+ID4+PiBJZiB4MTAgYW5kIHgxMSBhcmUgdXNlZCBhcyBhY2NtdWxhdG9ycyBieSB0 aGUgY2FsbGVyLCBJIGd1ZXNzIHRoaXMgd29ya3MuCj4gPj4KPiA+PiBYMTAgYW5kIFgxMSBhcmUg cGFydCBBIGFuZCBwYXJ0IEIgb2YgdGhlIGluaXRpYWwgdmFsdWUgb2YgYWRsZXIzMiBwYXNzZWQg aW4gYnkgdGhlIGNhbGxlci4KPiA+IAo+ID4gUmlnaHQsIHRoYXQgbWFrZXMgc2Vuc2UuCj4gPiAK PiA+Pgo+ID4+Pgo+ID4+Pj4gICAgICAgICBtb2Q2NTUyMSAgICAgICAgMTAsIDE0LCAxMgo+ID4+ Pj4gICAgICAgICBtb2Q2NTUyMSAgICAgICAgMTEsIDE0LCAxMgo+ID4gCj4gPiBOb3RlLCBjYW4g eW91IHJlcGxhY2UgdGhlc2Ugd2l0aCB1ZGl2Pwo+ID4gCj4gPiBXaGlsZSB1c2luZyBtdWwgbWln aHQgYmUgc2xpZ2h0bHkgY2hlYXBlciB0byBhY2hpZXZlIHRoaXMsIGl0IG1ha2VzIHRoZQo+ID4g Y29kZSBtb3JlIGNvbXBsZXggYW5kIHdpbGwgaGF2ZSBhIG5lZ2xpZ2libGUgaW1wYWN0IG9uIHRo ZSBvdmVyYWxsCj4gPiBjb3N0Li4uCj4gPiAKPiA+IFNvLCBkb2VzIHNvbWV0aGluZyBsaWtlIHRo aXMgd29yazoKPiA+IAo+ID4gCW1vdgl4MywgIzY1NTIxCj4gPiAJdWRpdgl4NCwgeDEwLCB4Mwo+ ID4gCXVkaXYJeDUsIHgxMSwgeDMKPiA+IAltc3ViCXgxMCwgeDQsIHgzLCB4MTAKPiA+IAltc3Vi CXgxMSwgeDUsIHgzLCB4MTEKPiAKPiBZZXMsIHRoZSByZWFzb24gZm9yIGRvaW5nIHRoaXMgaGVy ZSBpcyB0aGF0IEkgaW5pdGlhbGx5IHBlcmZvcm1lZCBtb2R1bG8gZGl2aXNpb24KPiBpbiB0aGUg bG9vcCBib2R5LCBzbyB0aGF0IHdlIGNhbiBuZXZlciBvdmVyZmxvdyB0aGUgZGF0YSwgc28gSSBj b25zaWRlcmVkIG9wdGltaXppbmcKPiB0aGUgZGl2aXNpb24gdG8gbXVsdGlwbGljYXRpb24uSWYg d2Ugb25seSBkbyBhIG1vZHVsYXIgZGl2aXNpb24gYXQgdGhlIGVuZCwgd2UgZG9uJ3QKPiBuZWVk IHRvIGltcGxlbWVudCB0aGUgY29kZSBsaWtlIHRoaXMuCj4gCj4gPiAKPiA+Pj4+ICAgICAgICAg bHNsICAgICB4MTEsIHgxMSwgIzE2Cj4gPj4+PiAgICAgICAgIG9yciAgICAgeDAsIHgxMCwgeDEx Cj4gPj4+PiAgICAgICAgIHJldAo+ID4+Pj4gLS0+OC0tCj4gPj4+Pgo+ID4+Pj4gQWZ0ZXIgdGhp cyBtb2RpZmljYXRpb24sIFRoZSB0ZXN0IHJlc3VsdHMgYXJlIGNvcnJlY3Qgd2hlbiB0aGUgZGF0 YSBsZW5ndGggaXMgbGVzcyB0aGFuIGFib3V0IDggS2J5dGUsCj4gPj4+PiBwYXJ0IEEgd2lsbCBz dGlsbCBiZSBjb3JyZWN0IGFmdGVyIDhLLCBhbmQgYW4gb3ZlcmZsb3cgZXJyb3Igd2lsbCBvY2N1 ciBpbiBwYXJ0IEIuIFRoaXMgaXMgYmVjYXVzZSBBCj4gPj4+PiBvbmx5IGFjY3VtdWxhdGVzIGFs bCB0aGUgYnl0ZXMsIGFuZCB0aGUgYWNjdW11bGF0aXZlIGFjY2VsZXJhdGlvbiBvZiBCIGV4cGFu ZHMgZmFzdGVyLCBiZWNhdXNlIHRoZQo+ID4+Pj4gYWNjdW11bGF0aXZlIGZvcm11bGEgb2YgQiBp czoKPiA+Pj4+IAlCID0gKDEgKyBEMSkgKyAoMSArIEQxICsgRDIpICsgLi4uICsgKDEgKyBEMSAr IEQyICsgLi4uICsgRG4pIChtb2QgNjU1MjEpCj4gPj4+PiAgICAgICAgICAgID0gbsOXRDEgKyAo buKIkjEpw5dEMiArIChu4oiSMinDl0QzICsgLi4uICsgRG4gKyBuIChtb2QgNjU1MjEpCj4gPj4+ Pgo+ID4+Pj4gSWYgd2UgdGFrZSB0aGUgYXZlcmFnZSB2YWx1ZSBvZiBEeCB0byAxMjggYW5kIG4g dG8gODE5MjoKPiA+Pj4+IAlCID0gKDEgKyAyICsgLi4uICsgODEyOSkgKiAxMjggKyA4MTkyCj4g Pj4+PiAJICA9IDQsMjk1LDQ5OSw3NzYgKDMyYml0IG92ZXJmbG93KQo+ID4+Pj4KPiA+Pj4+IFNv IEkgdGhpbmsgdGhlIDMyLWJpdCBhY2N1bXVsYXRvciBpcyBzdGlsbCBub3QgZW5vdWdoIGZvciBw YXJ0IEIgaGVyZS4gOikKPiA+Pj4+Cj4gPj4+PiAtLSAKPiA+Pj4+IEJlc3QgcmVnYXJkcywKPiA+ Pj4+IExpIFFpYW5nCj4gPj4+IFRoYXQgbWFrZXMgc2Vuc2UuICBJIGhhZG4ndCB0cmllZCB0byBj YWxjdWxhdGUgdGhlIGFjdHVhbCBib3VuZC4KPiA+Pj4KPiA+Pj4gSXQgbWF5IGJlIHdvcnRoIHRy eWluZyB0aGlzIHdpdGggNjQtYml0IGFjY3VtdWxhdG9ycy4gIFRoaXMgd2lsbAo+ID4+PiBwcm9i YWJseSBzbG93IHRoaW5ncyBkb3duLCBidXQgaXQgZGVwZW5kcyBvbiB0aGUgcmVsYXRpdmUgY29t cHV0YXRpb24gLwo+ID4+PiBtZW1vcnkgdGhyb3VnaHB1dCBleGhpYml0ZWQgYnkgdGhlIGhhcmR3 YXJlLgo+ID4+Cj4gPj4gSWYgYSA2NC1iaXQgd2lkZSB2ZWN0b3IgcmVnaXN0ZXIgaXMgdXNlZCwg Zm9yIG1vc3Qgc2NlbmVzIHdoZXJlIHRoZSBhbW91bnQgb2YgZGF0YSBpcyBub3QgcGFydGljdWxh cmx5IGxhcmdlLAo+ID4+IGlzIGl0IHdhc3RlZCBtb3JlIHZlY3RvciByZXNvdXJjZXM/Cj4gPiAK PiA+IFllcyA6KQo+ID4gCj4gPiBEZXBlbmRpbmcgb24gdGhlIGFsZ29yaXRobSwgdGhpcyBtaWdo dCBiZSBhIGJldHRlciB0cmFkZW9mZiBpZiBpdCBtZWFudAo+ID4gdGhhdCB0aGUgZGF0YSBjb3Vs ZCBiZSBwcm9jZXNzZWQgaW4gbGFyZ2VyIGNodW5rcy4gIEkgc3VzcGVjdCB0aGF0IHRoZQo+ID4g dHJhZGVvZmYgaXMgdW5mYXZvdXJhYmxlIGluIHRoaXMgcGFydGljbHVhciBjYXNlIHRob3VnaCAt LSBidXQgSSBoYXZlbid0Cj4gPiB0cmllZCBpdC4KPiA+IAo+ID4+IE1heWJlIHdlIGNhbiBhbHNv IHRyeSB0byB1c2UgMTYtYml0IHdpZGUgdmVjdG9yIHJlZ2lzdGVycyB0byBsb2FkIGRhdGEgYW5k IGNhbGN1bGF0aW9ucywKPiA+PiBhbmQgYWNjdW11bGF0ZSB0aGVtIGludG8gdGhlIHNjYWxhciBy ZWdpc3RlciB4biBiZWZvcmUgb3ZlcmZsb3csIGp1c3QgbGlrZSBteSBvcmlnaW5hbCBwYXRjaCwK PiA+PiBidXQgSSBjYW4gdHJ5IHRvIHVzZSBhc2NlbmRpbmcgb3JkZXIgdG8gY2hhbmdlIHRoZSBw cm9jZXNzaW5nIG9mIHRoZSBsYXN0IGxvb3AgQmUgbW9yZSBlbGVnYW50Lgo+ID4gCj4gPiBQZXJo YXBzLiAgQnV0IEkgYXNzdW1lZCB0aGF0IDE2LWJpdCBlbGVtZW50cyB3b3VsZCBvdmVyZmxvdyBt dWNoIHRvbwo+ID4gZmFzdCB0byBiZSBwcmFjdGljYWwuICBKdXN0IG11bHRpcGx5aW5nIHpYIGJ5 IHpKIG9uY2UgY2FuIHByb2R1Y2UKPiA+IGVsZW1lbnQgdmFsdWVzIHVwIHRvIDB4ZmUwMSBpZiBW TCBpcyAyNTYgYnl0ZXMuCj4gPiAKPiA+IERpZCB5b3UgaGF2ZSBzb21lIGlkZWEgZm9yIGhvdyB0 byBtYWtlIHRoaXMgd29yaz8KPiA+IAo+ID4gSXQgbWF5IGJlIHBvc3NpYmxlIHRvIGRvIDE2LWJp dCBtdWx0aXBsaWVzIHdpdGggMzItYml0IGFjY3VtdWxhdGlvbi4KPiA+IFNWRTIgaGFzIHNvbWUg TkVPTi1zdHlsZSBtaXhlZC13aWR0aCBtdWx0aXBseS1hY2N1bXVsYXRlIGluc3RydWN0aW9ucwo+ ID4gdGhhdCBjYW4gYWNoaWV2ZSB0aGlzIHNvcnQgb2YgdGhpbmcgZGlyZWN0bHksIGJ1dCBpbiBT VkUoMSkgSSB0aGluayAKPiA+IHlvdSB3b3VsZCBoYXZlIHRvIGJyZWFrIHRoZSBtdWx0aXBseS1h Y2N1bXVsYXRlcyB1cC4gIFNheToKPiA+IAo+ID4gCW11bAl6WC5oLCBwUC9tLCB6WC5oLCB6Si5o Cj4gPiAJc3ViCXpYLnMsIHBQL20sIHpCLnMsIHpYLnMKPiA+IAo+ID4gd2hpbGVsbyBwUC5zIHNo b3VsZCBnZW5lcmF0ZSBhIHByZWRpY2F0ZSB3aXRoIG9kZCAuaCBlbGVtZW50cyBpbmFjdGl2ZSwK PiA+IGFuZCBsZDFiIHpYLnMsIHBQL3osIC4uLiB3aWxsIG1ha2Ugc3VyZSB0aG9zZSBlbGVtZW50 cyBhcmUgYWxsIHplcm9lZCBpbgo+ID4geFguaC4KPiA+IAo+ID4gSSdtIG5vdCBzdXJlIHRoaXMg d291bGQgYmUgZmFzdGVyIHRoYW4gYSBzaW5nbGUgMzItYml0IG1zYiwgc2luY2UKPiA+IG11bHRp cGx5LWFjY3VtdWxhdGVzIGFyZSBsaWtlbHkgdG8gYmUgaGVhdmlseSBvcHRpbWlzZWQgaW4gdGhl IGhhcmR3YXJlLAo+ID4gYnV0IHlvdSBjb3VsZCB0cnkgaXQuCj4gCj4gSSBhZG9wdGVkIGEgcGFy dCBvZiB0aGUgcGF0Y2ggSSBvcmlnaW5hbGx5IHN1Ym1pdHRlZCwgY29tYmluZWQgd2l0aCB0aGUg Zm9ybXVsYSB5b3UgZ2F2ZQo+IHRvIGNhbGN1bGF0ZSBCW24rdl0gdXNpbmcgYW4gaW5jcmVhc2lu ZyBzZXF1ZW5jZSwgYW5kIHRoZW4gbW9kaWZ5IHRoZSBjb2RlIGFnYWluLiBUaGUgY29kZSBpcyBh dCB0aGUgZW5kLgo+IAo+ID4gCj4gPj4+Cj4gPj4+IEkgdGhpbmsgdGhlIGNvZGUgY2FuJ3QgYmUg YnVsbGV0cHJvb2Ygd2l0aG91dCBicmVha2luZyB0aGUgaW5wdXQgaW50bwo+ID4+PiBjaHVua3Mg YW55d2F5LCB0aG91Z2guCj4gPj4KPiA+PiBJIGRvbid0IHF1aXRlIHVuZGVyc3RhbmQgd2hhdCBp dCBtZWFucyBoZXJlLiBEb2VzIGl0IG1lYW4gdGhhdCB0aGUgaW5wdXQgYnl0ZXMgYXJlIHJlYWQg aW50byB0aGUgdmVjdG9yIGluCj4gPj4gYmxvY2tzIGZvciBjYWxjdWxhdGlvbiAodGhpcyBpcyBo b3cgaXQgaXMgZG9uZSBub3cpIG9yIHRoZSBpbnRlcm1lZGlhdGUgcmVzdWx0cyBhcmUgc3RvcmVk IGluIGRpZmZlcmVudCBlbGVtZW50cwo+ID4+IG9mIHRoZSB2ZWN0b3IgaW4gYmxvY2tzIGR1cmlu ZyB0aGUgY2FsY3VsYXRpb24gcHJvY2Vzcz8gOi0pCj4gPiAKPiA+IEkgbWVhbiwgeW91IGxpbWl0 IHRoZSBudW1iZXIgb2YgaXRlcmF0aW9ucyBvZiB0aGUgY29yZSBsb29wIHNvIHRoYXQKPiA+IG92 ZXJmbG93IGRvZXNuJ3QgaGFwcGVuLiAgWW91J3JlIGFscmVhZHkgZG9pbmcgdGhpczsgSSBqdXN0 IHdhbnRlZCB0bwo+ID4gIG1ha2UgdGhlIHBvaW50IHRoYXQgNjQtYml0IGFjY3VtdWxhdG9ycyBw cm9iYWJseSBkb24ndCBzb2x2ZSB0aGlzCj4gPiBwcm9ibGVtLCBldmVuIHRob3VnaCBpdCB3aWxs IHRha2UgYSB2ZXJ5IGxhcmdlIG51bWJlciBvZiBpdGVyYXRpb25zIHRvCj4gPiBjYXVzZSBhbiBv dmVyZmxvdy4KPiA+IAo+ID4gVW5sZXNzIEFkbGVyMzIgc3BlY2lmaWVzIHRoZSBtYXhpbXVtIGxl bmd0aCBvZiB0aGUgaW5wdXQgZGF0YSBub3QgdG8KPiA+IGV4Y2VlZCBzb21lIHZhbHVlLCBpdCBj b3VsZCBnbyBvbiBmb3JldmVyIC0tIHNvIG92ZXJmbG93IGJlY29tZXMKPiA+IGluZXZpdGFibGUu Cj4gCj4gWWVzLCBJIHVuZGVyc3RhbmQgdGhhdCBpZiB3ZSBkbyBub3QgcGVyZm9ybSB0aGUgbW9k dWxvIGRpdmlzaW9uIGluIHRpbWUsIGRhdGEgb3ZlcmZsb3cgd2lsbCBoYXBwZW4gc29vbmVyIG9y IGxhdGVyLgo+IFNvIG15IGluaXRpYWwgcGF0Y2ggYWRkZWQgbW9kdWxvIGRpdmlzaW9uIHRvIHRo ZSBsb29wIGJvZHkgYW5kIG9wdGltaXplZCBpdC46KQo+IAo+IEFzIHlvdSBzYWlkLCBpZiB3ZSBk b27igJl0IHdhbnQgdG8gcGVyZm9ybSBtb2R1bGFyIGRpdmlzaW9uIGluIHRoZSBsb29wIGJvZHks IHRoZW4gd2UgbmVlZCB0byBibG9jayB0aGUgaW5wdXQgYW5kCj4gcGVyZm9ybSB0aGUgbW9kdWxh ciBkaXZpc2lvbiBpbiB0aW1lIGJlZm9yZSB0aGUgZGF0YSB3aWxsIG5ldmVyIG92ZXJmbG93IChO ZWVkIHRvIGNvbnNpZGVyIHRoZSB3b3JzdCBjYXNlLAo+IHRoYXQgaXMsIGFsbCBkYXRhIGlzIDB4 ZmYpLgo+IAo+IC0tODwtLQo+IAkuLi4KPiAJYWRsZXJfQSAucmVxICAgIHgxMAo+IAlhZGxlcl9C IC5yZXEgICAgeDExCj4gCQo+IAkubWFjcm8JYWRsZXIzMl9jb3JlCj4gCWxkMWIJelguaCwgcDAv eiwgW3gxXQkvLyBsb2FkIGJ5dGVzCj4gCWluY2gJeDEKPiAKPiAJdWFkZHYJZDAsIHAwLCB6WC5o Cj4gCW11bAl6WC5oLCBwMC9tLCB6WC5oLCB6Si5oCS8vIFN1bSBbaj0wIC4uIHYtMV0gaipYW2or bl0KPiAJbW92CXg5LCB2MC5kWzBdCj4gCXVhZGR2CWQxLCBwMCwgelguaAo+IAlhZGQJYWRsZXJf QSwgYWRsZXJfQSwgeDkJLy8gQVtuK3ZdID0gQW4gKyBTdW0gW2o9MCAuLi4gdi0xXSBYW2pdCj4g CW1vdgl4OSwgdjEuZFswXQo+IAltYWRkCWFkbGVyX0IsIHg3LCBhZGxlcl9BLCBhZGxlcl9CCS8v IEJuICsgdipBW24rdl0KPiAJc3ViCWFkbGVyX0IsIGFkbGVyX0IsIHg5CQkvLyBCW24rdl0gPSBC biArIHYqQVtuK3ZdIC0gU3VtIFtqPTAgLi4gdi0xXSBqKlhbaituXQo+IAkuZW5kbQoKSWYgdGhp cyBoYXMgYmVzdCBwZXJmb3JtYW5jZSwgSSBmaW5kIHRoYXQgcXVpdGUgc3VycHJpc2luZy4gIFRo b3NlIHVhZGR2Cmluc3RydWN0aW9ucyB3aWxsIHN0b3AgdGhlIHZlY3RvciBsYW5lcyBmbG93aW5n IGluZGVwZW5kZW50bHkgaW5zaWRlIHRoZQpsb29wLCBzbyBpZiBhbiBpbmRpdmlkdWFsIGVsZW1l bnQgbG9hZCBpcyBzbG93IGFycml2aW5nIHRoZW4gZXZlcnl0aGluZwp3aWxsIGhhdmUgdG8gd2Fp dC4KCkEgZGVjZW50IGhhcmR3YXJlIHByZWZldGNoZXIgbWF5IHRlbmQgdG8gaGlkZSB0aGF0IGlz c3VlIGZvciBzZXF1ZW50aWFsCm1lbW9yeSBhY2Nlc3MsIHRob3VnaDogaS5lLiwgaWYgdGhlIGhh cmR3YXJlIGRvZXMgYSBkZWNlbnQgam9iIG9mCmZldGNoaW5nIGRhdGEgYmVmb3JlIHRoZSBhY3R1 YWwgbG9hZHMgYXJlIGlzc3VlZCwgdGhlIGRhdGEgbWF5IGFwcGVhciB0bwphcnJpdmUgd2l0aCBt aW5pbWFsIGRlbGF5LgoKVGhlIGVmZmVjdCBtaWdodCBiZSBhIGxvdCB3b3JzZSBmb3IgYWxnb3Jp dGhtcyB0aGF0IGhhdmUgbGVzcwpwcmVkaWN0YWJsZSBtZW1vcnkgYWNjZXNzIHBhdHRlcm5zLgoK UG9zc2libHkgeW91IGRvIHdpbiBzb21lIGFkZGl0aW9uYWwgcGVyZm9ybWFuY2UgZHVlIHRvIHBy b2Nlc3NpbmcgdHdpY2UKYXMgbWFueSBlbGVtZW50cyBhdCBvbmNlLCBoZXJlLgoKPiAKPiAJLm1h Y3JvCWFkbGVyMzJfbG9vcAo+IAl3aGlsZWxvCXAwLmgsIHgxLCB4TGltaXQKPiAJY250cAl4Nywg cDAsIHAwLmgJCS8vIHg3IGlzIHVzZWQgdG8gc3RvcmUgJ3YnCj4gCWFkbGVyMzJfY29yZQo+IAku ZW5kbQo+IAo+IEVOVFJZKFhYWCkKPiAJLi4uCj4gCWFkZAl4TGltaXQsIHgxLCB4Mgo+IAo+IGxv b3A6Cj4gCWFkbGVyMzJfbG9vcAo+IAlhZGxlcjMyX2xvb3AKPiAJYWRsZXIzMl9sb29wCj4gCWFk bGVyMzJfbG9vcAo+IAljbXAJeDcsICMwCj4gCWIubmUJbG9vcAo+IAo+IAltb3YJeDMsICM2NTUy MQo+IAl1ZGl2CXg0LCB4MTAsIHgzCj4gCXVkaXYJeDUsIHgxMSwgeDMKPiAJbXN1Ygl4MTAsIHg0 LCB4MywgeDEwCj4gCW1zdWIJeDExLCB4NSwgeDMsIHgxMQo+IAkuLi4KPiAtLT44LS0KPiAKPiAK PiBJIHRlc3RlZCA1MDBNYnl0ZSByYW5kb20gZGF0YSwgdGhlIHJlc3VsdCBpcyBjb21wbGV0ZWx5 IGNvcnJlY3QsIGxvbmdlciBkYXRhIHZvbHVtZSBkaWQgbm90IHRlc3QsCj4gSSB0aGluayB3ZSBj YW4gYWxzbyB3cmFwIGEgbGF5ZXIgb2YgZGF0YSBibG9jayBjb250cm9sIGNvZGUsIHRoYXQgaXMs IGFmdGVyIHRoZSBkYXRhIHZvbHVtZQo+IGV4Y2VlZHMgYSBjZXJ0YWluIHRocmVzaG9sZCwgdGhl IGNvbnRyb2wgY29kZSBUaGUgaW5wdXQgZGF0YSBpcyBzZWdtZW50ZWQgYnkgdGhyZXNob2xkLCBh bmQKPiB0aGUgaW5pdGlhbCBhZGxlcjMyIG9mIHRoZSBzdWJzZXF1ZW50IHNlZ21lbnQgaXMgdGhl IHJlc3VsdCBvZiB0aGUgcHJldmlvdXMgc2VnbWVudCBjYWxjdWxhdGlvbi4KPiAKPiBMaWtlOgo+ IAlhZGxlcjMyID0gMTsKPiAJaWYgKGxlbiA8PSBNQVhfQkxPQ0tfTEVOKQo+IAkJYWRsZXIzMiA9 IGFkbGVyMzJfc3ZlKGFkbGVyMzIsIGJ1ZiwgbGVuKTsKPiAJZWxzZSB7Cj4gCQlpbnQgaSA9IDA7 Cj4gCQl3aGlsZSAoaSA8IGxlbikgewo+IAkJCWludCBibG9ja19sZW4gPSAobGVuIC0gaSA+IE1B WF9CTE9DS19MRU4pID8gTUFYX0JMT0NLX0xFTiA6IChsZW4gLSBpKTsKPiAJCQlhZGxlcjMyID0g YWRsZXIzMl9zdmUoYWRsZXIzMiwgJmJ1ZltpXSwgYmxvY2tfbGVuKTsKPiAJCQlpICs9IGJsb2Nr X2xlbjsKPiAJCX0KPiAJfQo+IAlyZXR1cm4gYWRsZXIzMjsKPiAKPiBJbiB0aGlzIHdheSwgd2Ug ZG9uJ3QgaGF2ZSB0byB3b3JyeSBhYm91dCB3aGVuIHRoZSBkYXRhIG92ZXJmbG93cyBhbmQgd2hl biB0byBtb2R1bG8gaW4gdGhlIGNvcmUgYWxnb3JpdGhtIGNvZGUuCgpZZXMsIHNvbWV0aGluZyBs aWtlIHRoYXQgb3VnaHQgdG8gd29yay4KCkNoZWVycwotLS1EYXZlCgpfX19fX19fX19fX19fX19f X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXwpsaW51eC1hcm0ta2VybmVsIG1haWxpbmcg bGlzdApsaW51eC1hcm0ta2VybmVsQGxpc3RzLmluZnJhZGVhZC5vcmcKaHR0cDovL2xpc3RzLmlu ZnJhZGVhZC5vcmcvbWFpbG1hbi9saXN0aW5mby9saW51eC1hcm0ta2VybmVsCg==