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:
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