From 2556a8f5ebe413a96cc3b2e1ce3940150c7fa46b Mon Sep 17 00:00:00 2001 From: Andreas Omholt Olsen Date: Fri, 9 Jan 2026 11:53:16 +0100 Subject: [PATCH] Add oving 1 --- README.md | 1 + oppgavetekster/oving0/README.md | 2 +- oppgavetekster/oving1/Account.md | 24 ++ oppgavetekster/oving1/Digit.md | 28 ++ oppgavetekster/oving1/LineEditor.md | 35 +++ oppgavetekster/oving1/Location.md | 24 ++ oppgavetekster/oving1/README.md | 57 ++++ oppgavetekster/oving1/Rectangle.md | 51 ++++ oppgavetekster/oving1/StopWatch.md | 47 +++ oppgavetekster/oving1/UpOrDownCounter.md | 13 + oppgavetekster/oving1/img/coordinate.png | Bin 0 -> 14378 bytes oppgavetekster/oving1/img/stopwatch.gif | Bin 0 -> 57773 bytes src/main/java/oving1/.gitkeep | 0 src/test/java/oving1/AccountTest.java | 55 ++++ src/test/java/oving1/DigitTest.java | 69 +++++ src/test/java/oving1/LineEditorTest.java | 137 +++++++++ src/test/java/oving1/LocationTest.java | 101 +++++++ src/test/java/oving1/RectangleExtraTest.java | 31 ++ src/test/java/oving1/RectangleTest.java | 271 ++++++++++++++++++ src/test/java/oving1/StopWatchTest.java | 153 ++++++++++ src/test/java/oving1/UpOrDownCounterTest.java | 57 ++++ 21 files changed, 1155 insertions(+), 1 deletion(-) create mode 100644 oppgavetekster/oving1/Account.md create mode 100644 oppgavetekster/oving1/Digit.md create mode 100644 oppgavetekster/oving1/LineEditor.md create mode 100644 oppgavetekster/oving1/Location.md create mode 100644 oppgavetekster/oving1/README.md create mode 100644 oppgavetekster/oving1/Rectangle.md create mode 100644 oppgavetekster/oving1/StopWatch.md create mode 100644 oppgavetekster/oving1/UpOrDownCounter.md create mode 100644 oppgavetekster/oving1/img/coordinate.png create mode 100644 oppgavetekster/oving1/img/stopwatch.gif create mode 100644 src/main/java/oving1/.gitkeep create mode 100644 src/test/java/oving1/AccountTest.java create mode 100644 src/test/java/oving1/DigitTest.java create mode 100644 src/test/java/oving1/LineEditorTest.java create mode 100644 src/test/java/oving1/LocationTest.java create mode 100644 src/test/java/oving1/RectangleExtraTest.java create mode 100644 src/test/java/oving1/RectangleTest.java create mode 100644 src/test/java/oving1/StopWatchTest.java create mode 100644 src/test/java/oving1/UpOrDownCounterTest.java diff --git a/README.md b/README.md index c9af80a..4cce06a 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,4 @@ Denne mappen inneholder øvingstekster for TDT4100 - Objektorientert programmeri | Øving | Tema | | -------------------------------------------- | ----------------------------------------- | | [Øving 0](./oppgavetekster/oving0/README.md) | Introduksjon og oppsett av Java | +| [Øving 1](./oppgavetekster/oving1/README.md) | Java-syntaks og objektorientert tankegang | diff --git a/oppgavetekster/oving0/README.md b/oppgavetekster/oving0/README.md index e96af31..1074332 100644 --- a/oppgavetekster/oving0/README.md +++ b/oppgavetekster/oving0/README.md @@ -18,7 +18,7 @@ For å kunne programmere i Java må du installere Java Development Kit (JDK). Vi ### Del 2: Sette opp øvingsprosjektet -For å kunne gjøre øvingene i TDT4100 må du sette opp et prosjekt i VS Code. Dette gjør du ved å følge instruksjonene på [denne siden](https://www.ntnu.no/wiki/x/Ggb6DQ). Husk å følge instruksjonene for ditt operativsystem. **På grunn av samme feil som nevnt i forrige del vil også denne siden være litt utdatert. Dere vil ikke klone fra "", men fra [https://git.ntnu.no/tdt4100/tdt4100-ovinger-25](https://git.ntnu.no/tdt4100/tdt4100-ovinger-25).** +For å kunne gjøre øvingene i TDT4100 må du sette opp et prosjekt i VS Code. Dette gjør du ved å følge instruksjonene på [denne siden](https://www.ntnu.no/wiki/x/Ggb6DQ). Husk å følge instruksjonene for ditt operativsystem. **På grunn av samme feil som nevnt i forrige del vil også denne siden være litt utdatert. Dere vil ikke klone fra "", men fra [https://git.ntnu.no/tdt4100/tdt4100_ovinger-26](https://git.ntnu.no/tdt4100/tdt4100_ovinger-26).** ### Del 3: Hello world diff --git a/oppgavetekster/oving1/Account.md b/oppgavetekster/oving1/Account.md new file mode 100644 index 0000000..4672cd9 --- /dev/null +++ b/oppgavetekster/oving1/Account.md @@ -0,0 +1,24 @@ +# Tilstand og oppførsel – Account + +Oppgaven handler om en `Account`-klasse, som håndterer data i en bankkonto. Tilstanden i `Account`-objekter er som følger: + +- `balance` - et desimaltall som angir beløpet som er på kontoen +- `interestRate` - et desimaltall som angir rentefot som prosentpoeng. + +Begge verdiene skal være satt til `0.0` når klassen opprettes. + +`Account`-klassen har fem metoder, med følgende oppførsel: + +- `deposit(double)` - øker konto-beløpet med den angitte argument-verdien (et desimaltall), men kun dersom det er positivt. Dersom det er negativt skal ingen endring skje. +- `addInterest()` - beregner rente og legger det til konto-beløpet +- `getBalance()` - returnerer beløpet som er på kontoen. +- `getInterestRate()` - returnerer rentefoten +- `setInterestRate(double)` - oppdaterer renten til å være den nye verdien + +## Java-kode + +Skriv Java-kode for `Account`-klassen med oppførsel som er beskrevet over. + +Lag en passende `toString()`-metode og en `main`-metode, slik at du kan sjekke at oppførselen stemmer med tilstandsdiagrammet (bruk samme start-tilstand og sekvens av kall) + +Testkode for denne oppgaven finner du her: [src/test/java/oving1/AccountTest.java](../../src/test/java/oving1/AccountTest.java). diff --git a/oppgavetekster/oving1/Digit.md b/oppgavetekster/oving1/Digit.md new file mode 100644 index 0000000..65bdd84 --- /dev/null +++ b/oppgavetekster/oving1/Digit.md @@ -0,0 +1,28 @@ +# Tilstand og oppførsel – Digit + +Oppgaven handler om en `Digit`-klasse, som representerer ett siffer i et gitt tallsystem. + +Tilstanden i `Digit`-objekter er todelt: + +- en `int` som angir tallsystemet, f.eks. 10 for titallssystemet, som oppgis i konstruktøren. Denne verdien vil ikke bli satt over 36. +- en `int`-verdi, som representerer siffer-verdien i det angitt tallsystem og må være fra og med 0 til (men ikke med) tallsystem-verdien + +Konstruktøren tar inn en `int`, som initialiserer tallsystem-verdien, mens siffer-verdien settes til 0. + +`Digit`-tilstanden leses og oppdateres med følgende metoder: + +- `int getValue()` - returnerer siffer-verdien +- `boolean increment()` - øker siffer-verdien med én. Dersom den da når tallsystem-verdien, så (re)settes den til 0 og metoden returnerer `true`, ellers returnerer den `false`. +- `int getBase()` - returnerer tallsystemet. + +## Java-kode + +Skriv Java-kode for `Digit`-klassen med oppførsel som er beskrevet over. Lag også en `toString()`-metode som returnerer siffer-verdien som en `String`, hvor sifrene 0-9 etterfølges av A-Z. F.eks. skal siffer-verdien 10 i sekstentallssystemet gi "A" som returverdi. + +Lag en `main`-metode, slik at du kan sjekke at oppførselen stemmer med tilstandsdiagrammet (bruk samme sekvens av kall). + +Testkode for denne oppgaven finner du her: [src/test/java/oving1/DigitTest.java](../../src/test/java/oving1/DigitTest.java). + +## Ekstraoppgave + +La hovedprogrammet opprette tre `Digit`-objekter, som fungerer som sifre i et tre-sifret tall. Lag en løkke som teller fra 0 til høyest mulig verdi som kan representeres i tallsystemet. Legg så til 1 så verdien av det tre-sifrede tallet igjen blir 0. Velg selv hvilket tallsystem du vil bruke. diff --git a/oppgavetekster/oving1/LineEditor.md b/oppgavetekster/oving1/LineEditor.md new file mode 100644 index 0000000..13ee18c --- /dev/null +++ b/oppgavetekster/oving1/LineEditor.md @@ -0,0 +1,35 @@ +# Tilstand og oppførsel – LineEditor + +Oppgaven handler om en `LineEditor`-klasse, som håndterer data for redigering av en linje med tekst (altså tekst uten linjeskift). + +`LineEditor`-klassen holder rede på en `String`-verdi og en tekstinnsettingsposisjon og har metoder for å redigere teksten. Tekstinnsettingsposisjonen er et heltall som angir hvor endringer skjer, tilsvarende en (blinkende) cursor som står mellom tegn. Verdien `0` tilsvarer foran første tegn, og høyeste mulige verdi er lengden på teksten, som tilsvarer at cursoren står bak siste tegn. Tilstanden er altså som følger: + +- `text` (en `String`-verdi) - teksten +- `insertionIndex` - heltall som angir hvor i teksten redigeringer vil skje + +Klassen har metoder med følgende oppførsel: + +- `void left()` - flytter tekstinnsettingsposisjonen ett tegn til venstre (tilsvarende bruk av venstre piltast) +- `void right()` - flytter tekstinnsettingsposisjonen ett tegn til høyre (tilsvarende bruk av høyre piltast) +- `void insertString(String s)` - skyter teksten angitt av argumentet `s` inn i teksten på tekstinnsettingsposisjonen og forskyver tekstinnsettingsposisjonen mot høyre tilsvarende +- `void deleteLeft()` - fjerner tegnet til venstre for tekstinnsettingsposisjonen +- `void deleteRight()` - fjerner tegnet til høyre for tekstinnsettingsposisjonen +- `String getText()` - Returnerer teksten +- `void setText(String)` - oppdaterer teksten til å være den nye teksten +- `int getInsertionIndex` - returnerer hvor i teksten redigeringer nå skjer +- `void setInsertionIndex(int)` - oppdaterer hvor i teksten redigeringer skal skje + +## Java-kode + +Skriv Java-kode for `LineEditor`-klassen med oppførsel som beskrevet over. + +Lag en `toString()`-metode som viser teksten med tegnet `'|'` skutt inn på tekstinnsettingsposisjonen. Lag så en `main`-metode, slik at du kan sjekke at oppførselen stemmer med tilstandsdiagrammet (bruk samme start-tilstand og sekvens av kall). + +Testkode for oppgaven finner du her: [src/test/java/oving1/LineEditorTest.java](../../src/test/java/oving1/LineEditorTest.java). + +## Ekstraoppgave - frivillige utvidelser + +- Legg til metoden `void insert(Object o)`, som skyter inn en tekstlig representasjon av objektet `o` som gis som argument. +- Legg til metoden `void left(int n)`, som flytter tekstinnsettingsposisjonen `n` tegn til venstre, og skriv om `left()` til å bruke denne. Hva bør skje dersom tekstinnsettingsposisjonen er mindre enn `n`? +- Legg til metoden `right(int n)`, som flytter tekstinnsettingsposisjonen `n` tegn til høyre, og skriv om `right()` til å bruke denne. Hva bør skje dersom det er færre enn `n` tegn til høyre for tekstinnsettingsposisjonen? +- Utvid tilstanden og legg til metoder for å håndtere markering av region, tilsvarende bruk av shift og piltaster i en vanlig teksteditor. Husk at også de eksisterende metodene må endres til å håndtere dette riktig. diff --git a/oppgavetekster/oving1/Location.md b/oppgavetekster/oving1/Location.md new file mode 100644 index 0000000..1ebee5c --- /dev/null +++ b/oppgavetekster/oving1/Location.md @@ -0,0 +1,24 @@ +# Tilstand og oppførsel – Location + +Oppgaven handler om en `Location`-klasse, som holder rede på `(x,y)`-posisjonen til en figur som beveger seg i et rutenett. + +`Location`-klassen har følgende metoder: + +- `up()` - beveger figuren en rute opp +- `down()` - beveger figuren en rute ned +- `left()` - beveger figuren en rute til venstre +- `right()` - beveger figuren en rute til høyre +- `getX()` - returnerer x-posisjonen til figuren +- `getY()` - returnerer y-posisjonen til figuren + +Merk at konvensjonen innen datagrafikk og rutenettbaserte spill er at `x` øker mot høyre (som i matte) og `y` nedover (altså motsatt av i matte). + +![Koordinatsystem](./img/coordinate.png) + +## Java-kode + +Skriv Java-kode for `Location`-klassen med oppførsel som beskrevet over. + +Lag en passende `toString()`-metode og en `main`-metode, slik at du kan sjekke at oppførselen stemmer med tilstandsdiagrammet (bruk samme start-tilstand og sekvens av kall). + +Testkode for oppgaven finner du her: [src/test/java/oving1/LocationTest.java](../../src/test/java/oving1/LocationTest.java). diff --git a/oppgavetekster/oving1/README.md b/oppgavetekster/oving1/README.md new file mode 100644 index 0000000..5d45df8 --- /dev/null +++ b/oppgavetekster/oving1/README.md @@ -0,0 +1,57 @@ +# Øving 1: Objekter og klasser, tilstand og oppførsel + +## Øvingsmål + +- Bli kjent med Java-syntaks og bruk av VS Code +- Lære (enkel) objektorientert tankegang +- Lære å lage enkle Java-klasser og -programmer + +## Øvingskrav + +- Kunne tegne enkle tilstandsdiagrammer +- Kunne deklarere klasser, med data og kode, iht. oppgavespesifikasjon +- Kunne skrive main-metoder for å teste objekter +- Kunne bruke standardtyper og -metoder (e.g. `toString()`-metoden) + +## NB: Viktig beskjed + +For å få testene og eventuell kode til øvingene lokalt brukes systemet git. Det er flere måter å hente nye øvinger: + +- I VS Code kan du klikke på Source Control (Venstresiden av programmet) -> Tre prikker øverst -> Pull for å hente den nye øvingen ved hjelp av dette. +- Du kan også bruke terminalen til å skrive `git pull` i mappen til øvingen for å hente den nye øvingen. + +## Dette må du gjøre + +Oppgavene for denne øvingen skal lagres i [`src/main/java/oving1`](../../src/main/java/oving1). Test-filene som kjøres for å teste koden ligger i [`src/test/java/oving1`](../../src/test/java/oving1). + +Hvis du ikke allerede har gjort det, må du installere VS Code med det forhåndskonfigurerte oppsettet for TDT4100. Se [denne](https://www.ntnu.no/wiki/display/tdt4100/VS+Code) wikisiden for en guide. Dersom du opplever problemer med oppsett/installasjon send oss gjerne en melding på Teams i "Teknisk Hjelp" kanalen, still et spørsmål på Piazza eller spør din læringsassistent! + +### Del 1 - Tilstandsdiagram + +For én av oppgavene du gjør i del 2: Tegn et [objekttilstandsdiagram](https://www.ntnu.no/wiki/x/gSfuAw) for en tenkt bruk av klassen. Velg selv en passende start-tilstand og en sekvens av kall. Lag dette diagrammet før du begynner med koden. + +### Del 2 - Java-kode + +Du skal velge og gjennomføre minst tre av de følgende oppgavene angående [Tilstand og oppførsel](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937373). + +- [Account (Lett)](./Account.md) +- [Location (Lett)](./Location.md) +- [Digit (Lett)](./Digit.md) +- [UpOrDownCounter (Medium)](./UpOrDownCounter.md) +- [LineEditor (Vanskelig)](./LineEditor.md) +- [Rectangle (Vanskelig)](./Rectangle.md) +- [StopWatch (Vanskelig)](./StopWatch.md) + +Oppgavene er merket med en vanskelighetsgrad relativt til hverandre. Det er en god idé å begynne med de lettere oppgavene dersom du ikke er komfortabel med pensum så langt, men det er anbefalt å prøve seg på de vanskeligere oppgavene om du synes de første oppgavene er uproblematiske. Dersom du allerede føler deg trygg på punktene i øvingskravene kan du forsøke å gå rett på de vanskeligere oppgavene. Du er selvfølgelig velkommen til å løse flere oppgaver enn minstekravet, hvilket lurt gjøres med tanke på eksamen og et langt liv som programmerende. + +Før du setter i gang kan det vært lurt å lese wiki-siden om [Tilstand og oppførsel](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937373) nøye, samt ta en titt på det tilhørende `Counter`-eksempelet. Forelesningene og tilhørende øvingsforelesning er selvsagt også lure å få med seg + +Det finnes masse ressurser på [wikien](https://www.ntnu.no/wiki/display/tdt4100/Faginnhold) om hvordan ulike metoder skal fungere, f.eks `toString()`-metoden og ulike metoder for teksthåndtering. Naviger deg litt rundt om du lurer på noe. + +## Hjelp/Mistanke om bugs + +Ved spørsmål eller behov for hjelp konsulter studassen din i saltiden hans / hennes. Du kan også oppsøke andre studasser på sal eller legge ut et innlegg på [Piazza](https://piazza.com/ntnu.no/spring2025/tdt4100). + +## Godkjenning + +Last opp kildekode på Blackboard innen den angitte innleveringsfristen. Innlevert kode skal demonstreres for læringsassistent innen én uke etter innleveringsfrist. Se Blackboard-sidene for mer informasjon rundt organisering av øvingsopplegget og det tilhørende øvingsreglementet. diff --git a/oppgavetekster/oving1/Rectangle.md b/oppgavetekster/oving1/Rectangle.md new file mode 100644 index 0000000..1793780 --- /dev/null +++ b/oppgavetekster/oving1/Rectangle.md @@ -0,0 +1,51 @@ +# Tilstand og oppførsel – Rectangle + +Oppgaven handler om en `Rectangle`-klasse, som håndterer et firkantet område i et koordinatsystem, med sider parallelle med koordinataksene. + +Tilstanden i `Rectangle`-objekter er ikke spesifisert eksplisitt, men må velges slik at metodene nedenfor kan implementerers. Merk at alle metodene jobber med heltallsstørrelser. + +`Rectangle`-klassen har metoder for å spørre om tilstanden og endre tilstanden. Spørremetodene dekker høyde og bredde og koordinatene til øverste venstre og nederste høyre hjørne og om rektanglet inneholder spesifikke punkt(er). Endringsmetodene dekker utviding ved å legge til punkter. + +Logikken til metodene må tilfredsstille følgende krav: + +- Når en utvider et `Rectangle`-objekt med en av `add`-metodene, så skal ikke rektanglet bli større enn nødvendig, dvs. at dersom man må utvide rektangelet skal det bare utvides slik at det akkurat inneholder det nye punktet. +- Et rektangel er tomt dersom enten bredden eller høyden er 0. + +Konstruktører: + +- `Rectangle(int x1, int y1, int x2, int y2)` - Lager et minst mulig rektangel som inneholder både punktene `(x1, y1)` og `(x2, y2)` + +Spørremetoder: + +- `int getMinX()` og `int getMinY()` - returnerer henholdsvis `x`- og `y`-koordinatene til punktet med lavest `(x,y)`-verdier som er inneholdt i dette rektanglet. +- `int getMaxX()` og `int getMaxY()` - returnerer henholdsvis `x`- og `y`-koordinatene til punktet med høyest `(x,y)`-verdier som er inneholdt i dette rektanglet. +- `int getWidth()` og `int getHeight()` - returnerer henholdsvis bredden og høyden til rektanglet. Begge skal returnere `0`, dersom dette rektanglet er tomt. +- `boolean isEmpty()` - returnerer `true` om rektanglet er tomt, dvs. om bredden og/eller høyden er `0`. +- `boolean contains(int x, int y)` - returnerer `true` om punktet `(x,y)` er inneholdt i dette rektanglet, og `false` ellers. +- `boolean contains(Rectangle rect)` - returnerer `true` om hele `rect`, dvs. alle hjørnepunktene i `rect`, er inneholdt i dette rektanglet, og false ellers. + +Endringsmetoder: + +- `boolean add(int x, int y)` - utvider (om nødvendig) dette rektanglet slik at det (akkurat) inneholder punktet `(x,y)`. Returnerer `true` om dette rektanglet faktisk ble endret, ellers `false`. +- `boolean add(Rectangle rect)` - utvider (om nødvendig) dette rektanglet slik at det (akkurat) inneholder hele `rect`-argumentet. Returnerer `true` om dette rektanglet faktisk ble endret, ellers `false`. Dersom `rect` er tomt, så skal dette rektanglet ikke endres. + +Andre metoder: + +- `Rectangle union(Rectangle rect)` - returnerer et nytt `Rectangle`-objekt som tilsvarer kombisjonen av dette rektanglet og `rect`-argumentet. Alle punktene som finnes i ett av rektanglene skal altså være inneholdt i rektanglet som returneres. + +## Java-kode + +Skriv Java-kode for `Rectangle`-klassen med oppførsel som er beskrevet over. + +Lag en passende `toString()`-metode og et hovedprogram, slik at du kan sjekke at oppførselen stemmer med tilstandsdiagrammet (bruk samme sekvens av kall). + +Testkode for denne oppgaven finner du her: [src/test/java/oving1/RectangleTest.java](../../src/test/java/oving1/RectangleTest.java). + +## Ekstraoppgave + +Implementer følgende metoder: + +- `Rectangle intersection(Rectangle rect)` - returnerer et nytt `Rectangle`-objekt som tilsvarer overlappet mellom dette rektanglet og `rect`-argumentet. Alle punktene som finnes i begge rektanglene skal altså være inneholdt i rektanglet som returneres. Dersom rektanglene ikke overlapper skal `null` returneres. +- `boolean intersects(Rectangle rect)` - returnerer `true` om dette rektanglet og `rect`-argumentet overlapper, dvs. om det finnes ett eller flere punkter som er inneholdt i begge disse rektanglene. + +Testkode for ekstraoppgaven finner du her: [src/test/java/oving1/RectangleExtraTest.java](../../src/test/java/oving1/RectangleExtraTest.java). diff --git a/oppgavetekster/oving1/StopWatch.md b/oppgavetekster/oving1/StopWatch.md new file mode 100644 index 0000000..fbaa10e --- /dev/null +++ b/oppgavetekster/oving1/StopWatch.md @@ -0,0 +1,47 @@ +# Tilstand og oppførsel – StopWatch + +Oppgaven handler om en `StopWatch`-klasse, som implementerer en stoppeklokke med funksjoner for å starte, ta rundetider og stoppe. + +Klassen skal implementere logikken ofte funnet på stoppeklokker på smarttelefoner. + +![StopWatch](./img/stopwatch.gif) + +For å holde styr på tiden som går vil `StopWatch`-klassen bli kalt utenfra (altså noe du slipper å implementere i denne oppgaven) på en `tick(int)`-metode. I dataverdenen er et tick (tikk på norsk) minste målbare diskret tidsenhet. I denne oppgaven er 1 tikk = 1 millisekund. F.eks. vil et kall `tick(3)` fortelle en `StopWatch`-instans at `3` tikk har gått siden sist gang `tick()` ble kalt. + +> Spesielt i denne oppgaven kan det være fordelaktig å lese testene for å forstå hva som forventes av koden. Dette er en god øvelse for videre øvinger og eksamen. + +StopWatch skal fungere på følgende måte: + +- Klokken startes når man ønsker å starte tidtakningen. +- Alle tikk etter at klokken har blitt startet skal medregnes i tidtakningen. +- Klokken stoppes når man ønsker å stoppe tidtakningen. Da er tidtakningen helt ferdig. Det er ikke en pause i tidtakningen - klokken skal ikke startes igjen. +- Det skal være mulig å ta rundetider. +- Første rundetid startes når klokken starter. +- Under tidtakningen kan man starte en ny rundetid, og dermed implisitt avslutte den nåværende. +- Siste rundetid avsluttes når klokken stoppes. + +`StopWatch`-klassen må ha metoder for å spørre om tilstanden og metoder for å endre tilstanden. + +Klassen skal ha følgende spørremetoder: + +- `boolean isStarted()` - returnerer `true` om klokken har blitt startet eller `false` om den ikke har blitt startet. +- `boolean isStopped()` - returnerer `true` om klokken har blitt stoppet eller `false` om den ikke har blitt stoppet. Merk at her snakker vi om at klokken har blitt stoppet minst én gang, ikke om klokken går eller ikke. +- `int getTicks()` - returnerer det totale antall tikk (millisekunder) som har gått i levetiden til klokken uavhengig om klokken har vært startet eller stoppet. +- `int getTime()` - returnerer antall tikk som har gått under tidtakningen. Hvis tidtakningen ikke har startet returner `-1`. Merk at hvis klokken er startet, men ikke stoppet, skal metoden returnere antall tikk som har gått siden klokken ble startet til nå. Hvis klokken er stoppet skal metoden returnere antall tikk som har gått fra klokken ble startet til klokken ble stoppet. +- `int getLapTime()` - returnerer antall tikk som har gått under nåværende rundetid til nå. Hvis tidtakningen ikke har startet returner `-1`. +- `int getLastLapTime()` - returnerer lengden på forrige avsluttede rundetid. Hvis det ikke er noen tidligere rundetider returner `-1`. + +Klassen skal ha følgende metoder for å endre tilstanden: + +- `void tick(int ticks)` - forteller klokken at `ticks` antall tikk har gått. +- `void start()` - starter klokken. +- `void lap()` - avslutter nåværende rundetid og starter en ny. +- `void stop()` - stopper klokken. + +## Java-kode + +Skriv Java-kode for `StopWatch`-klassen med oppførsel som er beskrevet over. + +Lag en passende `toString()`-metode og en `main()`-metode, slik at du kan sjekke at oppførselen stemmer med tilstandsdiagrammet (bruk samme sekvens av kall). + +Testkode for denne oppgaven finner du her: [src/test/java/oving1/StopWatchTest.java](../../src/test/java/oving1/StopWatchTest.java). diff --git a/oppgavetekster/oving1/UpOrDownCounter.md b/oppgavetekster/oving1/UpOrDownCounter.md new file mode 100644 index 0000000..67a71a0 --- /dev/null +++ b/oppgavetekster/oving1/UpOrDownCounter.md @@ -0,0 +1,13 @@ +# Tilstand og oppførsel – UpOrDownCounter + +I denne oppgaven skal du programmere en teller, ikke ulik den som er beskrevet i [Tilstand og oppførsel](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937373) og kodet i [Tilstand og oppførsel - Java-kode for Counter-eksempel](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937823), som også skal håndtere tilfellet hvor slutt-verdien er lavere enn start-verdien ved å telle nedover. + +## Java-kode + +`UpOrDownCounter`-klassen skal altså ha de samme konstruktørene/metodene: + +- `UpOrDownCounter(int start, int end)` - initialiserer objektet med angitte start- og slutt-verdier, hvor altså slutt kan være større eller mindre enn start, slik at telleren teller henholdsvis opp eller ned. Lik start og slutt-verdi skal utløse unntak av typen `IllegalArgumentException` (se [Koding av valideringsmetoder](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+valideringsmetoder)). +- `int getCounter()` - returnerer telleren +- `boolean count()` - beveger telleren i retning av slutt-verdien og returnerer true så lenge den ikke har nådd den, altså om telleren har mer igjen, og false ellers. + +Testkode for oppgaven finner du her: [src/test/java/oving1/UpOrDownCounterTest.java](../../src/test/java/oving1/UpOrDownCounterTest.java) diff --git a/oppgavetekster/oving1/img/coordinate.png b/oppgavetekster/oving1/img/coordinate.png new file mode 100644 index 0000000000000000000000000000000000000000..cb750855cf48899617e231b78fb7a6786c86bbd0 GIT binary patch literal 14378 zcmb8W2{@Hs+ctg^B|{S-b0vyR+dP&eWtSl`Z1YCQoH?^d{Yoe@52Xy5=XoZ!S;*8j zq+MiAX2Sp4^*rzUy#Md}j^q1|L#nm!z3z3dbq(irUg!Pvw(1Q!Y8Gk)LFh0_*EA4> zoDo6D9#I{H5osAMKQe z(IbCGoy<*0mvXsS*_k6^8%GbrCx=O&+;OmQaW`=?M-1=plP;(EGr5(snXL(2>V%Zl z7l*+x6=_(<#L3>u-V&*2oq7UehyH%w)yd2pS=$es`}buE4t93t_AbbJXH*@G9VA_* zY2{*Tj=W#&<3$iI1anPZ^FhMG*rn&JmZmMsPYNlN4j%bv`|KK@rL&t;v+mEIT$GG2 z7@E%H8yrbKKz_P8NI8>2=aKU1F~vr*yU)0C$@{cEwGLk}*Gb?GIr`F{UBRpVRQ~ZJ z8U}a2P>0?RT4=(=F|^BY2tIi&W8k~%=l?}a%6s};bce^LZ&Wxph5|uu`-^1d!s>=O zC}`k+%mI`La-Oz<96=Z!wlCBFu@%U5J5{51eB4Mc%)Ts3GL$!$|7DJQjS*+2JA;6oZp!xD>hXK< zAV%pX8y+*~XiztyhU#d~anBmKX-iO@8=G-C(NPFL*EuF;TprK%&aJf!mS|_Ce&?(k zD_uw?P%*|3SRJZ`7*K*n)#bB%Wc8ykrECvt#h7>LNNJ)`x#5tCgPg3a>~cj{^QdFO zD%<0Vf2IvvBe+jd%U*}uaS|)-J*&)hXBHk$dnklS^ax!1z}=R0TWrzy&XDc-(^#Cn zWp~0UYDQ6SvSHCF)7YWy7w+m3^0?7vcaB4c4$&*Lr|hWEd{{=CCWWOGpF4vw3i`*k zMgD>zdMz9aD-3XeLRJAW}Gx`?xB$Q|_6B@Bo?r11GwMR5Ox9cnCXy~4D z*)lzJ5D{+aBHs$z-weBxdl*4=yBbP`ia1rB`sbdm_Tnz@z=B&pS!~kucMh}JFO`d} zK47xiSs|nL?JjILqRc}s)tT#Bm*~yx$B(clO{*AoSLx-z1#PZ|6}kL`Qs<46Cf`*$ zQ#JWQYL?m}cs=KG%2W%)k=xnUC43Vb-#wC!968d$!GvEPwo1BggdPTyQK&EP$QwD9 zJz(8c{lTrmK%4oowJ3{1gGu=LXT^1O7cr#mc&NC&F4k6vx>cg55^~*)yY-{#!`QJZ zcZaXLYo3N-9G9e1KOeHFq7Dlk75DbbE`J@+dBlVsZ(n;zNx zh_a)wW$=dQN$IbW9C}q}Ld{ADc8cnvC+fjGM(XzF>Qt-!^gIvwyNWL<`t%8Tj8TvD zv^tI*nfiK3O-Fe>K+cBw<*Qt~NssYQixEb7>1f)M6!P0@o+nis(QKLg4df#6rObmX z(>-gGPdI0%y5iF{&m!l$i-SFnQOpE+4K6;;o;TGYl3y9$e1@hqC#(+#`<0fQS&Ff( zDGF<|xzr{zc%SV?DDi#oz19sMtt>4Ji*7|kq=ZletAu^0%cz>~uZVL)6iAd=-y2L( zoVoffuP~bs$?=o)_=5KDzdo$Q;lB+K;G2Arql8o)0p2Ock^Ezilh@bRHTfc)qLBYMJow{fPdPMS6k%61r^J{E)1ttQqa?D(6{I5u6el91aP_EwkT!g_R8M!o7m{DRMyiTEzIt{BH~ua|8qa| z;^d_T&RgCGu$C4LyykoG$S$%}TdgH*Q=(Tn{#bWP#;Z=Z z{Fc<5a%Uk`jK^MJ1MY{^pmoWI!t5YzA~zP7nWdB0eG(;TSXn&Fcc30?aoDEl_AA|D zJ_o-eMX!~P^V{>Zl_m33wSS+SH2EsoeftY0+#&FvisAU3&hRlF+L-AGfqsg}>T?Z6 z=BG;Z?Cv$>1_lPg^rMQ6M~)t)?*In;x%#UZVFNg=u^G3$vv&rM!InF*iLn)6gE=-@R zq)$&jfkOAjJJe=etlczS#2Z#WaJ)->0>SMZhjE*{nzZPP#-XFWQ=PG^>+?Oj)g=gS z13kuIM#>}=4<{GLs-&H#2xZ;ruYKcoHg$(;5ybYW9tu79y(9XDHlf;cEtBJtXNvQ5 z_srJHbhuH8@@SR2n)~ADDd~0CtL@pWJk^|9#DCyjGr3s^%}EdYiQ1eh_oX9a>uW>C zb3cCEvsycVWOD{kI$KWR?lT&&d-XQ)9*Mcb@kCr^6OlW`qE2@Z8RL&koFL%D|fuCApV4W>6p2Lh+8ouS!>=k&{- z_&p_2390te2q%(1GwwMj;IloGzP;e!mxT}G%+ln8aH?k;{RmgkeEIj!CQ;?l3TIx! zw4Kgak?qxfwZkV7w-(r0J)&_JQQ6n+}p~M1W2MO%#{(K_$iDbeyL%BjfoXIGH z>7v*lZEY@_`RuL@nT6H3%=C>`y&|tK^<4eY`=Md(qH*mfB0kLZd;7_QKHX1o1ty_K z8@<=(M?T>VYCN^nUSGZx^!cP&>>VyZHpeJ#UGR#%vlrJjrP03otK;HYmhrCj1#^e5 zHZ>VbHO9NMI%mg{6mqPu{Y0$Y+qQw(AL0tgTId=Rng_CU)_(N9 zJ2#m7pd*32qVwp6x$2fp8R6E24bmp$`R&f7zN&eex0Y7B;k!F$d{F?4nyV=j%v(Fe z>HP*grQ1L}@N)WDY<7lKAllM#V{r`J2)X~6>4)0F7qBL0#^J@86dWYJrG&qxBEu90%?H9MA1?@gm(+KtNm)RJVS)T4WvgPGPAtCsjMCz>6r z`}tYvQG=hr8BgL1!nu2^mTI%W3Om;}!Kh~2Bl(|W59Xr@rLOnybHUVxk*U()-QYl% zMBQu|nFI|!lFsg18-255cG%_Zdl<1%6xi6$-W!}VS$Va65xi5&_Pk6gecG6FAi%K3U*Pkt@xxooy|AF)|z31PjX5OnHMEcer-%RV<_VvaYM(u~&n z$gr<&z2n@yH~*#w{JoGJ*M#E-IjzkyYZy%1Du= zG(3_<*@0T$UC-^J!_0vHO_&825)kxLfXeGsw2cNF5Y<{)SHa$H)$FblZqYE6+I5gH zIV?Hz$x+VP-Hi!e93m-p0RDf6u7-Wm=b17CW2k3lx5p zR6WqrfFsPFS#XSQL zqc&8sNtD|&+wIx^9cEqfIYZfp5=jtj!Qy5L%>;aZ26DC*qPA*#hN+R^s}wY-vE_&6 zW%gq?58Wq6N@*I%)885hrpvvS^D~0czKecaw%VGXNlh-6~bd0T@bw z<@3`jte+(F7q|+aN&4+ov*Az0dCpemqB`CVzmsE_BVrc_sI!f&f>wFDsjKXx!)W<@F=PErne@^zT z%OYsGo)w12$*>osOB>tcAw}{#bkFYX?o{5N43O>NPm%pb6M(bz4<=&O844b)#8Efh zF171nt1T+Z9ln1gJt+5Bb_nUPqU(O2+pV|-nXVXWR7gqU&Jyumt8Nn;I);jD^olPt z2QmQ)D;tKYGeBAUUN0xjFmk|JEAnPg-9;3;0Ay!jJyeQL4r5=|fK&lns)p6PPsnwJ z9PK#{(s`@XLI~4MUBIYX1IGOMT>l+*2B||-H#y|Q*-c&SU&YW24|Nc*E zax=Nj_nM9II}D1-a)!b~uI7Z&6df}&u%PYT%gLr)F>fT8X)Sz}i$*lr@D+6(s_E|} zLr*<_n);-XfXL;R;_KgqHiV)Mc7PpxE4-UX^fEaPB7y`C`ZE1zF&y!3}MO&l#s{U<#*`j1N_9Hxoz z4NGed7&(n)gz2L?e@&P$_gNQEz~0p^b-0d+mPdA&A6zq0I!5uCK)A)!aKJ)7m~t>m zcPKzR_-jS3OmW;>51%f|Mebv>~11~!P~T{Bo3ktdwI?ohv1T`Q^xQAx*r zvKC|K;K=Ns$*$xZ=Axj<7Asm6Wqozh(qP}qE#rrMnOB@vBa7K6-IO9ckH`Tjs}k&G z=i??V^%g`2%B*WJ=*)Jb)b3~(uKstYMh`e9#S;N@y#yXt^Vl6M8x3h6uHym=BWRk? z?9V0a9l2{~>`Qek1v-}=^qtYG#{heB+?EhUlq4BxW>#Tu=imsjG)+R_RA=Le^3;a- zt>EVJ4dlKC1&!@t%$Ft?|j^8SC-=1%$ zW14cK|Mbutsw}e@2LfuXrW|+(>k{d{?=Q!e3kR%w_iHT*%}mom!mLDD!e3Pl$Hp!+ zt*WEw8^~FkT@4e#?`w=42DC<$px}iX+(0mE%$}c$+_lOOPC!vTB{1-S9{=Dqu&NAA z4fv@r=&q^J$apRb6}_u8JEi;^&zf8)O&=$NdxDcfIO{YtbYv^aJv`M?Is*<+rt*3U zl1P6;!$XL85J+4yr^gw=VF(L+v`xjjqbss{e5Fck_?acF#GY)r|8_c;QCy~$p_Pj>Xh_ZgAzb=WKvdu^PRF>Mk))Vn$ z!;_MaR)*$kx6D#PyA&DHFVsTL+E$qD!uj%I-EXIX0W##b99Z59E%zYijirepi3#r} ze+2|fUyQ}Q12+Q}E#`YI8BPVJ?)`Cs%zq7zxW#Ka(H#;M$7S!DDE4W ze&K8;Qm=Xzi{np#EnTYFNP@^kUrKSv|Ku4g&c=JmYodQ`ZfNc2q(X~6Lix{Ir2*^6 zhTa&OhPPM1$02qY<>giK$#jr-l6N~BOZ0;N^uK(JYnRgFqsfYJ?7`$vXN!(F>~vHL z&S9VTFSxBiy_041C!=KL9?5^%>3eo%Q7B;Sda&Zd&V#C`!D3HRNDrc6>Z2@D93laprNHg+?k%mY* z_ogRDAy$ohE!EJ6Rs3+P0%}rPRyJW%K#q!htM;{<7&hwMkZbg3tq zsy8eapa774yqQhK&f+W~LETmax33;XlWXMp`qVZBaDSjabLZM!$Id)U2p;)|WP)lw zgAPWuOOV`jqr^nFr9z3Tgo$$Dcjv=_C?#T{gin)KRcD~1i(8R(Aa0jQWHdK!LYd*R zzvly3PQT241^|z9w0hpq_nq49-M!%rU?jnWHM0gZ-t{t9UZhn3Z;}eV^JAFSx%>LN zWbG_5r|z4nX9%UdN&UqiRy}JXf72ZpKElgkcAUhnI7xREf=!IrPI1ib)I#FP9GJzZ z$)t?g+xYnz1Mk$AHHGW9G|17$5AxC~EG#XhI~PNd@rTu#@kyJg7_-mP$T%NLOvc9( zpyKD0!4!?K@#&nfF-`~v&Io)+e|vnRHXTf@=TaKivoqlF1J)%4$$yBd1sBm>N1%qZ zvo6p!H4YwZOo6;&f)@9?f#ko45ceq0mHC81T_`~DZ(w^?PGFwSRq|f4 z1D@_&c(`6pl$Rg|sMWJ!A}XH~Re5Xd(99`#LTTdVJfK60vyJkiIc#*bo$lX%!Ho%r zEg1E@8=E%U)YOzrP|+Q!%&ttsv~-c(aQ zSHQm0T-&OKt8(LaCYOMHJak_?B}T4LFGqY;-hNhiv1#m+rGeoyZ$s6lv_rw%9&0kN z0$<^kL+Pio&|qD6{GvzdA~2`TwTr6#EH0&cyteQ2I^NMg8_SmsCAO9>#I}5;kbff?nqKQ}Ib+nemjpRKBF3EaiET~S zgdGRo@vd%^_e9TbRinL-AE&pt2?sAVW9`z>E}{fePj&M@g`B>M6f`0{4UP$;zjzXR zHg-qPSr_piv*3)NV;CvyTA^)1`^=Y5TL?;$S_Z^bQwv>fmfP5ZmDMik`k~+5ymW<$ zuAME*ILGkjDn?x~=g($9kOwl{QFD)EmmX8000r@Ky`@#9;8!6FIvOh{oyULbMC&qwLXT9a z*}&Y|(;W7>?%$!M6(iodtgbo4gShz8G;04f4W)eaZh^*L*{-7iEo9XJ>yX{SDEERc z-(zYtCyBEWmIj-#EsxlST+@By#l|=->MhC$Dsz=>6GtRU##cT^(&QfgC+nUHS^gF^ z4P)OuriVhg!40Jg1w{6iwu8ZrOuwp^Pr{S!@F;sK8(m7(p}EV>`O~UWG-Wa>ZoXhd zF|yOFi~4oaG=S1h(M--P3ANzPYUVhNEM;%dV+qv?LD7moS3T=XlqH~StS4%rH`344E-!bpO}j&*-BvZ{L?R90dZHPzoIIi6K|` z-M1@J{HW%TXN^&%ZVXWp!sX0k9I(4JT~M^TE*=V$*OOZ@F4~IrIO1VGCFRS}v(o2t zrw|X8zv?}e5K6TDlxBvvYce{$g5GIXCKPwhw+UGlU0iX0S+*bGQ3nxvWv3~@d34IJ zzuO~W#MUl}?sI-3^ueGBMm^Ghudu|x}$mAmO6^*W|wMdl==5sd}o>(&-u<@wKstVS2Nm8 zOLR8aCBq%(-CO;!<_Sr=eHRg(@J_QEc-~cH(_q@SSd5R2LIDjP@D1*KpuRjRe_k+U zrIdVHb;^OQ^tMRL4yn-5gE6 z8MChE$&fJ$%)||bw9@-e#i@%1TTOQ^&WW@Mw_GC|7UIR68xdl_*FB7Nuqg6wYML4; zVGQQpTHn3w;64aIqi=4_VsQ`+)nX%&P1Q*i<1BXsd9D%8-S##a(k-Cjxi4REKUh_P zKdmuR+rZbaOSvy5B+2;+Vx$}N@4frrn7zB}11!9~Yn?K*BLUmUmW-TA-~El)rqioP zi1kZDVrEdJ4!8nL8vKM@54`;_$5b~847ZZ8U6@jy3-74v+HP4j7WJxbnG@-py(Rz! zqtw!Zo6PPTS2oLg#zkO&R zud>n!3|h55%~y2)vv8DY-q2hjYEJXerFi+_V&SjlXjGKpB`ZIuabie4GkHI^zq4r1tT`uUN_Dcdpg2r@<-_7Vp~L(b z7&Fvl$Z@Efs}CK)eLNWuH1DLNq|Knnx|__37kWs3B^h8lFapSnCqf`Cc!U0T6I(S~ zlP^P)k8XkW05Yu7N!{=i&^dzpXbKEAwL(5Lyh%K^4Ns^UR2Pz=5}CHPug^cz%+Wqc z(LgHcujV6z%BdRasTvq}RspC;jPyo^FGJn9;RBIEONWy6QA2!vT-zc;HOAa*C+7S2 z=zx7r}bOiNvXp1+z8NQ(ZzqZCmTGazp0pkTteYd&%wrH0z`N+FZY9s8_-IBdG@ z1Ep|oDM?ACo`HL!;Q*R0=cD*f(H>MVkKaCr2|q~X{QN>wVfM@WB02U5K)$Owtx<;q zu7Jz%fia86&$?AX!2@~*C^}$J)Uv03!)IX5b)+n+0&%txBM01qw6nRDQO&{T?_N@UtlMjQ$zgv1mif`h+^dLi>s=gn5l2+r%y* zix8Tbfo)1+T1uhywEg4?xwF1&p?ra|$WFN*zRu*Lb-X@GnZGe*h<`_#6{-t60ZCa| zlnd4Ojdc~vH(@p-yL(MtwBMH{La?!D)bFoagv5`rSzI!PFbE5`^O}d+ z&PyZTq=yRWKZf#uK996GGEJgsd5s~k4o=mh7` zLCrH@Emrp2@%s&R(NA>!w&DY};+CGih#L*i(9Mnsxks$x;F!hls92)yUg)h~_3>^` z7n<9oZhAE>+5PcKyX|eXIXmYkD{V@4_gdTc%sQo_QncS)12=1Xurx>ob9F^fBU%$& zYg>GwGPnJEAz$P(py8B#79zM~*;V#uUhhGflDOo3V`6vF-8pRHmAoI+%&vwIrO>oD zW}z56iC_NsJeW9TS0Fi_W7>ENlpJBpvT7#Gp^pMAilhqkryfKrlTrchJcA0Wv17I3 zUA;0o|FP@&!@{A&ACPFiDNGci0Fi**i^X>p6kQMCipd~+13rAFXNq+t1ve?#eVqcV zmqEE`zXd3j6dk4s>ypg#&#qt9A1q!z;-P#iK&sX#FHBh~XTbWGkDTFZ`#kZbVK(2A zcN#6;kpW)-q$Nv8%k7c=fEO9BfjF-{L)5c@4z9LDBX&eFk!L|sDA>-3iyvboAo;!0 z-V_RG_^qq0ku{Je_{5$uQ<%oQt33f|^@1?59Unk_50awU*x0ypwD}D=yhqKHM?#6; zqU_qy!B*cRK`sJ4@{dWp$cyw>+_B+mi%tf7iw@T>^J9 zG{1R_x4N`ShL+UTM-73e688GK%Wen3xS@{$BL z<5tAtRWNx|)tnQ#>`1=;y#fUJ2As%%wd=j~yEqm-)P2(&ZJPltB%k6>PQ`@JRr2T3 ziBpnNd54RgXHwbMI9x=2iUeb0_&6;6In40it!I*+3>N(#`Tk!^L4)8XE0;^lkRcxbv!VXKSp+Wit0cf3XsNe9G69tr!!tNPW}6YF8Z@nzU^K)6jh>=zfK{_= z+nHJF%s_`<#p20_?H$?XKnAfL?YG5Lv$I&WT~)VJl@aDh(mi!~hr*XLGy&fCHJ7`o zv*7`1II-PGR0Rf3N(h8?0DemKnIA(*Nop=ax2}&M&=F3QRKQE;6(GD2SOy%fJCKolD>fF4{7()(kvY11B1sI=kGF`jz>zEiZb1Xg23 zm}$dR(<6pU6P0d4crgfjeFMTDq)Wi+EFsqc(}V-7|G!3jz;gCWD4>}oWD5@d)O*RLelUz zhe}NVK4Q$YQ;W6tL|FiR0d)az*7=g#aljsqY`}xQ90Jt>#9^TKNP#;6Kn53;xM!9p zXfScv^OTF5!_TaKS$IA&dCx_ac94=#3bpR&w5Y*QyvCdf!Mg@5UHmBvbVgOu!i?j4r1hyei@m+80!YrDp-Of7ag$1XV zsh)Tm<#GD2;$qkYLsyWR&dp5h?@si;#+P6zob71`X-JM*ae4d4yVYr^hazvLzVx$*Gf(eVubX%+NoX>_s+py=Sb>{aOW>=C z=PYJC^>ip`5@KSVYcgh7>fcL-u8{;{;VE+aD{?x&tmT_u`UN&z0Y+D(>(}rn-6KWl zj_{q@*E_N*!QIv3j+bai{{5#ybC{>HK;9KH70d1R%Nd%ehzCx4z^Ot|>#`7`V{&AJ zIMd;nki)7ke8ddGTY5nzyMMpf()P&*CE`!)2!K36L>f($gm3)O{V71S?OP8Kk_sic z^h?7qQ^LhZio*Q9Q$i+o1Y@;I(Ap|Y8P=I7(EU z&Bqtq&eiD2+OmlJbf(wm=LU=W5@TM1jVTI{#mFK0{W zuEDxjjfvLZ?THq+vF*l#mn_QdAy(%8tpq$rt|F!TG>2vhgdrkX0zKmz9^}>SONLpuoEH{bbQ(i zvtt8;tOm|UcUUo9XHW!s9Vq-7cHIu!7ZuI*~zf zc4H?ixBVJW{8RyVYU(B8RX~dgEkS!*9jbm6G9*0{RCCXhu{Y$8C3w0&TH1kqH6zJM z5F`Owtx(T=vM(EQGZQW<_=-7{-Q6@VRJdq(t)Eo&emqJ3g&Q(Ks>aK?N*njnAw(=# z514KGpcAeo$wfLbHZ3Jrp6t>*&{GbO4{YgVxj zK8<}_Ntb*TI!E#l0~CekF!QrK=KX`K!=nF%6S9vX_5W~Dtq%F^4sGvF?E8#WdpOVM zCe}Mb3+r?-79n(?`dkvGJ63|EW4$$YuNrz3o^}2RJMnZX`Wv2Ul2?|hw6Weo=CupB(D2! zEMR-J^x+@k5x5u;c1}p8M}ko<2M|(MTU_Z6mkRk?#4erAiOz za{`E%SP4XUj1ziO=QWk`0u*z!A!%qaKkj%hjNon@M~VB}5uD}H2n#pE4TidECY~3d z@npzt3c#dLWrtta z>6v8J`ZjSU7NIdjI6LwN(_69iX!{pw6*byfiF%0$LI~46&B_JAHs9PW6*Axz@30^gY4^L02W|2;H2JrsN#lG2ts@XRE)OaVdX(v@pOBKaqAMNLnh*XLI(f? z=y!5a0ic9R{V8KM2$TL&f?owF5HuB|4A&p>3QY@A4uhdf%l;}5#kr3Db#x$`9?<_M z+4O&dp8x+~_sa*UOyCFJdC1HnN3aD&@T!P7XZ#CDF8@!M`F~pUzd5@9O9%Lm%R5K3 zbddOqROFa&b6w~dHZ|{;)W4Tc&1dracMaK!pTm^eWoc*Sl#3(&S`Z!JXwqJBV+v<);#Lx5 z+*Lua7|p%RNRBW=RQ(eH(BD7|2l$(>xbt7i)S#%t2v&`PhIVVD$2YBP!nf+<{bgRi zU%cDFb(_JB?I20M2%4-9b9r^S6MO4rp37gGhs`Y5%|Ixebjhnlk`@_04uSi_M%}&- zNhKU*p7=hYBl_YT#KTIL84Y@G~OLBR`Kp6NfM{K6O##h%bfcK zKS{S_D!TedzTNM5q6J=+aNSv7c&U3D1a_59-`hVHkc5vq#y+mrYm`VfhX5nB}=8L>JBh5sjOZ6`n zCdqodEgSc=nQ^6n*T>+pUec>JP6XJiCyYJqG$%nv%&m#kAA#)oR`HOrg(ZYDk|sN5 zwq(etA1<5rq;=p2<{7p-lk@kNL(c3Dj6>weJVyX!8o)+p_I)Df{sW8{*so0GRdfVN zK5Xo_yQYl)6-DkprWj0VAv_nyFn^aCsS3e8jE!MeOE=-Y18-ls1mP^A%xB=p%$DSw zr#e4w10WnzfG>#K*-^U*QzGy>NyRsTWjlD|<_aiPWt%@E2nnn>w-J^#O<|IHewaki zLni4aY5oVG5$_bplX;Y=Oop5QJz4!SyV^(_1A;q-LYEO`k_sa(AoXwfDQMajAGGUJ zky!MHL9igwH7`KTbhR@yoitJFTezmmd#ez1kd?F6a8nX94?!jO*nhoQ*11pi=vnV<<_~7m^xWnKYf;&Njg%DhWyCs9W2G_yeAvnQh=`g&kPJYJuK}};8b4PW9v^<*WE(Cn_MD|!pC8Ua zG;Hn)jsD!9p2z9^NH)+ch`Qe!1XFSBtPdUSj6a{w{w%XxTkd-(dv{Ud-f5^vB%tx9 z)d?}>ebpKEa|(W4?eV<-HD{pw_Q%0lg2~1Aw3pkBpZ)QNp*nrzO^B!4m!*i`b#~9+ z+y_lnW@bAO8_DZo780L)FY-QKHU^GOwXS;WAZGoar-BjJqko5d1}B>l+xaeSx#QDq zk5$HJQE#u$77_o|=n$B1cu008h0WT37Iku&o!`}dW2?c-u*;b7$f zF}Jd@b9zO8)%lGcVrTh^USCihs_r6dWoxJ8=Vqnj_eR&k&%r{}lK!n z%EJue~5nfIRAC#A$2g=XG%g@EjCk7Q2;}wMbcccF&&CSwUOj}Ojzh(U!y`s1E z@Ng01;qmtN=Jw|2c6PJj;T07X<$?0?@bPi|!{BoFb@DLt;c{|k_#X~(R_+#Vb}k-v z&Q6g3a5OV__Vjo~|4-BZWrCxNy88cS?Bve9sgt3e_^|O zXj}ciYWyFu-F1Ckta!Ao+?_q$EUf;aGyD(nKi~b|1^ox{pEhC|Zg&3`#q6V;vxTRl zm6M0E+$;KjGu)PTmSUDdf>u@*)@EEn)Q^Ump?Af&rVN{j}8y^_jY%-w>CG{*H%}SmlhZ1=VoW7rzR)H$3{oK4-X9v z^!N3C>*?<5>}YRm{o2yp)Ywp8S6fqERasG9R$5YAR9FDd&&$op&dSV4PfJbtlAM&7 z5FZyC6CD*95gryA5*!5k{3$TN-_O^_+so6#-Obg-*~#&vgT0-tjkT4fg}IsO2b1^4 z?~Du$^!48A>S$|eYP?ZbQ&mw`QdE$ala-N{dM)`%LR?H#L|8~rfS->S%EQgY$-&OX z%EHXV$UsjAp{1dwqNE@vBPAgwA|$}a!^OeI!USWWqoJZ8BZ2-^8@fki6coT~0O!BL z1Ncu(1)w6N@?DfTDH2AC-9|r(<^JGuxqaOh z@TPA4>Y?k?^wkpr78D#38WtWA85JE98yBCDn3Vh_B{eNQBQq;ICs#+1T7;8@pP#%; zyh5Cnj7&&Cgg&pSx#eqXTN5*NQCZKo^2+{7RyG0Z_R+ENiODG&dJ&f1g>M7P{X-(t z8=G6(J71aS77u$?P6mZWc7I%6UElZ!avuHaJAEYMy#4!v03az;@c!894nWr_5TUCt z7>K~j#}`oIEf@;J=eOS&xzP&tBjPY>jtqML{R^v3W$Zj%@puwchtWW}sdO@1Pmaig zjo@eq;V2RUrnYYiVaw~a&gq>kP{77#R*S>~Mx$U;l4(8+S`Q%4)|n1~k)s3#uuA!h z0-6gv9oVr12gh<%mkon+xu?g*v_nu)TUlXC)M{6>tjff zqs+{=FB}^28Rm|n7;f)kySh5=XE31Igc8StN|(z6G3hbO8!lqh3VbxvJ+Iz09$8m^ z(;4s)BQn`M-`;LG>?Z$uNZ0=Xy`*E*Gz8Bq`0jCx`{+mQ2v(sR^AD7gtuO0{UNyi0?pt3PC^rgza zr<*GY*+^6qT4e~K#W4p(4D1{qq#De5Ys7}`z-E#S!`Y4~#6a$h^vYft+Zi9fl82-J z&hQxte}tVL<@y+D9q|zG?X3p-qO!9Rajy|F(d!%y?89UERLlhfnP;VwYafDB*;Dh} zPTvVx!eJn!Vi_O^^V*cXyfE3G9k;xPn2kWP(89E+dCqT_CxYPS_}#bSDCxTXXTPyZ zOmzOv4%k~zXL&3=@+A#g^o5QDWV zIfg$v&Gt0+2<%F)F+;^y&M|xb;B;1k`ZZyky_M+C&O)k@aNI7e7~gX?eyABOln|la zz%z0y6$B-Bu2%Wpe;|h3vs}G;Jt~uBI$bCSgIz#NYS$`wc(cW@0PF|VGQ(oWIlNP1 zUtkxgs)Nzkwf8d_Mes|v!7DU1k49b z*>fMOSe-@j`GGBy{c7Fzi#(Y8LdzqU@4s%#@=LqbJCf4u4Uk*<4{qo$tKakJS#%3- zn`peW9tQFBIgN&U=k3JwxIB7nRjxR)Z)u&!`5v17?(V3t;b`CzD)e_kbb7jEF}RHv zmzj&u9cI#+`4>rQ$rB6ZggK=`(MZ5BE)b9W4gO$UG%QK@bn@J$rrRVda`&@;3M{AzP_^raFwKP6c!u zGKxXbAD{b+b~ZPJ`>1=bBDNi5K(`Iz6cqTJaPc8*#pQut*!2-omS(7@5upK`H<1QZ zDg^v4@)$=meyqA??1d*XSL(AEj}SAGW!o=YB(CVp9ve9M5AuB4h^bgB0ZNMb0tKNz zyi86WGE_(+ikJkN(Y^!l(Hmd_fB)oi@pqM$D zzB<2q>Z3GeHaC~0UcW7P{e9|FKZ9RS#ufo8o5s#8f8Knvjo|bu@=Zx_z)Nhou$z9o zF#%EREaTy;D8q3ks74{=?=Z>JNk|OHJ*B_7Smr}ps0BEr7(o|z%87^ehKK&HcuwF@ zMq?Bi1w3HMht1C2#n#Q7;gN&BSRv}$fgEuO%a@5e;{u$;DA5QOu^pDH+(ZerDBoAL zXs&vdqfPqG@m3rTGRUTyHamzAR=cDk?;%HrR%Hoh+;3FsEmQOa00e7cHON|@XXQI7 z?y8gDRTyPPBZrkBRR+azGSyn)+`rPvF_=6z5guD%c+e$IORcsg4Qj)>n5thp|Q%F-R?(;w^tlnm)dE)add4byls#mBom%+rM z$S0tOIzk*?YH$G9dsDyjY}17|;(FUb&R`H4FYB4@yW3buIt13^3=w}?XCHd_?#Wu~ z`_Zp|qSW9!mDeeB3w*$wUhq}DlQTFjXMeGAxtn!N(It1nN+@NCGgh=g;J$N(;~c-| z_o!9+@^cuQzpYwmLDWtkQhk|IHm4YBrIS zv;GjdoRkcx`a~tB5Bb9{qHy;rGe+kGPJluK#oG$SO5|@lSMn52)`#eOhpK zm8-pIsnJx`uw_*p{+hv47MQVCkj|O<#5mn?x!>oeE*<-$J9;OaTrUhx8ha7J_q1jN zKWpMuxlr^M3t0?j{rdfH6qo%N>d92^R@(TbSEs+*E1f<7tJIa>G9~30b8AmF^xVJt zX%m3^a8MZ+Z*ujtqui#d^ul!=-kH7g)=#jXWowr_KX?~s`e6wYU!kP^ye|)p{wDaw z&C5Li?LDx4EFVtje+@&1CbUo3aJ$E%QSAj8wom0l+>C54j-_l|hB3Gaw|06_*_42o zQe8iP1q>cbCv?nE$6q904bSOhG&(ta5PUT*kE?M=^+n; z1AjMx2Jy+bf#6Y%7va?Hr^C%$-^sF~59HU+$G(JqtqDDwB?;=fGV$08-k^^@FJKfx!Aelw+LNEvb*ESv-YLP1o} z5V40);fxTChagCA2oN6df)_df1IpUsD7%F!MH0}@d?tp5S=)xt_=M}Q1~X>h8?|YC zPz+tT{$NTLj-Lde1R(Li!vn~|0?NZt07#Ckc+Tj)LU<9qE?%Ct5dbp~uR-|dhlo#P zQ2`Gi3Oqo=Ax;>&aaM3-nTr>AHaK-AD0oDXEXa--^M@`LfMWRO! zLZXX&FtMSa#I|UJe;EK1FBS?{_%bJsqG*H~sc_#7hDbZj};}&_Pg_gb8@UqdBe+%m=}W zg=fnSQcfJ50qjR6V!{*8MiNPO6Y)lqEBKP;ZIhN*alVSg{GLgAK~E&-Pi9ocq=$n1 zBcqSo5~1z!CvGY4c4L3GMWeuhugUa;Gvj_Gg2=8t5hsaNdtY8xU@+GM>Bytl>{3)l zlerDk5Q?KIrjwD(!Im9pmpQhY zUR0PVHj?&Wi{5US`kGIWt6s1d!>eC32X>t*>zh-%nu9@?G))%!^MNhuH(M`j?6hs{ zsB+%PPAm~Q3}q%WPBYg;#15t2c%992^EB5#5x}6CMcW40XUj(+Oqex@mtM{rRB}du z#jx%~!$^Fy!r=Lb(A0{V(DPsUTBLB0JsLe6Sa#xtE>3|nN3q0OvgJ?NhC}gE z$;S#P$+JlClToe<{8kUGREB(s zDpnB-%iqg8i3EHo1DrnOyk3sIVNE!#2$yHe4mK>-UM~FvD2r+<@>DL{uq*SnNA*<+ zsgcRd^vz+bNH(toqt|~KfTyx3I%64?3XY^}fOCPY6@F3`z4T>t(G`Ho3PkoFX1PC= zihd~tXB8HPmFT;P$nMyBU#j0F72iHo{zR=r0#rzU2MSSGih$EoqpJXwRdi!j-D4;i zriFPFHGzf&s4K;Rc7n(@&{xP`ELCcdLg2$4DYt*zp@3R@)2beqlBo2O>t98AoG9WT0DkWYX zF=e$AZ#7R==`BXXDWu^*Is4qMHmE%#3Jt^n&9$6qAm6D&C2KT7DwNU(Fs-9t<7EPE zo36*|{n4t}JCH7-aW4PJgU1@3NJ}%KnqH6jSim*PA~H58nzz6n2*oNCdH|BZNHbPc z3tC&5mOnRI2=+KdE74iAX<3Vkas!nCa5)+kNgHAwgEu@HB=eN5jRvyuM?sCLCm;Cg z)KF?_)8OTgT}BbK46jU$!7c_>q+ostaDY)HMp@#uHS)DRYPQk(w8?d{L+jh#;sIDy zKP6Ho89T(gbhfX+Qp4EWOSS8~VrsusHA3+_Am=TP%#v$(o$GotPZYN9+KZms6_UxasQ8BJTRE=GK|1?Z>3bVV^kAOf< zV%;kBKvb~TvIBl_W((rHye6xsTC{S%sxZO6Skbprs|8UoSx;}5P5dS8d z?F$45K2&|vzynss6!vE0^k90lSLIBdRobY0I%ChreEzCF9>kOQZI2=k3@0XiZn}Mn zcvT5%Sni|6>Q{$?POIuU9J(-6yD(!~xdM9Go{Mod;+bNxiC_)dXh_A`D9q4KaQ%Q( zOf-t-7oh2YoCLCRcCA_tF7Wy5+l}h}r@_OM`a0kKm$u=n$RSU=;kPhPqH9m~@@#Uo zDB}7dn~%t-**!eI*!mJ(o)?2+4`oG_iIYCDI2D}t;pNli<)~DinwS}m7o$EB`KOpj zl$cy|H{TN+aznb(!UINR5{H3p0B24tuV)@4s&+)8aRWMMfn;9}Lqk93M9`XQW@`X@ zQ7pF3L`J|fGE4c`eq+Hak%{aM8!)Jqyq5kWEav0*2ImxW^`ORiW1aHUl-<-R#Ska? z*wOk_sb3vW{dAbp^beouQ)qvW$l#J1GUZO&DE-WN&J+o65w^q3LDYax&C=}H8w>)uqn}KVqG=;N5;xmwHc3^l<#WelSYfBAf%fM zr0nz}^4{fy<$}kJiTZ4Sbho!cH&Uzrl5*^V=HyDn%u@E#QX9t%fZDSpuszCqQNp|9 zTkopG=sdi;&HP9Ip2YI&h&9!UHQuo`x6YNZ$@Y=lG$OkNxo&UR4%z(Hw18z`VZma|NdbhYtKM_EH*$#6~REu|H^WUgo zjGf5LJN|s8eMj(3arkVN!6xJ*2tff|K~Q@zIu4C@txaZb{lFG5Z{3x}1pIW|X78A~ zJD!XF>z<5FIRC?o3c`ofBgj{u@T+$n93PEU`;8vntl?Pa!nH0A**oUQKEVd@!T706 z4zSBWG+Q7Z*f!hW$(P*0=5AuI_khph$g85FeDwnXul7?6_UG3De`EUbqZ%}7_qAJ> zRM>zhGaxyq1E;B7fabQ^RN1}VMqg|LizLAywiiU|HPj`Q!9IDm|;^Bx>$zWUR-vu~|0@E;$Ssi1^KfoByoWrc-+ zn%dktr-GuHyS<9@zhd=ZD&A!utMo z{4B*)aXV+0?W+s z|N5 z8F!9|P#@}jGe+szd|h@w=M*mSp^>5fYKZc#rDtE!?sg~d?j`DAP+#Z}-TkuW*Yzd{ zyj@*}J^Pt<4yv_C8wEHQdLX6s2){bh$ap{&KWB~Jo69%`1ayAAB0+IEV^27siGOs% z@?hNAa>4;DzZyuS{fW2zpyAM7@$HY+@}F;`_pi~OcE#2-UK1E&&bDdX+G#wd;Xd~{ z!v^Yh&0Fu(w`cYoj|l6Id9rdad!4vkqUW4XbWiTR{eC!=O*Vp&F~{N!1Zcd`Q4&@y z+3}Q4@BST*8W4s*($0U}o{}?n;)BedPh! z8+{VhEAD0LQ&0k<6!T$9dR^}zXF*X(*`%dqaweCq5o2^h$`mMuc(Tyzr(47bX?6n* zLqiJ(7q7#QtDB$qzaO9eB7lF|QgK$qSUSKY?6MP-_!wM!R$t1wCdlkbd996qG8a;j z^H7lBAB|IXgbm%1QH&nMrFq*k+G+7>sCUDJU5^&pzu0Kx7s%}xF}#@>W5!e|<&<4m z$5bFJu=hulCfUEOQs3wuMFm(YffceL8z^aL+NPGvu$@~zbu|@1{=X^W=}zxEyO2huwUc z*7o8!^L4ICItC`6FJJNQz&HW_Qnxq*M$H%`LXP9>^hcJ4D>B`A8N*}#{Om9%J~2tas&)n;+-3Lq9g;FRuPyt(Xd~;dA6ZZA-vh(ZSaBRB0kf0E- zNn*UPZNPaXK^?P0f2a6}pC2kDn26iv(qI9Ss}R0cta+vf=_z~<>Bn7*7U2#e>)?p z5E(EWTr1F+#okWflwoe-UMRsvabG>g0sUx^j4!a-INmmVu`sV8jba5CTu3RDo>1c~ z(iNwP{wW{B-AEXY2fChLdgpq`3&TU@TF_)pya2(R(vXYQm*2edidM?cUI;LtF+G@vyp5ixq5^8VL57s}v~MFq=V^iZ(CN-xZKH%@p9fGjvO;?T8IO4p3g zlc!gA-ZeyBJFQ+^+6loInnW(>NOjy`{r+wt8>}xPCMfvcJ=H1N@xuQH$Qw-Qxg+KCqKP;M7m6QTc1YP>FLmD@C z8ryWnpSOn+NLNvf;SF`1P5}bGaS%Fh{91RW1>Q#TU@j+AZHDqsi56(VgYghs#P1&u z?-@7msx-gT&zg?}Kg6TfL=f5Yg)Yl@sf)piy*5U9wvVvABI>bpQIs2w8CWILeN+=) zavo{0pnzaBnhY2Kq*otIHn@Yz1lq8skk zBMbUv;vIg8KMBY=kpfy;OA^=4m;zU}T#?<$O`$PpshC;M0_h}VAJu&SoNt3YU{Z8A zNs;oL^hg-jFm%2;AO)VG#h$dFLWz=%JizRH3L- zpVb!+1EsfsDV{})mH-l|(7aE5V=IUH#x$y69e`${l#1n)=vVy?vE(wct8wcR86efqL4A(TD6(kMJfge&B_` zsq*oyh@jVJkpm?5znv*NV#G zOC?Fw2W*UzE&Tg~?2f5W)hULNl?EZ1$err&hC73oVnSBp3SJXPBt=>T62E;)|I8$Py&5MqcZp24pI7O}(^nf~HEl z6@X1&5L4}j^MSr~bjs|!FThu2B8;!6TuJjD)7F|K9s(Z4Ez82mmkk%m6ttL&moz#bN`2}^v2ftvIBrmO{KG9*+fN`3<+Je!+^`mGQ z&Shac%Ntn~$*2W1)|E(Op`XY$RMxx>zL7hxn)4`j%ua@OyDl7quVzL<*^`?vzLoqe zTg(JFY2(Cv8QqdVaTUG0UdDQfTQdGvmuB0O%=qV-nI@e|p4f&Tn-5PUbX1ems1UJ+z*96`GJ2PXibvLY>RFdrjFd<9j4F;zd?}lc zk$2BBLsdZtEv){2$?)$et{5V;x9r*yM6D=GkZLVX(fIqS@BIrF543Spx{iyfNcw<9 zCRnZN#rs$&c#KCxB)?rCYdtT+D;B4SEg#@yvpCFv-z~sRYp(!(2Yo^b+o9Mo&++nl)6`RNK=HU9 z7W}UYpo_>c{?a*%3n1gtPv2Vgh*82$x3#T`>VR_HJ`? z8=J;u-+V?eYcj`VN5}xXw)Ue2R$XL&%rIUwKn;qFeS(yG{oICioN)|<;@y5kxe|~e z(6!mB99b}(!4la)9i5TY=I*wf!I)q>&{jvUh`oMB24&&FPp&dSSV%=PC{j>xE7owv zcL3QWYz9~VSHfd}BY34Ax92c0-0pPj8ed}oSx@uqLh2{$EXra%Hii?3qh90VRHP=< z<1PYl4NVAken5{dsGdPFhP0546)f=-GJ~`IPU7|8IB#e0)+aWvFHj=XF6jXTVGRPh z;Gdy(Kwr&y0bce>1Xq6aj3NY1go5wkXP*T+tBp?3Ac!W`!Q4$&Nhv)YI=j{b-5@8a zVDj(btJFLlaGgz=Vny)MobF-$?w#v#M(Zt%4!(vWC!m?aUL>_5X_O%PRa6P+l!sQ7 z?|2c3_MeTaQpRHKAX1RYE11)bo`7k_pDRQ_JrKnOO4ghB-%g6q6FK+_Yq($X9>J}3OpOxsz66s-@ zKncdMADf(a^~8CDB?TcncRN882H{^ew-mH?)|Hk<$;izR8q&rrEr(tFn)jo~DC z5O`G2uyi7lIvJhT3A7_K_$Z=mnV%R*Yl4w;oWaG;{Q<7*Lx~*Z^Ry?tR~M%E`14xT z+@Oeh4Dilqu@rFcfVAC%@hXu;>7n4hIYMldfCXAEO_|vy4K+Nr0(=&o7@5{4WQsfK zR6Oyh$iBN`=BGgk!VyJJMr46G0`QJ3hZdjsErBc_HWr?IiwDudyvXM-XnsfRsSUWM z+^@F4IKSnTfzitA$q;YG@e5XZ$2e)~J8?VCf41%V407R~D7@mu!(C9!nPWGRQW@go zRlbA7<6}!Wt5SOsfus)EIpnTjDp#gEc>6U9y?TW@vX98huE=j_6VyqYk*Rutp&mIoTqir-ziw%s&PTiC(#^87a<-*}2MOpRz-^hKo!o@z;*?^Ul{*DQ(hiFUsIK^*Xx z5SmmHrus?g+VEq)IfUj6YCJPHN56C0P^mYXfTAQEq7(Qv7)8AY~ z>(X|sZI0ERbIWiKOnECn9w&5&qNmymx%aprIMdZ>*)8^rymUr6K*6Jo(;Z)A_t23W zkTvKg;%q;A@BbEg`2}F zP(xLSrst6HGQ_-$Nb4%b0u#@IxwX~xx1y2l=#oPM+0b#(c$>*4ExixSDnRe6QcUtSY(N!%iqm=3O_Tks z`IZFj65vcsGS{~)*6w`S1X2M*tpQ9T0d+z^KkqlxvnroSAfc0vF!`11J+iaTljSGliv9RCHW>AX$B{j zSa<^V?Dinx9vZRe6Ujp6u$qw^;d2qY0!&;9G<(|uP4T)B(Wv7>t^|GNxbfri@(eth z2K$mVT@Cd3gn#~Z^`e9IH+1${AdW-Sn`tF|)5kT;H?Hpz@CI=Y5F-2BtgaZ=`uADD zm!X04NU!#gmeBNJ&?C`;oFMAg6LUyh$75O!rWD7{8W|VSXL8pdGViLd$-X$5 z)KZ!0qDDxbDY7EU>g^hlX*8t|sZGo18J6<u^1rJL^??bAMl_L=5+-ik6&>~ zRB>%JdAv1)pTfaCKJUk4-fu=RUj3}6ObyNSPcp*TZ;NAQAtHogcm1m+eJkiG`%iZq zLGrTvo}q@T?M3{VuA&`9uR3<6jEWHgj-u9CL-(^-rUXTF*%f4JOliXT%NQ3zr8RXx zGO;AP4qNFZ^CcE9Z%ePwVnrc-u2DvLj@bX|qAm|yFbUC7UEo#W-l!p_&j8AT2QK-s zd4fS0r7c;fkzZ|6bUfev!2t*>kF;q_P!xxNVR5bD3hJgK4XmCp68EoiWe5!SuR z(lAzHC~i!B>yv#Sul3rwJ~O^*7O%k<{=umO*G=qb43Mm7l&XnD_ZaDh_Zlb>X zAo}hD<&UN!&&Vl~@G|MtZogE75g3%+@}sui;qj>_EG{jLyzvr+Cayvip3d+tIFPcY zJ61y@)zs!|+wVbZI}$NZe~p(+?BYM@jg&3KU$7dpI&LOgl&>CFMp*r<8@eIqtZRa) z{>ig{lE=l1qR3`(Si7}a3|GZ3QnFYc8#T23uq6@tybbTP@)NL-v%j5AbQJi@X>ElSeyOyYUq|9=?w|MAC?wanPFMcH$&mBT$wItE>zVQ_ zZSaN*1d56qCjVA6Q3_~|^_3e3Gz~9{WH{!ObNK|nsJIP%{T}!%a||7EKI9jEr&&?h zqewdVc^)3sd!n#0DtwVeY8d`jLG2q)Ax|+DyQyJVd6vJ_7rKn$Zvi#50lqKbkk#dh z>1V|$1C!>7nLxSeqJri!Bde5wc{B3q*N!m=|Ea31I0FfZlxrCCVfIc4zpHI(W!CCgTVXig_gGy*g1Q6vct52vKFv=Z|OC?v~HZManu>=&_12} zH)q&+Ul^;hW^EyfB&-D1+b)H&6jA1Sz@MbA$nJY+xYbHWwEG^=&arW{KlH)EUE}r- zy-4m}-`Z9tU>}#>FN~&}!cCRth6d0f|lq2m36_I$5v<>Y2FEvdJ((P>8BJ+eG;{QlEbOJMMcXeRK($o`XM z#rxxPxy>8i+sQn%Kc=Yq>o?!*3pORgDk#1_S#6U)@#W+(^XDk~wf%hcxkma!zpUR~ zXF8W_ScT0eMJy@bUG^IdJK`TUqK%mn*@M%&nLzOZ8i3yj5v7)v~k*D{AW7m~5 z%iTHoqcY-d-1*u6;JQHiKCbY?OYl^u;dw1BdO$y2gRKPpmocWM(*SOD2u+L8+l$6E zuAwo?j{}`S>GWG)InM+;xqjZ7Ai71nw?a~GAG7-;HPDg4IG>0pK4CGlbMpy_i7`JY zztR3fEn+H0rR(P9_bE7h4gcg+I`UU_N}7aAe{bLLaIPbp0oRN*oe$l?CFjVkfD}F{ zB`pIB2M@on_-k237Dwce!(_w9OP}2%mFA$Pie6M*ijC?#6)_qL$)17luL}!JEX9mx z&aZxkRHohHdpf23>7VE;T+#A#vrCWiKLeav(=*%+h^H*YZJmrdIw($AobXE(GC{J5VButa68YcAa zZO3e-L5aScA%p;i#o@)d+K8~8Ca}}2F(d!55zy^IjswU6sWJ&JHU$$D%44{J&e(jQ zbVXl+%_;1Ccr8>=&8ycq?EBfXQ{`0RhOUj)jrA%_UlkEAp*NRm6uqfgyW=qAm=1X9 zUmA1Z1sr_U0?w<`xHot zrbw}_evUp^APsum(}bXMM~2w01}pyh*~<vT9R(gZjS8WDx}4I)on|eO)_(cf{v}bS4^Y1C7|RQaX{|C zFnb6y#ewaq;A;EgU7K3wn1-|B>Sjh+>E~G!PoQS)iP4Lf8gc>zUv40pRqxT;c{&pZdQ3*6=gt! zmZ&pXGL9Ng(Yvl=c{{-`?|)4Yby!2Q+I9UER6icsgi)ndYX>LcVL9FMPXnVD(1YF%-fCe0c5u_VcIwzJsJB6rktjzpR@6S)r|v4G+(8tmG``F z_JH7G7Ru?2LDJ;gexHZqe$p%}cbAhK5=Qv}PM1ki2-(|>N_aObmtBzX$_^FQOgws; zLvU=x&MWRn>AqK`nqDv%PH0BF%0q$*F<|pX;yCP)4L97Jia`+`LEoZfW@V_fdeE#X zMV>STM$bWh!jc+7IWzSng!R$^LggTAuDog!p@jM9EqAkK-ZCGQ(7!jVS~R~dbE*K5MC`MSxNPf zuR5kyU=SVs5?u^2feCgMDVv3pI!W43M*eJNEb^X1gL&D4gMcLH#$W^3Jq7tp^6NOt znmVzVrV*!w6=V<{B|QXl74Owb_s#`7YR|x$7u7M%)9GGJAyyIp)2Rl-1}BOAhV&S! zk>+fswp01Sz$U?H=UbxwghR#|QT$>uPf2Xojd2E(EqM$xr-%$g+iWs+-*7cOvo8qa z3Vvw}H12)@(&bJKp$e#g80f&?+Zc||!rm7xc3enyxFUNG9aI@4G`UI67w~l%_<(@8 zV{)hwsaCB`z=rlJ8tg1czMjPhZQfuAs$f>uIo-Q6P#CCBbjea-E5?q`JR)O30bdR zkL^iLnk=Mhxc<2ehrKr2>kv-FG;1M8OqMUM@;xZ*_m`0Px@2=`BWzm5o;Tv$*6l5iXm) z=IDwa)Vx~%=pp&nzT2_S%;|E|9d-|yR+b~*3G(r@_{!B8>`w_PkfLt+k?0zB=eK`y z6kPm8Ka9jg;^b&mK-u@wweC=e)HGF9-50W|?BlXasdq1?I~DnQF*y;ck=~yfkgF`w zA#)JlrY9UKmPsO)rGUR=fst|c}~fPhk(xVEHgtu%W&IIwKKlB z1uCDVz0pu6Iiz1ESWU9un>=E?<0Q&{po1td!W!{ohfChohXq;8riAGR@2*ZRz>V6< zjXPF8h#k_xcQThJF_`KNttV)h?{oHjjX0~{fjUeDOt%q_k82qB#HJ0Q{Y12Qfw?Rf zDkp)U-U?MT=3z8Ybn@<-=S$HIX?yr?)~QZ&hh zyi$bM6+2yFeMk7TRI#ud>RlE&>enfG=k*)kP-GY{L)<*|YTue$SQfHO6cP+i6A!Ns z+~}~e?-Nj^B^2cUctzJg5}AZCK&prR%?iT4Lyy=#p{HIj-@=psg&0nZsxRpXJxO?W@0SUD#J3>0=gKl$T`SLO18|lWtz^>*LD*VesMgM~iBY z{mLJMs=>iBCgidz-&OSbp(-+mK!|`I8P}kKq#lkUK?&}~u6S@9K+^O_)S3Xz%=uv- zy<4y>AF4DnnhYPU#+__6ewI4B67DVSi72Y~F4gZaOYAo)T5gQud{#)GEY0PMu16zT z>EE)y&3;dzph^&~wiIOYCs)#?q}rFsLz4+1*H6QAVf0I#O5c#tVW|JM{E+W!F8>rn zf2fG(!s6FV92qm!o1>Dqs2Cr>iW;U?Ol?%H*v#%!RJY2(Hd8^0%yLv~yLIhpEYU#2 zTd4ziYqsH?ACnnlM7a8Kj3S!*^N-RY62&Y;rNI(~rq3u0VAR(+N8=^T9=@#-4n` zZh5INEtvu3A&jL`>@(wcV{}v(sy@kklZK_*a|Pz@lgMvWP?!r?+L$MHk0pyuc5ZAe zs+mXR4yYKdtqqn*$_sVrn97Gh54`0FqBK@yKdrgU{GjMQlHh@EYI4p}MmN2^@H5$n z%B={Y;aYeRtq-0Y_lUuMBZ2J{D>yGx*d` zr&stP8Z+xnVOFi(Yxn_6^Ql3pUTzj1k-bh{kEydtj)nOtE&U0*wryO%bS=P=KCXNx zitIId0h(l`8N}Y^5>DG?CUamMG;Ea*X5u`_D~;avi6=FFVXP0dC&T(cd1CuI&$5!w zNIRvd@W}ji^BG=GrCDMHr&}q4QLR`%;}Gl8UhTmiz|qQ)VqTV?ie!UB(9pavHTusMaDr3ukVu88V+>7ZBmb()WdbsQJWUA-3fbjfANkToG-{hIJ$q z&CF`d$z*+@(aR~+SjA|}CO>D_pOr{T1s|=Yb~0yVmpsK;T^~QN{QAb~yPuVNt?}gT z_9TtnRB(1dv`OV~xeT7I_OIA>z5>#NjM>`Fynaf}rGhUnRU{WAq_+$SdsUtGTeYkuwg_QBAFPyVFeSWpudtg^Jz2Q(yV*u;r?w;(hhCN+qVT z)I;>uLwFr#PUY|T{$Dg@8GodutguI>tb3zt!X)ZuL~Uopb~73-C{D`#dN>#Kf|J`> z{-{tQQ2`$5p^r2Gq&7C-auDFB6HT>6Nk_?8+WB+1@n6*3z8gs)z%8kZt>tD4e zm32tRy+D?0FvB$hn2~|xUUx`uhVj(54B}dqE*cpFh1k^(tzO@%@TAyRkkp$~7MC?H z_4TE0-1ODc7DF**T|u2LggpNbWoP*ig%>sG2^boN4uK!t-5@b^3rI>ycXx?HcXvuk zhje#$m!x!qq=2x#yB~JH?)@Lmx%ZxUo{9+*OLDo5Can*ch1k1&pp^ToA3w~`>E?HT zsX~YC1vEbF3(e7UR9v0_9%ZgcscX^n9dL1L=%d+CFssjEKaHVR;lnJ5-o10+GM>x4 z#+15<9te~dt-bD}!k-3$XPjy2o$}SLr(O@!)cU%#MvliWT4k($U`~fY>wd82vEW_U zH&wmkWv$WO-6>w<72-e``9Zr|_t*wMaHs~EvwjojB6G5lsI1#ftn2O*N6W3tEMpBW zD7BB}B6bAE)``EZeR_i#S}nM6p^L#$wYfy$u5ADHu-mc@c-TKI1w3oXb$g_&%A-Pu zLBH7QW1Y(}C0+m9tS2a97JPBswsCY49e2W&Q2twL&LfozC+1$~5ztRG+ zG;kD04w9T71irm{tHL}10@bV$FCe0HAQsN9_`J!b?8IKzhgFDwA4=XA1`I z3CEx`{r(%MFNe=1jHErlnf@D#?ac+MBPK{WBxP(7@PVvcs zwSS=79@5tfoJaiT9tIJ0duc8*?IO~GSm2rrJs^01)OfM}NFVQV0No9ezGHQ$XI}h& zcVWy|ZZ{7J%cs_|PA*N39s1>|UqjQtE>U{ovs!<|VmMQqO;wpT&WBvZxSiVz{%qj% z$hQk-MLEXDvD<|`{=u}UTKyv-e??Pz$V{xp5=@sIPhTb^?BGcK&cK-Y<-e_!tvVB~*b>(3K#QfT#hn6#F| z^+j0;cAWKk{LSAy@AV|BiyW_aS~4+_(It@15{wTn!qYNUdbT7GV?FKo^5B z;IzK$zjRmES{jpKacXglzmC~*IT^}?w>mm*TE(aHJ&GCmM&Je zS@cN}&7K5JKO=_qQaEQcuM{s@E9e*>l(D!98SM5S-ue1(y&<9Fk2gOYX| zT6_nlm$wctAKs8wN;gRr&e?hu=cmSDXt@(`3C#~wjJA4BDPD{H_?7s~>7|h_Y(M1^ zC36{VJ#bdXQhI!NUFo7pcoS&t3)*xF^oF$|T5@GA=Ic@g>Nb4xB~H&wg zop8-6!@*3=(XO$}C6UH(X6IJ308D&?-$`hf4-In9Lmw9YH* zK_cN+5_j3Y^g2xmt0QeJA90(h7c$LrcP6MxOM`xO_6FFA$RLLbziY zS~8m=>f)TVKN%v|s(2dUygRUp=Gl&&vOg|txb4q6bPV|UXfSTX+5=Uku7!cIq@eK% z*$N|kUXS&`;$<{{6rurZnV=3;s(C2;A0HyiRPx*?N@3iGKzZ6Tow@3^uC|+4c(f9W zbqA#cUNHjpO(c6H=C^-4gimae0lm{*>B=!bsNBl=xP|066d2!1ig5A}iCIj62n=}u zAbp0ERi7La`mThA56h11NFdL(B!j~dBN|EZ{C)=BUtMfs!shmyHlmdDdKrhMVD1zw z5wjZtp7*35%cydMexo5ao#9Au>_qTHL(2CW)K$d(Qgi>Ut(=Yu>LU$T;+RD=R`z~$#Ti8OyH0-Y{=<|<&?SMl!Vftm1>=cRBfl^i1 zaA|AoMKx;0{T&S@F<_&<>$FI%9tV*hs=hNPc0HzMw~=P4PK`GK^|4D^GZHvn2XV$6 z+Z8C?dqtBWMzAOu)c zpQCsD-O(u1M1zDV!=QA*IL!{bBUIRB^g*s1{f8&V&+%zIo$VZUZ-IA|01H0q;OzZ@ z5O@pNQX~thboaRp|8Dcy$k^=#_i730pjk>EQiAC0g#TIFl?LhP4DHvXoRQ1nH|rn+D>bq^J-YP1wTl?Zwtx zk|+RuD3MYwVJbcBV@5B+jHD4rj8=r(%n@VJ6pD~T1)fV02RBfBd$i-^BwL&g(!0vo zD8=P5xr$(?_e+@FlkLV*2n%|wAN*_e1L%YgfakXEw4v%i5btNg)P4a-NlQbgc@Zg7 zU66tpMnnX9073ih5TvA~zNadH$C=)LsErJjhF&m#jpQu68cIoxV1Pcm(M)1T9-OrL zM~n%e7i(WYcD@G|jxJGv{T~8E#H0WbA1?@3jZ#KG9y^*`<1>N1WeF=^qR{N)FG}y! zUWRnXAoX+;+zA~fo=8M=`MzKh?;e>F1dca!#5_|bO>g}7nfYC9BoUBwA_L-PF^_hvAqiYT2?fm*SPOZR=sm3p8U1n%zE?s) zl|ADBvtw5@C!VAznZQ1FOA%iD3n19ZF-xYQk#DlyBN-yJ0Gn~j78|r8k&;&k?l3A$ zl)zlD!Y0n>aLqdDMagSP-fTxSRpAG5O6nICO%#^PxH!y&Ou?LzaNQI zXfaw)=tr%-Lm3c|zRyC0bIfXmy3}*LU8K9tbpG)X9gBiHF*s#P_Q#K^KXB$MWJW4I z@5lj;aEt11h?Z{?@e5?>E?H+MwzMbv!CyBEGGYVH)+N$4O_uJ9SW-SKn=0b?_uhDh zN`|zpIj^36F%Gwm-^SkI0L}0bW@E#H49mVtmgu7z-d$A?DIGS!4L`EAr@Yg$*3%n^ zy|Q}F{?7~drq4?zz$q+AceKN4os6cr#e4-d`s8TOD%r&MntHDHk)b#2g%44l_G@!&XxyUY4mmh>ojMaR=$ZN z7kZ2JJ6mHneebh+f+$IM2FL-P&y^=J&YU_C#)$-3mD6tu#GzQ9W9?LhoRjg9`P8Lr z2U!d&)NL4102-h))YL#nXG$$fZuQ~!nvg*4iXJ+%SJF;ICfkCw+U(+%%Q0m~LVa^=5vC^Ga{GPGQu3qf0T0RR zQ;82domZI2n4uzm7&bS6D1S>_>Wl%Vl4!e_wnOAC)1^DSLZ>&Q`tu^*+o`9!&2oCVx=qVcs1Gm3KY zdgZ1i`qV@(GXQ*%+*fZazQll#aqPtjm=zR__W9}udVt)^y7T{Lr4Sxf#;mPOYj^K+ zMQw9ZbzZ3yzJEhW?L?233EA95A8b}!1501FI3`xxb%5KOSB%=bRDlj5>%`>WJO6}F z#&7jC8!DKo|MlaIvidNv!rkcf#JZyQ2PU!&&^IdrQ?z<-xsk8T_<1tW#6?;&|DzdD zH0Qn(y`yj4d_pc?&$DEADoKAgbS#>~Z7a;MIeq+q{W^G+%NU+eLuxgZ{j`tEr zM;8Z$pppijsiV&KBP#N{t`m-7=r|dTbd117Iq+__gg2nm2>w|b(Gqpg0)VE0?ZKRPKSBJrA2)u zXM1^-0RfFS;)b^EK4&}-9T^B*hUKMHU1^Z@QO3PCKa0A-P$N;AdL&67fi4+?UVlJf zp)M4m8_Zot6ipY&fJ6Yo?0twlNun778gMFeOWkNg7GY=S!ub%jW*> z63Ub0rjVi68D&|G9wHfG1YOknK2cP;-2wYVbnJJ1v0~EqLn`{I- zTm(L-hOtM5`xU`DZxu9%291u!?=wf@NhVY|#Hm(AL#btd^w%`GDZ!5Td!GlT-Wmi; zyg0p`M|4Nh%uTC&(I;*xYrv06n#qCHQtuziL*fZRBc8@fjqhPbZkhl`X&9=n~g z6qoD>or1$yPlJ^kdKjSGb5fJD2$O3#%Dd>@n3)}yk;2;gJY0LjirM2F;e~4hAA|)a zur$Exk&30kqY6&qS~ODog@Nj~VL_mnVNtAbIz$oW{{m(xuK>3lrRb$y7 zT7$z%i@-n8z#Vq@OkWC^gJ;@44WW$$5PV_9w{N21RjPAi_SjLw4k=`>Qcl^Ms=rnF z{34qX5v(6ojq47w@|n|J8M*fZWV;Q2b^_j_55@_pQnsk+a!5Juh<;j^mJ(4A|Dwcf zpkydKlBm(kt3y?0hZI4rg1QmtC*PHmJg?{;A;kLuGw~yB%m}5brZVzq>K>!oFQ6Ei zu!j4@Yg?h3=7_>TuoBOlcCg$Jzc#l8g{VEvE+fEhbhJLJ4C=;*!?*DnO4>y(=cVC) zGqL7iu1g zn;KLBIa;iLRW>>%1dTKV@)jKgrcE1Kq6Y*VF?7L+(htZb|*IgCvM;7=Gykv?#0l`>7x*J>m*lw31uDU;e(vfXjvGU)b zZj+LZ52AR3aEXL+keAy0RG&zJy9$5RFv_Fi&yxj|Jq-)SY4SG)Ei!xwbz0U5K@Dx* zpyAPj9klhyk0erl_TNcR_Ievlfc zj^g&YOqG4!)63>~459H$;C08Mre8zDJ7uuy&zXev56X>RvLH-OJ&YUigcu%h?Lu~gQS>vz*F}Qq+mVD>gNl8< z^c>yEJE3rq?Ib_lwqQxbsdXL!!*815v4}W_`Wto1pqjUd?KP9>9Q|10*3Wi;CCrR< zZKY+=I|B}*Qhb?Fld7NP_LFIvLD3t6rBxwRq56f7X?5kYiS|WF8}i_7d~o>36ATfT zK?5>#LK>G|lBQ|jc zB5=zCk#9YqzwVoTQeg>#OHOzeGxi0BqViaaUCwVTns}mzJhe-LkGt%p3iRm)-@RFx zu8c&Iw=4wU7(*@lErEt8iaV7dpTBp14>sN`Qp&_WOj_4-n))haIUeGHmyH3w+W#ZL?O6Nj_r@xSUrW~XE$E8&KnnIFt` zu8wRd&0>cH4s=wxngsltKwo847coL0@DBoSM;0*ULo%yvS_qgu=2Xw@IIU~-0?~pO zHlJZwTDhb(z-C7G$e@#|wpwfjemwohqp2tFY_M2TIe`6q&E}mv3t(9)Gu*Z(G2*m7 zWZxc$=qEt#Ip#=8k+IlcToPx3Q`$BK=EX*@kOe~0&Biw9wqlKQc!LBt?H#gD*R)Q& z7biu(gFDI3VNFMKe$gYyT8B#tV7Ox&?1H)Wh(gbleb??%r2jQ_^6Eln-JH<8lhnMp zhAaig@qa>zR1jRD2|E<9Jk!c-FN4#=8*OwRic~0N6da`C1E%O&&jA-MI(x zRW15!CCS{MpjfssXr8kT1yYzEufHK)jq+V7y@IX?uJuSA)9yFMGmK}4*t@Dhmak-w z-z|uz-$y}6k0B@M(+)J4wd3x)WLV${Oc!3WcyS`&n_DIoCU0Yv3EiU_x>5kRh<+XD>~y=hyTGmfCD#%i%d~!wQ-=E4%?e^ez-6aLcI;7l)9i7& z!Fu`T2A|ncWqM*T zEba2@URZR$6)It>g>2^Gq4_pE)}`f7 zvA6PDO(|m6-dn5fh(Qi6K61A=xGcZ1b{}drBSDYYB!{C+UcajyDW6`wl4RYiqdXeF zKe5O@UphSNX-xWQPn5mY{4pW-V1Im|OFtyK4De?2?h0d(SPH0#{8&gB7{cyekEB0# z@1aW?l>EVu7VijnP?_?1R9x0DhaFdb(lL=@j|gfv8Al{B_3|?Nv@QRqDDL_4{NBm% zuWYinfvY#?d0FPe=X z?&+;>pY?1&EMR_xNlC;)GD<{^Y;oYQTy~Pldp3NIUe>~)7}EDXFOT#+v~DI)n6<~z zCtSIIxVG}5tB6ny36DD;&H3E!9AV$57h)!Q?0{M!Z>so6c_l{DNY-8Px^eYn6Ac++ zAXmjwm43TV&$m_lTujXAoEkJt?ESS3|0u+wrbj{Nx;d@KhlPFUVND#-tw zgaGl%s&YW`&8^4=V0WGb!lUI2@EYNYt3#9a`-?8ra5M`~PR(B%EGjV3QgcnXnwwiA zEO$$mKvgU)>+eW{c5v>TLk;C@hPX-W>L|DD7%NFIq|K{p6A+$_sWrG@N0pda( zkJo$RS>gJSdZO_?qKq6Jr8ydpMB^UjBNS~n-;fOO7hiRe7Hl3V_UmL=_E!%j9Tz#}; z^cD3tj}$sTv3ox@U=WVrFLi#Dko#F)`g2zOQj)DSbWcPV9JHiRkx6M9IDcQX#pA!v3kXO8Ck6SD>_G_aO{6K zObxmCK%UgV+H`&s>;bhv4nu?kT^>`89n;B)XNgN$u=`o^WUTH#ngG3>+lI&1Ie6gXi=C1OsZjnc=Ju5K z4%#b~xG4&kYm8%cSXyUV7v%b@8PfbZ+(uzb=#$_I6m48wM0w5pxQ7ju_GD>`PPL zXXb?vE`S4womqgm!w)wd(y1DvV5GWaVGPq;Kl2fG$~tbS6*05pKt-*{!;WLfymr2X zmZ0gUH1s2G{BUHXm|sACfdX?r)~1uprm@0xfu>*g$oTB@ zCp8hU*!;1_(EC5J55bVqszD{Ir1CJ-YgNkvl3|9zh*~aT#cwpM$7Cb7WyTn_d^a3o+@S>5HspGvH*;`-;9QEenl!SI9%YQ>%6wtv6 zQpPYpugt`>m9a+P+Q$;5m=O?mpDGa2oC$D29CBcT6qX~yxS8J&Kt^4-MU2=-b8NH4 zr6=w+Rst#tl4IO_hAvKsRUeSGt`!VGmnp7{r%QGtGk!FwaEEoMaUbdg) zQI`UAs>Wzx+Zp>~{NKoshiD_lHzg=|Swm9Lk!8Xo>sOhphX1g6&9@nln#B0z!=ug7 zD70CunhNbWSt)C|w$Pd9(^!iw2SI5X!v2b66v51JG$O7yNQu2>vvi^)B~n0x=&-?! zUPzEP(XxUJaNyCL8jq%Qhr$!5cxsaULT4=Km#(oB$K7Rww3?m{&hHYI&9x{!g569K zg#V)Lov~(YKTS|kwaEBiU{>OU_@a7Gx@6lBC;KfU88-IeInO^;H|wrzX%I{$I0u}% z?|YcHe&5Ic3Cm5}-nb`-kz0(;khk99+Dn;C7A-)TT2X;q=qR5{+kcfD(baVGrf@p$ z%_9^HxL%{3@hQi*i^vP|J{lS?>>(l61|GyvEiz@3U04UnBSZa~<_4?n%gGz%Y{^bNM}da%V_o2u2tgZxuCrI1rH^>)i-MC4MH|aXz#S#i{}=uX*u-I zyu#x8ixDbQc>bQUC6|vbI&VeSrdr`0+TxaAW?(+m722~O0+%8Dzow2mNV48-ghX+q z{?7T-%G!<&OKl!DmNiq_)|>LwpZC9<=-=EZp5fFBY5A*pg=g7LkIIj3*kKSD^zD~E z0{W$s#bywc)YFEb288&*bF|QO9W_(_X80Rs`>f1PqUK3sa4*2b=}VgsDG|G=dwayE z{-7W2>42F(YB%2&BKMsrPd|xyjMv1aeOn&qEV=W2@ZDlV-41Bmx?IvPpZYUm*YK36 zd3BaOd0c?=*|-dU&7Ku&_-yTihQU>z$90>__#5!WT0+|L6XW?)zq!oG3>tK+yCT;L zgQ#t%0`3OMotg+#2X+Ik2ixJ;XCg!u%r@@lJA006o*y~I6jJ}VQDxhbyGG+Mj7h&k z$_+z>)(dB}U0@z!lT=)a&aQVfC!DIiFcl!TI9%>&!3gYmxI~4i*#A(e`qNtvBVFyP zAb_$BP)uakRlM0zfpBYKvoA1QL?;V$vnEgdUtcg*IOsUO6lp4<0!;K=Ibw?s8KidArqw2~+UF$*EswUp6~5V3*KS0>d=?hX|3z`$PXRk7_F zWyYYP6VlH~gar?&YYdS!0zfDfy?X;8X*R;xes8XXx8FnMirvQO49@tx)Htl&=jQz4Eda%a7hGaRt=ok5#u$F`^*P7){0SXjzm6o2!{JGGp11V zDU~w#+NV3`KC=FKWMCq+=XD3mehMEsiY00#M1Y0rv`|Ci{VoVm9|d9-wPUP20<2Qw z`|>Q*%hG;Px@U!&iAe&LW##2UKvAiNt#G!Ja2rQ@Qky0=x6wD8B;-|>ykONr# zWActLW5xT;tQc)_yv0z|hAyR(DKybmq=$|+rd2VL-^v7+9UDsO#4yAszk5XW$~W&x z2$8b(9#Btal<|9KlrEsRwU>xrGag&??Zt%cMEx*>8KB$gj0wmgU7aE(UvLE|6G@^#hXkscGlsF-&R05C;5 zpe=X)5wwMpo9Icp`v>=5b7y%euQo3iGzLD=QVQV!fb(GSdBl4}Nxd_k?-YvkoeTaV zm{58UWCpyED9ie4_5iI%g6VXkns@SGz(PLH+#_N%>YkM48$ChPq8Vud<+P&7V+8hi zfb`gRUCx|dSnxFpzRZf7sV+L{Rq-&6P4}`v(qX~h_+lH&@c+Dv=d22gtuuqHb6g{X z%}DI-SHCBmV87?VAw-SUh8H!WSZDi|`m_TnG&~Q&AgU z>Qe5kZCOEthN!k$^IsMQ6-5n&kk8*yL~E}AP1SV1%Xj*RtLy7&P(SS&hHfRH4}jc z&HM>mF9mPZ&O{Izud`ODX+BX}0!drl7getz5Zg33KO;5j=Cs*xUTM_0|3p;NZQ77; z)O>C{PHsegU*5VJ!msCH^1i7I_4~}pH*!>!lbs@0M)YN-=CpQ|zL}ahyb1sFEy_D>QHv_@WeUZGwzOwNXa0%m(P5V5j2^K17jn^J5sh4EXz%hmY9B$C&#gt4 zh@#Ahi{V|$dEa|ayZowg60iXjA98RkibhCEQq9_1hrm<=!om4Psk3?DO>kNQ| zqwbe)Q(RQash;ENvWoYoUVcBGskX>jU1Ve;9*nbpPO@%`o{rABBs#l3alyA_ksm<6 z_XGS#EnAsxSi(tncohQCgP!p)z<4_`nm(kppXs!>khm|6FoOB214x=2goekw-gow= zZ{N2Ycg-41U3`@~=q-%ork6N66nAl)C~jBFu-4rn9vw?e8Zax7#OR$&oE${inJu3{ z$uWd-)?yXa0k3Q}r9-bQ8~(4+Z`B$=ol%^^XA`6kWiM z-k-(N;Ld{oQ7AI+Y}~;p3V=4sV&L!v0pDo6h3ReTgI)~jv#IUc z`Rv7PO zwoS<%=}{x)Oq&3Xt~XPt0!b+P>hS>=c5~)>?P<|~45_KDw*E(9V$qRV(j0)P-#mKs z7^&jy%+u5r3nD|+Om!vwKVUws{Q?^0beiAz+u5*2C)r0`fKcbW@cBF_XVgG=Sleg@ zS~Z4x1>%5$m<{+?F~IoHNfpvXt{i#EbG&BXX&sC*z0O4{c+a-bL~K-r+hm1G71RO) zj9RtyItzHm0kZ$QbST{QQDhPLW!aOcP2e2iu539g2I9BzhN;3l7^^tBsE-a&Uawqs=$h>e}p~|<@OrzY7L`j-v-gwJH}39tHS z&#uX>58AI!V{EOULxzRdlD~}p%-DRx*gkIByw~G=Voe`lS-(-L??8t*mA*-GpL ze<$0g-$+(aj>6b>4_f0kk% zBlZA|MInl@rv)O*lQ9Rv0Ps)YQz1i*OYaHsT(Tmzb7#Xt+>4_w|D8G3v;Hn<%Il_* z|7_0s*?ue}dn1^?UsWnuFt0kcyiAuVz0Tk4mibkHCV29rU+QR{YHHQu^9diz5>l}+{AFW39~Og z8~A_2oDCGcQMm-?_#cnWYzlt9M*e*Jz6aQhaZ4+9%kcS@%i*)u)*<*mTvCct)wpZk zm0gR=>xc`V?Wt3I_@;O6n%>)$Zb7f$9o3~qF-1>>(J;;)%8P*2 zGb+c+$DGGHtesxyBWxQNGeUY7J41slL-`PwB;`Yy<|%&GqC=~}W%G*H^@#b+C-K#) zRr?2$e1SClpa-&UNtT1Zh!U(SPVu!)JrmWc&T7szju&y)tQQIN&Iy4vztL#`XSh5X zZpjS`w|Kmojf_AG*9({Hs~;cU=3-1>H}1w1GxyrgsVoF7L|EFM^-~Ya*9k4qox4gZ zlRJ|4dcIlhKcAniB^@%AfW2B?)k{R4FO3)LI$f+}KIBBkJzw~{R#xLa6?& zjU__Ri22+~70`Q)n;>&_^o-x64F6u_OD$xWK_;4{FQ}Q+ z_z=Y3bc9)8y`?%r=z%-t{lv6$JK~O@)#>}^{;q-vgjAiH*-N|IXeyet$5)W^oKW>s zKWYnj9*$K`&CP1IP@bLm$KCe>@wb=K@9DN2IYY6;RXOzDK=UDa+;<1Y z+H>lG5Jf_Mf6~?k2tI_mX;7E!a-h~U>r)RJ1LB&eo#|K_v!}aIYKVmFuFugnC^xa$ z^Nrk}50~0a`};DcJ;b{h~u3J!xcs8i=4E(z53Tq1N-cdhU#pw+j`j0e^4 zNQKAsV~V^uMeRetY%O&jYiu-wPkIT`)c_htv8}eYD>Lc6Oq4#S$ zVXS;k`PG9o&%T^;eoDfUZQFL);7|mg4xDk84KyVQJkAE}Au7*!J1*KujS^1Im?FiD z=PO^AB*K;X#3`Y-^IO{>kV#aXRQ;od>%=96@D1G=IzmGXWdsXks*OkBXiAPYM}rRA zj^u25jf#a{Ny^cAy!X`QC*C9gNq9k>GNF=L{gYfyVIYJdCuK<&AJr&Yq;sOMZ!A-3 zu}G|e+i_V#Gh2quXl1V)j$!wOV9iQ91B+KFYOudU)~mHi&brc9l9oRP18Y^wpy~sd zx!53W!=t-aL)bM&w{h}wl!7c!IB}GJQU1t0Meo8eL{rNXLzHNTQL6I8rgw>OrMgs7 zYj_~{lfl(zxTCFGk_GN7(O!JQ3WYYxxd`2^!W@Fzf5TJAkFCk9|6bRz%s^uVIqA&| z%iuD6_D>#fX5Y;vnsldj_eT&&TcYiKaaDjAd}kRa=?SI=`rf$_aX%i( z*V9umpz&O!=n&gV|91{ntRl}vNr-~wLUh(c+Ml@}i{)lsvmx@cCjtxt7X$buJb{Wg z1P_g2yIdOWiyuIQ3NIt1Zi5BmNgZ*h2m0B!bEkIMgW=Z5N;BJTFCp?78Cz~1L+ zCAyUFS(&rSZ)A_;YM-4ms3z&~ylH!56QpN@zLNWpzdP)7fW@hh`YZZpPIx;UA-r6_7kvmL$ad3UR5g*?OL0xYfSzaI$fGp#!ze_>hRxfc0 zzre>|fln#uNWQzL1(D2rPTby+_yijY%OYMg!hFnlM~)rgYfa%gwbx2vQjH$JOR<@J z8wjPtj{4N00Co!(rWgGYA}%%bNbqiA$FX7e zA}fRncN(z7+W4Rmm#6_SCfMU5G|VJhR)+WfswoH1tWyBJdbrtek}?ovk=l@ZTvR_W zB))_oylKmGMrY3%b0v`VUs9ccK1O`MQ#DI_9G044hSY~CobkZ5>BO=VQ6op#rApL}F2MnBcw$D@6ii&Lq2kW|8xSJUP9d!YKpEQPuGTTiyxwtvMQQ(k3 z#t#v0H(5Jw^xApE0QX|tZ+|*uKzsnO>{LD0)$Mm-pl*b|Lr?aT#*u`kv$6+)8zu;m zUTXRZL`0FCPrP^}3g!S|w#?<=&>jhk_XJq2ITtc&GBQ!sPQ^v47yV)>gYdyeQa15Q zN}=Mi#;QPjD)#SZ-~ee^_-r;ge%b2EF@&clfbhjlTHJmEO#l#tjX;qvzj&h6h#i1w zXM~_!1p}7SYZBta%e6KDT56-3&0TlzP^^w2J>@{69yl1kvlwqheZkM5>n3081+7l8xquKES^~i{`@p8M1%Z)6{0S{Wq=f55%5nT z+Vg)DqTPu&|DzC9-QoJD5DlBJ@bH~*>DFWaCk2d-8k@_=7%y9%=CoWc zxsyO=Blr3W$3jPkn>f+3MTSJ%273Ctc(JhwMWs1Gk=Wq!Iktj%1&O9?gej>-Mi3-I zgwh%#T0*48GJSN+d><_9CVeE#Dlf+$(PaLdS;;s|&Zys8b4`p1VWRh7razi0_eEq-#ImC;Xvj@+*Dn%sNo^s|#nR)_E4Hf4T zj`0){5ig|pqbl=aI9k>AdmB!=mL5$8^5V3c+5rZ$#tUYa{KB|D(pj-5XEgJ)_H$*)Z%*T7q@q# zcD8!$IlweXMzKsXD#!`+PdYBq^Kz}C&+)ydbpEN5zi5et`)Hv0mhO+jWo!^|tJboE zy2>V35EH3$3{INetwe2b=NDrr%Ao(mH;{a-~_6SWk{wcc&?F4l@q0VuKcm@T__L7_$o7H8M7LQ`jlv&gU3K3WytwK*)N6AWvafhEFqwDg%T!zHrp1L$Mk^DyFKRdLL% zQwxTd$2f7r_%|?LQTg?KWwVl+-kgXY*Pj}n&RoKltF|@w^{gIpn&vo3jyv0l2&N*V!Kj>lrMIK!r9YjdB!yH4N zqTV?J&dTm@GX*m^Y*@7WdU zMMTnHASreKhpX;k#&yfP$#en)2RmUj*BfC&)Wk&6r@W*_LXeKaV6a=Pz&PYQK(EvL z%6%gc#zYKge=qR;QLS89TMtF}Odr*qMTAT#CC;ZpCTeS_(g`pF*YGe8r;-&F#58wU zo>!QLG93M$R1*6Cc$6)GKLO5w>HjAQ{_`leX2a6|w?~;ou{g{9pGP@jWzJKHjk^D! zVJ*tAeM_m|6tU)KROic=&(jZ&a9jU?V6&i*aD%XjXpN|tct!uO2`Tb%sTq>#HsqQ5 z*%su2x!+>*&B)QwziXD7@>d8}l_c8=vY{6@s3tjZvat#3b|@wJ*r4}{aSjf3<**^u zPfgbt%&{$Y&R7T`&HqdswqYZz{H3sGEl7x@a-6ndj)Zx1Eq7{;j)??${BsaP>C_A4 zh0EL~lz9S-du%mb1e_>_D9J+%9q<+`uBiqXne~;{7uAqu*dULBPQaSIIMb$%y;9Ge zJjHZVi4Sg^y~U$%Nm?HXgb!~@dRVzV?^#f}zo#HkkwMKP=h1=!|B~Z<`IMrFQmI}B z%E4*e(yBJ7lFPQbvQsZnCtt`~nXzAEVv^03_SJ|)YGQuq+Mpuj0QPzo3y-`bx?Z7$ zto`VOOVK!ZXHmm7hm+dq@+oLVvS9heWn{W-WEN zzJHj5R%e#fiwNRk4bg*Cw!R<(?>~}hefy#?l12u8Ltk0=@<%=-*9#2?DNm$aoX8jl z0`h<(q3D*rwPgVhW}wT0wNq*h(IH%yfxq`ua5j>N%62c(N^1C}&xp|Mu9I zEBBeb!3=gzII5izegJ1ktvwP)UMmg}Z zg=UWW<>a&p2i0mDIffMUwoL4d&^rJU3$-X2c&?dp&X+lk(4~ zOI*#VJSzBKjJ<_h)PJ}w%D~7F!!U#nB8;?@Lkoi-Fmwt^gGe_>NJu#_ba!_*NOyM& zlG4%*A|WW3-#*Vh`?vSm=ehS!nCF@A`mFV?wceK*L!>$1kRQ4o!GlXb+G`LWc@e~> zAlk9yx^k#jNWC3-xpG8;RYZ!QIB{7%(i&DLjr~Q_zjpLX?gc4A;Y?t;s*I+g;-N_9 z5ssSLr|WU0nIQ4VN3otFGOi={O>S+5PkVZV_G1LG&c-i}i|_1| zir9mB*p5-Fa1ZGM()YIjE}{36IFr@Sf%`KFK$h*k*UFYE-LH>c0+$jP_PN@6-nZYa9Ba3U%RKMU?>^#-FVibv>B4?1+{) zw&p?Uovj1BZ>SB#aYgl1+Gg8m8-DO#Pi4Ly3aBJ&(kG|=mg$sw3hN*T>X$MD075YF z$H|2Zz!ODIhZ8KW+wOp;r~w79pE$fh_(ATpOK&!4#V;xDcSaY{^wr34|H(Q)vARS0 ze;ZXj4!6ft;bZ?N57Y(hVkKcU{Z|P5hX=Nk{>LeKO-qZEe@@9CsZ-vclHcNbe1A$l zWv|`?p;57W)#7rivwMfu_VD)glf(E21qU4kRg!ZA^C(J{jD$*zeh=^ql4GIC6_ zatjR73QM%|ODj~%E2|Z=vTGV->gpR?#p~KSQO#X#Jx;yW{kBLX|6pE+?LAl`;bap< zlh*vxV3F~K_rum;1Q`C~d$G0P_U^v^rYRH*0u!HYESd=-K)ON;E`EB64-tq48qtXm zF-|BcoMMi1Mw|l<5fI2Mr;C5r(nT@hDw-6|_fSqQPF)lA zP}6y60wGLHwK+F)@SXiK)7C?4c2JAs)i1cr!t4h%_KWa3W2`K@vSy8RR4dK&X>_|L zT*hB}-XXf$JdL7Z4aH&RnI5NK=b&c)?OTGz>s_sd8uhsh6A^Fd+zZ?3ri=|f#mh^Q ziL5yCX_=NBd-{PQYk$Fp)1P!-vxM;25&7lV%v$Q-%H>D+fWZ5-WrB;xEx(*WLo5}iPT6lMB zBxQ=OVgY#N^+41UC8MrS2W8)xS;*V@gm7*1G;e9|2=hc^hh| z5~~*>yQrB-2v;4Ti;*(NWpH9}+sTfTQRg=fVR}T;{qch6f-%v6;56Mj1^L|x=)54GBtQn+bZVOkSad9ynOryjoOzu9zK0ryI-E`y@@1Mo= z_`r|koyF72COy3Ps-9hFX(f%#rT7;{#gN5_In!%K2SzFyiNdR$#2sKJ3C-P6ZETa^ z^H*$hXQGPYH7VmYlHeDzuVLThcWth=bQIhF_7y7QJL87-w8yiu13S@>_{wf6iABy{ zab0}!KE>Pyu72v2db$Bx#}{0Kvbv&=hg21M{(tbmkIGwT2XDtoNmCv3gAc9E=~mZtAsk$`sW9v#d+*1wsN)V0F`Ai7Mimk6|Q0O_;Tu39KAy{ zpom?Av&_6#4!ekOU6bEm=F{4DoULzf3#iIBq__PZXIp#q>sryR`PNs^T;xQK3hH&b ziv0fPjei^E1E{hBGXRU2LS*Fmi6Ax=4e8dir+@s8@W8ICj+Qx9gRvYYlT$Rdf4zxa z2k@yuc!5J3XE=>`98b?N!~mg*M3APmj7qQ=L|rt@N|oL{Dw~4o@1@8gzKT1pFF7eO zStTQPp1P8CKv%X5ez+uwiPR#zZ6-%DG$}iTJtljsc%X{rUj$GYaCeV=N&hGMO_)0{ zmT6bal{ycz#}zJCx+)-^wVL~Ux<|hlvh#}b^6$rs%iSTzH+OQvKEh;Z{{T;nPhe=6 zRj_b)bc}XnY(nDe_@vY{$&~c0q|EHR=*X!2;?UfZ@}RPcst<+LbxyVQO%aXFZJ!E@ z+Pk$nd-~Noy9P#-hDIk8`o4aX9vYsWpPFABUtE}7S#4dPn4R0~U;7M)?=~#E!jWL) z(Q(VEGe7tb`0{EbwY$5+PYf6EbJsDZyVo1%7byXi<~9HmfS_266tJgF34uWxRyDU4 zv-yz_freFV@E{k{cZ#buu{;b8QZJe-NGa6&4n#zaCs{~lI>IODL*R6pS*Q|hVo2UX zbB?W2VBngZ#Hm(@0~4$a{wmjpB~t-S34Va9rXW|W_Rcl2v5s^+UDML-A;DLZB}3!d z5#2SzW4Xr9^L4%0SrP`_P+Ep%2Nyzbf4j@NRE0+F_+bQh&gkG{JAF226Q#3V?Ri}D zvod`>&zNv4_R23Uc{XZihsKR%0*?;)j3M`do{qj|V3*M{WlZ}8_yINmx29xW0zp0G zuq6$5#Y_8RH+M8t#1Gi3yj7gs$?^C+IbrHta_U{8@n+?ZM*B6AXNB|!N&UiulLv+O z79&(N{WHR@K%Z>I`w|BSx7?rX9oZYrJ6ckxW^9%LdmN2HfUN4=6dZQl55uv{+*w0Q zP^^mGA2I_*hRS=G_qbkM!yZTQaLWKA>C3meq$K3TbD)C0@eIcJFOsq&L=yQnEz7h% zF@{kzHOvCI+#bvL;Zj6KC*I#e&F5PY%ftz{_TrmOF+;c*MH-?$!qe^cuDby`(GAp@ zG9DUTT2AFX9T|a-qHwHrNfvhG1SE@n3AqwKekD#29Lma<<;+x&L-{r_6e&qC2Lgn@ z@%H55RS`T$N}v|)$)lgA>=lO{Xq)A?nW{XKXA9M~erUa}I#8uiSwvsc*CfDDJH1@b zTBjWI4c(JnWKLq{-6&!wfnw?vg?hi7i^U_Zuo-S8lcn^7a9p|2~jEwxb($x6gu1 z+@L*S3i7_))-&?lK@OXCX?ci+;b+Ed)4CM^_DhcnP?l-H@u)0e-tlw{(o&JfMSu`(-iP+`3VTU@rVh;W+2mvmPaDZ{r7j*1_shRF zm!`_RFub_H>I6bw`PQH8?+^w-_<3W+A<+RDA9hlXhrMq0+g}ScoQ;p${5cxVxVyQj z=6Ln@G^9uNjXh<^A2j-u;9ulR7C;3UzgM9DIfT2{pZ}1rd;O`+s`wx9b7)zjUvUpV zDebF!|G-b9#lE{z#q1A}O$Ie*e%gOkm(W{8$T%M1GDD=aRp{E%AP*e+UI-&xt)KUg?AIobPpaCWhAb$xhqd;0gUq$5aH zpU)?QcT+Rb$fuJ9FG6-Q3Io7_J#s8{6=2QIeTdWzBv1N^Bh5^*vik{o*5OA8g6OvZ z))?d>JmFC0wY7D?9Z1xm$=tk}9`y?7_WKt9zNvxxDR@~(+YBWMGA}IU#Yo zw&}OCw-Dpkvc7d2YX>YMp2Saw0XRpZmDsOettGrSFC)PMy#Hgfh7IhaSY3L93pY=& z<>#EsgjoXu?_}{`{h>+n>#42#>Tl+1*9}~H_t6{5;C~q_%UnkrBQM$0plvcUJrEG? z-`mqc_v!**Y#Vo&ZxH1~H6Lr+!ex;g!*jvB8O!iSdO6Ndbq+42tF)fyMG;qNoEUl_ zL#<+N$*pAqrQ-}JPZd$H>tGV|mQ9Y^^1NJU0yAj%$dn^#=iDIR;O!^KOeX-2 z3*8r#ag4BFKfzU_C<7^CHGlU>)`F90tQ4o{Vxdhe)&c;2Zmw38UDYII0E6UdyM`|a zO65+5UFTOAQ=m&?6iMWzAOnz+QdrFk!f(|^t~8~7-{MIQqvqqqM5~vQzm$cqMQ3%_ zuEoj!=xg9AX7dNt{)JXFzoTlNCP{j6O4=$bGjP-@D?n30Cfn3;OakS!d)~>>`Hjtq zktVylQ%XiNsui2x`>U-mbah>sIPwj1@58&y%6EzzqH+U$6bfe~aO+`TLIbhN?O~}( z)mc(l=}AJmz|$=8BKd|@1IK9(t{Cth(bMT^~=xF`>_CHJfZa)2oE^7hiF7>Oy z9Aj6|PGvsu$<6o=)h=5J?p>dt|0%kp0j~hl_v82f>AT-!*ODhLcD|oGwdj()$w1lY8t}psv28;np@hsFdcQ>{T97_10#Av zrK4Zn#K$KlXBDQ$=N2UwhQ7kV{7cI#&9lTH$N_j32;J^j5`-OGA}7g!#JFemOYpho z{v&K(b1)t>arJm5*FgkaO7A)@S%X6Wd-2g6&#)(m{FL!joGlCGe<$?Fo+Ntx0ksST zd5hf|S`jAj+(EGNcth&Wj#Z-${Haqsij+No^n8PszTHsFh}FhoYOAB@fpFta5vN{d zp|p@bwfu!o0S?l^RWs|vjJ8sCFDk9%{NZTvBYSTB$P2J+1BymBPyP!2youLpeR}wQ z#kDCZq5N2}rNdfJ$m2`Kli4@M!wJ$!4;0;O4SPWjM@u(z5I|(!U^FFFv$MqrzZ$f{HgcF8J&=-%`nBoHucp!!1AB*q;sI1A}i8zQt)9Zo|8~D(SoisUpz>$p?N=JMK$uI7>;RB<+9Mh|EsNpRJwvE#MQzW8WKFcgi( z>Jz*j0KVn6Q+j1l%n~HPe~U3qt+4|Vq`J1K_CTB%7PFW$?qNP5Pa;89k(*KhSgbeNoFkzJ}B15+`a4EXPVvc!M=Cx7!L3w1k&I8fVO$%q(Nv8sXOU<x2n| z*aSsI$EcY4gu8-cQ&Lme5Q*Uk*|fZT4`T1EL|{RAg%L8dBoR?r-|$8jVP2D!EJfDX zU5U%6-P%?H(qR)}17{+@#62@9@k0x+McAOlG8mpayZM>FdSS0*4cWDI;=Zzfj#->K zy|#n@`jhn=YcSW5x94dY8m}ALz*`t7rT`fTB>$G7bgppolpGiF&ewec;kjAjP()EJEttzjf9oAUxDd0PQ0kw zN9?H*%_LUF=(wF|K5bV+hBZ~C4FxS+D`R3Y&Iuy3NCaEwZr?g#^!ce9tuj&~3RZ**J{T#b0gINFf0sPBY#g zuU6sWxYFKtUwa<7PUG+|(&&(kadO>>VN{g|X8G1!MMXX7UN}qm#N-ynYcRl9eLxR_ z>!U)QbwLYF_!A9j zIFYNo9=+)fpXg8|dy(pUf(xvOSX|m93`FOUk2ICo9>e4&TE<5zcfk1;6~~+B^ei3i z(+v(}Gs&AIfTwz!XLEKxW&*Yd>6~FrqnB(UJps z$;g8gddaCs+;g&5bs&US-b5H90V#E%_(Y6Dt*)!;+-)hOSO?!WKF*iI)jd-$6KmS1 zaW5ZXc=w%}#;BNK*qYl5p-|mhA^1TOUG-{~k~gMet8$ZA>|csj7J&S(lrpFlo-4sg z^$(@|=Yzagv^S|xw{+dHUK;U+oqwK|>SvocIST2`4*2z6_a9^g8yf-ygFu`dgTPNe zhuuFdC?yoJh!Mii-`^qFIW#IWD_aW}4u%_pl1q{UgJW{OR8@=MB8!tsOH+dpW;Gq1 z5l}=^pMSYYd)LUQEq{OiAaHDY#=P-s2`CVGuV+D4J;vmjk-98u6?-_o6mLCdkW4Q6j)AaXb%ezXHJ&5x_4gy~M`o2=A@^J5 z-){}y7C^JobBAiBWm)^ZfHg(a#u~-A19}gTzNrgU)Z?teSew{c^(vujeor(IEp&@C zt~)BG$1!UD*KcCK(-%g1gAJ8EJ7`YHhJ&ARKax0!)g?~kNibP7LPb2xeH*M7#^AIE z`lwame7I0-{k7)pN>dur<#4V=`i60sp0sE+zSia88&9)}{Wd-Cc)U&yTblu)ngV~8052BxG+0+|*7J-oX(LN;@ORV_Gt(#ix zE*SUfkrC=yux5?}fBDZ;D$1j|cM54ijlmEWq?s4~}D;Twc~FEd}&s1g0_!4dN;}P^|C-{KMw?<+D!(&oxK|Wf`5b!F|A8AR>7%R6e zY|+uUN0P6PGE;GrK(dkWhtj@DD=g32FBgHXT1g!Pzu#LLf+}5^zo5C+A$ zIkGUnINGxmMo{p{*;Z4Yl<91SS>2W815-;z>pCM}9LAP8Yct`1kW1^(~d0_Dz3OfXLEdx-C*la&@YB z7{NYgOfSo36hkaGfRu;4o;0O zqJt7L-W-BiAuq8iW;#Q^)^h0c7|Bm03uxDI64&f)0=Oy^34wN3F|~_bbIHzCt{`#r zGBj$DpR;hoaO)dpoX&aSxnPH3p+%3YG~x;kp)+8~ER#F`VY*5SoDiac#(vNg=ikIUzOzIrC-TkFQ!@{kB7{ zg3vHqggycRf(S08UOx2}(f5X6NK)$0w%R7MFO1)C7a-yNZLdTKfmG+acYf2w246E99`B9%@I^zfX5TP3TDP_>6k z@Psp4n*EWuc2DA$`8+Y@SPxyKWUdSjL!SQ4M;2w;c!8*fnufC6$~*N$C2r*;p6xN; zj~-P7L+7aeY_0j z-ig*fo0z};JnYPYr@s?b4Fq_Te(>Ee**ElCX*Y9^9k9>o5B9|+b(4|r>5S|R(N*vi z;wZP^NzFmSH{y6-PE|VgsHVBp?ax*!HWzlyT-Z1^DvqZ)OkaLj?TmPpS2l2Ezc?t_ zH&C^M+}NQr(YXxqGzXRp7%zdr38t>I`A8dBFD$wq z#GGHa9?V|1vmV0P$NVFdcjn&O6xb^K5iWGG^F!VC&)gUQXwA&<^8Q)fh+1~d%#BuX z6W@qYmXaN%QhjA%!u{6tIyXq?%rG(GH7Zg$v5HC=J4w#enJKxFVK86Du5H~XM(>rV zSz7cQQ*wgs(V!VTlo0;JZn3-^Pm6@!=oKJVuI++{I?cz-Bi!?iBEKNVA zo#7^%A+3Yo6}_Qy*n0{GHnEn-VAUql@3zX8K)qX=bjr!@D}g{Z&XXcdjB0FprMOV_ zf-;JInBCBT_9n8Xs|`S@MIP3FK(v6^iuGJ381Ad%-%-6olwDB|lS_L#eWxzNMH4RQ zsp#vl0{dpRP)*le{A?vvOHH{=gI|X#niIj;Ob>G>wNbTVM5GnuBxDL)>Jy}ioF$9` z&Fbn}#<~_2*3?*>GO5m%qWWN*>a&MT#Dd-fhKF^MqfU>S?diqhQ+4tzZV%97?`njY zEv<+yIAP-aL+9Fvn!0L>w7>jk0m?@2ipn0kut&{e{DrtN8axZkV@8{H_SCUwpLn{j zq_SV668jFTE=4X96)cO<+FZK3;)LVlyuMTvNZ!`#t%ZjKTyxKjHa?g0v{ORN{BYL~ z-|9NfUTT!LI}2fXT!GibxMO7TAY@Pd5tVD{7iQI8A0|*$%QFaJ1?8^e(3R;*!n1+1 zo70B7t9Nti2|hw6>RpSR>#aiVCIboK?%*?ZO0=+g`G*M8FA32jrOdb5P95L3k5%-p z*XlWfAnJ{g4Y%%#%AZfKq^lmYg7%-nX|Tvc@Ht6Mt;VPzc=$Mh>=Xw^`mjl5=5XHE zKDsob4I=?!LKOO#(|vtvn#z`(AZ21^#~%DsJ03|pTR#m>WyBGvn>{i7F-hs#Lv}7a z>KShz$WH^NX4G>yaz0Q&lzi@>^Jip*vJmrsF<(EH2!!!l7UPEHzMomgxN z2dq9$x=tMAYR2ON&a{_6z<=r)T>w2G5v%!sD{~Z0@Bg02$*H+f7>Z1T& z{f9m&QJsVg_cna;+Ntvz57=5o+k-%01d?CtlV?b%CBi$>9)j@)v$=*PCBFqlrF+91 zTvBrLB#ASMGLbU*<;mdUs+fw}u(awT7;#;z+lR&?P+42AGPcr}&J1whm=d*z*C+hkZY^L8sT4pFx7mexotqmgrEroZ0YOUyc8&1$ zF{&%FeISq#MF4z>$<$qGibp#;s?GDnRNZ52;&DW3-em^xS17hwilB*FLe1BtRxjw5 znf+xd33;AguJM0jd%-Bg$K%saYL9K<@>NZjU7NVYHBdx#xXO2Ldd5O;|M}=c-OYtA z%^&JH%?-8RF5kpGUhZqK-xw68hK}8a{9FenJBQY^xY=4x7UIjAr@E28TPA9U%aJG- zp63TAOf2^RJg-ijcp4%^a*jagUmD8xNFehnPhVX3igz1D0mcYCldI!RB6#0l##K)w z7s{{*ywewex-rvQ(n02M3>dxU!~qY1Z9|tHlh_??EV9SESs^a zh)-Amu-aok5Sek23Ad*IH6t0q*J3MCmI{hVGRtP^0Wek$=BJ3b(rl;Na@!T8YFoN& zrrRjt2dJX`EkseT^`L=J*rGBsa1B{x&VY(-+{T&VL>3{OhdxQ=%PHlh76kamYyg=& z9mF7}UIY=*3Ts9zfbzEd4b4uH6VakxMlDovdINgbDezIF4@K30gf*XZIeK4q_>p`r zr~`#P z=y8d|?Zr!D?!fQ~0Y^;uP!_hDDFFhzjW(89aed%HTViI3^KMGQ7 zPd2Mq!im$u9>pE?T~4FRhI;>nu~Lz!Ms=C^qr&0@M9giD&xoi2Rg{3cLuZ>(3)MzN zMkPhf=t8j@<}2#b+@X>e7UdK{j6Q((Ghec9v=D{}@lA`WH}8>4gRVIky55jnMv%7? z)z6EpKVhG8|9NZFeBYx2uDoiZsl##7@D_+r{$LdM$ogggtMZM}bAh?F7ekVpLd)n+ zu+05|qjinXZODk9rf>lPk!(u*`SA9=yBrX$j@ioyTwg1_!~6>Y@3|j)aH0M0JJy#+ zg0@65NsU|?^tGAH_bUurLiVtO^<*M)s{P0Ugd<*wjJKYS95El~yH#+th4Zg2q+K1a zn0G8fCc?dPkwZ@c_ji6o{iM?8d_hGl%|EB=pB(dwT5;1wUm!CK92xe2JMyzY7d1^O zOq4n-&(*yWchxqR>&9+EkeFEvU_)Mw6M6z;RW-QNckm|jqV;FXCHch!;C)qi;=#M1 zPQ(L{p!6n$Xk^Dc6#V*%5wi9%#yp8Ez)k`rI-%%Qy-NPnS(0BE6`}-Ad6%E)tBH;b z{Xj`i8PE}nL#`4OjFXw7%$G(b$aU)Aa85`2CZO*LIbDP~t1nTBs!t=Q=x4bR08Ue4 zfp~dT7*doW?chX)ZV??}f%h+8!}Q+Q_%FG-&$xkW1OLm{_|48G_wC+R-E>2MWbSiy zA09*cY0Ye#SGwc{56{@wt~|(vU%%gT^Y#%3I(T{_kS>_e*TTXoh=3SFU0Z&$NM|R^xzBYV z0v9ATlG!_9G8vJrIP18)V6!L)d%X%7`eC{_y)(OhV7zFpJd3FKc|HI=K52v)f3MHe zBR9pyk0zNjrjayGwRe0s)E71L)pad&`S$}ym(ng1!Lb`J_vAjYoDcy20k)h^~Pf=T=fCJ*jdFurI^=z$<@u* z{3uKd*ZirRckb5i_X0Do2QtK)|C>+h1I)8N0zdBfK!D#GXmOAiiEH;>0`rC+Sb=6E z^3j&vMikU-y90`#XfzHdaNFLXMNx4wMuV2ChKOO-yYxQD4LaXw!#&qb){NT@qht#b zSG1m?l0McmTx^~N0(m8@P8M7*T8d=kZzQ3Q<9gQR#nTW zm+x4%1c#ZNr|rdQq)Vw_9A0EKFQs3N(2`{Wel%q=k?gN0VA?IILMvNu%TBB*kK@^h zK1Y>DFA+n3xOBH{B+qbpk_``>_TH+vi={py41EeL8F+Y-bH(MMI~<@iY-P{fzujrh zKAYk=8ap6V&3-F9tmDc%6;M%jpD@;IkC9;U8)lH!_%iS&9cuA_@J4%%@fJsaGKD;twPMZKM6nJW!gZeTU-_quCZT_&xD zZ*Kx_J{Z;M?ERveJf**JU_96hJ*|Hmp!YPGlS9aft0?=1qU^@mWni*iJ7u9~mS5OO zWSpyhxrM_F&6=FCbnBqUYWwMTPuO4Ax&E8{QXCjo`{+&2>6@Dq{#Wa+>yBT@QfW8L zn$H;Ic#>xcM`?ZGvlpi0XaXW zGaxFvggQ9=+h{c@vq1q*Y|D6y?69y0?Zju1Xm6tiZ{mcv`4@>{WL`n_!agW}Hy*D;T_B=H&Z!$=)E!-N>f_yp++fmtWYx&AU) z85Bg~NPRT*huhlxvt`S;( ze?T3^M@1Em!!Yt9d{Bvp(v!31xSgL0A345jYo}RC#2V4$fc_) zQFAqh)DS}~9Mi8F%84S%1S>R8s!Efp1GV~|KLL>##ankZJRZ)0&T&Vz_yd7SgeF6J zvkkQ#NyRg5x;N&NZ+?xbmEWZc$_CTPd?EjunGK#JI}7$M%|!hY;)^C^9g!ZA(+ZPg zHWOFUd%s}(hP6~v#WcxT&PpM6I6HMYUzKszY_aU)pmXu}3z8KjX(!0291G`YryJpi z*57@cixT1Eq}p=1^7j)7M)}at4eaeU!fS*-sap*Vb%tVz-e^ zQY?=)*B`HRUHqeKi1Otb9*93K0~Rs>DT^Twbja4Ycup~9Wf`(iQgpv z24_;Bd7u8P1|dTctN;40R-Kk@XV%5@F2L(gxnfrkbpMaE9$Nd8!K!x|zRVyo0b(xQ z(yNq_7E_`|G3!T`oPU>v*!x8G?!zp z9Qt>yFeT@)?4XG40KW0FL{rOcb$N%$4U$;fhm6mt>#mE`(i9}knev?`ee-pK2IFHQ z*-c9*D);0D%Ni`dP^4#nC3yIy<-+n|)gUQ%sp05e=d+q>R>HWyyuV3)1xkF!9{y{;I8kSJV z5GK?kXh_uH&8a;9gl=^k2iK73m>|w*$ixYSa7Zg;K;C|sDc^~icBK30Ut|jB%J0Q1M6lrP&s-X}9O3!z~8e3tRVjpJ;39;Yr z8YR4Q>Z%$a6RZ0kxq{G=1FhQn0fp)_T<8h`r9{fwvTKylWGN4ENzfk3$rdQq&<3qy+x0rdBnjU7) z@wv)|YrA(2JJs4tBh*w7sl}HWPly;1uC6e5bx%(TxGe_b=C2$WC=vn-3xf$pDn&aI zBSRAsKq>lZ24Dm#yEG!tq5u~T@)IvjfB`E|1zI68^PYrtH50v1|tv)Y4LU`83slEyph@EFt*MICiT~~l2H9A-q2B=#BB6H z5moKc=81ZBpzNK_soU>ZU=~$1A%nXdf$>J6WFD#a(h%df%~Ws6eD&HOysoa&QY_m) zw7V>w`m#E5a>JZ+fOs871K|=U>)A%p5BB821Kk=;Wqr|yKkesL8|-fm9z0P$(dV-{ zc<^Yk0Jz-JV9uoeL*tA&Fi0d%(C+b1n%r;=hH68L`}C}u_K#15FO{t7YK_{*{(qv% zXuyCfa_ksVr78mQb~i-*Cya?M%gT6A}WiH0c_@&S|Du)jS4Ei+4$l zt3Go;8uI5rDPs3TR=WC}3Ht)>n7oGPtYrn15nNfsO3l0&a=ITrlt=8=yX;!b& z9k)221#bgPH{_4h)AX>n$hrtjjxCzg90!m*3H?76K?%6AaDaiDBh{R0w`OH~_- zAI`cDzx>tKJyCZJeo{3o92Gi9OILEhIS%`vw%Nh$*u<))cCH)K=-Ly?J;qlSLOH=x z!luKg!Q4t%gb`A3%DizC_%*AI_wdB}g*5grM6_)R{X^VB`!l8iUc(PCz?Pz)&>C*oV#H|D=RL$W~Jz+z2~1KWopbz(fgm&qN}JR@#Q9tc;FH*4Q{y9HUpffvJ#diaj^E_k`vD# z2)&@3sJ0n=g7-`Hvaapz`(JDqxWuQIc?x#@r@ztfh6r35GR>%_6S99U?Xum;dBSef zJQaNsl_YOd8ZcMi@?Xh_ViD7nyjRTJ5MtwmIOjoourf~2AgOZl8W|lbnuZ{dZl<@! zDh-3(x(F!Pj(D(;f>Nmg1RJr0u}k0pe!q>ONH-uM%k?3;jtnNWH4M;`>0PD)NU*u0 z0myGz#FvpcxP^8Y$y^i}pLrm&+-Z>V;san4sGE_yKe!u^W7k7L#VtttucqriAv=fv zUh*2$y>J`-$J|iQP%gO-+mL?I`X^-P)4f^a=GIrfa$@|1s-k<_rdcnvpPvZo?qlld zB}WE^<6_+X!p%{*XfOf>4heONh(Iyof@5Kj9C(s_il-nHDFOz?<$TEj=erbYk|E*u zjtESosxt>sYgPX`5CoFy>TK_|tcMTFj#e>FSk(8(&vef(L_ojEEkj_5s~!=!i%ZLM zd&Uu^3R_2KdIuYuGa%q~--MN6xt^$}UtjA_|Krto>6+rCm#_ia1Vw2otQ3LabxCR5 zD|_;pi5zm(Z&ELsmA^7PaT`~C!M1s3a=%EC^EZ6DoSK5BU$=5ng8c1__wmcjqhj{Y}= zifV|?tdHj$=Fqru`@wtv66Z7B;+)qp9<4W*CqITWUbnaXxw-nezcSw5es_n3%e?x3 z7o)zPq*jTxsdnzVm?^P$#5jR)8il}{&DGJ*KT3C=DkJ`Ogjk$Xn}$R0e;-VhXA%z{ zirAXKgxfvDhj{=KU2_OEp%x4tX-lr11o{;E45G)oCt1k&8Uw5-Wjem7dl0->awoGM zOa+B^JY#YM*WhO)Cy>w-xYMsQZlvwAScEZ}oXMsKU(T^GfAUi5XL@$T#i^1?Q5G;I z>@5)tk7P?5WOid?XqtkiLXlnBok z)>NAi-lbRYT<0A{B$!;_RJF&|k;a#_T2ZrSY+aN&UPOP>Yx;!#UW|je);VzofE(q5RmM>buwq7^l8skf`6<2t`|vA=ub zplq~V5&9F!CBao?e|Y}AXQF-S4#>3x967)l77{SILRuZaaLk0Xu*Mc&5EuA2q z9k1+NW2wuLSKakdz{QFEN787uHL-{8#Y4vub<_wVlkkG9!HcSX(ctAa&%o09yQ|Tv z^D@7g29k+)W$GW(hzcnWAK&4qU(TQbZJRvL1#_28`r zd{fZCBID<$8JoL{Er8c|uNz%r>qh%sTZ(5}51Q|6bWdmYDwU24LL7w7(0B1Kam?sq zZ$GdFuKV0);6Ec-6v_U!T9bX`jkqrMOf-6&9}>SkxNH`Y5;V$I-fk^JOZ{iH;PLyy zBYU7OSo!yM`<;a;CB}W_H?Jc`kF(OdVNi_2QlkxDs_!AlLk4?0yOWUMgCl`)$a7_x z#5&)~kB=342($!X#2YjqF?S3I=73>4#~Lr2a%WjgA|Qd%4* zV5+S98@n0|YNGp#o!iT9%O(2WLFwQ1*5~(TM#BHp?Hbb~{*Nrk!CThp`*4#6%#*!N?Nrnd0on<#g0)6c$MK0bayE&<-b;kKcH5itf)VX=vN@xstI zX&!R8Njc~MC>)HuZ?y6k0g;R%{6G2K8zsXQ>#)>7DKu^BO zhpU>EhS+5S!&TNob>{6rpO@3m1)J<=cf7HzbCzsee`B7>(d!{*@PW5Q8 zuEmZffJeW-?SZWV<1~;PS4bkhbWXC&T_l@tc+CLYjB7;w{G^h5&=@8dO>uHX>3rDD zK%96mYIUZ(9%~yeb;P{$VJpIi+YkO{%W==o$EW+%;1g+Bd85nSE8W{GN0{yoZv@Md z>BT%XCn51uG80_ReIti2sQ*vo`|o2oGgp0zAh|_5G!8SjLH3L1OaO1fonCWa^V8J1 zY`Nh1D{=MZUZWg2F@kwR1Acoxy7z2#ygh(f3v(=EF?@S5i0}?{(@%iuy88i`qT_)v z73qX=^lBy7rUQwkteT0Nf?cXFFK(P3e%Qn_19iD27Fj>=`7R3I@W_Dny`;l=x7lYo znV2^tP`g0C4)gvFoKR(%sie-eBo;qa)@`w@FcgJ$+Vdo)o&wTkG^;G4wA<3RT(Ezx z6#AEUUaLWH0A^SW;o9{@GVJMn`ld^_7Sib_D^Wr8==&R!LVTTK45J-o1mRSf+jERn zzFZ;63U^_4gEW}%&Hx`lL8wW($-0xT(IWhW!$*R3%ev_j9$09&^n3Dy_Z?jiR@DaU zzJ4J>t*)ozfwPuM2mX`a`gJR#T3pm5ZN-Xi`@*FsJ_iopJ3gR$+ca;8znz zJvtpNnIlx2%FoH@*$UYNK?ft%qxipLcL22QBVRCa**we-g`ZL&+z@!eHE@XB;^>qz znJ0~nXuqKUEH33LSE3th>-i|0$EbF}nwP>{5Sa;fc>7;G zQaa(OkL&RIx_aOnVdL&Sna^)|X#X~y2nB7%(3f_x&GwMFNzPh2G zFbM|X{{jr)#2bkIJvVCrWTCw6(HUsfFQT~4Yod2KD-S%4E3h0U&@ZAhBSa}s#Nn0R zROd6eZwtG`DwX{>4l)Vf2X>LqL}C~<#hw{5`;uNMdvM&+Lba~M;rB-;rG(a@Z#8=u zUPJ|;KCY9}lK(4$`tUxy()>SmxOOU@<-RJLyf+&T-O^esUD?y$bgP}a+?OckHyVWg z-iN8L&OKg_-`t@!JTNBS{s95LzV{t2*I>+jiNY$n0$#Pfdns9fTt4{&`*DSxOj7$nw%rSqir?c>48rjOwLjDi zEH|KlL5b^f3P;y{!(`}ue$0I}gc##W3cAXiJuUzVgU?S+EC=c%S%qsp4nEgqzk{{- z7LpfFdbg$(vwsnKQ&WT?B#6p?^Kmk5DjSh4$H-^(8b4F6{LzC3zPJhjC`D!stNO-d zRgrEFRGf@qs*Z$X$JZ*P6IiQsvNb(4Tl%ifXQk}90@tZnXy}udNBmr8t%D>_HT#~` z!Ia}d_|2Lv57&a9T5?c%u>DTbY?qvbD-$U>TAAmv!5Y_Csj9!1S5&>qo_}Fym^)1U zO!mpXL$0K!u=D~IymVKGgj*okbDMwMy;=TOsgBU(+1CHkxBnBr^UegVJ_8H!>9aI? zxU4<{HcX8BBy%dK`9S4RQ_2syQD|RD>iD}g>N1a+Zvkj)SF|R>@6%~t$-F>NfK8)$ zKMrJFQo@|uRwYwkyg?KV5ZJ=&l?}qTM}rI%pt+IqyaU;9MN%bk0rK1yz5q+c8A2R) zW{XWyCwz5EV4vuP3CZVC{{%h}AkLtj0;L@$(B@qZhS?1AbtuU79nKWXCjzl;H4rB~ zS-Q7dM}g+#u7PYOZ(Xg)t5hIC&RZP^&`wgYGj523xb@J6|7**dP?BtdMzf+nfj^(* z#c6*Mk5{_R#xcn1V`TuAR~FB`Y7i_QNT`}7cvc#zc^&sffo5nU?~~_(Z#mMrINGcN zAyyVb_6?5`XHD_tL3M}}P`+XGmG<3X=E71(TEh>Alw_GoQD|x1V3WCM;wNgtV>-m6 zlmx_=v#!XxcJFZcFVPPPwuM$Gm^pg%9uO%+6Cg z;=*|DzY7Mxd|Dehi5Haam)#5d4|i442II)QX2)Jw8btHEMs{z7NwhW4r-PDTJkO3$ zlG+W53I9D_P;u0^R9^i`>L4G6(rP}aUh%5gYZ?}kJAQWy@i-|XuHZiIBaC)GYvUNd zWf~C4C_kJOf9?5eh!Ep>p=n(a{&V?8Gilw zkJHL)-CzIC*W&&6d~L-4u5885m1w(9{Ri5#&zCe1-1p^oYCewi8BU)xc>J~ge&3gW zWpfseGD zYAX_pM1qNJDjI$Ag`r?^5Exp})a%$HjFh|wAH%*0j{zt`8V1HK_5Qk;G@uTX9W7nj zG+0q@ms^8w9Xai&;ZDmzARzwZ-!i8fz_nio@8~NlGNG{U-(LyM$Gr(na4)G7MT6EU z0_7=Rv?z~?_PBuX&OdczCKKWcx|J3>bp%O;%{sWATz&a2!G$ZtDS!o?^SjFsPHOS^ zRQ66WrIe`>JTxH|oyGAkR=^UGNSKbu5`3OLttVf6lt^&c^b4c~f_D@%I@H;8`&msl z*|^h6E!H}I?`n1DJ=UW3DEckj%zx6>X~vPurwjd}mmJ#c=O_d-+OUmXf^)h(u3pKo z<nCbWPioQrABKh}^8+T5A=SLjw zcRM&~Lc+7}C2*PZR=gyL8i;KeA0#IF98oBf`VgZihycG;OtTQsT+=%p(+lgwAvq0| zoy5Rw(aK408cW~oXN<;k*&!T;+-Vq|NdX`aA8GWI8WP3W^ZMD6vR;H+>4biy1Q$jR z--s%JffCxINf(3d6hIm4oXDRC!-cM91tq2Rp+StLnWMB_o<)8jjj@3x*>eCkfxfK+ zU^{?j>=UW2>dB-|`fL3{?T`YSUA@ee8d+u%=*~y;q|QR=4&XfX*YWHK$mx5GvGW6R zH}s3IuM1=%1C7i<=ATUepSsS&pAGhH`$&ioNl1wrQ6;oSN(Z7wtlEmUHjNggMa|aU zD>hYo#NNAzS-Yw>wTjw%w?%b$b3gCz^St-F{)g*4uj4wem}$cpsl; zeFj@Uzeb@saB+vXrSI4}^o_luoySzCzeXVilWQqxcW=x-^GUD!aF!%o`v&K!)=$5; zP=Mr`|L7Jm1+ChKN@pRkL6y^zWUl4s^Yv;t_()=zKiBP2Y+EsQ`edduiAa?$H2yd@X?BjwcS ztjxS%LUvI=e(B5Niqx{|XO-`AY8p&2@Vb@~(t9Haf~M779D+({|73&$HuvDkjR4_4 zFs=U?2cIB(dFQDm6SExfQb%j&UG&;X32L&Jp~4}&s`}rN}0-#SMc#{?@B)B z8jqKl?tIsu&3U9M3IXvzyPwN{VP#`o_ zXwHciEZYdbHxy3Twi@cQ9FLBE>%he+?g1M$_Y?XY=3ac`2QRAlhb6*R>yB~ttuK!% zi$m8`^O`*-pGo&9|4oo7dbq`j|LD`Rx5~xX(sv2$pJO+f6hO|lB6w{ZwdEQ4wy2lX zMvIQh{LqVar?VknM5iZYZn+kP`o))tC_&#|hR26T$I?d92Y#ewii#!~Xx)wV-h{_w z4*+r>YmX}A34RyBXU47x8S<)pU=52(sNx_&68o(`LXyngu(0Io1b1|bm6d{3iVYQy zWg3Mr_*wexp<;`SSE&fiOa+!M{*2b&GyDPyRo9C$i#a5DvcqK(taG71of-1D-{On! zxNA3!0TPW$6p~YqEpUa%kS5)K8N30M(&PiI^dDQJ8#~+oy0!jEg~Y_BPdfk4nAY#~ zRaOX6vXJMWn3k8vxAgIf7Qu#8vPSmThoNmiw!rEz%%vISia zJ9lDG)~#cZU*7^!+2Ih}H6}#)Jj#k2-`syPR}64Hn7t#I$Mdyqh>9MiG2I@1D-3$t zN_2BH>QsnV>z&Csvo?JocHsS*YLJW2MJSWuZgwB$8~znCQp*{* zUGk>%gO**J_gc#$;TH7$Z|}8$GVM;kJ;!$3;7x-~`W781Zr}0RO?|O~1;CQs124e;K^d2G*Ck#oy69^=I!_p&;NEEGfdr zd7bD0%_*-#=+PFZfGF*L>s&nCVY(~&6}DdfMGz#}A{GaI*GG3IikpEUMQ~Y+mdtv3 zX||KJUl>p_Bl#*Y4T2s75KmrL25ekRgJL)w{h@8tSD!k;=3;q_sGw*3ME?LW%D8(R z?dkkpomfT4n|Ru|t6!&kPzB0vhsLVen2_f(%5)!s3Ign2PkL8ET?!D$Rboc@BjTeX zYfvAkth`NeX{UPdPlQVeWroC7bT=xPC%mEta! z*{4z|2|-;BIoffCH2MC?A22^lMekDa#VHur{KX%CR8Sd+}Wh{(Vqjqid>S<12S^+f@!Q>x$;ZP91##C1}z;^Uf+NRqsd*na zvS-uw>G)^t?#aF+*VK3L9H88!m09|b!G!h$>rET1Ju7Wr)zvip{cW9EtLoeoSkbxN z&$a5jWFR8i^0vxYp1L4d>+-v#f}qYMi3~3l?3)7UML0H)k3a}1P*KJO3;e+mGe>QJ z6apD)dCEGuaPUY@KQoV>&2y6OXQjeje*luBNLoa5cY*^n(VwxiNM2-aA1Lo2*YMI5 zi8x+gWtlpdGYc5(CRyV3jk=sX=ziUsL?-yn^0Df@NhShCQG;y^)@C=)Gbn zlpyC?7hfAVW1kN9U--lrF{Rb^8|LPQ_rA#JqFDQ(JGUi}}26s+X>1 z@HFiPphh3>V(BnG`x@!RViKq)WpIU6l;n3|=8^T*Z^?~~&5hcXPOlHg$780m9P*I$ z%H!~?y3NIiRh_u>fXs696=#XWO+{$TksAfm=^qKDSb5A6SIoEuJUJeBv+*(0wc|}c z`iJ4>Yk>;iaqU6ZUu~@=p^Z8(2U{ij7lf2CZ_QHMH62-bHvg$I$#9#yZ0h8)y@ky3 zJhS02QBu6Oc_n0lZzU)7wnrKigli(?RT+2`X9+qKU+1?LJF&U0aZ^O#x`v*|wg6iV zNkH)Y=UIuwNQ$K3Es}dvI6ZK#B*-P71J_>F_575_tBh8Cw?@7WFmW_DbH}jvhp3moJ*ao|gKo{9Shm`dZpo2e z9;@FotzISCwNs@zB5P^+P3{SuF^OPOp$&|mfQSQQM|O-w*-@|6z?ev%1&R0-2)DMx ze0=)7hEw2mO5ZUnW=`A0$(n(CxBu3SY`seYTH2k-SfqJPgY*q<{t;#|N6JW5n7KiY zvugH@VbvE~xJwXH+Pic*dB@w$efRzVrln*Y{XDH;{`HAkCRLa!yEnx@Cbc$jufNFi zx`{7n*qVxcRgyS)y$GFFlRy|2WOSQ=ZZ}IX-U`uFe2i2fFGRe|SzGW@o|SKLUH!_K zU;x+gHNh>N-Q$MkGUT^ct|h$~jh;8xqwPHr3-hh#`Y&ilxM5=SjR!K*X2%!@@$XnA+hpM#QLO=EFy zt}Bd<|Ae%$d^O0H8|`s7d4;k+0q}_vuX=??Lkz8UMd0<}9K%JdxZ}EzIXzrBrd*AJ zA=S#7z#hURg_8n`y($6m~>AtE-0lGBd0M*7e>x?IgDLmM+yVs?mR~@r;UIDVG%H(&Erm z8rX1xYs<{{+b=w(i;+=&_iOdQoKNn?R_AZ`?~muUS{K{>=`;54`+{C7J&&%(jOb?p zK;PrDd=4F}JAEmk5{urTtJ54vv&O~6V9X&iH}Y4$)de+>!Ayl{FlR@r6pGR_=MH(; zIHF77lkvamt3&=N2)yOLcJ-O4ndsASowCh#>+B3+wYSn8UC(e^04vEkG7)q# z^j67Y7ASm{7L9pMTXZQ~}0@#(PjD?4THmI=yM@4<#&m1y?QZ7SQCxKov`Mq3M2_SF;% z0m|m5cdHc*T79`UT3!hW^St10T$6OfWHJ;-a8vFz{i=A^+5ZXG zdeU~6PQai0F>K?cej<->E0^Q#Dz zX2nd4gR?V05VWIY`j$CL0=SXrJ-STOZFW&_C8_U_UN9r~fw`hZjN=-B~7 zbH`o!vu;X3R-vZ;CCNh|V9dGA?gv23G48uS>5ezFZl$=3)iOv;$dl-| zYUM*O+t`e60Lv(8&Hw`~RgarpVgW&G_kFcUI4IDdk_t5CwvZlMaJo@N$=u5JTe9)%ogSchU@W2J`};nY)%t*U~_W5<~z|%@(`3Ts0Q*{V|hT^5mKwKn&mpQ7^1!M!fc(1Ty|2o%CMDA9%#|8lxb`5=dySn3fDb`qhhWY7H1|SjR(q|%>zi~N_Ay_xx5HT1Si@9XC zHuhOVf(TL5;QW=hnF400E4DEHESv&EjXcRQ?%7AWFx*Haeh^pF$qf}Uo6sw%GJkJ6 zwq?5G2DZQ~;S0)z)jIBiABBM9A%QtnKm#I^l*zJ^#P6YU~p!VnMP3Eb_Xvesd5!Lr}X4=uP#Xs3-?sN+8`Ts|LLP&{Xm$r#UP~p*-{ukc^nuizJIn7 ztBm=%ny{hiXB&J=gmOKQ%i-r*(93VwO+QZ=$t_=cr}GV;h&IV>59GJ=O|Q^97k76& zV;X+$d9QfhrF34iJKy~}w0FLRsG4a8Rk9yG`+TQC)!DXCq5a!8zURp+`#>oN?;Pgd zFTfw|cQ0Bq4o%yu02nj9X;86-%KjZgM1wZAD^V4|TVnNE}kA zn)`w5@vTXZtf_|$AA_Ut?=0c%A)M!Q;DhlX5z0};i0#F*XzQG}M=uFi1;-u;B<3dQ zTvym}qN2-HK{K&1`ZL{*qZHT#ni!BFu!>9w({4Gi_o$VOZU|EhzW0va^P(u>mitgQ zD<0a-SbxwVYK(^P4sI_JqxOVmJcLy7%G5S8Y?pa*msz#NbeAS!AeSeGhSG*(F^0z| zjS}cck1UF@boZnHy7*BerIe-UYVy{6h{^Zs`85NYdv0n?HfSxzJG9)E|F_LW_5Y%{ z-=n4ckCqjPTyy#-ZI-dlnrTd)&Z@sRsP$+)A+hd~(m&y)*3VkrED4Yi#`5+K{=v>< zWf~IUhykfb##ul>Ht|VDSY&u|#sdTrM3b4P4ML*wEqvTD(4Z1ygk+gB77D61<>Jb9 zhC`w7CgT<^cTi<_Pm5bc<0r#jHwY9NJNjVU3Dp5P_jg5URcKHPhAN1ZC|kf*3up~efGA!Gqq$}S3H;;-mH!gA%Ktp3V< z4+C0U4kW4gSg6o+Vqa`OL>cxu4RpobB#e~l({MFiq~EehZDii<6d_8!P85X;vC8Mk zzB+z4GeIp;r0Xdr4To>XmYG7r$ZRfKWWwgY8}pM_5nbiQlQqn?Rg?9DPLlbJoH&eIE_WU5owrp#I&C0?9b~qm1jng#NL2PM@@zQ&Jz5;ki^EteDYqd7Y=O6 z`{U1F?hM^YBQm@qP9CglT&=YF&FPiEK5{>TCJTSp`gpAH;P(h1JYsscuHgX;DAR$n zJQt;B-BLlU(QI-t{5p%$ z(-Nw8n=5cpfo)teqSvD+y1wafJDi@8umTG-dc#|Vu9ztix1qbX8~Q0{{CZ8zc~NE# zaxqmfDPWe3nDr4u2rDQ34i`$Yi#NXbm~R_pSX{LGSGzpM^JLQY#c(PuS0gg4qawX0 z6sS8b?{y+>fFsJW4npe*4)R7ti6$~a zK{;!tk=4P&j2#=)x0@piW9*>WHG|5FQUb%$&B=h0oXeEm>Ac%N?N^f|vNs9pK72j7#)v0Pf!y$C zOD4#+vqjxzV%JK9k29cKNn;w$BDi=c_TfRrBrv|H zLF__qx8_y{#pc7x&*eEdEk7*KRF_2rF(oDAPpJ{ECxVE+kNaJfpt;CH3|gM8)jzK+y#EH~S{@Zqg92O!kEqniTUFHq-yuM=K~ULdNVo7p1+sLBY*& z#cvSM|D*jhkVA^Xf1XD*nk%^t-pL`}*0kl&6 zA+7_EV}Iav8stDPgdIr3+~AtYKG1j%<3Ri NA&0ZjBFA^{{tt2`<01e6 literal 0 HcmV?d00001 diff --git a/src/main/java/oving1/.gitkeep b/src/main/java/oving1/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/test/java/oving1/AccountTest.java b/src/test/java/oving1/AccountTest.java new file mode 100644 index 0000000..bd916db --- /dev/null +++ b/src/test/java/oving1/AccountTest.java @@ -0,0 +1,55 @@ +package oving1; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class AccountTest { + + private double delta = 1e-8; + private Account account; + + @BeforeEach + public void setUp() { + account = new Account(); + } + + @Test + @DisplayName("Constructor") + public void testConstructor() { + assertEquals(0.0, account.getBalance(), delta, "Wrong balance for newly created account"); + } + + @Test + @DisplayName("Deposit") + public void testDeposit() { + account.deposit(100); + + assertEquals(100.0, account.getBalance(), delta, "Wrong balance after depositing"); + } + + @Test + @DisplayName("Negative deposit") + public void testNegativeDeposit() { + account.deposit(-50); + + assertEquals(0.0, account.getBalance(), delta, + "Wrong balance after making negative deposit"); + } + + @Test + @DisplayName("Adding interest") + public void testAddInterest() { + account.setInterestRate(5); + assertEquals(0, account.getBalance(), delta, "Wrong balance after updating interest rate"); + assertEquals(5, account.getInterestRate(), delta, + "Wrong interest rate after updating interest rate"); + + account.deposit(100); + assertEquals(100, account.getBalance(), delta, "Wrong balance after depositing"); + + account.addInterest(); + assertEquals(105, account.getBalance(), delta, "Wrong balance after adding interest"); + } +} diff --git a/src/test/java/oving1/DigitTest.java b/src/test/java/oving1/DigitTest.java new file mode 100644 index 0000000..e32d45d --- /dev/null +++ b/src/test/java/oving1/DigitTest.java @@ -0,0 +1,69 @@ +package oving1; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + + +public class DigitTest { + + private String digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + @Test + @DisplayName("Constructor") + public void testConstructor() { + Digit digit = new Digit(10); + assertEquals(0, digit.getValue(), "Wrong value when using constructor"); + assertEquals(10, digit.getBase(), "Wrong base when using constructor"); + } + + @Test + @DisplayName("Increment value") + public void testIncrementedValue() { + for (int base = 2; base < 17; base++) { + Digit digit = new Digit(base); + assertEquals(0, digit.getValue(), "Wrong value for created digit"); + + int i = 1; + + while (i < base) { + boolean result = digit.increment(); + assertEquals(i, digit.getValue(), "The value was not incremented correctly"); + assertFalse(result, + "Increment should return false when the value is less than the base"); + i++; + } + + boolean result = digit.increment(); + assertEquals(0, digit.getValue(), + "The value was not reset to 0 when it became equal to the base"); + assertTrue(result, "Increment should return true when the value is reset to 0"); + } + } + + @Test + @DisplayName("Increment value and convert to string") + public void testIncrementedToString() { + for (int base = 2; base < 17; base++) { + Digit digit = new Digit(base); + assertEquals("0", digit.toString(), "Wrong string representation"); + + int i = 1; + + while (i < base) { + boolean result = digit.increment(); + assertEquals(String.valueOf(digits.charAt(i)), digit.toString(), + "Wrong string representation"); + assertFalse(result, + "Increment should return false when the value is less than the base"); + i++; + } + + boolean result = digit.increment(); + assertEquals("0", digit.toString(), "Wrong string representation"); + assertTrue(result, "Increment should return true when the value is reset to 0"); + } + } +} diff --git a/src/test/java/oving1/LineEditorTest.java b/src/test/java/oving1/LineEditorTest.java new file mode 100644 index 0000000..4b6cd2c --- /dev/null +++ b/src/test/java/oving1/LineEditorTest.java @@ -0,0 +1,137 @@ +package oving1; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class LineEditorTest { + + private LineEditor lineEditor; + + private void checkEditorContent(String s) { + assertEquals(s, lineEditor.toString(), "Wrong value returned by toString()"); + + int pos = s.indexOf('|'); + assertEquals(s.substring(0, pos) + s.substring(pos + 1), lineEditor.getText(), + "Wrong text value returned"); + assertEquals(pos, lineEditor.getInsertionIndex(), "Wrong insertion index"); + } + + @BeforeEach + public void setUp() { + lineEditor = new LineEditor(); + } + + @Test + @DisplayName("Constructor") + public void testContstructor() { + this.checkEditorContent("|"); + } + + @Test + @DisplayName("Setter methods") + public void testSetters() { + lineEditor.setText("Hello World!"); + this.checkEditorContent("|Hello World!"); + + lineEditor.setInsertionIndex(5); + this.checkEditorContent("Hello| World!"); + } + + @Test + @DisplayName("Insert string at end") + public void testInsertStringAtEnd() { + lineEditor.insertString(""); + this.checkEditorContent("|"); + + lineEditor.insertString("Java"); + this.checkEditorContent("Java|"); + + lineEditor.insertString(" er gøy!"); + this.checkEditorContent("Java er gøy!|"); + } + + @Test + @DisplayName("Insert string in the middle") + public void testInsertStringMiddle() { + lineEditor.setText("Javagøy!"); + lineEditor.setInsertionIndex(4); + lineEditor.insertString(" er "); + this.checkEditorContent("Java er |gøy!"); + } + + @Test + @DisplayName("Insert string at the begginning") + public void testInsertStringAtBeginning() { + lineEditor.setText("er gøy!"); + lineEditor.setInsertionIndex(0); + lineEditor.insertString("Java "); + this.checkEditorContent("Java |er gøy!"); + } + + @Test + @DisplayName("Move left") + public void testLeft() { + lineEditor.left(); + this.checkEditorContent("|"); + + lineEditor.setText("J"); + lineEditor.setInsertionIndex(1); + this.checkEditorContent("J|"); + + lineEditor.left(); + this.checkEditorContent("|J"); + } + + @Test + @DisplayName("Move right") + public void testRight() { + lineEditor.right(); + this.checkEditorContent("|"); + + lineEditor.setText("J"); + lineEditor.setInsertionIndex(0); + this.checkEditorContent("|J"); + + lineEditor.right(); + this.checkEditorContent("J|"); + } + + @Test + @DisplayName("Delete left") + public void testDeleteLeft() { + lineEditor.deleteLeft(); + this.checkEditorContent("|"); + + lineEditor.insertString("J"); + lineEditor.deleteLeft(); + this.checkEditorContent("|"); + + lineEditor.insertString("Java"); + lineEditor.setInsertionIndex(2); + this.checkEditorContent("Ja|va"); + + lineEditor.deleteLeft(); + this.checkEditorContent("J|va"); + } + + @Test + @DisplayName("Delete right") + public void testDeleteRight() { + lineEditor.deleteRight(); + this.checkEditorContent("|"); + + lineEditor.insertString("J"); + lineEditor.setInsertionIndex(0); + lineEditor.deleteRight(); + this.checkEditorContent("|"); + + lineEditor.insertString("Java"); + lineEditor.setInsertionIndex(2); + this.checkEditorContent("Ja|va"); + + lineEditor.deleteRight(); + this.checkEditorContent("Ja|a"); + } +} diff --git a/src/test/java/oving1/LocationTest.java b/src/test/java/oving1/LocationTest.java new file mode 100644 index 0000000..95b5d18 --- /dev/null +++ b/src/test/java/oving1/LocationTest.java @@ -0,0 +1,101 @@ +package oving1; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class LocationTest { + + private Location loc; + + /** + * Check that the position of {@link #loc} is equal to the parameters. + * + * @param x Expected x position + * @param y Expected y position + */ + private void checkPos(int x, int y) { + assertEquals(x, loc.getX(), "Wrong x coordinate"); + assertEquals(y, loc.getY(), "Wrong y coordinate"); + } + + @BeforeEach + public void beforeEach() { + loc = new Location(); + } + + @Test + @DisplayName("Constructor") + public void testConstructor() { + this.checkPos(0, 0); + } + + @Test + @DisplayName("Move up") + public void testUp() { + loc.up(); + this.checkPos(0, -1); + + loc.up(); + this.checkPos(0, -2); + } + + @Test + @DisplayName("Move down") + public void testDown() { + loc.down(); + this.checkPos(0, 1); + + loc.down(); + this.checkPos(0, 2); + } + + @Test + @DisplayName("Move left") + public void testLeft() { + loc.left(); + this.checkPos(-1, 0); + + loc.left(); + this.checkPos(-2, 0); + } + + @Test + @DisplayName("Move right") + public void testRight() { + loc.right(); + this.checkPos(1, 0); + + loc.right(); + this.checkPos(2, 0); + } + + @Test + @DisplayName("Move multiple directions") + public void testComplexMovement() { + loc.right(); + this.checkPos(1, 0); + + loc.down(); + this.checkPos(1, 1); + + loc.right(); + this.checkPos(2, 1); + + loc.down(); + this.checkPos(2, 2); + + loc.left(); + this.checkPos(1, 2); + + loc.up(); + this.checkPos(1, 1); + + loc.up(); + this.checkPos(1, 0); + + loc.left(); + this.checkPos(0, 0); + } +} diff --git a/src/test/java/oving1/RectangleExtraTest.java b/src/test/java/oving1/RectangleExtraTest.java new file mode 100644 index 0000000..a55c070 --- /dev/null +++ b/src/test/java/oving1/RectangleExtraTest.java @@ -0,0 +1,31 @@ +package oving1; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static oving1.RectangleTest.assertValues; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class RectangleExtraTest { + + @Test + @DisplayName("Intersecting rectangles") + public void testIntersection() { + Rectangle rect1 = new Rectangle(0, 0, 5, 5); + Rectangle rect2 = new Rectangle(3, 3, 7, 7); + Rectangle intersection = rect1.intersection(rect2); + assertValues(intersection, 3, 3, 5, 5, 2, 2, " for intersecting rectangle"); + assertTrue(rect1.intersects(rect2), "Wrong value from #intersects(Rectangle)"); + } + + @Test + @DisplayName("Non-intersecting rectangles") + public void testNonIntersection() { + Rectangle rect1 = new Rectangle(-3, -3, 0, 0); + Rectangle rect2 = new Rectangle(3, 3, 7, 7); + Rectangle intersection = rect1.intersection(rect2); + assertNull(intersection, "Intersection of two non-intersecting rectangles should be null"); + assertFalse(rect1.intersects(rect2), "Wrong value from #intersects(Rectangle)"); + } +} diff --git a/src/test/java/oving1/RectangleTest.java b/src/test/java/oving1/RectangleTest.java new file mode 100644 index 0000000..9db9c8e --- /dev/null +++ b/src/test/java/oving1/RectangleTest.java @@ -0,0 +1,271 @@ +package oving1; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class RectangleTest { + + // Used in RectangleExtraTest as well + /** + * Compares all values in a given {@link Rectangle} to a set of expected values. + * + * @param rect The rectangle to check + * @param minX The expected minimum x value of rect + * @param minY The expected minimum y value of rect + * @param maxX The expected maximum x value of rect + * @param maxY The expected maximum y value of rect + * @param width The expected width of rect + * @param height The expected height of rect + */ + public static void assertValues(Rectangle rect, int minX, int minY, int maxX, int maxY, + int width, int height, String suffix) { + assertEquals(minX, rect.getMinX(), "Wrong minX " + suffix); + assertEquals(minY, rect.getMinY(), "Wrong minY " + suffix); + assertEquals(maxX, rect.getMaxX(), "Wrong maxX " + suffix); + assertEquals(maxY, rect.getMaxY(), "Wrong maxY " + suffix); + assertEquals(width, rect.getWidth(), "Wrong width " + suffix); + assertEquals(height, rect.getHeight(), "Wrong height " + suffix); + } + + /** + * Check that a rectangle is empty. + * + * @param rect The rectangle to check + */ + private static void assertEmpty(Rectangle rect) { + assertTrue(rect.isEmpty(), "Expected rectangle to be empty!"); + assertTrue(rect.getWidth() == 0 || rect.getHeight() == 0, + "Empty rectangle should either have a width or height of 0!"); + } + + @Test + @DisplayName("Empty rectangle") + public void testEmpty() { + // Test creating empty rectangle + Rectangle rect1 = new Rectangle(0, 0, 0, 0); + assertEmpty(rect1); + + // Test creating empty rectangle with only 0 height + Rectangle rect2 = new Rectangle(3, 2, 1, 2); + assertEmpty(rect2); + + // Test creating empty rectangle with only 0 width + Rectangle rect3 = new Rectangle(3, 1, 3, 2); + assertEmpty(rect3); + } + + @Test + @DisplayName("Rectangle constructor") + public void testConstructor() { + // Simple test + Rectangle rect1 = new Rectangle(0, 0, 1, 2); + RectangleTest.assertValues(rect1, 0, 0, 1, 2, 1, 2, "when testing constructor"); + + // Test providing points in opposite order + Rectangle rect2 = new Rectangle(1, 2, 0, 0); + RectangleTest.assertValues(rect2, 0, 0, 1, 2, 1, 2, "when testing constructor"); + + // Test negative values + Rectangle rect3 = new Rectangle(3, 3, -1, 5); + RectangleTest.assertValues(rect3, -1, 3, 3, 5, 4, 2, "when testing constructor"); + } + + private void testAdd(Rectangle rect, int x, int y, boolean expected) { + assertEquals(expected, rect.add(x, y), + "Wrong value returned when adding (" + x + ", " + y + ") to " + rect); + assertFalse(rect.isEmpty(), "Rectangle should not be empty after adding a point!"); + assertTrue(rect.contains(x, y), + "Rectangle should contain the point that has been just added!"); + } + + @Test + @DisplayName("Adding point to rectangle") + public void testAddXY() { + int x1 = 13; + int y1 = -27; + int x2 = -11; + int y2 = 23; + int x3 = 15; + int y3 = 33; + + Rectangle rect = new Rectangle(x1, y1, x2, y2); + + // Add (x3, y3) and check that rect is updated accordingly + this.testAdd(rect, x3, y3, true); + + int minX1X2 = Math.min(x1, x2), minY1Y2 = Math.min(y1, y2); + int maxX1X2 = Math.max(x1, x2), maxY1Y2 = Math.max(y1, y2); + int minX1X2X3 = Math.min(minX1X2, x3), minY1Y2Y3 = Math.min(minY1Y2, y3); + int maxX1X2X3 = Math.max(maxX1X2, x3), maxY1Y2Y3 = Math.max(maxY1Y2, y3); + + RectangleTest.assertValues(rect, minX1X2X3, minY1Y2Y3, maxX1X2X3, maxY1Y2Y3, + maxX1X2X3 - minX1X2X3, maxY1Y2Y3 - minY1Y2Y3, + "when adding point that is not in rectangle"); + } + + @Test + @DisplayName("Adding point already in rectangle") + public void testAddSameXY() { + int x1 = 13; + int y1 = -27; + int x2 = -11; + int y2 = 23; + int x3 = 15; + int y3 = 33; + + int minX1X2 = Math.min(x1, x2); + int minY1Y2 = Math.min(y1, y2); + int maxX1X2 = Math.max(x1, x2); + int maxY1Y2 = Math.max(y1, y2); + int minX1X2X3 = Math.min(minX1X2, x3); + int minY1Y2Y3 = Math.min(minY1Y2, y3); + int maxX1X2X3 = Math.max(maxX1X2, x3); + int maxY1Y2Y3 = Math.max(maxY1Y2, y3); + + Rectangle rect = new Rectangle(x1, y1, x2, y2); + + // Add (x3, y3) and check that rect is updated accordingly + this.testAdd(rect, x3, y3, true); + RectangleTest.assertValues(rect, minX1X2X3, minY1Y2Y3, maxX1X2X3, maxY1Y2Y3, + maxX1X2X3 - minX1X2X3, maxY1Y2Y3 - minY1Y2Y3, + "when adding point that is not in rectangle"); + + // Add (x3, y3) again and check that all state is the same, with false response + this.testAdd(rect, x3, y3, false); + RectangleTest.assertValues(rect, minX1X2X3, minY1Y2Y3, maxX1X2X3, maxY1Y2Y3, + maxX1X2X3 - minX1X2X3, maxY1Y2Y3 - minY1Y2Y3, + "when adding point that is in rectangle"); + } + + @Test + @DisplayName("Adding other rectangle") + public void testAddRectangle() { + // Add a point to this.rect + int x1 = 13; + int y1 = -27; + int x2 = -11; + int y2 = 23; + int x3 = 15; + int y3 = 33; + int minX1X2 = Math.min(x1, x2); + int minY1Y2 = Math.min(y1, y2); + int maxX1X2 = Math.max(x1, x2); + int maxY1Y2 = Math.max(y1, y2); + int minX1X3 = Math.min(x1, x3); + int minY1Y3 = Math.min(y1, y3); + int maxX1X3 = Math.max(x1, x3); + int maxY1Y3 = Math.max(y1, y3); + + int minX1X2X3 = Math.min(minX1X2, x3); + int minY1Y2Y3 = Math.min(minY1Y2, y3); + int maxX1X2X3 = Math.max(maxX1X2, x3); + int maxY1Y2Y3 = Math.max(maxY1Y2, y3); + int widthX1X2X3 = maxX1X2X3 - minX1X2X3; + int heightX1X2X3 = maxY1Y2Y3 - minY1Y2Y3; + + // Create a rectangle and fill it with some points. Assert that this rect is correct + Rectangle rect = new Rectangle(x1, y1, x2, y2); + RectangleTest.assertValues(rect, minX1X2, minY1Y2, maxX1X2, maxY1Y2, maxX1X2 - minX1X2, + maxY1Y2 - minY1Y2, "when creating new rectangle"); + + // Create another rectangle and fill it with some points. Assert that this rect is correct + Rectangle rect2 = new Rectangle(x1, y1, x3, y3); + RectangleTest.assertValues(rect2, minX1X3, minY1Y3, maxX1X3, maxY1Y3, maxX1X3 - minX1X3, + maxY1Y3 - minY1Y3, "when creating new rectangle"); + + // Add rect to this.rect, and check correctness + assertTrue(rect.add(rect2)); + RectangleTest.assertValues(rect, minX1X2X3, minY1Y2Y3, maxX1X2X3, maxY1Y2Y3, widthX1X2X3, + heightX1X2X3, "when adding another rectangle"); + } + + @Test + @DisplayName("Adding rectangle to itself") + public void testAddSameRectangle() { + int x1 = 13; + int y1 = -27; + int x2 = -11; + int y2 = 23; + int width = Math.abs(x1 - x2); + int height = Math.abs(y1 - y2); + int minX1X2 = Math.min(x1, x2); + int minY1Y2 = Math.min(y1, y2); + int maxX1X2 = Math.max(x1, x2); + int maxY1Y2 = Math.max(y1, y2); + + // Create a rectangle and fill it with some points. Assert that this rect is correct + Rectangle rect = new Rectangle(x1, y1, x2, y2); + RectangleTest.assertValues(rect, minX1X2, minY1Y2, maxX1X2, maxY1Y2, width, height, + "when creating new rectangle"); + + // Add rectangle to itself and check that state stays the same + assertFalse(rect.add(rect), + "Expected no change when adding equal rectangle using #add(Rectangle)"); + RectangleTest.assertValues(rect, minX1X2, minY1Y2, maxX1X2, maxY1Y2, width, height, + "when adding the same rectangle to itself"); + } + + @Test + @DisplayName("Rectangle union") + public void testUnion() { + int x1 = 13; + int y1 = -27; + int x2 = -11; + int y2 = 23; + int x3 = 15; + int y3 = 33; + int x4 = 17; + int y4 = -33; + + int minX1X2 = Math.min(x1, x2); + int minY1Y2 = Math.min(y1, y2); + int maxX1X2 = Math.max(x1, x2); + int maxY1Y2 = Math.max(y1, y2); + int minX3X4 = Math.min(x3, x4); + int minY3Y4 = Math.min(y3, y4); + int maxX3X4 = Math.max(x3, x4); + int maxY3Y4 = Math.max(y3, y4); + + // Create two rectangles and check correctness + Rectangle rect1 = new Rectangle(x1, y1, x2, y2); + RectangleTest.assertValues(rect1, minX1X2, minY1Y2, maxX1X2, maxY1Y2, maxX1X2 - minX1X2, + maxY1Y2 - minY1Y2, "when creating new rectangle"); + + Rectangle rect2 = new Rectangle(x3, y3, x4, y4); + RectangleTest.assertValues(rect2, minX3X4, minY3Y4, maxX3X4, maxY3Y4, maxX3X4 - minX3X4, + maxY3Y4 - minY3Y4, "when creating new rectangle"); + + // Take the union (both ways), and check that both are the same + int minX = Math.min(minX1X2, minX3X4); + int minY = Math.min(minY1Y2, minY3Y4); + int maxX = Math.max(maxX1X2, maxX3X4); + int maxY = Math.max(maxY1Y2, maxY3Y4); + + Rectangle union1 = rect1.union(rect2); + RectangleTest.assertValues(union1, minX, minY, maxX, maxY, maxX - minX, maxY - minY, + "when calling #union with another rectangle"); + RectangleTest.assertValues(rect1, minX1X2, minY1Y2, maxX1X2, maxY1Y2, maxX1X2 - minX1X2, + maxY1Y2 - minY1Y2, + "Values changed when calling #union with another rectangle! Make sure to not " + + "create a new Rectangle and not modify the current ones!"); + RectangleTest.assertValues(rect2, minX3X4, minY3Y4, maxX3X4, maxY3Y4, maxX3X4 - minX3X4, + maxY3Y4 - minY3Y4, + "Values changed when calling #union with another rectangle! Make sure to not " + + "create a new Rectangle and not modify the current ones!"); + + Rectangle union2 = rect2.union(rect1); + RectangleTest.assertValues(union2, minX, minY, maxX, maxY, maxX - minX, maxY - minY, + "when calling #union with another rectangle"); + RectangleTest.assertValues(rect1, minX1X2, minY1Y2, maxX1X2, maxY1Y2, maxX1X2 - minX1X2, + maxY1Y2 - minY1Y2, + "Values changed when calling #union with another rectangle! Make sure to not " + + "create a new Rectangle and not modify the current ones!"); + RectangleTest.assertValues(rect2, minX3X4, minY3Y4, maxX3X4, maxY3Y4, maxX3X4 - minX3X4, + maxY3Y4 - minY3Y4, + "Values changed when calling #union with another rectangle! Make sure to not " + + "create a new Rectangle and not modify the current ones!"); + } +} diff --git a/src/test/java/oving1/StopWatchTest.java b/src/test/java/oving1/StopWatchTest.java new file mode 100644 index 0000000..57923fd --- /dev/null +++ b/src/test/java/oving1/StopWatchTest.java @@ -0,0 +1,153 @@ +package oving1; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + + +public class StopWatchTest { + + private StopWatch stopWatch; + + @BeforeEach + public void beforeEach() { + stopWatch = new StopWatch(); + } + + @Test + @DisplayName("Constructor") + public void testConstructor() { + assertFalse(stopWatch.isStarted(), "Stopwatch should not be started"); + assertFalse(stopWatch.isStopped(), "Stopwatch should not be stopped"); + assertEquals(0, stopWatch.getTicks(), "Wrong ticks returned"); + assertEquals(-1, stopWatch.getTime(), "Time should be -1 when not started"); + assertEquals(-1, stopWatch.getLapTime(), "Lap time should be -1 when not started"); + assertEquals(-1, stopWatch.getLastLapTime(), "Last lap time should be -1 when not started"); + } + + @Test + @DisplayName("Tick without starting") + public void testTicksWithoutStart() { + stopWatch.tick(1); + assertEquals(-1, stopWatch.getTime(), "Time should be -1 when not started"); + assertEquals(1, stopWatch.getTicks(), "Ticks should be 1 after calling #tick(1)"); + + stopWatch.tick(4); + assertEquals(-1, stopWatch.getTime(), "Time should be -1 when not started"); + assertEquals(5, stopWatch.getTicks(), "Ticks should be 5 after calling #tick(4)"); + } + + @Test + @DisplayName("Tick, start and stop 1") + public void testStartTickStop() { + stopWatch.start(); + assertEquals(0, stopWatch.getTime(), "Time should be 0 when just started"); + assertEquals(0, stopWatch.getTicks(), "Ticks should be 0 when #tick() has not been called"); + assertTrue(stopWatch.isStarted(), "Should be started after calling #start()"); + assertFalse(stopWatch.isStopped(), "Should not be stopped before calling #stop()"); + + stopWatch.tick(3); + assertEquals(3, stopWatch.getTime(), + "Time should be 3 when started and #tick(3) has been called"); + assertEquals(3, stopWatch.getTicks(), "Ticks should be 3 when #tick(3) has been called"); + assertTrue(stopWatch.isStarted(), "Should be started after calling #start()"); + assertFalse(stopWatch.isStopped(), "Should not be stopped before calling #stop()"); + + stopWatch.tick(5); + assertEquals(8, stopWatch.getTime(), + "Time should be 8 when started and #tick(5) has been called"); + assertEquals(8, stopWatch.getTicks(), "Ticks should be 8 when #tick(5) has been called"); + assertTrue(stopWatch.isStarted(), "Should be started after calling #start()"); + assertFalse(stopWatch.isStopped(), "Should not be stopped before calling #stop()"); + + stopWatch.stop(); + assertEquals(8, stopWatch.getTime(), "Time should be 8 after #stop() has been called"); + assertEquals(8, stopWatch.getTicks(), "Ticks should be 8 after #stop() has been called"); + assertTrue(stopWatch.isStarted(), "Should be started even after #stop() has been called"); + assertTrue(stopWatch.isStopped(), "Should be stopped after calling #stop()"); + } + + @Test + @DisplayName("Tick, start and stop 2") + public void testTickStartTickStopTick() { + stopWatch.tick(2); + assertEquals(-1, stopWatch.getTime(), "Time should be -1 when not started"); + assertEquals(2, stopWatch.getTicks(), "Ticks should be 2 when #tick(2) has been called"); + assertFalse(stopWatch.isStarted(), "Stopwatch should not be started"); + assertFalse(stopWatch.isStopped(), "Stopwatch should not be stopped"); + + stopWatch.start(); + assertEquals(0, stopWatch.getTime(), "Time should be 0 when just started"); + assertEquals(2, stopWatch.getTicks(), "Ticks should be 2 after #start() has been called"); + assertTrue(stopWatch.isStarted(), "Should be started after calling #start()"); + assertFalse(stopWatch.isStopped(), "Should not be stopped before calling #stop()"); + + stopWatch.tick(3); + assertEquals(3, stopWatch.getTime(), + "Time should be 3 when started and #tick(3) has been called"); + assertEquals(5, stopWatch.getTicks(), "Ticks should be 5 when #tick(3) has been called"); + assertTrue(stopWatch.isStarted(), "Should be started after calling #tick(3)"); + assertFalse(stopWatch.isStopped(), "Should not be stopped before calling #tick(3)"); + + stopWatch.tick(5); + assertEquals(8, stopWatch.getTime(), + "Time should be 8 when started and #tick(5) has been called"); + assertEquals(10, stopWatch.getTicks(), "Ticks should be 10 when #tick(5) has been called"); + assertTrue(stopWatch.isStarted(), "Should be started after calling #tick(5)"); + assertFalse(stopWatch.isStopped(), "Should not be stopped before calling #tick(5)"); + + stopWatch.stop(); + assertEquals(8, stopWatch.getTime(), "Time should be 8 after #stop() has been called"); + assertEquals(10, stopWatch.getTicks(), "Ticks should be 10 after #stop() has been called"); + assertTrue(stopWatch.isStarted(), "Should be started even after #stop() has been called"); + assertTrue(stopWatch.isStopped(), "Should be stopped after calling #stop()"); + + stopWatch.tick(3); + assertEquals(8, stopWatch.getTime(), + "Time should be 8 after #tick(3) has been called while stopped"); + assertEquals(13, stopWatch.getTicks(), + "Ticks should be 13 when #tick(3) has been called while stopped"); + assertTrue(stopWatch.isStarted(), + "Should be started even after #tick() has been called while stopped"); + assertTrue(stopWatch.isStopped(), "Should be stopped after calling #tick() while stopped"); + } + + @Test + @DisplayName("Lap times") + public void testLaps() { + stopWatch.start(); + assertEquals(0, stopWatch.getTime(), "Time should be 0 when just started"); + assertEquals(0, stopWatch.getLapTime(), "Lap time should be 0 when just started"); + assertEquals(-1, stopWatch.getLastLapTime(), + "Last lap time should be -1 when there is no previous lap"); + + stopWatch.tick(3); + assertEquals(3, stopWatch.getTime(), "Time should be 3 after #tick(3) has been called"); + assertEquals(3, stopWatch.getLapTime(), + "Lap time should be 3 after calling #tick(3) while started"); + assertEquals(-1, stopWatch.getLastLapTime(), + "Last lap time should be -1 when there is no previous lap"); + + stopWatch.lap(); + assertEquals(3, stopWatch.getTime(), "Time should still be 3 after starting a new lap"); + assertEquals(0, stopWatch.getLapTime(), "Current lap time should be 0 when just started"); + assertEquals(3, stopWatch.getLastLapTime(), + "Last lap time should be 3 when we just started a new lap"); + + stopWatch.tick(5); + assertEquals(8, stopWatch.getTime(), "Time should be 8 after #tick(5) has been called"); + assertEquals(5, stopWatch.getLapTime(), + "Current lap time should be 5 after calling #tick(5)"); + assertEquals(3, stopWatch.getLastLapTime(), + "Last lap time should be 3 even after time passes"); + + stopWatch.stop(); + assertEquals(8, stopWatch.getTime(), "Time should be 8 after stopping"); + assertEquals(0, stopWatch.getLapTime(), "Current lap time should be 0 when stopped"); + assertEquals(5, stopWatch.getLastLapTime(), + "Last lap should be the lap time of the current lap when stopping"); + } +} diff --git a/src/test/java/oving1/UpOrDownCounterTest.java b/src/test/java/oving1/UpOrDownCounterTest.java new file mode 100644 index 0000000..948d39e --- /dev/null +++ b/src/test/java/oving1/UpOrDownCounterTest.java @@ -0,0 +1,57 @@ +package oving1; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + + +public class UpOrDownCounterTest { + + @Test + @DisplayName("Count up") + public void testCountUp() { + UpOrDownCounter counter = new UpOrDownCounter(1, 5); + assertEquals(1, counter.getCounter(), + "Counter value should be equal to starting value when created"); + + for (int i = 2; i < 5; i++) { + boolean result = counter.count(); + assertEquals(i, counter.getCounter(), "Wrong counter value"); + assertTrue(result, "#count() should return true while within legal values"); + } + + boolean result = counter.count(); + assertEquals(5, counter.getCounter(), "Wrong counter value"); + assertFalse(result, "#count() should return false when we cannot count further"); + + result = counter.count(); + assertEquals(5, counter.getCounter(), + "Counter value should not change when counting beyond legal values"); + assertFalse(result, "#count() should return false when we cannot count further"); + } + + @Test + @DisplayName("Count down") + public void testCountDown() { + UpOrDownCounter counter = new UpOrDownCounter(1, -5); + assertEquals(1, counter.getCounter(), + "Counter value should be equal to starting value when created"); + + for (int i = 0; i > -5; i--) { + boolean result = counter.count(); + assertEquals(i, counter.getCounter(), "Wrong counter value"); + assertTrue(result, "#count() should return true while within legal values"); + } + + boolean result = counter.count(); + assertEquals(-5, counter.getCounter(), "Wrong counter value"); + assertFalse(result, "#count() should return false when we cannot count further"); + + result = counter.count(); + assertEquals(-5, counter.getCounter(), + "Counter value should not change when counting beyond legal values"); + assertFalse(result, "#count() should return false when we cannot count further"); + } +}