From d19bac7a1ff514108bb8ade8ab15f9acc886c8e8 Mon Sep 17 00:00:00 2001 From: Simon Hensch Date: Mon, 14 Sep 2026 12:43:17 +0200 Subject: [PATCH] =?UTF-8?q?[ADD]=20Erste=20Einbindung=20eines=20Men=C3=BCs?= =?UTF-8?q?=20zum=20Konfigurieren=20und=20Einf=C3=BCgen=20von=20Ger=C3=BCs?= =?UTF-8?q?tkomponenten;=20TODOs:=20Anzahl=20der=20Ger=C3=BCstkompnenten?= =?UTF-8?q?=20Variabel=20machen=20und=20f=C3=BCr=20den=20Benutzer=20die=20?= =?UTF-8?q?Anzahl=20der=20Baugruppe=20sichtbar=20machen;=20Vorkonfiguriert?= =?UTF-8?q?e=20Baugruppen=20anbieten;=20Einfachere=20Auswahl=20mit=20Check?= =?UTF-8?q?box=20oder=20Input=20(Anzahl)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 5 +- Lisp/GeruestInsert.lsp | 482 +++++++++++++++++++++++++++ Lisp/SSG_LIB_Commands.lsp | 8 + Lisp/ssg_core.lsp | 207 ++++++++++++ data/geruest/R2-Saeule_dummy.dwg | Bin 0 -> 43607 bytes data/geruest/Rohrabhaenger_dummy.dwg | Bin 0 -> 45273 bytes data/json/geruest.json | 419 +++++++++++++++++++++++ dcl/geruest.dcl | 61 ++++ lang/de_DE.json | 14 +- lang/en_GB.json | 14 +- menu/SSG_LIB.cui | 306 ++++++++++------- menu/SSG_LIB.mnu | 6 + tests/test_unit.lsp | 130 ++++++++ 13 files changed, 1524 insertions(+), 128 deletions(-) create mode 100644 Lisp/GeruestInsert.lsp create mode 100644 data/geruest/R2-Saeule_dummy.dwg create mode 100644 data/geruest/Rohrabhaenger_dummy.dwg create mode 100644 data/json/geruest.json create mode 100644 dcl/geruest.dcl diff --git a/CLAUDE.md b/CLAUDE.md index 0b0de11..d2535cb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -75,6 +75,7 @@ Die `start_briscad.bat` ruft `setenv.bat` auf und startet BricsCAD. | VarioFoerderer | `VarioFoerderer` | FOERDERANLAGE, VARIOFOERDERER_EDIT, Vario_Kette_Merge | | Gefaellestrecke | `Gefaellestrecke` | GEFAELLESTRECKE, GEFAELLESTRECKE_EDIT | | Omniflo (komplett) | `OmniModulInsert` | OMNI_APB_*, OMNI_W*_*, OMNI_AP*, OMNI_TEF_*, OMNI_TV_*, OMNI_EDIT | +| Geruest | `GeruestInsert` | GERUEST_ROHRABHAENGER, GERUEST_SAEULE_2ZOLL, GERUEST_SAEULE_1ZOLL, GERUEST_WANDBEFESTIGUNG | | Connections | `Connection_Edit` + `SSG_LIB_Commands` | CONNECTION_INSERT, CONNECTION_EDIT, ILS_EINGANG_INSERT, ILS_AUSGANG_INSERT | | Export | `export` | EXPORTSIVAS, EXPORTCSV, CALLPYTHON | | Konfiguration | `SSG_LIB_Commands` + `DrawPlanquadrate` | SSG_DIM_SWITCH, SSG_SPRACHE, PLANQUADRATE_TOGGLE (DRAW_PLANQUADRATE/DELETE_PLANQUADRATE) | @@ -92,7 +93,7 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`. | Datei | Befehle | Beschreibung | |---|---|---| -| `ssg_core.lsp` | - | Kernel: ssg-start/ssg-end, ssg-cfg-or, ssg-attrib-read/write, Layer/Block-Ops. Zentrale Helfer: `ssg-lisp-verzeichnis`/`ssg-lisp-datei-pfad` (Lisp-Pfad-Ermittlung), `ssg-rot-matrix-zy` (4x4 Rz(hz)*Ry(vert)-Rotationsmatrix, clusteruebergreifend), GUI-Aus-Schalter `*ssg-gui-aus*` + `ssg-gui-aus`/`ssg-gui-an`/`ssg-gui-p` (fuer automatische Tests: schaltet DCL-/Wizard-Dialoge ab, Module fallen auf Konsolen-/getXXX-Pfad zurueck; der Linienzug hat zusaetzlich `*vfl-headless*`, das die Konsolen-Rueckfrage selbst verbietet - siehe VarioFoerderer-Zeile). **Layer-Zuordnung**: `ssg-layer-cfg-load`/`-reset` (liest `cfg/layer.cfg` lazy), `ssg-layer-cfg-eintrag` (reine Aufloesung "Layername, Farbe" -> `(layer . farbe)`, in `tests/test_unit.lsp` getestet), `ssg-layer-eintrag` (Suchreihenfolge `[ils_]` -> `[allgemein]`), `ssg-layer-anlegen` (Layer mit cfg-Farbe anlegen, optional aktivieren) und `ssg-layer-setzen` (Blockreferenz auf den Baugruppen-Layer legen) - fuer ZUSAMMENGEBAUTE Baugruppen; `ssg-block-layer-vorgabe` (LAYER-Attribut -> `ssg-ils-block-ebene`) fuer nur EINGEFUEGTE Bloecke. Alle liefern nil, wenn nichts konfiguriert ist, und lassen den Layer dann unangetastet | +| `ssg_core.lsp` | - | Kernel: ssg-start/ssg-end, ssg-cfg-or, ssg-attrib-read/write, Layer/Block-Ops. Zentrale Helfer: `ssg-lisp-verzeichnis`/`ssg-lisp-datei-pfad` (Lisp-Pfad-Ermittlung), `ssg-rot-matrix-zy` (4x4 Rz(hz)*Ry(vert)-Rotationsmatrix, clusteruebergreifend), GUI-Aus-Schalter `*ssg-gui-aus*` + `ssg-gui-aus`/`ssg-gui-an`/`ssg-gui-p` (fuer automatische Tests: schaltet DCL-/Wizard-Dialoge ab, Module fallen auf Konsolen-/getXXX-Pfad zurueck; der Linienzug hat zusaetzlich `*vfl-headless*`, das die Konsolen-Rueckfrage selbst verbietet - siehe VarioFoerderer-Zeile). **Layer-Zuordnung**: `ssg-layer-cfg-load`/`-reset` (liest `cfg/layer.cfg` lazy), `ssg-layer-cfg-eintrag` (reine Aufloesung "Layername, Farbe" -> `(layer . farbe)`, in `tests/test_unit.lsp` getestet), `ssg-layer-eintrag` (Suchreihenfolge `[ils_]` -> `[allgemein]`), `ssg-layer-anlegen` (Layer mit cfg-Farbe anlegen, optional aktivieren) und `ssg-layer-setzen` (Blockreferenz auf den Baugruppen-Layer legen) - fuer ZUSAMMENGEBAUTE Baugruppen; `ssg-block-layer-vorgabe` (LAYER-Attribut -> `ssg-ils-block-ebene`) fuer nur EINGEFUEGTE Bloecke. Alle liefern nil, wenn nichts konfiguriert ist, und lassen den Layer dann unangetastet. **JSON**: drei Parser nebeneinander, bewusst nicht zusammengelegt - `ssg-load-json`/`ssg-parse-json-array` (flaches Array von Objekten) und `ssg-cfg-parse-nested` (genau 1 Ebene) sind ZEILENbasiert und koennen weder echte Verschachtelung noch mehrzeilige Arrays, maskierte Anfuehrungszeichen (`\"`) oder Kommas in Strings lesen; ihre Konsumenten (Konfiguration, Sprache, VF-Spec) sind auf dieses Verhalten eingestellt und bleiben unangetastet. Fuer alles andere `ssg-json-laden`/`ssg-json-parse`/`ssg-json-get` (rekursiver Descent-Parser ueber einen Positionszeiger): Objekt -> Alist, Array -> Liste, true -> T, false/null -> nil (beide nil, "Feld fehlt" ist also nicht von "Feld ist false" unterscheidbar), `\uXXXX` nur fuer ASCII. In `tests/test_unit.lsp` (`tu-tests-json`) gegen die echte Geruest-Datendatei getestet. Dazu `ssg-join` (Liste + Trennzeichen -> String) | | `ssg_ks_insert.lsp` | - | Gemeinsame KS-Extraktion + Block-Einfuegeprimitiven (von VarioFoerderer UND Gefaellestrecke genutzt, frueher in beiden dupliziert): `vec-length`, `ks-line-axis`, `ks-normalize-name`, `ks-relativize`/`-absolutize`, `ensure-block-loaded`, `extract-ks-from-block[-raw]` (Cache `*ks-cache*`), `insert-block-by-ks` (nur Rz), `insert-inclined-scaled-block`/`insert-rotated-block-with-ks` (Rz*Ry). MNL laedt es als Core-Modul VOR Gefaellestrecke/VarioFoerderer; vf_core/Gefaellestrecke haben einen guarded Nachlade-Load fuers isolierte Test-Laden | | `ssg_lang.lsp` | - | Sprachumschaltung Deutsch/Englisch (ssg-text, ssg-textf, *ssg-lang*) | | `ssg_dbg.lsp` | - | Debug-Logging (dbgopen, dbgmsg, dbgflush, dbgclose) | @@ -106,6 +107,7 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`. | `OmniModulInsert.lsp` | OMNI_LOAD, OMNI_APB_*, OMNI_W*_*, OMNI_AP60/AP110, OMNI_TEF_*, OMNI_TV_*, OMNI_APBW_*, OMNI_EDIT | Omniflo-Komponenten: Boegen, Weichen, Verbinder, Transferwagen, Edit-Dialog | | `SSG_LIB_Commands.lsp` | ILS_BTMT_Beladung, ILS_BTMT_Entladung, ILS_SCANNER_INSERT, ILS_SEPARATOR_INSERT, ILS_EINGANG_INSERT, ILS_AUSGANG_INSERT, ILS_ETF_*, ILS_GK*_*, SSG_BLOCKEDIT, SSG_DIM_SWITCH, SSG_SPRACHE | BTMT Be-/Entladung (`ils-insert-station`: wiederholtes Einfuegen von `BTMT-Beladung` bzw. `SC_Entladung`, Drehung interaktiv, Layer aus `LAYER`-Attribut, Dimension via `ssg-ils-block-laden`) + Sensoren (`ils-insert-sensor`) + Eingang-/Ausgang-Symbol (frei platzierbar wie BTMT Be-/Entladung, ueber `ils-insert-station` mit den Rohnamen `Eingang`/`Ausgang`, Bloecke `Eingang_.dwg`/`Ausgang_.dwg` in data/ils) + Dummy-Befehle fuer Menue + Doppelklick/Kontextmenue-Dispatcher + Konfiguration (2D/3D, Sprache) | | `Tro_Edit.lsp` | TRO_INSERT, TRO_EDIT | TRO-Marker (Block `TRO_SYM_`) fuer sps_skel (lib/tro_annotate.py): TRO_INSERT fuegt eine bestehende Blockdefinition an einem gewaehlten Punkt ein und setzt ID/TYPE (bricht ab, wenn der Typ noch keine Blockdefinition in der Zeichnung hat - die entsteht nur ueber tro_annotate.py); TRO_EDIT aendert ID/TYPE eines bestehenden Markers per Dialog. Typliste aus `tro_types.lsp` (von `tro_annotate.py --emit-lisp` nach DXFM_LISP geschrieben). Der Dispatcher SSG_BLOCKEDIT leitet Blocknamen `TRO_*` zu TRO_EDIT. | +| `GeruestInsert.lsp` | GERUEST_ROHRABHAENGER, GERUEST_SAEULE_2ZOLL, GERUEST_SAEULE_1ZOLL, GERUEST_WANDBEFESTIGUNG | Geruest-Baugruppen aus `data/json/geruest.json` konfigurieren und einfuegen. Dialog (`dcl/geruest.dcl`): Basis-Variante aus `geometrie_varianten` (Dropdown), Anbauteile aus `anbaugruppen` (`list_box` mit `multiple_select` - im Projekt der erste), Laenge (gegen `attribute.Laenge` min/max geprueft, Retry-Schleife) und Farbe (`attribute.Farbe`, Freitext bei `frei_option`). Baugruppen ohne `attribute`-Block (Wandbefestigung) bekommen Laenge/Farbe gesperrt. Der Block wird - wie KREISEL_n/VF_n - SELBST zusammengebaut: Dummy-DWG aus `data/geruest` liefert nur Geometrie, das Attribut-Schema kommt aus `*geruest-attrib-defs*` (ARTINR, LAENGE, BEZEICHNUNG, TEILEART, FARBE, LAYER) und wird per `ssg-attrib-merge` mit den Dummy-Tags zusammengefuehrt - sonst verschluckt `ssg-attrib-set-on` stillschweigend jeden Tag, den die Dummy-DWG nicht als ATTDEF kennt. Blockname `GR__` (Kollisionszaehler wie in OmniModulInsert). **Das `GR_`-Praefix ist nicht kosmetisch**: `SSG_BLOCKEDIT` haelt JEDEN Block mit TEILEART-Attribut fuer ein Omniflo-Element, deshalb steht in `SSG_LIB_Commands.lsp` ein `GR_*`-Zweig VOR dem Omniflo-Zweig. ARTINR listet jede Sivas-ID so oft, wie `menge` es vorgibt (benannte Gruppen mit `teile` werden rekursiv aufgeloest); BEZEICHNUNG verkettet mit `" + "`, NICHT mit Komma - die Artikelbezeichnungen enthalten selbst Kommas ("ROHR R 1\" 33,7 x 2,6"). Zuordnung Menue -> JSON laeuft ueber das ASCII-Feld `id`, weil die aeusseren JSON-Schluessel Umlaute bzw. Anfuehrungszeichen tragen. Noch NICHT enthalten: Edit-Pfad (GERUEST_EDIT) und CSV-/Sivas-Export (`csv:collect-export-blocks` braucht dafuer ein `pattern_geruest = GR_*`) | | `menu-inventory.lsp` | MenuInventory | BricsCAD Diagnostik (Menuegruppen, Plugins, Startdateien) | | `DrawPlanquadrate.lsp` | DRAW_PLANQUADRATE, DELETE_PLANQUADRATE, PLANQUADRATE_TOGGLE | Zeichnet auf Z=0 ein Planquadrat-Linienraster (senkrecht/waagerecht), im Abstand/Zaehlschema der aktiven `[Planquadrate]`-Konfiguration aus `cfg/export.cfg` (dieselbe Quelle wie `lib/export_planquadrat.py` fuer den CSV-Export). Bereich kommt NICHT aus den Zeichnungs-Extents, sondern aus derselben Blockmenge wie EXPORTCSV (`csv:collect-export-blocks`, export.lsp; siehe `plq-export-bbox`): Ursprung (Index 1) UND obere Grenze kommen BEIDE aus der tatsaechlichen Bounding-Box jedes Blocks (`csv:get-bbox`, wie `bbox_columns()`/`planquadrat_column()`/`_bbox_min()` in `lib/export_csv.py` bzw. `lib/export_planquadrat.py`), NICHT aus dem reinen Einfuegepunkt - bei einem gedrehten/exzentrisch eingefuegten Block (z.B. ein um 90 Grad gedrehter Kreisel) kann das Bounding-Box-Zentrum weit vom Einfuegepunkt abweichen; Ursprung und Zellzuordnung muessen auf derselben Referenz beruhen, sonst driften sie auseinander (realer Bug, behoben). Reine Hilfsgeometrie (Kreisel-Umfahrungsboegen als eigene Linien, Texte) zaehlt bewusst nicht mit, sonst weicht das Raster vom CSV-Export ab. `x_origin`/`y_origin` ueberschreiben den Ursprung, Fallback auf EXTMIN/EXTMAX falls keine Export-Bloecke vorhanden sind bzw. `csv:get-bbox` fuer einen Block scheitert. Spalten-/Zeilenzahl wird IMMER aufgerundet auf volle Rasterschritte, damit alle senkrechten Linien auf der letzten waagerechten enden und umgekehrt - das Raster bildet immer ein geschlossenes Rechteck. Zaehlung (alphabetisch/numerisch je Achse) als Text am linken/unteren Rand. Das fertige Raster (Linien+Texte) wird per `_.-BLOCK`/`_.INSERT` zu einem einzigen Block `ZZ_PLANQUADRATE` bei (0,0,0) zusammengefasst. Layer aus `cfg/layer.cfg` (`[allgemein]` Key `planquadrate`, Fallback `ZZ_Planquadrate`). DRAW_PLANQUADRATE entfernt vor dem Neuaufbau zuerst einen evtl. vorhandenen Block (`plq-block-entfernen`: INSERTs loeschen + `_-PURGE _B`), DELETE_PLANQUADRATE ruft nur diese Entfernung auf. PLANQUADRATE_TOGGLE ("Planquadrate an/aus", Menue: Konfiguration) prueft per `tblsearch`/`ssget "X"`, ob der Block existiert, und dispatcht auf Zeichnen bzw. Loeschen. Lazy geladen ueber `(ssg-ensure "DrawPlanquadrate")`, nicht in der Core-Ladereihenfolge. Die Planquadrat-SPALTE im CSV-Export (`export_csv.py::planquadrat_column`/`resolve_origins`, `lib/export_planquadrat.py::compute_planquadrat`/`_bbox_min`) rechnet ebenfalls durchgaengig auf Bounding-Box-Basis (nicht nur Einfuegepunkt) und liefert eine SPANNE ("A/1-J/1") statt nur einer Zelle, wenn ein Element mehr als eine Zelle ueberdeckt (Kreisel, lange VF-/GF-Ketten). | @@ -121,6 +123,7 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`. | `sprache.dcl` | SSG_SPRACHE (SSG_LIB_Commands.lsp) | | `tro_insert.dcl` | TRO_INSERT (Tro_Edit.lsp) | | `tro_edit.dcl` | TRO_EDIT (Tro_Edit.lsp) | +| `geruest.dcl` | Alle GERUEST_*-Befehle (GeruestInsert.lsp, `geruest-dialog`) - ein Dialog fuer alle Baugruppen, Listen zur Laufzeit gefuellt | | `vfl_edit.dcl` | Linienzug-Editieren/Fortsetzen (vf_linienzug.lsp, `vfl-dlg-position`, aufgerufen aus `vfl-edit-ent`) | | `vf_linienzug_wizard.dcl` | Linienzug-Assistent (Modus 1, 2 UND 3): zwei generische Einzel-Dialoge (Zahl-Eingabe, Mehrfachauswahl; `vflw-zahl`/`vflw-wahl`, aufgerufen aus `vfl-in-real`/`vfl-in-string`/`vfl-in-int` bei aktivem `*vfl-wizard-mode*`) PLUS sieben Gruppen-Dialoge, die mehrere immer unmittelbar aufeinanderfolgende Fragen in einem Bildschirm zusammenfassen (Kettenstart, AS-Element, GF-Bogen, ES-Element, Vario-Kurve, Gefaelle festlegen, Ziel-Hoehe, Horizontales Stueck - inkl. Punktwahl-Button mit X/Y/Z-Anzeige; `vflw-gruppe-*-impl` in vf_linienzug.lsp fuellen dafuer vorab eine kleine Pending-Queue `*vflw-pending*`, die einzelnen `vfl-in-*`-Aufrufe bleiben dabei unveraendert) PLUS drei Segment-Gruppen-Dialoge fuer die segmentweise Klassifizierung in Modus 2/3 (`vflw_seg_linie_m2` = Typ+GF-Neigung, `vflw_seg_linie_m3` = 4-fach-Typ+kontextabh. Wertfeld, `vflw_seg_bogen` = Typ+Variante; `vflw-seg-*-impl`). Beide Modi heben vor der Frage das aktuelle Pfad-Segment (LINE/ARC) per `vfl-seg-highlight` (`redraw`) in der Zeichnung hervor und zeigen "Segment i/n ..." im Dialogkopf. Details: `doc/variofoerderer.md` | diff --git a/Lisp/GeruestInsert.lsp b/Lisp/GeruestInsert.lsp new file mode 100644 index 0000000..01e6938 --- /dev/null +++ b/Lisp/GeruestInsert.lsp @@ -0,0 +1,482 @@ +;; ============================================================ +;; GeruestInsert.lsp - Geruest-Baugruppen konfigurieren und einfuegen +;; +;; Liest data/json/geruest.json (rekursiv per ssg-json-laden), +;; laesst den Nutzer im Dialog dcl/geruest.dcl eine Basis-Variante, beliebige +;; Anbauteile, Laenge und Farbe waehlen, und fuegt das Ergebnis als Block +;; GR__ ein. +;; +;; Der Block wird - wie KREISEL_n/VF_n - selbst zusammengebaut: die Dummy-DWG +;; aus data/geruest liefert nur die Geometrie, das Attribut-Schema kommt aus +;; *geruest-attrib-defs*. Damit kann kein Attribut still verlorengehen, wenn +;; die Dummy-DWG einen Tag nicht kennt (ssg-attrib-set-on schreibt nur Tags, +;; die als ATTDEF existieren). +;; +;; Das GR_-Praefix ist nicht kosmetisch: SSG_BLOCKEDIT haelt jeden Block mit +;; TEILEART-Attribut fuer ein Omniflo-Element (SSG_LIB_Commands.lsp). Der +;; Doppelklick-Dispatcher erkennt Geruest-Bloecke an diesem Praefix. +;; +;; Befehle: GERUEST_ROHRABHAENGER, GERUEST_SAEULE_2ZOLL, +;; GERUEST_SAEULE_1ZOLL, GERUEST_WANDBEFESTIGUNG +;; ============================================================ + +(vl-load-com) + +;; Attribut-Schema des Wrapper-Blocks. Die Dummy-DWG darf eigene Tags +;; mitbringen - die bleiben erhalten (ssg-attrib-merge). +(if (null *geruest-attrib-defs*) + (setq *geruest-attrib-defs* + '(("ARTINR" "") + ("LAENGE" "") + ("BEZEICHNUNG" "") + ("TEILEART" "") + ("FARBE" "") + ("LAYER" "") + ) + ) +) + +(if (null *geruest-block-counter*) (setq *geruest-block-counter* 0)) + + +;; ============================================================ +;; DATEN +;; ============================================================ + +(defun geruest-data-verzeichnis ( / ) + (cond + ((getenv "DXFM_DATA") (vl-string-translate "\\" "/" (getenv "DXFM_DATA"))) + ((getenv "DXFMAKRO") + (strcat (vl-string-translate "\\" "/" (getenv "DXFMAKRO")) "/data")) + ) +) + +(defun geruest-json-pfad ( / basis) + (if (setq basis (geruest-data-verzeichnis)) + (strcat basis "/json/geruest.json") + ) +) + +(defun geruest-dummy-pfad (datei / basis) + (if (and datei (setq basis (geruest-data-verzeichnis))) + (strcat basis "/geruest/" datei) + ) +) + +;; Datendatei einmal je Zeichnungssitzung laden. +(defun geruest-daten ( / pfad) + (if (null *geruest-daten*) + (progn + (setq pfad (geruest-json-pfad)) + (cond + ((null pfad) (princ (ssg-text "gr-fehler-datenpfad"))) + ((null (findfile pfad)) (princ (ssg-textf "gr-fehler-json-fehlt" (list pfad)))) + (T + (setq *geruest-daten* (ssg-json-laden pfad)) + (if (null *geruest-daten*) + (princ (ssg-textf "gr-fehler-json-lesbar" (list pfad))) + ) + ) + ) + ) + ) + *geruest-daten* +) + +;; Baugruppe ueber ihr ASCII-Feld "id" suchen. Der aeussere JSON-Schluessel +;; ("Rohrabhaenger" mit Umlaut, "2\" Saeulen" mit Anfuehrungszeichen) taugt +;; dafuer nicht - er ist aus ASCII-Quelltext nicht vergleichbar. +(defun geruest-baugruppe (id / erg) + (foreach eintrag (geruest-daten) + ;; equal statt = : fehlt einer Baugruppe das id-Feld, liefert ssg-json-get + ;; nil - = ist mit nil-Argumenten nicht zuverlaessig. + (if (and (null erg) + (listp (cdr eintrag)) + (equal (ssg-json-get (cdr eintrag) "id") id)) + (setq erg (cdr eintrag)) + ) + ) + erg +) + + +;; ============================================================ +;; TEILE AUSWERTEN +;; ============================================================ + +;; Anzeigename eines Eintrags: benannte Gruppe ("name") oder Einzelteil +;; ("artikel"). +(defun geruest-eintrag-name (eintrag / n) + (setq n (ssg-json-get eintrag "name")) + (if (and n (= (type n) 'STR) (/= n "")) + n + (ssg-json-get eintrag "artikel") + ) +) + +(defun geruest-menge (eintrag / m) + (setq m (ssg-json-get eintrag "menge")) + (if (and m (numberp m) (> m 0)) (fix m) 1) +) + +;; Sivas-ID als String. Im JSON stehen rein numerische IDs als Zahl +;; (Projektkonvention), zusammengesetzte Codes waeren Strings. +(defun geruest-id-string (wert) + (cond + ((null wert) nil) + ((= (type wert) 'STR) wert) + ((= (type wert) 'INT) (itoa wert)) + ((= (type wert) 'REAL) (rtos wert 2 0)) + ) +) + +(defun geruest-wiederholen (wert n / erg i) + (setq erg nil i 0) + (if wert + (while (< i n) + (setq erg (cons wert erg)) + (setq i (1+ i)) + ) + ) + erg +) + +;; Alle Artikelnummern eines Eintrags, jede gemaess "menge" wiederholt. +;; Benannte Gruppen ("teile") werden rekursiv aufgeloest. +(defun geruest-artikelnummern (eintrag / erg teile) + (setq erg nil) + (setq teile (ssg-json-get eintrag "teile")) + (if teile + (foreach teil teile + (setq erg (append erg (geruest-artikelnummern teil))) + ) + (setq erg (geruest-wiederholen + (geruest-id-string (ssg-json-get eintrag "sivas_id")) + (geruest-menge eintrag))) + ) + erg +) + + +;; ============================================================ +;; DIALOG +;; ============================================================ + +;; "0 3 5" -> (0 3 5). Rueckgabe von get_tile bei multiple_select. +(defun geruest-indizes (s / erg tok i c) + (setq erg nil tok "" i 1) + (if (null s) (setq s "")) + (while (<= i (strlen s)) + (setq c (substr s i 1)) + (if (= c " ") + (progn + (if (/= tok "") (setq erg (cons (atoi tok) erg))) + (setq tok "") + ) + (setq tok (strcat tok c)) + ) + (setq i (1+ i)) + ) + (if (/= tok "") (setq erg (cons (atoi tok) erg))) + (reverse erg) +) + +;; Dialog zeigen. Rueckgabe: +;; (variante-index (anbau-indizes) laenge-string farbe-string) oder nil +(defun geruest-dialog (bg / dcl-pfad dat varianten anbauteile attr laenge-def + farb-opts frei-opt fertig rueckgabe ergebnis + dlg-variante dlg-anbau dlg-laenge dlg-farbe dlg-frei + min-l max-l wert-l farbe-txt) + (setq dcl-pfad (strcat (getenv "DXFM_DCL") "/geruest.dcl")) + (setq varianten (ssg-json-get bg "geometrie_varianten")) + (setq anbauteile (ssg-json-get bg "anbaugruppen")) + (setq attr (ssg-json-get bg "attribute")) + (setq laenge-def (if attr (ssg-json-get attr "Laenge"))) + (setq farb-opts (if attr (ssg-json-get attr "Farbe"))) + (setq min-l (if laenge-def (ssg-json-get laenge-def "min"))) + (setq max-l (if laenge-def (ssg-json-get laenge-def "max"))) + + ;; Farboptionen + freie Option ans Listenende; Index davon merkt sich der + ;; Callback, um das Freitextfeld freizuschalten. + (setq *geruest-farb-liste* nil *geruest-frei-idx* -1) + (if farb-opts + (progn + (setq *geruest-farb-liste* (ssg-json-get farb-opts "optionen")) + (if (ssg-json-get farb-opts "frei_erlaubt") + (progn + (setq frei-opt (ssg-json-get farb-opts "frei_option")) + (if (null frei-opt) (setq frei-opt "Individuell")) + (setq *geruest-frei-idx* (length *geruest-farb-liste*)) + (setq *geruest-farb-liste* + (append *geruest-farb-liste* (list frei-opt))) + ) + ) + ) + ) + + (setq dlg-variante "0" dlg-anbau "" dlg-laenge "" dlg-farbe "0" dlg-frei "") + (setq fertig nil rueckgabe nil) + + ;; Schleife, damit der Dialog nach einer ungueltigen Laenge mit den + ;; bisherigen Eingaben erneut erscheint (Projektmuster, vgl. vf_linienzug). + (while (not fertig) + (setq dat (load_dialog dcl-pfad)) + (if (or (< dat 0) (not (new_dialog "geruest" dat))) + (progn + (alert (ssg-text "gr-fehler-dcl")) + (setq fertig T) + ) + (progn + (set_tile "kopf" (ssg-textf "gr-dlg-kopf" + (list (ssg-json-get bg "teileart")))) + + (start_list "variante") + (foreach v varianten (add_list (geruest-eintrag-name v))) + (end_list) + (set_tile "variante" dlg-variante) + + (start_list "anbau") + (foreach a anbauteile (add_list (geruest-eintrag-name a))) + (end_list) + (if (/= dlg-anbau "") (set_tile "anbau" dlg-anbau)) + + ;; Laenge/Farbe nur bei Baugruppen mit "attribute"-Block + (if laenge-def + (set_tile "laenge" dlg-laenge) + (mode_tile "laenge" 1) + ) + (if *geruest-farb-liste* + (progn + (start_list "farbe") + (foreach f *geruest-farb-liste* (add_list f)) + (end_list) + (set_tile "farbe" dlg-farbe) + (set_tile "farbe_frei" dlg-frei) + (mode_tile "farbe_frei" + (if (= (atoi dlg-farbe) *geruest-frei-idx*) 0 1)) + (action_tile "farbe" + "(mode_tile \"farbe_frei\" (if (= (atoi $value) *geruest-frei-idx*) 0 1))") + ) + (progn + (mode_tile "farbe" 1) + (mode_tile "farbe_frei" 1) + ) + ) + + (action_tile "accept" + (strcat "(setq dlg-variante (get_tile \"variante\"))" + "(setq dlg-anbau (get_tile \"anbau\"))" + "(setq dlg-laenge (get_tile \"laenge\"))" + "(setq dlg-farbe (get_tile \"farbe\"))" + "(setq dlg-frei (get_tile \"farbe_frei\"))" + "(done_dialog 1)")) + (action_tile "cancel" "(done_dialog 0)") + (setq ergebnis (start_dialog)) + (unload_dialog dat) + + (cond + ((/= ergebnis 1) (setq fertig T)) + ;; Laenge pruefen - Bereichspruefung erfolgt im Projekt immer nach + ;; done_dialog, DCL kann das nicht. + ((and laenge-def + (progn (setq wert-l (atof dlg-laenge)) + (or (= (ssg-cfg-trim dlg-laenge) "") + (and min-l (< wert-l min-l)) + (and max-l (> wert-l max-l))))) + (alert (ssg-textf "gr-alert-laenge" + (list dlg-laenge + (if min-l (rtos min-l 2 0) "0") + (if max-l (rtos max-l 2 0) "-")))) + ) + (T + (setq farbe-txt + (cond + ((null *geruest-farb-liste*) "") + ((= (atoi dlg-farbe) *geruest-frei-idx*) dlg-frei) + (T (nth (atoi dlg-farbe) *geruest-farb-liste*)) + ) + ) + (setq rueckgabe (list (atoi dlg-variante) + (geruest-indizes dlg-anbau) + (if laenge-def dlg-laenge "") + (if farbe-txt farbe-txt ""))) + (setq fertig T) + ) + ) + ) + ) + ) + rueckgabe +) + + +;; ============================================================ +;; BLOCK BAUEN UND EINFUEGEN +;; ============================================================ + +;; Eindeutiger Blockname. ssg-timestamp hat Sekundenaufloesung - bei zwei +;; Aufrufen in derselben Sekunde wird ein Zaehler angehaengt (Muster aus +;; OmniModulInsert.lsp). +(defun geruest-blockname (id / bname) + (setq bname (strcat "GR_" id "_" (ssg-timestamp))) + (while (tblsearch "BLOCK" bname) + (setq *geruest-block-counter* (1+ *geruest-block-counter*)) + (setq bname (strcat "GR_" id "_" (ssg-timestamp) "_" + (itoa *geruest-block-counter*))) + ) + bname +) + +;; Dummy-Geometrie + eigene ATTDEFs zu einem Block zusammenfassen und am +;; gewaehlten Punkt einfuegen. werte = alist der berechneten Attributwerte. +;; Rueckgabe: (blockname . entity) oder nil. +(defun geruest-block-bauen (bg werte pt / datei dummy lastEnt dummyEnt e ss + bname blockEnt dummy-attribs attribs) + (setq datei (ssg-json-get bg "dummy")) + (setq dummy (geruest-dummy-pfad datei)) + (if (or (null dummy) (null (findfile dummy))) + (progn + (princ (ssg-textf "gr-fehler-dummy" (list (if dummy dummy datei)))) + nil + ) + (progn + (setvar "ATTREQ" 0) + (setvar "ATTDIA" 0) + (setvar "OSMODE" 0) + (setq lastEnt (entlast)) + + ;; Dummy bei (0,0,0) einfuegen - seine Attribute liefern u.a. die + ;; LAYER-Vorgabe. + (command "_.INSERT" dummy (list 0.0 0.0 0.0) 1 1 0) + (setq dummyEnt (entlast)) + (setq dummy-attribs (ssg-attrib-read dummyEnt)) + + ;; Unser Schema gewinnt an Struktur, die Dummy-Werte an Inhalt: + ;; ssg-attrib-merge legt alle Tags aus *geruest-attrib-defs* an und + ;; uebernimmt zusaetzlich alle Tags der Dummy-DWG. + (setq attribs (ssg-attrib-merge dummy-attribs *geruest-attrib-defs*)) + (foreach paar werte + (if (assoc (car paar) attribs) + (setq attribs (subst paar (assoc (car paar) attribs) attribs)) + (setq attribs (append attribs (list paar))) + ) + ) + + (ssg-attrib-make-defs (ssg-attrib-alist-to-defs attribs) 50.0) + + ;; Alles Neue seit lastEnt einsammeln (Dummy-INSERT + ATTDEFs) + (setq ss (ssadd)) + (setq e (if lastEnt (entnext lastEnt) (entnext))) + (while e + (ssadd e ss) + (setq e (entnext e)) + ) + + (setq bname (geruest-blockname (ssg-json-get bg "id"))) + (command "_.-BLOCK" bname (list 0.0 0.0 0.0) ss "") + + ;; Stand VOR dem Einfuegen merken: bricht der Nutzer die Drehung mit ESC + ;; ab, entsteht kein neues INSERT und (entlast) zeigt auf ein voellig + ;; fremdes Objekt - ohne diesen Vergleich wuerden dessen Attribute + ;; ueberschrieben. + (setq lastEnt (entlast)) + ;; pause = Drehwinkel interaktiv am Cursor + (command "_.INSERT" bname pt 1 1 pause) + ;; Laufende Einfuegung sicher beenden (z.B. ESC waehrend der Drehung) + (if (> (getvar "CMDACTIVE") 0) (command)) + (if (> (getvar "CMDACTIVE") 0) (command)) + + (setq blockEnt (entlast)) + (if (and blockEnt (not (eq blockEnt lastEnt)) + (= (cdr (assoc 0 (entget blockEnt))) "INSERT")) + (progn + (ssg-attrib-set-on blockEnt attribs) + (ssg-block-layer-vorgabe blockEnt bname attribs) + (cons bname blockEnt) + ) + ) + ) + ) +) + + +;; ============================================================ +;; ABLAUF +;; ============================================================ + +(defun geruest-run (id / bg wahl varianten anbauteile variante gewaehlt + artnr bezeichnung teile-namen werte pt ergebnis) + (setq bg (geruest-baugruppe id)) + (cond + ((null bg) + (if (geruest-daten) (princ (ssg-textf "gr-fehler-baugruppe" (list id)))) + ) + ((null (setq varianten (ssg-json-get bg "geometrie_varianten"))) + (princ (ssg-textf "gr-fehler-keine-varianten" (list id))) + ) + (T + (setq anbauteile (ssg-json-get bg "anbaugruppen")) + (setq wahl (geruest-dialog bg)) + (if (null wahl) + (princ (ssg-text "gr-abgebrochen")) + (progn + (setq variante (nth (car wahl) varianten)) + (setq gewaehlt nil) + (foreach i (cadr wahl) + (if (< i (length anbauteile)) + (setq gewaehlt (append gewaehlt (list (nth i anbauteile)))) + ) + ) + + ;; ARTINR: jede Sivas-ID so oft, wie "menge" es vorgibt + (setq artnr (geruest-artikelnummern variante)) + (foreach a gewaehlt + (setq artnr (append artnr (geruest-artikelnummern a))) + ) + + ;; BEZEICHNUNG: mit " + " verketten - die Artikelbezeichnungen + ;; enthalten selbst Kommas ("ROHR R 1\" 33,7 x 2,6"). + (setq teile-namen (list (ssg-json-get bg "teileart") + (geruest-eintrag-name variante))) + (foreach a gewaehlt + (setq teile-namen (append teile-namen + (list (geruest-eintrag-name a)))) + ) + (setq bezeichnung (ssg-join teile-namen " + ")) + + (setq werte (list (cons "ARTINR" (ssg-join artnr ",")) + (cons "BEZEICHNUNG" bezeichnung) + (cons "TEILEART" (ssg-json-get bg "teileart")))) + (if (/= (caddr wahl) "") + (setq werte (append werte (list (cons "LAENGE" (caddr wahl)))))) + (if (/= (cadddr wahl) "") + (setq werte (append werte (list (cons "FARBE" (cadddr wahl)))))) + + (setq pt (getpoint (ssg-text "gr-prompt-einfuegepunkt"))) + (if (null pt) + (princ (ssg-text "gr-abgebrochen")) + (progn + (ssg-start "Geruest" '(("OSMODE") ("ATTREQ") ("ATTDIA"))) + (setq ergebnis (geruest-block-bauen bg werte pt)) + (ssg-end) + (if ergebnis + (princ (ssg-textf "gr-fertig" + (list (ssg-json-get bg "teileart") (car ergebnis)))) + ) + ) + ) + ) + ) + ) + ) + (princ) +) + + +(defun c:GERUEST_ROHRABHAENGER () (geruest-run "Rohrabhaenger")) +(defun c:GERUEST_SAEULE_2ZOLL () (geruest-run "Saeulen_2Zoll")) +(defun c:GERUEST_SAEULE_1ZOLL () (geruest-run "Saeulen_1Zoll")) +(defun c:GERUEST_WANDBEFESTIGUNG () (geruest-run "Wandbefestigung")) + +(princ "\nGeruestInsert.lsp geladen. Befehle: GERUEST_ROHRABHAENGER, GERUEST_SAEULE_2ZOLL, GERUEST_SAEULE_1ZOLL, GERUEST_WANDBEFESTIGUNG") +(princ) diff --git a/Lisp/SSG_LIB_Commands.lsp b/Lisp/SSG_LIB_Commands.lsp index d8698d5..870d10f 100644 --- a/Lisp/SSG_LIB_Commands.lsp +++ b/Lisp/SSG_LIB_Commands.lsp @@ -400,6 +400,14 @@ ) ) + ;; Geruest-Baugruppen -> nativer Attribut-Editor. + ;; MUSS vor dem Omniflo-Zweig stehen: Geruest-Bloecke tragen ein + ;; TEILEART-Attribut und wuerden dort sonst als Omniflo-Element erkannt. + ((wcmatch bname "GR_*") + (princ (ssg-textf "cmd-attribute-bearbeiten" (list bname))) + (command "_.EATTEDIT") + ) + ;; Omniflo-Bloecke -> OMNI_EDIT ;; Erkennung: TEILEART-Attribut vorhanden, AP110-Blockname oder rein numerisch ((progn diff --git a/Lisp/ssg_core.lsp b/Lisp/ssg_core.lsp index b69d061..8972d77 100644 --- a/Lisp/ssg_core.lsp +++ b/Lisp/ssg_core.lsp @@ -1561,6 +1561,213 @@ ) +;; ------------------------------------------------------------ +;; STRING-LISTE VERKETTEN +;; ------------------------------------------------------------ +;; (ssg-join '("a" "b" "c") ",") -> "a,b,c"; leere Liste -> "". +;; Alle Elemente muessen Strings sein (Zahlen vorher mit itoa/rtos wandeln). +(defun ssg-join (lst sep / s erst) + (setq s "" erst T) + (foreach x lst + (if erst + (setq s x erst nil) + (setq s (strcat s sep x)) + ) + ) + s +) + + +;; ------------------------------------------------------------ +;; REKURSIVER JSON-PARSER +;; ------------------------------------------------------------ +;; Ergaenzung zu ssg-parse-json-array/ssg-cfg-parse-nested, die BEIDE +;; zeilenbasiert und flach arbeiten und daher prinzipiell weder echte +;; Verschachtelung noch mehrzeilige Arrays, maskierte Anfuehrungszeichen +;; (\") oder Kommas innerhalb von Strings lesen koennen. Die beiden alten +;; Parser bleiben unveraendert - ihre Konsumenten (Konfiguration, Sprache, +;; VF-Spec) sind auf ihr Verhalten eingestellt. +;; +;; Abbildung: Objekt -> Alist (("key" . wert) ...), Array -> Liste, +;; String -> STR, Zahl -> INT/REAL, true -> T, false/null -> nil. +;; Da false und null beide nil ergeben, ist "Feld fehlt" nicht von +;; "Feld ist false" unterscheidbar. +;; \uXXXX wird nur fuer ASCII (< 128) aufgeloest, sonst "?" - echte +;; Umlaute stehen in unseren Daten direkt als UTF-8 in der Datei. + +(defun ssg-json-zeichen ( / ) + (if (<= *ssg-json-pos* *ssg-json-len*) + (substr *ssg-json-txt* *ssg-json-pos* 1) + ) +) + +(defun ssg-json-weiter ( / ) + (setq *ssg-json-pos* (1+ *ssg-json-pos*)) +) + +(defun ssg-json-leerraum ( / c) + (while (and (setq c (ssg-json-zeichen)) + (member c '(" " "\t" "\r" "\n"))) + (ssg-json-weiter) + ) +) + +;; String ab dem oeffnenden Anfuehrungszeichen lesen, Escapes aufloesen. +(defun ssg-json-string ( / c erg fertig code) + (ssg-json-weiter) + (setq erg "" fertig nil) + (while (and (not fertig) (setq c (ssg-json-zeichen))) + (cond + ((= c "\"") (setq fertig T) (ssg-json-weiter)) + ((= c "\\") + (ssg-json-weiter) + (setq c (ssg-json-zeichen)) + ;; equal statt = : nach dem Backslash kann das Textende erreicht sein + ;; (c = nil), und = ist mit nil-Argumenten nicht zuverlaessig. + (cond + ((equal c "n") (setq erg (strcat erg "\n")) (ssg-json-weiter)) + ((equal c "t") (setq erg (strcat erg "\t")) (ssg-json-weiter)) + ((equal c "r") (setq erg (strcat erg "\r")) (ssg-json-weiter)) + ;; AutoLISP kennt nur \\ \" \e \n \r \t - Backspace/Seitenvorschub + ;; daher ueber chr statt als Escape-Literal. + ((equal c "b") (setq erg (strcat erg (chr 8))) (ssg-json-weiter)) + ((equal c "f") (setq erg (strcat erg (chr 12))) (ssg-json-weiter)) + ((equal c "u") + (ssg-json-weiter) + (setq code (ssg-json-hex4)) + (setq erg (strcat erg (if (< code 128) (chr code) "?"))) + ) + ;; \" \\ \/ und alles andere: Zeichen unveraendert uebernehmen + (c (setq erg (strcat erg c)) (ssg-json-weiter)) + ) + ) + (T (setq erg (strcat erg c)) (ssg-json-weiter)) + ) + ) + erg +) + +;; Vier Hex-Ziffern lesen und als Zahl liefern (Position rueckt mit). +(defun ssg-json-hex4 ( / i c code ziffer) + (setq code 0 i 0) + (while (< i 4) + (setq c (ssg-json-zeichen)) + (if c + (progn + (setq ziffer + (cond + ((and (>= (ascii c) 48) (<= (ascii c) 57)) (- (ascii c) 48)) + ((and (>= (ascii c) 97) (<= (ascii c) 102)) (+ 10 (- (ascii c) 97))) + ((and (>= (ascii c) 65) (<= (ascii c) 70)) (+ 10 (- (ascii c) 65))) + (T 0) + ) + ) + (setq code (+ (* code 16) ziffer)) + (ssg-json-weiter) + ) + (setq i 4) + ) + (setq i (1+ i)) + ) + code +) + +;; Zahl bzw. true/false/null bis zum naechsten Trennzeichen lesen. +(defun ssg-json-literal ( / start s c) + (setq start *ssg-json-pos*) + (while (and (setq c (ssg-json-zeichen)) + (not (member c '("," "}" "]" " " "\t" "\r" "\n")))) + (ssg-json-weiter) + ) + (setq s (substr *ssg-json-txt* start (- *ssg-json-pos* start))) + (cond + ((= s "true") T) + ((= s "false") nil) + ((= s "null") nil) + ((vl-string-search "." s) (atof s)) + (T (atoi s)) + ) +) + +(defun ssg-json-objekt ( / erg key c vorher) + (ssg-json-weiter) + (setq erg nil) + (ssg-json-leerraum) + (while (and (setq c (ssg-json-zeichen)) (/= c "}")) + (setq vorher *ssg-json-pos*) + (if (= c "\"") + (progn + (setq key (ssg-json-string)) + (ssg-json-leerraum) + (if (equal (ssg-json-zeichen) ":") (ssg-json-weiter)) + (setq erg (cons (cons key (ssg-json-wert)) erg)) + ) + ;; Komma oder unerwartetes Zeichen ueberspringen + (ssg-json-weiter) + ) + (ssg-json-leerraum) + ;; Schutz gegen Endlosschleife bei fehlerhafter Datei: kommt die Position + ;; nicht voran, wird zwangsweise weitergerueckt. Ein haengendes BricsCAD + ;; waere schlimmer als ein unvollstaendig gelesenes Objekt. + (if (= vorher *ssg-json-pos*) (ssg-json-weiter)) + ) + (if (equal (ssg-json-zeichen) "}") (ssg-json-weiter)) + (reverse erg) +) + +(defun ssg-json-array ( / erg c vorher) + (ssg-json-weiter) + (setq erg nil) + (ssg-json-leerraum) + (while (and (setq c (ssg-json-zeichen)) (/= c "]")) + (setq vorher *ssg-json-pos*) + (if (= c ",") + (ssg-json-weiter) + (setq erg (cons (ssg-json-wert) erg)) + ) + (ssg-json-leerraum) + (if (= vorher *ssg-json-pos*) (ssg-json-weiter)) + ) + (if (equal (ssg-json-zeichen) "]") (ssg-json-weiter)) + (reverse erg) +) + +(defun ssg-json-wert ( / c) + (ssg-json-leerraum) + (setq c (ssg-json-zeichen)) + (cond + ((null c) nil) + ((= c "{") (ssg-json-objekt)) + ((= c "[") (ssg-json-array)) + ((= c "\"") (ssg-json-string)) + (T (ssg-json-literal)) + ) +) + +;; JSON-Text parsen. Rueckgabe: Struktur (siehe Abbildung oben). +(defun ssg-json-parse (text / erg) + (setq *ssg-json-txt* text + *ssg-json-len* (strlen text) + *ssg-json-pos* 1) + (setq erg (ssg-json-wert)) + ;; Quelltext nicht global stehen lassen - eine Datendatei haelt sonst + ;; dauerhaft mehrere KB in einer globalen Variablen fest. + (setq *ssg-json-txt* nil *ssg-json-len* 0 *ssg-json-pos* 1) + erg +) + +;; JSON-Datei parsen. Rueckgabe: Struktur oder nil, wenn die Datei fehlt. +(defun ssg-json-laden (datei / zeilen) + (setq zeilen (ssg-read-file-lines datei)) + (if zeilen (ssg-json-parse (ssg-join zeilen "\n"))) +) + +;; Feld eines geparsten JSON-Objekts lesen. +(defun ssg-json-get (obj key) + (cdr (assoc key obj)) +) + + ;; Verschachteltes JSON-Objekt parsen (1 Ebene tief) ;; {"section": {"key": val, ...}, ...} ;; -> (("section" ("key" . val) ...) ...) diff --git a/data/geruest/R2-Saeule_dummy.dwg b/data/geruest/R2-Saeule_dummy.dwg new file mode 100644 index 0000000000000000000000000000000000000000..a0b9d74dc92fcd3e506144e7291bc99e6fa0625d GIT binary patch literal 43607 zcmeFZc~}$6w4b47&jUk~#l?w^RZ{6M@=woPjm9~2DU$bZH!-|%>u)7^8a)AoLm zrV^`XncT7Ro|4l3IWn?sOv-F|tO1xbQW5+Lg^MUm`zH_yOHCh*pS@Av{C^(|!7R`h zq<~~F1H~qz*#E%!mnEYs1h;&2jDFCzh2#c6?2JvIOG`F z86kX5M7U%ms#IT8!_t3#PM3+%$1o#&>?{D-Xw-)c$nz~_kL z6E?n@p7d*Net11=*U?p1$vH{md6hnC_Xkd|wC&1_D##`)MA0{YI_4f}x<6pv(F3oZ zNobHQO6mWpEt&*49W~@*&m=MhP@sEo@voELJ6^r)+Jpg2qwf84 zDoSmf3m4d-W(6MKn9!?p+nA~JlYL^8Y&-kMt^U-^aW;r#+U5lhHv*2H>a7>i3`F(P z;BVJ|t367(^ESu;5!B1Zvh{7x-Sl4e-GuoMfXjXTx2Zv^EK74p!dUV30zZP3h+`4sZ(YdF`P6Lc`_N}}mS~+$0>;s!OpGg!e zn(j0-c&uZ3czHz$%}sC5PELMyVU?$YgF|R&=(mtNH&grv*_XV3KXYP|UO;nt#>B>s z8$TYs?M>KUY>dCAwBO`p9}k z^4zMU&1wCoG>siQYPeU6>f6($OP9(-n|GrT&|%ig+WKgG-};!*qi<}fncq;+uzmYV zm&k1?{&(BjIx+@S&wG1#)22=N`B>>W!Q|r?pLuwA+}$%7`r8dbl|OoSaNN$pbKYhh zju)+Tw_0}m``e>vApLyZbfwb<6$|O>WM^;x`puhF*n>B-Ylh5={pERb&$ev{^-kIW zolb}7=AkZ1Ie4$sexUghNkc;eO3jQ8)T=_aa>^7_`vJT~yYHm^^~dpdCwt}#&94?t zet+Jz#;qQ8(KF*Pd8v-Vl zUVqtsC1UL*-GKuKs;eCbR=;@tx^>75y@I+*D^ILCv9QmrXq%Sx{VR7OwAC+6Pd{#K?KkAh>(jSy$3b`w>CBI~goGd;ALpjf zUcGuf&RFZ$3a~Gsb33osmCl+!x&~iyW#OB<`-j!wb`B0v^&^!{cb;Xw97DWm*tSF# zbvQG&?3t&h=d_yKo?k!r-dFf8K9iJ}5tRF-v9)`LYvrYq<;(XUJa{qo?Af!+mMzP4 z>&QHegtTkI(baQS*do>6y*XNEY+~Y3|3OU~lZ$S>LgV_;$ok9G zLn@DYp=k>WR}QpV7Up#BlY6a9)P#DszTRc?)vZ^)ZmnYX%<^tAEIi{_IwdpnTB5jW zz|1Evu5RXVIIn&paKL~8ZchGaF6eskVr^B`zPLUng-@^55A%KDS&ycNM{}P$oP6Z% zH2R{#cTZgFq&@xt0j0~Azki5SqjVPeBuD_ZPFRJMR=+ zd5-EZH~!h${q@i1_1ul}@V?S(JtlMB-Y@@j{pCkJ()*X!w|stmN3i{+ffbrz9pC#? zzUx{muY7%6=ydvVcW0*}(D9c4^qDh#<}?fu`)->5_Zf}ULY2yx z44pUc`{~oCpFG*rvn4yTex&=)B4@|aSBrAQHmwr|+AZ%Y{lw1SdnfH-s(u!?K?*7p zxw^VmR#x7+clNLEKM#MV{VFeQ-@e^fHl^v#`}gnD`d7}G#X?i#$r~jNK>^ypM+khlV!!4IMiFL+5!3zkK+XVUdxYqmH8K zS=qF6*RBsz#PH&WG*ZH@%|DPlnwtwoUU->x;@7uVk=rg`zKodo?l({%*sD8zIz(&} znDwIV%a=pB<2zS-jX#k7sBdM_-o1Nk0%9(_`@Fr}?4cc+r~>XU>)C(etFHG?kJ_6z zZz6TLd-v|o@1HOJyjwN=`OTf<4M_5BPu{P29zm8y=MO(Z19ifYtZm1R{jd(=iH^)o zz;GImF1r3QaL(JR_{SDTi%~_6HnvCyw|z4B+_MCkRhZ`bU{=nTDJ>gbeEyn?=KRBt z-rt+uUAz0_qg74os~${GOKW`~ez$G@`%s|oztphKQ;TB1Pn`sZemr{Qh{qgV`E=J^ z6EFREm5_R=zUuHn;}xZgwq^JItoZDNmWjIt*tfPGv2EMpkd1RL*R1)pVfgd;l~KFc zI9jZo$M2m;qRDkf?Dmlt-ppRJYE|~Ug?(*p9_9yQtUIywqS92g8gk-x%`)W&Iv9f9G;iw?J{j;T^Vd;Yf$ZELm zK%}hi9}sZvg((;OIMq9`f911N`3!@uSFcu$T)uqyyW^j$`(zE;aQ*uA5B*NYZoe^U zk8owg;hxg9>(}p%>+>yOfOgWJoWRTa;%PLgXd3%<{i8phU%9vfSq8Tz&hxS~N^ZI% z6J3A!ux||`Sl6t3PpKzEUY&KvrIg9XIaVgE^1f>mTMkZVp1C!tBZ} zDU%HnPV@|W`#ksYX=dRWGo!_uuBHqdwoE&M&u`_NB`XY zF)_k}tkID-Zr$p=T#L}_+YdgPfA7sF)cd26wQ#QKPTKV8JN-4O`|XxX=PC#GY4i8` zd~ntdWN-eM!4X$p3JVK6I_F(qPtWmjap9tsj~4VCKlgO(*s&uby}Z498iUHX7gvo;xYF9E=c$wLiIYD*TttS*3h-ir&pKJ; zHng69-TOtW)rJcn8UqHb3pTG9*_00>-D|+|(b-RDmd}9JP{)zf< z~>+tn8H9Z}ROg}z*Ffsjebo9d`%6^4sd;$WtOdaRDZr{B( zS5so#pJg6K!xJsH!#50C^}XLpG!Be>7}K`USGN7(#Aly8>f0|>?s@w*YtWEEgTCo1 z(@lQ7jNbcUnD_Atndo5J9W-c?BLW>y&U!vvwDQjWX={Gvl}))^b!X4yb3c9@`?2G_}ME!#GR7IwBa_IZ4kKw8@H*I$1)w4Tkv$L|=-^||q z;OLrRG?H*y;(E1X&y1BwRjc zeSXr)j+#LO@c-9+?0>#H{eRlI{^xlV+U*j^N%Y_OVS#^kp#NJR#}_@=+xs_azx7LO zL)?Lgu;G$jA&Cn+?ymUadH&X|Nn87ACLMj(uFw1TPlm(*P}JYPmuY$*j6RNnrRS`V z3b&ezNY1E-fQD;E#g%clcimDp9$V-;{Z%wy7WuQQ?6(h{i1RP5b@LLa|vWHXG&XJqDA& zT=Y&t{P93E;#BXs;vqhh5tg03Y7Bez#DcKN-cd&^P^e zsdSL}JNIwOdTc!pdaS>_V}IBGAD8a?zcvnR1A)kMm5Hb;KsKV1^?yq>6HNb~YMz0h z>A(3L0{9>VmC@Ha3xpybBq)W4V(ADY1L4g;?H~Sc@OdczzbXekrAdevy`&W&;kQgP zf6GlT0e#(n_mmzl6Y(@2VgIX#_4rc}1wC%kZ`mgxSYN~6rS*AJe$)TE z7ye`VX{e3*9?_422-I#pKR$>fKlH~3-eBl|AmjheByO+)a1BHLNr@tO848{#t`Da3 zOsrk!D|;|h8S<`y;NM&-z@XowJ_of~uM_&5q~ATKZ$V#_nuW$RAEkyO%y=*cwP`qN zRWyo?La<)DW+0qdDE@DsSZwyBmQSWAiHU*<3R93Ivk=8uNLO-D{9hFR9$QmTo%+$k z1Jd99s@H?~e=~;W{yX^ZF~tM1zx9)c=q00ml7KK&O8TGhl+$m(q_IJ$V!hw&H&0oJ zlmF8^HBttk5cLcDpMKGQ_5JiMwB z6zY#@xKRe6PlO_P+Z@C@3Z5vg4_hvd(Vp|4ddBnu2Y_&}iZBLLsfq{1asxPl5Vpu+2@@MbE!lZqgy2q6_Ap&}GiL;)31 zM@2MK5uH>dK~0oU*#%T~9hKcoWp`4u32L^Gnk}JbE2!B8)a*KHb~81*lbSWSx|3mXe)P143#blp08+ z1`4S`fz+T*YS1h-=!9cC;Wz@$6~eg^I9CDZ7QjD*@DB<6qZ$6u3Fi@To)FF}fb;6$ zyiS-)z+55BmB8FOn5Tew^wzNcU@?OOs4fYM;0vipA(beh5*1Xc5Yo+%?u7XS%ooCZ z3Cvf(`~sL?2lJa@ekWWAW)BGl4;UsOI91X@@SA<=e443=om2urB?zem3DqGCrV1nm z%@Tu7iA*ASA`CN>I3V#GHp9G5I3IPhh_B+;26U5vV+w*Bioi!{91eQX!CfdSGXxG? z5aNM4LP$;9Wp!T@V0$+t2(V-rpZCt{gz>F~h<6^m(Jr<4yVlfzn znFs%4FeD(eLxvWb7_`vDfUzhZhL)aS5QV;>Z4_6-rObe}#sVFJno}0E8D**I1*{Nc zMwwZ12{FMXC}uYk5E`PJNioHQT!OJEqKxPuG7#j|=!A<=?TTqma*TV(i4}{Mwoza- zszG*v7bL|B*@v&3X* zN>C)@W_ui?(gr0^VPIs7BQOjrqii&0z=rNc+feqj9c4q=(7k~zWd>w8Q{w>asNS01 zX(Z-IaX;1yq5|831sM{G>}DnsTqchxZcAVpBjJcq2*)fNoelNNTnyqf0?A}dNEjG% zo4+rZX{j+oLO~oSu{0K_GIPp|>ZNI%47@mtUJ%Z8&dvNzflwe37Pu9-6J8Rp0XnNPz>Z;u5Zam)L(p3)R!hY#Fs=(6`7{kwV#ZWSwfQGA z9)@~`lpBc2GBShgAjG7c>@XNZ@Nf`Da%DJ(;Q}6!$R!dfz_a7=MuTu6#-55+>Pf&0 zS&!++7VtvxWMnUF?=WH+mf!TqFTa^oesf^?&9UV-Q|Y}+62Kb>`&8`xJQMjS0H z(!nJpyy6CnQUM)O!ZzOp@J*aaD^7G(7}}?Tq%MprEsMZu2jL5YPeDhSL$E|2`faf zLJBLCutEhZ)UZMeD|B!b30H~WDk)s0gsW6=l^U+n!c{uBnuM!GaJ3Y!R>IXPxLOTY zYvF1gTtmV&BDh8h*C^o{6(p?a7OvC5^(0&`g6pMly%Mfh!S!mmUJKXjU?mAFMX*u|E0wTP1uNCCQVT0} zu!@9LB3LDbRZ3W;f>ml*rG-^GxPgQlL~w%?ZcxGvD!4%nH)!Do9jqo{wFp*AVYL!g zt6;SnR%>Ck4%U#cMg(i5uto`MRIo-3YqYRN2Wv@KD}uFBSgVA!Dp;$AwOUxKgBwY> zQ3N+i;YKCgsDc~SaHAG()WJ<8+$4gVq;QiGZc@QbYPd-YH|gMJ5^fg3%~H5o2{)_Y zW;NWbg`0J-j)Zk0SSN*bN?50Yb!u3rg>^buPr`Z;te3)iC9GG$dNr)q!g?K4lTa;! zYAIAJp;`siYN*yiwGM6};T93xB86L&aEl6VQNt}-xJ3uIl5ndCZk58VO1M=8x2oY* zE!?Vu+eo-g1h+}yHYMDqg4@(^n-*@cZlE)DcqriJ5+Fo8t%}-9Xhy^ggZrWrxfl~!ksF(Qw?`& z;Z7agMZ#SoxJwFmDd8>^+@*%Qv~ZUW?k3@G5!@|>yOnUa3hq|J-CDR?2OCM)D1wbr z*r)=8gvUkjxD*~&!s9A2LBbOvctQ$KDB%eeJfVgswD5!uo+RN(5j-h{ zCzbG|3Z7KMlUjIE2Q?(rh@eIaHA<*aL5&(}v{0jir$~581W!rfDJ49mf~VB*lop=S z!P6u>ErO?|@U#-1R>9M1cv=fj>);s@o)N(_Qg}uQ%TH9Vt*XLPWcgv}z@EQQTV z*sOxhYS^rW%{q9Ngl9$YtQ4MA!m}!PRt?W;;aMHjl29vxS}D{jp;iU8YN*vhtqz_e z;W-gJCxz#f@SF;sQ^RvwcuogfNZ2BREmGK`ge@xAqJ}M6*rJ2yNqAla&r9KXB|NW! z=hg7M7M|C^3naWCf)}Llf)ZX(!3%15K?^VF;6)N%6v2y9cu@&2s^CR6yr_j2b+DC$ zts>Yeg{?~1s)DU**s6tBJpk|BWUw3=38kkz%2WcGz&K2Z>^Bx@Ms|V*bOPZ7!R>Us z6}0jz1i%e1q*HhvBz&fV9a`9-gO5r0SOgzS;bSFytb&i#@Ua#?*1;zvd?JERr0`h- z*cg}qe)1D}SaTP>NRZ-cm{g36C^0guim=>mww+0ioxygkG9n&hHDkG;1KA}l_~s%9 zh88|i!zU69;5zt}gil5ADI)j+K8XaIyreU2A7t2?AgjiU0A-jlxQ#4O0^FAY{vOeN zQGo;K^-&4`RKq{D@J}85NWzaI_)!W!7Qv4Vz~osZsK<>d*i}}*=ZOrFrRR@~wlZYt zHTrz<0A@lG-q6AuI(UP`9ge)km1_XNiRiw)+s6oZsdHgusnlNJz-l5ku|4Xk(4UqZAA< z0N@OuWPn6QWu%dFms=PA0@%I52aGV*Byf|h+w^zldJ(D! zA!{OYAF}c-k*sbezK_i^_0MG}5|ySc!1pguoP*#hhGJ5(X{+B~U<>+A@;}B{AWN)g_q^Bl1^ zPDZ)?jd%``NE8qK4P+$7^!|`o!T1M=Lpc}B=W@zWsfW|A9I-e(-C=BAv)?ZcZ+DR?YCzh|6jT4z7UkCw)XGSkg`o8ovnvtw znvs@`plXXhJ?5IOKwx4j0^gbqIr=*@YvS19_x-vEtBt=w#Ttc^{}W5mvENX}jQBkN zCuG9&xuz>o$&49srU+b;`A_C=Xj96+g@oUSLy1d^2;d%2Yy<&uxmdj5m1Q%~2#&dl=D3#G4$rXk|H7Oe9Tq@g=tdVt0e*=%g>*aPHY6)N z$I?HXOwJlOU>Fw9*y{e-{ost43=4mnOpY5dK#WavZ3glo_&zKibJMy@a_7!??Jr~| z4Ry${BIH;cnocyH!$r5TBGZBQiSwrIUf;pi9%^d8*Z##s@(=ecC(;rz#zh;w+>zt) ztyiSYlcX7D{%OQialZ_+`@!)zej8{QDHqIVm*)xI4D=_81xroAKEAQrNN6@*s=L*C_IIDT&I+krBoD0ZK7ivO!vlL4Om(`E~4 zSE{dRh_%KXSZgeS0}{NQ#sTm>j+)7m+$XXNue!tgTol?l7ITKa zX7IT@lzVJzfs-C!@ljrl(1>4bq6dKxAw2f}}+{`bW8ppqBSF+Ea$4s&qJ5Y=X?0d)A z;NfT(+k}Cw?oIr(Z;?sXANhLd`?UX{pg>D zN>3diCI~cokdpbQ74ci<8CW7l`yfWe`1Op%AsHFA{>{uJ+a)~nGOVu?3AP%!@%xrH z&5Zoy--ZczEXwJR6j()v?ykvZ-`z6$5@l2`&*C&M7u zjmSd2Aas&5K#Ac)@;urHePrlj8M3s|0UsGgH%A8o2M$$=H59F1HwwHAqL(F02rquX z%tU0F#0R<9Y&OgV%^2hF1FGZUxmYS_=vm0^D$3(@3zTGnOc-wo_!Bp=|O32AGl*XXfBV%A=*E1d#0f5uA-OqS5WTF zp-lYt2X09nqQmegVtcI>My%*WW$u6A-q8N0v$y34y6*D;rFDGZzCMi78j7sPi6d4B z@1V5KKe zJODVEP+<*q$lYozIJA{)!|ITSt{poj1MF7JXcTg%>5qpMfR~0D%|L>T!D<(71?|0^ z;!TLjsi+(W`KA}LF=h-pHi$sZ*gz=@!|IG0)J&Z+lP@`_pH=T>nwPBa_!ej(j)jPf8KuDS|gCz0AtFmog%r& zhe#vuA%z2yGAJWE(9mo{15_N(h{DB_c6O5;QG`ZDuscXOlk#ABEL=$MG3QSV z&ex3bHtWFTqhEW^8)oL=HqLNieoTmw0}o9n6cW%OYzOu-sy#jS@O+Pv>>|^+M9i6m zbqXFAb@Dy;pb>K_tuuvf*l%bJ2r{9HOuH8O$-zWvNNVhwDGG5KAvCl+FP=e&)Z>j) zqdO{wm10Iqw(+l9R#kqXiAoHwxgtu*oVB~rZ2NJ=JqlA0|;f5ZNemOOw zXze(Evkw00W1#)E?;jC1<*9~CnhQovSk)?C57PIg8ZNasZ+Kf=2^v>CH$KClcd_`}sAAClSW2Bf;w{-igHQUV{gVClj{D7@ru=59gxEx^DtfS&WA? zEFSPY{Q(}sK>a^0&f=nad`rpq209H}iTWiqU)d?%$C#Uc7`*`&yaFKqGC+UG^=_Wo zh69}E#sMbuC4);QhS)F@s^oiDjt8R}m7N@rl$g_2RQen}!4u zK4s{)2f^*Y93Tff-3y?(iZbul%`5;I4w&6lfM*&7*jXXWh3KK*B-Bot@xvoKw2Ig3 z2}xb0-0)t&JhLM_jpt?z7J1N|HM7_JNafj=;fBHGh@_>)9Pwus?!j|Y7)AuPty!}y z+@Al{GEA_w7yqLri-k3!olh~g1J4zlwp=tMgT?>Eb&#|8@zsKyUNj5Xj?UfzMsum~ zhUND5z9TAD`(v_qUq_p7dxo3eeEy*4Y(MZaS{r=X?K<8de9?!6x!^Ql$d$?|1%g~N zZwr8R%uge5^d37viG%VSw352)>r>2FkKvLmoQ+FJwYrYO48A~fDH}miD_}d&wv;37 z=4`|=auEqrjlh;BOVFFEku=a;iA!rMNz@v#B58cTSWX%yq|#_a!#T2zNukM{i#+dQ13>ck_{k{_ zCN?Mola0;Nt>nfAid32#D@MdMkw!e`WL|it=Qxr+87^`kbft~p$6qD61VzZv2}R;) z%ru>SWN5?!b{B2t*5{hc0AMsBC)$_*oxF4tiA)~RO|(gIF(F*mE@n~??;t3CN*j)j zXv8LyMSFVtC@nM|6M~2WHh~UkxXeT*-}$OoV$7d@9YhPb%(4YR19~}RaWKSHK}-}n zK;WU(h>`*xYl{1%)%{{wrP%OZ5?W$`m=Y5-!ISY&b&W*EL|3LAOF_|5zlX-Dy|ainaxI^yq;=9#UZ`iLp!shG5OBGeC`* zQ6)dRmWq7vEK`6p%ItzXaB8uqO`3t1eesCCluNRMPZo;-2ukJK zJm@t}qG6as^TO@OA>BB@3fyVr?x0=KJq{l2j^3jq7KuxK#%u))OG|K(x{~SpD^<*6 z2rF&>h(0fH6jrbF=P}|F2k;mr#u%PI`Aqj@+CRvjBo-t4lNQl&{S5oiBEYA;X&ZX< zSpSiXpcQ5P$|{&+equbY{>&H%(0QVQEaT#=Nvy<;ZRp|@Izp@?ktc$%<$uuu!xn5Ga=A2O zTk(Z93vEIiH#v@6hA!n8(P5p5hM2PqYYSx^!pbBV-h!3kZnKQUrH)C~(pqcg9!(G! zZA42cYYd%NItbVD`Q<@veER{wH3}`9`dcSyAox14p&Y5+8VAY&7$g2toQs?b1kJ8> zZoK!^}(h0c*oJQp$v(zqU07@Z(+>r=Sx(1-c@mzxEP*RTF1! zY){)z4wNl0BQnTt13BtBU}1n(I%Wo0W zInhXeSL^S4XBmjiY-G=+0qQEhs2iiW1w6sI(HSHy<1^*x0M9{=)rOj)c~r(D4q|MejUBL& z_u?Sguz&;F&!Zz*6tkT`XrNfbP2OsSkrd(VrqS~K!?7xi?I+0+vq&;TrZGc!=Adbk zXMvl%7`O`Sgw4WE7s5s8B5_f;6u8v6G`n<)2$4`E5h+9kqB>EtsMD2j6}n1X6|Mzd z%>i`*n%F;s<=e0kLXsM>jpcDD#1u0KL41WEf$Oi5O#{K@X;Q)liLf`Y&e>+_R_E62 z)^XOW54MLeqDKnJaOxUh7!qhBga@O$W>gtvOml@?hL{LuwvAx+8^PoxF*8OmJ4P@E zQcP3Y7;uF=2A42offfu80e-&`Rq#|T>$l8$gAypyOA+;f!Lh3@)AzMQlLUx4g3TX`48!8VS85$EB8yXinE;K%L zVrXLMq)9)r+PdMid_4tfbJ zmB7GE(~CmZ425<;8g7LcEnpq!j32FVGJxxsvmqXjC1Q(d$U3xi8TG{yV+rFX@rxsv zqtKWP1tI2f))=xkdPSjI2)0aaMH>^XolNsX1STf91b?j$(_MevVE{Hj@TDvsH@}Y; zB>~$PAzdM_Lq3FjlrU-~j7<{8L&===FXxy5*HCn!gDkUTVm#D55G&^JpIFnUTr;hh z!>14>f7Xfm+GtiUCORt!z{dgPzh4O2)On__ww5;opH6?KC2MAJ3${B!D zYh?JLi00aHfD1;+o#WWX;jS0>U?;E#o){=jN{SH}9(FF{UX=$~0dKBIz5o}p(#uQk0TiKArE=-~+*^3;|JalEz7~}#Y^wS%#ZV*6@ z2>?VgT+k;H`#PTLnFJ=I8!Q72LAQ&)e-(Df2wioG0dXKEk>Ka~VG2A1?LUhx`?i9< zfk0;D!vaWw(6v>;CtDzRJtG$<{<#5J%#QCrq2KzbepNC8=b=iNGPFyi#?9{^lwRTt z0Dt#rCHmRhMBp@O2m7^I|9H@9oyPVy2i-x{?q;sXCdEepW)UjqfWqx+hmUJYFwNa( z6N;Z}jD9%DES57hRfgf{1_TSZ+#VfN2Z-(Q@Gu#h@T35@i=YLME_14oSymxj=mtDM9~t`LlM9fSR9i|cEiLJanDH@Tmc0vY^9t_+xR4bjkf6jDU@JV*C^2~8W;)@jQ@wX){OTWqL}NxJsB(qKeid*@F4R(@H@H_HT#Q>$ABM`6#I zH`d;Ae@pro$D5Gpm0kI8fgj>KgH2X=4g}r=Rmw@>GoyBt7>#=saBQ+6BmY+JT*Cnl za&h8*vr(@L6+eWmP=l{Q&JG{lPSTEoB0E8tWnv1tQ3desl6yvpezVCvMX!Ba7com* zY&OmGGwJY?Cn2PXNP6_9_{sAnO^`faw23Bt4?4`px+um)T!Xl$-Fwp!?Sd^XKa z^s5-kVV*o-aP`ZG27~lB`5)(7PE%P`@1xnReVeU{#RXPm`$`ue!!)I)X?+1FX|mbv zNzo)5KeezuEPAig%5M_EiN%5w`vqs(M(=Y-HsYrAN{qof1AZ8FRj{yTYPV;exw*of znUn*kFBaP#7572MQH?Jv63`N)DYZ-rS|Dr=kq=7%EU`EaV*^YAfDHi(s(>wRCj*S$ z$b_{)SCN1<&1>NqQAV^O&roATv3yy08D5Kr;~97vBZI?ww=w+P9*Jbcv-(^cg}`e$ zX8gm3Q|?LgX(8=Q3z0p^1@7oH2-!YJNv&uRirb;M4Wir|9fmP%fEC~Z7upSofHjJV zP(hD0%-A`FV%4%yFyyfk4S@_f=xYr%tPa*VF7Af6p%9MS;8d9VRvEws5Zw-IJcd0D z&+<^jde!45ogNH~w+$F}gxfFytCrP)$ z0Q54*&$Z!qSP-SAF%oe1corMuI)~devp4i17vlM43bx_STp!Q}sVp}s+(W|l&fpcW z`6)K~E6xN@Avklx49wEe;(}3(LR9mSgqh;%tWm*Yo?Og$!Y`s14g5oBws9z6Fw86^ zK&D*GevE)v_I4^$Z0^i6KpD+pW}a$tv80Fg|JXsGII5sF4ae% ze37~J#kyzspm&mUw%gr6Y4{AUuZ?TO7+@nF zkD;Z8!R1<#WfsDeKLFAAQyz)8UA-{!!Ljg%wDALz(GJdmwTY(yNn#<)ktBsK^iv=U z3ki%L!5v{H|LZ9oP&9$D3`sp0pBYe_Gr=!|!J_mF{ZpeN8n@02uz8#qzzLZf_%d>b zx)Z0C{M!8F52hv{mTmbgGr-n4F+j<#Qx>xKGLe#HFpQ&JH@L?X->1XPg4%OqsrLbj z{D7oM0sB59>IE|x#U6Zc{4^af@EIvi48VPh=Oq_>C>? z;N($tp0(ji++o-0)3Yb2!s{7N5#q252ytU#z!dh0mgd>)xi9krz`!^o&eb!VBx1}R z8`}DF)|E}~hRh5AOX`*R0n;W0gy(0^*ofM@Je3b9cVTw#jaf4o*~rJi;4Z(*Cun(E!eAHOK$^=Z_qaJqZI~r<(HM(Bi-3C6X{Me zTWQ`q;7w+DzXPDpuGwq{=T5dsZ+`~-+!L5-5InCfj=+y#^Q|TYEoLVv*i-nQ-oNnL z4;HG}!wwg)g~i+o{uK7l+&`9EHK^DRMisC#9`xG})~ndN>1?*6&wc#3)lwB(T3Nup z;??v1#bVw@d^=B5&> z4o04p^WX>gise>67oT>{^_nv9U8dWV@jv1h7E4!WI@D#4cWxffc%R9#{x&ye%3pa# z!;77>yrx)neqdEt7D%e!6;yWn!Ua8(dEVD7>aRHY+t#UYIUq6t5QRpBXH zEMg+3WmyO*2a|bMgM4!EIt8~^r1|r^el4j+ei?%ke6kB0t1H#Pqh4ppYGW5A6 z%;gr3Fn$;-Io-Z%a+~+~VLsyyUKdM9%-IraQ4BF}2xvsD30~c=X;tn%WGjT*={9WZ?(~6P z!?FU3sPIMM6pp*(lD zf+XV~zhX1DoOoL1^!><(PG5@+?vlD+82aHrqXfjn$mIOfr@_9kL!nrLfx-ou=OoIm43;d+!cFgDLfo{celLv#UhdwG)a0L&?mOpBda{CKDB&R>E z3H+4rCJW@>&2Pu}JAdBh%g9Aw`^2q7K&iGyRwPB_P! z-!(bD-~0QyACLR@*YEz7EAMOHU$57-yq;I5-~$WS0WG|C)EPFBzx1E`k+t{1qi~=d z&L~0nlNNP_8S*=|{7X!?5xc99mT-C$%QqYYeV_%jhlB1g2Hpx!&hLYcaI`}(=8yI< z=-x=O7M_%Q-RGg(L6Q;dERp4!wcRe$)j?JBo0i~GV_}jwFS6vE;}A|lYJ>y-P-#op zasJ3tO_?$r=?vGk3RK0rB13)Q_rlE%=S=q`Pt34ztXjZ0H`#6HM1MsTuy9R{^J2SA zF_3E!hc45wKV$Fj00?1Y3QlmTyZzMMs)dZR@9SR9e9;;eV0g13#+_N^e(p1g0Yna* z0`_U8af#Nj^X!MruNpzEeKkFkHT+Vo%T4-63-hE)2pn9s8S1X|qpk`(g2JD~Y#B3S zmw;n_Fzjq>XHxN$v0Xr$LEe>a%$F~QE!3^RCM@p+yTTb=N#^OSDLY${a();~;MdtT zXU}HVin$vWA5puP%x!Wfc_iE#`lq0(+tmg$h_lnh-W0rIb^6NEK_Y4hovq$+luTB1Cz*2VJ^bQbq$xKy<_&*PH=^Ncf$2Z z+ocRwr;>^$R}!#))VUo*M7a*2l3SCR$gNm80yl{p!dzk#`FG8_GsYei5C>Yu@R;F$ zwGvr1ac+I#PEC>tJhg-sI;&MC3 zuyvUc-AI=*YJ|Zqv~QE5;?q8*XVnIsCvNdnFzemn(QbF?-}!`9SC|Q^g*%H)=}2re z-=zN9j@qQ~$nJTf!-O=jQ29V<8DhH^n<4IopNRrf2JEIx4c|-?|r>!gj7&&v^CY!*z$D z>ubffcFW2+%d6IRurYTgybk3XF@-9G=V1YZbE=D#n2d=W&JOuoCR{FbE-7K0YpnUL zy>xmvE4GxOf$~+g?%A`KF8Qc8U z{kI_kX2gv2d}W-Yk#*h@NMFY?o&@Ud`$K{TY$J1qT&c2G4-o}OP#bsv2&P=tAzcy3 zeqx%LiV#;tFs`u|Wq~7)m}WYoH5-SFb$?|aAND&x!VzzU9OfYCJ4;AoKT2^7jZtA&|dC^*0aM zkYEct9N@e|`y7m{C!7~9jg$zlc>{y7tK{TqUoF_k@gHwm_BI-zMt?o3xIOBdalZ-w zW5h$^W2mGn$o`*DC^gszcoR3g|EkmkNz_61A3`(X6^9BEp|H8Kl)kx)Zb??r-3INIrF1B;&znJ{ zjwgB(uo@a4Jlid7ELkV)O`z!DLJ7~p-(ox>e>~tflweO-<=ULmY`J^Zb6}XlD<5Sj zs*^JlQ|xXrZ;#(lGi61k6scX}rV9g6U#n=sZWcaG(V*T}F0G=G{%DIj%3E@nUqfrS z4E^F+09P(b9yT?tPHsBNPR?g(rL(?i8(B^@G+#apaLc5LM`P7ZB>MJ87+ZU!oeOQI z*>r1YQnfRlzGB^Nr;#~T9K7^W^7R_?bhB$g#pd z5<`~yPa{{{dQx#{S3P}7q>AnVLy!wE^5Rdd>Jgz1(^uL*ITcDyUJ%|~3+ijk8hv3J zYXLcX+pJMBtKfi6JQ;pi5EDwa^cd1=THjPg*IH?OJ=6&RD~u^KdLPdcnGUgbSv)dv z%~b~y=|Zz_b|S0-^v*ggA=2}5MC=JZ`bzx;*^5142x`RNpHh?5 zBiY!Q8%M7Ckt|U1*F)@s&kC=+6{;3LB9x0M1~R)LQ$^)K&{l4oH@U&s2(*5u{*|`R z0>x7UR)w{EGxqudEEVO(O0NcE5_-HPZO)C=9LXm@DIq|QoyP+8ZC07BnOnJWmOl&t z26j|5|2dK)d5gM!0mh^AjlI-Cq)_RD6`rFU*)6Ul((XH-hLi>_@#;rGd%3YX2p`^c zI_XZO-$CQR*Vu)B^iDe4?Li-GUHj2l$MjZ`oO!;wo0h=d9u)W$T&R`^LXy4JL4ZE> zv21{u(=RgZIybs^pRWAYp`zSMC;WwCQ%u4!&8g{xyT&_jC?L<^r#r29Une4IWIcCx z_&bqf3QMy0-lV=)zEy@u8gcR$_L|fSR!_7^HkQ>TIe&k zpo*q+A*USK%&u74BLXD`IEgLWU0}Z_*U!fb_MoJRMq_o%HTm<;S!eoIc15H1H7vsg znkXN&Gb4S(0?js)9+9bd9)4`g;VK%Aqrp>O)eOt!uDGe<`YI)Wq-OoS@#XFw5oytW zKPvT$6aLztnf&>BAuS~X=B!WMv?^=o9yK*&;nQ&kpV#v{N9f(j(~e(y>*D@ziF&=; zd#27TYPxt#?7P=QM*HPk>m`?o!A#YypU=(94&%eLajnSW)$BkG$Dwo?j0ju7EFkNe zXP9W_zU}>BT{FjZW)(*%VN8#@$~^e4M`YHNvw&}wH$V#v*U*Nloylq6$`ih1|Jyus zgq@SGT`A;!v4J`n6aq_soUrt)uBjR>spLkK7GC(7at1|T-Gk*N$ zwZ^1J#H?Bncf$QU?0b@Yh|Qi2?^JwlX+nU$3pC@uoqOosFJivT`?QZC&%M0j46i`P zH!hqa8Qhu=fSXNlH)8t3YyT(J^+AN;C?g6;rzt|_1wg%ZXl55e1MQ|+`(Gkz{2B0f z7wqmAnM4$7}tYY4`a;o+X^_!H|8>Xl#XgzDc$bl5@^smwz*l z9-+^Kk^M5f!+mMLh~1Hw@?+Te0opcWRVIwUn7ZP-;W(3i5l2`+ePy#zKcVk1+`26$ z=JFR7m2lz2NfqqP)h!KU7?jLEa_7z4)3TJ=snU8r^@fTogX4A1o7pFBzy!=9@<;C1 zmFq`EC-wSn?TcJ4nFoWYU9c4UJ28IlFoo>}-&$w{>P>a-&iWGUrt~6WO{I%R7AN;4$b)KWEBF(pIA&agNsx0T_6Eg}d_pQorc^u=xG(rtUX*LL zV4un?R4vib=_W}F?Fg3vht{eyKl=G)HxB^V-V#Q7iSEc_7|*1mq>J@=>;UbW5hMs= z@?5vkj~!Sn5m`SYGrssHRh=&6y;`gH(^Fkx**tKu;X#gv!GY(^ymfF!4ugG^-;24V z*0DsalH#eaH{2hf-FY_HscDN2uc(BHv0j!idb8XHXq|fU?M%+{$Zo&bHdtt!H1Y4g zD<8%cI%hfR{q)2)qV&Lp(1q)Rua3#Q)9hRms-cC!NV$JH(hQ}n!C{4u_}CaS>2V=v z!uE*79P-%rF)R_sv$!U626(&JruXKl<$UYX zTIfxh<#()s-NO>;U^@w3ztp)JXgF%ZiwXT;9scEWKP9qe&WWv6JU!?6#nX^a2b!Du zbDVIeRd$AveubQc^EI^LgSG$3*wRb90`6~})qKvlSTziK{0Cn{T zTFwt6j+sOM|6pwW1GFK<8jc+1`-`*b^CcZ!$Mey%gz*X zu9azMBL|z}CvS5izC@ZOqRpHeE&53R!P^SH(>h9rjr?C&+h7ZoEbx2xHLj3nE7y}d z*x5f>TgTILuVPHTH1qztrJ;?|k8(Y4gX{>`mmp_;-*m;(Le7#E{|;F0AamoctzNeJ z{PSjxP5R(~{hz#T<^ZjprOJef{ReORp(jGgdDUTE`MLa$G!=KWzCr#ca~tJl>i(eU zVLktEr$K)lGBrI%B3_AB>ZAM5ed)MnRWA2ibqPc5B7P}iyxFy}F!k95Cz5DR(`t%D z#jq`n9<6kPUbr+zX}x#t;by_j=Ngv?82pu}c!1{J4hyaSwj7|{dZ2GX`#{lP8<}N< zsOOw0biT;_SzRGaBZKtr@TY{f)z0kdmt)k$+~9VHHN;yVY~?gG-aeJbL^l6GAW$&R?#v&aJ zxxc8Ivgl-8J%4kae!ReFmHKMmn;vy-%Ul(=o+#yy9jrHgcUa7Zi}{uHT$4T(H*&B- z5`CgwQ$FrBA?0$LCum&s&M?shH@d^P?gjPSM3;Z7eh$hXPxbQ)W&kUTlQt*Bu6*c_ z%inT9#f=(_g{8#IE^l7zFyL7p3f*8(FOgn4!F3Zp7mPlX%iY+dC-&k95gz&YnajiI z#Ad%yYX;L_?XU(mvw?~q4P&i#c&g8!IA+V6JtK4ZM(pu!P^@!dTw&q?3ymV^G+_aOS%Y93U4ZIkbD#-!L0cE>1oEX>* zB!|Inm>fa9R+3T3o4e%S?t^)x3Svz5Y)!x zau}5M1DXmYPdBMKWWQ?v!OIP?YG+_4PyX|RcYXpJe4BV>Y`z}{B?lZmru+0c^1Zy| z()ag@kznJq0%|hN?BA6cYnwFBIM0nSO!KWd@w^`!e7KVE=-HTq#^g`icU-(-z35qR zf#L1q5*wulJ|g9hak&X2_u7z14t~VB0tYms^dV5i!=dQtekjv~ow^)i88;|mhcTS)h~(cjp5xV? zh9N(eR7ouS*jT5iz-|r4VsnY%+rg_OaP{gJzo*kZ+PKDZ-4d;}+S6Xlhv%>nRg><1 zQYQPAZoX(K#e?Jrbhi5nL_=R^TTjUIFdQqTbo!mQV1sQj=UgwIv1p0TvZgG0^zG+D z7+q-XbJ10>G-!g-x8ba?MQAuWOMU-Ue6c?{D`r%h!SO0$YmjG3xwYDtAwP2D?fpiW zlK(Z68BRs+RTwteH_owORAEIC-w*zzuZyaX6UEkKu^d@Owtd=-kg_I=i1gb zZ~@8&g}V|%bghsgFsn0d2M3vmwuQ+(tb^R?}vqE zSDxN@zMQKj0>hyp0=XXk9(dRpG3nv4}`6lfiIzv z435X&&gIYDtl}r>#bga@5|qYzMC@z(mabiP=;TPpR{JmY{FkFt+$1Q1dM-T2yuQDNqv($T3+(~K|d#weOTlemG9$M7qHoW@>zn+`D0JgRZ zBj>;tD7He9QF4H3w&4Nf793dE|KaYJot))8Bsh*!SO~suIn>&+w5z6Y&JL$m9G4=!c{z|!GxG89< ziuTwQS5`Wt-o)Wv=yXi~F3jgKY+ogx0HCNMkyb&IS& z>6MNh0Hg}f? zBaa<+h-v0Ii>i2V{LRVj>^K_JCNkT;b8FX(djquLuQiFuaQqi;b6-`^2Fn@o8|7YA zjlP_#ZW=rw-Oc4keNu7Zl_!AZ*%nCsq-`S3%k4>hhr=97xVf2$_58wbde%qIp1bZD zx2;NrIU^U_>q*P|<$G`j`iNk*%^P@~E#oyP~o}Nwo7M)zFVoZm9IsZla zzJ#_g20$3cCcGMl4l~@j=W?hNr!2oe7A0buPtG>GvXUBj40CRN*v=m(sdZP^n3p;VxWdAIW4;}Y^n(5f z^!KtmoEnEqb`y!^<_g%i|Fs92bWD(52ZdK0^7~t-Yu@K_%bhiTDX{VB%Idb!)j9nl z*0)2xHSH^=8x1{wW2Ka|Xp1PyVY-TuO{j*Xz`1`=Ok?RzEU$eXu~xRaX2`nww&ajO zx=}cO*deDF7~k{Q^YvD6B;uqUJFk6@4bJ6SJQW6}rNH(iXAcbh=gM5z!y~_#oc(>4 zl)PYk$Cb?1N}|q5*e`tMFhl8zN9Gzn3m)kx;_2Fmy$MMx_(b0Exoa#bVimZnmupcgOeK=e=hA9 z=U8BQ?oNItbOpFtVO%@6PAW1@uitw4uVMdg?Gpu#2eiV~E;I8y8ZsH&59NIYg;oD< z7E{IxHlX&+E)`l0cc$8nQTWoTr?4(YWLDMiV(4}GAFLMVMmvTV$iD0}vmOz4 zWYfMF?iVLATw+rNuk9_fEZ3IMtHc2RcaQ%+HmB=-?U(8=(BKQoXLHzbb>8-r8LEmf!As9Nk*SU3&)=it z#g_xiW8pybVx%qqqpeU^C*qJqxmx94B`Gya*H&q0vy@(dG`sivv6OvM5%{%<`*Yhe zr*}0C`vvCKPe<<(}$5Kb1%=m zsxjy+&`Rib-INFmucux)H`tQM+^{hK}x{bY+|DJ5s)7Sm3 z5ofB!49z0?+Qu5w`q?>%aSKCqfoxsS_AEx$x#g z0Gx@oGv_Z@Jbi!@W2xa~DH9l)R|Ap;4IG+F*sJ#_=mVUZp(>v&wKGXMF1KpZsyb0q zrsOyC275Sr#CYnwjVDN7WA@UqHzyb_v47i#@^190=-J=IUUQHg?t?R|Fiah~G?8`G zOnk%gcb#xIH(NiaejT0tI??*Cqcex?=IZbE_T#*VX|1xP_*78k+IJ_%!NQ6%`N<)- zx^;6OsOUM+W6t*y$4M3}2WuftnG)rBVD{LRf%|R=AMl_jV_H~U)Ieg6$fU5x=Go5E z6;sGxMma30V&r)=32@WnopxX1ya;QiL`t@@%FoYHyVXWuEbj)HfG)YiIk7u`^jxJ| z!)V`Bn=hlgmHC!)^E`DG`3@c**DNDOj+mP#Z6I)NeZx|rZfJRM&5gGA!s}*p^V}K; zhrLnu9k!BU7JGWWq4I+V>DS3%%L2(A5WFitTd8zs6dZ~*(>3OZD2sQ@TbDy~@pIlv zBurC6eu$}6-&yNFLE(D%uoF{hF+Yza?RSWBiD$(yKy5kB9PqJO5Vn(Gu&M+8BF<;3 z^rPKlBg=1_W|B%NUd=&EY~e}R$&M>ub_?T7w-JthD%g9!4l_M=fJMYhp0VnId^9}5 zNFfc}N*tOaVsGr_$C?shrWonbUu~ojMD)<{gd==lvdy**q9J zCB?UQU$ajP6A#aS2^|<2aLl|sUMFH7_}z!y^Spxn4AsZC_R zc{YY;U&YQbh{Q za>KnUasEd!LR5K5hI_QX*N<~RN>?q3>wvQY|9J%NM421!HN8w^f5_|`G07%hE!HKF zNI%cOrOs``19xje3kD~iiS2Zo>DQH2+-mXfgpC3^>g+UQc9d|dX|ISaq5@bRj_}x! zsU?2ZG$Ue+xByb?_z!i}v=V5U4+X;6wrh{ab-=^&gl#KwHtlvivj7gupBwF(TjaMMsgfYd#!h8hA39LUo>7 zqa~(omaL?%T2VrO>)vn^e(pX7kK5fzs)|vO>!-!Yy$bmJox)2Nnk*@t<%W-4G$O7js*PAA^ExKQR^zTwyW^R!ZynvIZQD+hT*vkh308$_cU>T6ntESme?-Fa zjb10b&MVAv_36UA>WH;M$d}!HUSjZ&yTU z>`FS$&L)*$OJ{$#J4=w-wXn1v{!Pd2SU$gL#6#ZdWv*g>_K-Om zPBE+%2WOTC$v2sN7Jo5YG2*A7fSdXTsT_K2#G}lTm#dZ)!!f7!x?^=6-tiNNn;nu#;(p$!i7>#UH0>0Mh?W1#idofPlM+}gMjJIGD) zw@lbx{4{2Jyz}^FFaFWcO7*GIQO4W>>OFW0((_=`(Pu9LXT4fZ{E zshgmYsm#liE~Kp2-rut9W8@CC6R_}^tDBbsdxV%nM2{#_Ln=Z-U!_fL<$aM#jt$Kv z42Uu%lmZINmYn&A=$BK(%1U?#<=4#!VVzWn=6{9p03{T=-*^E_5F%q+_vGI=&q0ZF z1G;#V=8AAhGTb5)zN%so)#Iy)?{$mv+{vK>kDV$7X+qScB;M;Y@F7y>EK+;X3uZLl ztR&UR*&&hVx>_W!t;Soc5*8`F=nHJ6MJ6Pvob4D=96H6md;(7X?cOD!CdPi61RJUH zS)}w~jFIdZU-tVJA+{>(lXz{2NUd{lGsRcs7G~eH<(B#EbjYtNSNhP^8{NAHMo8tR zX*cR`Y?I`pONh9H@;s?aC)K%b4NYCe$&=D&$eD+3%!+%#6GvMfAs(bxK+>Y1ob=F<+vMD(R~)O9L&a@YmzU)ahd!jS zvF{as{goheXbrYcs?D9V=n-KpvZvK;a)5Vb+!&!7{@NOLI!%bY4_eS!{Y(^L1qc4h zc43B9h0+TTomRASOkIBPDC^K9l}%-qFPtRX=ZbHHId6yCdjWX3*({H7>+;?9(hT2c zMNCK?x`O%bq#UOt?){r^(QOUXysT6QtiFyAA2M*tMH@c6Uew%t$RppkGS3$u<=EDG zQkoY0Wr+I&jc9S6K5?%KESC6w16t_wmj>@~Y3-4lo%eH>-IUPAeR{1DEmnKrqqQ6M zjecLTSLC&OL^N@G$l@$-`)&@-MgGErcby?pBT9FUBetBYQhIEsrL+bP(Cl(lXR7kx zzL1$mqE*<${Atj#{0xa;Ov5RV-`&aXZk#5l?yiCp;R(($*}WiVd3Y70O#E@BW=geE z?2}9H z7Y`fu?{XBP**W}Wqzzu8+7{eIe}OG)XO$V01WCUxYFSrR%s$$!to!#~*lNjPV$6#g zxFVasb?MN8qdCOei6rCUD`q{#auUlVMMG4POO%ptG~5t#zcTLl^Jdy)N0r?Yp?R_{ z(h$V;-Y6K3CQ`nvu>Jhua1%kxHJgyHTA~&MWaQ8po!lXgY|uWiIFN@(Q1TIeeqsU9 zWaWC;w8=ovG2A`@LOIdlpea5tZ0Osd6M*1bOf1ki)qe9L+fMD8x`fBBBYpH{6V-vW zN^g7l81j(ktz+-)w63HKL`?rf69HXy1x*VvlIJ@?nLFX~;Kmh5dEIqpO|}@R{qEHb zcL?v3)>@5tDWsxNGjtnBlwmwK!MFi8 z&GIFp|5*yz&;gvI_1}N$+`G^F^N8hE(CItvL;wEwUim~FEPp&&m9d$??w~6?uyq>VIt9>vAmrFNl71Ht2zZP0bM-#+Q$BISwn^nCmS?Ab z-8Z+2Ch>P%HB#fZ9B$(H;Y|NFE2}J1aSqqqPnd19Tb{YhlKI5u<+HpFdEE=x_%XqP?jk((wSO=Tqh^>@+P zORAyOoOWdB^DBNytkQ3hm zAJ1mVnOHJd3K07zpHoKXZ*CMOn=7aWhIMk}!O=HAete!q&SuC7wk2R(>>JdaU$Snh zUNc`lqG1$Ni+kaz*S24B(Jl!$ie}eBkO3aX#AF!2YBAmjsE?660;$E^!k{7^z<5ZD zrO79#_Sm&C7QVsLzU1WE7^4O4d6pXxHzjJ=0RUTpLujg&LA~4LB!sTl8Ks7b232q4 zp+X;{1r>o^hSYB+VIg5*p9+i29`NvFgvO(_P}>c(2KQmyYXf0scv^d41+{mfk}$NJgQI}N6D3n==E&PMA-AK3Dj^zxmSh054S*+-QE5yx zYlVZkSBTn}hgy@!px6RNh04ESCK!)OqhfDYn5cfFrI?(CAObir28$e_2CxN4guhH+ zrB@P$;RB%ILL0ABmYPa}y1;rhWM=A2466U4wWOT^M>b4RTJf+j5~kN*2IeUe%UF~Q z%>Pwgj$7G2#T=?FD;j?)Ew717xiMO8&Lbs}7+T(z7N}y}4)uzml5lF|wk*^)tmwCd zB`Y-BV4<2w6vU1O4DQpC4S30p*nJo>R6Am811g{Hl!s>RIW^WYa;j4Y59=dgMeCq` zv$@RKP_He_gYExITR2o)OIhz+^(9Kp);AA=@cOjFSTI!hYi;OFe_n`bO9)3k>ShCF z`$=-q54IjOVM&J~mNpAwB)koW7zyv(d_9FHO)e^|pyuxVNPb>_(1av_O?ud+U)98KHg2whc)%FNQh(Q^6kbRkEmXb`9#x-&RGWbR(1?fX_(B8IME)%f_EQ_4-^W3+ z;B@Evo4)1Dwk<9TPqW>g93+uq^j)`X;i%gJ_?K)9`17ryHavf?4WieEUnmXh@Kdo; z-0d*K`%(jh_us1Ui;VpPBb3I{7?M%eT{{qT-*mdS+$|}KEuBO%{>vK_Z`m{s>axd0 z8Sqc?dHm@XP?bGnEL3F|%(Z~f0MpEmXXO_ikRHPfBu6piJRI*1BQ2!-dmKX)IG@tA!h$p~+AJCz6L zl)Mz7I=P`Su1s|EY?6#Y2OLBuPq#uL6`p^3TKfn=Y$gsnM-P`W`dAc&=ZZ37(q|cs zm>y39Au!Nk3uEA+2ey8s2bf}7#xO0TKDAl^c8E;=Toz>nkTP)6p)Lx=9`!Pcl7%du z+aVfoV1r?g$h<|oMc~xXSP;&V4HuFjk}w{Mq4F}mQ-m9wo`5HIQW8MM}NtK5RH$auh1 zQd>`MajJB`5o}4>H|yDxm@G$F9R&|-#WWL0$5#cP;1`JI9WX(4dqeQ581zXw5kI%X(@`hQRt4~FXkt!T(N`7knMDiB7= zSG&1<7~Tgf;eIb`a(a})rHiaslq~dS0u(mf)~#NxsKk)0-JT2#96DjJsBJd^5uzQb zAlEwK+7=NF)wal~Nz>37GtU-k)g$PXPAUT%L|v0&Jh#++xV9bQz{$`sx|~665rtx~ zkmiv4#WNukaf`f)4h%(37*C4IqOOr;3S8T5iOS?~P6=`CMma;fnV~QtGf}jWC$hwB z!gVa+NGn+u#lvzi5zSMDNk=?^w1XFvjTzSZ7)^jE zUP`FiAN~uNEa(vCHGilw>hA2JkI1r&96668ri2ZZP2&ZND0IpQ2v((n;5k#_Uqw(U zW;4(C(2KXRzUU|+S=Jq;3@X8RlR_xWPrf|nL<#~nPuM+q*rVh?R6!153di5jcKWJ~ z5ZJKA9wkQMD&Wtcli09j$l7ie&!*UjE{)h8<&O?Rnx)Lk2-_8S$Vf78-qw!$$^Nh& zAyGW(TFg=?WJUS&?BL_9Jmyl2$nh(#P|l@3B8SxS^*u(zhXzwvXptm|E+-AvvVm3DtJeQ2*7K)aw>4IN5?n z_);)ZD6H??F}~A-R2XNT&(oI=6h34HIh4tVgCv#TW#t=h#`ol@w<2yHJf5!`rF`vS9dk(|mSud|NQWU&zLV_6{T4d4fbC z7B)ti5%id5IN!JTNngA#8X+VPV+t5HyxW?Yp(ZT5oS}Ep z(&r)(3i2=kNhIf)?Z&1B<~j3-hcrs8+y~Xa>^YlM|lYD$|EWCK6F9;h@Tih2H=`Tmc{!+6lf6Yi3t^x?2s zgiIFCWE-X`c^a!i#t6w_xzA{1Pxwv^4!~3`KaA&j%K?RR2?74e-aPww3jZPpf}{qC)wPq&(6@O2Yna>He@6!kJDaUR)IM(Qkop37gW%7QFG3Gl~Vn!x0((kspq0 zI~koZB*8U5%#L@x0UYB~YX#x-LLKj~i zgYHfjBag`8wLHU1XeiI>63Vb=3_ZGthhInCgfuq!e17amVX*>f zcVmq5jt%y`Mi6<9Ji=2#`URKAL(~|t({23259vOz9>Z(5`}kFj(a+jaX_t?nYRJ$- zZm~S!151-bI~5_n%@Z`RF$m}gJDx`l8ATF43B5B-ARC253dzIE-^gJd&yuJcy6&*RO@d5>(;_uD(9&1L^3X;LG%T4=>lv_j2#>(&2o+*5 z3dF(u5x($Y)}RO$A(aHM16|S1$=hi;EW#1C-lkVx$9%`TJ$rBpU=^i{w{wS3| za4$Zy)xB!CbTj63%Yr|LkD|PBRl^Yj4-h7M#-p_kCjtb3dB%1s5QMZxE66}L?#GWG z1_*-8TDfrx0-~5qvn^X^)e3zSnD)C4a!Wvk7tcsrXRT}{$z-y`ja#bb%~SurI;`iMu=Bjgb&=F;O5Xo#V}*QEX;a3xs_Zy`vXA0G^zzCrY@vsQy(p*hGbJM z>!@$4-++CNxi)j`td(kP_G`Z^Zh4z z%vxgt*MrK%z-TR41ff#2=2{D24aHt27y?CLbFHZsuCAb6HP3(epT4QatIZ7#8k5Qz zU{EVy>HZF6hcIFHfDZ=ZgRThJxs&O_X%?M+*IS)^?~$- z8w1N4<&vRKk<9hplLBV14q$W)iI0#T!c%esqL-;{mIp8-y~xlWbXvULVhAnLR#J_P zA}7r#ef3boE*k_cOtpR&(8Qa0M$3m9NvzcN%@M&uLP8y6z#w;Q^)Q zS~m+?Twz>#R?6;-)jr1iEf<-`ZckFW`%)L}&f6)Cv&v}p1tseCSR4MgTo)>>%ti(B z3|IRgorco429<_k2d5TgtjmWJ}gw0_=pTb&0;yoW&q%` zGqN;%1EPg!dQ9Lvxw`#>v-Lnot%VY2U|)ay7(n;+^%{q6xzg<@jRqs3sc?@0R}I{w zL60;FP`kqnFj-VA8BX+gK!O_OFbftmd}uNLC4EtBJjCl*bf7+`E8Tu#G8-vE#6B=k|Do%=ZnX(^%^56Ne z{%7Xdr2jQT5NL>ovfJULlMN!A$*kcZ6LO*kU+>uk-#83e(GKyaAzGv5~GLWnQ`1ZM9WKnDbl57@qlLDqC09xRvoor0Ixjrk9b}qP8->|Xz z^@=YO!McS61jvn?ItY*pxDaa=bf6*d0<{cI5nv&MdUO!*Q3wru0uv2^PY%G=U_#XG zG?c9sa?~SJq2x-zK_9L7TJg2ymK?B-9}5gC`ZZuqsCJ@)CaZ-4;+9TXgftQGWD^ho zZwn!i;fEkdNUh8x%0CT9>d6q(JdKpAWWX`zhlWPgF*ibJjZ=fqIi7-#HFYAxLDWR% zL}pFfwQx2BQU>M+hX5k9eUgw2T)!ER`C5^rPjY|W&U~uEcQL4_WhzYYR!G<(#$2J{ zo-G-MlamZd3@9cWLO@d;SZsEeEle^5w8=0~=q;IfySWS^HOs8qtzZ~PMBt!sS-^Uh zW-^YZRSM(GwPofL50#P>5csW(w8b%(H2={}Qo>WtcxKJ}BpTiY7f%eCk=l$QIL8EI z!a&XLR&x1>_|5SEi3Aj_Vge6#$N$xziA+c#|F?&PyU$R-tCNb5P-y7Ty!h9wxteKq zzAM3t@~e!cSS(@yKu!$-5|28exKxd8KoqTL5sFqp=oth~=pW@M?^p2ui~kSkN&o*= zv43AkcI%)3qjp6|B{UbIq5o?n9S^N5+82&P;kpXIQTG4C&sgw(D0ic9P(5};)@cfe zJf{dLKcxs64f|#^{MMVzF}-Wu;w07}jq(msq00aQai9UnIe^gNuMy&a@{xgm0Ahj^ zSE5Mq(@^A-CsJd9z7Ij{kW*ukJ+{cp3KTh05ekExy(dEko}h>`vK2wrKMO@JJwTCn zBxF}(D6(WFikvPFMf$5z2-${QC}M%wl|c-`eSm1ffq}`BqoK%=4ix!*Iuy}eMv{$AZI{a z;mKq&aDdj6;2gP?C??-kASFb*(=~;MOQ1_7vO$QruE$%E9n=dc9xjomiKD1PIes*S z>_T?AN_R>(^=LwYT|2r@OY#%#T6mwmj?K8w;=?rS_wbi zG=`w{tI|zltuFy_I;uik($QFCGA4#kO~+#20Db}JXUf~;(Rc@dn?%S31ww&|cEXAR zF2W!(J?iV#d;@V8MuI>djbEl0h$sHHJCS zj)J|Ynyh5>qDpEklH{TWI)fyGq{&G`l2CPWY?>M*rKxF2#yS+fljMUOi}l%Fs9c~k zn8$QMf>SCyGv)=SJro21cdv!nD_i(c2gWVOjY zb5Dh+i5Z)$&nk}Tldg!hN-|Fx8cR+xPQsEnYM>;=GLkSI1}maU!jtGp*d$b}2um_k zVo9VX01M6ls2CA5itmLWD-EFF^clcEJCFxRSps(6oe7K=Isc{xrRACz*Ab+m2_dc- z@hzA?6z>>8a4I=H01l0Pq3GfpD8I)1HsE)fBliH(E?CEW4frOe-GWc%3^;1817yeD zP;~7*6d4i*lAHnR!WtNb+|@tmcLbI1`ZLu2-JfV`bTY*lSr&?%L=hc{>@cempl}%c z-(&?B|KB-4)+Km$a9$B|s!I{F7@E=WTW@6Ravntg_WMbIRIlwg*s!=m-aBK-F7tftTG56P_YD9wpk0xH ztp7lcVgs3T^f%1d&BAQd<{!wS2F7AY8j)dLBqZDk$h6FmN0U8E6zH!pyN2i2KPz+o33EjwNGMtRW2UmSPR4v>2TIe|o#}uqLi8erE|8 zl7Nxjf-p%iB7y=c$WjRs0V&idD4+udBnlXm0#a1e5fQ_pBDEE_8f`?OF1S!P6hWV!QY zw*%}&2O#zkU4zUf)C_5lK?&YNRG?=itQ1L*6gfi389s-DDGbgKh6@Y2hmj1)0ezt# zt0^^y?tva-OAg@kp#U=kSQalM_7Z8hQ8}svOIEr)tUDS9K#M!;gH)Et!v)Tq5`bRZ zSZ}yIz)K#|A~gk@J;7NM+Ei_-7ttoHKyAtnf^U(Ynpp+fh|2(bp#W+v1CX{M4C6cD z(jS4Y7SxnD&Qyo;P!;G@%?ASNQ4i_~;}Jb5pJv^a9=JP_vu+^{3R}cSV_7$-T4JmR zu!H45L}QwwjaiDuNAS$brlBv&p}*?kDNO*t0P!3oD`ll4x4PLY(VKggG z8X};fBc;%#oTU+$L=qnjwaFvr;p_-jO6jIy#(HD+D=95v1MC&`5eZ4^DI^6yvJ(!H zFdv>yUlal8jy3}aEy%1_WIBfY*pUk{$zE0wX{7!HYf|AgiagltlxkP7$go+=(c`tG=35 zLWE4h#L+WJ;4FIs%2imCdx10q+7}pSK&48oY&I=w6(A;?CxU)4MkZZhcoIWUODNDL z9ox++7zQVu09ek()odJ%zY?rA97H_e1wSCe2RMXoWI22nFk!s_WPAc?StMBwEZdD9 zhP=Z&fIWM@Dyv85lV%9>D}XOY04GX;JDrmN$zml)j%eO=VQ3k1w3=^`3k;D9+E zjf6Bi9*qail|gxL#F#LA;x8OAdyi-W@Us_`9S?MPA-`^m4y(whNSYanh;b~Zf7=m~ z-2r8vfH>9Nl0;h^P)OvRD))AkW4>$@0P$|xtFqOm^pt%gqK2VNUZ+pO#vvL@E zZmeQBADS&m)(e2Gv|R)XJ1+izDNvM^9s&i;EN#4`I7J01c!s0M!>dose|l8=lF`*IZ4OAP8~Dbo`CgBW|f2? z^Dg4exy8f`zm5jkRQo)oYk>sD5c1GY87>Ow=H5hOA<)-u4(5#fTdH(ztB_$1gudT zsPmcDu2x8~B-4>BNmo>+q*SU<7G;&D#R!p+3-xheDvC}MU~V=gbfS?fFecxSND>?_ zTV5VJxMCsLP!q4fOauS?2KUa!1n8xIWxywW{r2Cz3n2-R)hJYj6X7trPh^ag)S~=} zJ5^2w|HMoqez(8QPVvde)YpWX&Bm#fbch*0#4JXcH#I+zw=smTGDAWQh}8URuUXm* zbp~0=@IR55ra=yiVF-mxTSnP!A44rC5WXHOU5S`kP!Ym)*(4f@??P@fbrr7zdzXu> zb~!IA>ECg~6klpFD)?UdPTJn0uW{~+k?n(5)`3_MCwXb}k-agNiYl;CsM4_czo42? zhv~p1bupl>8xdFw#qt1|EdjC7QCOQn|0!zMlBVsj)Lny=4k+7=S}Q8)ZY(yIqxJYdZpIuU|ya0P-_!mS>V z=?OOkqOoWhT7#toFHTW}I!h~^DxE@Qf^;w&3)Z;6nH$k^I*6W2m(#z}JrW~{ToNj& zl=espWuaiFs>!KPp{{f+6v$F!90Xq|Z4wCh18)aDt{B)c@bdU0x4f`@5ese-2bNbbxX(1>z(@k*4CSbS9kWjZEf$owX?qFe*d0lyWbn?YF}12 z7M!&S8ts)4RBE&JfQHcEgp^_s@0LwO;#W-PNL@NrGk5NDYw{JAQ?ymI}bZP?2Z4=ZcXV z8>@;!0JbM6UGza*|Xz^UKs3$(x&ha(jvfemc{%zUNx`yFHKFR=56ia?Qt`f7IW({rl=$PhYHl-+%l) zJ5Xqq!Pm}+D(TUGvF%0O&&3UPS*CNwO-c|ATYK?#+uhuY2fNDO<~QHCb^rD02c7#5 zy=|*KKV3qs8O57`pn~SnEly8h_rb?Ivk19zT4jesS{YVUvs*gM#DY;-jKQ zr+TFO2gFF{`H?}cO38-)vp+v5zI?pvZp*R$U6-t~yjRG)vz#=;){3X_aw5EvBgU1z z`>otw;j?w<_E#rz`;Ogzes$mA8L{EB5?!=2`CF{zjyYnQ8!G>vbNR=m#23v8=;k5R z3xk)NwS=n_mV9^_=u-q`MnJMjHVB%?%uj+mq@|AXXgjb%tFl;U!D2bqF{G%N6a~yt zCiny;Yc)zuT3B*Mo}4yr#K;fHVEfL`D>bl&z__aNzs@0@S(N{)lhOuLW*WiW`>SOQ#L4WaPEZT!Z>$3&zgo< zu*%@@a2XYojcFS@{2AA1qiCtX2g(PfYcvsQ@pCjP9xV`|*n)~?A#*I6U}8FKnG+1u zPDW}{)3ouK^q_qhGbpxhr3zn*ma7!S8#-*aNwl^^Nc@6Ct%erw<3-!VemGY$nwwGy zmD;@qi)I)hylEAvjnBxSEo(68TJwTt+igCh{CG^&K3k)JMcE;m_(V;5BuSfq>CkYG zx<-*Ln?I@s(-L6!?^9$3>sB98389c+v^F8CbfxPWsbWM}g3X?;!S1n3~P z3Otu{!o+cwDfblp-w7<-PK04_wnK=7Z#`u4)?!8wJPSoY>Vi!`psEzZ6`t@i6GWk) z0HR%xD-6S8jYSYa&>j=+7GOnFE)Gu_6b2_&1!QnLHySDjYaHk-c&@?FEC_vapBtQ{ z=zCM@T>Z@Ej_~>OQ;(k9Ex+xzN;Ki~PO0;VS$M<5Q$^(>V&*dA4Z|E_TQ2o4VWxdDI_qLB% zU0tVMy?(5IFm$=`@U4Q$YzN{PY&~Kg));tdjoO$#VezWXE>4qfUjES&96ZE=y~@bp z!9kyX+vM-!(g*xpAL{eDb#ds!)8Urg+3%FiGaE$5_(OK`p7RQ}KUw2+KYAtq`&Vfq zzG=S6J!|jj2{*rt7|}c~Ax%Ek08AOJ2zZUcWfW%q6B=S;Odm}2-=QD=e-4IVKJWqw zU>cZ>VpCDQ9b3AqLU_2u4Z&yPs9^QIKt9Z-m-q zhlWT-qfYf*Xqfuf&sj1NdKo5%jGqqx%R(P8u&vk@0P8QIl7Ptc`)?o3Ntk%&=f$p; z2vyAe;39*1LuK%IRbtUW*=eP{Fz{ZoEFwgdJ#q}I-M}!?kHLQkbDd@^QB0+U&MocT z(AHmSD+}~G+fRJ%!ufsEEJW2O`W_82eB~*)J8{57(-Gf>`F)(X^vj6DeJ?MaQXiYQ zYv%a%F3&Ts4SGEP`r-q-cL&uC{xy8SZL@Vo)O+))sfRvTmkv&q>{b9KIGF@U0E}sX zM!oX~8~~R8;e7||L-ppD$yd9xylpj1MBV;;uyA_YOhSZ492o|hz3D)T22BGM;!#Y1 zC0BJinFCui4<2vYyy)dUrl(T@9~D3;TtKa!>d9eJKQAr~1aY`N1ppjWjdY_^D8>Pc za(~$X)beNS2>Ey3A0#;7)Up%f{=J4lw4o6~fCO(0zsDf*gN59;gXbv3kTa8 zZ}Krye7QE{J=6Jg&$}VR`%^$Oib2Divv0A(%SUfhhsk7?GYSz=pLHFNw;i(ew^?B3 zuWv7wFJJC53eOn2L#Kd_r+JAJb98nbaDc(u^L#)@%5XArBrt?CZMhq5sjb*sVKvzI^`d)wgfvr(5F;-VSS5#Bo4+)T!U+j((2a zVUd5#GHIJmqtP@r=2TYZKK6KUwP;{e)Y~_2`pzraxN+m@)29_a8BZHl)xB^*3|KYn z@Q>FC7dLO-9QjgvENdy=w(WbzzC-(^OA%ccVrieBmCqU6es})4xiO~=)iVZf22gf_F-c>w& z@%L-nhXlXl^PM+bGBz=3$||`2VvTh7okOvQBCk3idQMct4zXV2zIt@)!`wF~@^W)G z)lUpswCL31_s5QHx$RN9!8LqS1a>g8enLx4d3kx@(#;p2g$g&kN?E8-Gz{KyRcSyF zzrNo&v%`OB>k`ypa`MJIlcFqL3PKb-n#U4A1n8B9jxdX3uW=bZCWeS?ki@i_eR?hX)7W9)aP& zc-o=(uk3;C@O) z&c=;alkTv>@NYkV$%N?dqBv6%}6W7qM{W z^G^#cR%OTAUa+}T7&yD%+pctvo&{F-Q(oRXGX25A;uxboikK8c>|nlqdGj)8JoWI1TCI9-#RLv1 zlMj7CbJP9f*64R=$2~M-g2%@DPcFp25xTmfvCe(ow9)(Y*TC6Pf^gi|c>TOtS+iyZGQ(QLVV4)JtP6L3e&A`7 zE;TN$t^Lx5f)GJLNKT0tW9nn0T}ki4R;u^xDH*=_{_WdJw#i?wQo_T-yY`A^@*0;k zi?1RI?iiZU{o~t*+d^xEUg6TN5mUWO&%1M&*?~K|y1JgOdxX|t>ynXCQ-%9Xocc%RaTvf1n`vhC`?$CiQn4;~ym zcH-=BDVe>p&im8a>}4BAPn@`~vT|`t?|~VK2jgwuzI~eu{FeMUURjy3XGHtQ#bIIV z-ppRw^Y+;1#~UxcWzB2%8mx+cdGd5^tuUKOEWe}}eJ8cg(AvzwV5L8(+T0#>UaQrv zzoclroW0TKM8pqC%%MB46J9?*Z?8g>=si3$Vd-eyXtcx@Nm|5{?i{(df5x^IUKV3x z25&c6c|Lel!?2~l>Nw{nwM~kN5gR`ncjmiQ35^7Chx8(;;rvR|3*na?T!q5gA&_v@%*DYbmT>Vh65Xy_!4A`Q)inj%SXZJlWyra=bA& zX?RIEvkMW}Ja)8uxIvr90Hn4J`&%kII&K_z=guAW`GlD>=i}5V)f>Oyu_x2s-v9La z&d7TOVF%3)J5~Q4u`1jE?T%?Tf7%4%%g<`H(hRf(@>lQj8*$cU?6d&1r~rwkA~ z7J}q2A7AKnyn>ulmNC2Ph8zwH5C4re#8JWxK|w*cPn<>j7ot>U<-CnnNVd;jCEcC7 zbra&Hz=?C3ENE}abEW^#^v*UU_KrCnLeIW^Jr}%} zR99Dbd~7}5Rt z!JLvo^K~V^f9#psU4QgwRPSTCUo%f1Jm_`r&adlBe_0q4Lk164tz7xmHO*y1Xp4A{ zTD_m||D*FfT5$V|cFIGdZoPUnW6{<-OJbN^M@oLaxw6%Ft4#ZA&DcKS1$r%EPy47@ zpX(Q*wM)+;c?Idu)(w1ff!Y&ZvT0ne;$t{os#w&advPce#yd9XWScdM6ht0Rsl;9z8+Ts@B})?BXpu+qTNW^$pxV zX6Xu!UZSqxe4f9+v)jHe;^VR4=B-;tYikF;`~0YXzmlc02U=I49UFDbgutzdoL zSeiv5n(c0kcvq#ky3Bqk=Vcm0lLw6e0p&m!6~njxq?L%F{9;1(QO z0?pFVSactw2E3(#ayq>P3M~`|s8E7UCE-gBHuOHKG`NL=NDJhYk_n$(`{DJr7 zqG!*Z&Aa6Pbz7DhF!AkSqz~(T9rZTU&+l$aOAvv~jmi+O&8Ox*(>69XZuOlml}git z0IiL?%d&2f9oL&a2+hitkB0_io<#%i7`pUV40A4;-`Zzru@RWwNV=7E8Vx-1(9xs) zTX72b9pA#Z&psQ6w(rE&?ksO4mm+t6|Gx0dV}G%@VIo?`#x#>bCD*6_?5Y3Kie$u{ zPe^+_`5Upz`Kj~g`);^Ym);fF8Z&8b=v2T(Wn4T{D;;>@Apfb^^z@C7h3MGSWb|k$TKlKFzGbf7Ro3zaAe{?2N^GsI+t{_}eeq%_hy8Nt5!YhdM zIC}c@Y+I?oyn5X7BlnU#HvT0(P+mR_Sz*t*8lH8f46gsMx7Svui+N5j_(*jou%6v} z_l7dNKHZ6BIiLQ~eK~#TLATY5V-sfb65`{ZJj?U9X6Qa18##IQWb5)*zvwvl9Dp>) zc-zAlVxyxufyIMUnI|{!@ZhLU^E?TUUK3@Fg(Y<@)y+{JB zK__H(L?esN*1;jAa9JO>&6n4$U7NCC0WxhqfBEwMP6qgBOP#ksywk+Je8#l*&}4_d z)u;%ggZqmH&Z(@t|KP!*uBR6~&JcDa$j5rx&_;J;#?<<;N5abqs?m+`Mf53KH0Kqc9ll)wM*EM$K6wLWq?C3oGrL7!8r8W-sn zi!b^(pD4=bOi4&+j7WO1y#()e=Xo5ic8oQp%*dmS$|GZ)Vrq+W zKS#V{)j#K5o8>-j&JCSTxwYR;x!ath_lBo*rVmBRNOklZq^PY~vu5J22v8{o6?mQb z{rmS*CJi*^7la_){^@jj`@9oKX7%si|84Rt^H}4y&OFda(GQ+JJvpLqrKwZX>_w}3 z&JJ7r{pPU#lGmolR@_)qQ^Ry_w*#+IkWn$3Mn*-Q7bX1EFE`R<)&kd(r`=-{4vb%P z=uR(_m1Fi6)(sjm1jW$KUvR?l(4C&M22M<8p-}j>3h6N$Yc>bRjsJD@GqvFvSm1uQ zdTxHa?X;2svT>l|Bw8=YBD7^0I=9=)*C56jUDba|{>YI?UjOOOIDG{&eW$)XGt{+I z?jCItHl;c`EbMC9AxG^V)5tk!S`F6q{XJN@sD0JN;1OqAT3Qg3>Fpqw+dYWQ?K(TU z(uY1tj=q!nB`;;tm!6-fP&e(|q)C&;9gK0GyTE983I(|I+S*6kGarsyP(8<_V1bof zxM6EaiA^AymJ>@)*46dx-ni`T?(Dw}^QXGGnGZF!4(scw1WN~67P9B(ImJ$zbTs@# z`q17P^^YGMn=?c`Ua!5MPU-3Bs9YSV{n_{T&-Y7_#5+JtiYP=g}M^jzZ3qjEq5>FWaeZw6;!evYOD* z(Xl$l_pfJNNPeqF?|nLC@n8DVc2%c#-FS)i$pb~7Pwq>dkv{0{?$-->1)bkAFT6;k zWl_7_a#^eIl>|pcwTLfXy!dtj(lTczC8Z3#`St5p6Yt?azEt=7R^9#BmJ1re9`7?h z{+f)}hF=ql#momCvJ3HdmsgNnxa@$cr{mT>8}G?)-o8zZy>@43 z-P;F$bMRf(;U9~B|606gx_{ACG<{{3LkA47Hd%=*%*6{YBCGG+JzWf{+#K>4c(&H#SyQpsUnupl3Tz?Wcyfm(;9W{;1;T)_VRQ~PT zH*|{R#drV>_ePFrskwc7?9A1VPa9PnJUEJZ_@%`Z)FzaNXRsOddV zKP_DP?Q-mrFOL?yn|=C6#7<%Biw~DnXj9m>&Hl?Hbb4`PH+;UhJ?P%jlP6BhhBuSu z-uVdCbGRl-uK85+6(!Pbh!usc(Ji;>Tu|vHNO+C359Hg(PTzhEpuYY&hSeXs{&TSU z=Kz~T8UJ&c|F5+sW4{eL7iCuwGk!Ut9zbaDD9ORVxMgkk*MIGE>CT-g75z0+PQAaW z&->3$=O_c9(VzQ=A|C|`e|!K4E_9y%v7J|P86Ey25(a=|^a(}&2ql;UBp@E7pdbS? zkbfctm7xFUW)W-wB9RAU2AGcg7xBOij0WyV=KG+J7Z9T$`V;>9ib_;3;ZMt4)Y}Y@ zgj(T(1*nbr=$-WEoq}TXQ7jGRDNuY0$Uxum2tO_eN0{n8CtQT*G*n8DfgTD#b(0XJ zFE#Z~n{KGA1f}coivKf0eg0GwPeJcFe`?JE^Zw-i-Lk&49tVA?zr8bm_y1ql_WJJ! z2lj%Y$a69ewXOiEsFjreo2v7`tpBFx*$A5T2hU-E2Le$YeXsLD5W+!%Qn)BK2bD-h zD{s#|b{ORF8;J?)0 z!V!&4$HHxn{I_(>K{!{1>v7h57XSa}|9c9^7Z`x4^A!PaP}qmU%zr}Zf}yvn=AU0o z{0jw4j&c7L|3CSb8Ur%`-1(=opCsKrI66P{&ozM~=$gPv{WXDng#Uho^G(E4dcM++ zK_9G)Wi4)o_v%fX*Z_opjkqx&6Qw!0YXdGv4qK^rI}FF+VjM0hgDcBmPBvVnfUDc# znrygM0M|Cabqbh^!z~gVhi%#LsRD=ZbbK8@j^4)ydv0z_krm?`337&nhkIvxw~KI* zKqL_%qC$O2^&b*PKMxQ zh=2@{kRb{(B%2H=BSRXvwyLbeG4$ZUy0gT$a+B9lm-3W5zKj)?q*4RCQg zT!N-q$W!r32X+#GZ3+S^bHGPwH1a}I0UanRGX#zt5a@v>LO{;kS+$5Qg4C%_K*|A4d3`>m}A_~Gdo~f}wotcwn zq?M*>D)3}4e~CMnIXCdy`2xN~knNW3j(bWxvpw6r6=Yl)%xHiaZB=DnAk3Hyugr?X z-Ve zA6ADQN5wWL7=m6>kyxL2)TiX$S0DCHe5u=i8h0A zIF|;3366{gA~}GI$8qpD5^(Lg+$aztz*sYoq@Dsik@h$T=>nc8J`L#$``V0{h6T4h zvI=g;7u+6HaC>~g?L_MEs#x%rj=KBqk)LxD9pPXnPdyzy@BE zLX`@t)KH~`Dji%&z?DL{QVLfp;Ytoi8hV~-B9(PBQbsrIi#lJE zV+b%UT&aUO1k4e_YcJu-)xd3XF3ZfLp4GUWbzNe_*1{YeTt&d^ZZIbiP=UED^NxB} z^L`eC;c%`CWg-#J~ZgCAC?(&2Lm^0ht`;D z;hGSDqi#9?ZoJCeRu;gwu;5s#2r-jXxptrREt2agDbnu||s1k^h-#@#VK)j)_Yg~6= zv^m{Th9UZ{3u(w-&2HAhTq(>|!dw;1Rm0p6I(Vytc?8T8!aOO=Q^GtI%u~ZWEzHxw zd;;bRVZIdRD`CD0=Br`87Ut_<0Ranyus{k6l(0Yr3)HYc3k!5`JptDX;d&`tuY~JW zaJ?F?*TVHWxPgEhgm8ltZcxGvD!4%nH)!Do9V{eZp%4~IVWAQhs$iiS7HVOk4sImi zMj_lNg&UP{qY7?R!;MaFY;jlEO_&xJd;!so^Fq+@yn>3AkAZH%sAWCETon zo7Hf$7H-zTEd*>vD-CmHY!65D5mizV6a{Q%l^IDMA9llsP8lY1^_aHYg{QDY+wD-m z6PVM`z6CV-@BXMS?`{xqiwbU0!!264MF+PMaH|k*mBOt`xK#zWs^L~G+^U1y2)IoM zw@KkPCETWh+thHI7H-qQA_5i(VUZLTDPfTc7O7#878dDXF#(H(uviLcEb?^WI4+!A_DLkNr2UPHY8XnNX13Gw+fCq)}pcEcd!hw zgw;}5t%TJoSgnTDT3D@vhY5IC2oFo)VI@4Qf``@cuofQH!6O7bB7{ez@Q4x~QNbf> zcti`2=wJ;2YlN^y3Tu?GMg?osutp1Obnqwvj|$;YDLkr#M^*5s8XnccqdHhiz*-@! zmBLyjtX08UHLTUbS{*z_z+*yqObU-F;V~6FriRC~@R$xBC*W}*JT8UDmGHO<9#_NT zT6kOsPZ02g5T2026H0hO1y88q2`xOKgC_}iQV35<;YlStse&if@T3-=)WK5(JSBvu zr0|pyo>IY6YIsTuPw8MC0qcaYP73RkuucW*)UZwq>vXW5fb~LHFNO6=Sg(ThYFMv@ z^*VT(fTxAFNEi%@VpY9SHbgYcwP(7>tF)`8-%by3LBKLK?NJsut5tObnpTJF9_iUDZHSB z7gX?q8eY)C3p%JJpjHUAQm9oztqN+@P^*Pn9lS`ui$Zu&3NI?*MHRfLh8MN)q7F6^ zuu%vbrLa*68&$AT4I8zvQ3o#(@RAT-lEO<$cu55>so^CpyrhFo1Z)z*CMj%E!X_1L zQo|-KY|_EY1iUPSm!Ri5zEmh!aRCcrP zEx;{jH|p4~@zdCG;scndoP9;dK3Sd0mir#S$Z~eGj@@3G%Fah6<>l;~&)H;ZP@H%Y zJ+7R6SH~WlzdA>tJ`s1V^d7UnRG>* z(zFrydbP@pJ`6(2IUfL zj(3{z$=*5aLCHh{`}@UQDu*rJPghJ!VE?)}wSNXXSG<;W(X< z{{uv$oF?-Oc0Q_=9H)mGO#dzGgx$XZj)J+(`f@Yaj_GWl9cE>VGE55)inHc8j$hp1 z`-{yzlw*npkTf^JRJ_Tepanr!=t=Vtw9O)|2tjY?NvR0hVe!Eu!*o3Y;}Q}0&TQDJ zKba|$$B%mG+kso}`~xaBE1bknEroS|pyb&xi^Wfg*cTb58&J*U+0mv5Ts7~X%s1zYp|q5c0e@Zm`r4}iIG(#z-;-h(V18b|FvZU(D3WrgbQ5r z?M5YAioY^ug$EBLF&bLKB;YL(B@k}s-G-%vq+5zZh-oQ<{6=Ci^a}Sc?#E_FCR>Op zVp?>Bp9q`m+5qIkXf;?2=B9O*WM<5MBNi~?hdU-)<8mw-?IxN&ql9;{9MeG$@k^#R zJ$pdKZ*oTeH{!MUv>)y%PJ|^wjDzgF%+V8R71yN=Q>4je;v{^AsDHBA!+;nX?Jm&J z6PnD^3KsL<4ie*Q`KwJq4bRx;J-;kEa284n!#zbqX*6W`(q3NYt#%li{-=Td&Wp@s zSj-&f7@FHT&PTJkrEdqt;8bZcCtAE|l?>}H{Yj-t-*GVoe#8QGqLVs2%n-jDq`*Bx z>GBTo%840c-wl%CIb&;_6U48_n)vnMoikfTxsqO{fi@a*V56}Fj)?H~8b`qMIAtb_ zcc08!dBasU$Q5|gErEr`Qqza#?QD_CdQ@hkU}44-i|khmzTQn`z5QivtnsY{-X==c zs@-+g^cw)JN)n}9+m^c$WF$c68paqGZGM^HCX$Rb z$cnz%XE$%E)FLCifAH_+i_6$D}jw9|ti%gu9!#lpnz!D+a7a=O5 zwIr_yOis2FH!$MuR&mYqF)t?qR2aGOyb6xY9q~!rjPbcH{Yr7(&Dh9wUc2z~(=$>( ziK7h?Ec!(oG+KBarD69|zg}1z|5=}C)6aouwBgcv6Az>GNmT}Mb|}doHLIY%i*Mhv zIR1-RL8lpCD(2!SgI71rr#BHU(2?Q z+NY-W`6}+z7ptK)S`4YB6|YTQ_Z1De-%X|_h)cW89L7fpVlG<|gOdL4Rktqi72i5M z_Z#Y$-^5|t^5y;>Lo9x64d9(~&;9P2?qMjoqcPW5ppPYG#tYj_3Dy?TEGSEjH9u!N zPcWHgMw(CwZu=6V++C-c^|&Q_;rXN$umYAmWie&%(dGfzQM1Q_UTjhl;bMk-&N#<_ zOXL$ALW3tGfoxRDE1iMdt>ekKWEyfDn_3E&Q-y3 z^~bdn3c`HW(VXYFEu9fH+f8%BSAzBXNjJ!J!&A^v5OU=BDKXlx#U3{Yf28Zi(q&0e z13%J@ZbuCTj%+dytIXN9bqsjrPpz3I!995c=f)w;B*x!`x!W)kG+^|<{ZsWJnOGvI z{Jo6Rk+YcHHXLABBSue27&MQw{&Old~SwK0;}2-JCZkP+Db<%>+^Cdci%E*8YL>binW_>**b<=Kgh-)6bV@ z2sB$bj4Y&@CW7a0(m5FJk)woJ`2z$})7?YRs>wAeyl%orQYtO~or<3`*vESqlldl7e>AywZIZB9dHx}dq)aG`mv`8>nZ9ubBj z@o8*LB4rro5@GmIB)|X**cF04CM0#ba)oZ?d828=k9_3tZjFoyadc`Qt0rIqyd67$S2wmYc?YW6~)lQ zux5VD0p8{VokUw0u94xaHbTxIJ(wQLmQmH_yvYGsnsMG{ZJ0djjrXFFW*%-643}j^ z1{yhX(RM;20v*TpW3R$)&KY}RiN|PGj%jop=FG&}`8RUfdA+I;$DB=SPhc7LA6^3d zO~@S6j^(~`Fj*RyICk@Ng(wLZ7+PKu&Blf5iN=ZHZG|KAFr!s_c`cU3MPDhr2&0v; zC$7AL8K>t9?%Lu3=;S#BNca~{;qvXc8+yQ;Yl)#bTPBFj+IWZSz|Ffp3qx%S5)D^1 zWRID&@rr00m{XHzxZ2{9;ayP?sNDE!j5N0=k&FTeh71u+#qEqS9zKy5!a0Hd=Qa81JiDUX7WC99OX=&YzIHxiedu0sMP%+ibIA}u5^s{UtD=hOzRxEJ}cquUOMkq zk>=YCGq7RmFJI-u>$FP2%f3v^g+>9oT&bL%&CfLRwg6b$k|dmlzQ_A1X`mn-Nm7@Z zzH8~*Fq$NV#-d3GwYrSW2xy`>q%A-F3Sc=>c4TkL&Dn@)kyKI~ ziA&uT0xvaUj-Y5qMRLM0Hjy$Da0n63A@sft4O$TS=v?WwXd9HVjP(2x-Q#*Q&Xe6BI5>&Rk%J;G3NxL;-4roMNNdAMUUV}Jd1yo?Q-#&N`YA0m9+Uj>Y!;6E zGaN=7gJ*U_Br)bqY60PV4kLf5zn_(33L8UMWk-e~{{a_CBT@>u%<1k^HuWFN%)>@m zNhpa0LP~^FI9J9+-BsbqlU*5dncXBtXP9-T1fMRLm28t@BD2OK<&4sB4+@XV9AOY+ zxgRLaNO@&`>Jqa>7-;vOOQWx40J>?=_cB{gNo0@nEB{h-YX}E8gGm(>8#8foWwgS> zincoFv!JmT%_Qh+R)frp5l4{#5KKIQW)xzMXK_=qTna;iIig@3@U9Hb#DKB2-1Pm2 zo*&Bvlsn~0SyOhvo{Aa-FL?3co%1DsU(Gz@-2BM=+LQ```HVpt~)VA<{z zI_;ob(KQY(<&M6iLYIqje@0dSx}_z!OkSVo^D9xrr3;Ge7KUHq_ZIBfAm-9z;{3Su zTw{!uHT8VwR7&hGCh!$V|D=RebbrIXlo0SJZ_1X68ZRDA_g|miKfkbH`l1PbrkZ$~ zN&ggr%a_yr3yFTC>2_eiXnMk+{QmtK4F!9IAFrS1dpHmC&7S1hXvmG0(+8@FQInSS z|Lr0AY8amYxPA8!pn=(^FNbUY&Cnj@I*mkiWo)jGobE+gQ2n_U`t&jan7BVT27KgB zR1ofvkYqvVjjoxB^a#{%cr@`HxlLbj<=DP+Ayj${vgVTl9DzUa#uPEw(xib{>?lG zKj0dM1gHMeNfHQX0k&jsvX{n@bOgo-zXazT=WKq1YndCjYsW;|ExHjEDj=k&1v6W$ zD#Q_M#@JHQgs#7}H4gAXUK%GLhn@o6kkQ}%3b>((HaB*lY)MDb4w&J|M5loq%^a{W zK$4D;OynDo1X^7LiJWK1iH7$@SR)&-3@Ivw*p^@{dn8$i8Ae+Jy8iN$@}G+}(*WAD z?NN&o#3sEcP9&G$eI3R?!V-50!3t$Av?|oZl@JIF%g~-^q`#{5_qDTRgk~zz=g`l< zZ}8GPF_M$b$p+WQz7t85ci}v1CY{;5`jW2J%6?dlB>7Tq7vQIi}Ea-M=-8VAZkV@KIhoaf1wJjFYnECBB$;*eUJ zEb*qJeGzw*&FRyOt(ABKtQ})SBM;(eG^h+9iPYEwTe%e*(S`{ekv)$*vM6RZ3D-c8 zhBK|g8Y4*D*-fM6i9@hrjO8my5itoOP^K|M<;=m6DSfit#8u&%?b$G}Y@lZBhXDCrEJA=dc5k1d zo~D>VAVMn;(OZ9!Y$gaONRr~Vh=RRj#(*Q>(mA*x6EtG9K;ZkQ zfLxB0Q;hBb6jnTuCml86Q;J7g(~T=I95l~2=nu#U1NJh5WIW!WE!n_m5C||rQp=b` z%>_;};RN^MM42`OJK%F|dyfm;H%1r`VH2rLcU z6<8Li4y*{Q4BQ`hFt94{aF9G`bWmi_*r4d32|+PIlY`=drUWU1ltBqWi9tz0v(ugr z0^vr~1hla8Ik?woGtAJdE}o4}7O_$lSH(G6(3?eVS;h}FpBOw#F8TbhGbA#AG@L>al3#7e z!;b`<4AAt_Y?#LriO6CiQVuO$g1%Z}OhNP%-r7*c7{ro6AkaM821DwGRT#R1V8`GT zHZzduWSCdrm405oWZ0cUaqQW^1taCo(JbQ-*Crk~ z031Lc46KchkH#7Ds7|~MtxVpJ2$NDHvo+sys0I5?fhR_;jo&F}5+PFW4jw4?>&rW3 zms_34bGg;uO?XR&nWy8t$Pp%f=nP#(PoQO|bV^d!MoZkr+T|XZ(pz53TZ#NF71nh7pa+?!dbhn90WRBN{YNH9%qbzFbo5Xv6na4Iw^n-1n#S|6SjM(dF9_pC5u z@NGJ+Z3+A!Ceh>`)}KG43ya1e6BwcY(*Ros19UC{fKW!`_Z@+Kn@Iki0;Zz-D}xL{ zr;Auzj2$;Z7o8$OG>DADd5e891uYPnpKC3>t^lv0KxWjB3D5#Umsa_o>;V7G>`WT| z&;7@>te63l`fm&CUo69ET+|6ehKxjV!jb|0b8?*l;2nxms?c8-0H-PYS#Qh+#DFU{ zNi1)3(CNR!-OROaN=ztV>u2mrYKia{~IBy5!hP;7}=;Ie;6x|@$B*uMBT7n8B@ zJ|y6D;1pjl6r<7p!V=CAzqDEXN7JZVA(2jih@+ey|Ni}nw`oY~n`;Z3z)%SHmp*LB zn%djeC}a)>z_PU%J%(Cd`l)O_eKP4EV}aj!AQx83ZPAc5fRslkVZdKUKs@=t>n%=uj;WO; zC*NViZB6EATa*S7UJlOfv^!b(x!)~gHBMLDlGzFe_M-6)mPZ@seC>T3GCWg@T9^7F zypvf(VV^<38z=MF2|PyF{#>I8uLssmHKb?V$;>eHbCiqXj+%{mvr_Rxzzj0@=I`wI z(d`V?o1bIP54Ma;K=-Kttuyz5QJilYQJwRqpX+i)u8ZxixxOZCzVc}?$o;?|UN8q{ zT`>B+jBWES)z$=+v?Y!wvkkCHnwDSHu*2ATnQwg>#ftMS9L{E(sWrIqHKNjB&fBby zODt!qtasE2THxk=-d#U*wFcdQ}*U zM35%WGTwiwpdnB`G8QmJqG*f-FbP1vg9F*cz>cz)0eUZ_!P=sWNWg~THgb(fBg&9# zs4*g$K1^CZt&|o*OQz-1liAGoJ44=AOC%A`%QI~ie9v{5@ef;eK_7}o2`FbufYeD2 za7WG{r23#mYE21I+#bbkQOmuMD~xUntN{nOP;Nj7Y*0*yDtaVg#?A>Ovy_E`A(t6v z2xRDdzSK~|Y-3K~(A;RvD1^{#X=Jc_g$!VRc&FoLkCD$pQalu6J!a{jTXydv{)7>x%{K%+Y?3*^3+Z|4$sFm5`f8Z(tuZHZd#c`xxEet z?gpCNpHD_pU}dsG?Zp8G1}na$?#2svqs#;klMm3X&`!wsk-eZ}7uU~ZE`NK1zV#)Q zJS1JLllciDY`LI{&Y_{D_oO3-8sg>7M}OR4`XmHhPF^WE$N>O-8DwSJ^4cu$Jkv-C zxL=>b!Z^+$_6@A-lTk@H06 z&RheO(GY9~_{&he>^OSPY96qHl&Cz8j1$o&-fmSfF3|{Pu^G-QsBz`Nh6C6u2!rvPdX&zTAK`by=yB^QpE2HfvY=sV4PAQwX@zC zX~LLxd#$--mEX7}+YMG^Vo4=*D}+$t)>L6h&e+kMS3LdzpS+Uvu~-eVb>Te?-YEe@ z8?vXlxP~t0iG6^>)2C=W>)b&foXWG=8G;1iN@?!sO>a9?AA$1ayo#4wpVJ1vmt3^# zAviu}ku1a7q8Wf>fjpZhA$>Nno=Q_#^jC(@Y2~HSRTv%Yq{U!J)X+Jc|BJTw4r?lF z_dxf~-YFyjLP()XNU}qKgc5oW7%(CVB3KX<6ci97K!Sh?1`G&D6A?iXQL$sg-qAs2 z6h}nIHtJY##IC3_Ht??Cc)oMK``kY-Je!Br>swZNe~Uq=Gv%iiLg?P%sBm20)r-;h zPRQ?TsrQUxt!@3=63;*c0vv*mCP=|`ekr7cL;T|>(2fZGQ%l2x^ zPjFKpB&lA|KPH!JaqJu~vxkXZh9L$1&%!&l4B(^{Bby!-;=8=2QW#Hiyv%JAz2;Hc z=Pjdj;sKE<5Nf2|I{TQayIQ$P&~^1x={v9FVy~n*UI+dGx$71pRSsO})I<7EM|fs8|;+^H=)1Rszy9W?E;iibFZXC2DNb3^+iCXYeHIf)=*lD@o+ zTgoV(A1(#pV9nkG1j9C?^lJ?<_qF*RB{b_Gf6n}cC@e`?IH%ZaUXs^#>@st-;EP~l z{hjT#0hG)EC4;GTs3FJ85@ZMwCYdcDargFIhfW{gqG206!&&b>dp0kjOuiX;3?fdr z2qJDs^qNmO)zh6vDR@@w1^G-T8(#gzFbSP@+f2*;M|oxAn}BRDsABWHVy^{ryyV5I zg&RP7tJArV)Sj=h+@M^DsK6EnLaV+*<0>tY$0g-j>y3Q;pUfeew#T|o-SaX6(DM`tc`P`512E_-Vk}}GKqdE$|iq^oLPZ?GhR@Ni|^Z(Y6Cso-h#q(JML!<_VPS*NHcU8sW_Ld{hhDhwsJ}&d% zTQjT3y`pg}_NQi--Oz7CDqHRa(W4xam_cmy%H6D^bU!8mC!z3r8?YJntroSF#d+1M zD7^D=&xSk?LDQG%DCflQyfR@LmL%p~5rYb4iNVk`@XNBp7FDr&#Stl^12CV0;=;=) z-*qYqzgO%8%=&P1$|!d&z#u8iGqQmPipwdZyaIb{-^6xSEHq$AA6PGBlto?b0E1Jm z&)AWG!--Ge30zSqxL+)-#rQq8hKnDC`_2FA|G18|boSYz{1t*p6{rC=s6#(kyTdv} z%`4BMTe*`5ak~5m8#(RZ*!sqJC7rLfFnj8pq5^fEA${44nRZXVepDH_hefQix=a~~+;|^bQkr7~92HcYzK;PlD7qlk0 z(PRHA)gpmzvf+FC9G=}mUZ2b3U2k5R_t!bI@YYtcaAFtSgS>^Cz$Vb4MNfXwVO8TA zFq9TiB%vP%kL8%sYTGsPVMyC5x3vEy%6qL0`x9!RD5l>KhRmD^PJoU$h_O|4xpbr;btRhCZSi|<3EM5G}iIS#CvwCo+ zO;0PQs7-aV3?G%!!m3_9?lTp7G`GIHPUE##!EQnOKQ~N(eMs>2n2>lsJah!U3tHSD z^Pkn-hn!*#AJNzgbxG83BU5v>o_bto^Yz%!fW&m2y{vr%24(|$Wl&5^L=^YzS?IuT z_I9UK<-)4S*QUOvExS&c+V)M1+#5eXq6rM%BUGgk`-lLvD|jdzt1kZeJDL7D|btdxs*>Ak+qR zg+hHYD%uVX&L2SCp}20|#6P+w;toU-wBVrJ;{gZP0mU1E!4g`m-_YkYR~=ZjxTO}A z81v&r`H{K{_G6ecQX}lRM@!per?}(Kwq!|BxF@usou?|<8yVsa{$IMy?tWo(c)=n1zgAl*T%(VbA#wSzk50Izcrhiyn5l&gmxQqq$l9 zWf)AZx=eL%#tG+SG=j)YptVh$x0lB>J0d$D(-U7Zdr~i?%_Qv2Fy_jagC6QjMy9Rm zf%b+{d*jVA=(G2zd=d0Lpki(qmGtax|}XsW9}9KR&yOAJkI8;hhaJKqi;kUZUn^u{-U2yz_FVvxBap#TgF?26{T6h$!a~ocPu(8hk584ud`7 zCYe)A9QPipFLTln9(K5GB8L|GxE)i}#JUWII-X*3rF0TUP*FGSNpynJz}d;*!z=oS zl1aPaBN_Hwe_Cwg-sBxI#SHEA_)`hq{rZ60E=Blbq-3-K)25v)nN6aSi!Eu=XC{6X! z!xIsq-T5(pOZlt#jyfInLUYaTuF|=E^w4r&r{(-iG+8+RHF>V*&44J-JD zR*t$<%7b-#vn~X*(@0OOC$+wH-93g!i%r@wf}01TVoZ3pNMH)v;MHSJgaibyg4x+i2XX|0?v=c_pb zKK1=g+ks{SoYCWfireAejE79PA0r-NA49~wfr5WRfYu=J_rz{^KCaXRiq(OF4MsiHXXlY`c=EU}J>;zQ6FK0hBfWp3L9+o;4yE_A^A#e3UQls z$Y1nwZBA*n+*R=$GECu=hZ_p(<+QjI+gr5TQ#aSlUR^1H>uOyZWKh)CDl)&1j?PJL zQXedrRFUz2bVi-v)E?v3kehx3Vbkdls$7;hHYu%MZW?YY=hC&3^l#c`xGdx*i6!FIn7?w3QUy@hp?;&O8IEB3vqUH z*9iVP8$w82653h^HP*12y+IxuAVT)e^zi7lU_!^82|dP(4k1{$jcGS+Y$>B?Ej8Y5 z>KPDH6kTTYemY%fI>yRr`S`f?SMBili>=<-ai9qxSaD2@W#s1w88?Ux8$XIFn_gzaaw#`f_<{(~$gYakKgTo0Z*k6FAma%I#vbZGe2C=XYWHw^Mw>Gp zzwgecF{J^u9z!^&tK3)}h=%q45`U-C=ZJB@YvkfTdankXj=&E#&O^BLlX@p!PW!dG zk356X5g70lx>zmd1t)r{10hP%6X^&oXGmz;dtt)egAL`kjuz)tI-oC-w?@x6sX03r zbJcj}`$2?x+zf}+@9TwljkN#XE?);ibdfImz@4hvoqbwoHCyQgQ8K&Fb-UJg3TggJ zAI#iq8W7aEp}pRL7)bdrZLhACYFeP#<(cnJo%`L!EKZUmWVYyB6TGL0?wc$>pdovy zovDg*%7;^P=t4$&YZ84&9SF%StRgF&2r0+6F)CK{3n85Wif2go6$YFOIw^9t@)%3r&yxi9>#4kJKLn3`~KwtaP5G03%r3?Zg3bD}p)or#PuU9Y&~fL`=S z+28X|9(Cp(725}ti=>5@&d37gGGcOvS93NuUg7;A>~G`}RQlYJtiR(+R!oi{BL@}@ zD!+UjA;*s$Ep^F;YcKk*g4*i);fM>vPEt|CIeAni26^gBWh!$6r?0PzYPl$QaMl4| z@2DapyYi?8?kcvgQjE*^Jf|pD#wIiGF0v2IzIR<{bV$CVQT!V=TBy4H^Mys(GA_s) z)e6mDEeOys?MhdIgrFB#{)+4FGU5D#9YfH@R;Kg(DyC9Qof~zPcH~{Zkkyj2gv-hw zAqRwN$bM=^LfW_T8DFyh?Vg4Fo>|wg6mh!I73LCjavvq7T*k=M85py41cjfIH(%Y@OkcbeKnjFma1-A9z zqvV4RxbH7+&z-&G$VJFgXcAGlY2*E-ANe?-o>sk>@zNg*TG=Ze-R%#s9nCkrW_e)> zDB0wIdTQOa7cLEfq+2--c)q#1aS?M7l{;Xkq9!F%A$)dR$fhk3^J1V`Z0wd`qdXi>AE{Rf`fc3?Bi`YK0+=Cs$zmsULdVm^9JOlA2R$)V{QDtUyC>vfS7}Ta3GTz z$?C->*+y{A1-reztsF{(z7T5m%g}Du6+=QscYezEu~SFLJB?LYAOSUL^|0X-lOZ9K zUr2goy+u#1cc?CX<}>rSON(`EKo+UI19=TAWK;v);^TMTy!}O*vLIE`$R*uSv87-( zE_}1#^bL>!E23chp@wq(nD>x=-=}?%$0l$<6txqQBDfRn;|g-qe1bvMQa>JEWHTAewNci{(pRk= z=iV4?oX7RNsm~cgvWgdWNr&h{T1)Bj@g<4ELe%003dJOTsV(L-V%J`E?)wn8?B-zz^tYIrp=&t)1f(2GDeJ~di_OwZLP6d(;lse8Nf%)$}! zw7Z(MATuQQY$SePzkW!{))ub>kx{(ITYv6_MR{E38WlTK&j|__5+=QmrVE+wB{f;| zptsAd@7_GSifdI`2ZHhGJ|~+P{d8di=qKLmmkrJa8m1capn)*ZhJU5l4}GhZd3r|` zM=w`?N-X%(;ntR+90$~4t*v3CPZ4wJVhuTLwDmv4R))^Q|G|#*)(gfZs<9y8zoga) zeTB_dUoARxNyR={9T%q0{HM?&hJN`&G$+mhB{UQdjY%jXvzF?(qqX`Ww8+Gs0)<8N zD(-shpvAcVCADz+F&|nXNvU{M#9TF9B?f{2ky`P;HrnmK7~+8bR#|#vhDjdvRfUQR zV*Iz%+Q@pCJl5A?6<3CxGy~!PCANk}$YV-0OgSj`r`*z)i$Af<-SO;(B97tjDlrKB zkKAhL+e0oJvT#6ml>W8O!7Yzkc2-aJ5Hc#auJ#Lm$gMxuD7RhQL2nd$^^OX~A9AZ- zNYrQEsI4ekd9H|gy-Y(MKiU;PB$osBC6X0~Yh~VO(NK{w5n+IoGNBP6z3xjgXa*s;o()4j`yd{M+QVxxo+f zxDN|e>`6+ozRE+z)x>3I>KnP+^7V5DB&*an`~KXh>)Ufx>_)7VJ887t=)JMgn=chq zHnL3yRqV*o28s2V_RjuzzyzPiZk?ua(tCqUm)z)+v0V!r*>O()HvIz7FrMmZ3={ww zi{iJBTXdZh@i=N?& zB7|t{n!2&y$$JqhZBoJ40(C&!75#iyx z4!#;zu`g7oOaa+}n7BhX55KdwYv-&}03QKrr{|Lb8FkHRTMcul^cL_OtqT%0y5zJI z+uXrQ-SkoMcBA^de@l`0=~gw9;8X28dVL{A?FiY*6aW0+SuleEeVch@Qh^T>NAN#! zvfB|q|GQ}SkvKVXf=+xbXRC#!1Ptx?t19g?3k;weJ75e~p79*<}3GY)x#w4|UU*8g0uXv45{ z&&V{;iT_#TnEH3PWPPy|%@Oah&6dO}Bt{X7#>zgfa zKEy&lZta?vNam;tB0+5GxO?++Zaqt5z^_*$&cB}Pi9}+tz_8QL)C$PYJR2br`n}G!nwIZoI7ve6@%h!90qu;w;C$(vdD~=# z6>-_Xw;xMg+)^vAOU}F%fzy=UP3QS-e8UL}^@CT_OMD56=Cej?~#@SZ6jqec!Lla!oVY6WP`qwSPI z%dS5XdeeSv%d8_8W2JU8@?8juk>3ehqlbWKVxZ{h{ru%GcNMxA`e>LS5Qr0#j3Y5O z7sb~*cXp0k1iFKRp8k-eDGB#dIwKqr>Z$FwH#3IGe_^Gf89>YwZk^X4lMMY3Ggl8N z%}T9{IF@x9X56T7qD35K7=Fs`o5NTX*5ArW;g9B)9Od=!j6~jx3>;3QIoXepC+<)k z2MMT}cIlDR^ZUvx5RW*-Ol?|5m`hB$}azase5rPt6`yI z4UjT(I#ukMK!hqH`U(CZ1J;SX!l{RY^vvqxan8B@pYz!BBS93P4<~MjyZdDb9_N^x!Y9Sc)hBup(g;1tY65uesINxl}FEvw{I7GY2>~PSFz)P8j=@Q z2GlwIqGkBBjcg=N zA4p9!o(5D&ux-$BDC(BgO?~p9o{TCJER3$+uVN>todjBQpt~N}^*p4w(W_%@24a_NNGHeTN3 zu$~_a0M&t|K)3D(iu(SulFyUg&S|TTbZF$p&rz{wf$q*CT>|<#2#!B|bE05GKVB(U z47UcZP_bW@ew(H5mn8}AYoFwRMenymqS-MWo-JR3SNpDF-!44{f|2}vn&?eKpb9Mq ziqlGx^;KBZmwMsG_dNDyca0N>g3G~gF(?83;@^3H@ivo9HZzC+3~6LH{G}n!1{o|U zc->dH8FOs z>vVN`IR4}@yXaPqqp*qt=HJ4+p6(OTokCW}o*ljO?vIeeUTfkK!Tc}U>AJSC6Vx+e zH}QT|jlQ01ZyG$r-^=4heNwT(r6vf;w<(nPNIHegmmTqg$7FUoc3xH-Si67Ii#|ej z?#5^A&dyOnLHFF5k<~Im!(4}@B!IMfm0h=E-7hasc zLGJ_$(_L0}VkOv=|IyZ6dx%bEdg_(ZaetvB0s22a^QSA1)tMk|c=Tz)DU`ot4H!6Gsh(j{VVyUOT z-VmDqy1s4%@C(>B?%r$XFB0_1@&QHwHZ`(qi;4@<=yheb`!V_Xt8eO}K8y`?kl-Iy z2fJTelE)=))+=@pIgMvI?gZdetc+7%{t(Ba*Cu{1lSt6_b#=ciz;;hgtW;6wg0Wou zBIDqUP8k(~sVAqsnt~f^xM%-wArh3h>cJ!&c4Nv1D;-to0+Cg-4@Q^)p2O@onqsUy zTPSIU zQ1Ra@?=WlZF5Aw;R+%M(vHhQ2&iIqOjC!EFqCno?`laT59=qI8Rr>ddajY*uQ%36@XNLfqwMCYsM(lmObVd=BO)@r;quSbqZ0t-7 z9_1TFQ^y{4hz9B1Pd?vd31%XS-?iuZa7<7h+x#g%AT0&-Cn0;p@1HBVV1&niF*$!N zT|!tgwfjm|dnH!yz#rm2vzw=MMkDhKp9PJ#7jm2g3Rk;$5mGo|(jShwf>|QB<^sEU z`4Whnb!zFzT8TtRi*NX2y8Mrl=aF^?d?onJNW+jjmX^_6Q94iIj>}%O=;4{o)IV2r zPq8nwxNxT+3;05)I@!1`w_YMN&1l^5+vBnSZr)>s_J_6nbx!m1-I}tf><{IGg+*2W zZU+;m@;2iHtxgqM4ST-Yg*}^jvvfUWAzJ> z<693#v%feHz%rY}do8d~SZvTyszea_?=Jp-Z9g}93s&f_$e>Hg7cd#I^_~Kpw-B5O ziP7x0bjNDd5b#L-C%Z09Ery5_PH)+G$ae}*ifY*rz<@m{DQl~CsPhegwW4z+8-N} zId|9hSCRF40TuN=a;onu*Wu?DFL~Hdb17Xfae5OrBQsqoZkUTQ zne!i6oE!U5oP)vR7J&JlobroPYD2V;y_uVm_j2tpnN`{e{9D$dwCXFQEB0T9$-S6~ zv)+r0nkJ~#Xys$ww$e#=u-i))d~-CQhjuP}6`xS>a#~)R9hu9|NZTY| z*MKg3MzA-{NiyG6^GS5d$L3@9wv2qIW*JicCrX^Pn0ar_)cVtFKNif-P&h+KZtros z6TgG`CiYB--Eh1TaUHsAAvnYop;cssGoe5=wd~~qF!nkF1b@_3xs*|v^PQ+6XDXaq zm=t_JHCixmR)QWTJks_GQcA#bRw=4`ppB{$7e_f^C&Mzq8P&h%AA3Uu2O`ALsP25Z zS-w+C5z4&mgvOXJ{%rOV+hwv4c;$NF@b;$)W-bI3!{_APuw8o-aQ5MurU9qGQN^Pd z34<*qYi71h*VryBa8_g2O*Knq4%!#k=BIHs+((;+y*jYqr3&>7YEB-w+u#o< z(PsYQCClfIFrzIr9EEZQRr6{@+@t|hvzD=Le==o+S>vbjQm7sA$|-qO32WJt8!9(U#Nruh9o6$eYttrY8Nm0M`vpv65%qDyZ zx2vt9=DW4pJ8FqGZcw1VH@R~}Szqw_ugcUEq`OA}^yAWG0} zBQBvc<`x(#Ke*w4p8d5g5#ND=_7-F-m9EsnqcN<8<{Tk$`L0D9bI49Ujytd!bCf_H zB5KtzebXnPT!)Q4okWiQap)-6CCtN~m4FC!4B$ZXk03q{vpYQn#*1I* z*(H*3cglqRKfNZI41kkWWt^aw;em;=F+6<{&{PVx%ik1@0f7(tHLOJ-c1nu(-Gi-O z(KIx);3e>&W`a5M@>IQ$arl}SqyK%?QSlmMT9_txkI-IlpPWehFK{vZS6akwD9AJD_LSH3Q*#h4N3TkrG zK4_i&HqmCsG1%30`CyYe2W3yY_?Kz;{*SgknC-fP4uPs z1Y=e1;xMx4~4#eE)9&^itf}^Z&Sb}wdTGTKDkNv8zSbFzaOC*bCcnbA3kyMgnEaDtl5{@RWFu^% zs1R`L=^yH=$vWUz3>3lzHXBaGc7x;hXLc<LixJ5)S&p36?7_GlEk1=yd#(4U=FyBo_jS|drrSLSB41t_F1Ud` zNmqk_Nbj78!~Ns=xbG#~@^%LcqCAP;jNjWh`kb$u%@GHt?#s?DQs=ugTOit2@fy+fx0VRRS_P!X->4#_4Un*FZ;GI2OkFkPdR;PX@B{J%V^ zmp4cpH`0hU8dr_l70r5m>%>8AX9rpQJF*{}VOgYhZ3sY2Qy)kL$HjE-$s75Sx2^*Pp$PH@tR$#@zQ=1p|o48=0vZ2@#XG1X+eutDKeZC7Xt($(u09rRJ8q?UQ2`yIpwww~k?HsqW?_uYt`BhI>&&LUsNn1vc<31}4)rIiQD zx0-wwePOKzXV7{@0X6jwRN3`gi^5rnt5hpXz?@Ti+_C%}+~rY<@JsG0JIr|Cs`4F6 z@n?sz3WdSfoGyv36_`*F20h z(=c^v#|AIW>44#ht=PFuzy&7y@ATl2xxL#}6M^&RtrO2#?7CQ;Es!P!+oyGuJdN&{ z?l^Vji+?oaQhlxz95r`@bRV3A^grBs;u%(x7V58;=8MP-K0K*?f5DAknK&%NfFjzexx%ka z1Y2bMS5YA7rh9ybys(so%hVo}!g!D zi^a|;&zCs$kR0pRlhswsdUl0^-6%wzqu2gsfBI(E#-HwRKL}zf*g^ob{`ZtMsOrJmu4Cm2kP*4V|Ffd~m}1iUUHAed8u$ zx5q43cnbQMC>wr^2JJnErACzQnSyP;t_y*S_g!rdXERm9=L>0R#iXpPyqyVuS(PzWZ1A2vEQO0i-1rDKLed+qtS>>Tb& z{ALefT^nkmzoMqtQ^f+BApYl7EUT)D1th+O?*)UR%DBJtne#5k%PUR zi8l^go&FTbi7OKqk5NU|D#hQ(s3GEdWy-1Nt>jtuD%)DVSz<$^AryP}MqwB(j`(G@ z&F2rtS}<}RYg&P-RxN_y@qY7q*kkM&P}j)v01hn15sq^U;tH`AOXp*zEe3jt;qnQ> zlr!y)n4*hhes2R$Lm1b5W}(KR?wbd}CaE{6mcyvWfAnNv)d6)%Pl0?QVa)UPN%!|y zRT4)c=Ki6H0KN@{ElUx+`#WBlE9Ug@#udPMT^m>}HVD4!-qp=_FwZkqT8(H0;G$9U z8aCsJV>vFoDHoe+rt#R>a^P(knps-=0xIC+%A|>5TUWVuO!K%+_r@mwvjT{r!zfeh zd+6+icb^aCW2-EI*D(3R(4qGp1z0_(|8!hc<~9tugInz$`$a%5OxVD`Y+J8BN6pzZ zF?KyATF$25?#uq{>^{#9eXN;S&rhYJX{$|-KS`$x-Tg%A+S4jvM*%z+kF4o0s*LvB zEiFU~+*a}xbR?Yld5X(N$=fg#d4Fphajca%o9=)G@K)x#)mb7YaV06;uf-*EzDE^Q zuKeVRus1e--*Uj|WWf`#Xmf4yciRn3bISH_$ag`0+${9o7o^rvK8QT_1^t1kW;K9? z%>3XB11Bcr$W0u6yIq98CK};!mKWsdLl&oeV#{_)OcD!c>&QHuK?UTdz_&p<`cp%zlBNAyim<{2jQUS(U#k-vv-}TZgEOS29a8 zVU^OOVVZ}ppUqi-c&Bzleq37g^6~o(`?aG{=}s*wTjpL-2`*3;ZYF=*zD`u=ZSz(Y z-UtSB4bY`OV7L1l)gt`-JL#uykUQn7+VvuH=@RUj-)g;P=eEXw{&Edx_2?3uS+f1_ zHGAHPDt++;y*lQ%5Eqn`5k5kIn&!9h(edskVI$&1PlK9oHC(c zTQfh=ESY3rSTBbko_O>9`{#5*HdT%>EFkKo!BN}z+Kowi+k5%Arf^{R_QG9nPhWe< zHXb#a%%}q}0vv>fND&CBLpbAr#YN&cxDIiV0aH5&;lOPcCZB*|WABz2@WxJ8?U@bH zMoYT#EjGh0N}ORg1Tm6PfIZbVYFV2Y4^#A3p`;MusB!IdV6sPT!wI3ihNN#MvS69) zQ&BPNAqP!_$sBSUsmnlX@BpMujAD^VxNSHWTp}(Xn8Y?+W4-$=B1LpI)i+=o563_2j7nJQv!Q`E#3gqHbESrltiZC6szsbJc8B69Gn%N0F{`7 zq`t%$@jt$Kw$hS=$nc2XLK!kk5nIII2$0#o#>ug3x@MaJ!{p@V zA7;txV^eNSP@8e^@p!71v%L)%5qAO0VPFQF8o5(}^OhCwo>9A6vlCRR1y6)+$&kSV za-snz(H?mKVuonP?P$Wu=Q`x$6#LIkvWQG_=;k1Uc%*nEuwXWmIvVQjfjOZ6f7%0w zi0X)&9IL)WsTun2fnm;|b}Svl3Vp2&x#`OZHf;-L%ENCq5j)NhioY}Tun4u?$uZ<~ zfP(Ng6rdoybMbcPpE0?VYzb_0A5?Oob1y>pT_%YX^5ilX0O!&LP@OpVw{}NO>Ua>H zf6;`4B*3&#qBepk^E3ieZ4EF~1P}*qz-IJZ(oDKiz&~I@GOm+=kUEoXU!Ro{H=H9< z8i+(Q%pURtcDm;t#t?aZ41n3@ZMKkxyO;`-2?z^ZneM)>t(I<|m`6EpdyWFG_3+A> zFs$t^E~CW!rlVwF)huB(>$g%vdMoR$A@Hy-rFb_ORI|>tvUYcuPf1<_TJR{H3uVj~ zrcD{lK&ep&XgDeAf+PaS3>u<{fDBy_1)|VfkA6K?u5B*1Pn!5N$xgb^9N&-%!w6h~ zOTs}>lhLooK-E7)>L`hJ9tWVP*m;zxWd)L_U9kY#z~*~2^fe@(1>hn;o$pPKeO)nr zFe!G)6oc=tNDT%hfdinz1LYdlUB z(YiwJ0GPrHas#&EKG1*Jy8~l;{%3j|w}*-RuyyC&ViFaA)WP(H-!d1B*;EsnHl`yn zK+HubdvA>aQ*JxNJ!x&gU0?-_-MQ`7u-@2xsjIBpM@3I@H9-s?xEjEm|5&;&GxiOL zP#Q}@@J5Pzwh*t+6t;jk+dcYB=X%Q96bgo9BHsA1C$wx+aulHm^3nPNisd-X!;`hK zu?Ae2$Kh@=2iEJEQ-Jk4uhSe-C&{~JMUtGagyI3_KyqIY>K_HI0O3BnBZk*b-+xW0p;QR3ZwK&}6blG6EpL zvt58`Q6KmP&(Q#6$u?j~cSH|H3$Pj=0CzX|Jpy%QAclNk^9StjK)hb49Tz-BK9&H) z41iAZ)@~~wiw=UO7@k$k%7{vK>LnyEOXPdfAROqv9XmYQabiP)wj&WTu?L1 zbODg9E_Rn?mwA^}mmYK#Kz?aWpj3W2Uwg6od4?z4PLM`%kQ_uvc2^;iaeo0J$qCFx z4C}m%rUB$GC9vlY{Ru`Ecogwi?5B*nx4>_JAkEB?bC@Ec%uhOp6C@;3h~of4l?pJX zB=9#muoSUg5Ol%^Ew97=TMA1k?Ca zZ)khG)kZMr*%CLMQK$;?rBd(=&@%*WADv@eVnmTdbVT{$Mq$iS7G=uz1{^gKFIu#t z`$3{FXh(1qN3sF2;PdHGz8qVOZor|fK(HL2k_u%mX@C%1$JGxR8TAVy(s9LNeA8<)fJiYIbJM-T&od=>kL0#6aF>yJuCG1&c1$j50-q4v=YZ4pka5225Q)bv za!{f<2lFN(_z=+E+=uBquv3#xN$h%F2r&_ z4(37}1!0qKC+J`990g%Z4Gb`#+b>ql>$yMIe}E9eFE*$8_L05zGQHan%$LtV`2xG~ zT^wE zZFBtYq-Es75y^zHJiJiOVeLca1mrt%ut#KKjNA*Sf6Q|M2Weqqk^D;gzp`@M31j)! z?KsBe2ySVc&=<#nQD+WTiBKX!vlk}+#kFw;z`PNvAVB>k9AD;Q-vPo{PJq&g!`~~x zQNlo<@M(08$xcM2dm5vHC-Mohyk}&BJ9w7{g`gxYSH^L_WrqWV1Vg@wo*cn+BKHy# zz@m-K9Sf!MqC&U>l7K$ua@CAOJ2QO27=+Rs@SNBv?4!>Tel3I8!{EK~kyA@}VWF^; zJK%$&+8%08G@f_e2QlSH3^_3h1CltV&SHB;{dhsFN4x=!hBnKsd5J!FdPMuhi1y|v zzRU-QAKz>cK7ON;=WT-K{J@_JM48w$t1)JKG91A~42|L#p2UT7EKlG@;p$Q^wM^rn zy|~)x6LI@8MDPG1w2otV85hE_yo{p?sD3AwanRp!uKcg8hQl9Y#ONeGA+(Q+a`%cn z7}SMD9H9fmg}oty8I~WSS;6JVaa^M*8C~21B9i_|*(?Bn$AQN2JlqQcs||ee0__Jg zz9W;831KuY$EeB0?Fka+iMNz+9OK-HQ+O{e5G5G9R4Awe!HAam%l=3%0JRQ_QWkJy z#`Eiw;Vu_yxMxg|_jL@*x94E)V*F2lJPxczustqQAAQL10__N^>+tfany9DQQc15D zuWF3nQI{AF|DlD+(LKq*yBG1A7zluH1T&UH2oA^d{|dP?hbIk(Bl(1|@;A~vp9hF9 z&0BmI!ERY<^ElW&gm5IxC0x^wtO6p9l@$OI0YIm&)lWapAZkclza&b7$l&Ub;zzvc~pK-|TVVFOVhUaXOAYO3Swm9;Hep0g)?!-wnQ?SOXhfgSDCkS92(&eh}&z=J>kCETv;(YQ!>E88ezh?6tg)z za`29=N^l*)MRq?lc{R~Lg!&DM}^?*U=l&lM!?4$}6%d{QeHHYp%k95Qi? z8FgUgq{AjHCpoe1u`OH}PJv4D6S&r^#YOI0;MM=_h$AL5+bE2b?Yaq~_J>4paFnNiwt~CN|2va^m_!X%15xY;pJow4{mmpfSo| zxx;j>Z-QI;dK0ifR4#{%HbBb&I*QgzYYtff^@RqF(b7OKnQFmu3%FH_e8c|qPBmU< zW^lw9U)BVoT0$1C@1V>a<4-RX#;bzK;`a~$iu4|WXdi)FP62N49v^VuyO3nOpfP~5 zbW1>4vs~==6wcb@nc%-*oj0boC}J+n2vb5^I^+>J91CT`|_&Z+T8{DP`6w zDBp0M7u;hgd23KyK9A{N`eQ>lI39d6&qEzJT_wOUPgM)XCL$+V(vzJVU#)GfIH5$5V z;5r%VmqbCNKA8a`jfx=vBA*W7!R`gzPH$iZPX+6iwKxDai=uL5lJlpaA~?{PA5*b5 z3wiKOLD)-z2z|)}X(CXJQD_9vmUdU67t*&Q_X8OeK?fffd?DZ~0j1#Zu(U3QD_SNA zM);h1DFX1!rF~c3$+GzNRE~!f`Tu?aweBo<|MYxv@bPoteQ@yYbOIl6*A<-+F;vp; z5b-}n)BkUMvhn}B4p4w0E`-qqXiz$eaVE6_bB@o98hyQIFL>iHcy$*5payu6rjUu0 z4;}#IfTssg@S+BI`34s}tqaUf5J;1fwcEsUaTr+(5O^H8mEDlHI|Ny4Tl2Eu6>x~O z7XSa(=oslorwszQh7ADK338D_iOCS!k^4m&#W282(}AA^qFF$Y7P8mQGA7^LqzE8i z2x`~&b&P%u<4uE+K0XEzawC&Q5pp3m0Goxnalzn%wG>bdWG)4^b^!8`4?ge&CN3Df zIRJWt24J_-a11SV<5wdAq})JL_0H?EUg3D;`SbC zgd`5)NTF&%IJdFJeWH_1k>1Ub8jh=|CIlpwni1i_M}p(iXhglI`i5E>ByUIBeVt0f=W_H8!C;# zF~eQsb3_Y+VxVA0sVui&!lnekAs`p6@ALF?ywl?YExj=R!;!G*gd9?8t;RzX7Et)` z@8J7uoZIvxeZ%Dcv~fOXMkPRMK|5HS8Nei=8Qcc6ma@5wEhO?8q%r;`m+}7;e}~uC z_L<;61pC`V@^+H1fMilp7)Ze`qdjan@mp(UZDu^9O%_=i-eGh9ud2cDU)ueJ?`VhC zvTys{KQ4!#Yph16I;-*6&lD(b55=Rb##19_EPx>S19}Uc0HWexJ@5#inodV-&-D1> zDWEfE=QGT{8I8}vv7jYuHlhv>4aS|`ct4L}OKIic>F5E z$5Pz&MKms7#qdaOG(K6xh_wg*MEni$G2$)6>sC}XAl^WHfcQJ&A68=czk~LFckT2l z#JGfe5MLy7(4rCXIb!37IA83IOGL!Zcy2Dk^It@>JTm)oiJ$U4g~3VUV&i^QH7nMID_J5xpkM#%OKC6T%mect;r z-q^vRdt3LI>pOa5RJuGt5vBn_9KT-e8+@_zdLkj&qNq&@3mWocyGqtRS5lcI3z7w$QU};v!it$=72A;^ zTT5N3d+1y!8V7BPW#Dk|IK$uTq9q;hya9hxuqSl}s{T1%2K*WDN-{W}S;XPN6Vcd$ zotur&Jj$gh(!O>LDxOYY%O16N){9%}(lI^jLUuMrCvZb0_Ag6+F$V0BB0 zJv|xE(F#K@FCBp0anVRTDD(_>1^JI4-~5QRdAdK^Fqd_r-tRRzX2u;^X=G1|DDq2@k>#$U$ek~YY}sm?i@Ei22(+>37-VB+9L)uiuW2*) z5+t=?A1e1P10~f!HY2RHqMROjlmrF5Nb2iEnJ*EW02_hYeiX3^@igLI#BV49c(&Lf z&1tmfLs-WLiQc)qj1~WAWI5G{rgp`UpbaK$WOM}u^2|nLa7yn zl42AjYar07Ft;z1*P~fk%oW?q6w3t^WW2l%1y!22*)an$Bm&c~(MF~C1^Sq@X4M!j z3d55xn5BZU9?VWuld2MSu?$(7rkADSr*VR`7^vYIV1XE@nb%;%RZ3bpJqP;Of>$wnBv%un1*e>jMPV>Mch=2w zh?yRG}rS>oD15Ga)N&%0d{f4G!mUEuz3p{@T{ZUVB0Ic%)N_!9V&Mqf?o_T6W zYt8|dUyf#L8W^^X0ih$XpcbzO;AOi@zkMur^Ob17Kd!Lkp%%FlE|128ET~ktrDSqiKa)D$#rDF`m>U`lC~CqRaf!UT{b6Yaj!Dq0G9au!a(6~&P0lq+JP^WZ1QC1Yos#ANJd2LQV(2va23mKAZExT#E51{F{Yxf=n!>uw^Z@ek$UP&2p%%5Ar zwh#PZ2^ZT3wF#pnq*-lCVV=Q5vWY}1h0VQ3m%_3bEE>aW&Z$61mujeAl;F@9$C3q3 z@tK17q!X`+20>sFe*&Jf{mX>~hk}4y1{C($0Y75gae-zz=xoaP@#9Vb^ZC>pW73 zd8FM3Vq31<~iXdx4$2TW9rRp|p?emg3i~lOINk?-NiTY@4*Zg-4uL@TQ%*E|=H% zl(dNN;=Nn+Ri1F*`6*P7ym3hn^OTm)mb zmcjhu-=%({9}T@=(a>H_&DKpSQIT5N8aa{1E3cqBK;kG1Nqdz#IUk)mgTlSFlUliW<0i{hF_6Iud>(+r*r8B*KUNyW|J|-jiyTK`8W7={w zfBCp4Be*njPjBySv3qa77)hPlu%rVsOs*)^5!7RCNgrKWnY8^w4HH!mBh5fgsST*8 zD4D`I@g*%diE!P}&;K_Qx3O;ziM``|Zf%3fIbW^QDn1Kv2GQQKr5 zBCG=0Ti;)*s7~|fRo00# zQ(kJ7aCWe^tSr&Fd18y)zo!-Ti0hb6Pi0-be?;VOodD$o%$iPRbmR1V-Qr7 zesP!B8FMk0JW?+t5VjD=Unph~P`iZeM!RToCJI!zXp=Zzq`B@S<9JF+&wp`zf99pkU#@Mq{d$krl84#V1@6TjGrD~iK3%%{`l!y}k9@rwiOIJq+Q?nsz>^Z~DfgPw|_5HM_j@AIqnY>^Zf6VcVs|mfm%? zY54o1L01pAF|HsRjqd;^dKI`A>$8_%PAjMLKPm>@(l7enlrv ze%-k4-ml-ao~SP@-&3=*;Kg??YcFldZ{eSsFm|SVz|z06U!VQz`$L|cQ z_vEI^qnQntHvUxJ^v%Nazdgu$ed)@^-{;keulHxyZQpV=r>Q}_vf;P+f9yWC%gfk< zttj7f>*U@aUhjRhX-Ce*lGelHhjm=hX1s4|G4t6>YI6IuI_5OuvfgdzG=&z`wf@M+oT1lmwBu$t*M-M=Z{?H)h_=1 zf~Kmh+{BTiI`!!`B}KFP&DSRu)|}m3bA0Fay8M%Ow>mF+cB!Fp+fQyEhkX8Ut4H9# z!Z(LY+f2};q<4#soS;ZGDwo`xSG&W_KV(K9O?>D8^}vY!!PDZWw2NGRd&k2DuLKGRjViI({1JhK?ALX6dc_Z(3{m#6`iX)ywLTqSg z_j%jIs82#|Q@f4T+eYb%pRK>QNgE48oMPgo(>qKKjZ;~scZdm-jrss|Y261ee|#bH zW#-*Cz8PBHK2+Y_5%_O&NhFLnUXubmq_Jy%Y58RNuaY}Z7Ejp@>11b;q`fud zflaYX%C~L@#U|GRR}nKjMoKgbB^u-Sm~q|vPO>G%Bu$N*G%d@ISJt2;RwcN_;v|s9 zkjrfW1~4cU!$!9TZojuW-fsD{c}t8_r}qD7!jxPIuW1RKS#3cRqe|;MGH|q4qsM+P zRk}38OJYh*`eahl40|#1XA&m3Ep-?svP3U2E=<5gP%f0(A|&zCQ&PsJe#*Nn_v|K5#@PyIMb9#NJ9v#x5Yvs{I^2^}eG)plu9c?xBQ9`E#S%O~Goyzx3 zO-hQNI(B@LNy@7bLJ!txyu|FFO0;otDq95av7M}k^gQyX z0}ghv|2}AOW!Hj#l5c-_N_6l!kag7-={Vk)OR4!3?00+}pQk(8pHlL726$2nE&`s$glu@*bipcF*y@1$Cky*OZ|8SzKfr_f=+DP}+ahRyoPy(n$4s@-OlFiVb zrsp`t(Vo>nH>iogQ}yJL1F9(4n@kO(*xTcuCMO%7JzW;C`%lly>IV<@-MVw7>{5?R zO|O5}N)0<(3_2RUZ$X)c8#OC$WjpW0v&UZk)u&ISi$k8O^`)($A*)RHEa_ceE$bip oq+wS7`U68;A7 '%2' Abstand=%3 mm (innerhalb Toleranz, aber nicht ~0)" + "vfl-kette-diag-verdaechtig": "\n [Diagnose] VERDAECHTIG: '%1' -> '%2' Abstand=%3 mm (innerhalb Toleranz, aber nicht ~0)", + "gr-fehler-datenpfad": "\nGeruest: Datenverzeichnis unbekannt - DXFM_DATA bzw. DXFMAKRO nicht gesetzt (bin/setenv.bat ausfuehren).", + "gr-fehler-json-fehlt": "\nGeruest: Datendatei nicht gefunden: %1", + "gr-fehler-json-lesbar": "\nGeruest: Datendatei nicht lesbar: %1", + "gr-fehler-baugruppe": "\nGeruest: Baugruppe '%1' steht nicht in der Datendatei.", + "gr-fehler-keine-varianten": "\nGeruest: Baugruppe '%1' hat keine Geometrie-Varianten.", + "gr-fehler-dummy": "\nGeruest: Dummy-Baugruppe nicht gefunden: %1", + "gr-fehler-dcl": "Dialog geruest.dcl konnte nicht geladen werden.", + "gr-dlg-kopf": "%1 konfigurieren", + "gr-alert-laenge": "Laenge ungueltig: '%1' mm. Erlaubt: %2 bis %3 mm.", + "gr-prompt-einfuegepunkt": "\nEinfuegepunkt waehlen: ", + "gr-abgebrochen": "\nAbgebrochen.", + "gr-fertig": "\nGeruest-Baugruppe '%1' eingefuegt (Block %2)." } } diff --git a/lang/en_GB.json b/lang/en_GB.json index 49fb868..67a507e 100644 --- a/lang/en_GB.json +++ b/lang/en_GB.json @@ -969,6 +969,18 @@ "vfl-kette-diag-startbaustein": "\n [Diagnose] KS_AUS start block (%1): X=%2 Y=%3 Z=%4", "vfl-kette-diag-naechster-kandidat": "\n [Diagnose] Next candidate: %1 distance=%2 mm (tolerance tight=%3 mm, wide=%4 mm)", "vfl-kette-diag-kein-anderer": "\n [Diagnose] No other block with KS_EIN found (%1 blocks captured in total).", - "vfl-kette-diag-verdaechtig": "\n [Diagnose] SUSPICIOUS: '%1' -> '%2' distance=%3 mm (within tolerance, but not ~0)" + "vfl-kette-diag-verdaechtig": "\n [Diagnose] SUSPICIOUS: '%1' -> '%2' distance=%3 mm (within tolerance, but not ~0)", + "gr-fehler-datenpfad": "\nScaffold: data directory unknown - DXFM_DATA or DXFMAKRO not set (run bin/setenv.bat).", + "gr-fehler-json-fehlt": "\nScaffold: data file not found: %1", + "gr-fehler-json-lesbar": "\nScaffold: data file not readable: %1", + "gr-fehler-baugruppe": "\nScaffold: assembly '%1' is not in the data file.", + "gr-fehler-keine-varianten": "\nScaffold: assembly '%1' has no geometry variants.", + "gr-fehler-dummy": "\nScaffold: dummy assembly not found: %1", + "gr-fehler-dcl": "Dialog geruest.dcl could not be loaded.", + "gr-dlg-kopf": "Configure %1", + "gr-alert-laenge": "Invalid length: '%1' mm. Allowed: %2 to %3 mm.", + "gr-prompt-einfuegepunkt": "\nSelect insertion point: ", + "gr-abgebrochen": "\nCancelled.", + "gr-fertig": "\nScaffold assembly '%1' inserted (block %2)." } } diff --git a/menu/SSG_LIB.cui b/menu/SSG_LIB.cui index e728c14..39d447f 100644 --- a/menu/SSG_LIB.cui +++ b/menu/SSG_LIB.cui @@ -530,205 +530,229 @@ ^C^C(ssg-ensure "OmniModulInsert") OMNI_EDIT - + + + Rohrabhaenger + ^C^C(ssg-ensure "GeruestInsert") GERUEST_ROHRABHAENGER + + + + + 2 Zoll Saeulen + ^C^C(ssg-ensure "GeruestInsert") GERUEST_SAEULE_2ZOLL + + + + + 1 Zoll Saeulen + ^C^C(ssg-ensure "GeruestInsert") GERUEST_SAEULE_1ZOLL + + + + + Wandbefestigung + ^C^C(ssg-ensure "GeruestInsert") GERUEST_WANDBEFESTIGUNG + + + Marker einfuegen ^C^C(ssg-ensure "Tro_Edit") TRO_INSERT - + Marker bearbeiten ^C^C(ssg-ensure "Tro_Edit") TRO_EDIT - + Bearbeiten (Auto) ^C^C(ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT - + Verbindung einfuegen ^C^C(ssg-ensure "Connection_Edit") CONNECTION_INSERT - + Verbindung bearbeiten ^C^C(ssg-ensure "Connection_Edit") CONNECTION_EDIT - + Eingang ^C^C(ssg-ensure "SSG_LIB_Commands") ILS_EINGANG_INSERT - + Ausgang ^C^C(ssg-ensure "SSG_LIB_Commands") ILS_AUSGANG_INSERT - + SPS Schliessen ^C^C - + Export Sivas ^C^C(ssg-ensure "export") EXPORTSIVAS - + Export CSV ^C^C(ssg-ensure "export") EXPORTCSV - + Umschalten 2D und 3D ^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_SWITCH - + Alle Bloecke auf 2D ^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_ALL_2D - + Alle Bloecke auf 3D ^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_ALL_3D - + Sprache ^C^C(ssg-ensure "SSG_LIB_Commands") SSG_SPRACHE - + Planquadrate an/aus ^C^C(ssg-ensure "DrawPlanquadrate") PLANQUADRATE_TOGGLE - + alle Tests ausfuehren und exportieren ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_run_all.lsp")) SSG_RUN_ALL_TESTS_EXPORT - + Tests ausfuehren und stehen lassen ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_run_all.lsp")) SSG_RUN_ALL_TESTS_OFFEN - + Export CSV/Sivas ALL ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_export_all.lsp")) TEST_EXPORT_ALL - + Kreisel ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_kreisel.lsp")) TEST_KREISEL - + Foerderer ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_foerderer.lsp")) TEST_FOERDERER - + Foerderer-Linienzug ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_linienzug.lsp")) TEST_LINIENZUG TEST_LINIENZUG_STUFE2 - + Omniflo ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_omniflo.lsp")) TEST_OMNIFLO - + Omniflo Strecke ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_omniflo_strecke.lsp")) TEST_OMNIFLO_STRECKE - + Gefaellestrecke ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_gefaellestrecke.lsp")) TEST_GEFAELLESTRECKE - + Mubea Gesamtmodell ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_mubea.lsp")) TEST_MUBEA - + HundM05 Kreisel-Abschnitt ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_hundm05.lsp")) TEST_HUNDM05 - + KS 2D/3D pruefen (CSV) ^C^C(load (strcat (getenv "DXFMAKRO") "/Lisp/ks_pruefung.lsp")) KS_PRUEFEN - + Kreisel bearbeiten (ssg-ensure "KreiselInsert") KreiselEdit - + Omniflo Element bearbeiten (ssg-ensure "OmniModulInsert") OMNI_EDIT - + Gefaellestrecke bearbeiten (ssg-ensure "Gefaellestrecke") GEFAELLESTRECKE_EDIT - + VarioFoerderer bearbeiten (ssg-ensure "VarioFoerderer") VARIOFOERDERER_EDIT - + TRO-Marker bearbeiten (ssg-ensure "Tro_Edit") TRO_EDIT - + Verbindung bearbeiten (ssg-ensure "Connection_Edit") CONNECTION_EDIT - + SSG Bearbeiten (Auto) (ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT @@ -746,9 +770,10 @@ - - - + + + + ILS @@ -1342,244 +1367,275 @@ - SPS - - + Geruest - Eingang + Rohrabhaenger - + + - Ausgang + Wandbefestigung - - - - - SPS Schliessen - - + - - TRO + + Saeulen - Marker einfuegen - - - - - - Marker bearbeiten + 2 Zoll Saeulen - Bearbeiten (Auto) + 1 Zoll Saeulen - - Connections + + SPS + + - Verbindung einfuegen + Eingang - + - Verbindung bearbeiten - - - - - - Bearbeiten (Auto) - - - - - - - Export - - Export Sivas - - - - - - Export CSV - - - - - - - Konfiguration - - Umschalten 2D und 3D + Ausgang - Alle Bloecke auf 2D + SPS Schliessen + + + TRO - Alle Bloecke auf 3D + Marker einfuegen - + - Sprache + Marker bearbeiten + + + + + + Bearbeiten (Auto) + + + + + + + Connections + + Verbindung einfuegen + + + + + + Verbindung bearbeiten + + + + + + Bearbeiten (Auto) + + + + + + + Export + + Export Sivas - Planquadrate an/aus + Export CSV + + + Konfiguration - Konfiguration Schliessen + Umschalten 2D und 3D - + - - - Tests - alle Tests ausfuehren und exportieren + Alle Bloecke auf 2D - Tests ausfuehren und stehen lassen + Alle Bloecke auf 3D - Export CSV/Sivas ALL + Sprache + + Planquadrate an/aus + + + + + + Konfiguration Schliessen + + + + + + + Tests + + alle Tests ausfuehren und exportieren + + + + + + Tests ausfuehren und stehen lassen + + + + + + Export CSV/Sivas ALL + + + + Kreisel - + Foerderer - + Foerderer-Linienzug - + Omniflo - + Omniflo Strecke - + Gefaellestrecke - + Mubea Gesamtmodell - + HundM05 Kreisel-Abschnitt - + KS 2D/3D pruefen (CSV) - + - + POP501 SSG_LIB Edit Context Kreisel bearbeiten - + Omniflo Element bearbeiten - + Gefaellestrecke bearbeiten - + VarioFoerderer bearbeiten - + TRO-Marker bearbeiten - + Verbindung bearbeiten - + SSG Bearbeiten (Auto) - + diff --git a/menu/SSG_LIB.mnu b/menu/SSG_LIB.mnu index 7294389..539922f 100644 --- a/menu/SSG_LIB.mnu +++ b/menu/SSG_LIB.mnu @@ -110,6 +110,12 @@ [~APBW 110 R550/67,5 208x347]^C^C(ssg-ensure "OmniModulInsert") OMNI_APBW_R550_675_208 [<-~APBW 110 R550/67,5 58x497]^C^C(ssg-ensure "OmniModulInsert") OMNI_APBW_R550_675_58 [<-Element bearbeiten]^C^C(ssg-ensure "OmniModulInsert") OMNI_EDIT +[->Geruest] +[Rohrabhaenger]^C^C(ssg-ensure "GeruestInsert") GERUEST_ROHRABHAENGER +[->Saeulen] +[2 Zoll Saeulen]^C^C(ssg-ensure "GeruestInsert") GERUEST_SAEULE_2ZOLL +[<-1 Zoll Saeulen]^C^C(ssg-ensure "GeruestInsert") GERUEST_SAEULE_1ZOLL +[<-Wandbefestigung]^C^C(ssg-ensure "GeruestInsert") GERUEST_WANDBEFESTIGUNG [->SPS] [->TRO] [Marker einfuegen]^C^C(ssg-ensure "Tro_Edit") TRO_INSERT diff --git a/tests/test_unit.lsp b/tests/test_unit.lsp index 406e669..36920bf 100644 --- a/tests/test_unit.lsp +++ b/tests/test_unit.lsp @@ -424,6 +424,134 @@ ) +;; ------------------------------------------------------------ +;; TESTS: ssg_core.lsp - rekursiver JSON-Parser + ssg-join +;; ------------------------------------------------------------ +(defun tu-tests-json ( / _p _datei _daten _bg _var _anb) + (princ "\n[ssg_core/json]") + + ;; ssg-join + (tu-eq "ssg-join/leer" "" (ssg-join '() ",")) + (tu-eq "ssg-join/eins" "a" (ssg-join '("a") ",")) + (tu-eq "ssg-join/drei" "a,b,c" (ssg-join '("a" "b" "c") ",")) + (tu-eq "ssg-join/plus" "a + b" (ssg-join '("a" "b") " + ")) + + ;; Skalare und flaches Objekt + (tu-eq "json/objekt-flach" '(("a" . 1)) (ssg-json-parse "{\"a\": 1}")) + (tu-eq "json/zwei-keys" '(("a" . 1) ("b" . 2)) + (ssg-json-parse "{\"a\": 1, \"b\": 2}")) + (tu-eq "json/string" "hallo" (ssg-json-get (ssg-json-parse "{\"a\": \"hallo\"}") "a")) + (tu-eq "json/int" 6000 (ssg-json-get (ssg-json-parse "{\"a\": 6000}") "a")) + (tu-eqf "json/real" 1.5 (ssg-json-get (ssg-json-parse "{\"a\": 1.5}") "a")) + (tu-eq "json/true" T (ssg-json-get (ssg-json-parse "{\"a\": true}") "a")) + (tu-eq "json/false" nil (ssg-json-get (ssg-json-parse "{\"a\": false}") "a")) + (tu-eq "json/null" nil (ssg-json-get (ssg-json-parse "{\"a\": null}") "a")) + + ;; Verschachtelung - genau das, was die alten Zeilen-Parser nicht koennen + (tu-eq "json/objekt-verschachtelt" '(("a" ("b" . 2))) + (ssg-json-parse "{\"a\": {\"b\": 2}}")) + (tu-eq "json/array" '(("a" 1 2 3)) + (ssg-json-parse "{\"a\": [1, 2, 3]}")) + (tu-eq "json/array-objekte" '(("a" (("b" . 1)) (("b" . 2)))) + (ssg-json-parse "{\"a\": [{\"b\": 1}, {\"b\": 2}]}")) + (tu-eq "json/array-leer" '(("a")) (ssg-json-parse "{\"a\": []}")) + + ;; Mehrzeilig (alte Parser verlangen Arrays in EINER Zeile) + (tu-eq "json/mehrzeilig" '(("a" "x" "y")) + (ssg-json-parse "{\n \"a\": [\n \"x\",\n \"y\"\n ]\n}")) + + ;; Maskiertes Anfuehrungszeichen und Komma im String - beides kommt in den + ;; Geruest-Artikelbezeichnungen real vor ("ROHR R 1\" 33,7 x 2,6") + (tu-eq "json/escape-quote" "R 1\" x" + (ssg-json-get (ssg-json-parse "{\"a\": \"R 1\\\" x\"}") "a")) + (tu-eq "json/komma-im-string" "33,7 x 2,6" + (ssg-json-get (ssg-json-parse "{\"a\": \"33,7 x 2,6\"}") "a")) + (tu-eq "json/escape-backslash" "a\\b" + (ssg-json-get (ssg-json-parse "{\"a\": \"a\\\\b\"}") "a")) + (tu-eq "json/escape-newline" (chr 10) + (ssg-json-get (ssg-json-parse "{\"a\": \"\\n\"}") "a")) + + ;; Echte Datendatei: der eigentliche Praxisfall + (setq _datei (strcat (getenv "DXFMAKRO") "/data/json/geruest.json")) + (if (findfile _datei) + (progn + (setq _daten (ssg-json-laden _datei)) + (tu-eq "json/geruest-baugruppen" 4 (length _daten)) + ;; Baugruppe ueber das ASCII-Feld "id" finden (aeusserer Schluessel + ;; traegt Umlaute bzw. Anfuehrungszeichen) + (setq _bg nil) + (foreach _e _daten + (if (and (null _bg) (= (ssg-json-get (cdr _e) "id") "Rohrabhaenger")) + (setq _bg (cdr _e)))) + (tu-eq "json/geruest-id-gefunden" T (if _bg T nil)) + (if _bg + (progn + (setq _var (ssg-json-get _bg "geometrie_varianten")) + (setq _anb (ssg-json-get _bg "anbaugruppen")) + (tu-eq "json/geruest-varianten" 3 (length _var)) + (tu-eq "json/geruest-teileart" "Geruest Rohrabhaenger" + (ssg-json-get _bg "teileart")) + (tu-eq "json/geruest-dummy" "Rohrabhaenger_dummy.dwg" + (ssg-json-get _bg "dummy")) + ;; Sivas-ID als Zahl geparst, Laengengrenze aus dem attribute-Block + (tu-eq "json/geruest-sivas" 821006026 + (ssg-json-get (car (ssg-json-get (car _var) "teile")) "sivas_id")) + (tu-eq "json/geruest-max" 6000 + (ssg-json-get (ssg-json-get (ssg-json-get _bg "attribute") "Laenge") "max")) + ) + ) + ) + (tu-record nil "json/geruest-datei" (strcat "nicht gefunden: " _datei)) + ) +) + + +;; ------------------------------------------------------------ +;; TESTS: GeruestInsert.lsp - reine Hilfsfunktionen +;; ------------------------------------------------------------ +(defun tu-tests-geruest ( / _eintrag _gruppe) + (princ "\n[GeruestInsert]") + (if (and (null (atoms-family 1 '("GERUEST-ARTIKELNUMMERN"))) + (atoms-family 1 '("SSG-ENSURE"))) + (ssg-ensure "GeruestInsert") + ) + (if (null (atoms-family 1 '("GERUEST-ARTIKELNUMMERN"))) + (tu-record nil "geruest/modul" "GeruestInsert.lsp nicht geladen") + (progn + ;; Indizes aus get_tile bei multiple_select + (tu-eq "geruest-indizes/leer" '() (geruest-indizes "")) + (tu-eq "geruest-indizes/eins" '(0) (geruest-indizes "0")) + (tu-eq "geruest-indizes/drei" '(0 3 5) (geruest-indizes "0 3 5")) + + (tu-eq "geruest-id-string/int" "821006026" (geruest-id-string 821006026)) + (tu-eq "geruest-id-string/str" "0_B10071" (geruest-id-string "0_B10071")) + (tu-eq "geruest-id-string/nil" nil (geruest-id-string nil)) + + ;; Einzelteil mit menge 2 -> Artikelnummer zweimal (ARTINR-Regel) + (setq _eintrag (ssg-json-parse + "{\"artikel\": \"X\", \"sivas_id\": 825002019, \"menge\": 2}")) + (tu-eq "geruest-artnr/menge2" '("825002019" "825002019") + (geruest-artikelnummern _eintrag)) + (tu-eq "geruest-name/artikel" "X" (geruest-eintrag-name _eintrag)) + + ;; Benannte Gruppe -> teile rekursiv aufloesen, Mengen beachten + (setq _gruppe (ssg-json-parse + (strcat "{\"name\": \"Schraubenset\", \"teile\": [" + "{\"artikel\": \"A\", \"sivas_id\": 111, \"menge\": 1}," + "{\"artikel\": \"B\", \"sivas_id\": 222, \"menge\": 2}]}"))) + (tu-eq "geruest-artnr/gruppe" '("111" "222" "222") + (geruest-artikelnummern _gruppe)) + (tu-eq "geruest-name/gruppe" "Schraubenset" (geruest-eintrag-name _gruppe)) + + ;; Fehlendes menge-Feld -> 1 + (tu-eq "geruest-artnr/ohne-menge" '("333") + (geruest-artikelnummern + (ssg-json-parse "{\"artikel\": \"C\", \"sivas_id\": 333}"))) + ) + ) +) + + ;; ------------------------------------------------------------ ;; REPORT SCHREIBEN ;; ------------------------------------------------------------ @@ -467,6 +595,8 @@ (tu-tests-omni) (tu-tests-kreisel) (tu-tests-sepliste) + (tu-tests-json) + (tu-tests-geruest) (princ "\n\n----------------------------------------------------------------") (princ (strcat "\n Gesamt: " (itoa (+ *tu-pass* *tu-fail*))