From ed5f4eedc8f47dfd28025d13793b54c8cfbbe98d Mon Sep 17 00:00:00 2001 From: Kilian Muckelbauer Date: Mon, 15 Dec 2025 21:04:35 +0100 Subject: [PATCH] Final --- bintree.c | 66 +++++++++++++++++++++++++++++++----------- bintree.o | Bin 5751 -> 5763 bytes doble.exe | Bin 81600 -> 81600 bytes highscores.txt | 1 + test_numbers.c | 73 ++++++++++++++++++++++++++++++----------------- test_numbers.exe | Bin 71133 -> 71133 bytes test_numbers.o | Bin 6927 -> 6931 bytes 7 files changed, 97 insertions(+), 43 deletions(-) diff --git a/bintree.c b/bintree.c index 75c5aac..57cb486 100644 --- a/bintree.c +++ b/bintree.c @@ -2,126 +2,158 @@ #include "stack.h" #include "bintree.h" -// Adds a copy of data's pointer destination to the tree using compareFct for ordering. Accepts duplicates -// if isDuplicate is NULL, otherwise ignores duplicates and sets isDuplicate to 1 (or to 0 if a new entry is added). +// Fügt ein neues Element in den Binärbaum ein. +// - data wird kopiert (tiefe Kopie) +// - compareFct bestimmt die Sortierreihenfolge +// - Duplikate sind erlaubt, außer isDuplicate ist gesetzt TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFctType compareFct, int *isDuplicate) { + // Ungültige Parameter → Baum unverändert zurückgeben if (data == NULL || compareFct == NULL) return root; + // Fall: leerer Teilbaum → neuer Knoten wird erzeugt if (root == NULL) { - // neuer Knoten + // Speicher für neuen Baumknoten reservieren TreeNode *node = malloc(sizeof(TreeNode)); if (node == NULL) return NULL; + // Speicher für die Nutzdaten reservieren node->data = malloc(dataSize); if (node->data == NULL) { free(node); return NULL; } + + // Daten in den Knoten kopieren memcpy(node->data, data, dataSize); + + // Linke und rechte Kindzeiger initialisieren node->left = NULL; node->right = NULL; + // Kein Duplikat, da neuer Knoten if (isDuplicate != NULL) *isDuplicate = 0; return node; } + // Vergleich der neuen Daten mit den Daten im aktuellen Knoten int cmp = compareFct(data, root->data); + if (cmp < 0) { + // Neuer Wert ist kleiner → Rekursion im linken Teilbaum root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate); } else if (cmp > 0) { + // Neuer Wert ist größer → Rekursion im rechten Teilbaum root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate); } else { - // Element bereits vorhanden + // Gleicher Wert → Duplikat if (isDuplicate != NULL) { + // Duplikat melden, aber nicht einfügen *isDuplicate = 1; - // Duplikate werden ignoriert } else { - // Duplikate dürfen eingefügt werden -> wir hängen sie z.B. links an + // Duplikate sind erlaubt → z.B. links einfügen root->left = addToTree(root->left, data, dataSize, compareFct, NULL); } } + // Wurzel des (Teil-)Baumes zurückgeben return root; } -// Interner Stack für die Traversierung (strtok-ähnliches Verhalten) +// Globaler interner Stack für die Baum-Traversierung +// Ermöglicht ein strtok-ähnliches Iterator-Verhalten static StackNode *iterStack = NULL; -// Hilfsfunktion: legt ab start alle linken Knoten auf den Stack +// Hilfsfunktion: +// Legt den Startknoten und alle seine linken Nachfolger auf den Stack static void pushLeftPath(TreeNode *start) { + // Solange noch ein Knoten existiert while (start != NULL) { + // Aktuellen Knoten auf den Stack legen iterStack = push(iterStack, start); + + // Zum linken Kind weitergehen start = start->left; } } -// Iterates over the tree given by root. Follows the usage of strtok. If tree is NULL, -// the next entry of the last tree given is returned in ordering direction. -// Use your implementation of a stack to organize the iterator. Push the root node and all left nodes first. -// On returning the next element, push the top node and push all its left nodes. +// Gibt nacheinander die Daten des Baumes in sortierter Reihenfolge zurück. +// - Beim ersten Aufruf root ≠ NULL → neue Traversierung +// - Danach root == NULL → nächstes Element liefern void *nextTreeData(TreeNode *root) { if (root != NULL) { - // neue Traversierung starten: alten Stack aufräumen + // Neue Traversierung starten → alten Stack leeren if (iterStack != NULL) { clearStack(iterStack); iterStack = NULL; } + + // Wurzel und alle linken Knoten auf den Stack legen pushLeftPath(root); } + // Kein weiteres Element vorhanden if (iterStack == NULL) return NULL; - // Nächsten Knoten holen + // Oberstes Stack-Element holen (nächster Baumknoten) TreeNode *node = (TreeNode *)top(iterStack); iterStack = pop(iterStack); - // rechten Teilbaum dieses Knotens auf den Stack bringen + // Rechten Teilbaum dieses Knotens behandeln + // → dessen linker Pfad wird auf den Stack gelegt if (node->right != NULL) pushLeftPath(node->right); + // Daten des aktuellen Knotens zurückgeben return node->data; } -// Releases all memory resources (including data copies). +// Gibt den gesamten Speicher des Baumes frei (inkl. gespeicherter Daten) void clearTree(TreeNode *root) { + // Abbruchbedingung der Rekursion if (root == NULL) return; + // Zuerst linken Teilbaum freigeben clearTree(root->left); + + // Dann rechten Teilbaum freigeben clearTree(root->right); + // Danach Daten und Knoten selbst freigeben free(root->data); free(root); } -// Returns the number of entries in the tree given by root. +// Liefert die Anzahl aller Knoten im Baum unsigned int treeSize(const TreeNode *root) { + // Leerer Baum hat Größe 0 if (root == NULL) return 0; + // 1 (aktueller Knoten) + Größe linker + Größe rechter Teilbaum return 1u + treeSize(root->left) + treeSize(root->right); } diff --git a/bintree.o b/bintree.o index e0d6dccb0a93182de1eda2ea035ec2d69c0ddd95..4eadc813e15a228862ed19e035d4f90f7e7ba200 100644 GIT binary patch delta 1362 zcmX|BO>7%g5T3WYv)=XkXKk^I-cd~e>o zC;#)n<{`cMdKAQZ(A8%dcMn}=CHGE2UseFVM}XVz-v!)e07Wmy=5W^lD0@$`g1$xE zUGi#Plg+Y@=dwArLDur_uoOGv{X*Gq#sIz@MWt_K>jdc`FimU~K7R7~nWfT0-XC~h z2&U!Z85z8sdYEzV8TH-%HA*3208=u|M`#yY6u7LtkdHl#j;1q(;%WLCISBt zQd|~TTWU4icE@?D!prB|4k&88O0-fZ6BFvM5nvMo%7Fa1dA3z`U`pw0<_+?LO_lI? z(}uIkK{1jdq+zFix<+oqcTG?>1zYYo&Pk!6L}fuFz96I4xzjnt?aHgLoZ2Hom&MV< zQWnV#wOehFG7%AqNz~R}pAdw&pmQN{;-XIrHd$#n_E=52M}2e>2(`OwHIS0N%*sB^ z{CS0%PP5WJ4_HbHe?vqC2nN7KC#h9x9n`Owc~OG-APiR1ZZujIP`_pgk^iwM^wV`t z>8I)!9N3QC_TJLc{hui&cUmnD+j9a-E*7+ z3+xw)x#{ATMcpxR_f>xoe-SU%C&7oxPY30F+PHdPT(A1~*oy~l4KiXjC+PZ?ZC(Rb zj$R$hEKOuC3z;=;cQ}-b`)y?HYPH-7TGtK1?yr3Tb}49Gf2lAolKq#eFNn7#xtj0# zgVG;!s9JJOxiWd15H3FF=S|7=wPW3Oqh7JO1L21apPf-F%{Cm?uX+FJ)3TMyd4=#t z>P2R_OdWk0KFHXn_owknUk)?Wv~!pNNnsq#E@~d;uVUmUw7QsiA1x916%$>IaE!i) z*6s(Wb4+UsyBN8F`7WAQu=qB{Iq6tftYc;n6lpscy@Chpm>pnRs1OnP5cBJJ|4qyf zaEiF2Oqxw%WDD7|vc8JObxbc{%1@rh_&O%#3p>a*-au^^v+Ls)vcIAJDW*R6{*7dg z^iUgMeg*XbnnPsWGpJq1R1a~0sUg`RCClf2L?#FA6^?`c$vzszuv zrhBh<%S_qgkAT4n*=hP2kf31}eDuTw-6fRp^L9ufsMwpJ5?yt`^(N@E37Q{0o_I%M Y%ia&ES!tWOi7%g5T3W*@$P2rcZ8_^E zDM)VJDu@#-%Z(x;Rh%ld2MeK)g35t#p*KX&m0AfTBqRjNfeSOc_C9If&U`c9H}mGL z_WIoZb6RyP4!laPHZ5xI+*jg|`=F?;$^gG2z&-chBDw-#);l0(a7PDt#ak9dtw-1& zDQ;0%4P3&Jfu&} ztvS_~D&;a1yi9anY32BdXKM~+pRHf9VRQT!V*f)C zUMGSVh?|cIiF8^m2R3KemU=E7_aUid{3GzFKs2IX4w-L+%mPg(U^en>OHAZJYJADe zJH}VP;!oyp!g<{eUR3yENMD&8(xZH>J+KmiUxO?U>m=RhisjTmEXD5*WtKvjTP(BY zSRDtJN~1P%R<*j?3R+imMn`KTAZ`Y&E5#uv;(w<~`PxY;A-SsW`;$`^>%^8^BT{KK z+g8UuUU5Jzo$>P~BK5Un-FBm1u^b!J7ZW}@6|FSeP||k1|FlBHFbm#OV_y&U#tsVc zf%m8WPA`wD+QlqLvT)GoqWT%;?_%sBrU~7~GzT7IvWqbX<8NYmcN;YaGwS><#_nOh zi^evVJWM#GV`8a>*?vG$zr*-89;{)mj~P}WAa)D$mvQz!=KDConADq2VQdS4?4x;|_8Y{GT=@;f3`L&sX(z?y!E5m;Y2YN@7QJPf?lbi0@M>tCB8&eD z0?yN3pht%cjkf4p$3yEcvXXw@5A=NojQ!9$Ocx(uABNU9p|wDNHi{0;BtMbFs`tp8 UmR#W;O?&zDg6IuSr4LL017N`TE&u=k diff --git a/doble.exe b/doble.exe index 17251ed554d7be2234e656521208a616b63b9cac..6e9e7305ac2f6b9d0daec35ed50fe5acadfb4b07 100644 GIT binary patch delta 1445 zcmX|BZERCj7(VCT``+HJ?RwYGwOeq|&Fu%-7&@Z{3}y=p;lmk<29w2I+Pk&b(bCBwbsLp`7(h1GbNnMZ2^0vr{(H!Y=@>LZonZ`R| zyUgpgGVO{2{)ial2}9_3O#N)~iLaj)J0`f)EiTYJge*g+X){+_xc6fY1jJ;DJA(p#W|JJg-1t@k4ML5LCJs z!MzO80;t!Z^$K_mBhV%G! zp&#e5b+Hn1J&fSXhtm{k2cx%wa0}P4huvO{(VY3;>MW)0Y-TNnv-)AJA7>T*>7(QS E147P9djJ3c delta 1448 zcmX|BVQdst5PolWXLpag?QQRRy>?TOatAFo=~WXGjIqHKX$+uks2DU=mSgK4+;P|I z9;Ik4U_tORT`9O+(!M;bi zhiwd`1kqsaFLy$RL5RxgrJGa7GK$tG5dws&KM90vW7@HO?5n{PoF{|`tpBxUws-gt zyEL3&*6TX^>!ef;>gdvipx#Z9c*=B4B4|Nw5IAd{cZg6I-;b6`LaNk&ipB{-Sme6e zlg|~*qIEFs42%^lBFSMJVJU~A?pus|2Nk*{h;#+*uzogw$|6mYsxQ&INdt!UO7 za)=a+EMce$#mt*Fnjz2BiS!;v2a1;UBF`adA_wa4p^!6e5qL7f?Tg&Y6@LJH1tdCztrPh>s(zGf5(5(Ed;h+2!UKD@y`gU=X zm^HZW5&7H#e9N47fbYJ~8^pC{pMJ|{BmIm|KZ1q(cGUQ?w?{MVn>K0WvpJ0G>oXmj z&>rojTAH4%G&gvf5yvb#q$jzZQ8QXrf}B4Q_bX0fNBl>x`=?YMs}&QytEoJ}FOyJJ zJ=2?v(w)J0p{k4Nt;r<7i=oa7oSgv--Qy<=;^ewhjZDkKW>Mk3g zswHS7fKteju-k@s1!6o40-+mVoP+j7Fe=c*F`=A@KI`#pm^AI1cm5;AO;5I}TK*eT{!>&=NS5px7o76@@t$=YH0u|6N11+8A+~_Dk z1)`TwUPiWf;TNE7=vn8d-9gW}iL1ZkT-2%09<f7HK=9voV;(3z%Xn_Y>51|G7U&X$QNtoWTA4xZF$cy78aCJpDfdxJf|( diff --git a/highscores.txt b/highscores.txt index 2e6dc4f..be116ac 100644 --- a/highscores.txt +++ b/highscores.txt @@ -1,3 +1,4 @@ Kilian;9927 +Kilian;9915 Kilian;4975 player1;3999 diff --git a/test_numbers.c b/test_numbers.c index 84062e8..64f3b83 100644 --- a/test_numbers.c +++ b/test_numbers.c @@ -12,91 +12,109 @@ * - Fehlerfälle (NULL-Pointer, zu kleine Länge) */ +/* + * Prüft ein Zahlenarray auf Korrektheit: + * - Array darf nicht NULL sein + * - Wertebereich muss stimmen + * - genau eine Zahl darf doppelt vorkommen + */ static void check_numbers_array(unsigned int *numbers, unsigned int len) { - unsigned int maxValue = 2 * len; - unsigned int *counts; + unsigned int maxValue = 2 * len; // Maximal erlaubter Zahlenwert + unsigned int *counts; // Array zum Zählen der Vorkommen unsigned int i; - unsigned int duplicatesCount = 0; + unsigned int duplicatesCount = 0; // Zähler für doppelte Werte - assert(numbers != NULL); + assert(numbers != NULL); // Sicherstellen, dass das Array existiert + // Speicher für Zähler-Array reservieren und mit 0 initialisieren counts = (unsigned int *)calloc(maxValue + 1, sizeof(unsigned int)); - assert(counts != NULL); + assert(counts != NULL); // Prüfen, ob Speicher erfolgreich reserviert wurde - // Werte zählen und Bereich prüfen + // Schleife über alle Elemente des numbers-Arrays + // Zählt die Werte und prüft gleichzeitig den gültigen Wertebereich for (i = 0; i < len; ++i) { - unsigned int v = numbers[i]; - assert(v >= 1 && v <= maxValue); - counts[v]++; + unsigned int v = numbers[i]; // Aktueller Wert + assert(v >= 1 && v <= maxValue); // Wertebereich prüfen + counts[v]++; // Anzahl dieses Wertes erhöhen } - // Prüfen: genau eine Zahl kommt zweimal vor, alle anderen höchstens einmal + // Schleife über alle möglichen Werte + // Prüft, dass genau ein Wert doppelt vorkommt for (i = 1; i <= maxValue; ++i) { if (counts[i] == 2) - duplicatesCount++; + duplicatesCount++; // Ein doppelter Wert gefunden else - assert(counts[i] == 0 || counts[i] == 1); + assert(counts[i] == 0 || counts[i] == 1); // Alle anderen max. einmal } - assert(duplicatesCount == 1); + assert(duplicatesCount == 1); // Es darf genau ein Duplikat geben - free(counts); + free(counts); // Speicher freigeben } +/* + * Testet createNumbers und getDuplicate für eine gegebene Länge + */ static void test_createNumbers_and_getDuplicate(unsigned int len) { printf("test_createNumbers_and_getDuplicate(len=%u)...\n", len); + // Zahlenarray erzeugen unsigned int *numbers = createNumbers(len); - assert(numbers != NULL); + assert(numbers != NULL); // Rückgabewert prüfen - // Arraystruktur überprüfen + // Struktur und Inhalt des Arrays überprüfen check_numbers_array(numbers, len); - // Nochmal zählen, um das Duplikat zu kennen + // Erneutes Zählen, um das erwartete Duplikat zu ermitteln unsigned int maxValue = 2 * len; unsigned int *counts = (unsigned int *)calloc(maxValue + 1, sizeof(unsigned int)); assert(counts != NULL); + // Schleife zum Zählen aller Werte for (unsigned int i = 0; i < len; ++i) { unsigned int v = numbers[i]; counts[v]++; } + // Schleife zur Bestimmung des doppelten Wertes unsigned int expectedDuplicate = 0; for (unsigned int v = 1; v <= maxValue; ++v) { if (counts[v] == 2) { - expectedDuplicate = v; + expectedDuplicate = v; // Doppelten Wert merken break; } } - assert(expectedDuplicate != 0); + assert(expectedDuplicate != 0); // Es muss ein Duplikat existieren - // getDuplicate testen + // getDuplicate-Funktion testen unsigned int found = getDuplicate(numbers, len); - assert(found == expectedDuplicate); + assert(found == expectedDuplicate); // Ergebnis vergleichen - free(counts); + free(counts); // Speicher freigeben free(numbers); printf("...OK\n"); } +/* + * Testet Fehlerfälle für getDuplicate + */ static void test_getDuplicate_error_cases(void) { printf("test_getDuplicate_error_cases...\n"); - // NULL-Array + // Test mit NULL-Pointer unsigned int dup = getDuplicate(NULL, 10); assert(dup == 0); - // Länge < 2 + // Test mit zu kleiner Länge (< 2) unsigned int dummy[1] = {42}; dup = getDuplicate(dummy, 1); assert(dup == 0); @@ -104,16 +122,19 @@ static void test_getDuplicate_error_cases(void) printf("...OK\n"); } +/* + * Hauptfunktion: startet alle Unit-Tests + */ int main(void) { printf("Running numbers unit tests...\n\n"); - // Typische Testfälle mit verschiedenen Längen + // Typische Testfälle mit unterschiedlichen Array-Längen test_createNumbers_and_getDuplicate(5); test_createNumbers_and_getDuplicate(10); test_createNumbers_and_getDuplicate(20); - // Fehlerfälle + // Test der Fehlerfälle test_getDuplicate_error_cases(); printf("\nAll numbers tests passed.\n"); diff --git a/test_numbers.exe b/test_numbers.exe index eddc46e70687d359394bc10f81e73a22ff056f5b..96610d7d5355c50ab9f2820e0353b61d7f747822 100644 GIT binary patch delta 1285 zcmYjRUrbwN6hG(w>s|kBP}%|uBipLXsA*-J|1#N_IO?|27NmeIwz-sIac#l2AS)&? zl9?GFMt8zO=9-z2h=xq#EK^Tj70Fvcu?Uwa$-CHLh1&iS41 zoZtEG{We#m%@t|>yqHV07vAd<9=-V4Pn`mG(jIXi4$yIChxIo_hY6eL9rF>~P2ZWj za347=Mchv}EUox7{c4F}2fb|V!;^H?x{ML>*`CKCI%|6yv-Hr`j#K)P?OScQK+pQU zn5WnMUg2L0E&4qeqT7D2JXHgr>a~)Lj)DXr1FFE5%@+vo?%02TuJ^Ql^ z7Js8zKqZ#YGN8UjrvnLPozb_ebZ_tgS%WoB4xc~(sbn|;`3GtZI%a~*%O9!iQkatS~&r!`$ouInHp+NG2W!>07Kh&m(u33(PSsnH-CO^bOW>$1;z+!Cp!R zYNJ{<8_(oGZuH!)Y{&PkH458P+GrYZR$igI!Gl_Ay&l>b4?biMUw9=fFgrXk4jDUd zQYa)DN~O-gq@9O^LPE4Pl#_UIF;E4dj;F*K&B1#Du+Xf>9fCH+RnGW=*J2B=ToVD5 zWIPRRhs$LE(-WzocqR)k*6~3Jg#jz49xsP`F*Y`l&Sl|5U63|IjR(UqQPNu3 zxdi%*(Urh_6#!Q(5s#gY8hWByCZnAJblO4=HU+xX?Fu`+Wr^hxIFFUfsU^Xc+7-bK zm2of)ja6qdo*a#7xp=mN&johc*U8gUuSMB+%oMa3B6~%*^Z&yFae=qM#Fm=ATMfRd z;)#D%0f76)zcIivtz-*4M?d#8;D)}{b3u}SWT$N^IdAnh;79ts{wD>r)3(80JgP?q zABxJ7%v!ohLy^7sIh~8#!6yCH=(4@=lyot(q0FPDfQr76Ib^~w3G=5~oG9CWJ15JN zAV`9elRb+1KyDt9Wz}nVtcv;v&E;D~aoZfN=3h|rvV2|Mr8?>Fyc-`-)ASyEmkv)K zWk3Dw^kbOUKb{UNH|Bn+V|T&*1vk54v{@&8P?V_ol7}hjy7VWq(4j>hiZ1$?Yk6@C zS^Cw>ZVQ*lk49wIO(pO5%a1UzmiM~_ex*c;dzt35MIY1reX-zT_sgkOH!jf(vp4mn X)lU(p=#@1$o4{LZJ~n}kwdUx5MQ1S* delta 1267 zcmYjRZD?Cn7=GV-?@6*IF>RWr+tBD1=NF@CLFcsFn(DUHk)}3nS4|;ZmfUQ?G)t4T z)wUQqP=*RFzI4R7xPj3AC|c$n)%~!=vMIwqejti4VWRbyzxHFeAH{R-y}=&H3(t9; z^L{)x=iFXWZZ9e4&&kz2`z!DEN;m$!`rAaU2iPWyd1+QONlm4$z!)78A7T3}RaAdf?tc{E(gv z2k=cg6%I(?plY}c2kF~zfXy}n*t8~vp>tjVV1PE@%Sqc}`>8v!ho>2josw+p^nIkK zyw0fr0w{VA2-uH2Y)cl@L`jWIjGa^qMX zH43}O5}6#}YiyZ*=zDr(WVIE7Tm}mwIbV3`Rf)&cL>^AKMUzq`%hA}_8<27fN~)Ze zo!gB=qP85_0-#wO$#9t`M<-r>s@!4m8qzOC5x(%!f(&5EsyXO5WaJ0TnJzukEF6+5 zuQ-JCfI$yp^2|i8RD^>j+k@)sc{N#5Q->$=*-Vn#3`fmgPh*;kQxG+9xpLV0nc2Y+ zk&~}JULP|MY&FtnTfQ&_?FJ7x2`_jJG6-P3Z_@irdR&~JgpheY1@f?|(Cw&~(}DN6 zX|ImX*8zRpzY|?_z5l^ZSHKwAu*7x5sB6;=Xfi;mZh*UP4{yKOy-tnM*0QHQxb=6B z;dI>WGs*wFyicDw%Ao9T`TwhdJa5YN&VJqszTTpf?`{DAcgzbB<)6tBiVQtM3(+us zrQMCbudrJZcT2w|Ys1@dLHl)huY?X_u|e$8VzEuxQe(WORf@-V<5ilA|AafWq0vS6 z2Zc55W}(wsX}92Y!s(I6T*%yiy~WrhNQz`Bu{MisgZa{o*#hQ{!(#iJ&Q5pWJG3%= zz&6F$r|cn{hwe}Naf5s_yYL(ZXS#8U&dxl6Q`*wZkVV^^`=gm({5$i0D$Tnq?FwC~ zDdfA*#z*MA@DFmXv+Czh&1f~mw-&4SkiQU@FZ%6bHt|hle382IE&92Qi;wat?H~0^ z;sJ@*cJpE9Y9T)Cty;y)Uz3q*eymZM$5rigZzR~{xGI%M> diff --git a/test_numbers.o b/test_numbers.o index 6d60ab7aebbb0888d9fadd2580c2df7b3212afc5..0d7fbe72482fa53c4f3bd8f1245f0c189a98ea3c 100644 GIT binary patch delta 1326 zcmYjPO-xi*6h7y@cOUP~fDdLE{sImL3&utbipI3qN~salf|^1wMsOS+0i79eW*~Li zhNkfcW76_+!cJY-L>HthlBU%-Ce)vec4HT&8{@+0#*In0#&hSsd6}EsbHDTdoqO-E zduWe6d3giKbF$A!w54%KyqaRZmY^_VA>HvCZxArx*QbGF?+v%?M4R(>G-@u)8Tkpo*^g>T! zKlK`;c$mnzgX4@_Xhg*E49$xlFh}pje*A82m)WP)drV6JGBJRfgF_?j{X2KjlGRxS zZTi6m9q2FX6FfyPt!XUMfIYBS$+e(!rCS2dCn#XB@?OCY!ZxzSjUFX;Er^i zWlu>!-9#dnbF+C6&D)=p>y?VC#*uVlCIk4BSf#;G&&hN~msJ67s{`=*hz5X@^K+1m zNRpbDVlAD%01FZ6(!6Q?zMmv}>mMoTz~o}UcbfhX3EXL zzAsdLz>I%OUq$5&Y2H}1Oc_+}Z!tMLpULOoP;{7%g`1-zKCfweRk8jEx@+@c?CDAK)f^Qp8nFTiM{DmMvw74ws;M1QwVd=v6K`4d@5_xYBHwDwXs83zR&+l@+pT9-7M-_RyrKHGkth zTHbK7GlnAa`jQX}pc$H(7u!wijo3IPgtf!BRGZdYs&l$^-Ts;SonEUT#P6cbiqRFP z1K-f9vk9-#Gp7#=2Wn;0A8MX5cgCwfZAfpg(gcB@Vd57U&=BI2XzmGzn z2WTKb*8;T8Xk#h2n%_-jYzxp4g%-Jq{PD{DeSnq%v=X4Z0eZ(smG0)-SGmUnbUHxk m02KmsB|s*VTYDTE({P32O&vH!n;N4u+SG#&)~+>O)II`;&**{x delta 1331 zcmYjPPiPcp6n}4KzRm0=+1Y>QA0`{wVp^!CQ%z1$HyRTUrV(SrV%v0_-DMYd*JO8N zP{j05i&P=54-Y9;5Fvu4rwP%H1i|?D69rlBH-|zi@zxNG| zu8syXUxh)QFY3V*4%T-)!O8l6$>5p>P}2b(Q9mZdaRlh3aU2r!9NnN2CWBjSTV?G9 z-NFXig+?mX^dNwCIgUV{UU#U|dPu;;{rE55@yceKa?C$TaDyzB4?y zt6IzKvrQBFCjqD!?hPpzu=7?0bc_Dd^Vpz?;E`<&P8{aRHAhGd#51}ZOsveZReqXl zlrpqXDU@qDP#b7$uwIpJDGU$=)+4z}HVfJ>7`ZMzf3gWewpA>ivq5`|QEB*2dddtO z0nvG@TFq8!AbNK{ZI+WYqh8}!$tsiq&x#GgSe#CUc37v&s;msK?p}bmKUM%Rv$O!8 z$K;?&y~tXrbOACk=~C)>b<0bV1E~MEC=iz^1rAHdxd7TJ-S^In`H%zU`N6yK_Bv+; zirF%l``s>p<)zY@Y^4e#ak(p{eyT&JjJfU;XrEgu*Q&5DK1y?ZCU>GGIkM+T02 z`b67j++Lo;?gca43JZ|#ZqPUFeJcmM+=^*{wg+D5s292@L(juhw;PfHwatrgs5UD* zN}z?enr=~`%g$%*v-2LGd8<;fE&v*xP7~VG(KTlbyA<6O7>C_o0mv45@zaUK*+3Dm^yfc{Dho(XWgKoZJY%i<@Ek=$MZxK3d86@VbxIeDugiuNk?eM|jh2>61Rn m_-N5bmwj|?b8A~x!8J