From d65da2dbb4c43bd7c0e83e299b374acd6bff54f9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Jan 2009 23:33:03 +1030 Subject: [PATCH] Much nicer example: genetic algo to approximate jpeg (And cute image!) --- ccan/antithread/examples/Makefile | 15 +- ccan/antithread/examples/arabella.c | 722 ++++++++++++++++++++++++++ ccan/antithread/examples/arabella.jpg | Bin 0 -> 31201 bytes ccan/antithread/examples/md5_server.c | 129 ----- ccan/antithread/examples/md5_worker.c | 274 ---------- 5 files changed, 728 insertions(+), 412 deletions(-) create mode 100644 ccan/antithread/examples/arabella.c create mode 100644 ccan/antithread/examples/arabella.jpg delete mode 100644 ccan/antithread/examples/md5_server.c delete mode 100644 ccan/antithread/examples/md5_worker.c diff --git a/ccan/antithread/examples/Makefile b/ccan/antithread/examples/Makefile index 40391853..f562dd78 100644 --- a/ccan/antithread/examples/Makefile +++ b/ccan/antithread/examples/Makefile @@ -1,12 +1,9 @@ -CFLAGS=-g -Wall -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -I../../.. +CFLAGS=-g -O3 -Wall -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -I../../.. -all: find_md5 md5_worker dns_lookup - -find_md5: md5_server.c ../../../libccan.a - $(CC) $(CFLAGS) -o $@ $^ - -md5_worker: md5_worker.c ../../../libccan.a - $(CC) $(CFLAGS) -o $@ $^ +all: dns_lookup dns_lookup: dns_lookup.c ../../../libccan.a - $(CC) $(CFLAGS) -o $@ $^ + $(CC) $(CFLAGS) -o $@ $^ + +arabella: arabella.c ../../../libccan.a + $(CC) $(CFLAGS) -o $@ $^ -ljpeg -lm diff --git a/ccan/antithread/examples/arabella.c b/ccan/antithread/examples/arabella.c new file mode 100644 index 00000000..2171dc12 --- /dev/null +++ b/ccan/antithread/examples/arabella.c @@ -0,0 +1,722 @@ +/* Antithread example to approximate picture with triangles using + * genetic algorithm. Example for a 2 cpu system: + * ./arabella arabella.jpg out out.save 2 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Define this to run 100 times without dumping state +//#define BENCHMARK + +/* How many drawings in entire population. */ +#define POPULATION_SIZE 1000 + +/* How many generations without 1% improvement before we terminate. */ +#define PLATEAU_GENS 200 + +/* An image buffer to render into. */ +struct image { + unsigned height, width; + unsigned int stride; + /* RGB data */ + unsigned char *buffer; +}; + +/* A drawing is a (fixed) number of triangles. */ +struct drawing { + struct triangle *tri; + unsigned int num_tris; + unsigned long score; +}; + +/* Here's a triangle. */ +struct triangle { + struct { + unsigned int x, y; + } coord[3]; + + /* RGBA */ + unsigned char color[4]; + unsigned char mult; + uint16_t add[3]; +}; + +/* Get pointer into image at a specific location. */ +static unsigned char *image_at(struct image *image, + unsigned int x, unsigned int y) +{ + return image->buffer + y * image->stride + x * 3; +} + +/* Blend a dot into this location. */ +static void add_dot(unsigned char *buf, + unsigned char mult, const uint16_t add[]) +{ + unsigned int c; + /* /256 isn't quite right, but it's much faster than /255 */ + for (c = 0; c < 3; c++) + buf[c] = (buf[c] * mult + add[c]) / 256; +} + +/* Code based on example taken from: + * http://www.devmaster.net/forums/showthread.php?t=1094 */ +static void add_flat_triangle(struct image *image, + int x0, int y0, int x1, int y1, int x2, int y2, + unsigned char mult, const uint16_t add[]) +{ + unsigned char *buf; + unsigned int i, j; + + // compute slopes for the two triangle legs + float dx0 = (float)(x2 - x0) / (y2 - y0); + float dx1 = (float)(x2 - x1) / (y2 - y1); + + int yRange = 0; + + float lx = (float) x0, rx = (float) x1; + + if (y0 < y2) { + yRange = y2 - y0; + buf = image_at(image, 0, y0); + } else { + yRange = y0 - y2; + buf = image_at(image, 0, y2); + lx = rx = (float)x2; + } + + for (i=0; i < yRange; ++i) { + for (j=(int)(lx); j<(int)((rx) + 1.0f); ++j) + add_dot(buf + 3 * j, mult, add); + + lx += dx0; + rx += dx1; + buf += image->stride; + } +} + +static void swap(int *a, int *b) +{ + int tmp = *a; + *a = *b; + *b = tmp; +} + +static void paint_triangle(struct image *image, const struct triangle *tri) +{ + int i0 = 0, i1 = 1, i2 = 2; + int x0, y0, x1, y1, x2, y2; + + /* Could do this on triangle creation. */ + // sort verts by height + if (tri->coord[i0].y > tri->coord[i1].y) swap(&i0, &i1); + if (tri->coord[i0].y > tri->coord[i2].y) swap(&i0, &i2); + if (tri->coord[i1].y > tri->coord[i2].y) swap(&i1, &i2); + + x0 = tri->coord[i0].x, y0 = tri->coord[i0].y; + x1 = tri->coord[i1].x, y1 = tri->coord[i1].y; + x2 = tri->coord[i2].x, y2 = tri->coord[i2].y; + + // test for easy cases, else split trinagle in two and render both halfs + if (y1 == y2) { + if (x1 > x2) swap(&x1, &x2); + add_flat_triangle(image, x1, y1, x2, y2, x0, y0, + tri->mult, tri->add); + } else if (y0 == y1) { + if (x0 > x1) swap(&x0, &x1); + add_flat_triangle(image, x0, y0, x1, y1, x2, y2, + tri->mult, tri->add); + } else { + // compute x pos of the vert that builds the splitting line with x1 + int tmp_x = x0 + (int)(0.5f + (float)(y1-y0) * (float)(x2-x0) / (float)(y2-y0)); + + if (x1 > tmp_x) swap(&x1, &tmp_x); + add_flat_triangle(image, x1, y1, tmp_x, y1, x0, y0, + tri->mult, tri->add); + add_flat_triangle(image, x1, y1, tmp_x, y1, x2, y2, + tri->mult, tri->add); + } +} + +/* Create a new image, allocated off context. */ +static struct image *new_image(const void *ctx, + unsigned width, unsigned height, unsigned stride) +{ + struct image *image = talloc(ctx, struct image); + + image->width = width; + image->height = height; + image->stride = stride; + image->buffer = talloc_zero_array(image, unsigned char, + stride * height); + return image; +} + +/* Taken from JPEG example code. Quality is 1 to 100. */ +static void write_jpeg_file(const struct image *image, + const char *filename, int quality) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + FILE *outfile; + JSAMPROW row_pointer[1]; + int row_stride; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + + if ((outfile = fopen(filename, "wb")) == NULL) + err(1, "can't open %s", filename); + + jpeg_stdio_dest(&cinfo, outfile); + + cinfo.image_width = image->width; + cinfo.image_height = image->height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, TRUE); + + jpeg_start_compress(&cinfo, TRUE); + row_stride = image->width * 3; + + while (cinfo.next_scanline < cinfo.image_height) { + row_pointer[0] = image->buffer + cinfo.next_scanline*row_stride; + (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + jpeg_finish_compress(&cinfo); + fclose(outfile); + + jpeg_destroy_compress(&cinfo); +} + +static struct image *read_jpeg_file(const void *ctx, const char *filename) +{ + struct jpeg_decompress_struct cinfo; + struct image *image; + struct jpeg_error_mgr jerr; + FILE *infile; + int row_stride; + + if ((infile = fopen(filename, "rb")) == NULL) + err(1, "can't open %s", filename); + + cinfo.err = jpeg_std_error(&jerr); + + jpeg_create_decompress(&cinfo); + + jpeg_stdio_src(&cinfo, infile); + + jpeg_read_header(&cinfo, TRUE); + + jpeg_start_decompress(&cinfo); + row_stride = cinfo.output_width * cinfo.output_components; + + image = new_image(ctx, + cinfo.output_width, cinfo.output_height, row_stride); + while (cinfo.output_scanline < cinfo.output_height) { + JSAMPROW row = &image->buffer[cinfo.output_scanline*row_stride]; + jpeg_read_scanlines(&cinfo, &row, 1); + } + + (void) jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + fclose(infile); + return image; +} + +/* Higher means closer to perfect match. We assume images same size. */ +static unsigned long compare_images(const struct image *a, + const struct image *b) +{ + unsigned long result = 0; + unsigned i; + + /* Huge images won't work here. We'd need to get cleverer. */ + assert(a->height * a->stride < ULONG_MAX / 8); + + for (i = 0; i < a->height * a->stride; i++) { + if (a->buffer[i] > b->buffer[i]) + result += a->buffer[i] - b->buffer[i]; + else + result += b->buffer[i] - a->buffer[i]; + } + return result; +} + +/* Precalculate the alpha adds and multiplies for this color/alpha combo. */ +static void calc_multipliers(struct triangle *tri) +{ + /* Multiply by 255 - alpha. */ + tri->mult = (255 - tri->color[3]); + /* Add alpha * color */ + tri->add[0] = (tri->color[0] * tri->color[3]); + tri->add[1] = (tri->color[1] * tri->color[3]); + tri->add[2] = (tri->color[2] * tri->color[3]); +} + +/* Render the image of this drawing, and return it. */ +static struct image *image_of_drawing(const void *ctx, + const struct drawing *drawing, + const struct image *master) +{ + struct image *image; + unsigned int i; + + image = new_image(ctx, master->width, master->height, master->stride); + + for (i = 0; i < drawing->num_tris; i++) + paint_triangle(image, &drawing->tri[i]); + return image; +} + +/* Render the image and compare with the master. */ +static void score_drawing(struct drawing *drawing, + const struct image *master) +{ + struct image *image; + + /* We don't allocate it off the drawing, since we don't need + * it inside the shared area. */ + image = image_of_drawing(NULL, drawing, master); + drawing->score = compare_images(image, master); + talloc_free(image); +} + +/* Create a new drawing allocated off context (which is the antithread + * pool context, so this is all allocated inside the pool so the + * antithreads can access it). + */ +static struct drawing *new_drawing(const void *ctx, unsigned int num_tris) +{ + struct drawing *drawing = talloc_zero(ctx, struct drawing); + drawing->num_tris = num_tris; + drawing->tri = talloc_array(drawing, struct triangle, num_tris); + return drawing; +} + +/* Make a random change to the drawing: frob one triangle. */ +static void mutate_drawing(struct drawing *drawing, + const struct image *master) +{ + unsigned int r = random(); + struct triangle *tri = &drawing->tri[r % drawing->num_tris]; + + r /= drawing->num_tris; + r %= 10; + if (r < 6) { + if (r % 2) + tri->coord[r/2].x = random() % master->width; + else + tri->coord[r/2].y = random() % master->height; + } else { + tri->color[r - 6] = random() % 256; + } + calc_multipliers(tri); +} + +/* Breed two drawings together, and throw in a mutation. */ +static struct drawing *breed_drawing(const void *ctx, + const struct drawing *a, + const struct drawing *b, + const struct image *master) +{ + unsigned int i; + struct drawing *drawing; + unsigned int r = random(), randmax = RAND_MAX; + + assert(a->num_tris == b->num_tris); + drawing = new_drawing(ctx, a->num_tris); + + for (i = 0; i < a->num_tris; i++) { + switch (r & 1) { + case 0: + /* Take from A. */ + drawing->tri[i] = a->tri[i]; + break; + case 1: + /* Take from B. */ + drawing->tri[i] = b->tri[i]; + break; + } + r >>= 1; + randmax >>= 1; + if (randmax == 0) { + r = random(); + randmax = RAND_MAX; + } + } + mutate_drawing(drawing, master); + score_drawing(drawing, master); + return drawing; +} + +/* This is our anti-thread. It does the time-consuming operation of + * breeding two drawings together and scoring the result. */ +static void *breeder(struct at_pool *atp, const struct image *master) +{ + const struct drawing *a, *b; + + /* For simplicity, controller just hands us two pointers in + * separate writes. It could put them in the pool for us to + * read. */ + while ((a = at_read_parent(atp)) != NULL) { + struct drawing *child; + b = at_read_parent(atp); + + child = breed_drawing(at_pool_ctx(atp), a, b, master); + at_tell_parent(atp, child); + } + /* Unused: we never exit. */ + return NULL; +} + +/* We keep a very rough count of how much work each athread has. This + * works fine since fairly all rendering takes about the same time. + * + * Better alternative would be to put all the pending work somewhere + * in the shared area and notify any idle thread. The threads would + * keep looking in that shared area until they can't see any more + * work, then they'd at_tell_parent() back. */ +struct athread_work { + struct athread *at; + unsigned int pending; +}; + +/* It's assumed that there isn't more than num results pending. */ +static unsigned gather_results(struct athread_work *athreads, + unsigned int num_threads, + struct drawing **drawing, + unsigned int num, + bool block) +{ + unsigned int i, maxfd = 0, done = 0; + struct timeval zero = { .tv_sec = 0, .tv_usec = 0 }; + + /* If it mattered, we could cache this fd mask and maxfd. */ + for (i = 0; i < num_threads; i++) { + if (at_fd(athreads[i].at) > maxfd) + maxfd = at_fd(athreads[i].at); + } + + do { + fd_set set; + FD_ZERO(&set); + for (i = 0; i < num_threads; i++) + FD_SET(at_fd(athreads[i].at), &set); + + if (select(maxfd+1, &set, NULL, NULL, block ? NULL : &zero) < 0) + err(1, "Selecting on antithread results"); + + for (i = 0; i < num_threads; i++) { + if (!FD_ISSET(at_fd(athreads[i].at), &set)) + continue; + *drawing = at_read(athreads[i].at); + if (!*drawing) + err(1, "Error with thread %u", i); + drawing++; + num--; + athreads[i].pending--; + done++; + } + } while (block && num); + + return done; +} + +/* Hand work to an antithread to breed these two together. */ +static void tell_some_breeder(struct athread_work *athreads, + unsigned int num_threads, + const struct drawing *a, const struct drawing *b) +{ + unsigned int i, best = 0; + + /* Find least loaded thread. */ + for (i = 1; i < num_threads; i++) { + if (athreads[i].pending < athreads[best].pending) + best = i; + } + + at_tell(athreads[best].at, a); + at_tell(athreads[best].at, b); + athreads[best].pending++; +} + +/* We seed initial triangles colours from the master image. */ +static const unsigned char *initial_random_color(const struct image *master) +{ + return master->buffer + (random() % (master->height * master->width))*3; +} + +/* Create an initial random drawing. */ +static struct drawing *random_drawing(const void *ctx, + const struct image *master, + unsigned int num_tris) +{ + struct drawing *drawing = new_drawing(ctx, num_tris); + unsigned int i; + + for (i = 0; i < drawing->num_tris; i++) { + unsigned int c; + struct triangle *tri = &drawing->tri[i]; + for (c = 0; c < 3; c++) { + tri->coord[c].x = random() % master->width; + tri->coord[c].y = random() % master->height; + } + memcpy(tri->color, initial_random_color(master), 3); + tri->color[3] = (random() % 255) + 1; + calc_multipliers(tri); + } + score_drawing(drawing, master); + return drawing; +} + +/* Read in a drawing from the saved state file. */ +static struct drawing *read_drawing(const void *ctx, FILE *in, + const struct image *master) +{ + struct drawing *drawing; + unsigned int i; + + if (fscanf(in, "%u triangles:\n", &i) != 1) + errx(1, "Reading saved state"); + drawing = new_drawing(ctx, i); + for (i = 0; i < drawing->num_tris; i++) { + unsigned int color[4]; + if (fscanf(in, "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u\n", + &drawing->tri[i].coord[0].x, + &drawing->tri[i].coord[0].y, + &drawing->tri[i].coord[1].x, + &drawing->tri[i].coord[1].y, + &drawing->tri[i].coord[2].x, + &drawing->tri[i].coord[2].y, + &color[0], &color[1], &color[2], &color[3]) != 10) + errx(1, "Reading saved state"); + drawing->tri[i].color[0] = color[0]; + drawing->tri[i].color[1] = color[1]; + drawing->tri[i].color[2] = color[2]; + drawing->tri[i].color[3] = color[3]; + calc_multipliers(&drawing->tri[i]); + } + score_drawing(drawing, master); + return drawing; +} + +/* Comparison function for sorting drawings best to worst. */ +static int compare_drawing_scores(const void *_a, const void *_b) +{ + struct drawing **a = (void *)_a, **b = (void *)_b; + + return (*a)->score - (*b)->score; +} + +/* Save one drawing to state file */ +static void dump_drawings(struct drawing **drawing, const char *outname) +{ + FILE *out; + unsigned int i, j; + char *tmpout = talloc_asprintf(NULL, "%s.tmp", outname); + + out = fopen(tmpout, "w"); + if (!out) + err(1, "Opening %s", tmpout); + fprintf(out, "POPULATION_SIZE=%u\n", POPULATION_SIZE); + for (i = 0; i < POPULATION_SIZE; i++) { + fprintf(out, "%u triangles:\n", drawing[i]->num_tris); + for (j = 0; j < drawing[i]->num_tris; j++) { + fprintf(out, "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u\n", + drawing[i]->tri[i].coord[0].x, + drawing[i]->tri[i].coord[0].y, + drawing[i]->tri[i].coord[1].x, + drawing[i]->tri[i].coord[1].y, + drawing[i]->tri[i].coord[2].x, + drawing[i]->tri[i].coord[2].y, + drawing[i]->tri[j].color[0], + drawing[i]->tri[j].color[1], + drawing[i]->tri[j].color[2], + drawing[i]->tri[j].color[3]); + } + } + if (fclose(out) != 0) + err(1, "Failure closing %s", tmpout); + + if (rename(tmpout, outname) != 0) + err(1, "Renaming %s over %s", tmpout, outname); + talloc_free(tmpout); +} + +/* Save state file. */ +static void dump_state(struct drawing *drawing[POPULATION_SIZE], + const struct image *master, + const char *outpic, + const char *outstate, + unsigned int gen) +{ + char *out = talloc_asprintf(NULL, "%s.%08u.jpg", outpic, gen); + struct image *image; + printf("Dumping gen %u to %s & %s\n", gen, out, outstate); + dump_drawings(drawing, outstate); + image = image_of_drawing(out, drawing[0], master); + write_jpeg_file(image, out, 80); + talloc_free(out); +} + +/* Biassed coinflip moves us towards top performers. I didn't spend + * too much time on it, but 1/32 seems to give decent results (see + * breeding-algos.gnumeric). */ +static struct drawing *select_good_drawing(struct drawing *drawing[], + unsigned int population) +{ + if (population == 1) + return drawing[0]; + if (random() % 32) + return select_good_drawing(drawing, population/2); + return select_good_drawing(drawing + population/2, population/2); +} + +static void usage(void) +{ + errx(1, "usage: []"); +} + +int main(int argc, char *argv[]) +{ + struct image *master; + unsigned int gen, since_prev_best, num_threads, i; + struct drawing *drawing[POPULATION_SIZE]; + unsigned long prev_best, poolsize; + struct at_pool *atp; + struct athread_work *athreads; + + if (argc != 6 && argc != 7) + usage(); + + /* Room for triangles and master image, with some spare. + * We should really read master image header first, so we can be + * more precise than "about 3MB". ccan/alloc also needs some + * more work to be more efficient. */ + poolsize = (POPULATION_SIZE + POPULATION_SIZE/4) * (sizeof(struct drawing) + atoi(argv[4]) * sizeof(struct triangle)) * 2 + 1000 * 1000 * 3; + atp = at_pool(poolsize); + if (!atp) + err(1, "Creating pool of %lu bytes", poolsize); + + /* Auto-free the pool and anything hanging off it (eg. threads). */ + talloc_steal(talloc_autofree_context(), atp); + + /* Read in file */ + master = read_jpeg_file(at_pool_ctx(atp), argv[1]); + + if (argc == 6) { + printf("Creating initial population"); + fflush(stdout); + for (i = 0; i < POPULATION_SIZE; i++) { + drawing[i] = random_drawing(at_pool_ctx(atp), + master, atoi(argv[4])); + printf("."); + fflush(stdout); + } + printf("\n"); + } else { + FILE *state; + char header[100]; + state = fopen(argv[5], "r"); + if (!state) + err(1, "Opening %s", argv[5]); + fflush(stdout); + fgets(header, 100, state); + printf("Loading initial population from %s: %s", argv[5], + header); + for (i = 0; i < POPULATION_SIZE; i++) { + drawing[i] = read_drawing(at_pool_ctx(atp), + state, master); + printf("."); + fflush(stdout); + } + } + + num_threads = atoi(argv[5]); + if (!num_threads) + usage(); + + /* Hang the threads off the pool (not *in* the pool). */ + athreads = talloc_array(atp, struct athread_work, num_threads); + for (i = 0; i < num_threads; i++) { + athreads[i].pending = 0; + athreads[i].at = at_run(atp, breeder, master); + if (!athreads[i].at) + err(1, "Creating antithread %u", i); + } + + since_prev_best = 0; + /* Worse than theoretically worst case. */ + prev_best = master->height * master->stride * 256; + + for (gen = 0; since_prev_best < PLATEAU_GENS; gen++) { + unsigned int j, done = 0; + struct drawing *new[POPULATION_SIZE/4]; + + qsort(drawing, POPULATION_SIZE, sizeof(drawing[0]), + compare_drawing_scores); + + printf("Best %lu, worst %lu\n", + drawing[0]->score, drawing[POPULATION_SIZE-1]->score); + + /* Probability of being chosen to breed depends on + * rank. We breed over lowest 1/4 population. */ + for (j = 0; j < POPULATION_SIZE / 4; j++) { + struct drawing *best1, *best2; + + best1 = select_good_drawing(drawing, POPULATION_SIZE); + best2 = select_good_drawing(drawing, POPULATION_SIZE); + + tell_some_breeder(athreads, num_threads, best1, best2); + + /* We reap during loop, so return pipes don't fill. + * See "Better alternative" above. */ + done += gather_results(athreads, num_threads, + new + done, j - done, false); + } + + /* Collate final results. */ + gather_results(athreads, num_threads, new+done, j-done, true); + + /* Overwrite bottom 1/4 */ + for (j = POPULATION_SIZE * 3 / 4; j < POPULATION_SIZE; j++) { + talloc_free(drawing[j]); + drawing[j] = new[j - POPULATION_SIZE * 3 / 4]; + } + + /* We dump on every 1% improvement in score. */ + if (drawing[0]->score < prev_best * 0.99) { +#ifndef BENCHMARK + dump_state(drawing, master, argv[2], argv[3], gen); +#endif + prev_best = drawing[0]->score; + since_prev_best = 0; + } else + since_prev_best++; + +#ifdef BENCHMARK + if (gen == 100) + exit(0); +#endif + } + + /* Dump final state */ + printf("No improvement over %lu for %u gens\n", + prev_best, since_prev_best); + dump_state(drawing, master, argv[2], argv[3], gen); + return 0; +} diff --git a/ccan/antithread/examples/arabella.jpg b/ccan/antithread/examples/arabella.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4063fb631f6f9567bf9ccf4701cd20dee077d25f GIT binary patch literal 31201 zcmbTdbzB?W*FBm*aJS+fEJ$(J;_gzcxCIMdsNhyK5ZonbptzM5D8*WeyA*e6DN<-l zZ~B$zeSi11v-u!GHN@Qo z0MOM1KmY&$E`R_?0>D5c8gzpLN&kfj(YO{!4!}h3f#?kYqyu363j+Y{K>C01TOjv; ze9#eqLjM{|fc6^#3jYgJqVY5a#s9>Ka|eL_9S2A`SP8&EV*zyA1E5`$m5ep@S(Np4 z4NOf`jnF`VPsc<>0HA8YZz&-t$bvSs>t7asXY}vAx~8rHi?D#WfDiy6EGQ@?Ehr={ zB*Y>lCM_x=Ei45v@N@SL(9{J8iUz`u)I@!zq3F~@ptZj?f7NyVG5{AF8wVQ;7Y7Fi z4-XfgkdlayfPj#eoPvask&c;(k&c0Z1uVeD!p6tWz`!lR!zU;tDk{p%B_$^*EGr-) zD*QJH5DyQJkbsbeh=@j*m4Q|G|Fiw+1CZeY-U9|PfouQ_G9V@y@XsKC89h%d^qJ!C z$>KkTE?*E9HV!TxJ^|XH4SjL|Vq#)|FtM;e=u`lo|1ED285TLKkP8j8Zrv zy%d-IQByya(bQM4h=X4w9{vMr8d^FIPA+a9h^Uyjgrt@Qh4ER(4KqURilXC96zKN`Gv)$<;|__o!!0tgTte57vC?hu5W(a-u>l;uJM1<`iI&7jTaf3 z7X}D~3Bviy3y2YnZkS{sELI_GawS6?dl&_qa0D*pqx90Iemr&&qpwsBepC1lz@nQR z-~Lkj7qkCKKHG5d$u|L|G{5MrXsn+%f-pa8hJs}sqPpVR=n2)Nx75)KD~{0$hg z){0v3ntR^^gz`qZGw}T_sre_Ul7m_LtJ9_h1Eu_npow#wAB#PX+BEfO7D#HkfL-X0gKR)vl_PaNkEbh@TTX+k!!Dwc#i5(m0wY|=NgRZc|i>JDVbwUfrz;J!@&$q`CZ71;o7%z$2V^N^|)OBAyB$n>B@t zjH|MYZ1-wA?w+X8cc2(D`VJo@SK*Xa5}a$sLjgFwkCc{`enH+77D2c^23EqT7@QnR ze2FY!c`@)B(oZ)af5Wkm&mKE95hl(!FcxDGXT@D%sUf$Tpqf)v!4~=Y9;r0oEPhg9 zO(NVdKW@U))cr}+c#z11Mx`HY{q;F>EGi5+A{G_8N;js{>K|JEtDZMuZU-NiYT-yX zZSFnPh#`Hs)LSma?B2SaS6MxZG%~aZ%5->RjWzMiTpw9tV(=WY+Y~^x;!=Q1O;b05 zt?p^+Hbi8ZmC0WLy5*?{z{|8~!Tj{y7k`z_7 z(KL6YPSU>4H&d{V5HreMBWJJXQ|CT|u}*9&w=7Xl)bM;_v3yzLvw>at62BKNTvGLi z6+3fFi=g^f39yVJD5_;50qaxFgjihYFN(Zge*ymti)^@~lpK`vqZ;GwsMrA1=etKWHFw8sG5@#im+aY&U%l*>6i0obY!JqbnqF zG+?Fom7a}w?W-Cxv&IPF}aRkeV^0U67FGA6hy9zrhAJ;&Nk zPjsiEux62*5G^y6JF@MQ+^<1~PG>VFv(VDkyq1ZVx0Q2;<=J#me(j_bl1XCOrkpnl zsR5j847_@kStb3=#M)O0C8dU=GyklLpuo6`m;*=BK}eNvbu=x^&@5L%KBr5T?){r* zKrOF9*08)4F-d8|7u`%*T=kL$nu5He0NVE}wXW$O+{H@1Hs9n) zcjjY?ar@-ZRZ)<(xrm=y6h*uc->M=O|8A20wT&U0%Nr+D2bJz^m6`fZOig> z-*XR{NCl#ZdZf~>_IL&4D#r3ypGM5xfp&!Qcjskk*~S%bbz0D&nWHP_UgbKD(xWsr zRcG$(uneA&t>P4j=&3Qc7@yvFGT`!qSpV^K)yff1y$oHkQuS)3fN$7*z_wrBTskbT ziqD$P9X^jVn{=zg=3~kYTpU1k=CR_qhnyW*n1DZ?93<|l|!*($ryqSGrTG9GuMyE4~);D z8m%29u2P;!g7xQZO5kg%u1aOOyf9o;pMCkIGr6&jzryXj!Y|ln`Rlgp4xH=7Jj#w@iB9(@jq57Pu z_|l)nMm({i{Sq)&7f2{o3wIJ?%G~KByBG$9&aq6+NHRE-3jo=D>$MAmXmYC4dR_)f zRkIhH%re{x;Tr!OAFY@n&pYsLt*v7GZmbfVrs}eENX{2&mAeC1UM%8$fop04kgN_; zU2Cdg)GKe3kFv~?^p(&}A}1r= z>ToCx1N3=LJ5ZhG533cxF5SFC1MRwg%Taz$_j-^a-_)>hI^=12NF!*ryEptl?_4{FIbvUd_x+8M67J^^|3{$ zps;r!C~2!>ipb@XpEr{um47DjBUGa@H~Q0RQS>((_LZ}}y3s1WH^O^)h=}RgO0UYL zA>|VzsICrorva8RofHlad#dN$N*FU=@{)ojLj!(3Smj|09g*fve-SL&XEH3c~WLe5d2Tb~2cb zYwTNQFDcdrlf5aY9hfJVV~`)_TcE~xDi|{qnqeYv>ROmoae7TPs|)f~@hz`(ZUPc- z0gx3?$%ZN+i1sh*2&1J(*%GkQt5K#lB%2u*W~$}HWAYheb%t4CIqJ3@dzend7vtY=fQEOxN=_tw4`-jayBk`&kEa{J8m zI){k?pfc@NJ>7gO5EDDDEycb^EGb{2dGWL6t<=81i%l8#@Z`plC0{`Yt1#BZERskbnl-YMFmJH;bp_BVbjIS`rFAN~`!x)_6PR$d;giKaV_(r%b2e>zN)-wTGXj}@O zO-cQ%ld|GKdW-}nN(uU%@YeNo2#{1dYI?AzA#91bTb}afQqVd%t0;EW-q2|#@l{vG zvt;BgMv`BMY?t<-fHXpgVX7*zcmsXEO#Ea|VjO*~-m;c{_Tml4>6S_&Z>`kM;Z%>M z)>Kaqz^T@aayv3Jn=ND=hp+2Qvbr7~hKHBpD#TQ~ni%mh&Tt`bJ{2jP$zSq5C8>5E& zn&y*;s#ta&&+6EI&yxVLk)z>VvAJ~#+C%Hu_Cb|>u_g8)b;P=icyEAuHFKqH8@LEc z{5Vga%m#8f?!6SrD|HfNpX;rGh|?PC4zJ;&nk)f1)Y)3a8L9E|7XGYbIIU+@PPwi0 z$rb|_ccm50*=~7_X&jn|<%*4c`dK4yUz8sc5VYuo-Px zv3+^*Znkwv^~csE13loULgrG8GCC>lMMpZhV5yC3ROg9HJSHun96gF7I*csj(DE=T zpuXbsvd55X&eX$mvg{)Wo_A)=dy>!XYnTr|@v>b^3u~GJX1xRaTgZ3#axrP^m!`^- z?!x8~gPs+4W=xGD%AV&vRAnLscP%+{4M{Dx&>NHQRxhY#+oL@8X{0Q7b9OJDo*RD< zikMfCv>*B4105PJs_mTJlQWX|k~h&cqjST{>J1$Epj3Lq3~e*BkCmlc136N~)*OXO z7-<>u)&nVDY85g#<^=(_3RpqETI(guJpD%KSxm+xD^eC{|vV1dG=6B4l`u4dK;2 z1#|gf+AG%tQ11_#L*M%vGW5PagQbgI7C90JT~lfC=Zx- z{h5>$@=aAnr6|bbi}>n7)(@nk{5z1;t)aH#>H_k}Q^jckZasSaj@){5mIt3Vxa&R9 zc$iY(R%^K;mQgEn3RJ}x%bC3?h-{YZakhIAqI&6rK_|^3rH&NCgDo4SK) zP=_1wb7<^0M+eE7xfV;MwOq4lqJp$zGrRDvQ7VsNou}dHv*Dp;^_Kp=f-fOQUHfVR z%lX}m>2#LDDWP-cr?{Kx4h5%N;rhx}bX4JVCXNi-d7tK5ixrF_gp%5!QjdEWTfNf* zX&(z$&@~YdCVqm?oodzVz|4bmF?fZ~a2j~!&AgEfZso+Sl|vyIwd#@qt5wJP-_2)V z1Quk7=~9p@&_%2YxF<_Ty~mH5F!!p-c3gT|-NHC|&5(+mL04jvqdSl zl3|1OHoeLs`8yf=AHdUbqd0#aU4|=U@zZB7r#4w%zTL*?CQu+)e&24@@%_nEH_1R{ z{@{bDXK94#zn4on%L9WJX)z~FxmO&XR*ZKEpimS}9&bX;0`8()$bricnv=bZtU7lG zAmFOXAz*caHD?FzhH^Oc`PoDdJ7_hU^*HskS#Y^PIXl3@b)O0#avL~vzW0t!Y}=-9 zTcvwdTFd)trOCJTxlMU?Sf_^0T}iond#?xpwi;Q6r|VWyr0OF4+VZ8-X}Pd#^fU6_ zu?)=7D(#iDI;}ml<&F79$a!qs5lxbbF>Z4@va!Gs5UJ}Pa)MZ(V*@a`fa5cI5hz5XT89W(0yU*7ekZxn~ z{2r$7gQLWH%(nl#_TX|w=ksUvF-a=aPPH75xPW6sMxXeZ61jHqO|rBC;i~(QZo@n~ za?>`lDJj(07VC`1+IFhQN9ISQ_#!)Q)sO5vmYK$e5eW*3ecRZ1jqS6PHZxsO!c&?G z%WD7mJ1}=i)2gb6sw?^PIV!1$?IWIGs#Dd)t_CIx0#ldH2kqajn`PpMf=WikyehCr zrt2!8!S141xxvmBjn;HzOjWYapk$7_OYq)TW58e4D5a`{b_oA?lBWq_3yZ@`3RU-z zAn8nZxtivJufoWO`@QXgi>>B*0Krn~lwtK;PCZk^6uXr;^=Jl}+oS`m(ke zvhhuolG4am*n_}~w;Ev_<1|WX_=v~TO#}NZs;ZLG(8y@<&(gnSFiZ@LzYny5&<`x! zzk(7LJ`N5xE+IZ4Apt%C0TD4dDG@OlF#!ST15z>yN-8QULK1442b47AlvI@elEE<1 zV?bDVSXg+JL2GJ zD_W2GCprKf9WA_J+_LJt{L3PZg=NRB`+{ZO% z*OPUiPxUhIVVwGi=!iR1NT<}M^69Fj;!+qscWeXZJ1VQPb z>tWpPq5j$Tmc&vp)<@GPL8OW@IA`%*Jjs1dB=|vB?Ljcd?S~gytsO+3t#JHecb!NT zjqe1RZf{wUGBHr7HvuqSfyXRw3HmG3a~G8>kZu2lhj^wx=(>Qg-w1`;QIgW1ZI@B> zdEx8|F2tc)t1E49HSsLSvGo!L>NV%O5E~C=EoMr1`tWML9m>_%(i!s2uw)V+gD;4R zCX8*A>!i18o`rFAn7SBkH?y?4BY|-kxz9VH4(yv2&1-`cEWlmy3-L5dg!fcs^0;R` zO6l$NeDbE4?RuR%UeG2*%Gihb?;ymXou@>^D(bTqw^Lb1S^H8yYH9W6qSC0|Ez&_f zhVg{*=!`jT>`I;S{cubMVkqW)v#NP}CpOEiMu?*&d(m!SLmF^+N#Zcul+71j`MZYD zy;qb^t~72%WI(@UUlS<1tn_C0=@o$$CZeswJrVBnHeMn?Ilpbu_xG&FLym^ZaAK40 zasM5sj5T$ycSs2eM3?1@fK2pwX%pF&Ki#ERF)a^Lv`EUt`vn)CS(V-VSbGNV{{~1d zSXWoCyz@=@0;FP%5$CgG7-!Pb^iL>lF^S;J68S%0D5 zhVnO13gGeeyC?2aw5OZYP(-qIwih_Z19(Iik>8%4L>>l~cplSWHdS~fH%j$|(`ty6 zxW(^_M!XEtlB}T}Pb5k6@pbp_6#rCVqqk|=n&Oy89acvsLR~X(0~eHqAhon|#0I5i z-D&vi_64{gPbS(n|{eZr}HJ2+O+<=lim3 z_t`D4LcMoC-vB6#t7mwBbl#4Ock`q82|R@9Y=Bg5m(E+3nj4Q$uUo4jn-wV+NtYkZ zUbjC6T~uXeil=I((q|a*ow#9u@PzJol9`e<{hgSO=QFKzaC5n?&G_u<{YNxmy;Igt zA5u;3Hhw1{(82wU_|Qx3ISK%jFB?JKGlbPJno2dKNZ}wdCXYEJ~yh!LL zmeSg&;t_n1agk+I`C&#KOKJkP1lyx4CV~U3Yg7u_A9O46K(^AEUImXrZ1DN3xVo$> z_2_W$_BwIw1Xb}nClfiUK+f!pHs#3p5EGPit7~2s>_g*w zkYE#!=)M^CEJJV!6oBUCNqk;>Wr@-DGRgwzLl6 zxt4MN1IQxhB{LY@U_JC4)dasC$y`?H8YY-K5}i&14cSYX6^P^QssP7fU-Rz)i3nb{ z-pbFQd#11D^KQaGp`SGqL>X-ICg!aaWW1Cab95Q6yx88NWU~^OAsm)qX&3s{I!K>J z(laE)hynr5THZY2Z)*#C$?Mv2U7F zS~vyk&K>dx3)Jz~k8pya<_h~v zerAisPGem`#2K*yrwpA7rjW#SULR7UeCMqD+hJejIfCl@h>WJNpVpf<{=u>lrx$TP zYj1j*WU0h;Q7Mry&E^c_8rHQwWog7CcX=BH{9}V!r4Vo$>3pS0q7R$Qrmj2W_15(w zYlF2|s_*D>%RRD|VL@&&BNK`;Ss#|f?Zf1`i1<6y) zXzlvdtlpDiU+@^tthafLAU1VO~_|~f1yD;eYC2p&Wy;Xi_#@QQ6|7C zWIl%fLy1#$WBH{0D^)}r(I>3c8lt>F#gmr-b3H-Ek*bPQWnyY{;VSXEQdNK(qgtsX zr+zT^?C8T)qt@#IpO0?dmBP60AgrO+6s+I$hO2${e##=4a#JiTE&yvsWRC zyU&w+n26{XPt?xFj>Ob}kTI3(|4i)cf<7^v88JoBJvJCA#$9R6P3Ij`>@-fO z0)tc{8*`P{_4n$0PW7y(adCU|Ghd$6cDf9O3u{$(d0B$HP;1{dZQtu;`{nnvI9oE! zP-I%eSGWv(%wDWrF=~{n6};f0)?a+NW;)g8?@(UusbMCksql5?4?yDDkcWK5U;+0J z;50PgkV$@lKiRaV*);~^qIyF#SKqvTIX#knEGp1^<#X60m3)TG!j38It1Zkh)-@yK zLLWQkr8zZ%E0V*|gGxDp#YNcU0&(Ev$vjI(x0uOWXUdQD);B(oI-A@k@?{y-ytWmM zKmjw5&D4sByF}gkse70MS87br@k4JXWJA)bcu)Yi#tc=s;?YZYX^7%fO6u`Y!6(me6-zwj*_HxqZ0KC~wGiheO4R z8OG67&U=G*-MF*7iHH;y{k>O#cc)S8#aEPg{uXq@EErGC-iN8c{q!=3r6I2zv|my) z6BtdQ23y=R2KoK~%Du*AAOgz_`^3+%Di8KB!QtECluH~fbH6lTLvKHh1z?Jdnu#Z~ zMEdIE%Srp|XcQwL5u-&I#d(&}q&o<4J>nGr-jG&?4;2G0n3IOZe=OTke>!NpIHtYY zON~few+*>$mjf>5un>P0j}t>4O|Ha)5kv$Up7_#%qD-B zCMPQ`e_Z*YkA&b$LLZF=>}?`-o-Y0p|9N? z*8)5Evubm|>x?hETK>Ofk#(}Bo)11D)$|XtU~N-0=IKM`32{kINT-=KGM;N{Nzq6S zdQG)l>11h#qF~b1sINx6BpNzrKe-sjkxFYkR5;a}Z0ZMXi~%0j`2pgD_s=*?${>P1 zyL>ai`$_rHDQ$nJ9t8s9&@Yb0rcNZA+KeL6k&D8}S$Co#MV`2z!CO_l#t&tjjT-e` zrNo@xZ%R2BVIcQriH|2PCX>;SJv-7x1q{EA` z?xW5xnB9J72g%c*KLAxz%Wa=A>%!*v_BGbJ?r`cz*jULMR7cm1{bQTC^#zL2O@sA$ zV56`^5Q2V&F3*OFu2NlzMTN-#S!XStfyCofGrc->_}y1zfNYqa1PX_Z>EtPKPlMX3 zn4ihWjbY4vY1+Z$e0r;vH|X=S8!(#6&1>QrC;5URvyC@MSd6M%{;1Pg;yl2c!%5E2 zL|#KE9&=)B?ykF(4Y_%WnfmKjmfS3YvtH#UmtSP|$#S=>Y9;e^RuVsfb_tVmGvHG+)(o(f z-;MBN9@%yJuXg3aYka;_F1Kq*b|$|7_CQug zODTSxQ+pQ*Lq{uDt6o{p>T8`!r@H$d<;UkJY{s4)RK?5*Hp+<0paz=@62@e`*SS-B zr|ZtoVKD`GACZ<|w8BgyS|j|tV>1_C3;=>$$C4FehXcXgii&fGDYfdV2J^qF+-ATN z;OcKuUyDl3=+BCbRs#+!Bj@0_-b$ky{+N08p~@`-2La+reY+g0gJq^#@nQ$w_DNEl zg*@dM=Zul*);R((w2c|OnoyU*cVr6TemY=k#SHMZUKA)HEkB4cc5L>SVRf$&y_a$C zw9qbR>FUxZ&z2gwHm)1`956O5=CeZ2Mk5okD?B#7{+MEwDNUTU}Y;*sBG=E$gN6R-D<Ce8ZA z@q-n7J%wd(70q@B`$^Uja1eWYH>fe3yK%fzXyg6Jxm3(dtFR^eN%yiIjx6FR zb6kq?O+ECdLFEI)6yBBg=vPW;YU`z?t5oma_^csDGt#P%#}# z8NTpx&eGNhuA_NY=g|UhrTyRzi_w-LcAgh&{&5LUCj-SA;kua^xL}_{?@br1@Uu(^ zKK$&!(_b+h#-Z_Lnjq18Y+UQ0HBA33=1ThKalyhfn>6qXPoM`zu>{=VzVY8v(u=aN%zq3SU z#jfXTm9C3<&5p+Qa%ypffl@#DtZD@a-mk8znUYqWt5sDmi`bTQL+jq?aYchy;^-r8 zb}f(RT(-)sa!oviOp%h1@xdJ}BVA^tfL>*zf!J^IlN?NzCctsGpmPG5gXPHHWZ9?_ z<~;f7qnBb)NkNA4Q>XVL-aPM=OfwQ#a=}29vU@| z+(K-JYkF4N6+{mz`9KWSnTMnVI}^i!%fSL8jPFd0l!gM@tt~r;yYZV!wixIae?eYx zQTL~z++>BzU3_CS1IC`3ijH3X0Wd>!C1C9M9MUB{7z*9b&#y67IXH`keKVSkZ2@wT zr_%YzXJ1XU0=1(*RVIFs4N^P`hqr3i*ye*2Z(|teFeChlH@A!h!l2ZcsnH|yS-_tI zv(ha>U0w>xTf3i;O22H4wA&L{)#`j{Z)6b+!Urn(h6zYtuF*bzYJWf8CU;WZUW?q|FAU z5NL?znM|I#wqh2;zUF!vN6_f@PC9KFw;pD9w9C|2_clFqx}03X)|9%{OC7)&Fs_OC zf~VtXJpexYzDd`RCqZKtKkmJd&NTaz#(y)wm|qHCo2RSw{X1v)iS}^tp!4O>q^1V! z#yf$TXC<6K=`2IH`Ac-&PlCj)Jn5NESai0u5bdo#V+CMUjT~aUk{$T}Mrx#gwbf1As6Kwj4d84sk9M09F)RDf#5 zr<+8(~Enl=TVpmC709`s#k>ESjrfmR6=i1bb<@<#s!$vUe=nDbUq_@`fTd>%^2T#JFebp z)vh{FfxHa~Z$WT9zxI5hcPw@Hod2_A>>x^QYO82!lyRQ_a>D3gyJ+!`nr@{ZeU|t6 z)+UawLzIvCHCtAHX>ujx=F?JA zt2O1`nfPM;8TTg{sM1-!v*tN(LldM!$6qrAbawMH-K$hzM&DfkWWu4bTwFn-L`T-7_^N0B?9XH;=? zEZqrJ8*W#Z8epCk>pc|; zH727gu> zGPBfR1h3^XoOSN=j$!{bm_!ov)TGs|vj}190Q9KW_PK?-I%h;4rD@IB%OgIA@eK|_*Jscs_ zKt$(g1p;$GOZ+FJ;P=!)ii-YiygYXQsQA?G^w+W*eU^<*Ew2J8MV=7#of5PzqE&_; z@Ns!HT-_6Xc~%|{E=NkAFUrXkK$ab317lNkgkVvahTF4I$>{7Tmh1Rv$ozPlwr9*S zcB>sVQ@@yP37BHw`K48cjEToMJS_Uhs9+F+?d@w&#^AL{2S>yiSaqhrp%KBgtI3&_u2%Uv*szE4W6GB1Cca0oTKPLlHG6B1xjariE$Ue&C2+1$ zhZUX`qz%%*wR~OxQJ-7A&@4s>+`nb`A~$C`{@$&HmPc2shG<*g$*TMHFf|Pj2b!G( zebTYaPKyYvvFSYtc)@h=(6)!NFg;u$Ak0eQS+LskIdgg|gV8Onu_5X1rfLs>3+|_{ zZ2~oK#CYcH*sxv96lUAN@(kdeI>DY_2^UsTw&5!qRt6ap&Xmam>Id0=H}W;B&Z2}= zGn%@dJ2#6qmp2BY4-0+5nRDgUnP*WG8a3Tfj6WY1+=YJsp{s{b8_b}!n$WXtnq46t z)QS?dwqa_#v+i=-w*P<#%8Kug@ltzNr`%6t^5C;fg|AATmi|V{c5{rXs^>W@V4BZ; zmVTgMD6UmQWY^TVRy-%|QK1&{D@*#o(&n z702#zol^!g4^zvUOd(J4<_*b*gv@b)!A8od1^5Pdgoo1e)e*Hn1D5dm%v^|o;MC7L z4{;PyM&pN-cD9}0w`Qt_l>OGOAM%vQMQmBjGtf2uSO!i7U=)2X*unLUU6cPcT3Zr& zG!;!PD%Sg&Vk^qpDM4u9Y1UU_mds;RdnaCjZ|0A=Oh;Ilqe+doyBmNcb619CK{5?= zf5>-lRVq;Vyd;=M3$K@(0q8vd-z?d1hR0M3%^ zUN*6_@YhW?!C1p&Pfz=ya)J0j_gLnrB}J4@cEP5VT?W%?1Q$a zOarFK$Q@(U&6Z}5;D%IYX8}xOM3ypLWSLrXoc_xh#bA;r##D4FLE)zCULOUqN_oZ*?esZZyEo*4QIe>oT37_&ywMQxjjYF9kXVwUQI-$seg9+>;Cf78G3?P4ZI6 zS}|gL&YVBnf6V{V44x;Xb>kYNn589#5%=%ZtmrG3Q(GGv2ODQ}`rp-B-A!?UYHqv< z*K%;fjC#Bz0sA>8*_>CD(p|jHW>wFQykOW;(O9!|QOvsIFcHug`+VR7JON22{PDXe z!*ynC9hGl*Q(gGF9d>cXw#I1PHC%y!b3p*F`VSx==OjfsrHMaGDuo15;L!oRD!`E& zJ8+C`*XGh)fuMhR1<%);`^A2iXXf}zgA*Pqz3@PiY{cr}%&AM^X(4xTZyICgr}`eX z!;iggQkQ+s4>o3BQheK`4X{CZVH#2LO1sM@$RT}=L^|7SKYzLW^wi{5^hXPU)2|i16(*jVh&J8G^@GOF=Z}7W z5`G4j@n6RKWi#-)vjf;aK3IL_)`fILpv0+=^|A8|pUo|vGB>B;&}dX+GLf7#J@v|H zt?Zcm&Gp_c$oO>B-+2{bGMtei*eE*R-muPsrD9~#2XBRgmuA9jz>M$a^nS7QrEgTe z&cda|++>+|a|g5LFE>rcK&0`cHc|-&s-G7A0puyUR}Tbxfw*^*F|Jabm1A~Z}#H_Lv#oxG2b`KlfFn-t^1<9)mW`|9d3-OIaH!G_3Q=ZdWJ+I4eO3t z$rV(QsPMIb$5V-)1ZaNf8-TZ)UFH*fW52DNBJkOAqgLQi=I@*)_zE)hjacG=`p#Sr zl7cSsoqLobZuIH$=`df4@tT-Y@wnNDD*__MPVKhNoH%Csa5gzU6M-W&jGekf$F`Xu z2aaKj`efHyGQ}D}T-_E>1=^3#1Uk>`Qr#4qpO}_r2V{*%ff6>^wsQFd?P>(7+J2Vf z%k_iff5}aW<|Dl;mn^s+d@K~yjKePddZ5ek5d?*1a)apZ}Qfi0%~(Ta%6Q4!Zmj>9 z2oN$FJGw{ct7X)xhvzefkqwwvU|tb@tj!Kr^xbq_h`u{t`_WUb>!|;(&bCt8>^v-v zCz<+i?)R>t<=LxTEhmXCv;J?X84vCStl`ylp!(La9rg4!H7?i*Dgh!59lXRhdGy>utOgqs#Ey zNm;iT#^$o2dy(*L!LZ7xykv0Kw*9tVSZ}`meN~&IS3Yb3Nb7)j8lTQ zz19Dl^FE!<IKK)ZbkvWZM0q-_^MS%Y?)dtmysjYk~JM7}0hdid?2{q_3( zAAksZb8n?aUy=Sv4PViG?2T+$m>PZ(v2S*e1&aMC;~ky&$_hxsrqOe#m3*qrqB-GN z#BGCl%e^&)X9#!q5FP*>%QdTk_e#W)c1JflET0jvEAh+Q1yaVe+_3>$lSO>Z);;} zB{)#q^70OZ>vpEaG$#!o*2AbA_ZGqFxFGxLorvqEj+vy&C~5s zRZ76%!1-3`t?#m(!Db2$Q?3iHopxLu5AP2rYtOEfYmEpIT7JsgZJVvN_ICc%t?wk> zt9k^aLI|qjz$wd>l1@BLGH6{?8DW;Kd6eOpVp1Dmx%aHn$j&hZ{H&%@25(!p%8J$v z*hGHLJMJ0zjB6DXPb!Nwr6Tm!b)e|jIb{BAYy?>SC_hL)`lkxk05}7WsJh)Ezt+Ke z9mlRGwskWghe1nQVYP^92Kz5b$1 z*{CdGvMkpVjUYQE@+3CNJH4{$iQ^<7UOL}&Y}1iQwrN)h zzmd~trI*fU1XnP4IO;5jR_Cxi=3cE~(8(L>p1PWp`2$LcEc5eji272@o3jwb{I4{Z z52)v(acF5q74J}X3*_uqs@>baIcZrs3Rt`zk4NEfv2dgH#7cz{W3$@l>KWuZ_Cy}u z*UUk#K++?k#qV!M_PHHR!-%*fe_BwsRJO}YtmRHvco3EvL_QzR;FXJ|S=Mkv<$!+ngcL4|<&8H%bzaad6c$c-FUN#@fg8>5% ztMp#Exe)IAVYR(;e(+iIqF!Nw&Q0%dNClB z)>>Mwr07A*l?=*#X>qoMnVL!gd;QUS8(&+9b_Y4J={stMns(n8L4nMm6V5||79Dww zRCE#?d4B-We%hHDkkFNxO}!Tk=!1IYmDvnr!>{HC&Es%=kL% zbc~wt;gFuMX^}*H-{GkwDcQ{U@$*2oxyMtK@K@Py@uq6}GmH>k&Z$84(6anbq_B$z zhzc9=t@Zo}3P!l(7?UtJLaRU-KP>1ei?e#|bqjb?z0{zqd~IYzJ9108B$Zc}^ECWx z^=&BR3j1!b56nQN40FT$yvi1O`CE8{FvsNTKynuBYitj3X;$rA=6(olYY%k2f3Q;; zmeS%DmiuUJ!uKqM`=fOky@$nKw&+HTKGNL`Rn?a4+4-}oIdu6XMK~`N4=52DXXx=c z#6FOd3!SWGx`HaL|%^yX3N5B zReht$K#z^>!^crSxoRiv;sgcWc76h#ihC0==p!i1PE(>Q1yUyMs{u5msA^+3B%KWR ztXroYNdYfGC`@0raxAax;KN*#$>;ZY-!v5A!ZO@Ht!@2pD-+I4;96tsw5nEAI@vOw z8jK5&4o`I~mcqtxbI6=z2E_Ft>nfdDH|Yl6vP@<(CM|h7n{uqZT!-(-@R(z!v>)eh!TXlNQJ?pcnIf&ZdyDO>|~wy7iRd=ZscPTSae8K(G;JP*>G3Pfy~D z5*ThpmnlJFhv#qJHz8h_1=o-63e9G~1u0b5B@7EuG&I~$OW|SQ=vi-;ht@>ac4|A; z@p-n?n@A_}h&IbogtbPvU{ae_#C^%9LH%fgx=8vF|ECkIigTH!m&8xzR;{;WN{GAX zA0sVHUlJ}m%1xoJ945~$UcalO8>xA&HI$Yvta(=P=@9%;@O@TR-<~kC#Sdx%8&ws5 zz*jQV4oZz7QJzt+n8tR{MjW;VihF~o{o2IyrjGCBCAT$O)dTf6*H{O!tEIEvX%+H3 zHvqkeFoE6`j$`+t*hmn$f!Mt-4fiaq2OJrA%3tLp1GDcXFOO3@GaMhiu%`OS*ARMvsth)! zW_M#?dU1qxK1+~S&+qJOwc?FW(K+K`8g{ft>_=t6nGr8?LB)%qvFAJv*VZYlwpo}x z&O>-mcm4r%)*hA5L>32H0FtWPY&~dMO;a^F{Wy4R*FUc3HaxayLrO4Dxk*3o$7(+J zfj1`$pDeA-ywE6!e(qi)-o94RTF|c!mwlpY;o-X#RmatK8ug=dti95w6R;&e8OJ9O z&MzQ^f0&IThBj|yj&;_4akH@sHmqfsA!ZzqJfy!va0?Y=-QetEtlE$a?JG*DtuaU# zdRgk6MS2S>Zu#w;$d(+WLINRnGoup_##h>TB;-}iuM&oE5Cn*^SCU`q=;~asgtxXx z?80h$Uis62x?#A9g9U4J%|6d}GfqwPrPIJU2Uw z2V5Q`-&@+w@J)Op^a>+?1R53f zM*SXSZ(c=jI(+NJb?<$Ibb zbFWYvL8Bivm0;3gCOsDaElgc0}3R6N>8zdI9oxXMl#e=lI7XXTI#&mY| zxo0#leHf<}UW;`SWUdEoq~B-;+RjA2S&fw8kUwsrv<{j?=D=Vy)-~1`A|T|NL2Chb zBkbWCPx6AxUTt=~E0$C+C?AjDwJ2>s6%GIuYQa{QD*^AM~L zoe0`1Yr*YnJriiYu`K1x5@#6KVE_&!^ber4%H&4fCpxS1^`{(7e~qVupv26N7Pyjf zX=Bff8x<>O6tn`d73@U5M6KaMS-N(7?IKg!q;*#W?O1gGuK+7H)X8R(roSd~GnMS8 zv*pm1k^wcBxve3%9|#{;_Ll&2|xA!p9mXSsV|O7icQ}`(_n7BSh9sj7wipb zgYv#<)-T_x^;OfI{LPgM0U5x;`V+4&e}~cU9UunMbYuNcSI(U~V(QhZS8<0I69dXj zlWS*V;j5Ot2AWwEs5Ut8p1r>o%^v73;XrL?vVmxCx>X*nUgofn;sC@B$|r8mO^vCl zMK4xsbeKy6w^niGy@DHUDplzKJ;aR4r0V*Qu&B>b#l(|8B~#mVZCW5SQzkewD!geW zVdRvg_@-QI+Fcqp?sN=)RhrhlrrWxM+5j;UiuEl|P}SMDX2v@c-3`|1SG#kZ^E|*G z)jAh`M>{?&lT;{O)2U3$!5;{JQrY}OvjI6?#^Qs>Yn?v^6dIeG?QkIcr*E^DmJ*MC zg@zXc35WYk92GXsvl`8g11f%>rMFYmXfl}-xELa46RlmMT9!#&7hN0CbuU`my6opH zY4PO{YrQ}G5M!t}VP_-=N1ZpPGL5Y;SOY);2XzyyEQqG+tjG=bHajRXXPmr_^l&V2 zR0iAM8b+BT+uW-PokeYJ>}&l>rXU0ZsB4u4+wI#l*xXNfM_)_&LH$X19QtMX4lj3M z{nfOZzeheZd8fl4qW3}2bsnp_YaTs14aqJ3pLMwOzfH4Cv!zL(mbJS>O10^ZkfEg7 zv5ce!I}HA4vX-1$(d__7Frmpiw7NSV8{??0vZ|edUH`qp_j{1z%dH zr1J}&r^#A-EO_0%BinqghX%c^BLy)^?|kkc5I&!&5(zR@sqvNtR;M*CfW) z_DTK_55fVC>Rl+QNuj3)x^z*KwZFP0^b6wbgxgjSWujxcdX|{SQUuKQ0I9Sr**@nJ z_EQ~1F}qmP1G>k4j8z>KEwo^9n4Uh##_8VDT0OrxR-eH(8g>$SnB_~OYqya;R-LZc z*rzA5nYN~z274~Dke_hma8<2)a14>{QQb@j$l@a@F7}y9kG3&R+-&xfyTM5m3!DS6 zKI%ZGSV0F1l@et7VW@tHmo!}ZfH&~@DYj9EZ7>R$<~wYjQ%zvsCzZJ5e$6#{V-;xN zjzW)Fj$p?f)rDqo?r@GK50nF`X1kp19smnV*KJx_;vkOG*=Jh2CGH>rn8J;ZhS`z* zTc~aNTJgi$OLm{lz<+g;XcXK0Gcl9Mg{b{$t+S@9H+=6b5%ats?fWdxL}XgDleQGz zj@QBQwp@EdA^`dFsP#LVR`lNLkpsH0x~Wi6J3kb6L$_K*Tgrv?9qjM$r)c#UVxMMv ztl2wX)T%#hQU)3bPE+Z2QJ_d*8+Zuzj+Dx6A*}qAEkFtlKG7g&3tad*C~#4Zy8w-t zr1H|y?c!H1?J3m+wSa%Q(UmIhud8GFN=QVw$98A>o}K>y81{^| zVL-gl*;TOC$@xW^trkz=+HXhs*H>y&0@EamORCM+-zj>dP65~gwQQM1mJIam1q%~xVT%L^8 z^eQ&ANgicKxpih3Yp{Eib*gD1ggv51^g~*mCcTmx2Gx<8PWz#flv?HNnV?;|qXjn^ zfjf%7WlV!Ie#k3ItFohjFiFBKv3RP$UCCVvZ5*kzr{r2v8=$}+3Uww(mtZJ1?j^v1 zF@&RJ!pBLRBJ?JcT0ZSVjMt~)>9&!h?UCJcQvHsq&C~4{99kLEl|jo z%>1T_XaE8Fr)sh}Ow?!~M;|DA@eA=*My*~r35sRVjm>!?KS0P~aLlNL6&l z!b!HDv3rZ%9!o=wQxioV7i_`&X>)v7li_W4o= z^Y;;f@}W2nTWRC6B^uEFF&!d+~Z1k!lbf z5}G77b0f-nH@K#2Qi+TseP{_qHjvde?jw)QDC4sAC~ThTy3ZxVOoSE-3KCIB<@DA+uM_P?7g8;$(kT$JD(=Q}>mF!xpN6Oo+8UC`#sPPaFWuEKw zS|0K+34pZCVT^~ok&GxC~*r(Do|qO)>VVWD0Zv8`kr zXW?ldUg|oZPmV_Ftpmp^HRJ8No!c83Aq3dKuI7UFw4e|F>eMzCSP6fV(IS%GOHAEVXBu_jKQ*Zqjy&Yq4H2N*2UBD4M;z~JXO-7SI zc&AaIbKIfcf@43+sD_YXqaEcRvWN1vEFT2AIp2Oo4-^xo#KkxYW=Wk&}PdlV9>EqXN-%1qxWUN*HU z;l(=rLY0NNeMfC*9G>Vq{NU4Uxy_~za$qSm^*UOG0>`*Nw&{?f{GN;A=_tQrcRyUT zZ8GdqEU!~*pq8^3N1ZX(dQVi+^)&m2)NuS%-5ZrjDcXd2*H8hbc%UfjYIx}#=5hhV3v%d%~#|n>1Z>o3y0H_Qi3N%An z*#dm7gjXF1l#`pX?$y9kXdRT=6+@r2;uT$(fMltZ0Nf9SkL-3jZp%ho&H%z)SGCxj zjPjzgqeVP-NI!`8KhYvzW7M2l;yGGyN%pQZwA0`uR0YfiW)dk?!4Q+a(N!sJ9je?w z5%y84)HJ}`$xpnxGNRXHd!;IL-o|lP$sMb4xsYH-Dj7T769Fc(AL^B4(<)Id@F322m>fU_x8T3SZg zNvlvffS3o$Wh+f|B27grXX<9NR566NB?Rk)8brXJAooU>@bK(#5N5U;QM*nHcwVkZ z`Aj6`bOGwj1&$8Pc3J+u8XZuPAYo{FpcMm)u>i?g-o6PH3tT`3e(HH7-68SXNcrdM z%?S zr*7iioj-`vpBw3hntcjD7i|QC$x~`kYlQ6$KMF0Ifu|!LQ`KHv;2Sdv7YEAETDT;i zWj+1A+OEKZ%pr|c>}!lSZ^AOYwx}3hNCgaP8fmQeJ;amb_Cpq|bDt`tX1zssQO|QJ zw29=EMQ@^9UAR80pdQ`UiK*#yy)ylyaPlTqMLTVeBmi2i$27IsHbu!MkUDg_cJkMT z!0rNxX{EB_`b$pMoGZ;|+W~+V@T6%rE@cKi!U@SCZWC=Xem+SuU4@%kLqjl?tMttq z+jokzn(#+qkgcq!wT%vtNmE59RiX_2JLr{r?iOY3K;g8ZxO3&p{-@yYHJE<2eXQuO;6^}7<{-Z?y`tJT~ifV>(( z<`0siT+}Ub5i#5)NvKjoV$*glmBU=%IUU!ZTigqs?J5|iYDd^F27|#&HNnq2kIW;3NpfscbyklD z07yJ0sXAQXGYQI#^%{1Zq%?*%IC6Vd>~xxhbaJqkoWW45RRNGdg(0B0IFrw19^#4H zBw=EeZII3i3gpk#9YMq~#9+b;)?VXZEu3~nm#z+^=@L%}M^@m#$mUnDl{9>%Ede^I z+6+KF)@jsFg6Nnes^s~>{{W%{>C1;U(vv`z?T~yeD6Svux8qI5 zR)d-k6#!##D$3fC4l+x;N*%PWTv|>K$|BR=A`U@4lyhF0>#(ILsK{~FrsiAOR^C}i za0AFKMS_za#=rv+0YSL14kkyp;Y_xUo;bLwk(^%HdPUMDrx2(vYy%z(aNh{?TecgE zLr4wcLL0fH?89UxNB;nKqgmjemFNc7DUahRG_(mhM0Qpo)Ii#JlRPT@PS$Q%_p}ny zF$k*4hRadlfU1q~v~u!SFA}@>Uj&=Y8;YCljc#q zt*b7ZS1@_6micuJ#S>Nml`l1coM z+^y2=FXL{wgzhbUFL03^k|^2JAZ;<#_I1fO7AJ7QHNw**QS(c5szZqXYL+wHUf2wC|A^G_=H!xax4(cKOrbrME zge9p(N^cVcnMBvsLz>_?2vbwRmY;$btL<$0dRj~i*8`Z-Ops?kWteq6Lq*kEepZ*R z67ci*NZ^%|^_^!hKWy&ue5vQAF;zBvul^wGwf9!^^SQ4sGr$Lr?uEK}%zd>ZG6;+* z{{Z4D?lzH7g4bpaOrY+E)Sp_ypDqOCo>nNg3q$@i*D8uMdsi{e4ciBaluf2nY%#48 zB?;I*8XQ_l>_RQlo*|?T+^Di6*nDZ!QFPN7z=Wq#amnF!-220tGCLJDqt?r^fPfCLO6kAI-~sd&+u zUR#=k4Y?l*(@w4*L=Nr2Pil6X2p~t0t0+|ztM1@;3D~FFeG_KE?45r_&jHO6)5@!^ z1vbl-qwZMxe$$Tfs4wnzxPoLJ6REbEB9DWzyHczGIe>ikLYmFc^vvQsD~(RR9z&(T zAq`^lWdoV>T;F|lYUn0K zL6{&9{=(gK`;KEEgjpctW?;&v3q`g710_WigX)X^$`seJ zS$jz>msVPpJvzAb}%JndWTw5Xf0qhAKV2)UheZ# ztu0{$_{<=NHrwo_xXCo4Zzt-FudK$@8Fp}D6qEH5!nV?GDbpmxIK-?j!=|pT>xWc( zo9E>aj+XQ&2N%nSWk>Nf`7m;PX#4EesQp3Hg+?%3usO&5(I|sX>q|?3S}L{Wyup(T z0>A07C^6JQESO~>%mGmpB~bbg{Wn%Z?i4~}P)5{p_F9K2r!6I7`H3H+A`Yndu5 zE`?CjgsQF*HuK!6WSeJ7IpUym)5#r|fNeh6T@H~yszaO#1#MIGJ`zmzMu!z0;IZVC z^PK=fVy@?8{z(lkjBcO#87>*eIbDOax?&P0W=0nsREF&x)}d`u88*EfH3ax6A=xC! z`=mOcaWTs4N$$76DBEAL6%HLLJ9igjx_-C}j|Ctwf*e0Jk^ED$6x6@Su}|Dc<`*7e zz84=?VIjY|Vf9?&m7G=mB6R%N;Q{0S05Fo$13brt7`OS7NnND)nREq;RNECfa?#RE zp5F_V9#RcPlHdtHB{ceubAw0jwcFV{X}u%$0`Rr14ksZB>Q&^DIYd|WK*0o`ge|L7 z8&241p52$Jok;n-ewZJwIx3Lkz|X?Ty5g{HCldkFlC>>bT-W=zc`F(EhkNPU*D_&% znIo`LWP2SH??=Z!;tNm}?v8jQmX6s~zf9>{oi3AwK;q#bPk#RZys7YBo z;Tq#J&pacZp89)dX>U!er?4<0=UAz-?~9G{Sv?NeF|K1=2q1O{r%$3b2m!d|Rkx(; z+7nXM`~LvsJgF91o9$|Bx?`yyk%bd{k?8R`C;ABOy`TW5v}}hQs;gVdRbkD3;qa^- zSU3VWQ^|cDj9!TqDo7ZB6=tHrP)Qi)Da(t{2R{(2uP<6znU3i@PN;fnFK)qIV%Sx4 zrvPw-FPl=3C9nwYjc;puq4?T7A)Qi-O*cjd!h8}-eVfTY5P7$*sPf7D;Wc=7QyyU| zy_xj}+zwL9&O62wr>Mk5`Pbd$nZ&_hQ&fEzm0CCDQ)~}k@xVUYM z$ynD`>khxZA+9f`+yYftvGQjh=lrFE6Ryk8rkz0DJF00uv!S&P*@8w_)p}*iUrmk& z@c=;!;#(`XTfMJt9Md6vKlI9uQiInrkP-#NbZh1lYWWu&yBedI&VR3*!5in#TL-%(MhcZ zxa1g!Qo3J6I!(PgC4{@NIfPG2(Xsr&llvl`P8}Vr93zg~EA-t25H_$+IaXD)FyjCM zJ_3iR(hk$XO>UquHgZ^l*WmOKIWA>Mgtp|c~t?jfsQ$h;?aNNwOVf#FQ1-)`giqE^+k zbEhc1dmW%lM%h1QAXk3{*ii`FWN@8iyOg~o3|+K7Yrl@mzOTFCGS=gix=jR~+@(p< z*G0UXoem0x-S}RR@q?7nc&73RmC{FU6z(tCWG5Hm`x}1HPh}m|1Ds&Wa-+8sBxAyl zAUK%H#;F+@x3(zolIqal2*;JrQY4YW=^@ZsLfeaqwmnYK+M{s=c*$s9bBsXAO`3-@ zz)M!pxXLM>dqaWpn5cG9`CKn8d331tYdP+5b{pXuPhjMdt6j6zYI{Sn2;_EAg=jK= z50&cmgV_x;oTjO?)oe#~sict&FxeraBlSke7dJNw#nY3*Z%=*Pm- zbhc_9PfYzgysZ*kdm$}es1*$R8stf+xE8o>i9$NvNTyCqFHbo|v%tpdV7|Xt3N8Z^ z`>&k7w(GSt{T{O^7^}3&n8M#R`=WKZ@4v^y)+ybDZ)I&IBm3u96&} z9M=)^#3-sNnr))6-0B8v7No){fXd{FZtGdzM!wQN> zv$C-a$r<1&FVbzVSXow!Uf9TRPf&E0&x{o|n;HcEs&_4HzB2yQ3fJg+QBc*Py4m0I zJ0GH|zS8v@J;IB{NbGy90nr7pGvzm0+MlHbvnf(m)s)6h$w8RAl zzO%YWX5rXM(RGa?opw06kUmg$t)NgKhSLl`7(|k0)?2V%;`P-ehCx-;tBug`VCGeY z{f$~JI~*s>Dx0d6$cKRgxc;fpjBh~8JgbZBYON|&0U(e&sx4SjV;pI?N$jjIYP-^g zoStC^bp`!fDlaY@@xpXcZ$#3|!B`3P2Urwnx{DvR?+_tnRmpQ?l1%bZA5+~v?^7>% zC)BCYuj$=LGOPDcehmKrr3YG&S^(|J zlEbqg#3~x*nI;*mElV5PKDyGiYIhdRcc?M}1ZN0~O?Y?)L=NlcPo~{^8(N)(-AN>E zVR8MI@uOc;^y%{iM6NbSN*ZUs_@5^rIS?1fNx0XI^_vBaFh$ih5qQ=Hb*+ZZ+6%6maDQ`XhaKJc7vkf_ltKMGAf zBBX-B0w zOZ6o23Da#R2kMjQ#t4uPFp}!FRT|@BDY)(+@Rrx$WJ2@Vn4Bo}np+!2J`$wT>||$2 zqD@AE(oZRuz9|vkvT4;fGdYZ=jcjAGQw}rXDv{QckWBoQ=N!+49;?7_;@W3FA$hJZ zY%F@%_cW>~=&!lN5DM#S2DG%yjuRoCn~99}2>?CvV1#|(o z$V+7hla`&S(`v^sx-yqaIu~2C$R)+j1agpA`n;)zh{OO2jaIuT?w5fa$>D1F2BEDn zx7r9BO7q^!YGm*Se=54`TWjiyESET#Xkd~`wa~tuYrS6TPGes})$SmggFlm2M;=U# zQUR#rT%=LnYR;`yv@KZbwOxSL=1MQ=kE7Sx%Y{u<&tP+RHk}eD^CfJZ4fHnCK^~*2 zdi9P?M~QQAwOv1_tu3gKDKxwYk`<~D#Fw^5Jg(YZGflpzAntUSLK?Ms+#*3Jk5p-6 zS)YU_tGj4?yh`gy?D5O!2I_juqi;|&W+qDbr>|~Kuk|A44&g=nC%FKV{nyt208({d z^6e(#uyAQ*H#%qFmGO^ChiZD|<=c#JbDsynJSlvolHa227Ub`wX}To=LY*-L@Bk>b z$)q$GgTkQFsDZX)bO?k~W(EL+xx0EhSe4!An`|-x5#AK)V;TID7WAC=2^byKgDs21 zoB_b{r=H4S++DOFTL3p10Hjxb!SbgDdwBe%N3Bj;)awZcj$y=$D(8aN-S7}V((4Z*n>AT92{L4S}}RJ8(J4ts!x@9i*0XqiQ~(f|;s zZz-Lm92DiK+U#80Pe6^+ML97)WuN^)x^2FhUG5NenfO|L^%gO%%yugu`j=7f((3Px zrUQxXl>AX{O31V75|NBnyLEb{^$fW2@$jP0aAYLhW&%qFAmlE#IMY0Ivo|KBx+U9G z3?^I}K2qO#7;F*cLR;QIA9mn=%jv$9zO7Ei zfUI{+4fFXd1E)0Y+LZwy4dXr6(;8NnOlj1M8|?rzQ z##FITyB*3DvD6=7SO*AU!9ErB8|Kvj?ggg+g~yyF?Dd#($2Q_vvC=-tO<)h_^GKjU zBZVliFbT;*+e(a-Ut;OhG~7YqN(~PpM=BEoTT*5TR#YMH2N*mQYosnPwn?RDksv~_ zr$h#T(Jn}3NobG^cM3&4yg`tZo$PzGl8M&Q^u*7*%InzI;HfmrTEb7ddnO-Iw1@?> zJL6K^-dkHx!eq zBak0d=N~EhjttCk;Y1m9!jVdes$fZ!YFDU>)mXNj?FYJ++6`qe=#%>;+FDYlAdSbg zr;D1vONa|iCiwvdr0pRz>0p0Cqu1F%rR2cyQflhntya;tqUzNgcD1b>luM$0B-gr_ z)2nw))>iH@w*|ybaX3c|QTd?~p zSAV&_@6)&DzK7`l0HeBhPSZY}yG>9Cby}_WI1@Q2`qxjkuwzMUWxwh^RcjPtt?iQJ z`7SHK$9gO1CY96o4Y;VoadOd$wM>wFf!S+%euZhK(_=#b4GG)rM&6AxP>=xmge$9c zLqGjw$0iEjHq{*Ix#X9V3a&LuHiU)|2RK5yuT(Lk@dNM>zNN2Mx_mNP-}e(Fs(Q6w zSi)0-li295Pto-Lu~F@(wV-D*fwiZWZgY<#^G4c*QBjavCKi3v6-_GX($N{Q~@VSq3P6t19OsF5g^+Zp$lGWU)B~zD(}UhtG4*b#p`(`1;-~?S z&28F{KJCM@z<#w>ac!j%Vjf)++80Y5TBDisv_Nny(EV=s>i+eXl;-aI>L zJ_IhbdOSA?WXSlDY;@X}4h|#R+vO5k>4BR!NNDUks|!64(K|~ENaU4d-8QkvWV6p@ zwv#Ma+#-`(CG=jL+Pc-Y#z++!4E@*CdR~%WY|z%TY(NAo7wP_szgxB3(g|=l?csf0 zrf7ndvs<(rM3k(pB(0X*K|j)9ij>D7H6$>4cAN5E(DrwKl^8e^ggkpwbZHt zF&q_w{Z7%{@Amsi08e!_#(Mib1}LdVYhedS{XwCuTCwk8qW1|KkL-@^bt-k3!W`Ea z;bxsJ(G?x1(Q{~;P20QdKdQc{+PY(dTH@Wj5S5n=tq#ZXU2&Gf2MNiDJ(Rn8gn$r! zpR8P5RNCP!@(~pquRsG+*avb_lxJ4aTyjUyOG2qF26*tM(x_h@ilwx<4`_lQ_XSLA z&OC)YqS~ZWYJQ6fk93xjKpqojuHnqWj_T&2hb3-dXe~J$sN|df03)L+d|2yh3W6LV z+yYk}ULCZYLT}U_WGMCO?Wrbl;b#l{gVmCp@Y#~9tK0_eV3HSWUCtoEz)_tIY?Q05 zhPc(Snzdh1!Z4et(8AD2aVb?R1~y8L%IzjelgusSi()4w=@V@&4Oas0IfZFY(63~|%_dV511A2NPo=7FOX8!=h z*Si}mZK~e7aN0~K`UF|ipGI}vW~(adj%}?1MAB&lpCg=p>u=He3rf(%A#X*r!+D6w z@`%mVrL!T9Y4eoW#t_{R&*VC&f_*vkuT1INdP1G0J>x#3vfzH`!pfBmJ%Ajz4$7BQ z>b7a(YE)=x{3DbbtbIm?x}7%eaN*OB(QlKMy#dB|$(kk3ze3sWFCncwNeEY3>ncUZ z(m3BZfKxmU8F5c3mcZzh{T>k(Nt|(~k-UC5@IDUdM|Ow4&;@fYc)W7+QN8%R$tdQ zK83wse0zBvin>m_Jkq3(jDN%yt^8+G*;&lCm>}ckKlALTbfqxL6j_LY2rsU0AFF*r ztml@8F~m!F!6^HtCynU7TL}mm5oT^Php@w-e%DTIGJoi+W?mP^Sm8G`B z{{ZAr-arO%JR$qsm&zo&q+BJ$A?-p7i$*}^5T#Pm(AgxP&?l>~xH*BaJFl94veyS& zWf22wcSn56`fF3B;?uOjp6lg*s_!?QZC;CAx$N6awEqC79##JUAIfPYdH(?FQ*y^Z z=T7wb)~|%0<%5-~>AfsyF`|n}JQ6}{>Hh#sHr|6y$$1hSR;#9H8!7H1aPbSO3_;sQB37;~hs0rI9dN^K$u6FfjebU8x2lhIqy+9;!{ zTRp(H-GBo6V@T87`$k}70wrUeHKqzWjVgo^;@}$7fWC>*tQz`lB;W``IXOnyczGvC z8`U(XK7)aR0L~U8*ZMH0sM=Eu^&3*49^vHhzMRxF*1D63Zey~?dZS5k>TN%BODXR% z=)~ebbtD~5k=ui9Q%9}zU*K#hFj5i#1dk|JUelV#hQ!Y@wria+pXIT(NNn!mX8lEO z!%|zE#zJ>1mcU7*@0AW0~vQP9~i z0XcwEIaZJmjZ2Y+KHl?3skyFnG{I@`uXLOD>Ne(~X3`88MBPQ94X!S1_%M;ty)RHw zs%kKXJmJ1lZQ9ieZT|q=X(}aMdWmr^r~vN=gs1%n)ZI`A-P}a; zGe5ek)jz|ot8!fGT8&z6;2oq9_k^GL?XSo+@-8;m_0Lq5Bz9Fc{?`qH~6tVKhfI1 zP(S(3G${Gxt#hS5lxg;1l`a6dc!@4TwtaDFq%hOm8b>WHl0ULVf34PbZ6cLdx=#Q? z+VRw5MD5OPo8FmgW2Vy$3}XP`G(wYGU$nAo-YW`iXv~nlWz>GUY8UX!W(sUPjk7<| zR^4@;t*srED?4fb084oN7Bx2QBR_$+O!^C0`lDrUN#(T#M}zoAP$skWI_mmaMY~(r z&+dS9?H>D4<*s!3?u+!Ugt)HO?+!T5A`z@xPD^a^y$IfKUvZ}8OU2qyq`3c>VC#Wp!^zQfO@82wtgiM%W)( zt7)|Hxxh-x-pJvWQOKnT*k!FauxA7gO2mGuYEHM(E^0V6qic+nv~@0NxQvj#Z~F79 zf0y*_>$d>F#}OwI0as?K7(B{05R>!1J18M&NK){qa7Lxiqcv6Nm=)1DFEd#kK^4X=kcXn0w&XU*}SC;hJ z;0!74ofy(q$rPGhCy<|GeXe%|3?)!$7(7P_Z>Dw;`UHlbLt}1Q>5<4Ku{^F-Yh2ju zpAD9PL=o9Ba5jSjw5i`|bUGe7^i|z^eqG^JT>@Sw!n3)1w@C`N;vVCK#G1BkrP?X) zX$+8e1U+{^Q<4M%i0%$cc^oP`Ah-@bq6$n&_T&C0>Si~aaUEUNrLB>IQh!z{8pi78|A|3Ge_vn6}1}1 zmJt#8tv1h0^r*Bn4lp4L^mT)SLx_iSAmM9zWs8NqFXh47GZ|0h+d1kDC17oBFqe?# z0p?PvX+Zw~qw@h5-Dz~H8QNPu7j1O1c@WPCmeq>lo8sPzofAcC?pR3Rhy`x?1-o5o zMtCg}epYv->D0|ub|NsfjSk#WqfBk=5YASrigCq8(ie4Ox`BqqJFKT&vf0#jY;fK0 zpWSZNY@1rt$F#>QDeDb5Q`K!A>^-Lr9wkn^WR8X{MOk5gY|55EMt1PBKBuDERrZ__ z$Az?9S`%pvIf%-}`s$gbekXuPA1KpTK(B41Li%5%pYo?yG7J3nANNIqmyaWgv0h`x~7}!T#82{bQlt?^s7o#ZfLQr z4F(#lX;?|ASc&H(Cv;0j-D-5(w8rk-p^wrVDrv7p?L7=tW(W>EVM%XIFk$a6Y49Ny zT4_x*n=OyA$xgdYl3O^R93)Cs=tU*4YL8D)Xkpk)c?s9_)he|b=C>mpW)W38I$|-$ zWc@loVWAz1c_nlO)agoO?li-Xk|3(Pu8Xw19{ND`CCyq}twX;PcP46S%p?*ZcEVjV zBa%qMx`*iomwxlQ)G_=AQvtFN7P0;vD7ogfy;GiAf*HB#R)o2Z3{^shPCVFh%^77X90(f6veN@o3 zZldYiohL=vPU7eWzO`o%*0yAU0P_*{B@1*f_-oKKy*``u>VM{%i3ijHE&OCWv1AmH5TmJyb?ChPq?so5t2>#>uNs{L^6T>K`(o7oYeK6Cu zqrU@?0pzVh!GvuM;P4PPOH&l+us0YJ`XlW*vPMsUhJBuc99)$m`p%gVi8z9;?Lpv) z`*te3O#%FlAzj;OCw*Ax zCv?eWqrYVa?XJiXl{Ty302Ou3LMk5VQj&d)inqtvUtONxDg|bc-O>7~_c&;U4aayR zvRv9j99$!EWfjD8x;(9%3|uuAy$ActEk~c?O>Lc zkp*Mg9kZXQ!D!3iwBr)C-m7goHjgE;Tpjs9SpNV?H`l)|aV5m^RhT+*M>FTeNybtA zo0mh`f5WqIjLbsSY^aw~ZZjSb2She)Sn_i|7MrJ24sd6V>FutO&VDOGo2NC-XxZ;R zQ`9uT$L%eTvc0El#?t@{k&>h~*{-Gsp6fZZk_I@_TPrPevr@ANKGvDS(6p;qrqdq* z4cby=I*e!-MtWO8rq9V1x(Ox8(QMmhi(=5&!ppk;z}wViZc7iJm9J1}Z8JIhpdPrk zWlAqDb^R=p*lZ@M>6ra|Gq-AtvYGNo8?kvu{( z)2yjlFg1fGl2*D_Pb{sf6-tO}K#Zh%WVqnMpG|<&2~{Zu(=ppBX&|wyMR08AKPp`+ zC6vs8KLIpEx(brRxr=|G9DP~ZEe>e}E?1&J1QgqDeZ`xS>fp?5}kv zIo}@%#!wCrL@Biz2|pzyF`>L6eHw0+q8YLl!w!E z07W(JskrVf5)?|-Z6KU}i&f}9jNT?5{{{RKR2_yAZ cw{+?H|PvpapqE*y7Yhl*=Tr>O8@`> literal 0 HcmV?d00001 diff --git a/ccan/antithread/examples/md5_server.c b/ccan/antithread/examples/md5_server.c deleted file mode 100644 index 3aa98bbd..00000000 --- a/ccan/antithread/examples/md5_server.c +++ /dev/null @@ -1,129 +0,0 @@ -/* Tries to find data with a given MD5 (up to N bits). */ -#include "ccan/antithread/antithread.h" -#include "ccan/string/string.h" -#include "ccan/talloc/talloc.h" -#include "md5_finder.h" -#include -#include -#include -#include - -static void usage(void) -{ - errx(1, "Usage: md5calc "); -} - -static void parse_hexstring(const char *string, struct md5_search *md5s) -{ - unsigned int i; - - if (strstarts(string, "0x") || strstarts(string, "0X")) - string += 2; - - for (i = 0; i < MD5_HASH_WORDS; i++) { - unsigned int n[4], j; - int ret; - - ret = sscanf(string, "%02x%02x%02x%02x", - &n[0], &n[1], &n[2], &n[3]); - string += 8; - - if (ret == EOF) - break; - for (j = 0; j < ret; j++) { - md5s->mask[MD5_HASH_WORDS-i-1] |= (0xFF << (8*j)); - md5s->md5[MD5_HASH_WORDS-i-1] |= (n[j] << (8*j)); - } - - if (ret != 4) - break; - } -} - -static void init_pattern(u8 *pattern, unsigned int num_bytes, u64 total) -{ - unsigned int i; - - for (i = 0; i < num_bytes; i++) { - pattern[i] = 'A' + (total % 26); - total /= 26; - } -} - -#define PATTERN_BYTES 32 - -int main(int argc, char *argv[]) -{ - struct at_pool *atp; - struct md5_search md5s; - unsigned int i, maxfd, numathreads = argc == 3 ? atoi(argv[2]) : 0; - u64 total = 0; - fd_set fds; - char *cmdline[] = { "./md5_worker", NULL }; - struct athread *at[numathreads]; - - if (numathreads == 0) - usage(); - - memset(&md5s, 0, sizeof(md5s)); - parse_hexstring(argv[1], &md5s); - - md5s.num_tries = 1024*1024; - md5s.num_bytes = PATTERN_BYTES; - - /* *2 to allow for allocation inefficiency. */ - atp = at_pool((sizeof(md5s) + PATTERN_BYTES) * (numathreads + 1) * 2); - if (!atp) - err(1, "Can't create pool"); - - /* Free pool on exit. */ -// talloc_steal(talloc_autofree_context(), atp); - - FD_ZERO(&fds); - maxfd = 0; - for (i = 0; i < numathreads; i++) { - at[i] = at_spawn(atp, NULL, cmdline); - if (!at[i]) - err(1, "Can't spawn child"); - FD_SET(at_fd(at[i]), &fds); - if (at_fd(at[i]) > maxfd) - maxfd = at_fd(at[i]); - } - - for (;;) { - struct md5_search *m, *res; - fd_set in = fds; - - /* Shouldn't fail! */ - m = talloc(at_pool_ctx(atp), struct md5_search); - *m = md5s; - md5s.num_tries++; - m->pattern = talloc_array(m, u8, m->num_bytes); - init_pattern(m->pattern, m->num_bytes, total); - - select(maxfd+1, &in, NULL, NULL, NULL); - for (i = 0; i < numathreads; i++) - if (FD_ISSET(at_fd(at[i]), &in)) - break; - if (i == numathreads) - errx(1, "Select returned, but noone ready?"); - - res = at_read(at[i]); - if (res == NULL) { - warn("Thread died?"); - FD_CLR(at_fd(at[i]), &fds); - continue; - } - if (res != INITIAL_POINTER) { - if (res->success) { - printf("Success! '%.*s'\n", - res->num_bytes, (char *)res->pattern); - exit(0); - } - m->num_tries++; - talloc_free(res); - } - at_tell(at[i], m); - total += m->num_tries; - } -} diff --git a/ccan/antithread/examples/md5_worker.c b/ccan/antithread/examples/md5_worker.c deleted file mode 100644 index 4eb62215..00000000 --- a/ccan/antithread/examples/md5_worker.c +++ /dev/null @@ -1,274 +0,0 @@ -/* Worker thread: tries to find data with given MD5. */ -#include "ccan/antithread/antithread.h" -#include "md5_finder.h" -#include -#include -#include - -/* - * Cryptographic API. - * - * MD5 Message Digest Algorithm (RFC1321). - * - * Derived from cryptoapi implementation, originally based on the - * public domain implementation written by Colin Plumb in 1993. - * - * Copyright (c) Cryptoapi developers. - * Copyright (c) 2002 James Morris - * - * This program 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 2 of the License, or (at your option) - * any later version. - */ -#define MD5_DIGEST_SIZE 16 -#define MD5_HMAC_BLOCK_SIZE 64 -#define MD5_BLOCK_WORDS 16 - -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -#define MD5STEP(f, w, x, y, z, in, s) \ - (w += f(x, y, z) + in, w = (w<>(32-s)) + x) - -struct md5_ctx { - u32 hash[MD5_HASH_WORDS]; - u32 block[MD5_BLOCK_WORDS]; - u64 byte_count; -}; - -static void md5_transform(u32 *hash, u32 const *in) -{ - u32 a, b, c, d; - - a = hash[0]; - b = hash[1]; - c = hash[2]; - d = hash[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - hash[0] += a; - hash[1] += b; - hash[2] += c; - hash[3] += d; -} - -/* XXX: this stuff can be optimized */ -static inline void le32_to_cpu_array(u32 *buf, unsigned int words) -{ - while (words--) { - *buf = ntohl(*buf); - buf++; - } -} - -static inline void cpu_to_le32_array(u32 *buf, unsigned int words) -{ - while (words--) { - *buf = htonl(*buf); - buf++; - } -} - -static inline void md5_transform_helper(struct md5_ctx *ctx) -{ - le32_to_cpu_array(ctx->block, sizeof(ctx->block) / sizeof(u32)); - md5_transform(ctx->hash, ctx->block); -} - -static void md5_init(struct md5_ctx *mctx) -{ - mctx->hash[0] = 0x67452301; - mctx->hash[1] = 0xefcdab89; - mctx->hash[2] = 0x98badcfe; - mctx->hash[3] = 0x10325476; - mctx->byte_count = 0; -} - -static void md5_update(struct md5_ctx *mctx, const u8 *data, unsigned int len) -{ - const u32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f); - - mctx->byte_count += len; - - if (avail > len) { - memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), - data, len); - return; - } - - memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), - data, avail); - - md5_transform_helper(mctx); - data += avail; - len -= avail; - - while (len >= sizeof(mctx->block)) { - memcpy(mctx->block, data, sizeof(mctx->block)); - md5_transform_helper(mctx); - data += sizeof(mctx->block); - len -= sizeof(mctx->block); - } - - memcpy(mctx->block, data, len); -} - -static void md5_final(struct md5_ctx *mctx) -{ - const unsigned int offset = mctx->byte_count & 0x3f; - char *p = (char *)mctx->block + offset; - int padding = 56 - (offset + 1); - - *p++ = 0x80; - if (padding < 0) { - memset(p, 0x00, padding + sizeof (u64)); - md5_transform_helper(mctx); - p = (char *)mctx->block; - padding = 56; - } - - memset(p, 0, padding); - mctx->block[14] = mctx->byte_count << 3; - mctx->block[15] = mctx->byte_count >> 29; - le32_to_cpu_array(mctx->block, (sizeof(mctx->block) - - sizeof(u64)) / sizeof(u32)); - md5_transform(mctx->hash, mctx->block); - cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(u32)); -} - -static bool bits_match(const u32 a[MD5_HASH_WORDS], - const u32 b[MD5_HASH_WORDS], - const u32 mask[MD5_HASH_WORDS]) -{ - unsigned int i; - - for (i = 0; i < MD5_HASH_WORDS; i++) { - if ((a[i] & mask[i]) != (b[i] & mask[i])) - return false; - } - -#if 0 - printf("mask = %08x%08x%08x%08x\n" - "a = %08x%08x%08x%08x\n" - "b = %08x%08x%08x%08x\n", - mask[0], mask[1], mask[2], mask[3], - a[0], a[1], a[2], a[3], - b[0], b[1], b[2], b[3]); -#endif - - return true; -} - -static void inc_pattern(u8 *pattern, unsigned int len) -{ - unsigned int i; - - for (i = 0; i < len; i++) { - pattern[i]++; - if (pattern[i] <= 'Z') - break; - pattern[i] = 'A'; - } -} - -int main(int argc, char *argv[]) -{ - struct at_pool *atp = at_get_pool(&argc, argv, NULL); - struct md5_search *md5s; - - if (!atp) - err(1, "Not a worker thread?"); - - /* Tell parent we're ready. */ - at_tell_parent(atp, INITIAL_POINTER); - while ((md5s = at_read_parent(atp)) != NULL) { - unsigned int i; - md5s->success = false; - - for (i = 0; i < md5s->num_tries; i++) { - struct md5_ctx ctx; - - md5_init(&ctx); - md5_update(&ctx, md5s->pattern, md5s->num_bytes); - md5_final(&ctx); - - if (bits_match(ctx.hash, md5s->md5, md5s->mask)) { - md5s->success = true; - break; - } - inc_pattern(md5s->pattern, md5s->num_bytes); - } - at_tell_parent(atp, md5s); - } - return 0; -} -- 2.39.2