From d552f3732156470500fa6e63ce50af23bbb0fe60 Mon Sep 17 00:00:00 2001 From: Rory Healy Date: Wed, 15 Sep 2021 00:06:04 +1000 Subject: [PATCH] Final submission --- .vscode/settings.json | 5 + Makefile | 2 +- README.md | 0 common.c | 4 +- dcel.c | 882 ++++++++++++++++++++++++------------------ dcel.h | 226 ++++++----- dcel.o | Bin 41232 -> 43984 bytes ed.supp | 156 ++++++++ input.c | 6 +- input.h | 4 + input.o | Bin 8752 -> 8752 bytes main.c | 17 +- main.o | Bin 4960 -> 5000 bytes output.txt | 7 +- towers.c | 93 +++-- towers.h | 6 +- towers.o | Bin 9816 -> 9976 bytes voronoi.c | 88 ++++- voronoi.h | 14 + voronoi.o | Bin 14544 -> 19304 bytes voronoi2 | Bin 61896 -> 64192 bytes 21 files changed, 966 insertions(+), 544 deletions(-) create mode 100644 .vscode/settings.json mode change 100644 => 100755 README.md create mode 100644 ed.supp diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..14004e0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "type_traits": "c" + } +} \ No newline at end of file diff --git a/Makefile b/Makefile index a96ce28..e196e78 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Link command: voronoi2: common.o towers.o dcel.o voronoi.o input.o main.o - gcc -Wall -Wextra -Werror -pedantic -g -o voronoi2 main.o input.o voronoi.o dcel.o towers.o common.o + gcc -Wall -Wextra -Werror -pedantic -g -o voronoi2 main.o input.o voronoi.o dcel.o towers.o common.o -lm # Compilation commands common.o: common.c diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/common.c b/common.c index d0bc6c3..3c7b41b 100644 --- a/common.c +++ b/common.c @@ -2,9 +2,9 @@ * * Created by Rory Healy (healyr@student.unimelb.edu.au) * Created on 25th August 2021 - * Last modified 9th September 2021 + * Last modified 14th September 2021 * - * Contains functions for general use throughout other files. + * Contains functions and headers for general use throughout other files. * */ diff --git a/dcel.c b/dcel.c index bc785a0..75626f7 100644 --- a/dcel.c +++ b/dcel.c @@ -2,10 +2,10 @@ * * Created by Rory Healy (healyr@student.unimelb.edu.au) * Created on 25th August 2021 - * Last modified 13th September 2021 + * Last modified 14th September 2021 * - * Contains functions for the DCEL data structure, including initialisation, - * splitting an edge, and identifying towers in faces. + * Contains functions for the DCEL data structure and related data structures + * (including points, intersections, and bisectors). * * Contains several functions and structures from Grady Fitzpatrick's Base * Code on the Ed Discussion Forum. @@ -16,106 +16,29 @@ #include "dcel.h" #endif -void freePoints(vertex_t **points, int numPoints) { - for (int i = 0; i < numPoints; i++) { - free(points[i]); - } - free(points); +#define INITIALVERTICES 4 +#define INITIALEDGES 4 +#define INITIALFACES 1 +#define NOVERTEX (-1) +#define NOEDGE (-1) + +#define DIR_UNDECIDED (0) +#define INSIDE (1) +#define OUTSIDE (-1) +#define NODIAMETER (-1) + +/* ----------------------- My points functions start ----------------------- */ +vertex_t *newPoint() { + vertex_t *newPoint = malloc(sizeof(*newPoint)); + checkNullPointer(newPoint); + return newPoint; } -void freeBisectors(bisector_t **bisectors, int numBisectors) { - for (int i = 0; i < numBisectors; i++) { - free(bisectors[i]->mid); - free(bisectors[i]); - } - free(bisectors); -} - -void freeIntersections(intersection_t **intersections, int numIntersections) { - for (int i = 0; i < numIntersections; i++) { - free(intersections[i]->fromPoint); - free(intersections[i]->toPoint); - free(intersections[i]); - } - free(intersections); -} - -bisector_t **getBisectors(bisector_t **bisectors, vertex_t **points, \ - int numBisectors) { - for (int i = 0; i < numBisectors; i++) { - bisectors[i] = malloc(sizeof(*bisectors[i])); - bisectors[i]->mid = malloc(sizeof(*bisectors[i]->mid)); - - /* Calculate midpoint of the two points */ - bisectors[i]->mid->x = (points[2 * i]->x + points[2 * i + 1]->x) / 2; - bisectors[i]->mid->y = (points[2 * i]->y + points[2 * i + 1]->y) / 2; - - /* Calculating bisector slope according to slope of AB */ - if (points[2 * i]->x == points[2 * i + 1]->x) { - /* The line segment AB has an infinite gradient, - * so the orthogonal line will have zero slope. - */ - bisectors[i]->slope = 0; - bisectors[i]->isSlopeInfinite = 0; - } else if (points[2 * i]->y == points[2 * i + 1]->y) { - /* The line segment AB has gradient of zero, so - * the orthogonal line will have an infinite slope. - */ - bisectors[i]->isSlopeInfinite = 1; - - /* Not actually zero, just a placeholder to prevent - * accidental errors - */ - bisectors[i]->slope = 0; - } else { - /* The line segment AB has a non-zero and finite gradient, - * so the gradient of the bisector can be calculated. - */ - bisectors[i]->isSlopeInfinite = 0; - bisectors[i]->slope = -1 / \ - ((points[2 * i + 1]->y - points[2 * i]->y) / \ - (points[2 * i + 1]->x - points[2 * i]->x)); - } - } - - return bisectors; -} - -vertex_t *getBisectorPoint(double distance, bisector_t *b, int direction) { - vertex_t *returnPoint = malloc(sizeof(*returnPoint)); - - if (b->isSlopeInfinite) { - /* Vertical line - just add vertical distance */ - returnPoint->x = b->mid->x; - returnPoint->y = b->mid->y + (distance * direction); - } else if (b->slope == 0) { - /* Horizontal line - just add horizontal distance */ - returnPoint->x = b->mid->x + (distance * direction); - returnPoint->y = b->mid->y; - } else { - /* Not horizontal or vertical - add distance to x, then find - * y-intercept of the bisector, and plug it in to y = mx + c to find - * the corresponding y-value. - */ - double c = b->mid->y - b->slope * b->mid->x; - returnPoint->x = b->mid->x + (distance * direction); - returnPoint->y = b->slope * returnPoint->x + c; - } - - return returnPoint; -} - -intersection_t *newIntersection() { - intersection_t *intersection = malloc(sizeof(*intersection)); - checkNullPointer(intersection); - - intersection->fromPoint = malloc(sizeof(*intersection->fromPoint)); - checkNullPointer(intersection->fromPoint); - - intersection->toPoint = malloc(sizeof(*intersection->toPoint)); - checkNullPointer(intersection->toPoint); - - return intersection; +vertex_t *getAPoint(double x, double y) { + vertex_t *point = newPoint(); + point->x = x; + point->y = y; + return point; } vertex_t **readPoints(vertex_t **points, FILE *pointsFile, int *numPoints) { @@ -157,224 +80,302 @@ vertex_t **readPoints(vertex_t **points, FILE *pointsFile, int *numPoints) { return points; } -/* Here on out is the base code from Grady, with some alterations from me */ - -#define INITIALVERTICES 4 -#define INITIALEDGES 4 -#define INITIALFACES 1 -#define NOVERTEX (-1) -#define NOEDGE (-1) - -#define DIR_UNDECIDED (0) -#define INSIDE (1) -#define OUTSIDE (-1) -#define NODIAMETER (-1) - -/* -This intersection is based on code by Joseph O'Rourke and is provided for use in -COMP20003 Assignment 2. - -The approach for intersections is: -- Use the bisector to construct a finite segment and test it against the half-edge. -- Use O'Rourke's segseg intersection - (https://hydra.smith.edu/~jorourke/books/ftp.html) - to check if the values overlap/intersect - -Generates a segment with each end at least minLength away in each direction -from the bisector midpoint. -Returns 1 if b intersects the given half-edge on this segment, 0 otherwise. -Sets the intersection point to the given x, y positions. -*/ - -int areaSign(double sx, double sy, double ex, double ey, double x, double y) { - double areaSq; - /* |AB x AC|^2, squared area */ - /* See https://mathworld.wolfram.com/CrossProduct.html */ - areaSq = (ex - sx) * (y - sy) - - (x - sx) * (ey - sy); - - if (areaSq > 0.0) { - return 1; - } else if (areaSq == 0.0) { - return 0; - } else { - return -1; +void freePoints(vertex_t **points, int numPoints) { + if (!points) { + return; } -} -int between(double sx, double sy, double ex, double ey, double x, double y) { - if (sx != ex) { - /* If not vertical, check whether between x. */ - if ((sx <= x && x <= ex) || (sx >= x && x >= ex)) { - return 1; - } else { - return 0; - } - } else { - /* Vertical, so can't check _between_ x-values. Check y-axis. */ - if ((sy <= y && y <= ey) || (sy >= y && y >= ey)) { - return 1; - } else { - return 0; + for (int i = 0; i < numPoints; i++) { + if (points[i]) { + free(points[i]); } } + free(points); } + +double distPoints(vertex_t *pointA, vertex_t *pointB) { + double a = pointB->x - pointA->x; + double b = pointB->y - pointA->y; + return sqrt(a * a + b * b); +} + +/* ------------------------ My points functions end ------------------------ */ + +/* -------------------- My intersection functions start -------------------- */ + +intersection_t *newIntersection() { + intersection_t *intersection = malloc(sizeof(*intersection)); + checkNullPointer(intersection); + + intersection->fromPoint = malloc(sizeof(*intersection->fromPoint)); + checkNullPointer(intersection->fromPoint); + + intersection->toPoint = malloc(sizeof(*intersection->toPoint)); + checkNullPointer(intersection->toPoint); + + return intersection; +} + +intersection_t *getAnIntersection(intersection_t *intersection, DCEL_t *dcel, \ + bisector_t *bisector, int face, int minLength) { + + /* Intersection coordinates */ + double x, y; + + /* Flag is raised when first intersection is found */ + int isIntersectionFound = 0; + + halfEdge_t *startHE = (dcel->faces)[face].start; + halfEdge_t *currentHE = startHE; + int startVertex = startHE->startVertex; + int first = 1; + + /* Loop through the face until the starting vertex is reached */ + while (first || currentHE->startVertex != startVertex) { + enum intersectType typeOfIntersection = \ + intersects(currentHE, bisector, dcel, minLength, &x, &y); + + switch (typeOfIntersection) { + case INTERSECT: + case SAME_LINE_OVERLAP: + case ENDS_OVERLAP: + if (!isIntersectionFound) { + /* First point of intersection */ + isIntersectionFound = 1; + intersection = newIntersection(); + intersection->fromPoint->x = x; + intersection->fromPoint->y = y; + intersection->fromEdge = currentHE->edge; + } else { + /* Second point of intersection */ + intersection->toPoint->x = x; + intersection->toPoint->y = y; + intersection->toEdge = currentHE->edge; + } + break; + case DOESNT_INTERSECT: + default: + break; + } + currentHE = currentHE->next; + first = 0; + } + + return intersection; +} + +intersection_t **getIntersections(intersection_t **intersections, \ + int *numIntersections, bisector_t **bisectors, int numBisectors, \ + DCEL_t *dcel, int face, double minLength) { -int collinear(double sx, double sy, double ex, double ey, double x, double y) { - /* If area of the parallelogram is 0, then the points - * are in the same line. + int maxSizeIntersections = 1; + + for (int i = 0; i < numBisectors; i++) { + intersection_t *intersection = NULL; + intersection = getAnIntersection(intersection, dcel, bisectors[i], \ + face, minLength); + + /* If there is an intersection, add it to the array */ + if (intersection != NULL) { + /* Ensure there is enough space in the array */ + if (*numIntersections == maxSizeIntersections) { + maxSizeIntersections *= 2; + intersection_t **temp = realloc(intersections, \ + maxSizeIntersections * sizeof(*intersections)); + checkNullPointer(temp); + intersections = temp; + } + + intersections[*numIntersections] = intersection; + *numIntersections += 1; + } + } + + return intersections; +} + +void freeIntersections(intersection_t **intersections, int numIntersections) { + if (!intersections) { + return; + } + + for (int i = 0; i < numIntersections; i++) { + if (intersections[i]->fromPoint) { + free(intersections[i]->fromPoint); + } + if (intersections[i]->toPoint) { + free(intersections[i]->toPoint); + } + if (intersections[i]) { + free(intersections[i]); + } + } + free(intersections); +} + +/* --------------------- My intersection functions end --------------------- */ + +/* ---------------------- My bisector functions start ---------------------- */ + +bisector_t *newBisector() { + bisector_t *newBisector = malloc(sizeof(*newBisector)); + checkNullPointer(newBisector); + newBisector->mid = malloc(sizeof(*newBisector->mid)); + checkNullPointer(newBisector->mid); + return newBisector; +} + +bisector_t *getABisector(bisector_t *bisector, vertex_t *pointA, \ + vertex_t *pointB) { + + /* Calculate midpoint of the two points */ + bisector->mid->x = (pointA->x + pointB->x) / 2; + bisector->mid->y = (pointA->y + pointB->y) / 2; + + /* Calculating bisector slope according to slope of AB */ + if (pointA->x == pointB->x) { + /* The line segment AB has an infinite gradient, + * so the orthogonal line will have zero slope. + */ + bisector->slope = 0; + bisector->isSlopeInfinite = 0; + } else if (pointA->y == pointB->y) { + /* The line segment AB has gradient of zero, so + * the orthogonal line will have an infinite slope. + */ + bisector->isSlopeInfinite = 1; + + /* Not actually zero, just a placeholder to prevent + * accidental errors + */ + bisector->slope = 0; + } else { + /* The line segment AB has a non-zero and finite gradient, + * so the gradient of the bisector can be calculated. + */ + bisector->isSlopeInfinite = 0; + bisector->slope = -1 / ((pointB->y - pointA->y) / \ + (pointB->x - pointA->x)); + } + + return bisector; +} + +bisector_t **getBisectors(bisector_t **bisectors, vertex_t **points, \ + int numBisectors) { + + for (int i = 0; i < numBisectors; i++) { + bisectors[i] = newBisector(); + bisectors[i] = getABisector(bisectors[i], points[2 * i], \ + points[2 * i + 1]); + } + + return bisectors; +} + +vertex_t *getBisectorPoint(double distance, bisector_t *b, int direction) { + vertex_t *returnPoint = malloc(sizeof(*returnPoint)); + + if (b->isSlopeInfinite) { + /* Vertical line - just add vertical distance */ + returnPoint->x = b->mid->x; + returnPoint->y = b->mid->y + (distance * direction); + } else if (b->slope == 0) { + /* Horizontal line - just add horizontal distance */ + returnPoint->x = b->mid->x + (distance * direction); + returnPoint->y = b->mid->y; + } else { + /* Not horizontal or vertical - add distance to x, then find + * y-intercept of the bisector, and plug it in to y = mx + c to find + * the corresponding y-value. + */ + double c = b->mid->y - b->slope * b->mid->x; + returnPoint->x = b->mid->x + (distance * direction); + returnPoint->y = b->slope * returnPoint->x + c; + } + + return returnPoint; +} + +void freeBisectors(bisector_t **bisectors, int numBisectors) { + if (!bisectors) { + return; + } + + for (int i = 0; i < numBisectors; i++) { + if (bisectors[i]->mid) { + free(bisectors[i]->mid); + } + if (bisectors[i]) { + free(bisectors[i]); + } + } + free(bisectors); +} + +/* ----------------------- My bisector functions end ----------------------- */ + +/* ------------------------ My DCEL functions start ------------------------ */ + +double getDiameter(DCEL_t *dcel, int faceIndex) { + /* firstHE and firstVertex refer to the first HE/vertex from the face used + * to control when the loop stops + * + * startHE and startVertex refer to the starting point used to + * advance the new starting vertex after a complete cycle + * + * currentHE and currentVertex refer to the current HE/vertex being used to + * compute the diameter */ - if (areaSign(sx, sy, ex, ey, x, y) == 0) { - return 1; - } else { - return 0; + halfEdge_t *firstHE = (dcel->faces)[faceIndex].start; + halfEdge_t *startHE = firstHE; + halfEdge_t *currentHE = startHE; + int firstVertex = firstHE->startVertex; + int startVertex = firstVertex; + int currentVertex = startVertex; + int first = 1; + double currentDiameter = NODIAMETER, maxDiameter = NODIAMETER; + + while (first || !(currentHE->startVertex == firstVertex && \ + startHE->startVertex == firstVertex)) { + first = 0; + + /* Compute currentDiameter, compare to maxDiameter */ + currentDiameter = distPoints(&(dcel->vertices)[startVertex], \ + &(dcel->vertices)[currentVertex]); + if (currentDiameter >= maxDiameter) { + maxDiameter = currentDiameter; + } + + /* Advance current HE, update currentVertex */ + currentHE = currentHE->next; + currentVertex = currentHE->startVertex; + + /* When the starting vertex is reached, move the startHE to the next HE + * and update the vertices. + */ + if (currentVertex == startVertex) { + startHE = startHE->next; + currentHE = startHE; + startVertex = startHE->startVertex; + currentVertex = startVertex; + } } + + if (currentDiameter == NODIAMETER) { + return NODIAMETER; + } + + return maxDiameter; } -enum intersectType parallelIntersects(double heSx, double heSy, \ - double heEx, double heEy, double bSx, double bSy, \ - double bEx, double bEy, double *x, double *y) { - if (!collinear(heSx, heSy, heEx, heEy, bSx, bSy)) { - /* Parallel, no intersection so don't set (x, y) */ - return DOESNT_INTERSECT; - } - /* bS between heS and heE */ - if (between(heSx, heSy, heEx, heEy, bSx, bSy)) { - *x = bSx; - *y = bSy; - return SAME_LINE_OVERLAP; - } - /* bE between heS and heE */ - if (between(heSx, heSy, heEx, heEy, bEx, bEy)) { - *x = bEx; - *y = bEy; - return SAME_LINE_OVERLAP; - } - /* heS between bS and bE */ - if (between(bSx, bSy, bEx, bEy, heSx, heSy)) { - *x = heSx; - *y = heSy; - return SAME_LINE_OVERLAP; - } - /* heE between bS and bE */ - if (between(bSx, bSy, bEx, bEy, heEx, heEy)) { - *x = heEx; - *y = heEy; - return SAME_LINE_OVERLAP; - } - - return DOESNT_INTERSECT; -} +/* ------------------------- My DCEL functions end ------------------------- */ -enum intersectType intersects(halfEdge_t *he, bisector_t *b, \ - DCEL_t *dcel, double minLength, double *x, double *y) { - /* Half-edge x, y twin */ - double heSx = dcel->vertices[he->startVertex].x; - double heSy = dcel->vertices[he->startVertex].y; - double heEx = dcel->vertices[he->endVertex].x; - double heEy = dcel->vertices[he->endVertex].y; - - /* Bisector x, y twin */ - vertex_t *startPoint = getBisectorPoint(minLength, b, -1); - vertex_t *endPoint = getBisectorPoint(minLength, b, 1); - double bSx = startPoint->x; - double bSy = startPoint->y; - double bEx = endPoint->x; - double bEy = endPoint->y; - - free(startPoint); - free(endPoint); - - /* Fill in segment. */ - - /* Parametric equation parameters */ - double t1, t2; - - /* Numerators for X and Y coordinate of intersection. */ - double numeratorX, numeratorY; - - /* Denominators of intersection coordinates. */ - double denominator; - - /* - See http://www.cs.jhu.edu/~misha/Spring20/15.pdf - for explanation and intuition of the algorithm here. - x_1 = heSx, y_1 = heSy | p_1 = heS - x_2 = heEx, y_2 = heEy | q_1 = heE - x_3 = bSx , y_3 = bSy | p_2 = bS - x_4 = bEx , y_4 = bEy | q_2 = bE - ---------------------------------------- - So the parameters t1 and t2 are given by: - | t1 | | heEx - heSx bSx - bEx | -1 | bSx - heSx | - | | = | | | | - | t2 | | heEy - heSy bSy - bEy | | bSy - heSy | - - Hence: - | t1 | 1 | bSy - bEy bEx - bSx | | bSx - heSx | - | | = --------- | | | | - | t2 | ad - bc | heSy - heEy heEx - heSx | | bSy - heSy | - - where - a = heEx - heSx - b = bSx - bEx - c = heEy - heSy - d = bSy - bEy - */ - - /* Here we calculate ad - bc */ - denominator = heSx * (bEy - bSy) + - heEx * (bSy - bEy) + - bEx * (heEy - heSy) + - bSx * (heSy - heEy); - - if (denominator == 0) { - /* In this case the two are parallel */ - return parallelIntersects(heSx, heSy, heEx, heEy, \ - bSx, bSy, bEx, bEy, x, y); - } - - /* - Here we calculate the top row. - | bSy - bEy bEx - bSx | | bSx - heSx | - | | | | - | | | bSy - heSy | - */ - numeratorX = heSx * (bEy - bSy) + - bSx * (heSy - bEy) + - bEx * (bSy - heSy); - - /* - Here we calculate the bottom row. - | | | bSx - heSx | - | | | | - | heSy - heEy heEx - heSx | | bSy - heSy | - */ - numeratorY = -(heSx * (bSy - heEy) + - heEx * (heSy - bSy) + - bSx * (heEy - heSy)); - - /* Use parameters to convert to the intersection point */ - t1 = numeratorX/denominator; - t2 = numeratorY/denominator; - *x = heSx + t1 * (heEx - heSx); - *y = heSy + t1 * (heEy - heSy); - - /* Make final decision - if point is on segments, parameter values will be - between 0, the start of the line segment, and 1, the end of the line segment. - */ - if (0.0 < t1 && t1 < 1.0 && 0.0 < t2 && t2 < 1.0) { - return INTERSECT; - } else if (t1 < 0.0 || 1.0 < t1 || t2 < 0.0 || 1.0 < t2) { - /* s or t outside of line segment. */ - return DOESNT_INTERSECT; - } else { - /* - ((numeratorX == 0) || (numeratorY == 0) || - (numeratorX == denominator) || (numeratorY == denominator)) - */ - return ENDS_OVERLAP; - } -} +/* ------------------------------------------------------------------------- */ +/* */ +/* Here onwards are the functions from Grady's base code on Ed */ +/* */ +/* ------------------------------------------------------------------------- */ DCEL_t *newDCEL() { /* Setup DCEL. */ @@ -1011,83 +1012,208 @@ int inFace(DCEL_t *dcel, double x, double y, int faceIndex) { return 1; } -intersection_t **getIntersections(intersection_t **intersections, \ - int *numIntersections, bisector_t **bisectors, int numBisectors, \ - DCEL_t *dcel, int face, double minLength) { - int maxSizeIntersections = 1; +/* This intersection is based on code by Joseph O'Rourke and is provided for + * use in COMP20003 Assignment 2. + * + * The approach for intersections is: + * - Use the bisector to construct a finite segment and test it against + * the half-edge. + * - Use O'Rourke's segseg intersection + * (https://hydra.smith.edu/~jorourke/books/ftp.html) to check if the values + * overlap/intersect + * + * Generates a segment with each end at least minLength away in each direction + * from the bisector midpoint. + * + * Returns 1 if b intersects the given half-edge on this segment, 0 otherwise. + * Sets the intersection point to the given x, y positions. + */ - for (int i = 0; i < numBisectors; i++) { - intersection_t *currentIntersection = NULL; +int areaSign(double sx, double sy, double ex, double ey, double x, double y) { + double areaSq; + /* |AB x AC|^2, squared area */ + /* See https://mathworld.wolfram.com/CrossProduct.html */ + areaSq = (ex - sx) * (y - sy) - + (x - sx) * (ey - sy); + + if (areaSq > 0.0) { + return 1; + } else if (areaSq == 0.0) { + return 0; + } else { + return -1; + } +} - /* Intersection coordinates */ - double x, y; - - /* Flag is raised when first intersection is found */ - int isIntersectionFound = 0; - - halfEdge_t *startHE = (dcel->faces)[face].start; - halfEdge_t *currentHE = startHE; - int startVertex = startHE->startVertex; - int first = 1; - - /* Loop through the face until the starting vertex is reached */ - while (first || currentHE->startVertex != startVertex) { - enum intersectType typeOfIntersection = \ - intersects(currentHE, bisectors[i], dcel, minLength, &x, &y); - - switch (typeOfIntersection) { - case INTERSECT: - case SAME_LINE_OVERLAP: - case ENDS_OVERLAP: - if (!isIntersectionFound) { - /* First point of intersection */ - isIntersectionFound = 1; - currentIntersection = newIntersection(); - currentIntersection->fromPoint->x = x; - currentIntersection->fromPoint->y = y; - currentIntersection->fromEdge = currentHE->edge; - } else { - /* Second point of intersection */ - currentIntersection->toPoint->x = x; - currentIntersection->toPoint->y = y; - currentIntersection->toEdge = currentHE->edge; - } - break; - case DOESNT_INTERSECT: - default: - break; - } - currentHE = currentHE->next; - first = 0; +int between(double sx, double sy, double ex, double ey, double x, double y) { + if (sx != ex) { + /* If not vertical, check whether between x. */ + if ((sx <= x && x <= ex) || (sx >= x && x >= ex)) { + return 1; + } else { + return 0; } - - /* If there is an intersection, add it to the array */ - if (currentIntersection != NULL) { - /* Ensure there is enough space in the array */ - if (*numIntersections == maxSizeIntersections) { - maxSizeIntersections *= 2; - intersection_t **temp = realloc(intersections, maxSizeIntersections * sizeof(*intersections)); - checkNullPointer(temp); - intersections = temp; - } - - intersections[*numIntersections] = currentIntersection; - *numIntersections += 1; + } else { + /* Vertical, so can't check _between_ x-values. Check y-axis. */ + if ((sy <= y && y <= ey) || (sy >= y && y >= ey)) { + return 1; + } else { + return 0; } } - - return intersections; +} + +int collinear(double sx, double sy, double ex, double ey, double x, double y) { + /* If area of the parallelogram is 0, then the points + * are in the same line. + */ + if (areaSign(sx, sy, ex, ey, x, y) == 0) { + return 1; + } else { + return 0; + } } -double getDiameter(DCEL_t *dcel, int faceIndex) { - // FILL IN - printf("in getDiamter(): faceIndex = %d, dcel faces = %d\n", \ - faceIndex, dcel->facesAllocated); - return NODIAMETER; +enum intersectType parallelIntersects(double heSx, double heSy, \ + double heEx, double heEy, double bSx, double bSy, \ + double bEx, double bEy, double *x, double *y) { + + if (!collinear(heSx, heSy, heEx, heEy, bSx, bSy)) { + /* Parallel, no intersection so don't set (x, y) */ + return DOESNT_INTERSECT; + } + /* bS between heS and heE */ + if (between(heSx, heSy, heEx, heEy, bSx, bSy)) { + *x = bSx; + *y = bSy; + return SAME_LINE_OVERLAP; + } + /* bE between heS and heE */ + if (between(heSx, heSy, heEx, heEy, bEx, bEy)) { + *x = bEx; + *y = bEy; + return SAME_LINE_OVERLAP; + } + /* heS between bS and bE */ + if (between(bSx, bSy, bEx, bEy, heSx, heSy)) { + *x = heSx; + *y = heSy; + return SAME_LINE_OVERLAP; + } + /* heE between bS and bE */ + if (between(bSx, bSy, bEx, bEy, heEx, heEy)) { + *x = heEx; + *y = heEy; + return SAME_LINE_OVERLAP; + } + + return DOESNT_INTERSECT; } -void incrementalVoronoi(DCEL_t *dcel, tower_t *tower) { - // FILL IN - printf("In incrementalVoronoi(): tower id = %s, dcel faces = %d\n", \ - tower->id, dcel->facesAllocated); +enum intersectType intersects(halfEdge_t *he, bisector_t *b, \ + DCEL_t *dcel, double minLength, double *x, double *y) { + + /* Half-edge x, y twin */ + double heSx = dcel->vertices[he->startVertex].x; + double heSy = dcel->vertices[he->startVertex].y; + double heEx = dcel->vertices[he->endVertex].x; + double heEy = dcel->vertices[he->endVertex].y; + + /* Bisector x, y twin */ + vertex_t *startPoint = getBisectorPoint(minLength, b, -1); + vertex_t *endPoint = getBisectorPoint(minLength, b, 1); + double bSx = startPoint->x; + double bSy = startPoint->y; + double bEx = endPoint->x; + double bEy = endPoint->y; + free(startPoint); + free(endPoint); + + /* Parametric equation parameters */ + double t1, t2; + + /* Numerators for X and Y coordinate of intersection. */ + double numeratorX, numeratorY; + + /* Denominators of intersection coordinates. */ + double denominator; + + /* + See http://www.cs.jhu.edu/~misha/Spring20/15.pdf + for explanation and intuition of the algorithm here. + x_1 = heSx, y_1 = heSy | p_1 = heS + x_2 = heEx, y_2 = heEy | q_1 = heE + x_3 = bSx , y_3 = bSy | p_2 = bS + x_4 = bEx , y_4 = bEy | q_2 = bE + ---------------------------------------- + So the parameters t1 and t2 are given by: + | t1 | | heEx - heSx bSx - bEx | -1 | bSx - heSx | + | | = | | | | + | t2 | | heEy - heSy bSy - bEy | | bSy - heSy | + + Hence: + | t1 | 1 | bSy - bEy bEx - bSx | | bSx - heSx | + | | = --------- | | | | + | t2 | ad - bc | heSy - heEy heEx - heSx | | bSy - heSy | + + where + a = heEx - heSx + b = bSx - bEx + c = heEy - heSy + d = bSy - bEy + */ + + /* Here we calculate ad - bc */ + denominator = heSx * (bEy - bSy) + + heEx * (bSy - bEy) + + bEx * (heEy - heSy) + + bSx * (heSy - heEy); + + if (denominator == 0) { + /* In this case the two are parallel */ + return parallelIntersects(heSx, heSy, heEx, heEy, \ + bSx, bSy, bEx, bEy, x, y); + } + + /* + Here we calculate the top row. + | bSy - bEy bEx - bSx | | bSx - heSx | + | | | | + | | | bSy - heSy | + */ + numeratorX = heSx * (bEy - bSy) + + bSx * (heSy - bEy) + + bEx * (bSy - heSy); + + /* + Here we calculate the bottom row. + | | | bSx - heSx | + | | | | + | heSy - heEy heEx - heSx | | bSy - heSy | + */ + numeratorY = -(heSx * (bSy - heEy) + + heEx * (heSy - bSy) + + bSx * (heEy - heSy)); + + /* Use parameters to convert to the intersection point */ + t1 = numeratorX/denominator; + t2 = numeratorY/denominator; + *x = heSx + t1 * (heEx - heSx); + *y = heSy + t1 * (heEy - heSy); + + /* Make final decision - if point is on segments, parameter values will be + between 0, the start of the line segment, and 1, the end of the line segment. + */ + if (0.0 < t1 && t1 < 1.0 && 0.0 < t2 && t2 < 1.0) { + return INTERSECT; + } else if (t1 < 0.0 || 1.0 < t1 || t2 < 0.0 || 1.0 < t2) { + /* s or t outside of line segment. */ + return DOESNT_INTERSECT; + } else { + /* + ((numeratorX == 0) || (numeratorY == 0) || + (numeratorX == denominator) || (numeratorY == denominator)) + */ + return ENDS_OVERLAP; + } } diff --git a/dcel.h b/dcel.h index 25672ae..36bbc0e 100644 --- a/dcel.h +++ b/dcel.h @@ -2,10 +2,23 @@ #include "towers.h" #endif +#ifndef MATH_HEADER +#include +#endif + #ifndef DCEL_HEADER #define DCEL_HEADER -/* Here is the Base Code from Grady, with some alterations by me */ +/* -------- Definitions, enums, and structs from Grady's base code --------- */ + +/* Representation of no face */ +#define NOFACE (-1) + +/* Default face for intersections. */ +#define DEFAULT_FACE 0 + +/* Default minimum length for bisector in each direction */ +#define DEFAULTMINLENGTH (200) enum intersectType { DOESNT_INTERSECT = 0, // Doesn't intersect @@ -19,12 +32,6 @@ typedef struct vertex { double y; } vertex_t; -typedef struct bisector { - vertex_t *mid; - int isSlopeInfinite; - double slope; -} bisector_t; - typedef struct halfEdge { struct halfEdge *previous; struct halfEdge *next; @@ -53,13 +60,6 @@ typedef struct split { vertex_t endPoint; } split_t; -typedef struct intersection { - int fromEdge; - int toEdge; - vertex_t *fromPoint; - vertex_t *toPoint; -} intersection_t; - typedef struct DCEL { edge_t *edges; int edgesUsed; @@ -74,35 +74,114 @@ typedef struct DCEL { int verticesAllocated; } DCEL_t; +/* ------------------------------ My structs ------------------------------- */ + +typedef struct bisector { + vertex_t *mid; + int isSlopeInfinite; + double slope; +} bisector_t; + +typedef struct intersection { + int fromEdge; + int toEdge; + vertex_t *fromPoint; + vertex_t *toPoint; +} intersection_t; + +/* ----------------------- My points functions start ----------------------- */ + +/* Creates a new point */ +vertex_t *newPoint(); + +/* Gets a single point from an x-y pair */ +vertex_t *getAPoint(double x, double y); + +/* Reads a points file and stores the information in the points array */ +vertex_t **readPoints(vertex_t **points, FILE *pointsFile, int *numPoints); + +/* Frees an array of points */ +void freePoints(vertex_t **points, int numPoints); + +/* Returns the distance between two points */ +double distPoints(vertex_t *pointA, vertex_t *pointB); + +/* ------------------------ My points functions end ------------------------ */ + +/* -------------------- My intersection functions start -------------------- */ + +/* Create a new intersection */ +intersection_t *newIntersection(); + +/* Fills in a single intersection structure */ +intersection_t *getAnIntersection(intersection_t *intersection, DCEL_t *dcel, \ + bisector_t *bisector, int face, int minLength); + +/* Gets the intersection between the given bisector and the given DCEL + * for the given face. + */ +intersection_t **getIntersections(intersection_t **intersections, \ + int *numIntersections, bisector_t **bisectors, int numBisectors, \ + DCEL_t *dcel, int face, double minLength); + +/* Frees an array of intersections */ +void freeIntersections(intersection_t **intersections, int numIntersections); + +/* --------------------- My intersection functions end --------------------- */ + +/* ---------------------- My bisector functions start ---------------------- */ + +/* Creates a new bisector */ +bisector_t *newBisector(); + +/* Calculates and returns the equation of a bisector of two points */ +bisector_t *getABisector(bisector_t *bisector, vertex_t *pointA, \ + vertex_t *pointB); + +/* Returns a list of bisectors built from a list of points */ +bisector_t **getBisectors(bisector_t **bisectors, vertex_t **points, \ + int numPoints); +/* Returns a point at least distance away from the midpoint of the + * bisector given. If direction is 0, then the point is +distance away in the + * x-direction, otherwise -distance away in the x-direction. + */ +vertex_t *getBisectorPoint(double distance, bisector_t *b, int direction); + +/* Frees an array of bisectors */ +void freeBisectors(bisector_t **bisectors, int numBisectors); + +/* ----------------------- My bisector functions end ----------------------- */ + +/* ------------------------ My DCEL functions start ------------------------ */ + +/* Gets the diameter of the given face. */ +double getDiameter(DCEL_t *dcel, int faceIndex); + +/* ------------------------- My DCEL functions end ------------------------- */ + +/* ---------------------------------------------------------------------------- + * Here on out are the functions from Grady's base code on Ed + * ---------------------------------------------------------------------------- + */ + /* Allocate a new DCEL and return it. */ DCEL_t *newDCEL(); /* Allocate a new halfEdge and return it. */ halfEdge_t *newHalfEdge(); -/* Returns INSIDE if the points is on the INSIDE of the vector twin by the CW - * winding order, OUTSIDE if it is OUTSIDE by the CW winding order, and - * DIR_UNDECIDED if the point lies on the vector between the points v1 and v2. - */ -int getRelativeDir(double x, double y, vertex_t *v1, vertex_t *v2); - -/* Takes an established direction and a new direction, and returns 1 if the - * direction matches the decidedDirection or if the direction is undecided. - */ -int directionOrUndecided(int decidedDirection, int direction); - -/* Check there's space for another vertex in the DCEL, - * or increase the allocated space. +/* Check there's space for another vertex in the DCEL, or increase the + * allocated space. */ void ensureSpaceForVertex(DCEL_t *dcel); -/* Check there's space for another edge in the DCEL, - * or increase the allocated space. +/* Check there's space for another edge in the DCEL, or increase the + * allocated space. */ void ensureSpaceForEdge(DCEL_t *dcel); -/* Check there's space for another face in the DCEL, - * or increase the allocated space. +/* Check there's space for another face in the DCEL, or increase the + * allocated space. */ void ensureSpaceForFace(DCEL_t *dcel); @@ -121,24 +200,12 @@ DCEL_t *readPolygonFile(char *polygonfileName); /* Reads the next split from the given file. */ split_t *readNextSplit(FILE *splitfile); -/* Frees a given split. */ +/* Frees a given split. */ void freeSplit(split_t *split); /* Returns 1 if vertices are sufficiently close, 0 otherwise. */ int vertexMatch(vertex_t *v1, vertex_t *v2); -/* Gets the string for the given bisector equation. */ -char *getBisectorEquation(bisector_t *b); - -/* Representation of no face */ -#define NOFACE (-1) - -/* Default face for intersections. */ -#define DEFAULT_FACE 0 - -/* Default minimum length for bisector in each direction */ -#define DEFAULTMINLENGTH (200) - /* Applies a given split to the DCEL. */ void applySplit(split_t *split, DCEL_t *dcel); @@ -148,40 +215,37 @@ void freeDCEL(DCEL_t *dcel); /* Gets the number of faces in the DCEL. */ int getFaceCount(DCEL_t *dcel); +/* Returns INSIDE if the points is on the INSIDE of the vector twin by the CW + * winding order, OUTSIDE if it is OUTSIDE by the CW winding order, and + * DIR_UNDECIDED if the point lies on the vector between the points v1 and v2. + */ +int getRelativeDir(double x, double y, vertex_t *v1, vertex_t *v2); + +/* Takes an established direction and a new direction, and returns 1 if the + * direction matches the decidedDirection or if the direction is undecided. + */ +int directionOrUndecided(int decidedDirection, int direction); + /* Returns 1 if the given x,y point is inside the given face. */ int inFace(DCEL_t *dcel, double x, double y, int faceIndex); -/* Gets the intersection between the given bisector and the given DCEL - * for the given face. +/* ------------------- O'Rourke's intersection functions ------------------- */ + +/* Returns -1, 0 or 1, based on the area enclosed by the three points. + * 0 corresponds to no area enclosed */ -intersection_t **getIntersections(intersection_t **intersections, \ - int *numIntersections, bisector_t **bisectors, int numBisectors, \ - DCEL_t *dcel, int face, double minLength); - -/* Gets the diameter of the given face. */ -double getDiameter(DCEL_t *dcel, int faceIndex); - -/* Adds the watchtower to the Voronoi diagram represented by the given DCEL, - * applying required splits and setting the watchtower as required. - */ -void incrementalVoronoi(DCEL_t *dcel, tower_t *watchTower); - -/* O'Rourke's intersection functions */ -/* Returns -1, 0 or 1, based on the area enclosed by the three points. 0 corresponds - to no area enclosed. -*/ int areaSign(double sx, double sy, double ex, double ey, double x, double y); /* Returns 1 if point (x, y) is between (sx, sy) and (ex, ey) */ int between(double sx, double sy, double ex, double ey, double x, double y); /* Returns 1 if the point (x, y) is in the line from s(x, y) to e(x, y), - * 0 otherwise. + * 0 otherwise */ int collinear(double sx, double sy, double ex, double ey, double x, double y); /* Tests if the half edge and bisector are parallel and overlapping, or not - * intersecting. + * intersecting */ enum intersectType parallelIntersects(double heSx, double heSy, \ double heEx, double heEy, double bSx, double bSy, \ @@ -191,38 +255,4 @@ enum intersectType parallelIntersects(double heSx, double heSy, \ enum intersectType intersects(halfEdge_t *he, bisector_t *b, \ DCEL_t *dcel, double minLength, double *x, double *y); -/* My own functions */ - -/* Reads the polygon file and stores the information in the vertices array */ -vertex_t **readPolygon(vertex_t **vertices, FILE *polygonFile, \ - int *numVertices); - -/* Frees an array of vertices */ -void freeVertices(vertex_t **vertices, int numVertices); - -/* Calculates and returns the equation of a bisector of two points */ -bisector_t **getBisectors(bisector_t **bisectors, vertex_t **points, \ - int numPoints); - -/* Returns a point at least distance away from the midpoint of the - * bisector given. If direction is 0, then the point is +distance away in the - * x-direction, otherwise -distance away in the x-direction. - */ -vertex_t *getBisectorPoint(double distance, bisector_t *b, int direction); - -/* Create a new intersection */ -intersection_t *newIntersection(); - -/* Frees an array of bisectors */ -void freeBisectors(bisector_t **bisectors, int numBisectors); - -/* Frees an array of points */ -void freePoints(vertex_t **points, int numPoints); - -/* Frees an array of intersections */ -void freeIntersections(intersection_t **intersections, int numIntersections); - -/* Reads a points file and stores the information in the points array */ -vertex_t **readPoints(vertex_t **points, FILE *pointsFile, int *numPoints); - #endif diff --git a/dcel.o b/dcel.o index 78c1473ec0047feb09396d7756740da098999e80..a271dd555e16564cec2ed1c82d264b8a81b4b2d8 100644 GIT binary patch literal 43984 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 W=Y2@0uF7|O-f+F-Wk%XA|9=5Yll7?p diff --git a/ed.supp b/ed.supp new file mode 100644 index 0000000..ff5a81e --- /dev/null +++ b/ed.supp @@ -0,0 +1,156 @@ +{ + + Memcheck:Cond + fun:runtime.adjustframe + fun:runtime.gentraceback + fun:runtime.copystack + fun:runtime.newstack + fun:runtime.morestack + fun:runtime.rt0_go +} +{ + + Memcheck:Cond + fun:runtime.adjustframe + fun:runtime.gentraceback + fun:runtime.copystack + fun:runtime.newstack + fun:runtime.morestack + fun:runtime.rt0_go +} +{ + + Memcheck:Cond + fun:runtime.adjustpointer + fun:runtime.adjustframe + fun:runtime.gentraceback + fun:runtime.copystack + fun:runtime.newstack + fun:runtime.morestack + fun:runtime.rt0_go +} +{ + + Memcheck:Cond + fun:runtime.adjustpointer + fun:runtime.adjustframe + fun:runtime.gentraceback + fun:runtime.copystack + fun:runtime.newstack + fun:runtime.morestack + fun:runtime.rt0_go +} +{ + + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_cgo_try_pthread_create + fun:x_cgo_sys_thread_create + fun:_rt0_amd64_lib + fun:call_init + fun:_dl_init + obj:/usr/lib/ld-2.33.so + obj:* + obj:* + obj:* +} +{ + + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_cgo_try_pthread_create + fun:_cgo_sys_thread_start + fun:runtime.asmcgocall + obj:* + fun:runtime.newm + fun:runtime.main.func1 + fun:runtime.systemstack + obj:/mnt/share/ed/libX11.so +} +{ + + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_cgo_try_pthread_create + fun:_cgo_sys_thread_start + fun:runtime.asmcgocall + obj:* + fun:runtime.newm + fun:runtime.startm + fun:runtime.newproc1 + fun:runtime.newproc.func1 + fun:runtime.systemstack +} +{ + + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_cgo_try_pthread_create + fun:_cgo_sys_thread_start + fun:runtime.asmcgocall + obj:* + fun:runtime.newm + fun:runtime.startm + fun:runtime.handoffp + fun:runtime.stoplockedm + fun:runtime.schedule +} +{ + + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_cgo_try_pthread_create + fun:_cgo_sys_thread_start + fun:runtime.asmcgocall + obj:* + fun:runtime.malg.func1 + fun:runtime.systemstack + obj:/mnt/share/ed/libX11.so + fun:runtime.rt0_go +} +{ + + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_cgo_try_pthread_create + fun:x_cgo_sys_thread_create + fun:_rt0_amd64_lib + fun:call_init + fun:_dl_init + obj:/usr/lib/ld-2.33.so +} +{ + + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_cgo_try_pthread_create + fun:_cgo_sys_thread_start + fun:runtime.asmcgocall + obj:* + obj:* + obj:* + obj:* + obj:* + obj:* +} diff --git a/input.c b/input.c index 1bec771..5a5927d 100644 --- a/input.c +++ b/input.c @@ -2,7 +2,7 @@ * * Created by Rory Healy (healyr@student.unimelb.edu.au) * Created on 8th September 2021 - * Last modified 9th September 2021 + * Last modified 14th September 2021 * * Contains functions for ensuring correct input arguments are given for * each stage of the voronoi2 program. Adapted from Grady's base code on Ed. @@ -13,10 +13,6 @@ #include "input.h" #endif -#ifndef COMMON_HEADER -#include "common.h" -#endif - #define STAGE_1_ARG_COUNT 4 #define STAGE_2_ARG_COUNT 5 #define STAGE_3_ARG_COUNT 5 diff --git a/input.h b/input.h index c8d5559..0d277ab 100644 --- a/input.h +++ b/input.h @@ -1,3 +1,7 @@ +#ifndef COMMON_HEADER +#include "common.h" +#endif + #ifndef INPUT_HEADER #define INPUT_HEADER diff --git a/input.o b/input.o index 325dbb47f8f935acdcbeb60b68960fe5217e05f2..a7d87f1f76d54822bc4f5dba76f2cfe4ecfff4cc 100644 GIT binary patch delta 2004 zcmZ9MT}TvB6vyxE%_f}!ODe-Yq@eHoAVVw4Xp0_#k$fod zA>|e+6lf0-5g}|)J(W=p5qjFO{TIUV`IcTj{P=HXUU=c#Sf--~+3aSt`Dp-xspr8(6lY%V> zTNLa<*y;|1uSMEQvmir3;;J|7jBI-!TlV>LzTPQDwRF?x#*wMLZ z7fe-bUp_VkvGS1PDs8}*2Hh08W4?53OP+qrsg6!UKtf7}8Y5~n)o2w+J!DB`x7b}` z#3@a-l#fD6Wau1ADZN6xcb0?|Wuz9OSS{~UWT>4LDNe_woqul8#JGpi>BSh`4P7LS zrGCmY{SrTE6vheysFGo(zrow+)qBmbq*=*4uMr|(>k=@9A5+OfCBarlr6j{*DZ}(V z5AzHVocO#lb&%%H)rGxHf!n}gI}M{bCF2*Cv*y$)h>Ne8m$;eBAm4)GjhgJ#63@-LmyH#dvZs13tn6kMV?F14FK1yYAG?CN|F^^}GHmyc?T9fioqOs% zaK&`m;SID+^4#SYTy3DRT^N~ww`lHJs&+usfO}MPFA#T#j!cluQ@-+`_?`~LXvX1v zTHa%-dRt6D)e+78Kpj04GvMYlSKWNjUW@V|2tR7>GphVm(>I$a{Lpn;>#wE{l5WsD z|C{8OC?6kn@jOWK5-p~kxJrBv0=`yr-%y9!L~>EU_i1h#%d_KRE4V$Hng|lUvn$SeJm!yeXhA*$bBme9)y#cTS~k4P18-%e`$Id@vNqQrr&PUJHM;f zD6SB7ahE4~nHH1()K`cJ9`H4qJ4^i^6EonR*4%#L8=|~8;CD2)gL3bQ*5V)>)7(~a nbDBOy`b$lpCjFJB|0MmLrvE1W1L~goLzW-rGBej%{3-Si%Nnug delta 1999 zcmZ9NUuYCZ9LHz>-R@?*y<9HI-e`&umDp-*(~3q-O=5$1C2G>ANMlSyC81&rUZ9|` z{ske5Ne9$ag`_AgrIZWtArC%i(9*P2o2LrCSn$C|MFgM3-^}k9BC~Ki^Z9;$GdDXo zo0Qs0ZKYRU;<-JHv6P!qjRPNM>AS7(j=sCD$`j_f-oVi!ODVGF;~{ALz*pqEx(Nc` z3N+goTe{Y_Jsk$R(UEga+=MKZDB+ARQxFETaQAUcnG#RjhM>b{j4N=z@YNa#Himbh zAE4Q!p&X%6LmpwXhFXLz8rC3e)sTFK)TF5q;Y$rI2rU|RBD8AgK-jLK3!$xg02bPd zu%mJipdVqUUiLABf`%f(*Zh`x{_l}?Ra^i#g|Itw8Q=`Uo?sZJJB!enIS+6FVQ;zw zWupk+#-m`aA#{uD022uN^m=|l=n2)AmJpKtWru)fkb3p%?jalqCSisL_;Y){n1i|{ z@L}%SD6QHsI0yw{phPqaH?^>gIu$DI2B5N$#qJg8>e?x+t!FF;#p*{cSL6JVNEsiG z3Z1U3GFFGzBgcc~*^U)3h}mODV5G|H%_9e4v$)nKu>0L4C;2u^GUieP_Kqf}WACg*%{VxInBXd{#^S)2=YGzYdxz$RK& zA*U9SS-}dqPb;CYe8UGLn?;kDej5&3>jsV<8!oPgM`P@G@!-H9bk+Optz;qWG+s{L z^1|0%ASZd%^zZzqFzF=ILallq)jR!!c!!ZcBW^eH7sTHf`Agz1BmYGI#bGu(EL*E$ zQ8aYDg~16q1ooU^zoQ2lk&|G5Fzg`lxSR(&Vc1dP+tR9z#gt)xrI{bf2C#n__HSC% zf3gqkbHl=U(AO(DRvjnr410%W<}}B`MmOU|u)LWQuQu{g;?IqI;oLW4F6-i~Xg6wl ziTBFRI2MNuJ4-7(E>D9!W!Nfuo=b8H?2m>m68|j!2Ybh`ztPzHGMkUZBg3vF`&71q zePP&t$-a?8`8cs@jSIWCoOUl`NWhQT661R|J zWMJR~$ui3Dfu$$LK)KQ&DW-`5Twqyc!9I}Yi5U>C%I5z}QjEOPjI10S2CSUvtZE!$ zo7Gt27+KSD@)JuYH?yAI?8|nWnUQ0%Cf9q$f0HG-^%;jnw1R#Uhf%pfA z0|fmL3NAhyXgvrloV<}+Tyi;t1DCad^4Cs&$Sp3o8BNw_vLcW8WCb1$$pdII`cVGq z$%$Ozl8?}2&qMhiCl~UFOEN%R0COqK4g8xI@?2+Rys^2GkDZZGb#f!WIi~(ypF;MW4!jUY9-e%VPaXiD C6(gW|j`@848%dO^G*;<~p64#S=L>YY3zAlG^WNBGA zlOjzVvwVrm9(J`&enoXID%Atx$<7CREx=(fVsDA7-E!nPObm0ePx{3~e41)ljV~3d-XtM!L{yxUV2e|)Db(%H`WQOH zSDX)>2h>d$0=|N&apR^ZJwid_C~FGcVmHR=6?+;leDu8f3HYz4e-W_aK4?!w_pXzS z5{;pu7uC*sB5&edFOm*3Wl;ka;Y~QgU(ilj>`lYCF|Qs<{-ip#j3VXX7#1y}ExZJ? Y&!Qbvgooe=zeHR33e2=cgQ%o)|K+4nhX4Qo diff --git a/output.txt b/output.txt index 5080ec9..a394812 100644 --- a/output.txt +++ b/output.txt @@ -1,6 +1 @@ -From Edge 0 (140.900000, -34.700000) to Edge 2 (150.000000, -34.700000) -From Edge 0 (140.900000, -35.200000) to Edge 2 (150.000000, -35.200000) -From Edge 1 (147.100000, -33.900000) to Edge 3 (147.100000, -39.200000) -From Edge 1 (147.100000, -33.900000) to Edge 3 (147.100000, -39.200000) -From Edge 1 (147.600000, -33.900000) to Edge 3 (147.600000, -39.200000) -From Edge 1 (146.900000, -33.900000) to Edge 3 (141.600000, -39.200000) +Watchtower ID: WT3765SHSPB, Postcode: 3765, Population Served: 3380, Watchtower Point of Contact Name: Eilene Horner, x: 145.362014, y: -37.818943, Diameter of Cell: 10.000000 diff --git a/towers.c b/towers.c index e9dc503..60527ce 100644 --- a/towers.c +++ b/towers.c @@ -2,7 +2,7 @@ * * Created by Rory Healy (healyr@student.unimelb.edu.au) * Created on 11th September 2021 - * Last modified 13th September 2021 + * Last modified 14th September 2021 * * Contains functions for reading a CSV of watchtower information into an * array of towers. @@ -18,38 +18,7 @@ #define MAX_FIELD_LEN 128 #define NUM_CSV_FIELDS 6 -tower_t **readTowers(tower_t **towers, FILE *datasetFile, int *numTowers) { - /* Maximum length of a single CSV line (+1 for null char in string) */ - size_t lineBufferSize = MAX_CSV_ENTRY_LEN + 1; - - /* Stores the current line from the CSV */ - char *lineBuffer = malloc(lineBufferSize * sizeof(*lineBuffer)); - checkNullPointer(lineBuffer); - - int maxSizetowers = 1; - - /* Discard the header line, then read the rest of the CSV */ - getline(&lineBuffer, &lineBufferSize, datasetFile); - while (getline(&lineBuffer, &lineBufferSize, datasetFile) > 0) { - /* Ensure there is enough space in the towers array */ - if (*numTowers == maxSizetowers) { - maxSizetowers *= 2; - tower_t **temp = realloc(towers, maxSizetowers * sizeof(*towers)); - checkNullPointer(temp); - towers = temp; - } - - /* The current tower being filled in with information */ - towers[*numTowers] = malloc(sizeof(*towers[*numTowers])); - - readCurrentTower(lineBuffer, towers[*numTowers]); - *numTowers += 1; - } - free(lineBuffer); - return towers; -} - -void readCurrentTower(char *lineBuffer, tower_t *tower) { +void readATower(char *lineBuffer, tower_t *tower) { /* Stores the current CSV field for the current line */ char *token = strtok(lineBuffer, ","); size_t tokenLength; @@ -57,11 +26,8 @@ void readCurrentTower(char *lineBuffer, tower_t *tower) { for (int i = 0; i < NUM_CSV_FIELDS; i++) { tokenLength = strlen(token); switch(i) { - /* Case 0, 1, and 3 deal with strings - * Case 2 deals with an integer - * Case 4 and 5 deal with doubles - * Each case is handled seperately to fill in the - * tower with no space wasted. + /* Cases represent each field in the CSV, and as such are handled + * differently. */ case 0: tower->id = malloc(sizeof(char) * tokenLength + 1); @@ -95,12 +61,55 @@ void readCurrentTower(char *lineBuffer, tower_t *tower) { } } +tower_t **readTowers(tower_t **towers, FILE *datasetFile, int *numTowers) { + /* Maximum length of a single CSV line (+1 for null char in string) */ + size_t lineBufferSize = MAX_CSV_ENTRY_LEN + 1; + + /* Stores the current line from the CSV */ + char *lineBuffer = malloc(lineBufferSize * sizeof(*lineBuffer)); + checkNullPointer(lineBuffer); + + int maxSizetowers = 1; + + /* Discard the header line, then read the rest of the CSV */ + getline(&lineBuffer, &lineBufferSize, datasetFile); + while (getline(&lineBuffer, &lineBufferSize, datasetFile) > 0) { + /* Ensure there is enough space in the towers array */ + if (*numTowers == maxSizetowers) { + maxSizetowers *= 2; + tower_t **temp = realloc(towers, maxSizetowers * sizeof(*towers)); + checkNullPointer(temp); + towers = temp; + } + + /* The current tower being filled in with information */ + towers[*numTowers] = malloc(sizeof(*towers[*numTowers])); + + readATower(lineBuffer, towers[*numTowers]); + *numTowers += 1; + } + free(lineBuffer); + return towers; +} + void freeTowers(tower_t **towers, int numTowers) { + if (!towers) { + return; + } + for (int i = 0; i < numTowers; i++) { - free(towers[i]->id); - free(towers[i]->manager); - free(towers[i]->postcode); - free(towers[i]); + if (towers[i]->id) { + free(towers[i]->id); + } + if (towers[i]->manager) { + free(towers[i]->manager); + } + if (towers[i]->postcode) { + free(towers[i]->postcode); + } + if (towers[i]) { + free(towers[i]); + } } free(towers); } diff --git a/towers.h b/towers.h index 2e9a696..967c903 100644 --- a/towers.h +++ b/towers.h @@ -14,12 +14,12 @@ typedef struct tower { double y; } tower_t; +/* Reads the current row from the CSV and converts it into a new tower */ +void readATower(char *lineBuffer, tower_t *tower); + /* Reads the CSV file and stores the information in the towers array */ tower_t **readTowers(tower_t **towers, FILE *datasetFile, int *numTowers); -/* Reads the current row from the CSV and converts it into a new tower */ -void readCurrentTower(char *lineBuffer, tower_t *tower); - /* Frees all towers in a towers array */ void freeTowers(tower_t **towers, int numTowers); diff --git a/towers.o b/towers.o index 32c15bb7a03a4bf6c21109ec958cbda33fb3d90d..f4723f38ee2b0b8eaa0fa25d3acc5366ba45db0b 100644 GIT binary patch delta 3354 zcmZ`*drTBZ7@yg_g*y&T?g4uX9vm;#iS!#JK z$k~bT7KhG85lXRcCUGg&y8=Qks>3yQn#r*BMy>c^tHmf*^%QF>xsFrg?B(`a*IM5;eM8Z{Mf6LER17Xg6Zw^Y~P=6$GHB3)ab`T%5$Oqn%Q* zhmtsJYDPp-UV{$n4Q0u!0&^G8K~B||S^dp=dN1p)W2*dB(_H1Sxx&waV7m=C%{SM* z6p4=I_9}a&{VBKLCGC{rfMP(i6(@G3)_C8nQ($IVE*^p20vT_Fb!k79EI~HbDH*T_ zOKxkm}qKiY_t2~ zi>DP&L`q*Fc{fP80f|^+=%_K`P z6V7Gk;}viuGe6%?{LSQ`raim$j?6v6%8V58Nt~W)g<-!Byv|Q-#wupPxKoBZ&PBLV zzw=w*p3{SysfX8VXO*6n2+8O|7#Weq)7qtyw5J`lcZA!!o*sxs z*Y$LE$HIN^kvqGMrqbr6A3YzAMLWC0G`psc^o7GnRr_P1_Aa%(qf3p1I-`i9EPzfF zLb>_{(I{BTmYBDn09aRACaa=FTBn2Wlm!3P0*WaVwfl9v=^ki*D)3PK(kU!$7x z@o6+x_HNKvhbi_6NOA8=*U%e}kG8*?@|q#4zF}x<1DmfG&=F~5UE_*6D&cJ0^#+TrhtSSvG z$4XA)gOFENF0u?Q92TvGU2F}Fu`h>lT9=^FTf^hz_mIs8!_izWz-VY0haY4GR<8HS`RJFJuM64A<&P z1e}jxd49xI!+07_4^bFBo!@}Tf8l+9g|O8Fiwmmp82L#IkaJQykCXE;C@&1yEHn|Z z-LSo|LRjR5(}mTxA)5{^h5LmS_$-tc1@H~%EehEFqO(D46Pzik5HkI6x2W2-ntq;$ z4MAmb1-=Ts#R2>PlEncyRbqvS;$?#035!drg*krcE2$=xlO+M$MeXMpp?BeaNk!UL Nzm@`BMDW0$_dmKAT-yKu delta 3198 zcmaJ@YfKbZ6ux(c!C98qz8nUG-IbR$2x8F%AHYJfyjqKr2&L_UxL6`VU9c^-&7wpj zF-Df-v(={7)S8&IvHZ{!(`u~MCVi;C)L2s*(%8h>(;$6_8- z#bv|xI4_)wbGf^xrlx9l)!eBazI2DnM!vmJ06~8S-42rEqrR~`=@%ioCZ2QLF6tAZ zhhG*6N#S>NO$J{Ju@=0|4>MGVHK(GM9K(g9o6yc=o$VncUT$Z8@$v(KkYp~QB~SB+ z4AY>)KZ|4`CMwC?#V{dOt0yX1!2N|-rBhQbvg>TSej9*xTc)!N)4f87OH+AZldQCE zi!+M%_;~SR>_k)#>M0y#sm0ib#(gE%im`)HJ*<*APpbiwreo4toy~*^Io+eLa}~WU zFY#v#6WC$9S5QL4r_sc~o-A zdH*GK0B&Y5Ws*~b{vxzfgwsW)KjN^>0h3XOy z#GDF@IOo%9zM>6$R97+5NhmQ?#%5cqX}Yy>3m;+7l}2}J_j|N)KOd`(pnn_NIoymx zLHc?9eTAKbKa=(_eI)dJg(_5Tp#LueSN5_qq~Rn^+)`U=bc+EfsfoXX%`fG6uJ*68$X7lZl(CgX&nAG;ry@~f&38jDC#2YH&4UQk-@$Q z_dQCq4_Ur@dCLs;ZIhS7gdZ^B{45#z)0Q}l((J-C_zfM0{Fx5*fhne)c^NTf*g8`w zF2sXnZBv~IH+!{nue#kP)@_P;(uA9v{*4JYNBfCKqXIY-h0f+HUEpsJ$&fD!mhf}6qiKrv(A*Fn;Ov%%hi4ppINVL&ZX-B{vYizWrmvj|Tx`*90< z5$>x+^fU0MD{2--xM|T0LK9?zpF*m^{hQDs#49h_op6wx^rwhks3TY!11 zEbOP?$S9(pKrlm5k0JMlMe`SIV(fLyK4M`X!ci9C09<7n5xK7|ngO7*6m=ceaNEMR zLNR0KBl^g~mO%$&Pa^8X-_UsWN{BI5#K;8}_5qw{Y#REuS=dRq&)8oP9k#H$!Jnz9 zYw)S}S=b}c#MpR5$1SWKwlektqW3K9A8?AXm$9lT3%eR7GK;7Tjx0spjhZF+eEgQn zdm)&mM9Z+YUJGmd`E5ts=+!pV@3r7ZFv~Fuo{p7$Zo&Cy60;~dkAzXy5C)pCIHX{p zyB573hrI{!U1Jm~qt0c)Ph*^H4UhhNf{i{sYjIeH1yxyaqZ3UQJc5Zk5H}ud3sxF2 z>m>%dDBSdRIiEwG*T=6GI=0fcoU(7cL;iqp#Rb>=<+PoB@Kvy}NgHEqBye?hh3y>- zIt*%dKp0VAWp=rZeU@pK4u`S>)C*U$D`+J+aw=>qc$b*1hSr>bu+a;nIpwzF`1aic zH*x}WAvkg?Xd5);RtW8>Fq&H~45q?0HtqC+Gp`&*a%E`AE49thzMKg;0f+Je30J&& LtoPoint->x, intersection->toPoint->y); } +void stage34PrintTowers(tower_t *tower, FILE *outputFile, double diameter) { + fprintf(outputFile, "Watchtower ID: %s, Postcode: %s, Population " \ + "Served: %d, Watchtower Point of Contact Name: %s, x: %lf, y: %lf, " \ + "Diameter of Cell: %lf\n", tower->id, tower->postcode, \ + tower->population, tower->manager, tower->x, tower->y, diameter); +} + +int getFaceTowerIsIn(DCEL_t *dcel, tower_t *tower) { + for (int i = 0; i < getFaceCount(dcel); i++) { + if (inFace(dcel, tower->x, tower->y, i)) { + return i; + } + } + return -1; +} + +void incrementalVoronoi(DCEL_t *voronoi, tower_t *tower) { + int faceTowerIsIn = getFaceTowerIsIn(voronoi, tower); + if (faceTowerIsIn == -1) { + fprintf(stderr, "Error: Watchtower %s is outside the polygon.\n", \ + tower->id); + exit(EXIT_FAILURE); + } +} + +DCEL_t *newVoronoi(DCEL_t *voronoi, tower_t *towerA, tower_t *towerB) { + /* Get vertices from towers */ + vertex_t *pointA = getAPoint(towerA->x, towerA->y); + vertex_t *pointB = getAPoint(towerB->x, towerB->y); + + /* Create first bisector */ + bisector_t *bisector = newBisector(); + bisector = getABisector(bisector, pointA, pointB); + + /* Get intersection of bisector with the polygon */ + intersection_t *intersection = NULL; + intersection = getAnIntersection(intersection, voronoi, bisector, \ + DEFAULT_FACE, DEFAULTMINLENGTH); + + if (intersection) { + free(intersection->fromPoint); + free(intersection->toPoint); + free(intersection); + } + + free(bisector->mid); + free(bisector); + free(pointA); + free(pointB); + return voronoi; +} + void stage1(char *pointsFileName, char *outputFileName) { FILE *pointsFile = NULL, *outputFile = NULL; pointsFile = safeFileOpen(&pointsFile, pointsFileName, "r"); @@ -59,7 +111,9 @@ void stage1(char *pointsFileName, char *outputFileName) { fclose(outputFile); } -void stage2(char *pointsFileName, char *polygonFileName, char *outputFileName) { +void stage2(char *pointsFileName, char *polygonFileName, \ + char *outputFileName) { + FILE *pointsFile = NULL, *outputFile = NULL; pointsFile = safeFileOpen(&pointsFile, pointsFileName, "r"); outputFile = safeFileOpen(&outputFile, outputFileName, "w"); @@ -83,8 +137,8 @@ void stage2(char *pointsFileName, char *polygonFileName, char *outputFileName) { intersection_t **intersections = malloc(sizeof(*intersections)); checkNullPointer(intersections); int numIntersections = 0; - intersections = getIntersections(intersections, &numIntersections, bisectors, numBisectors, dcel, DEFAULT_FACE, \ - DEFAULTMINLENGTH); + intersections = getIntersections(intersections, &numIntersections, \ + bisectors, numBisectors, dcel, DEFAULT_FACE, DEFAULTMINLENGTH); for (int i = 0; i < numIntersections; i++) { stage2PrintIntersection(intersections[i], outputFile); @@ -105,9 +159,22 @@ void stage3(char *dataFileName, char *polygonFileName, char *outputFileName) { polygonFile = safeFileOpen(&polygonFile, polygonFileName, "r"); outputFile = safeFileOpen(&outputFile, outputFileName, "w"); + /* Read towers from the data file */ + tower_t **towers = malloc(sizeof(*towers)); + checkNullPointer(towers); + int numTowers = 0; + towers = readTowers(towers, dataFile, &numTowers); + /* Construct the DCEL from the polygon file */ + DCEL_t *dcel = readPolygonFile(polygonFileName); + /* Add each tower to the Voronoi DCEL */ + + /* Get diameters and output to outputFile */ + /* Clean up */ + freeDCEL(dcel); + freeTowers(towers, numTowers); fclose(dataFile); fclose(polygonFile); fclose(outputFile); @@ -119,9 +186,22 @@ void stage4(char *dataFileName, char *polygonFileName, char *outputFileName) { polygonFile = safeFileOpen(&polygonFile, polygonFileName, "r"); outputFile = safeFileOpen(&outputFile, outputFileName, "w"); + /* Read towers from the data file */ + tower_t **towers = malloc(sizeof(*towers)); + checkNullPointer(towers); + int numTowers = 0; + towers = readTowers(towers, dataFile, &numTowers); + /* Construct the DCEL from the polygon file */ + DCEL_t *dcel = readPolygonFile(polygonFileName); + + /* Add each tower to the Voronoi DCEL */ + + /* Get diameters and sort, then output to outputFile */ /* Clean up */ + freeDCEL(dcel); + freeTowers(towers, numTowers); fclose(dataFile); fclose(polygonFile); fclose(outputFile); diff --git a/voronoi.h b/voronoi.h index 7088143..0aaaecf 100644 --- a/voronoi.h +++ b/voronoi.h @@ -11,6 +11,20 @@ void stage1PrintBisector(bisector_t *bisector, FILE *outputFile); /* Prints intersections to an output file */ void stage2PrintIntersection(intersection_t *intersection, FILE *outputFile); +/* Prints the watchtower information to an output file */ +void stage34PrintTowers(tower_t *tower, FILE *outputFile, double diameter); + +/* Returns the face a tower is in */ +int getFaceTowerIsIn(DCEL_t *dcel, tower_t *tower); + +/* Adds the watchtower to the Voronoi diagram represented by the given DCEL, + * applying required splits and setting the watchtower as required. + */ +void incrementalVoronoi(DCEL_t *voronoi, tower_t *tower); + +/* Creates a new Voronoi structure */ +DCEL_t *newVoronoi(DCEL_t *voronoi, tower_t *towerA, tower_t *towerB); + /* Outputs the bisector equations from pointsFile into outputFile */ void stage1(char *pointsFileName, char *outputFileName); diff --git a/voronoi.o b/voronoi.o index bd233458d34b814f4f823169176895e37959bd9f..343e528eb4736c89701de66f324746c7fb35fa5c 100644 GIT binary patch literal 19304 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 literal 14544 zcmd^FdvH|Oc|WVw)vhcgEfNn2Y_nh+gh4OBn5Phe6)O;+2Fo$A-DS0VSK6@JRrbNd zae{2}K-myWj+?=;lOdT-NRaFI^^d&fKl+x(Kk|ti{*laef7W*++wT7KM0zQ9{aNA9dZ2eb;;)W>&wu(@;?jhZhko~N` zRM}{E!+1N7!?gu*^1Hg>Ww4yH{`Xo8rG>5cKr+T58K=YkZ0Q{hz~mqC-DsWgpZ8q_ zLcn!WUb8#L{l|S*{l{lZa^xjQjLZq}U-f5qo`%uMf}@kqdq9MFuYtSQSoXZ{A}I&O zy%*JxuK8NlcX8xYXIw5$LmaId#`{qXapc#)AjSd zOK9uOJN?<-$*k`Z=s7;)`^r2pJ1$`TNB)Hs9rayu0M~lom4{!-D%bLZ(u(hT9sqk+ zSrYn_+#dzvM%cehr8aHJEKDi?5ES4P6yT(Pr1!-1B3zITlJ5}NDM<5$f@uc-cS1-I zSfLVKnoouIgPTn*`|yPEZlYehJh^_`nmgh@T>Z0|X628}=>bWO+w zf#xR8wddVnny1E;LZ0`XRbFkxOE*lK>`u_y3v(=U7SzZ^dVceBk}0nd$~=LPEc2zT zZ{iP@c>>COcCBv$%PfP>S5?O0o74emIuC#&xycOu3%S^Ef{|B$cn-9~9DwPQ0g>Jd z)91h>1_TRH<>|cdx}-6|MVB0;ALar0R%kB3>cCRNq%SmzRe-Wlji$y2Yf9n&k7B}P z3vtN1(R)WMa+5f$I^H|I4Trpqh%NOlQ$xttnM@3Nec^uFdq>#Y0HezlOFB^iy-6g+ zL6qVaZO_7jz^z&cx4ga0;32-QE90IJCD<*wJa;WzoNgCV&Qf_Pbvqrnw1l%n!44_r zEU^%@O08%-4I+|~TAU~9V=$unPtM1FnR^U=ok~(_xmw!lUH~jobLmQ#4dXk0RDcqD~QOCE-=XI!QDrV!b3<6tO`PYZS3j677m;mxNytnEMmf%qy2?YMta1hl zdn*r%vMSka03&ju0}L)!JD-4#wB&Kk^elqI5{S8KB(DH5SFP;2RSI0QoWE!OI@cUe z2>A!WHP_r~>?_w?bALHdIoK!SR!NYW z1(Hw^y1XhvC9_ZxH!EV1B)p2aO%k_*=}gY_?N01d#EU(3kPOAVWG42BXsEhX4wSin z4%}t#cffDu`cmihy!b*?p@Z{%dwN(ag2I!Tl&Z`R9v zQtTTf;UNz;I>W53#kI+E7wjfVro9sTL~NGYNI{1rW+=9;mFt0xGU<~eb!wzj5|l~5 zH0o6&U2>#B5!)rP9MW<-I8x@u9H>BdIKK`3DsVzL(zzY;3ewOVPoD?F)v}we#i?8} z<|lHABHM{5P6Ft?;~=BBKMKyNCC^60aLmoQ!PiPT8C`XxYSM)K8dXu!B&%&k!D$aHl z_Erz7h7>AN70)?#ilkMQoMX=ty`yHcWTzbVRPK}{<#5lOJ(Ap~l=aFIJIDU&$V@B% zw)N^FYNEoix5@^}Id&R$-ZD3?{4)18kS}vbWq&i&xy-#+A~@UCj98e<&Li{;>3AAv zp325T%FZdu#zM-@N%DUYRf57@M9H9VmQXS%oTcRdSrTrPbjjFO65HA3#SW0z^^hykjqu; za8^}RC@}{B?n+I|8jvc_tA%@2EsSochuf3G(N&B5GZu=j+GgOZ1VJ~ON5QHl$EJGr zXh}V~*c{K_{OF!vnInN+el7VT^94~3c!tyvvdy{a)5jb{!u_Qx~L ztu4*bcqo<$+d?uDF`cn5noj)>Seny^hwPLP2NKCdJP~aQp_i$2IBZ9n2Jn4~9EUDA zoJuD%p>&|LtJ?~6QLBi8Kux8GnV6w)#DxjU;k)Rv%u8i-;gh}GgcLbmABnA)%J2_ zd#<#tT-fj+x&Tf{FLtpPw>)aCc+~P7g;++dT8Jtiy0!a{<}H~ElP(ZQL?VH-2z2cZ zB<)~0FqBS;KxiNsjSCwdSwt%Okd5Q1fkZOhmx%~?N`OM!p>#A67eh(=Ks1p_$&yPY zz!K;Srfeyl>P^{U5lh7Tz49j$PeuFVcG!!L8e6ycxD54`2b0mXEoXqNws(qlWJm=M*s8jLSR%Av$VE??T_FO4P&t8K z^kC_f|EAZ42$LZNgHWx| z{>GtXA}wcgBkYwp?|C2;3#JBm7?utUgd!RZ*^?#389J1S1?BRR+1ea~pn_>wNV$5* z>7R-vhHOlwJ~>iySwRL=1+fe=tI@C>2nW+a5jY?{<{67vZf@d&!j&XfEu{}Mq>w>Y z4)jXXoJ^pli&6kMa~lIJMzpYc6>wl3frg664MuJbNXf~Bg^s57U?^SjNHh);5JZQ9 z;k%}6G@vGsiDzIpS&>`s`Mhut(nueaAv2_ADLR*54Y6p7yvd6O4(2i=mmy3ywS>Sy zwH&0sYO4V+--ZaOMkBoTrAL>qH-FFoHd$1d}Lk#xIDTW z68NpC5RPxJQ4h=WxxJV0N=yheeh_&8`i1mFi{L{=@JEW^pD%(xSp@&{BKWt9;LjDo z{}FJ^!x!j)$d9F$i}1f%1b?RpUJ7|ElsC*T+I<`s9W;IgFpKaAO)bW?ySZBaQ*UX3 zZQZ*&Hg^YhcXsaa-4nQHb4Rx?AR*yv8xS)VhK2c1p;6N?uVg){eeg@8slwC>C2r< z1SsvAvU#Jn>4-eANx$=39_CMe6NM|55%;k7KTxd$`q(hd_@H2i)IhX>*uf4_#~J&5(Bir~jI ze744aiEzB&eBk{5RO8q4|DOgw=l|Uz{4TmtV!V3Z=4!Z}x4Sf4&;MQx*Y)6QB5bH% z*K%K?B;r&WZPe8-xY50B(*XPN>BKYSuT%RZZN;t>M^W+yAzdldiE5Z-Y%(CII z&R<73>*slOTM_=18m`B+sR+KK2)<9l^>|Mc&i-(`XEc7@|CfsJ|D*{2TLwSJ``aS? zE;=e<-t>5Di{J~2;HxxTkC$M%_mc~~1me=LzpNa9+<`Pxf;zIrR~P|1{A- z1OExF^MrxFN_5)5i4?;IzL(a?69&%v!jlI6WAgJG2L1-o-!X8!$6%X~zxldcA^L{~ z9wmQXHgH~_zc6rqVfbqUKTdwi7o{-9dEQBURa76w<-~xo`38O`>1i!j)oZtb^L%~4z%P=YeBNL`@!K)B0|x&(qK_K*45IOw5F6`xoBa8Tfj1~N@r;4v z=WuM_HSkWN&l&jRid|ebaDEhf#lU|?`FzvBe?;^R1Al_}E%J-w>L$9zz@H(0uYu1a ze7S*NBzl#BKTCe_xrps9Av|L6|BUiHV&Gq={EQlSnD9R{aQqJhY~M2QNutjf_`~GS z1q1&g`NQWZ_J1Yu|H|P1I?*2(I6u4JM*3LK6570*4SW~r?=tXDQU32Wa6aFB)WBC0 z{{aJMzmFRDCY3fZYT(BS=W`ePKTL5xWAML1cE4-jH&+oVP8T@b4d>Art_J7#G+5b@k zUqJakY2fVVUmN&SWcTk3`~c1KmkfNE_+K&b*NOj^2JR>R_YIu)oeDgBL*u%-kwGVXzJgf^3Oq!DOlWX@bkXiY2X~!y$1dO*?o|3_J`Dq zA%maK14j&;_0x2ab*CvSRq;DoQ~z6J$i%&bqkH6{MA{9U^RUmrd7ca#IIq)_2G0A> zgn{$EDwN)9pGNljs=_GD|K2l$y`L*#w`K;3Fiw|0Np!d87$G zPY%IHzQd%)9)M59_!<2FqTNy{I4|0D5cZ1>QpWJ9!&wfo~!B5)vBb4^^@{&fj*xOq~FUgZPpX+8krhAjN~gV`3I{-4IgLSuRKAOF$W^#5aknK}Uye@pg1YZS)hjR9{V`;%m!^Mv;C zeXD8z6M&&k_7l3YZAAfS-E`klRNhzi;}{GSk{>0*IKTDAa(o?wLGJ5(f5VRJU;*B* z0fx68BTZtQT8~2+8D8LlQGHJggEV zU`ZpD+}nru78U!X7A|XFV{3)p+RD9R8w#}%^_I5Q{J-D5_c`+*QEz|u z|Nr;@|1kMv_TFo)z4qE`ueJ8$oH?i2U%bfaa2VFdWn62pv@cUZiZr6*ZJj|c?fekCQBxK5Z{e3zHD2$aF9#v*N z!K^MPrY|f~bXIEN?e^9wypfQeb}P7*#@3^5a_&|5oZa#1MXeXB|5iPMSubDL%h&a+ z)X?>;)Y2z)!v9%XpNVN9aQs+lV!aw&FEL%Hsu@kcYDiRt@# zpsmzu?`G7aUOw)}8s=Mcd;N_=x7MeX>gJ8fb+zlSo<6y*a#CGwePr{b&9knaboKOt zP(#5~DFWs4;h#2j!;LFA936ydH5$%ss%-Hi&a3gHzv4^vpAXHv=KPM7f1h*a#p0=d zxcK@h(ok;JAq_v|XAL>a%;(^rG*Taq`;ar7gyrJ@<={^?%)ZcOxeak~pJY_jt{n#_ zKVKX~6_z6Di~dXf!0+z|{+)i{U+f3Ip&$6W{lNdw5Bw+nz=s3w!++xEvjFtf-|zPW zU)v9SYd`SY`hnBU`)c>qe&DzC13%smd?MgJ{3m`E0nk^wzX5!7A~DJx_|Gtg8ooUW zX~hwvZ1M7vvdUmnu(~!B4mK?>xxTKUKDfMmeO=HfE34knP+t}bmp6sW$`omzgrU2d z!bT|ERMEJ_2yU(o8&wUB!FtrM4K-BEo?R9SRg~9Pp$t|Pca~Mu+*wvtUR!501uT=~l4He&MnDlHDs;Ctl-Jf9RZYR50pJD(imREcP`Mk57caQJY-+*O zf*FRDpKj-7QkV2>C;Z(h|B`e{SQM&oj5^Hm@sHPva0X2>s#}dHO||z$OUcDVDYiwMH+4<*o9R-^`N3>%u^Ah3FQq5`y4qODNBXEl>Hw;?|e%2(GI;;;=iwlsqP-sER%Uhq3+)7OjLPyPhj`F z*~kQTi>XXzUkqo%wBNc%nRaULY4^x&AUA%fQ_!bM`Wa3pSAGy^?Fk%x>$`p< zM}e0=2z$Zc1q+eTmsX>zm=UK-;{pq0s&3zxDQZ zSHhTnB>oL$$6D02+h_f=n}B3y1?s^WeXaMN541*3x7>T)5y=Sbo^gAzVLS(#-tM2l z)dFq)(}9)_xGdT^-dzY#tN&t~{~`*H!1Y@F@3;BipW{Ct&HxbAC!soUp9f*fy%&?* zJDz}uGWX!iD(4C}pzLx#NL3=|2oJ=!Gkn$AgY1oJFelLF(OINu`&Bld24_9{)hLxO zN`(p#QVOdo{Dc+Oz?;u)((sR@o(kVSxA@~yzXH!cm#2EY3`ZAeZXZN@4m~%z@54A8 zLdH1p!x&|I0;?3fa}WB8E+mC#@VohHe7nM<`Q1DL-;S_} zZ)Z65+^|DZU4@4@QJlL{C8Je>(S9EqdQaK-cMx+`TmOu<;x#J=TB;V_Qee<<5T@|D6_> z7R`lr(7h8}_Vh1KpsmOQex;hXEXaIYAW5}RWCCAg;gJ>+uH|;;|3_@=KSCeI>ei{a z?vzVf_gTug;Lh-OlNH(LtF{_pQvQp@kV&?&8Gg)eT0N(vC9D(I#ZdoxZk`_W|o}{Yg}x@X9MD8wLhVV8G7} zZSlYFAPwEsKhZE|``^Fk=}X46KuMyZggQpeU|hc-Csq_3$(Hof2TzP`dE_filkfl z_z^5YP@xB6L(?U!*b``#@$-)s>bU;TE-VgSM1 z2(zCUMi>!L;Xn2E>M|nW@$O2ijQmQKN8^&qVyg($Je5DqDpOQm4#7B;KNV-b3#xh4cVMj1qO`nEATG7|yBwgJ&LEHc|VgUAI0QSK=AG&l9Xe$N=L&}bx?T4B>q2`|i zs2{1Sug#XKg8U`u5<0d!`#VwWE`{wYTN|BM=&d4IUUFp6OrZ6)A}2(dfz4A{t-;P`yok8-dG0mq)>{tniY!`#*8 z@A!~m&P2#<@poXDpPB9N;4sV1t5JOe^O)?1|Fkv=*iqXG*|Y+Di!JK#wq(4_cd~DP zdq>(p7xQl;e~Ase4`7@Pth1aDHn9Iv8&G54w?lHYpI^kTOFjQ9j;W!AE~uXlNq7B9 z6nl@tVql~l(Oq{%v3Dr!X*DKaU@v0a_5G_kCQ`Skj%HVMsLsoFXHS>y((ewk-#^AV zq8g23ax}Ioo7wHP9IiXyfRG*zq;|S{5xHCaoozBmYu9=fW^=(kv7^=hQkDCUCoZ@r zl)Qwwd+Q11!!*O)(_>inz=t`vAJsgc#JterKZX(RnC(C2eqbY}6xHPM?k|kxc*;(w z@%(r8fq?$`${x>GqSylp z8#|tdqS((UtRBzfZ^w`4L|~)gm*W{5eru=7cJ1-}E4I%ZPfK~L$545)hEm?1X&}(b zd%q{DydIU8$~%MnCCd9FfN{$6s7N(L$$OE?rsnTaqjNEi)+d)n_$oOqV_5f79ANCU zSPPT5zZ*EpB@0-wevG9y}-)>)Nolu|KW;L*RLc zV!ereANL>NHsfp+2m#;KhLz5N$jq062ip7xXri{&qZK*9tX5o3gIa3lQKdcP{SQgb zyRl^1jU~%&8IwB}xBhuC_Po1iyuEEVHBjN z%w&5zJKPW6jqL?QlcO{BEt!W-*LGtT9wrw!n1%0_@U;oX1T*Mvjhw+stlJ3RVJ&^H z7x@1e1;!l16?HfO8c>vVJn&|?-m7#=&K z_7C&-JeLFO?fx3%vRsFjJ#Y#(250>MntRU+;mOAompHB3AnUI zX%dWtGOFExzgq|EmU~|^+&g(LN+dcx+u!Bh$#YTFKnbU&G|SN2&m~0HQmFkDEDf{V ziq6)OW5wvp!qyU)ONRg8aep^^dfa~=-^N)kz_9e}VS!Q~#8iwktn+c+(5WW!wvrAg z6&mpOaEA$%dX%Pm;7&b8xB-|h2F7j%#%}g2)~6tn42<(3Z>$mCU!Y6}Fbo_yBvfGTrIm_GKJI~bG zPfLuB0NS_Vfbf58>!JBr*XB_jPjFhfEB5!ma7?7#NMS@PkCIwVJcD-3rCr^@07oVy zmFE|vr6a`=-xej7b2zD4i^OJ?yWr^EWB45z&IaK5v}koWa=eI}4CCx)@THrP99T1; zs^S;`Rfn=FsObZ7T+ZF9G30}+5)c_BGJ~{+va1B<@T>>$pR2fh$BTf8z1R=4zo0>g z*3zxD4?4LNnPs_W>x!2wH~az`P4qgf4u&~ZM4B%B%moSuH#;pIj+g~Um-bGLjBa>= z=)}>l=4-E^PX!!37@fAr`9Q^g2X@bWPxTll7rTee)H7|SYS8wum)sAm)gFd zdG$9t!P=C9{~q!dRH63q5DQC{@&jZ;mWm^WhxHYK>tGumE747OYdxKQ%a>k;^2A zL8e$DxF`F2ng+-`o04{`=VS^98-{={f&UzTXJjlOr!*ER^6Jpzl%(oZ9ca0?3sd;* zN?)u$+y9Dt=ThxMR(TG}>{b=F>GLWL5`CLXQ`vS~H-g2)NNS=7gihFRJ8reT8@B@1X7lW=6}b48Tp z@&BT7^!!%pvnk*qNw!!MlR$dz4kFu1^JP*rarX1~6ziTp}p#NA#w0Ij7Hd{Wrkm9U(Z z;7Dl@mY3<6O%N8f<@)O=IZIi&pt9H+wxnscq-p(1idk1`mR8eU8Y`*{o?xoB1&@!l z27DO(+0tx|l5kj5v%?aEWpycSmM!f9tp&xoRC8`C8fMEoEM8tKsJ5;+jrK4@9q-=3 zc@23?O0E8P)#U2O^+>odWQNvOoM~0Ifm6uVQo1@|@3zV%em4%Y+WhZY@kE;vl}LVS zS=x=wQy)_3eV>%!aV-M_q^g?}%ks2Z=we$zEkOt-X!;W3t{8to2QjOm6X&?V(RDAX zy6Bj~0dp}h$A3EVnxrYpu|>{_7g>h!gpHcg)h?WxqZx>U@Va}(t?~%zc=y)VC75xu zhid-rXwF~9=%3hGspKV9uNJ5>RO(S-v_c@pjKRtFDX zhl!l!q%4E`vA9}^w4bCo$r00|YwF&#bbA7FD}#=hx-A>!(-$Y&GWGTwM3>YD5sj{| z`e5D30Kxhm9Gl?Wcf~2t@<8c3m>i&JHG*S)jVT?Q-yQeD3^7afMsTr2JbXJzjvmGV zoPB}071g@WfLvY3eGxe=H7Dy8FY#Iegi#vWX%FHhQvW~JYo&A^P-^qBlx1B}!l{C- zBzeXGO~Rwquj_fKd#m$U!|<_#DOx^FN!zk%_!miAz_W zwiV|oN^5Bnlj!u?gMECQKqZL+A^c)ilGiKQ8%%7+yF32_XS*?CTf0D@;is@Yx!lFd z7#p=@tf`J#;=#fsiM8;ywq$WcLSE;OZj}P}o%b+Gv_5-LZbtsCN5;*aGJ!PT$GLx? zO%@r=cjh3jM9VS^NTfW?vTL;@)Z!ZAqidBU)Z*&pqia>mi?Le5{MMbG*jg%3X$kW+ zGiq5~&W+acesnEHik;LF0)Q1cEOiMowrDD2Lk;=|;W9i=UHY{IvKd_sVq#A3i{q1hpEp}>GosAfc ztq*QV(;XeFM(1pa?TIpgXl#I` zE%=8~=74emcCDx-hXUKmoeA2DZ>9^BgL)@K4`Eo7Bg2|nnSBc=YrYVzN$0gCM3y%5 z@hqed%Kj`wk=o-$8Kl`I^wDlUj`HJK2+czESXxMeaMqv>`Kvvsnxb_lJu$&T`ebGc zWifjZ7ooL<=niUTmsp5&_C74ccZr4cN16X|EF`wF9OqwSyJj8JeW-<8QuE8vnsF9# zsTs)9lF|JS zNT4eWIvzu?%Ql-n<0GcsJ$+Hww$KHNhj57(|10*OF+Ujkx5?wSlhGTL#-(6oda*Z> z30{Se21|F$#PMNVwy%xS*2$&qYfFoz_~`b(njCGP40QV{Ye{4VW=rpnq#fUx1|qq$ z8=&$Cd5p%Y0w!<&zGzQO6!&W+B1h{^?)){ntq37FUzMfgpW|4{y_gEopQ8z2N2XS> z?efZ_{~&aDuuY@ji!7*mV_68aeNGCZmH3=1`!1)utBre%WU&pd??(7TFH11Cnri zPcb^8&4CTqOmasY?Y&$^#q=IbbnkzsE;)9BOK02XIO1x9jY4#!fR$Y{LB%d3a8jJk zs3MTHrRj`H11?;PB}hY%W%pdugB_<=+o8B@_?hfk6vo399_vLRIMq`c9jm&~1x{U- z5~)!Xv{AAEu}^E&F@H*SA_GRZflSzAPQ>uXs^KH zj%X$AMdZ769xRzh3phB58DY|66G3>VCTE^tjE?M_C5h@>w?6o=2UvEln+DnPETfJQ z&IJQ%iVRH67@G2&E6|Aj8nGWlL};3QD!VUQ6~?DsSsgmfllKltb+;AewCD%|}>jgKD+}hM^p5m6SjSOH>2c>Wtk*;dchuYjtC<)or+Ud>{vkdf{Q9dj}64 zth*GpLVh5yW97{X(NlFSEz0^qwaM`hkNyxewj6(aNk)>scmw+~u-0w9up8Xr>wcg~ zDnyA1k|%8jsy#ilC;-_WMe;m0Y$4HY-2ZAJk*`5M{)&RN2gOeSIPm9E&<~t!=0tai zSnc=^;S2YPSoc`A`S)XfL-Jjk8A*1EUX68@v>O$AXtC>HY%*(IIG@s0#}Wmw*J?mw;XZy^}0 ztcz~lQgOU2cpAA96j!R-We7h=hCLkWi!ADXP3F}^cHrVh{o9cx#9mZa3@0B2lmecc zE~Ps+DG|v&of{I%M3M_h9<@FEY@#*7AN<7Xd=L#vYHDRHf1Omj9>>l^dkx-6|oIA6TlYSLFp`yBBFSv6MUn{8n zwbXtP#+R`{S{a;^_f)N+S;@QP*3im$xzy@$3B}V#bkE#u~Lwr)Tdt_eKmNciW+I?7bA%y&J>4WUT zT6D&<0^O3ni))x1JgD|O{@=iV+)rjYx{IvAm)ca#Mn@EmRQgfPW<)Te8TWR;;jpZY z97M~1k06<_Cs?75^lcF5?_}hbJ>tTEGgb6@1_hJRz&IptEzQ?WbO62@%8cfpG643;=tyQ+EM=CXYs$pBJMkXkP)Hv+07WRKjn(NR3Mbr z5Wb4`uO@F88fR!OtM0P1k*Yx3?ov54pW}FeT?EWM8^}0_wmfUWz_S9ZJSAw*!y^|A zlr=wn0NLK_-+~X}NbhWeaDSGx$HR2b|rjYoGN5(!248%?DsqF zt%G)E^G1)`B&Tk20=qlp6;65WgtL_{UL+QsT;tzGrajP{v`M9y41?%>ws;avQ>6On zEJP!k^Ij>ecc!B#a%J1N(`Dm0ViAKimJ3_)S1%=tSuzFGxPX4s6v+)=gMOi?j&2%Z zVJ~iya}C8E^t8reeFc-`?`mC0;-YsUmqCciv@~ATdH_{uWW`6NY2&9o=UMeEf8`32 z9^&?ONZds%WQ6-ujKsX9ZMSzPDe(*QV(@57a$@1MJG+Sd99`G;oYG6bJF@DBWW*!-;W?trV`0!x+<2b+<>A2&OpKT zyXd!Ki*l6j7i_+g++xh%H-Wn6@Bn0fAUg#fzXx%v6d60=v!7EmcH^&+l|o~atGlH3 zTUSRL2b{Zf*{Pw?#$$SQ?mD(?H<}G1b#IhBj9B11H!=?Y9s*J{@0H}T zx3@kk^AINfw+)X-c*1>{KB%=jdbjgLbjG@ekWU-@kedWC?|#HRg#Z3b|Mw8KsXhOP z+%D~*azFi{EI!s9*Ef6!3-sZ`ZkJ9&)!5sm-}*pmwwsdm97#NPzz-&%Om+QR-XC!< zes;?HBSylutRLXNjmMF69|K=?f5hX+#TYv5oC9(Zu?`-HGqE=QGoVSMCO4U-4tSc*>h<*OW{M(9m&-*cc!#Drd#jUT- zU$MCLz4^=Mx4yq3uzS)o052=P@^28!?0)e3X#D8kT;Y*Zex;wkQQUf=xb>}tt-qV! z>wP`Y@fy)}WtbxlKxU7N88u$neIPf&y#q|{pO-;dyurCrSuMQT(0O}(f)(4w>4OPC{ z`o>5&MBGL^1i8|OCooqBeZhJ>>p56iU`#Ql8qW_`)WhJ3g|N26sZUYD<}Fw!A5qAEbstsOTYoC z(7YVnS{U7?dS68lNU3I0h2#4nk?VuxF(6ew%{zp?E8B?ayzVur3w+Dbu^1dxL3!&d zeYN2bySgEWp{KaYsw{qqHWo9E(?5C~mw~OXJfxjw+C<-ajFN63Dow~&-W22@H8kO2 zmO?<{s~l%3txr=FPr&zHxO`1B@*y+&ff_G`!liGE8r9;3^z(_n(uPpDqM;7+BcD zvS8E3U?sp9@0fa}i~{W4^$qoSjCIRNkWPx?q6OWNcz*?N4oyl|^%G()tIbE@=YY&Wd_I$ezYffoILb8y8;hFUD}v zoFf=ChN@_DQ02Hvs|A@BsZx(Tu0npBDqG(@*YW3UKf({x^UnA7hVlOp{@=oXI_?1? zm6qfB^7{IQFkPmhf?{k4ZfI!Qf*xKXCm)>et751{EXGqO^IUUy%ycby&3e4Iw-D!9 zo4?uHyAtUxq>V@q9q8@NLHgJey}i4TrhluqcR$jHk#-_&J=oiO2I-%WnmFvc|J%L2 z`ACz#gKa(1W~6+7$a$o@kS_f$>Laav3iXlx{@?HfOQdf-jruqM{Q-^x@{#`aFzO?{ z0w)bMNb8X9LK?q1>>X1H*^Z}%Q z`XTBg?L5}o+l4gkN4>p0NPmvhgBuQ)c0vzGUqf1o^tKbw1JaZiaiW6s+ei;0E&nm< zBmEuH9;E;J66)iI$+RxiNBS_*Qlvjb+K6=Y%czeu^*5-GblmHxk2HX^2k9~Vw9I2d z4@e7<2JvgfQl!aeP#+`kuJusn!Av`_j}YwdJ%Tv!2_a7aktQPqz~h! z#Fa?@6X|B8D|=u+NIQ|9M#>M4r{%|S_brBFv&WH_mSXOKJrK?xTRsGvzz;nPV_}NC z`ED$-(^=l%+gk)WBi*woJ!gq~;3jjMaox~sCQKVUnrPNvga4dwKqdn8hww1|J%AUo zzW%VS2k@T(o7+IRKizY`^ZG%_u1W_)lCm5C_zu|OgGg<>^M0a@9DrWJ|Gxu&J{FI> z5C7fxe;D>#k2G30DC-i$_S4Yr??BTRf8u$9dFzwCy<1tqlFu2DJcus_{wd%;8HaDO z@pZtv;WHcJ@Rc_H0pQmFUlxbI)y5wH{)@o#o*27*SGKL6mjM5lgT1}?#o;&G`164O z1-_XCUyauPtBp?s{5SB;uDJTgZTv*wKLcN#7l)s2CN-UZ%^7%l7!a3;f?O$GYO`t03b;n_kStr!n`gjMXpW&=RR)gEX^1122l#T}8)Na#YDvI4dx1X$e31A>>7ECji_>#H4>G=!NeGAfa7NmO?q?-ok-ZS8H8hkM9 zmp_*^a9IPFHE>x2mo;!%1D7>$Sp%0fa9IPFHE>x2A5#O?drqzQnsWQc58t)Qk5%To zRr%o-UVgX{!v_zSm!Ctosx()Zt@o+s=`xNqC1bf7WHI zAByUK@7sR=dIHNrJ>Kd^;y7>7<#jr3)af>z?$PN!ogUO_hfce6dRnJFI>k$u<;SB_ zpH2&PTBOrbohFF+kwxo0y*FHc{WZS)73(AQ;fQZaVZpS5!bwvivN+{R4KR4{s|G?1 zUu%})FAwzlXiCwKv&Po=Z*5IywQ(g@QS>|>RQz8btSFK!t4^q(=tFLc{tcfZ_{-!3 zLS0n!NAoTEUzaMxeze%vN6|0BBa4+hJ69;Gr}`oWY!v#XSecGjP)?(_-+}+W6y-Ei zih*(_rQFQKVLrgbnQ}mulKEs|r&GL$#FZp)<eMz+ndUcHm?;Y$^D)pS z&60&2Gn+k|Eek$#2r<{lLcVzu3v*Ro@Qab zER>qxVqt+StTgXpVd3CeNY+9Hs5%1H{~9E(EN_oJP#e?&U~!nZF}u)u3jO?J@s@h1-Xe0rQY~ zFUi*Aegerplh_Q44wKh(lGdAzNRAm! zUPTfqxetLZbuBShr4RTc+Dn%5B%4GG0Wyv;$^3N|Kj&2f>LQzZj89W%{!kK6 zqV+l3*BRb4&^WRO5pB85gEj-zRQ51MU1J!(LhMX3egYA-yFH2ObhpIdI)}ntb~k9| zvb+7?<8EeO?v`>RX3WB5)AJ|TUwUn&k(@e^#%q}010s2VESTmQSZ(q^S@4)YBPK1C z4wsX@*+?Fg#>&3*Ek<&>%;v+HllB4ae3GPY7oEkWrp1&^VC6{};hBLHwNpmQDh6t& z_+-{JoWDgxNEzkI07=RTu*gpt<9Qqfa~>gM2QzP&>sc5l3#R$6AWIo93m)@X*1tkY zXpSXWzAOwg&$Do)Ecnc8SZ9JPj74CY51f;C%%oi9Vm^g6Jt>ns?7c}jQVKGdH%w|R zWpeuOi9k(m50XuW73bH9DReypND9U9q)hdU1q4PVJ;nSi7ZS+M|wb*Y*OlElf&dpC4Q-^klelsKu*d{o@Bt%H^5?2 zN+nCfY$Vmql8b5nnzXmb0reQp8;DxwdYQmN$zX+NIbh~^LT;6H4D)qT-6ji3<}hMb zO4g?NC=07(0o=BcZnY~Di8~WpFvpSPuZ@I?@R2u9Aa(2=0EEW_633A&TC}Tuf3t_b zP<0%+LY4P5dpIFju}85fCuX1FQ_GA2U`$1_t^u{7iTfha!j zW<$>Ql^F|(Jtn1hwLjCGv51vB2YDoV9aKqcNj6^qnR_6m-kbdDaLQ~L@|F;!j=h-} zekJwZ1gZBXNWC{f>WAZ{emGw0hvTLGa=g@E)lwhSQh&peI`fF$G5)iZ6E`N*8jdH^ zgDAP$pK8uLrqOq6bOue{@vT9M;81f$HI;Bsm!IMC#ht{yHzof}@5SZqGJrewreT@y z`4lTO>ApXtN37{eH9~vzh=pV%a<%Vj&X~XkBeL(Vb~Gr*C^FI-!A7#X+e6mQ_CUrS zj?q23xBHtjRuH{Sm!E3Rpl>+t%gmJIrD_Q8*UDI#v4(B64jv(~>oP8qxJ_3+(409* zkLDb$4C?AZt*e9Rda_CO$z~4fC!6#iIGN1T72JO#_Z7)E4^`FWZAMZrZ-7SWWku8` zq~)&AdfBJ-aI3ZfZK}6wHI!yZHQcV%uu`dEt(F|7D%DUf%g*+NN(<|?7S?4h(jyhy zuWH#Z_z14k&!ynkq|(r*=>r#e50wYYg|}Y-&pKE4*V3pE8xe zx}LEGnKDWVveLsa?wB%72~wKzN7kLH)lg@6e@@={a%EYq`N^Bhz|*mJ7*=*3>Ern$ z;`rrH()=<%sVm*0g`h!PsTGF}kb60sAC-K5h{Au;mbHewrEsH0D`qtwY1pGCE5lyr zk&ZQL0cTzWA9xjc4Nb1sytdlBzDizFOG68^mK3j{3p6hydm}4*lC3pqbUo{l+V+fB z+SWpNuC^)~OEBI^MmF@Mm$$!0Yh2#G;Zp)wX952K)m~oSGKyVZ-v0l2dF#F#i-MG- zA`EoOkmR{=49{>L!g|Ky+u`{xzMY<2O!6+nUJ0)Q)kmy^$;e&_|AOwOSHjHOD`APS zR>Ca&y({55)U{T^%-btrHv)yV5@z0B31<_5`RrGqj$aA?j`gjT@J%GsE8&>>)6-Rb zYbAUI$Wq43mGDf@8vS6AoVH!x8lTM9#%pO_m>Lk$oHJ>_s-??nf`O$=Y6Ivk+ZmKZH`oD5%x3D#^P8!My%+=nt zHFGnIljMAp$^A zqX1aUWQV61jty3EU3PdXi*k33<%#zLiqL4GOTDQSx-lt7PvhPO7^x#<;R7%YT0pY7 zRDP2xKs_nNHt40soZ5LobWZOAOUR6c4os%?D z6;<0gNfT62wVjhRLltG)IY~vTB-_qOTB1s5`zr`s4pX&VqT7DVZksDq*|wRKZE}ef1?c;%Vjz2i;rOmq zD$TAi98X!LmEK=+fWBvy*LjnupQo)d`nHvw539Zn(tR6a>*qTlS0fm94OJspV+?tR zqqNGE{Py*VP~PS)bissW{F4R1T>Cu&{@DU_Zj~1P1FPJHGEDe6t9)v!wD1nA zd>Uook6PvKt=aUTA362ByRA8!9(3F)W2P=fTu#w7FJ!jaNENkuzPjvChc2n*`mCR65P}sfxG!wTx^d7xyM)J;FA+9V6QS z;>o60F0jrzQZEn19X$^1lL*{A75@XtX>v?H4)5Q@5`6uQYqt(0rHJlBoE-)G7zOU!-8gR=HFeSBW(k3za$EXc-%< z?+h#4WC37(av@P_)hP1bMzL?U40@&adP;D!L$A@`3`KVxpr^n@?`0Rs}D9^TSzjiX(cGhaDUw$heiQcpU>7Tna@gs`2!S%ve zhqb$a1f$8`bfmK*uV1OCUe?5py-BzMNcGipA03ic(UqyYE;>|f6{!u zWi^V<541YJhPxB?cj(*H`I$lN%2)Fhsr;2CvW~s0!8}i%C7v0tI$M(`k9p5r!^-Ge zo;>D#Q+IWxF>E6Fy(anHs`=ey_k_<89WE(;ouT;|Bd$Tyju(=bT%pM1Z$xG5-E3VR z-oJ1q^H6-AJiLEluF~}K+!|f!*Sd+3=;VvB1N;{1C7aw%``%|eU(Y0``FgsLzgLxQ zy@#6$R9j!4uG$Kyw)PJo3L4In*0%pj4glB9dD7bU&!Q+?Hs{IM_zk^P-rt-juc>@v zh{7Li&XZf`Z;Z7TsKav@tG;Qis&gB)Os~Xr>-UEE{}$v%w_**VqF@q z*LTM7n`u%{k5+xDVJ+O)fp1?yG7y_GSnsID6slvJ7OTjy zuUQ$yW;uY|kE@Pu9;rI|S>4giSE}-!=8>4gK50#1BQb@2(yiR6bod||%T_&s9cmsf zmyFx=^s#QZj5zn{;>zK2cG#{JRy15jlNMe1ndaPC<5X{cp%P>GQ4aX0hVk?Rmq+mH zLz6oBgzWlq-Sv%{*s(VsNAOCkcV;WC))>AnDr=pLt0%`OQu!NdWF323P(!)Hy5Y(l zRw{Q`H(3=+N0wu8s=K~!B!b3zU8pfe{F%ZmOJ1YdMrVcB$yU1evSG@*SLVqJV9WBU zQfK?}+>N?Ysg|B*zcOm}zJH-}wJp3p}Zi>F1x4(IKG4V6>VC-$qJ)oQA8!z}7 zzK$Xl$(YBZhT*-`Y|#ki4H)_j5X4*Qz2A??`cXj zP6lzeruNIG0p2?j7d%G64#y2s99NEl9gfo{C8fjvg{_U(L9E6Yy^F$(l)T>XDJFU< z0k7c_>$9ni`!KmD8TUei`fAEgQJq&)67Q7!JR`~Fn}wj6%U4t27yrBW=EdKSXWt-n z4jw%Sw|o2#Zx9+JZxCW--ynH|5VM!uAoN)P)4xw=DNCNUTseyT?KiSmJs|M9<=qx# zdCX(}cFVgh%C69X%yX-*WL-`CD|^mI)tJ zZ58=Q!HLU#R@Nv`L2X~NYP_h3w4AuIWKdfmgBq7mGN$=uOv7d?>pj*E*z=2L^)l5# zD+XoBDYkTo>KbB@nqa-hST8%-a(?-HdMTq@Hjq7}4J9<-mRE|iC$5vKDav|-^@ix? z*JVAyVvZKB#$(>BT7N|cRBCFdwLAbS4_uNA9t!9n-#N5|k4I^&5I%9a~df>7}mpzbQ*^O(bljfeL?I0lbZzOv4<0AR=9>8iLA zu0}o?x#a0iwoH7X&ClH!ZUScb4S?|c$8cmg#-(QsbfxE|XE-ubJamGw#=wEs0)%Lw zvsgkTxdB~_HNHr4xYDm3$ig)P=M0=Z&;ejFLSXtZcxH;s41%{6IIhcer7Myvau;Wj zeNoQm7vz#2ci>sL#aT`)yar590Q4!@;25H)bP!ZZ2ic*tu914RkmbXHnL)QO4q|$)Fn)E8}&!Buei~8E#7%9!nV+AF2%8_!qB? zta$P`&3WT-F?z+2_BEQ(&T;gt5&eHtXVAZ?hGTIS_3w`FQe1a&$78&1GGYgTmXkRU zz2<#}S-{wQiRoWe9Mq799R zCB7mzPPmbYX61_~PT)OCYZ5{#O^%Kwk0JiYw6jFtOpld5K{M_|oqMdYqLaqCVrD#> z7&UN=>A8BQP&!NSTzfqJF?ALzy`C06w7b+>oOUh^a`E+J`_E%b7j_&7_Kv@%55l*D zD_{IOj1A?r^#v6u8S**H1(@IQ5u=P$r}YawSDr(dW}!%}7URvqB6qvUIVsE&BDF;b zlt(#dWQ#!n&c*jhF<}i0qgb#(Q$Gf1nwcfi9uv3@>V$whAe>7+Q}Gg!Cr3g}lj~~N zPp+=0n7nz`)n!*tpHx>{AK5&qx;`>_O5x<%`iix^OAsdOUXbV~|V^Z)pt1 z6I!a0S__3MD}z-~8w8f5$YMFA9?)90XmPP00PH8CIn=0a01!K;IF*lq%B`PMxx5aM z`VO>SAUsPW!rf=OQ8-qMIfWR6$)NnD7-2Y*990}9)BJs0>3nR-HPAd0Qx@h~k^h(w zZwjw-ym#ZqjbhLy429z{ISyZh=q}?un8ZKgIOqpOEBpPQkA!?gbqGU&PbHqxta35q zV~7P96ziT~m)jvS%z45Li>bvTH7p!Z-Ry}XH6+Hb;6km%GYeBwju$X?WNC!s|7K0{>H0M>B!n;%yU>u#did+uo5+Rrz z7HMnY8seHlkxL0ov{&sM?G=uF;)ZKQj=9k}CQA%+j>#6DlOo%EOn6&_$>Pw6NZl@m zw&1%(WIl_gT8_lV4;GRZ9w;-pADBGMybG?=B% z6(f#7>ZKwBF!P@&a4O_-O%~&sEKwzJ%V+TyO6Vn@GPBGqv~)z|nYGZ`z~BB4oARQm z@$YF_vLhm$3XZl-Ma8r|QMEk;iKVx>YbgSRkW$=e)=-K!#qjN7>UQC2L0|TWEc7bc z^_-Zld(rjuKe53nWIb7UnV^?BGF>VL$uv{-9;%W>pRJ5>vV4_@lj!qD{!hdaf3~VS zhrlHdx#^x4{z2VS`qbYUB|nC54u)^O;iL0H2_qb0C`|OEHtj15#Sl12`pFX_?Idlz zxX|1#(tay)c8YsE`jDv`HJjGQM1nTUZ$MC$#*<1EM)?(Jg41u^Uq z;hD$$NYf`g?N-kFsPO#uBT3R+cb*Xg%_<>WIeWBZ)69Q@|4tU^n`u~CaC_i$R-yH| zf(GM!-0~ig>e~FOaJW)^VrHQj48I?ZE^HTr+r?;bgXOl1yiR5uW<-p+Ta1+o82OAC z-YG_7a^5b6?;wTBr|qB~rcW_n6{)`$zKtS(yT~|<@X+$87`Ta=NOjKg3h!sdl{I2) zM2tEq3b%_rF$~hticZyLFwv&d~y;KaD0*km#T(w<{+0K>*{wYDS_xgE zp^7MHrO5t{7#bF%Rb z9{gPz9tM5V#tGH z45-2)8*Da`YHW*`0oqtL+buRynh#;KU9yqV#Il*og%K7u&xqW8++qxVRAhWv4A>_! z!(!ZFkq3Dp09uTQi6osT1{@JXuy)zQION?Z(!*lF#wa9X2MLBxrra4Y-$Nqr%TX4E z*Ip3bH%0C#aH6$7w9H(ew5 zV2x+K`6VG9#X6-(+vppfD);^`=n8WCyFN;5N8Ul6Ik1MrOStP+Dao)lgv$(mX8E-xPW8h$|l9 zhzg8xe9;qId`t{^Q;c{=OnQVnN)Ttq6upm$(T|8>2gIPT$b3}z9uY$ii-EBD$Hb%q zw3{^Kk3{o1C&X0;#25*O%h~z#6T*B%4B8<^9uQLxhzW-o!_!_BBa4L>3*Yev#OwoN z3Osm;$db@Zvses2AQqhw(++dtZ0Bch66qI2?g26XjF@~_%w8H-%vvkP!ii-2SRC8= zj1yw?0Ws{b7yxTM63xfT1q-s}8g9!qGe#~zwOk9%P+BFI%Fndqy6%jabXd%^H#C3N@x(3_TzQN5shAiV3Gg{t3?DR~!+ZC#Vk})?81p1+X7;L`*zF zvl%ltB8LA~r0-z;k?jn-*ogs>e^QKx8c)y`M;{R*pAh39F&G^Y`A4X`QK*^sTVdYM zYJiUXvG8Fee5?Syy6Tufy1w*4-PuW8QR^>06loJ z7=bu{Sa{@QiCA>SVNp1j_Ul_M^21`{VKHN_$X+6b?GR%T5j14747?Lr`EG)z{`cEfL z;LHf)OSX{FgB6V%am4%ci()uy-YMkK z(C@j}_C6u9AjF#+McO8YdKR95NVkjYAmK@I1!ciDuo|${VqPKE4@am!ZYv!t#nlQu zpUGjlGmY~td1fU6O63)^}X%iF9+7%OjdWfh1_=lH>51eRD> zWh~X1w8a8DW5h^z1e#nWoa1qZ2F-G12$|)6^swwt5(3u|C}MN}IOydj9P&9Q z3fA>;YZ}9i7^i7)|@F~i1V6khCY}l z!t;>!nEazL`55@H$HY+QSac6xC$Os%Cy!t~hwu))_~2)ci2U1xuQ)D~u|k-igMwCz zk*)#GDu)>I2%QVxSKP+9j*RD4G0HUnBlQS(I{2P&n;2cp%$2tZ57ca8>KTTh^oVe; z6y{1X3~M!=|F*Jbb(rQnBYnq(*dd2Sp59>o!ALl<=#&Qt2Ovw~-7q$Jw8p1Ixs0D- z^ZJi*v}Win_y59%-4N5TH@ENFz+q+<_66x?E%#4YYvO2gxU*mgrjP=01)M01NQRXk zHVBAI8S})L)nXJ;Eg}P_R)euV!_KHcqQV@;Ao!wSVv6150B z$8i{~!`mI0g5cKTmC6vUVsKwmce!1xDYn#FGM7#NM{1 zbKBbRD*BV@lh);v2k5`~hq12M!+67}XxOl!L0@tozRYkrCB#ExY8X$ydzu@ zoaA6qyyjXgrLh=+m2ky@Gs{&7`)T{IgOEC^YrqTqiQsF=?8w3YZ^8LC*!4*TP81TsEBO98!JPT z%R{7KzR+UizvY&jZZYht6*c8exO{+jTpE=P5xo1&z$^DEj8IKO6JC=U+W_DjjjFna z^03CT)f;YH;k$myO#Q%2-=q!UNPTdge7|CC1&ZZO6*cp4)nwAu(|wbweUqxnLtE-A zYWSwiNN7@|ep79I<)kp*ITr#Fa#Y+osj;acEZ>Yd370jfGYe5sS01Xd(0uXUq^gRl zn6m0e$S5mosH!Rp8)b`cD#KgsD$96FNf~+y?S^Xa4iY6hxIR)PiIA49FAoKcGQLQz zz9CvOSYHWXT{*^x+(;-v6_KX0iiWz#hI$F-YXK`Zf^9=xB^wU~RqN>goxvufIvDoH z4tH5qQxLBc4UwO$zp1G<98_YWl~DP{pq2-(zq?ZlFx4o->nMZzDo#aAJt-0Dkyv+| z*MC4Sn^&T5!rKDN_!>x~Y@<}TEhjTqPmk4?(uF07WM9@;UOB@E@~x*O!4O}cYLrFl zBcWjBRHz-VafF7cAPiun(Nd=FWnFD3Y&15(USRC<1%A}XGHG)YAbMU zO}$%_uOsFA7>(!)FJ;ks^QjTN*rVTfXxFGIudCuaBcU;LV^MiUa5-N-x;V7B9*$fO z)ku$O3g4<)j;XIKbZRMeu{gGSRd_w6d|#x&9J$8`W?%#~FLYNn7E;rv;x}!&WT+8U z5GXWXzc{f{QB08pmZA(vLeOe0j+ z(5OdlfvP)SB@5V2Y+pkL-zf>s*6UfN-mT%0gBl$oDJp2|gPU$u@7J~E095uv)`+#0 zkTMUWHnfaFEv~QP>tyLTQF}sVtHr3QEP+6Yw_h@9me*OWvMI$kO0P|XDsG~}6};@GzrIrPfoaXJkJTHI@R_D?z`wkqq_(oOytc_e zypgaQY55=koM$Kx-H8Z>uPONy}kiYJcdDq@HWq7jp)>(h9+%W2w{j3l?!cY zV0J5Md#m`RjH4-t*MQcmd?nmk=0b_0DUHZ()CBQ_2YhWYwp`zU_r2EE<8_|%V+#um z|BVZmmECl!|CZwUrAA0f%dpKpDcKn=V^tAizu89w1p}eD2}5PQLKX8@uzcBF1|F0r z&1e;J^~J&Z>Trz#LnyD4VyzE`HwA`qpSeLB#fI|Du`jmGjOyje9^XNwS`fVTwsKHH5!7y!hZU-=1G z2W*9gs0JLjJp>m-s;aE`rl6LhSJDcUGGK+&2W#5BKosDMkQ*DuFdp-0iaX&XfoQlC)7GR)*s0;w&#VW|^kfGbt9ih?acLjk{3 zOT6*RCbSJffgp|gkg;rjiNCCP@r{1lJfk{b`?R0UoSQ0Zxt3dH8KagKFOrt*L&|OG zMyd&0@mR$ui|4|F3bXVTlD4DFpN>{u4<%`T_O-(D{Gcuq^N5;TFfoM%QO8;i59SanP3ciL zlzY@-!K{J@mduFecLwdSfu8Egm8A1!Ud?VH+zdjPM$1}qV#5lM3kIumVXhED(Tzx` zyedfiO?Z2;QBf1DxbwzHU7ZvQ-t{3L?6Yty&)dC;B=`{*?g>gV9>5(m{P^TIZd^m! z7ySspop|a{SEeS^iBH32#Te8y4Y%GCVWAcy<1mIA`Fj+E^6|s_#2v<9!+Oty1mf!+ z{IlI{6fEYm3wa0L)?mFi!a{u=nZD$IvLE>O`hj=!13%smysIC0fBO6-=pA@V@M{go zBz*dk|96rm`x5b77(Qpf4xi z;^DC%D&=EbGXYHe74?l4{8mLLbhSGqJe~(j4Vpgj@r~OwJn`uYd^a=OP5j02UJXxt zXv$Xs_g&ho!KX9&4BYRo?a(4Qs`(`T#_1&qx2U4&>zY3C$DP0Lhn^2prTmFcvq+cl zC|@f#O4D0+c|E8RGXVEMeoun@b2WY9^8^-ZxP9t{GCzFseu7I&_XVdtD?GeDUG;>~G@3WOww_?ic!je@F94{6Wcc8va$@KZ=j_i}sf^JnjaLoCjFZz9e`(os?vft{5{&~PNoVkYe z{uGP;r;^@dBt8S+KQ(-i9+xq?!dv7+iPk9q!jb&^P17eno+A?k9GArB+z!|9A|02k z@Ha`qHPe`+NYf`iOmAsF^g&IZ_~?&!z|({It}ww*`IZmLxlb#5vTpk>%_s3OB|9`c z@$o7TXn5j7+CDGgF&)xbK9q;;CO*>kS->f0Vm#qP)k&ZDJIq%!Jn={8uWPtzIg9F5}%E*QdTTmsYRK^3RI8yz)2XzO2Ma9FCNg zfqfiH97-=-b>sY!#n;DU(YPh9J#I4f))6WQHB`)=T~-yUD6g-w$!1Cx zYNrei;f6{ZMTw!3>R`h8XIW4lghN&-Ud*Ph3N{i7#xX#bQQFXGcgWW9G^0w^ijE9XWo6a4^rou9 zj^zMFwV@~~Lk(p$<@J@?8ZOZ`El!nI<$>3XSxqI<2)9R z6CJyLY|L@|N+oFn3ndyD^lKjn#w$ynOIXJ|(X$a))FmxRV~IbiFDqLg3Td^*oFXWn zlIIcfiXetE?($I|$J3B7R&CnqFFjd^KeVaB@p!x`$nm)|PD5yYnY>hk43B;ik@kUP zaC2=q4k>%w*ToKb!6%NitPDZHigk<@R@{z@6`{MD;#;7mqQ}HBr`@vY>2#!oL-an5 zKFi7~HkUKrmDk}&FHS7$+{`*IR#&>>Nc8!oQ4rd)0oT=#A_mE{#>z<>YcvY#ab2?j zzZxsR70yPu@0J+Q`bceE}qLs89Nuo#y`s|`tBgUg;qLFJZuG@(*l&y*Mhfe?=n zV+&=#HQ^|i6?Cq#E^HJ?4=6ykpt=D?W?=V50j^e{$jJ@V4Kgqm1Z(tkR#ORXR!Q+t z^O~w*<x*GH;BSzeEec2=>rzN*2>m#<&XgM3X$8?*Az z;Q!Ux8S*+2L{WGZ77!#2m<@!apelF-NdS)`B&Z@@fMDPckP-|AjX{7yVEQB&Fh~rl z37P;740!?)&gs41c6$372q)Q|nLGFN&d$#6+nK)IIpCh+e@rJN(va2<%Pld|{kj3O z4ry4!m$dnmHa)>T=r5!mp4h1Y_br&tE79t2rykDREZjBcwGPj-wR-0H;C|hGLw{Z6 zKecu!`#;b4a1OUS-DWLO>wi;LJ)=GB|04CU7qc^5N4GP2J>NPP6{ z|I^f8O27SdfLVXmdTXlwg=}tDzF)WUfn@jZ7PbF8Q(>#`QwLvkVT1DZ-Ak3V`l;S# zX~6i0)nm^9JXf~W-%C9_`AhuQMgKg<+3>L|88C^sSl<>s^IEXp2eO9Re(zJy@$aVT zp8Q4Qt`;@^2Nil3(KBBQ|0?@m|te6T9zp+%M@mn z$4!;TS+H8+9{)(t2mA7RK0ZI=u~P^3UK{k}h@5-bW_6ytpQYJ6+GS`;WiKdEEawXw z>t=Jy#e|oK^LrV;Po_J|H|h6t+|&I28-D*W-L)VX$0f<*IJSb{qxe0S-^2L*W`5sH zced1&xt1U;#*)CCnoUk8tK^lezde>Q`{5pQvsYAP50Y+~P&oSLSp%|j1GT|{+JNkV z?7quMtkm8!N|MM8ilpK{{j*65dpIbmkm_{Fpd3PJj+W#uFAtNz^(PbO;jk+G#e}Qk z%Y#YY8NhU9u{7Ny=ddCA6gh;|>7F<=4_s|qMAeSM!w%?wmxI}}!Tsb=b}INoIi~5| z>Gx_cI(I93p_sTl%UA1d_tgzd7!PqZ&8;t6RO;;I0+Z?=C#a8FRF$Y1 zCiPR3x|S`CzF970yP|u`&$6?)uVQ{p}_3W!OHm&=<@%zEs3M*Tw?L&ju@%TjLyAbwOe0o;7ptgb9M%2-0TRZK2 z2&M)=af!dvSbjqPyWaWE@89Tck!!sz!@Td`_jc{Hw@7Nu2h{QkoWWaq+XSLN1_9*b zM4aCaBWhtN>N8QV2hcvx*W1=T3pQ42t)=bk?S!#~_i-O`om&R-UL1ufThYfB^s%Ia zx=c`8L>~u{cuhFH!YmE3!_|1t z3Wtp2i2YDtdguip0P$gYB9qsTlIl5Mac{LyeLCvj^VV!2S^v0rW1g>8xDb}N_dn=s z_MYV?HG5mlvW=J2RS_pkebg=9@lD>#=>JY$*8xP8z#B?x#srXQ5Ee0irP=gxD z2y6CADDOV>MMR`z$?GtwLzeHq)t0Y!Rrft@M%auFb@>5_|4v`PnE%u{eSSptp?eDH zUUhTlPIl5dKjQBP(Xo7|NjJLqT58VM$@!NSVEy=^-gnsgpUm<7PwcTlpZv34(M}p@ zDPY~DjlXBuSL@}IPI*z@mv=fg5OMvNm5QrY8bSFZ_+Yk`re$fMB<#jdV>F;`V@h4{`SG&3=`Z>&h1Bvp}CE^u=E6 zd|GDYm!oJ-Yu-cS+F6!9GqOKhOcP@BV`kTkiQ6 zpS!sDuP*cYR$uL1tyo|atp#@E6~f=u-ZZ}OW1BMPYnFLyx-#->HvZNz;Xk{;+w3Ge z9=Oo-*sbGa<=Z@VY0^EZKh2Xc^8LJRE4#y$-gZR6n}83kj3Li1&0`yjx+&xG*i%J` zN}oK|T$IK}W(T=GIS+}Qe7G---JWf@CU-&iN@K0r_awdkDREnVO*Q?B`LVf`r81*m zzOw04cBo&BG9;CK)Gsd~rZc}&u#?wXFQl+OIiq4venS13hS5_x5x1nUjX6V$v@LArWHpt`4!sw)?mI0e(-*-8k z#w+_YX6$w);>T+-ARE`V_^W#PYPB7A63d~z*fB`2(9X|SH{yLXYxRR&Yz03%-SkB6 z06G5LNiv-FYE`s5%C!dX(n;1fa6rU8{W>WtJIVSFax0&lWVZ~;Q;JVw7nz~FccSTc zgI-dV*Lt({!!udR2&Wphsdv-ELodk6ww}P{Df4@>eZyZ?exJowj!02{lGXIf5f|*r zx5t<ruY>piluRi+_(a2+IVT)RiAx&;Q2Y>|SWeZ8wu01YQR-njH`L~O zoYm&rH!vffx^0;2pQylcF)kR;_f^(wN}`g~%zRV&x?T}fxKd26t5o*jlq_Z58|;NC zy=*fuy6nu9^nr7TjV90n;azmLxpm^}K+;^@Wdi*<$KTb?P822eXpQBW*xF9W8gE;( z_X>ncmngr{m`@SIUuVgale_HzYpqQo=SDYDGr47?pDb(l(RvAuGhb&VlRdG$#M3|v zb-Q*NMDSs&J;Jt3?v|u+MKmHUz8b#tfKMzv@5!uXa

O5q5EM@7sQH1d+}8YKmen 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 CwzzKq