libgpuverify

Signature verification on GPUs (WiP)
Log | Files | Refs | README | LICENSE

commit 42ef43ecf28d78426d83833faaeb2380029165de
parent 36724ccea92c0594381774f589bcfa9f73c6d658
Author: Cedric <cedric.zwahlen@students.bfh.ch>
Date:   Fri,  8 Dec 2023 10:51:24 +0100

Clean up files and add license information

Diffstat:
Msource/Makefile | 2+-
Dsource/big-int-test.c | 1089-------------------------------------------------------------------------------
Dsource/big-int-test.h | 127-------------------------------------------------------------------------------
Msource/gmp.c | 25+++++++++----------------
Msource/gmp.h | 7-------
Msource/lib-gpu-verify.c | 14+++++++++-----
Dsource/montgomery-orig.cl | 2982-------------------------------------------------------------------------------
Msource/montgomery-test.c | 118++++++++-----------------------------------------------------------------------
Msource/montgomery-test.h | 17+----------------
Tsource/montgomery.cl | 0
Msource/montgomery.h | 7+------
Dsource/opencl-test.c | 205-------------------------------------------------------------------------------
Dsource/opencl-test.h | 30------------------------------
Dsource/openssl-test.c | 3465-------------------------------------------------------------------------------
Dsource/openssl-test.h | 531-------------------------------------------------------------------------------
Asource/reference-test.c | 252+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/reference-test.h | 15+++++++++++++++
Msource/rsa-test.c | 657+++++++++++++++++++------------------------------------------------------------
Msource/rsa-test.h | 56++------------------------------------------------------
Dsource/square.c | 555-------------------------------------------------------------------------------
Asource/util.c | 352+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asource/util.h | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mxcode/.DS_Store | 0
Mxcode/lib-gpu-generate/msgsig.txt | 1024++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mxcode/lib-gpu-generate/publickey.txt | 32++++++++++++++++----------------
Mxcode/lib-gpu-verify.xcodeproj/project.pbxproj | 32+++++++++++++-------------------
Mxcode/lib-gpu-verify.xcodeproj/project.xcworkspace/xcuserdata/cedriczwahlen.xcuserdatad/UserInterfaceState.xcuserstate | 0
Mxcode/lib-gpu-verify.xcodeproj/xcuserdata/cedriczwahlen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist | 80++++++++++++++++++++++++++-----------------------------------------------------
Mxcode/montgomery.cl | 7+------
Mxcode/verify.cl | 25+++++++++++++++++++++++++
30 files changed, 1469 insertions(+), 10306 deletions(-)

diff --git a/source/Makefile b/source/Makefile @@ -1,3 +1,3 @@ all: - gcc -g -O0 -D CL_TARGET_OPENCL_VERSION=100 -o gpu-verify lib-gpu-verify.c rsa-test.c big-int-test.c montgomery-test.c gmp.c -lgcrypt -lOpenCL -lm + gcc -g -O0 -D CL_TARGET_OPENCL_VERSION=100 -o gpu-verify lib-gpu-verify.c rsa-test.c montgomery-test.c reference-test.c util.c gmp.c -lgcrypt -lOpenCL -lm diff --git a/source/big-int-test.c b/source/big-int-test.c @@ -1,1089 +0,0 @@ -// -// bigNum_test.c -// hello -// -// Created by Cedric Zwahlen on 25.09.23. -// - -#include "big-int-test.h" - - - -// MARK: min functionality - -int mpModulo(DIGIT_T r[], const DIGIT_T u[], size_t udigits, - DIGIT_T v[], size_t vdigits) -{ - /* Computes r = u mod v - where r, v are multiprecision integers of length vdigits - and u is a multiprecision integer of length udigits. - r may overlap v. - - Note that r here is only vdigits long, - whereas in mpDivide it is udigits long. - - Use remainder from mpDivide function. - */ - - size_t nn = max(udigits, vdigits); -/* Allocate temp storage */ -//#ifdef NO_ALLOCS - // [v2.6] increased to two times - DIGIT_T qq[MAX_FIXED_DIGITS*2]; - DIGIT_T rr[MAX_FIXED_DIGITS*2]; - // assert(nn <= (MAX_FIXED_DIGITS*2)); -/*#else - DIGIT_T *qq, *rr; - qq = mpAlloc(udigits); - rr = mpAlloc(nn); -#endif -*/ - - /* rr[nn] = u mod v */ - mpDivide(qq, rr, u, udigits, v, vdigits); - - /* Final r is only vdigits long */ - mpSetEqual(r, rr, vdigits); - - mpDESTROY(rr, udigits); - mpDESTROY(qq, udigits); - - return 0; -} - -int mpModMult(DIGIT_T a[], const DIGIT_T x[], const DIGIT_T y[], - DIGIT_T m[], size_t ndigits) -{ /* Computes a = (x * y) mod m */ - -/* Double-length temp variable p */ -// #ifdef NO_ALLOCS - DIGIT_T p[MAX_FIXED_DIGITS * 2]; -// assert(ndigits <= MAX_FIXED_DIGITS); -/*#else - DIGIT_T *p; - p = mpAlloc(ndigits * 2); -#endif -*/ - /* Calc p[2n] = x * y */ - mpMultiply(p, x, y, ndigits); - - /* Then modulo (NOTE: a is OK at only ndigits long) */ - mpModulo(a, p, ndigits * 2, m, ndigits); - - mpDESTROY(p, ndigits * 2); - - return 0; -} - -int mpMultiply(DIGIT_T w[], const DIGIT_T u[], const DIGIT_T v[], size_t ndigits) -{ - /* Computes product w = u * v - where u, v are multiprecision integers of ndigits each - and w is a multiprecision integer of 2*ndigits - - Ref: Knuth Vol 2 Ch 4.3.1 p 268 Algorithm M. - */ - - DIGIT_T k, t[2]; - size_t i, j, m, n; - - // assert(w != u && w != v); - - m = n = ndigits; - - /* Step M1. Initialise */ - for (i = 0; i < 2 * m; i++) - w[i] = 0; - - for (j = 0; j < n; j++) - { - /* Step M2. Zero multiplier? */ - if (v[j] == 0) - { - w[j + m] = 0; - } - else - { - /* Step M3. Initialise i */ - k = 0; - for (i = 0; i < m; i++) - { - /* Step M4. Multiply and add */ - /* t = u_i * v_j + w_(i+j) + k */ - spMultiply(t, u[i], v[j]); - - t[0] += k; - if (t[0] < k) - t[1]++; - t[0] += w[i+j]; - if (t[0] < w[i+j]) - t[1]++; - - w[i+j] = t[0]; - k = t[1]; - } - /* Step M5. Loop on i, set w_(j+m) = k */ - w[j+m] = k; - } - } /* Step M6. Loop on j */ - - return 0; -} - -DIGIT_T mpAdd(DIGIT_T w[], const DIGIT_T u[], const DIGIT_T v[], size_t ndigits) -{ - /* Calculates w = u + v - where w, u, v are multiprecision integers of ndigits each - Returns carry if overflow. Carry = 0 or 1. - - Ref: Knuth Vol 2 Ch 4.3.1 p 266 Algorithm A. - */ - - DIGIT_T k; - size_t j; - - // assert(w != v); - - /* Step A1. Initialise */ - k = 0; - - for (j = 0; j < ndigits; j++) - { - /* Step A2. Add digits w_j = (u_j + v_j + k) - Set k = 1 if carry (overflow) occurs - */ - w[j] = u[j] + k; - if (w[j] < k) - k = 1; - else - k = 0; - - w[j] += v[j]; - if (w[j] < v[j]) - k++; - - } /* Step A3. Loop on j */ - - return k; /* w_n = k */ -} - -int mpDivide(DIGIT_T q[], DIGIT_T r[], const DIGIT_T u[], - size_t udigits, DIGIT_T v[], size_t vdigits) -{ /* Computes quotient q = u / v and remainder r = u mod v - where q, r, u are multiple precision digits - all of udigits and the divisor v is vdigits. - - Ref: Knuth Vol 2 Ch 4.3.1 p 272 Algorithm D. - - Do without extra storage space, i.e. use r[] for - normalised u[], unnormalise v[] at end, and cope with - extra digit Uj+n added to u after normalisation. - - WARNING: this trashes q and r first, so cannot do - u = u / v or v = u mod v. - It also changes v temporarily so cannot make it const. - */ - size_t shift; - int n, m, j; - DIGIT_T bitmask, overflow; - DIGIT_T qhat, rhat, t[2]; - DIGIT_T *uu, *ww; - int qhatOK, cmp; - - /* Clear q and r */ - mpSetZero(q, udigits); - mpSetZero(r, udigits); - - /* Work out exact sizes of u and v */ - n = (int)mpSizeof(v, vdigits); - m = (int)mpSizeof(u, udigits); - m -= n; - - /* Catch special cases */ - if (n == 0) - return -1; /* Error: divide by zero */ - - if (n == 1) - { /* Use short division instead */ - r[0] = mpShortDiv(q, u, v[0], udigits); - return 0; - } - - if (m < 0) - { /* v > u, so just set q = 0 and r = u */ - mpSetEqual(r, u, udigits); - return 0; - } - - if (m == 0) - { /* u and v are the same length */ - cmp = mpCompare(u, v, (size_t)n); - if (cmp < 0) - { /* v > u, as above */ - mpSetEqual(r, u, udigits); - return 0; - } - else if (cmp == 0) - { /* v == u, so set q = 1 and r = 0 */ - mpSetDigit(q, 1, udigits); - return 0; - } - } - - /* In Knuth notation, we have: - Given - u = (Um+n-1 ... U1U0) - v = (Vn-1 ... V1V0) - Compute - q = u/v = (QmQm-1 ... Q0) - r = u mod v = (Rn-1 ... R1R0) - */ - - /* Step D1. Normalise */ - /* Requires high bit of Vn-1 - to be set, so find most signif. bit then shift left, - i.e. d = 2^shift, u' = u * d, v' = v * d. - */ - bitmask = HIBITMASK; - for (shift = 0; shift < BITS_PER_DIGIT; shift++) - { - if (v[n-1] & bitmask) - break; - bitmask >>= 1; - } - - /* Normalise v in situ - NB only shift non-zero digits */ - overflow = mpShiftLeft(v, v, shift, n); - - /* Copy normalised dividend u*d into r */ - overflow = mpShiftLeft(r, u, shift, n + m); - uu = r; /* Use ptr to keep notation constant */ - - t[0] = overflow; /* Extra digit Um+n */ - - /* Step D2. Initialise j. Set j = m */ - for (j = m; j >= 0; j--) - { - /* Step D3. Set Qhat = [(b.Uj+n + Uj+n-1)/Vn-1] - and Rhat = remainder */ - qhatOK = 0; - t[1] = t[0]; /* This is Uj+n */ - t[0] = uu[j+n-1]; - overflow = spDivide(&qhat, &rhat, t, v[n-1]); - - /* Test Qhat */ - if (overflow) - { /* Qhat == b so set Qhat = b - 1 */ - qhat = MAX_DIGIT; - rhat = uu[j+n-1]; - rhat += v[n-1]; - if (rhat < v[n-1]) /* Rhat >= b, so no re-test */ - qhatOK = 1; - } - /* [VERSION 2: Added extra test "qhat && "] */ - if (qhat && !qhatOK && QhatTooBig(qhat, rhat, v[n-2], uu[j+n-2])) - { /* If Qhat.Vn-2 > b.Rhat + Uj+n-2 - decrease Qhat by one, increase Rhat by Vn-1 - */ - qhat--; - rhat += v[n-1]; - /* Repeat this test if Rhat < b */ - if (!(rhat < v[n-1])) - if (QhatTooBig(qhat, rhat, v[n-2], uu[j+n-2])) - qhat--; - } - - - /* Step D4. Multiply and subtract */ - ww = &uu[j]; - overflow = mpMultSub(t[1], ww, v, qhat, (size_t)n); - - /* Step D5. Test remainder. Set Qj = Qhat */ - q[j] = qhat; - if (overflow) - { /* Step D6. Add back if D4 was negative */ - q[j]--; - overflow = mpAdd(ww, ww, v, (size_t)n); - } - - t[0] = uu[j+n-1]; /* Uj+n on next round */ - - } /* Step D7. Loop on j */ - - /* Clear high digits in uu */ - for (j = n; j < m+n; j++) - uu[j] = 0; - - /* Step D8. Unnormalise. */ - - mpShiftRight(r, r, shift, n); - mpShiftRight(v, v, shift, n); - - return 0; -} - -void mpSetDigit(DIGIT_T a[], DIGIT_T d, size_t ndigits) -{ /* Sets a = d where d is a single digit */ - size_t i; - - for (i = 1; i < ndigits; i++) - { - a[i] = 0; - } - a[0] = d; -} - -DIGIT_T mpShortDiv(DIGIT_T q[], const DIGIT_T u[], DIGIT_T v, - size_t ndigits) -{ - /* Calculates quotient q = u div v - Returns remainder r = u mod v - where q, u are multiprecision integers of ndigits each - and r, v are single precision digits. - - Makes no assumptions about normalisation. - - Ref: Knuth Vol 2 Ch 4.3.1 Exercise 16 p625 - */ - size_t j; - DIGIT_T t[2], r; - size_t shift; - DIGIT_T bitmask, overflow, *uu; - - if (ndigits == 0) return 0; - if (v == 0) return 0; /* Divide by zero error */ - - /* Normalise first */ - /* Requires high bit of V - to be set, so find most signif. bit then shift left, - i.e. d = 2^shift, u' = u * d, v' = v * d. - */ - bitmask = HIBITMASK; - for (shift = 0; shift < BITS_PER_DIGIT; shift++) - { - if (v & bitmask) - break; - bitmask >>= 1; - } - - v <<= shift; - overflow = mpShiftLeft(q, u, shift, ndigits); - uu = q; - - /* Step S1 - modified for extra digit. */ - r = overflow; /* New digit Un */ - j = ndigits; - while (j--) - { - /* Step S2. */ - t[1] = r; - t[0] = uu[j]; - overflow = spDivide(&q[j], &r, t, v); - } - - /* Unnormalise */ - r >>= shift; - - return r; -} - -static int QhatTooBig(DIGIT_T qhat, DIGIT_T rhat, - DIGIT_T vn2, DIGIT_T ujn2) -{ /* Returns true if Qhat is too big - i.e. if (Qhat * Vn-2) > (b.Rhat + Uj+n-2) - */ - DIGIT_T t[2]; - - spMultiply(t, qhat, vn2); - if (t[1] < rhat) - return 0; - else if (t[1] > rhat) - return 1; - else if (t[0] > ujn2) - return 1; - - return 0; -} - -static DIGIT_T mpMultSub(DIGIT_T wn, DIGIT_T *w, const DIGIT_T v[], - DIGIT_T q, size_t n) -{ /* Compute w = w - qv - where w = (WnW[n-1]...W[0]) - return modified Wn. - */ - DIGIT_T k, t[2]; - size_t i; - - if (q == 0) /* No change */ - return wn; - - k = 0; - - for (i = 0; i < n; i++) - { - spMultiply(t, q, v[i]); - w[i] -= k; - if (w[i] > MAX_DIGIT - k) - k = 1; - else - k = 0; - w[i] -= t[0]; - if (w[i] > MAX_DIGIT - t[0]) - k++; - k += t[1]; - } - - /* Cope with Wn not stored in array w[0..n-1] */ - wn -= k; - - return wn; -} - -DIGIT_T mpShiftLeft(DIGIT_T *a, const DIGIT_T *b, - size_t shift, size_t ndigits) -{ /* Computes a = b << shift */ - /* [v2.1] Modified to cope with shift > BITS_PERDIGIT */ - size_t i, y, nw, bits; - DIGIT_T mask, carry, nextcarry; - - /* Do we shift whole digits? */ - if (shift >= BITS_PER_DIGIT) - { - nw = shift / BITS_PER_DIGIT; - i = ndigits; - while (i--) - { - if (i >= nw) - a[i] = b[i-nw]; - else - a[i] = 0; - } - /* Call again to shift bits inside digits */ - bits = shift % BITS_PER_DIGIT; - carry = b[ndigits-nw] << bits; - if (bits) - carry |= mpShiftLeft(a, a, bits, ndigits); - return carry; - } - else - { - bits = shift; - } - - /* Construct mask = high bits set */ - mask = ~(~(DIGIT_T)0 >> bits); - - y = BITS_PER_DIGIT - bits; - carry = 0; - for (i = 0; i < ndigits; i++) - { - nextcarry = (b[i] & mask) >> y; - a[i] = b[i] << bits | carry; - carry = nextcarry; - } - - return carry; -} - -DIGIT_T mpShiftRight(DIGIT_T *a, const DIGIT_T *b, size_t shift, size_t ndigits) -{ /* Computes a = b >> shift */ - /* [v2.1] Modified to cope with shift > BITS_PERDIGIT */ - size_t i, y, nw, bits; - DIGIT_T mask, carry, nextcarry; - - /* Do we shift whole digits? */ - if (shift >= BITS_PER_DIGIT) - { - nw = shift / BITS_PER_DIGIT; - for (i = 0; i < ndigits; i++) - { - if ((i+nw) < ndigits) - a[i] = b[i+nw]; - else - a[i] = 0; - } - /* Call again to shift bits inside digits */ - bits = shift % BITS_PER_DIGIT; - carry = b[nw-1] >> bits; - if (bits) - carry |= mpShiftRight(a, a, bits, ndigits); - return carry; - } - else - { - bits = shift; - } - - /* Construct mask to set low bits */ - /* (thanks to Jesse Chisholm for suggesting this improved technique) */ - mask = ~(~(DIGIT_T)0 << bits); - - y = BITS_PER_DIGIT - bits; - carry = 0; - i = ndigits; - while (i--) - { - nextcarry = (b[i] & mask) << y; - a[i] = b[i] >> bits | carry; - carry = nextcarry; - } - - return carry; -} - - -int spMultiply(uint32_t p[2], uint32_t x, uint32_t y) -{ - /* Use a 64-bit temp for product */ - uint64_t t = (uint64_t)x * (uint64_t)y; - /* then split into two parts */ - p[1] = (uint32_t)(t >> 32); - p[0] = (uint32_t)(t & 0xFFFFFFFF); - - return 0; -} - -uint32_t spDivide(uint32_t *pq, uint32_t *pr, const uint32_t u[2], uint32_t v) -{ - - uint64_t uu, q; - uu = (uint64_t)u[1] << 32 | (uint64_t)u[0]; - q = uu / (uint64_t)v; - //r = uu % (uint64_t)v; - *pr = (uint32_t)(uu - q * v); - *pq = (uint32_t)(q & 0xFFFFFFFF); - return (uint32_t)(q >> 32); - - - -} - -int mpCompare(const DIGIT_T a[], const DIGIT_T b[], size_t ndigits) -{ - /* if (ndigits == 0) return 0; // deleted [v2.5] */ - - while (ndigits--) - { - if (a[ndigits] > b[ndigits]) - return 1; /* GT */ - if (a[ndigits] < b[ndigits]) - return -1; /* LT */ - } - - return 0; /* EQ */ -} - -void mpSetEqual(DIGIT_T a[], const DIGIT_T b[], size_t ndigits) -{ /* Sets a = b */ - size_t i; - - for (i = 0; i < ndigits; i++) - { - a[i] = b[i]; - } -} - -volatile DIGIT_T mpSetZero(volatile DIGIT_T a[], size_t ndigits) -{ /* Sets a = 0 */ - - /* Prevent optimiser ignoring this */ - volatile DIGIT_T optdummy; - volatile DIGIT_T *p = a; - - while (ndigits--) - a[ndigits] = 0; - - optdummy = *p; - return optdummy; -} - -size_t mpSizeof(const DIGIT_T a[], size_t ndigits) -{ - while(ndigits--) - { - if (a[ndigits] != 0) - return (++ndigits); - } - return 0; -} - -// MARK: HELPERS - - -size_t mpConvToOctets(const DIGIT_T a[], size_t ndigits, unsigned char *c, size_t nbytes) -/* Convert big digit a into string of octets, in big-endian order, - padding on the left to nbytes or truncating if necessary. - Return number of octets required excluding leading zero bytes. -*/ -{ - int j, k, len; - DIGIT_T t; - size_t i, noctets, nbits; - - nbits = mpBitLength(a, ndigits); - noctets = (nbits + 7) / 8; - - len = (int)nbytes; - - for (i = 0, j = len - 1; i < ndigits && j >= 0; i++) - { - t = a[i]; - for (k = 0; j >= 0 && k < BITS_PER_DIGIT; j--, k += 8) - c[j] = (unsigned char)(t >> k); - } - - for ( ; j >= 0; j--) - c[j] = 0; - - return (size_t)noctets; -} - - - -size_t mpConvFromOctets(DIGIT_T a[], size_t ndigits, const unsigned char *c, size_t nbytes) -/* Converts nbytes octets into big digit a of max size ndigits - Returns actual number of digits set (may be larger than mpSizeof) -*/ -{ - size_t i; - int j, k; - DIGIT_T t; - - mpSetZero(a, ndigits); - - /* Read in octets, least significant first */ - /* i counts into big_d, j along c, and k is # bits to shift */ - for (i = 0, j = (int)nbytes - 1; i < ndigits && j >= 0; i++) - { - t = 0; - for (k = 0; j >= 0 && k < BITS_PER_DIGIT; j--, k += 8) - t |= ((DIGIT_T)c[j]) << k; - a[i] = t; - } - - return i; -} - - -size_t mpConvFromHex(DIGIT_T a[], size_t ndigits, const char *s) -/* Convert a string in hexadecimal format to a big digit. - Return actual number of digits set (may be larger than mpSizeof). - Just ignores invalid characters in s. -*/ -{ -//#ifdef NO_ALLOCS - uint8_t newdigits[MAX_ALLOC_SIZE*2]; // [v2.6] increased -/*#else - uint8_t *newdigits; -#endif*/ - size_t newlen; - size_t n; - unsigned long t; - size_t i, j; - - mpSetZero(a, ndigits); - - /* Create some temp storage for int values */ - n = strlen(s); - if (0 == n) return 0; - newlen = uiceil(n * 0.5); /* log(16)/log(256)=0.5 */ - ALLOC_BYTES(newdigits, newlen); - - /* Work through zero-terminated string */ - for (i = 0; s[i]; i++) - { - t = s[i]; - if ((t >= '0') && (t <= '9')) t = (t - '0'); - else if ((t >= 'a') && (t <= 'f')) t = (t - 'a' + 10); - else if ((t >= 'A') && (t <= 'F')) t = (t - 'A' + 10); - else continue; - for (j = newlen; j > 0; j--) - { - t += (unsigned long)newdigits[j-1] << 4; - newdigits[j-1] = (unsigned char)(t & 0xFF); - t >>= 8; - } - } - - /* Convert bytes to big digits */ - n = mpConvFromOctets(a, ndigits, newdigits, newlen); - - /* Clean up */ - FREE_BYTES(newdigits, newlen); - - return n; -} - -static size_t uiceil(double x) -/* Returns ceil(x) as a non-negative integer or 0 if x < 0 */ -{ - size_t c; - - if (x < 0) return 0; - c = (size_t)x; - if ((x - c) > 0.0) - c++; - - return c; -} - -volatile uint8_t zeroise_bytes(volatile void *v, size_t n) -{ /* Zeroise byte array b and make sure optimiser does not ignore this */ - volatile uint8_t optdummy; - volatile uint8_t *b = (uint8_t*)v; - while(n--) - b[n] = 0; - optdummy = *b; - return optdummy; -} - -void mpFail(char *msg) -{ - perror(msg); - printf("the program should stop here"); -} - -size_t mpBitLength(const DIGIT_T d[], size_t ndigits) -/* Returns no of significant bits in d */ -{ - size_t n, i, bits; - DIGIT_T mask; - - if (!d || ndigits == 0) - return 0; - - n = mpSizeof(d, ndigits); - if (0 == n) return 0; - - for (i = 0, mask = HIBITMASK; mask > 0; mask >>= 1, i++) - { - if (d[n-1] & mask) - break; - } - - bits = n * BITS_PER_DIGIT - i; - - return bits; -} - -void mpPrintHex(const char *prefix, const DIGIT_T *a, size_t len, const char *suffix) -{ - if (prefix) printf("%s", prefix); - /* Trim leading digits which are zero */ - while (len--) - { - if (a[len] != 0) - break; - } - len++; - if (0 == len) len = 1; - /* print first digit without leading zeros */ - printf("%" PRIxBIGD, a[--len]); - while (len--) - { - printf("%08" PRIxBIGD, a[len]); - } - if (suffix) printf("%s", suffix); -} - - -int mpModExpO(DIGIT_T *yout, const DIGIT_T *x, const DIGIT_T *e, DIGIT_T *m, - size_t ndigits, size_t edigits) -{ - /* Computes y = x^e mod m */ - /* "Classic" binary left-to-right method */ - - DIGIT_T mask; - size_t n; - size_t nn = ndigits * 2; - - DIGIT_T t1[nn]; - DIGIT_T t2[nn]; - DIGIT_T y[nn]; - - DIGIT_T const *window_x = &x[0]; - DIGIT_T const *window_e = &e[0]; - DIGIT_T *window_m = &m[0]; - - assert(ndigits <= MAX_FIXED_DIGITS); - assert(ndigits != 0); - - n = mpSizeof(window_e, edigits); - /* Catch e==0 => x^0=1 */ - if (0 == n) - { - mpSetDigit(yout, 1, ndigits); - goto done; - } - /* Find second-most significant bit in e */ - for (mask = HIBITMASK; mask > 0; mask >>= 1) - { - if (e[n-1] & mask) - break; - } - mpNEXTBITMASK(mask, n); - - /* Set y = x */ - mpSetEqual(y, window_x, ndigits); - - - // the number of bits in e - size_t bitlength_n = log2(mask) + sizeof(DIGIT_T) * (n - 1) + 1; - - //size_t ctr = 0; - - size_t xyz = 0; - for(xyz = bitlength_n; xyz > 0; xyz--) - { - /* Square y = y * y mod n */ - mpMODSQUARETEMP(y, window_m, ndigits, t1, t2); - if (e[n-1] & mask) - { /* if e(j) == 1 then multiply - y = y * x mod n */ - mpMODMULTTEMP(y, window_x, window_m, ndigits, t1, t2); - } - - /* Move to next bit */ - mpNEXTBITMASK(mask, n); - - } - - /* Return y */ - mpSetEqual(yout, y, ndigits); - -done: - mpDESTROY(t1, nn); - mpDESTROY(t2, nn); - mpDESTROY(y, ndigits); - - return 0; -} - -int mpSquare(DIGIT_T *w, const DIGIT_T *x, size_t ndigits) -/* New in Version 2.0 */ -{ - /* Computes square w = x * x - where x is a multiprecision integer of ndigits - and w is a multiprecision integer of 2*ndigits - - Ref: Menezes p596 Algorithm 14.16 with errata. - */ - - DIGIT_T k, p[2], u[2], cbit, carry; - size_t i, j, t, i2, cpos; - - assert(w != x); - - t = ndigits; - - /* 1. For i from 0 to (2t-1) do: w_i = 0 */ - i2 = t << 1; - for (i = 0; i < i2; i++) - w[i] = 0; - - carry = 0; - cpos = i2-1; - /* 2. For i from 0 to (t-1) do: */ - for (i = 0; i < t; i++) - { - /* 2.1 (uv) = w_2i + x_i * x_i, w_2i = v, c = u - Careful, w_2i may be double-prec - */ - i2 = i << 1; /* 2*i */ - spMultiply(p, x[i], x[i]); - p[0] += w[i2]; - if (p[0] < w[i2]) - p[1]++; - k = 0; /* p[1] < b, so no overflow here */ - if (i2 == cpos && carry) - { - p[1] += carry; - if (p[1] < carry) - k++; - carry = 0; - } - w[i2] = p[0]; - u[0] = p[1]; - u[1] = k; - - /* 2.2 for j from (i+1) to (t-1) do: - (uv) = w_{i+j} + 2x_j * x_i + c, - w_{i+j} = v, c = u, - u is double-prec - w_{i+j} is dbl if [i+j] == cpos - */ - k = 0; - for (j = i+1; j < t; j++) - { - /* p = x_j * x_i */ - spMultiply(p, x[j], x[i]); - /* p = 2p <=> p <<= 1 */ - cbit = (p[0] & HIBITMASK) != 0; - k = (p[1] & HIBITMASK) != 0; - p[0] <<= 1; - p[1] <<= 1; - p[1] |= cbit; - /* p = p + c */ - p[0] += u[0]; - if (p[0] < u[0]) - { - p[1]++; - if (p[1] == 0) - k++; - } - p[1] += u[1]; - if (p[1] < u[1]) - k++; - /* p = p + w_{i+j} */ - p[0] += w[i+j]; - if (p[0] < w[i+j]) - { - p[1]++; - if (p[1] == 0) - k++; - } - if ((i+j) == cpos && carry) - { /* catch overflow from last round */ - p[1] += carry; - if (p[1] < carry) - k++; - carry = 0; - } - /* w_{i+j} = v, c = u */ - w[i+j] = p[0]; - u[0] = p[1]; - u[1] = k; - } - /* 2.3 w_{i+t} = u */ - w[i+t] = u[0]; - /* remember overflow in w_{i+t} */ - carry = u[1]; - cpos = i+t; - } - - /* (NB original step 3 deleted in Menezes errata) */ - - /* Return w */ - - return 0; -} - - -size_t mpConvToHex(const DIGIT_T a[], size_t ndigits, char *s, size_t smax) -/* Convert big digit a into a string in hexadecimal format, - where s has max size smax. - Return number of chars set excluding leading zeroes. -*/ -{ - return conv_to_base(a, ndigits, s, smax, 16); -} - - -static size_t conv_to_base(const DIGIT_T a[], size_t ndigits, char *s, size_t smax, int base) -/* Convert big digit a into a string in given base format, - where s has max size smax. - Return number of chars set excluding leading zeroes. - smax can be 0 to find out the required length. -*/ -{ - - uint8_t bytes[MAX_ALLOC_SIZE], newdigits[MAX_ALLOC_SIZE*3]; // [v2.6] increased - - const char DEC_DIGITS[] = "0123456789"; - const char HEX_DIGITS[] = "0123456789abcdef"; - size_t newlen, nbytes, nchars; - size_t n; - unsigned long t; - size_t i, j, isig; - const char *digits; - double factor; - - switch (base) - { - case 10: - digits = DEC_DIGITS; - factor = 2.40824; /* log(256)/log(10)=2.40824 */ - break; - case 16: - digits = HEX_DIGITS; - factor = 2.0; /* log(256)/log(16)=2.0 */ - break; - default: - assert (10 == base || 16 == base); - return 0; - } - - /* Set up output string with null chars */ - if (smax > 0 && s) - { - memset(s, '0', smax-1); - s[smax-1] = '\0'; - } - - /* Catch zero input value (return 1 not zero) */ - if (mpIsZero(a, ndigits)) - { - if (smax > 0 && s) - s[1] = '\0'; - return 1; - } - - /* First, we convert to 8-bit octets (bytes), which are easier to handle */ - nbytes = ndigits * BITS_PER_DIGIT / 8; - ALLOC_BYTES(bytes, nbytes); - - n = mpConvToOctets(a, ndigits, bytes, nbytes); - - /* Create some temp storage for int values */ - newlen = uiceil(n * factor); - ALLOC_BYTES(newdigits, newlen); - - for (i = 0; i < nbytes; i++) - { - t = bytes[i]; - for (j = newlen; j > 0; j--) - { - t += (unsigned long)newdigits[j-1] * 256; - newdigits[j-1] = (unsigned char)(t % base); - t /= base; - } - } - - /* Find index of leading significant digit */ - for (isig = 0; isig < newlen; isig++) - if (newdigits[isig]) - break; - - nchars = newlen - isig; - - /* Convert to a null-terminated string of decimal chars */ - /* up to limit, unless user has specified null or size == 0 */ - if (smax > 0 && s) - { - for (i = 0; i < nchars && i < smax-1; i++) - { - s[i] = digits[newdigits[isig+i]]; - } - s[i] = '\0'; - } - - FREE_BYTES(bytes, nbytes); - FREE_BYTES(newdigits, newlen); - - return nchars; -} - -int mpIsZero(const DIGIT_T a[], size_t ndigits) -{ - size_t i; - - /* if (ndigits == 0) return -1; // deleted [v2.5] */ - - for (i = 0; i < ndigits; i++) /* Start at lsb */ - { - if (a[i] != 0) - return 0; /* False */ - } - - return (!0); /* True */ -} diff --git a/source/big-int-test.h b/source/big-int-test.h @@ -1,127 +0,0 @@ -// -// Created by Cedric Zwahlen on 25.09.23. -// - -#ifndef big_int_test_h -#define big_int_test_h - -#include <stdio.h> - - - - -#include <inttypes.h> - -#include <stdint.h> -#include <stdlib.h> - -#include <string.h> // only used for the convert from hex function -#include <assert.h> - -#include <math.h> - -// MARK: definitions - -typedef uint32_t DIGIT_T; // for gpu might need to be half? is that half? - -typedef uint16_t HALF_DIGIT_T; - -/* Sizes to match */ - - - -// MARK: MACROS - -#define mpDESTROY(b, n) do{if(b)mpSetZero(b,n);}while(0) -#define max(a,b) (((a) > (b)) ? (a) : (b)) - -// only for that string conversion -#define ALLOC_BYTES(b,n) do{assert((n)<=sizeof((b)));zeroise_bytes((b),(n));}while(0) -#define FREE_BYTES(b,n) zeroise_bytes((b),(n)) - - -#define MAX_DIGIT 0xFFFFFFFFUL -#define MAX_HALF_DIGIT 0xFFFFUL /* NB 'L' */ -#define BITS_PER_DIGIT 32 -#define HIBITMASK 0x80000000UL - -#define MAX_FIXED_BIT_LENGTH 8192 -#define MAX_FIXED_DIGITS ((MAX_FIXED_BIT_LENGTH + BITS_PER_DIGIT - 1) / BITS_PER_DIGIT) - -#define MAX_ALLOC_SIZE (MAX_FIXED_DIGITS*BYTES_PER_DIGIT) - -#define BYTES_PER_DIGIT (BITS_PER_DIGIT / 8) - -#define PRIuBIGD PRIu32 -#define PRIxBIGD PRIx32 -#define PRIXBIGD PRIX32 - -/* MACROS TO DO MODULAR SQUARING AND MULTIPLICATION USING PRE-ALLOCATED TEMPS */ -/* Required lengths |y|=|t1|=|t2|=2*n, |m|=n; but final |y|=n */ -/* Square: y = (y * y) mod m */ -#define mpMODSQUARETEMP(y,m,n,t1,t2) do{mpSquare(t1,y,n);mpDivide(t2,y,t1,n*2,m,n);}while(0) -/* Mult: y = (y * x) mod m */ -#define mpMODMULTTEMP(y,x,m,n,t1,t2) do{mpMultiply(t1,x,y,n);mpDivide(t2,y,t1,n*2,m,n);}while(0) - -#define mpNEXTBITMASK(mask, n) do{if(mask==1){mask=HIBITMASK;n--;}else{mask>>=1;}}while(0) - -int mpModulo(DIGIT_T r[], const DIGIT_T u[], size_t udigits, DIGIT_T v[], size_t vdigits); - -int mpModMult(DIGIT_T a[], const DIGIT_T x[], const DIGIT_T y[], DIGIT_T m[], size_t ndigits); - -int mpMultiply(DIGIT_T w[], const DIGIT_T u[], const DIGIT_T v[], size_t ndigits); -DIGIT_T mpAdd(DIGIT_T w[], const DIGIT_T u[], const DIGIT_T v[], size_t ndigits); -int mpDivide(DIGIT_T q[], DIGIT_T r[], const DIGIT_T u[], size_t udigits, DIGIT_T v[], size_t vdigits); -static int QhatTooBig(DIGIT_T qhat, DIGIT_T rhat, DIGIT_T vn2, DIGIT_T ujn2); -static DIGIT_T mpMultSub(DIGIT_T wn, DIGIT_T w[], const DIGIT_T v[], DIGIT_T q, size_t n); -DIGIT_T mpShiftLeft(DIGIT_T a[], const DIGIT_T *b, size_t shift, size_t ndigits); - - -void mpSetDigit(DIGIT_T a[], DIGIT_T d, size_t ndigits); - -int mpCompare(const DIGIT_T a[], const DIGIT_T b[], size_t ndigits); - - -DIGIT_T mpShiftRight(DIGIT_T a[], const DIGIT_T b[], size_t shift, size_t ndigits); -int spMultiply(uint32_t p[2], uint32_t x, uint32_t y); -uint32_t spDivide(uint32_t *pq, uint32_t *pr, const uint32_t u[2], uint32_t v); - -int mpSquare(DIGIT_T w[], const DIGIT_T x[], size_t ndigits); - -size_t mpBitLength(const DIGIT_T d[], size_t ndigits); - -size_t mpConvToOctets(const DIGIT_T a[], size_t ndigits, unsigned char *c, size_t nbytes); - -DIGIT_T mpShortDiv(DIGIT_T q[], const DIGIT_T u[], DIGIT_T v, - size_t ndigits); - -void mpSetEqual(DIGIT_T a[], const DIGIT_T b[], size_t ndigits); - - - -size_t mpSizeof(const DIGIT_T a[], size_t ndigits); - -volatile DIGIT_T mpSetZero(volatile DIGIT_T a[], size_t ndigits); - - -void mpPrintDecimal(const char *prefix, const DIGIT_T *a, size_t ndigits, const char *suffix); - - -size_t mpConvFromOctets(DIGIT_T a[], size_t ndigits, const unsigned char *c, size_t nbytes); -size_t mpConvFromHex(DIGIT_T a[], size_t ndigits, const char *s); - -static size_t uiceil(double x); -volatile uint8_t zeroise_bytes(volatile void *v, size_t n); -void mpFail(char *msg); - -void mpPrintHex(const char *prefix, const DIGIT_T *a, size_t len, const char *suffix); - -int mpModExpO(DIGIT_T yout[], const DIGIT_T x[], const DIGIT_T e[], DIGIT_T m[], size_t ndigits, size_t edigits); - -static size_t conv_to_base(const DIGIT_T a[], size_t ndigits, char *s, size_t smax, int base); - -int mpIsZero(const DIGIT_T a[], size_t ndigits); - -size_t mpConvToHex(const DIGIT_T a[], size_t ndigits, char *s, size_t smax); - -#endif /* big_int_test_h */ diff --git a/source/gmp.c b/source/gmp.c @@ -1,10 +1,3 @@ -// -// gmp.c -// lib-gpu-verify -// -// Created by Cedric Zwahlen on 12.11.2023. -// - #include "gmp.h" @@ -3817,7 +3810,7 @@ void mpz_and (mpz_t r, const mpz_t u, const mpz_t v) { mp_size_t un, vn, rn, i; - mp_ptr up, vp, rp; + mp_ptr up, vp, rp; mp_limb_t ux, vx, rx; mp_limb_t uc, vc, rc; @@ -3849,8 +3842,8 @@ mpz_and (mpz_t r, const mpz_t u, const mpz_t v) rp = MPZ_REALLOC (r, rn + (mp_size_t) rc); - up = u->_mp_d; - vp = v->_mp_d; + up = *(mp_ptr *)u->_mp_d; + vp = *(mp_ptr *)v->_mp_d; i = 0; do @@ -3922,8 +3915,8 @@ mpz_ior (mpz_t r, const mpz_t u, const mpz_t v) rp = MPZ_REALLOC (r, rn + (mp_size_t) rc); - up = u->_mp_d; - vp = v->_mp_d; + up = *(mp_ptr *)u->_mp_d; + vp = *(mp_ptr *)v->_mp_d; i = 0; do @@ -3991,8 +3984,8 @@ mpz_xor (mpz_t r, const mpz_t u, const mpz_t v) rp = MPZ_REALLOC (r, un + (mp_size_t) rc); - up = u->_mp_d; - vp = v->_mp_d; + up = *(mp_ptr *)u->_mp_d; + vp = *(mp_ptr *)v->_mp_d; i = 0; do @@ -4141,7 +4134,7 @@ mpz_scan1 (const mpz_t u, mp_bitcnt_t starting_bit) if (i >= un) return (us >= 0 ? ~(mp_bitcnt_t) 0 : starting_bit); - up = u->_mp_d; + up = *(mp_ptr *)u->_mp_d; ux = 0; limb = up[i]; @@ -4178,7 +4171,7 @@ mpz_scan0 (const mpz_t u, mp_bitcnt_t starting_bit) if (i >= un) return (ux ? starting_bit : ~(mp_bitcnt_t) 0); - up = u->_mp_d; + up = *(mp_ptr *)u->_mp_d; limb = up[i] ^ ux; if (ux == 0) diff --git a/source/gmp.h b/source/gmp.h @@ -1,10 +1,3 @@ -// -// gmp.h -// lib-gpu-verify -// -// Created by Cedric Zwahlen on 12.11.2023. -// - #ifndef gmp_h #define gmp_h diff --git a/source/lib-gpu-verify.c b/source/lib-gpu-verify.c @@ -1,13 +1,17 @@ +// +// lib-gpu-verify.c +// lib-gpu-verify +// +// Created by Cedric Zwahlen on 28.09.2023. +// -#include "big-int-test.h" - #include "rsa-test.h" -#include "opencl-test.h" -#include "montgomery.h" +#include "reference-test.h" + +#include "montgomery-test.h" -#include <time.h> int main(int argc, char** argv) { diff --git a/source/montgomery-orig.cl b/source/montgomery-orig.cl @@ -1,2982 +0,0 @@ -// -// gmp_GPU.c -// lib-gpu-verify -// -// Created by Cedric Zwahlen on 25.11.2023. -// - -#ifndef MINI_GMP_LIMB_TYPE -#define MINI_GMP_LIMB_TYPE long -#endif - - -#define ULONG_MAX_gpu 0xFFFFFFFFUL - -#define GMP_LIMB_BITS (sizeof(mp_limb_t) * CHAR_BIT) - -#define GMP_LIMB_MAX ((mp_limb_t) ~ (mp_limb_t) 0) -#define GMP_LIMB_HIGHBIT ((mp_limb_t) 1 << (GMP_LIMB_BITS - 1)) - -#define GMP_HLIMB_BIT ((mp_limb_t) 1 << (GMP_LIMB_BITS / 2)) -#define GMP_LLIMB_MASK (GMP_HLIMB_BIT - 1) - -#define GMP_ULONG_BITS (sizeof(unsigned long) * CHAR_BIT) -#define GMP_ULONG_HIGHBIT ((unsigned long) 1 << (GMP_ULONG_BITS - 1)) - -#define GMP_ABS(x) ((x) >= 0 ? (x) : -(x)) -#define GMP_NEG_CAST(T,x) (-((T)((x) + 1) - 1)) - -#define GMP_MIN(a, b) ((a) < (b) ? (a) : (b)) -#define GMP_MAX(a, b) ((a) > (b) ? (a) : (b)) - -#define GMP_CMP(a,b) (((a) > (b)) - ((a) < (b))) - -#define GMP_MPN_OVERLAP_P(xp, xsize, yp, ysize) \ - ((xp) + (xsize) > (yp) && (yp) + (ysize) > (xp)) - - -#define gmp_clz(count, x) do { \ - mp_limb_t __clz_x = (x); \ - unsigned __clz_c = 0; \ - int LOCAL_SHIFT_BITS = 8; \ - if (GMP_LIMB_BITS > LOCAL_SHIFT_BITS) \ - for (; \ - (__clz_x & ((mp_limb_t) 0xff << (GMP_LIMB_BITS - 8))) == 0; \ - __clz_c += 8) \ - { __clz_x <<= LOCAL_SHIFT_BITS; } \ - for (; (__clz_x & GMP_LIMB_HIGHBIT) == 0; __clz_c++) \ - __clz_x <<= 1; \ - (count) = __clz_c; \ - } while (0) - -#define gmp_umullo_limb(u, v) \ - ((sizeof(mp_limb_t) >= sizeof(int)) ? (u)*(v) : (unsigned int)(u) * (v)) - -#define gmp_umul_ppmm(w1, w0, u, v) \ - do { \ - int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS; \ - if (sizeof(unsigned int) * CHAR_BIT >= 2 * GMP_LIMB_BITS) \ - { \ - unsigned int __ww = (unsigned int) (u) * (v); \ - w0 = (mp_limb_t) __ww; \ - w1 = (mp_limb_t) (__ww >> LOCAL_GMP_LIMB_BITS); \ - } \ - else if (GMP_ULONG_BITS >= 2 * GMP_LIMB_BITS) \ - { \ - unsigned long int __ww = (unsigned long int) (u) * (v); \ - w0 = (mp_limb_t) __ww; \ - w1 = (mp_limb_t) (__ww >> LOCAL_GMP_LIMB_BITS); \ - } \ - else { \ - mp_limb_t __x0, __x1, __x2, __x3; \ - unsigned __ul, __vl, __uh, __vh; \ - mp_limb_t __u = (u), __v = (v); \ - assert (sizeof (unsigned) * 2 >= sizeof (mp_limb_t)); \ - \ - __ul = __u & GMP_LLIMB_MASK; \ - __uh = __u >> (GMP_LIMB_BITS / 2); \ - __vl = __v & GMP_LLIMB_MASK; \ - __vh = __v >> (GMP_LIMB_BITS / 2); \ - \ - __x0 = (mp_limb_t) __ul * __vl; \ - __x1 = (mp_limb_t) __ul * __vh; \ - __x2 = (mp_limb_t) __uh * __vl; \ - __x3 = (mp_limb_t) __uh * __vh; \ - \ - __x1 += __x0 >> (GMP_LIMB_BITS / 2);/* this can't give carry */ \ - __x1 += __x2; /* but this indeed can */ \ - if (__x1 < __x2) /* did we get it? */ \ - __x3 += GMP_HLIMB_BIT; /* yes, add it in the proper pos. */ \ - \ - (w1) = __x3 + (__x1 >> (GMP_LIMB_BITS / 2)); \ - (w0) = (__x1 << (GMP_LIMB_BITS / 2)) + (__x0 & GMP_LLIMB_MASK); \ - } \ - } while (0) - -#define gmp_assert_nocarry(x) do { \ - mp_limb_t __cy = (x); \ - assert (__cy == 0); \ - (void) (__cy); \ - } while (0) - -#define gmp_add_ssaaaa(sh, sl, ah, al, bh, bl) \ - do { \ - mp_limb_t __x; \ - __x = (al) + (bl); \ - (sh) = (ah) + (bh) + (__x < (al)); \ - (sl) = __x; \ - } while (0) - -#define gmp_sub_ddmmss(sh, sl, ah, al, bh, bl) \ - do { \ - mp_limb_t __x; \ - __x = (al) - (bl); \ - (sh) = (ah) - (bh) - ((al) < (bl)); \ - (sl) = __x; \ - } while (0) - - -#define gmp_udiv_qrnnd_preinv(q, r, nh, nl, d, di) \ - do { \ - mp_limb_t _qh, _ql, _r, _mask; \ - gmp_umul_ppmm (_qh, _ql, (nh), (di)); \ - gmp_add_ssaaaa (_qh, _ql, _qh, _ql, (nh) + 1, (nl)); \ - _r = (nl) - gmp_umullo_limb (_qh, (d)); \ - _mask = -(mp_limb_t) (_r > _ql); /* both > and >= are OK */ \ - _qh += _mask; \ - _r += _mask & (d); \ - if (_r >= (d)) \ - { \ - _r -= (d); \ - _qh++; \ - } \ - \ - (r) = _r; \ - (q) = _qh; \ - } while (0) - -#define gmp_udiv_qr_3by2(q, r1, r0, n2, n1, n0, d1, d0, dinv) \ - do { \ - mp_limb_t _q0, _t1, _t0, _mask; \ - gmp_umul_ppmm ((q), _q0, (n2), (dinv)); \ - gmp_add_ssaaaa ((q), _q0, (q), _q0, (n2), (n1)); \ - \ - /* Compute the two most significant limbs of n - q'd */ \ - (r1) = (n1) - gmp_umullo_limb ((d1), (q)); \ - gmp_sub_ddmmss ((r1), (r0), (r1), (n0), (d1), (d0)); \ - gmp_umul_ppmm (_t1, _t0, (d0), (q)); \ - gmp_sub_ddmmss ((r1), (r0), (r1), (r0), _t1, _t0); \ - (q)++; \ - \ - /* Conditionally adjust q and the remainders */ \ - _mask = - (mp_limb_t) ((r1) >= _q0); \ - (q) += _mask; \ - gmp_add_ssaaaa ((r1), (r0), (r1), (r0), _mask & (d1), _mask & (d0)); \ - if ((r1) >= (d1)) \ - { \ - if ((r1) > (d1) || (r0) >= (d0)) \ - { \ - (q)++; \ - gmp_sub_ddmmss ((r1), (r0), (r1), (r0), (d1), (d0)); \ - } \ - } \ - } while (0) - -#define gmp_ctz(count, x) do { \ - mp_limb_t __ctz_x = (x); \ - unsigned __ctz_c = 0; \ - gmp_clz (__ctz_c, __ctz_x & - __ctz_x); \ - (count) = GMP_LIMB_BITS - 1 - __ctz_c; \ - } while (0) - - -#define MPZ_SRCPTR_SWAP(x, y) \ - do { \ - mpz_srcptr __mpz_srcptr_swap__tmp = (x); \ - (x) = (y); \ - (y) = __mpz_srcptr_swap__tmp; \ - } while (0) - -#define MP_SIZE_T_SWAP(x, y) \ - do { \ - mp_size_t __mp_size_t_swap__tmp = (x); \ - (x) = (y); \ - (y) = __mp_size_t_swap__tmp; \ - } while (0) - -#define MPZ_PTR_SWAP(x, y) \ - do { \ - mpz_ptr __mpz_ptr_swap__tmp = (x); \ - (x) = (y); \ - (y) = __mpz_ptr_swap__tmp; \ - } while (0) - -#define MP_BITCNT_T_SWAP(x,y) \ - do { \ - mp_bitcnt_t __mp_bitcnt_t_swap__tmp = (x); \ - (x) = (y); \ - (y) = __mp_bitcnt_t_swap__tmp; \ - } while (0) - - -#define assert(x){if((x)==0){printf((char __constant *)"assert reached\n");}} - -#define NULL ((void*)0) - -typedef unsigned MINI_GMP_LIMB_TYPE mp_limb_t; -typedef long mp_size_t; -typedef unsigned long mp_bitcnt_t; - -typedef mp_limb_t *mp_ptr; -typedef const mp_limb_t *mp_srcptr; - -typedef struct -{ - int _mp_alloc; /* Number of *limbs* allocated and pointed - to by the _mp_d field. */ - int _mp_size; /* abs(_mp_size) is the number of limbs the - last field points to. If _mp_size is - negative this is a negative number. */ - //mp_limb_t *_mp_d; /* Pointer to the limbs. */ - - mp_limb_t _mp_d[256]; - -} __mpz_struct; - -typedef __mpz_struct mpz_t[1]; - -typedef __mpz_struct *mpz_ptr; - -typedef const __mpz_struct *mpz_srcptr; - -struct gmp_div_inverse -{ - /* Normalization shift count. */ - unsigned shift; - /* Normalized divisor (d0 unused for mpn_div_qr_1) */ - mp_limb_t d1, d0; - /* Inverse, for 2/1 or 3/2. */ - mp_limb_t di; -}; - - -struct mpn_base_info -{ - /* bb is the largest power of the base which fits in one limb, and - exp is the corresponding exponent. */ - unsigned exp; - mp_limb_t bb; -}; - - -enum mpz_div_round_mode { GMP_DIV_FLOOR, GMP_DIV_CEIL, GMP_DIV_TRUNC }; - -void mpz_init (mpz_t r); -void mpn_copyi (mp_ptr d, mp_srcptr s, mp_size_t n); -void mpz_set (mpz_t r, const mpz_t x); -void -mpz_set (mpz_t r, const mpz_t x); -void -mpz_set_ui (mpz_t r, unsigned long int x); -void -mpz_set_si (mpz_t r, signed long int x); -void -mpz_init_set_si (mpz_t r, signed long int x); -void -mpz_init_set (mpz_t r, const mpz_t x); -void -mpz_init2 (mpz_t r, mp_bitcnt_t bits); -void -mpz_init_set_ui (mpz_t r, unsigned long int x); -void -mpz_clear (mpz_t r); -void -gmp_die (const char *msg); - - -mp_size_t mpn_normalized_size (mp_srcptr xp, mp_size_t n); -void -mpz_add_ui (mpz_t r, const mpz_t a, unsigned long b); -void -mpz_ui_sub (mpz_t r, unsigned long a, const mpz_t b); -void -mpz_sub_ui (mpz_t r, const mpz_t a, unsigned long b); -int -mpn_absfits_ulong_p (mp_srcptr up, mp_size_t un); -unsigned long int -mpz_get_ui (const mpz_t u); -int -mpz_cmpabs_ui (const mpz_t u, unsigned long v); -mp_limb_t -mpn_sub_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b); -mp_limb_t -mpn_sub_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n); -mp_limb_t -mpn_sub (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn); -mp_limb_t -mpn_invert_3by2 (mp_limb_t u1, mp_limb_t u0); -int -mpz_div_qr (mpz_t q, mpz_t r, - const mpz_t n, const mpz_t d, enum mpz_div_round_mode mode); -void -mpz_mod (mpz_t r, const mpz_t n, const mpz_t d); -void -mpn_div_qr_1_invert (struct gmp_div_inverse *inv, mp_limb_t d); - -void -mpn_div_qr_2_invert (struct gmp_div_inverse *inv, - mp_limb_t d1, mp_limb_t d0); - -void -mpn_div_qr_invert (struct gmp_div_inverse *inv, - mp_srcptr dp, mp_size_t dn); -int -mpz_cmp_ui (const mpz_t u, unsigned long v); -int -mpn_cmp (mp_srcptr ap, mp_srcptr bp, mp_size_t n); -mp_limb_t -mpn_lshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt); -mp_limb_t -mpn_rshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt); -int -mpz_invert (mpz_t r, const mpz_t u, const mpz_t m); -mp_limb_t -mpn_div_qr_1_preinv (mp_ptr qp, mp_srcptr np, mp_size_t nn, - const struct gmp_div_inverse *inv); -mp_limb_t -mpn_add_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n); -void -mpn_div_qr_2_preinv (mp_ptr qp, mp_ptr np, mp_size_t nn, - const struct gmp_div_inverse *inv); -mp_limb_t -mpn_submul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl); -void -mpn_div_qr_pi1 (mp_ptr qp, - mp_ptr np, mp_size_t nn, mp_limb_t n1, - mp_srcptr dp, mp_size_t dn, - mp_limb_t dinv); -void -mpn_div_qr_preinv (mp_ptr qp, mp_ptr np, mp_size_t nn, - mp_srcptr dp, mp_size_t dn, - const struct gmp_div_inverse *inv); -void -mpz_powm (mpz_t r, const mpz_t b, const mpz_t e, const mpz_t m); -int -mpn_cmp4 (mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn); -mp_size_t -mpz_abs_sub (mpz_t r, const mpz_t a, const mpz_t b); -mp_limb_t -mpn_add_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b); -mp_limb_t -mpn_add (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn); -mp_size_t -mpz_abs_add (mpz_t r, const mpz_t a, const mpz_t b); -void -mpz_sub (mpz_t r, const mpz_t a, const mpz_t b); -mp_limb_t -mpn_addmul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl); -mp_limb_t -mpn_mul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl); -mp_limb_t -mpn_mul (mp_ptr rp, mp_srcptr up, mp_size_t un, mp_srcptr vp, mp_size_t vn); -void -mpz_mul (mpz_t r, const mpz_t u, const mpz_t v); -void -mpn_copyd (mp_ptr d, mp_srcptr s, mp_size_t n); -void -mpn_zero (mp_ptr rp, mp_size_t n); -void -mpz_mul_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t bits); -int -mpn_zero_p(mp_srcptr rp, mp_size_t n); -void -mpz_div_q_2exp (mpz_t q, const mpz_t u, mp_bitcnt_t bit_index, - enum mpz_div_round_mode mode); -void -mpz_tdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt); -int -mpz_cmp (const mpz_t a, const mpz_t b); -void -mpz_add (mpz_t r, const mpz_t a, const mpz_t b); -int -mpz_tstbit (const mpz_t d, mp_bitcnt_t bit_index); -mp_bitcnt_t -mpn_limb_size_in_base_2 (mp_limb_t u); -size_t -mpz_sizeinbase (const mpz_t u, int base); -int -mpz_sgn (const mpz_t u); -mp_bitcnt_t -mpn_common_scan (mp_limb_t limb, mp_size_t i, mp_srcptr up, mp_size_t un, - mp_limb_t ux); -mp_bitcnt_t -mpn_scan1 (mp_srcptr ptr, mp_bitcnt_t bit); -mp_bitcnt_t -mpz_scan1 (mpz_t u, mp_bitcnt_t starting_bit); -mp_bitcnt_t -mpz_make_odd (mpz_t r); -void -mpz_tdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d); -void -mpz_abs_add_bit (mpz_t d, mp_bitcnt_t bit_index); -void -mpz_abs_sub_bit (mpz_t d, mp_bitcnt_t bit_index); -void -mpz_setbit (mpz_t d, mp_bitcnt_t bit_index); -void -mpz_divexact (mpz_t q, const mpz_t n, const mpz_t d); -int -mpz_cmpabs (const mpz_t u, const mpz_t v); -void -mpz_gcdext (mpz_t g, mpz_t s, mpz_t t, const mpz_t u, const mpz_t v); -void -mpz_addmul_ui (mpz_t r, const mpz_t u, unsigned long int v); - -unsigned -mpn_base_power_of_two_p (unsigned b); -void -mpn_get_base_info (struct mpn_base_info *info, mp_limb_t b); -int isspace_gpu(unsigned char c); -int strlen_c(__global char *c); -mp_size_t mpn_set_str_bits (mp_ptr rp, const unsigned char *sp, size_t sn, - unsigned bits); -mp_size_t -mpn_set_str_other (mp_ptr rp, const unsigned char *sp, size_t sn, - mp_limb_t b, const struct mpn_base_info *info); -int -mpz_set_str (mpz_t r, __global char *sp, int base); -int -mpz_init_set_str (mpz_t r, __global char *sp, int base); - -//void mpz_sub (mpz_t r, const mpz_t a, const mpz_t b); -////void mpz_add (mpz_t, const mpz_t, const mpz_t); - -void mpz_abs (mpz_t, const mpz_t); - -void mpz_neg (mpz_t, const mpz_t); -void mpz_swap (mpz_t, mpz_t); -//void mpz_mod (mpz_t, const mpz_t, const mpz_t); -// -////int mpz_sgn (const mpz_t); -// -////void mpz_mul (mpz_t, const mpz_t, const mpz_t); -//void mpz_mul_2exp (mpz_t, const mpz_t, mp_bitcnt_t); -// -//void mpz_gcdext (mpz_t, mpz_t, mpz_t, const mpz_t, const mpz_t); -////void mpz_powm (mpz_t, const mpz_t, const mpz_t, const mpz_t); -// -void mpz_addmul (mpz_t, const mpz_t, const mpz_t); -// -//int mpz_tstbit (const mpz_t, mp_bitcnt_t); -// -//int mpz_cmp_ui (const mpz_t u, unsigned long v); -// -void mpn_div_qr (mp_ptr qp, mp_ptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn); -// -//mp_limb_t mpn_invert_3by2 (mp_limb_t, mp_limb_t); - -void -mpz_set_lg (unsigned long *r, __global unsigned long *x); - -#define mpn_invert_limb(x) mpn_invert_3by2 ((x), 0) - -#define MPZ_REALLOC(z,n) (z)->_mp_d - -void -mpz_init (mpz_t r) -{ - const mp_limb_t dummy_limb = GMP_LIMB_MAX & 0xc1a0; - - r->_mp_alloc = 0; - r->_mp_size = 0; - - //memset(r->_mp_d, 0, 256); - - for (int i = 0; i < 256; i++) { - r->_mp_d[i] = 0; - } - - // r->_mp_d = (mp_ptr) &dummy_limb; -} - -void -mpn_copyi (mp_ptr d, mp_srcptr s, mp_size_t n) -{ - mp_size_t i; - for (i = 0; i < n; i++) - d[i] = s[i]; -} - -void -mpz_set (mpz_t r, const mpz_t x) -{ - /* Allow the NOP r == x */ - if (r != x) - { - mp_size_t n; - mp_ptr rp; - - n = GMP_ABS (x->_mp_size); - rp = MPZ_REALLOC (r, n); - - mpn_copyi (rp, x->_mp_d, n); - r->_mp_size = x->_mp_size; - } -} - -void -mpz_set_lg (unsigned long *r, __global unsigned long *x) -{ - - - - - // event_t wait; - - //wait = async_work_group_strided_copy(r,x, 256 + 2, 4,0); - - - - //wait_group_events(0,&wait); - - r[0] = x[0]; - r[1] = x[1]; - - for (int i = 2; i < 256; i++) { - - r[i] = x[i]; - - } - - //printf((__constant char *)"%i\n",r->_mp_size); - - // memcpy(r->_mp_d,(*(mpz_t *)x)->_mp_d,256); - -} - - -void -mpz_set_ui (mpz_t r, unsigned long int x) -{ - if (x > 0) - { - r->_mp_size = 1; - MPZ_REALLOC (r, 1)[0] = x; - if (GMP_LIMB_BITS < GMP_ULONG_BITS) - { - int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS; - while (x >>= LOCAL_GMP_LIMB_BITS) - { - ++ r->_mp_size; - MPZ_REALLOC (r, r->_mp_size)[r->_mp_size - 1] = x; - } - } - } - else - r->_mp_size = 0; -} - - -void -mpz_neg (mpz_t r, const mpz_t u) -{ - mpz_set (r, u); - r->_mp_size = -r->_mp_size; -} - - -void -mpz_set_si (mpz_t r, signed long int x) -{ - if (x >= 0) - mpz_set_ui (r, x); - else /* (x < 0) */ - if (GMP_LIMB_BITS < GMP_ULONG_BITS) - { - mpz_set_ui (r, GMP_NEG_CAST (unsigned long int, x)); - mpz_neg (r, r); - } - else - { - r->_mp_size = -1; - MPZ_REALLOC (r, 1)[0] = GMP_NEG_CAST (unsigned long int, x); - } -} - -void -mpz_init_set_si (mpz_t r, signed long int x) -{ - mpz_init (r); - mpz_set_si (r, x); -} - - -void -mpz_init_set (mpz_t r, const mpz_t x) -{ - mpz_init (r); - mpz_set (r, x); -} - -void -mpz_init2 (mpz_t r, mp_bitcnt_t bits) -{ - mp_size_t rn; - - bits -= (bits != 0); /* Round down, except if 0 */ - rn = 1 + bits / GMP_LIMB_BITS; - - r->_mp_alloc = rn; - r->_mp_size = 0; - // r->_mp_d = gmp_alloc_limbs (rn); -} - -void -mpz_init_set_ui (mpz_t r, unsigned long int x) -{ - mpz_init (r); - mpz_set_ui (r, x); -} - -void -mpz_clear (mpz_t r) -{ - //if (r->_mp_alloc) - //gmp_free_limbs (r->_mp_d, r->_mp_alloc); -} - - -void -gmp_die (const char *msg) -{ - //fprintf (stderr, "%s\n", msg); - //abort(); -} - -mp_size_t mpn_normalized_size (mp_srcptr xp, mp_size_t n) -{ - while (n > 0 && xp[n-1] == 0) - --n; - return n; -} - -void -mpz_add_ui (mpz_t r, const mpz_t a, unsigned long b) -{ - mpz_t bb; - mpz_init_set_ui (bb, b); - mpz_add (r, a, bb); - mpz_clear (bb); -} - -void -mpz_ui_sub (mpz_t r, unsigned long a, const mpz_t b) -{ - mpz_neg (r, b); - mpz_add_ui (r, r, a); -} - - -void -mpz_sub_ui (mpz_t r, const mpz_t a, unsigned long b) -{ - mpz_ui_sub (r, b, a); - mpz_neg (r, r); -} - -int -mpn_absfits_ulong_p (mp_srcptr up, mp_size_t un) -{ - int ulongsize = GMP_ULONG_BITS / GMP_LIMB_BITS; - mp_limb_t ulongrem = 0; - - if (GMP_ULONG_BITS % GMP_LIMB_BITS != 0) - ulongrem = (mp_limb_t) (ULONG_MAX_gpu >> GMP_LIMB_BITS * ulongsize) + 1; - - return un <= ulongsize || (up[ulongsize] < ulongrem && un == ulongsize + 1); -} - -unsigned long int -mpz_get_ui (const mpz_t u) -{ - if (GMP_LIMB_BITS < GMP_ULONG_BITS) - { - int LOCAL_GMP_LIMB_BITS = GMP_LIMB_BITS; - unsigned long r = 0; - mp_size_t n = GMP_ABS (u->_mp_size); - n = GMP_MIN (n, 1 + (mp_size_t) (GMP_ULONG_BITS - 1) / GMP_LIMB_BITS); - while (--n >= 0) - r = (r << LOCAL_GMP_LIMB_BITS) + u->_mp_d[n]; - return r; - } - - return u->_mp_size == 0 ? 0 : u->_mp_d[0]; -} - -int -mpz_cmpabs_ui (const mpz_t u, unsigned long v) -{ - mp_size_t un = GMP_ABS (u->_mp_size); - - if (! mpn_absfits_ulong_p (u->_mp_d, un)) - return 1; - else - { - unsigned long uu = mpz_get_ui (u); - return GMP_CMP(uu, v); - } -} - -mp_limb_t -mpn_sub_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b) -{ - mp_size_t i; - - assert (n > 0); - - i = 0; - do - { - mp_limb_t a = ap[i]; - /* Carry out */ - mp_limb_t cy = a < b; - rp[i] = a - b; - b = cy; - } - while (++i < n); - - return b; -} - -mp_limb_t -mpn_sub_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) -{ - mp_size_t i; - mp_limb_t cy; - - for (i = 0, cy = 0; i < n; i++) - { - mp_limb_t a, b; - a = ap[i]; b = bp[i]; - b += cy; - cy = (b < cy); - cy += (a < b); - rp[i] = a - b; - } - return cy; -} - -mp_limb_t -mpn_sub (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) -{ - mp_limb_t cy; - - assert (an >= bn); - - cy = mpn_sub_n (rp, ap, bp, bn); - if (an > bn) - cy = mpn_sub_1 (rp + bn, ap + bn, an - bn, cy); - return cy; -} - - -mp_limb_t -mpn_invert_3by2 (mp_limb_t u1, mp_limb_t u0) -{ - mp_limb_t r, m; - - { - mp_limb_t p, ql; - unsigned ul, uh, qh; - - assert (sizeof (unsigned) * 2 >= sizeof (mp_limb_t)); - /* For notation, let b denote the half-limb base, so that B = b^2. - Split u1 = b uh + ul. */ - ul = u1 & GMP_LLIMB_MASK; - uh = u1 >> (GMP_LIMB_BITS / 2); - - /* Approximation of the high half of quotient. Differs from the 2/1 - inverse of the half limb uh, since we have already subtracted - u0. */ - qh = (u1 ^ GMP_LIMB_MAX) / uh; - - /* Adjust to get a half-limb 3/2 inverse, i.e., we want - - qh' = floor( (b^3 - 1) / u) - b = floor ((b^3 - b u - 1) / u - = floor( (b (~u) + b-1) / u), - - and the remainder - - r = b (~u) + b-1 - qh (b uh + ul) - = b (~u - qh uh) + b-1 - qh ul - - Subtraction of qh ul may underflow, which implies adjustments. - But by normalization, 2 u >= B > qh ul, so we need to adjust by - at most 2. - */ - - r = ((~u1 - (mp_limb_t) qh * uh) << (GMP_LIMB_BITS / 2)) | GMP_LLIMB_MASK; - - p = (mp_limb_t) qh * ul; - /* Adjustment steps taken from udiv_qrnnd_c */ - if (r < p) - { - qh--; - r += u1; - if (r >= u1) /* i.e. we didn't get carry when adding to r */ - if (r < p) - { - qh--; - r += u1; - } - } - r -= p; - - /* Low half of the quotient is - - ql = floor ( (b r + b-1) / u1). - - This is a 3/2 division (on half-limbs), for which qh is a - suitable inverse. */ - - p = (r >> (GMP_LIMB_BITS / 2)) * qh + r; - /* Unlike full-limb 3/2, we can add 1 without overflow. For this to - work, it is essential that ql is a full mp_limb_t. */ - ql = (p >> (GMP_LIMB_BITS / 2)) + 1; - - /* By the 3/2 trick, we don't need the high half limb. */ - r = (r << (GMP_LIMB_BITS / 2)) + GMP_LLIMB_MASK - ql * u1; - - if (r >= (GMP_LIMB_MAX & (p << (GMP_LIMB_BITS / 2)))) - { - ql--; - r += u1; - } - m = ((mp_limb_t) qh << (GMP_LIMB_BITS / 2)) + ql; - if (r >= u1) - { - m++; - r -= u1; - } - } - - /* Now m is the 2/1 inverse of u1. If u0 > 0, adjust it to become a - 3/2 inverse. */ - if (u0 > 0) - { - mp_limb_t th, tl; - r = ~r; - r += u0; - if (r < u0) - { - m--; - if (r >= u1) - { - m--; - r -= u1; - } - r -= u1; - } - gmp_umul_ppmm (th, tl, u0, m); - r += th; - if (r < th) - { - m--; - m -= ((r > u1) | ((r == u1) & (tl > u0))); - } - } - - return m; -} - -int -mpz_div_qr (mpz_t q, mpz_t r, - const mpz_t n, const mpz_t d, enum mpz_div_round_mode mode) -{ - mp_size_t ns, ds, nn, dn, qs; - ns = n->_mp_size; - ds = d->_mp_size; - - if (ds == 0) { - - } - //gmp_die("mpz_div_qr: Divide by zero."); - - if (ns == 0) - { - if (q) - q->_mp_size = 0; - if (r) - r->_mp_size = 0; - return 0; - } - - nn = GMP_ABS (ns); - dn = GMP_ABS (ds); - - qs = ds ^ ns; - - if (nn < dn) - { - if (mode == GMP_DIV_CEIL && qs >= 0) - { - /* q = 1, r = n - d */ - if (r) - mpz_sub (r, n, d); - if (q) - mpz_set_ui (q, 1); - } - else if (mode == GMP_DIV_FLOOR && qs < 0) - { - /* q = -1, r = n + d */ - if (r) - mpz_add (r, n, d); - if (q) - mpz_set_si (q, -1); - } - else - { - /* q = 0, r = d */ - if (r) - mpz_set (r, n); - if (q) - q->_mp_size = 0; - } - return 1; - } - else - { - mp_ptr np, qp; - mp_size_t qn, rn; - mpz_t tq, tr; - - mpz_init_set (tr, n); - np = tr->_mp_d; - - qn = nn - dn + 1; - - if (q) - { - mpz_init2 (tq, qn * GMP_LIMB_BITS); - qp = tq->_mp_d; - } - else - qp = NULL; - - mpn_div_qr (qp, np, nn, d->_mp_d, dn); - - if (qp) - { - qn -= (qp[qn-1] == 0); - - tq->_mp_size = qs < 0 ? -qn : qn; - } - rn = mpn_normalized_size (np, dn); - tr->_mp_size = ns < 0 ? - rn : rn; - - if (mode == GMP_DIV_FLOOR && qs < 0 && rn != 0) - { - if (q) - mpz_sub_ui (tq, tq, 1); - if (r) - mpz_add (tr, tr, d); - } - else if (mode == GMP_DIV_CEIL && qs >= 0 && rn != 0) - { - if (q) - mpz_add_ui (tq, tq, 1); - if (r) - mpz_sub (tr, tr, d); - } - - if (q) - { - mpz_swap (tq, q); - mpz_clear (tq); - } - if (r) - mpz_swap (tr, r); - - mpz_clear (tr); - - return rn != 0; - } -} - -void -mpn_div_qr (mp_ptr qp, mp_ptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn) -{ - struct gmp_div_inverse inv; - // mp_ptr tp = NULL; - - mpz_t tp; - - - - assert (dn > 0); - assert (nn >= dn); - - mpn_div_qr_invert (&inv, dp, dn); - if (dn > 2 && inv.shift > 0) - { - //tp = gmp_alloc_limbs (dn); - gmp_assert_nocarry (mpn_lshift (tp->_mp_d, dp, dn, inv.shift)); - dp = tp->_mp_d; - } - mpn_div_qr_preinv (qp, np, nn, dp, dn, &inv); - if (tp) {} - //gmp_free_limbs (tp, dn); -} - -void -mpz_addmul (mpz_t r, const mpz_t u, const mpz_t v) -{ - mpz_t t; - mpz_init (t); - mpz_mul (t, u, v); - mpz_add (r, r, t); - mpz_clear (t); -} - -void -mpz_swap (mpz_t u, mpz_t v) -{ - //MP_SIZE_T_SWAP (u->_mp_alloc, v->_mp_alloc); - //MPN_PTR_SWAP (u->_mp_d, u->_mp_size, v->_mp_d, v->_mp_size); - - mpz_t temp; - mpz_init(temp); - - *temp = *u; - *u = *v; - *v = *temp; - -} - -void -mpz_mod (mpz_t r, const mpz_t n, const mpz_t d) -{ - mpz_div_qr (NULL, r, n, d, d->_mp_size >= 0 ? GMP_DIV_FLOOR : GMP_DIV_CEIL); -} - -void -mpn_div_qr_1_invert (struct gmp_div_inverse *inv, mp_limb_t d) -{ - unsigned shift; - - assert (d > 0); - gmp_clz (shift, d); - inv->shift = shift; - inv->d1 = d << shift; - inv->di = mpn_invert_limb (inv->d1); -} - -void -mpn_div_qr_2_invert (struct gmp_div_inverse *inv, - mp_limb_t d1, mp_limb_t d0) -{ - unsigned shift; - - assert (d1 > 0); - gmp_clz (shift, d1); - inv->shift = shift; - if (shift > 0) - { - d1 = (d1 << shift) | (d0 >> (GMP_LIMB_BITS - shift)); - d0 <<= shift; - } - inv->d1 = d1; - inv->d0 = d0; - inv->di = mpn_invert_3by2 (d1, d0); -} - -void -mpn_div_qr_invert (struct gmp_div_inverse *inv, - mp_srcptr dp, mp_size_t dn) -{ - assert (dn > 0); - - if (dn == 1) - mpn_div_qr_1_invert (inv, dp[0]); - else if (dn == 2) - mpn_div_qr_2_invert (inv, dp[1], dp[0]); - else - { - unsigned shift; - mp_limb_t d1, d0; - - d1 = dp[dn-1]; - d0 = dp[dn-2]; - assert (d1 > 0); - gmp_clz (shift, d1); - inv->shift = shift; - if (shift > 0) - { - d1 = (d1 << shift) | (d0 >> (GMP_LIMB_BITS - shift)); - d0 = (d0 << shift) | (dp[dn-3] >> (GMP_LIMB_BITS - shift)); - } - inv->d1 = d1; - inv->d0 = d0; - inv->di = mpn_invert_3by2 (d1, d0); - } -} - - -int -mpz_cmp_ui (const mpz_t u, unsigned long v) -{ - mp_size_t usize = u->_mp_size; - - if (usize < 0) - return -1; - else - return mpz_cmpabs_ui (u, v); -} - -int -mpn_cmp (mp_srcptr ap, mp_srcptr bp, mp_size_t n) -{ - while (--n >= 0) - { - if (ap[n] != bp[n]) - return ap[n] > bp[n] ? 1 : -1; - } - return 0; -} - -mp_limb_t -mpn_lshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt) -{ - mp_limb_t high_limb, low_limb; - unsigned int tnc; - mp_limb_t retval; - - assert (n >= 1); - assert (cnt >= 1); - assert (cnt < GMP_LIMB_BITS); - - up += n; - rp += n; - - tnc = GMP_LIMB_BITS - cnt; - low_limb = *--up; - retval = low_limb >> tnc; - high_limb = (low_limb << cnt); - - while (--n != 0) - { - low_limb = *--up; - *--rp = high_limb | (low_limb >> tnc); - high_limb = (low_limb << cnt); - } - *--rp = high_limb; - - return retval; -} - -mp_limb_t -mpn_rshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt) -{ - mp_limb_t high_limb, low_limb; - unsigned int tnc; - mp_limb_t retval; - - assert (n >= 1); - assert (cnt >= 1); - assert (cnt < GMP_LIMB_BITS); - - tnc = GMP_LIMB_BITS - cnt; - high_limb = *up++; - retval = (high_limb << tnc); - low_limb = high_limb >> cnt; - - while (--n != 0) - { - high_limb = *up++; - *rp++ = low_limb | (high_limb << tnc); - low_limb = high_limb >> cnt; - } - *rp = low_limb; - - return retval; -} - -int -mpz_invert (mpz_t r, const mpz_t u, const mpz_t m) -{ - mpz_t g, tr; - int invertible; - - if (u->_mp_size == 0 || mpz_cmpabs_ui (m, 1) <= 0) - return 0; - - mpz_init (g); - mpz_init (tr); - - mpz_gcdext (g, tr, NULL, u, m); - invertible = (mpz_cmp_ui (g, 1) == 0); - - if (invertible) - { - if (tr->_mp_size < 0) - { - if (m->_mp_size >= 0) - mpz_add (tr, tr, m); - else - mpz_sub (tr, tr, m); - } - mpz_swap (r, tr); - } - - mpz_clear (g); - mpz_clear (tr); - return invertible; -} - -/* Not matching current public gmp interface, rather corresponding to - the sbpi1_div_* functions. */ -mp_limb_t -mpn_div_qr_1_preinv (mp_ptr qp, mp_srcptr np, mp_size_t nn, - const struct gmp_div_inverse *inv) -{ - mp_limb_t d, di; - mp_limb_t r; - mp_ptr tp = NULL; - mp_size_t tn = 0; - - if (inv->shift > 0) - { - /* Shift, reusing qp area if possible. In-place shift if qp == np. */ - tp = qp; - if (!tp) - { - tn = nn; - - // tp = gmp_alloc_limbs (tn); - } - r = mpn_lshift (tp, np, nn, inv->shift); - np = tp; - } - else - r = 0; - - d = inv->d1; - di = inv->di; - while (--nn >= 0) - { - mp_limb_t q; - - gmp_udiv_qrnnd_preinv (q, r, r, np[nn], d, di); - if (qp) - qp[nn] = q; - } - //if (tn) - //gmp_free_limbs (tp, tn); - - return r >> inv->shift; -} - -mp_limb_t -mpn_add_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) -{ - mp_size_t i; - mp_limb_t cy; - - for (i = 0, cy = 0; i < n; i++) - { - mp_limb_t a, b, r; - a = ap[i]; b = bp[i]; - r = a + cy; - cy = (r < cy); - r += b; - cy += (r < b); - rp[i] = r; - } - return cy; -} - -void -mpn_div_qr_2_preinv (mp_ptr qp, mp_ptr np, mp_size_t nn, - const struct gmp_div_inverse *inv) -{ - unsigned shift; - mp_size_t i; - mp_limb_t d1, d0, di, r1, r0; - - assert (nn >= 2); - shift = inv->shift; - d1 = inv->d1; - d0 = inv->d0; - di = inv->di; - - if (shift > 0) - r1 = mpn_lshift (np, np, nn, shift); - else - r1 = 0; - - r0 = np[nn - 1]; - - i = nn - 2; - do - { - mp_limb_t n0, q; - n0 = np[i]; - gmp_udiv_qr_3by2 (q, r1, r0, r1, r0, n0, d1, d0, di); - - if (qp) - qp[i] = q; - } - while (--i >= 0); - - if (shift > 0) - { - assert ((r0 & (GMP_LIMB_MAX >> (GMP_LIMB_BITS - shift))) == 0); - r0 = (r0 >> shift) | (r1 << (GMP_LIMB_BITS - shift)); - r1 >>= shift; - } - - np[1] = r1; - np[0] = r0; -} - -mp_limb_t -mpn_submul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) -{ - mp_limb_t ul, cl, hpl, lpl, rl; - - assert (n >= 1); - - cl = 0; - do - { - ul = *up++; - gmp_umul_ppmm (hpl, lpl, ul, vl); - - lpl += cl; - cl = (lpl < cl) + hpl; - - rl = *rp; - lpl = rl - lpl; - cl += lpl > rl; - *rp++ = lpl; - } - while (--n != 0); - - return cl; -} - -void -mpn_div_qr_pi1 (mp_ptr qp, - mp_ptr np, mp_size_t nn, mp_limb_t n1, - mp_srcptr dp, mp_size_t dn, - mp_limb_t dinv) -{ - mp_size_t i; - - mp_limb_t d1, d0; - mp_limb_t cy, cy1; - mp_limb_t q; - - assert (dn > 2); - assert (nn >= dn); - - d1 = dp[dn - 1]; - d0 = dp[dn - 2]; - - assert ((d1 & GMP_LIMB_HIGHBIT) != 0); - /* Iteration variable is the index of the q limb. - * - * We divide <n1, np[dn-1+i], np[dn-2+i], np[dn-3+i],..., np[i]> - * by <d1, d0, dp[dn-3], ..., dp[0] > - */ - - i = nn - dn; - do - { - mp_limb_t n0 = np[dn-1+i]; - - if (n1 == d1 && n0 == d0) - { - q = GMP_LIMB_MAX; - mpn_submul_1 (np+i, dp, dn, q); - n1 = np[dn-1+i]; /* update n1, last loop's value will now be invalid */ - } - else - { - gmp_udiv_qr_3by2 (q, n1, n0, n1, n0, np[dn-2+i], d1, d0, dinv); - - cy = mpn_submul_1 (np + i, dp, dn-2, q); - - cy1 = n0 < cy; - n0 = n0 - cy; - cy = n1 < cy1; - n1 = n1 - cy1; - np[dn-2+i] = n0; - - if (cy != 0) - { - n1 += d1 + mpn_add_n (np + i, np + i, dp, dn - 1); - q--; - } - } - - if (qp) - qp[i] = q; - } - while (--i >= 0); - - np[dn - 1] = n1; -} - -void -mpn_div_qr_preinv (mp_ptr qp, mp_ptr np, mp_size_t nn, - mp_srcptr dp, mp_size_t dn, - const struct gmp_div_inverse *inv) -{ - assert (dn > 0); - assert (nn >= dn); - - if (dn == 1) - np[0] = mpn_div_qr_1_preinv (qp, np, nn, inv); - else if (dn == 2) - mpn_div_qr_2_preinv (qp, np, nn, inv); - else - { - mp_limb_t nh; - unsigned shift; - - assert (inv->d1 == dp[dn-1]); - assert (inv->d0 == dp[dn-2]); - assert ((inv->d1 & GMP_LIMB_HIGHBIT) != 0); - - shift = inv->shift; - if (shift > 0) - nh = mpn_lshift (np, np, nn, shift); - else - nh = 0; - - mpn_div_qr_pi1 (qp, np, nn, nh, dp, dn, inv->di); - - if (shift > 0) - gmp_assert_nocarry (mpn_rshift (np, np, dn, shift)); - } -} - -void -mpz_powm (mpz_t r, const mpz_t b, const mpz_t e, const mpz_t m) -{ - mpz_t tr; - mpz_t base; - mp_size_t en, mn; - mp_srcptr mp; - struct gmp_div_inverse minv; - unsigned shift; - //mp_ptr tp = NULL; - mpz_t tp; - - //mpz_init(tp); - - en = GMP_ABS (e->_mp_size); - mn = GMP_ABS (m->_mp_size); - if (mn == 0) {} - //gmp_die ("mpz_powm: Zero modulo."); - - if (en == 0) - { - mpz_set_ui (r, mpz_cmpabs_ui (m, 1)); - return; - } - - mp = m->_mp_d; - mpn_div_qr_invert (&minv, mp, mn); - shift = minv.shift; - - if (shift > 0) - { - /* To avoid shifts, we do all our reductions, except the final - one, using a *normalized* m. */ - minv.shift = 0; - - // tp = gmp_alloc_limbs (mn); - gmp_assert_nocarry (mpn_lshift (tp->_mp_d, mp, mn, shift)); - mp = tp->_mp_d; - } - - mpz_init (base); - - if (e->_mp_size < 0) - { - if (!mpz_invert (base, b, m)) {} - //gmp_die ("mpz_powm: Negative exponent and non-invertible base."); - } - else - { - mp_size_t bn; - mpz_abs (base, b); - - bn = base->_mp_size; - if (bn >= mn) - { - mpn_div_qr_preinv (NULL, base->_mp_d, base->_mp_size, mp, mn, &minv); - bn = mn; - } - - /* We have reduced the absolute value. Now take care of the - sign. Note that we get zero represented non-canonically as - m. */ - if (b->_mp_size < 0) - { - mp_ptr bp = MPZ_REALLOC (base, mn); - gmp_assert_nocarry (mpn_sub (bp, mp, mn, bp, bn)); - bn = mn; - } - base->_mp_size = mpn_normalized_size (base->_mp_d, bn); - } - mpz_init_set_ui (tr, 1); - - while (--en >= 0) - { - mp_limb_t w = e->_mp_d[en]; - mp_limb_t bit; - - bit = GMP_LIMB_HIGHBIT; - do - { - mpz_mul (tr, tr, tr); - if (w & bit) - mpz_mul (tr, tr, base); - if (tr->_mp_size > mn) - { - mpn_div_qr_preinv (NULL, tr->_mp_d, tr->_mp_size, mp, mn, &minv); - tr->_mp_size = mpn_normalized_size (tr->_mp_d, mn); - } - bit >>= 1; - } - while (bit > 0); - } - - /* Final reduction */ - if (tr->_mp_size >= mn) - { - minv.shift = shift; - mpn_div_qr_preinv (NULL, tr->_mp_d, tr->_mp_size, mp, mn, &minv); - tr->_mp_size = mpn_normalized_size (tr->_mp_d, mn); - } - //if (tp) - //gmp_free_limbs (tp, mn); - - mpz_swap (r, tr); - mpz_clear (tr); - mpz_clear (base); -} - -int -mpn_cmp4 (mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) -{ - if (an != bn) - return an < bn ? -1 : 1; - else - return mpn_cmp (ap, bp, an); -} - - -mp_size_t -mpz_abs_sub (mpz_t r, const mpz_t a, const mpz_t b) -{ - mp_size_t an = GMP_ABS (a->_mp_size); - mp_size_t bn = GMP_ABS (b->_mp_size); - int cmp; - mp_ptr rp; - - cmp = mpn_cmp4 (a->_mp_d, an, b->_mp_d, bn); - if (cmp > 0) - { - rp = MPZ_REALLOC (r, an); - gmp_assert_nocarry (mpn_sub (rp, a->_mp_d, an, b->_mp_d, bn)); - return mpn_normalized_size (rp, an); - } - else if (cmp < 0) - { - rp = MPZ_REALLOC (r, bn); - gmp_assert_nocarry (mpn_sub (rp, b->_mp_d, bn, a->_mp_d, an)); - return -mpn_normalized_size (rp, bn); - } - else - return 0; -} - -mp_limb_t -mpn_add_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b) -{ - mp_size_t i; - - assert (n > 0); - i = 0; - do - { - mp_limb_t r = ap[i] + b; - /* Carry out */ - b = (r < b); - rp[i] = r; - } - while (++i < n); - - return b; -} - - -mp_limb_t -mpn_add (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) -{ - mp_limb_t cy; - - assert (an >= bn); - - cy = mpn_add_n (rp, ap, bp, bn); - if (an > bn) - cy = mpn_add_1 (rp + bn, ap + bn, an - bn, cy); - return cy; -} - -mp_size_t -mpz_abs_add (mpz_t r, const mpz_t a, const mpz_t b) -{ - mp_size_t an = GMP_ABS (a->_mp_size); - mp_size_t bn = GMP_ABS (b->_mp_size); - mp_ptr rp; - mp_limb_t cy; - - if (an < bn) - { - MPZ_SRCPTR_SWAP (a, b); - MP_SIZE_T_SWAP (an, bn); - } - - rp = MPZ_REALLOC (r, an + 1); - cy = mpn_add (rp, a->_mp_d, an, b->_mp_d, bn); - - rp[an] = cy; - - return an + cy; -} - -void -mpz_sub (mpz_t r, const mpz_t a, const mpz_t b) -{ - mp_size_t rn; - - if ( (a->_mp_size ^ b->_mp_size) >= 0) - rn = mpz_abs_sub (r, a, b); - else - rn = mpz_abs_add (r, a, b); - - r->_mp_size = a->_mp_size >= 0 ? rn : - rn; -} - -mp_limb_t -mpn_addmul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) -{ - mp_limb_t ul, cl, hpl, lpl, rl; - - assert (n >= 1); - - cl = 0; - do - { - ul = *up++; - gmp_umul_ppmm (hpl, lpl, ul, vl); - - lpl += cl; - cl = (lpl < cl) + hpl; - - rl = *rp; - lpl = rl + lpl; - cl += lpl < rl; - *rp++ = lpl; - } - while (--n != 0); - - return cl; -} - -mp_limb_t -mpn_mul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) -{ - mp_limb_t ul, cl, hpl, lpl; - - assert (n >= 1); - - cl = 0; - do - { - ul = *up++; - gmp_umul_ppmm (hpl, lpl, ul, vl); - - lpl += cl; - cl = (lpl < cl) + hpl; - - *rp++ = lpl; - } - while (--n != 0); - - return cl; -} - - -mp_limb_t -mpn_mul (mp_ptr rp, mp_srcptr up, mp_size_t un, mp_srcptr vp, mp_size_t vn) -{ - assert (un >= vn); - assert (vn >= 1); - assert (!GMP_MPN_OVERLAP_P(rp, un + vn, up, un)); - assert (!GMP_MPN_OVERLAP_P(rp, un + vn, vp, vn)); - - /* We first multiply by the low order limb. This result can be - stored, not added, to rp. We also avoid a loop for zeroing this - way. */ - - rp[un] = mpn_mul_1 (rp, up, un, vp[0]); - - /* Now accumulate the product of up[] and the next higher limb from - vp[]. */ - - while (--vn >= 1) - { - rp += 1, vp += 1; - rp[un] = mpn_addmul_1 (rp, up, un, vp[0]); - } - return rp[un]; -} - - -void -mpz_mul (mpz_t r, const mpz_t u, const mpz_t v) -{ - int sign; - mp_size_t un, vn, rn; - mpz_t t; - mp_ptr tp; - - un = u->_mp_size; - vn = v->_mp_size; - - if (un == 0 || vn == 0) - { - r->_mp_size = 0; - return; - } - - sign = (un ^ vn) < 0; - - un = GMP_ABS (un); - vn = GMP_ABS (vn); - - mpz_init2 (t, (un + vn) * GMP_LIMB_BITS); - - tp = t->_mp_d; - if (un >= vn) - mpn_mul (tp, u->_mp_d, un, v->_mp_d, vn); - else - mpn_mul (tp, v->_mp_d, vn, u->_mp_d, un); - - rn = un + vn; - rn -= tp[rn-1] == 0; - - t->_mp_size = sign ? - rn : rn; - mpz_swap (r, t); - mpz_clear (t); -} - -void -mpn_copyd (mp_ptr d, mp_srcptr s, mp_size_t n) -{ - while (--n >= 0) - d[n] = s[n]; -} - -void -mpn_zero (mp_ptr rp, mp_size_t n) -{ - while (--n >= 0) - rp[n] = 0; -} - - -void -mpz_mul_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t bits) -{ - mp_size_t un, rn; - mp_size_t limbs; - unsigned shift; - mp_ptr rp; - - un = GMP_ABS (u->_mp_size); - if (un == 0) - { - r->_mp_size = 0; - return; - } - - limbs = bits / GMP_LIMB_BITS; - shift = bits % GMP_LIMB_BITS; - - rn = un + limbs + (shift > 0); - rp = MPZ_REALLOC (r, rn); - if (shift > 0) - { - mp_limb_t cy = mpn_lshift (rp + limbs, u->_mp_d, un, shift); - rp[rn-1] = cy; - rn -= (cy == 0); - } - else - mpn_copyd (rp + limbs, u->_mp_d, un); - - mpn_zero (rp, limbs); - - r->_mp_size = (u->_mp_size < 0) ? - rn : rn; -} - -int -mpn_zero_p(mp_srcptr rp, mp_size_t n) -{ - return mpn_normalized_size (rp, n) == 0; -} - - -void -mpz_div_q_2exp (mpz_t q, const mpz_t u, mp_bitcnt_t bit_index, - enum mpz_div_round_mode mode) -{ - mp_size_t un, qn; - mp_size_t limb_cnt; - mp_ptr qp; - int adjust; - - un = u->_mp_size; - if (un == 0) - { - q->_mp_size = 0; - return; - } - limb_cnt = bit_index / GMP_LIMB_BITS; - qn = GMP_ABS (un) - limb_cnt; - bit_index %= GMP_LIMB_BITS; - - if (mode == ((un > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* un != 0 here. */ - /* Note: Below, the final indexing at limb_cnt is valid because at - that point we have qn > 0. */ - adjust = (qn <= 0 - || !mpn_zero_p (u->_mp_d, limb_cnt) - || (u->_mp_d[limb_cnt] - & (((mp_limb_t) 1 << bit_index) - 1))); - else - adjust = 0; - - if (qn <= 0) - qn = 0; - else - { - qp = MPZ_REALLOC (q, qn); - - if (bit_index != 0) - { - mpn_rshift (qp, u->_mp_d + limb_cnt, qn, bit_index); - qn -= qp[qn - 1] == 0; - } - else - { - mpn_copyi (qp, u->_mp_d + limb_cnt, qn); - } - } - - q->_mp_size = qn; - - if (adjust) - mpz_add_ui (q, q, 1); - if (un < 0) - mpz_neg (q, q); -} - -void -mpz_tdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) -{ - mpz_div_q_2exp (r, u, cnt, GMP_DIV_TRUNC); -} - -int -mpz_cmp (const mpz_t a, const mpz_t b) -{ - mp_size_t asize = a->_mp_size; - mp_size_t bsize = b->_mp_size; - - if (asize != bsize) - return (asize < bsize) ? -1 : 1; - else if (asize >= 0) - return mpn_cmp (a->_mp_d, b->_mp_d, asize); - else - return mpn_cmp (b->_mp_d, a->_mp_d, -asize); -} - -void -mpz_add (mpz_t r, const mpz_t a, const mpz_t b) -{ - mp_size_t rn; - - if ( (a->_mp_size ^ b->_mp_size) >= 0) - rn = mpz_abs_add (r, a, b); - else - rn = mpz_abs_sub (r, a, b); - - r->_mp_size = a->_mp_size >= 0 ? rn : - rn; -} - - -int -mpz_tstbit (const mpz_t d, mp_bitcnt_t bit_index) -{ - mp_size_t limb_index; - unsigned shift; - mp_size_t ds; - mp_size_t dn; - mp_limb_t w; - int bit; - - ds = d->_mp_size; - dn = GMP_ABS (ds); - limb_index = bit_index / GMP_LIMB_BITS; - if (limb_index >= dn) - return ds < 0; - - shift = bit_index % GMP_LIMB_BITS; - w = d->_mp_d[limb_index]; - bit = (w >> shift) & 1; - - if (ds < 0) - { - /* d < 0. Check if any of the bits below is set: If so, our bit - must be complemented. */ - if (shift > 0 && (mp_limb_t) (w << (GMP_LIMB_BITS - shift)) > 0) - return bit ^ 1; - while (--limb_index >= 0) - if (d->_mp_d[limb_index] > 0) - return bit ^ 1; - } - return bit; -} - -mp_bitcnt_t -mpn_limb_size_in_base_2 (mp_limb_t u) -{ - unsigned shift; - - assert (u > 0); - gmp_clz (shift, u); - return GMP_LIMB_BITS - shift; -} - -size_t -mpz_sizeinbase (const mpz_t u, int base) -{ - mp_size_t un, tn; - mp_srcptr up; - //mp_ptr tp; - mpz_t tp; - - mp_bitcnt_t bits; - struct gmp_div_inverse bi; - size_t ndigits; - - mpz_init(tp); - - assert (base >= 2); -assert (base <= 62); - - un = GMP_ABS (u->_mp_size); - if (un == 0) - return 1; - - up = u->_mp_d; - - bits = (un - 1) * GMP_LIMB_BITS + mpn_limb_size_in_base_2 (up[un-1]); - switch (base) - { - case 2: - return bits; - case 4: - return (bits + 1) / 2; - case 8: - return (bits + 2) / 3; - case 16: - return (bits + 3) / 4; - case 32: - return (bits + 4) / 5; - /* FIXME: Do something more clever for the common case of base - 10. */ - } - - //tp = gmp_alloc_limbs (un); - - mpn_copyi (tp->_mp_d, up, un); - mpn_div_qr_1_invert (&bi, base); - - tn = un; - ndigits = 0; - do - { - ndigits++; - mpn_div_qr_1_preinv (tp->_mp_d, tp->_mp_d, tn, &bi); - tn -= (tp->_mp_d[tn-1] == 0); - } - while (tn > 0); - - //gmp_free_limbs (tp, un); - return ndigits; -} - -int -mpz_sgn (const mpz_t u) -{ - return GMP_CMP (u->_mp_size, 0); -} - -mp_bitcnt_t -mpn_common_scan (mp_limb_t limb, mp_size_t i, mp_srcptr up, mp_size_t un, - mp_limb_t ux) -{ - unsigned cnt; - - assert (ux == 0 || ux == GMP_LIMB_MAX); - assert (0 <= i && i <= un ); - - while (limb == 0) - { - i++; - if (i == un) - return (ux == 0 ? ~(mp_bitcnt_t) 0 : un * GMP_LIMB_BITS); - limb = ux ^ up[i]; - } - gmp_ctz (cnt, limb); - return (mp_bitcnt_t) i * GMP_LIMB_BITS + cnt; -} - -void -mpz_abs (mpz_t r, const mpz_t u) -{ - mpz_set (r, u); - r->_mp_size = GMP_ABS (r->_mp_size); -} - - -mp_bitcnt_t -mpn_scan1 (mp_srcptr ptr, mp_bitcnt_t bit) -{ - mp_size_t i; - i = bit / GMP_LIMB_BITS; - - return mpn_common_scan ( ptr[i] & (GMP_LIMB_MAX << (bit % GMP_LIMB_BITS)), - i, ptr, i, 0); -} - -mp_bitcnt_t -mpz_scan1 (mpz_t u, mp_bitcnt_t starting_bit) -{ - mp_ptr up; - mp_size_t us, un, i; - mp_limb_t limb, ux; - - us = u->_mp_size; - un = GMP_ABS (us); - i = starting_bit / GMP_LIMB_BITS; - - /* Past the end there's no 1 bits for u>=0, or an immediate 1 bit - for u<0. Notice this test picks up any u==0 too. */ - if (i >= un) - return (us >= 0 ? ~(mp_bitcnt_t) 0 : starting_bit); - - up = u->_mp_d; - ux = 0; - limb = up[i]; - - if (starting_bit != 0) - { - if (us < 0) - { - ux = mpn_zero_p (up, i); - limb = ~ limb + ux; - ux = - (mp_limb_t) (limb >= ux); - } - - /* Mask to 0 all bits before starting_bit, thus ignoring them. */ - limb &= GMP_LIMB_MAX << (starting_bit % GMP_LIMB_BITS); - } - - return mpn_common_scan (limb, i, up, un, ux); -} - - -mp_bitcnt_t -mpz_make_odd (mpz_t r) -{ - mp_bitcnt_t shift; - - assert (r->_mp_size > 0); - /* Count trailing zeros, equivalent to mpn_scan1, because we know that there is a 1 */ - shift = mpn_scan1 (r->_mp_d, 0); - mpz_tdiv_q_2exp (r, r, shift); - - return shift; -} - -void -mpz_tdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d) -{ - mpz_div_qr (q, r, n, d, GMP_DIV_TRUNC); -} - -void -mpz_abs_add_bit (mpz_t d, mp_bitcnt_t bit_index) -{ - mp_size_t dn, limb_index; - mp_limb_t bit; - mp_ptr dp; - - dn = GMP_ABS (d->_mp_size); - - limb_index = bit_index / GMP_LIMB_BITS; - bit = (mp_limb_t) 1 << (bit_index % GMP_LIMB_BITS); - - if (limb_index >= dn) - { - mp_size_t i; - /* The bit should be set outside of the end of the number. - We have to increase the size of the number. */ - dp = MPZ_REALLOC (d, limb_index + 1); - - dp[limb_index] = bit; - for (i = dn; i < limb_index; i++) - dp[i] = 0; - dn = limb_index + 1; - } - else - { - mp_limb_t cy; - - dp = d->_mp_d; - - cy = mpn_add_1 (dp + limb_index, dp + limb_index, dn - limb_index, bit); - if (cy > 0) - { - dp = MPZ_REALLOC (d, dn + 1); - dp[dn++] = cy; - } - } - - d->_mp_size = (d->_mp_size < 0) ? - dn : dn; -} - -void -mpz_abs_sub_bit (mpz_t d, mp_bitcnt_t bit_index) -{ - mp_size_t dn, limb_index; - mp_ptr dp; - mp_limb_t bit; - - dn = GMP_ABS (d->_mp_size); - dp = d->_mp_d; - - limb_index = bit_index / GMP_LIMB_BITS; - bit = (mp_limb_t) 1 << (bit_index % GMP_LIMB_BITS); - - assert (limb_index < dn); - - gmp_assert_nocarry (mpn_sub_1 (dp + limb_index, dp + limb_index, - dn - limb_index, bit)); - dn = mpn_normalized_size (dp, dn); - d->_mp_size = (d->_mp_size < 0) ? - dn : dn; -} - -void -mpz_setbit (mpz_t d, mp_bitcnt_t bit_index) -{ - if (!mpz_tstbit (d, bit_index)) - { - if (d->_mp_size >= 0) - mpz_abs_add_bit (d, bit_index); - else - mpz_abs_sub_bit (d, bit_index); - } -} - -void -mpz_divexact (mpz_t q, const mpz_t n, const mpz_t d) -{ - gmp_assert_nocarry (mpz_div_qr (q, NULL, n, d, GMP_DIV_TRUNC)); -} - -#define mpz_odd_p(z) (((z)->_mp_size != 0) & (int) (z)->_mp_d[0]) -#define mpz_even_p(z) (! mpz_odd_p (z)) - -int -mpz_cmpabs (const mpz_t u, const mpz_t v) -{ - return mpn_cmp4 (u->_mp_d, GMP_ABS (u->_mp_size), - v->_mp_d, GMP_ABS (v->_mp_size)); -} - -void -mpz_gcdext (mpz_t g, mpz_t s, mpz_t t, const mpz_t u, const mpz_t v) -{ - mpz_t tu, tv, s0, s1, t0, t1; - mp_bitcnt_t uz, vz, gz; - mp_bitcnt_t power; - - if (u->_mp_size == 0) - { - /* g = 0 u + sgn(v) v */ - signed long sign = mpz_sgn (v); - mpz_abs (g, v); - if (s) - s->_mp_size = 0; - if (t) - mpz_set_si (t, sign); - return; - } - - if (v->_mp_size == 0) - { - /* g = sgn(u) u + 0 v */ - signed long sign = mpz_sgn (u); - mpz_abs (g, u); - if (s) - mpz_set_si (s, sign); - if (t) - t->_mp_size = 0; - return; - } - - mpz_init (tu); - mpz_init (tv); - mpz_init (s0); - mpz_init (s1); - mpz_init (t0); - mpz_init (t1); - - mpz_abs (tu, u); - uz = mpz_make_odd (tu); - mpz_abs (tv, v); - vz = mpz_make_odd (tv); -gz = GMP_MIN (uz, vz); - - uz -= gz; - vz -= gz; - - /* Cofactors corresponding to odd gcd. gz handled later. */ - if (tu->_mp_size < tv->_mp_size) - { - mpz_swap (tu, tv); - MPZ_SRCPTR_SWAP (u, v); - MPZ_PTR_SWAP (s, t); - MP_BITCNT_T_SWAP (uz, vz); - } - - /* Maintain - * - * u = t0 tu + t1 tv - * v = s0 tu + s1 tv - * - * where u and v denote the inputs with common factors of two - * eliminated, and det (s0, t0; s1, t1) = 2^p. Then - * - * 2^p tu = s1 u - t1 v - * 2^p tv = -s0 u + t0 v - */ - - /* After initial division, tu = q tv + tu', we have - * - * u = 2^uz (tu' + q tv) - * v = 2^vz tv - * - * or - * - * t0 = 2^uz, t1 = 2^uz q - * s0 = 0, s1 = 2^vz - */ - - mpz_tdiv_qr (t1, tu, tu, tv); - mpz_mul_2exp (t1, t1, uz); - - mpz_setbit (s1, vz); - power = uz + vz; - - if (tu->_mp_size > 0) - { - mp_bitcnt_t shift; - shift = mpz_make_odd (tu); - mpz_setbit (t0, uz + shift); - power += shift; - - for (;;) - { - int c; - c = mpz_cmp (tu, tv); - if (c == 0) - break; - - if (c < 0) - { - /* tv = tv' + tu - * - * u = t0 tu + t1 (tv' + tu) = (t0 + t1) tu + t1 tv' - * v = s0 tu + s1 (tv' + tu) = (s0 + s1) tu + s1 tv' */ - - mpz_sub (tv, tv, tu); - mpz_add (t0, t0, t1); - mpz_add (s0, s0, s1); - - shift = mpz_make_odd (tv); - mpz_mul_2exp (t1, t1, shift); - mpz_mul_2exp (s1, s1, shift); - } - else - { - mpz_sub (tu, tu, tv); - mpz_add (t1, t0, t1); - mpz_add (s1, s0, s1); - - shift = mpz_make_odd (tu); - mpz_mul_2exp (t0, t0, shift); - mpz_mul_2exp (s0, s0, shift); - } - power += shift; - } - } - else - mpz_setbit (t0, uz); - - /* Now tv = odd part of gcd, and -s0 and t0 are corresponding - cofactors. */ - - mpz_mul_2exp (tv, tv, gz); - mpz_neg (s0, s0); - - /* 2^p g = s0 u + t0 v. Eliminate one factor of two at a time. To - adjust cofactors, we need u / g and v / g */ - - mpz_divexact (s1, v, tv); - mpz_abs (s1, s1); - mpz_divexact (t1, u, tv); - mpz_abs (t1, t1); - - while (power-- > 0) - { - /* s0 u + t0 v = (s0 - v/g) u - (t0 + u/g) v */ - if (mpz_odd_p (s0) || mpz_odd_p (t0)) - { - mpz_sub (s0, s0, s1); - mpz_add (t0, t0, t1); - } - //assert (mpz_even_p (t0) && mpz_even_p (s0)); - mpz_tdiv_q_2exp (s0, s0, 1); - mpz_tdiv_q_2exp (t0, t0, 1); - } - - /* Arrange so that |s| < |u| / 2g */ - mpz_add (s1, s0, s1); - if (mpz_cmpabs (s0, s1) > 0) - { - mpz_swap (s0, s1); - mpz_sub (t0, t0, t1); - } - if (u->_mp_size < 0) - mpz_neg (s0, s0); - if (v->_mp_size < 0) - mpz_neg (t0, t0); - - mpz_swap (g, tv); - if (s) - mpz_swap (s, s0); - if (t) - mpz_swap (t, t0); - - mpz_clear (tu); - mpz_clear (tv); - mpz_clear (s0); - mpz_clear (s1); - mpz_clear (t0); - mpz_clear (t1); -} - - -void -mpz_addmul_ui (mpz_t r, const mpz_t u, unsigned long int v) -{ - mpz_t t; - mpz_init_set_ui (t, v); - mpz_mul (t, u, t); - mpz_add (r, r, t); - mpz_clear (t); -} - - -// STRING CONVERSION - -unsigned -mpn_base_power_of_two_p (unsigned b) -{ - switch (b) - { - case 2: return 1; - case 4: return 2; - case 8: return 3; - case 16: return 4; - case 32: return 5; - case 64: return 6; - case 128: return 7; - case 256: return 8; - default: return 0; - } -} - - - -void -mpn_get_base_info (struct mpn_base_info *info, mp_limb_t b) -{ - mp_limb_t m; - mp_limb_t p; - unsigned exp; - - m = GMP_LIMB_MAX / b; - for (exp = 1, p = b; p <= m; exp++) - p *= b; - - info->exp = exp; - info->bb = p; -} - -int isspace_gpu(unsigned char c) { - if (c == '\n' || c == ' ' || c == '\t' || c == '\r' || c == '\f' || c == '\v') - return 1; - return 0; -} - -int strlen_c(__global char *c) { - - // rather naive implementation – we assume a string is terminated, and is not 0 characters long. - - int i = 0; - while (1) { - if (c[i] == '\0') - return i; - i++; - } - return i; -} - -mp_size_t -mpn_set_str_bits (mp_ptr rp, const unsigned char *sp, size_t sn, - unsigned bits) -{ - mp_size_t rn; - mp_limb_t limb; - unsigned shift; - - for (limb = 0, rn = 0, shift = 0; sn-- > 0; ) - { - limb |= (mp_limb_t) sp[sn] << shift; - shift += bits; - if (shift >= GMP_LIMB_BITS) - { - shift -= GMP_LIMB_BITS; - rp[rn++] = limb; - /* Next line is correct also if shift == 0, - bits == 8, and mp_limb_t == unsigned char. */ - limb = (unsigned int) sp[sn] >> (bits - shift); - } - } - if (limb != 0) - rp[rn++] = limb; - else - rn = mpn_normalized_size (rp, rn); - return rn; -} - -mp_size_t -mpn_set_str_other (mp_ptr rp, const unsigned char *sp, size_t sn, - mp_limb_t b, const struct mpn_base_info *info) -{ - mp_size_t rn; - mp_limb_t w; - unsigned k; - size_t j; - - assert (sn > 0); - - k = 1 + (sn - 1) % info->exp; - - j = 0; - w = sp[j++]; - while (--k != 0) - w = w * b + sp[j++]; - - rp[0] = w; - - for (rn = 1; j < sn;) - { - mp_limb_t cy; - - w = sp[j++]; - for (k = 1; k < info->exp; k++) - w = w * b + sp[j++]; - - cy = mpn_mul_1 (rp, rp, rn, info->bb); - cy += mpn_add_1 (rp, rp, rn, w); - if (cy > 0) - rp[rn++] = cy; - } - assert (j == sn); - - return rn; -} - - -int -mpz_set_str (mpz_t r, __global char *sp, int base) -{ - unsigned bits, value_of_a; - mp_size_t rn, alloc; - mp_ptr rp; - size_t dn, sn; - int sign; - unsigned char dp[256]; - - assert (base == 0 || (base >= 2 && base <= 62)); - - while (isspace_gpu( (unsigned char) *sp)) - sp++; - - sign = (*sp == '-'); - sp += sign; - - if (base == 0) - { - if (sp[0] == '0') - { - if (sp[1] == 'x' || sp[1] == 'X') - { - base = 16; - sp += 2; - } - else if (sp[1] == 'b' || sp[1] == 'B') - { - base = 2; - sp += 2; - } - else - base = 8; - } - else - base = 10; - } - - if (!*sp) - { - r->_mp_size = 0; - return -1; - } - sn = strlen_c(sp); - //dp = (unsigned char *) gmp_alloc (sn); - - - value_of_a = (base > 36) ? 36 : 10; - for (dn = 0; *sp; sp++) - { - unsigned digit; - - if (isspace_gpu ((unsigned char) *sp)) - continue; - else if (*sp >= '0' && *sp <= '9') - digit = *sp - '0'; - else if (*sp >= 'a' && *sp <= 'z') - digit = *sp - 'a' + value_of_a; - else if (*sp >= 'A' && *sp <= 'Z') - digit = *sp - 'A' + 10; - else - digit = base; /* fail */ - - if (digit >= (unsigned) base) - { - //gmp_free (dp, sn); - r->_mp_size = 0; - return -1; - } - - dp[dn++] = digit; - } - - if (!dn) - { - //gmp_free (dp, sn); - r->_mp_size = 0; - return -1; - } - bits = mpn_base_power_of_two_p (base); - - if (bits > 0) - { - alloc = (dn * bits + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; - rp = MPZ_REALLOC (r, alloc); - rn = mpn_set_str_bits (rp, dp, dn, bits); - } - else - { - struct mpn_base_info info; - mpn_get_base_info (&info, base); - alloc = (dn + info.exp - 1) / info.exp; - rp = MPZ_REALLOC (r, alloc); - rn = mpn_set_str_other (rp, dp, dn, base, &info); - /* Normalization, needed for all-zero input. */ - assert (rn > 0); - rn -= rp[rn-1] == 0; - } - assert (rn <= alloc); - //gmp_free (dp, sn); - - r->_mp_size = sign ? - rn : rn; - - return 0; -} - - - -int -mpz_init_set_str (mpz_t r, __global char *sp, int base) -{ - mpz_init (r); - return mpz_set_str (r, sp, base); -} - - -// Montgomery multiplication - -void mont_prepare(mpz_t b, mpz_t e, mpz_t m, - mpz_t r, mpz_t r_1, - mpz_t ni, mpz_t M, mpz_t x - ); - -void mont_product(mpz_t ret, - const mpz_t a, const mpz_t b, - const mpz_t r, const mpz_t r_1, - const mpz_t n, const mpz_t ni - ); - -void mont_modexp(mpz_t ret, - mpz_t a, mpz_t e, - const mpz_t M, - const mpz_t n, const mpz_t ni, - const mpz_t r, const mpz_t r_1 - ); - -void mont_finish(mpz_t ret, - const mpz_t xx, - const mpz_t n, const mpz_t ni, - const mpz_t r, const mpz_t r_1 - ); - -void mont_prepare_even_modulus(mpz_t m, mpz_t q, mpz_t powj); - -void mont_mulmod(mpz_t res, const mpz_t a, const mpz_t b, const mpz_t mod); - - - - -void mont_prepare_even_modulus(mpz_t m, mpz_t q, mpz_t powj) { - - mpz_t two; // powj == 2^j - - mpz_init_set_ui(two, 2); - - mp_bitcnt_t j = mpz_scan1(m, 0); - - mpz_tdiv_q_2exp(q,m,j); - mpz_mul_2exp(powj,two,j - 1); - - mpz_clear(two); - -} - -// CPU -void mont_prepare(mpz_t b, mpz_t e, mpz_t m, - mpz_t r, mpz_t r_1, - mpz_t ni, mpz_t M, mpz_t x) { - - - // r and n (modulus) must be relatively prime (this is a given if n (modulus) is odd) - - // calculate r, which must be larger than the modulo and also a power of 2 - - mpz_t one, oo; // some helper variables - mpz_init_set_si(one,1); - mpz_init_set_si(oo,0); - - //unsigned long len = mpz_sizeinbase(m,2); - - unsigned long len = 2049; - - mpz_mul_2exp(r,one,len); - - mpz_set_si(one, 0); - - - mpz_gcdext(one, r_1, ni, r, m); // set r_1 and ni - - int sgn = mpz_sgn(r_1); - - mpz_abs(r_1, r_1); - mpz_abs(ni, ni); - - if (sgn == -1) { - mpz_sub(ni, r, ni); - mpz_sub(r_1, m, r_1); - } - - if (mpz_cmp_ui(one, 1)) - assert(0); - - mpz_mul(one, r, r_1); - mpz_mul(oo,ni,m); - - mpz_sub(one, one, oo); // oo must be one - - if (mpz_cmp_ui(one, 1)) - assert(0); - - mpz_mul(M, b, r); - mpz_mod(M, M, m); // set M - - mpz_mod(x, r, m); // set x - - mpz_clear(one); - mpz_clear(oo); - -} - -// maybe GPU? -// MARK: n MUST be an odd number -void mont_modexp(mpz_t ret, - mpz_t a, mpz_t e, - const mpz_t M, - const mpz_t n, const mpz_t ni, - const mpz_t r, const mpz_t r_1 - ) { - - mpz_t aa,xx; - - mpz_init_set(aa, M); - mpz_init_set(xx, a); - - int k = (int)mpz_sizeinbase(e,2); - - for (int i = k - 1; i >= 0; i--) { - - mont_product(xx, xx, xx, r, r_1, n, ni); - - if (mpz_tstbit(e, i)) - mont_product(xx, aa, xx, r, r_1, n, ni); - - } - - mpz_set(ret, xx); - - mpz_clear(aa); - mpz_clear(xx); - -} - -void mont_finish(mpz_t ret, - const mpz_t xx, - const mpz_t n, const mpz_t ni, - const mpz_t r, const mpz_t r_1 - ) { - - - mpz_t x,one; - - mpz_init(x); - mpz_init_set_ui(one, 1); - - mont_product(x, xx, one, r, r_1, n, ni); - - mpz_set(ret, x); - - mpz_clear(x); - mpz_clear(one); - -} - - -// GPU -void mont_product(mpz_t ret, - const mpz_t a, const mpz_t b, - const mpz_t r, const mpz_t r_1, - const mpz_t n, const mpz_t ni - ) { - - mpz_t t,m,u; - - mpz_init(t); - mpz_init(m); - mpz_init(u); - - - - mont_mulmod(t, b, a, r); - - mont_mulmod(m, ni, t, r); - - mpz_t ab,mn; - - mpz_init(ab); - mpz_init(mn); - - mpz_mul(ab, a, b); - mpz_mul(mn, m, n); - - mpz_add(ab, ab, mn); - - unsigned long sz = mpz_sizeinbase(r,2) - 1; - mpz_tdiv_q_2exp(u, ab, sz); // this is essentially a bit shift, instead of a division - - if (mpz_cmp(u, n) >= 0) - mpz_sub(u, u, n); - - mpz_set(ret, u); - - mpz_clear(ab); - mpz_clear(mn); - mpz_clear(t); - mpz_clear(m); - mpz_clear(u); - -} - -// not the fastest... but it does not increase the variable sizes -void mont_mulmod(mpz_t res, const mpz_t a, const mpz_t b, const mpz_t mod) { - - mpz_t aa, bb; - mpz_init_set(aa, a); - mpz_init_set(bb,b); - - mpz_mod(aa, aa, mod); // in case a is bigger - - while (mpz_cmp_ui(bb, 0) > 0) { - if (mpz_odd_p(bb)) { - mpz_add(res, res, aa); - mpz_mod(res, res, mod); - } - - mpz_mul_2exp(aa,aa,1); - mpz_mod(aa, aa, mod); - mpz_tdiv_q_2exp(bb, bb, 1); - } -} - -void printmpz(mpz_t n) { - - for (int i = 0; i < n->_mp_size; i++) { - - printf((char __constant *)"%lu", n->_mp_d[i]); - - } - printf((char __constant *)"\n\n"); - -} - -__kernel void montgomery(__global void *signature, __global unsigned long *s_offsets, - __global void *exponent, __global unsigned long *e_offsets, - __global void *modulus, __global unsigned long *m_offsets, - __global void *base, __global unsigned long *b_offsets, - __global unsigned long *valid, - __global unsigned long *pks, - unsigned long n) -{ - - - int index = get_global_id(0); - - int pk = 0; - - while (1) { - if (pks[pk] >= index) - break; - pk++; - } - - mpz_t b,e,m,sig,res; - mpz_init(res); - - mpz_set_lg((unsigned long *)b,&base[b_offsets[index]]); // this is sacrilegious really... - - mpz_set_lg((unsigned long *)sig,&signature[s_offsets[index]]); - - mpz_set_lg((unsigned long *)e,&exponent[e_offsets[pk]]); - mpz_set_lg((unsigned long *)m,&modulus[m_offsets[pk]]); // n - - - mpz_t r, r_1, ni, M, x; - mpz_init(r); - mpz_init(r_1); - mpz_init(ni); - mpz_init(M); - mpz_init(x); - - - mpz_t xx; - mpz_init(xx); - - // the modulus can be assumed to be uneven – always - if (mpz_even_p(m)) { - /* - mpz_t bb, x1, x2, q, powj; - mpz_init(bb); - mpz_init(x1); - mpz_init(x2); - mpz_init(q); - mpz_init(powj); - - mont_prepare_even_modulus(m, q, powj); - - // q is uneven, so we can use regular modexp - // MARK: we can improve the efficiency here by doing simple reductions - - mpz_mod(bb, b, q); // reductions like this - - mont_prepare(bb, e, q, r, r_1, ni, M, x); - mont_modexp(xx, x, e, M, q, ni, r, r_1); - mont_finish(x1, xx, q, ni, r, r_1); - - - // MARK: we can also reduce and really speed this up as well -> binary method? - mpz_powm(x2, b, e, powj); - - mpz_t y, q_1; - mpz_init(y); - mpz_init(q_1); - - mpz_sub(y, x2, x1); - - mpz_invert(q_1, q, powj); - - mpz_mul(y, y, q_1); - mpz_mod(y, y, powj); - - mpz_addmul(x1, q, y); - - mpz_set(res, x1); - - - */ - - printf((__constant char *)"An even modulus is not allowed here."); - - } else { - - // MARK: prepare might not have to run individually on each kernel (prepare might even run on CPU) - mont_prepare(b, e, m, r, r_1, ni, M, x); - - - mont_modexp(xx, x, e, M, m, ni, r, r_1); - mont_finish(res, xx, m, ni, r, r_1); - - } - - if (mpz_cmp(sig,res) != 0) { - - *valid += 1; - - } - - - - -} diff --git a/source/montgomery-test.c b/source/montgomery-test.c @@ -7,114 +7,20 @@ #include "montgomery-test.h" -#include "rsa-test.h" - #include "gmp.h" // has been adapted -// MARK: mont - -void mont_pairs_from_files(char *bases, unsigned long *b_off, - char *exponents, unsigned long *e_off, - char *moduli, unsigned long *m_off, - char *signatures, unsigned long *s_off, - unsigned long *pks, - unsigned long *n) { - - FILE *pkfile; - FILE *msfile; - - pkfile = fopen("lib-gpu-generate/publickey.txt", "r"); - msfile = fopen("lib-gpu-generate/msgsig.txt", "r"); - - if (pkfile == NULL || msfile == NULL) { - printf("Auxiliary files not found."); - abort(); - } - - - int i = 0; - - unsigned long b_offset = 0; - unsigned long e_offset = 0; - unsigned long m_offset = 0; - unsigned long s_offset = 0; - - while (1) { - - char n_buf[2048]; // need to be 0 - char e_buf[2048]; - - memset(n_buf, 0, 2048); - memset(e_buf, 0, 2048); - - unsigned long lastIndex = 0; - - if (fscanf(pkfile, "%s %s %lu", n_buf,e_buf, &lastIndex) == -1) - break; - - unsigned long n_buf_len = strlen(n_buf); - unsigned long e_buf_len = strlen(e_buf); - - memcpy(&moduli[m_offset], n_buf, n_buf_len); - memcpy(&exponents[e_offset], e_buf, e_buf_len); - - m_off[i] = m_offset; - e_off[i] = e_offset; - - m_offset += n_buf_len + 1; - e_offset += e_buf_len + 1; - - pks[i] = lastIndex; - - i++; - - // break; // testing with just one - } - - int j = 0; - - while (1) { - - char m_buf[2048]; // temp storage, large enough - char s_buf[2048]; - - memset(m_buf, 0, 2048); - memset(s_buf, 0, 2048); - - if (fscanf(msfile, "%s %s", m_buf,s_buf) == -1) - break; - - unsigned long m_buf_len = strlen(m_buf); - unsigned long s_buf_len = strlen(s_buf); - - memcpy(&bases[b_offset], m_buf, m_buf_len); - memcpy(&signatures[s_offset], s_buf, s_buf_len); - - b_off[j] = b_offset; - s_off[j] = s_offset; - - b_offset += m_buf_len + 1; - s_offset += s_buf_len + 1; - - j++; - - // break; // testing with just one - - } - - fclose(pkfile); - fclose(msfile); - - *n = j; -} - -void mpz_pairs_from_files(void *bases, unsigned long *b_off, +void mont_pairs_from_files(void *bases, unsigned long *b_off, void *exponents, unsigned long *e_off, void *moduli, unsigned long *m_off, void *signatures, unsigned long *s_off, unsigned long *pks, unsigned long *n) { + char *bases_t = bases; + char *exponents_t = exponents; + char *moduli_t = moduli; + char *signatures_t = signatures; + FILE *pkfile; FILE *msfile; @@ -152,8 +58,8 @@ void mpz_pairs_from_files(void *bases, unsigned long *b_off, mpz_init_set_str(n,n_buf,16); mpz_init_set_str(e,e_buf,16); - memcpy(&moduli[m_offset], n, sizeof(mpz_t)); - memcpy(&exponents[e_offset], e, sizeof(mpz_t)); + memcpy(&moduli_t[m_offset], n, sizeof(mpz_t)); + memcpy(&exponents_t[e_offset], e, sizeof(mpz_t)); m_off[i] = m_offset; e_off[i] = e_offset; @@ -186,8 +92,8 @@ void mpz_pairs_from_files(void *bases, unsigned long *b_off, mpz_init_set_str(m,m_buf,16); mpz_init_set_str(s,s_buf,16); - memcpy(&bases[b_offset], m, sizeof(mpz_t)); - memcpy(&signatures[s_offset], s, sizeof(mpz_t)); + memcpy(&bases_t[b_offset], m, sizeof(mpz_t)); + memcpy(&signatures_t[s_offset], s, sizeof(mpz_t)); b_off[j] = b_offset; s_off[j] = s_offset; @@ -432,10 +338,10 @@ int mont_rsa_tests(void) { memset(pks, 0, pairs * sizeof(unsigned long)); - mpz_pairs_from_files(b, b_off, e, e_off, m, m_off, s, s_off, pks, + mont_pairs_from_files(b, b_off, e, e_off, m, m_off, s, s_off, pks, &pairs); - pairs = 1; // FIXME: forced to 1 to make this fast + printf("VERIFYING %lu SIGNATURES...\n", pairs); unsigned long result = 0; diff --git a/source/montgomery-test.h b/source/montgomery-test.h @@ -8,22 +8,7 @@ #ifndef montgomery_test_h #define montgomery_test_h -#include <stdio.h> - -void mont_pairs_from_files(char *bases, unsigned long *b_off, - char *exponents, unsigned long *e_off, - char *moduli, unsigned long *m_off, - char *signatures, unsigned long *s_off, - unsigned long *pks, - unsigned long *n); - -int mont_verify_pairs_with_opencl(void *bases, unsigned long *b_off, - void *exponents, unsigned long *e_off, - void *moduli, unsigned long *m_off, - void *signatures, unsigned long *s_off, - const unsigned long *pks, - const unsigned long n, - unsigned long *result); +#include "util.h" int mont_rsa_tests(void); diff --git a/source/montgomery.cl b/source/montgomery.cl diff --git a/source/montgomery.h b/source/montgomery.h @@ -15,10 +15,5 @@ #include <assert.h> void mont_go(mpz_t res, char *base, char *exponent, char *modulus, int radix); -/* -void mont_prepare(mpz_t b, mpz_t e, mpz_t m, - mpz_t r, mpz_t r_1, - mpz_t ni, mpz_t M, mpz_t x - ); -*/ + #endif /* montgomery_h */ diff --git a/source/opencl-test.c b/source/opencl-test.c @@ -1,205 +0,0 @@ -// -// opencl-test.c -// hello -// -// Created by Cedric Zwahlen on 28.09.2023. -// - -#include "opencl-test.h" - -#define DATA_SIZE (1024) - -int opencl_tests(void) { - - int err; // error code returned from api calls - - float data[DATA_SIZE]; // original data set given to device - float results[DATA_SIZE]; // results returned from device - unsigned int correct; // number of correct results returned - - size_t global; // global domain size for our calculation - size_t local; // local domain size for our calculation - - cl_device_id device_id; // compute device id - cl_context context; // compute context - cl_command_queue commands; // compute command queue - cl_program program; // compute program - cl_kernel kernel; // compute kernel - - cl_mem input; // device memory used for the input array - cl_mem output; // device memory used for the output array - - - int i = 0; - unsigned int count = DATA_SIZE; - - - // Connect to a compute device - // - int gpu = 1; - err = clGetDeviceIDs(NULL, gpu ? CL_DEVICE_TYPE_GPU : CL_DEVICE_TYPE_CPU, 1, &device_id, NULL); - if (err != CL_SUCCESS) - { - printf("Error: Failed to create a device group!\n"); - return EXIT_FAILURE; - } - - size_t retSize_1 = 0; - clGetDeviceInfo(device_id, CL_DRIVER_VERSION, 0, NULL, &retSize_1); - char driver_version[retSize_1]; - clGetDeviceInfo(device_id, CL_DRIVER_VERSION, retSize_1, &driver_version, &retSize_1); - - printf("driver version: %s\n", driver_version); - - - size_t retSize_2 = sizeof(cl_uint); - cl_uint address_bits = 0; - clGetDeviceInfo(device_id, CL_DEVICE_ADDRESS_BITS, 0, NULL, &retSize_2); - clGetDeviceInfo(device_id, CL_DEVICE_ADDRESS_BITS, retSize_2, &address_bits, &retSize_2); - - printf("device address bits: %i\n", address_bits); - - - // Create a compute context - // - context = clCreateContext(0, 1, &device_id, NULL, NULL, &err); - if (!context) - { - printf("Error: Failed to create a compute context!\n"); - return EXIT_FAILURE; - } - - // Create a command commands - // - commands = clCreateCommandQueue(context, device_id, 0, &err); - if (!commands) - { - printf("Error: Failed to create a command commands!\n"); - return EXIT_FAILURE; - } - - // get the kernel from a file instead of a constant - - - FILE *fp = fopen("./verify.cl", "r"); - fseek(fp, 0L, SEEK_END); - size_t sz = ftell(fp); - rewind(fp); - - char *kernelBuf = malloc(sz); - fread(kernelBuf, sizeof(char), sz, fp); - fclose(fp); - - // Create the compute program from the source buffer - // - //program = clCreateProgramWithSource(context, 1, (const char **) & KernelSource, NULL, &err); - program = clCreateProgramWithSource(context, 1, (const char **) & kernelBuf, &sz, &err); - if (!program) - { - printf("Error: Failed to create compute program!\n"); - return EXIT_FAILURE; - } - - // Build the program executable - // - err = clBuildProgram(program, 0, NULL, NULL, NULL, NULL); - if (err != CL_SUCCESS) - { - size_t len; - char buffer[2048]; - - printf("Error: Failed to build program executable!\n"); - clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, sizeof(buffer), buffer, &len); - printf("%s\n", buffer); - exit(1); - } - - // Create the compute kernel in the program we wish to run - // - kernel = clCreateKernel(program, "single", &err); - if (!kernel || err != CL_SUCCESS) - { - printf("Error: Failed to create compute kernel!\n"); - exit(1); - } - - // Create the input and output arrays in device memory for our calculation - // - input = clCreateBuffer(context, CL_MEM_READ_ONLY, sizeof(float) * count, NULL, NULL); - output = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(float) * count, NULL, NULL); - if (!input || !output) - { - printf("Error: Failed to allocate device memory!\n"); - exit(1); - } - - // Write our data set into the input array in device memory - // - err = clEnqueueWriteBuffer(commands, input, CL_TRUE, 0, sizeof(float) * count, data, 0, NULL, NULL); - if (err != CL_SUCCESS) - { - printf("Error: Failed to write to source array!\n"); - exit(1); - } - - // Set the arguments to our compute kernel - // - err = 0; - err = clSetKernelArg(kernel, 0, sizeof(cl_mem), &input); - err |= clSetKernelArg(kernel, 1, sizeof(cl_mem), &output); - err |= clSetKernelArg(kernel, 2, sizeof(unsigned int), &count); - if (err != CL_SUCCESS) - { - printf("Error: Failed to set kernel arguments! %d\n", err); - exit(1); - } - - // Get the maximum work group size for executing the kernel on the device - // - err = clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(local), &local, NULL); - if (err != CL_SUCCESS) - { - printf("Error: Failed to retrieve kernel work group info! %d\n", err); - exit(1); - } - - // Execute the kernel over the entire range of our 1d input data set - // using the maximum number of work group items for this device - // - global = count; - err = clEnqueueNDRangeKernel(commands, kernel, 1, NULL, &global, &local, 0, NULL, NULL); - if (err) - { - printf("Error: Failed to execute kernel!\n"); - return EXIT_FAILURE; - } - - // Wait for the command commands to get serviced before reading back results - // - clFinish(commands); - - // Read back the results from the device to verify the output - // - err = clEnqueueReadBuffer( commands, output, CL_TRUE, 0, sizeof(float) * count, results, 0, NULL, NULL ); - if (err != CL_SUCCESS) - { - printf("Error: Failed to read output array! %d\n", err); - exit(1); - } - - - - // Print a brief summary detailing the results - // - printf("Computed '%d/%d' correct values!\n", correct, count); - - // Shutdown and cleanup - // - clReleaseMemObject(input); - clReleaseMemObject(output); - clReleaseProgram(program); - clReleaseKernel(kernel); - clReleaseCommandQueue(commands); - clReleaseContext(context); - -} diff --git a/source/opencl-test.h b/source/opencl-test.h @@ -1,30 +0,0 @@ -// -// opencl-test.h -// hello -// -// Created by Cedric Zwahlen on 28.09.2023. -// - -#ifndef opencl_test_h -#define opencl_test_h - -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/stat.h> - -#if __APPLE__ -#include <OpenCL/opencl.h> -#else -#include <CL/opencl.h> -#endif - -#include "big-int-test.h" - -int opencl_tests(void); - -#endif /* opencl_test_h */ diff --git a/source/openssl-test.c b/source/openssl-test.c @@ -1,3465 +0,0 @@ -// -// openssl-test.c -// hello -// -// Created by Cedric Zwahlen on 07.10.2023. -// - -#include "openssl-test.h" - -#include <stdlib.h> -#include <string.h> - -typedef struct ossl_ex_data_global_st -{ - CRYPTO_RWLOCK *ex_data_lock; - EX_CALLBACKS ex_data[CRYPTO_EX_INDEX__COUNT]; -} OSSL_EX_DATA_GLOBAL; - - -struct ossl_lib_ctx_st -{ - CRYPTO_RWLOCK *lock, *rand_crngt_lock; - OSSL_EX_DATA_GLOBAL global; - - void *property_string_data; - void *evp_method_store; - void *provider_store; - void *namemap; - void *property_defns; - void *global_properties; - void *drbg; - void *drbg_nonce; -/*#ifndef FIPS_MODULE - void *provider_conf; - void *bio_core; - void *child_provider; - OSSL_METHOD_STORE *decoder_store; - void *decoder_cache; - OSSL_METHOD_STORE *encoder_store; - OSSL_METHOD_STORE *store_loader_store; - void *self_test_cb; -#endif*/ -#if defined(OPENSSL_THREADS) - void *threads; -#endif - void *rand_crngt; -#ifdef FIPS_MODULE - void *thread_event_handler; - void *fips_prov; -#endif - - unsigned int ischild : 1; -}; - - -/* -typedef void CRYPTO_EX_new (void *parent, void *ptr, CRYPTO_EX_DATA *ad, - int idx, long argl, void *argp); -typedef void CRYPTO_EX_free (void *parent, void *ptr, CRYPTO_EX_DATA *ad, - int idx, long argl, void *argp); -typedef int CRYPTO_EX_dup (CRYPTO_EX_DATA *to, const CRYPTO_EX_DATA *from, - void *from_d, int idx, long argl, void *argp); -__owur int CRYPTO_get_ex_new_index(int class_index, long argl, void *argp, - CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, - CRYPTO_EX_free *free_func); -*/ - -struct crypto_ex_data_st -{ - // STACK_OF(void) *sk; - void *sk; -}; -// DEFINE_STACK_OF(void) - -typedef struct bn_mont_ctx_st BN_MONT_CTX; -struct bn_mont_ctx_st -{ - int ri; /* number of bits in R */ - BIGNUM RR; /* used to convert to montgomery form, - possibly zero-padded */ - BIGNUM N; /* The modulus */ - BIGNUM Ni; /* R*(1/R mod N) - N*Ni = 1 (Ni is only - * stored for bignum algorithm) */ - BN_ULONG n0[2]; /* least significant word(s) of Ni; (type - * changed with 0.9.9, was "BN_ULONG n0;" - * before) */ - int flags; -}; - - -int -BN_get_flags (const BIGNUM *b, int n) -{ - return b->flags & n; -} - - -int -BN_is_zero (const BIGNUM *a) -{ - return a->top == 0; -} - - -int -BN_num_bits_word (BN_ULONG l) -{ - BN_ULONG x, mask; - int bits = (l != 0); - -#if BN_BITS2 > 32 - x = l >> 32; - mask = (0 - x) & BN_MASK2; - mask = (0 - (mask >> (BN_BITS2 - 1))); - bits += 32 & mask; - l ^= (x ^ l) & mask; -#endif - - x = l >> 16; - mask = (0 - x) & BN_MASK2; - mask = (0 - (mask >> (BN_BITS2 - 1))); - bits += 16 & mask; - l ^= (x ^ l) & mask; - - x = l >> 8; - mask = (0 - x) & BN_MASK2; - mask = (0 - (mask >> (BN_BITS2 - 1))); - bits += 8 & mask; - l ^= (x ^ l) & mask; - - x = l >> 4; - mask = (0 - x) & BN_MASK2; - mask = (0 - (mask >> (BN_BITS2 - 1))); - bits += 4 & mask; - l ^= (x ^ l) & mask; - - x = l >> 2; - mask = (0 - x) & BN_MASK2; - mask = (0 - (mask >> (BN_BITS2 - 1))); - bits += 2 & mask; - l ^= (x ^ l) & mask; - - x = l >> 1; - mask = (0 - x) & BN_MASK2; - mask = (0 - (mask >> (BN_BITS2 - 1))); - bits += 1 & mask; - - return bits; -} - - -int -BN_num_bits (const BIGNUM *a) -{ - int i = a->top - 1; - bn_check_top (a); - - if (BN_is_zero (a)) - return 0; - - return ((i * BN_BITS2) + BN_num_bits_word (a->d[i])); -} - - -void -CRYPTO_free (void *str, const char *file, int line) -{/* - INCREMENT(free_count); - if (free_impl != CRYPTO_free) { - free_impl(str, file, line); - return; - } -*/ - free (str); -} - - -void -CRYPTO_clear_free (void *str, size_t num, const char *file, int line) -{ - if (str == NULL) - return; - if (num) - { - } // OPENSSL_cleanse(str, num); - CRYPTO_free (str, file, line); -} - - -static BN_ULONG * -bn_expand_internal (const BIGNUM *b, int words) -{ - BN_ULONG *a = NULL; - - if (words > (INT_MAX / (4 * BN_BITS2))) - { - // ERR_raise(ERR_LIB_BN, BN_R_BIGNUM_TOO_LONG); - assert (0); - return NULL; - } - - if (BN_get_flags (b, BN_FLG_SECURE)) - { - } // a = OPENSSL_secure_zalloc(words * sizeof(*a)); - else - a = OPENSSL_zalloc (words * sizeof(*a)); - memset (a, 0, words * sizeof(*a)); - if (a == NULL) - return NULL; - - assert (b->top <= words); - if (b->top > 0) - memcpy (a, b->d, sizeof(*a) * b->top); - - return a; -} - - -static void -bn_free_d (BIGNUM *a, int clear) -{ - // if (BN_get_flags(a, BN_FLG_SECURE)) - // OPENSSL_secure_clear_free(a->d, a->dmax * sizeof(a->d[0])); - /*else*/ // if (clear != 0) - // OPENSSL_clear_free(a->d, a->dmax * sizeof(a->d[0])); - // else - OPENSSL_free (a->d); -} - - -BIGNUM * -bn_expand2 (BIGNUM *b, int words) -{ - if (words > b->dmax) - { - BN_ULONG *a = bn_expand_internal (b, words); - if (! a) - return NULL; - if (b->d != NULL) - bn_free_d (b, 1); - b->d = a; - b->dmax = words; - } - - return b; -} - - -static ossl_inline BIGNUM * -bn_expand (BIGNUM *a, int bits) -{ - if (bits > (INT_MAX - BN_BITS2 + 1)) - return NULL; - - if (((bits + BN_BITS2 - 1) / BN_BITS2) <= (a)->dmax) - return a; - - return bn_expand2 ((a),(bits + BN_BITS2 - 1) / BN_BITS2); -} - - -int -BN_set_word (BIGNUM *a, BN_ULONG w) -{ - bn_check_top (a); - if (bn_expand (a, (int) sizeof(BN_ULONG) * 8) == NULL) - return 0; - a->neg = 0; - a->d[0] = w; - a->top = (w ? 1 : 0); - a->flags &= ~BN_FLG_FIXED_TOP; - bn_check_top (a); - return 1; -} - - -int -BN_abs_is_word (const BIGNUM *a, const BN_ULONG w) -{ - return ((a->top == 1) && (a->d[0] == w)) || ((w == 0) && (a->top == 0)); -} - - -void -BN_CTX_start (BN_CTX *ctx) -{ - // CTXDBG("ENTER BN_CTX_start()", ctx); - /* If we're already overflowing ... */ - if (ctx->err_stack || ctx->too_many) - ctx->err_stack++; - /* (Try to) get a new frame pointer */ - else if (! BN_STACK_push (&ctx->stack, ctx->used)) - { - // ERR_raise(ERR_LIB_BN, BN_R_TOO_MANY_TEMPORARY_VARIABLES); - assert (0); - ctx->err_stack++; - } - // CTXDBG("LEAVE BN_CTX_start()", ctx); -} - - -BIGNUM * -BN_CTX_get (BN_CTX *ctx) -{ - BIGNUM *ret; - - // CTXDBG("ENTER BN_CTX_get()", ctx); - if (ctx->err_stack || ctx->too_many) - return NULL; - if ((ret = BN_POOL_get (&ctx->pool, ctx->flags)) == NULL) - { - /* - * Setting too_many prevents repeated "get" attempts from cluttering - * the error stack. - */ - ctx->too_many = 1; - // ERR_raise(ERR_LIB_BN, BN_R_TOO_MANY_TEMPORARY_VARIABLES); - assert (0); - return NULL; - } - /* OK, make sure the returned bignum is "zero" */ - BN_zero (ret); - /* clear BN_FLG_CONSTTIME if leaked from previous frames */ - ret->flags &= (~BN_FLG_CONSTTIME); - ctx->used++; - // CTXDBG("LEAVE BN_CTX_get()", ctx); - return ret; -} - - -void -bn_init (BIGNUM *a) -{ - static BIGNUM nilbn; - - *a = nilbn; - bn_check_top (a); -} - - -void -BN_MONT_CTX_init (BN_MONT_CTX *ctx) -{ - ctx->ri = 0; - bn_init (&ctx->RR); - bn_init (&ctx->N); - bn_init (&ctx->Ni); - ctx->n0[0] = ctx->n0[1] = 0; - ctx->flags = 0; -} - - -BN_MONT_CTX * -BN_MONT_CTX_new (void) -{ - BN_MONT_CTX *ret; - - if ((ret = OPENSSL_malloc (sizeof(*ret))) == NULL) - return NULL; - - BN_MONT_CTX_init (ret); - ret->flags = BN_FLG_MALLOCED; - return ret; -} - - -int -BN_ucmp (const BIGNUM *a, const BIGNUM *b) -{ - int i; - BN_ULONG t1, t2, *ap, *bp; - - bn_check_top (a); - bn_check_top (b); - - i = a->top - b->top; - if (i != 0) - return i; - ap = a->d; - bp = b->d; - for (i = a->top - 1; i >= 0; i--) - { - t1 = ap[i]; - t2 = bp[i]; - if (t1 != t2) - return ((t1 > t2) ? 1 : -1); - } - return 0; -} - - -BIGNUM * -bn_wexpand (BIGNUM *a, int words) -{ - return (words <= a->dmax) ? a : bn_expand2 (a, words); -} - - -BN_ULONG -bn_sub_words (BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b, - int n) -{ - BN_ULONG t1, t2; - int c = 0; - - assert (n >= 0); - if (n <= 0) - return (BN_ULONG) 0; - -#ifndef OPENSSL_SMALL_FOOTPRINT - while (n & ~3) - { - t1 = a[0]; - t2 = (t1 - c) & BN_MASK2; - c = (t2 > t1); - t1 = b[0]; - t1 = (t2 - t1) & BN_MASK2; - r[0] = t1; - c += (t1 > t2); - t1 = a[1]; - t2 = (t1 - c) & BN_MASK2; - c = (t2 > t1); - t1 = b[1]; - t1 = (t2 - t1) & BN_MASK2; - r[1] = t1; - c += (t1 > t2); - t1 = a[2]; - t2 = (t1 - c) & BN_MASK2; - c = (t2 > t1); - t1 = b[2]; - t1 = (t2 - t1) & BN_MASK2; - r[2] = t1; - c += (t1 > t2); - t1 = a[3]; - t2 = (t1 - c) & BN_MASK2; - c = (t2 > t1); - t1 = b[3]; - t1 = (t2 - t1) & BN_MASK2; - r[3] = t1; - c += (t1 > t2); - a += 4; - b += 4; - r += 4; - n -= 4; - } -#endif - while (n) - { - t1 = a[0]; - t2 = (t1 - c) & BN_MASK2; - c = (t2 > t1); - t1 = b[0]; - t1 = (t2 - t1) & BN_MASK2; - r[0] = t1; - c += (t1 > t2); - a++; - b++; - r++; - n--; - } - return c; -} - - -int -BN_usub (BIGNUM *r, const BIGNUM *a, const BIGNUM *b) -{ - int max, min, dif; - BN_ULONG t1, t2, borrow, *rp; - const BN_ULONG *ap, *bp; - - bn_check_top (a); - bn_check_top (b); - - max = a->top; - min = b->top; - dif = max - min; - - if (dif < 0) /* hmm... should not be happening */ - // ERR_raise(ERR_LIB_BN, BN_R_ARG2_LT_ARG3); - { - assert (0); - return 0; - } - - if (bn_wexpand (r, max) == NULL) - return 0; - - ap = a->d; - bp = b->d; - rp = r->d; - - borrow = bn_sub_words (rp, ap, bp, min); - ap += min; - rp += min; - - while (dif) - { - dif--; - t1 = *(ap++); - t2 = (t1 - borrow) & BN_MASK2; - *(rp++) = t2; - borrow &= (t1 == 0); - } - - while (max && *--rp == 0) - max--; - - r->top = max; - r->neg = 0; - bn_pollute (r); - - return 1; -} - - -#ifdef BN_LLONG -BN_ULONG -bn_add_words (BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b, - int n) -{ - BN_ULLONG ll = 0; - - assert (n >= 0); - if (n <= 0) - return (BN_ULONG) 0; - -# ifndef OPENSSL_SMALL_FOOTPRINT - while (n & ~3) - { - ll += (BN_ULLONG) a[0] + b[0]; - r[0] = (BN_ULONG) ll & BN_MASK2; - ll >>= BN_BITS2; - ll += (BN_ULLONG) a[1] + b[1]; - r[1] = (BN_ULONG) ll & BN_MASK2; - ll >>= BN_BITS2; - ll += (BN_ULLONG) a[2] + b[2]; - r[2] = (BN_ULONG) ll & BN_MASK2; - ll >>= BN_BITS2; - ll += (BN_ULLONG) a[3] + b[3]; - r[3] = (BN_ULONG) ll & BN_MASK2; - ll >>= BN_BITS2; - a += 4; - b += 4; - r += 4; - n -= 4; - } -# endif - while (n) - { - ll += (BN_ULLONG) a[0] + b[0]; - r[0] = (BN_ULONG) ll & BN_MASK2; - ll >>= BN_BITS2; - a++; - b++; - r++; - n--; - } - return (BN_ULONG) ll; -} - - -#else /* !BN_LLONG */ -BN_ULONG -bn_add_words (BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b, - int n) -{ - BN_ULONG c, l, t; - - assert (n >= 0); - if (n <= 0) - return (BN_ULONG) 0; - - c = 0; -# ifndef OPENSSL_SMALL_FOOTPRINT - while (n & ~3) - { - t = a[0]; - t = (t + c) & BN_MASK2; - c = (t < c); - l = (t + b[0]) & BN_MASK2; - c += (l < t); - r[0] = l; - t = a[1]; - t = (t + c) & BN_MASK2; - c = (t < c); - l = (t + b[1]) & BN_MASK2; - c += (l < t); - r[1] = l; - t = a[2]; - t = (t + c) & BN_MASK2; - c = (t < c); - l = (t + b[2]) & BN_MASK2; - c += (l < t); - r[2] = l; - t = a[3]; - t = (t + c) & BN_MASK2; - c = (t < c); - l = (t + b[3]) & BN_MASK2; - c += (l < t); - r[3] = l; - a += 4; - b += 4; - r += 4; - n -= 4; - } -# endif - while (n) - { - t = a[0]; - t = (t + c) & BN_MASK2; - c = (t < c); - l = (t + b[0]) & BN_MASK2; - c += (l < t); - r[0] = l; - a++; - b++; - r++; - n--; - } - return (BN_ULONG) c; -} - - -#endif /* !BN_LLONG */ - -/* unsigned add of b to a, r can be equal to a or b. */ -int -BN_uadd (BIGNUM *r, const BIGNUM *a, const BIGNUM *b) -{ - int max, min, dif; - const BN_ULONG *ap, *bp; - BN_ULONG *rp, carry, t1, t2; - - bn_check_top (a); - bn_check_top (b); - - if (a->top < b->top) - { - const BIGNUM *tmp; - - tmp = a; - a = b; - b = tmp; - } - max = a->top; - min = b->top; - dif = max - min; - - if (bn_wexpand (r, max + 1) == NULL) - return 0; - - r->top = max; - - ap = a->d; - bp = b->d; - rp = r->d; - - carry = bn_add_words (rp, ap, bp, min); - rp += min; - ap += min; - - while (dif) - { - dif--; - t1 = *(ap++); - t2 = (t1 + carry) & BN_MASK2; - *(rp++) = t2; - carry &= (t2 == 0); - } - *rp = carry; - r->top += carry; - - r->neg = 0; - bn_check_top (r); - return 1; -} - - -int -BN_sub (BIGNUM *r, const BIGNUM *a, const BIGNUM *b) -{ - int ret, r_neg, cmp_res; - - bn_check_top (a); - bn_check_top (b); - - if (a->neg != b->neg) - { - r_neg = a->neg; - ret = BN_uadd (r, a, b); - } - else - { - cmp_res = BN_ucmp (a, b); - if (cmp_res > 0) - { - r_neg = a->neg; - ret = BN_usub (r, a, b); - } - else if (cmp_res < 0) - { - r_neg = ! b->neg; - ret = BN_usub (r, b, a); - } - else - { - r_neg = 0; - BN_zero (r); - ret = 1; - } - } - - r->neg = r_neg; - bn_check_top (r); - return ret; -} - - -int -BN_add (BIGNUM *r, const BIGNUM *a, const BIGNUM *b) -{ - int ret, r_neg, cmp_res; - - bn_check_top (a); - bn_check_top (b); - - if (a->neg == b->neg) - { - r_neg = a->neg; - ret = BN_uadd (r, a, b); - } - else - { - cmp_res = BN_ucmp (a, b); - if (cmp_res > 0) - { - r_neg = a->neg; - ret = BN_usub (r, a, b); - } - else if (cmp_res < 0) - { - r_neg = b->neg; - ret = BN_usub (r, b, a); - } - else - { - r_neg = 0; - BN_zero (r); - ret = 1; - } - } - - r->neg = r_neg; - bn_check_top (r); - return ret; -} - - -int -BN_nnmod (BIGNUM *r, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx) -{ - /* - * like BN_mod, but returns non-negative remainder (i.e., 0 <= r < |d| - * always holds) - */ - - if (! (BN_mod (r, m, d, ctx))) - return 0; - if (! r->neg) - return 1; - /* now -|d| < r < 0, so we have to set r := r + |d| */ - return (d->neg ? BN_sub : BN_add)(r, r, d); -} - - -BIGNUM * -BN_copy (BIGNUM *a, const BIGNUM *b) -{ - int bn_words; - - bn_check_top (b); - - bn_words = BN_get_flags (b, BN_FLG_CONSTTIME) ? b->dmax : b->top; - - if (a == b) - return a; - if (bn_wexpand (a, bn_words) == NULL) - return NULL; - - if (b->top > 0) - memcpy (a->d, b->d, sizeof(b->d[0]) * bn_words); - - a->neg = b->neg; - a->top = b->top; - a->flags |= b->flags & BN_FLG_FIXED_TOP; - bn_check_top (a); - return a; -} - - -static int -bn_left_align (BIGNUM *num) -{ - BN_ULONG *d = num->d, n, m, rmask; - int top = num->top; - int rshift = BN_num_bits_word (d[top - 1]), lshift, i; - - lshift = BN_BITS2 - rshift; - rshift %= BN_BITS2; /* say no to undefined behaviour */ - rmask = (BN_ULONG) 0 - rshift; /* rmask = 0 - (rshift != 0) */ - rmask |= rmask >> 8; - - for (i = 0, m = 0; i < top; i++) - { - n = d[i]; - d[i] = ((n << lshift) | m) & BN_MASK2; - m = (n >> rshift) & rmask; - } - - return lshift; -} - - -/* - * In respect to shift factor the execution time is invariant of - * |n % BN_BITS2|, but not |n / BN_BITS2|. Or in other words pre-condition - * for constant-time-ness is |n < BN_BITS2| or |n / BN_BITS2| being - * non-secret. - */ -int -bn_lshift_fixed_top (BIGNUM *r, const BIGNUM *a, int n) -{ - int i, nw; - unsigned int lb, rb; - BN_ULONG *t, *f; - BN_ULONG l, m, rmask = 0; - - assert (n >= 0); - - bn_check_top (r); - bn_check_top (a); - - nw = n / BN_BITS2; - if (bn_wexpand (r, a->top + nw + 1) == NULL) - return 0; - - if (a->top != 0) - { - lb = (unsigned int) n % BN_BITS2; - rb = BN_BITS2 - lb; - rb %= BN_BITS2; /* say no to undefined behaviour */ - rmask = (BN_ULONG) 0 - rb; /* rmask = 0 - (rb != 0) */ - rmask |= rmask >> 8; - f = &(a->d[0]); - t = &(r->d[nw]); - l = f[a->top - 1]; - t[a->top] = (l >> rb) & rmask; - for (i = a->top - 1; i > 0; i--) - { - m = l << lb; - l = f[i - 1]; - t[i] = (m | ((l >> rb) & rmask)) & BN_MASK2; - } - t[0] = (l << lb) & BN_MASK2; - } - else - { - /* shouldn't happen, but formally required */ - r->d[nw] = 0; - } - if (nw != 0) - memset (r->d, 0, sizeof(*t) * nw); - - r->neg = a->neg; - r->top = a->top + nw + 1; - r->flags |= BN_FLG_FIXED_TOP; - - return 1; -} - - -#if defined(BN_LLONG) && defined(BN_DIV2W) - -BN_ULONG -bn_div_words (BN_ULONG h, BN_ULONG l, BN_ULONG d) -{ - return ((BN_ULONG) (((((BN_ULLONG) h) << BN_BITS2) | l) / (BN_ULLONG) d)); -} - - -#else - -/* Divide h,l by d and return the result. */ -/* I need to test this some more :-( */ -BN_ULONG -bn_div_words (BN_ULONG h, BN_ULONG l, BN_ULONG d) -{ - BN_ULONG dh, dl, q, ret = 0, th, tl, t; - int i, count = 2; - - if (d == 0) - return BN_MASK2; - - i = BN_num_bits_word (d); - assert ((i == BN_BITS2) || (h <= (BN_ULONG) 1 << i)); - - i = BN_BITS2 - i; - if (h >= d) - h -= d; - - if (i) - { - d <<= i; - h = (h << i) | (l >> (BN_BITS2 - i)); - l <<= i; - } - dh = (d & BN_MASK2h) >> BN_BITS4; - dl = (d & BN_MASK2l); - for (;;) - { - if ((h >> BN_BITS4) == dh) - q = BN_MASK2l; - else - q = h / dh; - - th = q * dh; - tl = dl * q; - for (;;) - { - t = h - th; - if ((t & BN_MASK2h) || - ((tl) <= ((t << BN_BITS4) | ((l & BN_MASK2h) >> BN_BITS4)))) - break; - q--; - th -= dh; - tl -= dl; - } - t = (tl >> BN_BITS4); - tl = (tl << BN_BITS4) & BN_MASK2h; - th += t; - - if (l < tl) - th++; - l -= tl; - if (h < th) - { - h += d; - q--; - } - h -= th; - - if (--count == 0) - break; - - ret = q << BN_BITS4; - h = ((h << BN_BITS4) | (l >> BN_BITS4)) & BN_MASK2; - l = (l & BN_MASK2l) << BN_BITS4; - } - ret |= q; - return ret; -} - - -#endif /* !defined(BN_LLONG) && defined(BN_DIV2W) */ - - -BN_ULONG -bn_mul_words (BN_ULONG *rp, const BN_ULONG *ap, int num, BN_ULONG w) -{ - BN_ULONG carry = 0; - BN_ULONG bl, bh; - - assert (num >= 0); - if (num <= 0) - return (BN_ULONG) 0; - - bl = LBITS (w); - bh = HBITS (w); - -# ifndef OPENSSL_SMALL_FOOTPRINT - while (num & ~3) - { - mul (rp[0], ap[0], bl, bh, carry); - mul (rp[1], ap[1], bl, bh, carry); - mul (rp[2], ap[2], bl, bh, carry); - mul (rp[3], ap[3], bl, bh, carry); - ap += 4; - rp += 4; - num -= 4; - } -# endif - while (num) - { - mul (rp[0], ap[0], bl, bh, carry); - ap++; - rp++; - num--; - } - return carry; -} - - -void -BN_CTX_end (BN_CTX *ctx) -{ - if (ctx == NULL) - return; - // CTXDBG("ENTER BN_CTX_end()", ctx); - if (ctx->err_stack) - ctx->err_stack--; - else - { - unsigned int fp = BN_STACK_pop (&ctx->stack); - /* Does this stack frame have anything to release? */ - if (fp < ctx->used) - BN_POOL_release (&ctx->pool, ctx->used - fp); - ctx->used = fp; - /* Unjam "too_many" in case "get" had failed */ - ctx->too_many = 0; - } - // CTXDBG("LEAVE BN_CTX_end()", ctx); -} - - -int -bn_rshift_fixed_top (BIGNUM *r, const BIGNUM *a, int n) -{ - int i, top, nw; - unsigned int lb, rb; - BN_ULONG *t, *f; - BN_ULONG l, m, mask; - - bn_check_top (r); - bn_check_top (a); - - assert (n >= 0); - - nw = n / BN_BITS2; - if (nw >= a->top) - { - /* shouldn't happen, but formally required */ - BN_zero (r); - return 1; - } - - rb = (unsigned int) n % BN_BITS2; - lb = BN_BITS2 - rb; - lb %= BN_BITS2; /* say no to undefined behaviour */ - mask = (BN_ULONG) 0 - lb; /* mask = 0 - (lb != 0) */ - mask |= mask >> 8; - top = a->top - nw; - if (r != a && bn_wexpand (r, top) == NULL) - return 0; - - t = &(r->d[0]); - f = &(a->d[nw]); - l = f[0]; - for (i = 0; i < top - 1; i++) - { - m = f[i + 1]; - t[i] = (l >> rb) | ((m << lb) & mask); - l = m; - } - t[i] = l >> rb; - - r->neg = a->neg; - r->top = top; - r->flags |= BN_FLG_FIXED_TOP; - - return 1; -} - - -BN_ULONG -bn_mul_add_words (BN_ULONG *rp, const BN_ULONG *ap, int num, - BN_ULONG w) -{ - BN_ULONG c = 0; - BN_ULONG bl, bh; - - assert (num >= 0); - if (num <= 0) - return (BN_ULONG) 0; - - bl = LBITS (w); - bh = HBITS (w); - -# ifndef OPENSSL_SMALL_FOOTPRINT - while (num & ~3) - { - mul_add (rp[0], ap[0], bl, bh, c); - mul_add (rp[1], ap[1], bl, bh, c); - mul_add (rp[2], ap[2], bl, bh, c); - mul_add (rp[3], ap[3], bl, bh, c); - ap += 4; - rp += 4; - num -= 4; - } -# endif - while (num) - { - mul_add (rp[0], ap[0], bl, bh, c); - ap++; - rp++; - num--; - } - return c; -} - - -/* - * It's argued that *length* of *significant* part of divisor is public. - * Even if it's private modulus that is. Again, *length* is assumed - * public, but not *value*. Former is likely to be pre-defined by - * algorithm with bit granularity, though below subroutine is invariant - * of limb length. Thanks to this assumption we can require that |divisor| - * may not be zero-padded, yet claim this subroutine "constant-time"(*). - * This is because zero-padded dividend, |num|, is tolerated, so that - * caller can pass dividend of public length(*), but with smaller amount - * of significant limbs. This naturally means that quotient, |dv|, would - * contain correspongly less significant limbs as well, and will be zero- - * padded accordingly. Returned remainder, |rm|, will have same bit length - * as divisor, also zero-padded if needed. These actually leave sign bits - * in ambiguous state. In sense that we try to avoid negative zeros, while - * zero-padded zeros would retain sign. - * - * (*) "Constant-time-ness" has two pre-conditions: - * - * - availability of constant-time bn_div_3_words; - * - dividend is at least as "wide" as divisor, limb-wise, zero-padded - * if so required, which shouldn't be a privacy problem, because - * divisor's length is considered public; - */ -int -bn_div_fixed_top (BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, - const BIGNUM *divisor, BN_CTX *ctx) -{ - int norm_shift, i, j, loop; - BIGNUM *tmp, *snum, *sdiv, *res; - BN_ULONG *resp, *wnum, *wnumtop; - BN_ULONG d0, d1; - int num_n, div_n, num_neg; - - assert (divisor->top > 0 && divisor->d[divisor->top - 1] != 0); - - bn_check_top (num); - bn_check_top (divisor); - bn_check_top (dv); - bn_check_top (rm); - - BN_CTX_start (ctx); - res = (dv == NULL) ? BN_CTX_get (ctx) : dv; - tmp = BN_CTX_get (ctx); - snum = BN_CTX_get (ctx); - sdiv = BN_CTX_get (ctx); - if (sdiv == NULL) - goto err; - - /* First we normalise the numbers */ - if (! BN_copy (sdiv, divisor)) - goto err; - norm_shift = bn_left_align (sdiv); - sdiv->neg = 0; - /* - * Note that bn_lshift_fixed_top's output is always one limb longer - * than input, even when norm_shift is zero. This means that amount of - * inner loop iterations is invariant of dividend value, and that one - * doesn't need to compare dividend and divisor if they were originally - * of the same bit length. - */ - if (! (bn_lshift_fixed_top (snum, num, norm_shift))) - goto err; - - div_n = sdiv->top; - num_n = snum->top; - - if (num_n <= div_n) - { - /* caller didn't pad dividend -> no constant-time guarantee... */ - if (bn_wexpand (snum, div_n + 1) == NULL) - goto err; - memset (&(snum->d[num_n]), 0, (div_n - num_n + 1) * sizeof(BN_ULONG)); - snum->top = num_n = div_n + 1; - } - - loop = num_n - div_n; - /* - * Lets setup a 'window' into snum This is the part that corresponds to - * the current 'area' being divided - */ - wnum = &(snum->d[loop]); - wnumtop = &(snum->d[num_n - 1]); - - /* Get the top 2 words of sdiv */ - d0 = sdiv->d[div_n - 1]; - d1 = (div_n == 1) ? 0 : sdiv->d[div_n - 2]; - - /* Setup quotient */ - if (! bn_wexpand (res, loop)) - goto err; - num_neg = num->neg; - res->neg = (num_neg ^ divisor->neg); - res->top = loop; - res->flags |= BN_FLG_FIXED_TOP; - resp = &(res->d[loop]); - - /* space for temp */ - if (! bn_wexpand (tmp, (div_n + 1))) - goto err; - - for (i = 0; i < loop; i++, wnumtop--) - { - BN_ULONG q, l0; - /* - * the first part of the loop uses the top two words of snum and sdiv - * to calculate a BN_ULONG q such that | wnum - sdiv * q | < sdiv - */ -# if defined(BN_DIV3W) - q = bn_div_3_words (wnumtop, d1, d0); -# else - BN_ULONG n0, n1, rem = 0; - - n0 = wnumtop[0]; - n1 = wnumtop[-1]; - if (n0 == d0) - q = BN_MASK2; - else /* n0 < d0 */ - { - BN_ULONG n2 = (wnumtop == wnum) ? 0 : wnumtop[-2]; -# ifdef BN_LLONG - BN_ULLONG t2; - -# if defined(BN_LLONG) && defined(BN_DIV2W) && ! defined(bn_div_words) - q = (BN_ULONG) (((((BN_ULLONG) n0) << BN_BITS2) | n1) / d0); -# else - q = bn_div_words (n0, n1, d0); -# endif - -# ifndef REMAINDER_IS_ALREADY_CALCULATED - /* - * rem doesn't have to be BN_ULLONG. The least we - * know it's less that d0, isn't it? - */ - rem = (n1 - q * d0) & BN_MASK2; -# endif - t2 = (BN_ULLONG) d1 * q; - - for (;;) - { - if (t2 <= ((((BN_ULLONG) rem) << BN_BITS2) | n2)) - break; - q--; - rem += d0; - if (rem < d0) - break; /* don't let rem overflow */ - t2 -= d1; - } -# else /* !BN_LLONG */ - BN_ULONG t2l, t2h; - - q = bn_div_words (n0, n1, d0); -# ifndef REMAINDER_IS_ALREADY_CALCULATED - rem = (n1 - q * d0) & BN_MASK2; -# endif - -# if defined(BN_UMULT_LOHI) - BN_UMULT_LOHI (t2l, t2h, d1, q); -# elif defined(BN_UMULT_HIGH) - t2l = d1 * q; - t2h = BN_UMULT_HIGH (d1, q); -# else - { - BN_ULONG ql, qh; - t2l = LBITS (d1); - t2h = HBITS (d1); - ql = LBITS (q); - qh = HBITS (q); - mul64 (t2l, t2h, ql, qh); /* t2=(BN_ULLONG)d1*q; */ - } -# endif - - for (;;) - { - if ((t2h < rem) || ((t2h == rem) && (t2l <= n2))) - break; - q--; - rem += d0; - if (rem < d0) - break; /* don't let rem overflow */ - if (t2l < d1) - t2h--; - t2l -= d1; - } -# endif /* !BN_LLONG */ - } -# endif /* !BN_DIV3W */ - - l0 = bn_mul_words (tmp->d, sdiv->d, div_n, q); - tmp->d[div_n] = l0; - wnum--; - /* - * ignore top values of the bignums just sub the two BN_ULONG arrays - * with bn_sub_words - */ - l0 = bn_sub_words (wnum, wnum, tmp->d, div_n + 1); - q -= l0; - /* - * Note: As we have considered only the leading two BN_ULONGs in - * the calculation of q, sdiv * q might be greater than wnum (but - * then (q-1) * sdiv is less or equal than wnum) - */ - for (l0 = 0 - l0, j = 0; j < div_n; j++) - tmp->d[j] = sdiv->d[j] & l0; - l0 = bn_add_words (wnum, wnum, tmp->d, div_n); - (*wnumtop) += l0; - assert ((*wnumtop) == 0); - - - /* store part of the result */ - *--resp = q; - } - /* snum holds remainder, it's as wide as divisor */ - snum->neg = num_neg; - snum->top = div_n; - snum->flags |= BN_FLG_FIXED_TOP; - - if (rm != NULL && bn_rshift_fixed_top (rm, snum, norm_shift) == 0) - goto err; - - BN_CTX_end (ctx); - return 1; -err: - bn_check_top (rm); - BN_CTX_end (ctx); - return 0; -} - - -void -bn_mul_normal (BN_ULONG *r, BN_ULONG *a, int na, BN_ULONG *b, int nb) -{ - BN_ULONG *rr; - - if (na < nb) - { - int itmp; - BN_ULONG *ltmp; - - itmp = na; - na = nb; - nb = itmp; - ltmp = a; - a = b; - b = ltmp; - - } - rr = &(r[na]); - if (nb <= 0) - { - (void) bn_mul_words (r, a, na, 0); - return; - } - else - rr[0] = bn_mul_words (r, a, na, b[0]); - - for (;;) - { - if (--nb <= 0) - return; - rr[1] = bn_mul_add_words (&(r[1]), a, na, b[1]); - if (--nb <= 0) - return; - rr[2] = bn_mul_add_words (&(r[2]), a, na, b[2]); - if (--nb <= 0) - return; - rr[3] = bn_mul_add_words (&(r[3]), a, na, b[3]); - if (--nb <= 0) - return; - rr[4] = bn_mul_add_words (&(r[4]), a, na, b[4]); - rr += 4; - r += 4; - b += 4; - } -} - - -int -bn_mul_fixed_top (BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) -{ - int ret = 0; - int top, al, bl; - BIGNUM *rr; -#if defined(BN_MUL_COMBA) || defined(BN_RECURSION) - int i; -#endif -#ifdef BN_RECURSION - BIGNUM *t = NULL; - int j = 0, k; -#endif - - bn_check_top (a); - bn_check_top (b); - bn_check_top (r); - - al = a->top; - bl = b->top; - - if ((al == 0) || (bl == 0)) - { - BN_zero (r); - return 1; - } - top = al + bl; - - BN_CTX_start (ctx); - if ((r == a) || (r == b)) - { - if ((rr = BN_CTX_get (ctx)) == NULL) - goto err; - } - else - rr = r; - -#if defined(BN_MUL_COMBA) || defined(BN_RECURSION) - i = al - bl; -#endif -#ifdef BN_MUL_COMBA - if (i == 0) - { -# if 0 - if (al == 4) - { - if (bn_wexpand (rr, 8) == NULL) - goto err; - rr->top = 8; - bn_mul_comba4 (rr->d, a->d, b->d); - goto end; - } -# endif - if (al == 8) - { - if (bn_wexpand (rr, 16) == NULL) - goto err; - rr->top = 16; - bn_mul_comba8 (rr->d, a->d, b->d); - goto end; - } - } -#endif /* BN_MUL_COMBA */ -#ifdef BN_RECURSION - if ((al >= BN_MULL_SIZE_NORMAL) && (bl >= BN_MULL_SIZE_NORMAL)) - { - if (i >= -1 && i <= 1) - { - /* - * Find out the power of two lower or equal to the longest of the - * two numbers - */ - if (i >= 0) - { - j = BN_num_bits_word ((BN_ULONG) al); - } - if (i == -1) - { - j = BN_num_bits_word ((BN_ULONG) bl); - } - j = 1 << (j - 1); - assert (j <= al || j <= bl); - k = j + j; - t = BN_CTX_get (ctx); - if (t == NULL) - goto err; - if (al > j || bl > j) - { - if (bn_wexpand (t, k * 4) == NULL) - goto err; - if (bn_wexpand (rr, k * 4) == NULL) - goto err; - bn_mul_part_recursive (rr->d, a->d, b->d, - j, al - j, bl - j, t->d); - } - else /* al <= j || bl <= j */ - - { - if (bn_wexpand (t, k * 2) == NULL) - goto err; - if (bn_wexpand (rr, k * 2) == NULL) - goto err; - bn_mul_recursive (rr->d, a->d, b->d, j, al - j, bl - j, t->d); - } - rr->top = top; - goto end; - } - } -#endif /* BN_RECURSION */ - if (bn_wexpand (rr, top) == NULL) - goto err; - rr->top = top; - bn_mul_normal (rr->d, a->d, al, b->d, bl); - -#if defined(BN_MUL_COMBA) || defined(BN_RECURSION) -end: -#endif - rr->neg = a->neg ^ b->neg; - rr->flags |= BN_FLG_FIXED_TOP; - if (r != rr && BN_copy (r, rr) == NULL) - goto err; - - ret = 1; -err: - bn_check_top (r); - BN_CTX_end (ctx); - return ret; -} - - -void -bn_correct_top (BIGNUM *a) -{ - BN_ULONG *ftl; - int tmp_top = a->top; - - if (tmp_top > 0) - { - for (ftl = &(a->d[tmp_top]); tmp_top > 0; tmp_top--) - { - ftl--; - if (*ftl != 0) - break; - } - a->top = tmp_top; - } - if (a->top == 0) - a->neg = 0; - a->flags &= ~BN_FLG_FIXED_TOP; - bn_pollute (a); -} - - -int -BN_div (BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor, - BN_CTX *ctx) -{ - int ret; - - if (BN_is_zero (divisor)) - { - // ERR_raise(ERR_LIB_BN, BN_R_DIV_BY_ZERO); - // return 0; - assert (0); - } - - /* - * Invalid zero-padding would have particularly bad consequences so don't - * just rely on bn_check_top() here (bn_check_top() works only for - * BN_DEBUG builds) - */ - if (divisor->d[divisor->top - 1] == 0) - { - // ERR_raise(ERR_LIB_BN, BN_R_NOT_INITIALIZED); - // return 0; - assert (0); - } - - ret = bn_div_fixed_top (dv, rm, num, divisor, ctx); - - if (ret) - { - if (dv != NULL) - bn_correct_top (dv); - if (rm != NULL) - bn_correct_top (rm); - } - - return ret; -} - - -void -bn_sqr_words (BN_ULONG *r, const BN_ULONG *a, int n) -{ - assert (n >= 0); - if (n <= 0) - return; - -# ifndef OPENSSL_SMALL_FOOTPRINT - while (n & ~3) - { - sqr64 (r[0], r[1], a[0]); - sqr64 (r[2], r[3], a[1]); - sqr64 (r[4], r[5], a[2]); - sqr64 (r[6], r[7], a[3]); - a += 4; - r += 8; - n -= 4; - } -# endif - while (n) - { - sqr64 (r[0], r[1], a[0]); - a++; - r += 2; - n--; - } -} - - -/* tmp must have 2*n words */ -void -bn_sqr_normal (BN_ULONG *r, const BN_ULONG *a, int n, BN_ULONG *tmp) -{ - int i, j, max; - const BN_ULONG *ap; - BN_ULONG *rp; - - max = n * 2; - ap = a; - rp = r; - rp[0] = rp[max - 1] = 0; - rp++; - j = n; - - if (--j > 0) - { - ap++; - rp[j] = bn_mul_words (rp, ap, j, ap[-1]); - rp += 2; - } - - for (i = n - 2; i > 0; i--) - { - j--; - ap++; - rp[j] = bn_mul_add_words (rp, ap, j, ap[-1]); - rp += 2; - } - - bn_add_words (r, r, r, max); - - /* There will not be a carry */ - - bn_sqr_words (tmp, a, n); - - bn_add_words (r, r, tmp, max); -} - - -int -bn_sqr_fixed_top (BIGNUM *r, const BIGNUM *a, BN_CTX *ctx) -{ - int max, al; - int ret = 0; - BIGNUM *tmp, *rr; - - bn_check_top (a); - - al = a->top; - if (al <= 0) - { - r->top = 0; - r->neg = 0; - return 1; - } - - BN_CTX_start (ctx); - rr = (a != r) ? r : BN_CTX_get (ctx); - tmp = BN_CTX_get (ctx); - if (rr == NULL || tmp == NULL) - goto err; - - max = 2 * al; /* Non-zero (from above) */ - if (bn_wexpand (rr, max) == NULL) - goto err; - - if (al == 4) - { -#ifndef BN_SQR_COMBA - BN_ULONG t[8]; - bn_sqr_normal (rr->d, a->d, 4, t); -#else - bn_sqr_comba4 (rr->d, a->d); -#endif - } - else if (al == 8) - { -#ifndef BN_SQR_COMBA - BN_ULONG t[16]; - bn_sqr_normal (rr->d, a->d, 8, t); -#else - bn_sqr_comba8 (rr->d, a->d); -#endif - } - else - { -#if defined(BN_RECURSION) - if (al < BN_SQR_RECURSIVE_SIZE_NORMAL) - { - BN_ULONG t[BN_SQR_RECURSIVE_SIZE_NORMAL * 2]; - bn_sqr_normal (rr->d, a->d, al, t); - } - else - { - int j, k; - - j = BN_num_bits_word ((BN_ULONG) al); - j = 1 << (j - 1); - k = j + j; - if (al == j) - { - if (bn_wexpand (tmp, k * 2) == NULL) - goto err; - bn_sqr_recursive (rr->d, a->d, al, tmp->d); - } - else - { - if (bn_wexpand (tmp, max) == NULL) - goto err; - bn_sqr_normal (rr->d, a->d, al, tmp->d); - } - } -#else - if (bn_wexpand (tmp, max) == NULL) - goto err; - bn_sqr_normal (rr->d, a->d, al, tmp->d); -#endif - } - - rr->neg = 0; - rr->top = max; - rr->flags |= BN_FLG_FIXED_TOP; - if (r != rr && BN_copy (r, rr) == NULL) - goto err; - - ret = 1; -err: - bn_check_top (rr); - bn_check_top (tmp); - BN_CTX_end (ctx); - return ret; -} - - -int -BN_rshift (BIGNUM *r, const BIGNUM *a, int n) -{ - int ret = 0; - - if (n < 0) - { - // ERR_raise(ERR_LIB_BN, BN_R_INVALID_SHIFT); - assert (0); - // return 0; - } - - ret = bn_rshift_fixed_top (r, a, n); - - bn_correct_top (r); - bn_check_top (r); - - return ret; -} - - -int -BN_mul (BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) -{ - int ret = bn_mul_fixed_top (r, a, b, ctx); - - bn_correct_top (r); - bn_check_top (r); - - return ret; -} - - -int -BN_mask_bits (BIGNUM *a, int n) -{ - int b, w; - - bn_check_top (a); - if (n < 0) - return 0; - - w = n / BN_BITS2; - b = n % BN_BITS2; - if (w >= a->top) - return 0; - if (b == 0) - a->top = w; - else - { - a->top = w + 1; - a->d[w] &= ~(BN_MASK2 << b); - } - bn_correct_top (a); - return 1; -} - - -static int -bn_from_montgomery_word (BIGNUM *ret, BIGNUM *r, BN_MONT_CTX *mont) -{ - BIGNUM *n; - BN_ULONG *ap, *np, *rp, n0, v, carry; - int nl, max, i; - unsigned int rtop; - - n = &(mont->N); - nl = n->top; - if (nl == 0) - { - ret->top = 0; - return 1; - } - - max = (2 * nl); /* carry is stored separately */ - if (bn_wexpand (r, max) == NULL) - return 0; - - r->neg ^= n->neg; - np = n->d; - rp = r->d; - - /* clear the top words of T */ - for (rtop = r->top, i = 0; i < max; i++) - { - v = (BN_ULONG) 0 - ((i - rtop) >> (8 * sizeof(rtop) - 1)); - rp[i] &= v; - } - - r->top = max; - r->flags |= BN_FLG_FIXED_TOP; - n0 = mont->n0[0]; - - /* - * Add multiples of |n| to |r| until R = 2^(nl * BN_BITS2) divides it. On - * input, we had |r| < |n| * R, so now |r| < 2 * |n| * R. Note that |r| - * includes |carry| which is stored separately. - */ - for (carry = 0, i = 0; i < nl; i++, rp++) - { - v = bn_mul_add_words (rp, np, nl, (rp[0] * n0) & BN_MASK2); - v = (v + carry + rp[nl]) & BN_MASK2; - carry |= (v != rp[nl]); - carry &= (v <= rp[nl]); - rp[nl] = v; - } - - if (bn_wexpand (ret, nl) == NULL) - return 0; - ret->top = nl; - ret->flags |= BN_FLG_FIXED_TOP; - ret->neg = r->neg; - - rp = ret->d; - - /* - * Shift |nl| words to divide by R. We have |ap| < 2 * |n|. Note that |ap| - * includes |carry| which is stored separately. - */ - ap = &(r->d[nl]); - - carry -= bn_sub_words (rp, ap, np, nl); - /* - * |carry| is -1 if |ap| - |np| underflowed or zero if it did not. Note - * |carry| cannot be 1. That would imply the subtraction did not fit in - * |nl| words, and we know at most one subtraction is needed. - */ - for (i = 0; i < nl; i++) - { - rp[i] = (carry & ap[i]) | (~carry & rp[i]); - ap[i] = 0; - } - - return 1; -} - - -int -bn_from_mont_fixed_top (BIGNUM *ret, const BIGNUM *a, BN_MONT_CTX *mont, - BN_CTX *ctx) -{ - int retn = 0; -#ifdef MONT_WORD - BIGNUM *t; - - BN_CTX_start (ctx); - if ((t = BN_CTX_get (ctx)) && BN_copy (t, a)) - { - retn = bn_from_montgomery_word (ret, t, mont); - } - BN_CTX_end (ctx); -#else /* !MONT_WORD */ - BIGNUM *t1, *t2; - - BN_CTX_start (ctx); - t1 = BN_CTX_get (ctx); - t2 = BN_CTX_get (ctx); - if (t2 == NULL) - goto err; - - if (! BN_copy (t1, a)) - goto err; - BN_mask_bits (t1, mont->ri); - - if (! BN_mul (t2, t1, &mont->Ni, ctx)) - goto err; - BN_mask_bits (t2, mont->ri); - - if (! BN_mul (t1, t2, &mont->N, ctx)) - goto err; - if (! BN_add (t2, a, t1)) - goto err; - if (! BN_rshift (ret, t2, mont->ri)) - goto err; - - if (BN_ucmp (ret, &(mont->N)) >= 0) - { - if (! BN_usub (ret, ret, &(mont->N))) - goto err; - } - retn = 1; - bn_check_top (ret); -err: - BN_CTX_end (ctx); -#endif /* MONT_WORD */ - return retn; -} - - -int -BN_from_montgomery (BIGNUM *ret, const BIGNUM *a, BN_MONT_CTX *mont, - BN_CTX *ctx) -{ - int retn; - - retn = bn_from_mont_fixed_top (ret, a, mont, ctx); - bn_correct_top (ret); - bn_check_top (ret); - - return retn; -} - - -int -bn_mul_mont_fixed_top (BIGNUM *r, const BIGNUM *a, const BIGNUM *b, - BN_MONT_CTX *mont, BN_CTX *ctx) -{ - BIGNUM *tmp; - int ret = 0; - int num = mont->N.top; - -#if defined(OPENSSL_BN_ASM_MONT) && defined(MONT_WORD) - if (num > 1 && num <= BN_SOFT_LIMIT && a->top == num && b->top == num) - { - if (bn_wexpand (r, num) == NULL) - return 0; - if (bn_mul_mont (r->d, a->d, b->d, mont->N.d, mont->n0, num)) - { - r->neg = a->neg ^ b->neg; - r->top = num; - r->flags |= BN_FLG_FIXED_TOP; - return 1; - } - } -#endif - - if ((a->top + b->top) > 2 * num) - return 0; - - BN_CTX_start (ctx); - tmp = BN_CTX_get (ctx); - if (tmp == NULL) - goto err; - - bn_check_top (tmp); - if (a == b) - { - if (! bn_sqr_fixed_top (tmp, a, ctx)) - goto err; - } - else - { - if (! bn_mul_fixed_top (tmp, a, b, ctx)) - goto err; - } - /* reduce from aRR to aR */ -#ifdef MONT_WORD - if (! bn_from_montgomery_word (r, tmp, mont)) - goto err; -#else - if (! BN_from_montgomery (r, tmp, mont, ctx)) - goto err; -#endif - ret = 1; -err: - BN_CTX_end (ctx); - return ret; -} - - -int -bn_to_mont_fixed_top (BIGNUM *r, const BIGNUM *a, BN_MONT_CTX *mont, - BN_CTX *ctx) -{ - return bn_mul_mont_fixed_top (r, a, &(mont->RR), mont, ctx); -} - - -static int -BN_STACK_push (BN_STACK *st, unsigned int idx) -{ - if (st->depth == st->size) - { - /* Need to expand */ - unsigned int newsize = - st->size ? (st->size * 3 / 2) : BN_CTX_START_FRAMES; - unsigned int *newitems; - - if ((newitems = OPENSSL_malloc (sizeof(*newitems) * newsize)) == NULL) - return 0; - if (st->depth) - memcpy (newitems, st->indexes, sizeof(*newitems) * st->depth); - OPENSSL_free (st->indexes); - st->indexes = newitems; - st->size = newsize; - } - st->indexes[(st->depth)++] = idx; - return 1; -} - - -static unsigned int -BN_STACK_pop (BN_STACK *st) -{ - return st->indexes[--(st->depth)]; -} - - -static void -BN_STACK_init (BN_STACK *st) -{ - st->indexes = NULL; - st->depth = st->size = 0; -} - - -static void -BN_STACK_finish (BN_STACK *st) -{ - OPENSSL_free (st->indexes); - st->indexes = NULL; -} - - -static void -BN_POOL_init (BN_POOL *p) -{ - p->head = p->current = p->tail = NULL; - p->used = p->size = 0; -} - - -typedef void *(*memset_t)(void *, int, size_t); - -static volatile memset_t memset_func = memset; - -void -OPENSSL_cleanse (void *ptr, size_t len) -{ - memset_func (ptr, 0, len); -} - - -void -BN_clear_free (BIGNUM *a) -{ - if (a == NULL) - return; - if (a->d != NULL && ! BN_get_flags (a, BN_FLG_STATIC_DATA)) - bn_free_d (a, 1); - if (BN_get_flags (a, BN_FLG_MALLOCED)) - { - OPENSSL_cleanse (a, sizeof(*a)); - OPENSSL_free (a); - } -} - - -static void -BN_POOL_finish (BN_POOL *p) -{ - unsigned int loop; - BIGNUM *bn; - - while (p->head) - { - for (loop = 0, bn = p->head->vals; loop++ < BN_CTX_POOL_SIZE; bn++) - if (bn->d) - BN_clear_free (bn); - p->current = p->head->next; - OPENSSL_free (p->head); - p->head = p->current; - } -} - - -void -BN_set_flags (BIGNUM *b, int n) -{ - b->flags |= n; -} - - -static BIGNUM * -BN_POOL_get (BN_POOL *p, int flag) -{ - BIGNUM *bn; - unsigned int loop; - - /* Full; allocate a new pool item and link it in. */ - if (p->used == p->size) - { - BN_POOL_ITEM *item; - - if ((item = OPENSSL_malloc (sizeof(*item))) == NULL) - return NULL; - for (loop = 0, bn = item->vals; loop++ < BN_CTX_POOL_SIZE; bn++) - { - bn_init (bn); - if ((flag & BN_FLG_SECURE) != 0) - BN_set_flags (bn, BN_FLG_SECURE); - } - item->prev = p->tail; - item->next = NULL; - - if (p->head == NULL) - p->head = p->current = p->tail = item; - else - { - p->tail->next = item; - p->tail = item; - p->current = item; - } - p->size += BN_CTX_POOL_SIZE; - p->used++; - /* Return the first bignum from the new pool */ - return item->vals; - } - - if (! p->used) - p->current = p->head; - else if ((p->used % BN_CTX_POOL_SIZE) == 0) - p->current = p->current->next; - return p->current->vals + ((p->used++) % BN_CTX_POOL_SIZE); -} - - -static void -BN_POOL_release (BN_POOL *p, unsigned int num) -{ - unsigned int offset = (p->used - 1) % BN_CTX_POOL_SIZE; - - p->used -= num; - while (num--) - { - bn_check_top (p->current->vals + offset); - if (offset == 0) - { - offset = BN_CTX_POOL_SIZE - 1; - p->current = p->current->prev; - } - else - offset--; - } -} - - -const BIGNUM * -BN_value_one (void) -{ - static const BN_ULONG data_one = 1L; - static const BIGNUM const_one = - { (BN_ULONG *) &data_one, 1, 1, 0, BN_FLG_STATIC_DATA }; - - return &const_one; -} - - -int -BN_is_bit_set (const BIGNUM *a, int n) -{ - int i, j; - - bn_check_top (a); - if (n < 0) - return 0; - i = n / BN_BITS2; - j = n % BN_BITS2; - if (a->top <= i) - return 0; - return (int) (((a->d[i]) >> j) & ((BN_ULONG) 1)); -} - - -int -BN_set_bit (BIGNUM *a, int n) -{ - int i, j, k; - - if (n < 0) - return 0; - - i = n / BN_BITS2; - j = n % BN_BITS2; - if (a->top <= i) - { - if (bn_wexpand (a, i + 1) == NULL) - return 0; - for (k = a->top; k < i + 1; k++) - a->d[k] = 0; - a->top = i + 1; - a->flags &= ~BN_FLG_FIXED_TOP; - } - - a->d[i] |= (((BN_ULONG) 1) << j); - bn_check_top (a); - return 1; -} - - -BN_CTX * -BN_CTX_new_ex (OSSL_LIB_CTX *ctx) -{ - BN_CTX *ret; - - if ((ret = OPENSSL_zalloc (sizeof(*ret))) == NULL) - return NULL; - memset (ret, - 0, - sizeof (*ret)); - /* Initialise the structure */ - BN_POOL_init (&ret->pool); - BN_STACK_init (&ret->stack); - ret->libctx = ctx; - return ret; -} - - -BIGNUM * -BN_new (void) -{ - BIGNUM *ret; - - if ((ret = OPENSSL_zalloc (sizeof(*ret))) == NULL) - return NULL; - memset (ret, 0, sizeof (*ret)); - ret->flags = BN_FLG_MALLOCED; - bn_check_top (ret); - return ret; -} - - -int -BN_is_odd (const BIGNUM *a) -{ - return (a->top > 0) && (a->d[0] & 1); -} - - -int -BN_lshift1 (BIGNUM *r, const BIGNUM *a) -{ - register BN_ULONG *ap, *rp, t, c; - int i; - - bn_check_top (r); - bn_check_top (a); - - if (r != a) - { - r->neg = a->neg; - if (bn_wexpand (r, a->top + 1) == NULL) - return 0; - r->top = a->top; - } - else - { - if (bn_wexpand (r, a->top + 1) == NULL) - return 0; - } - ap = a->d; - rp = r->d; - c = 0; - for (i = 0; i < a->top; i++) - { - t = *(ap++); - *(rp++) = ((t << 1) | c) & BN_MASK2; - c = t >> (BN_BITS2 - 1); - } - *rp = c; - r->top += c; - bn_check_top (r); - return 1; -} - - -int -BN_rshift1 (BIGNUM *r, const BIGNUM *a) -{ - BN_ULONG *ap, *rp, t, c; - int i; - - bn_check_top (r); - bn_check_top (a); - - if (BN_is_zero (a)) - { - BN_zero (r); - return 1; - } - i = a->top; - ap = a->d; - if (a != r) - { - if (bn_wexpand (r, i) == NULL) - return 0; - r->neg = a->neg; - } - rp = r->d; - r->top = i; - t = ap[--i]; - rp[i] = t >> 1; - c = t << (BN_BITS2 - 1); - r->top -= (t == 1); - while (i > 0) - { - t = ap[--i]; - rp[i] = ((t >> 1) & BN_MASK2) | c; - c = t << (BN_BITS2 - 1); - } - if (! r->top) - r->neg = 0; /* don't allow negative zero */ - bn_check_top (r); - return 1; -} - - -int -BN_is_word (const BIGNUM *a, const BN_ULONG w) -{ - return BN_abs_is_word (a, w) && (! w || ! a->neg); -} - - -int -BN_is_one (const BIGNUM *a) -{ - return BN_abs_is_word (a, 1) && ! a->neg; -} - - -int -BN_lshift (BIGNUM *r, const BIGNUM *a, int n) -{ - int ret; - - if (n < 0) - { - // ERR_raise(ERR_LIB_BN, BN_R_INVALID_SHIFT); - // return 0; - assert (0); - } - - ret = bn_lshift_fixed_top (r, a, n); - - bn_correct_top (r); - bn_check_top (r); - - return ret; -} - - -int -BN_mul_word (BIGNUM *a, BN_ULONG w) -{ - BN_ULONG ll; - - bn_check_top (a); - w &= BN_MASK2; - if (a->top) - { - if (w == 0) - BN_zero (a); - else - { - ll = bn_mul_words (a->d, a->d, a->top, w); - if (ll) - { - if (bn_wexpand (a, a->top + 1) == NULL) - return 0; - a->d[a->top++] = ll; - } - } - } - bn_check_top (a); - return 1; -} - - -/* - * This is an internal function, we assume all callers pass valid arguments: - * all pointers passed here are assumed non-NULL. - */ -BIGNUM * -int_bn_mod_inverse (BIGNUM *in, - const BIGNUM *a, const BIGNUM *n, BN_CTX *ctx, - int *pnoinv) -{ - BIGNUM *A, *B, *X, *Y, *M, *D, *T, *R = NULL; - BIGNUM *ret = NULL; - int sign; - - /* This is invalid input so we don't worry about constant time here */ - if (BN_abs_is_word (n, 1) || BN_is_zero (n)) - { - *pnoinv = 1; - return NULL; - } - - *pnoinv = 0; -/* - if ((BN_get_flags(a, BN_FLG_CONSTTIME) != 0) - || (BN_get_flags(n, BN_FLG_CONSTTIME) != 0)) { - return bn_mod_inverse_no_branch(in, a, n, ctx, pnoinv); - } -*/ - bn_check_top (a); - bn_check_top (n); - - BN_CTX_start (ctx); - A = BN_CTX_get (ctx); - B = BN_CTX_get (ctx); - X = BN_CTX_get (ctx); - D = BN_CTX_get (ctx); - M = BN_CTX_get (ctx); - Y = BN_CTX_get (ctx); - T = BN_CTX_get (ctx); - if (T == NULL) - goto err; - - if (in == NULL) - R = BN_new (); - else - R = in; - if (R == NULL) - goto err; - - if (! BN_one (X)) - goto err; - BN_zero (Y); - if (BN_copy (B, a) == NULL) - goto err; - if (BN_copy (A, n) == NULL) - goto err; - A->neg = 0; - if (B->neg || (BN_ucmp (B, A) >= 0)) - { - if (! BN_nnmod (B, B, A, ctx)) - goto err; - } - sign = -1; - /*- - * From B = a mod |n|, A = |n| it follows that - * - * 0 <= B < A, - * -sign*X*a == B (mod |n|), - * sign*Y*a == A (mod |n|). - */ - - if (BN_is_odd (n) && (BN_num_bits (n) <= 2048)) - { - /* - * Binary inversion algorithm; requires odd modulus. This is faster - * than the general algorithm if the modulus is sufficiently small - * (about 400 .. 500 bits on 32-bit systems, but much more on 64-bit - * systems) - */ - int shift; - - while (! BN_is_zero (B)) - { - /*- - * 0 < B < |n|, - * 0 < A <= |n|, - * (1) -sign*X*a == B (mod |n|), - * (2) sign*Y*a == A (mod |n|) - */ - - /* - * Now divide B by the maximum possible power of two in the - * integers, and divide X by the same value mod |n|. When we're - * done, (1) still holds. - */ - shift = 0; - while (! BN_is_bit_set (B, shift)) /* note that 0 < B */ - { - shift++; - - if (BN_is_odd (X)) - { - if (! BN_uadd (X, X, n)) - goto err; - } - /* - * now X is even, so we can easily divide it by two - */ - if (! BN_rshift1 (X, X)) - goto err; - } - if (shift > 0) - { - if (! BN_rshift (B, B, shift)) - goto err; - } - - /* - * Same for A and Y. Afterwards, (2) still holds. - */ - shift = 0; - while (! BN_is_bit_set (A, shift)) /* note that 0 < A */ - { - shift++; - - if (BN_is_odd (Y)) - { - if (! BN_uadd (Y, Y, n)) - goto err; - } - /* now Y is even */ - if (! BN_rshift1 (Y, Y)) - goto err; - } - if (shift > 0) - { - if (! BN_rshift (A, A, shift)) - goto err; - } - - /*- - * We still have (1) and (2). - * Both A and B are odd. - * The following computations ensure that - * - * 0 <= B < |n|, - * 0 < A < |n|, - * (1) -sign*X*a == B (mod |n|), - * (2) sign*Y*a == A (mod |n|), - * - * and that either A or B is even in the next iteration. - */ - if (BN_ucmp (B, A) >= 0) - { - /* -sign*(X + Y)*a == B - A (mod |n|) */ - if (! BN_uadd (X, X, Y)) - goto err; - /* - * NB: we could use BN_mod_add_quick(X, X, Y, n), but that - * actually makes the algorithm slower - */ - if (! BN_usub (B, B, A)) - goto err; - } - else - { - /* sign*(X + Y)*a == A - B (mod |n|) */ - if (! BN_uadd (Y, Y, X)) - goto err; - /* - * as above, BN_mod_add_quick(Y, Y, X, n) would slow things down - */ - if (! BN_usub (A, A, B)) - goto err; - } - } - } - else - { - /* general inversion algorithm */ - - while (! BN_is_zero (B)) - { - BIGNUM *tmp; - - /*- - * 0 < B < A, - * (*) -sign*X*a == B (mod |n|), - * sign*Y*a == A (mod |n|) - */ - - /* (D, M) := (A/B, A%B) ... */ - if (BN_num_bits (A) == BN_num_bits (B)) - { - if (! BN_one (D)) - goto err; - if (! BN_sub (M, A, B)) - goto err; - } - else if (BN_num_bits (A) == BN_num_bits (B) + 1) - { - /* A/B is 1, 2, or 3 */ - if (! BN_lshift1 (T, B)) - goto err; - if (BN_ucmp (A, T) < 0) - { - /* A < 2*B, so D=1 */ - if (! BN_one (D)) - goto err; - if (! BN_sub (M, A, B)) - goto err; - } - else - { - /* A >= 2*B, so D=2 or D=3 */ - if (! BN_sub (M, A, T)) - goto err; - if (! BN_add (D, T, B)) - goto err; /* use D (:= 3*B) as temp */ - if (BN_ucmp (A, D) < 0) - { - /* A < 3*B, so D=2 */ - if (! BN_set_word (D, 2)) - goto err; - /* - * M (= A - 2*B) already has the correct value - */ - } - else - { - /* only D=3 remains */ - if (! BN_set_word (D, 3)) - goto err; - /* - * currently M = A - 2*B, but we need M = A - 3*B - */ - if (! BN_sub (M, M, B)) - goto err; - } - } - } - else - { - if (! BN_div (D, M, A, B, ctx)) - goto err; - } - - /*- - * Now - * A = D*B + M; - * thus we have - * (**) sign*Y*a == D*B + M (mod |n|). - */ - - tmp = A; /* keep the BIGNUM object, the value does not matter */ - - /* (A, B) := (B, A mod B) ... */ - A = B; - B = M; - /* ... so we have 0 <= B < A again */ - - /*- - * Since the former M is now B and the former B is now A, - * (**) translates into - * sign*Y*a == D*A + B (mod |n|), - * i.e. - * sign*Y*a - D*A == B (mod |n|). - * Similarly, (*) translates into - * -sign*X*a == A (mod |n|). - * - * Thus, - * sign*Y*a + D*sign*X*a == B (mod |n|), - * i.e. - * sign*(Y + D*X)*a == B (mod |n|). - * - * So if we set (X, Y, sign) := (Y + D*X, X, -sign), we arrive back at - * -sign*X*a == B (mod |n|), - * sign*Y*a == A (mod |n|). - * Note that X and Y stay non-negative all the time. - */ - - /* - * most of the time D is very small, so we can optimize tmp := D*X+Y - */ - if (BN_is_one (D)) - { - if (! BN_add (tmp, X, Y)) - goto err; - } - else - { - if (BN_is_word (D, 2)) - { - if (! BN_lshift1 (tmp, X)) - goto err; - } - else if (BN_is_word (D, 4)) - { - if (! BN_lshift (tmp, X, 2)) - goto err; - } - else if (D->top == 1) - { - if (! BN_copy (tmp, X)) - goto err; - if (! BN_mul_word (tmp, D->d[0])) - goto err; - } - else - { - if (! BN_mul (tmp, D, X, ctx)) - goto err; - } - if (! BN_add (tmp, tmp, Y)) - goto err; - } - - M = Y; /* keep the BIGNUM object, the value does not matter */ - Y = X; - X = tmp; - sign = -sign; - } - } - - /*- - * The while loop (Euclid's algorithm) ends when - * A == gcd(a,n); - * we have - * sign*Y*a == A (mod |n|), - * where Y is non-negative. - */ - - if (sign < 0) - { - if (! BN_sub (Y, n, Y)) - goto err; - } - /* Now Y*a == A (mod |n|). */ - - if (BN_is_one (A)) - { - /* Y*a == 1 (mod |n|) */ - if (! Y->neg && BN_ucmp (Y, n) < 0) - { - if (! BN_copy (R, Y)) - goto err; - } - else - { - if (! BN_nnmod (R, Y, n, ctx)) - goto err; - } - } - else - { - *pnoinv = 1; - goto err; - } - ret = R; -err: - if ((ret == NULL) && (in == NULL)) - // BN_free(R); - return 0; - BN_CTX_end (ctx); - bn_check_top (ret); - return ret; -} - - -void -BN_set_negative (BIGNUM *a, int b) -{ - if (b && ! BN_is_zero (a)) - a->neg = 1; - else - a->neg = 0; -} - - -BIGNUM * -BN_mod_inverse (BIGNUM *in, - const BIGNUM *a, const BIGNUM *n, BN_CTX *ctx) -{ - BN_CTX *new_ctx = NULL; - BIGNUM *rv; - int noinv = 0; - - if (ctx == NULL) - { - ctx = new_ctx = BN_CTX_new_ex (NULL); - if (ctx == NULL) - { - // ERR_raise(ERR_LIB_BN, ERR_R_BN_LIB); - assert (0); - return NULL; - } - } - - rv = int_bn_mod_inverse (in, a, n, ctx, &noinv); - if (noinv) - // ERR_raise(ERR_LIB_BN, BN_R_NO_INVERSE); - assert (0); - return 0; - // BN_CTX_free(new_ctx); - return rv; -} - - -int -BN_sub_word (BIGNUM *a, BN_ULONG w) -{ - int i; - - bn_check_top (a); - w &= BN_MASK2; - - /* degenerate case: w is zero */ - if (! w) - return 1; - /* degenerate case: a is zero */ - if (BN_is_zero (a)) - { - i = BN_set_word (a, w); - if (i != 0) - BN_set_negative (a, 1); - return i; - } - /* handle 'a' when negative */ - if (a->neg) - { - a->neg = 0; - i = BN_add_word (a, w); - a->neg = 1; - return i; - } - - if ((a->top == 1) && (a->d[0] < w)) - { - a->d[0] = w - a->d[0]; - a->neg = 1; - return 1; - } - i = 0; - for (;;) - { - if (a->d[i] >= w) - { - a->d[i] -= w; - break; - } - else - { - a->d[i] = (a->d[i] - w) & BN_MASK2; - i++; - w = 1; - } - } - if ((a->d[i] == 0) && (i == (a->top - 1))) - a->top--; - bn_check_top (a); - return 1; -} - - -int -BN_add_word (BIGNUM *a, BN_ULONG w) -{ - BN_ULONG l; - int i; - - bn_check_top (a); - w &= BN_MASK2; - - /* degenerate case: w is zero */ - if (! w) - return 1; - /* degenerate case: a is zero */ - if (BN_is_zero (a)) - return BN_set_word (a, w); - /* handle 'a' when negative */ - if (a->neg) - { - a->neg = 0; - i = BN_sub_word (a, w); - if (! BN_is_zero (a)) - a->neg = ! (a->neg); - return i; - } - for (i = 0; w != 0 && i < a->top; i++) - { - a->d[i] = l = (a->d[i] + w) & BN_MASK2; - w = (w > l) ? 1 : 0; - } - if (w && i == a->top) - { - if (bn_wexpand (a, a->top + 1) == NULL) - return 0; - a->top++; - a->d[i] = w; - } - bn_check_top (a); - return 1; -} - - -int -BN_MONT_CTX_set (BN_MONT_CTX *mont, - const BIGNUM *mod, - BN_CTX *ctx) -{ - int i, ret = 0; - BIGNUM *Ri, *R; - - if (BN_is_zero (mod)) - return 0; - - BN_CTX_start (ctx); - if ((Ri = BN_CTX_get (ctx)) == NULL) - goto err; - R = &(mont->RR); /* grab RR as a temp */ - if (! BN_copy (&(mont->N), mod)) - goto err; /* Set N */ - if (BN_get_flags (mod, BN_FLG_CONSTTIME) != 0) - BN_set_flags (&(mont->N), BN_FLG_CONSTTIME); - mont->N.neg = 0; - -#ifdef MONT_WORD - { - BIGNUM tmod; - BN_ULONG buf[2]; - - bn_init (&tmod); - tmod.d = buf; - tmod.dmax = 2; - tmod.neg = 0; - - if (BN_get_flags (mod, BN_FLG_CONSTTIME) != 0) - BN_set_flags (&tmod, BN_FLG_CONSTTIME); - - mont->ri = (BN_num_bits (mod) + (BN_BITS2 - 1)) / BN_BITS2 * BN_BITS2; - -# if defined(OPENSSL_BN_ASM_MONT) && (BN_BITS2<=32) - /* - * Only certain BN_BITS2<=32 platforms actually make use of n0[1], - * and we could use the #else case (with a shorter R value) for the - * others. However, currently only the assembler files do know which - * is which. - */ - - BN_zero (R); - if (! (BN_set_bit (R, 2 * BN_BITS2))) - goto err; - - tmod.top = 0; - if ((buf[0] = mod->d[0])) - tmod.top = 1; - if ((buf[1] = mod->top > 1 ? mod->d[1] : 0)) - tmod.top = 2; - - if (BN_is_one (&tmod)) - BN_zero (Ri); - else if ((BN_mod_inverse (Ri, R, &tmod, ctx)) == NULL) - goto err; - if (! BN_lshift (Ri, Ri, 2 * BN_BITS2)) - goto err; /* R*Ri */ - if (! BN_is_zero (Ri)) - { - if (! BN_sub_word (Ri, 1)) - goto err; - } - else /* if N mod word size == 1 */ - - { - if (bn_expand (Ri, (int) sizeof(BN_ULONG) * 2) == NULL) - goto err; - /* Ri-- (mod double word size) */ - Ri->neg = 0; - Ri->d[0] = BN_MASK2; - Ri->d[1] = BN_MASK2; - Ri->top = 2; - } - if (! BN_div (Ri, NULL, Ri, &tmod, ctx)) - goto err; - /* - * Ni = (R*Ri-1)/N, keep only couple of least significant words: - */ - mont->n0[0] = (Ri->top > 0) ? Ri->d[0] : 0; - mont->n0[1] = (Ri->top > 1) ? Ri->d[1] : 0; -# else - BN_zero (R); - if (! (BN_set_bit (R, BN_BITS2))) - goto err; /* R */ - - buf[0] = mod->d[0]; /* tmod = N mod word size */ - buf[1] = 0; - tmod.top = buf[0] != 0 ? 1 : 0; - /* Ri = R^-1 mod N */ - if (BN_is_one (&tmod)) - BN_zero (Ri); - else if ((BN_mod_inverse (Ri, R, &tmod, ctx)) == NULL) - goto err; - if (! BN_lshift (Ri, Ri, BN_BITS2)) - goto err; /* R*Ri */ - if (! BN_is_zero (Ri)) - { - if (! BN_sub_word (Ri, 1)) - goto err; - } - else /* if N mod word size == 1 */ - - { - if (! BN_set_word (Ri, BN_MASK2)) - goto err; /* Ri-- (mod word size) */ - } - if (! BN_div (Ri, NULL, Ri, &tmod, ctx)) - goto err; - /* - * Ni = (R*Ri-1)/N, keep only least significant word: - */ - mont->n0[0] = (Ri->top > 0) ? Ri->d[0] : 0; - mont->n0[1] = 0; -# endif - } -#else /* !MONT_WORD */ - { /* bignum version */ - mont->ri = BN_num_bits (&mont->N); - BN_zero (R); - if (! BN_set_bit (R, mont->ri)) - goto err; /* R = 2^ri */ - /* Ri = R^-1 mod N */ - if ((BN_mod_inverse (Ri, R, &mont->N, ctx)) == NULL) - goto err; - if (! BN_lshift (Ri, Ri, mont->ri)) - goto err; /* R*Ri */ - if (! BN_sub_word (Ri, 1)) - goto err; - /* - * Ni = (R*Ri-1) / N - */ - if (! BN_div (&(mont->Ni), NULL, Ri, &mont->N, ctx)) - goto err; - } -#endif - - /* setup RR for conversions */ - BN_zero (&(mont->RR)); - if (! BN_set_bit (&(mont->RR), mont->ri * 2)) - goto err; - if (! BN_mod (&(mont->RR), &(mont->RR), &(mont->N), ctx)) - goto err; - - for (i = mont->RR.top, ret = mont->N.top; i < ret; i++) - mont->RR.d[i] = 0; - mont->RR.top = ret; - mont->RR.flags |= BN_FLG_FIXED_TOP; - - ret = 1; -err: - BN_CTX_end (ctx); - return ret; -} - - -// FINALLYYYYYYY - - -int -BN_mod_exp_mont (BIGNUM *rr, - const BIGNUM *a, - const BIGNUM *p, - const BIGNUM *m, - BN_CTX *ctx, - BN_MONT_CTX *in_mont) -{ - int i, j, bits, ret = 0, wstart, wend, window; - int start = 1; - BIGNUM *d, *r; - const BIGNUM *aa; - /* Table of variables obtained from 'ctx' */ - BIGNUM *val[TABLE_SIZE]; - BN_MONT_CTX *mont = NULL; - - bn_check_top (a); - bn_check_top (p); - bn_check_top (m); - - if (! BN_is_odd (m)) - { - // ERR_raise(ERR_LIB_BN, BN_R_CALLED_WITH_EVEN_MODULUS); - assert (0); - return 0; - } - - - /* - if (m->top <= BN_CONSTTIME_SIZE_LIMIT - && (BN_get_flags(p, BN_FLG_CONSTTIME) != 0 - || BN_get_flags(a, BN_FLG_CONSTTIME) != 0 - || BN_get_flags(m, BN_FLG_CONSTTIME) != 0)) { - return BN_mod_exp_mont_consttime(rr, a, p, m, ctx, in_mont); - } -*/ - bits = BN_num_bits (p); - if (bits == 0) - { - /* x**0 mod 1, or x**0 mod -1 is still zero. */ - if (BN_abs_is_word (m, 1)) - { - ret = 1; - BN_zero (rr); - } - else - { - ret = BN_one (rr); - } - return ret; - } - - BN_CTX_start (ctx); - d = BN_CTX_get (ctx); - r = BN_CTX_get (ctx); - val[0] = BN_CTX_get (ctx); - if (val[0] == NULL) - goto err; - - /* - * If this is not done, things will break in the montgomery part - */ - - if (in_mont != NULL) - mont = in_mont; - else - { - if ((mont = BN_MONT_CTX_new ()) == NULL) - goto err; - if (! BN_MONT_CTX_set (mont, m, ctx)) - goto err; - } - - if (a->neg || BN_ucmp (a, m) >= 0) - { - if (! BN_nnmod (val[0], a, m, ctx)) - goto err; - aa = val[0]; - } - else - aa = a; - if (! bn_to_mont_fixed_top (val[0], aa, mont, ctx)) - goto err; /* 1 */ - - window = BN_window_bits_for_exponent_size (bits); - if (window > 1) - { - if (! bn_mul_mont_fixed_top (d, val[0], val[0], mont, ctx)) - goto err; /* 2 */ - j = 1 << (window - 1); - for (i = 1; i < j; i++) - { - if (((val[i] = BN_CTX_get (ctx)) == NULL) || - ! bn_mul_mont_fixed_top (val[i], val[i - 1], d, mont, ctx)) - goto err; - } - } - - start = 1; /* This is used to avoid multiplication etc - * when there is only the value '1' in the - * buffer. */ - wstart = bits - 1; /* The top bit of the window */ - wend = 0; /* The bottom bit of the window */ - -#if 1 /* by Shay Gueron's suggestion */ - j = m->top; /* borrow j */ - if (m->d[j - 1] & (((BN_ULONG) 1) << (BN_BITS2 - 1))) - { - if (bn_wexpand (r, j) == NULL) - goto err; - /* 2^(top*BN_BITS2) - m */ - r->d[0] = (0 - m->d[0]) & BN_MASK2; - for (i = 1; i < j; i++) - r->d[i] = (~m->d[i]) & BN_MASK2; - r->top = j; - r->flags |= BN_FLG_FIXED_TOP; - } - else -#endif - if (! bn_to_mont_fixed_top (r, BN_value_one (), mont, ctx)) - goto err; - for (;;) - { - int wvalue; /* The 'value' of the window */ - - if (BN_is_bit_set (p, wstart) == 0) - { - if (! start) - { - if (! bn_mul_mont_fixed_top (r, r, r, mont, ctx)) - goto err; - } - if (wstart == 0) - break; - wstart--; - continue; - } - /* - * We now have wstart on a 'set' bit, we now need to work out how bit - * a window to do. To do this we need to scan forward until the last - * set bit before the end of the window - */ - wvalue = 1; - wend = 0; - for (i = 1; i < window; i++) - { - if (wstart - i < 0) - break; - if (BN_is_bit_set (p, wstart - i)) - { - wvalue <<= (i - wend); - wvalue |= 1; - wend = i; - } - } - - /* wend is the size of the current window */ - j = wend + 1; - /* add the 'bytes above' */ - if (! start) - for (i = 0; i < j; i++) - { - if (! bn_mul_mont_fixed_top (r, r, r, mont, ctx)) - goto err; - } - - /* wvalue will be an odd number < 2^window */ - if (! bn_mul_mont_fixed_top (r, r, val[wvalue >> 1], mont, ctx)) - goto err; - - /* move the 'window' down further */ - wstart -= wend + 1; - start = 0; - if (wstart < 0) - break; - } - /* - * Done with zero-padded intermediate BIGNUMs. Final BN_from_montgomery - * removes padding [if any] and makes return value suitable for public - * API consumer. - */ -#if defined(SPARC_T4_MONT) - if (OPENSSL_sparcv9cap_P[0] & (SPARCV9_VIS3 | SPARCV9_PREFER_FPU)) - { - j = mont->N.top; /* borrow j */ - val[0]->d[0] = 1; /* borrow val[0] */ - for (i = 1; i < j; i++) - val[0]->d[i] = 0; - val[0]->top = j; - if (! BN_mod_mul_montgomery (rr, r, val[0], mont, ctx)) - goto err; - } - else -#endif - if (! BN_from_montgomery (rr, r, mont, ctx)) - goto err; - ret = 1; -err: - if (in_mont == NULL) - // BN_MONT_CTX_free(mont); - return 0; - BN_CTX_end (ctx); - bn_check_top (rr); - return ret; -} - - -int -ossl_ctype_check (int c, unsigned int mask) -{ - const int max = sizeof(ctype_char_map) / sizeof(*ctype_char_map); - const int a = ossl_toascii (c); - - return a >= 0 && a < max && (ctype_char_map[a] & mask) != 0; -} - - -int -OPENSSL_hexchar2int (unsigned char c) -{ -#ifdef CHARSET_EBCDIC - c = os_toebcdic[c]; -#endif - - switch (c) - { - case '0': - return 0; - case '1': - return 1; - case '2': - return 2; - case '3': - return 3; - case '4': - return 4; - case '5': - return 5; - case '6': - return 6; - case '7': - return 7; - case '8': - return 8; - case '9': - return 9; - case 'a': case 'A': - return 0x0A; - case 'b': case 'B': - return 0x0B; - case 'c': case 'C': - return 0x0C; - case 'd': case 'D': - return 0x0D; - case 'e': case 'E': - return 0x0E; - case 'f': case 'F': - return 0x0F; - } - return -1; -} - - -int -BN_hex2bn (BIGNUM **bn, const char *a) -{ - BIGNUM *ret = NULL; - BN_ULONG l = 0; - int neg = 0, h, m, i, j, k, c; - int num; - - if (a == NULL || *a == '\0') - return 0; - - if (*a == '-') - { - neg = 1; - a++; - } - - for (i = 0; i <= INT_MAX / 4 && ossl_isxdigit (a[i]); i++) - continue; - - if (i == 0 || i > INT_MAX / 4) - return 0; - - num = i + neg; - if (bn == NULL) - return num; - - /* a is the start of the hex digits, and it is 'i' long */ - if (*bn == NULL) - { - if ((ret = BN_new ()) == NULL) - return 0; - } - else - { - ret = *bn; - if (BN_get_flags (ret, BN_FLG_STATIC_DATA)) - { - // ERR_raise(ERR_LIB_BN, ERR_R_PASSED_INVALID_ARGUMENT); - assert (0); - return 0; - } - BN_zero (ret); - } - - /* i is the number of hex digits */ - if (bn_expand (ret, i * 4) == NULL) - goto err; - - j = i; /* least significant 'hex' */ - m = 0; - h = 0; - while (j > 0) - { - m = (BN_BYTES * 2 <= j) ? BN_BYTES * 2 : j; - l = 0; - for (;;) - { - c = a[j - m]; - k = OPENSSL_hexchar2int (c); - if (k < 0) - k = 0; /* paranoia */ - l = (l << 4) | k; - - if (--m <= 0) - { - ret->d[h++] = l; - break; - } - } - j -= BN_BYTES * 2; - } - ret->top = h; - bn_correct_top (ret); - - *bn = ret; - bn_check_top (ret); - /* Don't set the negative flag if it's zero. */ - if (ret->top != 0) - ret->neg = neg; - return num; -err: - if (*bn == NULL) - // BN_free(ret); - return 0; - return 0; -} - - -// USAGE? - -// int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx) - -// BN_mod_exp_mont(r, a, p, m, ctx, NULL); - -// mm = modulus (n), aa = e, pp = d -void -test (char *aa, char *pp, char *mm) -{ - - OSSL_LIB_CTX ossl; // should be initialised - - memset (&ossl, - 0, - sizeof (ossl)); - BN_CTX ctx = *BN_CTX_new_ex (&ossl); - - BIGNUM *a = BN_new (); - BIGNUM *p = BN_new (); - BIGNUM *m = BN_new (); - BIGNUM *r = BN_new (); - - if (0 == BN_hex2bn (&a, aa)) - abort (); - if (0 == BN_hex2bn (&p, pp)) - abort (); - if (0 == BN_hex2bn (&m, mm)) - abort (); - - // NOTE: r==0, why? - BN_mod_exp_mont (r, a, p, m, &ctx, NULL); // it still hits assert 99% of the time – but for some reason the program does not stop... - - - return; -} diff --git a/source/openssl-test.h b/source/openssl-test.h @@ -1,531 +0,0 @@ -// -// openssl-test.h -// hello -// -// Created by Cedric Zwahlen on 07.10.2023. -// - -#ifndef openssl_test_h -#define openssl_test_h - -#include <stdio.h> -#include <assert.h> - -/* How many bignums are in each "pool item"; */ -#define BN_CTX_POOL_SIZE 16 -/* The stack frame info is resizing, set a first-time expansion size; */ -#define BN_CTX_START_FRAMES 32 - - -#define THIRTY_TWO_BIT // on gpu that would be 32 - -# ifdef SIXTY_FOUR_BIT -# define BN_ULONG unsigned long long -# define BN_BYTES 8 -# endif - -# ifdef THIRTY_TWO_BIT -# define BN_ULONG unsigned int -# define BN_BYTES 4 -# endif - - -# define CRYPTO_EX_INDEX__COUNT 16 - - -typedef struct bignum_st BIGNUM; - -struct bignum_st -{ - BN_ULONG *d; /* - * Pointer to an array of 'BN_BITS2' bit - * chunks. These chunks are organised in - * a least significant chunk first order. - */ - int top; /* Index of last used d +1. */ - /* The next are internal book keeping for bn_expand. */ - int dmax; /* Size of the d array. */ - int neg; /* one if the number is negative */ - int flags; -}; - - -typedef struct crypto_ex_data_st CRYPTO_EX_DATA; - -typedef struct ex_callback_st EX_CALLBACK; - -typedef void CRYPTO_RWLOCK; - -typedef void CRYPTO_EX_new (void *parent, void *ptr, CRYPTO_EX_DATA *ad, - int idx, long argl, void *argp); -typedef void CRYPTO_EX_free (void *parent, void *ptr, CRYPTO_EX_DATA *ad, - int idx, long argl, void *argp); -typedef int CRYPTO_EX_dup (CRYPTO_EX_DATA *to, const CRYPTO_EX_DATA *from, - void *from_d, int idx, long argl, void *argp); -/* __owur */ int CRYPTO_get_ex_new_index (int class_index, long argl, - void *argp, - CRYPTO_EX_new *new_func, - CRYPTO_EX_dup *dup_func, - CRYPTO_EX_free *free_func); - - -typedef struct ossl_lib_ctx_st OSSL_LIB_CTX; - - -struct ex_callback_st -{ - long argl; /* Arbitrary long */ - void *argp; /* Arbitrary void * */ - int priority; /* Priority ordering for freeing */ - CRYPTO_EX_new *new_func; - CRYPTO_EX_free *free_func; - CRYPTO_EX_dup *dup_func; -}; - - -typedef struct ex_callbacks_st -{ - // STACK_OF(EX_CALLBACK) *meth; - - void *meth; -} EX_CALLBACKS; - - -typedef struct bignum_pool_item -{ - /* The bignum values */ - BIGNUM vals[BN_CTX_POOL_SIZE]; - /* Linked-list admin */ - struct bignum_pool_item *prev, *next; -} BN_POOL_ITEM; - - -typedef struct bignum_ctx_stack -{ - /* Array of indexes into the bignum stack */ - unsigned int *indexes; - /* Number of stack frames, and the size of the allocated array */ - unsigned int depth, size; -} BN_STACK; -static void BN_STACK_init (BN_STACK *); - -static void BN_STACK_finish (BN_STACK *); - -static int BN_STACK_push (BN_STACK *, unsigned int); - -static unsigned int BN_STACK_pop (BN_STACK *); - - -typedef struct bignum_pool -{ - /* Linked-list admin */ - BN_POOL_ITEM *head, *current, *tail; - /* Stack depth and allocation size */ - unsigned used, size; -} BN_POOL; -static void BN_POOL_init (BN_POOL *); - -static void BN_POOL_finish (BN_POOL *); - -static BIGNUM *BN_POOL_get (BN_POOL *, int); - -static void BN_POOL_release (BN_POOL *, unsigned int); - - -typedef struct bignum_ctx BN_CTX; - -struct bignum_ctx -{ - /* The bignum bundles */ - BN_POOL pool; - /* The "stack frames", if you will */ - BN_STACK stack; - /* The number of bignums currently assigned */ - unsigned int used; - /* Depth of stack overflow */ - int err_stack; - /* Block "gets" until an "end" (compatibility behaviour) */ - int too_many; - /* Flags. */ - int flags; - /* The library context */ - OSSL_LIB_CTX *libctx; -}; - - -# define BN_FLG_FIXED_TOP 0 -# define bn_pollute(a) -# define bn_check_top(a) -# define bn_fix_top(a) bn_correct_top (a) -# define bn_check_size(bn, bits) -# define bn_wcheck_size(bn, words) - - -#define BN_CONSTTIME_SIZE_LIMIT (INT_MAX / BN_BYTES / 256) - -#define BN_FLG_CONSTTIME 0x04 -# define BN_FLG_SECURE 0x08 - -# define BN_BITS2 (BN_BYTES * 8) -# define BN_BITS (BN_BITS2 * 2) -# define BN_TBIT ((BN_ULONG) 1 << (BN_BITS2 - 1)) - -# define BN_MASK2 (0xffffffffffffffffLL) - -# define BN_zero(a) (BN_set_word ((a),0)) - -# define ossl_inline inline - - -#define INT_MAX __INT_MAX__ - - -# define BN_BITS2 (BN_BYTES * 8) - - -#define OPENSSL_zalloc malloc -#define OPENSSL_malloc malloc -#define OPENSSL_free free - - -# define OPENSSL_clear_free(addr, num) \ - CRYPTO_clear_free (addr, num, OPENSSL_FILE, OPENSSL_LINE) - - -# define BN_one(a) (BN_set_word ((a),1)) - -# define BN_FLG_MALLOCED 0x01 -# define BN_FLG_STATIC_DATA 0x02 - -# define BN_BITS4 16 -# define BN_MASK2l (0xffff) -# define BN_MASK2h1 (0xffff8000L) -# define BN_MASK2h (0xffff0000L) - -# define LBITS(a) ((a) & BN_MASK2l) -# define HBITS(a) (((a) >> BN_BITS4) & BN_MASK2l) -# define L2HBITS(a) (((a) << BN_BITS4) & BN_MASK2) - -# define LLBITS(a) ((a) & BN_MASKl) -# define LHBITS(a) (((a) >> BN_BITS2) & BN_MASKl) -# define LL2HBITS(a) ((BN_ULLONG) ((a) & BN_MASKl) << BN_BITS2) - -# define mul64(l,h,bl,bh) \ - { \ - BN_ULONG m,m1,lt,ht; \ - \ - lt = l; \ - ht = h; \ - m = (bh) * (lt); \ - lt = (bl) * (lt); \ - m1 = (bl) * (ht); \ - ht = (bh) * (ht); \ - m = (m + m1) & BN_MASK2; ht += L2HBITS ((BN_ULONG) (m < m1)); \ - ht += HBITS (m); \ - m1 = L2HBITS (m); \ - lt = (lt + m1) & BN_MASK2; ht += (lt < m1); \ - (l) = lt; \ - (h) = ht; \ - } - - -# define mul(r,a,bl,bh,c) { \ - BN_ULONG l,h; \ - \ - h = (a); \ - l = LBITS (h); \ - h = HBITS (h); \ - mul64 (l,h,(bl),(bh)); \ - \ - /* non-multiply part */ \ - l += (c); h += ((l & BN_MASK2) < (c)); \ - (c) = h & BN_MASK2; \ - (r) = l & BN_MASK2; \ -} - - -# define mul_add(r,a,bl,bh,c) { \ - BN_ULONG l,h; \ - \ - h = (a); \ - l = LBITS (h); \ - h = HBITS (h); \ - mul64 (l,h,(bl),(bh)); \ - \ - /* non-multiply part */ \ - l = (l + (c)) & BN_MASK2; h += (l < (c)); \ - (c) = (r); \ - l = (l + (c)) & BN_MASK2; h += (l < (c)); \ - (c) = h & BN_MASK2; \ - (r) = l; \ -} - -# define sqr64(lo,ho,in) \ - { \ - BN_ULONG l,h,m; \ - \ - h = (in); \ - l = LBITS (h); \ - h = HBITS (h); \ - m = (l) * (h); \ - l *= l; \ - h *= h; \ - h += (m & BN_MASK2h1) >> (BN_BITS4 - 1); \ - m = (m & BN_MASK2l) << (BN_BITS4 + 1); \ - l = (l + m) & BN_MASK2; h += (l < m); \ - (lo) = l; \ - (ho) = h; \ - } - - -# define BN_window_bits_for_exponent_size(b) \ - ((b) > 671 ? 6 : \ - (b) > 239 ? 5 : \ - (b) > 79 ? 4 : \ - (b) > 23 ? 3 : 1) - -int BN_add_word (BIGNUM *a, BN_ULONG w); - -int BN_sub_word (BIGNUM *a, BN_ULONG w); - -# define ossl_toascii(c) (c) - -# define ossl_isxdigit(c) (ossl_ctype_check ((c), CTYPE_MASK_xdigit)) - -/* maximum precomputation table size for *variable* sliding windows */ -#define TABLE_SIZE 32 - -# define BN_mod(rem,m,d,ctx) BN_div (NULL,(rem),(m),(d),(ctx)) - -int BN_div (BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d, - BN_CTX *ctx); - -# define CTYPE_MASK_lower 0x1 -# define CTYPE_MASK_upper 0x2 -# define CTYPE_MASK_digit 0x4 -# define CTYPE_MASK_space 0x8 -# define CTYPE_MASK_xdigit 0x10 -# define CTYPE_MASK_blank 0x20 -# define CTYPE_MASK_cntrl 0x40 -# define CTYPE_MASK_graph 0x80 -# define CTYPE_MASK_print 0x100 -# define CTYPE_MASK_punct 0x200 -# define CTYPE_MASK_base64 0x400 -# define CTYPE_MASK_asn1print 0x800 - -static const unsigned short ctype_char_map[128] = { - /* 00 nul */ CTYPE_MASK_cntrl, - /* 01 soh */ CTYPE_MASK_cntrl, - /* 02 stx */ CTYPE_MASK_cntrl, - /* 03 etx */ CTYPE_MASK_cntrl, - /* 04 eot */ CTYPE_MASK_cntrl, - /* 05 enq */ CTYPE_MASK_cntrl, - /* 06 ack */ CTYPE_MASK_cntrl, - /* 07 \a */ CTYPE_MASK_cntrl, - /* 08 \b */ CTYPE_MASK_cntrl, - /* 09 \t */ CTYPE_MASK_blank | CTYPE_MASK_cntrl | CTYPE_MASK_space, - /* 0A \n */ CTYPE_MASK_cntrl | CTYPE_MASK_space, - /* 0B \v */ CTYPE_MASK_cntrl | CTYPE_MASK_space, - /* 0C \f */ CTYPE_MASK_cntrl | CTYPE_MASK_space, - /* 0D \r */ CTYPE_MASK_cntrl | CTYPE_MASK_space, - /* 0E so */ CTYPE_MASK_cntrl, - /* 0F si */ CTYPE_MASK_cntrl, - /* 10 dle */ CTYPE_MASK_cntrl, - /* 11 dc1 */ CTYPE_MASK_cntrl, - /* 12 dc2 */ CTYPE_MASK_cntrl, - /* 13 dc3 */ CTYPE_MASK_cntrl, - /* 14 dc4 */ CTYPE_MASK_cntrl, - /* 15 nak */ CTYPE_MASK_cntrl, - /* 16 syn */ CTYPE_MASK_cntrl, - /* 17 etb */ CTYPE_MASK_cntrl, - /* 18 can */ CTYPE_MASK_cntrl, - /* 19 em */ CTYPE_MASK_cntrl, - /* 1A sub */ CTYPE_MASK_cntrl, - /* 1B esc */ CTYPE_MASK_cntrl, - /* 1C fs */ CTYPE_MASK_cntrl, - /* 1D gs */ CTYPE_MASK_cntrl, - /* 1E rs */ CTYPE_MASK_cntrl, - /* 1F us */ CTYPE_MASK_cntrl, - /* 20 */ CTYPE_MASK_blank | CTYPE_MASK_print | CTYPE_MASK_space - | CTYPE_MASK_asn1print, - /* 21 ! */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 22 " */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 23 # */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 24 $ */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 25 % */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 26 & */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 27 ' */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct - | CTYPE_MASK_asn1print, - /* 28 ( */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct - | CTYPE_MASK_asn1print, - /* 29 ) */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct - | CTYPE_MASK_asn1print, - /* 2A * */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 2B + */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 2C , */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct - | CTYPE_MASK_asn1print, - /* 2D - */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct - | CTYPE_MASK_asn1print, - /* 2E . */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct - | CTYPE_MASK_asn1print, - /* 2F / */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 30 0 */ CTYPE_MASK_digit | CTYPE_MASK_graph | CTYPE_MASK_print - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 31 1 */ CTYPE_MASK_digit | CTYPE_MASK_graph | CTYPE_MASK_print - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 32 2 */ CTYPE_MASK_digit | CTYPE_MASK_graph | CTYPE_MASK_print - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 33 3 */ CTYPE_MASK_digit | CTYPE_MASK_graph | CTYPE_MASK_print - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 34 4 */ CTYPE_MASK_digit | CTYPE_MASK_graph | CTYPE_MASK_print - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 35 5 */ CTYPE_MASK_digit | CTYPE_MASK_graph | CTYPE_MASK_print - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 36 6 */ CTYPE_MASK_digit | CTYPE_MASK_graph | CTYPE_MASK_print - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 37 7 */ CTYPE_MASK_digit | CTYPE_MASK_graph | CTYPE_MASK_print - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 38 8 */ CTYPE_MASK_digit | CTYPE_MASK_graph | CTYPE_MASK_print - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 39 9 */ CTYPE_MASK_digit | CTYPE_MASK_graph | CTYPE_MASK_print - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 3A : */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct - | CTYPE_MASK_asn1print, - /* 3B ; */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 3C < */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 3D = */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 3E > */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 3F ? */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct - | CTYPE_MASK_asn1print, - /* 40 @ */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 41 A */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 42 B */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 43 C */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 44 D */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 45 E */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 46 F */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 47 G */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 48 H */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 49 I */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 4A J */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 4B K */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 4C L */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 4D M */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 4E N */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 4F O */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 50 P */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 51 Q */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 52 R */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 53 S */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 54 T */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 55 U */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 56 V */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 57 W */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 58 X */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 59 Y */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 5A Z */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_upper - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 5B [ */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 5C \ */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 5D ] */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 5E ^ */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 5F _ */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 60 ` */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 61 a */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 62 b */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 63 c */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 64 d */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 65 e */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 66 f */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_xdigit | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 67 g */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 68 h */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 69 i */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 6A j */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 6B k */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 6C l */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 6D m */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 6E n */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 6F o */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 70 p */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 71 q */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 72 r */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 73 s */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 74 t */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 75 u */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 76 v */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 77 w */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 78 x */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 79 y */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 7A z */ CTYPE_MASK_graph | CTYPE_MASK_lower | CTYPE_MASK_print - | CTYPE_MASK_base64 | CTYPE_MASK_asn1print, - /* 7B { */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 7C | */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 7D } */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 7E ~ */ CTYPE_MASK_graph | CTYPE_MASK_print | CTYPE_MASK_punct, - /* 7F del */ CTYPE_MASK_cntrl -}; - - -// maybe I should not use this is not - -#define MONT_WORD -// #define BN_MUL_COMBA - - -// aa = e, pp = d, mm = n -void test (char *aa, char *pp, char *mm); - -#endif /* openssl_test_h */ diff --git a/source/reference-test.c b/source/reference-test.c @@ -0,0 +1,252 @@ +// +// reference-test.c +// lib-gpu-verify +// +// Created by Cedric Zwahlen on 06.12.2023. +// + +#include "reference-test.h" + + +void ref_pairs_from_files(char *bases, unsigned long *b_off, + char *exponents, unsigned long *e_off, + char *moduli, unsigned long *m_off, + char *signatures, unsigned long *s_off, + unsigned long *pks, + unsigned long *n) { + + FILE *pkfile; + FILE *msfile; + + pkfile = fopen("lib-gpu-generate/publickey.txt", "r"); + msfile = fopen("lib-gpu-generate/msgsig.txt", "r"); + + if (pkfile == NULL || msfile == NULL) { + printf("Auxiliary files not found."); + abort(); + } + + + int i = 0; + + unsigned long b_offset = 0; + unsigned long e_offset = 0; + unsigned long m_offset = 0; + unsigned long s_offset = 0; + + while (1) { + + char n_buf[2048]; // need to be 0 + char e_buf[2048]; + + memset(n_buf, 0, 2048); + memset(e_buf, 0, 2048); + + unsigned long lastIndex = 0; + + if (fscanf(pkfile, "%s %s %lu", n_buf,e_buf, &lastIndex) == -1) + break; + + unsigned long n_buf_len = strlen(n_buf); + unsigned long e_buf_len = strlen(e_buf); + + memcpy(&moduli[m_offset], n_buf, n_buf_len); + memcpy(&exponents[e_offset], e_buf, e_buf_len); + + m_off[i] = m_offset; + e_off[i] = e_offset; + + m_offset += n_buf_len + 1; + e_offset += e_buf_len + 1; + + pks[i] = lastIndex; + + i++; + + // break; // testing with just one + } + + int j = 0; + + while (1) { + + char m_buf[2048]; // temp storage, large enough + char s_buf[2048]; + + memset(m_buf, 0, 2048); + memset(s_buf, 0, 2048); + + if (fscanf(msfile, "%s %s", m_buf,s_buf) == -1) + break; + + unsigned long m_buf_len = strlen(m_buf); + unsigned long s_buf_len = strlen(s_buf); + + memcpy(&bases[b_offset], m_buf, m_buf_len); + memcpy(&signatures[s_offset], s_buf, s_buf_len); + + b_off[j] = b_offset; + s_off[j] = s_offset; + + b_offset += m_buf_len + 1; + s_offset += s_buf_len + 1; + + j++; + + // break; // testing with just one + + } + + fclose(pkfile); + fclose(msfile); + + *n = j; +} + +gcry_sexp_t sexp_from_string(char* str, const char *format) { + + gcry_sexp_t sexp; + + gcry_mpi_t mpi = gcry_mpi_new((int)strlen(str) * 8); + //size_t scanned = 0; + gcry_mpi_scan(&mpi, GCRYMPI_FMT_HEX, str, 0, NULL); + + size_t errOff = 0; + gcry_sexp_build(&sexp,&errOff,format,mpi); + + return sexp; +} + +gcry_sexp_t sexp_from_string_key(char* str_1, char* str_2, const char *format) { + + gcry_sexp_t sexp; + + gcry_mpi_t mpi_1 = gcry_mpi_new((int)strlen(str_1) * 8); + //size_t scanned = 0; + gcry_mpi_scan(&mpi_1, GCRYMPI_FMT_HEX, str_1, 0, NULL); + + gcry_mpi_t mpi_2 = gcry_mpi_new((int)strlen(str_2) * 8); + //size_t scanned = 0; + gcry_mpi_scan(&mpi_2, GCRYMPI_FMT_HEX, str_2, 0, NULL); + + size_t errOff = 0; + gcry_sexp_build(&sexp,&errOff,format,mpi_1,mpi_2); + + return sexp; +} + +int reference_tests(void) { + + // setup_gcry(); + + unsigned long pairs = number_of_pairs(); + + unsigned long str_sz = (2048) * pairs; + + + char *b = malloc(str_sz); + char *e = malloc(str_sz); + char *m = malloc(str_sz); + char *s = malloc(str_sz); + + unsigned long *b_off = malloc(str_sz); + unsigned long *e_off = malloc(str_sz); + unsigned long *m_off = malloc(str_sz); + unsigned long *s_off = malloc(str_sz); + + memset(b, 0, str_sz); + memset(e, 0, str_sz); + memset(m, 0, str_sz); + memset(s, 0, str_sz); + + memset(b_off, 0, str_sz); + memset(e_off, 0, str_sz); + memset(m_off, 0, str_sz); + memset(s_off, 0, str_sz); + + unsigned long *pks = malloc(str_sz); + memset(pks, 0, str_sz); + + ref_pairs_from_files(b, b_off, e, e_off, m, m_off, s, s_off, pks, + &pairs); + + unsigned long pk = 0; + + while (1) { + if (pks[pk] + 1 == pairs) + break; + pk++; + } + + + + gcry_sexp_t *m_sexps = malloc(pairs * sizeof(gcry_sexp_t)); + gcry_sexp_t *s_sexps = malloc(pairs * sizeof(gcry_sexp_t)); + gcry_sexp_t *key_sexps = malloc((pk + 1) * sizeof(gcry_sexp_t)); + + for (int i = 0; i < pairs; i++) { + + m_sexps[i] = sexp_from_string(&b[b_off[i]], "(data (flags raw) (value %m))"); // message format (for comparison) + + s_sexps[i] = sexp_from_string(&s[s_off[i]], "(sig-val (rsa (s %m)))"); // signature format + } + + + for (int i = 0; i <= pk; i++) { + + key_sexps[i] = sexp_from_string_key(&m[m_off[i]], &e[e_off[i]], "(public-key (rsa (n %m) (e %m)))" ); // pub key data + + } + + unsigned long result = 0; + + struct timespec t1, t2; + + printf("VERIFYING %lu SIGNATURES...\n", pairs); + + clock_gettime(CLOCK_REALTIME, &t1); + + pk = 0; // reuse pk + + for (int i = 0; i < pairs; i++) { + + while (1) { + if (pks[pk] >= i) + break; + pk++; + } + + if ( gcry_pk_verify(s_sexps[i], m_sexps[i], key_sexps[pk]) == 0 ) + result += 1; + + } + + clock_gettime(CLOCK_REALTIME, &t2); + + printf("CPU (Reference) verification took %ld.%06ld s\n", ( t2.tv_nsec < t1.tv_nsec ? t2.tv_sec - (t1.tv_sec + 1) : t2.tv_sec - t1.tv_sec ), ( t2.tv_nsec < t1.tv_nsec ? ((999999999 - t1.tv_nsec) + t2.tv_nsec) : (t2.tv_nsec - t1.tv_nsec) ) / 1000); + + if (result == pairs) { + printf("VERIFICATION RESULT: %lu - OK\n\n",result); + } else { + printf("VERIFICATION RESULT: %lu - NOT OK\n\n",result); + } + + + free(b); + free(e); + free(m); + free(s); + + free(b_off); + free(e_off); + free(m_off); + free(s_off); + + free(pks); + + free(m_sexps); + free(s_sexps); + free(key_sexps); + + return result == pairs ? 1 : 0; +} diff --git a/source/reference-test.h b/source/reference-test.h @@ -0,0 +1,15 @@ +// +// reference-test.h +// lib-gpu-verify +// +// Created by Cedric Zwahlen on 06.12.2023. +// + +#ifndef reference_test_h +#define reference_test_h + +#include "util.h" + +int reference_tests(void); + +#endif /* reference_test_h */ diff --git a/source/rsa-test.c b/source/rsa-test.c @@ -1,363 +1,182 @@ -// -// rsa-test.c -// hello -// -// Created by Cedric Zwahlen on 28.09.2023. -// +/* + * lib-gpu-verify + * + * This software contains code derived from or inspired by the BigDigit library, + * <http://www.di-mgt.com.au/bigdigits.html> + * which is distributed under the Mozilla Public License, version 2.0. + * + * The original code and modifications made to it are subject to the terms and + * conditions of the Mozilla Public License, version 2.0. A copy of the + * MPL license can be obtained at + * https://www.mozilla.org/en-US/MPL/2.0/. + * + * Changes and additions to the original code are as follows: + * - Copied some functions of the BigDigit library into this file, to convert strings read from files to BigDigit type numbers. + * + * Contributors: + * - Cedric Zwahlen cedric.zwahlen@bfh.ch + * + * Please note that this software is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Mozilla Public License, version 2.0, for the specific language + * governing permissions and limitations under the License. + */ #include "rsa-test.h" -#include "big-int-test.h" -#include "montgomery-test.h" +#define BITS_PER_DIGIT 32 -#include <errno.h> -#include <string.h> -#include <fcntl.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <stdbool.h> -#include <unistd.h> +#define MAX_ALLOC_SIZE 256 -#define NEED_LIBGCRYPT_VERSION "1.10.1" +#define BYTES_PER_DIGIT (BITS_PER_DIGIT / 8) typedef uint32_t DIGIT_T; // for gpu might need to be half? is that half? -void setup_gcry(void) { - /* Version check should be the very first call because it - makes sure that important subsystems are initialized. - #define NEED_LIBGCRYPT_VERSION to the minimum required version. */ - if (!gcry_check_version (NEED_LIBGCRYPT_VERSION)) - { - fprintf (stderr, "libgcrypt is too old (need %s, have %s)\n", - NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL)); - exit (2); } - /* Disable secure memory. */ - gcry_control (GCRYCTL_DISABLE_SECMEM, 0); - /* ... If required, other initialization goes here. */ - /* Tell Libgcrypt that initialization has completed. */ - gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); +//size_t retSize_3 = sizeof(cl_ulong); +//cl_ulong max_stor = 0; +//clGetDeviceInfo(device_id, CL_DEVICE_MAX_MEM_ALLOC_SIZE, 0, NULL, &retSize_3); +//clGetDeviceInfo(device_id, CL_DEVICE_MAX_MEM_ALLOC_SIZE, retSize_3, &max_stor, &retSize_3); -} +//printf("max memory: %llu\n", max_stor); -cl_platform_id -select_platform (unsigned int offset, - bool print_platforms) + +size_t mpSizeof(const DIGIT_T a[], size_t ndigits) { - cl_uint max_platforms = 4; - cl_platform_id platforms[max_platforms]; - cl_uint num_platforms; - cl_int rplat; - - rplat = clGetPlatformIDs (max_platforms, - platforms, - &num_platforms); - if (CL_SUCCESS != rplat) - { - fprintf (stderr, - "Error: Failed to lookup platforms! (%d)\n", - rplat); - exit (1); - } - if (print_platforms) - { - for (unsigned int i = 0; i<num_platforms; i++) + while(ndigits--) { - char buf[128]; - size_t rbuf; - static struct - { - cl_platform_info cpi; - const char *name; - } param[] = { - { CL_PLATFORM_PROFILE, "profile" }, - { CL_PLATFORM_VENDOR, "vendor" }, - { CL_PLATFORM_NAME, "name" }, - { CL_PLATFORM_EXTENSIONS, "extensions" }, - { 0, NULL } - }; - - for (unsigned int j = 0; NULL != param[j].name; j++) - { - cl_int err; - - err = clGetPlatformInfo (platforms[i], - param[j].cpi, - sizeof (buf), - buf, - &rbuf); - if (err != CL_SUCCESS) - { - fprintf (stderr, - "Error: Failed to get platform info for %s! (%d)\n", - param[j].name, - err); - } - else - { - printf ("#%u %s %.*s\n", - i, - param[j].name, - (int) rbuf, - buf); - } - } + if (a[ndigits] != 0) + return (++ndigits); } - exit (0); - } - if (offset >= num_platforms) - { - fprintf (stderr, - "Found only %u platforms\n", - (unsigned int) num_platforms); - exit (1); - } - return platforms[offset]; + return 0; } +volatile DIGIT_T mpSetZero(volatile DIGIT_T a[], size_t ndigits) +{ /* Sets a = 0 */ -cl_device_id -select_device (cl_platform_id platform) -{ - cl_device_id device_id; - char buf[1024]; - size_t len; - cl_int err; - cl_uint address_bits = 0; - - err = clGetDeviceIDs (platform, - CL_DEVICE_TYPE_ALL, - 1, /* 1 device */ - &device_id, - NULL); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to find a device! (%d)\n", - err); - exit (1); - } - - err = clGetDeviceInfo (device_id, - CL_DRIVER_VERSION, - sizeof (buf), - buf, - &len); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to get device driver version! (%d)\n", - err); - exit (1); - } - printf ("Driver version: %.*s\n", - (int) len, - buf); - clGetDeviceInfo (device_id, - CL_DEVICE_ADDRESS_BITS, - sizeof (address_bits), - &address_bits, - &len); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to get device address bits! (%d)\n", - err); - exit (1); - } - printf ("device address bits: %d\n", - (int) address_bits); - return device_id; -} + /* Prevent optimiser ignoring this */ + volatile DIGIT_T optdummy; + volatile DIGIT_T *p = a; -void -logger (const char *errinfo, - const void *private_info, - size_t cb, - void *user_data) -{ - fprintf (stderr, - "<OpenCL>: %s\n", - errinfo); + while (ndigits--) + a[ndigits] = 0; + + optdummy = *p; + return optdummy; } -cl_context -create_compute_context (cl_device_id device_id) +size_t uiceil(double x) +/* Returns ceil(x) as a non-negative integer or 0 if x < 0 */ { - cl_int err; - cl_context context; - - context = clCreateContext (NULL, - 1, - &device_id, - &logger, NULL, - &err); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to create a compute context (%d)\n", - err); - exit (1); - } - return context; + size_t c; + + if (x < 0) return 0; + c = (size_t)x; + if ((x - c) > 0.0) + c++; + + return c; } -cl_command_queue -create_command_queue (cl_device_id device_id, - cl_context context) -{ - cl_int err; - cl_command_queue commands; - - commands = clCreateCommandQueue (context, - device_id, - 0, /* properties */ - &err); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to create a command queue!\n (%d)", - err); - exit (1); - } - return commands; +volatile uint8_t zeroise_bytes(volatile void *v, size_t n) +{ /* Zeroise byte array b and make sure optimiser does not ignore this */ + volatile uint8_t optdummy; + volatile uint8_t *b = (uint8_t*)v; + while(n--) + b[n] = 0; + optdummy = *b; + return optdummy; } -cl_program -compile_program (cl_device_id device_id, - cl_context context, - const char *sourcefile) +size_t mpConvFromOctets(DIGIT_T a[], size_t ndigits, const unsigned char *c, size_t nbytes) +/* Converts nbytes octets into big digit a of max size ndigits + Returns actual number of digits set (may be larger than mpSizeof) +*/ { - cl_program program; - int fd; - void *code; - struct stat ss; - cl_int err; - - fd = open (sourcefile, - O_RDONLY); - if (-1 == fd) - { - fprintf (stderr, - "Failed to open %s: %s\n", - sourcefile, - strerror (errno)); - exit (1); - } - if (0 != fstat (fd, - &ss)) - { - fprintf (stderr, - "Failed to stat %s: %s\n", - sourcefile, - strerror (errno)); - close (fd); - exit (1); - } - code = mmap (NULL, - ss.st_size, - PROT_READ, - MAP_PRIVATE, - fd, - 0 /* offset */); - close (fd); - { - size_t sz = ss.st_size; - - program = clCreateProgramWithSource (context, - 1, /* 1 source file */ - (const char **) &code, - &sz, - &err); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to create compute program (%d)!\n", - err); - munmap (code, - ss.st_size); - exit (1); - } - } - err = clBuildProgram (program, - 0, /* number of devices */ - NULL, /* devices */ - NULL, /* options (char *) */ - NULL, /* callback */ - NULL); - munmap (code, - ss.st_size); - if (CL_SUCCESS != err) - { - size_t len; - char buffer[2048]; - - fprintf (stderr, - "Error: Failed to build program executable (%d)!\n", - err); - err = clGetProgramBuildInfo (program, - device_id, - CL_PROGRAM_BUILD_LOG, - sizeof(buffer), - buffer, - &len); - if (CL_SUCCESS != err) + size_t i; + int j, k; + DIGIT_T t; + + mpSetZero(a, ndigits); + //memset(a, 0, ndigits); + + /* Read in octets, least significant first */ + /* i counts into big_d, j along c, and k is # bits to shift */ + for (i = 0, j = (int)nbytes - 1; i < ndigits && j >= 0; i++) { - fprintf (stderr, - "Error: could not get build logs (%d)!\n", - err); - exit (1); + t = 0; + for (k = 0; j >= 0 && k < BITS_PER_DIGIT; j--, k += 8) + t |= ((DIGIT_T)c[j]) << k; + a[i] = t; } - fprintf (stderr, - "<clBuild>: %.*s\n", - (int) len, - buffer); - exit (1); - } - return program; + + return i; } -cl_kernel -create_kernel (cl_program program, - const char *name) +size_t mpConvFromHex(DIGIT_T a[], size_t ndigits, const char *s) +/* Convert a string in hexadecimal format to a big digit. + Return actual number of digits set (may be larger than mpSizeof). + Just ignores invalid characters in s. +*/ { - cl_kernel kernel; - cl_int err; - - kernel = clCreateKernel (program, - name, - &err); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to create compute kernel %s: %d!\n", - name, - err); - exit (1); - } - return kernel; -} + uint8_t newdigits[MAX_ALLOC_SIZE*2]; + + size_t newlen; + size_t n; + unsigned long t; + size_t i, j; + + mpSetZero(a, ndigits); + //memset(&a, 0, ndigits); + + /* Create some temp storage for int values */ + n = strlen(s); + if (0 == n) return 0; + newlen = uiceil(n * 0.5); /* log(16)/log(256)=0.5 */ + //ALLOC_BYTES(newdigits, newlen); + memset(&newdigits, 0, newlen); + + /* Work through zero-terminated string */ + for (i = 0; s[i]; i++) + { + t = s[i]; + if ((t >= '0') && (t <= '9')) t = (t - '0'); + else if ((t >= 'a') && (t <= 'f')) t = (t - 'a' + 10); + else if ((t >= 'A') && (t <= 'F')) t = (t - 'A' + 10); + else continue; + for (j = newlen; j > 0; j--) + { + t += (unsigned long)newdigits[j-1] << 4; + newdigits[j-1] = (unsigned char)(t & 0xFF); + t >>= 8; + } + } + + /* Convert bytes to big digits */ + n = mpConvFromOctets(a, ndigits, newdigits, newlen); + + /* Clean up */ + //FREE_BYTES(newdigits, newlen); + + memset(&newdigits, 0, newlen); + + return n; +} -//size_t retSize_3 = sizeof(cl_ulong); -//cl_ulong max_stor = 0; -//clGetDeviceInfo(device_id, CL_DEVICE_MAX_MEM_ALLOC_SIZE, 0, NULL, &retSize_3); -//clGetDeviceInfo(device_id, CL_DEVICE_MAX_MEM_ALLOC_SIZE, retSize_3, &max_stor, &retSize_3); -//printf("max memory: %llu\n", max_stor); -void -pairs_from_files(void *bases, unsigned long *b_len, - void *exponents, unsigned long *e_len, - void *moduli, unsigned long *m_len, - void *signatures, unsigned long *s_len, - unsigned long *pks, - unsigned long *n - ) { +void pairs_from_files(void *bases, unsigned long *b_len, + void *exponents, unsigned long *e_len, + void *moduli, unsigned long *m_len, + void *signatures, unsigned long *s_len, + unsigned long *pks, + unsigned long *n + ) { FILE *pkfile; FILE *msfile; @@ -365,6 +184,11 @@ pairs_from_files(void *bases, unsigned long *b_len, pkfile = fopen("lib-gpu-generate/publickey.txt", "r"); msfile = fopen("lib-gpu-generate/msgsig.txt", "r"); + DIGIT_T *bases_t = bases; + DIGIT_T *exponents_t = exponents; + DIGIT_T *moduli_t = moduli; + DIGIT_T *signatures_t = signatures; + if (pkfile == NULL || msfile == NULL) { printf("Auxiliary files not found."); abort(); @@ -393,6 +217,7 @@ pairs_from_files(void *bases, unsigned long *b_len, mpSetZero(exponent, sz*2); mpSetZero(modulus, sz*2); + mpConvFromHex(exponent, e_buf_len, e_buf); mpConvFromHex(modulus, n_buf_len, n_buf); @@ -401,8 +226,8 @@ pairs_from_files(void *bases, unsigned long *b_len, e_len[i] = (i == 0 ? 0 : e_len[i - 1]) + mpSizeof(exponent, sz*2); m_len[i] = (i == 0 ? 0 : m_len[i - 1]) + max_len; - memcpy(&moduli[i == 0 ? 0 : (m_len[i - 1] * sizeof(DIGIT_T))], modulus, ( m_len[i] - (i == 0 ? 0 : m_len[i - 1]) ) * sizeof(DIGIT_T)); - memcpy(&exponents[i == 0 ? 0 : (e_len[i - 1] * sizeof(DIGIT_T))], exponent, ( e_len[i] - (i == 0 ? 0 : e_len[i - 1]) ) * sizeof(DIGIT_T)); + memcpy(&moduli_t[i == 0 ? 0 : (m_len[i - 1])], modulus, ( m_len[i] - (i == 0 ? 0 : m_len[i - 1]) ) * sizeof(DIGIT_T)); + memcpy(&exponents_t[i == 0 ? 0 : (e_len[i - 1])], exponent, ( e_len[i] - (i == 0 ? 0 : e_len[i - 1]) ) * sizeof(DIGIT_T)); pks[i] = lastIndex; @@ -438,8 +263,8 @@ pairs_from_files(void *bases, unsigned long *b_len, - memcpy(&bases[j == 0 ? 0 : (b_len[j - 1] * sizeof(DIGIT_T))], base, ( b_len[j] - (j == 0 ? 0 : b_len[j - 1]) ) * sizeof(DIGIT_T)); - memcpy(&signatures[j == 0 ? 0 : (s_len[j - 1] * sizeof(DIGIT_T))], signature, ( s_len[j] - (j == 0 ? 0 : s_len[j - 1]) ) * sizeof(DIGIT_T)); + memcpy(&bases_t[j == 0 ? 0 : (b_len[j - 1])], base, ( b_len[j] - (j == 0 ? 0 : b_len[j - 1]) ) * sizeof(DIGIT_T)); + memcpy(&signatures_t[j == 0 ? 0 : (s_len[j - 1])], signature, ( s_len[j] - (j == 0 ? 0 : s_len[j - 1]) ) * sizeof(DIGIT_T)); j++; @@ -449,34 +274,10 @@ pairs_from_files(void *bases, unsigned long *b_len, fclose(msfile); *n = j; + } -unsigned long number_of_pairs(void) { - - struct stat ss; - - int msfile = open("lib-gpu-generate/msgsig.txt", O_RDONLY); - - if (-1 == msfile) - { - fprintf (stderr, - "Failed to open: err %s\n", - strerror (errno)); - exit (1); - } - - fstat(msfile, &ss); - - unsigned long len_f = ss.st_size; - unsigned long len_sig = (2048 / 8) * 2 + 1; // this is the size of a 2048 bit signature in the file - - unsigned long n_min = len_f / (len_sig + 3); // if each message was only one character, then this would be the maximum amount of signatures that could be in the file – use this estimate to allocate storage for the signatures - - close(msfile); - - return n_min; -} // MARK: non-mont @@ -665,7 +466,7 @@ int verify_pairs_with_opencl(void *bases, unsigned long *b_len, clReleaseContext(context); return 0; - + } int rsa_tests(void) { @@ -708,8 +509,7 @@ int rsa_tests(void) { t, x, pks, &pairs); // this returns the actual amount of pairs unsigned long result = 0; - - pairs = 1; // FIXME: forced to 1 to not delay forever! + printf("VERIFYING %lu SIGNATURES...\n", pairs); verify_pairs_with_opencl(q, u, @@ -742,154 +542,3 @@ int rsa_tests(void) { return 0; } - -// MARK: ref - -gcry_sexp_t sexp_from_string(char* str, const char *format) { - - gcry_sexp_t sexp; - - gcry_mpi_t mpi = gcry_mpi_new((int)strlen(str) * 8); - //size_t scanned = 0; - gcry_mpi_scan(&mpi, GCRYMPI_FMT_HEX, str, 0, NULL); - - size_t errOff = 0; - gcry_sexp_build(&sexp,&errOff,format,mpi); - - return sexp; -} - -gcry_sexp_t sexp_from_string_key(char* str_1, char* str_2, const char *format) { - - gcry_sexp_t sexp; - - gcry_mpi_t mpi_1 = gcry_mpi_new((int)strlen(str_1) * 8); - //size_t scanned = 0; - gcry_mpi_scan(&mpi_1, GCRYMPI_FMT_HEX, str_1, 0, NULL); - - gcry_mpi_t mpi_2 = gcry_mpi_new((int)strlen(str_2) * 8); - //size_t scanned = 0; - gcry_mpi_scan(&mpi_2, GCRYMPI_FMT_HEX, str_2, 0, NULL); - - size_t errOff = 0; - gcry_sexp_build(&sexp,&errOff,format,mpi_1,mpi_2); - - return sexp; -} - -int reference_tests(void) { - - // setup_gcry(); - - unsigned long pairs = number_of_pairs(); - - unsigned long str_sz = (2048) * pairs; - - - char *b = malloc(str_sz); - char *e = malloc(str_sz); - char *m = malloc(str_sz); - char *s = malloc(str_sz); - - unsigned long *b_off = malloc(str_sz); - unsigned long *e_off = malloc(str_sz); - unsigned long *m_off = malloc(str_sz); - unsigned long *s_off = malloc(str_sz); - - memset(b, 0, str_sz); - memset(e, 0, str_sz); - memset(m, 0, str_sz); - memset(s, 0, str_sz); - - memset(b_off, 0, str_sz); - memset(e_off, 0, str_sz); - memset(m_off, 0, str_sz); - memset(s_off, 0, str_sz); - - unsigned long *pks = malloc(str_sz); - memset(pks, 0, str_sz); - - mont_pairs_from_files(b, b_off, e, e_off, m, m_off, s, s_off, pks, - &pairs); - - unsigned long pk = 0; - - while (1) { - if (pks[pk] + 1 == pairs) - break; - pk++; - } - - - - gcry_sexp_t *m_sexps = malloc(pairs * sizeof(gcry_sexp_t)); - gcry_sexp_t *s_sexps = malloc(pairs * sizeof(gcry_sexp_t)); - gcry_sexp_t *key_sexps = malloc((pk + 1) * sizeof(gcry_sexp_t)); - - for (int i = 0; i < pairs; i++) { - - m_sexps[i] = sexp_from_string(&b[b_off[i]], "(data (flags raw) (value %m))"); // message format (for comparison) - - s_sexps[i] = sexp_from_string(&s[s_off[i]], "(sig-val (rsa (s %m)))"); // signature format - } - - - for (int i = 0; i <= pk; i++) { - - key_sexps[i] = sexp_from_string_key(&m[m_off[i]], &e[e_off[i]], "(public-key (rsa (n %m) (e %m)))" ); // pub key data - - } - - unsigned long result = 0; - - struct timespec t1, t2; - - printf("VERIFYING %lu SIGNATURES...\n", pairs); - - clock_gettime(CLOCK_REALTIME, &t1); - - pk = 0; // reuse pk - - for (int i = 0; i < pairs; i++) { - - while (1) { - if (pks[pk] >= i) - break; - pk++; - } - - if ( gcry_pk_verify(s_sexps[i], m_sexps[i], key_sexps[pk]) == 0 ) - result += 1; - - } - - clock_gettime(CLOCK_REALTIME, &t2); - - printf("CPU (Reference) verification took %ld.%06ld s\n", ( t2.tv_nsec < t1.tv_nsec ? t2.tv_sec - (t1.tv_sec + 1) : t2.tv_sec - t1.tv_sec ), ( t2.tv_nsec < t1.tv_nsec ? ((999999999 - t1.tv_nsec) + t2.tv_nsec) : (t2.tv_nsec - t1.tv_nsec) ) / 1000); - - if (result == pairs) { - printf("VERIFICATION RESULT: %lu - OK\n\n",result); - } else { - printf("VERIFICATION RESULT: %lu - NOT OK\n\n",result); - } - - - free(b); - free(e); - free(m); - free(s); - - free(b_off); - free(e_off); - free(m_off); - free(s_off); - - free(pks); - - free(m_sexps); - free(s_sexps); - free(key_sexps); - - return result == pairs ? 1 : 0; -} - diff --git a/source/rsa-test.h b/source/rsa-test.h @@ -1,6 +1,6 @@ // // rsa-test.h -// hello +// lib-gpu-verify // // Created by Cedric Zwahlen on 28.09.2023. // @@ -8,65 +8,13 @@ #ifndef rsa_test_h #define rsa_test_h -#include <stdio.h> - - -#if __APPLE__ -#include <OpenCL/opencl.h> -#else -#include <CL/opencl.h> -#endif +#include "util.h" #include "ctype.h" -#include "time.h" - -#include <stdbool.h> -#include <gcrypt.h> - -void setup_gcry(void); int rsa_tests(void); -int mont_rsa_tests(void); - - -//void bigNum_tests(unsigned char* n, unsigned char* e, unsigned char* d); - -void montgomery_test(void); - -//int verify(unsigned char* sign, unsigned char* ee, unsigned char* nn, unsigned char* mm); - -int reference_tests(void); -unsigned long number_of_pairs(void); - -//MARK: put these in another file - -cl_platform_id select_platform (unsigned int offset, bool print_platforms); - -cl_device_id select_device (cl_platform_id platform); - -void -logger (const char *errinfo, - const void *private_info, - size_t cb, - void *user_data); - -cl_context -create_compute_context (cl_device_id device_id); - -cl_command_queue -create_command_queue (cl_device_id device_id, - cl_context context); - -cl_program -compile_program (cl_device_id device_id, - cl_context context, - const char *sourcefile); - -cl_kernel -create_kernel (cl_program program, - const char *name); #endif /* rsa_test_h */ diff --git a/source/square.c b/source/square.c @@ -1,555 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file square.c - * @brief square floats on a GPU. Sample program to start testing on OpenCL. - * @author Christian Grothoff - */ -#include <stdio.h> -#include <stdlib.h> -#include <CL/opencl.h> -#include <errno.h> -#include <string.h> -#include <fcntl.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <stdbool.h> -#include <unistd.h> - - -static cl_platform_id -select_platform (unsigned int offset, - bool print_platforms) -{ - cl_uint max_platforms = 4; - cl_platform_id platforms[max_platforms]; - cl_uint num_platforms; - cl_int rplat; - - rplat = clGetPlatformIDs (max_platforms, - platforms, - &num_platforms); - if (CL_SUCCESS != rplat) - { - fprintf (stderr, - "Error: Failed to lookup platforms! (%d)\n", - rplat); - exit (1); - } - if (print_platforms) - { - for (unsigned int i = 0; i<num_platforms; i++) - { - char buf[128]; - size_t rbuf; - static struct - { - cl_platform_info cpi; - const char *name; - } param[] = { - { CL_PLATFORM_PROFILE, "profile" }, - { CL_PLATFORM_VENDOR, "vendor" }, - { CL_PLATFORM_NAME, "name" }, - { CL_PLATFORM_EXTENSIONS, "extensions" }, - { 0, NULL } - }; - - for (unsigned int j = 0; NULL != param[j].name; j++) - { - cl_int err; - - err = clGetPlatformInfo (platforms[i], - param[j].cpi, - sizeof (buf), - buf, - &rbuf); - if (err != CL_SUCCESS) - { - fprintf (stderr, - "Error: Failed to get platform info for %s! (%d)\n", - param[j].name, - err); - } - else - { - printf ("#%u %s %.*s\n", - i, - param[j].name, - (int) rbuf, - buf); - } - } - } - exit (0); - } - if (offset >= num_platforms) - { - fprintf (stderr, - "Found only %u platforms\n", - (unsigned int) num_platforms); - exit (1); - } - return platforms[offset]; -} - - -static cl_device_id -select_device (cl_platform_id platform) -{ - cl_device_id device_id; - char buf[1024]; - size_t len; - cl_int err; - cl_uint address_bits = 0; - - err = clGetDeviceIDs (platform, - CL_DEVICE_TYPE_ALL, - 1, /* 1 device */ - &device_id, - NULL); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to find a device! (%d)\n", - err); - exit (1); - } - - err = clGetDeviceInfo (device_id, - CL_DRIVER_VERSION, - sizeof (buf), - buf, - &len); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to get device driver version! (%d)\n", - err); - exit (1); - } - printf ("Driver version: %.*s\n", - (int) len, - buf); - clGetDeviceInfo (device_id, - CL_DEVICE_ADDRESS_BITS, - sizeof (address_bits), - &address_bits, - &len); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to get device address bits! (%d)\n", - err); - exit (1); - } - printf ("device address bits: %d\n", - (int) address_bits); - return device_id; -} - - -static void -logger (const char *errinfo, - const void *private_info, - size_t cb, - void *user_data) -{ - fprintf (stderr, - "<OpenCL>: %s\n", - errinfo); -} - - -static cl_context -create_compute_context (cl_device_id device_id) -{ - cl_int err; - cl_context context; - - context = clCreateContext (NULL, - 1, - &device_id, - &logger, NULL, - &err); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to create a compute context (%d)\n", - err); - exit (1); - } - return context; -} - - -static cl_command_queue -create_command_queue (cl_device_id device_id, - cl_context context) -{ - cl_int err; - cl_command_queue commands; - - commands = clCreateCommandQueue (context, - device_id, - 0, /* properties */ - &err); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to create a command queue!\n (%d)", - err); - exit (1); - } - return commands; -} - - -static cl_program -compile_program (cl_device_id device_id, - cl_context context, - const char *sourcefile) -{ - cl_program program; - int fd; - void *code; - struct stat ss; - cl_int err; - - fd = open (sourcefile, - O_RDONLY); - if (-1 == fd) - { - fprintf (stderr, - "Failed to open %s: %s\n", - sourcefile, - strerror (errno)); - exit (1); - } - if (0 != fstat (fd, - &ss)) - { - fprintf (stderr, - "Failed to stat %s: %s\n", - sourcefile, - strerror (errno)); - close (fd); - exit (1); - } - code = mmap (NULL, - ss.st_size, - PROT_READ, - MAP_PRIVATE, - fd, - 0 /* offset */); - close (fd); - { - size_t sz = ss.st_size; - - program = clCreateProgramWithSource (context, - 1, /* 1 source file */ - (const char **) &code, - &sz, - &err); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to create compute program (%d)!\n", - err); - munmap (code, - ss.st_size); - exit (1); - } - } - err = clBuildProgram (program, - 0, /* number of devices */ - NULL, /* devices */ - NULL, /* options (char *) */ - NULL, /* callback */ - NULL); - munmap (code, - ss.st_size); - if (CL_SUCCESS != err) - { - size_t len; - char buffer[2048]; - - fprintf (stderr, - "Error: Failed to build program executable (%d)!\n", - err); - err = clGetProgramBuildInfo (program, - device_id, - CL_PROGRAM_BUILD_LOG, - sizeof(buffer), - buffer, - &len); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: could not get build logs (%d)!\n", - err); - exit (1); - } - fprintf (stderr, - "<clBuild>: %.*s\n", - (int) len, - buffer); - exit (1); - } - return program; -} - - -static cl_kernel -create_kernel (cl_program program, - const char *name) -{ - cl_kernel kernel; - cl_int err; - - kernel = clCreateKernel (program, - name, - &err); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to create compute kernel %s: %d!\n", - name, - err); - exit (1); - } - return kernel; -} - - -static void -square_with_kernel (cl_device_id device_id, - cl_context context, - cl_command_queue commands, - cl_kernel kernel, - unsigned int problem_size, - const float problem[static problem_size], - float solution[static problem_size]) -{ - // Create the input and output arrays in device memory for our calculation - cl_int err; - cl_mem sq_input; - cl_mem sq_output; - size_t local; - - sq_input = clCreateBuffer (context, - CL_MEM_READ_ONLY - | CL_MEM_USE_HOST_PTR, - sizeof(float) * problem_size, - (void *) problem, - &err); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to create input buffer: %d!\n", - err); - exit (1); - } - sq_output = clCreateBuffer (context, - CL_MEM_WRITE_ONLY, - sizeof(float) * problem_size, - NULL /* no previous data */, - &err); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to create output buffer: %d!\n", - err); - exit (1); - } -#if DEAD - // Write our data set into the input array in device memory - err = clEnqueueWriteBuffer (commands, - sq_input, - CL_TRUE, - 0, - sizeof(DIGIT_T) * s_len[n - 1], - signatures, - 0, - NULL, - NULL); - if (err != CL_SUCCESS) - { - printf ("Error: Failed to write to source array!\n"); - exit (1); - } -#endif - err = 0; - err = clSetKernelArg (kernel, 0, sizeof(cl_mem), &sq_input); - err |= clSetKernelArg (kernel, 1, sizeof(cl_mem), &sq_output); - err |= clSetKernelArg (kernel, 2, sizeof(problem_size), &problem_size); - if (err != CL_SUCCESS) - { - fprintf (stderr, - "Failed to set kernel arguments! %d\n", - err); - exit (1); - } - - // Get the maximum work group size for executing the kernel on the device - // FIXME: do this once when the kernel is created, not every time we run it! - err = clGetKernelWorkGroupInfo (kernel, - device_id, - CL_KERNEL_WORK_GROUP_SIZE, - sizeof(local), - &local, - NULL); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to retrieve kernel work group info! %d\n", err); - exit (1); - } - - // Execute the kernel over the entire range of our 1d input data set - // using the maximum number of work group items for this device - { - size_t global_problem_size = problem_size; - - { - static int once; - - if (0 == once) - { - once = 1; - fprintf (stderr, - "Global problem size: %llu - local problem size: %llu\n", - (unsigned long long) problem_size, - (unsigned long long) local); - } - } - local = (local > problem_size) ? problem_size : local; - err = clEnqueueNDRangeKernel (commands, - kernel, - 1 /* work_dim */, - NULL /* global work offset */, - &global_problem_size, /* array of work_dim values */ - &local, - 0, /* num_events */ - NULL, /* event wait list */ - NULL /* event */); - } - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to execute kernel (%d)!\n", - err); - exit (1); - } - - // Wait for the command commands to get serviced before reading back results - clFinish (commands); - - // Read back the results from the device to verify the output - err = clEnqueueReadBuffer (commands, - sq_output, - CL_TRUE /* blocking read */, - 0 /* offset */, - sizeof (float) * problem_size, - solution, - 0, /* num events in wait list */ - NULL /* wait list */, - NULL /* event */); - if (CL_SUCCESS != err) - { - fprintf (stderr, - "Error: Failed to read output array! %d\n", - err); - exit (1); - } - clReleaseMemObject (sq_output); - clReleaseMemObject (sq_input); -} - - -int -main (int argc, - char **argv) -{ - unsigned int offset = 0; - bool print_platforms = false; - cl_platform_id platform; - cl_device_id device_id; - cl_context context; - cl_command_queue commands; - cl_program program; - cl_kernel kernel; - - /* TODO: replace by getopt-style command-line parsing ... */ - if ( (NULL != argv[1]) && - (0 == strcmp (argv[1], - "list")) ) - print_platforms = true; - if ( (NULL != argv[1]) && - (0 == strncmp (argv[1], - "platform=", - strlen ("platform="))) ) - offset = atoi (&argv[1][strlen ("platform=")]); - - platform = select_platform (offset, - print_platforms); - device_id = select_device (platform); - context = create_compute_context (device_id); - commands = create_command_queue (device_id, - context); - program = compile_program (device_id, - context, - "square.cl"); - kernel = create_kernel (program, - "square"); - { - unsigned int scale = 1024 * 1024; - float *inputs = malloc (sizeof (float) * scale); - float *squares = malloc (sizeof (float) * scale); - - if ( (NULL == inputs) || - (NULL == squares) ) - { - fprintf (stderr, - "allocation failed (%s)\n", - strerror (errno)); - exit (1); - } - for (unsigned int i = 0; i<scale; i++) - inputs[i] = 1.0 * i + i * 0.01; - for (unsigned int b = 0; b < 10000; b++) - { - square_with_kernel (device_id, - context, - commands, - kernel, - scale, - inputs, - squares); - } - for (unsigned int i = 0; i<scale; i++) - if (squares[i] != (inputs[i] * inputs[i])) - fprintf (stderr, - "Bad computation (%u)\n", - i); - free (inputs); - free (squares); - } - clReleaseKernel (kernel); - clReleaseProgram (program); - clReleaseCommandQueue (commands); - clReleaseContext (context); - return 0; -} diff --git a/source/util.c b/source/util.c @@ -0,0 +1,352 @@ +// +// util.c +// lib-gpu-verify +// +// Created by Cedric Zwahlen on 06.12.2023. +// + +#include "util.h" + +unsigned long number_of_pairs(void) { + + struct stat ss; + + int msfile = open("lib-gpu-generate/msgsig.txt", O_RDONLY); + + if (-1 == msfile) + { + fprintf (stderr, + "Failed to open: err %s\n", + strerror (errno)); + exit (1); + } + + fstat(msfile, &ss); + + unsigned long len_f = ss.st_size; + unsigned long len_sig = (2048 / 8) * 2 + 1; // this is the size of a 2048 bit signature in the file + + unsigned long n_min = len_f / (len_sig + 3); // if each message was only one character, then this would be the maximum amount of signatures that could be in the file – use this estimate to allocate storage for the signatures + + close(msfile); + + return n_min; +} + + +void setup_gcry(void) { + + + /* Version check should be the very first call because it + makes sure that important subsystems are initialized. + #define NEED_LIBGCRYPT_VERSION to the minimum required version. */ + if (!gcry_check_version (NEED_LIBGCRYPT_VERSION)) + { + fprintf (stderr, "libgcrypt is too old (need %s, have %s)\n", + NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL)); + exit (2); } + /* Disable secure memory. */ + gcry_control (GCRYCTL_DISABLE_SECMEM, 0); + /* ... If required, other initialization goes here. */ + /* Tell Libgcrypt that initialization has completed. */ + gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); + +} + +cl_platform_id +select_platform (unsigned int offset, + bool print_platforms) +{ + cl_uint max_platforms = 4; + cl_platform_id platforms[max_platforms]; + cl_uint num_platforms; + cl_int rplat; + + rplat = clGetPlatformIDs (max_platforms, + platforms, + &num_platforms); + if (CL_SUCCESS != rplat) + { + fprintf (stderr, + "Error: Failed to lookup platforms! (%d)\n", + rplat); + exit (1); + } + if (print_platforms) + { + for (unsigned int i = 0; i<num_platforms; i++) + { + char buf[128]; + size_t rbuf; + static struct + { + cl_platform_info cpi; + const char *name; + } param[] = { + { CL_PLATFORM_PROFILE, "profile" }, + { CL_PLATFORM_VENDOR, "vendor" }, + { CL_PLATFORM_NAME, "name" }, + { CL_PLATFORM_EXTENSIONS, "extensions" }, + { 0, NULL } + }; + + for (unsigned int j = 0; NULL != param[j].name; j++) + { + cl_int err; + + err = clGetPlatformInfo (platforms[i], + param[j].cpi, + sizeof (buf), + buf, + &rbuf); + if (err != CL_SUCCESS) + { + fprintf (stderr, + "Error: Failed to get platform info for %s! (%d)\n", + param[j].name, + err); + } + else + { + printf ("#%u %s %.*s\n", + i, + param[j].name, + (int) rbuf, + buf); + } + } + } + exit (0); + } + if (offset >= num_platforms) + { + fprintf (stderr, + "Found only %u platforms\n", + (unsigned int) num_platforms); + exit (1); + } + return platforms[offset]; +} + + +cl_device_id +select_device (cl_platform_id platform) +{ + cl_device_id device_id; + char buf[1024]; + size_t len; + cl_int err; + cl_uint address_bits = 0; + + err = clGetDeviceIDs (platform, + CL_DEVICE_TYPE_ALL, + 1, /* 1 device */ + &device_id, + NULL); + if (CL_SUCCESS != err) + { + fprintf (stderr, + "Error: Failed to find a device! (%d)\n", + err); + exit (1); + } + + err = clGetDeviceInfo (device_id, + CL_DRIVER_VERSION, + sizeof (buf), + buf, + &len); + if (CL_SUCCESS != err) + { + fprintf (stderr, + "Error: Failed to get device driver version! (%d)\n", + err); + exit (1); + } + printf ("Driver version: %.*s\n", + (int) len, + buf); + clGetDeviceInfo (device_id, + CL_DEVICE_ADDRESS_BITS, + sizeof (address_bits), + &address_bits, + &len); + if (CL_SUCCESS != err) + { + fprintf (stderr, + "Error: Failed to get device address bits! (%d)\n", + err); + exit (1); + } + printf ("device address bits: %d\n", + (int) address_bits); + return device_id; +} + +void +logger (const char *errinfo, + const void *private_info, + size_t cb, + void *user_data) +{ + fprintf (stderr, + "<OpenCL>: %s\n", + errinfo); +} + +cl_context +create_compute_context (cl_device_id device_id) +{ + cl_int err; + cl_context context; + + context = clCreateContext (NULL, + 1, + &device_id, + &logger, NULL, + &err); + if (CL_SUCCESS != err) + { + fprintf (stderr, + "Error: Failed to create a compute context (%d)\n", + err); + exit (1); + } + return context; +} + +cl_command_queue +create_command_queue (cl_device_id device_id, + cl_context context) +{ + cl_int err; + cl_command_queue commands; + + commands = clCreateCommandQueue (context, + device_id, + 0, /* properties */ + &err); + if (CL_SUCCESS != err) + { + fprintf (stderr, + "Error: Failed to create a command queue!\n (%d)", + err); + exit (1); + } + return commands; +} + +cl_program +compile_program (cl_device_id device_id, + cl_context context, + const char *sourcefile) +{ + cl_program program; + int fd; + void *code; + struct stat ss; + cl_int err; + + fd = open (sourcefile, + O_RDONLY); + if (-1 == fd) + { + fprintf (stderr, + "Failed to open %s: %s\n", + sourcefile, + strerror (errno)); + exit (1); + } + if (0 != fstat (fd, + &ss)) + { + fprintf (stderr, + "Failed to stat %s: %s\n", + sourcefile, + strerror (errno)); + close (fd); + exit (1); + } + code = mmap (NULL, + ss.st_size, + PROT_READ, + MAP_PRIVATE, + fd, + 0 /* offset */); + close (fd); + { + size_t sz = ss.st_size; + + program = clCreateProgramWithSource (context, + 1, /* 1 source file */ + (const char **) &code, + &sz, + &err); + if (CL_SUCCESS != err) + { + fprintf (stderr, + "Error: Failed to create compute program (%d)!\n", + err); + munmap (code, + ss.st_size); + exit (1); + } + } + err = clBuildProgram (program, + 0, /* number of devices */ + NULL, /* devices */ + NULL, /* options (char *) */ + NULL, /* callback */ + NULL); + munmap (code, + ss.st_size); + if (CL_SUCCESS != err) + { + size_t len; + char buffer[2048]; + + fprintf (stderr, + "Error: Failed to build program executable (%d)!\n", + err); + err = clGetProgramBuildInfo (program, + device_id, + CL_PROGRAM_BUILD_LOG, + sizeof(buffer), + buffer, + &len); + if (CL_SUCCESS != err) + { + fprintf (stderr, + "Error: could not get build logs (%d)!\n", + err); + exit (1); + } + fprintf (stderr, + "<clBuild>: %.*s\n", + (int) len, + buffer); + exit (1); + } + return program; +} + +cl_kernel +create_kernel (cl_program program, + const char *name) +{ + cl_kernel kernel; + cl_int err; + + kernel = clCreateKernel (program, + name, + &err); + if (CL_SUCCESS != err) + { + fprintf (stderr, + "Error: Failed to create compute kernel %s: %d!\n", + name, + err); + exit (1); + } + return kernel; +} diff --git a/source/util.h b/source/util.h @@ -0,0 +1,69 @@ +// +// util.h +// lib-gpu-verify +// +// Created by Cedric Zwahlen on 06.12.2023. +// + +#ifndef util_h +#define util_h + +#include <stdio.h> +#include <stdlib.h> + +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <stdbool.h> +#include <unistd.h> +#include <time.h> + +#include <gcrypt.h> + +#if __APPLE__ +#include <OpenCL/opencl.h> +#else +#include <CL/opencl.h> +#endif + + + + + +#define NEED_LIBGCRYPT_VERSION "1.9.4" + +unsigned long number_of_pairs(void); + +void setup_gcry(void); + + +cl_platform_id select_platform (unsigned int offset, bool print_platforms); + +cl_device_id select_device (cl_platform_id platform); + +void +logger (const char *errinfo, + const void *private_info, + size_t cb, + void *user_data); + +cl_context +create_compute_context (cl_device_id device_id); + +cl_command_queue +create_command_queue (cl_device_id device_id, + cl_context context); + +cl_program +compile_program (cl_device_id device_id, + cl_context context, + const char *sourcefile); + +cl_kernel +create_kernel (cl_program program, + const char *name); + + +#endif /* util_h */ diff --git a/xcode/.DS_Store b/xcode/.DS_Store Binary files differ. diff --git a/xcode/lib-gpu-generate/msgsig.txt b/xcode/lib-gpu-generate/msgsig.txt @@ -1,512 +1,512 @@ -6B6702B9C16DB6DC -7D3A35D4E572E54B2107E454F753F81B4552F784A7F5159AC104ACE7A896EC0BFF57F54A12E92F6210473BED3E5F1F85F81F118640724DD50430515AAAFF1A6F3A38DF1014A45E7D3C036417126BEE15A30079037EBB64614FEFB6BBEC85B9D6B5486D97AB801CB2C370C076C81DB09BC3BBD14F3276994F67890FA223D74B503ADA2694555EB0AF004739EE2F057436F962A9D6C01905695BD44C1DDD39EFD6B4825C6EDD98C398E24A25FC313F888814B61E829EC01BABD1A8987680112E2B28E93BA2F3F78A757F42DE44BAC636A857B34761A47C93787F8604CD7A33A05B0AAC128EEF824BF4AB56B1EC922E09D8185D174C5B9AC36A3A188027AACA6CFC -0E6B3106CEEA35E4 -12C7EF7C1345CDF083DF02D4954F135A519091E62CC424B914DD267C61FE8695EB4322D159DA61467D838A44AADA361B56D1DD39A1DF77074A23571EDD3A38640C2CB5581BFF4A2B5B69186E4910538D06502E25D33B6A7C16EBD56FF86F2ABC8DE1C9DE122FB1DEDEA893817C9382DF2230605BDB9E47634C700BA75BEFF745C6C4744A709B1ED4BD3D2FD6A507FAC93AF10F6DD7AA467EF909F5E147B7BAC79A72600BC445446A30A49E19B48F7B50A3EB9FE553335C5822212582B03C9A5A1D9E406690B55688BF5D0EAEB874AE387715AE4188275167471F015CA33EAA1964E946CDC92C1BA75B8F82BD63AB25ED6CAEACB82F19530323DEAA47534DC73A -62B1DC9F21CC07F8 -4C73B30FF7B2EE0342E9B823F77868C7405BCEBC6D583A306BD375418DE6AA692FB7A58C6A9E15CB8E512EFC45C623AA3860DCD43127B4268EAB6418D53F1EB6F5248F8CB9572D6A30750073B2E64C83EBEC11F85B503DD49A07EA1431FAB0E3133E075D9E5B0CA00DB536B32053DBE7787014D5EFDDFE776D979CCA905FA7A0FF77D5A37AA9DB1041F8FFF936222081AA3D8A27768EFE0DBAC36A7033B034C637117FC72B2CE840BF9AEBFBA22E6476E3F09BAA2FA43E704934EA43A9867624367F797E09B3CE8D37E9CF45D7D85A4F573894966884CB7E482B9D677BC1A9C69E7BD54E21907C6824473480082DB7A98C07BCE30D082B806597416C176108AD -6B566CC8DE799BD1 -0F0945F64369CCF25D8596E58D614DE69DAE9CC6C4D282B47956EA4860645AA690E9A94B4B80D4BF152386627418317F379627712E0A765266010336386C44359B18A5EA046DA45E00B3F2990DC8EBBEF3E5A913269B519152C9036D4707B756A64744A239FFD11C19E1BDB0A4490BA8202B0C3EDF31C26D42E2E6BF605D106CD0D48BAA1AFBA6AB8F1B3D7BB5C1518C62B76E923A1909EF4A86694C09DA63CA911B381156B1824004D40E295D978E25F4DAACF0374212B881BBD80CBE62CE47C9BEE98C86963A55DAEA436ACC677EB970DA9B03DAC9D2CE858075CC47F168EAAFD6C9210D998CCA0210B863AC9A8E710806171E10E22990BA5CC4AE0E5111E1 -208718AF4850B966 -0E7A06EC47F66CB9C8137CA20224034239330A9E96B75EBB9D78A4FBE9F7A62BD6805483264A0AC4BC20C62808D1633308DA5A84A0FEE0B5656CF8E0874BEC50734225C7851CE8333FDA1F8E4ACED881146A39B1CDE39BC4801DBF57B732B605A3F90A007D6CF197D7FD4EEB7A28C5EDB32B06F34C5D03B4D4F10361750861EFEC398BEF77E99C42061F29BD2B037540FF1D885DF98DCF5CB7E7CDCFDDD4FE2ED20992AE384479135182527AFBF49C15C3BC54BEB63CFE6ECC69E41CE0AD8CF44710D21AA9E4D896684A04AC9F2ED1DC8AA76AE3FF1035B0BAA8134E9DB23CAB4DCE4F902049B9C2ECADC63569DA1C1AC91688ABD3521D31E3948302806A86E6 -162BF12FAC70FD68 -675EF2B30E8956B32FF205DA5888D3D9A47660BF565B57726891FF10B635AB5A68AD834F733870218FBB0626130DA113D810E8D14A6D617E1B6B79E8680EDC79610B36B8215551C07DF6A8EBC81C4D653E0EEE477240269A14A8C621183A15E440F13AB9CC695B93D72137D8CBD429F846C893155BCBBF3DEBD5F4AB2EE79A551DEA9D7D172138CBD7290937BF3B7069BFD4B57C8DB449CC8B3A51B4609803848780EC9C4423E81052974E17B905F8D73AC7126548A88D340B9DA36939D66FF82E459347E47DC75551BFD3C20D2E3164C34FB9DFEF8432A390AD5A0E8FA31274D2146F7287F48EF7A3104E4F00D308F4DD05272B22C0256D97386A4A73EA1F71 -4EE1FC537BB7B2BB -09BF06E9E188662EBFA743BC9CA023CBD1C8F12A6D7B00ABC37A9CBE68E801F65E4FACA6F0AA30D59BAC9CFF4702BF54E5897918614CD823D4805CCE07409552A989D157B2F16C6C5191547F5559CBCB6B1EA6F66F9DE37CACF6220EBFFF5DBE2B66B711351BCB11A1309577898AAB63768C13A7398535C49304F115CC05512FE5177E861F5F3263266863C017FDB3D6305EE827EDC570E6EAB281642E4D5574FABF9052397B2C6F502AB7384CCD4432591A1D10CEC8CBA08A0C0F9E60C54AC3A53F8C079054C6B53B58127EF36F63AA7E11F5B8D8E18178C5520FE92AF5EB3444C3C51FF61B5236A08A6BA7D6C5B34A506CE24FDE1339EC73CBE929AB053E25 -7BC52A8C7B97F31E -7B0A8E8248F0062E5FA1528A6E46701E4F196E46FC41EE3F30CF630BE85C517DDB33695CAAE0992BBBB92DB8C0DF52D4E610BA3041E389B74ADD94E818113E047C16A26DC4EAFA56B180E6E546F9C059391FB35C5847CCD132BBC80B0B487A59D96D48A13EC324D37B27C7BB15580E4CC094B912431CA9CEA3B1C52F0E98B58052547C0CE48E3A1C27953ECE947B7BCCB3BB9AD7B0C223647B31C51820235580FB1EDCBD5F153972A1083606BA835966825523377C05178C82888D77B2F2F9CD507CDFBB11F24F275DB34989F5CC50FA2BE1A51D6AAEB8FAB61B9B2A417637936B44551AF1E3C741DCAAC8A9187DDCB4627397EB35775C5F443D8EBF7DC7A742 -62E85AECFFE36A47 -232311044FF3CDDAA3492BEFA69B8F7D54665152F912CD026B2A5ACC542C77F3C2A9DCF1B3BA34717AB33AEC3E4FD812374BF4E61B54D1C6D1224C1CCDEBF2311362D39F325C3347F5FDC22D7C61D89DE53463ABEEC0E91C8AA447C5E1EE89BD213B3D7690D0E96A75134BF802ED834226AEAE208608FFB786282B8260C49DEC633FEF208ECCD3FDB8B16AD4269F6324F2B356D2AB95E0BC1F0F887E64D9AF0AEFE1DE4D6BD0A44740A6E821894696050AC4B0A51002D510C389F7432921BA255692F8336877FD5B4DE76EC02C7049C04BDDA05213861F6DADD9B834D6BCCE4F2C41EF69462A88D8081971C74E590E18C46A54A970DC40EE0D962ABF7C593526 -46BA6A88CCF7C9AE -0098532935182F6DFC2F03A37FB6923AFC34D20ADD1260C7C56403592168B554F0D2F0EF92C96F6BD93402CFF569DEA1B05B44AB8B66D34D82CC8865DB962B956C02822D2AECDE40CE79220CDF6E65B5E133B56208EA77AB0266BC12B189B210682E1D1047B5956FB5F071D59A8173F32459994C2347A5AF8D0E8D5D27E6E45A9DB83527E75FBA8A868654B09E231277FB1EF13E49994BDA9E726D7A530FC19688ABB8E47F99623E654D2B7174FB4A3FEAA14299F077D2C8FA8D348235CC9A761186028A025857E43CD8C56436CC7D2BEB9B964EA2BCBAEC8E0B48D274843F7351C989B5113F9FD8FE059C08C5990322A839AEB2120B2FFB7EA539969D425A32BC -52923F71B627B8BA -5F3E76B94D50D84F82E184B5FEBE97B2DE289E5B9D62CF7A10CC83A4EFA5F72E78E0658DF8D0E2C88F7E93E75FC46D96E6ACBB90DCB7D46A7D13884E1CB5C6799BF2DCEA12EAC37321401D2054F983624725EE25898CD1C7BB12C82AB515D0DDA6EA31793EA5E49D4A1E77B376871CD5BCE448641915EE676D4C617049D6CFAA60A623DD782CDBBCAFCA538D95E8A4C5579C479AD363A3037317B187F632B85E161FD53D5D6C49CAC86A9D062076DF861B67FA8080E816B83DB50F077ADECCB70C9415C6F3F1986D16112BDC093A834A37E972B381EA46C029A2AF1D2EB47E4396CACC4B3E140D7136DF626F8B478A2C384E348478B748DEAF631FD20EB2132D -6F41D6BF020C5732 -008E56FF781C2A09064D6576F4E830260A744B3C655E64006D1288E43A78A462602C455C873559230D3049815E7920B69F1C5444A6D7AF2410E2761E8ECD749D5A14B166EB5D6A41C15C41B628452A9089A7E85DDD086D04CD18444EEE1537176950D1A14F0FB8DFBF6248F0968C5817B235640AB847EAF6581E95422B5D8416A75C6F5E74729312A6B424FE272ED4CA6E17D6A4A5DC9779B2074AD323996C5D8933C84F7F35517D95062233A811BE0759689BEA1C4112A3BD3B652208FB63CD9E6A0955829A2CC060CC8331EE434FA40C2E4FF6007C627FAD93F911545222D91748AD946F216208CA9D06EB2B4AB209F35FA08FBC3AFED289CCF04FED2F0EC401 -702CD68577BA341E -611B76BE33AB2426CE04FDCB1A178E7DDE1251CB3F00F54085379C1FA76CD1A86439320F8029DBEA4E6EE4267D2C92C796EE34D445A5C77761E5EE0144B3C66DE601C1967A00283F991E0496F185A32918A6A2F08D5C7866B5E88AEABA89C6A527936D9DE47AD2155E29AB4E9470E7AF2B626020233B800DF20EAF4F39DC72021B99B41BC0FDACA9B7955EEB3F619792EF4B270FC55D14ADA433BFEB51E48C603E7AB3CFB9C3B482780D8DD56A037D3BCF9391726F96E34B7DA209E9D45FBF22356AB40B600BE82C98DAC970C15109485022A61D3061A1183D82DD30E5C06730638B1A894A04F3E932268E11FA7F61CBBA4E4BA39D8C6C9B22A4FFFB69B75E3E -2218EDE5BDFB3D40 -2B99230D3E1409827E7F8DB3C6D113526D5C699BEEF2A365C0205498878E3758F2C031A6A9D191B29C94FA628A856F5C7722D1111A612E6547344285EED5F4F31281820EA40BA35281CAAFF49BE176BF70A1CF54546E7014584ECE84C92F99A77AF8F0832092DCA3A91879356B14CE2D2B556AEA28C8BC3F6BDC89025E045C9751A15C3CF3BFB4398FD06E122795876B55428B49D0DC4A345C8C4982017DCCB86C526E42DAE8D9C9E083C7BEB85D6FE89CC09F41FE3AC19C58E20DA9DCE839B7A3C94E899983D35DE28389A304877E60E4937526C402422BDC2132F4C8F1AD29B1BE0158A09453FFC2FECFF26E3DC0D76CF17293B22E4F142A926CF600D1DEA2 -7F96E4824E00CF3D -089E91C428328013B72E1148D5704C9E5EB2D637340F8B7B2175D0A7A0B806A33C5B107A6D98F729E29BB00CE0107E3DB6C290A274FE39BBECF448A256EA2104BFC082A3AC38A2353062D0990879F8C14D6D857AC0A770147B4563273F352B2A93C9E47F2BD59C469169C0BE35DC0470FDE19D832FC02D083B2339E4434F2E5AA3AF181507ED78E0A03AC83C903F8C8F98466FEB0936A8E3B9294F76C0C01C75F4E641F9F725E5068D5FFD5B6F60758B4B7CE7476F118DB49449C4664D61495B11536C394F63B8CF0C2BD8600C7C6045038F70DE65641F04E022782C873AEF66D6246BDE9A41589847F32268C2538B95E4EE9AEDA87AB140FB4190568CF310AB -2B6418FB03EAF1E7 -0A85FE90E5DFC6032EDB129DA75124B36AA1DE2E1491F1D57821362C72F7D27E79363E53EE6ACFB72D3C1992DF2C08B2AC978BF04C0EBD582517C45341EF1DE131CF772758F5A0943EA6BB0CB5C7DB9E319D79E4FDDB6EFBBBE16D4FD0F5301EF954EF769BAF745A808DEFFA686439B27C405B809A272303F4FF85DCE5779D35F064B0FCB7EECACBA1212D2A83EF5C0EB0413EBF6781F0CC9854BE6DD02C8BC5B9CFA18A5152A4CA91CEF078328AB7825E84BA9B99658D9AAC7924176ADDA1C284E2D5788063270F8FDC2B4D5D0AA810DAFCAB923C7B0200BBDCF231B4002DD5140F947C7D5D842B8E0BF1020243AD0A01814C5F17CB52E224480FCABE975CBD -16F8AA11C9B12D94 -6C83B47917706425AAA5EA67D19C82D764FE6D733E799E33BE026B03944B50DA5DB7FF6A0CF9D470C2D9874BB1ABFF4D9538C96DF2147AFD89977B20B25D6E5DA17556859ECCA77D3C22D1FE8101BE27768B88C9684C18984CB884B2E91D977B349F50A6FADEC2D76E1C66BE7A63DEA0420C24E535046D90E72BB4285B2883079836DE94C6AE746727A45A0AFD2B15FF3174A571FD0D5926759EF4B95C4B75F7F83949D59CA11028CA17A92843E345F714AE0576E39985D3BA089B8DAF8ACAB013A007EB863676B043AC3AAD05290D209EA191CFC10D9DA6CEC656D66BED5782D3DFD42917BFF33CDEDE5DC0B8D7904FE996011BFE8DD26D2F2432C72388CE65 -7E2EA371F80E3FB1 -504AEBB2920D80D78A78AFBDF0812681C99B53D9DC3D05FD53B37AB64465A061DF248F66C17AAB655F6AE772838B00F44564B1DEDCC8C3493C93D371BE31DAA114CE73469FBBC964ED3AD1684B9D3B5810845805B74F61C6B8F06E950EDDBEE383EF236105964F97BA72CE9E015B347A305D7DB39B5F5A2C4D8EC8887852A2F9861BB9D3E18123BBC0835C710F46C2A7751EA2ABC093D7D5E12D5AB271B943CE385B2D30294497A59AC417BDF5695D2DA470EE4608D388455DB30C6B5692D47797084B4E255DBA5E1C2A73156440CC65014C695C96464CEDB6EDEE8EF24A90E74F12368B9BB9E14757D9F47199D86B8D23DAD64A8BD1E8B72F83FEBB6623003D -2D8283F041818C4F -009B0277772A8106C6A9A25D770F01E71D7B83B33D5D2DBF265B7425E4222DBCF003D072DC97DDFC1B1C3C5391DD3E24F5FD2A56D1BA322801D47C9FFE529A77D7264C372CE26B8CCAE4843B8D930F8590E6C8E93B48D44707733833B2F6DB51BBA4CAEC99420935263C5671F7E0D98A1D18A83394DA58EEA1CF3BA692745ACEAB5F863F32539AD8FC62085338B71F574B57E73EAFDF8CC1464278D077A04E22B4803293475E4FFEB6D90EFDF414A16628B0A1906455A83E52360B14D075F9BF8E1E09ED69AF8654779A3F0A660CCCBF662D65F3CD67B35CFCFB2FC2C01FC43D5019E5736B1EB65C53E6B188736A80627BF47421A13DDF820F478BCACE1AD2EB -0E4C2DE11E5D43D5 -70C043A097EB6168B48990E3D088F91908A9A2B2E236D3E870C765B09506E62F4B824548293471335BF4D9B3B69A9D8E65B126D55159715E56C2A2DFD2C76988AF9C2F45904669A951D0DDF816D0EB53B0CF7DDFEF30214A5A69EE4B2C677AB9BE7784D447CD120B46197FDC4C4A89FDAA6363FB6E55B0B1AE53ECA464678C6CE01787903CD71BB5CDAAA1DD4D3352A281200BA25F4D78B7DD7FDDB688E95A855BE293EB8BC7AE11B5B0A9C65E08C97E23D3B51A1EA64D9C0C1DF5DF1835152B9A78F2B8CB53102C40CFA970313816BB5123B2A372EC03CEB1DDAE090C4E793B4345C2BD2C522960023E1BCF0AE95525E91539E085B7BD8C2C6A48D628F6BBE0 -4E56B4D3E1C927C6 -0CABC190D60869E374E51410027F49F21970F898C039E038742DA3C389BCBFEB9B41B7DF118E8BEFC783258573E5BC7BEA51999747E30B815AA83C88B324B65803996D7EAEDC8508409A9B2771D51485D8D52C9D8F3CB512A3CCE467ED05F4BBE6EF6EC40A15E014AB9F165B5D882E144C036C5163B18D96EA881885EDAC84DC0C2C565243D4F7DF20458BC88DABEF1214A746718A07D4A2E5CACDFFE764E47CD3284318CD418B8A54A3FF152BF66D336218B3C47524AD68AC13114A0D5A17F46FEFF4E72E55CFE69367D3301C274938C6EC6646C134F3C8F22036F28A33985EF2BF3D0EF79F16AF47002A936A70DEC7F13EF98E34ADA98A0C40988546F45EE3 -4D26218ED9F26EE5 -5B76FABDFC8B98ED2622AC87493E045F86B62A3BAF7DDC335B300B6EF004D54B917FAD3CEDF2CDE17E87A94C19F787C3BDDF90FBE596CFACEEEC72175122604520F5A030B8CED5C36D4176DDA91689E2535CEECB4C1A67178AA26EB30E641F995072D101A2AC2F1C789A5E7BAD1DB64F392A1E37540596DD831B406EF14F59F48A2CEC6EB1932D661F4ED9715C5EE32184EEF00490F883BFEBFE2B4E0C2824CC250FDEF1015B10B7177044483A7099750B6A2B11134FD0E8A43DE28F5623BF1F6DE9AE7CF92DE97308B332A44621F46847EFB7A9336EA6A678583B054C48A0750C7C1118EC715E73BBFDB26683ABEB20F2F1F69AD39E0CA5A614EFBF005A9E89 -3F8BDD7C921A71FC -1FC93DB9568C9ED59D505C3F802084081BD627690D7C3FC559C0068E753A6E22D874195AA24FCE28602EA4DCAF57F93973A93EB8D2F06984E529CB70BBBF5F22EE125251F3E637DB7F84206E6B2366C494D24C68EB9CE9BBAB3CF8D79FB52F6EB40686BF9BD4631FCB7F6D2B67352D6F9F81838CDB68F4F5310D10578ED9B112D586CD3A4C75246FFFCBD1AF2661E74689B629E727D319D73EB5E900A831A32D0833CB7A29870EFD3ACC0D2168E0F010B593903992B61D11A6593D1D67F62B44032A59DDFA21FBB03EABD2E2FB8D224C309601ECED8FE57EB49274CF1F09E832D5C69C8D21B1B543C1EC74E21CCF1D3CC5818D01BE9CEB484CEB5D9BE17F67A6 -5871E8546F0DAA9E -3DCA8B73A70B7D8A025791996C8EB4A3824E966C918EF56C3A5154881888401557C398CDB94B2294904DF2F36CB684D5D222F4990F97FD997FEE439432EEACD8EE4BFF7503B718D30C40A3BEC973E160F7698D6702879B38948B2C91D9C9EDFE6E16E50CB6E5E08B4EF08934938525C576B70342EECAC1CF0F706A959B3476C31D6376CAE0D1574085931827754E2CDA50ED415F94028057F84CB3D80C4DDA5E290DEB1578DF49EA03D37ABF9BC811E9B0DDEA3FF2A54C7CF5E6E309DA4AAF385A753EE74683DD7583BF5136CFB33214EFF26216AEF57C1AB97CE769302AC2D44D0E3ED15E3D94DE9303BB6D7748194F3DB4BCA8819309137D878727767C0327 -6BA33F5C161E4343 -77E0038647B9F2FBD9E64BA8E855831BE3DE93D8804E80D81B04CC4C297913C12336198880933555E4A300FE44342B998D7F8027FA780C09428C5CEC6B4C62137E29D7EFF67EE6ACAE73954F8EC55490885CC93512E6C301EAC16F9BC993E7073EFE4DFEAE73CA136DB40814724BF5E1BFF5C99E0E1AF8C0E949C04B642FBE010F9F11D5FA971F0DB985D1DD0BF3D2385ED92FEE08D52AED27660A53937573844E92D6262CC40C717A3C72DD4E92FCC672FEDF4AF9D68ED1B05493F7785684813B2430BFD4F4A50DE99427F80C866A7565BADCBB41A150518299CDF4FEBF924B00A566A539004623B01647752E9295F52A21842E584B983E6DD5F331873C28B5 -1C5555BFA4ACC29C -008D19B802AFC3383764AB163AEE7969AAFE94140E56B12CF9739CDACD937EF8157754BD81568DB9E44CB28A51FE377243ADA82AE8ACAFBB548DDD50F007D80C210D86561BE389004113F57E5B14F7106F117E069D58FF723C81924684ABD3C369F4AEAEB9310FA0994C9C15F6A00881F4CF17A9D0D5C3557E0453944DF3739194EE1632163160217FC5739A1EB66B8EF934779C784127A2C3DE0B04EA8FDE28049F3FAFC3F0E55CE7E50BFDC170A03E3801FBE73572E78E79D278C9ACAF504C8B2BB81490B457E249858C0A514DDE9BBC1F4D4AD100002CDC412EBD2A244544F72732D4AE710D40B2C60CF23A5159CCB1BC2335F8BCDCAF427AE71C8F4BFA37C4 -42283933F26ABDD4 -354F4F00924D3CEF1A7DAB38A13834513E893699294BB0DB8685046105179DECDED4593F9E88D50BFF85ED8DA7924B2FE0B4F6504462C1CF273E0F71F21D9E4C1E37A8B2DE387EBA930F5ACDB8FA0B4F6F1A8ED3D4F5227E314532FEF2EE9051D6E4AF6C69E79E07BC1A2B55F47FB99EBAB3D142A4FC95658638AFF69F854366351CDDDD5EFEE83C693E67E95984F8497BAEB9EC3862D08C33A25F52426AFEECF0A4AB5311FA108102C1155ED98C7347F2BB3D562DE3C3259CA5B2C0AB5096B7FA81A20DABA6655695BBCC518F4518CFE47AD8B5907E9E3E9C74B62ABC0BB8BA0FC2181157C8B9AAC4C9AA0BBE2791FE05CC986558D20C6145CE5539D9F280DB -4C982E2781D5794F -66C6BDACE49269030D2EE6680545215B1B42A56705E436FB1FE9AD324240784C271351450C210A71507BD5D8A2D45168E92F85928839B8D88583752FBBDF126BAAEAB9A0E14577AB9F37E31CA8875CFD90C45568473633D621F4FD6F0822515A6788C9BAAD881B897515CE8315C075A479883E51BDAD8456FD81B0F38F3FB1ACBA59155B1A0EFC011624A2BC1C74FADB1B96BEE1E486077E260E26CA65BD1792DA0D8610A464331CF507E12CF87090C4D789466DD018410CE1730F338BD35659DC8183E0F86AA5828B207AE5076216FCECAEB29B1D7BCD74D354FDBAF6E1B5427BC2031CF24399A388C07C62836B0B88A9BCF23A8431854AB6577CD0C0C036C6 -5F648508D3D96EAA -00CE3A5ABEE1E584CC90EE932FD27A14AD819BF9EE520D473BB6DE6A40B5E45D46779F745005FADD162F392C6643D07323803AA049F20DBD4485514B1168E10323246B0AB9627F561B4A936D4D5610FE8F7093393F79FBCB48568C5C2EE6C1CE1F4E8C449565F141E34E95E33434EFE6ED4B83FD0C55043FCA3222323B074668CB5B244491D883CED9F81E722A6977DDD5064FE1D80D0799E1C44DE3E0564B04DEB1379D62E52D10AE19306AF2C85AD0658797520C8654EB0FA85A1266945EE14EB7CA30BCD11D27A7D7C1360B478DF458D30BA4B587EAE8239F1A027C6EF960545A1B39E1601229BEC95DE092313369D7B9305CF89351C452B7A3BDF7EFFED72D -565E0090BBE86902 -7E4246557F8B29CE823FC5546005E3DDCEA870F5CB7203DF0525FF95C897C134672EB3A2FC6A00BB50661142E4D223D3A25B284097AB3199D449CAD53D9EED6D6F631755AA5AEF31DBB9B232E84522059DAF8A9FAE1FD1386F07147A55C564D4F8403B9F7DE5123D33164293A654649D28153B3AEE254342BB571A69594FF3DDBCAE8FC32362E78304F1B938EF3D5E9BEB4F965F53898D63E54E6F37B527027CEA100C8E248F322EFBE699B90780B94DE45E5FC6C106D69E283DA5E0AE59939774DCA79DD3C9196578A452E2567E5E49FD4A8F3304AA1F53BD6AB36ACD8E541C23B4A9F1D92DD3F4FBFE83EA9C193470DFB8EDF3C0B5DEF1BE4C5B375DB7DD6E -7FC989F67326CC37 -00AF48D1F851441740610E19B1DA102F2D5DE07BBD5036F9253B02C6EF938EE0418834F1EF2203094001401B40D444B994238BF456574C524569BA14720DEFFD6529F042AA5862C993162D519AB0471CE37C835B080CF6757F1E282812696777C12AF8E8C547125CBC81C4497C089F8A90EA955284DB1DCBEDA0BA37C357B66867A5F0A7B1606A06620434B38D27256A7B660D04B4B2B3A173C350DC98DD3735200E2936D2EB7B950FCBD7B5C77458016822D8A1158E37888FC011A02B18AA1A13810FA8EEC135D502FC994A1026496A13DB6C575A07AB92521A62F69B894E43717593D1DD78D698286822B0C1AC6FC3C0873CD1CB19A8A91E4D690829E825A535 -2B6A4CCB0516D32A -009958181621CFABA561E252CF8450EF725148870012028A5EDF2D12B6898FBED1CBA7769ABEF512E516C36F7D88426C0BDB4E0A4D862A6A6226F6534A0ADD8D28C5AC511CAB812769FBAD877328653AA5A772A7C71BB6F5A6A186C1FA132EB8079C2914883BD482107B387CA791BD528D642639EBFEE31AB4984ED4F601DAC01B9DCE018FD68371E018CFDCC87A97F1F36C6FA5FDF225F21B29D2D6B456C76EBB4BB0488F47173C8CDE37B366754E594175D20D39E54D5E78AB872435BB0A279D3655BCD0EF708C4C52FB2A049C8FD034DAA681249197BB0CE3F05ED9CB7177AA9D8C72861484C88CDB6F848EFAEB6AE4757316607F3508453D6DA298FF441809 -775DDD0C11B9DB35 -0B8B34EF154F8FE115A4A1F8D29114007BEEDA6BEF368AFC23CFBABE9043132C9201A31CA37AB3A5C1DE96DC0CF97258DDC6A4E828AA294506EAF60C2DA3839BCFF4F946DFC5F86CEE6453256F01D99464BFBC5A7C0B82584FE0794C22F356515AB14609ADF0A81E4732F4EA5D85BA92841F998FE721033D4724AD4E3F422506DEFD31883D4B39690C9A7D6A744AE103986C6CA0D32A7BD911A4305C8BAEAC0F7368DD1D2EE5BEA5622C6992FD0A1EC4BEBAC0A2A213B4A26677657EFA236797339CD26CBD1FB05398A9E58ABDAAF7B484FAF6AF1B24DB5283AB812E07452AE938BD8C508CE3BD67E9D010B7AA0C9EA541E391BFB75A1E0D870A6D06E622562C -053B866F5AF5D84E -4E42A52E1C73C78A54ED7DAC751B5BB0FDB8B397B87FDF92AA31359FD0F0921F0EE62D731ED1A532495B20712CB5DFBD927C3DC607A5576EE85DB6F4EFA4AAEE0BDD4EC1A6DA5EB6036168C4A34333D663C48B9D3A6863CB3E1F3977449FE42FEFA148017D0868939528C652E8A6D0CCB72DA101E8106D63490D098B8B31E1722FAE6A8C666471BC3E92A20E895AF11A45C9458BFD6124FAB02C728AB40903836CF803368D2122D060D29A42599821BFC5422999980E49281BF71E9DEF73583B7E5F810F6DBC6C7C343786455FFF96C9CFDF32498B29276009FB7F4DDE4EF66875692AE18216DA942A5B55549FDF43F78ED793B24694C0D3D28B1B749C41557C -53812F83B01D9A63 -49E11E9D2DC638E21C59D69F7836EAC97457CD99A39B8A9C80D1A8D9C3CC1E540220319F8A36D43ED9DF92953BDDAF8F0F5B91D34E706EE6E6350FA93F4BF943318100565D6E6992C740E95599B4026397FA2A58CCD5815343AD10C064CD78AE74A58A7083C717DE19634D27BED955C5D3A7D53CE7F8A23F5608B84EA899FEB6FC14824A59888824FB6BFE5584F58343DCB246519DB1474C450E55AE9E26BA291EA0F35271D1E57B1ADBE623411D973CF3E212F63A31C48C6C13D8B38DD797E989EF0F9352BBF3A452C2A5A509E0D3494F256636BB5C77A5612D4D5CE2DB6C6895B6788976140EA5A2732C2D88410C877F37DE6E49481936BB3E58A90D387F90 -1DE10C1E47556581 -7BBB3744B4E4CAE48AAA9F29E7B78336EA62662CEB7119D0168FF29808C0387B69C251533237D3D04121AA6BE3C5551CB6707C1BD83AE1EF83C6647C5CDB317231320108DD7AAAEF0934EAF918C8E564CDD205CB6E380881D560F1999F527059BA5616F5AA21EC5B91A91EDA08BE0C564F5DE0C65D7E408CDB4DF8E12FA3235892FA37C2FF08454382B3EE9BAD63C9749C4BE3BA2E1BA3F5C1A309368A9244DB67EC057B0C0D61758D8FFB2FF4A5836074E5CA6EE433917CC6D4F4DA63B23CF03F7EF1E520801C2EA872FE09716EDB65778969DB07BBB7FC6252F27B7774750CBFFB9E769C3FD9D747A8F896226EB9B854ABB9C30B24B4E26AB548DC8CC36BA9 -5C38184D4189472A -243320A3D3C36CCDD3E520D433C25A31A4D30569DE84CFC1C27AB744326E3BACB69BEC4549C68D8C4B7B51D800873EEBDAEFA0CF5F421B60A315CD280A31A01B1DC6DC6143AA5EA23C1FE09DF005378DDA36F65CD3E040A5B57B71FCC5DDBA108C7F875AB421890C69F9ED42833D19C01A004706A90B42C71843CB952FBE250CE0A61F14300DDA912B01A446C4DEE7921C37807E422398FE51219EFDD921D863774F67D3712D85832E39897EF6632BF167BB01611799500C6D5AE6E392C81724E5460F2226B412953B835E2BA5144459CC454014F7A8EBE07F49334765E2488C98145508452D16CBE0D996FA0F48B0633879B7EE0399ED9392654C428BDBC57C -5BE7C518E3DCF5B9 -59AC38C0057E6873CF246A2BD294AE7CB327DEA9C65FAF9182CE0B37581FD18E2A5FD6703EB08257C67840BEF6D2537B209303CC7A48A531C3245DF7195C45B1D084724482D64BD1A9589C459A260A32ADE19822548ED25513055E8227EB3F785B57949FE2B0F1C104C42C41381E88CE0144490C14ED3B6DBC746BF798DADA7442851E24ABFC8402EF4A4E78C523F1359BCB9C409F6370FEE8350FD8527DFCBCD1C256D6D4D20349EA1BC1CE14EB5A2A993659C937319E63A6B817E4EEE383A10BA2006DB36A94B988466CE7466C37438434FFD73EF6A0D1EFE5F438B14D79584EC2A9956512BE873CCDEB6CC4F1C6B426FEDF6D9C66A14398BAA64C20D84D8A -609AE6E6433259B8 -66A363E3A00B76253EB62D85FFAEF20FCACDC8F4A47E73799EF11BDDD944FF7B3EF67C9E242E36203E898725C1C03CCF6456DEE5E4F410AC8F10958A45034A534B60F07F3F151ED214C659134ADF542065EFB2FABA97CAB2FB2859B3C14E684D38623464F9AABE5A44A8D96C391946C5DB5B657FE502AF547B065A68D0C246EB77336382839A1A228C042FD2128F1861C17CF02AB59DAA5C554D95953D0F80FF8652610348DA88C86F3C421DA0770871BE103A8ACB23AE3CE5D5B62E794D10BC676ACB26E8FC9E99CB93705D73722CBD1EAE2840FAED303AD90765F591CF8EA8E57E429196DBEBE4C475D9C0B6EFC666ECF9DBA615A110EB999B3E6269DB17A6 -303B663334F4CC79 -3448B95D4908C5B4A4467077DE495DD90D512F2BF98680F9D13B503C3EF29F804B9E625C0DE95982DDC1823719A2F59229143E464CECB3A5F012E2BD13541F89F0E758A333F5F97C22F0A8E43D1D3613C0546A384C6AFB386A2C26F2188A3A55D943B3082B29839980C9E9F97B75D0E6C4EE3F8B6AE8D436BDDDFE77034B32CEA86CACB5203CF2BABF3CD793B9442B9CD60688271B2737D4725BBCB1AC0991DA594005F3B2602FDA1BA0151E0FC2EA962CAC7048861F0F3269D42D230BC51C7AFB9DC3446587AD6B4C68B146DBA20503002CB8FCF96DDA291460424EEC19943248DFD50253C01EDAEB37BE1353CED6D6C552EA4416E7083376067DDBDBA6157F -0EC4449D781C4F27 -5172BF00C98CF81AA6C647990351AA1144B63EA10DE1EEF943E05C41574537FFFDA3A54BDCD9B65A5CDF0DD655736096B93E263024A13ED0B2166FA04E3DBB8EA9A0D1214F8CAEBFDF87B93136166B7973645B959B921A0BF52A8524F6C06985848E317CA5298FDBD01A6D15DF5491C78F11DBFF30DD5B8F3A1E4B9B6B0B1BCE4ED4BD4E58F475814E3D2351C271946DDB0BAB4909F2DB496DDA3E58104025AD42D49357F843F9C31AC1D3FE95768B70E3343B2263C114AA5A88768F392F60E2F6A99F428FA9AC02C2491A28BDC74C1932DEF80B7769A06ADFBF5F8395594A6FBDD5DE794F3006D81B14F0EEC48465FA8E6C0BD1B35F448535C22561949AE53C -747F5CD66C70816A -008692D181AD2E405D91F79F0939F6B2C6E30CDF7C3625469C573AFE0A9F3446614E962CB0F17C80046D1E565B3FA98524174B70BA47807FF94371B1D04764AA6217A4BE963472306998D0D9843BDCF42DD0F19306C194B924EEAE483C800B2C849854553642CA80CA9BB6C36FADAC788C8B6E7DA2ECF69250B17F4092EF16C1BC7B6FA26A17525A08C875F841E3AB9800B6194937D447C567E00892B7B59D9BF4D3745B845FB32BE1720C791AEB58219C0C2F836C6F6F74BF59FC519331DB3DDB4584C4C9E28E5C92984A3F028BD3F90408521BB40FA0CC83A550592E0DEA5D27B75C862512912F3D632ACA377341296F6CC1DA0675E74111F7CAD3CCD4A46F5A -33859A2AFA01AC6D -6A5292837770B2C4EFD72CAFB223ADB28FE81990D5433BD25F01796FB25D1B2A0D1FE433BD38C753262DA3A15FC613A8AD4892C20C118F2B3A451B93E14110A3579ED834C4F54C173F37D7EF29DB07CFBE756705A678B0E0AEEC2C108EB5E044E0220E66A183FDA85305A45241951A8A4ADCB1F19D21FCD4B60C8669EBF9D3DBBFC4C8D24D6BF48D4FAA27C1713C0B874D29B9E56ED60DD2C86649F55C70B49A3C062A1EBE518DBF8AE7DC2EF026F9A12EA682368A4650F97496AF6D8CB6B1BCDA253B4C545B2965206890AE44AC5CB6611101E55DA75731FD0ECE3F7016BD389E8ED70E47D644AEBBE5570171BC254C28B946BC9E53ABCE8C80D879EAC068A4 -58A5DACB1FA27C32 -0F2AECB2A469ACD81C7F365D8D5C6CAD934F9091C500036C64FBBE8AD0471A8C8CC35B0DE6DC2049DDCF793ADFE43CDC81D033B1AB62717A97AC0A1F8A757A39EA77C47156BBC4AB3BA3A204BEF831D074D51745E2DB342A7E1FB4998524F421C7D4F81F4F5BC96F395459D99307DFA3AAFB13849B561A1A39A8FD5FB890193F46BFA33DC81ED096B79193A69D68CBE6D41836BBD89866EBBC0A3D9A66D0F62C4854A27B25FDCBB1A3ADF0E55C78E7F2ECCE9249B41F8894A5179F026437A3E4D62EBFE952181DFAFA3B70E6D5F84CA6000B4666BB9B2AD0ACFEE8A38EF580D5AB07E7BADF42AED74DBE35062FD0A62BF17F3D10165AC7F7379CCA8DE6909CBD -7AC47FEB12C4BAAB -20031EAD690331F8D4A6CB1E5A7B63A7333055351CDAFB406E3B37D2031E75E70200C8BB2E459D1DB1F329FB3473D56E296CD83CB98599FEBEF2E9DFBA2395C7A8E4A923C574FFF8534C4424530810A206B237E13893846366F9C545FEC187595182A0F527CEEF97E215B8AE56C85F9CA1AA34D3CDD09375890F8BC60F0F3907CA522594C37F5D0C28689267FA3B0C6C083B8B9BF32C69F0741560E6082D2EA65EA66EF256001D0A305C1286C40CD7C712D8B0B0710C8A84179E87D190D3E5E2DC4A89EB155655D8D371D233B97A64E13E0B16AD06A386A3E95D668C5614FC11DB7E452B23E479CB4DF4E6E036C2A2E29E6F9EAB952191906B327F8E1CC31205 -04571CC730FE2904 -2ABAB63DD3DDAD906D70E51C0B30D8487A9757CBD83B34910D15963D90931681D0922046586A7E8E6979FEFC51F638136064BEB6D98FE1094805E8468DC2E9AD4948567453B7792516EC463E364500F1D30332AFF7A8B989C424594AD09556BCEEAD74122EC6B0E6EF57F6299FB5D5A12E4ED1155900C38C5B9DE41E92394B5D8D0BDDF6F97323F517791612D10B16E20F95515767E8A764FF26C7104B337A042444EB8F9261CE89CC612F9B52970F10BEF05F5A21CD91A60F58DC2B777CD735628B2B78EA993A7105D9FBF1FE18239019DCDFAAF45FB8DC688307CA54656DBAF277DED4208B5DAD9C02780582CF8801204CC47058A2524D3FBD1850F5E9D67B -7375BCED4F75CD59 -1633CCDA4564EC73BDF5309EC526514A331AFC7E75AF4BCD1A4EEB92050C0DC536EBC76185B208BBF4513675B53A691BF9A88CAC9CF4D065E2507533CF3E1626531C4EA90FDD86C8CBD0D9A8FF8DD9AF573F4D5A931413B0D7AA315D05F640182CD827E9CFFF7A1211A14274C347EE51B5E360CF224D4020EC6AEA521DBEB5C05A552EFD943EA153765D3C0D249B38DBD9375DDB2A963D16E60D8531D7E2DBFB9706667B654093323E8E366ED569248935F8E765E526CB3868CF92E0277E79ABEDB58E5C38EF5D6096BA16733B36C4793F523196A977F6AB3CDC04B0F41CF5D75C3AE13C1C4C595517D346FFF9D5FA7B88E1C0E1D60F959A95E48908BCE5C862 -7CFAB0498CE9622D -00B96EF9ED544B571511D225CE1A9716C1278F50A58218F6EE3B6C6FEFD810CD12D04E760C8D5ED95ACBEDD9D018238651092E08C81EC06E1889934BC45AA57A9348F317F3E98E5A087F17EC180F0287631CD43ECA088EF603914DBD4DA1890207972CD2A57F09FA07AB4EB0D170CCAD2DE3788C4A39FD2A2490FBAE065DC3A7EDBA206E8A14283A98FA4786EBBE95258E90178DEB839AF7440AA9F6585E225AADA37A27C09416CFA01D061E83A624DACF45FAECF7DAC0339164FE3F9EBACFAF3225B5EAA927D9C21398B571462C025743B360D7888182563598B3F466579A471926A10C6726E417A326231430D086ECD0B9AA80592133919A92B0CBD1EDED50CD -4E69F910E30E74D4 -456B2840E13433BA4B80106861C69609FDE2A41460227AF08FCBB074856B04662DE11790D7CFA036778DD7ED55A6E5C8F5533A04BCFF746B2A019204574CA4144E5DC54AE8F739BE689464A6EC95984D456CD1B6F3C3E875C1444BCBBA325437FD3676B6823D3D0112367B2CA75399AB5761D49409B1495ED8E46A1C10619A989A7E9C77F0BFF2AA044B6DBBF049A59EB1F70ED3443AEEA8A75EFAD8CFA8ADDBE12D43ECBA51F1E07A24025BBD969C089ADA082E6946C37BA414E7E94B6265E8415900C41DF60EC444F2126723B50A5C601F7FBA802324607BEB62791151E503BEE7C20C2285803487F5CB11932881ED2AAE549A00EA9BFB2D4EBC2A708F9D40 -24EDCDFD1F85F516 -1F1529B9CA78D30D458824E7AF1FE53623333D5633BD3100B8481015E55B2BF9544016A589A1E0DD9804D1658CAA46BBAE79412AECD114EC191997EAA4591A5510E9B3EEAC1BD3E4B2E98653E2444A73199BB82EECF8494CDCBC5AE82E314B1AF2A47782814145C4F9ED0048DEDC635B45471C68D1BF41D93AAEF22E0CF204D139E57BEBECB0D99513E1C035E31B3B41F5C7D2EE44B84C2E859F613BC4ACE2262E65749363A4DF7F9FF49575F6429DE68CE2F4FD53BDC25E91E9CFF435F7A5DF06CAD058C9253AE88ED807E7534525F3F704B803EC237206DFFBE622D1155B9F0E1994E0BE22857C05EADF70523EBF5DF7DB6A3B1FF4D197EBBF5686CEFBA938 -634814E9057742D1 -0087F25B2969C766DF809AEB5556F96881E019D7F2EEC93A44462FBFE47B287FB649ADA3D8A23F1DC9FC30BFFD39A2DDFACB8B67B20ACFE05A794E2F1DDBBD8B4BD1945678EC2A27073CC86AE4678EEAC5BC992D76097E2CE3507E80383C464F2293AC084EADEAF607BB9D882F8FB53601A6D451CF8991FDC5686513A6569D68498BB57CC5A86F6616876A584307C20745E42A0858FB0976B6279A583C52DFA11D464D1458D546483AADB6F6EEF603F37662E378CA8E75AC3AB18E1F9E5029ABFF30C55D640C7203811CF9179A6C5E5DCCE36241B6DBEF88593A17B9F7DA2B6BD91F99E4386EB75E0C3669F110D1A690EFF206F3DF2B9F883434D6452157829A0D -38D94A46BA6CE10F -0097C4C3E386DD2137EF0180C32864E4E130C84C09F3EC3EB2534B0D1C99484383BFB97A3279A9308FD1517698891DC9136A4520B44908911F8C2F034684162F0689F5D5E4096C5304A5E1A2DAEE67A737514493F6207A91E1377AD0BC531F768F715C8CF317FB52711E7D43B737027870DBC3214222D9F52263F9C1CF63C5F3D2BCAF2A804369A118F40C53044306E237EA7E596D99376AE32FC29ADF0A51DD9E46808744C27F3137D63AC5254B44144051A502DFF72258C51D876F8F10BD5394A62C3847758B6C62BA616B6BA42EBDF16F10D6ED3F8B170101B7391D13956E266D81E74986F35FB75C6AE5A3E46E0852EC99D35AF7CD5E1BD1F9249742C2EFAD -75080972294C6B28 -009B6B42CCB2F80A60CAB27EE4FC1898F2E74516B6A166EF978CB486300EE92627462DC7DF7DFF150E392625AC322C47880FFCB2284C52224C6205E1D3075422D6E9A7D00FC18326E58E8A80F30A50BF7DCE7C11462B59AC3416847C4D462E79203B74C184B3F69509F12328A4D286B843341C82D285592460C97250E609D2F69D5A5A4FE7B1B1212605F51A67A5541CEDAA48D83C422562877F1DC8C6B1E124F5342091E24BF82E44C48B2F85928A457BA85E0EB85A363EF7AAA523F79E3119AE5FC24F0B6EECB5BD04F92BE5214D78967AD1196EC71EE001D7E082DAA2333C5F06AA42CFA123909FD376D94FA40D2EFD54E675A496AE68C67E4B8CF28E89AD4B -35A3DAC74CD61F6D -0EBA5003EC1FDAF95DED04D09BBF4BC05A5692B3F939863D284A6E03404A767773CA0D8E47DF4DE0FCF8C1289A15647A71AB12EA6C6FD41AF151321B646BB4B0C6570A8D791BAD79BAA2A8FB1167B93A0A745FFD6C6C4A033B5DAAE33AD77001C07F3016A7E1574E3FFB0DFDE60D71320BB0FC81B213F8299CE3078E629FA74239334F157EE3DE0401788B721F7BB6A25FEBFDFFCB17B25A14672D87F8F9D92C4B61DFC2F4389D58A0055F620633732640E780225DFC1C56A4D524028F2EC096F0049F396B2414E8764E2D83C0933CFA50C76FA19CD63EEC503FF9209CC012D922462C1034861790549245A1B9F29099195811077612D474E2489DEF9072FA47 -78A05E41BD949F93 -2C863D55A5BA58671BC75BB08D3AE0D7811CD8AF931478499FC6B689C7722A43DF5C2378C4F65E8B1783BD3EC3E780F0C5006A46C4C2ADFFF8009A7131ADBAA528EA5A5D3E1B384BB9F1C5803290B6031DB88045AF316564E5A85FDF98091026A5A99500EA5C08C2BE2432C8D69F83927DCEAE66AE9C0794E634997661A743D32995371885F6921E139602C5EB466753DEF5C56835BD1C407A13A9C2171C542390B91ADC330603AFCD57D85D68BB6EAC0795F1B18CC96B0B39E36CC1EE2D5AE5694FF5167A88ACB635EAB84D0B12CCE992056EB345526CEA359F8FBE8E918912047EFC5D58E76C7F070C42A7F6EAA24665D3F501900988524A6FDB94A49E80DE -5781232F611612F2 -662C77CEC7B0B3DA9DF5FC8E18E6E9288F1B2414E418CA0B74BE1DCD8068214C11A49E3631C1FD03FD28E25CF3379852E4556854B6504B3EB8C75EA413A1C5813C272D5C2188AAE69FCE6305F11E5175B06BC9F2AC2124E4421FDA46C6CBC68728529CB385D9E25C12BA3507D3F6CD7FACF276F5EBF0F263DC41D26D6D684E9468E43DDC2B757665311467A9AE95433F3E6985E99BFD97CAFF062C46D91E3AABB2F246C73FC1062375D9289B71A3475548B457A0AE1C2DBE56F433920773CEF624A98B783C30ABF8E783C2022F8ECDA249EBC19B3F28989D9B3C9CCEF26AE60694FFB8C668B66F7E620528F9035AE2D21694E5E7B328DE1595C5C5BC2EA721A7 -5D13C26036F2E924 -00ABEBB72712E681771F78199C7CBC3816F89DEDB64944B2E8DEA0685E2A658CA44BADAFD23B02EF0F535563BD0D3C380ED9CCB57DF93CC86EA872FA8469EAF3B771ACBDDADCD54E3AF3444726590E4D70B18509779E757556BC97812C9D72F01873B69649523EB2F0B991EC5E0395B35B8EA08CB539AF0A3738350483A9B92C79E239C378578902665D9C421C0FEB98CCCF6A4AC462CDC46E4472EBC9E37C04C6C4CB0BE29DA479B255CC413E85F209A2F42AF5324BF968F87EC9777773D3B5F8FCD7A338537A3BF8281924C16E2C766BBDAEB3EDB08B0C7C3518704C0BD0C7C73EA9971D6F0A30E527960DEA1D9E42C76247D8960F90AEA05FD32011B073B67F -6BCECB11D5026545 -00BB9CE95B52CE83E5CCAE87647455DFDEB2816DE4BA22D28F8B6A8345D3E60B52F1397075513E2A0FE252714EA629A763515A9BEC80F86B27FF833673B23280818093AEADA2431FE772D9ADD800AD414015945EF952DEAB9ABCFEEB0E062485974F19A58E522B227FF31337126E411C712837D649CC175829A85713AA87C581AC18345A4E02A47CD91C01719BC5F70EA9892E8416628EF4CC082040483A370F72CB6EB86B9594070ECADA231BE39069A01FD4B90F7912D7B9A027DE4EFC8BC7DD5A9D4234AF0E652E8F86ACE5B640BCCD99AF5647743408F084B4CC0A754A15A1A15482500F2F739BF7EC6DEC2331F9AFFF465D8A8C0F8D572628F661EB1834 -02E8190C5FF7D4CD -1443CD3EBF3A40E2E6FF81AFCF5326CF8D5BCAA8AA61A092A69A2A8ABB886296FF356B251FFC48FD1EA78D6C3634DB92D9E31EA3FC0405049D8987D8A704DF729263312D3985B94ED240F38ECFF9068275E4BB52EC948086CF0902F62F73682B91832713972E7E08213A09F886393D5F18062807B37CE53F3793EF71215A5D8E14FCE17A92E13856A4BF8AFF1A13623F9EEA09399932C212B51890562472EF4E21CA2F63B182375ED4E38919A9D613DB654D64E45AAAFB26898D600BC3AE700C22BACE70CA411951C86E7BB8CB06A1D94647DBE988C277B87BD0E05A18C621FCAB4C70B39B83B6A0E9AEB4B9F40DA865D1E5B3604045F594A59ECB6692DCA964 -3AA7A430DE94C1EA -73AA71BEE976155DB9E2287514C8A635CD6E3575A09F51B0B4A20701A249CE5553C0135B7F225B57CB4DCCC7A5009FD97F23B99322E2C9FAA79BA1A583BC6CE941DAF59228C5CA6FE4584AEEA4C4AE2C44A6A0B0CD857A82D7DBBECC455EAC4464E5C0D867FE02FBEEDC43D462C4D9EA00300592640A1BEF43BB5EC14B2265366D324E2BD2BF9F2CB72EA61175869A80460A2557A694A99591293C2D3F2E3CE7C2D9AC1ACE1CADD2FB1781877BCCB8EE4BC2A4680E5194430BCAB4043422D01358F0E9FF3EDD1C7982D8E1D787858AEBF98BA44BD19923991F243FB08AF8AA815AA8F1F1F7ED4FAE177E77735283EA33071B8880C356C55396E2D0044E96B162 -2BB2A3ADDE03B80A -008953AD6E54CDCBEED29162A15F0CCD22196427CB41EA6A5865DF8CB74B057335382DBA6B9BB48600CD8C35CDE0F03645C4A67679CB864E39CC6D6A2BF26D9862A11EDC070F31BBA5C7C171BA84EE80E3B605B0A1840FE5403FEF8A70BEC0E300285201F23C7DD163A23E3AD9B9A917882CB75BD1274EE585C7F29DCAFAB43E39ED01F08D0E2B0FFFCE72B4C5965D549FE52C5997F0BF2646394805792F7C80636120F89ED6DC233ED6E723D50A5C7A116DCD8BB3CDFDC37504BECF5429E8EBF16BD2AE8594BBA1A03C19B3F04F0C91E7E3A856D5D7B14B6CBB0A222DE72329A55BB6EDAFC4514DB20ED8EC09892B0B9C8A628E878066F64669265A8CBEDC33A7 -3EAA62A3F24512AB -0091CBFFCD27611DFAF3A6EAE130CC3FBFA02DF58D57D374FEBFC89A72866D1F59E89C1293360A7D1B4C186AC5728A4DCAD1974AC9B7D21DFA48465595BF1267B649FCFB839B5B908C49ECD7B7B15979AEE1C7D7CD3792C08B203007E96EFB88BCE9FAEBC9E69158B5C552DFD6F860BB3F2796CE39A47D5994D853AE98E682D2C878C622C7A8B30695F7B47A468582E0EB9B7DE961A76598D53425D98A90B6FB9726AC06EFF5ED6D64D0BB88B10A8721AA08F6DA80693BDE8B4ABE5BA841DE7715E94558A68AFFD2137B6EA880CC78E5A695E381B0C7C0D161C27361990F99D21F7619BC99BBC26672ADDB50CF4A611168870DEA6372F2E4517219186725BD3AE2 -0E8675CF6B29F8CE -00A37BD4560DE8E3670E9AB64A52C6794DBDA76F21834B3ABD41E964D7053BB308DDA487A7F4391D613E5A9A7A7D8C1737E439A1D2ED3D0F1A8D1EBCCD3500CA95FC949821F930FCB8B8FAD51263D3EA4E0D4338201039223076AD6D67B202AAC1DEB736A627FC52BD7DF8D62979A1E540DD6202CD33DBEF3A3FE504DB08D46FF536E2C5487DFAFCF3EF121F56820AF4EDE0380A96B09484EDEDA76773EA37BCE6CB3746E87A8FDE1B6F11B22394F9BE35E46ABC4EF4D55A6E070832E47B1A219CE11E2139E39C7899264B926A226206176C4C4B9DC7E12B4BD48447B919A41D0143D6944B5E2B75FC66E152308E8515A8E80F6690783BACED2A088059183B7EFB -4187A566D46802FC -17BCC3CCA6CA8E04A1A44F1B934BF82D9E246F3CB48259997263AF37CAFC013F3B2858CDDF70742ECBE544D5704BA7AEEBE146C9C74D7B5192C6D5A3EE1BF727E968D9EF6013B3812D38DCCF090DD038FA713FF2A08D59EDA22338BF031742532E0684A6DB242D174C381EE8DD8BA54ED1197617DE7FFF85A6E8BE1310D88B22E1F6A9EC8D9F9348782FEC921F5D14EAFD4CD7C0FDC286223FC7F1DAF930CBD876655564EC6CF0BBE5BE3A761CD61B1591CC8F747B595483D17DDE607F87759A2D7AA8736A5E18736FDB1EC3239D02A0B3F87A00BF4C251D6AF6AECDBDC085B5B218EF6CB5A33BFB9A54DF2BC33DF3231B219F55DC052D1AF568F954C4BD34C3 -1175394D39146DE5 -67A2D1C163777F4AB5550B268DEB8E153FF091C4C4B531310677A69881483BE91E63EEA726479CDD3469BF042851A7AF8313C3F476C3FAE247C3609CCA256A01BF2E67AB889499F4130214ADF9CAB45AD0730E7DFBA576D21BE5A75A23EAD654335DF8FE97940EB5E56CD95A1A38A21ECFA1188CAF5D33CA33E63E89D83F9312D68B785033C0E8ABC62F67C86E4C48333E72C26A739638D14607D17BC41783A6733600C80FAD6058A032F6FFD4881F1A76F4426D28E88BDA6CD030B0011D03FE9269B0A562E89BC3500B4B03EAED1F7043F88A8CDB54FC34DD53A9FA3F5C301268F95D38D7A1759CDB3225EEF28E61419FAC53BB7D2CB1DA4D38644228D5A04E -2687CE8EF2CCB8F6 -0096EAA25DDEB5F95D55A41AAC13354C9E68C354F8C89DB981E20D81B87130A330AAD8E682B35E9313B361422FA1A48BE89C6249C3C911B9BEFBA292BD6CD3800FCD4734DC9F24B988F562D00E6ACA612F0D5D34441849CAA169A3212C6237C2428221737B32FBD6A3972FDAEEBEE4F8B234DE3166715506B874AF64122713E8CC689019C100A88D23DA3B76ECF0975783DC740579C0F2CCF1E8B2922582A12AE6831FD8D15691564B56B80BDAF42589E3973CAF8821BB01B14CC2DE8066C5DD4D94D940B8BFCC027CAE84A0950603ABF82BBEADFB8DBB336FCF7FDA90A9B1400C740C95C6A54BDBADC72424C542DB91F250FCCA5B0E3F7F42F7F507C2B79631AC -0DFCE2F4DF068E03 -30ADC8EBC3C2BE0D4C14074D4DB0319481566D341A3F766C5FF0A7EB2CE158F5B5AB68D155083491527F2742A8E16294A589A116CE6CB078C0101048140E1CB0523A2A7F2C2CEDBF5B0653A4A1284EBCA95BA0B537F2F05EC7DC4FAC14C204061828178738FF644A31EBAD08A00C7898093685ED4CDFC7C0D15AC9EF04D26CFCD712C7BF6A3AA6E5866AE5FED27BB861AABEEAEB1EA7480293AF95115524395B4AE1A4B058F5951441E70CE1858171BE5CE5DB5724EFADE07AF4CAECC32F1D7B2D671410FC225B32563EE01807202FBA2005E6495DED7A8C15E4D835883C0D36B6EBED165B27B08206075FD90AF28648CD973CB75F9E965E28E6532114D23F13 -4C5DAE5382BDE21A -00CE259D10918876D88FE5FD495DAAA8887E71053E684070518F01FE716A56D60C591E39C63DB504CEF2175229826D4569AF4C019D9379266A489BF2E84AF88E4A04FE15EB955748D7B5CD2814DB873BFA8A454ADF60D036B42C0D47E13D35C38B3489CAF32AD84A489FE64AF359DBC8A3E3D0FA551CB2660CFA23F5DC330ABB1748E0AC69D60833A4041C87CCA81E359F7825D22680318391EF006B324EBA75AD763B0AAF28A6D335AEDA99D4EC1D3A40AEBB3567034FA17F006117ACE5173EB4D9C82F0D72181A20A6465FA86B74E555D6C612EDA93B6873490B54DB18BAD913B08456879D3714F46373D632EC093644E0F3E97E1ED3B67564BE801F27A28AB6 -66C66A378E5214B7 -287906DF0A490E688FE585E86CAD06744EB581DF0D6B6E377354C823CF79530D5FFE70C05D11F44910FBA6452C3A5F68EEDD429451F5D97E15F2FD0BFBA538B69E5FC121E4802A98F226191D6C8DF5419B88FD7275FCAD5212A5AA9FE1C55E00D93A65F7E5E2828C8A50A6E4CB1244199C892297F7624183B07805D6E96630CACA92BC2D08D94B4DA6097062ACEAC3922E6E447307728E5E9AF6E3B683CAB4F2BD210FDB7146AA2A6088EB5695E1B59E7DBEC7CF3DF084ED43CF8EDEABD7A550D98FC90BEDE2DFB70DEC5467FB88D29A90891909414B23E3912113031AFB69D7E5751466903D1320E9C08653E8850C1AC32B89AAF5FE36A07DCCA191929C54C7 -115FBCCB65168189 -009EE99BA5E2C439A203C35FCB285283800973D2EEC8B35C590127E08E681B729902045E8471C72CD6726E0708A83EFAF784C03FAA1C83A5AB7AA125F494904876612BFF3BAE9BC4A9FD7040D91162C02E630D9D215537A781B9207E9667B9B8DE3CF3FEEDB81E25390863A7BCAE99242EF37AD9BFCB4AF6F9F90384219B4BD8043FC44EC37F0C17F62B0B616713F4F6F5300018574960C9ECA7933EE930E88DCFBC03CDF4B2690B1F32E9EF4777AA51B0AEBE61AA3EC4D84A848891B67AA52DF43DB3C7C3F4DDAF546F960A6004C1D39A7F2447D931C2104BB77AF2814E7E9D41557B01D140B095ABB06374576C7DC6B3E0FCD0F1C7B0F923C1B24B101812FF81 -6A56A83088BE9AB2 -00D03ADDD7B47C8A9AB2D964BD4EC49B95816F44819A589289F8DAAAB9C7199558FD6A70B6151E7C2D625F6105EEAAD229F6B74F25CFF0F86595CCB8A684782BD9A35386BE053203DA0C8CA14771F05436C53828520D2CDEBDDA5163FE7265A00611588C71D59C717188AE9B9B5380C55F6B0BBACE59F613610B03D1BB698FDB9AEF8387AB1C5E344A92F959A9FBD99DB7C9A86B1EE0D5EFAAB868900EA7F76AA389E7DBCA33AF6FC0B52A32F048DA5725F12333E4BA094248C0A4B5A4C4409CF5043FA3711C8DA290FBBA9823E99CE71A6998A2B90A94360F7D41BADF7F37F7F4EFC55464C572B703EA431D6B739CD3BE06451873115763D669DDB5B2403DBE24 -77935724BC8C1E04 -0090584C4354EE03D589F3DBEF1D739DFC289C5801B75553B0DD5482DDB4E033B4B040D005CBFFD2AEC996D15D17F5CB8A5DC75A2C9AD075BC14BC7FDE93EFAECAD0C30592CCB48EF160C7771C478DCAAFD0295A55D50645E8B6F5B2D3F2CDE3BD02FA08D715DEE3DC81A525A15156B2530C22A1CFF582A1382B2680BCD6CF0979DA31ABE668ED759ADA7DECBA56683776E2925C5EBF315EE968CC1B098A45191DF198A6306532C531E334F683C0D17DE36CFC1228001D1C7F328E74DCC4074674099A24109B717B07A9AE638C41ABD41A80BDF3F8A9071EBD99969FE13C838E779D1FB336C8838E5B23BF2F520A7865A3443638C027F1E3573047B973720247BD -5EE273B354063A5B -00BE2456B8CCB3B2F0C4A6ED65D9434D9A50E68C7617620F8FDA749788E41B7EA80C8E4659C4BC56BCA878D647AB3D620C654624DF4A387424238A0CE66D6FB603AB2065698B5508B02EC84E975493C2DDDC265262A272AB6E30843DD3FB3E4D4011B8F639C73DFDD329A519DA0C5F2E436CC111603042F2D3AE1CD00A4B6E1B9A4CEDF9BCBECC7D8D2C1AF0227666FD9F66AEF430F707C5100EC2182F26B48A8262748CE1FFC15B59FB6EC7F7C2FAFD3076D09DCC94AF954CE753F9436B36DE771337733680398DA954318FF44BEE5815B6E190F6C85B1E7E952365D616FDBAB263B3BA1C5CF2295C9D2D4C85DF9283F42C51C68CEA59F0759E005446BF6EF893 -4151C319CD08A79D -00CA4502D83F453FDD9C4A22AB1456A9A346BE3D140D879DBC15D99A056FC52E9EC665F83A559E3F1FDCFBEB9D154A17594CB4C45F255A733F77D6B91AF613931F75D6ECCADA8C9BE98922F51BCE1DC8273A83F59132D17D10F1C18FFB53387A6C121A8D0BD93038D78CED7AEAFBBA3ECCC55B506C7251118EC5B5B36C12AD222F4B66D540E01ACF26DEFFA4E125268C78428B91F99337C6075B4D34E9A3358BD807315AC14404C819E1C4D902BABA26E7CB27621D5A5FD4FE863C256D3079DEEEAB192B22E6CCCBDE84551AD6DC98D73B940D0E2C98AD1DFED3CAA532B6AB228FBF32E102582DB9E65420541E1DCCD2DC324A461FE51B38C7CDC1FB788E229675 -15ABA5BE8DCEB856 -6A3B04BEF60730647CDD35C919BA8E736E2BEF26656E2E87B99AC8408FC38678E2A38836F2ED1DC98F4A89431FEB19918583C991103FA2D3FA00311B55CA5AE4EA7E67764401BCB3ACA040C60DF121551A7B5A475D5020454B9251EB8D9D2F30D03FBBE9234E22C8853847295AE30C7B06F804AD240D62809368D98D6B8C505F0E311FD4E653F7E10D3097F2F1956D9EB51DD2C1ABCBCA1C07D0C5BE75CE65228FF656C06CA83C487CA3E4CB34DDC06D2B3A80DDC443191DF4DC4D84A09A8F5B7DB0771A7ACA881AC6780D991AC1AADA92AAA032F1933F193108B6D1B3175EE5F44FD59634C02CD0BDF5023417F6ACACAF8422385CDC9E204CE07FFF59AC3485 -358DABF62A55AC40 -00914A743AFC4C0F8CBA1E5FEF85C2EF8D9D3CF6374CF46697F8165288A7BDEECFB5A21D9B615265650E231EF2511EC4D44C703281C09D1E5E9F259B2B52E6C97FC0FCA2F2B0AE4FB0F104812F9C8E89C84D5038487B48BFB966DB630CA8B7AC8CDFAF8B537EDDECD876D4ABD7BE358A5CBD86AEFDD2497C2F9D3144308B35F288F0C87E7912EF546A39FBF00FEC31A4DDA82480B66F1B743919F1E1632CD876ECF7CEE4959BC41FB6CB226A1FFAA798711DEC1530511883DE51E16C6D68A7A57EFF698A60671D67412427D96D367433D228EAD5DC415CD2CFF87D068354EA7241408C38619FED8ACA88D0EF3CC129CE08C5E6F6408035ECACD4EB962752C9C53B -4062673907D8E316 -39269175DB67F1B3F97B1DDC57130BDC25955B2D379428BA686786303D05C74D5C6CF95D5CD982843DCE0D312DFFB497B001D67B4F812D56221734A35D557B7C5697EFC3FF7F26AE9D8E4A5AD2C625173221062767CE7D53A73BC672675A02DBAF9D43C319317B36F433427CD87A10F34350F5A772A56DFA4B4F8BD0B60C159402A61C24F62CDD6D0E593C7B30C2FD645F73E8AE8B96B832104C42A0A0A51E95208244726E05FFAFA2B120467E8DA7A4C6A4D9A5AC0826FFED7E9FE4DA33BD6BD550B620521E17384DE751E745B9BE4B4AF02E9CFEBD6538A9E3B9ED447024DEB6D51BBCDD9BB5AEB95762FA7DBB212981DC15418563EB563BBBEC630832DAFE -35E461A9A295DF9C -615A7251BE5271BC8435204322B8BBB62AD30581266CBA8FC9757D899C2B79D846B493E96D473CAD7172E8841F164C9CDCDB68BB5F608DEBD3C8E0FC093572A35C722DAD42BB5E90834CD001E0F4449B13E734B957126BE870AB5B7F325C0E9A7EAA9E3063B0D20F00BD48388175999C600F27FEFFFBF2DFCF1F77BE56847CF3CD4CE4EAEEE6A330DED5D2BF62416727B08126AD2B138335D10D08EFC79B8EB61CA2D145358BD60817751A82656D70B817889CA481F823A8DE9E1E20A90933AC45BEA187F475B89CD030BD7DE29CE6639D8AFDEC5592BD7F8195C8EAFED90C2CB2C06E3DE4BD64C4FF07F3FAFE8D6818A12FFE35C0ECD4A054C2B947A1BA89D1 -573111111ACA0D93 -6316A80ECB6275D6C3BFDD3E7FE6154E74024828F0F4FDF5AB2313328B02233F53F4D7564261F85AE5EADC7C66E832624D59AEC5A383451D23616528EA35F6667484E695A891F825D6BD2FF9EC273AA395A07C1349AF995782CC418A280AD9AB2B2F09EB3C1BD3AEB6F16E1E4869AC50582B3F93F4D0EE3DECF9F66B1537809208DD70ABA43156589F6DA0B9B97976A3CB6445594BCA64FB579A0387A3EAA9DC494DD4C7DB27C09D9EF2402E7511D5660C6A25DA3CAE159BA730FAF2D941838DBD449103475DA2460740FD468E42D7ECE7106CF7A6B253F2925DA720A6C5EEF16EBC9BBFDB844468047B82EB2FD6D6B9DA3FB256CB246106350EEBFB30A0C726 -772B2430C9450598 -00BF90136D8864E6DEED58C561CAB7671C36DD21AC5C8F2040B35A65BDC8E960F1A02237248247370C620B03E1AA31505478843417B08B6BE03A128DF36872E26BB386DFB9C1DA55245680A7BCA3285DA1BBC57B94854EAECC42FCECF3E353B029466545F8946B25896355E036092CDA3820B2844CC8BF511757393805C922A60F0A99038AFC26198488F1D27B8B02169A99D1BF0595A4788877CCAFE662FE9BC3B479A47DA88D3BFA22D9627735F44BB730EF2096BF206F9B8A876F5D4AD6EFCCB4CD01116C7350ED015A992F061C4836B381A3DA3833B74F0282169D84D53BA60FC65A08378BB662EA4DF7F8D43D2DF9B04EA3BECD7E773537E2C79075900B58 -01DF50A664CF1D14 -60E101B0E7C738EDB968359C344DAE563D4AB2F9D3426E011D62E866EADBAEB7A4E76ADD22A91F2DF9BBFF8AA35F5241085B08FC498DE35AEFBCD2A3C8AAA495083D821FCACA1DA95DEA3BEAE177613140A9BB67892EF19CB214BE0F571300DB39A23E9F89B13674A5BDA37A04CC5AD60F79EF7CEEC84F1A97F9891F618167D0D76E96707E6C16ADABDE52DBBF8134A4E47CC82A32A73D2BC90E57BEFFB0ADAC64399456D7D3F79E089DEE4CE668A569301C97F625FBA7D6AB81388B963A4CCC93A540B2F3DF5B57F07D3118E0BE12C4BB1C4803093B5C2AB1D339F79BE138B13743C18BB08906E198EB4BC687D3F62685F84ECB6AB2E13245C7991A7EB28DB3 -7F6C9FC2003FE0DD -3ED84F388DE329902A741978EB5FB06D0628B9D749DC5814428534392A4D20ADA153C7693CAF78D9FD2F21499A6F555C98A489534AE5F342F039B0A2D4F70E5F6DB1E9A77201F1DA3FF4BC5361437FF972BCDADC2D53B418EBF95904DA2DF5384EFE37C4EA93FEBCBCDD1EC9F44996A3CCCD56992FC8C4137A12C30E2F74093C0F893E12F501991C3AF7156EE92CEC0B7A41D56C54EECC44EFDB217C140BB3C4C1831F659EDD5E56AF4D6DAACAAE997BAD2999BD4FBEDAA16B3AEE70BF3800773E0C6F876E9C0EE6679E939E6A24B0D1007D30AC448D9F387B92A2E2FB235E944CA4E28A94C13CDA148F3943FAA5C55CF4209B87C99345DE474777F657E52C5A -5F3BF3C635D3E6E5 -009FD2B9DD4604B189A8F36BCA322413F2BD58260C7122F608116C9AE252C777AB97250FA9244992617C5617142BF54ED34A2654EE9B72705B9D4EE5765FDF741E3F723C52BAC25D1AF19A01BD738281B73BD04BDD500AB1450DF130C55C30BFC0B5FC0A7AF8869AE45DCA02271E1D4BB88A6D1365133E5D8B3D03804B57178DDE1C6CB8C65E716D31BD62DB4337D329FBA97EF669D0D500C562FD87C7C492E9A809203683838A7F93C508064268ADFA34152AE7A9F9167909E551CED3141D4DA2DB29C8710FDDC1F49F1B58A374F4D3CFB03AA0AE91A93B77306169E0EE1A272F174B801EB4CD846EA90BCC8F8D3F1C0CCE3BD8AA19C4EBD3A043EFBDF532833E -34F6B6CC1BAE576E -35C54FD3679D8208F4A8B362DEC23F919ECAF2658704E6F2363F42984E3CBCEAB528CD0271C99EE18D613BF585039812C79A2F044AE1EAC2564765D0C53971384F932AA3FEFA3961BFF1BF63247A9E3600AB0123F325E5A941C7F2B353A22D20154D547EB32DA0FE72E3800A2F84B0764FE793AECBF95CA447D78F188BC7DF3A6B3FF79D474033B2CC9951AAAD3F494F47CD660322D384B56C4EA19234F6D7BF2E837EE65E61A3024EBDCFBA9997175E0333B86B89077D4C5D3AB23B445AC1FF09B3137272D0AEEC2696BFB23CF87B753F34551DD42F1E211B907FFDA7FBABFDE960B244F444967018FDC504F2AB7F153116B41B4BCEF7BC5A86B0DEFA0FA4DE -2A6FF2A300F1C8E2 -0096E56DAE3D7F1240848D881A713AEF17A450954E6DE20503ECC4B22593264014986514EA7043089F7B34F53B28EB0FF2E3D70935CD4128C65AA392E0E3A9ACB510AA87E31AAF801305488448572EC26EDB1AB4DCC5C15DA1F15EBBD41B31E1B095E081AEAF71504C0CE6295F3D149E99658131D89DAD45463D8134F7154C35AA15C9892475449748EED894BD7F266B1ED02B5DECF657BC275EE697729770FDEBE930FCFBF96182BA227B6B0BB9FC12CF5E0C14CCAB8C2A96C7B62A425A175436A7A56FB237354840A72586F99A144048D10CF584D32EAFC70B39B65F6066479A56B31C09AEA6EFBB7A50185981CB7DCD8C3FC54D41C841544A1CB5E842ED3FA3 -73C741E8AB7B59C4 -2F4888EA006EA072FCAD2F6BCA21A515267E2842CFD0CFE240C0F1D1BAC4A6CB2B933CB4DEC80CDCE9ECD2ECBA6D86C74B55E68467627DF4B82608780D5913C7CB50A8D1F99623D30C4D37646D49F0846B3398791DA3FEA8CB38F2749CF3C8631FFA1DE5CBFDF8DD1175342073529890185A797B7B61F10C933346877F8B9B78DBEDE80681ACFA983FFC63197420B69F16656DF3E9A43EC42B3BFB70FEEF9C95630B02DB796C3D88A323F3EB79FFA4CAF61B9D581A85AA715A11589C62E6E69A7C9294B70EA3D229F920FC009657040A45A2E435ABF14A7764ABDEE385B7CB75C86F01F1C78F6077B12843AE2EBD357A313BAD1AD237F62DAF06A4EFA9603413 -763C89357ECD0309 -35241D47D7E90678AF1315A5A5E0F67713BC401C7512B5DD1F1AB14EEDDBB28CD9217EBD460F025A6C63922470EFB3ECC8339D08F28EC2C1A54DA814D43244F5DB7122C725B5AA6A4DDB54E09213AD7F3EE17B12C8FC2D09A7B86BAFA3BCE9F63EA04B16397476BB0CE95A2E1D2C5F8756266A128732B16C0F4C977B05B07052A20520D7BD42E60BBCD6807A140AD2333BDCC8AFB1BCA07C90B63E89606C1D356AF8ECE6252BC4432FD904AF92251354370F78AF4512C5E3573165FEC4245A6C7C30A30066913E8463C82BA7736E3925D050076ED7C99399E69E20889893A1478173E7C04D055B60FABD85A0708F426AA1DBC2B99EE628BB2BE58432676FDBA8 -0C08D5823DD90A96 -360DE338658FAD10388E313BD110B498400E43B9064A97666496E346D57946B510163270FC96907220FBCE9BBF469ECBEFE660C44EBD37A17B86DF01C444537280F136416DD13D39552433E900B91844638CC047F5E80897A32A0E3CEEB1C461CC839056189E19959E8F57EA91A9B1CB7C31206506D231E3B502DF9BCB4657BB36FCF1F7EC2ADAE7693099C31E547EDF346794E4D40230D6D71ECA6084C848A02A41D70525F23E076A190A2CA66951C25D37D5F5F3409356856C5D0801A7224432B0A2BF06FE93666BBFBDCDEBEAF8A06229D13DF27CE9A05B4CECD7EAD5DBB332C49D7D3DC1ADC134434134AD87E74AFCEC036643ABA7CD16FD79F8D84E0353 -6CB934A440B4A980 -620E5A339FD5AF9F6D0AB5F903778ED72E5E9EBE991E1041A7839181EE732867588DCAB67B68BF697987B3FD42E1C3555E6E77FEE4B3BFA2775C8C8BEA88632C3E3B53BB9BC2B0A3F96E4253A93982C44BAB47675B4A9D1DD1768C631ECB1CDB5D07657F23AEE31097B36D6FF53B223A8219E5F2377C419BE919E24135623D149714545A3471891F028E5E8CAA979ABCD44A576CE3D6D6AD0833CDAE73F8439112726D79DFF5EDB42D0F56DE80C19BBFF846200CA51DAEAF63D9A0CAB9D8B40F358A46DAC79B010F23EEBDFAB262D181040828B8FE063E076199A071985D332CA0DFAB348D0FA94AAE591CA2F965CC2C26667F1C27F50B25C7223129FDF28EB6 -632E01149E5FB819 -3229260C855E0AAEDA15FE704A87D5E753A69BCA3E2D3C2F403602F8F70204CB9B01959E0EEE034CB7AAB8657055F1176D01DCBF98C25B61D47EEA0596F0643B5F4D1D16F81EA2FABFCB9F331F1A2C78BC96D3155E1C0036B284AEE35BEF1B9DC2C53138959C4FAD45BDD4B530699D2EB6F16349834DAE6861A49162FC26CC33EB980BF5202DC0C0C96C45CF6CA99ADE15876690579F657C4931E11518A6A8DC6D47732F81BEEF1E8BEA7CDEF8DD38E477427E20C8F9193A39093BB98707AFCDFED25E26044661E13BB28F53A2E03B5778D202763422F368C9F56279BF7ED8D588528C6C2F899FFEB18C141FE427341821330D3BD230E0A1D193DAAC832FED1D -5BA46BE8545E8985 -3DF9DE862D35071664EE985E0597EC26362A454F9DE002467EC40AC601B163F2E56B4150F0F3248EF01545C860E95AD92B813674EC98D447A63417D3C3435FFF6A5EFF4DAF3F4235F12E99E7D0AF0352E3788B5708F7E74F44DEBEEC533BC9896641B24E7967378E64A4E81A05B3CCE6AC64AE0D5A1E59107F2F9C0A9FBAB5EAEF5A8398CD3ADC4DDB540C90EBE0F6FDF879092E07C9641707F13F07D224FB5CD85ACE3C7329F5152F9231CDBAB4BF6630D29468435708B30455DAD0CB9C3BB3C47A46AFD914AF9BB243EC56754D99D3764F2D41A93194206BA58D4FDE87B5EA1A2A86553B4ACC473FF408D85BEB3172DFB5D2FBFC8C33326B2D79CAF38EB25F -438CF1A4D969EC4D -07C2884362ECF72A371FF4194F0DAA6186DAFB944ECEFEA6F5CEE7508D2B5A42580E9E87F338968418DBC25C1A595132F32575AEA3A16F40CDC1754143D56FBAE43C815567592440F45353E984392D2A585DE20F32FB7AE647ABED2F274BC49EA919C08B6D7F41E000A2F9063E55329A0503E6CBE6E9477C035A87B1423C50CB84191661B4A9D9C7AC0092D82B3F1ABA6FA922F4809C71AD0D9DAFE01B37C6CC535FF8FC7AFA4DA90EFA6BEC879D524E4DBF6142855849A000066CECAFEA05C7E7035F9DD85CE692A6AEAA4F496ABF2228C9AB3EE9CAA7BA6F94487D81C029396B724E4A7D2686EE2ED0C10EC13C79C5902B048FEEFAAAA3AF79A7F0C3798B03 -06AAB25728AA7059 -383F58C646B3253445DC5D68D2733D5482CB1DEC003D5D9D28B30580F5712DB106F32FBF931B4EA19E5BF47E350996DDA5AADA73ABAC48C5FE6D579317E01760838067C5EAE0382C421180910530FB579FD7719AE82237AD64402A7A3604EAC7DFD1554BC243671663AEF228890FDF964363EAF6DC2203C4706DDB962B60CDB9B0C78CD4308F17866C61222277EB62269D24CDC344405C5D4DFD6799F799D4495FCB612D8CA53A040A0D273FB9C72BED37FF07058D71745CC9C05CCF3FC050E5499654D4B30E785CC4CB40976A54E69867B88672EFDE298DA4CE5F1C7A88753DA6F18D760CAA2CA0CC083F6AF853B6705C3A1473DA3D3CE2343F96B6A46B72F1 -4556B6352391B454 -45E4F96A28F29D79114283A669AA223BD1CD41A9A2C17F41960B944D5FCA6D50DF6DD6D8800538266DD1D2424B0FE208A029FA92B7A3C7F4BC529F265A559FB310126BC10420D5AB9442799ABA51E7B57954A111841778181E5E99803E567B0BAED02BFFD7B1AE6049DA3C5E09BF78184931B3EF778E7ABA4A49D9ADFA2435D4AB7F30F23CAEFAA7E6739484637101B6CA2CCAD20D2C47D9005B7A623260495F59674542CE7B4C5EF26ABE00E8F0556B852181DB92D13ED1416AA5B6D20290B7066C36CC49644FEBEEADA65B2231D856A8880D299A14E704B0AF5E52FD4C13C43F3B7784C5322F882F640A9CEB458C5815459E8D85FAF8207EF104F6493D844B -3ECCECA82D715914 -009159309B27909B9D1D56631F36C2963A5B9E0B1A30BD21B2D63D19AC536C97448A7C04A333B328168F99534F55E0E7D39CD8163ED50F034432ABDEE0C7D44EA0B9E19C2BC97F061348833CA4C1C6B2124705372BB7AAE0601BD6128E167942C8EE2D2C32A984A58F70A3B6E04165491031A3133FDD552D4D7B96000044541731F990CF936F36078BB79B43BBDFC6D91D88B063688A57759BDC2C7C8F3C58612AD7B16B17EA5EB85E06E9B514A5FB7C39D0D6BFF1725B714BD321B540A42C594F465C2304A2EEADB32F3F3C9927D30055B14ABE8CDAE76131E920518D43B7148F4AE47C23DD21EA7C83E78EF436034C02C6C0F292FA4FE46504D5508AF3984A2C -630B38D791A5F2B0 -1770F84456306A6F3848A6C9AC81F968624546F4DE2CA2698EF7520D4B351ACB810E43AF558BFA6ED55F34732076463D09DFC0C05E8A2396D0CB09E973E7DA2211AA098BE2C211BE4B82AD25786F0A4338D5D78786EAA4663F59141E6CE2B7E041F7A57FCAC0AF33C74D42766CE8F95DDB38E1FD3A9B5A68585C2A2A380B3D51BC7DDC5B23D8AE363B5BCA9A444CF0D06519807ABFA82F7A10F08B4D1F6B0FF105763F4D4095147C4826B8784F16C677A18826B2F10B7B545D92C504AF6661C98D0100C65F4B7976291C3E775D52F26CB77008F0733D5AFEAAEBDAF5544D16AF21EE2051990576199AB568A172C0673A61E90178AEA6399DCA5AC80541CE34E1 -33D68258B6D6CC11 -79CDB710C9963C9D33050F0A26BFC0B28F6F6FD318DA7A553D45C04F79AC3D99308E63D53B3316E9FBDC159095A8ED6A260AE26B35040616AE6BAF14F7F8802B3B22C9C2CF96B055207C4C434CDF077FF72D20967A053867784F7AD21B75389F65AC4D8AEC1D441A7A7FFF390C071494A027CD6463CABF97A1E89FEC9BA5CAB5A2CA4E39006611BEA4791BE98D14924960F36EE81E64A4049CCD257CB25565F4A9D0F3B00639FA42AC9751DE2D183703EDC24FF77DF94D83B591E4C6193EEC49D95628C275B4BEED591718C3B01384498D54322460980863271552A27CEDF6AF822B57CBA1D336B89B1850930B06F8AC230FD650EEC31C0D00365D1C82CE9D4A -60862228C0C518D2 -09529C29CADE27ED4A053B91F2F4DC4CFCDEF0410914F4346ED4FC4C9EDAD71F4639005DF68EB7346584C9D0611864FABEAEAF513811325808B50708B37249C44AEB5B3A6EED2C6A563B810D8643ACE78D17C7636AF6EE1D43500D4043A16A331FB7D33E2BED586A1ACE2F23D2C1751CF7603A2EBC88EB563DCE06938068835AB133013F665C21F6E9FA1EFB31241E4B9738CE87CAB6042427D5659DF1FF1EF3F5AE22C7CE2C74E21D6CD8E1364D973A00E330113195C6A40F63B07E4A8B3C06FD73CC2BDAC7510FA7F838A2675ACD38FF2F1A9461F10A0488DCE51321C0C07DCF30A587AF72BCD136816054B9BB857B1CDFEC0EAD8417CD4BDB1FDC551CE075 -4DE080F22520150E -777243C554C8721613CD33206C24E38618DD0146B5AE9C1C1A6F742BD7EA7385830F0D1C519E6BB8BF69757497E66CA8B934A583D81366EF380A26C7CE8D8CE089929BE9A4BFFF9F1C7EC8151B2672405BE320CFD76A0BD10C3DF551C5D1D81A478428DEB95A347B544079E1BEBE757F866AC1D4159253ED110E3103C46A610CA89697F35BBDD0E227A555A9D0FD48BE124C227F98C1C351E701E2684ED124586A7A3F789E32614B0876CB9D38C6E07EAAE9AD16615896FA69CCE6D5B158DF4BC1286EFAB746DF9967C8DDB4D1ACEAD3A1FE249451E3CE5ED7D36CAEFB95F58A55C56A91AC8BA86D799AD16311BC63E63711032067CAB58B29B92E9F11A51ED7 -11F3527CF2550530 -009D501910593158BC8950AFCD0C116AFD71BA369C6C342895B5302ACFF0C7AC350B5268D9E41C080088ABAD6D6D82F64745C92444A56B518B62A669B5483E8A2D90500C4D96A7543867A563E4BF5882B09697D9305A3D38DAC3981C5E6BB29D1F51DADEA8A1A91BA9E9E3B7772297DD82654E08199FE47E7F0F998F1D4EA7248BBFB72B109C8D36E8A4BD6315E3A5C41693820BBF5203CD84F91CF7A9416457401A07245522FBCF58C66B847E53FCDAE91734F8FD404CED7C142F0E29EAACF4099BC17464DE8110C5527CA0FE2F4ADEB58BB4E38DC7E1B33ADD45F4A47730810B0697C40CD7237DC9AC2273AF610837D74FC2816C2BC307CC9359C903135F3418 -1915B7C682FD47AE -6600D202B72D91D19FF2317DDC01E7F725970EEF9FDC150F6ED0DB07EE32FD80BCB13B95F1D95D562436C3524527F28FCAE0CBB8ECB74AC8DD970328EFAF8257F87EB00C00FE4BA1A62836E4C6D50B21716A4416204F0329CF072C56CDAA4544E3E4FFA743776111CA93338B3AB385AEC19197EC22AE3D6E4E1F4EF95099C2ED1B149F0E2C80DC3735DE54251DB83EE6C0D9CF7E51FF2B7C06BBB809634E439B844DA8DC009A5837A4F95379D939E41619056847AC5E25F284AFDB191CBB89208C7F86940C1793658ADAA66783D94C80C7F48CCB2119A16B6C377EC8D4BB028009D4E45C9619DA45C0CE08B3509A8EF8DA2946240A437CE839183459F6B0621C -4C127BCC284030F1 -22723F65043F4DE5332648DE2CAECE881CAD7F538A08D5B9E13FB1F4C63A3495AA2EA718D2616B65DAEAAD4D099D70690A952A12B07EFE378D2D795B32471F8B9B546B3346AAD868DECE98E64CE6EBF642B89ED50BEFD8A219864CAD905B6C6C64FAAE4FBECFB3E7DD3FAAD2877717D8C88C98B1EE958810294713B8D92A450CC0AEBC89CA51F345CCC006B185EB7B0FAAF02C88584444AFEA750FD8D489A3EE7BD623987E138F3433EE91C4062B9AABD47E2FCEBD353364C3AEE7E979A66E42B1890EBBB0CDAA25BC6E22898EFD93221DCA357577A9A437F3BEB913D2EF112D6A6D29D4731B1F0EAF98AB02E43C2E6727DC8D3856CE85BF064F741261DFBF1C -0A26D1C7842248A3 -00C2A37727DA08FE01B2DD5A61ED35131A3595B26C8FF9E9E58387B151C640E080480A004963FBAE0E4B4638F161E451A7076BDB3DB58697B009E49C539E91D251E2FB76ED78C8EB66E393CDD7F7DAE20BD279F9DAF4499E6AC8D246952FBE591BFCDB4A2DE41AFC3C556573205E300156B4795EC949E5E25F9635A5E1C7BE78C4126560EAC1B8561F41B6D34675736096549EA6C845E2DF4FCC22AF0DAE44B5FE9091471996285C898F6A755FEA8E8CD69FD749C19AF632ED4F2B3351664B56F4F82F7C5CE84A1ED3886E44753925A5BD334C947C559EF8CA71B50EFD01486D8E4460C8D9F3EBA4E9BE867D17A32C061359B0EAFA80439CCB4DB7EFA8F20CE9B2 -5DCE97342F7DA25A -08B234C21977657CC7FC559DEB59EC73CA49C4783B29C02B05D1BE454FBEED7A1B2579A3233E506A93B9F786144A352A2FD2CCB69FCC6E08FA58DEAAFF4B354AC9D7C3D12A6948229352CE3EC2CFAF763F285E9836DF4DC375FE7F778E7F86C8AFAA8198FD4A36D329AB6E1D805B1DD6C74DBB607ADE31C5623D4ACBB92FDDD083EF18A1B36F5F7AD61B337CE0C28253B02B80CD77D7557CEE508C4943514CECC655C44F05EAAF9F9C270E98CEF2B0EE5BB5373F1BEBC0A494CBEDC9768F28F4BD63D5E84B110A7426DC067282691DF12DC8924E46D2240721AEB2A6FB7353B9F817674E9436DC0A1E020DADB66F13AC36253DD9B6CDCE01AA513A289F69EDAF -1BC5EDC32BD17681 -7A2FD83867513ABF203F51AD38381512C86756C025282F4D4E664965C2112EC4DDE37B6BD23B6ED268C1317DF8CEEA60C5F9CD1A0FDC9AFFC9583EC4E7D298ACCFFF4E874B26A37C93FD1102A748D24F060CC3B2C76377B6BFB393C52951A619FD7E71F46A47CBFBAAC6CE37E9D8E70E358C8266EC27F021AEC7517A83F23F2DAF923DA65AD259CA7E119C5CCDCDB1FA8DDE11E10AF0F1EE2EA467C32AFF0180A329526A6C322D9FF8EA0400B2D7FB53098F3343C8B18D26FF070E0A84CDA2DC4CD4ED1EAF5C1EDC03F97A801A3E204379CA1067773CF70EEB92E3976CD02794EE72A3FAA4B50F2F0128E4F2E7D63FDC3DB6CB1BC3F37CD9A60032F883360040 -0D32607995B71772 -384177F205C4F54704454E846AE27B1003856C199C74A9F151CE06AF1EC05A1CF740FA1225C1FEF34FD9BAD5CD9D205652F09F2DEB33CF65A298DE852CD806B43FF020EB925698E4AD25490D7CEC0F16FDD82EFCCE20DD4C0A166CE8AF972EF9BCDDB78364497D5BDB5C5377DBAFBFF2893EC4CF7B24C14C7B00E2B1A82C8673274DC1E7D74F964933D1432D88A9CE792DA56C05EAA3CC7134FEC98F7D6AFE0431D49637764C5CC58997944E8FD32CCAE0882AE39DE7E7A5E01DDA9EF7E9198AABA1A6B0BE2D5EC82AFAAE211D5A75E9CAD0E487FFF2D37D7F8AF8E6C9851C0757EFFE951C193438D3498FB3392ACA50B75C263ADED3148DD6422B3CC8EA07F7 -32CD62567D520C10 -6616BA0E1ADC4F0113268AF02464498F7280B1427AAE3DFD2F307E87F890881CB20759FD766C433EC209BC40BB344915BC02A5E3300DD15EC7C29E417CFF31165736FB88961C0761F2ADBDAB1365F5FD6BB18CA98014EFD59EDB9E43342103E3986E5FDAD5DADAA94C186A866BB0675B8871C4584939171EAA70B45E5DDB823EBE36C9740505CB590239FBF3645D915B74084A5DB4D2A471C293FF7DFD65DFF53A75F1FB1C143F4937304FADB486977221C84D31F718B5E78246F063584EF26C7EA521B71D683E6CFC40CAF73573E6EBC0128BC01872E142A85FB7F7BFD2B90A17DE0E5DC8B802CB574AF351948B8563E351CC5B92A24397588FBE5CF1834121 -0201949B8F871282 -3DDA1F5395B3C5BA681661540A619E37CAA719025637B6E01F4FB3CD049FA56CF36FD0DA8148B845E07504EC872B063EBBCF0D223675EBE0685EAE280E764CB10A81C533D466BDB9A0A2040E32801B4EFABEF30A40873364728C2761A1B4862A78E594770C3EF2BE71000FB27BFF27CC07C4367CE1AF1C8D89CAAA1CB816B7CBD6BEFE4C7354C218F2D67B861C902E336CDEDBBF59B656D5087A50221CDE9AD3A00AB79769ED05A17E5381335C243D05556D6B858946299491917C7478D3843F09B60C0ECDB0635F8F6B1E1A9070A1C05E86EA6CF2E9FBFEE02F2C5D1BBE0BF5CC34B0CAE4F5F44739D47F45C41160A490BC5E07C28BCE0C029B396C3D7CA7C0 -3708F5D33A072D9D -360C0D1B5FFEA3E327A102D77CC6CCD2513D83FF6E2C081CC6B7C781BA229270FC6E3CAF240F762F4DB866FAE7E086559831A0D2E3916CFF40533FBD1209A3143214700E362D00C3078EB6305AF020156E5C2D6CACAD84512C8303846122B7FA454B6F53851821429CDE43B59E06441BBE4F4784907D12A2F533A09A1DC1ABFDF2A289FCBCB73C8F3E2CB76353B9CE048D73640B4EA1974F6AEFCC0DD0B68CA690339936D739A59E30A17572ED9D3356913FFD05313EDD2C12489801176D231F0BE0648163700CE05981BD77CC1FA15466BF0B1E9A8A9B291DF441FDC39034064E9C109779F0E9EEAF86D95F5A32D186F019AA8C04BFEFE32436FE2FF3764287 -52D42BCCF2B153EF -06A9DF067C7FB8510421FEC1CB2217D7C59BEC7FD031C2753A13A177EAD2C2A6712823683BB3D3B221BB40D8044D6C23B1A5AB855AB9EAC93846AB92E17F95CDCF52E8C567D48B7780699B759EA51840B18DADD7DC8B04CF8DBED875F5CB9EB5435EA36AD3954C639BAC0EB523B233011CD8E25F5EA77754285FC2AE3D7A451754A3DA0349652CCDEF1485A2643C021804286756207ADD1F14583C31619F3EA9CD31D95C0D05FA93AF6F3F690300C442E2205706893CB33D5468189463511EF3192DEECDC884C1DA5BC9BDBBCDF64C441AA7721AB110367E62DD2C94ADCA75CE9BC308D53EAA5E50A5D0F1D415434B47411010C7823C3F320AC659C48B2DF3CE -7D6A1BEB8E845161 -00C2895874BCADF9135ADB60C46C93D8F7AC50BCA21066AC670DEB2066573911A6614F30EE03C9F69098D789E159AD4BC6CD99CE42EEB984E18848E8D8B4E3E776DC27DBE8ED4E38457A0B8379E172424B1F2917E040CF3344966EEC5C055A7DAC1702122800D5844804584EAB7524A6854624B23ADAE45A931DBC6DBFF2A7FFCA8211A626A03697B2986F8732057C1C708146DEA4A412DF39FAF934C8FE3BA70D9B2DC032DE9EF86BD74F0281D2D19A518239968D47D252FEB8FA2EBC9C183BF9E18010ACAFA6E4AAF1EC5301928444D89B0DA044B51D0E004A3EAF4086255EB23A61BD126A1EA78D529A59D579551AEB0E53C07F9EF5BFB4097D13F193516C4F -7F364FAAC5B73164 -6C559377A1697F66A5AD2DF9FFB7ABB7BB1122B0AA23050F02EB40091B69886679832D9D8D07153AD78339D41C344228522A92283D08A0578092E9875AE260AC2D680F4B41251E62ECA98881920ACD4DC5E7D32CAD9295443F57DA4607E9FA1E5CC50369C25F0B9ED71271303AEF22C8CE4D6CDB19128E3FF639E4B3E278BD6BC630F3095D230895B28FDE96941C88FEDB1796DDB18B5DED3FDD50B1D1EC110B66811936A01DA08EFA14466B61FCEBD144E87E595835BC254325797CAE2F3062208E40156988026C8004C7A894906B56A5A69C60AC533E131A346B4F3A55485AFD4D4FB8E77308BB0B9C6A46E534528455D1B54D308D72D5E8255C3DDD2B4253 -2ADDEA4231B0DC9D -00BB56D6F70D7DB8A033A9AF5983B9B97A175F219564D31D063772DD364F2E9ED20D8970BC27B7E4C7DC3ECA1826103DEE4E4973B85955F2FA4DBCA0DDBC5CD1E94254FF6EE9011B5C96E1D0272A61D1F0CE19DC4B6D767A01B7F4642BFE703B7B29550334FF1D0B19B52F1D417D7674AE1A6B88EE5804775FB17310113E1FC7B87DF59FE03218EE7F2782CADF229C2897610F6F01D96CA87DEAADB66B88FA0CBCD05C76A152EE8D4C22325B698FA0C9F88F6BB5128B186EAA2272E6BF97EE6CD61A9CBB81F16708557BCFB74692C0432E4FDEE12E3F6A93630BDD703DAE47950E26BE28EB11FBEED7C526C3F8BDAA2C259A6B9BA1C3ACA15A9A5E0CAF6342D63C -29C728DAE7B759D4 -7D9C6A8D3A69A5E6A76C1BAC8F78B5B451ACBF5D972758BBD76D045DA27E586572B25F2580497B41ABE72661ADD6089E716DCCED7F801144D75A32816E43BC9C392857C7AE47683FE399FB5E6E5F6D56C148DB5DF3E05B9D72FB6F81515FAA4E51944D9944F4CCBA4CB1DFA7317B09D8643D16ABF5D20FE66F76F19AAD5C9813E282C5A0A4E63F726DF5BECD9F6BCC6020EAC3701CBACA94FB77FDF91FBEECCE5AFE2932CAED413834032FF7BCF0B190CF420503875C2BD7013BA0D285881D5927F6F05690CA3CAAA9A4E13D3CB1585F2B1C759D83290A500DD7CA058D501349B1AB08035C9F37A3682FBC3DEFB02B82AE0D96135D6CB32803B980539414B2F0 -492244C9553DDB60 -190C6E7738DC9DA645CACC24CC781C9519397725599980309226ACEA63131AEF5934FB18D3B20D9C282DD460797C66000A6B4E19B8073FF52DB8AFFD8E4CDD753A9D50F1F8F6704F2ED31A13A229C92267637478572474241324E81E21296311EF73EEB3844D3F7CB24860C7E860C700B0069DEA5FCAF73D8A0DCDC858546588355F96D63E76834FC76990291F004C196A5568D93564084DC34C3DAA699E4DB664E03B424C1F880DAA6867A2FD07B6C52C87D6B1CA1AB7743C1717342FA114E4045031F4BC3140B6711AD68B1EA2C478867F8B48936F69A3B0E6821DC1745CE339F4D2207930494B9A72F08D148E825481880985D247ECD1A3A21A7866FCF460 -3CF3C8386B0AA07D -009E796F3B56BC0082797A88C379C27029817FCEF481AAA791234132013679448E139D9F1A44DB19B0920AA48D688818950A1753148EB682B8ED1EFCEDAAEE4BEA1D451B41D9CB754FD8F43AA6F40D5ADC7228F17F150C0211BAFB85C015324662D90562F15CC04C30D569B4BF46220EB89A17A18179720257F0D5440E1936A716E6032E0BD3DBC58C1E06F54EB90E7C76EDC18E0DAABDFFD0AA3383AEB29CF91E8C5913E6F30AB76DE8BEEE266806B5B0D52BDA63FCF4C578A880BEE6E6933DFBDA8567FD9C0835BF7334DE1EF5A9391F7318D418F83D3213E8E58847DE5E4968CF7CBFC184DB916952371B2DE98DF491488D25EE582579E0190DE29313F37A3A -5FBF258F30C8E4E8 -00A7566DBED7B9FF6DDAB5EB01957DF7272C3FD9D58D31A78FE2D460EBB2FC8A9A804A92F74A3A3BE4F1D214CFBC70D9B276812A3B08BAA09D84528086C5B41D600F1B5B8E7EB0C385B4F8D89C23B8C93B82DB20ED722A188DEE8606493CF19D2F57D96E92DD8CEE439682E3A563C5BDB732AF3AC6CE140FDD1713B48F880D758A8C5CD87C3CDC26027D3E42B3BA85A0B307825A5996F51B24BF6B5E422F82E1BE66B59B661C51D85483C412201E93911C7FCD0D90493CFD424FE1F0557CAEF0B4791F845B278D23EA8DCD6FB67D0CE3A37AA5477F2F602ABFE6E1AA753330C3FF5DE9D5ED20783C819CCAB78908123D60DED7FC1D744345DDBB860A86A2FD35B0 -0ACB09025ABAE789 -5DEE691D72EA64814E348AB19A7AB318E92F1CA4ED51D213FCF8DB3AD4E79995C46CEEA628D58032D81C81FF5B3361120C6BF73CA3D86A677B498261F9157D481297262B6425EEFD612F1505AAD3C2AE036BBD3DE5B48DA1B11876800972E998DF2F2C6D11BA4F0FC854A04D2699370472EFD228EA6C04558CD9352D6DBF9652C3612D816D9F209CD88254BCA0C67F1C0E76B4B02586BE68503DE913A82062A632677B77BE9667DAFEE3CC94E642ECDB98FE1A7DECCF0A6D2598F3139B025550FA63A76619FF1E65ED6A2BB920D3EA596928B10A055AC2647D1A584011B7EF67DE32DC55F4450FF528AAF4E11E15A74C4CACDF756B43B6D5DEE2172ABFF2445B -74755064453433E6 -4123D09042CEAD0644E077DAD88EE901515E8535584C90680C2A2293C730EB9620C9296A394E639285FA6770E498387D5B753CC29057C4ECCB9A360736651E717E3D0836664EAEFBDB4E75AD6A90D25F9B35299B1B7589ACD26F76DAD62186A1D71474C457BDC3F274694F53505F8F2F9844FEB337E23680AAABCC6C1D034444BF85FA6772F3DA09E9A0560B81CCBC9847E784593B5125FF29D13306468193C4B751DB3B9EAC32D754CC777049596CF6D5312D37C38676095E41569A387624C51FDFFEEB8A0FF17A160EC908C91D2195875EBE20377ADC924B8F97D9A1D25CC00D9FE955A5FEFE6F995E6C91495B1EDA9EA7042D631D27FBA81F8144E008735E -6686BD958771A83B -0096A97EEA4A7110FE8611A4975F7EC2944DF41F66CC7C4CD24646A36963B62FA7818811247549B02414DE7D8776630C0717013B18B0032BE44C0A4A7DE136C21FD94644985B8FA576782387F8BFC7032CB35E3A6874375CCF04DA2D8846D8534DC9E1E2DB9B34193297E6340DE69C21E46027144C82037B7BFD91D9936426002F3A000B7D6E464DA14593278E06B9615D71288D799CDCFC9889EB3746D0E7C665A58B1057B751A9677CFA0D403E6E5AFA23B8C50F9CF038BDF49899FB92BEF4AC83C521B45EADDD863D0C5347688A4FDE9927C6045F7ED20FD781A9FA42D9858B9FCCAB652081C6DBBD318C680C84DD00D2362FAABF416D78C446E39D36ED17BB -52B5C3585D5E3DD7 -0383858AD0988E14DFB489818FB7347EE8B90A1DFC50D435FD993F99E596AD208B785F690F9359678F0C94802B2DC748BBBB36FE47898D1C8BE1356F22EE0B22E0C7E451920E0C8D7D579457CB51781A4DA473D8F2A5A04929027C83E92028CABA77EF4A070E5E4728635BB97741EB590E9A90DDCF4C01216C54051663F522FB840ADE0286A98D69E85342C0AF8B971AE1889C0834BA29403A4A77BA6401AD362A26196AF5632A15EFBDAFECE3C80A9C5B992EBA71140BBA512BCB92995F1AD73FBF5C87D5BFFCE87BCE55F9908FE30D057A6D4C86E7C72835011C1E11FF3F60AC09B63C41065D9D33643B5303120A27F73340FA571341546528AF70C5AFA38D -31F5962CD90A3034 -0089ED83A2DF04D5E472BFBE6E27A876963E09DF495CAE2CD2DEFA96805D6EE9539230FBE51B6666668D4CF7B1DCD433D78CA706294978B1D04DD78068C4AA68935C5509AA36049C6C815F707E8F9ADFBC3BC495C1B50E770AF36CE673AC7DAFAEF6A0D2B3E7BEE207F5BD44DF2A43D47528FEB41F9CE606C1C0C16527F17164119A4E6FADD607FD57737B5A73FE8DC07B2E7F85A62FC6A4853A083381FC025BC7A676FA6A51AFEFB9233DC388BBE41F300A7FE07EF3C5ACC0F5A1916FBEB989DE2041EC1F7EB9C071940E7533049D4FAF73FC9B1F7AF86C95E53E5AA0DCA90199ED3F4617A87D504BB4E424329FA5C90D1DEC9E2AB4F3322748F07B31E930D894 -2A7BDDAF47351756 -32174E4FA3539BE11E823F9606EEBEFDF4527FC0DD7CDE683973A7324B53BF2A81BD4B780EB26324D6D83189A9761B179F6052843E1E47B8FCE1A7BF489893AEB71054DB5DFBE68D5CBEBCFEC3CC91A01E73A85EB7664C0B72BF9CD80B8C2F1A702AE2A186F58E6E241A01DB929B79B9BA03949FA640EA5715568ADA349084B7929985060F600ACD50C4DC9CE3F65D1CA90229043DE1E7E42B90011CD36D5FAC92553C62805DEE4946616B7B49DD7BEABAB56364130479ECDC3EC74895697C3A165DEB8BA63B655E021C937BAC45C2C554F082FC7D87BE36E7C26E09C0E2D32F4425C0F8E81885500C601AED7C13F903DFC67E7965B0A52496EBD1F070FBB1BE -072AE8E8FC24E8C1 -045AC33C9F721727EA82CEE2E5733519BAE08556016C513395879C2C2696D9065A6983800BD64837A8741388806ED99181031214A7AD3B5B9C8C0233F9B764D803F00A051379B3C5E41221153399C1298859FC3AEA42716C6629B3795B098FFCD4B14D10089FC4BF6510F4588F383BD65A67F2349DE7B06378CC67FF273C8F2EA28B767A58BAA97AB390466E8BA296DFA8F588F6219BE230C24D27C8A0D545DAACA23B92BFC3EBF5531242CA9D6EBE10CD41EC3212CDD031BD38DB4C73AFD13BC9C6E64C439F41C72AFBA0E2E9C37056FA30CF75E6B1983589952C49F5D1BAFE69B83C5E1A992C69558CA326A4008497C57877313C93F00BD2B8764AAC298B54 -12063DEF0680BE88 -008C3D506989B6AB18871E25B77172F14A4EAF2A6883C1BB4DEECB08E4BAA2945243ABADAB081F1A8B6D8DB7D04D58F50306181685A561C026897CE75A00797BF0FBB95C9B4E42342D36F4C187788832CFD65580BACBE6F3451CE91766EE280F5295D14F0E3863FB3A65C373428583A2F1992E1C7A2DC66AEDBD089ED067EDE39C793B794DD8503DC41217B133C2C59924BE7686583D03B2DD55BB5B7D7877EC0D0300272D0DD48B3D53A14959F90D094C36A6DEB419B1CE508B9FCCD8A1324524F7DEB269328B6EE5B9B15746AE6D6703CD5B8CDFBB4AF35ADA74317A3C07F6147A0FF8E406F3BCD810601E94B9874B98784B065CFBB70BD432AA335EE10E404A -150597715BED4232 -00BFAA72EE492EBB3BF51A0AB125E7ACF81C5B8991B2C1E23747B7A07DAB639A6E510D07C5D0757CD5478D1E10A95705DB222A1758891E94394963765D918F8BBD18CFA7F96F623513FDF4FFD45385581C16FBCAC9F0E29015CCFF2152E42678CF6755FB88277B629EE7DCF5A664D2F9134B92687024F8A458F3EE2018B5D5EC5B9226EDF56B1D5D06684943322702C0304DD37F92AAD7EC0D476873BE3EF264B696859BB210E8E2B737A529D072DFEAD3F82F51EF1E340331410C4886DD5051BF63412CC8B1CFCCC6AB9909004F86ACF8BE0E4EFBE12517D983E2338D4A9650D093824FAAF5ABF7B16925D7E88276004B98D9F529BA039FC550961F260592172C -560833CEB28AD013 -588308FBAF162333F810B6BFB834C185F0A08ACF364F0EE22D755652A7B4D0126155F59475E91264FA9EB7834B0EC0FD86C8F6DDFEFB29DEC1808D298FEBEED92CCFCD27A118F549B6863657BC26E3EE65AA42235D132828F5131E1C56227E5A18F851ECD7F70D4E665D3E288660E1FA3531D993CD2451D080F580A0653DF8F0D902297571882FA839DC12004E8A436CF73A31A34BF2941DDAF56B571DC9F1320AB2DE7B94638E29B6D36E11A1686EF5E6ABEE3C03AF10A1E505EF2383C889650EE16E79A052308044A83FB2E8E96E8B843A986451B06C37E58B657D0A0D2560924869010C9F9405F8B54BEF1B05F9899107FEDA14B370CFF3C87F9C649D6681 -1C1F16F1E6B6639C -00C3A29A622F4897FF1FA0026FDBF357CC2C49E9815568AFD0DB2573B5E78CFF0F1D3E0E665925FFCE3C1F2294BDC3F19F3E144D1A5741746A7AC5305A5D559C93288AD9B061B9933AC8AC75A61AD370F8CFE57F9193E41191F8BB308DF0F480D382C435E51ED52544903ED5056CE06A7E8663DAEB3F65A8E98F195150321F74B7EAFA522F399CA5E85AFA07684B31219D2DE6EC94A570F8BB13243C83DD9255E80C9DED8F755D44FEC1BEC445F7829790C4025F667AD582D95F35F43BD1075377EBC12D327A80B11A449D5F6A531FCFB1140AB53F5FDCCBCB882E9DF4534E0C8439B2EDEAD32D3035CF18AC884A2C854EF4D5435E1113822BE6E0F687B4838B2C -2A60E366B7762319 -6C32BB0E9CFD842557A9947D7651379BD903C069617EBB431173CC0932EE845BB22FF3B001136F52BBC562CA89861377535C69C86CCABFA5A5D14BFC32AFBDFFBBFBF5F6272DF14E0F141EB436D80B437B29B43B454943E635117EC45BD680F94794E3BA1F0B6CCFDC46D7C6FC7BC87B133D384E3D6E0FAA9AE5F28B5314D7E03BE82E989971DB886EB84916B75F2F3445C88F8DF8F227C32E925D7A50DB4629372A931A16D4CFA2DF3F47A76235B54C76DD892DEDA7632FCAEF7DFFDC14EC41B0CB6CDC42225FE9811921EBC02D162B69D9964254068A75147DE55A485BD78D81279D46E31A8C626189256F254EEA21545F52820C48C2A22380935D2124D271 -6C12BCD0509B79C1 -1FC3D687770421045857305610D319ECF69A45DC846736697E222727DA98D013D2A015863744337EA4EEF7F2E9C12406881A6D7DBCE690CDD5524BF44773DF244E3D8A658DA5C09E2A48DC5A8936CB3D3E3539C9C90CDA217DF234CA0D8B36A2337A9E4EFF7360C6D15C5323C26FB06A7D9EBC6346902462F0E3B4F97EED692A7AAF376045765EB5582196E459053061D316BF421C055B65D4490ADD2C8545F4BA31FEC83B4E7BB57DB250C21C68B721CECC92B79CD4D93DEACC04F1D3F4F75146376366DA2A73EA50E2715376FDE82B48FB97892331402761F9DD62A5FC71C73FFC5D1649E69F0A0331566DBF9AD6D2651AE898449AB736681DB7AFE53B0E82 -0B91DB01EECD2C3F -77247334C8EB829A1371935FF9459B1FD8288B1E2D4B76973D3198B216CB0BC163FAF1B66648C0E7E104A9F054DAD1F72FA2B038B57056C14A5B61E0283FADC50A74165B6C04F08A892BDC2AB97022922ACCCC89D375954D8C33B301377C68A5ADC75362B05D0D7D3C5A0FA9AD09C17BB251AF7BDB54E59B5D4D0E62BD89EE778EA7A49F4B6CFD069EFFC11A1C4D37F5CAD56B427539968E5792BE618B10EFBDCAB35B4E49A62A1671C8DD31191882A06485A762C693AB5FEAD0F261E7AE936D7EC4AF29880F004A8C51BD391E0E12AAFDE68D0D7B57C2145479C18D28D083E9404A4AA5BB2871215E2A85BE54BEA8718D5D4F28E1DB1C953CD558E712BCCDC7 -69829CC1DAA14289 -0096B2D4952B1B26B4EE49B4962E961CF6F6EDED245CA842C487E3D1A9FC79BB1A8A0F5CB70B9912E2C3A2BA7C15C2A61179D9E772A3D22BB6DAE44ECBBC7EDF4E98433C49B33F06A8C9FDF505E1812F7E7ACA6DBD2ACC263D56618F272FDCF0C40E966947181EF6ACD24FFE4F54D0CF53762BF34EDAAF129029C91E0CA43FCAC49F7E4E9EC4BB848E6FA8ADF88599FEB1051B138D51BFE42A9E561530F7D9BBF8012C6F2A166C4BE7911147DD59431C6ED4E0B6F67A763938B2423CDEBC664777A13223E983C5059E08E9301A8C3699E6AC9CC409A89B82F8329CCB1C2755899F7D42DC7E64F5E3657B8B9FE171CDF345BD63CD075E0E1FDC47FFB0A4328EC6D7 -33F9B8BB23B96F48 -6D3065E78E4285DCBF1C0C483665336F5FDD6D161FAEB8BCF7C51AA68A5EB3B5C8975754CE24CBA1287DD16C46F716A84AEB684BCEC50287AE6FA0186CC221C26D10A134C932792D49ADFC645D99D8EAF1621E1B81753B9DB743C08379B727A66CBF43B75E82DB27950185038794E666E13C4A01C6648FAC8D5524003FEF3688EDC27C8480B0DD520D75F725DCB11E1D98A83B4D104FC81E7E6A1BA36BEB20407E1B7DF4CA38B7620654BDFA8B3C0C643FC63336FB6C0DA264783FA0B3BCE97F95DEB2C017045C03E101690E68A2FACFBF7FFFDB084E1B4DEC0377658857BA6E8E9D1A05E2F4F490841CE290475EFFE072A391AE0F7A2A0C22B1EDEA94939E9C -168E3A153B8FEFDE -3880D257E7E95E1C38F4F45F629F403400ED9FDC64CDAC7064F79893B29D4C93F888042C662149B9DEC8069C4149DBDB2A96B59AE14490F93E7BA10E2F07E046CEBEA86555BA73206FE182492E0FE6EDC4DA369DF585FE9A933CF41FBF7EC605053D9B50D1638E30C4B1DF2E1EAF3AA15D206DA8F9E7E1283D08F3296A62928BFC56A42ACBD3ED452B4B1362C796BAE949FD6B0C3F47FDE9877BF6EB8D79AAE7FA20F50B0E16EB45E1F0422E29EDB9D9E602D641929AF990EE84BB92CBA7152F5343670CF8A898BBC69B7765AAC32E259FBE028DAD7925A933BB54EB62F9F0C1D11F3CF8D00197F2ECA76EE1173B79ACA23408752B607248BE0AF36326C69F3C -4FE850CB461A9088 -23CFF0432F4D59BBED3E4BC67004BBE1C8AA5B44BDD82CB996B685D01ADDFEEEE07084FFFCC061D9C15B6890EBCEA4A18DC8028CE29BF99D5AC1730A7C012E1E21501223234A9176D9B698F96F7748641ACCA2A76B7D3532105D3D87EF5521507D6D7FB63F0675DDE35B369075056A7B580C6DE8660735D0DD973E942FB6D6B9A1ECA942F958ED1BE04002539196BAFF9081270ACEAE9E0FE85FE0CC5F54D0ADF75D15B15ADBE19E60F18A473983F573C5DA043A787B7D6826BB7D8B5C8A2F17A58CD1203B3D67CD821F41F72FFF7B16C35B146A01EFB4F070F3DF1B398043EA0FCB1B4BE18E27C2335DCC5D783AA5BD2BEE470303EAB1B27D5DD5599A94D274 -5EF32E8DFEF4988E -18B70D69DCED5A25177607FD7F0ABA3EF72285704212BD392C12B7AADC5B83A45B3353E7C006FDA2B35B223F548DA907442BD0DFCC955536752BE9F5704432CD28BC667C3644482C54FE04253860D9D7D20D4443080381F179DABEDB284A04EFD10739EC76089C4AB5CF90F81DDCC2E2FF2C3A47EC503D69B98BF14E6DD6BAD5162D4A76D25A31BE58AA4BEB4B9825CA7BAB0852EE84B27FBC51E89B0A1BBCC763AFE62A9838CF04EB57BDEDE56D347C2DF428F3CE44BBCB0C0A18E6F0FF02F3453EAB81C6DED39E19020C1A45226B2C08FFC5D492195A9E8EB6897C7DC9DF47E2397BFED61835DF0542A0E7FF41696F9C1B6C879A44F3F7B293F361B1E848AF -65ECC6F6DD17F80F -7CEDE62EF5FB01FC159F25A90CDB6DCF0544EAEFD336326188EAB545AE398953758240506F44FD3DEAEC928E4076585F7515A5F59F2D9D850F09F79D50A8578FEA98AF3C4E636C9C442EE27AA22089CEF30B0816B3D3EC9C317A88C5BA817423A51859229DF1678CD677AF8C1B8A35D3575A3AF695D411257607B395D87FE64C339B252038587E80E46A6EC1E51394CAF1FB0FA37D855E8B0600431CF34BD445C7681BD225F103102A094C440EC7F089878B7EDE1888A682D93CE082781C0E1131CFD7DEB268B5F27D1AB03074B69D8AB83C6D204091095A6BEC77B049D395503D763FB15FC708D0327D5350E45F10C9F4FF6F2AED41FB19BCBCEBF4EDEA5463 -17BFC106DBCDC418 -00914021B61A740056FEFB4521824FC1D7AFC044ED4B4107A0CBE2B9C5CE31F495C041935E463EDCEC3C9826F95B3475D24DD3A204B9FFDA142F0BBFE513BE15221E4F80B272EABE4ADB8638F5E5AA322368D13078BAA4BE1DD7D51615A112303496646C665AD4B1D67D0EB6042EA899FFFEAD5BE40F7C77CB7019F30AAE5B72D7A400E4B7B7449AEDA311C6B1560B2FBE87D0175A4AC1D07E8AE1BA631E2F18602FC773D60D3792AE61A8D603ACE6AF6DB282ACD01A5B8553D184C7F183219EDD7046F69318D638C08B198672C626E9257A4C0F2A67ECF8A270AB2B5AFD3D04CDF0294DA661D0D636EE90C7DD6E83F7B2A27FDC3B270D46F322131690E6A2E3BA -0B011DA4B1D247A7 -00AC7BE557DABB201903D2A0BA918CE883C01781E9E85AF672566D6CE41F0D398104B95DD472B032F4DC7E1CA319CEEE6C96CC0F6BC0B910DD5065A24782E9886DEEFC79FFED59E4AC3A17C3E38096D6BDE54853CF5DAEDAD2C3697F27FC0638914E2F0EED576E605135C431FE45DDA10270FFB07B40F979FD6D9994AE28AB3E7054360230252E608EFE676493E82710A60CA64D9A9FE182369E11466AB92D5F706DF20256860FF2904197BE445298BD5B6F641613827B4F3BE5F3255D775644B68F9B1BC20AA741F6584C6AF39329830CAE81B03024962E42EB5BE3CD612BF6551FF20FE5B97B5342B7C7F7BA4A36F50F508FC0133CD7265F844C20920BE87508 -59458870F8653905 -603FB13829C81A361147681278FFBE00D0072ACE8C04A2FEBD399F5D31AF870D2A0CCE4E4B00F6A51C764FF8B285F04235DB79326DFF814CAF79B58E81375A6709661C2E236996380A5E6A44CC4E9D9C09A606300D73D89261135719CD6EE8BC3F0317ECEF820CAE8F2923F5411A0C1788DC6A919ACB3B2F520DA273BA9266D90BCA522227456C14BB44F0337B967A365992DE4B97ACC56D204E9CE5D1CCE71AADBD7767A47E5CD205E0FF0D371DB8083E934BDA1427AE876BA94D26207BCEA9EC16DE43547B837BD54B9B7644D5BBA64B0B39A12C3DEF0C40363DD5A7227225D5993B80178C2AE5221E6C2206837A23676576CF67BA031E144721CC54B4F0E8 -3A7DBE0772D8FEF6 -5D0B05898D14F1F711881CDCB448CAB69B34C95D604B961259FFD5E5881F60E0C38D21253F8FF3A99EF9938C958D605F7CEE6E5770B97134174B9B61B89EED8A0873A4B35A633709341C13866A39F9AA28F6ABF525744044A3EC8C8AFBD7D9314D2A16E0F370062DF6AE000D7E9C25EF3FEB3F7C608033FEE10489C651B21D22FD6730DEB362C0AA004F90B43FABCAF9F98B797CD2167464F1155177DF5C350125B583882FE277D6D69D7C25704EC3B24EF3784761B2150CBE0B578E0179F22778D48F73F43DE7A701D544DF4532D44EA96BE14A1101BCC2B44166E9A20D2316D2211D88FDBCF8DCAF06788F9B8679B7D62A4D2C32030B0CBBBC30E244ABFC44 -53370603298E16AF -771C2F91FF745EE8EEC27871369CE8D80B155AFE39B0C565D869D8B0A1B8D6C70EEA912825C44AD8AAA9663801D1B0EE115A4206877B179F6C694E9E89CCEA551689A99B83876AE8245CA82971CA4B6C7A5B5E0492932E6484D9C09CACF3DB369D6460B33BD1A1902EC92F797489814C851E11386B37FC3B2C5430107AD835E2C366C4C9F734D0DFF0A4917E5CE255CE4C17B5EAC3634FAAE57BBB38394315C80122C0D5E7B93C92F50619DBB3E1B88358C3F8DCF0312F8EC7424B3678733833E968E2BF01283EEA459C4D5E308414E1C17827D164024AC806D6323E2DF1C7F8CC5801EBD8350A9E3DD6DC65FD3D110F428E0A8DC0DA9C2B9E0A1B2AF4805596 -65D279B7BE46930C -167C11DC0919702519A55F211D2C01234365158E22545694BCCB90E2C502D5CF85F0C891804BBD1A18D2F490E9AFC9A5D551C24962918A3FBA23D980A550A5D1CF9688B772EB85D44220B72B0E417B4D4FEDC2EECD794461F5CA4A496FCBAF4DDE6F3D39D029AC5DA4502F8C6582EE0E3D86ECA525F7B2A261A5928CDD3555DE4100F0BE166DFC7702081FC9341542A29FC36626F28E5E501300FEA8261313C331BBEC68FAE3E3C322E9E50350D04B4AE01D0BC7586466F1F3F6F553ACBC530BC4C32CE127E520521B6369DF7E691ECDCA5AFA5F2FF59E907DAFD6BE66188B8E513F7915AB6841297B909E2828D3A82D1110175B333E31674F1A8C27C2E74272 -5EB41074EF42A69D -10FFCE1AFBC01FA47006F294DC9E6359B74F4A7A90E63EEE9BB204CB44BFA4CC68CBFF6424FAB8CB387CB903FE40AB72385ED9E8FBC5AB0E5843B63BE68CA3D65DA9B845B5F1BB4D44EF4EFA1BEEA60662DEE84ECAE6A0321E2D66B45995F64131C8CB04CD3FFF5E6B7B39366D10AC408DBD058997D1AB1956B5D7683DB18619E8551E876C6630D8A3D8FBAAD685D045CF04A387EBA319AFA5AD382AE07B4A6FDD57EAC0BE232415719CEEE4CC51C79E3BE8F2CA60779D37D9C964297FA15093BEBF414BD47A2D2F2D2E853864C22C16254846024AB28B7ADF2121DB29D3016C1C6FEBE70A3E38053A21A7A03AD3343A28D821C6CBA5C1B5537FC7AC4D1061D6 -7B63B44B8F108859 -0090B5DC74DD67C710AC5ABB0685CD2DC3495B4B2C165984A15810BBD7E9F58B63704E13220230DA749DC6E267CECFAC36ECDA1E106E3EFF2D122831AD053D150143B9F85BCC751C138CE8D3EF6CFA9BD3B6FF103D5D9CE71D960389FCA4B7B3ADB21777DE2C0590744693585EFEE5FFF8EC6D5A77B4B3DF1F2FFBA30CE9837B2EFAEB371C812F63198BAF45ABDF71EA13107D7E9ADBD3F50A981FB3995000C8857C6EE8BFD6A2434F08551E466137156BE4176801869FF005EA8CD0D38E1AAA6575B146636981535405CA4A65433D07F9022F942982A760E5D293B575C58A04298458EC41D1BFE6E4A125878C5197CB38A1D65599872CBC40AAC8866A1AAECD94 -05B402E281C956EF -2F001DDB804B06D1F6DCAE022464E3EE10E7AD792F679F234B65D6BD8895B6EC48A499C3507476A96568F54FD4FB010E34EB934FF25C645ACB60257D060D61561B5821698B49DC790F99BF56BA9F0B4D46969A17F520D4E01CC45DAE9F5BEEF22B61666556C3262BC02FF92CCF4B097D9D02808C819009E398028E27C543CDDDB7B50C11BBFC78217DCF59A5FB205A7EF0C36F7B3F643EB1D3BE0EE1326EB02847ABD4DF007B8BC146CF779EC601A2BEAFE8B4ECE47AB56B414B4416E9FDCE46FA312BC5128D99936972F6A55F0BCE0B9A6D85FAF1A7D9C13EC544DCBC80B2A498F167621B48899424D06EF24C29CB90856FB390066E27FC80F712C0CC7CAA04 -7F48FEE4C19A5862 -00D2A54A9F1A8A19378F3A1D551061C43F8D6A3C33636D7C8F18B5171CF7520A7B0EFA41F627163822A511E7A517EFE97232C751C7CC02BB4184BFBF25240D6062CB50941FC0D9D190D43933F13F470EC47612243B59DC0C82798A43F1DB428D1D16CCC50411FA5E86C82BEFF95DF00765261896571A9621603F5953FDB6E6C923DB962CBC0ABC2BB102CE4CA97598A88134395FDE54D4C1D9F53276D615C789C6FC69F11E9EDBF9BDD4A82B2A1B3205BAB26B643253C53B32D328993521943EC636455449AC206B24CCEE025DDBBF413C092CD56E772F1C969F6843C32AF5BD5F72D7ED8A5D955DF02DBA63E54E5C2D6CE03D03C830AB098EC8608570E3C9A4A8 -163BD74093C39D04 -0083AAD3E12C29620FEDA81026EA6BA6BFCCF29709C2267809576B13C11F9650C4EAA1DEC775C3DA7D92F817F6722690EC0CB435FDE26264FBBC7494AA7E1AC15B0F44364AD7107BDE92199CDD3013756C4BDF1E80755B4DEA49F78AE09F7ACE320FB86DE49D3971A429FD3FFD8D79395AA23D96DA5BDBBF892CCF926445DE7D61D3CAB122DD7D1547FFFCF5E4B035C223CCBD59D7E88A8CE060DD42A303AD512A9DE04D60B47DB018B3FB8D1F3726EDF26487EBB5EF3D6BCFEAFA80BD3560863EC2BC357BE7DF77F4FB7CB648A8CD99101F7A0F3B8CFC8F414FE98047628207929B16EE55108CEBD3350F77CCA5E9818067EF26E42AFE1D563F1AE4989E40811B -358E1B43429F4AF5 -34580C859D88106B8017C0A02B89F8BD689CB29126A5D71B5B6645D195A9EDA59EC6FB07435CDA24A2CB8CF14BE73A92F4A9FD67461EA4B3F2F68229B7116BC204E85F53FD6F3CF391B325E7761A85C152C3E7B059EAB024A24ED9BFD2B82F4D5015212440DEA3E067AC8D3451CF2EF42CC6D2A2974A7A492992E8FB52DA435135DB4E9F98970A93BE2B38E933B44B4EB713CA39E7F432827A4C2B728C881987B58F3AFC5B43B83C9A3E72B9597F2AE518C96A6B742EF6B252A712474AED58DDD9BAD810BF0E467A5066B798C0376F4F95CB0AA5F63A3477291F0E46C9713BF23A9D608EF308B1C1D412EE3E566B3B88440AEB2058F832DBF0D8DDE920A8508D -18675BC6DBA97B3A -00A25F0726AD9005ACBA79B279D488FE72AC9257C5308EC421CB21372162CEE444169567FA34CF879AFC53D461872F7B2DA55C1CC17D89CA7A6F3B51196AF48174D4EE0636AA7CFB43AA1C2D047E19B2E20FF0854BE4533D3AE4EA9EAE1A412731356221DB8E54C318CB7947F769B735750CD581EA095B67430BB02B9C46AD4BC15B8005899EB22F3FDF34236206BD48C25362645E22DD2A976690BFBADB759725E55DC161867B184001EA0169FFDD9F88DD04D0CE225172B9BACAD3F3CDB0ADB1AE677DC2C1A6DFD04C6BAE0C2C36AC1E2B6F1DC628787B732C5DEE765C621BAE91FA5808B04395C48289C1AA12A3E073E76463C074565DED8F69CCE5457F61C5 -583FA77890D40523 -0087A94516443FAAC73CAE809FCF30C3A51AF8F29F3F1D0CABD750523D90CD1AFAE9ACB0326CBB172644DEAD793EAAABCA105C7C818E1346C9F0E489022EBA7071948F0483E54E3D26AC2C8FC6FB339370BEC1AA4368FA3AD707AA3D7A8C06DA903D83D7C301090A2E35AA6DC1FBDA3E84F6789251A3F3B49A8AA1A172E1F581871509E0A277A2DA877CC96334D820559E61BE730118FE7B09994099E39C17551DF4059AB232FDA74B4B746DB062E052B9AC6AAF964B7BCFF111C5DA23BFF27329ED8C7EB50EE186D8A967007407B7BBF9AB37ACC813998D03D0231DBD747E6C31D6103BC1FDA5CDFB569DDB8103970132B21F9C778D432524AF39AA82BEAA3745 -7AE4B06709F45956 -009BEA61531C2F9E8958DA2A65FBAA27D49D832410FD3053485D095E0095B05FCC2FA78FD501657253A79D2D12066527D0A34EFCEE222DEE6C327FF7DBEF0D726B522A0AF71E0298B22A9FFB08AD9AF77A35994D05517258DE7846EFFEECB534EB82D022E94051D55860AEF2F1BA30A2B2D7E6633808FF84A2F33AF9FDB8C0B12B9FA070CC6CD76AF547A0B49B1808BDF299D29155AF302E1DBFB09F10D30F2A82AF003773B34315D805CA1D5749E61D404CC59B3C7DE7925A177D065A993141041FDB62EBB90598D46F7F52CEB1B7E6CE6E271B89193968CA3E64C810E796829B4D9A89583DAE3D204B3D36EC9FB87300A7F77F6795D455B228E5A5980B6023EF -3F0F4234BD0F297F -52030C49217941E1741732E9DC9E2AF3248A778BDEFBCF4DE61FA1ECDD399EE893EC9F338CB3A0D5F8FE8F7E777C8FC6DE8E0D4E29763F2E8B3CC6CA93A516247387F4C953FB66CBB2426F016D6E819D0D51EE656DBE50E8CE9A072933C74F0A1E88BCB79FBB21D0170FF2876E9FF8A812CC7880A686B0C939289FED579EA1B77198919DB62A15A9E229C7055925E29C9765EA837BC9C0A322D1AD3076FC204FD9B9D73847B68F50D581D0E792FE3A4BB58A1B5B777524EE38E678DC7302FCB9F3F218BE56902BC338B9FFF88D78DBEA4FA731D474CFE9FEA8D6B753B23F0D77B18C22447377E2A5450B7E025C1DA0D4926087F078CC6652CDDCF6FF4C848791 -65A724CDB3B8231D -00D991B92B4FB95ADF2D72AD15C9FCF7853853AAA36A0406A34CD82FFCAD15F534BA9BACB79525AB20463194DD9CEDC94B182C6EC1DC9A2531E30432E85330A73053D299AF5B533F6071D191E93111270DF2AE32FAF0BA29EE71A8F789EA195511619C3A5B89E7EADDE802BE083CFC90AF38F2E1F7393F76EE17E97E6CC100E92E5478BFFC003F4C7E85A6D497CD0439F240CFE3A06479E5A5F760F116E1FA8B6B47F592A72698732BC210CBF024CB4E42A9EC159CFB5E7B965026D6E49187180DEC80401AF848D06CC9FD5C9C8D0277F9568465C20984F06BEDB3BB8C71A19A442284B749C4FF60048A8821211CB5D9BBA72DC899F5E37D08337BE10F267A698B -41F0719B16A601B9 -5557CF4CFFFF0231303216907E4AE9ADF1C9A9623CC84800788CA69D17AC5CBFDF4026066563399A09919FC8D337E2103F9684B6B26AA61B899642351FC833AC43184C5359CCCBE5F6E79BFCB77E880B30B151E69647E43D155DB0E65545FBF0BC51FBEE57991D2F288A3FF1888473E3CD0CDCF9539E867F0503CDC0EE452AE2BFC676739EF16E06EE7D12D3C5F3CF0446E1D46D768589B6E35E751D84C9E0D1962C78E0C6E16F78AD4C5C86F8EFD28A6EF188FF2026B6DF3272B466F46C54FF0E5B0625D8DC3EABEC74C9AFC8DEBEFEE2FE6524384B79524E10365988C3D68BCDA27122B8323C5E27E90F1ECD899A52071DD5979B488E3EE8CDBC42166AB30E -730B77D955251E48 -63EDF2B513793CABDCCD4D0819D2A0773E3461C94365A78C325C9F0DB6309AC3B3A7CD8F1C6DA4FA31FCDE629032D046692EFC6AF99EE1EE10F54C145A3112784A56DB0FF32ED99BB841DFB3DBF9277A61C11FDE3496694B27923E6D11F8CD701AE2B695EB0AAB23DF22E1A8BFD9634CAB87C6589F52D0A08C451FCE1CA36754A31E6FF9CD35674CDF41BB51F0DB2030C5BA051DDD46A5380A65C73A08035D00B39D451E85DD926CEA9733624CC737F77541E403226BAA8A466F34AE620A46EAD8ACEC20D0E5596C0DA72636F2564BD92856985D58428E6DCBF9C2B4EBAEC0BCD89906C4CAE85A21F8F4C9F6E25CE753041FAA706CB1738973208FE2DF5E4EF7 -5BC836296CD04508 -67AAF25CEFE8589D0F7CF4A0FF067DBDEDF074CF2D86C8A38E76F3F77BBB6FE47B5340CC7AA30958E6E4AF21CAE87ED02D68D5BB9304BC5C368E017EA9B80A6B25F28F198B657F7C2DC6601E5AB60C8B4E23C978D406E8714022965DD5B7F96D209934A7A19C3A687426B29133A3909955BC0305634812DB8B9379DEEDEF5EA6EADC6B452A75D35744D53EF7C0673ABD13B7ED2DFA4A14602A7BD54D349B5DAF341815C12744DB21CEEAA1EE73B3DF657FB079954729C52CDDF8A4F127B86DF4B5D67FB1A437D57D11B2225752C6DA4538FA9E8EE46CD41DD2C90380D7D5F6B03F6236FC4F31C9D12300564BC4FC709BF3845A003B8F143D7C652F1E2382FB20 -62E3C399927A5EC7 -00AC0C4FC9A55F4EC39BA0A0CA9389296FE1ACEF6D60D9D78625D548A9F6BB345F114DDCCD5CF2C77BD6CD872F6B2F22F3EA34DE16328D902C14A951CEA55D5C7F1585BF5E6A502B0BFE748A78190ED93D481146D954B69DAD57A54FC0707CDD465161A7EFFE352BAAA9E0744940C9408E4A501C0D4973921B37C77B6E4257209AD93D23724FC0D5716D9B2B7410FBFC4D67ECF10804FF65A6CD4F1F3999EC0BA37675E5C3F2E17AD7D4C65CBC022326A4C58EF789999CCC17B4E856FF8255DD95C0D311D67E10CA4EA2478D6DAAE3CBEA889AA54774C0D0BBB1C8E8D8A114135E57179B77044461001CD804904CEE2DD1A266E5C3C4565F4241222A24AF8581BA -53DB5C91B0B7D863 -39152CE75EAA809CA1AF439E0F5EE4FB93D803469A22AC6235EF3A4342FF18CA18D0F807ADA3674F804DFC496DD2A51353A738034A400B5057DC265642F3A5244955D4BF89FF62F113148F762793770A63D4AE338E61EA2769963D8F4B5965CFB2B3308FA862FAFC62B5DAA34E1B2416C35BCC8247636F70C5428F8F8B46B482B644CA16BFC437A38B3AEC63F1E57C8FB221AD031432F4659A781797B10899B45A385BCA493AE8503423E71B3396DD955E3C85C611252D1CA1D680CFB8C066E2F4CFF7153D2F98D01ACF5658BEBFB2DFFC062AB201D717A088A462BC366B917E7B5CCC315FDC8B52ECDDF7640A09D1CACA441DD98A35DFFC4097CFD58A7AFCBF -22872A6187BCAED0 -00C8457B5A814036DBC857DBC1A6F82AB319A2BAB113FC1877DCD13E25F14F79E95F3B9C3FDDB7A956717805E28681CF244FA616DC1362714726224C4B0302BEBCEAE40481C5241BD701101719A04BE0C445AAAF3F7B6B1E774C6D2D516C66317A23A7CEAFC99C7F68C678295D2B88698042BD4043291C5ABFA51D0F30FEA347B09B02C3BCE62FFE330091671221430FE278F6EB32A069E7F741992D8FA9848F70F62C05EEFC14E919C6009A03E4770FFE29ED4E0C6705F07CFB8B9AC69F97132128C92E1B37F037B43200DA7A52016BE2EEC983D641E50E5526FD48B85FFE3BD731FCE8EDBCC17EB64EBAED71BFF91D352F217C4483A975F2ECC8CB0DE238EC33 -57AF4DB766D8994F -3F8CF0A2003D48948894079B7ABC0FE07ED6B65EB35FEB3B2DA991016C552EDB2DA66AC516CF92A4022A0586E5ABFDEE5ABFE23DCAC77AA843430873BAD653EB940FB773EBD881775B17FE5F43EDD055191AFAE25AD836E89C72D6BB9C20E65893BBC05F454CE4023DC16FB44399256BDCE31289AD0F7978821F6F9277E890F3932C87D2276C15B604D4F6F41565ADAAD54395B6E3878DD7C6834F97B5AD192BAE1649EA569A907F4C805AEB11579DC07F78F24D2D3D4482C5DA3B72526CE7D0C98197DE19A37B86DA6BF41609B8E4030EF96CCC5E1B79E17CB704A197C095A008D2CCA194674AD494C15FC8DD8CC7017663D692A376279D18C23002DD48E0DD -68CD17851AF018A2 -75E43636E4888736CA33AD33A51BF591730858417B8BA16784A91692662F35C2A35F91E2C0A85E022C85A9F545C2B3EDBCB17B78F2A4C2F2BD1679F57E19E19924C76570A8F650B434C37D3A2B1EA631C61A531C2C4F5E6A69FD400B9C86EA0E6C9C2B9B5B7C62C0BB3C75AE15249AC6E0F2FFE1C475499F9EDB721F62A641DFD6E4923E26877E7F233106DC99A2C4D84383B7E8BAAA2D6C08EFDBF7F68DD218936531068FF68CF9143C1529F38AD2E2C7F62AAEB581C0F876E175667531214D80CF31C246332738BAB74C8A265E850DD836212F72C061B3E3DEE22F054E41F02FD99A111F808366D3CE1AC12C4CF540B6DABBA62F32B2CED54DCC9155AA5E9F -76BF47D6E534B104 -60540FFF7A251EAE388FC30395E15B3E1774FE8A9225324EF9CF1DB099E161C5DEF3B1F1A7F11892BEA618A63D479E03AFBCA31FE1011AC207FF2E1F84E662C9BA94B4D076851F59572442FD05ED62BD36F719E7FE1C1BA888F33B416EEE341538C43D9A0467DE79C4990A89171496E813CBA6104083214C3B92C1E2F1D14D53D4CC9B07318DBAAC86AF8D136200B71C9941BAC0C8133ED0D5024EB45F1FD915EF7BD6F3D728CAD06689B528351CC00D9B02C38E72D30F9D2123EB958BB734279BF3289D7783F20DDCB10A4502FE62BF8ED694677891A6B622993FC7FDA335C09A4ECF21123F047431E4B38CA084CEC1716041F360CEDBA1BEA6CD6DDFDA0B6C -3035C4509A3FEE32 -5429B0F37178FE20E30FE21872046F51E308CCFA185D7B1DB3DE5138851EC8A217BE1EE72C76EA5BEDEB2E8EF0E8E63FB0F2289E3BBBC4DD8185668F3A71B6A51AA2FE60C439056A5FA9624019C16914B6289D15A73CEB746B8EE10F45FFCE9582F79D730C41DC6DCEA2048B4EAD044C7327EA6D8CE3055F8E09D21201D713DB20C4FF7392A9C5959C5306AB1549B0DF916878D628701F411C885D82C5F135037D2131622E8488675AD452492C0CFC29393778FF23D96BD80ADEA97B211EC881CA98E5F03E20E6DFA518DA2EEE13BE7EE13A58B64FAFEDC6F38CF21DDB6B0148A190B7BFBB7880072071DFB6BE42D5943F56C889D5025476B9CB4F5E63B8AFC3 -7E677A05790057AD -1DE6095DFE140E9BE7A39A9B2E32B0ACF5EDF0CE7DE02BD3FDF2C322C20AB99929DA2A157BA0C8DDBED6D4B268D51C5547B5FB8466B577522980D6554F4CBD80C4F95B8F5C1402F4E85BE3D71896BB64986AA033F201665A67604ABF65073BE3A8591B557B611E6F0CD1D29718D57118D8F972E04AA43380C3905DE0C444F817EF9BC8617FB89B54C1E2295B7BC9B624023F0199FC177A236246992F17090F53412B04ECE6056DA361757D7D37B62D29C3810CBE8977F3C78A8F8AC1BF234C4CF79ACE99DB098075FAC94D564E1DAD485F6D7F447E4AF1A1917F970790DE5BADB6DDBE6691957BC907026DDFF90D6CE0661CC7A34DEC346BABE06D86D8745B51 -541D6137B18B7740 -5CAE86F65855AC2B95A85827DA3CA7EAB21F19BBF6EA96BBA6786DC992FF4BCFA42F656D92A1527158C737B2014A5C3662C60B572F10A90506C3DD90A1F7565E7136A8F68BB9496E740DFDD9210EC7E061B60FA905E41276CB63E1B3B7C539A75FFB2AA1FCED33383DD5EDF0DDBF2A3DA5A3FB9C5FD85EF468E82B2F8F1D77E92AF25177F1FA042D176094A46C9A67B70A823A0F8329BA4FF05E8715F12AEF6A3E88DAD664939F3A0046AD01261D65CD043B11CD80D78A97823384F34C7F3ED450F8B7BE5074126473FCCDF532CE558788850885CDC51452123C3457719B7AF0A84A3FA4788FA42C5F712AF8540D9D351AA78604B79C01B6B18EDBCC577FDBD7 -012A1D3F8BC9EE65 -7C2C5E310A1F59E5FCE340522A8BC71F1A1D2059F99852BCACD32BC9B1BA2D07E76147E3D35BDF6E6906B0B9AEE9E18E23475ED9A674B6F49AF6451464F6B8F5D4A08539FD649574BA3713FA82CBBF87613E83C4CCB655A69B6EBDA0D55BC012D2075B42D86F4E66347DE8B666464D5591CCE701826ED5FB6995E426F4173CD2348F6AFD597CCFFF7A60DE3BD6F2F0D4BFA14AA7B2A5BE2AEDF17D2A92C79D0D8802827A928A82EEB6E6B76C9F03DA0B937FF5DAFE967B8C38CDE4FC5BED7F1A118E7F76238B03291D40CCD856750D4B5FE64E265C7937BDEB80BC71A23563D96678C4346FEFDBFFDB2BC3A2D4C4F7C24894362209E83D287B70F05FFF430B99 -0FDE01E1B70253F3 -48B23C08747291D359CABEDA583746D675D203E7C54F1733D2C3956FE70430FEF0CC63D7780ECB1BFF54693931CD71E1BABBE2CCF845C896E91FA7F2393D7D153A21DB68AEC9926AF326DFCF933AF69A9AE307D9F1F4A55D30F1A47192FB511A68BCDB468D41809EC6433ED8FC324BF126BDAB89F2683CF4809D479D0226C8AB6747D16935AC44E66C99D51B53B10FDE1ED10742655CCA8FD3A65E656FE8A35FFAE8775AD9138F1E622C28DF828DE9EBB4137EC484FF69267081DDCC24DF71CC9C44F529F5331599A487460F7EBE28A1E4461CCC643EBAF40B57C6C230F943572780D7B25398B75479FE71ADB773E2075A4B2F1BD4AC3CD8676D4C6AF7C3490C -436C0D8786641849 -1677DFDB2AAED59C5962C7A0E34494074922865E2D8BDE25708A22B5CCB2D1319B509CBA2453F3337DE0F6D9F9A2A3A9A8A12325AF91EB4B19A38576D2C10AA556956CBF28E31D9C10890A1AEA147E744B5FC3ED7850F8BFFE5D3C6D4E3395C169DB85EF44CB5B82F7004CCB416FFBB4C819D5D7B40638120E4F8C4D482DEFFAB3E6D5DC05781F3CF6D477B76B2F0D58F37F99FACA5EF5BB1AED18A2195246385A2A88B0690523CFF436700E8B79FAB90E415899E8E3AF971693FB1703BCAF59F3E699E8EFC71A6293F164CEFEEF9AC366F3C1B04FB52BA24E1F79D9E0FFA6047063966783FC4C4E9595743C40A223E7CCEFF1191B646615BE3D438C7744BB7B -7E0FF58122E85C65 -00D1DAB17EC2C787E965E0C25831006EE0DB5D679C77B972F7522E140E5D1B9344BEFFDE88EFE4485DDCD193F59077E3F5656217B08A404801636AE0D0E09F1A9A726791CAD44DB9F2B76FDCB0A529F68CAE71F9B1E4257F6E4AA2D7E9BFC5451C9568E4A78812D4A62F60BBAB9BF9EABF03A516F36D418FEFA5BB7B7AF20D10C46E78AEAD4E3959A075A5CAD7A41FCC970A7C1C9FEDE1B46D175A4B70FE899C485A05A5B061B3336D922744941C229670B1FF986D30A0ED31DD6AAD9E3C101918E8866145BC715CFCBE842BD3E1807CE72888E39D37217F067F0DE3FF52E700AD3DA2007F9B19CAEDBAA3DEE521A407FD9AB8D4FACD443A28455D79DA3F7FDB2C -72E82A6BE8CC163C -00CB9650D32BBC6614BC894013E4E12519D26F053AB6036DC76E056CD61F16D560369C815B4B9D9A10E63B84717304BDA17805D39E3238B2916BBFD356CDCC4DE2987D563314B8753B13360E7EF324FC06E75CAFA527D77A5B10ED8D981F4C167AE211A6875E4822B22F0721FDAB6D622419AE8A0C46AFD631B65C9FB4F01D8946FE45F1D95540379A68E59BF3BCD8CB632BFE440F93BCBA85667693ECD45E3A6F8AC13C31F339F556278D3915A96B00F3739698225345E82B82F82ACDEE84CAC969A24B1E70E1A71ADA8AF1704CA722A35B6671B3BE08165889853FFDA268EE88F32111CAF0D0FB6B5C1EACFBBB8D4A17E43FE584CC544526E9116E9A95D8A8F0 -4E47006B366157B6 -008225CE4D3766F336283F9348BCFCEDB71657D95D563838F4D167F796B9FAFAF42566C4B3D160B824BA6CB6150BE131666FCF1E50D93E374B39DB6AB93759A84737909AEA05B27E5D0D10CD86295B444437AD90B883FF3F4BB8097EB1CADB6109CC41E5FC2CE87D9D7B742005D840AB04112FE3B7A0A15D05124E62EC645C969A8A274B103E6F17397F269C30D7A380FA871D139E2687BD1D2DC393F3301B5BCBB509AE5F706BAF86CB7D59C3C28FC4A1E43A5D5656A63852F2F3594BF764412AED9DF6B4861773494D7296DC724F98CBF3B785AEAC857480EA214D6C97C5ADAD5035DCF96CF23439F597A3878D82FB590D766421E36A06652670EAD31F8C7535 -280DDD0C82F7A950 -10C0BDF780EE0ADA5943BE44D6E1D73A5CD3F2774238C4364E34F557FD397914EAD765DBF77287DC21E7B33D1BE83941194110DEF8607B91C37D7E4CD2310BF6350762F03C3DD29BF6B2920763981201B9863FCF88F0B9ACCE07635AC45E52FEA5C75BE45B1E608B2D430049374539B86028895385121FA2B4FB82DD95126A538995B232B598B4FFB905914AD9FE49B5C40FA87709CD246F1C76DB855D0A40A67C7CEB64302BCB973C4E7A938E953489EADD4E6537CC388EA8D2874C287E64F9A1C616AC0EEC78800BFE49E112C1E6CD352AFB4B0EA473AA9CE20FFB588F461FB413D972751F498BA043ABF11B791E6CF900113A77AC9B3F434581A654350DC9 -294DCEFA839A7E0E -5B8DB7C8E087F1050B90D0E32942DC528B4BA5F740391CDDF24A5244E3EB4C7089B1E5202E1BC8FA311013699009FA517EF2C55AC1AB25DDFCB949D56DE223425510A14A7B73C346DD96C64628CB5CEE011D41B3CFA046DD4EA89D22D4224E34C226CFE690D7373E03E549B18E1F9133CFF10AC52EFB46D960932CFDE723D8EDCC27D2432FD1C893188D03B58D7A64D2ACFCE2CF62D4078C17F212675698923A69B02160BE830294BA0809D505660C7ED28F9E21882A904A28B7278BC6E80654F3059854A1911F766D72AD45FF879839169AFDE266F4BCFE46D2D3AB40E824F4533E8B07444BE77ED8426C837E35944260B1DD3818510EB0840344BBE6F82D49 -110F024F1E8ACB14 -00902E695F3C75D606D2A66866A6EA8C3BE9C7586EF5E365AFCB0DE6EED83F5754BD914EFABA2B185A9E1897953B0A1853EAB2F9954569D218921554FDEE7C115C5706BC13F4C645DCDF4E86443A4E523CAF0D3921B02A8F887F395685CA24AF8E7743A95AD08A9D2E3650854911C3FADE92180D39D04DC63122AF19C53E28612FFE982819DA247BE02F9A1809E936A6F8E7811085C9BB9EAA60344561E35E7C04F5440D3D9C12516A1D990F11036C96F233DF0EB22F39A81DA4EB6CAF5873BF76791F2C87674D65B26E33584A36B8F41176B1C045B9CF59E1E09336A793A46E586DE31F6A904CF596760C05AC0062C9FB2D61CF37E0727EBD9CB952AD0F4E05D0 -7665AFC85EAAD7C5 -0087E61F2E78DFB89FBAD6463F2E45A8AB96E346AD700B1D5954D8AF47D7F9BC47215FC9DEE3E2332C83AB27BBAF58D79614179DF7DD11AE915FB31A801BBEC5175D6A65669EFB46DF215202F9022BC12BF347B55E5DC302C972F0DD2930B74F8480C465BA5E10FE1700C99E935DB62E3F3414D8C0FB424BB2ECE65241900E56B197D02220010E86DA03A7F4060CA986D306B5A89EA0DD52C98F779C9A8378F89D495F9D154226EA61D031A86F362A24F6CB4C4C7085FAAF30B0CD2800EAE670F5D7FEDC31D359C20CB84C56CB570EFB342CF9FB2F7A339A16CA7D00829ADAD49684243F56CA12E726B32752C90CD93A829A0B469ECAF57E43197B78233BF45F02 -217187C6C11E68BF -7ABE970F90CDA248A316FB8DEAA522E15755F26949BBDE39C912791CA2DC1CFC683BF1954D1F9D96E504056282BE81BD8D854A47CAD1BE91526A8F2C1BC17E5C20615AE60E02FAA4D20551103C23AE700EEF3EDE8F809A4245017F6CDD35B0469D6478A3ED89FA5D29F7795E65451C406450AB53BD8D960FA541A7E98F40F1AA954B435754924E99B4B67D3A0C0A6126D1133C52896B17BB6D4E8BED5FB7AED20CC94BC9CF4AED0924E4CA4B6740754E14781FA39075ED9AA216FFB95D15C9A51346E443B58BBC30AF407F0BEE6C51AA9679D95D0676BE2C639C03D7760948A8A59CEBDD94BA4971DE8EE465798CB3C85968F7CA0744CD21E141CAD1391B943F -52296CE3D5755A8A -229F7F271CA57E600C45A954014BFCC2DBB79FB3827B940449432F16D82F2BD0E24853A0D1223EA96915D7F5938CCA994F2882DDF54B6664DBB497A09B670219000044B8AED4ADF557835E5532FCBB6CD497CC998B353C1623BF2DCA3FB3F33EEE54F42BE574508238171074F5787C2804F5596E0198D2DAEC2B6A3149519CA14A0E80DF1807FA0678029776D8050A50F59F0CC947FD77E05D583037757F0AF0D6C6A57422986DFE21CDA3D451ED00190A4EDA169393FEC7D90A384A31D6349E107706C712123CD0F1E1807B5F8A5B7D30A196BB8175B53638DD032C46BC04C8B742AE585140F46F53E8AA80CC4B144EC46E97EAE7B1F3CA3CCBF73FFFFC98D8 -74FAB5B76E9CA081 -2AE1469BA02ABADC4D1114BA3C06702632F83DBFB64F13E25EB0224B4B7756D7634D730AB1A7178E75035AA3DE67CBF1703D862E8FFB5DB51D8BA00129632B8CC07A3A8D31CD6F07DC90D61705CEE75F16365B2A46701A29A7BC47F8D628EFEDFBCFA761AE79EF0ADC5A0B2E4E7C7CF9DCC77629DAD311A0BDE2207ABB6C7AF33E98566EF5CD69CAC36143119349808B649CEE9ADA8D78306E778C8F833968E29C5776FFCD76E8E157699A024A971FEF8B305A0221CDC41E6887A5BCB7DA82E384CD4DE729641FA8D6A3B08EBC2507762DA7D0D8DEB3ACCDDAEE6736CA11BE9A5DDD3E560E43AFEC5A9D4195B91B13DF3A48A78F99356A06CCF662EC4E9CEBA8 -4960F61775826F96 -00820260FB1D0DBEA5542CFCB934201417E0587B17615C55EF3FCEEE4C0397F67962C543E375021123B2F12BF8409DED550648636829DFDC5FA4E64E9E47D1D8DA7D4CA214319056584CD34E916C3BF2166DD000045F7C0693D20412DD84CD3E948246B95833E6B208796927BE4D5DB0A7A6A3B9D14C6C9E98CB9ED6EAA507082ED310390ABC0D5FAD589EEC73516D89E8A2C8C4D4CA9BD64744845C92059A8086C1057B728EBEE2EE4BB6BDAF454E1970F77150DA92DB9D167C7129EFFEC5AD0226987C7A243FC0EBBA57A3BC5ABA8B6EB4CAC98550B37CBA3536CCC52175BF8242597C2EB1489690490E164F5A15B953EFAAB156E4DB9A6819E2E19874507BF8 -078B05F7D178B00F -465F41B6DF71B711EEA6366FA0932671773CDC8255E6309E90F786BBA1086B3EF9E01EC2ABD0A76BBFB0B611AE9AFE4AE19A042BEA0938051E85CD13C6EC446F4F83106ADBDDC55346AB1BDCB7C7863E963AB0E9BB16BCFECBA833F59A0EE75E6000BF24358A756EC7D64A06A07928C50249764C4A3F0D7809C1F5902D2246B477F7E267AC622E49C7A5099BC2B92AEE3DF3CC3252FC1ED47605053D2FA9AD2CB6D80B6E668B80A3E85DE7567824C9D728D521E434346C314BCB02B17A37F4ACFD0C1B1E5F585BD764A64D2F7CE521AC92F88EFA6629B74AFDB4A25EC6C2B764ED86BB71EB15634A6874E54EA30CEE1B3E5FFF0F4E1EBFAB9F94BC6C4AD740BC -72238E8F8B4D40FF -32708FC0FCCBE842D3AE90B7FB8762B340D1B3FDF78259CFAB1A95A5E9BFB6EB994B913E4BB4556B9D193C2201441E4B93EA8431DAF9AF749AE28BD479CF54E6AD528B1ECE9386C590E1A47BB7153B70343D5B93672348663CCA1D63D0F727042239E78306ED77D0394DD799BB85FBA7AC95B254DCE1E6A2CFD74B9EE2B0ACDC8BAAAB14FBEAE4659161A6DE1C400B8D08188DFEA334A24A135A858E9AC15F0DC9998FF1292629114377140E18B461F599BE5F73BA4EBCED550DF711343A33151D949735B11711BA7A879DBD4638125DD434CEDBEF11856C4B558B25561F2CAE988CCA001F484D70208D4B679BB2053A28394787A24F0671C4E9D3B52DA7DEBD -7DC0F9CFD6B115D8 -00B9B51F57D569C0F53442354212F7E6A379CDB2B2844EC927EDB41910439FA57E0F22B5C2AF63609277CBF330821750C48F489B057DF5A1F7CA65AA9C05416198EDE9F0742409BBD3159323E18AC0FE43497C7A442AF5F9B5A933ECE9420E159C4E99B9EEB9E6FAF828B9B65F3EF5AC19BC76944683F01B57B7367297FE937A93943624E2CC873EF7D4F99BC978144036C65348412BE8DCDFE38BCF1F9FADFBB9C0E9D34E1321AA8622DEC95621D3F82669813A24F972B3A4432F9F9B184816625197479E51A1C28AD33795C92BADC99B4061AEF1A3A4A71B82A6AE85541955540BA2FF67D530A88C09C4952E059FA56DF9AA26B397486A4F6BD2CC2BCBED06B2 -18ED1A21510A3D43 -43D3D86913BFBC69AD5E025E1FE6D3926F1666FEC3249B8D0218DCCE15BC968BEDF749310F0544F4885547B442532404BA58009E2411CF3AD20AC12DF37002E2753BB193E8921E52EA4BAD3A68973AAD851C50F1BE24A7F691586FE3431EACF4D7FFB9E3E671F93398CE84ED6AFC71ABE3FE46CF3F84EFE86EBEA2DB2E16A1E793A0F85E6CD17B3D5D7C58930C3FEFE0EAB2AD48656E9567BCC0FB2F1C415068FE5E68C1DB7FE28924B8230723D7091EC0E5F597C0DD99EF71F0997E015C3D30FF113E95BDAC6B094111FA380C5E52C350C00AD376AC4419D95BBCD9FCD3F110E437392EFF039AE84A8220625F598A3E630CA810B39FF1564A667744989407C5 -17895724243CDE6B -4D55076C272D1DD89E5FB2A7C8665E49A913462A3504C2EDB134CB3C989F14540DDACA37C2E991B7E9A5CB8FBCFCFE0EE1847A2F29361D8C48D95E45D4A7128E2EDDA99DA247E734263AD67A544925F0E7DCA0A77BB81A5F6FEFA0E581D9CEF3A30516EC1BC0C35CBC4D74B2576E83E1533042B4C6C1EFBB10392CB71B51434E64FCF6C2FCD568E12CBE9E7AC262D0BAFC9991D8845CFD3A1296CC64DFB4818E761768B95E83DC5E36E1898F0B10C0F5AE4D19A84CD5CE4A49C33D5B5AC5E2856118D58DB038AB2A565B39BCDC00254CE81C86FCFBCBDC7D645D6E8F7D516C54BF4267A2F6BA8B1FC0C307E80C3F74AE0601C4495125EDC0101C22FD5CD27D3E -56B46DEDCE1B2A76 -2DF34355A6EC3796B2670F94E5612B96DC1A7EC3410F979E8B0B04EDD1B03E12BD06611C9C74847DF6F464CB3F43FC24FFE9E6E011BD7FC80D876F0259D73A9060E0ED0A7CF15CA5C3BB227AA18984A3E891712A8B2743C738A67CDB6143BAC3C4D1573DF3969C0389FDEFFF55505C8E68F846AAE1A0E6C5CAFD42CCF0BAB3AC17F104944F60626F52AAF3CDBAD7940FECBAE618FAAEF9FA8F0BB32DE21DE83A89A7B74F28A8758E75CC837A3B36531EF7AFB32D0C9AF9C29B2FA04C825C0BFD42607C0CF7FD8F6BB5F451BA9502CF87EB204E34A307C07D7A32EEB467A943927D12D57FD461706B722BEFF2E6E30CDFBB71F8511E78718DA58105FB0A01F06E -72BCB23AFA9DE276 -26819DDDDC15B2C006D1EB2F9953F820E4E4CB1115F8689FDD6EEEE8E855F5A348AD4C28FD98C4B7AE12BB300A6917BB7DC1070C53B5998BFEF8DD9C21D1DF8F83177321110E67E78A4D731582A6316FE4C488F59BE4C9D5DEA3E4E7C1E779F33F1CECF9C86E03C463058685BDA607DB0910DDE3F2181B0BD0897569992C2F445D019C7D8324F3CC9BDCFB036C3386D8DDB7A87E4EBF0201D233628120B1AE2A9EF4B43E0F9B88C7DD39F4C4CD6218CBA723238CF43E4D48DAD91A0DCD67690190AF2588FD9F4349627F77654C2A663166DF185A142068B4D614391B4F04D023AF29CDF59DE61EF25FA2A4F8E0B38F1B1518E5FCFC95BF61EC952E1CC51688C1 -6D85D283E65480C4 -27946D7DEB4C55595A3E41EE35994CE6666974C82FD6CDDA1675C7912F3BEE07B0FE7A82C7A9C51B3A4BB14456AAF8D4A693776840D2C8053B0C5A14E3828637CCE4B57F84DA6D43E6230151CBE97DF4AA927D22E2FC3EE5B1EC1A746584EAA8DFB0E6232F03F1F3D88F5F57293F38256AE4279B9A60E65E3A357296EC6F4B774290972E2B36CDA8DBBCEBB3903AFBABDA676AA9D0C3C1C593CABBE7C4039BCE58AFC791974B88624BBC02CEAB9AF11F72AFF9004303101F41F64042AE086EF4970F9B08EB9EBC7BCACDDC960955EF2635E67FD8706E64B13D5563AA3643217BC24505B9A2A01E87780E126BF9511EE7410415B3144439ECA64C242F78830F -6A85832897B3F339 -3653A99F8A577466E506D91FA831CA5FC3453B5FE8214386F3E56DA3CD8A065C960A5C7B1F674BDEECCABF7D06A15FC9AF9490CB3AEC9E3685A89DE2441B73E936F88D5AF4E97531473F38A89502DD668B05927ABCACE63E65397170B1066F3A6B99774355E8700E3B048FF8AE7CF6D62363AE3BD5ED4C5516995CFA01E1565700F390DC834C300E1B296BDE396E69584FD699A7B5773E83CB9292BAFE71D8DB2DF1A4862BD4C404557F0273540A989C8F9736A102A8C66DE52CFE38395B7677BEA8CBA2C36338F74C1E51DE7F33EBF2CCFE2047A94548A008B05D16AC82AAF35FF7FCCA18D1D945450A91DCD47F5B666912A1E2EA49F2E29843F5E847534896 -41AA43DA6CB5A1E4 -12BF120623A5029A02BFF707A2C6DAB350D46E1FC26F06431F0C03426778F24EDF9E3672F007A332B6C837BC0CDA8B5CD90B9A796B2057DC869D888B2249D13831C77EAD60AA988F48221699ACDFD2C17620F1F5C7B09EA175FBA2F6C350F58387A806EC8176A9E4E19D965376483FE0D6FCDCFB1B1D21D363F5EFF13648FF219C18BA4F87F295CF037EF880B37441D9EF80F16848D6B7B91486435D0EB1544D5413114E20BD9729D5F0A94B1D5136AB6AD8B64C5CA31EB2AFAB9AF691709575392CA16450EA660FD8ED42C495764CC40B00455BEDAD8C226F5D57F4AB0D8F645DC62DEAF356B65C4F81120D80602B9456ED73EBBC8D3EAE6528F531D45F2EC5 -594E0A0F1DB220B6 -14C22D319182F8C4FED6BC0454D16AEC22EBB0AAC816B21736684166A31A2AEE3726A6E5F5F83C33B0AE94BCCBD038B5DD04C50DE957BC6DBB2F8419D1590C4A3639FEEC6D6D7B4E4F61D31047ADAA820D2BF715FD85CA5B59BA6A78A182FBC1AFB2723F24C0BF6C9CA202D50149851C3FA989126D308821472E1EA63EB31CED7BEA785758E23EFBECEA6B9F7375328058D20D16615C341B1BA3F0C0505F260B95A0A3B645FEE3828148576EEC525229EB81CD00D87D9DA4AB98212F8C2DEDE440C1C4680A882FDEFF8E6E2F58603F8BA5A4EEED60455ED675B8EFD033929914E913EDF3D86CBBFB4174802A59DA2E067169CD1A2A75F5A8DE09E4DB15F2AD94 -4B9EE4A526D5DA6A -683CC817478BC931C7496065864A4971776705362026E20AFC4E59A6987C943E3379A3BD197F145322AC3E9C93B3A9BE3B7BD516A04E3E55B7A09F482A0934CC450BC5A9F246BA025740D25BE928112A0D214FE8112C4222C59E36D31557C23F2A7D79BE97C8A92D38D191FC6BB37A84DA67A84EB3F8EB6D748309A104E1DDE52B248DDA1B892174906600E0395F09129D7C0CAF87E97F36D3E63B13FB8CC007C984532C4BEDE6831CDF47A17FF054665860A782F8EE2AF4C2D6258BB179D535DDFCB4B6111CE70B47D5050F44E9B017B1E416BF740888DD3FFCDBDD62D9214C9184472E86C8E82E515458CF65B53834BD39B5D3F65512ABA9FF1FDA7BB454A4 -65C953CD55E05CE5 -114D6BF8B719BEDCBE13357E09CB6EE97317207043D8987A789FFDA9B2BF84677FC2129AB7442ECFF0556F9C790070A328E3EA9F1F0AE3E3A6DE03B8B86463D21A4CB9FEAF9EF40FCA46DC1BE1FA1741985313086D65EC8AE68D584FA0663892A5F2FE111145947B625FC03E876B5D095B87603ABCC786AB2CC6E6C233621A200DDC3F54FA03F26B177E6A32750F538F477AB346FDF515A1B399CAF89936DE5F6C81CE664D06118359C72A5A91FFA34A4C0533F1C07E20E0F4CB41BE486B5DE8E5BA8177A3AE7D4D06BF167F737CEB190D9D2657EA61FE5E797D3C612B973FAC2921109D85CBDD28B2EDB54240B250891A6CC909155E1446DA0E4DCA078C099B -4684CC25CC5A99BD -4E5ED1254B3240D58FB81FB248589F819787F4924FCC95C02FA896F5349AAF1698B3A52061701BC5D002395C4113258148A52443A3F91635F68A7202C0C1C26417A7A91855E9DE9491C9F2FBD90990D3FD63BBADBECDB9C910920F1CA9AE72EBDE9AC57721489F157BBBEB2356FB968ECD2D32E6EAD1F1975BC5F59AE525D3C5553F59A905901F75FEA7019415660E6193CD36EC16A50C231C49AFD546240133DE55E14E6DB6CCB86426586A63ADE0A72DDBDA4126162D7C1F761595BDBF4A50C82B4FE60F4A1E56E3F8A0802150B84BF0E2DBE011119FA4F243031DE05957AB916E842A4DE7B2DD52FAB85DD30C1C6FEE8CA4CAE1600A9C723EAEFAC7DC1E67 -6237590FC56252A6 -5805D18262FF0914FC83DD20EB870977D9FEA1EE80C07E9AB80B97E4B204F7BF192DA87CB0995575F7F10FC7D85AA05D6B1A471395820B8822EEE902E45330E6CB6B267D22E39922A68EF9B69D1E6807B3F4773F0741F2A7239257EA4BA1615B708D0F4959D4A0E105FD33908DC0D34AE387AD78E9F19214A7481A9D5BE31A48668D351E0DD03080F211B7E5C80C7089115183D78A1A19A2BBC742C6AAF166C27FAD472A06CB895A6D7ECE06A2E0C869451FE00F0357CC81376F2BECF39FA630B5D7ABDB3EFB560333104101206E9A15BD0CE877E1DC66E69D75EF43B0D639BE3B81CC4DFCCD283904811E59D18F8718C7A643AF54639186B25858EFFA59FB1B -2A4DEEACE9D5B330 -00B461DB22B95373FE834F30570C5E5CEE1E639A19842BA1A255B6FDD93764F276454988D28B50AB74626EE79FB835C59D603B462A15AE018B54D326FAE3572E916688B229EFD0903049F361B59E5D5C7A26DE4EE6295E1D4946E140F9F27139F4935759665B2C2A98FEB74B52E24431A8F145B6E912F710CD6B99CFF5E7FAE840BBEAAC6DCA24A2A35366D6A3636115AA32D54419B986CF4459413FA2A98F4B63DC0E58171247ED7CC92928785A9EB7920C0EBB0A79889CF1CFB85CF8477C85BF96D8B65E2BA72753603B27C7F7F4538B579A92C5EFEC4CBF69AC8888844946ACAA346B8BBFD01915A06143CFE08CEAC6CBC233988CB14FE75A43BEA0B66724C3 -385AB4B513494E25 -4E1593E98C1CA2A646ED433B827B49DC256F4B6AFB4C04125D030B361C85B7AF9F7608D3C168E453A60B5F7100A049E2CAB6CAF2E762CAA3721EAB8A2B32BCCB9EC122556F1B69F9E732F0726DA98087C8B019C95CD2583ECC8517CCD899901E99A2603B1B3DA957DFD525958DFDA3852BD36E6D3DE49E35ECA34EA658E4C1C0CC66213F0D25F219ABF60A579BFD5C7B415A472D03040CA3295A03EBC802CE56148BF25246FC2051447577DA69E91F109C980F224726F4F76F09AF59E61DB739352E1DF5FB9D49178BA0D4C64D7E9EBD72F6BC4BB54E64E7DC81CF0D1642CC947F02F527D4C8D1E8E571DC1A4F32249F2017554C20221A7FD5E90583FA33FDD9 -48B14BE604B90565 -039D2DC78D274E011071F5C52CDF87DD3BEB6755F03B77795648E89D452BE1AAC285AD64C8AB777A6442BE127FD4C8B7552355254A674617A839809792B3773AAC0741A8EABD083F2A44C805273EB4801FEBA5272747C01121D1A83750E64E2686CE12887C3E066FB6489E66165C881B2A90ADC45319F2F6C4F4D834F4D82952DC3FF182B6D1CAA18A0121B7E221EEBEDC187633DFF2C8AEBDACBD52D7843DAD939340811634F17FF06CD7BFE173E534E36AB5CC4D8E76B414FE44FCFA9C0493308EAC3780C1D832500CAB6AD77874353E2DD0E5504AD66E8287CE545124C12FDE33BA424EC5F2B9766001BB8D8660D73348E0AF9A1F746711327B2BDC9538E4 -49AC434CFF98E615 -008AA9B8FDF8A61CCB28B3EBB0C35672AE524BD72B55E2EE738198C91A1D0D0310892EF514E4AB5F7E225352251C5B145169F08DA6FA686045EC84B3DFD2368A83408BB066FAF683F74F8A2BDE36EA979EE85497A23E21D964E86C55CFD9CF916E03B358907DC4F3BC19E931A014249F95B8665A9D70BB969BB0E6CBBAC4E5665DFE216C12D7A7F36B71C6817D5D04D84550592B241A462541F158B1893BA524805F90E1EEE8DA8150761BE2226A07D9339C420C35ED2E91B98AE7D20D4DC36658A2FF75E9F6CDF1A35A0F10CC4EF3C49CA52AEEFF7AFFD3D7583D9E62B2B46D7566BDA66290E5FA2A03608203BC475BF0D4EBDF437F69BCCCBB6E3691A9CF1A4D -160AB4D3D09F486E -7905309358CC4C6BE1F400CA86C13BD3237509B32E3BF25BA7CFF10D194C0DFF5B23B980CC65AC6F6E21103D572B62CE9700A9FF8B7ADAC42E6077D1C472932BCF0FC5B74A95663DD88F78A7F9B72F5652A5331B1DE13B75866183090DC809DFF9ADC83E3096C85277E2FFE7F226842160B5B2848C3F82EB3AC869E63E1EFEDCA01F57B0D05291027D689840BEA46079A11C3820F0DB097FDFD5ECEE0FE6C056914F9D6A3DD7D493F0A0885227C3C4B652776DEF0D7340FE676917596FA52B0E2BC03E0EFE0B06A054307CF5B3DEBE006C3E0A5664C3AF04BECA2B2E5D3C5586D8C2314C97B3419075C28355C77E12F26C9AE544E4D88F15BA379FD1716BCED0 -3B46FDA4D897DEE1 -2E63D289E8424115D6B2C7C03CC0B74741EE002DD4853C1DC9F18B6353319A15F86F2A9ABA76CAB7684F7302470970124CDFFB2433492D2210CBFBA05CFCEDB8076D1DBAAFDEEC45678C6F0FE74C9A7546B65C52FA72FCE519B10670B71544C7F506EA1A337522BFE3E5015E448DFA40499CF50853129D1CB0B0D8022B29F6478FF2C6CE55D2AE29CB81A197556FA9203C9619C7937BC9581916F6E8E643E0E2B2276B605D30A5568A3F60572A3B0B8890E7079DC58783CB6F9DA2258CCA139568C866B24FFB41D7C4EEBE92DA101A72F95C3792C383EDC895DF7C251836D17492B260FDD5C597B02229DC98DD7557AA8F0ECA501CF86DDEB3BECF3CBD34D4A5 -4851A366E1B9FA2E -041004C740C1645F35A801C73625D3D86E281D300062CBB214E55E8952C57559A70E40CC420F7CA15F22B0777E18CDD474F0F95E7D4B867DADE0F582CB8BAA6C6EAB3E1E9261CC16F09AF58ADC6B40383998E4492567812FC5E52F5DAD49C3A99DC0282F219ED4710821342C89DB931E7DF5B9D9731A1838495B75945D0590F87AA73EA5F7EB1264ABF306A9273ED1837D90A2C6355327B0DF1B811B89AA881F646EB75655D7CC7DF768305BB90CEAACB2FE5A656C2914A468C70CF6D60D7C22824D4D2C4398720C379FECA9F866DD9FE54F2E84F09FB7AAB39FE4B0BD73F7DE758464766ED37EA3BE612AB5A60356F12F2ECAA1FFEED2BA4F505712A537F10C -34B404C231961734 -4000F949D398597B793AE17632E6BEBF7A33C469AE953248F8B3825CD8191C4CD1A966E37A3304E84191964900FE084CFD135C982C1D7EE638A7EBC284968DBA18B209A2AFFA3D3BD756D354144638384D63C3E9D5F382DED9F45CC30CD22D2DEF89330E835A8563DCF0CC48668388C9816EC61B18F636C5BC9BB30BBCC44DEAF911E299D74356224D8689B74FE3C21A484B473BCCA24357E986CE304F670CE404AB85DB935871A76787A89903344598DB74890E699E536C044C542BA8A59688CC90E802763B52E6D1837900196A709742FEAA0CDA53AD56C416C86304C2D167EF4E25B50AB54EC8FEF336E208F9B8EAF18796626B83962ED43A417649107A47 -0BBF745FBADC4DDF -00827A9888FF0198CBC8BED024B61B6D2B821C1D0240A4307407807C9D8B419A26BA2460B69D68C18B920520B46CC63520000CD55AD22926BDCD475C824B1DE47A8AA6939222C200749D2EB164A6BF686944A82B5E999684301AA9F0E81B4AC3C19AA91F0A71304F179494F83CE82820650641FF6D10250445B28D063EADF184F4F12B843588F4527665A224B1B446D232566ED4A9691E8B016D20654CEA2F662D0F0AA93D934CBDF63B7CADA36415E80434191BD1769D65A63BC9758AB5B6090E1F364851FDE814F8394CB3F16ADB11A4625DBBEAE2B031ECDFAD2EB6205F957D81BD52D7E4A2616922F11F5F0BF497BD328D36578EC1E9074AB707F6C4EB65BC -25284D970D11C4B1 -248A23163CCD7AF2993959647D62DB4B9999B6A95A827EF4E23C23AD6375A8701706B760D52056E69631542E922B699FB5B42A935F7CAE4A08DCF8E5E20EE580C79943F4F94335772A6EA35BD35B3A411E55D17E0ABA565928470A00B6E5EB8086923FF85CBAC7544527D64FB6439F0AC9F1F3A83D6DFFA54247C7DBA827D33E3EFF978FCF64037F4420C0733AB95821AAC36C49CAC19DE283244981CBEDF08EF0A3844CB1B326F75EF1A08B3E723352026424D923CE091300627BFCD7644235578B2DD83D64E8E2A968257AD0054A04BD9B8C824CFF1F50C111BD8FF9ABF0B91843D865A1D995FAEEE70BF8B6E4A34143636368007D6C7E0AE98CAEDD038DDA -64CF2E296FCC404A -008999B06870D7D8F2ED8CE19886F948C954805634D5B4D3DAA602E0E2D45DDD7A56AD1E8EC31F077FCE85EB6A427B67693619D5E0381DB9678AA65998C7DA0C84FC178B00516E2CB83CA3499D4AD823105EFBAEB8B663279CDE56287DDBBA0DBA7189E285680BE7A96C06DE474C7CFAB5C68F69968AA793F403426D4BB64ADAAD5A28398490122C3CB78312B62588FEDC2796AEC1258E182F9888B3EC62147718E2C24FBCDD0ED4EE83600782C7026256BE7ED1DCADBD06EEDF7490DB45DBC66922D337451CC5E9C3B41C69410A123F40AC548EC664A324F65BB14F7C9F61EB7FFC02398B60194BE97167974CAA2A07CB635AC5A210E36BE858A88490F6D7E707 -56CF191CC30511C3 -211CC7A3F58BB16444876C6150C1A2E8CFC1732236FE1F90F507DAD19405F094E2CC34A24B8C8178A65C9B100BAE96C68B55807C064024CDF954C042EC7F6BEA00385B52DFF8F9BCC756E00982B6AA6C7D74881430A6B90BC4E39525464EAA70DCBA03593F06C9B9BDD035E720187D8B742744E4FCE55494F15D7E5AC1D2AD4E8FAF901067443E6BACAA980164BF15C6E0C3B4C82CCFA5ECB13AA3BEA54008C4F43BC1D7AA76A95A76499B8C2290ED221514E090BBD38F84D21044ABF4BF2A490217FCBDAE2AB26818B60C29A5DE05D4CCDC4447C527FE0DE8984AC8BC2027F812E01FE5DC599F66056283BBC81BE74B50EC31A5C31A43662B762B788EBCC646 -267845285895C0EA -7986ED11FF85BDEEEB6041312236518243A0EAA84F92618B109A269316F79548FD327185ED9E2BD3348044184CDBD294183DC18B43F944C82F419A6E8BF29F5530B523AEA1A4B28C4F3DAEE0A8E866AC5A3E25610E9A244E7C306EE4B9257FF826045CB593C4AAEAA18FB4F2D7D8CD99C1A299C02A92B23259B8CEC924AEC294F96635A3FB8712F68D2F609BC2D9F8C4084782864E9E513194CFBA802DD7DCED9BDD0DEA9200C13493F83B2078CAA5913EE10E2F50B62FA8B06FAE8B8AFA58FD262C324E0EC737B1B74872B3EE735DE9AC75CF98D4253906E3BF95BE1AFAE6A6769097FD3B55A0F53F265C833F8A0CCB825F0F8D9A6C73C47E0C866C9BA1327C -2BBCFF3514299CAE -133D8F38E5965B7586DEC06B2912E2D78F0682E02F036B6D838F53B4EB679BB83CC8AACF498AFCE836466E3CFCAC9461B32EEC46D65DECB4ECD5E22DEED3B0A1EACD863F8B318AB69F4C063BD3577D1071C911298E41DB93CA2AE2A6F69E6137BAD7EFB6BFDB5E23C7A6DA308DA262F2B53F11D636341B7A3AF714BD7A86A72635F016BE13226CDFB45EC6E3099FC8A51A13F307C125CF9DBFC17929D3D22598851BA0CB14A7CFE4ED14B0D82486140C3C80E1049C85F02761A743C6E95570289FDB1BFDCF3B7F46C3D0F4E3EA0B1936556728264755FD6A84C57762DE7E227363CD1E5FA89D8305790543A55221645E2BC30275A370A840A8417D6C30B722B7 -025B6D873B611142 -00B35BBAB559D143F675769428459D95E0B4FECB7854D88BDDF8BA91AF4265E0FB4ED46AABE98D48A15FC16503591FB93AB4A5AC6757EC461E958E60D5BBAB9E738E80283BFCDA93A244DDCA145F98E08D44C9FE096DEA884AF3A03D537F062080EB8E425DFA31D266BFB8F0D10BC436212F2F575226161321027D421EF3000142D9ACCEFE1A18DCDAE91AA992B1514D64F4C276E590731DE3D62F492C351F45CBD5CB72D11D99CEE7ACDAB8AC488C1DBECF80DC90BFBE2B4191BC8CC34286D73DD7445BB62621C99EB780E17B1B96D60DBD3255928B7EFE25A4034E53ADC1D7FD2942798BEA59CD85047030978A1AADC663C9F51CF54464488907D8124DC0914C -409C80BD370516DE -7650DA22FB56DCA16990AB4CED82A062C07CE8A361757FAF3331DEC2FFFC359A3F6380992C7334FBA63932D9C2B14D15ED3A5BA63AFED5E56A4B67BA474062C0F7FDEBABCCD0D98722FDCD5CDDF9658A1EC1D4D945737E4546F30ACBCD56374D9E04AE1FA5CF0A201DF0600E7F7E62DBBECB982DBC88C1BF527B163D78D9CED1748E9537B75C806BCA6868516BE1B4DD4DEEC28459365E46D574AF37A96CF39BC07D9A7D3DBB24F2A065A7842F8FE7337D019EADBDC7B8A20C146559A323BCE8659DDBE54BBC58B93A714D198B61CB98903A10A4352961B59BE052578E9FEF3A8AE8650B5B42AF85AD759AB1A285C4C21707C257870BCE25C26364EECF2A6C46 -3C0077FC37949F4C -00C08EC192AC7D9D216BCCC887C0A2C3977A9F3DE8786B6CDF6F563153AB09F671B50A5ED30453EC89C3860F17BA474CE0DD9D9D66B8AD7B35483A5020FE6EA43A9F09DE3DD6C1628C027AC1728711BB64B39D4D2679DE11283BD0BD7CAFEFAB397AB1442CC0C1C3DAD30E77A05D091545074338B2D26512D85FA1BE14910B70C1ECB80E3379CA7E081BF0D84A060442746698B5986896CA977A7707B7CF636303FFDD6791C228D6C134AE3C93ACF57722710AB5471D406FD06E8BDFC8BABE57FC56BB44F9BBDB0A00A12EC3158F3E27C7C6E6BB232ED1C2A58180EDBECD096F3EDFCE75C72414336839B4E460E590673EBCEE492FC8C1BF519802AE68FF42F1F6 -21AF948AFA4BC813 -418CBDA54FB524940CA319658BC66E8F916F60DC8D655B0E4C36DC5C263BA6DCCB14155D54A16384D8679D5986CF539F8EF1BA5A69202CBB6811DD98A084C76629B475C9D23622C7428FD20704EDAAAEDE2C6E47F10ABD43949C2F95174189B1456673A62BFBF13CECA3BEBD161C6938CFF31BD6549EE7ED21A3E94DF9FC6DFC30404899BBCADFD879AD77AD4059CD3409CED9CC96CC18F9DDDB34F5265FD56B1A0C0C0D7DEBB0AF944C76EEFF2A38885DE46B9BB134DB5DF336F1065921F984A54B770036CF06CE36F6753FD4DE074BA7895C5124BD17833D1DCA93CF5FBDA16E352A1218FD7A386D74EDE46BE52F05B515A377F2FD9CFFAE88DE58996CC7E1 -0C814A137C48875A -009EB02E9254346F63BA0EB9A6F09D7816986DD1D6E04CDAAD8B69A5CB8731896C0FE9E7A468B195A25D774966505F9FFA8757458A70C86F3EA90D7F1D16A3F18075B5A490D59CD673CB5CE1F62A704E7C0EC4FB023F034B6398AEC09EC8F53151A93ABA9C5C0F016DD7055B6E23281616D731C4EAFC331CD3574DC83E7CF931C5A355E52B6E4B16DB79E7FA4B98F06CB5F0E93D45B9B1DD8F59F78FFFFF5C789CE3B94D4618194CFA8605EE97C81261EC2CBCD2DCBE1101C9106A5DA3AE86298643A263DD0A2E985FA5EDA3E0E954668487C35AB09BFCBCD5732804E6B2EF886D3A4B5A83E2BB0279B49EEC4355C7EFC2B9AF40979FCD93F08F5B98F74D47B5E6 -3E110DC6DE219BC3 -00A14282D6A215337D91E24D343488D16792C56F859D5A65CDABB8B797A2E064A40A6CA07C92E2D1C78E38EB907EA542D6E2806AF4B6F067876195C53064BB1A8CCB7466C47C23A08BED064E3596626AB502E715BD9AE3F759834555F1AA366B31B2F55EF6917D594D684B7B122C10F3F4F2D4BA0D82E4B4477F9E614FBA93F2DC782527F9E9776BDF6E5B47035A22306DBD7D4FD4CADC0E7E7B9BC91735D553A5F8799B50E806CF9B92C46C9035CB534B0BB89667BE0D7F1348180A820A6B4A9F4BBE17029C2E89E5A509B697644A94A3E92E8F6FE694FC0468BF224BAAD6B606A77B32DFAFE0315FE7890EEB7EBDBF64CFBF210A42BE8553114C4059DBF89BEB -2F216CAAE8B75E7D -3BADD897E12875B6243DAD00D8E4DCF6BA2FDE8509298A4435F877C019E7F69069D31BF0416CEEEA604C7CCF8E2670BB089D248D3B0F07D58ACFAAD7D42B99A98141A3CD8D8787E0770C75F051B9AA0E4736EE059319D4175CCBF196C76E5A45A40C2FC42AE3C2DF3F8675B4CC13500A5E4E3D6073886AED3B49D65BBB9C3C56D36619CE9A3775BAA14AF4672F7EB83ABA1B01D9CE3BC077926F334004C06B2465184EB5584B17E7B192818A2C642FCC89BD44F01A604E9DAAC21080752C8FFC60F65C79B81D0A3FCAD2B7AB097222E8137F131234BC8B7E10B7BA781809024468FEB498FB36BEBE9399CDFA816784D97B6BAE83F7D2E001B54803595372DD70 -52BE8435738F278F -00DE7A1DAF8AE1D9DC4C00CD94C79000DA86C088D91E278D9A985597DDC9EAEFA10B37FBD7339EFE91058AF0DAFFB1B6D604111D10BFF842BF0D6A3935F72EAC765D4A2C2B3844AC8DD0183AA9B891B229FB804D99C385BD9C490C476C0D2D784196223D9FC147B4876B800DAFF1C987555CEBF67CBFD50AB8000333930E2228B07FF9D3B1D4B9D4F004CC59AB7263561DB60AC8F793CB1B705B5AFF53967913C3BAC72E8F529D67C087AA4DCD25D3A457EED9FFAB10AF213A12D6A16A304F71841DEE062C792FFD9E268B4E4C24000779E3DF4B99B99E66CA4E83C6075E561B71549492249C7A0744029E6488E54E8E7CE663017F90072215500ADD2CF43BC6BA -5F13EBFAEF8D15D3 -00B23B8FF35C479FADA41E1AE16F9AF868A44069C47D8A719792D0354BB46F1B4DE6827C871E07BAF43847C0790DAE4B4FA2A44ED6DDF9E54086E61E32ACE335B6B903667127CF661004547F258F13273B4C689096DBE38CD5B8E386A0F38AAE0F847415C2751D9241AC91A3C39C6294BD25F873706CDCA1FEE2BFE284F8A0F5E79E25CA79F63CF8BD1AAAE83931C8E8FE90C4754AE4F47DB9D6DA688EF62EB175DA774DC2E79D0C9C687C574DC474DA2EAFAC56D0821D03D13EDE4AC253F88CE14F0E7EDA02F276E7A02F36585983DC9E3364CF6B79C7EC4A6F65A552FE837CE30041E35BA2114A0B01C3741AF3866418191F942B0D153A040DAAB348C4803261 -5CFCAF7AAE5570CC -00BFDF249134DE94F568E0576907C5ECD05D38620F1F9705252C84402876D6539FE047EEFB6F5F97900CE7FF7068ECFF2A64DF27D8D6944CED3C760338BBBCA10ADA798387037793265B4FB0F1A0EE485D16E0D2ACF36DFA8CDFDE001193F5E464797E0BB3620D432D0103F7C918533D945CCDA520FF14E80FC5D31A90C2A8D5DEAD615B41DBFE5A94ADA782D357B5883F2992A9897FEC7BA788B935162C0B1B054F58C36AAA316CDD22A3C54D71D59B5D0214B3C5675FADE7AC18D3537232622CD8D11549C9DF1CC935338161FBC927896A132221CC2D34024E599A5D53D79720701389FA934B131E12BA965000490FCBB685BA01A6B3EDADB169065901B1E855 -6C9A0F1D721CD87A -00985ABD7CBF2001F5FE8775E60FD4170A32E130CA139F12BD56C1BD017FEB6613F39B4738AA7B0E3D2B139CB9E40B5565818D1B215CACA1440D37E093ADBB4457E5B4B259D836871AC554988F3C1E0D63C6F7EC7EA866738780C6417A4CA1391D50DCC8226D6C14A9380B778ACB16A958452C35AD949B84397E8431339089C9F376495F6D5F70D048D3D564811665DA1B627AB7362A68AEF63266E1CADC46BDE0072C862DC45E4D8668CB81519A5AAE1CFE1D97804A4594F84DFCACD3B86DEA70FC2A3E4D66EA6FCC8D5AE8931B90FD0BA9E6BCC83185284F3025CB0F488F3C8ABCFADAB499CC5F4FF1A3195A9FB08C5E705DCCB3B8AEBF6603FBBEE203990CC6 -358A9A94646A9B67 -5D3D3FA1163F74360D539E33AAD49F52D66CCBEB4DFD9168EEB5F0AD57F77A2149A82FE4B376B6BE4B3EBDBE06C9D2E8A98ADA71405CFE88E5C1180B98954BE5CAE6E41761DC6138D4C821E9BF353CC60468B5B45AEB12EB31704994391F197502E6789CE7AE3BCD4982C3A4CD977238660C8ACD2D180464862BF0CEA95F07CA7B2FC88073C7CD052A5DD9CD26DE59FBCF420D88C3544E02DF76B38FF4C721FF7D42BEFE0215DB354E936323B51B40E64474FAB6A8F5CE199982A5C8F2CB973B62C42BE6697CD898B45D768F3BAE294121D6582FE34D2B23AE17BF209B083F1110FC11600015365F7AF47AFDF4D57C4D98944D680BD683D437B872814499AFAB -47155CCB014117A5 -009FCFBF3FF39EEA6AE004A77F71770AAB3995B7ACEBEB4A54A048D0B2148AB723E92F938D7A17A343EF1CBAA54FDDCF6F01896000665D93039A7347582F40674DF4DF73929FFE6A1556047D83E558C2C358A870877154BEFE305CD7C5C47238F3319C624CBDCEFC12A9830028EB7D1ED5B661FEF31814AD92F08ADBA49D2CAA2CFCB7C2F1A75104EB5042436CB114ACA7DFEB8C145645BFDCDCA4F969E7E265F1409B28273D72B9DD7F8A25EAB1BFB160E6C83364927D7CD83CF9FC843AF97C25B3F74427F19453CEE880A151290634C113F8D9A814C6F634AA46D45C1370CB3B882A19321BDF6CFE2178F3FABB76FB1336A07CF2AF4DEC0FBAFCC3EFE1BEE6BC -41D97F2200DFC7E8 -00DB6F1B09E407D45A2CE3C5A73B3AD6717B2AF0722DD287B942617CA9F113347B2C1FD09BD54B0ED5BB5C93E68D6D7ED022E02E0C7B9DE9E8A0AA29E46712B5E49B1FE5A126273E4A9E12D15CD92C139E501D0D7F8E6174465050CEFA523FC0ED2EC267B1FB60F435CE3B9C6F3E6DCA081B84F5B42BDE25E3C4DD8022C3D40B4B8D0933E55BFECB3AE81AE13723014ACDA7FE69C20098CB7A68B3018BA6B9E1DF9AC54870B2B7A56D0B7C49C2295112A9C1A38D11B4AD080AB1EA2325527619CFADF83CF2810120B7D24380D1C3C2793481740549CF14A8C86C5D5D9109969425ADB2A455C96CBD64715C198DB18F1F26CD73E5298A2104A6923C34F429C7574D -32975039B73E35A5 -2D403500367999E9D9D1AB25FCB0AB04D5F60BC822A90B7ECCCC6C415AC966C1959DD155DDEC591464D59FF6857243997151F5061EEB08AB0F3A639F85D72A8BC86C6A7AA38246DB3643BBF2433EFECF87ACE897F775CED37C8906EB53487FEA51F91233380A82C31B72F0D827C398F736528CBCA75789AA248291A62F75459713EF953816A5FE5CDD8B6A18C426F2569D2B41063B87B4634C541A4E7CB196FE1B5D34817CEB3551139CBCF779C3F3E64AE597F6DCD5C09EFB4F6A0DCB02C5B027E51B307CFAB37AF68E703B03CE74832EBF4CB8D8421EAA58703C0BCEE33B9C241B69DE00AB6910B68CFD0824B8E171C1323B09D5591573F6C7E7F77C2C0AAC -6275B93368B0FE1C -7387C48A259E253C945090DD1076425A1860E1504EE3107C36B84B2E467792BA6A820EC44ABD7E392A0BAA9E2E2834C2864B5F443C65B1B65F27BC9F20A36FE39F1008C27041C2755A57D9B8EB6193C3AE48582DA26307D2FCFB6269DFD14A49F20F6B4CC49C86C21D3DCFB685060317D282215F456AFF91D9BCF686D891F874C6BB34E73A5FC2E41E0C6C6E2613670307E585344A6BFF75DE13175D4176D8BA842281E59FBBABFDE53248AE3564BE6D13E5DDE99F30D8F44AE28CE6233681BF82472426C30860F0E8A3CA5139A756FE619CD9C162E8C5E604FF815876CE856222E4908931346BC2D2D8A4D2339BAA29B4A57EA4CB797014BD71CA68410AA77F -594EE96DC019DC6F -38D234B2E36FEAA0FF7776EEA68E47F565A5669D57A511EF0E7DC3B056524B87AC162CC5BA69EB36D950A9937A5D1FD720230C54AC07CC241791E3DFD8135FD1C2FADEC115F9EDC7E41E119F6E49ED82314D8A6124D7DF5457E5535EEB1DA6D9729F78D94FA3BE7F704AE7B6CFAC0AD298D27071E46E9182E95C80ECCAF71F92A59EFD9D9618156E71CD34F96313CA6D95C7DAD7CF87EE88CD02B91227C95107145789E1D84D4C9689E926C1BF16202DF7B04AB9DD849BEEC8B701FDFA6F89E74109586098244A26DC32C89EA12F9581A12728BAA235F74EECC158043BE1DBBE1C8635691CECE1660F40B5630702F252DCB8E66B08E1F39593EEEFBCE2768770 -0B13CB9109F028C4 -167ED3D92354DD4629EA7E4281BF6D626D3028C02C228632C65C56CE2E7F05C48D5644AF84E14C2C1C6E641AFDFE88D9733FA5503AD91C3049B1D51155BE8BBA74C53ED1FFF31B36076878BC25F850EB1A71386232DFAC0D2B993293D3213501AA8EF4DBD77D15A55178975F18BA6917DAEE76F2782942E345F43B988C7250B193C5F35E9D7CEA877D35DAB1FBF002B048B06C82493331148D5EBEAA7508BB032A9004B159B8CF96DCD389D0AB23E697A7D89D118FB7282CB94A22FE83EF2555906D52B23B8AD8FDC5378B82FE63DAB2A6227370E7D78DD8910E6A30903082774C41FBFEF386A2B1EC8D3E9A96286C4178288AAF6DA86DE7D8400062E66BE7D8 -334719CFBBEDFB72 -5210F9E1FEB854D6238D17143DDEB73FBC5E6F8A945E11977A01CD5701058A09B11824A1933EA11541043BD69A5ACFF0A622112C69F28F8AD35B8DBDB2934826B84BE256F1A9FEC4A28EA63586CC6BC4A3F024EFAF9C2C0B9F4C77F2A5A1049843E5395D26109C4E20BCCF717A12EB999F41C528D83B93849435F0257FABD38030D015D79035E7DF57380E32A98382BFF9ABECEF3AAEDEF2CD35C0291CF4D72A5AC8CAD7623A302CFD7D9F6F5A7C39D3B494814DCBE165C8F20C65D5A8273B5D392856BF5D07637B7117948075D62E99735BBAB93D7ED5D788D3710E75EDFC21588C33D0F9E8ABC863319301E6F6B1D6DE1549F3F73467A4C65EF40E04C44428 -7CC354077CA456F8 -075A8F82B13B470C0088859C137FDBB70C92971DE690999151CB76B57356F2B65086597FED9FF87BB20A86F63049C8F43987C6C6B6B56E896C4A2B773498C5362FB2DC7998226EA2F8EC6367962F5EEEAD86D55DFC761500CD7982345C29C1056B2D90394A7081516215E1691A6B72F8F0CAA02AF5AF7766110D8B6D23FBE53816CDD49C366783FEF0377FA65E5E18B88B05501588A245FE45A462D86788B634936D5CFF6AF2707934256E5B3DEDA9E14E39CDD5656C656F4C2966A0FD8C0904B34630BA36676BA60B912E12A912E55E14EFCE07907A7499CDB8642F84981279B4BC5F9C0CE0ABFF45C401A902F3B141F3DB22BAC79F14CBEA39551A01954686 -3F853E4862C92ADC -59B099259346CFB43EB3628D44A80E755CD43ED763BCF621A318348081AD41692776F64349C32768D49F356C1A47D5050C8CE150BD044A94A609A65450DF9A6CDF74FE9DA8402544C12ADA39BFDB23D3D73E129D8C35FA02B03F10951EC475CA567D76F2DD0D1E2F7E510FB8E88769FB3C0BA32A020C3939CEE8F27191CE8B62B04E1DC231CC5BF8F3250CC8B23FF8D9BBC53F2D3690F137244A3756207C6382377A62B226077B6DA64352F9EDC487FB136F93D6FCBD9154CE7D0896FAE57BF441A0D3768CD188E3D2228FCAE078665C2501BF08A809D020E1970EA8DA8524BF0160534D6822F64778C11CDC9EA057174121B1835520BCF233A08B52F56B52A5 -1FF159F4945E9333 -0098E8DA45C4FC48C969937D38009621E47F5F9BBB0754861F41FDA42C894EF58F1BEC5BDE510A8DADFBE2316348BC4196D2B9DC8CE43CD9CD0F03C702D5663E1910EC742C5D56CE02C5EACAD81418012DD0DA29B2DC29AD46FB2D556A12F456D15AE6EC3E984B20DF3A75B1E2B7BD03C5B25BD62861735366A75FC530DD096EDC05D87CCAE1C2DCD3214197FD30D02DA05F3AE097B4E293114D42F6B3AFF229F0D2C364A017D50A2FCB6A4411A05A379C209D66F1A34AD6A27101B88C66C808673D0D1888D3B7B67CA16C2C2226BB24993900632264C81C3146DA0348D907BD8002CA5F3C19513F5AC1DE7794ACD2DA73C36EB9AB46781BFFB4084FDE7A6ADE2B -78575A7DB8828142 -25F2A1B4D83213A692C16221C84DB8B368F551C770C583FCBC3EF5C2B5F4DD718FBE76927A529AC62247B583FA898DCAF4DA498A924E41658B1041A51E842D8434A370258EFDFCB41DA727D9F0B14292350C16B20658FF5578C40A75A0B9108855630A010A6B01380952454834AF3204B2C7396863994D8D1909A168A19286D94602C2DE4FA528CD23DD59F9BE37C05307766AD49378FD24466A707B644FE0AD1B9A649EB4059F7A2155838D8A64BE7E6AEA460D7D2CCC912D39C46DADA32072FBE675CA3298BFAE97A73E83A5AA7E56FFDED4BF4EDEC7083E28EAE6EBC0782E482100749BF9C8E5ABE7AA56435C2DD01F7B3FD917FB4F9BACE86C98FA64084D -59D8AF12F89FCCA1 -3B430F118D997A9C950B8A8CC84EF6E4C55C914832A8FCFD2D8ED7194B75D6C7B2FF0860F89EEDE860EA8875E955D362F6029F6DDD1F79ED23B905C3056F799CB9663F21EDB7CD29397273BA19D1F8C2B4D781EC714B9D64E30576D8AF44B63EF7B561E16A418F936E89BAA851DE2FD77E0F535712C2A6784BEA29B626D3642486F7FB60C1D428454A8EE94346502A93E3E3A6C145DAC7D581CEDAF9CF3B8387511E9EE367DABD931B4764F56877D218F43CD4D84E7D8A51AAEA810364E07612527994EC68C113F96F41453C1CCBF17C6101778CEEE79BE49CCB06C0C521515DF5EB7E0CD60438AB72B73D17D97470A7E3C1E5E070EF720AFC240A7139779695 -299D2A07D290F934 -008F5AC5394D9C78B2D7CB02D3B854164DB6BF032CBBB176E13226CD6F750D4CE5F9B6359B3E8939723458FFD86B2E678928FABA4060E0B7132CC31486FBED764B371D4F729836026B38041FC7D9C048E54737772FD6942619FFB05727DCEE75DD0E3A3D2CC2FABF66E8359AA86A8E39417E78BAD92BB8102587777E81B8E141CF1D92AC07F8CB31525E7A82EAB52CD5FB9CB2FF700C8D42756698342FCF9B79A503562C46C21161CC46FDD635341B4156A5B947B008CC8658013145F5CF746C25C700329309E483E1F17B3CB81505F6D8ED2FA6C458BB1791BA44F3CAFE400F6E27EF0FE633730291F537920FBA9E7A65221B691DF2185735ACABF0F0A470B461 -144C6F5DC5AF5258 -009BCA141F0DA42A602590D22DD8DA6B9EA7CBEA5DA422CD60B91E0C02412AEFFB9D77BAC42DDB7616A341A15006AF9B8F083AFAEC1FCC718EEE6753B9D609111B93B44ABEDC382010DC13A05B13A8E4123FDC5BCAA71344B6D1E945ADA7A03D847AF0CC1BBAF7272DDF15512438805301D2E72407B0C1EEBABE908A749AADA6982C614224D0B74E50ACB20127176DC0D31EC7B56F7DB0E6F44F9894ECCB3179A0BE75EF195EAD913FA74D0F1118599AA96E898F27E372B49674B10A064436C52EB6F39FD3B75E0FAC7DD2314365844049CB69F48E4BB18CD8A3390F7984ADE0CC22073FF3003A29E76E30A6399920CF3194BC6F9EFD366ABE76731C57667AACA6 -396FC72DD2208797 -00862302BF977F23CCD06D209F11DEFF6EEF1BB9B04715A4CA3F0EE8E73BBFEF539A65164966DD536851B2999928A26F818C486B5655311560FBA8870EF0DA13A5B149A46A8124BE7CB7E64DE7BD6961AE3FD4A06CC35A94AC09113761182E7297C32A1E316802CE38A04786F4A7885E31DCC3D81AED4EFF194F7D8F14C14236E9BC78E38F35D8984C1C9023103A2476955D4FF84F1A122626C21239909DEDA88E40FC455B5D8AB67E6AF79A136FB2043920D9D3A87417A4BF63EC69A782772D0AC2CE37907A1F07C8BD7D7A4ECD8990A48EE16ED4595101270C3FE0AE0F88B2036642967BD7FD5EB622AEA3AB1D715EFE875374666004CB685746C97F541FDAB3 -0F2D8FD5E732178F -4A2FB5AEDAFC62F46A227B89333790CE6A08933B84CAABEDFCDFBF492A5791B187E3ED95E5E78DB3507E77D2B8EA8A32C80725483E1B5483EEB2B584C6EA4C9D4EDE29B340D3403F597FBA03936913D2140A5AB878C90D855C5FD8E4D0AA21D730C1856F4D75226004E367DB22A97385A8F7A71A56812DACBC8CAE2B525F9CF39727B21AA3F12DE085376247CED95A186A86F89C50B0C4918E189F3D8EBC461879462F8DB9A4F480636400DB99B0AED8C343EF7BC05D5F3D497861B47B21792DF3432B8C7569B29659B0F77355B89F011810AEE826FE506BD2770DF5A831A277B202A2585774E681366C6AA482B00F2C753471D35D830878CD5C89D6C79578E2 -3E981855E77A1497 -7AA10D14C603BEEB7419911253370AF15BEA1459950FDA0715273D116ED1C652B26E749C6CCE700CD9A347CBB4FB05FBBB9C1F6BCE977D71A0939D2BEFDB4845CA500E6BB2C3E3846159B15C2B88504650D8001234EFA9844C27AB932AB475B38706AFCA3BA93205E429BA418293980AE183B51BDB168F0992899016F516F898E0833A387972429BE8849BAFC8CA26A7B2AB0BBE483FDD3855654F6A06DB9325CBFBAC61F8ED73A8D85E8A81CCB2F29E7A1A10CA01C51372BEBF7DC2DCECDF1450B74906819B0C52A6D0E1F09B87B3543B11D6FFF74509DDD01644ACF2D2BA6CC4BA0886C97821806ABD3C0CB2BD9AA967BDEF86F33AF8A6DAB589C43F30671B -627CD4E7AABE6CF7 -3FA16822AC639A8FD920ECB1AFD8E6730222F4C7D73C9CC13AF4B05C2F982A3B2A6E0B906B97B8313775788C30F3BDE8F75022F2C3772A413B9351FD3140DB1281F3605EC4E657B4CC9AB99704F99824E0F007AEF4B65A8DE05CC8B2408BC68EB14A8B72A2193BFB80460E50B875C718D8A15C7D9161A0037C8C25B314E1E5134537C6060CB3C6A6D9D614B9B6EB0F7C8F5AA34FC4E2D8DF3A34D56F78CD937CB551E6D0AEA5B0E4C83B4DD7AE3910B63BAC8E5D48DF5DE7E96A48353001B70821EFC46A6320739F5024E231AE2D0D76E7581FAD3BE465E01A0536B6A7C7AC47CFE33A80EB4B9BD2D72C41175C2DBF5D44CA566E838F7FC3FFE2FDCE43E6ACB9 -64B4770F626A45C1 -520E533BAD2058772FC479AFB0E4FE09DD2BAEA94CD018C1A3D094DFE5B3B6ED9567E6D7B5C8D07D52BE8146616FC49AF8C7BC3DC9144A637A41650AB2CB7C20DE25E980820213F6EB555234FF3CC2093C0CE73B96B6ED27AFE8A1522A5B843DE13055D45DF7AAB3CD80F34636F8731E508456149329AFF9EF0D0F3DB162F31B19BAF68F6BA9E63717F28BDB07A5BE10B3BFA59367C5FFE1D2CC005B88E8BA5B4E56A8AF6B014E2C40D530B81A855F7155D43B7D3CD058EAC5DA966665E4256BD6D6DF0293963D02D732A539ACC5A03AF11CCB8AB0813CE9F2D0E0AF7B31DDF501F23EA3C7FE368818427C4898FE250B62FDCF2165831252F9EA25F351B1D0DE -36F282A83333A2C7 -11344A88A0758B949E99FFC45A62FBDB46D7702247BEA2AC90F8745B66ED2F64E59F03A6B6762A60B401EBF40FF35FFE54B86EEB7185163C4D6C0CF45C5717CC30382B21F368BFF56878C94E885B559F8CA6EBEBA0A299B3EC362F53423714E5D0B414A510F882BE99CCEAD22BCF85F005061741CAD90B6E04515DFEEFCD4C806AE7CD98D1E6C8B12E71BAD0F9C3FE6595478FA71545E68BACBB41F615E8594568DE88372606B5B19CA8B2B012363604003C3143F16CBBD899AE3AEE6195CCA41C2EA80273DB3DBAEB88C03F5591FBA7B91684D1D489355FC90BD7551E1B7F05B407463AFFD1BAFDC8B29ACD91F79D8EE57E1A4C8FEF6C47CF8D79BD3A63CB70 -0C1D5C7BD366B9CB -00CA2ACB437075E0C3DC69E03FB126E4A805A3B5B02E390BC5E21DD0FB185752ABD85DCB807E8B0D1FCC2854B78D0A93E49FAFD5D02BEBED17E7CC53387DB0788A93EB129925385917EC25B483F717F6B8CEAA4452250E0B855C4A0D43E33144659A6D69F5EB67B165290F395668D0255E07161DA2F5F9791F4CEE9FC2BD239913E1A98DCADBA6EDD3BC21F929AAF1920D4D66EB6DCF892708D3BB2084B2197340EA9B967AB7C88F00E52B6565C30FE7F2E60C0E561DC617FF4B44BD5091D6F1E0F60502B8F6E9588360684182706C3C50B76511AB60E746BB83F5E75FD8E904B7EF8DC0AB1506C8C0C202D57777BC7A5E9A60FF865DE9DC7C002E651CAC83FACC -5A1D0F54E0E6C762 -1E1C090D667D0602EC3906869E59389856CC24A643BC1CFA8B36EE177249E590E8DDD7605FEF2CC8FB217E0ED52A35FF619BAE1606BB6067B52DF0C45E5D38F20A19AC1004880F0E1E3632C8D7AB2B60261CF07909A2FFC60C2CFA82365ECD78A24F3C3CFEBF6EF374F09546996F3AC144D3B3E206AF5D1B6A6AB580059C648F7DA195EECC596FD4FA21559040B96B31FC15D2DA33CD26D526ABFAD411ED1041E00EF54DFDD6DBC75B445F59B76DE2FD65A2D612BEDCBEA283E33C744B9A0D1524992B10C15B90E3D04DC2426DC2A566E9587D49CEF65A8C6619E74BD69C700DCBC17813F6F1F8C6E3BD36A1057AE2980DE4B9E7AA84869EF62745303C95824D -7294DE0CDB7D9441 -2EB229CD504B3F94C7A78BD930C67E0B61AB0081C067867E278D316AB52235690A1D8D4CFBDB7C3C720857D88E75CBF9E09ED22F8543CF9DC6800C9BF5E5884F94A52F9FA1A33BD581C4A0C1FF2E0FAF8FF7D8B588267BBFC7D1C4B0B3C918492F2C9D85420E305E7ED3C6346CCA6433CE849715835013ACEC3839020866DFF29A7CB94A05D220DB9F40EB80FB4C8AA2A5C0561DF8848008E0A20B98F2F159A8C87387592AF3B69A60887D6D0023551C030E21C746951ECFF570AC5D5EA73A9E7DB134FE46B95CC68591FF3D8B8673F49E263C71A293D026E88A9DA8E604E33B615393596F75ACFA4A448E7DDE688EF8ECDCD69C5352C481FF30AAD127B961E0 -66399276F8E0A4B5 -509761358BB3A5C503FC4EA2D65970CD90A1DC4BEC1AA78F4B17D310B352DA878CBF3F7CC85196F0A5A93ECF3BEB00FEB4F6F3E80BA56D0BFA92F6FD9C4821188C4104DF0B19205FF58A53E1682D28E153412B81956EE647A9AD7623F5CF21A73983E39B058320814C62D2628CE2702A1A18F35223BE7534543545EBF7FA093EFB07B33F788D05F47ADE194B803C5B617A3BD60A249D31D427A7B45EA2B72A6267826B8AC7DF0B3BFDE081579A5E9AAE066FDFE2D141DBD285EF7C7D400F92C23A90B120DD9A69153FF9586DA2EB3242E5B53EEF9A3C71CDE56C888C29578486A148394695F6D75078E292592797697C546BF9E4B2AA89E66CD80C6198077C44 -229FE86A761F3529 -69D3C052C758AB1C6D08730D0D071747FF43CEBA86EC5B8416F501AF1E777B8C1858DC171B471846F9368B98172F4314FEF1DC5A209352680207540D42779110BC11FB4840A4EC8189C281BC72B05479360DB503903958661F188FBC58A4E547F9659701F13E31B3BBBE9F4F5E23B170291BCECB8A2D0ECD8D249BD09483B7FFAC784F3B6708513ECE592E7985BD2EA49362064C8E9F9BADDDA92907694AA692DDF710D82069AC1A303AFBDB9F5A93E35E8FC48997CAC49DC85735BB5D0D36CF1CAB74CA67D2C28A958AF07C2538ECFA17ECEC107EFFE4DD0ED96E170ADDF47CED0CC13F514C932998874D3AF6201AA5A226F6AEDCA7057507420F3DC48F0DE0 -3027071B433EA9CF -33B74696A9E69E8AD63CCF27BEE21C90EAC579648B309EF4E7E0A901655B6F85D80D02C4E314765D13FBED074C373675E086B21C4025FC17A42BA3AD0647E6CBB5C5F7F015A1157A74976B1FC6C9C405F9F0E1A2D6310DB44A1CF1EA450D3EF5F774A8889F56F7DA7C438DEF45459A4BFDAB70F8554811E5F5E22DC19D98BF91516DF72815A8FF8CEBEB7D3B02E4E715C98B97724034D0A67ACA4A9FA6BC0CA643818969D52BC4A12E79479BC921EE4BA565CACC91D9DFE63B450CB315925A30258A5937F7DC37D933E9E69D84DA3372835AAD2FA33FE2BD99CF39A7F4D106674DF469C5FC4EE1984137322349D821DAF24A00C29DB428E0A4890D5BC1F0E000 -45D1F9F952B21CED -008D66990A40CA0F806B3793BF87FCEA2024B8D74930A575AD1E50A64B3993F62C259D64DE4DF80E90DF60A7F11F72704296C3EF569BF9C5CEEA3278BA6601F8FF815C82A12CD9BCF2316D4CAF281E2295774F14902D22D2EFD6CE2F44177AA1F98FA0263236480206922A8EB073D8EFCB8802FC4720576C369E97629DF669D0456BF08F5525F479ABACF11D7823EB6F604340254D8A72CBCA9EBFC8F281C4DC8E5FE0D1D62FDF717732B2B9A34F0510DD3664FF2250C10651409F347FCC119FE202D88809C35BBB695B90CD95442EAE71BC413492015BF1B2CDE0F8E1B53356064FD677750C293291544BFFBCA256FAF1982EC3D5B0D604BB308E5178E64E252A -75CACB9FBD0BDEA2 -00B411EFC35B20E4AE45E0886BA049A0B010CE5DC49C9AE48122ED1E1AF9E03AA2AA25F63CB133FA255ABE8807F6F596A3678B1046CA2AE9010099832EC7271E4214E3D0B2E8CDCEE8226BE74142ED4CDABD647FAF8435C56D89690931E41BCECE57EF0A83AB94D8C082471B15EB3DE83FA98908C92DDDF7B9DA5674D9DC81C99D9779EC2B6CB188BA8363F8FD8E09A9D0A0D3AD906376F827128A2AD6FECE2D9C5BF0DFFAD68424A952FD08CCF7C36A1253BE8E6EE7FCD7AEAAEA3C1DDFF36EBB577B46E8E044E64F843458ACC54DF5D40966CD7C63E34C22B23874EF256C3D2358DDC2527AC9E1E7D0413AECA17E2B243AAC64DDD53BEF5C4835DF887A4D5F9F -6703B7E66B2A26FF -7B311D86C79CC1E2DAC8974653C7EFE8B0C3D1EBE126EE615B8F377EE5C497AE4ECB961F2BDFBE3E5AF9E1753686587BCC0FDEE37456DA8922B78F110F20C300B2222A996F1E382AAD55071463B532D144A52C8DC1CFDE4E1066EC3A0AB4956176A8AE40A36EC51C34E3F8B27D89AD51A242F0131E0DD696060B3556D083EA0DE370F9A6FAF43895DE27C8FE853C2F8DC5BB8144CEA7887FF4142B21929BCBADDB4FE343D0F56E7091EC0AF3A2D0F1D7BAE3DAB5EBA7F07A93521212C76B575C969FBC09D4FEB4648DF0D8369258C41E508A33515038B99ADD242D7938D1D274693299C2A571BEA001F64D76ABD5D2BDA531024A777E0FCC8DACAB66246D49A0 -2E25CC42BF70E04F -55F6CFAC8B0D0843E10D410EC541FBA491C9993A375E57279D68D9D2975D1E586CC35D5E19A5850FAC9AF890B2C72C194F8799B9CD946A818A3F033C66766D4C658515AB46A88C6FECAD768FB662866C46779E4089F6385C066220554965CDBAA8BFFBEBA6F18328D66797B1E174A027FE7D72CDBAC497352C72AF794846E8452A876CE41B5C318F00E984398395FD89160D1640316A60728953D773406B14AF6C1D7B0044CFD49696BE98E29030943ECEEBCF94B158F50770A08E02665C9F8DE77D78D81BE05A7AF906D080231B1B3DA4CDA60C4EB77C6103246FF8A09E905C48AE11D563A64527A5A3B5D1C3C9A7D73FD13D3635DDA1D528FB3CA5A535D91A -67E969CAD4A74891 -3466EADEE0941891837EEE192A5F9924476B4ED09EB15C0316B948DEF560F5451782C02AA7CB2BE0DFC85B516A0A3C0B8033911EFD8323EDF3D7A8B9F8F1982B20A89F3CD0892CF71F3118AAB0A382ABF73FBEE93FC83DABC1C449300A85CEBD29A6C8D45D84B518B98E21C86A992EBB009C3541AA40E2D8AFA9CFC2F097E318B631ECFE42594A63EC73C60FE505AC05F4FE0B8036F3D9B27A9532FE10064EC54DD9ED7E49E0D7327A07479C288A864824826192471F8E3EE042780F9320BA9A9DF73D46FE859A965428D00DBA8DB0B095F5EF7EFFFB518CFE2462C8509D21D19D87642FF23F6EBD5AC83EEA6C404C4CE3615CBF3FB5286361D857C29ADABCB3 -658224377B493CC0 -45334C65DDA70DD6E621C9AFC36B7ABB02AEBB4DAB8626F835E47C68A714D341B63A7C3F9C386E581190ADA60AFAC3C9999D581FADE2590C62D1644004BACBA146E31CB6253CD819B5DC796BC4C74546A4E53D69AD043D8D16455A19A5D22AA9717019F5C451DFDFF117114F54F4059792CAE23D9CB9BA6DB2533640DEC1D3E4BD0A2A8BE8AEB02BDDF464608A8DC61356A376F6564D12BFE77FEFB5EEC79099F87428DF533C2F7BDA7750AAC778C469EC1123F4AA1F46A6DFF771580E9C44A9D6A8828121EE1ABFEBAAFCCEB65C7357EA310825C33C255C57C2456C90BA04D0556097A83FA8E9A56C1B18720462D9C33FBC12EE3621DCB25075E16C64D91C60 -360E015E06E7A87B -26A23A391E6B2AC7DE5B8E765D46B2D68803DB2F936FC28A5051DB58C7D5C1D764DCBBB43E20EC053585525ED4496507F68574749220D2F831043D02FA5EDED8DC7D9BDAAE93D47D6C13BA9E828950B57645C129DCC598BDA3C6149E10973A2746C2F6C1828FF83A857ADFBE2C39682C8988CEAA88114DE5A18B3046F3F3076263318411B864078371644B26CF8D599B0F81AA3573809A6F09261FAA66B2EAD200DEEE99A6E69139681188B20E56D27908BF15366D132A7506854AAC744456A99820F318B2954DAE8CFE6B4C169FE72E3C14AB35B72D99FCD4375606742FD7E7412FCEB87F372EB81080036762D2DC5D1E8580ACA907D5D96057AA7BD4F53428 -4FE6AF4FB7354884 -008468E946135222C8E2A690389B0C10D1F0DFDC79F0F32DD1DDCC199F761EF89D055D60FD6B26BA95A9B649B281D8CD5E021B95FE927F18D08364D7F79AFADC3B1539C03EC89210C812BA8BA5A4A3C5B669FB2C8873F4E10271CA347603A67521505A8F519E68C04FDFB08A8FF8E0D8502EC61C78A3EF5E4C13754B840B6DC5FE42B940AD56CF01500D2C2ACB3B547932CB2A085D7436780B8059D05DD283696AC00F29557CBB326BD8F4E4831D1FAC92529CB5DC165D7A0690170049A1F581E47C2A2971D81F2EBE015B4FBF9080C7A43F075DC0393712E2AB0C357A3380560E93F99A0C101DFB880FE037D53903AEDC87AD737E0F8B745F47EE2A0B0746803F -2AABC91CC2EC10E5 -0085690DDA22367585FD67F554FF9F773DCB31AB40CD7ECA6F9FA391518D3029876A9EBADFADE0BDAA8E63104F7F6536469B717E6980E662027389A03CCB92A70BC75DA294D359E8691765BA9B7B8F9D7C85D966AB0A5A4BCA2C2E4C68C2630A755F1896D5383F64828A4C9B01C4A3078635AF66FB8C4EFF30A52417E117FE7F49A1C7020E2A72B9075C771C340E9C6B3E77701F4218EDF49AF6D91A23BE54E568D4CB2D737D36673B28C925E46E6ED084CD6E90EAD86CB14EAE8B73FCD8A91D0D335EF320E26EF1AE1C971249F69E6EB62C30F329A9EB5134400F59927C46E187F6A6572500C27A5062D11948456F5E09DD41A797C207529E687A0D4D66EF7A61 +0257617538674392B59309FAE788E0FA46FAE99D9DD5CD2CB0E7E0BC62D40196 +25C57B664DD0DA55621A7DBCA2CB67C9E21E6222060A0AF74A5FA44BE7C2A7461AD83A98E9920F05D37B57A4A1D538ACAA810C6A46F08BF4E6349402DD15BC469E369FAECF626EBE248CD8634DC5E6F5573BEBB4BA26AA005CF3457626B2B06BCFB1F60B0E52E72FAA8E0138173FBB0B23F290CA22FFD84D9B8B63B72A53B34BC45607A944110B43187C61FFBB81F4E3CCB25A172C8FBC99BEE1AB8E413BDA45BAD04184A21049EB26402C84FF06B0860D90F3DCED4B3D529F6810578A169ACCD88A3D1A805F899AF497C734467DCCFB33C573A2F18786B130E2E7E158F43B1C8F208367F830671D4AE57C2910CDB814A23757002722E9AA564FDC3C5EDB2E0D +1DC5B27BEE0ED4515DEE2355BE66A502BDC7CDE73EDE395CABAF35C9D7D67F6C +6A868FE5151D554F25D9B78460D6CAF9D3EBEF84F18856135590E2DC29E3EC98B21575DEE608FBBD8FA5A8CEA89A13F75F218369105A40CAEF0B0D0887A7EC1305451568D945869CD56EF01C49AE3653FF01832DD3304C7C7365EFDB24487C00E761F479DD904B072DBBEF603D0050CB979CCB88C932AC6FB8BC6C7820A58F0B3F466BFE33AD2822BA61065EFF17CD3D1B5158D8935C0496CD8476A9814DD2037EEED4E3D1CB74D57898166F006D63A512E281BA50942F8096705B138CE28840A3B37024B70238A42B9EDB19CEE6818E1C05F056CB6ACD4B5163CDBC62768833C46329D2DD8B6D2F897FAA3D95696BD45878A95AC6024762BA125A258D2129DA +4DFFF350795BF632222CF963DD03E3EF3A886EABE96D9E52A35402395A9D16D1 +0080CA77FCBBB848B786EF925CD9326449B490AB8A6B60E9A6D224508786D0BAE6B1DA6F139A71F8F2AD0D3DFBF4CB85FC97124D76C74BFC0B086C74A5338CE198DDFF94312A1CE078163B0D4E7EE29396300963B3854F4701A7701AE18B2802FF9ADA371A6E48216AFC6D791363AB68F18D464DFA3CE66081CB5ECF1E8001E6796A664A274959A7E6BE8C1E73BFE701D326C84B6D0092D1439EFE00E81C73532E3902AE3E45C4AC2412A7847C1D85F0FD5A7DD11CBC5A4438752309C404858AFE1E55F21BC3CDED204966126C77027C32CC19691051D2857ACEB67F827F3366F014D87A536089032BD845BF9B5381A9B573D000E989112B4E2EACF4F3BFA25660 +537120105BFED5F16A63281C55778DA44FF8180E56FFD0D3ADF428A5C4807AFC +00AE038B2326CB1D07B2ADDB94A6617DBA992120653068DC454C99CF3858CB2AEE178997802AD352EF6D907EB495D4AE6E258E7F407B8C21264FA5FA0D43E5B8EECF6BE0BB2E9567B73549BD21844807C1971F543F3E17A9DF9534221B499CFCC853F27529306A9896E4202CA9A97C67E9E2B2F1DF5A71E1B12A5CD449F6AE93997BFCAC2A6422B7BF121212F27E2BAEE22E07371E9B60478A7EF87BF018852CF681FE58F294269DBE108AA82871432AB687415E47C5FBAE18B011CF3AA0D40ED7F192708CAF241F64E25FD61A75071EA4D6F385C7F63EB3144E7526E574D404527F34C49A458249A812F8BCDE8FC28A764C96539A106A1433DEFEE6E163559098 +7473A3D9D0E01D837D7B73D7A2BAD7399E4B7B7090010B943C8571AB16DC1A2B +00A95FB081B8A50F94CAAE480437BE9E90E604DC776D7C5C3EB6FA642C414C2D3E2F06D2C071A94B0BDE65E9B19141E86076A1903F56AA11F8F8C58EF7B3EA9A87EBCEF8176F5F6C307BD516E50DE19CC614E72DAE09D582FF230CF44B379F2E5F697AC696BB0A59A64123B87EC5888482294BDD8CEACED5ECC54E59D5DA66349B4BD62E633B688DA8DBCB2A24AF90001A7E27F4E166E33A5F28663BA69BD19AF37538EB181402013AEB5D6861FE0B8590A53BEB49B1ED5CD6BF54F2F3D88AA30355E4612D518BE4856114F462789BB89197469019E428F63D30534EA2F852EB43B4A88637B65E98BE230B28176B5B0AE3B91F2E300E09F153630E1919D53655FC +1B03B93DA3E6CE05DD0EB2A051C1DAF1D23078066EE6A2B8FC8A49AEC437E036 +1A8F7823ECFB4C523EE3112425DE9E4C955EBC9CB80D950E7BB9F4DC78BF8B5856EAFBA12C380AD4F35D931995EAEA02DF2446C3BC451E90F78E41EBABA8CCAAF2033EB854DEB673529C79B75B67C45C7C88004EFD6BFC1F44B123BD3F921BA28D13B95F4A7DB81519F7ED69BB868B7B872FA3F4BCA025EB8EAAE7DD0D605E2353BB878B95C43F360638EAAEFF4A4FDC76E060AD9C40017C160DA01B544BEFA8D29D5337F79CE0CF9D0470226C551FB1AB0E34640710B8C9E104986B48E5DCFFA021BCCB59FEA5B05F1167CA00D9A9D54A4B70CE8D8A7F5264E339A429091CE39540720ED87AC2D8288DD661FCAEA7FD0192D32583B8E2F81E244A8AE3B9D127 +60DEF7A9B99F1E8674B7724A5D690BAFD4F2A61FA9D468C44B7AF6DFF0C3A23B +43B6C3A694D2824AF0D9562D7BEC731DC937CE7A9C5B0D2430A0AC3B480A842404B983999DB14EBF7042C53896F737025FFF0D88CEF1A9C87136C6E44E2CD7D269E6B0C4CF82FB1712EE50D6C4F3CCDE1D5811D930319B8360583BEB25AD35A7DB960CFBB317D02B8DEE723F8D7E2215ED27AD45F7CED25742385D5C079EE884898A17B34C14E2AD2D76D32B99E28DC4F2A4BEBBF2830BC0B448EE477CD009CAE9689873C24A79C85487914F4D373BECC74C280C22AF579CCB756FEE1C0AE05D117A6052975EF0DEEFA2CA81A2A049886E0603CCFD5AC4BCCE8B0CFB7383D0C5C721FD10CAA1D7636EDFF670FB532F8232EE9A8D78F5BF832105FBD334E5D516 +332DEB487C565107029282205DE7CB4317823A8845E0EDD3E0D6CDC3C7F2F0F5 +4A624E2AD8C75629C9934649B21CF34A5E1FC6B7A075DF7F6AF5FECD9EBC440918A9A88C4192F411FD21B44526F885AA1F9AD0F9080DC656164908F660A4A9B89341EC6E6D31C4257A5B6E563D0C4AA1C7AA106CEC1B0120C4C0C50188E7CFD49DBD3F9B125C8C52C8E9FCAD18006EE63F2B709F2AF44F6F79AADBCFB60091AB9F8829FEA1C1042F0FA685B230ECB6B6384498364BCAD9D87B5CEEEB01AB88CB364E93310F913627C0961BE7D8F989EB55E63CE69FC5475CA059354331795F87A48B40FE4FC5476C10ADD64C72F9842D0AEEFF5DB3F945E17BD9461F3FBC99A11CF7789FA4B6F593C94FA6C7657E67A6A13F0A9DD3BE650EDD46B1EF41554EF3 +6CACC3A77A69D677DB05EB1DD72A90AC3784465BD5B7E8AB77165647240431B0 +532C997DC17B33992ADA87E8F69A68AB39F72D50DB3D504F6190F56F69D8AA06AEA082EACD9C47544AD3B9D49AC3521B4F36A26C78F19787BA990666A898AEF53563E8E08930CEBFBA1F65518D42710D384450D26664765CD1DDB80EE09AAF6C41AC8F4240EEF6184F09AE4DE0D59C221A7F81FF54CEE4E5D2EAEE2F6EF9F00A5EB1C2CD9BC6B1436E20521B2135F6BE137CB464C3F0CD0A3ABB41A5B21236F4E9906F6F33F007F0F55021CAB8010C6EF69F40820BCFE6CD0C39FF76CC64B5F7D521990014A667897D2A071C27AE888EF73CF352408C2528997EC703B8D753A96398CC38F086E0EC282BFA86E24398A0B7F8402BED7591353B865F4543070D1A +6DDB74EC73AC721CCFD62DBEB99A8CB193F326521F15A92D4A2A09273C5BAE6A +25B057A60B3E4BA4C27807738B566C47D11DEE420E3F00FDCA423C750121740BC24D88658017BBA438C165B293B17B452BC1832A2BB644E83640B32E09730494A5727C18878BE527F014AE5C0018794FF71AFF5985390C5F583D6EC335AD2314E04B1174E22C260B6C2CD15BB6F5098F450D4A45889AD2F77C3620FFB348D50A0100FAEB70FC36A44D928811FF5326A184A95A216F3E581A4F8052219E1C176B56BA7E9EF43E48888897CF9320C78D0C19F85D8A919EEE8356C5154A4E88E28CA682B192763561BE137E105364C234DD49CF05071B588B6906C70878B476D718C38E093F108FCE4C4CD769F402AF376F7ECF49831BDDBCC216C8230E56BD2D34 +261BEBB9C00A6513F7382298722AFD4B208C1047939047E798E669E03016AAC4 +0083414307F0E889967F9D0718E2DB7A9EDE6F0DF334B1AACB8B01255601F0AFBB0E4C26CD9E37C72CC3EE9BD985F6C0ADA575D86F1052989B31340EB9766DE01FD9517B93A8358BB8A7E39B32F263B01BCF9701D94966BF2496478F93F604C944561EE432E3600516259DF78B80747347782B5D8409B810513ADCD5FD299BF0001E8F516F6287E1BA27ED237AC8B68A7403265F3AD1C60E4EC2B35384259D59234BC2CD4D05CD60EC1CD19E3879F713800DB43F9CEF59022FE1116D453BE9F4302753CBAE1EA85807EAAA1E4C14DB23594448FD4BB74FD6BAB7D63F1D758147AEB36D8EFDC57B5BD47397D3C0895C72FE300E1CFFBFED6C0E975B59A2AA196D83 +325652E1823AFD562F842056AB541723BF292468CDE7974330F683B701A42684 +4003DF2DA0A80E51B359C4F222639C91C29F8133BA8B6B2CFF3EF04D6E5FBA12840684D13D0BB1E5DFC3CFE406D8764EAB909B93854A3F0BF434D0C168A9CE6252627C7F55C4CF02BD0CA28C4AE8599E3A4F1A137577DCF344A53C41E342A572DB1F09244F681FBD5684EC47AC8DBD302A71A797462C0957B0762AF21612EF6CA99D21C85C24E5BC7D26FCBFB495E48916BAB9976106D78ABB5E6A46F2324B77D691D4ADAC6064A24D788BBDD7958D7E9AB5617A859AC32C0C79BD123B6C48693E087BCD72A539E40FBBE8682819669E47ACB560DC0C6032890FB9E39B890404E1A0AE1E369515C2ACA0364A01A06032FE6F73D37190AC29DB4E1DEA39D181F1 +7AE789C678E1380D4C4C4621302A4A8B8DBEA73F41FED8DD820DA2493BC2B561 +0098311594379DA0BDE018C8815AA89A8E496EBE60F098F67CF0DE41AAEE110ADCB83305AE925EA94EA70E7DAEDC304568E6B171569B9AD78E10421B23F5AAA4F10EA42E8A26649902A1DD8CFCBB79D8ED2B8BBA6043D69A5A54B469221C3650D2A3A110CDE4D2655B66C37BDF48055301A3DC5DB201E2DA79F2D8F4A4D1E42F0F0A2B17FF309ACD2DA7B0E79E4B0DD7B44A6AAB86947364B8BD9CE350C62D8738F904F6B3D5A2682439D73D1112A54EAD7903A4C73B745CE5281A76D95F48B6ECA03D8138EB4135F10BE350C2C84B3058B1731E4CA1A1E919FDCAE73B77DF2D31D36DBD044CA11D27B4B7A77920402B5F78278F44234DE0A64586BD93196C0380 +0332B9FEB49E0E2996837C0A92FF3B3855D7FBBEB4450593F8CBEA658ED6E0A0 +31B40FB0843E2357495372B202593E85F8E81B66FCE3E7944960A3B740E6012258990486E9F23CEBD600AA9EBAC439D900153BA282C7F71F25A575FC15BC79F36F018899DC99A6FC1B86CDF30A08501CB38A8ADDED2A28DBCF1D334BF76C0EB57621029C91821815D716077DAF0DD998E1C2B42F2457154F77363B1D04FEB99A2B0296A0871F4103DA8549524A29A72A885E5ED169FADD9B5525D0D08586982A1E5BCF91B450DB1B90B49131A1457FBFA065FDBA0D54413C1C72BDFEF660CF6876EEA90B6D95EB30D86310E12F7BD6028D4B70F83C3FEF1D5B7FE19FA7ED7D275102E452FD8745A97CAE103CF2F40B0E1C6FE4C29CDB0410647756228540AE79 +4868320F200D01274418AE9436A00640A3EBDBA3358CFC99C1CA6F2D06A90DD4 +009545CB64E15AF5D2F20C7E3B8C6C1E2B766927FCB2B7D601B2BF59D9A0B6EF604A735F3E7F3FC20F01B3C3267C568A77181DA9858375F3FF0A25E0D016130231CBB4735FBB9D3D10FB1FBE9A8A39CF6C74B0933E867A7486F14878638D331093BDC437DED6C3B1E4519E4BA48CE9CEC4F048E486615DD11865C7A62F54DE4E65C636EE58E806E35C524E86813BFC6A97ACCB6BEE37E0DA39A8038429B28CC610CBA9081D608D687AAA9667E0671B0703C3044CFA68909D579D2B9EB3B37BA825B9461CA332AA8DC67EFF251F8C356A5348C30B4279A66A99E519B6CDB009C6DC9F54DE45480B3627D32B28EED1C13DC23B97AFDF5E49AD10CD981EB500EB24A3 +122177E3600F9C048A99D7950FD99B2CD6EAF07E069ADCE1AA4EB0594640200C +7675F22960B54A2786426F78A24D957D4CB1775C250ADA5E566426E70D00C416CBFE9306B9C22585C83A078FA2F92195D9475ADF577CFC758CF563E0A59C59586B5700B62FBBC9F081B984F08B69C669F030A2BAF24FB38F08302C9E2F13585E3CEACAF602F890D620A57B0DBC2CB3721544DCB41A90ABDDB53BA2FC04BC0C63947449526355B4853CBDA0E5F77DF66C6D97ABE41808CCDD3FE65F1B918AE8DBF145A2CF56B2AA6653EAF656EBD09CD255C968583C76082A0B94FE6B2A7115031CBC885F48984AC75B5D34B92FECEE72572FF911478B3A22F45B2D3F87F8721A38466B6148BA325823BD7474C2EB812C8610EF322BB9EC12F474EEFA4FE7F457 +1FA587F664E29AD9CA721C4CA8E3C9A8E33C5F8DE617D70EF0F88A1E8505C3B7 +00C0C257E68427C0DC6A38512DE0AAFA62EBD5B77C02E047F6C29098520D500E24BF7E10D8CAFE278BD96F90C9B6174F8740AE8340FC4179FAB1BB7E166C622DBD95E00B799643FA5891A29240F1EF88B8CBAACF876D7807BE82A6536B893A282B613B2503D03A20F9A6755E901EE60943145F55DB43AFD49E13E07B59C55319031235BDBE26A7515B57A468EA6CF8C63EB812869B3C3785AAA48A590F16DB238EF8AD0E190D86FC6D936C70E07E348FFAEA32A3C782D63D9A3F1B1EC50EB0DBE8415E7A81A0A4B29181DFFCFCBCD1E332C06A5AAD88F1512EE017693012BFFF2D2589EB2FCA39D40764C036F44E64E469583B681B0AD3D34CC9E1DD62505708FA +300A649E1B79DCD65FD34AD47F7546E04A021B914E81C3F02E5991AAD4EC7EFD +00CCFAAED1A37BDC85E3425AA4E153ADA0BBE8EB3CEEC08AF782615EE0C5D20BB7207740515E85F6535ED2E8611545AF9E56D12621C2D4CA7C65370B477165EC47DA3AEEA00AB06A6A59D5F8C1D2E2BA813C5E73A69B21D76F58D538D5CF459609A9894CAA139EBAB92F87D1BBC718963A6950EF5F95B84EDE5E313A3D13FB2E54D8E1711E35D924A80B0B39DED91D16BA820791DB1AA404BAF461ADB4818F83646E943CF92DAFFC75E530A1158286EB9D1CBB126149F2E1C9C89B2F9FA146E718866A3C4056B4E701F663106D60D7ED68116A1B62EE4D3CF9E75FD3D1B0B0B234608C3EFD7B6FDF9E00EE452B715B8A0127B4B235942910AB1E8677B6D677FC24 +68F0C5ADAFCFDB7B8928BF1ACC10D53A875260FAC0B28A071BF9DD7B1DF68322 +7DD2132EFD57EF47F296203104DEB23C8C8FFF3660C445FD3E585473C42D2F05ECD4DDCD15BF5687399FFC7DA9E57E87119A6FD284E35D096E8C966A1CE181C8C5E36D036306DD12A3F4047740491DD42ABB7ED8469257E0D7499B2592F17142F5848D4F03F50EB34968937A5B22B6F630F10E324AD99F0B9D78776574E275FA389C56CA70035B89AE90A1FCB1B373DC2D04526A3EE98965E8200C46F98936A68AA60D28A882FC182803F19AE60B356F7DA2E46C725019BEF0EF9082AAB37CB002D1F0BB324AF7A48EDD3CD217B1C345E7B12D29E1F530E90F2F958B27890C026AD3644A012A7FD4645559834EE52AB709DE24DC7319627B480004F02F37DEEF +37C86661F1FD1C09ECE713480083B18B59F38DCF8D6E8BCB723ABFE4C1F30BC9 +7DE7F1E9BF8DF45CCEEAD6C4C0F0516437F17C7AA263E25F8AC73209E393C571A1E8396ECE18A8A5179D52118163DA7DB67EAEB034C4D99B5ACFAB40420D4CBB6F4A9662CA91B64C7670D6D53597FFC9E311DF4930CA1AF687B7376ACB7EAECAF2AAB761D80D10446C55169E759771A271B19DB40DF8F273AB4E487FE0D24489D303430B8EB37417621FE57E1F580729926BEEBE5B38FF7383F18D88B31264D05E5B03A0C2B26B9E537673B7341B3AA7CD0FD6070CCDC9E221732D977F6C68ECCBC4FA22FFD379E1D374B520F7A210CB8CA32CA9D77BC48BA2B8B9AA2EE43F1A00D893C72A7B5B848F1968576307AFA46D87F1196FE78180B3BEDE354BFC928F +16ACBC7E86CEE2AA4A35B6CE3E97A2C176FD7A42F8E4908647D63083BA31D642 +13C38B33906A2881143A5DB11BF1E52FD7229FF7376E20CEB7B8DA239050B05A98583C9A5D928E91CF204527720A77664F83371F3B74AE8386D75C445F8AE518B4DAF5706D4ACC933253675B4E6F043CABE45B6939DDB4145CE876B0D2F61E038B0B7052AA3746E45F15A15BC615FDA06D443D59D6A8654DDB4E099984B806BB8471AC64856F44CA79B579B29FF16CE8F23D5E8405EFAF762A78D0229DC5E3048A31F9E8BA6271D8E33BD08B688107BA28AA32BFEFECA895BB3EB0775F70A019D672CB47CF7FA7A4211A82C15B84015AAA34C758A3C3B00FF3BBBA88ACA49AB88C6324CB8D7ED7A28DA22185951DBF7D42922679F08D5C8269503FB5BBB3905E +46BA6E963096265B7E699E5FE0FE0EB3FA2BAFEC539010F5DFF8607E1AF9B0CD +00A3D78A90F754F8ED4BE5FB95A5E9F8F1FCABBA701AA77CEA77A54B7D6B16766452080C161E451ECA8169A785D4D857B5ACCE92988BD87FCD1CBCD9CC62FDFCF5372A148BECBE08403F983FA07D79B63FA4BEEE5BE0459F8E21A45D3D62C3A9218AA58B85729DDF610A150989F3ED56565011BF8CAAC42F7A5600C11565C4B9687B4643B869BCF763F164E40CD6D6890CDC9682437A66E5CF37404B0ADFF642D4717F2C061F98E258BD6F0E93CF96981E63CDBCB9F4F5F257802C4F7E2D825DE8109A9AA1846E8FBBD6F282DD1C6D4500815CB66EFA7EDB098546DA29CE3EBD7D0C5D084FD296FE4E0CAA5623931D0FD852CA7DEDCF854F475B6AB2D28715A920 +085518DABE94669B84B130719D68FF85AD370DEA26E61BE4C657CA074C9ECE78 +00A0C41677873FF0585CDEF8AE80646768B0C118955C29B24A8F35065E62C4F80A04D93C31903A3999551E3F211433E36F176540BEE8E477595F6B242026F6240571A98CF5D373CBF6A4BE70815494C30434E4B1512EE76BFDC3D77D85D10423AB2070A645B0DD25D21B47FA12C503EC5F18A0353106EFAEB2E9FCE7CA11C46CCA2C13B0B7858407B517565C90DD69AF0E085C875B09AD7E88D5ECECF3CA1854C83F46796FC628266A1ED6C5EA306D81F99DD3BD6AD0A640565036A2A553C336CBC96C338205B3130AE60F86C17288342501CF270179D728CFB968BB0A1F26B84E1BF949E5F1F2CED87C6574396FFFBD6FB3A93E3CA461714B376EA66BC325DFD2 +6F5C55C8FC1C61AEDB01A30AEE5321FB7BDA87EAFB1F1E1D06FEEFDFC5B84B7F +1AFB95C2741C144F509174A8AA8F614B7E42B10A69CBAF16DDF651BFC48C6C62B5A11BA45D1F42D2B9E44C4AD47A36A159640804D6CC75294FD7939E30183AA062906CEDF7C066F9B82767D789B0D8C73240D72013017AD260ACD8A95E29DD931CB89C687430C3F2DF33725F1A2FC1087D5EF7F7B3FC57350CEEB502CE31D059955B2E1C8B753E1E77E8B6AB4086A1B5368C32A4A41502C66D41860AA4BA8C50B15559FB83F806DADDF534D3ECB443D255F5551F589989B08725D188597C22BA71D2748CF1C395249C0467EDB2E2A4ADF5E24C56A180B867A4C72C4F14BFDC25DA951A37401B21BAE41F7EE70180BF33D1C2CC8C097BC2456964A58E0CC9C657 +6854B297904F3F17EFB8E88946F212103661B9AB9FEBC3BC48DE2EC7B851CD4B +3F0DC4760365849BBCDD341A588E9C189DB9F827C73C69207695E7C3F9B48432207A9DBC10CE37745D0FCD171DB202BED84897DEB9BE59E2A3FF6927FDA43C72973C266B7A3D09DAE97CC58967AB0CD023ECD90555E40E1B80C017F1597CE4EB74B12BEF7F79B294E958696FDD45587B661C7872BCC5DDB31849F6DAADB43588C4BE623F3ADAF2F377F24706854E79F157F6BABCBB3295169D3F5F41D0BF7DB92291FEA645B374BBDE895194C189DD6584640581094C3687C21F8EBAA9CC5FBC3B3EA24508D87FF84D3050772B56F6DE0CB30A105715938315130AFDE16BA3E06A3929BAA97324EB80550411CCCE5E80DB344D920A71A723D0015075B53EF56E +448F67370A9911138B4A30CC21CF51DEE37BAACF46B1C41DF9EB486AA2690BDC +531A3B43A8CD87A601DC844BC3101EEBE3162E1D94C98CA9640E006B88AF0DAF6B659986CEDB1A982B18BE980C3B1EECAA9CE6732C3188230AF7CE6F4F51C1A7BFCFB1BD3217E850C98B9E8CD6A735A574D2325DB372B0D4183A484CAA777356D5CF76C08F61470DB57B2272D7602030499351EF9593D458897C03AAD8822AF0529B2F6206B3921C1048FCA4E37ECF3A17EAA9B440FE5820795E3347AD3505940880E2E620733FDE912C20BAD4E97C3368479B97FE326045121F17F4297590D42033F4E22B6561E694C97F71CE06104FA42272A43C6880E200BD8C60B3BBFDD359C1A5C2F10CF1218EA9B97CF895FEBA96AD65116EB56EF2B1D5DB279F67FAD8 +48C2BDAAC6981C4D494710F2EBF041F31638B8B7A21506E6C7AD89E02A48391B +4F2B6602D2991FB27A4A5B59C009FF56A779CA27C99E6E1BCD35A7030ADAED9FCC3CE45AFFB6359F22A208ED972A7943237636033D80B5CE997AEA46836CC64EEB26C05C9365FF234FDCA146A32617A509605B43166F77D9ECEB9E826CBC60740633C0E777530CA4F6EE38D096685D4AD4330DCE07F28B7126478361296C1415BBAD0894E34B89F6E7F95791034013E68FF6ECAC1A83601B59779C22128266E8F0AC3FA3BE435552D47ECD3E2E1FA91578125E0A1CA25D79A60468BBF4E16925CE3F8EB73695FE87CC7960D98268AA18C870EC81E5C7E7CCE78F7B3ECEA40E744B1A2579391FB19379345BA7CE3D9BACDC4C581C4490B35D25D3BE96472C3AB5 +68834602A172D4ED4ADA1105BB88BC3D2D013A0DD1AB41212C98A7C051A49A45 +424EFA9AA4AD5806B520C4042D66CF7F4466D9EFDA663E8324EB6871EC1416862B3857780A7B2A40F61031EFB0E9A2DB350FD3976AC3773B7879EE30EEA3F026889D6B0FB873CA8C443FE9D4C9E37E1FB1133AF12AD15EEF2598AFDA2F44686FF88E5755924F63642C084AE393CF1F277444DB8355C5CD56E2120C35444DD14A1524E91FD93CC50CDABA921B1DF09686C76D6F9F20C7485A296C849547E6E3ABE661A2F14AB558B3DE85DFFAA819E2B93AB8E813E8A8228FC14EC424683C168603485B8F66093E1D03ED850AACACF926BC710764D29073C9CFDC2BC74F84BA6DA072DA2563157EEC38790C4A9E8209757CF39278849510B3E52EF51F8DE45DB5 +1CD0844385CAEE9A4A2B4726546E63DEDF458DE4EE29717B18D308E51263A7DB +00B15B3B8F6473E96C3EAA7215182F0758386C2BA5252550099A79B8CE80A3613565353C294DB72FA1699FB3F33A0B7083FDB2E3D953698F717F1F04D4FA12F9864DF4DB65186FB2D494A9B32697F81BE134013BE26D2B03CC7B86F59A0505F1BE2AC986EE1D532CB7DC488EF75A195A938C73DB3B3BE470BC5A30E19B228CF498BE26335C2346D1FA06B83589D80D09E430C5F105D6263AF0093272DA1FAC6D38603AB73C2E051D7A841D3BB44D51B60BA57E125BCBEAC40E72C9FFA15E92DB7EF2C8F03E6943CC3B720E55FD052EEE2C088A63D6157A2F152E7BEE06A407ABBFAB98A5629E8B139682756B7CB52AE8CB7E90F8B7F79FDE63D2A82121789262C5 +2CE8B9E94E14B7ED1A82FBEFCF5A1D16B34147F2C07ABD27DC44EBBBC28300BD +5B4959364C9C06224AB4B79E77110D3ABE30B7011E5374893AFA45C0D044E2B8ADAD31757FCF85DB82186C54DA81A02779270E016DF67CF76859B3243D8ED9E0CFB57175BF9C653DE7591E11D1AAA03D7D5405A1D52A1623961CDBBC3BA2E021165FAC4110E6EB45C36DFBB0814276ABAE67D5ADF061099E31DFC8404EA91C9A41E95FCF67DA1F7A94FF54100B2A92D04A60C674A3EA3F4E2421678236893F3A0A4BB4CE3251F8AC4EFFE4C8EADB30A5587B86A40CD15051EFCBDC3C65EE595A0AC5BEAE0C6E4A45134AABD5B6C761F1169DD4C8535B63EFB2EC142BBE8F761C2B8F6D93B6937B810518257B635C0542587CCB3B5951EFCC224EEA73203FC8FB +3107C1EF891D5DA738F39A0F00944CCEC6F7190749FD36FD9A2C8911B3363874 +2ACE7F4DD6B13C4E5ECCA6300690DEC9811451318D7265BF7B268BC7B01882264114E1A4CA533B416DE49A279879DF795A0802D2F72F85EC9676EFEB4770068243931A7CA251496D87F14CAD9E3D8EE25C7209A65341B0773AFB8DEA7DD5F387C076DC9616D6AB36C790AFAB567A759C149D22E0BE7F22B090E70AA615F36DCFFE3791FEC8A35762A9A10D20BDD563943405EA7B2D9A61712E2BF9D13DF1FB9331202ADBB30F74201371D81AB56D6F2BE57B73FE9D1CEA7933E962FE0D73512576299E938944449A3D429FDB4B506A0E28A1ABB6B5ABE75BD3FA6345319BF53251A9BF366E1A23FFB9A8BBA5B70CD35322E7F15350B26E595CFA9B2518D2FE55 +26AF3E3A0BE10113B44AF25B16B2C0F08F50335B02DB73024DD39968BA57B74A +009CD5C488A9DCEE89783B0528CADADF3570FEE1E29B6FCE1D76FDFFC6FA48290A586ECC68650F472066063FEE9B62BE7D4BEB2DFC47E816FC9E630CE085327C2CDFC120DC8B5E84206C7F570DEEC4D2DF1F7C85C56291C082B233DEECA45C5FE2C67D2D2B24199ABEF7649728DA9F9CBD55D07E7BCF48A37481EA5FDCF02A8CDDF98FE16268E5FD2052A272F17EF0224C5E2F14C008B54601B8392FCFD6A3A559B04D19628645E06664776A7150C78B2634CEA57F643962F4B9F59A5D40EAD9CF68B79E81D046EF1DD057490E74F918620DEC964DC98225A9424498AABF3F95458F0733EC5787F269D7045CF327882D06E82A0E295D6BA3DFBC9A1B14488B2AB6 +28CA697C5C9B4A6A835E56DDE55617E517A6EDF50BCDD6FEE81C644682B9EECD +774C86E7AA95CB00CCBF1268C2580352C1631609092487C1EC5550D5B3E09EB50AE1B34C836577D2D31D48C6DF675797DFD6DC9C2BDE46A9187118F8DCFEDFBB100969265D123BB9203E5DB7B23436315058744BD0B82C8D42016EFB90BEAFF0AD138F1872AD5084635BEE3CE444A60ED14DA4CF3417CC7CE998F765B6F7D1C2A016D82315EA32D63AFC17194D9C72DA57C12DEEA29DFF23C65814B91902F4451727CF33A15CD8773DEC8C62F8A522C5BB9DA76432FAAE77339CDB4216D9BD4E6ADCB777F79101F14043481754774A888B9000C96FF0821BB3B660BA04B759E5F0A8563A50F6502F6A3357425A27F174CA2218DF73E3F382FD3645918E394EC9 +068A5E36B778C2B414F3D8B6DB39CC153C6AF1A0B3F8AA6AB1C6CE192C858854 +34C2F7E49BC952012EC933A648225226EC72CE4436AF8D1E62A4D6D74B38EBAEF396C85D3871D21217D7838BA24648C42CBFBEA4B2441C1F10859CA3867E834DCC6769759C086F81F748670B372FFBBD7613B022680899EBF4A6A2DCD473B54586BF7842BA9CF9C60877718476293DF8BFED2D79C70E5812250E841F3B47884C8A096EFEC8F240802259AA39E9C693B501DCA0C555CD0DFA6B5190A0E734282862AB17CE1A2DB135D3F71142391AA3A5F34EB6F8959C59EDB2C965494004A36010FF537ED284CA2F56EEB04433B9E96F19E8C2DC8556F1A17C9C58070EC73990C0FB14B2C7F7E1AF1B5DF6B254913A93C0C6957E38A9341BBE3F63B5E9E34860 +25C27EF99A5B662280FBFD348789F7C16EB56D8D3F0363F75917228AA5D7B5E0 +2CA95DB2B2DCA874C83B60ED9639D9EB518D194B6A8DF4156975931515B4DA523AF257507C7167E0EE4C6941E21D2C88A1B370C4CE3AB274CADF9DE34E4AA56EA0DEE1C4FCEF1A7818D9948A0C2C55BB7A29ABDE32F8E41EBC38DBC5C8821B5D9D3247079B78DF27BB98B8F570A769075C6216C2B7484DCB63E1E262F2A80B7C29B8B3B45FBBBB05220C80C91D5E832D8451F815E926324BEF6A8F98A338F5A4B32471B88A035D3AD8B08F8A4C4B02804A141234B4D75EB0F3DE4C51D34E3C2A1B31EF0F8BCDA66E33EF3295B24EF7E96C8D2897F7E986F2DC2712CBF9203F25D46715E764F99A65318670456E6B7F3A5F6B18C9AD2B328332CED7D00A2CB45C +106C11F2798C57230E254931E3EF371D195546EEEF5288E2D25460D4E76A71DD +00A169DA1A7BE6AF4F5B01FFBF7E8A185949E34969B9AB420B59AC351D861B0A0A21A07A81578F607B819F75957E81B22D033C4D90D21F664EE1BD25B079EDB930A5AD34405ABFEF63857EB7B2968ECC16BE068ACA1C4066907D763650D903B4A7D8F20311D29F03F36CC2715AB94BDC879793EF0316DCB4EAC2229D0568CDC5123B3A14B27165B7AE807AF08F9BDB44BCD04050CD879F6C623D3C2220EA8C01DC31D3A1BC9FD6CF17348C3DB21120B7557198B08D01279D6A663900667EE943AF15A091246AB9AF495190CFB6B36858678EE444F1C24836BB1B4301950518AF9D7E12044728B6252F2C18FA4498E2E3CA11CFCA33C92A6DC7F37B58611CF5C9D9 +555F034F6B957AAED0955A64291703A9466A094187D093983BB0317DF3A5EE1F +230E554FB214B7B23B68E758AE0BEDF01AB4D6F1C3F8689E24E5311C09B0F810E586561E075DE6677E98A480FAF5FDC0F74958B5C1BDB880C4177587D91520A1C8CF67CF44E72D8119A28714C0F99E48BD9300C3382352C0907B1984F5E70B7AB7489A99903E309629F8B9AFFCA166123475A8E4B79062DDA65900E4E812EB10D331BF662B31B2B11543D122B5B599F969BB0D6EAE8034AEDBC2A2D07BD70FED83EDFFE960C7EB191A446AA65C8DD035F3D926A1CDE15F048B3CE16092AE73A83F08E032C24EFE78F800DB5AF1A0FA2B3553ABFA4E6318932A3DD4B01EC945336897FDFFC7D04E90BF11546637801E17BF9FCE940B852C2B914F20C8AFE27811 +24ABB604076077E9BEEEBB60992C8B48B882C2DA6001939EF055D3DC3FC1B8C2 +008D7DBC5D8EC9DFFD4903271AC7626E726D41115C2317CC8818BCE64EED36B48A289D9BB8D10439BF89DADFF1F7EDF8267D67162C6622C5D0F38BD038BCD1AFB46FC59EB50EE05C674109578A2B7254E44C8C48879C6F9B828930A1D473059FC27E88736F4B68AFC7A5620CD7001E93D0569DC93EC39D189A96EF8F1A8E67A3AA9230D1D01C584524334B18F67FFA4F13ED215277E13953E6ACEFD53A5E74FE618CBDC80A1621C10C995A6C49FDF1C0513FCF1194629EAC19FB01A01B2F7A45934E4EE225BB3E4F9E2133567A05B55EA2A394A2F7B978563A614A409D0F3A10BDDFCAE34A3128DF1D20475DFA9590244C9A283B1B4B550AF4DCB0A46B25151C65 +6D1234EB897B7372821F89F30F83C685165A929969442D686F6E8B98068788C6 +17BF61F407372D463AC98C2611AAA3EB1294502B05AECD8217BE530168A57C5A90735CB6D7A0D434C41A4FFA7F731D03F7C662D7BBB6AED38CB1694419DE16DA914D53D0B90E759AE0E705D0B2AE2E18D0A0B435CDB6A92E2FBE21AF6DE3FF5CACC4E14B9F3D44CD23C2CE2422914FC30496B995F90207AD10672FDF49F1A2FC8A560063B6D5DD7950917DC6F97C2847308E408E335B60129783B0C7697E9D656D26209736879BFFF9935CFF0846300B4B21271724A42304B146B89395D9F3FE0B783ACC6073CA41FE8F58B9A2FF29D04FA2773D5C6385284944406422A775A4804073FA8BE225FFC3CD925D61CB2E364149D553737A29C2F60042E90D84641D +0DDFF0179108193751AFBFE30237DC2298018455E5538FB74A1FA10F9B8AFBDF +6F19EA484C9CA73711D18C210B7F835BEAB5D695AC550508AC74F5322B81E6656AD56836B146E09CEDF0C3658F1A4A0F6303D8C41286F790B26AF05921B3A79F7A826B4BAC3BEAB473AD6EB7A8A91D2405F6326C2B3E92EB8826A5EA4CFF72C19A082E199E7C71755E9B23E3D2942444A8D4EC1AE5CA70BC96B5279F30BDB42C67FD0017E8077FF31F916D712F9025BA7A3E8AEDE78FF2FC16C66B68CD965E3F815E8E480E6037DB361A00145BFFC1F40003E80A76E5704993B75E8C579B55794F52EDD1E3A7BCB05D8C0260F8182C79CDBC43906B1593D856456C6C6FB4F499FD693D0DAFBAB254C32BA7C1E4942017723DF04E91CCC8CEC06C71512634E369 +2E1E164EEB8899B96B06512396087264217B1EFD55B9B6ECE559B15E9506E20A +21414DC9C37BF55653617A02103F23CCAF311EDAF6F2AB90F7F4D21779D6719DDDD829F69785DC9BE6414F90CEEEA8A683018951EE269EADE334502EAA6CEDA8DF69E4041301526C7C95B12DEA2BC0392958BE45791141F5297B71EEFACAE67F2BA6379451A650EC6F1F3157FC0598E3269176EA797D27ADCF0BECFF8822F42A643D9573DFEF93531A5D3951C3F7F3CD8B79B05FAE4F1C45EEE1EE8A56320C64E58ABA951A5324E4CC55C2F49EEDA15A14DB690A6DB54D8CB65616B9AA9B3A9F0585DFAFDEA80BBFE02F784CB1465618D9F7F6306DB5F33AB54DB60F68BDD5240A9DF214FBD3A94380B2799DF00C87C139275AADDA4B622E739D45F278D425CB +7DBA070CF7D20A9F69A80ACA773652EF2FD8900A42E6470A9FE1CBEDEE3864C8 +23F384D7DAC587ABC8BA76E0645A3CBF901562C842F0CBDFAB5DEAC729EC8C61BB1687FC34573B2429DEABBE589EE52CB3E8D8EAACEA660A9D9043DF703E7033E635C3A4EE2A412BE07FA8ABAB903B458429AAEBA415417952417C00D5335E0B4F64CE6CB613EDE7A82C2754764D535E9F1E0166685E3BE0867EED959859C7FB871EE7FD915409183880B9776239543096EFA3C2538AB613AC623E18C0C881164C56C272299DF68ECFDB38197EF5A744BE16137773D6EA6460C83913C383C4D169F0EDA740CF47EEAF01E7869A48B7FE2E506FD8CCAED9C342929D8F9C89858BBF9D9390E9B7B3DA617907FDF76A22B620739BD783AF0439F18E8786BCD81288 +6E513A419B23C436B6FFF5966EBFDF6F708B8E9B97062EF42A78804502602F53 +008037A21660C2F46B524FC12B158ED70361B2FDA7C7A2D598A76CD7466827E89B992592A9E421A4F976ABE7362D8C7F543F2F5106C91830E9066002534B703C746A45B3106162C041C9FB7C093F6032DA09C5286E4799CF9937F595282BFEE5362FFA687A1DC749FB79630914FF05151F3F82F3DFAEB32A84B3BDC4B733221AB4AC88CA50D5B22DCE4B64CC1572C7C71049C592390A480E4907A023A86DDDF40F9A7FB361F90E346DFFE854639AA99E9AC1719D5F557F7526CE60171FA3AECF9762B0A628D7E309DECEB48F27A559EAD8C58D9B0F4C22D174109469BE86F11AD1DA4D3F06E86412BD1EBA690D23E2DAD92714FECAF00278292D7A627AA79FC0BC +531A752E5B5163A084C5989C72603F7EB3FE24D8336B86B4E7D63AF61EFCFBE5 +58A3A014B9F28E227070944B21A0B34A20D7E42534383822D522F115F36A002228B42F649FAB3CD747AFEA8C3A57DAB131D25DB0C4BFD1B2EB4C31FB170773F57A0B36A35520C6F42BBCA2321ECA20D5D95B3485A5FA0840C9AF2A5F0CCCC985CD1E3DB7FC19B839987FFB797D2FA761425FEDFDDC4797DB8287260554948FD6D00C394CF9993D73AB746416E851D51D5A9C1F7F05294D69C7292638A54840FB10B44BF828606E21DCE523C9F3814BC3B3176DD1D898D36A2E1714FEF0E2A50EABE583CAC48078AADE5E26447A5F17897BAE94CF29BB4C2A4719E4B49BA3BDB449580B27A151D96384BD031A5C709A35B035561613DCE13303C0879FF41FFBC1 +51DF1BDDBCB2632CFA84E45A1F4D502823FC2526F88ED9EC3B9EB48E7E685443 +34D1A43ED55661911CBE943F3B3D0B20DB438282EFCB2F6CAA79DB59EF7FC00D55599A56AFA7E409CFE7B4B6F75048B6D37772D43E405317D06CDB43F1F3E356F47B210E9A61E7CF87DF3D6E9B430B7B4C21E497CDFA637F8C92AB78DEAAB9647D1B31F95BEA059CF0347B1541A15A9A3D3227DA57AE5AA76C64BBF3D1136F26D58DF1E630841A67CC73A3DD4658B23C554EEDA056FD98BC394AAC1C934245CA2196598981FBAF03DF7D536DE23808D44916C88BADBE6818DE58A0E573648E7844965FE8ED60551B5B82D0A55C114718B6BE4509AB8FF0888150FFF32D7C527A7A65D94C91D3783F295AE0F90CCB4123CF8FB47B196281D2DB0CFFB648F71FA2 +39EBDEED932B3C35A4CF8F2E43C7E0598543A679D5A300C929083900B9D9C166 +6FBA5C2CAD26A314E60A4FDD4788522356CCA2C732B39D092A9BBD6A933FF9E7A9636F0F3A7628C0BEEC52C6A24B3813B90D97F59DFE41EBE88FC2037CA7A7253682D75D0C7EDA9B32001C7764F56188E3AB20BDC58869922C70394C1402D6A45881B1E998096B61E8587358ACA7E4C3553AD2976042AB6EE4E56F24A206F44535496A9CFC541B2331689C4008F6332D75DD8C506B449FA654EDEEA90DC38964FCA1771FFB3D232294372B7C6E10A9B41002D623A8CEF5553E743BDFFF1F51DF30DFD909B165539729F7AE8C560582B43D1B66ABA23B29BF3BF4C300D942F626C9CAF78B5FDB652A29E953670B4CA9C75C398786E7CC0FF41BFF1B840E4C5293 +260004F0D792033B14EEA5A4C28EEAD2C3F04F488CE4C51674F23A569048E733 +61D9F43290B5E02016CCE1F1062ACA00100AAC3EE0592AF513BB0DB250D7DD0F5477CD1557C433D0EC1F1B3BECBF10440295F5571101274CF1D072AA8DB6BD81E1F82C13CCF4852EE59655A8E41EE1A2D99432FA07CD1D715178653E8F9023E97F92046F3A51652825788B15EBB6AE18789FDA7BB5E21DBBEB5560207FAD7F2DFE702C5D74A9EEB69D2808A0D711898E8510DC7E576E9CE3BFD0595A8C77FC00A1B35A96F341651DB843A0AD7EE85BB03598722839245FD71BD7A5FD6B606C6DEF159B2777B8E3610E94CA5E7CB88A649A34EB3535A3F7B0EB56D64A5DD5EC540A2719AA28636F401F5A9F85CEB7F240441061862596FA8A70B09106446D51EA +5BB45CC5B398D9D92B82B16D6FCF4E426F259C90E2CFEC935C0FE3AD09DA8079 +36786313F03AE37AD628FDC5C1C538ADB9CD49B0F932E5BF92D164B70AF5FCC4269EAFE327D5FFA102242ABD8188CD905BBBD6BD668C55AEDC7482EC40FB1AC333CDBAD16F9DFD1F11E969E642700455CBC9B1CDB1B300A3A0B58EC53DD2732C923B8C7353B247058746B0EE7935AF408DE54C8CF1EFD181307C0DBAAAC7F1C77386E96EAA29CBBEE46247307399C04289E12CAF364EF54E1F8C2E619F4993786842DF02871EBE233CEBC133EBC00C10C36E643D7B62B39E1A4444891B312E2A4415C6336ED76599B2B4FBEDA5EFD7D241EF393CAAB02612B4ADF6D5BCFF54793FA9E5E23CE9D0082EF17922E1880689FEB5D1977D7A115920C81B1A5BEAE93F +2EE48D74494C794E5D3032CF0F220C1D4C6595A6B0C3FB8A13A87E6E2598CFA3 +00AB287098F1380B122F47541149302D97DF605C1699A4990DC776073F5E204A83AB286DBF578E3FB0C6F94FA0288EB2DB82327A427FA3A281FE4655BEEEEF1D2305F0524113A4102AAB4AC629DA8BB13DD2B9409A34EE6BB8029BDCF1F97A35AD81476C1E8259DB11E2E81CF6741FF124D4425B8065FE1D4468EF1C09CC00C737468FDAB8C86045BD91FC1F138ACF7E5C46FDA3F7864A2D27D6393C02F6492EA6DFA1575EED2907E57119E12DDA20EE81F2A2EC157ECBC4C44E7786ADFAF70F71CF1F901989FC8D14A5DFD4F1580DC44BB3EE81B44CC9F1BCEFE2E90E892055EA7BE080F1823A79E51864CF73F24A16831BB19173598A2460A9C22C553A62117F +3695068AC8DB7CB06B6527825DE233DEA532ED9E5628362661BC2C95FE118C01 +29BC04C800F1BE0B3AFFAEBC9AC22F95E73F1D06CB4067C54443A6A3E1A08F1E253AF2B1F59A94707AF05D1B17D5DB1F92066918EA35CF3395097878D38F62C235AE4316C0E1089A3828FD9F2337AEA58650EF5B120623F7AF81C97A717E8209611A14A77784669169B958600B89D19AB8D499ACEF7C8548B69BF1CDECF3201CE6130E5D3B6769A1EFC83DD604CA9E8F783AFA50091BA82D2E5D5EAA17BAAE6D436C84292DFDBF04CEED875FED00123AF16CC3C41B6BCF52220F3D887CC3F189CF5A2AAD39954BE3ED9E7AB73EB8D428372385819CD4219888F9E06E61932E6E2B754ED59DE2C5A65A54BA53E5242AC1BD01431CEF6B1652BE3005B6E1B60C87 +6CA32CCF9F01501710059B35543679F3F90676521B8D05CB5E3E42AF106E6EE7 +00B8EA1E5034FA2FA4A1A1B2419D6FF59BA34718A66495938CEE044BC83A4625CA1DD0A728FA2354862129FF6088657860A369D64CA4E0A05FE3D643F53B55AA1118A2452643E866224F2DF6628D591FC05BC6692E1B3A64C5E5AEB1D8D0298E1CA9068BAE3206797836968CEA14070381846C01D1720E53B6E2CF7C2E24B460E70C70E33E1199ED4F132D5EB5F1B307739B2E08A50E71291F3B0AD5C5C941B572D654C6B2AF2E3527631C5C29231CBEED46A2DDE296B8261E6FEE06B9533A4527696AC8E502216F018AAA8FD0F80280EE89D462AE82176D185072D60D84B832279DC891ED290510C609A31E0F497D797FC0060BE37236A7B23DF898370C320FCD +0DED97C9B0E085CA625031D38AA0044D56E1A1436EFA93EB756DF22A92C8E9B3 +00B166277417C5AB7BE06D3776FF72431FCD7AACF0248BD6FC0D93498DC080668B05B92608B0BB2CD520ABFC27A62E843B7CABA2CF1FD8E39DC4F7682607CE672B49B465286737EDBAAD5E92B45FCEA95A4DC02283275C8D2F89410EB2FE68288C3CDFFC2C9F8A3B40D93AFB8428C5C25FFE04E7C30B8B27E59D8A14E35133D37B3C23EC5603838B2E151D435E7CED4FAF7D9233FE37BDF866C51F2F80FC2FB144CA1EBFB206572A0F1DF6A3D49C02CD737FA126882DA3C6438E5DB481B8B34B69AE673D52222FF02532D4466069FE85C0977B4C30BA0D41BC040883A3CC20BB7DD909CBDFC2CD96F1CBFEB53B6873C0BCA2F20618369CD4574734292F61511763 +73C945B92A4E0F2D0A6135F97D9E22FB59CDB21039AEDD8D53DE5A0F1F9C1E99 +6FB8116E759050A6D3A2497093EA709A5FFE0DDE4EC51B6C11B1FD5A522AE03346782D4204EC9C379DA67F3EE4C8B30DA6613B5434D754DE1F747A3B6179AA95D012D6DC3F786A634900AEC03018446A2459B22B82F5537955EDADF1C562A973FDE4B2B2448CC982D2550AA45D83AF6F50958B244EA8070E066DF0759FEBC04634A55C950C8EAB0B54EFADFE136E974AC9A3AEFF7F102207FB3BE7AB4B9D410613773D2188DF5B488CBB08F7CED19B393694F89461E9714FA344B83F8F8B47198D76A015568CAA00F67C54E77E59A6A452B2CEDD9E0BDC3E13E5AA6FF2086F5A7FE292DB682E6B3FE08985406F031F34C2DCE7996EB62F720777B634395B4372 +4252448035D9A0B49221EE620AAD573DDEBA61DB02392D1A2A6DDA37E7DFFB0E +0083B89BE47D26C64C9436A997B0103594BCA16BB5F799E03850CC8556FEDAC951226A6D134E495D9E2483606C9CD13E14AC3D743982B884EA0CBA4D6A48640DBBA405B5132AF6D60333A97FDE8A0CC25D47BF5C4422BB86B150CD52D21FC9F31A89DB3C78B13D6FCA8124C1D0398C949ADAFDD5044EFA001CC0690823C7E7C524DBAB243535394261B40B787FAA22FBF9B6A98D1F8D31F7CAF47240EFECC00C06C6CC5908785245FD0874ED9A5F7C493A689820D8C7686BD34DC42B8EA89F4A1D447B723C8DA2BFCD7ECA736712CC7BE47A656ABE3E5038C80428AABCC54985C1A31E6CC895F5F44FA769EB197DDB2E4272FED63295CEBD04B645D243E5FB8367 +20C8542FE68EF95B6F2469D0F10D7C497673AB60430E4160A745DBB09C790750 +30B0D0DD2222051E8B73F38E0373AC778A499827258BA0F1D7696CC42A4929E96A95B218C20EED53108C3B3CEE62270CEAD1F436ADD02905E3BF2D17BF73534FE9D8B47AC8202E853039524CC91F1F86EE3FE18B8CAE2C5C2406A7C8D1A1967EACE1D355CCF980FC9F063A6312E709114623BE5FA83A42E7F6BD8445250CD0CA0F50DA98B6BA44805B52FBEF8820B947903B8986BCA62322259EE2A1DCB767802BC5313FA2F73A30E2CC05B8E32C9871F6F6087B81E064A522FCF110E8528E596937DC09EA4FBC082E4FE5DE04155492535F9FD300C13B05E8CFFA2AD56B62273C54CAEBB8077B62E3AE3F5B526A742E3B33485B1679563FA1B8F20A3A4173C1 +28A6AE32371CADF0D3F94F33ADC96F746A44C771DF8CF35D69C20EEFCAA2DD7F +031762294E7A79F4B68D2670019229876AB910701946AA2B0FDF00ADC423CCA80D549921F79C30C6894E87DEDBB1929D2CCBC30C144DD9D543F80F960660041991C4AD741FCF9FD101BCC452C5B0FBB4ABA00E2B4437F703B66E0D38ABD0894516555FDA535E7C9AD00406FD1FE051377DFDBA6CBF0066A40E2E12767A587BB08EC35380094243E5B567AA6F5850499DC57FFFD347E629C8B6CB464EE681EAAE027E9174FF9235A4AEF32664C9E23AC2C221DDD2F0E0F03E22AD1A5E405D9C946A6BCBBF1AA25071F0BAC7F8385F801BDF6C59CFDDE29EC1581BE002E545B488DFC2751FF5FBBFCD7354FA4F1374ECADAAB2F8F2D271F9766CC3228067146921 +491CD8E9F14CEEA4EF6F9A63670624098E9665423DC75ED148A0F2336F7F58AC +00929F2EED4CE878FA1C08E8C56748A7366DEE08EC4044887509A17846372592ACE724854C38CE59B9F5AC8AAFB5D0EB8BEFE6EEC7AE321BA90098493F1884C5552765819B9C42D08B106A64A9DB2610F0D6457024FB4A94F2C7EDFE0B8122CE2966B84FE37D035C7BDB392DD0708660E9EA981C4AD9B5B49F25A24AB74E81EE3171071DCE358E49E15EDC1E7A3F9FAC6462AE76A4510337560712DB570E88B376B428DA9FABD6D08D4A21A52B1CC11431B4CE6E64DE4A5DDF2BD9BB57C209137AEA757D88B7B7B48F11DF5B3132FE894CD545BB307807F7E6565CE0BF1C50411B28F1334E863A8AA26111B14D929B85E4C385CDF8083E0762A52B914442CF57A9 +7E89D32633B29AAB356EA6D82B03E48EBEC44305C2C3E36DCACB7DB57B97CF8B +724D93BBE4536E2E9E0F153BEDD3D7AC5804B19317A1E16B4FBA1993F5767EC06B46440D6F191AA6CDDBD5C5D952464C484F39472BACAF1093082AA27DAFC390D0059F915799504B0CF95F946F97C900519C6706B679938CF1222482E4648732B32CD3172AFBE4F680975F671F2C1AA684477CF5EB5C40722C8D1B26661261C2C5C97254449E2D03A2E01B2F2E5FC38180E42EF8BA068FB18AB5909F4BA9C745759B9D5D38672787616DD94CA4A4C62A32CB0ADB776F3F552B84F0C8FCB5BB1D0DD3561D2571F4C35E2289652CDD2C9BB0068F1DF9BE6F1A0C576D7F4C6680C448E6BF7531F36B166956932B0F507E9A6BAF04170AC15831136061BC9939E309 +239B4C38F142BF1A8204D5CF3E65AF9E3E0A30690C8D9500444EFF557D432C57 +0080E842C3EC69AE4FCE5537E2578BDFDB60F3F687BA4F0FCF2F5BAA32EAE55A862FBAC02D44CBCCDDA6574CB641D42D1ACC4DBCD1CBAA9BBCC4D5188EA7ADD868168B5D38B3E282F3A2D8247A9E47EF5F6F9281867E408F718B8AB5118A290BF42FE1D9D1BCBD4E21D5A13122C6AA62378139F330CA23B68652238477038D9C9B190462372DA158DEA3C780656735314393A14D2F265E6D0C466FC874D75471E4B34745AC82E9D411BCADDE50A9BDE11FDD28D5D693D843EEE01164D0BA6A2282050076A1382FA5B8DA30AAA681A6098246D91D05DE7FF81E8BBF89944C767ED7FE02D656D23FB93F51828A02527DE14C49641629FBF01F44FB416CA005FFDFDB +40F96927A68F3E9A002E47985BA0B516FD6765CA4B44B715457C1765D876ECBF +6F37C59B3693FE1DE1D2AA2DF391F283281E6B413DEF8F157E8FA864A5CCC46C673FB4DCC565331BF71A6FFB8B5DC1DF4BB0DBB9AC266986EC882FC24CD630BE04D2ADCC82C234F56449D840F82F3AF95AD6CECBA40D05FB9CC02EA4ADF0B60EBF9BE7F9BB02DAB77ED3763A7CAFE40A8CBC0A31FAB289A39785251C1C96C0D1D1A773B51258FF9EB23532BF0FFC9F7EC6C4E0488C42A5AD5455A94308EA2DC110E6B954A8CE3E6AA966888A5DE01AD543189BCCDAF06665CD850867BF56F76B208ED7A245EDDC905B0AFC745244D57D55C54AF8C89F84CA75C66D6DE80140A6F2AF98A4A12FFCB0C033A30F1927ED53A8BD25116A42C09E1B123E6E48A59FD8 +3EAA04938C1320D64CAF7E2826AA343263ED761E02472427AA8F5C6576B28283 +008E1DABA6519A17607AD428A2BF0EDBC3A5704609A6C92D5402CA1071821981655E058F80052226754B2D9D4E47B8E9B0D8989D80E7D5327BD18FB2DC42200992C9B153D55D933A68235478F048A096002A67A915FE26E23E092A779217C5E468885FB7A5CB7B5FD30F80027D06B6FE28EF461C39477526FAF10C5EC6F56CAA9A84348F34C01BC59ABECCD25C04B2F819893737D123744A5F84CC563A9F91502F2E3451678F50668839CABA4097001971B72BBA3A4D88ED7F7E9E3D2D56AD6FF0879B06A945E7DE645EFE9F37D7925D85718AD98A1F6C6178C6D08B2BA7FD4344402BF4637A7D4CB549A991495E863938D840FCB5253EE9BF6E0C641382E93BE4 +4AF1A68302C44607377BA67AF649B05399F28A9573FAE4D4820428A05C5EDD0F +4D1EEA51D214DF7EA03CD7BA4D2900C22328BAFD231DAE09D39ACBE33A90DD72CB4A04B3ACA6BC5BE1D7D45CB1C6AC2A55EB310C53A13726E289BA663E0D28D6C33FF6552CE5E91E015612090116C5095887DFF27CE7B096EDFF8AFCBA7BF1F3DC9A0E6F77F1A9FD595D68FF8BFEF87BC6703EB7AF3D7F4296E7E6ABFBDC035D80DF0DE878CBBB5FB162B5C2CF185397AE439DA24B4379810E335F50DF51929AAF91E1E4062BCCFCB1B4A2AF9AFB162902ADB55848E430E291D2D850B942E020C148024F6296ABF3861DD90E6E011A0A1D63449D25E703D93F86FB95EE8F5EE927B55A29E26F11F8DE00A4991F9FBE6B7546BD55C6579A6B269B4BD19765A36B +023A749DFBD5AF824D3A94A45623986E7FB6CB6078666F111F2C5F03CB4ED0A2 +50A46C67ABB97A78344B24E8D46FA23E1EC229A4CABF31EDF879A5FCA74BD5F7DFBE1F4EDF488ED6C840DAA2D5DE71B9CD0DCFC921F2D054C39433D073DA3509F1147D5DACC873941D9474AD7ECD1EE2620351E689CE08801E6042A53ABEB4C680060CA48C909E0D1238D6B082EB3C85892FF8308911C21073EB7162606B017C223F796810C0A2F0A5B7F76D84BE2C71C571BCA4E97DEC5C32DA575B75D19E286B4CB4B21EBE40050C6DD21C4AEDDA13684203646DC9AD2E38A9AFC7ADF8858A1DE7131C0FA89757D24D83776B2880514A24EB4C82045FEFA9483BD7543A5EB1B94BEA35E33FE8B0F34EDF7AEA833AB6CF203626C63374AACDA0B0BE42B2FBC3 +1DAD719C7F114329B833A7D43D82FC8CC242962777B27D4C7A5F10A98372C9AA +6A731066C60F032CC59EE6B212C335F5AE33DB2C0DABF39EF06B9BF3FEFC1AADA0FC682D10CDF6C8946CE1E942052C7571716B7B244C3BF8C70039BD6D9079A28AEBAA5F493BCC7A01EA4DF1B4A9AB9A37B87F6E8F42B69E432F2C2110AB8E2E968C91180C405FEF91B1FE267BB83B7BF640B04E6877F7FB22ADBA4FA219B8534F1AEB086733016C7D33C76E0F57F2C28D038436B4429B607CA55D465CBB903348137C143114CDD4F783C7B94FB224C60DB1199105D558CC779ACF2F4CF01AADF5FC9F5E5DEC219F13DF2BDFCA4158FA559AA2E2DC84FEB6F38787E6B45B059BA05D3BFD55495C4C4050DFDE795B43F95B4E36831A64B073BFB2A19856B0A8B2 +620E159C18AE535FDB3CE8922C92EF696A44D8AE350DD5E6892C1165C14B2F7F +6063C720BD5DA9EBF8DAB021CDA97227778BD4ED95DCE402A298DCE9CD2D943F0AA8F28C0E0465D361BBF2F436835DBAF88419B88DD007B8C8C9593B2FAFE9B29898EF7163709E2EA49B29CB1EECDF7F1758BD77E96D3ED44CE6237717DD88B7FF8E91D7D18361273F63710535AD68A4997A546A9E171DE1F98036F311BDB7340811321E9D227A53CF780B23829D606B7A2CF486CB07337FF6C883FBE21882DB81CD003669318112D224DD0A18F70F7937523865BE47A9C2F25848526836C46ABFA8B9AE0A039E4A226F45C50828903E04C303BFDC8E4ABE2C4C2A990094C77BCF28AB3A255F2F4A14BF031A6618CF582FC86F74A6F4E84689CBE47288344744 +2E21B24D346ED0CC2F39BE2EC4F9EFA4F9AB2F6963AAA437C08DB32E23E9E4DE +00B6E59C42C42F5EB0C22D561682CD3FD2AB8CCF4F9C7F90280A8FFE252768FAADA25B9CCBE1B21DEE0AC2EDC9AC4FB8C492DF042729C360EDA6703594689861DECEE84E46A4B06160F939C600EB8202C013AB5602987E01D4F894BEA148490E1E43A09D39C64BDEF8220CBD05A18A815158EAE3E4EDE160BA8511075AB8A7BC67A85D0C399D2B1A45AC9A6607DD4FD2C67C415293ABC484C7C72CC46576B4F1BE257BFF7E062B43E12EBD142F1F6BFC7D0B0523A4DA84CEF4BCDC21F85876FCEC3216C25F140A6272D95137A66DB7EEDB132C4F7F9AD9F631E05C5B0B83B5642CBE7D7B31A41B899C99C1D74B67771EBD4C288806952AB90A3D306ADB05660FDD +61C811C3067A1E1CE8BE46ADB46F533F43FEC4C366F9E42979854A62C905402D +00BA62195DBD215324390D99EE2F2BA8003181BFF925C6E022CDAE4164469B3E7458FF437D584E05AFD7A6F8181674E45E4B13DD07736B36D917F69A7B316D7924D53FA5882B83D4C4940BF61D0671FBDB5A073D2AF944EECFF7AF5431DA131919137CF1974BD707F505D70143A2B7025E5790CD4CB12912CBC4C8C6E2EE5BCFADF1B95D3EADAD5F64BB912ABD7F03D1214F4DCFC309A502396FC5A027C59C52488CE0E17E2261A2C84ABD69B2893E41CAF3373DE80137D8469A32A733EA53DC67D5A790839A703EB8B679326694C46A0B47B7724D417F093314DCB2407445E34B623783330A9F0F2F6A0029A2F116CF02D5C01337343B2D4D65D3A81F5ED0B805 +476ED7188335CB925B1C9326AAEB861C39D5785A2BF5EDDE16A82AF4AB077560 +0081DD483FF1D7C6EC4C5D50075AF09765338E6C5FF5E9320A3614B8F919C9517E4781111DDAF3C41A31B70238CEB7FD29769A907961D0215B76F5D2A4C43EC4D3F1A6DCD05C6CD1D6D05F6106B2E0360612103991D0ADD82B7D028B236E513C753494AC3027FC71AA384538459943A914C7B60A4F3A620A1418F58618FFA6AF799F280CE0B95B67A816681E58E3F9894ED82C9768A481F05B037B343F712EA9AC5B8C09FD627CB690C05520C18401EA2BDB7453F636CD189A4F019149A685CEB784DAB3DFC1AD0C1F507212D6E43D064476213817B693FD492C6A56FD180E5B70476FFAF06B84E100F2F7F98E45BA21955A03D03A6F72322754C56314A8387EC8 +1F5A8BC4AF31099CD469BCDE5CA7F193C7809A5B534DE27C4CEF2A0713981E1F +28AC193968D0FBBA23D55D3B4428D8B9D42B8B75B105E6C539D17970A2CBC02A3E40AFA96CBF05E2CA5964FA8C1CEE0588063356483EA8AF6F0B8D6952A3C8DB25C9A6F56948F4B408616D4EDCE81B1D78983DC5FDF1AE37F858B3DEBDA1DDB82B7056556FB39CAEF7B5F2D679471D07CB8D54A7D446A1E1E14CE367F554C50A237A67A150A83A08B604C39C45FEB425A114D99855DC651DFD85E28E390BED99C21ED70BB68554B0640431AD8C974052CA9F809D28E006C5256D4DE3A9096AF7C0DD252B6AE899FDE7A203C607468F62A8BB3D57A1762DE76FD11862BC78112F7E8A8A399E75AB41BD93BDF6FB754E26402E160F160CE5D1BF98922B2C4D1641 +4719AE11DA7F29A2EDCF7AEC730AFD2A3C53AFD40699FB47A6F1F03234E5F75B +4CAA5DE339FF1223B306CC56EC1B37295B6CDF8DCEA2AFE6F1A56F46AF698ADC23178EFD31A42D3C56B2880C4712571DA260C1B2DD8D1F927782535D4C615C3FF1D31E5DD8065CB9B35D1884B6F0D45C61FDF11739EAC38136480491935C14915F496B4CD6EC0A47B39DE0E24D2312BEB2563E0AC5D04AC2E9055CC5DC7E516641989C60A4F8DDC03D08D38D2F4FDEF0DB82D32B71A96D6D17B82C4975B6EA696513DE8A97CAC278A2346E291AE871DF57DD242CF67C7D98D19EB3DEFD7D97AEEF298556D85E459C34A14245D6083B2C13E5D4B9246C45009DE5897CA47A72F3F83C90384956F5F0831120CF242E601BE12F37DF42380597A98B39FDFD936DF1 +2135485AFAE77F200A55619D3407216F7BB07D7D4AF64DB3D58EFE7573D18005 +009C2BB4F0A124E747650C3E0FBF4D32F78D74AC79BD18300641C3D3D9BF281AD7EC6B91206BFC28DD01260A5657795BAA9E040B87AD1458C4D4098617FA302544630CA35EA85BCFC219433CB1009E41DC899F6B9D6DB49894682E7778043018C9DA6534F64BE1D1CAC34D0521F8D453EF4720BB2EC75BB83A1E4B07243F2648E5121AD928C9F0F5E70B3C34338F5686BD03F6914E49CA2332809DCF9E7AEC802DAE49D6C89E39491A2C56C9A2E618B9F0A4C41439BD1333746DF75BB1DCC5F3A932BB6BCC856A848FC2C31D545C95E9BD59C6C11CFE933E8FE779A018E8135284A40E47FA470286E68DE40ACCF4126D7DDD37A50E3E8C3F7D0C934F390CB872E8 +6BB5607134DFA451D3B69A0618F83EC8D93333032855EA91849BD522B43ABE53 +00A37DFE26E449E524B362EEF13B18647CBEBC9DB457DB850F539A7D1552B64D939F078DF932FD831418E74F99EF4ED65F3BCDC71CE08BEC5492D7BCDD731D46F5792A0A767B3B6A0128B97C8779C344E9451A6FA09CD8F34A3094264836A2CE35025B0ECB2D0842D79AA85B1D4B0D0FF6193663ED7637AED368137C1F22B3D8A9731E149AEDBBB5AD31BAB824852CA345DE7B4599625BE2133BBC2104C6A2D95A847C9B48AEC8E525230AE1D7DF5DE7FAA0E64C03483FA53341EE1A3BA7E174F913ED1637CD416E7D0FF53B004B980C021887ECB6D8A620560516898A3CD164898252DC800C93AD15FB66D3BF639A552D227D483F8902E6544F3E231D125FCAC1 +1A4E21986DB75600D2503B63122AB131CCFF155FABFBD101EC2D6C1858C7828C +3D0EFB7CCABFCD06816097EB836FF1D06DD94942C517CA5052F81F7F18BB9580E8552EAFA560C27AB4E9420F0F0EFE5954190C81F939772B0F1D73B05A00F5E004CE3EEE7DD1D3DD86B87859A84848A8DC579495983B6C106776488BA9944D02CC58D70932427C5CB88D0F93DA9261C5CEFAE3E4FBE78F6D7FF2FD074A6E91782561524D0DFD784BA425CAC2047AEAA1C50EEB1490CDEA07A451F67A2A100BCBE092424106DDEB89E7FDFDE0659727392CD8F04BB42D268D588A29637A9E105FBA48C49DA1294FA85415C9FC75FFC7D19AA537B2E131DCF745A7A8539057417DF0D9EA61639554E09C520268D6E9F5203CE067BCA216E31B1C838DF3C6C9EA23 +21E246F3977F5B1ADD04F8460FADE15AA5F91E7513A76EF7F769F0551CF7885B +00ADBA64212D126AF1DDEFBA55CD8AF5DA18D06E7B3688F6BC693D0CD05FBB2CAF4EF832E9B8ABBE752BCC3C6F731ED875A08A96980215EA675A8793A9EE4CEB6CB1E3819ED8D60C328434A10E1C4F7BCFDB4F96C09706EBB6C2A2828CABC89A2D7BA03DFE836D8E7CCFFBFD523581BF69331C3CA81DDBAC2DF8B2FDB05FBF0CD2AF70533DA583806D7318944EDE79DC0FE78178C12F89F8857CE75B7FF48B7FFD1768A935A69EA5F18E9E8321BB18A19EDD88B13B7FE665E264F7BDD1DA22D2AFA35966A6DC9F2FBA95817C813E1BC1C0A0974BA233F090109CE9D64185B701790C5E27C5E0391F83B8E1950898B3BD0F0F9D1E582DAE551B51C27D826B5E31F8 +16F19B2F68D22B22B95EEF6A1C18192AA062B37D71179FC990EC5C5716E1D335 +00B442A3BD2D983BFFCB06D9DD5A844C67640F8FE6B86D9E19030352C11EC4B36326769B52EDACB3BD6CECEE0C1EF5D0780D27C3777E5E9F144469235CA2094EB5E9F302FDE214AAD65943DDDE12F6B94BDFC682206B25FDB392B46B50F2CCC8AE65E33224266FD49BAB1D961AF87E05F760362DA6062F228A76F7D9085AEB4E8A7A9E2DC1C34013D65B9A5494528B65DDE114C06C31B541D7F66E2E7A18EF2F8B4E08761B64815E3A71E4ECA4B0140DFD849655C7A026EB4933E55FE1C58DB6524F00060FC038D7A39215A554C0C5ED2718F618587264802A00FC3E56F1D6303AC3FDD4C0F3DFF240CE16116087386FD4C2763E52C0913A9089BE5D79508F0F80 +540B3A49A1F0E2FBA0A2A8B72A88A2D560C3EC3A31BA59C323A34708F9F301BB +00B495A1A1F94D611550532C20142E0AE889113E7AEC0F86507DC5F528060018F4A44537134E5539F495FB7783A051377740E3A118416D2CBDA0A480AE31BB7F1F2A109C2B65FD20FFCDC2E2AC594ED593EE7CC3D27AE3AD6B603B7853324E9911AAB4C0FAEE840BF9CA5330C2AB5722F8E09E6FB9626D34C88F9BCF31E229FE07F79F4D3D4CD3BED6FD07B0724D217276D5E63BCB7E172FB02E1054A144F5234FBBAAE067D05010312277B3A65EF75D9AC17D26F2E9FA4B3A0A9281AEC9F5ECF5007C8A501F5582215C5264711CB2FB03504EFAE0ACA1009DD653F9C8AAF629DDEE6A921A0FCD69146A470BF80DE8B0C1586CD9362C0FBCB8A8A6BA147129FBA0 +30A19EFE872480C49836D98AE1A496BA6A31CE6687840AB91C8AC5B5C173C53E +4A0F250DBD24575214E06D06A2B8AC6EAE15AC6CDAA5A069CAB6669A8AB7AE11156A45655A6029A5DBC0D4D94ADF221F41E45040BCB1E452A2771FA5DFBC38411FAFE64C9DBEFE506A1F21A81EE8555EB866175C73CF40D8335F5A8785A3A14794B3181A40751737B8A0D66754F2F4DF79D9B1E9375A71C9F3E12EF1CF77712FDBFF4A970FF2650691AE2B817817796D1AB01506768F5E593E4C7F73535CED76324FFAE71837BFB5F00671D42EDE5CBAC29925F903057BA532593E9D6E7F3554B4862EC020C768EA4B9308ECFDB841DDB7E04DDF2E6EC6128A69271C38124729C578B7B6C4B24B5D29A6DABB621342B0980ED2CF48FC202AD83718FFBAF66A9D +414CB014BCF251EE39A3BF05E93DD6C0778517CC525D4730CFC7D40A2F29B500 +0088C588DF858717EDBDE4F12A5A62FCCFCDF7179F60B722CB8A9F68FA12E74D591B853F2F3D81F5573396313BDEDBBF8524214A2D7B6B907E1F5B0AB196A80EB0D976E799859BE95E31AF77A8CEA7D1E84FEDDD18C9E23CF80ECA4D688191D58B65A6FDF870BC9FCF1BC25EB0D129B65795BF91ADFF370F708A0306B3EE4F988441DE8549DEC1A37ACEF5550CAB8F5014E07B660CDB9FFDC57947C1D1ECEB6409D4B62F234259E5EAA1FD9904DED813478743A69EEB9BE7D552696FA3B13D71CDF47B55E3644ECAF9A4D3B363C9146CEE8239CC74CFCA074C87A3CD23EE7DAB8D42C9926BA6296A364B3507C2F13F5E17A0EBD89518A5AF77DF72B5C9B9BD28EE +304EE8558911E271EBDE3C676549DACA99C6D47F8CEE949686D8BA964977DF61 +7A87FA7A067B91377624D4F906568F641696B55A228BCB0F8FB6FCBFBE79F228A8E614BE7E69071D9F1F7BDBA208A105F559F3070A653EE5D4AC9341B6A003575799C79FC122DCF2604218DDD54337C047328AFFA9951A9AD90E71B9E29CDA92B5B60FD6E73D51E266B1D118E0678B710F54A3C8836D4BC09893CFFCF147BFCB3D350948484B1483028BA5A1362AE372F0EDFFEA9AD5BE94AB2F68522E09246ADA4727FECB1A357D72B9820C2AC248FC8449B9088068A4764943BEE25272292BB3CC541DD7CFEC5D2F1D11D3CA308DDAE47478E0A1D87DDADBE778CD6E795DA32F2FBB112860BAF475E99AF697EE8355EEFDC6CD916CEC6EFFE94CA3B2DB58A1 +3105DF7DD95A7F18D1999CD4994C391D734A08CC70B9BC5ABF6A8877DD5EADE9 +259B61D5A1301F3ADBC4EC4FE28F477D3A3ECA57C736C0C3249D99C7CC17F046F6214CD6C53B00D298CF3B199340E0D834ECCEFD7D0CD951B07F5F2127BCFF36015661212B9928A4E91C159618EEFEFB953B42428B06BF61F5E15443A24E4F1C3E6FA44540043B12F69AB3013F1398E5B5BDD63D596C75D80FD950B172C3B3FC6B7AA5F166CD254FAFA92EEAB13EA6175EC3B5A12A3229B3BBE2CF0AD0EBEB082C6EA7E1DB1824022D417E46597766B6175FDB6176A5E23FE2E717036B0DFFCEC982E488047101DC7FFCB51290D500613ADA3294AAFC199A57F843FB779ADC21BD4BB54F0D94D88E4E79D92CC59F5B6A772E352054EA1695CF6253D9E570A1F9 +328886E1A178AA3791C0C83DB8D0482931C7C5E18892691C9315CDF05759B2D2 +437A78E8C13E51A954DB42DCBA0A574ACACC82BC68BBC418F514CD9A98943C92D360B3A0D5A2E84C78FBC005D3E91BDAD56F70D94E23B1B437A324A43A6DC0E1D64BBE16F5BF69804ABEAD39A3988630CE62E36B03BED6588C34238355C3F119A60ABC991A99956A151E450DD7641CD2E0096A4030254F99C2033F8F8A63F95A4CF53EE288FDBDAE4FA1C6BA03A4439B22C22F0071091662C69FAAADD3DA13706A80C546BFF20C2696A5FC3FD599200D8BBD948B02E50A74E4BCAA9EF98D8A83A4AFA09E0ADACD48950D3EF2B31BBE00CC0209043AC31F233B8964AA159E963B9BBBE8C64AE77BA1065CC544DAB5852D9B266B0496B7F5DDB5E07EA465DD93BE +396ABE917E8AAE18F5872D433CC92B0E5AAB8541B432FF85058B49F179F8BA1C +62ED25A128CA2E374BED32360061CC9AE966BB5F91947A9063BDBF72CACEC539183C7A3E25C9D0D0F0F3B02725E135777718CBE6EBCE5CECD9BF9FC3AC66388E0CA4CAFD4CE4677FF2F90F79497B8D2E28FD179E5E5BA7E737E705B1F1C46DF856D0F324A5DC7A29CB10E3B91D58A5EE380452C23C58DB8603C4E4C5B2D5539BC8312664DCED76306400A15002955D38270746DAB0F70C530A536BEC68CDF0C28D6CE88ABCC560C17F79B58C05A881D877EA07C31269C5FFEC773BB0883696D30BF0643398C56CD08B5B6851655DB1070757FCF95A4C122A73A6E252F7DAF80831B6DF9E7C5AE71E0DE974D5B68553E7B592DC55EC37BC6AA486976F867D66F0 +263DC1E7849F6F8CA9027E419EA8F5CB4C8889720D924AF831D6188C32637122 +5DAF666F1DBCD3D4C24BFD67AEF9D55D938773F554B1D80E8272DA05814E4B4B979AC20E2DE54C9400C51AA0452B8ACF3278B54DB424E04519BFBB7FD83F2155B336566FD4F515069A54AEDC86800C1C2BCB1A751B3C69A4F839D067D737D79C598463B36E8BC1CC02251E7C88C013EEF54F2BEC5142BB30AFCEA6EC590251646E2D35889C3A1A975EC4180EC693D80EAC1B6D4FB9F3C2432EA983C5C73C55427A531B5D9BAEAE9B37237BAC6099E88B699BE44C12E41AE84E7F87BD2991DE843096FFBD585DEF62EDB8C43C5B705CD8BDDDE934B09B369288CCA2F3DF45A187CEB5A94588D3A4F2E0A470416AB1525433CF095E526014ED720BCFF2020F252C +72CDA0F6CF1637781E7BAA253B1171101E4F056E55A97403D9F6CC5BE71361C3 +00BDC49DE4D8839A51BF066A286D4032AAA4A5AF4C4090F30C21DD819D861830B62075693712558E9DA93D14AC696997A01BFE5C9237E8E98F94AC051BD941DD3722B6B3DFE48033B733FC91EC18AD82BDC1752690B8D8F833FDCA8D6A1E760DABAC3C9C81EC8FF2B6980D4E0449AD0790F7613F00A16D6A79F29C383147AA543369C058005FD7F23202DDF1DDE8742D016610199E476166FCB339447B918D377FC99696F605A43DD28BEE752D5E470B9C9B8CBADDABA90B3A8D4022D095EA1E642720A1380A7189495B2478C5FF82D5EFAC2299AD92963E66218A2FBDC904079CF4F4076E397FADD57A2D1893DE7453CDA4C43447C5324B50CE965FEFB752C821 +66A4219E3ADDDEB6B5DEAE13ABAD6140E486A499B26ACF1460494AD0B443938C +009E678AD4006D4FB419D973A9C6C71564FCBB3A00FC87DD29186BB6BAD0BDEAD7BC248546BD41EB4E25FD381A1E27574D8103189441D3B7B8A1623BAD5E956A87DE8627C4B5FA1D578436F44CB279CB937A944F7E9436DBE18532B039BEBEE6F5104C4D9487AE982CD7607B160F8E3715E788096451A829FF57707CF84714669589FAFD58076C5DEEA29304EEE16192A510480BB16D0906FFAAB4176209A0E7CC083527DDEB5D88F113C79A56D67220042A0812A28D8CCF888CF9B942597FA7FB5DA5F6803915383E77768318E1EF85F82DC3F2BDD638BBC0812DBE0709FE22AE744FA0C6226ED793AE9E5BF39FBACBCC4B6429E5B5114E16503F21EC8A1415A6 +6B1193CE9518249580CF482A30EC48C8C5A61C90DE73B462520373AD528A5F2D +00A267EBA317DF22AD14E42599339F270F2757B4F8C976537033E45C001B4C7395A6DE93ED198E1D4D7D005F7EBE914D3E88B9A49F8B9DB0BC9724F26A60890D1CB4F9856BAEBDADE4ADE7BFD4CF0831E3AFFDF66F347402B7BDC59DCC0DFDDCE7C1046B3D06AE4E3FF334422AE1EC8C8534660C8384260B7745593004487CC7DD8D219F5C2B50D639FAAEC424188B639AE3240D3ECF2F40EAD26AD3C99DFEED1E717C73F514DE4454676A5870211BC2B4C168A4066743B72AB4DE1ECB5D0590C3154D4766553B419FC5902BA7AFC1DC3AA57A20A8E410AC393A133EF75A852B5923616BB2D08FC875DB16A9B6B76FB9A5F4243D4639323D9BFC686277866DF526 +448D92DD20A51456AFE8095E252E6160C7E7DF9BB6BEC8A1DA819A61C44E66D3 +4C8D6A8CEFE24FE3A76E2FCF3F8CD43DD70559A89E32BD94385613B85D6167D8793D57F3D4E0E1E694B186AE8DBF9C8682C1EA1FBB1CF23F787A3A6C091C651969BF67E29308A6D247ED51BE1383B84CADAE694A7E266532B1C27777FD10AC69751367BF1966B87A9EB8B922A53CA3D0BAE4D749A1AFCC3AF47D7BE3DB7BC653FD4C87C2BAB62509F23AEFABDD8E01C4534BF123FD6FDA24615804ED52E84B63659D49EC0951CB4A3FE755008C7A0D39F7CE783C30A3BDFC39E4E8B8F19E6C21338FB3BD8A5605580706D2283944D43679CF04ACD1340FBF006848BAE3DCCCC979C97EC667A2C24239520B6763672E035A0A59C473B037994AA543775A784684 +69C85D35506D867E486D901B44984EB2F56A366947CE1EF3C1ADA2127509CA79 +435F1E2305FD10D5D4B1902DC7E434D08A43EB2B2DB595F8D49E08CFA2FE3C2942F00EA73CF9303DD98614A6B62FF5880EF6EFFD804DAD4AEEA92C138CB84F607FE3B5F439793D0BF11A594367506A311E595EB063052E026AAA2F708559B82EF717E3DD24941C1A4B5DE8D12761F51F53CA0D946B6CF08EF81290425E95DDB6B0A87EBB7AF040951705E85C2698DD1F43BBBE3702CA8343D326754AA7E3EF05AF9EA1BFD176E47F556A570787B1596B5B31E1CC5B17F929DC717B033995C35FCE9C0CC40F5D6CB8DB8020ACB932F7D08CFFAF72752DBE3C3A06B34ED5FCCB1613FF3B34A43F47C938052153ADAFD76B4292B48F61E7F4EE8AA2ABA1FBEE4544 +2F8CD5CADC80FB9561C0DD93CDE8F2EA21F7D84CC0D33679631DC4F844867C18 +78DD1B1F34CC3FA4AB370798AC3850BB4D26D77C622DA240F02E036013B731647BA446D61D3AB55B861FAC8452A95E0B08C79F6A781679D63122823DB151E168DC7590E6A41AF80B815E514289E85E78D5C58A9D65879FD2FA67497098D9F42DAC86C1E5B336F072E59AB8CF83858618731C31F29731311158BF6E2BCC49DDD4F316FD03F8FC66E85ABAE7D82678EB1882A9FFB27673C41A8826BB704CD78D81AE7848E933B835254E60B8D1B2C51E0CC77CC13D3832F07F5A7E28607ED60B0487E2296E7D1DB7F287B6498A2E3B82115EC09049AB9B7AA8EFAF886912190770362BAC7C482B9921D98279A9052843DF83394160D534DA922AB489AAC8F58421 +3E022C06035FB3758DB4A36BC5CE062709307D774BDFDFD6519A66A298F1C576 +6998918941F546447A9737AD9A0B703F162A9CFBC3546D5B21DA0E008A9D857CD3105E151D660AA38BA0EA59EA1658CE36A5C34967B0B05738F2535CE72D7505D6E479A2E56E5251BC3E33E1AD8F63F8761BDE4CE4F1F488E62DF5C7EC8FFEFF5D96DA9BB9C2F6A6A6FF7950203F31D4A4BA47697FC0D31DE97679CD9AD78F3B111387D1A639AB7E1AE75825F6E8D6AE41BA28E5FF24BAD1B342F3848425D07ED9D1A8D1001D58A3BC77089F82255B38AC5C118B6E1F9ECC13EAC9B62CE60A66F665EA8E8C53CD7F683DFC2A7F22DA259541561F2A74F0426BAE974F0F4FD3EF858124007663EFDAEFC64516D6295887D4E5F9140A43808015A0F6224CAAB9D3 +529E2E8CDBDB6A4C8651BAFAEB502EF97BCC7A0AFC81DB4F49ABBE5F603EBEDB +6102E3A47C3B955731EF754BA0DF3086B2C44BE7B4AFD418EC4E95C9483E608C952DAE5AF25E3EDEA2183F500FE6676DB4372F14BB8D2FC9E691E8156D58B23E581F8590751AF70548AFEBB9C44D668118C545EE2B15DAC854D47754FAA629A0A762A038F93B1D56968E69DD2E3DEBEA59C8BD9C25FA9DE27F4134E5E957C38F67D7D60AA721700ABAC54D9C512B784A65EA076262A7687EFB3FD23E3F44C29DE3ABE21224A99E50FC7EC7631579CA3CFECEA4D204CEDA98D8F81666C8FD4306E673DB07199FDA33FA3C92B719E2A3E793727B4D6F5F362AA25B6D265DE5992AEC985BC7137A4DE83E77498CC21ECA0DA0C33491CD6526161C76BA0FB76D245A +00BF8A6A148F01A1680A0429C1AD1A96EF9A70736DC120A3B31A8F14CED1A91C +0B4F20D3B9DB9DEC0F9549F85292533823BD40A0BFEA8609E2B6B72A9BA8D8AA99B66FB8BF666FED486E2C62A922E5E09B8DAC1F96457B6E65BD9B3DFF73D85214A783CCD387DEB7F6D9AF86159B56AAB275AE2B2BFB34A25192BEB15F5836351EA6D043C56A6A8E16C4D7873C3B36F15FAE6A740EED07B8BD5E3951A786A8602C79EBFF5EFB518171FCE6A14B6762BAA5B1E2FB6950236BC63CB8E624802F116CB3F71B9658473CB8F9E13AB39E53D76854874B620DBF11BD6757E02A861D577E5631C8F3F549A5EBC788E3DA18C8A595530A6931242F088A9DEAC1A8D5A67D500914F896419FEF9B62877A9A4FF9942F03F861B3A354D5DD86DBAE3B2177C5 +527DBA30D68CC9053E7397070CBCB2C00026A3F7E1B681F9908E838DCF46D7C7 +60502BD3040407A3E014EE4D7F776A07F4366E7C60F3A82058F4FFF27226591FB7AB67BBBAA17F369ADE38127906C83181833F176C37AF5503F632012A3310E0A1322B2D36CE479E486800337A6C11C9BB80B9EAE8C4A1B08FC268E3EC97738FF1E6FBA990AC2FA9963B9AC3053C757384A3514F863BF6B6ECDD873EC11841FBDBC613F2499A2CA26C8358DFADFC426E1FCC647BAB42B89327F39550AFE1CA89FB4A5EDC6D42FAE2A897193F6EC6CC17A6794A506BF9D6E21CB678A8CBDDBF5AE0160B938E388A8B1CA509C520EDF3726E358B50EFCAEED539110C1A8867797FD2E3F6C52CDFAD3420A29B5243C849446DE1A6F29309527E553C733E48274EAE +686B16D4B4C509E46992DCD128197730460587737C2689E8A5BDA2C25512A955 +15BCEDB2E4BBE6115D7CFD50C6B6C3EDE2F1F2E605ECE84B961FCD40BD29AB873702A5CEE9EC7DF0B4ABD49496EC6B544D87856E4794E44AECDBA17166A6D8779C9DDA910AB5EFE9D0D58BF04D5208EC69C22ED864779365EA1C3F031DD18402FA1890A30B5754D8D6E42D022E52FAA12168DF5371787669B0C936FD813CCB9E314989E2EED8CC8FD1D6A605DAC16148DB5887EA01D5F05BB7182D75A743467D14C8D1C9D15AF9953DBF0878D20790C0533FBAE760A1465188705C0FF93711ACB87A3AB01E37DCA07EDEA723AC8874C7A5F36BD82738FCA5F74EB6F714307EBF89D2ED67A36DFE478FAF8BD6193EEB227EC4153DA342D7EE0155E492540E60A9 +54F302722D428C4183A0B295B1A76D0A3FD7E3E2C1308401CB0E26F22E05AC00 +78EED9AB46FAD3CB5DBE8B1E24E31672825C845C61602C525D87B23425C22F4D8AE6FF55BECEC83ACF7530D4BC302D5F0BD52EFFE90802455B3CC93FACC390DB8DF4577EDF681722C42BA7F3EB0D225C4FA3A843E5A118A97C6325845CC693C1078629695198309E6CF4C66A1929D37D1E6102EEE987553BB3C7197247E0086DFA829CD39C39C73180626B124612BF7CD83E6A74248552685644786B1C1D780ECB481BFB848444C5700A26EF981D1484119C4C417583A5392C6170F14821B8B96E2C88AE90C13A662F8229AB68955E440F6883A06EF32D330EDF0259A2DBCD8CD9014258516A46D00416EB891340934BC1C0790228D5F9734F8ACE260056C97F +15A204D95094F3852584FF3DFEDFAE1B2D0BB9C82180B3E80741888D46374B27 +00AA45A0C35409934905AA833E730F9A31991DB6D0B6FCA0EF8E6CE02001789FC82ABCCCD921EA68114EB9E0294EB5204DAE5CDD04AD101080454B0FF48C22268BAB4F3923A30F535C50A4EB97496674F6430668683464CCDD20B0914942E69AD56F2881133AC1A653379B04092F37FF7AEC3C838ADE2EBA6A716B4AFA110EA1753871FBF1E1789FEA646394B27E88E91A9C072F19371C344AACC2A4A016C22756FD15EFF7E9154114106A2FC452B268F19E65DD76A9732D8D789C6B6968E9F5A58E26D13823278D4F7949CCECBE273D3877C07936289850549F60137E7EEC107ED398F29EEE213615E1EA3E217F4C184FF562413A63330DFBB74B5B1943ACD6EA +61BA3161D46A2358A20846263BB701500F82BCC15A99D56112722809B09E9607 +00BE0397574C2ACA126D8BA6E41444393101E4A656790210A27F02E836BFA1297C17798D6B49979851E30ECF6EF647E6A7A9791FD4EA2EC1173CCA97E9A521BB4D0D4E482E0A54D583F393CB5F5359AB61DCC0C711451B3D7CEF74D90321AC968C0F1CE7E07C25BC26B962B8AE110F505265E7931B812F06FF51A6EC7075A658FA2A2680FD6ED0481ACF5FDADC7391BE677DE1FEDCBEACB896E20717F05977F8F1E9C102EBA07638D6F0A8C652C683D5F0A794B8F6F40DF05D853C288FBB4547578682A91B707126E47C26E2FE2C8224CE1E6D07390EC496B61C8018573A288B1B2711A6F250C3B6ECF094681616A0717ED01B40920521CEE8EFC67C9E4FD60240 +49FE5B1CE78A6C5F298EF8E91A90A27A6A58D4664529C7E8AD4990399477250C +03D889C76D6268C3404C051D966E30658C634F59ED94F48D9A00C5B1AAA638668983C349C04909CBFE44A402D6116F41D8721D7E471F2B94753EF15EFCCAFE8451E50DF437D32C3BCAA214AED2BA8D7CED2DC5BC9BCFA3985933809E51C2D98DA869427C667ECF950549E27CBE1920DB06910BC8433CE9C7B0A0A3AC3546D977866479989DA46323C424747C90788336DCC51D83CDC62FE0DBA701494D85D0226EED5866A754695B62C54283D8C753FAF72FDED1DB44586DFA59584BAD4EBCEEA7FD4D51D4476206AB5EBDFF41EF2B0543724D8626EA262ABDBD9DE5B37E000C03F13E3819E31637561C04F109DE12CF9F35CD082FF0A0295F35EAC8D02E5E99 +11CEE6862BE230B686540E6B0F4EB4F612C03F7C5C8A67AD03FA230F805AC616 +432E826323977DCCA04F5A592CD0EEB09CDD2BF39C8E209C0203DE32E616A97743A45BD247FFF7BE7AD0D8022FDC33C5951086039E1E35033714DEC05A2B35D3C8933FFE3CAF51B68A52325A958D9C60E23F72F7F7223722FF733C466F3263AEEE20A3D4E79569B754B63C22EEB11169A4219CD2A425B8012990923E7426B3D6C98FDAAEF35FF5662D57C4B677DD4C2C3441BD770260618951710BD48CE955979C64D956DB53F1E3A4523A966DA7D531CEDAAC178E0578FD09CA489B6F092FC6928ED00F8EEBF2E11AFC4E2FCC937041215B96A187A3E076ACC7FFBEC0672E0CD5ADC835B599FB2431F21AE4685D79A982BEE380F29114FCE7FA22C9A9E6464D +0BDDE37265F99BCB2569789C51DFCCDDBCE33F7D127F87118FE17DEDD042DF2B +00A5546EA39C3638EB6809831B3F5943E383D5F38557544110967166DC94AEE2C649EB0DE25D98525A7C84F8F335C31C5E7B1D3F6E704FF03D2F18CF5423D5EEA82B4E811EFD320966C632D45AEE01C8E6516E7D5E0F362A39A6B3BB61A659D9EF33FFA3E3E306DB25F9C93EE3F11C2F0D3C608A6ED02C519F023F5F7A18C388147140CEF444068F4E53395796C22C1E4797AADF401C702C496AC875C298B415445F9F02A9EEC7F300901549C41C074267F7E46EADFD008CDAA7B8D5D5F3B9B08A9433ACC68486F946838C9AA3B128D4E21AF30F1277FC708BB895B1CF7DD724243D024862D130E0708D9C47357E95F95BF74ED0EBC868F5156357A14D2F37C9FB +52886903EA49FEBC7B1FA1564FE50CBB87BD0996F667967BCFD185CD7528B262 +39067DAF0D5935674E0609CC5B4CDC488F75C03AE80ECD6ECB3D1266C46A7A159A32EA3CE5F826960711737EC7DE122F8FFF242B4223648D98BD3523D258A7BA4AA5EC416ED064CC42CFA5E821E56A3129DF03B70F96882CCAC521862738F84FA3CBD997FDA22A179A5A8B9D9EF7C095319C1BBD4A6F0F1FE602DCFFE9AB31F82352F97440EB5928281D2DE4FC1DADF068CDA9D8F672244C7ABF7DFA7332073B48EA54B74246F27086BB17C17B49F81D580E73AA509391A731FDCFCAC1CFE54B5AD709E9FCFBA34EA6537D0AD2C9F63F807C2729F915014E8EFCD8AC5B65A6227D00E72988054D8FA39B0875901B3A9BF9A52C81FE637DD7912C018F224149C5 +3FB650E5EDA1D767152DD927AE150123A67CF45735C073F1D72C75235382EAEB +6D21BA2F012C72E9BC48908349D8B4430945DE607C9D9670DE3B978B0533528940F233699091671243D0768E1D16B2A16830201BE3B87D62C32E16C1DCD3B2288EE2C78DF228ED8B1BF311C29BA7E2C9123936281C4C1E2B6D9B89560D25B0F322AF443DFE2977516699FFA043556074BD7B50CF105D3E5F4CFC5D6FF3AA07A504AC8C352C3248B9907504011C3E644482F21D09B99481C1320B41AFAF8DA4039A8CCBAF61DE5D05B49CA28A321C26D2128985B31A68A8EDBC506F020226D9419D1C7A08B2422B05B7A31B08B7F3958C3EBD7AB5E7C81374351CF07A593AFBE8AAF82F14E845EB6192D899B7886CACEC158AF07E6AC4DCA3276F089FBE451F79 +2FD7B5178E17D13E6E84537A06712FBDED20C4A76C02D2BE58E7C3EC0086CF83 +00BD67882BFCA56C0C191D0CCD9287C2A66371C5D73B5D2775A4EB7F0F87CC63F8A2F00A154E2E0DA385BFA5DC740B46EFE069574963B8644CE43207C3AF7730B5EC44FFC9A966566EECFE61486D6B0AEB150F26422461356846F82303EDF2E015CA1FB13B9C082F3F1D4C49836C441A874FA7C00959836B2F671A33FBE193EBA1B0F7E16165259DC091919FDA9BACE7DEEF61A169712D4240BD27642E7AF6CF4C212403920B607EBC9F031848B1327D4CA5C4B6CAB67B92081AEB2A19B8C47AFFBCD3A9DA16EEF0FC8A32ED339E6BA4FBA22746EF8CBC0BF793AC72888894639CB7627766B968A36118742A69A3B9CDAED4F91B490037B22EFEA730D6100A8771 +55D7492CF0E12D9B8B5A9A7BF2E741144BE9AC400979740EC2E8D41BB67DAFB4 +1ECA73736E985317E632A261232194FED9A7CD7AD242C9206EDDDE16779B53EF21A31B0E305EDB65D92C37640FCBF44D9C62663A0316E176CA421A530033AC4BF67BD46D7D7DA083E013C9C99C44B57CA5C591298B653492C15ECF47AC728281BE2CE128935C806188F94A5B9EDF70FB0A314733A3CB58AC9D7D1A65A058F1EBC39ABF87EFCDF494DE488EB3F446576B331409DFBD87E5EF2DC425CF00C3899BFE5578AA7F8D599BD874BC9003F26730ED921C3BCF075EEB45530BB7C29FD00FD0D76F655E358F918EC0F2BD0AA8D968BDCD747FAC20EF627E3799E2306EA4B731129FB3D54B13077A224BF5BDBFCDEDCACD6FE8A4FF7B12D1182FD83567A91D +27435BE64E1D41180DA19BDD091491C017B2113341A2B2CA76806EE2E76B0B1A +00C664F08AAB1D3223D0A8775FA9933438F1CB8CBF43E16A1230CDDE83CEF90C7D9319D80B7DC39188F851A63D33F1A3600E7B58924961BD2DC891FA568A13C8747397BF27F48CCB27ACB35B4DA32A8B5419F0C534DC65A77BF26E7D965C3F3F145EB4E607C258914E2F78E2F117D9EB7C9AFC43E98019EB2D683E937D17A4CE9BEB43C1A52311C906B2074E93C4BC2608073FB1EDA9DA044A9A70901C3F4098BF2C3833E06AA07073AD8628E7085A08F45838BE429EF11044D28B719B570E8005B16A30C2B37E9FE6C274B5B60BFBC109214120B8E86331B955D7F314295A33E236EC833028BEFD0A4C729E8F030359D8E28177AFB55A9EEB58650C87EEDD0D92 +274B3BAA592514A857FCD9975CE4C47EAADC2A968256300A28C2A1F3C533E9D8 +6B60E89BC8569DFA4D56E436448C3DD136187FBE91D3A9829243B661F15EB51EE38C102D42FE24941B449629803BD1D7A3538D0E31ABFDE6FF290B4A9FD16DCCCAB3E3767FBEAD486BB7EE7A1459D5806A859463709E7C3DEA83A66FD265C98AF07B3F4FD264FD28ACE448B1FD40EBB364F3E0779ACADC512B4EB52599598BFBECEDF5774CA594C8EAF6BFE03BBF3D942417802AF2FE4697AACDC8C56585A837074F1B576F71012B869BF027357414A9D1778041D99353C0813F6B0C33F068039C2414A57C2AC3405DE09EB864191DD41EEA4EC19697727D8C0215EC80E3297E37D737360D57D3BEA0441EE67DAB60826EDB41610DA5189938C6B5C570F41660 +199CA7A478AB570673A4851188700C15C10842FE06ED1F815D7BCEBCFD9C209D +56A87ABF838F4EB23C9E79B0D62CF6A07EA9CA47F10C2FCA4C4D0AB441694A69060C9A56737B27FFDA414F5863BED6C81F0756A4FF02E0DA0248CFB6ABEB4ECD629824C1AB376BFEEEB0CF40FE6C1DBE83013F8F46028C245BC9521BCFC40892B07E6CB64EAABAF195C76A38A47369A8B93E3B54391C90E9690AC97CAACA2FE1A647C5D9C531394C8C528056157D45D7263FDFD3CEC35ED3DDBC98A4233D1E69C4074824C1143FD5292D61977DD0FF0C6AB49CECFC4C123A2B37CBD610AB574B8E95367EE8205B86B916DC581E08F51787F04E2E96CB89A25D96BF351B5F26403291968C855AC90FA4CB40840309D9EFC1E49AD32CF057341926F7BCF883CD0A +420DDDE7A573D7972A06FE6868281B30DC2CB08C2C67F40C7458C4667D766956 +00A617342973E9CEA6851B63C02A786C2A2E38519E1751B796A5F1B1DEB65EDFF60E082A1F7C7A0B26C5553709174793A4E712D80205B13AB614B1B1D9B3A479B7619F26C48AA7AAB0225516A60A27C97801DAA6345842585D2D08C44531B32B90FF800D150509AE5CE25A0FE2A4798BE709F3BF07EC0EB6C2BF6ADB5BC8C6C2BA47CE3594117F96F59EA3EE77CF152D8F1692C7F4470F995E80ADB79B02021981482BCE2709A82C063F0B35EDE1B790AEB7AC07944DE80AEED461B0053A41FDB04FEEB25914F4701FCE8B9596BDE5475F55F7CE67DFB78F0774F59AF93E68B44F7867BF69A641FE6D90839496CFCF28804C39D3FAF59534DCC424A89C5DF5EFED +3D24D4F74FE32EC9496181E1F8F6BA3E21A4B20F4E1A1FF11B46FFC15E7ADCF4 +6E565ADD3CB013079A810BAD4AF1F61098C980E8584C7AE79362B5FCB759B3DEE8DC2270DD6DC6D655FCAF61ECB34050A825C3AD32FB4938DBC8E3B54A7244A5BE5E0EBE91BBC1B8A9F207276218E5B7ECEF7EF5695AF558D6115597F36D3B8BAD6D756DEAECC5B336275DD4CB4426CD3B344B578BB909647E4D0DB9075452013D1A6297F6A25407EB338C111FF08A437B50371ADACDE4378E6FA821FD3C6A00391C83BFAE042DF62A2F427F75D4550D67B2D236F219F85F54F8880918AC4BF59F3C5931C9E7158322CD3372E76374BFCB9AA1BCDC6E8444AA3B4C41A0DBAE7B9101DC54BB5A3B2993C1DF0CC3E628C6FFBAFAA39225BFDD03DF75C45CDE9B29 +7757682FB1365101C9C7D495318D0388B6C934610C9F0E6A9E44364ED16001D3 +651D7BE1490243021C6C9B196023CDFBBA4E378C691A79AEEB8A8B2E2BD64707C5C08AC7ED4D2459D3D66065142403DD18B1D85625CAADFA0F1D36DADD16EC057E326E7DD6A6B87C0951670ED3605A1735FEC337E5EF7D0DCC607BEEA269674925C0C135522B65C538A350E8452DA2E89623C0B0C8FC298B217FFB41644BAB1D91DE309A65C9332B60BE558F2F78E53A3E7CC794D7C108C827BE691DD171E2DE8A0545EEEF1CC859C34EC64C21F05DC25642764E1CF1633384449089719D9B3675D5532832EF25DC5F5D519238F90545A04987A4B9EBE4BB07AACAF7E9F9CEB929B6AE6B19D87830FE58462A57EC4AAC023D773DF3FFBD4D92A7C2F90E135A1B +6E8B30E1F3E0440CC2607D1B80BDA7065D29EC27EE6AF640F5C8C64A7C4B1B1F +00838E510163A50007922AF80A9B567D1143E2E89E8C883C2D341FBEBFB310DE4D299D5347A4A2E2F2BE9990A95686EB22B12401E3D12B9FD13767AA371D4A854121BFA04D43D7D81CDA477D7B4050720CF97F1CE3327F6DD576C4883C0EFA8290A333467A71F0196E857B8BF2324186D1EAF92C2D5FB2420B3D17BACD6F0A117A24661D3BF58594BD5653C7820203A628EACA5A0DBFDA062A7FD4099E3E6F1FC427FE896F6DD9721F9137F86F0471BBE8E70C0261AB5E94ECDDFCA200E1FE4BC14E22BD50369BA99BC0FA1494D3ED57252615DBD4B4ACABF74F8DCCCC371860C2CBA7213DFEF6FEC2CCC2E6D07EF794D3F6A9770D2EC0141DE91AB25D3E641FCA +4B0FA5BEF5CD6B7CDDA30F35BE0609ADEB574D42F56047068CFC06D6E4A16D70 +0082A2C1DC931E8C5BFBE4DD8CFE7C40193AE6364300B265B194099EA220A2BF9AFECE2E8A0904BFC2644AE2ADF5DB66424670C7D601E8E62C192C71BBAD41E0094E6E16D4042B6A71570C98D38FD258B15F778376E42B254E5CE00E976B466CEF473653A536553ECA977CFBBFCCDD60E2DEFAC92D1D0875EFADC58BB169846DDBC757742A1F899998A8704C4AEF6ADE30257227981A838CF4A4E3A2836892FA10E0626330788C6A8E8C5FA87B76B71010C1F0BFE12CE1B8E9E981A2BED851F3B4433B165AB8FB9B6BEBC3AA66AC924BD29717E36416EE3465F1ED7C777DCDB9EB24BE9ED162FF682925238A177A5382B46D15CC88A19D51C5B2F7FDA1F965BB81 +4FE64BD0A4195AF4E51DA293F0B12E32E17C1EEBFFB0BC2C44FDCEC1576C8D54 +2D2C94226006A69FA2ED492BAD51361B30AB28A0ADD175657E817A50186855D019D23762E504C40B636F6AA68581295126053F2A4AAD696D470AF9EC5A8E404D9CE9F6819F7D89FB85C05C836BCC010C8543F45228DC89303D9FD5A838BF42F6537C284BCCE4BE0052FB0D366A0B081950A90ED294901BDF582DC6BB771839DCFBEE8C1B006D17AAB2681B6ECB55C6EB30D23E84E021EC8EDB834D3FA7243448CBA66A189A3A801FDD57604F383AEF802B98D98841CD0C75065FD7D75E95B5CD709D0AFC3D6A41F33C12A9D60021B8D6E9C1009DBEAE7D1B1FF594649FB917C29DC5BE04AD2B9BEB7EFF07B82869DBF0BE418ACA9D7825513234D26426A55189 +1F83A82468BE1FFFF23696F3C59A8AF04FAB33E8E9926E8C43A8D7E234E2C7C6 +0083AB5E9ED2C87BEF311A9896EBCB752CA245D7949FEEB57EFD61A569478240F565176C5750616AF6400CA1DAD38118C639587F3772392D3F275FF7117A1776B125325FBB61092AFBF00455F2B3C02DAC4AD130F31C1D6EC7BCAB8A64EE0F48732D0801DC99469DFBF3E424D5D414E99404B0F353EE2FC8137F89D94124CA5C192529A63CB2EAB9B316E736A71040F6D7359C8D5638E1C213392A003A6DB12E292E4B2BE366FF13BD58D5DF3F3742EE346C38AEB1657EFDE7EF90D9AB0FF9927BA2C68CA4E1D14AEF61DCBE67FD88945C00A6B4BFC7DDD30D63C5793E6EC495190CE4152473E99411F8E101A378BF2E7348C3955101620869EFB4D72BED0339EE +370FC808993B9D5D9E827E3907D9AB5780045093F3317386859B30383A2FECB7 +008BDEA5D3072AABB731F6DCEF2183AB2EE8D15BE290FB322978DBCEE1597265884FFDFA353007B74B3D081C271A362DC15038733A47F93C68735FB90FDE6B51B3FC393DAC74060070EB956D59FED7AE4D6D2E4D87B76A6C9064749879E8D02A47B2A3A4FCCB065BDBB1A2C902C463FE20A74511D6F9F37B14598A7CFA64400AE4205F9072D7F68A371D059C8C357E5373157739E74E741E3E43BB81EEB5F61AEC83C667BD9D9FF47DF4F67CC97FCBF10C92B4ECBD2F4A1A0DE4392552CF401B73E5449FF8424A4D49C22F21791AD7ED2162EC0579DB407AC12233AFD987752D599F2F3C8D9A92A4D1BC7036D96ADA64C1E901256F3219CCD8FED9C2ACB232CB3C +5DDF77979C1D3B5C910E31DF3D74D3D2F42BFAD60F69F5934F4C96B2674F3362 +4A6FFF3F4272E209C3DBDED3CB7E05F26B72F82B7487E69BBAA6550DCB083FB843F3D4FB41B50E3973E311F8FDF0D52893DF5449BB65EEA6E63B28BFE7B9EA93C3CB65C984652EBE5B45A04406363BC7A8FEBF17F3D26745048C86252BC9C08A417679F1EBD36DC138CA55435B76383089B3FDC7834D301E9BD82A83438867D5BDB06A8D4DFA85CA14F0E51030E257939CF82930AC527084F47706F8CE615E98BF38B96849F21A997D7BA2F862565184DCC3E6C4E481FB00A41C7F1AB87739EE860FD367780B9B652B1C30A7771B516EEA343AC8515CCE6E4724B4F5E44A8B15A7E1FDD3AB453EA947B039EE35B0014BE99560B183902E6BCC0C978812000CB4 +6E5CCC2E030EAE25F256C7841E7CA585F47FF5CF54C0DED2871F65CCBD29BE0E +62A6213B0D4308D357C17F35433E5E4623F6C168F08D646B23A7F0CA3D59C4DAC87F38242B581EEF34FA40D63E93955A0FE3727F9A42F32DD62CB8D7393DC5805EE745656FFD0172F1BEC76ECDBD6EE9E2C153010C78110317675831AC8FBC69836CE0F54C933AC9E74F1372390A0AB2D8A7D2D8743D8E0D771FE782B6C704BEFAF9638CDD02FA6E8A3339BD5FC166490DFAAA65E938BAEC62C8672FE2CB1DF8FD4AE29C1A412FA8138CC2D35836D6356E0BAB3ED297577062973FCE72685B450613590C221F613438F8233623485AC11E392D5DA4F499F0EC0CA916126957FFE042626E112BF205474FDBCF8371B2CCE11140D7C5D6AF2D56176C40ACC92C5D +31BA2154EE76A56112F061ECEA65FB3DA3A6E8728AA24EDAD858561BC3AB971D +626FF4F0B47FD8A892174DF7ACE18E78B2575C3333B8B78B3ECAD74B043A38BD603532B6391F4D683EFB360BCF9C9B41C2FEAF55B081EF7375239DCC6D089578FD1EA9594A045F85C847C5035104B27F47168E079BE3783F6DF9ACEFF9B4AA4B5898B3E180C567297BCDED543B727C4F315D4695575F98D01B1976551E6723092EB9E0DAA1EB2CE9D7A8E377C5A1A1E3F84D875C3BA2292931BD5DF79A87F0988BA9B74929C62B3D325166ED27822314754E9CFD54DE78018C47BB55116666B0E2AA141D55166603ECAB0EAF554FB419BE8A09E80D2CCA63E560DB9FC692E4ADD3D286A45A56911AC45BB4B3D5800E4F5A776A6075071463D6203DCB6E268684 +5D069D435D23E0BD624796B528084D54CE11A6B864D746D8C9D3FA4435BE484F +59B94A53A137A3B26E8983CF709759726CCD05B4730E29D9983898855565B4C642839071BE9A19A145EEC2A4AF2620D3E746692DACF2B5064B6A40AE7921D57E758992A3FC89DEC98E250CF6877CBA2242C2D73EACA771418A542F529E5B2B0F731D3AB781541E0A926A3F13E9ECFC5E9F9ECCDA3FF4BFFC40DA53069BE84E71B29223C5575439DFF7AFA1CEF11089D0EA2DD508545987A32761DCCA58E73D5D77829684C6BF43E6DE1B56EA515A03BEEFEEFD2419A405971A4C9BBFE30CD86A891CFFED17C3628E018CE9C164483AA68623C6837FB1AE592EF2846EF90260B4AF18E38630505AA6E98007E179C40896480DE4B6822CF6DC98D05DF556AE4D06 +7ACFED54A77A474A1DA120876DE9B0859F4A61BD01DB83077B772BD51544CB72 +009F9FABE70B555B4DE833A3D3EDDFD55D5CD78F930D0CA5A9C28F2AFFA0627A372409D5A8E19364367B84B9072EF3F0088B305401D22573E57816924B60970439BE93A2D70D48813B0E8EB40E0766FC6F3FFC838F72493F626F0353A029C614A30C40EC5D7B18AF5F489B9AC459B078AFD9C3D7CC4AD2E93DD0887DCEEF7830FEE4E90AD8ED18D708B0E27A8B8CF7E86C8961860DD1774BE1690D164F9A0D41F82C4BDB13E87506D3815D315F117330CE7CB5B8AA4F84F67CC18ACE52FACEFE603981AB3E707E344D37EBE72368706C8DDAB7A5F9A41D6DDE83D2BBF7C24111885BD928C85056E42BFF2916905A8D4CB9BECC03696E7675CE312EFA4B9E3D8C70 +14B17383C4FD03DF6C57289D00AE9486089E31738D15F9FDDCDC8A8C518E94F5 +63BDD938E0D581E9DD2C63BBD1D72F90FEA3847CA16B7571B30522E4FD43A292C78B1B2E863AE88D8BE3E7372809F7D7369E66438D39180D1FA4CBF614C9E9E2BE06120D619BD3DEB503A789CC8EFFD719A0B7962392B7F2EBE3690A3C7D8046E1DC3140C5B5CC234413BBEF6E9AC0A9928BE1CD8AA97BC4018DE4B44CADB2C0A0660F672ED6661C9283129CCB670D92494195BBCA6D37641E028DD55BA6DB0ACC81D8A4F6594AEA8664A86507B990A7B7F88F1FC27674984508CF01347935BFEC3AB7B2BA9F0AF2E3FA42D49D342137392D970A64971889E7DD36D01081B035989DFB77545E6754EB8EC2033B2D1B18D26D63868DC74A31F27161D116916214 +1EDEFD566AB193F8B708CAEA509D8966EFCF24CAC24FD1FAEF634F73DB90129C +0094AA39EBEFB6E21E3B822D7354AB981A3FB8808EF5DE2663CFFA0B5CC995825C08805A7803451965E852572C24D03E76304432D4A13375EB4D363976299EBDAC7D1AA9E4F02B3428CD8CDC8FE329C4C99991D0124B70D018115155EF12743192C95CC0DD271F2A54E33B084744161156BADE06CD797AC375E5ACD33CC063BE5D1615F45B43DB31D078BA409E21FE322A5102182B47FE5AE2A2F31772F242FB21B1FF8527F66A890037D32833A5674DE555475F954653E001332ACBDA50DBB5A7301A4134E214EAE521CDA8F579658428816069A017537DFF9B02757873A335300AD877160B07EC1D4FF7FA2355D09D7B9D36E9C8A35583C29045D3C97DE5FE42 +0C95006454019DB56E6DB78344FD9E965E78F2058CAF9909AAB4659667657864 +64A5FA82F9AA8488B797CCE064576A77591C6964A5736B5839CD443ABBD50C244DA4A7921CFC5DE36E5D2DA489B0B503E54E1FC506BCF22DEFB7DB0CDB6A9D98C05D90CC30B732F9B63D5D03354486EFBDD103AFFFA1D02D9EB149771F8035BBA4211700C3BF636BC9D387E07D1A8D26ACF5F33DDFEA0B48943BEDD839148A7A26B5BFF4849222839391BC71F141298A8FB4BE360239441B37CA11EA0C91BF6BCCED27F2A62D12226F98B990136D848B33FD5328C9C39596B911332B91BF1E4AAD55ADD6C2B74EE4FED59CE32B174F8888603C18EAB8EF687DA3370583C422A0C8EE479F4B95EB21CF09D2140A20A93D607E32EA775A1D5DF03B2A518F245025 +448BD01B541A5514317CDD418A7BF4196E7687CABC6DEB09A80558FB80E10460 +008337DFEBE14123A8D3280BB6BD2085C399A362DB382BF9C3B9756C262DE57FD81041E5FDB21B1F4D55B97A1EF56F48B2435E54656B83F0D74F1E3F4AD5908FA8155FE642BD1C0D1E028E43818AE90AD108F2CE67C321EF4C075D3DBE666A15558CB06D3F15B15D718F86E1D88AAC1CB675C14DE98EF33DC6B22B3543D24F21D8CF19B91BB33326BAC53ED0DA5AA967B40FF9ABCDD4F9EFE92E1BE2CCAA948F2D0D2F751BFC928EA40497A8F4F317CE2FF0139B4604C52F0E92C1C16915774875792395EB4C0F05E6F097C05FEFCC6098748804DFC96BB877FB232F792EDFF3FCE79E2B1B3040AD32B29498445001AE1CAB52C816621D0C0E647E7D8482BD6A71 +6E1E9F0F0B3319BE455539260680AF81A1430D71A127D3A5132C7E2308F8112A +4F391C423656AC24FA4F01DC2C66056FBB06BBDEFBDACCD3DE56D9BF3887FD87504AB010DE4A68B409249B5DFEA007F4451AD763C894B96209C64B35F8B97951E3DBCBC265B00FF3D2D8E873CB258261D8F5E7DCDB55BF00147620F74D7200C790C9BE69B618D3DE351E78D3C77D4F8ECA0B9C4D97C73F4A76830287B950942912A12D24CD27ADE68106FA687E296080FD4AD353E66E2311D5EE30453E1EDD930DB204E60022BAD79C6900F57731874399314CDE0A549A3327D14C88CF82F956A584E03C8E862BA4E81F2313E382BC0EF8FE4E0157C89D7325F0A3F0AD2B1B076B2F4309D037B539237315D8DF2321E5653FC2A463CA16FDD3C27447BA0EF8B2 +2F85B2EF274B24555A240EC59E532BC73ABEACAAFAC4019CB8639CA99DF25757 +00B050258A174B0F14D7F6815CA62B425DD292ACACC1B8AA36AE08F856CB94E57ECFB1FF4120D6F9F91F321E900E3CBB0E1AFDB669C21E86AEC5F8C7410D556B7AC99BBF161ED35535C21FFA623C5BA3CA6C0C3E916213AA4645E14778D4D6E1F8A4A05E36E5D7F49AED2CD35F140BFACA1B75A9D85164C8BFCED3452AC2DA9D06F3E33235631F7DA42F430C7D37F34B92A92264E59E1A49F01DB87DBB27593A76D93DDE7B526A4B197DBA1084A439C8786AD844DAF749A01C93E114B377232A5F4B4CC0E1168E9B241AE90C7899E1F1DF17A385ED9B539E5CB2C3CEDAAA6152EB5C7F35F380013CE71338AC2F2E00E8628940018D65D44F4E93C6DC28E6ECCBA1 +3B1CB9BFAC9AA3BD396C6489B1B980FAF9DE0282E88069AA85C5C07668253625 +6CFFF4DC32448C59F4930EA4F557F64A2AC4EF1798291B5674B93EF56BCACB2216D331DDDAF7984334ED0D8D1871B3F64C6777302697B7506E59D060DA500D400F43C417E6307189575BDA169EC74B20672069E4F55DA905D092F6C9D52920AADE7C76D9196C54EB1540BC98D4B5810E9D17DFB6E0DBD5E3FB7CF93CF74185368D75ECE836BCAA98A4C10EC9F15F7CF2FAFC862CFFC85607CF533C0DABEBCB8041DC857DA831619F6431BD432EC7B7E9DF8FFA0B6862267DA14767743FFD168D8CD66B7DE7C722177B6EA4E560DB2F78FD26F937CE5D179784D5204855C24582F35372B1FD5CAB731DF5FF4084C44920FCC6298DCB7E05A0E238608F3C003F2A +536AE167957568A55745B1BDE2BC36D229CD355586A4EC3FEB9F3F55DD6F499A +79AEFE0DD0FE3858111AF25FF6D4B9A82D66544E55043C3C8356559A98F3634DAB08FDE99B0EE4A8710D15012F5A49A645C9E156A10E7A1D6C7D805CC956A06F6717D0DD9B098380BC7B501103B5329B1A37D8DDCEA68E8C4865E954FED8F34F315F81B820EA31BD7B3AC7AC3E9D56B554E14CDD34E777C0A371EA9930D00F33F363CFFE24CF68FB5790B736A3C7B451E1CBAD0D4165EE32BB5F7DA2D9C3DABAA0D9BC81D96F12FD540E38C54A2A600FF3753D425325CD62F047964469ACB8D1AB077977F886D670BDE42F56014897088A17B353A9540A14B3D195B7EF2D8E20DBA1C49442C1936A952E1D0BDD0524012D6E6A1C6DA6205733088AE0BE27142F +1622C3635B982FD6EF80DFD9383337D1A4AB92D93EDEB8429F666309912A708A +00C101023EEDBF9FB488492F45B8260F0D37773A618F1644CFF8A2C81C67AFB69E90FAA12E09EB1336F9B62A0FEF1741D173833E1F7A4BF4A8229AAA052BC4BD30741E6ED966E536AA6EB84DC14DA5C72AB31B48B747420696B62DE061FE462A5197628A7F675404BB86770612CE02D791F981FF3ECE50710AD6FF0AEA1E4C424C88E0EB66E57B89D41BBE8DEF8C684850D88C02833DF93578B49D15A19F410E050F1958A4D2D390FC817556D00463FF6285D9889A4E7DAC4F17C9212CC632DE318E00C5D11540ECC7409948C33F5B388A08F73B5057CB562AFB29E5EF45666DA5F31E662E68CCB576217A778EDCFD841FC70220285A6A8192B1ADC5C93C060936 +74C51121DB207065CAA22FEF4B31238BBA4F3F4E11E054A3DED31887EC5BFAF3 +008D7D818E1911FC6422DB03C21A6620AB9D8C98FD9E29012BC23F36D4C54767ECFE6D2756494F8AE977DBF9442A8A0F601855886B8568DBDB5D29871462899A67642742F2075DE580358994C1042949843605049B0C6B29E78D481A5C832308720BBB54C154DE80675C20A635C72D8C894817DC6705E21FC64FE1E941246AEE6CD8AC5A788A649DA40AE94184EFB82963415DE1EC727187409CAC9A485293D0F01E39FAB5ACEB5059509CA0117090B88DA63CBE27B0138D3067CF50C735745A2AA1240BFBE7747A6AC37A66991D5D7E284E829E34D5724C3C283DD2127BFF9E03E867D62FDF107919B6875DAA542BD9CCA2CB32B2E494B9030509351F914A19B6 +5562CA7317C4B1CBB4EB0BD2C3A6776E4A45F7E77730E71BAA89008385C1C992 +00968EFCF4BCBB12FAAB8972D63721CD92AA6C8C1208A61AF64C87B6E28804E3F5FDC49BE1C70937C98BA96DFCD8BDF737F07F530FD1CBC6515BE0547996D615DD5075270C90F30788B651730F62255D01A6E3005E1F1951770D7B86D7DE91918C6D42C9F02DDFC2EB108C64504C5AC4361C829142C057528C0992C63A85B43E1784030372919587328C7101340D7449BFFB544F642F6E11F40B71D8528283F358A332A15E9D3F1BBF39A68316701F3FEE50D30288181F06B907D0618B9E559998D41C4384F2D06C615BB25D81C1A8A4B5655D802AA24E5E5A0FE5D0C07DA08D11DD8FF347743D3727BAF4CC4BD121CCBCB3ABFE44F50A3EB451D6D1557C07AAEB +54796ECE355D382BDBC1B01290C68C35CCA15C24D5FD26E4CB7E95E82020DAFC +75D6C1DE1C0E1F6B27ED66C33CCB4C7D19894CBE87CDF9F7FCAFED1B03D3579D78833EDD3DBF468D3E38C2FF9FEE707D6C7ED347EBC039A5236E9348A2638DAED31431E4B8DCE1C3BEDE3F1F6B9DFC3A5EF2C971B29F4F18F2890A95A67B43E06B83C374AEC28C6EF04EE5565751E9D75DE3B9697203155B9A8B24FB54771FB0E9A2C3211B33FB693BE8115A0E8F742235ABF56FDCA2723E632F778540D3CBD636076ADD248060B95169275B55B1EFE5629EABD7111ABBE7DCC08EDDA7E348D3926847C3FE76E2637B4DC8B6BCD9F31D3B5B89A1D2E09C0C0BD4731798DD9F24912042EF22CA104FA9E561A6CE7E5CCA3D325B7226973C5577AA6B13F7BC9FB4 +37572DC62D8AB7E2609E621981AB26F58FFA7E83E6862887D989968961D5CE0B +00B046B69341C1439E598ACAFCD09711295B2C14C34E18FCD8A54063057EE29518DB70D5F6AECF3209D46446E1C7F6B51B37F711BC91A57073D0095364969DAC34B933EB96964ACB5B3FBDB07D820808A53EB33715BB0D50E8099CE8B137267E64508B6707E124191D3D306BF83C7D18A0C0E116736C2CB58ADED3BB43C7D668986C24D6155329481C2D161CD33CB090C350BFB1A6699BA28FCA3092541DB787C739CA229472A900B43D568F10CBF4A427C087ECEDEE75594F68140F5A69E63E3BF40F4D263B9B43A4D8ECCD9E5DAEAC9FB39E6C76EA26FF3DD2F7F4FA9C248746DAB0FAA5025409322255FEA374EAA30C268867C1ACF85D4920959F70AC29C1B8 +39E73D5D754416D17C129EDB65C7DF8AE987D5A5B5B37BD2E1665425DA36E08F +6621B87A2FAC39D7AFE930FA1461C2AB4494C0AABABD00ED57B668D1C7DA712085C2086D681D6C9363CE2310E5EDD4CBE450754E9584F3128AB3CB6110C6B535F53AB957AD09CE9E945FD69DC036B69D793C8FB1CC51CC319C60AFBD314B3001112ECF893469159E2E79919E1D36C62665362166BEA102B3D935045CC2253A207CE02652ECB7D3DFB07989DF45D349DB58CD170A19EB6250B954CB4A97FFB2A0ADA00BCDB11F5B3A03680E8BB39AA45918B0A7EE2B212177BB1B24FE75448132B779C2982E185F677F99B2403E8432499A97C6C5A5C23416ACA46D05CB0AEB86DE87DCB4ADF5316C9C38C04F9E823AF95794F246FDA9D6730FECBAAEB22B6F5A +73E07095CC691647582BE83CC7916CDB5C36E99AB4E313F813056E235262A944 +00C3B0EE207ADE9E041828AD616105AF3DC0969F724ECFB09908101E96B00A0C59D7F2832B6F07F5DF71D9572BBEEE5A78E8A785CED6BAF9DE13CF4D0D49A40DCD9470507C5AEF9FF3C8CD352B473CEF78626F12D57B4860BD120F25B7381ECFB94DB2B563CCF2F6879E82E2EBEB5B4654C336A686AB2F35ABB8733FC66817CA93ADFD6E8AF2767AB5B6170663D1D5627C6F30E24B2CFACDCFC379879845A972F6ACFA5B0D0BB2D184A58FBD39D166795A0C72BEBAB05D83812BCD0CC534B521F9CD2AF5B3768826607074235A8AF7D434EE22485D7544AF2C07C57D95705C25B24019086C1D2372BAD3A4DD95E019255C2D80DF196319E9356D9F725367ACE567 +40E1B34B577DF36E8D4BBBD496B0BF071B9A6B1EC1EB02F2305B0551C3BA776F +3CB252B6A8151B469B123F5888533E81E1203902077F8D09217FD427EB2C57964047FD74D5287F4531512AE7E988F90A30AFC02D5BA1AF1E628EB89D2ADCB9207FF1E95A9F7CA6AC5D3CF4CF03194EE508D48B5CB466F014A40F65BFA911873525546F03CB2BA8CBB5D84339D55772AA1E101E28962FEDB2A07124307DFD0232CCECB628EE3116B91C95B65E0734E7904AAEBBF824345F3682E654CEC9CECF019F84D3D27ADCE6FD1A90EB0624041642580F48F66E79412B1ACE6152067A9B76B9599627DCC7E0D573CBA718ED1F32F63F20CDCD96682CA2875A987A3261D5071CE437922145C431E67454272EE503525D18519696CC8A28F72D2AD40EE321AA +2C5DC381B5EA1DBBD389AC755F94EEFC7C439F254809E18502A74CC617B7B866 +0082A9176E4A88540EE41A7BC35AB26F0299227BB56DD27405A85A3338FF30D2DE3B2BC28EDECAB4CFE5EA5CB08F0456F739F86E409A77857267D017B22305484C17D2DCC2D9E143640AE0DD92D9654EDF71B636DB402C114D17319CD30AEE689DBFF7D75D132CE0841B06854138CDE754A4CC7A2E106E2D8BD279876407D807BD3970E3445791D6F02CD04DB8B78E412EC3E4E48DFFE887BBFA9F83A99916DB7B0DDCCDBE9C8D1E8C41460A358E993DE119CC6A0307758DFB1B16432A57CA651EEF2A5D838396A0E3A944330BE9875961080F07346E0D5955E8E89D0DCF93A6E5376F38AC9912C6B968CE6DF58D5C098BD511949C2034CC813E94AA23883AC408 +1DBFC23C58F789999010CAE211B3516CA0EA92543CD51DF1D002C5BBE316178D +00DC34711E55D95826BF75A094C8F609623AA74CEE18ADF485B8F214574AE46588486ECC9DF699CD04B88098D565848FDEEF0B1CF45C5EACFD948FF79F6AFA10ED16AB763D844187264F6C7279598E73A8B47BC883BE52B0E69AA7FACFD5712F7A46C7C56E8C4FD36FA38789B8F5FBE64610A4C1F9F85A7C4181CF61B7D82200203A5D0C4D3F77DFB35C770DA81A094032A7A2403AEBF1501C1AD531857976A43B72E1A530252897DCDC9D0B851CD02328A297D658E42B9D182E404A3355B9610554D6DC8130C7785610A46A58EEC4156725D5CBDBE3EBA809A13FF78EDA735EC2B72E04E6EE2231FA4D2A41D206C219833B63EDA15596919B4B66310AEF2E395B +06F50E64ACD628B30C6EC91B290161645E32CD3D16ED4281364B24BCCDF160E3 +0931A9CDAFD28C475489E985970248E512C4D88E44474C580017773A6C16A04DE7B962F1A24206F3E6B6BEB5FD7EA2393703E7AC4690A7F130D6D883D69572265577E0B93BBFC79DF83F95F1A8D9A347E5CDC23D2C99EF56DEE4BB497F2DE4B45E70B6CE6F0149F6B395272D1AD3CE341B2E6F99CB93DEF57BC95559E12D2E9291A92545D04C90820C19FA01856DBE68CC6C1E731CD0C159BE11E877FAAB7322D70F214F8CC095D24D9BA0C979D2B7B6E664BE41EACD6E550FEB66F9064A3462AAA277E5298E55E030125D7CAF493FD90D623FFDE0F0737AA784878AE881D38BB6D7E86E75E5C2FD41E6F293B95C9ABE957A524CC2538A4704263EDA08FCB0DC +1C8BAAA4F02ABF1A3C124D99B60B4AB1A8984492765680E4758B03360640425D +27DE51B20629DACA993CFE78A73CB2E1B8F574F159137B82C98DED77A745E5E8718FA21F09406C932A0D742BE4C1BCC13F0ED364F60B55EAEC75FE0EBB26D9CDFA9578C108DB45361E0F16DC9EF3B98DC5A5208940B493D13B450B028CDFA66110A7DB98364D3E2ADC3B36DE4FE018715E93D4F076A6A413F2A21A429DC1F92B27FEABFB8B41E5170968101675B28BC2A32601FE346F772CE6516F41F187DCC6ABB0A6CDE3AA054579817C05CBF79977A42CC851ADC851FBC14789BA91DFBD90E99ED9D6B164C01A391F00D3501279D096E83A61A7751017558C526EEE2D99413B61473967CD48B1D16D0153A6CD3C8C0DC4A0B925B4EEA32F78C5C53A5AEB7F +135049C4CB79AE16AD4702115072A8FF6D1F4D50CE27FEAAC69D4163A130F868 +26258CA6378E0112D17BA2B776C458A90177DE6B2DD0E005C8279CB3B6432BE1088A42E18195778910086EBE575085FA430BB1622A3D472CF83765AD38DB9053EEA39A0B5906A57562D8A0B0365229A8604BA55CF34066B33BB597680784BDDCD7E97F340FA731C99288D03C0D11E55E2B5A56A168ED0600E41B6ABE109134F14342040DB20AD084284C527F2EB6323F523F3B17B7A34FF80FD5C748F75AE4F8792061DA64E1D4D4433A79D30FA31173A6570985CA7D7B33CFF3CAE88C1723D76FEBFF5E44B1FF88F48D9E6F504C685464BFA1ED6B99FEBD508CA93128A4BE5A28D8A7C1617AE9D13930A4BDD8FDDA57D9128435ACE10956CE77D703FF5CDDE3 +3D8A4D1F0441F05DD85361F030122C8A326A754D1CAC3CC656F9A0BE7B49801D +0089E50C439A549416FE51DF316C6A686BB7D31BCF8A17486EBA7F82CE4BB161A70D013695FF9D5E4622CE74161F22027150B19BFA048FAABAC9CD3D17FEBA88DFF9D497BD80D6C5CB46F12411CCBC938CA01DCF93DBDA946A06036B9ED3EFE2473C9778C6A5220A8B42709F786BBF52447134775C4FA165B6A89AAFC5BE7FC67272C0E1D03BA9E0C62A9CF77BB1F016396F3C079EC42FF7D2A73820D192260407A8595E8BDD9B1973DF017438A87D05C6D44E2E0DFAD68F45FF11FADD87125C745BE47D5428DD9B683FC276A32A2ACDB3CF96209261217877851A89826E336C2713822647025C7A395ACFDBA4A7F2B64AA24A4D677D8ACDE40056A6059951BB8F +5562111A58F338AB58D5FAB4EBA6D950A7C781D22FA29F375426D6CBB3ABA0F0 +0BF74F1C7465959443354759DA811BC205C24D001C2CE16C14251A6A9860B9154747D3B514F3A24DCDF9989D4C621A081D6E11D631B0C2B4195EE53AB35C9E1789F4FC0727A096EB4FE6F1595A49EFB6A90EEF03BE118ED3D63EFB9962325953C567E6470DD3C395AB28AA5E619896140DF0904449B14E112C747D6F6ECA835DA86AAA22ACB47AE8AC97CFEDC93914986F9DD82CA97C361EF80568FC4D30B1984D50362D2D8B9C5BF507202E27AD263E347D20526876997AA8755137C9DC0A21F4173AC609C711F98BEE9A443A636F98FA6D4AA7A050791945D10E7531C068EC4DF8335908CD2CAC99F66025449610197A09D0A0C3E7DC2B1000E9E4F43A5FA0 +25688740EB50DC4F3CDE1A73E38BB2C09A420FC440252017D287F28DED72D4DB +0092CED38E088E48E8CC7E65D2A56E13DC3C0F235CDAFA52BCDF664214909F2E84B609836AC48B1F7E3371AF88C3EB983E2B9AF349178342A822CAF5D67FB8B33A69EE52CC562520714FFC6DDCDCD11DFF654D21EDCFBBD50129189D51F93E5980AFB957192796247DC88F26788D0CC51850887CE9A9F4F1505281D0037FC177BBA8153C8F0D79CA9E845599CF182CBE206BE2536198C7FCD338AA512F4D1F1C92C0EE7DB7FEBCF8AEBAB2D8ED66F2D0A16BB62CC71F01372D586544B5E87A3EF64EE13224E1C718E0A9819ACEE5E65B0EDF2784808B2EDE60C14D6C6327695FA0C3B8496AA7D37F91E3742EAD5C64BE37586B38C75BC9C83874B1028832684043 +11739839F92994C6A5D8FE3E5A82B48E8DB502BE526B92FCC74106E2951ED81F +5027612D15D9C0E52DD12B5C0A2B2EEA4AA0428BEC3B27BC0829BA6B569BDC22188884BDEB9CFD210165F2B6ECE5E1714F027297053A750C54635352BE6B0FD8355EFB1D0CA292112B6504C21B4446E9670BD4FFB9784A0465E4EEC91CACFDFBFB5DB51184A662AAA1EC7CA5FC171E409CC4836682B2D01D1628B4DE4DD7951867BB014D7F93E91CA3509D156C8CD01579B933959E31DF4804414C9746C20540833D43922D521D2A8028295652D70CE1665CF89BE3A63DA0A7D4F491C4B24A3D55BE04D44EFCEAEF4185F44840CBE01BF18334A255411E826E89B240AD2E0271C820A8F5B193894EF6137A91A00623995DD26F5CD7E7E09D33B90BDEE811F374 +609F36FC90EBE1B5C42725D85BC72722FBDBA8E1196D82E026706CFBE285D14D +745856DEF9C8AA6D64678D818346809E6A79EED6EDFD9730ACE8BDA96E255245FBBFA17F7A9936B7DA2D7E4A925B16919AC59F9A4C410355046677D0340A3DA1A590CA3BD0C4AB982A2DB86A9EF0FD82EF6452719EE06CE0EF5515166F1790AE6C3E1135D48B761CAA3D62DAB32CE4C8938945504BB138B898B993342EA7EA974B259CD0EC06F30AECAEF1DECAD71DE8C9E76701424E06CCA05EA75404B4F53BCE3424B1ED249E94C488840020C9EECB4083D9AA8F227477785CBFC4083D35F0B01F997AD3D1A28FB1AB2D394853418EEE6A3805A4677243C7B271DEDF00E8CACF56B73EC39313EC97CD015023F102B4C5184560E3D8B9069485C7C43615EE3A +6FB4069D206FAE332B293F93EA11967249AFF59742CDFC56AE3694810081A69B +00ABE4853AE0B5B924C75679989C1F6023E1F5E23729A324A4A351F58972E5A9EDCE20C3243D05B77039F7B79EE5D7D489380FA5490AF0F463117495A94F62EF6C086F1E07ED3FF43415BB9EA672D333C7DB9982A1CB99592A71B9085AA711325563E94A26CC4629ADC1B2C966F7BC7316AF0069888D601245CD4303F4754C07AD2001B3D3D13E5DEE4534172408A147D0F068D7FC2B45F2CCC3034D003CC5EF0CB818BA986C39DA4CC713FA3B1280992A3740368EAB60FBE670A874C435985AFC682976A402493BC2F8F8B66A3343D69550FCAA9E85B91DD03829D38165E19B7C1B622F3D72436DA8742550BA543D33ED4671301C06BB39E5328A487DB3A6B9DA +31C8527A6CD064ACD80E065C2E9E30D1E516AE5890DFC65248C9C7E05EDFA47C +00B1DF9D133A184711CC775A28BC59B532266F11F8895F1DD8F4FEDF02530DE596271248FA325B015E87E6324416FE5F7B34B53C9145BD8CBC047891AD0FA57B040CA3968A6E5835CF8259C14E9E1186A1FA50C38B2C4751621DACE7C0DB75FB27635EF2913EEB06890DEF96C7FE3A7E6E16FDC9089AFCE00B8DF1F0FAF3F1D3CBDC641C61B0E2619FD8B44F696DE84DDA02FE6D1F959088B9C9C72C835714AE2AE0B247A176A7BBED83A4495BBC7308AD45C15F37D56C40A2453A5B5506420EFBC4050805E1E466888EA1AE654814858F7F2E15B67C08890EF3DCDB6043BBCCC291F42255AE9198A45433DEC2CE6E26A5292A842FF547B52B93FD5EF53C986121 +6DC20DFF2DA2567FE0478160E797E1958BB940AF38CAB074B4FBD2B75F201DEE +008FB6B464713F521B0C8F917848BE2E8F4EE96F60E9EFABB2C566DA7EB51F1FE8EF58D016FF302D6B8B1AE83D5A9D9D165F5F93568CC448C08F329E0D957DF2610BB1C0843BAA2FC369542B82B6446321ED66D7744752ACDBD0FB065829796713E7CA0E970006D9C193E464DCBE0C122E4ACA520AFA527F3E10F079EED45DE0BF4475E996DEBFA93495FCF0EDB75037BEE7A14CD9B80AF865ACB25B11CC4E718B8BECD0AB6C4CB3A9914814DBDF65D8467FA94090BD7C020B9C0207B18C01650660469709E8DB633B405A44F0FCDE98D320C139D2721A8AFB8AD1AFA74F970A6431F45C5A767CE7A85DF27D56F093CEB72714B3BD7ED8E0737E842DCAA6803AA9 +69DFBC7629794B5AD4E25A8AB66A82F7881B51A0460CE7CE0FD7EC7983F7A111 +1A92C771C6B76FA455835E7AF4F5621AF0FC5CC9C6F4047476133C2A53033D9270DC7E45A1846A749D12309A60E6363EE577E8466DF0E27B8F25DB0CEB6A854074D47A4D46BE1F5E2138DCD4E545B324AA1ABC907AAEEEB455A24F1170812F2D699608CA9F9D368D4F082C3B2B16EA1C0601B7CE170B0230CA686805893F141C89823DC77ED96A7F63E936FB97CE9022C2D54B94A379D0A3BB2B2A2513F0366A87E4366CE367381CAE2DB93F9F03DFCF83CCEC61EFEDE83E387745DE07DBF48FF94CDD87C7EBAE5F16E54F7FB36E615F8EE17653C47721EB55855F355B346F3CB5E50504B87A64C9AA521F6B1039B57FEF8BAFD351BFBA12715F1AAF760937BD +50D6E40E5B363A45B3E79A3E0A24B149FE60C0791183E1209FEED9E49017D230 +13258AC0776953E7583AF66D84B4E9DE029208ABB08CA3C398E8F701F7795CDADD966B7166ED45F259FD98FF0CA10CCCA52EA6E5E301CC8D4200BD6FEF077120E502033072C84E681C79F20ACB4045DBEFC4F3247238756495F3D1F1C8F18BFF1ED3D211F577FF71A25879DDB1B58CD5CDE748E3742C694EE7E005686543333C5DBCB11B2F51D6D04706FC591EC5513980D9F693AB8DEE52A0A81693872DE1DC1CE8728B8688438E916E1D801D530A114312D2E57D055FB2F9D24081043A6FAACAEA3339C2B2B09AC7C8133AE27BAFAFB32E0CDB370C2D2CC667DD8E44CD864A13B07C5C7F2CCA57F601D30230294C7740918879D93A52E9872DC76095A38C70 +2FFCCE12A5D4951E1BEBCB417E4755B7D6B97166DED35A0A2DCC61BC65896106 +2ACED605E489846B4DA14CD2BE8928D8A79FEE156C597E88E2A1A8029B42DBDAC8F8A2D28CF010E54BB76C01419719E8FFEEAE8C5BCE1475578F47D8F4172858BABD257C8039D355B61D2B9147B571402E1107EE92BC6CD8BB7BF9EA1F14A97B6A7E134433CAD8E6A9EA2174616D8870B233B2B91CDFAF07119AC44FD7B0DE7503261E12173AE9BCA40E2515D397E48EC24E6DB23CAC3B22F701E2094B63C48C9C33A2DC6593F267FC1DE91A058C3650A7888B3D2ABAE9790B90049026C4E5B3FEACDCB529F38E3DF06AA3000D488E612FE14E5DF772CEBAF44C21A9F3BD580B3B7BC9A5AC7B6B478D25E8723236151EEFE80C311D54B3EA6897A32C3C901525 +396EC59DC85BD9192D6C6DBBC71200B88DE9B4401A0B8894AAEE8A106C900636 +5DCE6154889ECFCC4DAEA9FA9C230CE945B090647549AB7CFB3A7DDE4F8A5DF7F2D470669883B6546C69C9044295C8737F599D54F5C38F1528A27E1F5CE7C5533DB7E19FB85E2355ECE004AC39E7444ACAFB4023B4E6E778BEB32873D83B5D7E4D05117A652D069338C04B2581083388D0326DBC8904FFB61D1F6E9BE5EF86155A10CC0649DE67F7264FA38951F20326B0C3C90851227D70BEEC33BF502E48C350207A231591D15FE7670FA5C0D8D8CC539FB207BA6F4BC1932447592078565C1EC7BFAEB0D3891E170D5417BAD7E7201BAF388C8D9A460B151647FA35E3A9A3D94FCEECF3AA35999A6C3C6000C99329FAF164DB561FC97C11A628549C90925F +785D55CCE56F540C8F145F45BB28AEC55717D065D674ACD61334E6D9E199AAE3 +5B809704E49E1F43924A5AAA7BAC17A184EB9E2DDD1514410257E7142C4009D5598EAC274A56AAD9B11729A99CA46AFD8FFD5C7C3DD86853FAD900C2F7BB5195540B9427218C87E8523BE32A8E664067AD18EA85ECB2CD6BE3AE0F5D4EE5D5DC4D14D2CD4BF0FAA1077568683B6383CEE8CA7228023DF309910A8AE1DAAA2DB91346FA6B1A8B202C632857C3A6B2891A366AAC6CFCF4D58735262295A65FB692D1B2E9B7F190844311BDD32C09637E41A7C05C6A4FA704535753DF93DB76640B9F430A9AFADCCCDF7E5CDA67FC67616FB6461B48061AF687ACD86FD127209F6FADBA70FA7E46EB377CC01E9A55ADEE3D0822ADFF8F3403E089DE00195D0F80B8 +5EB9D5CF85A1DCBB4E8FC780603346107B0F2A97BDB3FB890E60C88B75265A0E +40E9E33C19B0DD2A97EFCD9BCE39EA376867DE7BBBF68199B46647BAE4DC7414A88AEE58BBBE1EBE121050653C59E32C860AF4D29C33A380254DDDE436A86C87AD30765925FA8F87BEABF820ACF0E3FE96A604EDE1A672DD4CA4399EA0F57E0BB9C36CDC045ACA41368C8FA5E326EF4A7C8F8DC2C046DD68F1F73055F515078625D4FC8BECDCC23AE3D5EF9447EA320D2E25B849BCE31A7C299AE9206A6443159CD3EC5F3D2D2C3494D334C4537D23BC15556230FEC599714C3C0FDA1D7851A537FB1BE696C2A47A366885E7A66E71FC647BB0D56C4EB1B37FE4E405EDE249EC2E293332D56B74CE077D7CC6CA2AB0BB13CD8307A4E788B9ED7365262EF00E74 +116A158FE0F59F20C7D08923BB3B9457DBB51F57354251397A5CEDDF25D1E8A3 +3F7607BA40E8D7898B4943D9766F75A4B1DDAF62AE506880088B28E102223A80FC4299F023D666FEB9CAE0AE88D909CD5B53314273D26FE8F1084B5A4182C87C8A59F45F6B77F63FBD119878E0A29FE82FB9E146FEB687F0C0D37EA5674F2B9C8BE3F44AAC46B9D50D06F6F5CEA9E70C28BCED2E9EA137E2A0EF2834164D542583B0732FA67FE11F8CCEA104D5475E87F533AF8C0C155B48AFBA87348A8DA41A3517DED03E3E849BF3A4790624B170F517629AC5D53677B37F8D27F92D999466E18217213106B8F7D484316040391B51E2BD3779E09178B0892961871F4898F3D6EA1599F460CDF73A465C80C6C3F0C15D88B08B6D860A39A37F9C18315977BF +4C3A0B160E97B5EAFF7BC1007659305E3179C7A726935E5483DF7AB73F30F81A +00B934C0B9D87365F87C57709169D91A1BD1D64969F14B1FC94F9ECC9F562771F06EB1AFF2D096EDC914383480E17C2D438D159CB07A6DDC28009B8D9F69FA055F5E8CA6B91D2633DD367D4D2154CE96ADA319B546F05E203577EDD890E2F981EF8EEF086DEB89A490675BD41E7D49A713EDD42F44435CC93F81988D916D665BA89CA0BB9BCAE888AF87E2B39D28ECBCBF06D0CC36B6A2CB1050A30A3148FC3144A56860817DDB6E4BB5760BF90D12ECC45050C3647D45D17FF13436606D0078BF7D97A0449DCA2D11D5BAB83F613EF48929A379451C6D3B7C884CE8736DADF9C849665B1F6CBAE5403EFFCA3DC45857D1A8EB15BCFADDAE0FB633AA1D3C680ADE +27CEF3FD98248D7AF3C9434FD44180795AB766D2445D1193BB386936F068BF8D +40DEBECBC21AD4CA2F4C463DA8459DBD52EBC7696E450F8257AB500A69D9ABFB9B0D7A737F94868958525914CDF74939874D602E231FA5D07F5433450F4653F71C2A19FECCCDEE099F22C3B4B9A8464274C32FCAE040BF1D4F521007F19FB767EC98DE74E5D2C01B33E997E4A8735E7EA9CB23F170734EF5942C8171D315B4D6942704867B1D44287E1A181F4473CBEC3EC09899EEE33623BC9439CC6F4FABA6A3D0A82AD047BC0D83AC13A16E6536E3BF34CB652F63E5237618FF9E6AFFF1B2D5B0DA99C46B1F02B941B779269FF94AED1BAE756C8E229D370407489783B816A02A54119049D385C63BAEE1A0D474E5741A03F7DCC7ED4C0CB0AC6BAF95099C +2D81A3054E8D5272C4DC622C6682DD8B6BA3588B7AFB9585EA110B550A6F91C5 +00AA1CC9E4F4D664A37C37EFA0F098FF07280366244A0980E5ABCA30CBF2E0F8F3497D7F1F76E5B2C7FD00D532D3A554DEF37676AB70337A74090BD6EAEC310715423F0C579C4768CFEB409208B47B532F95C2BA199F3A8C02BECD45C5C06A63E0132A31906DB4474EB453DDBB1FFAEBEE838AEAA24DEB23E7DEB30AD2FBB87C24F2B58D835D5EAE33CD62F5976DF917567D64C90300B22E08ECB11852AB2D7555C52BEE00D4AB45FE18E390FC0ED437D4B66931E33F39972A2A368FFFDBC5379648F13997EDA2F53F7922EDF08B52AC87F7011CF3601D44E928FDE3E62492079EDCFC494653940A67F5FCCA4F40C35C65A27794B6DAC517E6C30D2105C95275C7 +1E2320F0A6A0F36BC79A6ECB48696AFF99193DF8A7A144B20CB3694870A53F +00ACD2295F39F287018FE1C1104C1B927818D5BB99CD02A6D6BFB6EA37BE25833AC37994ECC10960581ACE71BD3338AEE597CF8AAE08CFFE73121FCF63FC5A9E8EAE57F91A561079AF5E348E5316AB6E8B7CFD81CB2A0A08C79E72A598CAFD83CED6D1250A89F9A12BEFAD6295974AA1C83DA13561EF02CCB1F030C4FE4FF388D92DA4D0B68962BF98C7748B46D08D2A46F6120B559433756454FE2738A1A148F2F4B07B183BFB8AB4B1908EDF5A82656FD07D73B0A54E127C41CB5D1D451463D0F15CCA0CAD9001BCF17343B62CBC87CE5C61EA79CDF446A95F958CA3DEA1D29CCCF45EB858A71929D2943168C11C112081311285A52F21A6EF4E966B8497FD86 +514485F318D702BAA72B99A87748B06F342226E393B2D87EC0DF1326B147AE3F +49681A131DB5BAB94B2C70EB313626ACB93EA33DC9865AF6D78BA1DF02C269E4C3E09D3A1908869C1E53230FDC46F73050249D267235EAFF7799BF84A744060AAF5D8F6B4966A9280E83BC1987CA4457D86EC8BFD59CF005F71ACFE83AE8D14C5093A8708C6CCFE72200B92AF600698691BC60EA4B4DF4D3DF8752E8A20F6932C4144F76EB9E902B3505044FCE7286EF9B4E912FE3CBD2EE8FB385CC2EBD3F6B69FDEA112205BD89B5D3303B36FA843F75CA034C26E109D8AE030534CE2C1FC64702EEB553A2D4E7FCA66CD4F6E018A932ADE855BFA01EDAEA64E272DE51C27C435E6E983768026CC3D41AF2C91C2F8AEE27B1CFBF791E9BCE8145FF57C3C19C +42720DB43689561D1831998464E43C3547858F032EF0F6FD3B40F5C5599BECE0 +00B4CA14929EEEA8AC4171FE586C14FFD485318918087CE31F12BA13CBEDDB2CFDC4031EF8E06B1A216B9C76E09ED1C44F43EC976D1BEFEF6FAA4CCC024AA27138802B87FF7D677E88E10FF2699DE22FE3ED4CB1D09413EB449FF02EDC15F322DC4E47A272ABCB8FEB0CE4B011E511F807B5B0D2AD7F9E2D913D6B5DA51C15C7383BF3B09435CB16069D3A561A87A259F0210A703854D18B8212915FCCEA5606CA8A3E57211AA8CDF50ECCE897C28E97CFF8F37999EF23254D46B7CEB994DE229267748421922A0A72605C59BE102CF0AA73F92DF46AB0964A65FC7DB6AD83B40EB00D35BE04914BF893EF2B81F1D5F725DFE5E384C862D1CDF7902F5DD64ECBC5 +54882404ECFD61B7BD3C225FE9D5D82C4E37E116985F7D6AA7ABB8DC2A02D146 +00A28FA8BACAD6C883F7BB6551186EA430D9160CF201377F8479ED70765F6F06B9E3EF644E3958003A339EDBBF34E7879391E5841A8C1ED3CA7161827FC9F4402A4FF12BCBE12474CFE59301F8FE69644C7A2628BF34E3BFF162DA5DB0222EECE2C5DD8E342E69A254424383FD7B77C34EBABF5981429B78DAC5D555397F72FB65D81E03931B637B9E03219A595EFA6DDAEF589111C09EBD4823378D82A75C286BE8F7A984EF523CD64528FAEDFA2A85B98A642CF4079427B94CC13BEB8ABFFAD8B7DD98D5E1E01B08169148870EDDF3A259A7AFFB07B5F3E96988C95F3815572FB87BA6AD7A02CA5A42D72BAFD6DE038B95201750245475DB5448135F04E87279 +4940BB1E4B28F5B7CC1D759D6E0B1835CCE54454666E8490997737C7E220FD39 +2E4EE89C57C5C51DB901C024E770B0DCB822E0E30F078654E2AC5C1E02689A53FA486A86B3B82BCA6FC80733F93997538ED783F3C6477641BAF57ABFBE3E6BFC518546EAF31F47DF1281EBDAF9D0EBA3633CA99D1DA3383CFDF9C6FFB8E43593EEE413C6AB804912377771E3A59B495A7EDA97F71FDAB9BFC353A19516CB74DBE92AC1AD26FFAA38E9BB9C8625EC3D7770314A53020DFD427309FA781EAE5F9F50551354A7734DD4E915E59988D8591A67900A7776B8FFC7A02FD1BF0DA5FB9590ACB4DE0BBCC546672039499F8DB522A8D63B2EB05F6D81D8E1EA544E29BFC970719E0C95A4C97B6DA86409937A27C58A827F5E1B32A4784BC1206ABDA95F85 +69F0C64018EE72792AEECD4C63226FDE90DFA0A1E057DFB5B789CC3F3074607D +00A7360C5E8564B2E76E3A1D6BABC586A32F2EF6BBF2CDC74DED8C0E21AC74CE55DDFC7D8663D861901EB756AADBB4CE4C4C33D1CAD0B9609E68446A87C202A5DC1881A81BB4B47EF016FD310DBF43EC483DE1D26CACBC3E875AA2CD9D90ED073AB36BA130CE082B60FC9AA943F084455AF83DCDDC5B00FEE17B349A2389F80BE2F3EB1DE9F21A35FC90DD34EE902EBC9C17B765B414ABECCDA3C65A4F7825906CE5F882E35D4F9F57C873B507AC15F0B2186B5C8B6971FBDA83D8C466DFE70B8A9DAA3DDE56505EE2A80BB086C4BE07CDDC99AFEC504D3A5B0FCFEBCAE23D33F48EC1F964106E030BAD5E888F3588703A2F525FF1B9CBA7D035680E2959E22FCF +7E1E10D9D439B8511932E7ECBE3655C0C2ECB710D277B3C01827574D2C8AE123 +33C06BA82639BD5F9D93D7B6FA21975AC04CE162FEE639282AC30B5AD60E27666B3A88DE7F23EC7CE0654C074F32C4C130E30B2289BCC249D858CB8610FFD8063C906C4664E93929A824B8D6555876D704A6AEBAE5D7C6017BB6703EC36F01B731A325F1B34F6AED2F1512623F64937416E04C860F12E79B891F293267E067E4FBB1BCBD0B6AEC2F19159EB1D81E3DEB000EC618B83B91F316D3DA48BAE2A9C2345BC132A8382F4F617658E21D04613D23F023CE89D836B1A247C0F5F3485A11323042A99B6F6D48829FAF3C294631C03476495A228E4DC89D4B9AB5BDBBC7A9E5BA9FA2DE616B68C31E982DEDB1F131C597F49DBECABB247E9A1425732515D8 +3311A47EB585884284938537B8C37A4B9DA3D43EBAA779DD7C12DACB7BC49727 +3CC99DEB33633C9B80D1BFA04BBBFE34C33A0C9D55375C3048F920E62522DFB094E427E7190DF8A027A7F1F50D38C64BC7531C074B5996BF985B608A5A577C4A70DE11F20854959323A4D7CF9F06DD7C3AF5338E649B0D61AA5191DE3CA86FC2584752A07DE0ED7950DABA44E5098C360AA8D3DABE73D8A12AFC9EB87027E31AF45556BE3BDE783E0BEB89FB7A758CC88B3F389FEC912173F02ACF7775B74088E6A90CD3AC34011BFB4A493B4522AF6939859B548F51A5099B5062530011108C83A151B81ABF0A41D6C6CF68272AE13D1C34433DF60DE7D59BB36D126CA9BB8CC40B85C7A31A510922820ECB13C9F1DC5C527FBC33E0F1A62EAFF181BECC833D +2D07759BA1B336AC42FFB59D1914B56B5FE51DCF6560B71FBA1F39528568F4DD +008BB1347772FE503BA1E5528194CA2B6534645B699995CF756129E7537C224647746DA68E8ED5676106BE41CB7D24BC94000F07B3EB094FC464320C6D710B09AB62F898E54D79BD91768452B38C888AA41F9A48F9A8099161E9E56442D0FA4B16A07DDDAE354E486C12FF2F0FD73D87C0C63C83DEB268929B863B1B108BD17DD141F1F5434ECE916CE1FA2D4A348D064C3535A589395DDE886281E7D0DD51D38A8E63827493B67B7FD1A2F1B22EC694A2809B4B96693B94E8D95C3C8BF1E73846B77DC5561DE7A3084BFD753BC1FF5DCAF5B1741AB52C43282C14069F068745B5C3449711872D275B15FB3C964E8B76BA193AC1738C811652FF7AADEB1C784F04 +03EC00C5B811419E731A48C1E77D7D9F4AF342354C8B38F8AB64372FA6AA9FC4 +1C73969165BF649F930B4D76B43EA6F9C5C4DF66B44F3E947635C16542A93B095E46AB6678C35A4BF1F9651F613A9C657305829FF7525EB551EE26EF3E05E0D68058269CA570773EC0C5E6388E05FD09C443733ACBC256CCEDD810953DEB6CED820D738D49B6337853C60B8AE79436DEA91DE32D5A32BDF1C2891E3DE6E11D98C072D0C4346D7D8F094C5D591BAED501AFE867354981A55105D8CA40028ECE1559E2DD39A76CFB3DE16484A90EC4233BB37A491EC0508F786C038BB9248187D96B7C9574667216133E13B8C9BEA5E2A91CCF2FB3FA3EEC7EE52DFFD24ACDF15ACBD4D683E620FA3F951FC67AF144D946F922D97354FC58CCE276B813119A2188 +4F850C1131460CB44E71941FAA2717BF7CA429C11C745B0209E630B3EE5B9AF8 +262BA6E29EAFA9144417FB1D12F9FE4C80A04D65031CE41542516D54CEBE662EBA317D36A801D9430E09A85FAA7B83E4CB3BADE184664DEECFCAA1239AADD0853F5B47374298F9AC7000F96CBBDF0611A08374CAFE8507E46B759C1F21AE94945562E37F5355005D553A225BF48B3C8A62A3B576ECA194420B7914FE6F9F8D817F7B3497240BBEB2F211A6F8F014D3F37E554C72C37C86992A9377E1E16D6AFF6220353F5B1D2B7D0BE909E9EE61C3AB1505353A6D151D4F811D014776E70D36579F77ADCFCD36DAE3042ABB11E7D492A7853843F7AF891A470F00CF3A01F3ABE8EB5332B625E3AE4FFF4362EFF2CA5BA3C863DD4B68AAF6F9C239B0BB6BC2F1 +7789089C00EE2F136D1F15BA5307AC160CE9E92AD11490A475429D946B86E944 +00AD1CB94D83452FA96CA865CC7E5421373DE331654A31EA43520A437D17F877825AF008F46587177F415AB01DD40AA9B55B1134242D02D66D6940A5EE9F7AF70867A124521F8BAE7B1D299A2397FC864481BBEC596DD7769F74791150A9221B875C2758F5752952F40C142DF2E50808242B73E12C21C60D781BF21D29617B1E6D8AE4EF6B34D22E98022D14FDFE6FD15BF3C8AC99D07A9E502D152D9F989175684EDB30275456572EAAA686942EFFCA1234DEC69C7854B329CE9523C1603904D29C6FDF947F4D6C8D8E7007A181BCFE01C8D168341E8622F5A0F7C5D7EDD0C4B4A35A9C592C596C93C1F79EFA76150A181F69CBEECAE81D2DB92F9862F5CC09F5 +37C648D28326DF094B7DE9690B9F89C1B5B9E095FC9178F7217C2D38D4A4DBFE +1D740B32AB61125911AC630FE78C9512812572BD16C777CCEB5069A79D9D15085787AA9C2893DC673FB38B6B1537F6737E1E2EF98C2BDC8DF729680BC2CAD972BBFB3E0AADEA8608F54852FA99AB754245E7CBB5CE7B20E7243C7D956CA9F453A3603A74A981C9FE52E3AD54ED8253FAA9392507E14FD35FC43FF7DF21451577635A509C073D89BB94052F415666EE80074099174697F90C75458F6A169EC52B1EA40B71A7BF559E12DA2ABB3ED89655F9DC0D356A7E3BE661769D36AB8F0EDF57B621B4CE6989AB0711E491FEAF6C49A3591FEAF320B98B6A56D5059DCF558128C9382028F9047AAEEC3950DE1B9F93055963BEC5335FC7BD3F26F8425813B4 +4F6E64045C15FE5B117A0A6E2D50875C733659468C1D7BFAAA985A4D81552E +1117DACE3062C23425F569C8B9C624F22DE7FF7BFA512CD154889080C6D9C831BA0BA57390D5F036BA8507B2F7F086A41C14B3E47A7D65BCF541FA34A26C774274AD3CF20864ED6EE104A6131E69630770CDDC42B0F22D7994277AEADB7FEC2CD0BE3F96B9494837B1523DC401E5F8C3EE42B2E8ACFD453B32C085244D2E45C86BB5FC2CC22F6BE8E7DA1D72840D3BEE10713A42607E8C030AAC1261133EAC3F400D835BCEC928D27C6044F0B824C2049C676FADBF2EE7AAEDE7A4852B949D04D33D01FCDB860380A4B2F7404BD13E8A3F692FF0F1A1EA478824358498E3DBA8BD29AABA18F59EF065910A0668F3FA4C1BD5516A1DB5F588E4CFC9D865D9AC4A +6AC1B9FD6AB63FA8ECA112C4F43699AFA92CCEA493E6677A91457BEE07498B7B +330EE896E30B6E7FBA4CDA86F97D858A844BBA3200DBE46FE9E70E95C3068F482CDA06A8D17599C3ED0C502E58972957F5DCB782A13008317A5719EE1252E220B3F963D436BF78E94733BB18E3DDA5B70AED79CD24FDA1ECF0901F36A6C19DA63AA021AAEAF1AAB68223F62BBCBA2E1E98CEC6837601263E8ACC2A9453365405FB3BD95ECD50FDB3DDA3556962C7F4198D97837E466190C36BEC9A1E887DEFA8399CF13B140591B09CADC428D57E467F6539827133AE5E8747A11FD2F68029589F151A6EF7FBCB51BB2313C953A8F526C56677ADEB8869520A78DE9D31F681E830699D286A0E8FDA5B1EC4A965259EB40B4B37E9BD4E4B608533678AA2003E14 +3E15FBF6EED7D1D1EB2FFF0CEAA009E867BF0DB68B3F053DEE62404A14E77E86 +314F2F77AC72B47DBB64E971ACA373A717519C6D612FA58C73459710313C8F5FCF2ACD86F47DF0063CE73ABAFAA230F8743EB21207025BD18291A18CFC9B96E8F9B966CD3E7F06680769CA6C75A725FACD2CD866B8F8259C7AECA1D2C414EC38D17A0006720C31B397DEFD864C64CDFFD893199953C3A7185CDD1AEE37815D309ECB28B58885C1C1AA5AEE42AAC5B738A84EA19B2F43E7662BB3C8C7E5ACFA41DF92AD44A9FC7AB1E99AA030623B4C67B192C608A873E0CAC0B04591677BC8858D0BBE970C97B6BB71C933C90EB1A8450B4ED5731699F7E0982355A77662220C5B52340E78A9D0F7EA5831109C7231D55979D400795357C79E4C97FEDA3B3FAE +7A34A62665ABD550DC0F49DCE16E6839040C55E8508E5A3EE9C79ED9C0B7F066 +64A74803829ED3F4B8D988690C944DAE680D8E9727DF4B35637E4A374F3EDEC98E7EF92C64DB949FAEEEC648EEA5CB69D607B38A97DB9A21684C3B6B46D15FC8932194A6029D7826A084FD07A07D2709A62663586DEEE3724E6AAFEFCCE619598588DE93BA61045FE7047DFC2C7F7163014AA47162D8C5718FF6C5BA7380A321BBC0537F89AB9E7872580B02670B3A191BF4A5CDE4ADE7CE68AA9E6649DE81173C1BBB6078AD36457D275925D7D5CA85D0E3B6F2C2C9CB62052268928D62E114CDE643B8D4DDE6724823FF087B5413B7D85CF892075F2F3EA24CFDB1E74994D3747CACD1FE456238FEAD26F0BF33BC9EB96DCB54B5383A1207EE17A6D29A580A +0EAD33C408BD90EF1E447347ADD77897A5396596385809B9C5797459DAC254D2 +00B8B3BB722C55AF4E106E6C791CF078CAD37139DBE76AF50368E54F3501A93D884D3EEBD5CA633942B7EF10EDC288B94B3015CA917C51A387F5556074285F833323D880CADB97561EB6B5FDAF690CF206D8B2A835F963265E97EF61B7F1F8E6F94D0700736BE07DD264DEF7F38A68C6041EC3F1A958B083C6E175180BEF422954EAA5EAED3184F4E25D7350B0CE04F43697873E5741BEC43E7C5740666C327D6BD4F9EA4F1F82BCFF91EFEA37F6355315B457BD1C16908C53647A1657CCFEF880693E496A068548DAC44489CB05ADD9BD9917B4F070E05872BC0F40B7880510B35CDF31C9D7F2E4AFEBDFE6AE6B1037D8EC1165478AB06D2D3AA469064AC92DBC +7254CCE04B26CF2E9ED9E1CCA74F63D1181109346CBED3CAAB0AE11742810AD3 +3C4CD4D97B52177226FED1D633E4930418696DFC42BDCEF828887A592498BBB858B2126D1C560AAF82DEBAA823E3A9C688F6F029F0E791269D660899C0914AD8B4CEBC7B638ED58CA102E4BF7FE087C4C0D37D8B838220D1A040B29D019371DA2046CAD8F3CCB93BBE99F41AF5A944818D48CAE6B45E7E4882C39E2408B70957D6B716D7D5777B8195B7C9B98F3C19878C009F09E2267C7CBE69BCD92E1107A9F7521101AE43B7C18ED333C8FEDB34FFA9206ADA94E41F3A691A2D961D550E4956676CBB7824A596123CE719AB16D0AD728529C26BF347792342283731563540A0BD030ABEABB342A07C9EFA8FB1F55529494C53BE96E6E5D330CCB42F44D968 +50FA42E39358F54A71ACCAF8565148EDFBD587A5ADCF48B6123A87F29C799133 +7B2DE3D053E03EBF44C4EDD0937EA7B3AD29A15430F1317E217F75487A9D605BB1DBE624B75B987F4A20685621A61D38C83CBF927BFA5386D319F40270E997BCE0E1D9498ADAD9240CC210F13302B1A2D36F70340284D819F844C918B0371EBE3AECBA7E8E40DF4A43C5AF6ED5FFFFEC92D93365D10EDE9CF81F41D423122D5E9973D8F279C521989F1923ACD15DDEA5A20304164F24E5505E097827C8B1CA568617139239E2F59D2853A66C9B0E567A6F30D4CE6BFBA23DC5DF9BF4E6145790E8DCE601CCFA2DE74C2877A72D26A7FC3DBF14CE09460BBD4B163957A248B3972E80890F0645F7A7D9746FE0E023EBC6D9DA44D8D4E3A4C86E84DCB658557B9D +3CAB9ED4325B0C5532CAFB77BBD8FB41B7431A33E1A6C0FD5B0DFB39826605F2 +3F938E8DA25221C4753B36E7D4FD8C2B7713BAF365E4A156E9244833E33A2165D0A86271083616C55DDEC8950DC490ED0C15B72F783F3E4DE4D961B762F7E952111BB9155D5C3B9BB2E86F921EF350068D59FE5C7BABAE77D474630D2FB3A8734D0E1E91723FAA8FD6C53F5014034CB119D0495F67DD367F4D3BDACE410A8A665C33225D9D46B33A56836C3D22D119F79BD796B6518BC9214927E50D2559EFE62EC0BA17ACCA0BD4BDAA20E2A44D42DC94236BAC1E69D516E95B8EA1B9935E16349654FBF6987DEFE41EA5D7C85828A6E09E57CDA9833C16A6A9C5C6FAF4166B6EC600BDC50204DDA8EB119B955455308D2960CC494E53C7A9B76FDB312AF912 +4B3A2AF030B3DC76C83487D0D3663CCB81BF5746677612D30FE473A69C6D677C +00D1B1C5CE0CB6E4026DAB49D83E5627AE16DD4C9234A1611B7F2E5E011D7C0F42A26E2094D8A2948426237189EEC657DF3ACC7A7DCD1A204FB002F4D55D486C737270E68C246B06B5BC95A22F1304DF8F841EC275025A4A25C039FC5DB5281EE5C308A1BB97515121E8A90A299D0DE26BF5D17531FF2742DF4F869F66F6011E0E967D9983C78124A7B2CD38847B046376E8CA8541616EADCB7800DD23BB5EC3406BADA0CD56887BDECF21EB8CA4594CDEA28BF0633C45076EE2F897C293F17234AC3E2929C8B14EE28CF63A3CC75A1D47ACC3C1797B189F51A5CD869DAD2C804B0267140FEC1F0C5D60E0E33896A0A72F0421A4E87427AAA693D375CA551245A9 +7721323FFF50A132472E80F40E4913D56AD557BEA9D0DDAFEBC700CE468384B6 +195DA6FDB829554740359CB58F20F533BD1F9959767B9936949DF80DAE55CB9543A89DF82011743D2AE0B50CB0C6A7DC9D895EEB2A3B6106AB76C1ADC6A32540606B1D7506D25FA5ADF5D77D3E93386EF71CF701B65E4AC02F55C122A350705C824C79DD3FC6DCC6BE6DAE1787798EA557C06CAA44182A5D5A9FD5E6F50FC91CA7509D4054D7FDCACBC3CC83D6CFABF6CB50DDA09FFD4812A86BC3FC9038F56B3C50439D4A7FD45452D2B034B8FBDEBB083F2D79AEB72AC1A8A3A4F075BD7CF7FB7D508BA730236D193142DCEE1CA44727DDF8D233ABB1130B07AA91915EF9BEF82A0BE3E940F42AA2CBF39E328A972DB9056D339F9D262233FC198B48BFD3D5 +766B7C247A08C2B209A06F88CB235C021F87B532BE810F53E00EB4AABB698C54 +011465D5B420B6DD08FE8B2EF6188E90A408ABE8820B8BA117E5C94965EFBA31CE5BEFB93D1A6DA9F52AAC38EDE128652FD27A05AD75D88A4819DE9D46DA68682D641DAA1B04AAF3CB07DC9C2E27E94961D3BAD846F2B006D061B2D4A6C3A88E520CFDDBD58ABEE3ABF927FE2F774877100DB10715766C6966C90F3617F4EC87CB90B8F386B1CCCAA6C01794A290703666131150CA93423DA37466B0F1402AEAC30D2A61A3B87E30D737621CE29EC86EB90814FF03BC7816068E3C91EF428C73531B091E7362291F53CCFB6C1D99AF62B9D3CC3D63487CCDA16FF852578CECE8EA865A06D8BFF50C4DFAA7EF33E5D0B4D06AF97EB9D90E32FB046FD2E31F8CEE +45345AE18FEEA37C6485920AF8B29D93295690764AE6251E8F628E5DF955F229 +0F8CBDDDA5BBC730E80789D5B3C19D1B1A918701F4487C3D3C4693813D09ABF434EC2CC35623C11C52CEA3F4A189EAB2983E54B1EDF8A5FDEF48F00A15DA110448D3C89098A08968DC1F6F482FC596EE175686118867A4DD59CAB0B31AA894B4DA70C57035B8002749766AE6EA8BADA2DD62099FD20F021B0934DADE5F00171476CC27165C423A1F517534DDB1E9A9485EB70160E529186BB204E01014C23144DD50868BAFDA4D68744C0796B132781B81349ACC043B1FB95593B9E490389CD81E0A8D0E9A1298EE7CBCA58444FAE1E8918DF2E6916D1C5C9D417438B096A367479FA27938816823D65E4D5FC490C63D074F5FD191C243C9FB6BD5004E3A0FF6 +1CE9816D714583979080B33CBAECD387CD510598625F9D0E7B6F1EC049D29D8F +00DA716BC04651E5CBC8A655ACB7FE98ED377C780C437CAB9FB63269CB8C1370939FF49D550AF0C1FECFD9FE76E765B2D42402B7FCC75E72F9430A649FF1C8276951FBC4DB8F5F2D72F73424915428F2D746944377D7718A611252F605E0F26662907FB6FA556A9FD9B6413C6899D27A09CC433E123E16F8F53494AD49FACEDD767DCBE486357085C42535F70830D19786C740680910CCD39B083281D2685503BCF8B6BEC7B5A2650551BC76E9725961D858B66C232C754D8D1AA4D035C7B6611652C2AE5AF2AEF9BC9E8E591C5E25D1C4A0F9DADDE96D48103CEDF7B970B2EA04A7A2B715BAEBB5999CF3DE5613A61317768039418EF2DB862C6781E1270300E7 +1CA5530148646EA2803A98602596FD02E907613FDCFDDE22703713CDF276762A +00A7BFB1AE8595A3CE299CF649E145CAB52F67E161D9829BC40CE6A2DC394AE8149479AF871334FAD543018E80C6EE1F1A2F27032B5111F47743B1E7D37A49D4EEAA6455C04E9D1E9718E8694082BA7733C47F9C82BE0FC792288437E8296A9F2A69F275F1C3BFFE5732F23EA1B7E9494405EB30A8628235D02D810EAF720BC2491711BF5445959E48A73E521A78FE4CF0A88762442B5812A4E3D9488F4DA9E28A16536B460A930C3C3AB5750968CE273462DF1736CD20855C6A80D4E06BEB0990BC37EACA4AF593A58A10C325B52B546A28EF8E549B1A7468ADD6C717D24BB2A69039E14E6EC436B820F13776C5F32E25C3D84860BCB1DFD4215520D30887F8C4 +25780F81D135D24C020BAEEEA36187577B520A863D52F051ABCE8F30DBDF7289 +2F6DDB5468BFC594B7CD605847FAEDB9C91EE94486146FA0325D5DAF48DB3A4D311FD71B83D7F39369EFD6DED6DECD4EBAF71A695E9E8CBFCD5A75181030F9A49B6ED3CEDB16140EC9915C633AA3424C1DA63BC8F666F781621524ACCDCE774DADFAC0CD389CCEB272DCAE917A77D23E77C0103AB2F0238D088F22D5DF965A5A3B320866991F25AB49D39F530E7FD2982AE6D502D6BD14D586142728253FA8814E99D2561090B6C72F4D4D84E2BC0688FFE8D1BF81BB7F7B3072FB6AA3E4135FB1C1C24C6C90E03EDD64142CDFAB0A939892F66B2221AD2E52F29B5055616907463A3EAA36020B3F2EB76A549D922EC43BA7404002B88485BA4C726E07253C4F +6ACCA26191F51ECB05A0B03E3A817E9DB686D100BA0B4E29FC9CAD3482896CE2 +64AB8E880044B765205B7A9C79866E4F2EB42FEED3E31627BD581F290A4EBFF2DEF0C0FC14F0A05509C84775C895314F61A4CEC5617C3FB0CF467F76FE5C2C8126B352C4850F68BF219855A2AF7C3C2EAF406606B138DA6F84CF5DF2F22704288B61048D9CB10377B5A286D06987C5D7734AB760B0992C52C22E23AE814171D98B2E71D9EE17ADE4BD4A36D5D25177070365487864E01DF33B382F4A5831F4EB5D9C8CE3CCDA54D61B822E534649A404DF4A8F396303A0B98FCC1850ECA16BCE04CB1E26A659183601A95763FBF1307AF065A9699C115AA62A60DF25793D9C07446BA7B7A776D0C27FEE38569A9FF5A04524C167726078C4A8A783F5A51D288D +68EB5184723CB9544156F2816565D2A975D6B4177C6CB348EDC17800C5D61277 +00900D6488E644771AA4F1C5C145A9B164EBB48A14D62B7D72B6F4BDEE94257342E8E0337BEBE528DE79CAB270189D002266D21AB3AED4BBB37A83F45774010192C42E30EE139ACFABF5C0383F119E3DED298C653255D9E716227FF550C7EB9DC27547296E27FC48EBA962553D9E7666FC00C75495F367B8F9CCC520F5AA0F0F95B4723E004164B4EF98E4672BF0DB3349A68F26E6E5E3C9FA7D20D841E5DC0EE8DCAC5B8C49E31BAAA1957F17EEB27A1764EE491D8E71AE8F2CD4DEAEA052D29FB266A2A37AE3326A21C5E07B9066218B08FD31754561835438106834FE24A0668944B217F92997C9BD0E942657AF7EEA546CD39EF737D713A987C8AC1D2002 +4EEED28FC7646F603BD1C4741A20B6A7CCFC0656028DC0815EDE89E688F717BB +00A7D359C30D18807D79F920B3E03E8A4E0FB72CCCE035919B66F1C2CB4259D8F3747D9256BDA38E49F00C60A766FB58EB27A5C743361D41585FB91140BE17510DD31CE15C688DA41F56DBC68016E4F9F50D0A4DD3A0E71CBCC672C0D338B8CC686D84B1C5CD6EE66DDB03E9478C2C0824E6BC253198EC8D908658DCC470D906A9423994293E6929D86724E88AE250B5F234560597A285EA9FA87C99A990B4F348A3AC17B26F96B5FE480F6F896FD2589812F07A5F343D507C181ACAE110BB7924C3796E255DAAF2DC3792079D3AAD6B2FBDCC822B041FA976D9D79D52D95884D1416F628677BDDC5AA9E0F0C1166F9D21532B57C4F7B5118C3C2FC396FCBC6342 +0ADD034700D9A48782C20A175FB136F0D3E408762E14B042A1466B9586875CEC +00A84B7705E3BDD6DC285365FABD311C964FC937941970791970587C6B19D76D5F93550F48623CB25DCE27411D52E48C374C3D4CD59CC139DB027228B6E939BF4B20AAC00ED052A8268C967A06E8F37249DB451C720E24DE656A491D1A68C5F0E3FF06F756ECFE497F7151AFCD8BB4635807922B566FC16C8AAB8E835BEE359F6410E050105C6F3F8B518A78769DBF3F0337FE6DDF340EBC24AC71BA8E03B3697ACF9CA42061BD374BD1575BCE65B6D88FA52931033C41C4372EA1A0216BA5C44193AF201CCBB97B46B631FD6E93CFC83E3BD05D0BC6A97657CE14D181CEC87F9F3A7C324B50E4FF854292C6D9F01F0139F0749A9404033CC1D39FB441C4175BA7 +170EC2C1B66A7D1DD73F89662A1F8AADBBA071A3931FBC9EA4A91945434C9AD1 +00D9C564984BDEED9074869043E8AA012FE79121EDF63D183EE993137B2EA5D014B58B4339D864432583F7368E97191010C9CCAF4C52A47D040CD3064F3F5ED3F2AA374CADB2B354B228572103234764C1257DD736C98CA96AAA82B68DD9426D5181B96E9A5F1FD6FDE292D8AFBC924F89ADB3B0330B46D6874D2CEDB4CD927F60AFA2F57A7BA55EA0680D570118E699B781CE9D24F199602CE335E55E29903783E11ACCA8E8605627A99EE598A249F1A0C34BDE35BE1FA9528C7175DD498229ACCC4FD0E646CF369CA02FBF16DE5DBE03B92B0C305F8782B8E9EBD955E6DC0714B06E82443F99D60819EEE0EBF851DFA7D33FB4414B387C5A2A5B0AC93A9C310F +1247DD1A4984C145199E74867EBB371D7067B25CE71766A0B0E00F2DB3002CF7 +00ABEDB7FF1FFD56D51AF7C908724D161F3EA867057332D8985D3FB227F50B2DE96E7673BC7A2681C4FAA1B2BAF789BFCA03CD47659F2AA664CC34EBF1AD090A0E859F773B27D2D1BB2688E806FD9F351893C483444AA45A25B4B3BACE9EC43B4C96F3A3C1A8B5EFDDDBA1117A1F233A3A97C7B335D2BE67FD6922ACF52B7C21EFC9443D9C1956D680E73523498938F4271335CEE2C0E3C4C7C13E003C3C8BD183C9379EAA48D01BA1BEC597E312CDB89BE048D71A4AC529A00A696A92FEDA95C238979BC440ED0246D7ADDEBA8908F864113F8BE33575819F91322C398CDB8532D7BDB77450003190E342A05FBCF9686154F270356E8AC46D450E5A5C30E50116 +7E12D793887548F5C06D7C69968A4CD7679FCC44E2E7F8D0B2C76EB911FCB551 +1A82CACD4DE524801006E99D9E8B51603D8F0E79300FE98193A521E3B8142E7AABDCDC431A8BCA4AD8CD4FAE8B5A5A6D011C8B69346583B1F3BAD925A10215EB8BA84CA36010E9EAE3EE6177D229CB7EB0A874B0F39843835369F50986681DB8942D372874AD984AC2D703A43ED9387A7040B9316B1C2832482103883F77FC7BD858EF39D1E39C7E33C8CB43210F62006A0FE8EE21C7C9BD2170E3BDDBFA34A7DBCD162163415F79C271719B205CDE62708F25B450FB9C3FFDB7C22182DAE93969C21EF95CC29BB30F1B2A87F9EBE648FAAD77127D4A561BFF3E3A688B2659CB47D82E6D15AC37ADD698DE87DF6B43CA21307FE9013B9325B2CD687EED6EB3A4 +79725827FC0DE33293EC88FEADF122D9D1BC0F0BBE577865DDF11BB06EC9F587 +779690C53B577DED4F48D91E9F5F5DF0B0202AE3FE72E0E1E51AE674DEB06AD7E8D4693585B185A878626A5E29D429243FD1FD4018A34E98D39AC59D5EE0E63801E812D9BE1C87AADF17132A9E11E94B547F4AE83797BC36EE514D9244CD4BA9DD2A8B0B40AC6200C844FB978F48AF478C57136DDFD1ED05426B5644435835B33DDA24632FF8CF1AA0EE56A0C02236EEDAE16123FE0285E086FD21E8EC1E5C6E4D8DEB3213710E82CF9C789B634AD2EF7BF2CD2B067BC36DA1B9744D10D749D0C8F62E24B2C0BFF21A317C9D9DF2881BAE2EE65AFBF743B582A74D6359855A054CEEA1C5192C3C14A913782234B36065CFB7384EACE8A15901F1E6269199A120 +0390C499CDC463A25BFADFBC01414C8904F94F8BC7281AE8C19267DE9E2DF965 +00B5F80C6ADFE747B018DC38AF6C474DF431BB5E4A9EF0AFA1F7B1421A4FF901D6C6972A837729BC2747863E284478520DE28346A316F60690FD0742874C9964FEB026F94AD1C962F63DA7443CC7121BDA49FBB6E968E1620B233BE83C211B695FF7DA2B9FF0BCB2759C712564FC806F1773A5623DA6BFDE01AE01BCFD32988B167088A157BAA7FAA0A0B9B3F40A7B2B711B316F3B2C97BCA32F8462A6C70751F9BF36BC421155B61DA609CB222EDE2902FD4FC92228C38955FEB37EB3A509C17A1A839211813AE25357C7EA3962364E30C1250D29D1B9E88D500E83927B9A27442679C79BC0CFD28353FF22781145059302A0051A01F98E0A67291502966EB349 +095479C606DD7837DDC54F726D26972E3D6B9A19B13016E416828B6FFA19C353 +00AD3787E7010B599B213AC24EB1E1600B9BED02724054C4FD48366D95248A3F159817A0936BF26AFDB27B9D8A83451AEF0C89F9CEE177194D49817FAF71370F67B338060CA01EE15F4D0C43D8D4802BC7143C08723374440F5B872EE01A00A4FC08A6BF1B43D004BDD8732D68D2CCD43015D8DB2500995BB948FDA6809E341A33CCAFB8C594B14573688D7D60E750D3884EA3E2A213CADDC2AC8EE3059539DBB18EB27890B4493508799362670AE30A85D61173B618B643EA3855B3876D6D6BA340F2F1E0F721346A4AD653206D77983767999288F52186DE5CAA6E30D32B84763C91E46D04B34547C498B241C9F6B304E1A5B136BA1221A4012702FEA5B58256 +7664B6D38C1E8CBA10A0B7CF63762BC9649347F90960972B028DCED0A4D49464 +00B8EF88B451FA51E95F3898D64462AC803E495C776D76FC6216A62943C198AAF7A883E840C4F0D8C64433196386202EDDE1B0C98E44DA8D0FE6330BD1415BA3AC113D345A4817F56620F4BB9A20D14D6F24417AA5269EC3D534AF3C8536FFFBBD4FA83AF20CCC984A35965546A461AF24B13678C617A3560AEFA44D1DE977E97E2722CAB846DC1D8DD752495EFBCF1CD836F3D57D59692375272ADF81609925050E4584CB137E3BED01F696D0B7C78620EB73359D7645E8C40437BD3F82CA6273ED8A15D49267A4F000414B7E9E5AEC4C797C9CB9C8BF62EF36240453306063EB0227567FF2B081027B7453D4A6AF404D575C122C3D9D3BF84E671C2756FF2F77 +4DCBC5A9EEFDF6C0CFAA618A070B1A84A743E7A971C68D0AAA83E91422944969 +082E11782D63CE00AE3B46AA1426ECB8E338BC444C6A2D6E75F9285C1B8A676377D45ABF696D5EEAE666E52B94C84A64D89D54792E4A54612BE06489B0A4CFCA7A5CD7A558C926A84C016E52BE3981AF79413C9636E6212C5E1DFE8F356A290668DC904AFF9470526A06D28481C0663D4BB23F846C64B434D4E1FAC8BF7C80F1EDBDA6C5B90A2BF910CACFCA6604F5C64F95967530AD032A998791E66CF09FCDD17D7DF9E7AE6BBFAA4219360DA34AE263AF62A41AED0FF7A82F067351AC45F25EF1BF0480F8869438C7E2BF07220FF7E8D7D1D60F52BC54F1D89568C6F99F9DCA864C1B9EDB6D9E0167BE1892DA559F9B30BACF4BC85FDBB3C0DAB9A5AB5D98 +11B4FC86B029DFFF1442E1521094E577D170CBA2A5F8CB44E5CE05DFC9DCF259 +756C48907EE160B967FC17B0FD95A665F5BDE40FEA5ABA8539EC952CD053ECFBDF0ACEE19A125C4B7B5551ED1C09A1BDAE87C8BDFFBE0D2A3B5B9E7964E6C6140281557E74186E243FC6C03977A1272DBCB03FFC1B30B7CD66947E4C275E9959BAC2E535ED0B1433F9AD4DCB2F0393B97648122D3FA7B80E9355791C2D83B9CA5E3E863C1DB06CFAB452C0FC4A9886B638305B4D14A3AFDC2F1AA89001B459E9EE07308A025957A1214F471836CE45C5B8E238CC53920E4D24623C49099B7D3A5BACEFF6A083895C9F35C143E22B3479D9C13DB5F81BBFED775F87A1F96B198063B1E3AA3C37AD33DE46DD69D7FA17A0E81B5D9E5289285704C768517FF96388 +4D43A75BDEEECDA2BC542F5B1A4C58739784CEAAB7E6216808D73B83F7D32CA5 +009C001AEA25D5D44BD2F6A98CA85E0D3BE246EA07DC8623D777AD3FB48D1B97C53005143983F6A8D0B4593F3CF9771A4FA876B83ADCC7B929308B3651C7CA4B91708B5549860E3A1EBD7419E8D47737EB5069617ED65D33FB0AF19551F80A462A4F8AD9ED4E9AA711189996DB042AB5B49160F70480AEFA793A8297B3A085C5808D9C7A9262AB3A94DDF579496029B32E9159CAE659C8A42AD2B92841F347197036C99AD23D0128FDC7774F7139B90A8BBA9DD3B9FC1F8A4567D28154EF200AAAF366907C1D24B2ED2E07263A2DB98C6984C9A7F8920D708E5600C92104DE40B6E1087A67F21A019536DE774045F77E50456A3B80D60C597BA127BBAC2C139401 +2D751FE7188C936725EBDA9220944A2F3D709F3355469FADC5DBB7A8F276BD1A +264E63E16CE523E465821201C032BBB0A64553FC47CA9C7772DF04A35F21FAE92D59167CEC32AB86CF8241F627EFA26621C9D54CD2A35DB6FE22CE3172733BD004F7354EF091A291FE901CDF1848459CBCD29BD066542651CD731820465EC1D6550B3A10BD4792279DF544BD7E0650D81A83BB1AE9F2C82603C372A34419B7A8AE41770A9C989A337E45CB3B44F2F9A79AC0230426FD0E0493573ACAFF89A249F7AF8DBA363D07118CE8749FA228A6A13DE6AB21EFD60FB7866E7B0AB91467001C0BB502EC5013C07871DDA5B0FD54A62DC5DAFAB7B50121C43152E95CE1B58FA8C7788E65D2893D8929F8565F908942EF524CF77B6782F1F4F63284175B617C +36C4F85AC89531B6AE81EB9BC1CE452742A4D40A37FDB744E164BD3B8BCE5179 +0084C926EEB247F50DF141685019DCEDADB94A15C4FC81CC95CAF51EACED00F7B77990B484FFE3BAF1F485E52C4FEE52239B7CFBE38A897AA20322B0606573DE1C586997815D0BEF2BCA39DEF5A582DB4291D871D61BD2B719935F9D7E3B120C66F5A11004F3744582B6BC1EB45FE3D355D32C1C349DB26DFAC487B4E2711FA6FC7E8F96668130ACC4DD80E1A0941037EBA554C2126298A9C365E357E4E9B5DAF36F98A3C21BFC01CB4FCC12ED00F7EEB9224C0CB1352E3A060A11396772E0D310F97DEF5E3CD13B1E4569F86F6DB782C2849FAA5A9B8E0B7ED07F29C60A4497136BCDD12CE54409922605F8A291561FC3610ED88D32D215632D0992EF12E353B7 +06D17AE97100B4E82E233457757A7E968A400B0633FB71BD7FC9507F6F7AEFB4 +00B362824BDBA4D195B4EBF49F35EB7A4E023BDEA6109940DE1F729E48F05E90971E7787D5E0EE1DD1DE1094FF00C6A6704C0595BC9844C2E01DD7562F9B7F8F007AEA899520C7EAE0E74409C8510D4629AD7D1CE6A016C6DAD495A66C3E09AF47C633D8886458F77D12E08DA9653C1FCA5BB7407A3F47470778BCBDCED1792978FD8B2EB9DFE55C709C247029D8B0C77D327E45246D417FA31D392FB3C3F8BC49DB95460B73B361623BD8957B005341D19086C7DEBD973CAD7DA0AC17A13B0A93B4BC3BDDF4DC54AB7816E6C38F24DBF0443E28A8BA8F4D02A8E869CD7EE0812408FBF12D33A018BC4DEAA9A84D3C483781125208CBF6034A87AC08AFA06E3400 +1D87083F0A549B21B18FF0380907B9101A3DA6FCA7655F59EAA9E1BFF96E1BDA +23C079CA4F7C796C27DFDFD2154F0988640F4D3EA11424368C3F5A0A535CD8BFB3412FBDD3036B479EF01EF220DEAE6DE881483D65C0234FFFABA52A1972234ABD1D2CE7A5B06091ADA87443BBB526C843C68CFF3132DBB212690AA861BEEBDA7D54183479A417B5D3E957BF150755B409B8D353A53B3E973A50DECEFF373750B3274E884C8F6A8452B9EE860895034A75854911A023166D4E0BB3B39FA1CF1F5BFD87A6407FFF5B774BCF0DEA6D2565A232C2B71061DDC3797037A346BC02BB4461BC55F3791BB21CAC7DEF9396854D8299F17E0FC88DD9BDD9CA77A47CD13072DE0B279E4662B1A2ACCAC48BE337A8D2DF9597A04CD5DFDA284CDFDC4FB8BD +72F3F660C7B5C43D6A9960E80F89F6799DC1063C57A385715273AAB9C362120D +0089A373DC4C4B1B9AA64C041927398746833DBE3E0E01862BD23D1FE6C1EFEB46A79EE0B71BD909C94D4E069780C7C2E1E5EE232A8BA9400E71F66D4F1D27ABB924AA43EC880C0C4473B9E7FE8B3F9DE850E49C6CBEC798342871EBF829AD3A5C970E8CB4A1B6C5EC09072EB100A083C3E2259772D0E19F09DD88641B9A51E3DB78DDF65F13E003434950C41BB765BAB6B1799660B1FC1989C465F5B5DD2B21A9F37ABAF3646F0ABF2FD974FFD50DAB86A0590BADB3D5415B7F09B7E6FDF49326ECDC7939FAA4A415C18D5F00CC0C2E6308A1209C3808B9247D69B4C224ECEB02FA64468DE89B9691FC5BCFE17D7A289123183C04BCD68BECD23AC1C2A993743C +6A43E0731C1A62A9A176907361AA912D708D3C4161E27A2F6E76A6259BA184CF +14D11706C9D992378DDF5D4D9B7A88747F5C3F4FDDAC834A10ECF629958FBDBD91435135838EFF25C4A1EB6AAD17A3E797B68A39F9ED49BC2801A2C23CB11F066B300C15370E1A9A6FB8AA82AE838BD2389F1B2C2F0A1453AAB37EBD50C0A1BDCD6E0FC54A1319D44AD170C91E8DE7D3BF707A8B10DC25F7D46DA7F595CC77D809C809BFCE446DEF0BBDBCBE500AED6610A499A9A5B1224F4A737AF1EEEF3370C6849A648261995E1BD7B28F6C40E50C29DEAFC2D096B2FAB800CD7322F265313A03828061942B06B78DCCA4A4EA4019D51DBF97B8A99D177A498DE34F875766713489403EEBD70DA01FAD78F1D5E8935557F6FCF7181018D63B9A38681A6A53 +5D29E2830D96EF639C01FF60DB9ED5D781D325EF42FDD1247E68755EB2464E7F +319349509C0C8BA1C7587921F4AD1B524123FEE10B787013153B735C06389594E5F0E5DA2334F0B7E1CB74EDEBC1572BF57D4D8E021CB1D82A4E39F61718C89763AED7711AB6D217E1BCD3206CF394C1A6D26A0A1B35E1B2165EB477AF17B5C516D5D2164AC62099E9412EE78D69FB07CE2547AC69FC201E4D441C0D800461886FDF434A034261C1BBB526D221D128B27E7DF9796FAC74DF4ABBACFFB78BC9D213984077C2DB0C081DB70CEE0A3DE46C3BD6DA4BF9EFC8E0F974734236CA0DEDE7CEC24405B58D63F53F2CBF06D8870587707026C11D49C93E02FBCD6FFC14744A716EB8A5401BF3B85AB7B7B17EAA3F344AD31C94C70D17270A29484CCED4EB +6BD0BB3DE64BA3F00CD751008403EF43E6EFA51D28D4474638BBB697AE7112D2 +74791062C2BF96C7E4D4BA983C4887E69F97BF8D6BE07FEEF1D9AFE22BE81599A89C5ECE530663A542B7CB441A2D3AAD911D82C5503EBFE4B46295BA8A75224D237931A0863D9CBC63EB9E42066E17A4D82BB3CF5AD2C8ED7C3AE5836A21E2B5285146285D18BF67979D7B83F2CB940CC28B691F02809B9CE963CAB0A6768B942A74557128AE96BA32FB374BE8CD585AAFC5FB3DE796ABC185F571E5DE0D08FD1C2815B330A21814046DABDFDEF6E029E00415327F03C77338058126A4A9ED8104DAD7684FF6C68ED9506C37195C8291A640068F8B77F17DB9EED73258509CC6A31F6D87773734E8FECAE46DE6F559E2ED005A9B461641643397B4AE68C371A7 +7E331142029ACEDC93470C93B03D03705FEEEAC6591F8A7B740289624F632D56 +00A7A4712EE59705CA68B5F8A8433C0798ECD9A3152DC8C3ED92DB884D0EC7DF067DF2B2FB667516C70BB7C0BC66F8E6E731C3A595BDF03D64219A8F1426D863DCCDD231E7DFAB888EF748DFFF563814DA58A45BE268E0A84E147A24560D3AA3147A912063DA385D157E52BECD02A86B8F296C80595DC8D051EAA92EBD05DDA56C5DAC6573E710FB1D76EFE32B9B5E7400F54C090205B7996062465BCB166EE1EC17FBCB85D50AA4EECA157C5E168A622DD0CA8E7B095D9381BD1D7CA079F122EDAB8D9F4A24EE88765A68FA05442AD644F22494182AD6BA2735645008B6F64E2B395F0449DED324FADFAFC746CA1E40A33DE35FBA765A5906C80F99205422AAC3 +3E81FFDC1DD7F760B6549CE69DC2A57513D861F4AB8759D03BC88187D3091D51 +4DD0D7361D77A5A59B0D935CEC511F6DAAC6398FF22FB065EC5E48AD168E59A2855DADE9259B1B6AB72606AA2B189DAF2AA79556AD7BFBB32AA2393C4E56D11C8CAFF4C88136BC84D7F27B06917A6A01121826797C25B2F8F286FB595494536DEE5312BA4D8277A37C27D65B5A8141C055036E12A9CC8ACC8E69E8981F3681D12253C9BBF73A416FB701BDFB417DC7CC0AFE45AFC45E505843E873C75B0E4B46527278A2A73DCC5A06723D0517019CEE1CE8AF0484BDF6B8D38E610B2E1A5E3FD9DE0FE10F174EBB498A7D27D60A592FB33E498AFAC593CF9DE5D10742C739B25EEC15894FD7EB015D979A33256E1AE31BD7467C072228D7596789AF232EE910 +703F9B379E0B74C7BD87214DC6B8E292C24270D0600DC9AD69C0AE26ED94344A +00C00C3EEDB3D9074079115F958919C61E5AB818CD680A39585F269082F8F979F51DE61B07A01A478D1625B7154CEE8EA4FE44BFD15A51B4A1180B2E7588D62AE1935EF626183F10930900AD674EC8BA63BB822B962E9BE918681F341676706B284C39D506AA1BAA6F55E9A6DF290F3BD952DDA5ADD6000B5BC52CFB243E1AD14EF6C283EE3786DC71967CEEF9E71ADB27701C13D172C75B3827A9B675FDA88DC7B3440A59EB7BDB0220E900A4764D92FA63FFE75BB2B488A141D83C6969850C1FE1A443EF0AB91E9FD079E266CFC054E57B29D93E2031EF1604A1D06BC575D101028135823E815F2EFAB517E8F025AE8DAABECADBB87160F5CFD70EFBFEB03F14 +2E47CD0C7813A9BFF97FBC55C05FA515DB9E7AB6A42FDB07EAB0481CDB89BC4D +3EF94CF54F0EA8CCFCB28AB86AE14B7E7E56026A4E556C3557E4E2525FAD973B726480C8C9249C697CFC32B8C7F0A0B9CBB052A6EB75D2A5BB7B87B765172BEAAD545E69CFC51DAC74DBE65C96600504D8F3A59E7C05D2B2E12C5A35B9D1D7E90B17D2D4546CE7BF793966F24D3BC494FE4A1C4FFB1002C3FDB31A8F9C885A4D89C6DAAD5EE0CF0629C3AA2757A698059B17B3863B32A153B9878896300073233695654701B967E2051BD3BCE7F26A686225C50DDDBA3F3F1420FBE0C0BCB7BE5A716134FDF18562517C7587BF467F144A78F63F4CF43BE9D2E78972A6650D74E07CEB4B291069BE0A41210AFAAA161E1F75CD9126ABDA19EAE391A0E401B1CB +71AB1D402064CF63536DE04C13FCE8F25D0A6CF86220AB4C3BAA274146E81F6C +775BFB67E42B65A127FF77A1C081F66B4E7B92DB4E3E14C75207A5756242098CA642929C2E7ABD29FF32B329A543F9BDC479A4FB6783F85DA35AC84CBC00B98522971AE8F98E4785287B137311344A7C7E2BE787D66ABD97FCCF1778E61DD0C281402B74893AFDD7FCD1FFD458E3A811DC1F94872533675C5F5DC06B93997D187A889E6CC169791EDD4BE3286AA2E806AD1930D4B7751C7AF1EF3612C519642DC8C2B7D5EF209DC13DB67C317EEA90769A07A43903F376DCA424123398A811D83D8BFFD309D9033A2E74F09A0A4C6B10FAA18A400F1788F1D7AD4F6982D494124758CCF2B53E1BBCB225523534FAB3DC0F171ECF52D64F7DA5588D70688DEEA6 +44ABD32F3E90804A3BD55162967792CA2345883D6A7AB4D7094E4BBFB0F43FA6 +2D11917047B08C206F8709F8948D540F6D4A461B107581F2F7DF6554ED3963BBC5D5DE6E01F69D4DFDC95B7DBB2E34B0E4A4C09DC302B636EC72ED655BEDCAF1D35DE109740E83B5E65BF32D07BC2A51412708D5FF7592AABD85FF015700A9639FB084E83BF909D63BD9AA5BA9B97ADBED5CE683EFF04BDB62945598CF094BCA7409B6986905B3898BA002FBF4BBB5A3A1C947ACF61AA58471094C539CD1942E300D98096707A408DA3FD92A29656CD1BE4FB381195D5A5A4A4F4382E91F4A4DDE345C1C57AE4E9C3C5DD20E9DFC27205DCBBDE791D31757AC8F810C08E877ACD1DEE38F078F400B7409263F5221D21F7EAC61821431B686C15234AF7E2AC169 +32E9023DCA70083F1B5E95B0522F56D3E6CC6CA5A00D354E7E7C99B5E0764CEF +101F6132BF8CBE3B5E4CB1FF8D1C03541F5525B298631B60EE3C438FC065D34977CCAA3C15271D9D0A3A3BF5E3734E910834921847CD343F830D2A1882E84C69D5D473614CE175BD626BA28A1F53DB921D639E03C953DCC6766F32809E42E5FF238A3A9EDB4928FF9F996C0A63720A4529523671FF23E9B9713D0091D8561C6DE76027BF91C701DF0C1801B169561F632680A7823501F2F3A999ED5E03CEB5947AB8DEB4C78433AD020DA062B075AB37D1536B858DCB11615EDF4AC2059C31EFBDE794B7565B0B73EB96910AE348238BF98A3AE2068B63C3946537B4867E055486689C901B96A421D639B8F455999D316C2A2DAC791327399527F08AACC5D4C4 +33FD318099893589DE732C679AF439832F861EA419C00E4531F16A2B7F29C408 +4921CB2135C468BCA31496C4AEA0284F9015ED8E70867A424D3E01AE037171FC750C00ADA5FFB146809369A8B84A50C359CBA71FC0B6D4F8149A9247C8562BF169ECD73DC5A0A42BF5E84E748731D6357C4ED9A30C3A119E760480D853B6ACBC9828E6F5BFE2770C7740E5D1044DC737709EAC09CEF71D647C1E1E8C8A93A97FC7530C7BFF172A038A6C541BA69D566E9063A726FC42EB1D20F3272732768C0421414AD0B5AC741279F44CCBCF6A4457027D72DE7CD248B9944C99F449BD584E3A69A0A3EF0B9B4C402600E66F4B72191F77B41D079134F11D2835109F12DEFE2B06F51233299F75443B2027041CADDFFA5DE349E59047D92FBB78C4148F2868 +60E8095C23E250A0E9BBA399F4E06577453F95B3C6D5B923182840CD251A0686 +7A7F05A9671058028A86E4E1183EEBB733B6EE34CA77166AEA57D9440C201765FE8CBFE63AA8FD2B274919BDF7E0C48BBD5D31EB4C47DD3D8F6B65630D699CD6CCE4041AA42E03B7A4CD651C465FAD1FA61D7307FBD466208723D5451209B1D1D5B12B269C88A3CAA3503633954B6CBFF97BE31FCF804218E630F76BAC438C21CD6CA7503517BBA17857BA2A2C461CB75D6CEF7839D5D8B408366262E0BDE44A692CDB46EEBAC5A0E96C05EC3ED5E0A27FA23B9FE7A1BE63015E98CFC89E7C4C7B6534D95F7316DC9A5759C8174B80DD1D566B7CE22FE1F9D6D352ED5E5D10933C8CA7CBD7B3C9D83E3DCD1DDD45075BA674B1CA482CFE9B6797CEE71DE1CA7B +0B03B2945850B09DA418EBB3454CFF7462C04964D6B6C5545B4196C43B2D6202 +00B708E471C04529FED17A783E1B449FD0B699F2738B28326A0F51FF08A7500122A7F3FC0CDFF8E469112900E2E7E30F1D1FB908B5139C1271B87C7F90D16C5EAFCF3D6802E5C9DFEE6FCC24CA1F18FE5356956BB7FD956D5DB829502C363A5FA31188D7E3B4E04C625CF80867AEEBA6C93598303ED9E341E9E014D4E4BD725C0712BE878A64536395D73FB50D8F8CB45AEFD8BCB6D05AC39B642DBE7AAB170A1D337D95FD62A666159883244024A45DAD3BA4D6BA54AB64540A8F0638BC790236CE4F30D7145695CCEBDEA596291E01060C96BA68F6F0BE51B0B74191765481063745FCF9317D47B7F747C81C6E6EDE6F56FDE8A93FFA3FE981D431F93B850FEA +541E8EB4173B7C1E62A9D122A7D9F0B92C572C180A147F8715268626F4E4694F +451017AFA5302BAF02F2FD51B78E9C873340EB4D22D2152D872026B0826335E2615C28402BBB6A4BC70ADF3132A53576F05CE1E5A60C7DE6F610EF76ABCEE445440356680A0D680C3D42672BB593F41281C4536389E6FBA7C08200A8C9C202C2B71F733F41527543388420CEEE8997AAEF76806C24F9356B807CAD407B5A103138CCE31B34EE2157D08E316E95B6F5E7C6AE4EE29FB72CA6432E27DBBE6B384965AE1EA258B2BBE608C59745BBF11FA5F0648FEE9ED7AC274DE6A508AEC10EA5C4D900F440C01E609A23AC6CAD4647BDF1B905873D1E620683F4F18ED71AAC909E95B53C350899083A411EB2B4952F67D76475B53A44F65E1C4E1DE55328F284 +2F3FA8E5E0FC82AF4FB75080B148C03A7AD90D975132CD4F16C3E34E80532DCD +3845603EEA68D8C029FFA4C807A60160DACC7F20408C92989501F760E5CD22C8D75738FD4987FAE83344399F28615DD95AD2326FF1435DC32CCD52CD9C99B1645294E4BB29C2DEBA7985B74837F94DCF6D9186A3C249955A6C995E8AC38FA2A7558EA596875F4B2D925D2082BBC263ED31664C3E4645A672BE166BFEE526CE17FCDA572203378C1C4C784CEB6C029EE021EE7E0CFC86829181DB618BC3217AA5F2B04D7F68FFB625BB3C44DCF8981EECFA1E8A4BDFBD6D9DB6CBC39A9B889B2790FDE4CC02B6F4DF41470C03DD1F55677504091D5D6956D46F90FA017B6BDB2147F0DA828A252AECE01E3F7115E9F5B04C5F35F249716F3F833DFF99D47A33D5 +148611C7D15203330CA34B17F59F43AF04CE92FC1610588808CFB73E0CC51435 +1AE05BFA1947B590B7616087F5150021D807BCB5742C9F7CB47D22CC2DEFC8011E5A97FD7BE1729953C56BD4E286846F5FF4E3E91779C64BD65AA039A47A05666C87F480263EEBBBBA2DA75E343BAE2A0954A16ED895545619E744DE587A8E5ABE81ED3A323964F6A49F93AE07106BB6C09280749C2AE32DB22C737CDAAEE3239E3392E339C53D60FEB8BBC8985332956B8154781E6699C1192270E1041320B6F27F8B0D622F8A28F9FC71F088205AA149BEEA6157633E0182D486DA39281BFFB491897AB00A1A6C142EC59DC4903681A23B6E07DE819C19383A62707AC7B2200AC00CF459C8CEB679E43D8975A50F24D1F2827EBC8D6A07B351DA5A440D8873 +3DFACB52918DCCBC7E8CB5B137D55DB98F919832D89CC9168E0FAB330A5A0635 +6D987D2383A6EB95A6E236D98A0853BD7BAE792C07733577A5459C673A65533409E7CE71C5071978D703B002EC7D66ECE436EFB3B4A6BBABB33FCA85D33C957F80B60761FA66B41DC9191D5C48C2B6DF19E5760C20876E4330EF081BCA210A7EF8C3A704623CFA94207024ECC7F96B254690D808CEB338A990F67C7A6DF0894B3EA96045B1C543A9532532D7A9D9787F6310A9B75A9C112F431D00D47D5B867D9E508544F41C18A0FBDCFFEE47913B0D8CB8DE1DDC287D050927A18C91A5CEABC5D7A5116C1633979ADFFD5682CAB9A449A63D909B440EAFF2DF18B6DC67EC49F4661A2FB860286239A338A4B8CB66B1283F6FC7EBB3A70A65FA8627EBBDBCE9 +79AFEEE312EB9E855FEC8792885D652987C06AA7877EA0720CAD503AB9BD7BAA +7001CDAE5D1910B4B747D7750009491CC224473AC4B6CCA7EC3FBD2A37DF9D1F99B034D2817226DAF2E4B9DDC3D8E3DA79974D03ED6ECABC316EF415B2D6912E632AF3F2964275F6FE442E0AB68D993444872EC230773C73296F47C1FE46F5FA0B766866E55DC9D7FEC23B7FA3E64654F2097AB061AECE8C886D36931B0FD963D47A28C677091F9469B044C9B825C92AA935C501B9CC43B747A5888202AB56EC459A0D3C1F810196949030320655D7C4022DD24BA9648817560006B44BB489FC742607DF08D648ABB14E6D7574345F8272C3D52DD0BE2E791E79F0755F05729CFF2B09EE3E0AA86757BCFF74C76FFEDB2FAACC25F54D23E2BACAFB62526A8A7A +5B17815089B97804AC47E4909A5D5EE974B0C420BD780EFD11A2734DDB736471 +39EB6BF8761D068BB57CC7C17AE67A5E1C9C2CC1213CD4EC7C2A4683A72F9D9B7F7ACD592C7C4738777C7B491D906577FC2AE5E3D0BE4B719E5AFEE0B5B56D116FE1286DE48DF36E18F78A09099971F5022B91555F6A0A3EC11302C6FB44BD6477D3C4B6C6F69A854961610DAB086C4A23AD9E5EE155920AC1911CE24C0E231E1905DC27321875B7F77E7384A5BED9F528F4F8F5056838B64550D697286552CC8867B51B39317945D7A02636BD5F19393132189F8940BEF45AB22D398E47CEA0FE28C756D3E01ED58142E8E19C766D267880134A1BCDA3CF43E0EC734D74D6062E790585324B37E27F2CE79C675C38F1FA5A94B750D5A6596C70F5F115A0BDE9 +1403C45FB78F25329C56BE9C11180740FA7705C0D763B97FAE5CB45AE8ED1518 +6E8579BBBF58143B56EAF87B375FBCB2BE89548A5AF14E276EAAD078E792485AE7962212043FC59BFAFB8D5699B64FF4261E2931D9F166921991AB7C8D015F41A5DBAE1C70B2E02C9121B0744F2A9F161E145D392699A454CD2E640BC2518667F5761988AF92DF42A48E1DADBE4A76C40BBBB8A876A40359E5A86340898C02D7D61C7BE11F2BA507FD9B3B74C08C5249B8FBBE4D2E17C26B1D88FCB8F1E12964E6AB4F92B560F880BDE92A51D484B170FD49D0C34F658C8FAFEEF7D8453EBB268AB005575F5F7F30F4C6C146C1F314A615F6EBB80DC8D0D8D07276B1D5933A891EBEF18EF23F3BE0A3484BD432882E3379476AEE3EADA03390D5C5477FCF13C7 +2D319E544B4A6CECB4C728B77B6D100B8E6F7CF439337FF4B59513815E830587 +54C3DB8AA2CB5D613DE24A211E3FB0DC37F2152F2E5CA8E72BE53391BF04BA366D495D4B0101883D53C48E35988F12815EA92F85488AC4DD6232E266A50193F42D3D8D06C868A95D116F27BE59106EB8A8F13DA2D31780671F81CD2696492EB7C4C619024E0B60844A9BE7EF50EFBBF627E408D59E61B37DDBA92FBF0DAFB9A194C4E53D0E65B92A5F82A69C74119D4152CD09D958962B93077AFBCABCBEB0FCB7CA829BAC4E9FB3E0B068C57E80B82B6553876520759685573FB43BAE562B2D94692625085912E1910882C94006DBDCB2A91EABA6DD987C1C12A1D091E24061180E7B4E25AA7768E4897B37BB6813DA80BAA018656385B86A8E849EA69E2ED5 +0D821BFE6B94FF53423019168738A35EE0D09901F7C6A263E7ACA4FBE01D1F88 +4D932A707D64AC0D708CB7B8A67E23B85140779E01C2244C04BB8B203AEDAB0B5F9DBF73595EDDFC2AC065CDAE12C6509B063709D689975808D1E37E0E0A2C45105FCF203924BBC512018A912368861A4A5138CDE4139FFFAB87288FA5981C5523A46EB4A9D313D322900F0AA74C897F0801D884CCFE537D0DD6D18043096788C0B66E5E50FE1797443B70DE8F979DF50D2D125FF4ADC26BB43778059E8A3CAD839AE9EA033BDF1BDC86F3E11EAEB5A0DD40B08FDC7A9A54A80DAF00AB5B7A93574099851655EBF5002CCF50ED5A1CE2AAC85725AB5144F8DBF7A618703BD1B690C8ED129381B1C87016FD0DF1C7E9FF2E63AF7682BC5F5947FF51C5E0F78364 +052491FDDE7D7756ED7367F9636E36290B2FBC0872B75ABED16FC3644B2139E3 +0EDBDBA750FEAA36469F0942C241E048B5B2D40AB554B8E9BE45D29FE0D30D982E2A7942E3BD5E120B0DD5464A790072E3D80431FC10E4D3C4E697447CBBFC5BD9155DC9A0F8BB8E0EAA5110888695AD4718E85944091AF1548D355CC0A055901CF338F7B4E87744070551D99DC75FE9E7693C0654C7AFB6B9408866400F1174F5E79881A21C432014EADFA18729CE49629C05D4ED6DF26E3A178234B8562EF9C217044A64A9A54D10CA9C9265BEEA553F38CB10EBD676CDBE71EACFA3AC0EB3F09FB33211F2829019FB8D6F8F24544DCB24B96571E62454467E0EF90D7409D52A7F0DE2CF233FAFFFFAC8C5DF81BA519BF98FA35AA9AD368719A78342388F82 +6CD27660580B360791DEB21BBC3D2CFB06AA39141A384915B35ADF1C5A0C8653 +0080C56A9A7A0B1EC32671D380E7B024EF573E2C4A1F9D5D298367A8C3B3DF625244FCAC74260200DD0752C39A3694FFF0E41736FA5863C44AAD78EBF1AFF0787B9C27F81BE88EF050F27DB5954778DDEC98B0B8252EC6EEDC7AC02DD6535DBE37431545FAFD027BFCA8F988F5234F3FDBCED3948D536EB051D6E7D198EB6C2C812616E0C17B82344ABB7E075D47D71BFC81E833FB2A1B01280CB34FF4FC4857B3BD5AF33CF4D5C6E4B3901C4EF8FE7FAA7A8E7215D9E32C6905EA23E8589D40C17941DDE2989C8713E729D84C603614A86F8E9FE0E558D1FE1B28179CA69B663742C288D907BE5EFC0EEFF8E20523DC5DEA906DC8B1A21B8517C1C24B1F27CCE6 +721AA74DF5E57090DE610CA8114EA66D69142D475BEDEADC2D26CDAE7401C484 +0F2AE5D1F84BFA545B836D36B53478E1AFEB501F899259C43AB1284E76650AF4925AC7BB37DCD2591A199B80F170C101ACB8E3695A09D336A387D4772891411A492D555E25E0435774FC662038824D708A82C05ED623D3BA077688AABC48EB3F39C89772BCD688F4C7AA9B1CAF687B647D9E03AC3906552701DE234D2FAE8A95AE98C0F09A9A56CC50F34E848E0B240236ECE5A5A5A959196004ED169E094ADDD55E3CB7E7B51DDB8E0DD5F6124E35997BA706F10C20019821DE7FD64FAAE0625C9E6483FA464FE51A2309D7F590840E25FF94B376A39E9EDEAE145DE216FE1B6BA7EDD40B7028D6A533F4167DC2FD7B1BE10770A329307EAFB413FD6F1548FD +6AF5CD434506A9F86F35660D2522E8CB3CFA0ADDD09DF3C726CA08D6A82FD002 +6C1A3C09F37B66C290E5B4D6C6C2F90EF74BC98B2CB8BB720FA30B34C67B37B04090BF8477A50C761B3E829E27AA1D5829CDF47D5B05B066CC0B62ADE1EE5915BAE2CBE628C0857D9634E9C36A3CFBD6D93887E08E50B1FF852914035B14F56227E20B7F58AB9ED2D8E8050E49A60EDD720F262AE22BC278F490FFD1EEAB815548497FA4AE61636BD12C5438A7129CA1E3629FBC0D9EF2AF09E6B30C4C83EBBBE23938435A8A287B6F12835878D6ABF27ADBB1FC724E773060964AF053DA8117AF57E84329389817D2224255547CDEF118DDC3960FE0FB85ED7D0D7B0858D8BDFBF0AE0717441B740C5D7FFCA52BF66B2775E877606FE49D36141B8808A89A54 +54A0E255C3C1A9D9664610852F9CE4DD13D4FEFB75E06FBE693F2D7EF9A83596 +009CA069E95C566D263C3B8FB0F811543CBFDE223606262174FD71B8F9F219083B8437CBBEB709BE908410032B7B9F1AF32419A4F01BF56617E5A12EA44B179C2E959A7E147D09C4EBBE4D266A06813E13A631DDF8A79A2C6CC17B1A777AF1AF06A3B8C1C31B46BBC256B50B68715B3DD3A13BF7E7B6BC4F3B724FF32FA514BAF00CD99115EBA0A0EB4EACCE26A6DE2AA7E03F85E4CD17C830793D460CC1A3CBEB417AA6F4879ABB0180DF77122CA6E962D0771B8D7E2FEE41D65B0F154DBFCA30EBCFF64767B7CF5E447DB8E121A28F553D80237DEA02DB9A07855A8F0930BEE36C93313FB533175709161F0094AB8CDF931D1A07E9469B5093C0F1404C1A2128 +0C8CA70879F674D4281E0A0C01B1B09DACB5BF6D6D084C66215F15F63A039C4F +3D7C5FABB6D4A21C31CEDC853D3EB4F2110DC6FD3A134E92C7274F307B63ACBB76BCC5C4AD33DFD5F7E6C0815136C755091BC1A5B2E654FEE0B5F13F1A5EA0A22B2EDF6C3476B7AC15C705C30638714551C621345148C00AD35DB9C1EF0C0C0D6CC3219F1C7BEE42115AEAD98719E3CD0BBDB4145282ED57F44512D35C542E71BA5ACD50703F279F1962807B6D28FF86A41553744BA5369721A14FCEDA0CDE9EA4B5455EAAF68C7B8B3EFF027037753CB6896B85218A25B6CBFA5DFDAE132A8E69AC0EDD2C5A1C7258E20D2BA5CDD89999FEC01695C577CDDA38114236E496019B5245E49AAFDF8F13DDBBAA8D3C2D75148411B7E85B931053286BAD94D505C0 +50D74F29FEE8888911053536E79A8C6F302D8111C58FE27FFFDFA6F5FD4FC730 +747A7E69DB0E112736F6A4140949DA8EF1123257EFD2403D6F4C83CBEAC700BD04F1AC01DF5A162B5373569A1B3CDD985B5A535CDB28AA4543E31738EE3E76A54F07880A298A5B77DD77E25FDE3CF87D6443CF17C0A4D88A57D54379259914B06EFEA6346BB0599B5BD7142E1A50F2E63F60455898ABC20D7A40CCD2D0E0E8217907D7BFDA8E6AA6C75353C3B79F1C8B178DDF965D99991AD5B42E86F918166C9E0C417BF1F2F65EFBC5FBFB8A3B8AFB171D7104C3FEB6661418B8117E0B25C3EFDDA511EC0221DF2CA21480F43A41CF98FA457AF4B89D2576C9ED94D83F3A0D69083D61486BC7D90497F60EF713A2F7A9ED82D14F5EC9B61D4665CEB04B4446 +01FBE5EA9DEA64E9E5EB82D54448D617252FAD2139D2236758612C68F02D46EC +00A0615461B47DF8272ACCA557CA1F3387AF6142A26D47D23A32D01D72E64A15D80CBDBBE5C605F82A7D20E611BD9D3759B2BA77C0327C9E495A0DC21F74CD209A750D2E3C8BD7CAF32875EB44C26874C13EE77EADF3AE474E4D66E7EF653C2AE8CE730CB6095C1B7D78832973EF518E36BDF23737AFF93015516F91A1C0AF97773605D98CC850D1BDFA50EAFBFDBC2186FC09758A57E3F4DE509FDBB96FC33AEAD3A4DEF1ED4F3FE3BE13890C93255DD9FF7A5006BCE1901E06D4AA319517C035AF3920736A778612EDE8A069199CD094D862FC11467FE2702F2175954F0FAA07856A64CAC7B3BB909800337D800E209E20A5912C7E94DF97619FAAFFDB5E51C4 +332BE7FC8A2339D98BFD277207204172A3C373A72BB2010FE32296811F2B3D9B +6FDFF66DFABD050B21F1AFBC6C688B0E1CBF4B5A39F57A2FA0E1270C251E2407A61A9708CD8266F296EA25D85B68CCC3CD0E82569577CD8E0DDB0D3D8E9A54CA41E146107ACE7592DEA76F58C4BA9038FA4EE992C2097098C4D10A3FD7DFB5D8D5B90924994917CC9089E6406B2DCF7DC7053ABDE47B8EBC312D24EFBA5AD0D0AAD216E3EF7EFF71D6ADE1ACAB0CBCBF22C9F2692DD59D1EB9CCB7805423642F832CEF517AC3633FF2796196B7932C48A39775EDDD532D466A7D941D6721C012F4C0E2695A71CDB498428B8F513E9CAD0617100CBD23C5F575D8D9A7260792F17348920EBAFB387878170E10BB2DEBF4340389CC7855BA84F7B6E8FACCE903C8 +2554EA76299E1C61DCD56FB16A6B311FCD7233FF2BC659CAB19CDDAA75915A87 +5BEA48BFC2876E31AEAFDDE894BC6EAF11D975EF79E0180CCB8F39C2FF06E84A8215A009CAAF3E3185EA8EAF57AB38FFF171672F5766D2F9C5155BC2C4C47B9371CE3117912AE8D42511109F3373269C1F6B103D6A4681B4619DC73314F18DD7671691DEB890916BC435888BF47F0D3C1FACE4ADF546DB26B92FAE244FE1B46C3E1705DDEF4984F52E88D249B496F5AE34AFF4AA3E175FD0C33ECB060045727C4E9908199E4BBBFE49A1EE2B132A8813CEF2A3224477592CF438282AEFC508C50EDD35D6A0F89F9360542E9E82DE61EE022D121121897EC32724B635D483CFC6378452122DC8C55FD6491D56AB23A7CEE7B68F83F9B4BE8E83DBE1B4C0CB0733 +6DDF9D8225C7382B7A85F4F91016331B6F6AC9E55FF3A884C4BD27BE8F201C63 +2691CFA955368DB23CD84DFC603E40062EA9033C5B5AB5EC9F5A7A542CE3B8AE0E7A84A3447EFC9A45F92FA3278B697BCB11A3D702C97A25660D64CB7CC801B52137AFCCE81D49296D6CDD0E7103790FB2D8F4A5A263D071B792D388A2F60FA6666786E2AFF419C518470E290523E1E9ACC6ACF727822417FCFEC992CFD70D7B002C8F6D28FDA2E096ED50A743988F65D4A204B41BD21AC2DF65D07B7492F19F171C1238FA1731BDE50F0A944ECB8C8638C524C46E5C89671F5D43543052EDDA41CA25F661DBD18BA70EF1ADED0E31A3F84C9C892810960A226E1B161A4E107E55A9098F8852CC246E7B3B4764751D2419FAADE0E2E71F617A636AF665401732 +04BD53A1AED58B68C393654ED71F3771250BC2EC420A3476323668217E03046E +5AF8F7B40315D9AFF0C7E925C9118F8C3DC66D5D91A89C83E14E9C5475240B1FDF7F9D727B72454782A5FBA8A2E602AA4141C24E734C2BC82E34ABB7A2D3C866C7B4FE4FF42C96E4D7B71A0274DD0F0BACDCE8FA3372BF08CF1B77738FBA0D3F2D4755040C2FA2C8D20E5922B057746F408589C5D4E6A61303E2D8A505CD7381E8DED94666D3662818A37EA179B5E76262CF88088392FC607F553683850925F4590A447FD2A509029A99428F692D4D3118BBDA4C68BD3F527309E4F5F535CA637822387528F2A656EAFC1D67F5F580B97BF77393FBF11E34D1D36443CC54BA678DFFD2636D130EFAB528B596FE3A27E3261F836751192A92E89CBEFCC470D7A2 +4536B7F8CCF07C9A66DF0BCAD0A7DB6F2D2735CCA4ABD12959F42839318407CD +0B0EB218653599AF920CF50A53DAB9A4F2A15B74BF9A1EA84C4742069829BB282B0E3CF91E574A6AF47C07B97DB599ECB6B8469BD277A6F8F08DFF81E719D2FC12A14864F7E887B0C0E4B89F2AFCCE3B940D4A5DF4DE50831FEC024B33B81945A0DFEB4BEDC5A48803B168FE7C55AA7416D34E9E8C8AC350FB8432E18146A6C813B1A854F4A455063685E70D2EBFA84A81F8B6275F8C8182BDB4FACC49D8C22763C2A3E16D9217A1B53376212F744A5EDB04B583502A92A75B936E8031739777E4C5869FB125D2FD5AF75D50176BDB4AC844266A56320027FCF9180878B2E9BBFA3AB69C74F9EFAF84DB5FCF80ACCFB72CA9C2B693DE0E722A718E9190A1D44F +457DAB584DF1C4CB4428611E9D2B3F9F75E600B47CD9E6E0EE5CE868C7E8317B +708ED13A7E8C7707636E04AAB531DAA95D0C53040CEEA926500E06C3C3B875B9FC96053F0BADFE68BFC0E4CF7C1DD42FD47C700AED7E016F930F792160BC3E8DCB3F4E6D5D30EF3770D78B49A2FA0816A2623C711ECB2D81FEF24AEB78BD7205064F166B815827D93498431A06A8D1010AF751336B5398EC89F81F59FD9289E5E341EE635C3F988F5F049C9077C67CF9C242417148730C5590EC6FC706A3D3520617FE6F1BD07D71DDB3351BF454D79911BDE293F15E3F8042F52E002C83F9EE9F1EEC88212C07B3C3DBD453E177EE171A06644FD285F34E943F40E4F4B1FBD4E2CD5625901CEC536556C1EEEB61BE7C0CF14CF12B1BEDA4682FF68839DA8EDA +3F3112F47350B7F80F428309246394EAE0890F32CDCB7CE972980F352F8E8FA0 +4D85D156FBC8EBBAEF9748B2577B42343A849DFCE2FBF6C1619FA499728C79163123DE7C64C567FBF725D2664EEDCAAE974CECC16E819F347F532F70AAD0CB81620DD37B9C266B5C98B355A9CF71EF1C9E0AE224D1EB49539E0286374D813FC67933DEBC4860145B4F50A2AE355E5636E94D8013F031FEB47E8CBB967FD01206221FCACCD764FCF3C1A714DE384366FA10313CADDB88115669610D354A17490878BA8238EC92ECF8CCA64D3DB3E32967587B5DA6B5572526ED8A5070E54B2E97BDFA1DFFF606B4B02FF743258CF8CDF794DA641C24C0FF87F707B6CBFB8D9CD6A688146D4746F897E17CACC62547B7255D8107435139447794AEDE67E3489A +3B1A8FC98B9D6E383792DDDF6177562AC9A56B30268EC4F0D608EE408847095F +3583E6730A9B0A3B392DE301696C91BED61796A4939A44F7E417CE58C29CF805E6D52E65E24AD7BC9D47EA2B7C4BD258F8ECBE59115A501CFC376BD731792D039463871C78928E2796EFB98681FF74AF982E766C772FD7EB0C1C8748784A5338E211742E49B54113129BBBF352FB6678668E3165226107C3330F092A38BF34965DB623422CFF206AE076645BFD01F1AAEFB1FEA52967E72A8636BFA1AF3912CD7BE62212969D41A11D87EA3D04AE175CDEF9AF78575CB8CCAE1607E4637155ACF1D24A0EA20949969CBAAC69CBD79AE4FFC762944EC431C3829BE5F850BEC9134F3DCE26E632928322F0A991496A5A1ACAC132EF2B291D63E289D3B7B2473729 +79A485D50232498E14A0011D62A8A7B8BB31ECD45A87FEA02D6265914C8F38D7 +20B5786C46938611E9315AB0234859C145275A3148976A83142DFA7436F7F845B341E30F971F682897CFC9C2B10D1517DAD436E50276CE6C59A367D21E10E68C938A37CDAB49326947DCC3AB7BEF72E20313EA2DE1EA5EF8459A48E86B83BF8A1C32879FC1BA1D9301F4DDE724E15A1D946CDC0A85962F81AF651D9D25423084F396A8DA4CE8359374F7DFE438CFD774D7DDB2EECD3020B9712A141472F4EB8177FD72A70357DD2250BA6A59BF64957D36606B121D2C8AFC4024783055A9F4592E264176CE5C1D875D16F4814A637E451AD9A97A379B2E9EAB0AEE3608BED4CE6F18AC44D7B56AC66A4AE4BFDA249855082BB2EB360ECFA872D261EC53D6E013 +6767FF6A05B8842A4978BAADBC605BF2514C74E1AA0AFD2CC93435E414B55CED +0092BFD9FB4317D16B623B3F7BAA5700A9D8E934149EDE3BFD2446E1AF982D9E081208156F1A93D3F449167BCB9685845FABF98EE0FA48E96518EB8CE92A6C5481FC26B09792D029832FE72B9D1769BC9ACA732B27C71227344922642616AB6CC4072917F000B122511CE8BDE4F3BC98F0AB0D55EE67BCCAD85B7CBD8914D319B47D79DF97CF7C4B2B6CCAC096547B11E58DD6A24012857DBAD0EAF76B140B431D2448E6ABA9C6C3C22EA999285B7B629FE9581394A834CB2C8F11ECC7B09DEC171568C55BE685E36CA03459785CA19BBCC4733C8B4E555A7DC4B6D34C12785409EDEBB13DD3C7D945E0AD61A7BA24B54706CA2415677FF8B220CA85248A46DA6B +0290BC1F89D9F549125A2D7AFAB04396278EEAFBB9AA671A37039D164006026D +033C97FE9D45A58E23FCAEBBBC250122658F7F4081B949276ABE19DE95DCC07AF38A107633137288157DA38A058F87E4A213944FC8D49555ABE9D1D913F5DF92B33B377564B22710DA3621CCBFFB6E4D7457DA6D78AF2C1086F302A2D9AD0D1B30F787D9C911A88178779045AAA8EE8F84FB5861F410C4DC4424C67EED02A831ECEC66DBEBB16D3648C5F9B09AC3749FBFDC6B16D0CDA65554B0A014A601DD4CFCBA25078C6BFE6FBD02513FBD8F4B9D49F7C6FFFFFC2050E78CE54B543CC575201B60D8D26958C8C0B7761540C92BECD40AC302F6A77C0661FBDA255A4EE1DE115CBB6507DE4257282C0673854F3A524F9790CFD5B7E114466590C51F0459AD +146AFB03914A671FEB202E1B2BAF0049822311EC9C74A4A0AFBD8A687CF9D6E5 +00A5E554515E464F79A54CE9CBC96B83C22FCB99AE42C0C69D8DD69A7F90F56A3827C34DBE270B5FE8099D37D9EA0468460AE2A60C1F708CB01B659725DD4762E7C818342ADD583590A0308B338BC4EC1B9F18218B0D22BF072CD01512A4E756444EF2D6131C6C064E285F7E81208E1CD1A6EF115ECD5B78F82C7FD3A89FAE59042DA8A84060F999B965895B1579F11414B3A3E4E3D3D7AB98547A6DD93FEB82CC0EFAB0E81296C6038B3910153D728E8BCDA38B5182560724DD19B4F4064544D154AA386F90CC7B8ED56AB7DBBE9109F2365F9224AD892631EF15A88D8299E82AC362F6845C933A9B72D9CBE4497D3E82BCC5E211F45E0C63E21ADC375CF8D6F4 +03FCE9DDD65C1F5FAA1268323746C13CD960C49B3F25564D489465077732FA62 +2592B0F29A3D5A90EC66108058F323EDCE73EA5A0575045C614E5B51D07FA886BE118A18ECCDC3CD87FF3B6AAA429F4C6EFC5980C94C866C1BF3425FF089DD4F9C16D9E47C56397CA1B2E9B3BDA3A443DBACFB8634F263911E111413B52254D5F33ECF90633B994A1857279D9C8434CA61EE055E68A55350E8551B844B413869852E6E12900E8C56EE47F825FCC84C6E35A910781115E63E25B626B6196D4FF9897E89BCAA322FF0077388FC86E9775DC3ED2A95BC7520C6F4243A18C231C2E6464C3875C7FE71736AEEF7BF2D681544D3341186B40564C37424F1A5CC48A397A24F1379A36368B024D4E5E50FA17B745A043AF443F36DE32BB016487A0730CA +66E3BF0438A308571A4E12EDC5176C1729127A3BD6A654A897873B7ED158CB3E +00A6C669EBB9DD54738FB75FE76CA98845F76B2CA4113E172A4505B7A099BB397A16BCB2399A6BAF2223603B722DABC8257511243E7B49E129F9B53D252B0EAC052A0522DF44C988BABCB5D679923BE6BCD1488E5C5FAB974889190CD87B58D380C294F7617E73B430914F8375F5FD4CCA15D68B65C4E1E0267FBF02BBFFDC98724B0FBE64554F8E7657D3A7C0BC84D0F4D63B4CD55DBF9B691173E99C517F2E0E7E59D11FFD1DED016A9AFA1F28FB0BA70ED65F267CC9AA5120A4177FDEC9D3D357CDD73EB1004AAA48D379E91D6637410D3D67C206E3354C6C356AFC3937C88AEDAD23D70D1B35748CC4CE5D12781B36A973C0E4F2D3ABBA1FE51549056ACFB2 +671CDFE8F788DF8C5186AE3DC272ADB747862929BE59D394AA2C368C0A410BA9 +022387E08FEE1588827E2182C108C6645034CD7C56B626A0CEBB49CA67594AEDA90E98CEF35488208EBF68FF4BC014412366197A3084BE17D47BD959C9FD6F6D58528E2713B61822E4D128ED1DFEF4DD8D9006E0585A7F76FD9D466712D2922535355CC16433317A020D48931B0933CF08A02B4D8042EAFE46A74303A6D11FB7FC7CE436E96C9E9200121BD90FCEC42EE8466C8F9A673708EC69C063F954333243DAE3D92C998494C7148BE6ABE2D882D8C09612915B6224AB42CB8A4E5CDAA0039F4970AB49713AC276136ABE8663A6833985289D94C5B09D0B698BA4A942EF5A0448DE4CAC9C269EF665B77D5604B49DDD33749DB61AAD85C8699CBC5AC477 +77081823BA6886D49B3774687828B0E61E6380136760B9D71938941E0E53693A +00BADC5CA861F1B86389C8EF7BCD2C5CB65D229CFE8382167F3F6463737FCE62059E055B93969974BABD7567E268F54EA894350AEFDD055626C137CAB08279E72FF6D0FE4B1AA797A4DBCAE5B2B89F4B86846E0E1CD89800F205CB7F8F6F6614CAEBF7A4EA0CF87E43A13ABF012788DB9F1A5622A4583A2D5F4F2E8AEAE0720147A397CFB14147907A057AD187296D9538EDA8E3F814866F4F8C4E73B02552495B5ACD8F3BAC9DA50A6A5C201C87851DDAAA4EC766E3FF6613157B9D89DD708EB2F4DFDA7C5A9E7DF51A49A73C72E27B636BA973AF37F1EEBEBD9FAC914557DC7D8FA93BF8F824CEDF1BFF2BC38696EAB6C94E01987EDC1C3C3725CAE5BE0CCEAC +5CC82F6F9954207853CF78D8AFBFF5BBF866A32BAFDA56764A9E0973DF820FB9 +3CBDAA6C45FE3EA3CE1554C19CE94CBDD0BDECCF3A7BE567BE4E2024A4F1D3D6005945099ECACD9958E4B5A9AB0D21055F286B2437DC5DFC017B41A521F49E7B8D469E53E0C3DFB115F8A167AAB1F354F2123D5F88A5185155D0EB2CC1291ED1150E23EBF5937694AD03D9169A27087AB854E045C77C4E455974492FF32204DBE4542D9797CBB115B62BE2DC56C068F85C16F1AC2B73E7FD53FB614CC8A54A3EDAD5ADBDF0DE287E7A04658FF03C99A34B7C7922EA35E8B0A7C24E2DE11E5EC1A815E05AAF86044F5BCE90C70A556B40E2EC053309ADA13C7CEB1C8C61462F208A9B02CDBAF2B95E610DE33425ED93FF1583B830751C2D336E2345B92D82255E +41554E972C3F6B7494054937B26F9A4F50F240D3D8A50B8436783671EE02D415 +570ED13C26C42026967D1A32364981075342C9B1C485EC5B2124344648BCDEEE231F4353B082441D4D2D1C93DB1230E4E08B6126B8C5461BF49F56B1190EC8959657E4CEEADC6CFAB4B18D622E0E4042247A153D2264D98EEE4CDB1A1B53900E81E62DFED5978D82738A914D1E67F7E64612B678B8CA76D2CA926C86748D9E37F313F4A3F4FE529157139497E3AF835238B374DEBCD95192301B889CFF4152BDDEB4A9114951BBA04DBC0A3FFB8FCE7F5CC76920BCD87BA4D5B7663C59514C086245A4317EE0C21A302E8AE266F86B0A4F0CCBE6983F8D1845061300BB560B6177C2E3792A98F6F0790B837D7412D74E4D0A69CD657AFDB01B119D7052D421AC +1BF0CBFF821CCCF15806E872837A28E25D7B78E3B24835DAA14E0D12DF0AF324 +1262F375D27118D89D3EA068629AA866A1D036F94A05530BFC6A93B93A6B8990BF6D1BC4E3A4ACCB48833BD3096DDAAF7E2D26D5EEC474A00D147913FD83E9F96F60B4CBB028AA0DD39C93E68318BDBE0D077491421F0C5DE9904EAD13C49695E981383F4248623702265BC7A8D6D4A835CA1E8E5D016222DF84A48B6C2EC7B4909C835D7397438A57FFD3304D078A1381FAE4D3D427430AF72EB1D1EBA906E9578A19DA1FBFDE8A4EFFFDCAA8B5635B9F0031BE7238A9E6BB97EDFF34DB58E85D7E40BE5337B2ACF4B92AC6F440F20F607AF5B6B334016AA871814B733A2A77532289FD7B7E360F59731F867529B92D0974597922D340E293148536837C63C5 +3B23D30E81D1DC4FB3A7B40F3607336EC0DE9A88860D37F0E385BD480DC3266D +5EA496A0A68C346B3F9DAF766DF1E2ED61857FD5795E6169438EF44119456DFE172B54717844A60E5FB740A648D0FFA1871D1D46D8B554BD75891C66B330736134037C93154A6FEE4A8A167311DFA0F63B42874A2C5BEFA72B9393F311638D398B5CE1589D49EA8F47DBF1F35836565D76989ECB6543956FC8A7241D892970D92CA54BAFCA1B3C7A5D72AD28C31C30437960D9BDA6B62AF4C689CC002EDD912907AEC05D6C0D5F375DBE1B3A230C9CE16CBFECE9239E70C41B895C342E8E8A386B06156649D5E42E08D41EE0EFCCC4ABC6350B4FB5F884EE607E471BB855EF8041872AB693763D3D9E04A354EF262CB946B4C5CDC56DA787C06A072DE7B3320D diff --git a/xcode/lib-gpu-generate/publickey.txt b/xcode/lib-gpu-generate/publickey.txt @@ -1,48 +1,48 @@ -00A4DC07CE8A3AA65A4AD1E05F2C407753C7A7EA1A160DF0772DF2765820FAD5D9D28A083E5A6754819B9F33E1A4A5F550CB50D0B0852BDE85C4EDC93B565A7BA94FB4092252D1E220CEBA2E7BCF17598B15AC923C09255B8C3D62C400955762960D86E47C1C2BC218F610E831AC4F8207A0494618D4EF67792F17EE592C63995600C045A9DE8E988541FB26D61A18B2EFAC62CE77AB75BB35FE9ABC49D7E7876F27205672FFCAEB34612276AD2526A226FB25F629EE04C97DCA80697D3D1E62B54ED5E4D77B56ED2A50E0574D21787409E06B760EAA6962BD1E7C1E45FC7271D54AB758D2ED01F1DD1F82599EE7CC8DFEE3DD02A07D0A87604DCD5B669160557F +00B3283CC06DD36D1926AD29F2BAFE0CCAD46EFE2741C7B63EA31663E6178B48EEC61E87CA7555CAB8197AE087CD6BAA0129E2FAB524A84643EF7BFC8A5350B693CE993C65380EF63C9380751344B04CCB6D47107DE48B25BB89230BA0C1D079D28B96EBC5F2FF1D4E7575B33ACEDCF682794D4E00550C5644D5C5D7092BCA7AC78BB5CA4431D4FA34653BB1B73233C0D04A83BD3B5BE2D0862308F89E9A95A33E640DBA4104710952E391716B18F5E27DACC5628A1DF81DCAE305EA2F731FAB2AFC6FDB31298B8708F727B60733994FC92D4601DAFA51926022789DBC7304021FDDE007BA0039EFD05608FCCF4519C237D7527B8AABAC3D46F0D213DA5F57598F 010001 15 -00EF43AB1615D0454804F2AC3635B8C1B2EF1224090EC9A2FA404EB2B2ECF7679AE06C9D925393C60918075B8AF6EE11D7DD2781A4C61149EB38F5812C7FC414248123D1CE56FCEBF7DD7870C59698479EADB32843773405CCC61060A63414CE6ECF71FF4D1C0BEF170127D320CFDAED9F815664FDAAE9AF269BBE593861FAE8CDCFEC2C9641F874716F75071D72B44C15E492038BD4854B524C0DBB978C185071DDD0CE74EFDD3D4838D728DF1494208A0EC506672D372D8EA7D8628E7CE6FCACD53371494B214821F2C33048434617189B63AFF0E37B9B9FDB42523604DDA2066A28EA24ABAF919A6DB952C26AE744B1514FD89E3CC15AA4A164FF87968B1C87 +00DEFF6735C092090ED9376B5EE9FBCEF307F30F96BA01F1393486298DA2A8E1E01FC2AE438EF80D4D2B03AEE39D90F19CBC3049A71894F40B51747251000B736D586952A05446C418AC80C0815B8136D2DCCCFA4DBA8E6574B961FC4AD9CAD382D470ECA280724DADC32E55DDCABB51707A196372A33F69826DD5FC35BBBAEC43254056C33CD0B1F22859C4BE20A37D31BD55C99096FA809BEB3A6908F4E057017B7E8AF528D67A0B7CBB828DB7D26E368E5A0134CF02E9876BD5DA0091EBB5052B29AC01D8F7906D118F95C550F9004A2AB6474E322B7FEABB78E4D98ABAD2C5AACC3E647641AD86DD3B80C9747363A0ABCF13E8BFF34F9AFFC3EDD5F408A333 010001 31 -00CA70EA13B55C05E0DD24702E71BEEB96A22E165B062B8F21F27255DD243B53D45AE35A213D3EB9126A33DC5628CB6BA16BE9422CF5BF61797C789C7DE2FEA8A380FBD83FC08F64E51843018E5CE7B0814F7FB31187BE35B585C7A6E6217D2D67EDE6899938535360EAE5E69A2057209E7CB11720126D99077C4019FD7D994F6B7D8D1EEEA0E2F9375397973DB0C6CBCEA71C9037D868834CF84BE37E54A1683E9C6EA3CC8B172E2632F6FF3C38210D9FA11760C61A8FB0D017B827AC0AECA898799CE4C953A74A3A246377D7D1FD107CF5E3834BF6BC046C8174AD1C9B4D4D3A91CC39C23CFDB24042BCC1D23C314E4B04B4ABD7D8A30BF33004049A0408AE07 +00AA5831B73A35CF0C957FEA265D535ADD8EBEC725624C5C3B63D4A5B963105504F12942B4689EB904D9B91AA9158E3B923CD4462436AE96F141470EC8887C099D17D8952F745933379B7D855613C958AA97378E969533AF473E827430D2FA2BC80C8633471A9C53A8BA13A3DBD03CD35D37156C71481F198C43056DC6880BEC541844BA46B16EE9E2E14C0ED5B80DDFC9C520218821AD8B920327D17857814256DF137CAF20634EF582A669715A444A50FDF1008978EEF09B36E49C026C0B0CE819E1940144C5979F31B6A453098F14CFE0B7FB04D5EAE82572A62187C1E82F2F5F5EE801E480C9C441EF062BD164F141D1C29923D205A80B7F561C68147C1C29 010001 47 -00B10A67F7E281000B5C75EAB35603EDD89060C44A297C56DC591D38D66C6DD41F9DE3F162EAA38E63277F6459536995C03B615030D69E11E750F253F799E6F20A9A268EF2C4BB23CA27C0C3F189FC5CFCEBE7CA246AC06B1F94723C998C69F7D009F6E1FF90103F404C83A61F185682BFA5E7E046055AF3A51B5FB4009D0F12E8829F7CCD230856F15E781260FF941E104C841AAF2EE15D255F1B750B7CA19CFB28D1CA03483B85267A8D4A7EB5F8295E34BD5C0CE19929EBAF5D1DA7C30D9F626CEBFCFAF47AAC207A0C218E9AE6C2645E9CF30DE5B643BD9C306081E0EDE23DCAFE64A8A0EAA619AEE8CDE2D53D5DF9954836D04E564CD3D152DD0BABB728E5 +00BDD2025BA10E8C7A7F1106D2294984383057D2446178BCB6DBAFB85C2532AE5376C29C157821F6568782E4C2FFE374BAB62A87BEDB17D9AFFE78CB11B2E28538CC45BCD18BDC88E08C680F6899A25D457839F5276C442A67B1CCEAFE8BB091A13A58C0AFE54C3BFCC16812576723A8E650D234F5DD1D79D62F20B417344736DD0982FBFC5118336F02881D0DA4AC24BC417CC21D5A13B09FE4DFC8DA6DF07E7BCFF34DBAAEA34982637B67819D61CBA8D7CDD8805CC37668DC64640418415F8604A0215677A54F32554BFDB9E0BCCB78D7AC2E9FB1ABADDA696FC9CCAC92899111B0047449FC13B68C9915E5495B86E718A02F18A13CE92A7D0625C0861A3A67 010001 63 -00D3D413642EC6D8EA582C2DFE51D05353FC9D0586E8A751E7DE7A2D0303BE2687CF484E861B1E20D7FA26CC971F5AAED86DF9EC0E68288A2710B3965109EBF12AE5B0CA1D0B89F856E939AA8BD00FE04C8408C5402D5376BE34A318F867BCF07B6315F497D68DC043DC31BB7538FD86C15D027579FFEA87FCCB4B9D79FC6B5D0214488A7EBEC0A9C95F026E8C7ED77F9C56DEE1A6E561913B4C9AB022765C60108FB4D8559D79E2197CCD41C5150C98CC0A1B08E2759A615FD55A62607E64C02043885C6C3F7FA13F95D743C353679CBB392EB6E55303D94A86B61ADC69A3AFC64E63119F75992686AD8626220FB11760D657821DED99E0E62B8304FC09A39D9B +00E52AB8020B265E8A96206A39845A6205BEFDE78FECDAF6D8592311043FD4881A5C1588259DE21880E55670D5513CACF1EBC1A80969FE44D945A22FE10413639F33B75E61D0EF7DE0CE470E8C60A3191FA199B05964E090B650EBA46C6BDC0292CDFF20DD9BD4DBA74AD7D7C785493647422633E45ABD0214223EB8BDEF55E103382D0D2D48DE6D398006A27891F7B2131B93635B5CF9FD52163B44E1BA31012E9FF505F8A5C020964EAF5C4C196DEA61C505F765D89CE87136C2EE4B226829FFF8E1FEFA8697D4180C5FC7214B5DB8D964BBCE810C2D97D7278A6FB59B04B603617982DEF41253530377E6ED6A5ABE9515B354634255BA06DC517C8E61AF78D5 010001 79 -009FDB8691965A89D764B49EEED7B06BBE3555759298F99C041F1A9C25AC67EE63DCBF33A5FA13407437C1098CF6CC8E86AD10088C2AEC27BB48A35EC4301ADEB02475969D0AED3EB48AA20FA1AA95CD00CB6E8321B4E1EAEC4949C5C3B7C2056555C694C4270A51837791CB55801A6932859B387FAEDF78107D56111E7AF1E6BB202BDAE5C80770590BF3ADEF8CA8B49B3EF80C17B164310F86AD5AC5C83E10A735221BE7DFA9A86E4035A4E4DF8D10B7B8D0BCF16F632BED891E0790EA71C505EBA3FC56EF13337008058907647CB39256F088A31F426AE7212651CFABA1B573CB23066ED67587B8AEB49D5CE34BFCBBD54BCB7C425F03649EB0A21EFBEF192D +00BEB3BD8C479D3964A2E4DD497D89692B7063016D826062B645AC3D919D646F05FA63E5002FC691FCFFA305BF97201A4412E9C1D63FACFA6734E6E19879B79D555750D4BB78E94D1C137FA8486AF07CF285B437AB95721F1D1B194CFF61D5A29ADEFEC6C95DBD4F9E19856B1533505EFB569167B00E9F5A7C4BEAA67182EE35A1AB7B6968F7A1A795BBA73C8DC051DF3A60C3A202345AAF1F63EA55B1E8D3C9337D0F1655D67EA95AA3003B904FA8DC7DF86BAA41652C4515BEEAA0FF9149BD155CE09B7CD7BBF36B3752EBC2BCB14EA1696AD503367D0EB15A56E956BDA044DB36826503086D62301A8A03369CEA42C7C93E03455E886A2BC3B028A08DF30C6F 010001 95 -00C54DF8F711F5128CF09CBA65252F6A8A8B2E466C783EDDDC6A7378A457371C64B8B59533B46C026CC316B315660B8FE8E0A218F48ABC4699C8CB45BC27B3DD2ED02D5C8F843240DA5FF1B50342F4CD4FEADC247BE6E1A5110B9D765B3DD0576AE660B1B9DE81EE95F88576DB05930AE0251E9339FA1F9494E837E960E7242D199A3262820BA32C59771399E5A1E1384055D78F6DFCB6E6B348AE957FF7D5561C5C0333801FA99229016F48FB8FEF9779EC1AF688DEA8D09C077FC77AD17EBA5519CEF489025934C57E58506B129E0B240C65667E88E7C86DD912047017EE436E603D6B0C5B89F9EB68C8E06CF919FE91A6B6DB4D9E8EBB863C92F81F21657933 +00CABF17699711EF44F676635F40DAE59642C688E596866B38698A9AC64563FD7558B1D5B44A590780999FDEAEA0F2FD0E3E2E627E309C88DF221B3B2328B7340A80FF7F98A652B3089FCFE508089C3C930A952E94CDF8992BCB44CF8EBD7220E1B39BA89C11557D4F7EEA7D0B7CF2982F020EB4907B6CB1CE9A7D47D49925E84360DE482A0BA605A5F3485D3110C6B0FC3C5AE894FF0C4B11CAA91D35396EF625BFB69EB858CB7BA55BF774E32CF1D43D1B31ADBB0A521B3CB3A2465360B7BEC214CE1E375A2378CCF4F4D14F0D04E2CB3A5D49E08750AD50AAF9872D198E8CAFC687B4686352A6C282DB18D463AF057B0F6042B59321501F5B065A79D6087EA3 010001 111 -00C65E5C7AD132A9D55492BE5A545A5F9A1FD3E3111338E29F61B3EA769B9AB0FB052ADADD744D56D3F25AB5335CFA4139617FEA3545E0DCADD124107501D668DF5A80D8E60E40C7EBDFA7293820AA8A89E61EA6683E011E5F2909710C008F252574E4D3610FF18F508171C41E3A6E19570255442464B370C35A71206DA84B93BBB47DC6C7C6C7DACD19F8EF44CB59FC10ADCA36E6726CBEB51CF4BD7A8D2799E24ACE2CE68D1AEAA2D107047F34E96FC3875380D0EB75AE5AE8D3F63326DA2F6F3B3842606F3F83E2B688716D770F6A1048C08D279EF1E47F4A716CF5A96CE5CB3E421E912693C2C86C990C904E638E4050259B563E609BC2C2BE2BAD9A042159 +00B2C697504F99F0837330E3311E3D21F8044EB861266C5615383C7A40AEB0A68507F0B52DB31AA6A4A9A7E4CCC909A165B6A5CD712BB9C37A484B893BA1D5E719A3526D9F04B016F81E7367EFE3B8844D586AC1BCD77B8F07614C8C8AAB82204221084F8F30A5B5A0705A785BD4F2AAD20703139602B29783BFC66D4D8E25D5EF0F09677CA41F71DC9971AF6DB6624639CE81AD0419A46D4B18E75B6059BD3A18E0CFDB67BB05268CBC5B1E7404AC71A8BCB36713E0589387A0DD5C313E6CD1A43B0D69435C1A6FB3525E7F280665ABAC2AD7CAB0F15EF60F93AD5F311EF8AD4FECA5FC3B52AC9ABAAF7EB81EDD37A6AE2D71771F5851CF40AA7F9C65D432FC1D 010001 127 -00B70F9E20752DC7FA8C51CD32F4C3F4EF43E913065B65DCA1F355880E19B4BC94BAF9412ABF2A18221D3E663805786051C6681801C26B9DB920056500FFEEA7F90F6FF730742DEDBF8ACFF5B84DAA583054CBB76D75CD53B4387185F356D8AD64989CD5BC6A7245757028A936C1B76B94697D1B51754E2EE2575C5539E065141886AA19516DC6ED232CB8E5710FE5FD56C67F5BAE408B9A7E9506D5C855F7887B449B0A1E676B0B3FB01D4067BE23444A1E592570A07425A43555B73B168A7A737454ED5E0BD68BDE68112EE8976C9F3C5C3B338189E1DB3DE8F5F9E76C41B92C7617ADD2C39296E1B8D600D3C231FE19B5C573B41B1E0E77CE80E545945DFC93 +00DD33C34B6D53BA1B397397CF511D54392B02D33AD6C35B782CA8DF631D7E9441E9BEFE057448E90810348A8869283A85D5B873BDF0108C545468767E6C996E6A6E41A7A9A2B95C84B04499C8E4BEE9846EAFADF72FC2359A4B6892A84B95D641A8E47755E16E2782C87B8C2A2D742D0C02ED9AFBF8ACE45A6A21593B716E46E9E0398ED75FD9B902BC3BF9F26750CB63AA7FC852D2063B59296B1BDEA1F0BC1527A60743651F985C131ED01253C0C54C1A10E663F20A79945C125276BE667693F0D1871953BF6ED82974262ED33919050A0DF02B905A189C560061F9A09A58FB5984E8FF6B75959EE257741AFF9175E8FA260041251445DF22A0B5EF0871EB93 010001 143 -00DB7BDBD1425B7B2184AE87ED0C0C05A3BA3D424A58D7756037F0D652958D7A0CB7BE056ABDE63979B0853B46D6B756DF02A04A6495C8784ED0E18BFBD093DE35F492DDCA73F98E4C6F61A64858F6A62AA9815B9A38F73A554B7C1CEF6E7158F9AA577C29122E74610B2DE3737E1E6CD869C4BA64A0B7C45292DCEC2D0ED2AEEC2AD8281B48AF3C88065BF397041667252DED7E2CBCB08D52D3D2C3E39235865A48EB25A8F695A5025E44AA0C4012E31323D53D78BF7146787E2C3E97D6C9AA190E7C5AB87DABCB1C48FFF6D8C48A8F9A327E8AF83BED5E4F55FAFD5C0538C834981859B36BB2EE4C022BE75C5DDBD277BCD5C811F3A28AB8BD15E98844AE0CAD +00D2939544FF8FEAAB9ED33D42EA4C4F1E06E675C1F6054881ACFE77BB2B1B0C3D8EAA627373CA50D330AF29AC4481D26165B92BA9DFC541A7FAC430C6D596FAC7F42EFD7EA98D2D8DF47E98B6CFC4B11C2E6F82D5D2429ADFF017C68FAB61BB086D0C5F5F2DB651934EA978311D656489EEF01C02B55C75A74A742BE47F88BBCEAF2630C14C7954FCE06021BF1DF156B99A7B705680DDE5B18056285580AEE79A44C2216A717F9B07C0C97BE9FF98BE7161773FBFED7D3DA2ACF47711896B7902DE18B56D391F7F56334F5B4C6BF9629CB6CD516159FB2026B538BF2C303BA808BC7DF7ABB18BD9AC43CE4FFB65E243FA2786F6300C1FD216A50EA59D128DC9E9 010001 159 -00D397D80857658544D1585625B32E0E92FA84C4111B110E8B6E16AD2CA9ABA9F6042E14F7DCBD8E2499A3882354572D0E836A66A4A8A90D0BDB9678D23C82A8695983699D9CBF939FF2841AE6752CD4CC7BC197EBB971BFA24C0B4BB888C68C61A7B4C565F198140FA2B34AB617EE6C3261C09A7D3522711AB6A7B08A1ABD3D6E21E832A0AFDEDCD9472F96D7E3358FD1142E1D4E762056270D02A7E323F9B72D61A584641FD41044F2371FD9809B8DC27442D9FC4F16D77E25FC61591B658C12CC5F74EA284238798DBF6DA5881CA479C0879E8FDECE7EF740E32D851C88960962BAA7022BA1A677BD4FC59D7846ECFDF9516F3D8FBFD4DAA5049B0FC68182D5 +00BBD4F5CE28231D0C96324D414C15F2DC5220ADBC785644F0A5D2BD57E05E5BF31952BD69E52992ABC3142CB9BD98C4AAD3926717C15008C1F16E9BF5D1B7DD399C6511F9A1A2529E9DBFD3046BC1A8CE662C86CC9685502B67D67AF89F6B510A20E47192CC8EF841D6DBF2D796727B584974A37664EB65DE57EDDF2D2C227BCE604628172ADDFB9839F486A31E20DF7EDA83EF50D384079C93EE9B053BF6605E32105D9180E8A1D888CF86CAB9EA3E157DC7753B9FF078A8C79424387003F834ADD5A98A801D61496FD88F070C8F524ECAF58E34F34F4D04C9B8E48F7C83C645702D0E65402635A744CF5440BA100C2ACC810B75A116C3F997C0179F9B1C05D1 010001 175 -00BD98D579E1B90B72942CCF85B8638FDB34D7AD5F9AADFA73041FD1E2F1DCC5BA592D9D44F8E2FDC24BD153427419FAA94ACC9DE74B5CCA70239DF3A39D9BE13EFBF971C1413B05E39031829BEA2F90CB1A3941D93E5254EEB55A8F3BDFB972B6B94C32624C67EE0EA5FE85D3E004335AF533697B3F402ED0D6D7C68D97C8ABDBF144B74AA8B0CB9D1F63C4964A464BCFEA245C3FD32F37F5E8B478EC1064A3D22FE2D0BB1CF540ECC90978DF182FB6F2D76B7AF233CDC315881C649B698ADC28EA4A945BA6D5E7AA571C05698134437D6F87C62F0C6697394F6C57E7E86E78B88710C7C1AAE43253F04AFBA2023267789B9963DC20AC648E198213B0A06841BB +00EF628F98C4A1199AD42C675935FC8371FA5FDEC54DE1EEF0B834F678757D79C16BCED477DC3328FCF176FB71FD6856730F4B834F31C09697AC1AC808FFA7C3119630F814BF5287A9AAACE1949EDBD4481CB43BCB21BCBF82BC1B8183BC63DFCDE5D9D510D2BE9B229F42EC1CAEC79136909EC1444DD3F8854A317915F39836065054D98AC3566A7B4F0F8E4CB350F40633EE9B368B792D0869C1CC6587990C1CE7FFEC2A6F0E076DE8ABC7EE10823901ED7DD5BE593591A30595CBB243BBCAD4C7C319BF62DB1473806EB154ECB25D62BBDA57A56D356FE3621B7591627FF6DE5C8D44999CDD642941C04EF5D3D23B00504417A8CD225E40888D35C686E3D295 010001 191 -00B970D76FB495701D9583B002AB7461776823494C4FB3ED3AA8387B79824FFDCA92168872143188D59BB8551AC935CE92923311DD4E249DE9B2B69891C11CB3055FB0A88FCF18EA065EE139769DAE021D62FE2C0AB3F66825CF7BCAD2E7793BDCBEE45F400645ACCEAB92C69A1F66DD137BF88DA94A22C874F9982070396E80089A2FD069F17AA0B999D92CEE67936F0AABCC3DBC97F1AB579D3257056B735FF48B5F4E601CA1FE147B226E412A7BA9D1A164076F74F260DD2D1807C216D68610638B56C3FE13357DEBD98ABAF5D213044F3FDE12D94101B22BD28DB597FC48370DFFCDDC512F79354D45256E8CE77FA9DA041C4D4A6443AC48054B1B851A916D +00BA6211DF494D2FF3598604232046A98E31E960F420B56A36B380430DE71D369BA9716BA309802AB9946A18C674A461CBCDED42D9459F71EC4D17E1A11402768CFD92D154377E32C027F3F77CFD16A0D31D7FE0AE9B377ABDE976624B2CDFC4FBE6D4EA852E19B3EB7447F1CAB4D62CAA0CF0B00E91C18827AFD4F26ADFEECE8F24017E09C2BBA3B64096CF656815F5454441DE971948CD2416FF67B4022355D01CF364CC82C777D8B8C0A53E029B72D96AEE08D582ED0E6D57F29364EBCC3324D576FB737E93F1FB111CAAE90874F6FB5978CBB6BFA87FF616263D5712CD8D8D10F5F4E3243640691634642092034C43EB08DA59C9337BFAE621E5D1EE6D947F 010001 207 -00E6F7913DB334587E92802F60E4FC2E7AC9F0AD952CC2B55C355566441B46D0B9124D6D19205AE4CE6B021CA17E17FCE1932479BF581FBD8FB2601F9ED2D835199567E7E8C5463C419AE36FB3D85C8E2E379C3B600EC74958EF9EA724337854B22377B63BF737D9FEFE703D920510A40B4511F6F187E45E2ED34D59E3E63CC245F3F7358FB206A4F009534758897E6CD66A53E6DE447B100AB299F38A3C00FA52D858EEA6679E40D36BF6E02D8BE03FD05EC6220C955E0BECBBB5D07175E7FE714271E2EA658BF82674F9A0240446C2F28C035595F2BDD2DB5334A0CDDD3E0B9F95A895794E1DE2683F07B95ABB4C10AB11E79724C520900EC88C16536425AB91 +00D080C68C2AC8D64E61FB92948B0FF1CCD0C284E935575A364DED73A5FFCD8302683A4B913FB1231F9E4479DDEA646E7A3EB1FD0DCB1201D91A5C06DBC2E63C3CE36A4D928E16F1EFA109B98989E976653A12D5AFD2F9A5C59F917AF066854EDB180AB8EC7B4CA924619C5AD0FA6112A0AB0FAFDD022EA666195A16F2F09071573034EE5C92A55B5FA9E352180D212463C369F5BF7484C11187557FE1F457EA678EC8008B2D410E90B270A9D259BEB982D1F3BF523684475D613A212BF3B02D1D97E76020C8126CE8F3043F50CF06B00B1AF9EA05D9D8AA08CDC1B11D6ABA71A88F53C3BB0ED524601C6A266E7D29F97BC03EF4BDC6D6F6F2407121B72CFF42EB 010001 223 -00B196FFA45454C5A1E71AAC941EAC3D6E1D659813592848ED9B1A536C2791E648FAA21CF2EAB86D2AD761D36C4110E5CD02A64F3DA5F1A869CB7972AB438D36E145EF9FA30B870F3BF3DEA689E34FBBB4F3A66241D1BA0446D0DFFE099E993A87C0AB9ACF2A0CE8CB1C7F8839729B67D673046348B02179650AECBCC800D29F4292A66CDA62ACE203AFEC470A8DD66D820F4D6E44927A494CE7F4E5F368B7EA4F0AF674B42590DF10BC590ADA577002065F6B0A372A38B76107DBB1FFC3FFA5BA498B722FCF02092928207509E1B983D2A2F3E7290E6413063E44C2787B0F3EBAEBBF470FA06F363474989FE483BB3CAFA0FA46980C4D7F7F377F9E64ABF9747F +00A8FA8AA5430761553E2EC44D3D9D1047BB311879352578954011BA3A8411FBFC45A6481D39492801FDBC00F08A70F1B0BE87E2E4721934AF1040CA8E7B3AC2998DCD89F75689C8F34055C1B5EAA151805B56422D53125530E34A7C461F8CB8683897C84548A4825279695406BB0E9DD40EFD3837352660D019D72F1B33CF393ED153C9486F36B42B9574444CC8CA794505055DD891640FD748BD2178CD4A0FFFD41AE193AB4B1CBCE41A72402DEA88FED852EDBE46C9E5D078A71D7FE780789EF6031837C2F1299563141C1ADD867E2A8C4862B039D6E9B8F184772D30776CACD17B6F15178E32917B5CF486685E4C162A885507DB95B391603D9C71880FFC87 010001 239 -00CAE53325355D2A393D819B0DD1E15482021594534E0D573A9B465790FCD4DECDED05681622733F5659C3F1F49059C006F8BFE2A1322C42818DE2C6EEC6F3D926B15B230D4F2D2EF154C2399F4AB2C9F453382278924C6511BABDFC4216AE7D8A5F9E42E4D163C2091AB1C078D5FB397107C66D84258CB0C52C23C6B64ED71E7BB02DFC70BB54E0435C8FB8D5BEB537C25798749AB4B8AE702D73C830B00D1F4478CEF2CAE36D40E8180FA52C794AEB1860C2490DAB34DB04FB46DEBFAA64669D0BDBE06EF2FF20F1239263E8BBCD123B5D5B06ADA383A006E5423C3620E5177D41BDD3AC989023609E1BB96A92738D64CB6E5A1DBED7602EE3F5304BEF352557 +00BAFDD9025A6B0FF5AC8C8E7DB2DB7410EE078616B93CE87F026845D992398ADA2C58E8ED5267C2745AFEB04AECA56E137371E60D3D86E413B2EDF768E75EE9FEBFDE942162760C6C85D63E778AFBBA59130B522493D662A10833348732AB8606B219367D6C7F8C8E643EE26CC303C6B58BBB9AE159648FF303D1A38FA237C7549FEF0A86ADEAE978F0C7F7809D24A1C3BAEF34E89AD63E25204AB0811E74C4295F7580BB067420C3B8EEE467AF2D29D3D0E7A2E50C29FC114DE248EB8028E84415A22BFC4C0FEB7714E330899E235DC0AE105EE7373C79D47479B7B48DFCF411DD83347E6B0644EE1F59509236EDFA8CD3D3C9DE5F2A065AA1105E19AB47E161 010001 255 diff --git a/xcode/lib-gpu-verify.xcodeproj/project.pbxproj b/xcode/lib-gpu-verify.xcodeproj/project.pbxproj @@ -13,11 +13,10 @@ 6A99B06F2B1297220004E4B7 /* montgomery.c in Sources */ = {isa = PBXBuildFile; fileRef = 6A7914CD2B0CF320001EDCC1 /* montgomery.c */; }; 6AA38E5B2B0A97FC00E85243 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 6AA38E5A2B0A97FC00E85243 /* main.c */; }; 6AB4D99D2B1645F900A686F2 /* montgomery-test.c in Sources */ = {isa = PBXBuildFile; fileRef = 6AB4D99C2B1645F900A686F2 /* montgomery-test.c */; }; - 6AD85E072AF71AD900662919 /* big-int-test.c in Sources */ = {isa = PBXBuildFile; fileRef = 6AF7487D2ADADF4500D58E08 /* big-int-test.c */; }; - 6AD85E0C2AFA510C00662919 /* openssl-test.c in Sources */ = {isa = PBXBuildFile; fileRef = 6AD85E0B2AFA510C00662919 /* openssl-test.c */; }; + 6ABC2E842B231DFF00033B90 /* util.c in Sources */ = {isa = PBXBuildFile; fileRef = 6ABC2E832B231DFF00033B90 /* util.c */; }; + 6ABC2E882B231E3D00033B90 /* reference-test.c in Sources */ = {isa = PBXBuildFile; fileRef = 6ABC2E862B231E3D00033B90 /* reference-test.c */; }; 6AF7487A2ADADEBD00D58E08 /* lib-gpu-verify.c in Sources */ = {isa = PBXBuildFile; fileRef = 6AF748792ADADEBD00D58E08 /* lib-gpu-verify.c */; }; 6AF748832ADADF4500D58E08 /* rsa-test.c in Sources */ = {isa = PBXBuildFile; fileRef = 6AF7487F2ADADF4500D58E08 /* rsa-test.c */; }; - 6AF748862ADADFAD00D58E08 /* opencl-test.c in Sources */ = {isa = PBXBuildFile; fileRef = 6AF748852ADADFAD00D58E08 /* opencl-test.c */; }; C3770EFD0E6F1138009A5A77 /* OpenCL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3770EFC0E6F1138009A5A77 /* OpenCL.framework */; }; /* End PBXBuildFile section */ @@ -55,15 +54,13 @@ 6AA38E612B0A9B2100E85243 /* lib-gpu-generate.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "lib-gpu-generate.entitlements"; sourceTree = "<group>"; }; 6AB4D99B2B1645F900A686F2 /* montgomery-test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "montgomery-test.h"; path = "../source/montgomery-test.h"; sourceTree = "<group>"; }; 6AB4D99C2B1645F900A686F2 /* montgomery-test.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = "montgomery-test.c"; path = "../source/montgomery-test.c"; sourceTree = "<group>"; }; - 6AD85E0A2AFA510C00662919 /* openssl-test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "openssl-test.h"; path = "../source/openssl-test.h"; sourceTree = "<group>"; }; - 6AD85E0B2AFA510C00662919 /* openssl-test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "openssl-test.c"; path = "../source/openssl-test.c"; sourceTree = "<group>"; }; + 6ABC2E832B231DFF00033B90 /* util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = util.c; path = ../source/util.c; sourceTree = "<group>"; }; + 6ABC2E852B231E0400033B90 /* util.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = util.h; path = ../source/util.h; sourceTree = "<group>"; }; + 6ABC2E862B231E3D00033B90 /* reference-test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "reference-test.c"; path = "../source/reference-test.c"; sourceTree = "<group>"; }; + 6ABC2E872B231E3D00033B90 /* reference-test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "reference-test.h"; path = "../source/reference-test.h"; sourceTree = "<group>"; }; 6AF748792ADADEBD00D58E08 /* lib-gpu-verify.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "lib-gpu-verify.c"; path = "../source/lib-gpu-verify.c"; sourceTree = "<group>"; }; - 6AF7487B2ADADF4500D58E08 /* big-int-test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "big-int-test.h"; path = "../source/big-int-test.h"; sourceTree = "<group>"; }; - 6AF7487D2ADADF4500D58E08 /* big-int-test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "big-int-test.c"; path = "../source/big-int-test.c"; sourceTree = "<group>"; }; 6AF7487F2ADADF4500D58E08 /* rsa-test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "rsa-test.c"; path = "../source/rsa-test.c"; sourceTree = "<group>"; }; 6AF748802ADADF4500D58E08 /* rsa-test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "rsa-test.h"; path = "../source/rsa-test.h"; sourceTree = "<group>"; }; - 6AF748842ADADFAD00D58E08 /* opencl-test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "opencl-test.h"; path = "../source/opencl-test.h"; sourceTree = "<group>"; }; - 6AF748852ADADFAD00D58E08 /* opencl-test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "opencl-test.c"; path = "../source/opencl-test.c"; sourceTree = "<group>"; }; C3770EFC0E6F1138009A5A77 /* OpenCL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenCL.framework; path = /System/Library/Frameworks/OpenCL.framework; sourceTree = "<absolute>"; }; /* End PBXFileReference section */ @@ -109,11 +106,11 @@ isa = PBXGroup; children = ( 6AF748802ADADF4500D58E08 /* rsa-test.h */, - 6AF7487B2ADADF4500D58E08 /* big-int-test.h */, - 6AF748842ADADFAD00D58E08 /* opencl-test.h */, - 6AD85E0A2AFA510C00662919 /* openssl-test.h */, 6A7914CE2B0CF320001EDCC1 /* gmp.h */, 6A7914CC2B0CF320001EDCC1 /* montgomery.h */, + 6ABC2E852B231E0400033B90 /* util.h */, + 6ABC2E872B231E3D00033B90 /* reference-test.h */, + 6AB4D99B2B1645F900A686F2 /* montgomery-test.h */, ); name = Headers; sourceTree = "<group>"; @@ -134,14 +131,12 @@ 6A8A795E2A89672700116D7D /* verify.cl */, 6A36F8882B0F938E00AB772D /* montgomery.cl */, 6AF748792ADADEBD00D58E08 /* lib-gpu-verify.c */, - 6AD85E0B2AFA510C00662919 /* openssl-test.c */, - 6AF7487D2ADADF4500D58E08 /* big-int-test.c */, 6AF7487F2ADADF4500D58E08 /* rsa-test.c */, - 6AF748852ADADFAD00D58E08 /* opencl-test.c */, 6A7914CB2B0CF320001EDCC1 /* gmp.c */, 6A7914CD2B0CF320001EDCC1 /* montgomery.c */, - 6AB4D99B2B1645F900A686F2 /* montgomery-test.h */, 6AB4D99C2B1645F900A686F2 /* montgomery-test.c */, + 6ABC2E832B231DFF00033B90 /* util.c */, + 6ABC2E862B231E3D00033B90 /* reference-test.c */, ); name = Sources; sourceTree = "<group>"; @@ -231,15 +226,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 6AD85E0C2AFA510C00662919 /* openssl-test.c in Sources */, - 6AD85E072AF71AD900662919 /* big-int-test.c in Sources */, 6AB4D99D2B1645F900A686F2 /* montgomery-test.c in Sources */, 6A99B06F2B1297220004E4B7 /* montgomery.c in Sources */, + 6ABC2E882B231E3D00033B90 /* reference-test.c in Sources */, 6A99B06E2B1293DA0004E4B7 /* gmp.c in Sources */, 6AF7487A2ADADEBD00D58E08 /* lib-gpu-verify.c in Sources */, 6A8A795F2A89672700116D7D /* verify.cl in Sources */, + 6ABC2E842B231DFF00033B90 /* util.c in Sources */, 6AF748832ADADF4500D58E08 /* rsa-test.c in Sources */, - 6AF748862ADADFAD00D58E08 /* opencl-test.c in Sources */, 6A36F8892B0F938E00AB772D /* montgomery.cl in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/xcode/lib-gpu-verify.xcodeproj/project.xcworkspace/xcuserdata/cedriczwahlen.xcuserdatad/UserInterfaceState.xcuserstate b/xcode/lib-gpu-verify.xcodeproj/project.xcworkspace/xcuserdata/cedriczwahlen.xcuserdatad/UserInterfaceState.xcuserstate Binary files differ. diff --git a/xcode/lib-gpu-verify.xcodeproj/xcuserdata/cedriczwahlen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/xcode/lib-gpu-verify.xcodeproj/xcuserdata/cedriczwahlen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -1263,9 +1263,7 @@ startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "682" - endingLineNumber = "682" - landmarkName = "rsa_tests()" - landmarkType = "9"> + endingLineNumber = "682"> <Locations> <Location uuid = "BFF56279-A16A-4556-9919-058156F61FD5 - b0b9078e770ca85a" @@ -1491,9 +1489,7 @@ startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "669" - endingLineNumber = "669" - landmarkName = "verify_pairs_with_opencl(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n, result)" - landmarkType = "9"> + endingLineNumber = "669"> <Locations> <Location uuid = "E76A4300-645A-48D3-AFAA-F40E9454639D - b0b9078e770caf52" @@ -1585,7 +1581,7 @@ endingColumnNumber = "9223372036854775807" startingLineNumber = "429" endingLineNumber = "429" - landmarkName = "pairs_from_files(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n)" + landmarkName = "verify_pairs_with_opencl(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n, result)" landmarkType = "9"> <Locations> <Location @@ -1678,7 +1674,7 @@ endingColumnNumber = "9223372036854775807" startingLineNumber = "425" endingLineNumber = "425" - landmarkName = "pairs_from_files(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n)" + landmarkName = "verify_pairs_with_opencl(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n, result)" landmarkType = "9"> </BreakpointContent> </BreakpointProxy> @@ -1694,7 +1690,7 @@ endingColumnNumber = "9223372036854775807" startingLineNumber = "464" endingLineNumber = "464" - landmarkName = "number_of_pairs()" + landmarkName = "verify_pairs_with_opencl(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n, result)" landmarkType = "9"> </BreakpointContent> </BreakpointProxy> @@ -1710,7 +1706,7 @@ endingColumnNumber = "9223372036854775807" startingLineNumber = "414" endingLineNumber = "414" - landmarkName = "pairs_from_files(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n)" + landmarkName = "verify_pairs_with_opencl(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n, result)" landmarkType = "9"> </BreakpointContent> </BreakpointProxy> @@ -1725,9 +1721,7 @@ startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "721" - endingLineNumber = "721" - landmarkName = "rsa_tests()" - landmarkType = "9"> + endingLineNumber = "721"> </BreakpointContent> </BreakpointProxy> <BreakpointProxy @@ -1742,7 +1736,7 @@ endingColumnNumber = "9223372036854775807" startingLineNumber = "458" endingLineNumber = "458" - landmarkName = "pairs_from_files(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n)" + landmarkName = "verify_pairs_with_opencl(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n, result)" landmarkType = "9"> </BreakpointContent> </BreakpointProxy> @@ -1758,7 +1752,7 @@ endingColumnNumber = "9223372036854775807" startingLineNumber = "466" endingLineNumber = "466" - landmarkName = "number_of_pairs()" + landmarkName = "verify_pairs_with_opencl(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n, result)" landmarkType = "9"> </BreakpointContent> </BreakpointProxy> @@ -3345,9 +3339,7 @@ startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "784" - endingLineNumber = "784" - landmarkName = "reference_tests()" - landmarkType = "9"> + endingLineNumber = "784"> </BreakpointContent> </BreakpointProxy> <BreakpointProxy @@ -3361,9 +3353,7 @@ startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "768" - endingLineNumber = "768" - landmarkName = "sexp_from_string_key(str_1, str_2, format)" - landmarkType = "9"> + endingLineNumber = "768"> </BreakpointContent> </BreakpointProxy> <BreakpointProxy @@ -3377,9 +3367,7 @@ startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "754" - endingLineNumber = "754" - landmarkName = "sexp_from_string(str, format)" - landmarkType = "9"> + endingLineNumber = "754"> </BreakpointContent> </BreakpointProxy> <BreakpointProxy @@ -3393,9 +3381,7 @@ startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "761" - endingLineNumber = "761" - landmarkName = "sexp_from_string(str, format)" - landmarkType = "9"> + endingLineNumber = "761"> <Locations> <Location uuid = "CE83768C-EDB9-4387-B742-DC98D1A4CA5B - bca392ed5f046d52" @@ -3516,9 +3502,7 @@ startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "873" - endingLineNumber = "873" - landmarkName = "reference_tests()" - landmarkType = "9"> + endingLineNumber = "873"> <Locations> <Location uuid = "4D8DE650-DB66-4341-82CF-F6AD23B1CEDE - ac4f85360ab7eac" @@ -3579,9 +3563,7 @@ startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "652" - endingLineNumber = "652" - landmarkName = "verify_pairs_with_opencl(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n, result)" - landmarkType = "9"> + endingLineNumber = "652"> <Locations> <Location uuid = "5EE37865-6671-456D-BF88-7BF9E15A6B3A - 126d44634feb25b3" @@ -3627,9 +3609,7 @@ startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "867" - endingLineNumber = "867" - landmarkName = "reference_tests()" - landmarkType = "9"> + endingLineNumber = "867"> </BreakpointContent> </BreakpointProxy> <BreakpointProxy @@ -3643,9 +3623,7 @@ startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "826" - endingLineNumber = "826" - landmarkName = "reference_tests()" - landmarkType = "9"> + endingLineNumber = "826"> <Locations> <Location uuid = "64ADE5A5-3C16-4A3A-A62D-9A9A79DCC136 - ac4f85360ab7a30" @@ -3692,7 +3670,7 @@ endingColumnNumber = "9223372036854775807" startingLineNumber = "476" endingLineNumber = "476" - landmarkName = "number_of_pairs()" + landmarkName = "rsa_tests()" landmarkType = "9"> <Locations> <Location @@ -3787,7 +3765,7 @@ endingColumnNumber = "9223372036854775807" startingLineNumber = "541" endingLineNumber = "541" - landmarkName = "verify_pairs_with_opencl(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n, result)" + landmarkName = "rsa_tests()" landmarkType = "9"> <Locations> <Location @@ -3849,9 +3827,7 @@ startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "558" - endingLineNumber = "558" - landmarkName = "verify_pairs_with_opencl(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n, result)" - landmarkType = "9"> + endingLineNumber = "558"> <Locations> <Location uuid = "A6F69856-0EC4-4570-8FCB-05ABC0C321EB - 126d44634feb2834" @@ -3913,7 +3889,7 @@ endingColumnNumber = "9223372036854775807" startingLineNumber = "502" endingLineNumber = "502" - landmarkName = "verify_pairs_with_opencl(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n, result)" + landmarkName = "rsa_tests()" landmarkType = "9"> </BreakpointContent> </BreakpointProxy> @@ -3928,9 +3904,7 @@ startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "566" - endingLineNumber = "566" - landmarkName = "verify_pairs_with_opencl(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n, result)" - landmarkType = "9"> + endingLineNumber = "566"> </BreakpointContent> </BreakpointProxy> <BreakpointProxy @@ -3945,7 +3919,7 @@ endingColumnNumber = "9223372036854775807" startingLineNumber = "534" endingLineNumber = "534" - landmarkName = "verify_pairs_with_opencl(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n, result)" + landmarkName = "rsa_tests()" landmarkType = "9"> <Locations> <Location @@ -3993,7 +3967,7 @@ endingColumnNumber = "9223372036854775807" startingLineNumber = "489" endingLineNumber = "489" - landmarkName = "verify_pairs_with_opencl(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n, result)" + landmarkName = "rsa_tests()" landmarkType = "9"> </BreakpointContent> </BreakpointProxy> @@ -4008,9 +3982,7 @@ startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "712" - endingLineNumber = "712" - landmarkName = "rsa_tests()" - landmarkType = "9"> + endingLineNumber = "712"> </BreakpointContent> </BreakpointProxy> <BreakpointProxy @@ -4025,7 +3997,7 @@ endingColumnNumber = "9223372036854775807" startingLineNumber = "469" endingLineNumber = "469" - landmarkName = "number_of_pairs()" + landmarkName = "verify_pairs_with_opencl(bases, b_len, exponents, e_len, moduli, m_len, signatures, s_len, pks, n, result)" landmarkType = "9"> </BreakpointContent> </BreakpointProxy> diff --git a/xcode/montgomery.cl b/xcode/montgomery.cl @@ -1,9 +1,3 @@ -// -// gmp_GPU.c -// lib-gpu-verify -// -// Created by Cedric Zwahlen on 25.11.2023. -// #ifndef MINI_GMP_LIMB_TYPE #define MINI_GMP_LIMB_TYPE long @@ -2916,6 +2910,7 @@ __kernel void montgomery(__global void *signature, __global unsigned long *s_off // the modulus can be assumed to be uneven – always if (mpz_even_p(m)) { + /* mpz_t bb, x1, x2, q, powj; mpz_init(bb); diff --git a/xcode/verify.cl b/xcode/verify.cl @@ -1,3 +1,28 @@ +/* + * lib-gpu-verify + * + * This software contains code derived from or inspired by the BigDigit library, + * <http://www.di-mgt.com.au/bigdigits.html> + * which is distributed under the Mozilla Public License, version 2.0. + * + * The original code and modifications made to it are subject to the terms and + * conditions of the Mozilla Public License, version 2.0. A copy of the + * MPL license can be obtained at + * https://www.mozilla.org/en-US/MPL/2.0/. + * + * Changes and additions to the original code are as follows: + * - Copied various parts of the BigDigit library into this kernel. + * - Some functions and macros were changed to accomodate the architecture of OpenCL. + * - functions were added to extend the functionality required by this OpenCL kernel. + * + * Contributors: + * - Cedric Zwahlen cedric.zwahlen@bfh.ch + * + * Please note that this software is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Mozilla Public License, version 2.0, for the specific language + * governing permissions and limitations under the License. + */ // macros