From 29ac9be75fdcafbd7d05c15aa9eb8490d74caa98 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Wed, 26 Aug 2026 18:58:37 -0300 Subject: An edited row keeps its colours, four copies of forkShell become one, and Esc stops recentring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## A terminal row's ANSI colours survive being edited The loudest colour bug this editor had: one keystroke anywhere in a coloured shell row turned EVERY column of it grey. `EditAnchors` anchored a buffer line only when it was BYTE-IDENTICAL to the shell row it stood over, so a single differing byte dropped the whole row's colour projection. Worst shape is invisible: append past the pane's right edge, where the text is clipped, and the row looks the same and only its colour goes. Anchoring is byte-level now. An edit leaves the row's own bytes at both ends, and being the same bytes they keep the same colours; only what was typed has no cell under it, so only that takes none. Live, on real `fastfetch`: a 32-column blue run split into 6 + 26 around one typed character. Three defects underneath it, all found by machinery rather than by reading: * A JOIN removes a buffer line while the buffer's covered span grows, so `lines == covered` and both aligned guesses — Nth line over the Nth covered row, and the same counted from the bottom — resolved to the SAME wrong row. Every untouched row below a join went plain. Anchoring is now a streaming monotone matching: one shell-row cursor that only ever moves forward, advanced once per buffer line, linear in the buffer where the version before it was quadratic. * An EMPTY line is not evidence. Splitting a row makes one, it equals every blank row in the span, and left free to look ahead it claimed the blank row below the last output and took every coloured row in between out of reach of the lines that owned them. * Reflow under a scrolled viewport. `PageList.getTopLeft(.viewport)` returns the viewport pin verbatim, x and all, while `PageList.pin` forces x to 0 — so after a reflow remapped a tracked pin into the middle of a row, the text pass dumped row 0 from that column while the colour pass paired the fragment with the row's FIRST cells. Row 0 wore its left half's colours until the pane snapped back to live output. `bodyText` dumps from column zero now, which is also what ghostty's own renderer draws. Also here: DECSCNM (reverse video) was silently dropped whenever `tty_filter` was off, because the raw path resolved a `.none` colour by role and never consulted the mode. The test that found the first two is the one worth keeping: random editing against an ABSOLUTE oracle — every row's own text names the colour it must have — because the differential oracle it replaced was blind by construction. It skipped the edited row, which is the row the user is complaining about. ## Esc returns to a pane without moving its view Esc in body normal mode runs `Last`, "the pane you were in before this one", and that went through `focusPaneLine`, which recentred a file on the target line unconditionally. So returning to a buffer repainted the whole screen to show a line that was already on it. `focusPaneLine` takes a landing now: `.center` for the three callers going somewhere you have not been (a look target, a path a pane already holds, `@pN:LINE:COL`), `.keep` for Esc. `.keep` leaves the view alone and lets `ensureCursorVisible` — which already existed and already scrolls by the minimum into the `scroll_off` band — be the only thing that may move anything. Not `line = 0`, which `focusPaneLine` already understands as "focus and touch nothing": a background pane's view can move while you are away, because the wheel scrolls the pane under the POINTER and a resize reveals no cursor, so the recorded cursor plus a minimal nudge is what actually gets you back. Ctrl-o and Ctrl-i keep centring, and the asymmetry is structural rather than arbitrary: `Last` only ever CROSSES panes, so the pane it lands on already holds the view you left it with, while `jumpBy` can land in the SAME pane, where a long in-file jump would arrive on the very top or bottom row with `scroll_off` lines of context on one side. Helix splits the same pair the same way — its jumplist centres, its buffer switch does not. One deliberate consequence: under `.keep` a PDF's page is not restored AT ALL, because a page reveal IS that pane's view and a reveal of the page you are already on still snaps `document_scroll_y` to that page's start, discarding where you had read to. When something moved the pane while you were away — the wheel again — Esc leaves it where the wheel left it, and Ctrl-o is how you reach the recorded page. ## host_io.zig: the machine-local half of a host, once `host.zig` is the seam. The part of the answer that is identical on every host with an operating system under it — fork a pane's shell, put bytes on a disk — was written FOUR times: in tty.zig, gui.zig, macos.zig and detached/server.zig. What those copies had in common says what they were for: all four were missing FD_CLOEXEC on the pty master, so in every shell pardes has shipped, a program in one pane could read another pane's terminal. One copy now, and the wire got smaller for it: `ServerMsg.spawn` is gone. A frontend never asked the server to fork anything — the server has an operating system under it and forks through `host_io` like every other host — and `decodeClient` lost the scratch buffer that message needed. --- .gitignore | 4 + README.md | 127 + build.zig | 80 +- docs/acme-fs.md | 311 +- docs/config.md | 126 +- docs/design.pdf | 10295 +++++++++++++++++++++++++++++---- docs/design.typ | 2267 ++++++-- docs/detached.md | 324 ++ docs/ghostty-macos-notes.md | 55 +- docs/helix-keys.md | 106 +- docs/ideas.typ | 18 +- docs/lsp-evaluation.md | 36 +- docs/lsp.md | 148 +- docs/macos.md | 193 +- docs/web.md | 44 +- next-steps.txt | 32 + src/builtins.zig | 64 +- src/config.zig | 20 + src/detached/client.zig | 405 +- src/detached/server.zig | 1169 +++- src/detached/wire.zig | 466 +- src/gui/gui.zig | 976 +++- src/host.zig | 10 + src/host_io.zig | 178 + src/limits.zig | 15 + src/macos.zig | 57 +- src/main.zig | 59 +- src/nested.zig | 12 +- src/pardes.zig | 464 +- src/pdf_pane_integration_test.zig | 42 + src/term_pane.zig | 1632 +++++- src/tty/tty.zig | 1036 ++-- test/snapshots/boot.golden | 54 +- test/snapshots/builtins.golden | 62 +- test/snapshots/chordcut.golden | 58 +- test/snapshots/cursorline.golden | 58 +- test/snapshots/edit.golden | 54 +- test/snapshots/fsearch.golden | 54 +- test/snapshots/images.golden | 64 +- test/snapshots/jumps.golden | 54 +- test/snapshots/load.golden | 56 +- test/snapshots/look-file.golden | 54 +- test/snapshots/lookpanes.golden | 88 +- test/snapshots/modal.golden | 58 +- test/snapshots/multicursor.golden | 66 +- test/snapshots/psearch.golden | 46 +- test/snapshots/scroll.golden | 56 +- test/snapshots/syntax.golden | 66 +- test/snapshots/tagbottomimage.golden | 24 +- test/snapshots/theme.golden | 195 +- test/snapshots/ttylook.golden | 54 +- test/snapshots/windowops.golden | 54 +- transactions.txt | 8 + 53 files changed, 18094 insertions(+), 3960 deletions(-) create mode 100644 README.md create mode 100644 docs/detached.md create mode 100644 src/host_io.zig diff --git a/.gitignore b/.gitignore index 04a16752..68cbb61a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,7 @@ test/snapshots/*.actual test/macos-snapshots/*.actual # macOS finder litter; one arrived with the app-bundle branch .DS_Store +# a scratch note holding one long `zig build esp32p4-run` line, left in the +# worktree while flashing the board. Not source, and `tmp` is too likely a name +# for the next one to be worth tracking either +/tmp diff --git a/README.md b/README.md new file mode 100644 index 00000000..daeac183 --- /dev/null +++ b/README.md @@ -0,0 +1,127 @@ +# pardes + +A text environment in the acme tradition: columns of panes, each pane a tag line +plus a body, where the body is a live terminal, a file, an image, or a PDF. The +mouse carries meaning — left selects, middle executes, right looks — and +everything on screen is text that is equally alive, whether a shell printed it or +you typed it. + +One program, five thin shells. The core is a library in the way ghostty-vt is a +library: you feed it events, it returns a surface and a list of effects, and it +performs no IO itself. Everything a shell does is translate native input into +`pardes.Event`, render `pardes.Surface`, and perform `pardes.Effect`. + +## Requirements + +Zig **0.16.0** (`build.zig.zon` pins `minimum_zig_version`). Dependencies are +fetched and pinned by the manifest; no system package is required for the +terminal build. The SDL shell builds SDL3 and FreeType from source. Native PDF +support builds MuPDF and is on by default (`-Dmupdf=false` to drop it). + +## Build + +``` +zig build +``` + +That is the whole of it, and it is an *install*: it builds both native shells and +puts them in `~/.local/bin`. + +``` +~/.local/bin/pardes the terminal shell (libvaxis) +~/.local/bin/pardes-gui the SDL3 window +``` + +Override with `--prefix `. Six development binaries install under +`/dev` so they never land on a `PATH` by accident: `perf`, +`fs-bench`, `lspbench`, `pdf-scroll-bench`, `hxdiff` and the isolated build. +The other steps below build what they need and install nothing. + +``` +pardes --version e.g. pardes 0.0.1 (e61bbb2e86bd) +pardes --help every flag +``` + +The version comes from `build.zig.zon`'s `.version`; the commit is read from +`git` at configure time and is simply absent when there is no repository to ask. + +## The five platforms + +| build | what it is | +|---|---| +| `zig build` | the terminal shell and the SDL window, together | +| `zig build -Dplatform=tty` | the terminal shell alone | +| `zig build -Dplatform=gui` | the SDL3 window alone | +| `zig build web` | a freestanding wasm core plus vanilla JavaScript, rendered as HTML/CSS | +| `zig build -Dplatform=macos` | an AppKit and CoreText app over a static `libpardes.a` | +| `zig build -Dplatform=esp32p4 -Desp32p4-firmware` | firmware for an ESP32-P4: a freestanding riscv32 object driving libvaxis down a UART, in 384 KiB of heap | + +The board build needs an ESP-IDF checkout for its register headers, and adds +`esp32p4-flash`, `esp32p4-attach`, `esp32p4-run`, `esp32p4-reset`, +`esp32p4-image-size`, `esp32p4-image-check` and `esp32p4-test`. + +## Detached sessions + +A shell need not be in the same process as the core. + +``` +pardes --detach=work a core with no terminal of its own +pardes --attach=work become a frontend of it +pardes-gui --attach=work ...the SDL window can attach too +``` + +Several frontends may be attached at once and all see the same screen. The +detached core performs every effect that needs a disk or a process table, so its +pane shells outlive every frontend; a frontend keeps only what needs the human's +own display. From inside the editor, `Attach` and `Detach` do the same thing as +words. See `docs/detached.md`. + +## Tests + +``` +zig build unit-test module and shell unit tests +zig build snap scripted input traces against frozen golden grids +zig build hxdiff differential suite against helix's own behaviour +zig build hxparity file-pane vs pty-pane editing parity +zig build mupdf-check compile, link, render and search docs/design.pdf +zig build web-snap browser touch/LOOK snapshots +zig build web-e2e Chrome-driven DOM end-to-end suite +``` + +`snap` and `hxdiff` take `-- --update` and explicit case files respectively. The +benchmark steps — `perf`, `pdf-bench`, `pdf-scroll-bench`, `pdf-sections-bench`, +`lspbench`, `fs-bench` — all accept `-- --json`. + +## Documentation + +`docs/design.pdf` (from `docs/design.typ`) is the architecture document and the +place to start. It is also a test fixture: `mupdf-check` renders and searches it. + +| file | subject | +|---|---| +| `docs/design.typ` | architecture: the seams, the data model, the build graph | +| `docs/detached.md` | one core, many frontends, over a unix socket | +| `docs/config.md` | build options and runtime configuration | +| `docs/acme-fs.md` | the acme control filesystem (`--fs`) | +| `docs/lsp.md` | the in-process ZLS backend | +| `docs/lsp-evaluation.md` | why that backend, measured against the alternatives | +| `docs/helix-keys.md` | the helix-compatible key model and its differential suite | +| `docs/macos.md` | the native macOS shell, its bundle and its signing | +| `docs/web.md` | the browser shell | +| `docs/ghostty-macos-notes.md` | notes on the ghostty dependency | +| `docs/ideas.typ` | scratch notes; nothing compiles it, and it says so | + +`next-steps.txt` is a wishlist with a status header saying which items have +shipped; `transactions.txt` records one open structural gap against helix, and +says which waiver proves it is still open. + +## Layout + +``` +src/ the core (pardes.zig) and one module per platform +src/detached/ the wire, the detached core, the frontend client +src/lsp/ the in-process language backend +test/ harnesses, snapshot goldens, helix cases +build/ build-time helpers (the snapshot suite) +docs/ see above +``` diff --git a/build.zig b/build.zig index 07620ee1..c67caf20 100644 --- a/build.zig +++ b/build.zig @@ -3,6 +3,9 @@ const builtin = @import("builtin"); const mupdf_build = @import("mupdf.zig"); const snap_build = @import("build/snap.zig"); const grammar_manifest = @import("src/grammar_manifest.zig"); +/// The package manifest, imported UNTYPED on purpose — see the `version` +/// option in `shellOptions` for why that word is load-bearing. +const zon = @import("build.zig.zon"); /// `esp32p4` is not a shell in this package at all: it is one freestanding /// OBJECT, compiled for riscv32-freestanding, which the `zig_p4` firmware @@ -17,11 +20,30 @@ pub const Platform = enum { tty, gui, web, macos, esp32p4 }; /// from this, so there is one string and no drift. pub const macos_min_version: std.SemanticVersion = .{ .major = 13, .minor = 0, .patch = 0 }; -/// The ZLS the language backend links, spelled once. It names the commit -/// build.zig.zon pins (0.16.x branch) and is passed BOTH to ZLS's own -/// `-Dversion-string` (its build.zig otherwise shells out to `git describe`, -/// which fails on a fetched package that has no .git) and to `SPC l i`. +/// The ZLS the language backend links. It names the commit build.zig.zon pins +/// (0.16.x branch) and is passed BOTH to ZLS's own `-Dversion-string` (its +/// build.zig otherwise shells out to `git describe`, which fails on a fetched +/// package that has no .git) and to `SPC l i`. +/// +/// It is spelled TWICE — here and in `.dependencies.zls.url` — and the comment +/// that used to claim "spelled once" was wrong. The duplication is unavoidable +/// (the semver half exists nowhere in the manifest), so the `comptime` block +/// below makes the two AGREE by construction: a .zon bump that forgets this +/// line is now a build error instead of a `SPC l i` that names a build nobody +/// linked. const zls_version = "0.16.1-dev+3e0d0820"; + +comptime { + const plus = std.mem.indexOfScalar(u8, zls_version, '+') orelse + @compileError("zls_version must end in +"); + const short = zls_version[plus + 1 ..]; + const url = zon.dependencies.zls.url; + const hash = std.mem.indexOfScalar(u8, url, '#') orelse + @compileError("the zls dependency url must pin a commit with #"); + if (!std.mem.startsWith(u8, url[hash + 1 ..], short)) @compileError( + "zls_version pins commit " ++ short ++ ", build.zig.zon pins " ++ url[hash + 1 ..], + ); +} pub const TreeSitterGrammars = enum { disabled, zig, minimal, full }; /// The GUI shell's shaders, spelled ONCE. The runtime SPIR-V imports, their @@ -481,6 +503,7 @@ pub fn build(b: *std.Build) void { .theme_animation = theme_animation, .prebuilt_shaders = prebuilt_shaders, .zig_lib_dir = b.graph.zig_lib_directory.path orelse "", + .commit = gitCommit(b), }; // ONE `pardes_config` per distinct frontend in this build: two when the // cli and the gui are made together, one otherwise. hxdiff's core and @@ -1810,6 +1833,39 @@ fn prefixIsUntouched(b: *std.Build) bool { std.mem.eql(u8, b.h_dir, b.pathJoin(&.{ b.install_path, "include" })); } +/// The commit this build came from, or null when there is nothing to say. +/// +/// Read at CONFIGURE time and handed to `pardes_config`, so the binary carries +/// a string rather than the ability to shell out. That is the whole point: a +/// `--version` that runs `git` itself reports the tree it happens to be +/// standing in rather than the one it was built from, and on the board there is +/// no `git` to run and no process to run it with. +/// +/// ABSENCE IS NOT AN ERROR, and must not be. A release tarball has no `.git`, a +/// container may have no `git` binary, and a source drop is not a repository — +/// none of those is a reason to refuse to build. Every way of having no answer +/// (no binary, no repository, a git that exits non-zero, a git that prints +/// nothing) lands on the same `null`, and the frontends print the version alone. +/// +/// Deliberately NOT `--dirty`. Marking a dirty tree costs a worktree stat on +/// every configure, and — the real cost — it would change `pardes_config` on +/// every file edit. Every module in this build imports that options module, so +/// a dirty marker means editing one line rebuilds the world. The commit alone +/// changes only when a commit does. +fn gitCommit(b: *std.Build) ?[]const u8 { + var code: u8 = 0; + // `-C` the build root rather than trusting the cwd: `zig build` may be run + // from anywhere, and a `git` resolved against the wrong directory would + // cheerfully answer about a DIFFERENT repository. + const out = b.runAllowFail( + &.{ "git", "-C", b.build_root.path orelse ".", "rev-parse", "--short=12", "HEAD" }, + &code, + .ignore, + ) catch return null; + const trimmed = std.mem.trim(u8, out, " \t\r\n"); + return if (trimmed.len == 0) null else trimmed; +} + /// Everything `pardes_config` says that does NOT depend on which frontend is /// being built. Gathered into one value so the two options modules a default /// build makes cannot drift apart in any field but the one that is supposed to @@ -1827,6 +1883,10 @@ const ShellConfig = struct { theme_animation: ?bool, prebuilt_shaders: bool, zig_lib_dir: []const u8, + /// Which commit this build came from, or null when there is no answer. Read + /// once in `build()` so the two shells of a default build cannot disagree, + /// and so `git` is spawned once rather than per frontend. + commit: ?[]const u8, }; /// One `pardes_config` options module, for one frontend. `module_names` and @@ -1852,9 +1912,15 @@ fn shellOptions(b: *std.Build, cfg: ShellConfig, platform: Platform) *std.Build. // names: the .zon pins a commit, and a status screen that cannot say WHICH // analyser answered is not worth opening. o.addOption([]const u8, "zls_version", if (cfg.zls_backend) zls_version else "none"); - // Mirrors build.zig.zon's `.version`; kept in sync by hand because a typed - // @import of the .zon rejects its other fields on this Zig 0.16 build. - o.addOption([]const u8, "version", "0.0.1"); + // THE version, read from the manifest rather than copied beside it. The + // `@import` being UNTYPED is what makes that possible: annotating its type + // would demand an exact field match and reject `.dependencies`, `.paths` + // and the rest, which is the failure the hand-synced literal that used to + // sit here was working around. Verified against this exact manifest. + o.addOption([]const u8, "version", zon.version); + // ...and the commit it was built from, when there is one. See `gitCommit` + // for why this is optional and why it is read at configure time. + o.addOption(?[]const u8, "commit", cfg.commit); // The stdlib this binary was compiled against, so `gd` on `std.mem.count` // can open the same mem.zig the compiler used. ZLS resolves `@import("std")` // through `zig_lib_dir` and nothing else; without it every std symbol is a diff --git a/docs/acme-fs.md b/docs/acme-fs.md index ede3ed17..15f45c4f 100644 --- a/docs/acme-fs.md +++ b/docs/acme-fs.md @@ -1,15 +1,20 @@ # The control filesystem — pardes against acme `pardes --fs` serves plan9 [acme(4)](https://man.cat-v.org/plan_9/4/acme)'s control -filesystem over Linux FUSE: a directory per pane holding `addr`, `body`, `ctl`, -`data`, `event`, `tag`, ..., plus `index`, `new/` and `cons` at the root. -A program that opens those files IS an editor extension — no plugin API, no -embedded interpreter, no rebuild. `examples/acmefs/` has four of them. +filesystem over Linux FUSE: a directory per pane, named by the pane's serial and +holding `addr`, `body`, `ctl`, `data`, `errors`, `event`, `rdsel`, `tag`, `wrsel` +and `xdata`, plus `index`, `cons` and `new/` at the root (`PaneFile` and +`TopFile` in `src/acmefs.zig`). A program that opens those files IS an editor +extension — no plugin API, no embedded interpreter, no rebuild. +`examples/acmefs/` has four of them: `clock.py`, `eventlog`, `life.py` and +`pardesctl`. This document is the **comparison report**: what acme does, what pardes does, why they differ, and which one is simpler. acme's C is at -`/home/goblin/05-genizah/principia-softwarica/editors/acme`; every claim below -cites `file:line` on both sides. Where acme is better, it says so. +`/home/goblin/05-genizah/principia-softwarica/editors/acme` — the +principia-softwarica tree and not plan9port, which matters because the two +differ in what they serve; every `file:line` below is that checkout's. Every +claim cites both sides. Where acme is better, it says so. | | acme | pardes | |---|---|---| @@ -20,19 +25,23 @@ cites `file:line` on both sides. Where acme is better, it says so. | node ids | `QID/WIN/FILE` shift macros | `packed struct(u64) { file: u4, serial: u60 }` | | errors | 9P error strings (`Ebadctl`, `Edel`, ...) | errno | | event queue | `realloc` per record, unbounded | length-framed `ArrayList`, capped, drop-oldest | -| `ctl` write | applies the good prefix, then reports | validate-all then apply-all | +| `ctl` write | applies the good prefix, then reports 0 consumed | validate-all then apply-all | ## 1. The service: threads-and-channels against one transaction acme runs a dedicated process for the wire (`proccreate(fsysproc)`, `fsys.c:136`) whose loop reads a 9P message, borrows an `Xfid` from a pool, and dispatches -through a function table (`fsys.c:152-201`). Directory reads and stats are -answered inline; anything that touches a window is handed to that `Xfid`'s own -thread — `sendp(x->c, xfidread)` (`fsys.c:355`) — and `xfidallocthread` creates -**one thread per `Xfid`** on first use (`acme.c:744`), each parked in -`for(;;){ f = recvp(x->c); (*f)(x); ... }` (`xfid.c:64-74`). Serialisation is by -`QLock`: one on the row (`dat.h:329`), one per window plus an owner byte -(`wind.c:135`), one on the mount table (`fsys.c:270`). +through the `fcall[x->type]` function table (`fsysproc`, `fsys.c:140-193`; the +table itself at `fsys.c:41`, the dispatch at `fsys.c:190`). Directory reads and +stats are answered inline; anything that touches a window is handed to that +`Xfid`'s own thread — +`sendp(x->c, xfidread)` (`fsys.c:660`) — and `xfidallocthread` creates **one +thread per `Xfid`** on first use (`acme.c:718-744`), each parked in +`for(;;){ f = recvp(x->c); (*f)(x); ... }` (`xfidctl`, `xfid.c:42-55`). +Serialisation is by `QLock`: one on the row (`dat.h:313`), one per window plus an +owner byte (`dat.h:228` and `dat.h:250`, taken together in `winlock1`, +`wind.c:131-136`), one on the mount table (`struct Mnt`, `fsys.c:96`, taken at +`fsys.c:201`). pardes has none of that. A request is a value, an answer is a value, and the whole filesystem is one function: @@ -52,37 +61,73 @@ already followed. **Simpler: pardes, by a lot.** No channels, no locks, no thread per request, no fid bookkeeping, and the semantics are unit-testable with no scheduler and no -FUSE anywhere near them (`src/acmefs.zig` has 28 such tests). +FUSE anywhere near them (`src/acmefs.zig` has 21 such tests). **What acme buys, honestly:** isolation. Its request threads mean a slow read cannot stall the editor. In pardes `handle` runs on the loop thread, so a pathological request — reading the body of a 100 MB file, a `ctl get` that re-reads a huge file from disk — is a frame the user waits for. The measured -numbers say this is theoretical rather than practical (`zig build fs-bench`: -20-52 ns per request, and a 1 MiB `body` read costs the same 25 ns as a 4 KiB -one because it is zero-copy), but it is a real property of the design and the -reason acme's complexity exists. +numbers say this is theoretical rather than practical, but it is a real property +of the design and the reason acme's complexity exists. + +Every measurement in this document is one run of `zig build fs-bench +-Doptimize=ReleaseFast` on an i7-11700. Each row is `handle` called directly — +no FUSE, no thread — and its figure is the MEAN over the row's reps: 100 000, +except 10 000 for the 1 MiB read, 200 for the append and 2000 per keystroke row. + +| row | per request | allocations, whole row | +|---|---|---| +| `getattr` on a 1 MiB body | 19 ns | 0 | +| `lookup ctl` | 39 ns | 0 | +| `read body`, 4 KiB | 25 ns | 0 | +| `read body`, 1 MiB | 25 ns | 0 | +| `read ctl` | 404 ns | 0 | +| `read index` | 631 ns | 0 | +| `readdir` of the root | 39 ns | 0 | +| `read event` on an empty queue (`Status.again`) | 21 ns | 0 | +| `write body`, 1 KiB appended to a body growing from 1 MiB | 3.36 ms | 600 | + +Two things in that table are the point of having it. The rows that FORMAT — +`ctl` and `index`, which `bufPrint` a line of `%11d` fields — cost about twenty +times a row that hands back a slice, and are still well under a microsecond. And +a 1 MiB `body` read costs exactly what a 4 KiB one does, because it is +zero-copy: `Payload.region` is a window onto the pane's live text. Every read +row allocates nothing at all, which is a property the benchmark exists to check +rather than a pleasing number — a non-zero count there would mean a read had +stopped answering out of the live text or out of the staging buffer that is +cleared and never freed. The one row that allocates is the append, at 600 +allocations across 200 writes, and that is the core's whole-body swap plus its +undo snapshot rather than anything this filesystem does. ## 2. Blocking reads: a parked thread against a returned value -acme's `event` read blocks: `xfideventread` (`xfid.c:553-582`) stores its `Xfid` -in `w->eventx`, unlocks the window and sleeps on a channel; `winevent` -(`wind.c:543-569`) appends the record and wakes it; `windelete` -(`wind.c:217-225`) wakes it with no data so it can answer "window shut down"; -and `xfidflush` (`xfid.c:77-102`) exists solely to cancel a parked reader, +acme's `event` read blocks: `xfideventread` (`xfid.c:994-1025`) stores its `Xfid` +in `w->eventx`, unlocks the window and sleeps on a channel (`xfid.c:1008-1010`); +`winevent` (`wind.c:543-569`) appends the record and wakes it; `windelete` +(`wind.c:217-225`) wakes it with no data so it can answer "window shut down" +(`xfid.c:1005`); and `xfidflush` (`xfid.c:58-87`) exists solely to cancel a +parked reader — it walks every column and every window looking for the tag, because a blocked 9P read cannot otherwise be interrupted. pardes returns `Status.again` — "nothing consumed, ask me again" — and that is the entire blocking primitive. The core keeps no waiter, no channel, no cancel -path. The transport parks the kernel's request (32 slots, `src/fuse.zig`) and -re-submits it once per frame; a `FUSE_INTERRUPT` answers the original with -`-EINTR`, which is what keeps a SIGKILLed reader from sitting in uninterruptible -sleep forever (verified live: 40 concurrent blocked readers, all reaped). +path. The transport parks the kernel's request (`max_slots = 32`, `src/fuse.zig`) +and re-offers it once per frame, oldest first, with a per-round flag so a +permanently blocked reader cannot starve the others (`retry`). A `FUSE_INTERRUPT` +answers the original with `-EINTR`, which is what keeps a SIGKILLed reader out of +permanent uninterruptible sleep: after a fatal signal `fuse_dev`'s final +`wait_event` is not killable, so the process survives its own kill until the +server replies. Three tests pin that mechanism — "again holds the request, retry +offers it back once per round", "interrupt answers the original with EINTR and +drops it", and "a full table answers EAGAIN and keeps the descriptor flowing", +whose comment records that the tempting alternative, gating the read on a free +slot, wedges a real mount: the INTERRUPT that would free a slot is never read +either. Two deliberate differences: - acme hands back up to `count` bytes and keeps the remainder, so a small read - can split a record (`xfid.c:378-380`). pardes refuses a read smaller than one + can split a record (`xfid.c:1015-1017`). pardes refuses a read smaller than one record with `EINVAL`: half a record is unparseable and silently desynchronises a client. - acme's parked thread survives the client's death (it stays blocked until the @@ -95,10 +140,11 @@ are bounded by the park table because each one is a held kernel request. ## 3. Node identity acme packs a qid with macros: `QID(w,q) ((w<<8)|(q))`, `WIN(q)`, `FILE(q)` -(`dat.h:434-436`) — 24 bits of window id, 8 of file id, no validation. A window +(`dat.h:463-465`) — 24 bits of window id, 8 of file id, no validation. A window that dies while a client holds a file open is detected structurally, by every -handler remembering to check `if (w->col == nil) respond(..., Edel)` -(`xfid.c:414-421` and a dozen more). +handler remembering to check `if(w->col == nil) respond(..., Edel)` +(`xfid.c:422-425` in `xfidwrite`, and a dozen more; the string is at +`xfid.c:19`). pardes uses the type system: @@ -120,12 +166,14 @@ methods, and `Edel`-by-convention becomes `ENOENT` by construction. ## 4. Addressing: runes against bytes -acme's document is `Rune*`, so every read converts. `xfidutfread` keeps a -byte↔rune cache per window and, when it misses, scans from the beginning — -carrying the comment `/* BUG: stupid code: scan from beginning */` -(`xfid.c:855`). The address language lives in `addr.c`: `address()`, `number()`, -`regexp()`, with failure reported through two out-parameters and patterns grown -one rune at a time. +acme's document is `Rune*`, so every read converts. `xfidutfread` +(`xfid.c:875`) keeps a byte-to-rune cache per window — `w->utflastqid`, +`utflastboff`, `utflastq` (`xfid.c:891-897`) — and, when it misses, scans from +the beginning, carrying the comment `/* BUG: stupid code: scan from beginning */` +(`xfid.c:895`). The address language lives in `addr.c`: `number()` +(`addr.c:52`), `regexp()` (`addr.c:119`), `address()` (`addr.c:150`), with +failure reported through out-parameters (`int *evalp`, `uint *qp`) and patterns +grown one rune at a time. pardes is byte-addressed end to end (selections, look spots, LSP offsets), so **every offset in this filesystem is a byte offset**, clamped to grapheme @@ -142,39 +190,48 @@ promises; ours promises bytes. ## 5. Events, and the inversion that makes this a plugin API -The record is the same on both sides, byte for byte: origin char, type char, -four blank-separated decimals, the text, a newline -(`wind.c:543-569` + `text.c:377-382`; `formatRecord` in `src/acmefs.zig`). +The record is the same on both sides, byte for byte: origin char, type char, four +blank-separated decimals, the text, a newline. acme builds it in two halves — +`text.c:380` formats `"%c%d %d 0 %d %.*S\n"` and `winevent` prepends the owner +byte at `wind.c:561` — and pardes builds it in one, `formatRecord` +(`src/acmefs.zig`). The rule that matters is the inversion: **while a script holds a pane's `event` file open, buttons 2 and 3 in that pane belong to the script.** acme spells it -`if (!external && t->w->nopen[QWevent] > 0) { winevent(...); return; }` +`if(!external && t->w!=nil && t->w->nopen[QWevent]>0){ winevent(...); return; }` (`exec.c:150`, `look.c:35`); pardes spells it as the return value of `noteAction` — "true means the core must not perform it" — checked in `dispatchPointerBuiltin`. That is how `examples/acmefs/life.py` puts `Step Run Stop Clear Random` in a tag pardes has never heard of and makes them -work. `test/snapshots/acmefs-event.snap` is the A/B proof: with a reader -attached, a middle click on the word `Newcol` changes nothing on screen and -delivers `MX0 6 1 6 Newcol`; with the reader gone, the same click opens a column. +work. `test/snapshots/acmefs-event.snap` drives the A/B proof and +`acmefs-event.golden` records it: with a reader attached, a middle click on the +word `Newcol` changes nothing on screen and delivers `MX0 6 1 6 Newcol` +(`acmefs-event.golden:9`); with the reader gone, the same click opens a column. Differences worth knowing: -- **Keyboard equivalents are not suppressed** (acme has none to suppress). A - scripted pane stays editable, and a script that dies mid-run cannot leave you - unable to execute anything in it. - **Write-back takes four fields only** — `origin type q0 q1\n`, type in `xXlL` - — exactly as `xfideventwrite` demands (`xfid.c:791-830`, which rejects - anything else with `Ebadevent`). There is no text field, so a client that - wants to run text not already on screen appends it to the tag and execs that - range; `examples/acmefs/pardesctl exec` does precisely this, and it is how + — exactly as `xfideventwrite` demands (`xfid.c:791-872`: it reads the origin + byte, the type char, two `strtoul`s and a mandatory newline, and its `switch` + takes `x`, `X`, `l`, `L` and sends everything else to `Rescue`, which is + `err = Ebadevent`). There is no text field, so a client that wants to run text + not already on screen appends it to the tag and execs that range; + `examples/acmefs/pardesctl exec` does precisely this — `printf ' %s' "$text" + >>$d/tag` and then `printf 'Mx%d %d\n' "$q0" "$q1" >>$d/event` — and it is how acme clients have always done it. - pardes validates a whole batch of records before performing any of them; acme - performs them as it parses. -- acme records the origin byte the window's lock owner claimed; pardes sets - `State.origin` once per update from the event kind (`K` keyboard, `M` mouse, - `E`/`F` a filesystem write). Same fidelity for every real case, one field - instead of a lock argument. **acme is arguably better here**: its owner byte is - per-record provenance, and a writer can re-attribute an action. + performs them as it parses, which `writeEvent`'s own comment in + `src/acmefs.zig` names as the reason: a malformed batch is otherwise + half-applied and unrepeatable. +- acme records the origin byte the RECORD claimed — `w->owner = *p++;` with + `/* disgusting */` beside it (`xfid.c:812`) — and stamps it onto every record + it later produces (`wind.c:561`). pardes sets `State.origin` once per update + from the event kind (`K` keyboard, `M` mouse, `E` a write to body or tag + through this filesystem, `F` an action through one of its other files), + parses the character on the way in and drops it. **acme is arguably better + here**: its owner byte is per-record provenance and a writer can re-attribute + an action. Against that, a record saying where it came from is worth nothing + when the sender picks the answer, which is why pardes does not read it. ## 6. Reporting edits: known ranges against a diff @@ -184,96 +241,110 @@ range: `textinsert` emits `I` with `q0, q0+n` and the inserted runes pardes has no such pair — every edit lands in one place as a whole new buffer (`file_pane.setContent`) — so the range is recovered by diffing there: -`acmefs.diffSpan` skips the common prefix and suffix through vectorised -compares, and `noteReplace` emits the deletion then the insertion, the same two -records in the same order. It is behind `p.fs.listeners != 0`, so an editor -nobody is scripting pays one branch. Measured cost of a keystroke with a -listener attached on a 32 KiB body: **+1.6%** (`zig build fs-bench`). +`acmefs.diffSpan` skips the common prefix and suffix in 64-byte chunks through +`std.mem.eql`, which lowers to vectorised compares, and `noteReplace` emits the +deletion then the insertion, the same two records in the same order. The +vectorising is not premature: its own comment records that the byte-at-a-time +loop it replaced cost 2.4x per keystroke on a 40 KB body. The whole path is +behind `p.fs.scripted(id)` — that pane's reader count, not the session-wide +`listeners` total — so an editor nobody is scripting pays one branch. Measured +cost of a keystroke on a 32 KiB body: 32.9 µs with no listener against 34.1 µs +with one, **+3.9%**. **acme is better here in principle** — a known range beats a scan — and it pays for it by routing every mutation through a pair of functions that carry ranges everywhere. pardes's single funnel is worth more than the scan costs. Undo grouping is acme's `mark`/`nomark` on both sides: acme bumps a global -sequence number and merges an `elog` of edits (`elog.c`, `xfid.c:501-504`); -pardes suppresses the per-write `pushUndo` snapshot. Same verb, same effect on -the user's `u`, much less machinery — and the reason it matters is measurable: -an append to a 1 MiB body costs 25 ms because of that snapshot, so a script -writing a batch should say `nomark` first. +sequence number and merges an `elog` of edits (`elog.c`; `if(w->nomark == FALSE) +{ seq++; filemark(t->file); }` at `xfid.c:501-504`); pardes suppresses the +per-write `pushUndo` snapshot. Same verb, same effect on the user's `u`, much +less machinery — and the reason it matters is measurable: the benchmark's append +row is 3.36 ms per 1 KiB write against a body around a megabyte, almost all of +it the whole-body swap and that snapshot, so a script writing a batch should say +`nomark` first. ## 7. `ctl` -Both print the same five `%11d` fields (`winctlprint`, `wind.c:532-537`), and -pardes adds acme's three extras with the one honest substitution: width and tab -in **cells**, because pardes is a character grid where acme has pixels. +Both print the same five `%11d` fields — id, tag length, body length, isdir, +dirty (`winctlprint`, `wind.c:534-535`) — and pardes adds acme's three extras +(`wind.c:537-538`) with the one honest substitution: width and tab in **cells**, +because pardes is a character grid where acme has pixels (`Dx(w->body.r)`). The verb parsers differ in two ways that matter: - acme matches verbs by **prefix** with `strncmp` and advances by the matched - length, so the table order is load-bearing (`delete` before `del`, `nomark` - before `mark`). pardes matches a whole token through - `std.meta.stringToEnum`, which makes that class of bug unrepresentable. -- acme applies verbs as it parses and reports the byte count it consumed, so a - bad verb leaves the good prefix applied (`xfid.c:778-781`). pardes validates - every verb first and then applies them, because a short count on a Linux - `write(2)` is not read by anybody as "the rest failed". **acme is more - expressive here** — a 9P client can stream verbs and learn where it stopped — - and pardes trades that for atomicity. - -Verbs pardes cannot honour are refused loudly with a reason -(`dump`, `dumpdir`, `font`, `menu`, `nomenu`, `lock`, `unlock`) rather than -silently accepted. + length (`xfid.c:602-767`), so the table order is load-bearing: `delete` + (`xfid.c:697`) must precede `del` (`xfid.c:701`), `nomark` (`xfid.c:738`) + `mark` (`xfid.c:742`), `nomenu` `menu`, `noscroll` `scroll`. pardes matches a + whole token through `std.meta.stringToEnum`, which makes that class of bug + unrepresentable. +- acme applies verbs as it parses, so a bad verb leaves the good prefix applied + — the mutations already made stand — and then reports **zero** bytes consumed: + `err = Ebadctl` (`xfid.c:769`) falls through to `if(err) n = 0; fc.count = n;` + (`xfid.c:780-782`), and `xfideventwrite` repeats it verbatim at + `xfid.c:863-865`. So the client learns that it failed but not where, and the + editor has already been half-changed. pardes validates every verb first and + then applies them: a short count on a Linux `write(2)` is not read by anybody + as "the rest failed", and a half-applied batch is unrepeatable. **This is not + a trade**; the atomic answer is simply the better one. + +Verbs pardes cannot honour are refused loudly with a reason — +`refused_verbs` is `dump`, `dumpdir`, `font`, `lock`, `menu`, `nomenu`, +`unlock` — rather than silently accepted. ## 8. Errors -acme answers with strings: `Ebadctl` "ill-formed control message", `Ebadaddr` -"bad address syntax", `Eaddr` "address out of range", `Edel` "deleted window" -(`xfid.c:20-30`), handed through `respond(x, &fc, err)`. pardes answers with an -errno, because that is the only channel FUSE has: the client would never see the -string. Two acme errors that differ in wording collapse to `EINVAL` here, which -is a real loss of diagnostics — the message row and `PARDES_LOG` carry the -detail instead. +acme answers with strings: `Edel` "deleted window", `Ebadctl` "ill-formed control +message", `Ebadaddr` "bad address syntax", `Eaddr` "address out of range", +`Ebadevent` "bad event syntax" (`xfid.c:19-24`), handed through +`respond(x, &fc, err)`. pardes answers with an errno, because that is the only +channel FUSE has: the client would never see the string. Two acme errors that +differ in wording collapse to `EINVAL` here, which is a real loss of +diagnostics — the message row and `PARDES_LOG` carry the detail instead. ## 9. Memory and bounds acme grows `w->events` with `realloc` and never caps it (`wind.c:560`), and -re-allocates the remainder on every partial read (`xfid.c:578-580`). A client -that stops reading grows that buffer until `emalloc` fails and acme aborts. +re-allocates the remainder on every partial read — `w->events = +estrdup(w->events+n); free(b);` (`xfid.c:1022-1023`). A client that stops reading +grows that buffer until `emalloc` fails and acme aborts. pardes's queue is length-framed (records contain newlines, so a length is the -only way to hand one back whole), capped at 64 KiB per pane, and drops the -oldest record when full: an editor must not stall or grow without bound because -a script stopped reading, and a reader that far behind can re-read `body` and -resynchronise. Formatted answers go into one staging buffer that is cleared and -never freed, which is why every read in the benchmark reports **zero -allocations**. **acme's unbounded buffer is a flaw, not a feature.** +only way to hand one back whole), capped at 64 KiB per pane (`queue_cap`), and +drops the oldest record when full: an editor must not stall or grow without bound +because a script stopped reading, and a reader that far behind can re-read `body` +and resynchronise. Formatted answers go into one staging buffer that is cleared +and never freed, which is why every read in the benchmark reports zero +allocations. **acme's unbounded buffer is a flaw, not a feature.** ## 10. C-isms Zig removed Ranked by what they cost when they go wrong: 1. **Threads and channels standing in for a state machine** — one thread per - in-flight request (`acme.c:744`, `xfid.c:64-74`) → a `Status.again` return + in-flight request (`acme.c:718-744`, `xfid.c:42-55`) → a `Status.again` return value and a park table in the transport. -2. **Macro-packed qids** — `QID/WIN/FILE` (`dat.h:434-436`) → `packed +2. **Macro-packed qids** — `QID/WIN/FILE` (`dat.h:463-465`) → `packed struct(u64)` with one validating constructor. -3. **`Rune*` plus a byte↔rune cache** with a scan-from-zero fallback - (`xfid.c:855`) → byte slices clamped to grapheme boundaries. -4. **`strtoul` pointer walking with `goto Rescue`** (`xfid.c:791-830`) → a slice +3. **`Rune*` plus a byte-to-rune cache** with a scan-from-zero fallback + (`xfid.c:891-897`) → byte slices clamped to grapheme boundaries. +4. **`strtoul` pointer walking with `goto Rescue`** (`xfid.c:810-838`) → a slice reader returning `?u32`. -5. **`longjmp`-ish `error()`** that aborts the process (`util.c`) → an error - union and a `Reply` value. +5. **`longjmp`-ish `error()`** that aborts the process (`util.c:50-55`) → an + error union and a `Reply` value. 6. **Manual `realloc` growth** (`wind.c:560`) → `ArrayList` with retained capacity. -7. **Sentinel-terminated tables** (`fsys.c:57-73`) → exhaustive enums, so adding - a file to the tree does not compile until every switch has an answer for it. -8. **`sprint` into fixed buffers** (`wind.c:533`) → `bufPrint` returning an +7. **Sentinel-terminated tables** (`dirtab`, `fsys.c:62-74`; `dirtabw`, + `fsys.c:76-91`) → exhaustive enums, so adding a file to the tree does not + compile until every switch has an answer for it. +8. **`sprint` into fixed buffers** (`wind.c:534`) → `bufPrint` returning an error. 9. **Ownership by convention** — `fbufalloc`/`fbuffree` pairs the caller must match (`fns.h:5-6`) → `defer`, plus two explicit borrow windows (`Payload.staged`, `Payload.region`) documented at the seam. 10. **Prefix-matched command tables** whose order is load-bearing - (`xfid.c:283+`) → whole-token enum lookup. + (`xfid.c:602-767`) → whole-token enum lookup. One property comes along with the transport rather than with either design: a 9P `Twrite` IS a message, so acme never sees a fragment, while a POSIX client @@ -286,17 +357,21 @@ would trade a clear error for a half-applied verb that never completes. ## What is not served, and why -`acme`, `draw`, `consctl`, `label`, `editout` — acme keeps them for rio and for -its own `Edit` language, neither of which pardes has. `xdata`, `rdsel`, `wrsel`, -`index`, `cons` and `new/` are all here. `log` is NOT: it is a plan9port -addition this acme's `dirtab` does not have, no example needed it, and a script -that wants to notice panes it did not open reads `index` — which is what acme -gives it. It cost a second queue, a focus hook in the core and ~100 lines, and -it went out in review. +`acme`, `consctl`, `draw`, `editout`, `label` — acme's root `dirtab` +(`fsys.c:62-74`) keeps them for rio and for its own `Edit` language, neither of +which pardes has, and `editout` appears in the per-window `dirtabw` +(`fsys.c:76-91`) for the same reason. Everything else in `dirtabw` is here: +`addr`, `body`, `ctl`, `data`, `errors`, `event`, `rdsel`, `tag`, `wrsel`, +`xdata`, and `index`, `cons` and `new/` at the root. `log` is NOT — and this is +the one entry that is not a decision about acme, because this acme's `dirtab` +has no `log` either; it is a plan9port addition. No example needed it, and a +script that wants to notice panes it did not open reads `index`, which is what +acme gives it. ## Reading order `src/acmefs.zig` (semantics; start at its header), `src/fuse.zig` (the wire), `src/fs_service.zig` (mount lifecycle), `examples/README.md` (the client's view), -`test/snapshots/acmefs.snap` and `acmefs-event.snap` (what is proven end to end), +`test/snapshots/acmefs.snap` and `acmefs-event.snap` (the drivers, i.e. what is +exercised end to end) beside their `.golden` files (what was observed), `zig build fs-bench` (what it costs). diff --git a/docs/config.md b/docs/config.md index 11346f4e..58797dfe 100644 --- a/docs/config.md +++ b/docs/config.md @@ -12,11 +12,14 @@ main command file is named `init`: On the two unixes `XDG_CONFIG_HOME` counts only when it is ABSOLUTE, as the XDG base-directory specification requires; an empty or relative value falls -back to the home-directory form. Windows never consults it. An `init` of 1 MiB -or more, or one that cannot be read, is treated as no file at all — the path -still resolves, because "nothing is there yet" is the answer `Config` exists -to give. There is one case with no path at all: a native launch with no `HOME` -set, which `Config` reports as such. +back to the home-directory form (`user_config.xdgBase`, and the test beside +it). Windows never consults it. An `init` that does not fit the `max_bytes` +read limit — 1 MiB, `src/user_config.zig` — or that cannot be read at all is +treated as no file: `load` takes the `readFileAlloc` error and keeps going. The +path still resolves, because "nothing is there yet" is the answer `Config` +exists to give. There is one case with no path at all: a native launch with no +`HOME` set (or, on Windows, neither `%LOCALAPPDATA%` nor `%USERPROFILE%`), +which `Config` reports as `no per-user config path`. `Config` (`SPC f c`, or the word executed anywhere) opens one refreshable `+Config` pane. It reports the startup path and every live config-like value: @@ -26,10 +29,16 @@ and size, tagline scale, panel transition, scene effects, hover delay, platform, native-image support, and (on SDL) whether the executable uses live-built shaders or the paired prebuilt shader snapshot. Platform-dependent rows say `unsupported` instead of looking like an off or -empty supported setting: TTY reports Font and scene shaders as unsupported; -web reports Font, panel transitions, and scene shaders as unsupported. Tagline -font size is its own row and capability — GUI font selection is native-only, -while the browser still applies the compiled tagline percentage to its DOM. +empty supported setting. The four fields of `runtime_config.Capabilities` gate +them and are stated once as plain data in `builtins.capabilities`: +`font_picker` is the SDL GUI and +native macOS only, `scene_shaders` the same two, `panel_transitions` every +hosted shell, and `tagline_font_size` everything but the TTY and the board. So +the TTY reports Font, TaglineSize and the scene shaders as unsupported; the +browser reports Font, panel transitions and the scene shaders as unsupported, +and its TaglineSize row reads `82% (build-time only)` — tagline font size is +its own capability precisely because GUI font SELECTION is native-only while +the browser still applies the compiled percentage to its DOM glyphs. The startup path is printed whether or not a file exists — that is usually when it is most useful — and is ordinary selectable text, so a right click on it opens the file. Shell follows the same requested/effective/pending model as @@ -64,19 +73,25 @@ Wrap ``` A line matches a builtin whose name takes NO argument only as that whole word: -`Kill` runs, `Kill something` does not. Builtins that take one (`Theme`, -`ThemeFile`, `Font`, `TaglineSize`, `Shell`, `Restore`, `Find`, `Grep`, -`Rename`, `WsSymbols`, `Look`, `Exec`, `EffectCode`) take everything after the -name as the argument. +`Kill` runs, `Kill something` does not. A builtin that takes one +(`takes_arg` in `src/builtins.zig`, or a `settings` row whose action is +`shell`, `theme`, `font` or `tagline_size` in `src/runtime_config.zig`) takes +everything after the name as the argument. On a native build that is `Theme`, +`ThemeFile`, `Font`, `TaglineSize`, `Shell`, `Save`, `Restore`, `Attach`, +`Find`, `Grep`, `Rename`, `WsSymbols`, `Look`, `Exec`, `Msg` and `EffectCode`. +(`Peek`, `Poke`, `Hexdump` and `Gpio` take one too, but they exist only where +`board_memory.enabled` holds, and that build has no config file.) `Theme ` wants one of the 228 names in the ring. Do not derive the spelling — read it off `ThemeSel` (`SPC t t`), which lists every one as the -exact `Theme ` line that selects it. The generator lowercases, folds -punctuation runs to a single `_` and then TRIMS leading and trailing ones -(`penumbra+.toml` is `penumbra`, not `penumbra_`), and it suffixes every theme -that came from zed with `_zed` so it cannot collide with a helix theme of the -same name (zed's "Ayu Mirage" is `ayu_mirage_zed`; `ayu_mirage` is helix's). -A name that is not in the ring is ignored. +exact `Theme ` line that selects it. `slug` in `tools/gen_themes.zig` +lowercases, folds punctuation runs to a single `_` and then TRIMS leading and +trailing ones (`penumbra+.toml` is `penumbra`, not `penumbra_`), and every +variant read out of a zed `.json` gets `_zed` on the end so it cannot collide +with a helix theme of the same name — zed's "Ayu Mirage" is `ayu_mirage_zed` +and `ayu_mirage` is helix's `ayu_mirage.toml`. The suffix goes on all of them +rather than only the eight that clash today, so a name cannot move when either +project gains or loses a file. A name that is not in the ring is ignored. ## Runtime theme files @@ -114,10 +129,12 @@ Execute `DumpThemes` to write every theme compiled into the executable to: The command replaces those generated reference files but leaves unrelated files alone. Copy one into `themes/`, rename it, change its `.name`, and use it as the starting point for a custom theme. The dumped file is also the complete -format: one ZON struct with the theme name, the fifteen RGB roles, nullable -`bg`/`fg`, and either a 16-color RGB `palette` or `null`. RGB values are -three-byte arrays, and hex literals are accepted. There is no inheritance or -partial override layer. +format, and it is `pardes.Theme` serialised by `std.zon.stringify`: the theme +name, thirteen required RGB roles (`sel_bg`, `sel_fg`, `tag_bg`, `tag_fg`, +`box`, `box_dim`, `kw`, `str`, `num`, `comment`, `lineno`, `scroll_track`, +`scroll_thumb`), nullable `bg`/`fg`, and either a 16-color RGB `palette` or +`null`. RGB values are three-byte arrays, and hex literals are accepted. There +is no inheritance or partial override layer. `Shell ` sets the binary that the NEXT terminal pane execs; panes already open keep the shell they are running. A bare name is resolved against @@ -158,10 +175,16 @@ Mono, then installed `NotoSansMono-Regular`, `DejaVuSansMono`, `SymbolsNerdFont-Regular`, `NotoSansSymbols2-Regular`, `NotoSansSymbols-Regular`, and `DejaVuSans`, in that order, missing entries skipped; the rasterized glyphs are retained in its GPU atlas. The macOS shell -has none of that and needs none: it embeds no font, substitutes the system -monospaced face (else Menlo) when the NAME you asked for will not load, and -leaves per-codepoint fallback to CoreText's own cascade when it draws. What it -caches is glyph ids, not pixels. +has none of that and needs none. It embeds no font. Its default face is +`NSFont.monospacedSystemFont`, resolved through the descriptor rather than by +name, and `Menlo` only if that face turns out not to be fixed-pitch — a +proportional face in a fixed grid is a broken screen, not a cosmetic problem +(`PardesView.defaultFace`). A `Font ` arrives as a PATH the core already +resolved by walking the font directories, and a file CoreText cannot measure +leaves the face already on screen rather than substituting one, because the +alternative is a terminal with no way back out (`PardesView.fromFile`). +Per-codepoint fallback is CoreText's own cascade at draw time. What the shell +caches is `CGGlyph` ids, not pixels. Blank, unknown, malformed, or unsuccessful lines are ignored silently, and a bad line does not prevent later lines from running. Top-level text that is not @@ -263,9 +286,9 @@ prebuilt executable. ## Delayed Look preview The preview is enabled by default. Moving the pointer onto selectable text and -leaving it still for `look_preview_delay_frames` (2 animation ticks, roughly -33 ms at the 60 Hz animation cadence) paints a -subtle theme-derived preview of the exact operand a right-click Look would receive. +leaving it still for `look_preview_delay_frames` — 2 animation ticks, and +`animation.frame_ms` is 16, so about 32 ms — paints a subtle theme-derived +preview of the exact operand a right-click Look would receive. Repeated motion reports in the same semantic grid cell do not restart the delay. The preview uses the same side-effect-free word/path expansion as Look; it does not focus a pane, move a cursor, install a selection, activate a PDF @@ -277,3 +300,46 @@ Set this compile-time option in `src/config.zig` to disable the feature: ```zig pub const look_preview_delay_frames: ?u16 = null; ``` + +## Build-time configuration + +Everything above is chosen at runtime or in `src/config.zig`. The build itself +takes these, and this is the whole list — every `b.option` in `build.zig`, +besides `-Dtarget` and `-Doptimize` from `standardTargetOptions` and +`standardOptimizeOption`: + +| option | values | default | +|---|---|---| +| `-Dplatform` | `tty`, `gui`, `web`, `macos`, `esp32p4` | absent builds the tty cli and the SDL gui together | +| `-Dstatic` | bool | `false` | +| `-Dmupdf` | bool | on for a native target, off for web and esp32p4 | +| `-Djpx` | bool | `true` — JPEG 2000, and with it scanned PDFs | +| `-Dtree-sitter` | `disabled`, `zig`, `minimal`, `full` | `full` natively, `zig` for web, `disabled` for esp32p4 | +| `-Dtheme-animation` | bool | on everywhere except `-Dplatform=esp32p4` | +| `-Dprebuilt-shaders` | bool | on for a bare `zig build`, off when `-Dplatform` names a shell | +| `-Dtracy` | path to a Tracy source checkout | off | +| `-Dmacos-identity` | codesigning identity for `pardes.app` | `-` (ad-hoc) | +| `-Ddump` | a `dump.zon` to embed in the web shell | none | +| `-Dtest-filter` | substring; run only tests whose name contains it | none | +| `-Desp32p4-cols` | u16, the board's grid width in cells | `56` | +| `-Desp32p4-rows` | u16, the board's grid height in cells | `14` | +| `-Desp32p4-cpu-mhz` | u16: `90`, `180` or `360` | `90`, the bootloader default | +| `-Desp32p4-port` | serial port the board is wired to | `/dev/ttyUSB0` | +| `-Desp32p4-prof` | bool; per-phase cycle counts for every frame | `false` | +| `-Desp32p4-firmware` | bool; also build the flashable image and its board steps | `false` | + +The five `-Desp32p4-*` options that are not `-Desp32p4-firmware` are registered +unconditionally rather than inside the `-Desp32p4-firmware` block that consumes +them, so `zig build --help` lists them and passing one without the flag is not +an "unknown option" error. + +Two build inputs reach the running binary as ordinary values rather than as +behaviour. `build.zig` reads `.version` from `build.zig.zon` through an untyped +`@import("build.zig.zon")` — one place to bump — and `gitCommit(b)` reads the +revision at configure time. Both land in `pardes_config` and are re-exported as +`pardes.version` and `pardes.commit`, and `pardes --version` prints +`pardes ()`, or `pardes ` alone when there is no +commit: a tarball, a container with no `git`, or a checkout outside version +control all yield null, and the flag has to work anyway. Neither is a question +asked at runtime — a binary that shelled out to `git` would describe whatever +tree it was standing in rather than the one it came from. diff --git a/docs/design.pdf b/docs/design.pdf index b5b8a427..02363da3 100644 --- a/docs/design.pdf +++ b/docs/design.pdf @@ -2,1700 +2,7766 @@ %€€€€ 1 0 obj -<> +<> endobj 2 0 obj -<> +<> endobj 3 0 obj -<> +<> endobj 4 0 obj -<> +<> endobj 5 0 obj -<> +<> endobj 6 0 obj -<> +<> endobj 7 0 obj -<> +<> endobj 8 0 obj -<> +<> endobj 9 0 obj -<> +<> endobj 10 0 obj -<> +<> endobj 11 0 obj -<>/K[395 0 R]/ParentTree<>/ParentTreeNextKey 7>> +<> endobj 12 0 obj -[19 0 R 21 0 R 22 0 R 23 0 R 23 0 R 23 0 R 23 0 R 25 0 R 25 0 R 25 0 R 24 0 R 25 0 R 25 0 R 25 0 R 26 0 R 28 0 R 27 0 R 28 0 R 28 0 R 28 0 R 29 0 R 36 0 R 31 0 R 36 0 R 32 0 R 36 0 R 36 0 R 33 0 R 36 0 R 36 0 R 34 0 R 36 0 R 35 0 R 36 0 R 36 0 R 36 0 R 38 0 R 40 0 R 40 0 R 40 0 R 40 0 R 47 0 R 43 0 R 47 0 R 47 0 R 47 0 R 44 0 R 47 0 R 47 0 R 47 0 R 45 0 R 47 0 R 47 0 R 46 0 R 47 0 R 48 0 R 49 0 R 66 0 R 50 0 R 66 0 R 51 0 R 66 0 R 52 0 R 66 0 R 66 0 R 53 0 R 66 0 R 66 0 R 54 0 R 66 0 R 55 0 R 66 0 R 56 0 R 66 0 R 57 0 R 66 0 R 58 0 R 66 0 R 59 0 R 66 0 R 60 0 R 66 0 R 61 0 R 66 0 R 66 0 R 62 0 R 66 0 R 63 0 R 66 0 R 64 0 R 66 0 R 65 0 R 66 0 R 66 0 R 66 0 R 66 0 R] +<> endobj 13 0 obj -[67 0 R 72 0 R 68 0 R 72 0 R 69 0 R 72 0 R 72 0 R 70 0 R 72 0 R 72 0 R 72 0 R 72 0 R 72 0 R 72 0 R 71 0 R 72 0 R 72 0 R 73 0 R 96 0 R 74 0 R 96 0 R 75 0 R 96 0 R 76 0 R 96 0 R 77 0 R 96 0 R 78 0 R 96 0 R 79 0 R 96 0 R 80 0 R 96 0 R 81 0 R 96 0 R 82 0 R 96 0 R 83 0 R 96 0 R 84 0 R 96 0 R 85 0 R 96 0 R 86 0 R 96 0 R 96 0 R 87 0 R 96 0 R 88 0 R 96 0 R 96 0 R 89 0 R 96 0 R 90 0 R 96 0 R 91 0 R 96 0 R 92 0 R 96 0 R 93 0 R 96 0 R 94 0 R 96 0 R 96 0 R 95 0 R 96 0 R 96 0 R 98 0 R 97 0 R 98 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 99 0 R 100 0 R 101 0 R 101 0 R 101 0 R 101 0 R 101 0 R 101 0 R 101 0 R 101 0 R 101 0 R 101 0 R 101 0 R 101 0 R 101 0 R 101 0 R 101 0 R 101 0 R 101 0 R 102 0 R 102 0 R 102 0 R 102 0 R 102 0 R 102 0 R 102 0 R 102 0 R 102 0 R 102 0 R 102 0 R 102 0 R 102 0 R 102 0 R 102 0 R 108 0 R 104 0 R 108 0 R 105 0 R 108 0 R 108 0 R 106 0 R 108 0 R 107 0 R 108 0 R 108 0 R 109 0 R 110 0 R 110 0 R 110 0 R 111 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 113 0 R 113 0 R 113 0 R 113 0 R 113 0 R 113 0 R 113 0 R 113 0 R 113 0 R 113 0 R 113 0 R 114 0 R 114 0 R 114 0 R 114 0 R 114 0 R 115 0 R 115 0 R 115 0 R 115 0 R 115 0 R 115 0 R 115 0 R 116 0 R 116 0 R 116 0 R 116 0 R 116 0 R 116 0 R 116 0 R 117 0 R 117 0 R 117 0 R 117 0 R 117 0 R 118 0 R 118 0 R 118 0 R 118 0 R 118 0 R 118 0 R 118 0 R 118 0 R 118 0 R 119 0 R 119 0 R 119 0 R 119 0 R 119 0 R 119 0 R 119 0 R 119 0 R 119 0 R 119 0 R 119 0 R 119 0 R 119 0 R 119 0 R 119 0 R 121 0 R 122 0 R 122 0 R 122 0 R 122 0 R 122 0 R 122 0 R 122 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 123 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 124 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 126 0 R 126 0 R 126 0 R 126 0 R 126 0 R 126 0 R 126 0 R 126 0 R 126 0 R 126 0 R 126 0 R 126 0 R 126 0 R 126 0 R 126 0 R 126 0 R 126 0 R 126 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 127 0 R 128 0 R 128 0 R 128 0 R 128 0 R 128 0 R 129 0 R 129 0 R 129 0 R 129 0 R 129 0 R 129 0 R 129 0 R 129 0 R 129 0 R 129 0 R 129 0 R 129 0 R 129 0 R 129 0 R 129 0 R 129 0 R 129 0 R 129 0 R 129 0 R 130 0 R 130 0 R 131 0 R 131 0 R 132 0 R 132 0 R 132 0 R 132 0 R 132 0 R 132 0 R 132 0 R 132 0 R 132 0 R 132 0 R 132 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 133 0 R 134 0 R 134 0 R] +<> endobj 14 0 obj -[135 0 R 135 0 R 135 0 R 135 0 R 135 0 R 135 0 R 135 0 R 135 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 136 0 R 137 0 R 137 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 139 0 R 140 0 R 140 0 R 140 0 R 140 0 R 140 0 R 140 0 R 140 0 R 140 0 R 140 0 R 140 0 R 140 0 R 140 0 R 140 0 R 140 0 R 140 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 141 0 R 145 0 R 143 0 R 145 0 R 144 0 R 145 0 R 145 0 R 145 0 R 146 0 R 149 0 R 147 0 R 149 0 R 149 0 R 148 0 R 148 0 R 149 0 R 150 0 R 150 0 R 150 0 R 150 0 R 151 0 R 153 0 R 155 0 R 159 0 R 159 0 R 159 0 R 160 0 R 161 0 R 162 0 R 162 0 R 162 0 R 162 0 R 162 0 R 162 0 R 162 0 R 162 0 R 162 0 R 162 0 R 164 0 R 165 0 R 166 0 R 167 0 R 167 0 R 167 0 R 167 0 R 169 0 R 170 0 R 171 0 R 173 0 R 172 0 R 172 0 R 175 0 R 176 0 R 177 0 R 178 0 R 179 0 R 179 0 R 181 0 R 182 0 R 183 0 R 184 0 R 184 0 R 184 0 R 184 0 R 184 0 R 184 0 R 184 0 R 184 0 R 186 0 R 187 0 R 188 0 R 189 0 R 189 0 R 189 0 R 189 0 R 189 0 R 189 0 R 189 0 R 189 0 R 189 0 R] +<> endobj 15 0 obj -[191 0 R 195 0 R 192 0 R 195 0 R 193 0 R 195 0 R 194 0 R 196 0 R 198 0 R 200 0 R 200 0 R 200 0 R 200 0 R 207 0 R 207 0 R 207 0 R 203 0 R 207 0 R 207 0 R 207 0 R 204 0 R 207 0 R 205 0 R 207 0 R 206 0 R 207 0 R 207 0 R 208 0 R 208 0 R 208 0 R 209 0 R 218 0 R 210 0 R 218 0 R 211 0 R 218 0 R 218 0 R 212 0 R 218 0 R 218 0 R 218 0 R 213 0 R 218 0 R 218 0 R 214 0 R 218 0 R 218 0 R 218 0 R 215 0 R 218 0 R 216 0 R 216 0 R 218 0 R 217 0 R 218 0 R 220 0 R 219 0 R 220 0 R 220 0 R 220 0 R 220 0 R 220 0 R 227 0 R 221 0 R 227 0 R 222 0 R 227 0 R 227 0 R 223 0 R 227 0 R 224 0 R 227 0 R 225 0 R 227 0 R 227 0 R 226 0 R 227 0 R 227 0 R 227 0 R 227 0 R 228 0 R 250 0 R 229 0 R 250 0 R 230 0 R 250 0 R 231 0 R 250 0 R 232 0 R 250 0 R 233 0 R 250 0 R 234 0 R 250 0 R 235 0 R 250 0 R 236 0 R 236 0 R 250 0 R 237 0 R 250 0 R 250 0 R 238 0 R 250 0 R 239 0 R 250 0 R 240 0 R 250 0 R 241 0 R 250 0 R 250 0 R 250 0 R 242 0 R 250 0 R 250 0 R 243 0 R 250 0 R 244 0 R 250 0 R 245 0 R 250 0 R 246 0 R 250 0 R 247 0 R 250 0 R 250 0 R 248 0 R 250 0 R] +<> endobj 16 0 obj -[250 0 R 249 0 R 250 0 R 250 0 R 251 0 R 260 0 R 252 0 R 260 0 R 260 0 R 260 0 R 253 0 R 260 0 R 260 0 R 254 0 R 260 0 R 255 0 R 260 0 R 260 0 R 256 0 R 260 0 R 257 0 R 260 0 R 260 0 R 258 0 R 260 0 R 260 0 R 259 0 R 260 0 R 260 0 R 263 0 R 261 0 R 263 0 R 263 0 R 263 0 R 262 0 R 263 0 R 263 0 R 264 0 R 266 0 R 265 0 R 266 0 R 266 0 R 266 0 R 266 0 R 267 0 R 272 0 R 272 0 R 272 0 R 272 0 R 268 0 R 272 0 R 272 0 R 272 0 R 269 0 R 272 0 R 270 0 R 272 0 R 272 0 R 272 0 R 272 0 R 271 0 R 272 0 R 273 0 R 285 0 R 285 0 R 285 0 R 274 0 R 285 0 R 285 0 R 275 0 R 285 0 R 276 0 R 285 0 R 285 0 R 285 0 R 285 0 R 277 0 R 285 0 R 285 0 R 285 0 R 278 0 R 285 0 R 279 0 R 285 0 R 285 0 R 285 0 R 280 0 R 285 0 R 285 0 R 285 0 R 282 0 R 285 0 R 285 0 R 285 0 R 284 0 R 285 0 R 285 0 R 285 0 R 286 0 R 346 0 R 287 0 R 346 0 R 288 0 R 346 0 R 289 0 R 346 0 R 290 0 R 290 0 R 346 0 R 291 0 R 346 0 R 292 0 R 346 0 R 293 0 R 346 0 R 294 0 R 346 0 R 346 0 R 295 0 R 346 0 R 296 0 R 346 0 R 297 0 R 346 0 R 298 0 R 346 0 R 299 0 R 346 0 R 300 0 R 346 0 R 301 0 R 346 0 R 302 0 R 346 0 R 303 0 R 346 0 R 346 0 R 304 0 R 346 0 R 305 0 R 346 0 R 306 0 R 346 0 R 307 0 R 346 0 R 308 0 R 346 0 R] +<> endobj 17 0 obj -[346 0 R 309 0 R 346 0 R 310 0 R 346 0 R 311 0 R 346 0 R 312 0 R 346 0 R 313 0 R 346 0 R 314 0 R 346 0 R 315 0 R 346 0 R 316 0 R 346 0 R 317 0 R 346 0 R 318 0 R 346 0 R 346 0 R 320 0 R 321 0 R 322 0 R 346 0 R 346 0 R 324 0 R 346 0 R 325 0 R 346 0 R 326 0 R 346 0 R 327 0 R 346 0 R 328 0 R 328 0 R 346 0 R 329 0 R 346 0 R 330 0 R 346 0 R 331 0 R 346 0 R 332 0 R 346 0 R 333 0 R 346 0 R 334 0 R 346 0 R 335 0 R 346 0 R 336 0 R 346 0 R 346 0 R 346 0 R 346 0 R 337 0 R 346 0 R 338 0 R 346 0 R 346 0 R 346 0 R 339 0 R 346 0 R 346 0 R 340 0 R 346 0 R 341 0 R 346 0 R 346 0 R 342 0 R 346 0 R 343 0 R 346 0 R 344 0 R 346 0 R 346 0 R 345 0 R 346 0 R 347 0 R 353 0 R 348 0 R 348 0 R 353 0 R 349 0 R 353 0 R 350 0 R 353 0 R 353 0 R 351 0 R 353 0 R 352 0 R 353 0 R 353 0 R 354 0 R 358 0 R 358 0 R 355 0 R 358 0 R 358 0 R 358 0 R 358 0 R 358 0 R 356 0 R 358 0 R 358 0 R 357 0 R 358 0 R 358 0 R 358 0 R 359 0 R 372 0 R 372 0 R 360 0 R 372 0 R 372 0 R 361 0 R 372 0 R 362 0 R 372 0 R 363 0 R 372 0 R 364 0 R 372 0 R 365 0 R 372 0 R 366 0 R 372 0 R 367 0 R 372 0 R 368 0 R 372 0 R 369 0 R 372 0 R 370 0 R 372 0 R 372 0 R 371 0 R 372 0 R 372 0 R 373 0 R 388 0 R 374 0 R 388 0 R 388 0 R 388 0 R 375 0 R 388 0 R 376 0 R 388 0 R 388 0 R 377 0 R 388 0 R 378 0 R 388 0 R 379 0 R 388 0 R 388 0 R 388 0 R 380 0 R 388 0 R 381 0 R 388 0 R 388 0 R 388 0 R 388 0 R 382 0 R 388 0 R 383 0 R 388 0 R 388 0 R 388 0 R 384 0 R 388 0 R 388 0 R 385 0 R 388 0 R 386 0 R 388 0 R 387 0 R 388 0 R 388 0 R 388 0 R 388 0 R 389 0 R 394 0 R 394 0 R 394 0 R] +<> endobj 18 0 obj -[394 0 R 394 0 R 390 0 R 394 0 R 394 0 R 391 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 394 0 R 392 0 R 394 0 R 394 0 R 393 0 R 394 0 R 394 0 R] +<> endobj 19 0 obj -<> +<> endobj 20 0 obj -<> +<> endobj 21 0 obj -<> +<> endobj 22 0 obj -<> +<> endobj 23 0 obj -<> +<> endobj 24 0 obj -<> +<> endobj 25 0 obj -<> +<> endobj 26 0 obj -<> +<> endobj 27 0 obj -<> +<> endobj 28 0 obj -<> +<> endobj 29 0 obj -<> +<> endobj 30 0 obj -<><>]/K[29 0 R]>> +<> endobj 31 0 obj -<> +<> endobj 32 0 obj -<> +<> endobj 33 0 obj -<> +<> endobj 34 0 obj -<> +<> endobj 35 0 obj -<> +<> endobj 36 0 obj -<><>]/K[21 31 0 R 23 32 0 R 25 26 33 0 R 28 29 34 0 R 31 35 0 R 33 34 35]/Pg 425 0 R>> +<> endobj 37 0 obj -<> +<> endobj 38 0 obj -<> +<> endobj 39 0 obj -<><>]/K[38 0 R]>> +<> endobj 40 0 obj -<><>]/K[37 38 39 40]/Pg 425 0 R>> +<> endobj 41 0 obj -<> +<> endobj 42 0 obj -<>]/K[37 0 R 41 0 R]>> +<> endobj 43 0 obj -<> +<> endobj 44 0 obj -<> +<> endobj 45 0 obj -<> +<> endobj 46 0 obj -<> +<> endobj 47 0 obj -<> +<> endobj 48 0 obj -<> +<> endobj 49 0 obj -<> +<> endobj 50 0 obj -<> +<> endobj 51 0 obj -<> +<> endobj 52 0 obj -<> +<> endobj 53 0 obj -<> +<> endobj 54 0 obj -<> +<> endobj 55 0 obj -<> +<> endobj 56 0 obj -<> +<> endobj 57 0 obj -<> +<> endobj 58 0 obj -<> +<>/K[2033 0 R]/ParentTree<>/IDTree<>/ParentTreeNextKey 24>> endobj 59 0 obj -<> +[75 0 R 77 0 R 78 0 R 79 0 R 79 0 R 79 0 R 79 0 R 79 0 R 80 0 R 81 0 R 82 0 R 83 0 R 84 0 R 85 0 R 85 0 R 86 0 R 87 0 R 87 0 R 87 0 R 88 0 R 88 0 R 89 0 R 89 0 R 90 0 R 90 0 R 90 0 R 90 0 R 90 0 R 94 0 R 94 0 R 91 0 R 94 0 R 92 0 R 94 0 R 94 0 R 94 0 R 94 0 R 94 0 R 94 0 R 93 0 R 94 0 R 94 0 R 94 0 R 94 0 R 94 0 R 94 0 R 94 0 R 97 0 R 95 0 R 97 0 R 97 0 R 97 0 R 97 0 R 97 0 R 96 0 R 97 0 R 97 0 R 100 0 R 98 0 R 98 0 R 100 0 R 99 0 R 100 0 R 101 0 R 101 0 R 103 0 R 102 0 R 103 0 R 103 0 R 103 0 R 103 0 R 104 0 R 110 0 R 110 0 R 110 0 R 105 0 R 110 0 R 106 0 R 110 0 R 110 0 R 110 0 R 107 0 R 110 0 R 110 0 R 110 0 R 108 0 R 110 0 R 110 0 R 109 0 R 110 0 R 110 0 R 110 0 R 110 0 R 111 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 112 0 R 122 0 R 122 0 R 122 0 R 122 0 R 122 0 R 122 0 R 113 0 R 122 0 R 122 0 R 114 0 R 122 0 R 115 0 R 122 0 R 122 0 R 116 0 R 122 0 R 122 0 R 117 0 R 122 0 R 118 0 R 122 0 R 119 0 R 122 0 R 120 0 R 122 0 R 121 0 R 121 0 R 122 0 R 125 0 R 123 0 R 125 0 R 125 0 R 124 0 R 125 0 R 125 0 R 125 0 R 125 0 R 125 0 R 126 0 R 126 0 R 127 0 R 139 0 R 139 0 R 128 0 R 139 0 R 139 0 R 139 0 R 129 0 R 139 0 R 139 0 R 130 0 R 139 0 R 139 0 R 139 0 R 139 0 R 131 0 R 139 0 R 139 0 R 139 0 R 132 0 R 139 0 R 139 0 R 133 0 R 139 0 R 134 0 R 139 0 R 135 0 R 139 0 R 139 0 R 136 0 R 139 0 R 137 0 R 139 0 R 138 0 R 139 0 R 140 0 R 140 0 R 201 0 R 201 0 R 203 0 R 202 0 R 203 0 R 221 0 R 204 0 R 221 0 R 205 0 R 221 0 R 206 0 R 221 0 R 207 0 R 221 0 R 208 0 R 221 0 R 209 0 R 221 0 R 210 0 R 221 0 R 211 0 R 221 0 R 212 0 R 221 0 R 213 0 R 221 0 R 214 0 R 221 0 R 215 0 R 221 0 R 216 0 R 221 0 R 217 0 R 221 0 R 218 0 R 221 0 R 219 0 R 221 0 R 220 0 R 221 0 R 222 0 R 232 0 R 223 0 R 232 0 R 224 0 R 232 0 R 232 0 R 232 0 R 225 0 R 232 0 R 232 0 R 226 0 R 232 0 R 232 0 R 227 0 R 232 0 R 232 0 R 228 0 R 232 0 R 232 0 R 229 0 R 232 0 R 232 0 R 232 0 R 232 0 R] endobj 60 0 obj -<> +[155 0 R 156 0 R 157 0 R 158 0 R 159 0 R 160 0 R 161 0 R 162 0 R 163 0 R 164 0 R 165 0 R 166 0 R 167 0 R 168 0 R 169 0 R 170 0 R 171 0 R 171 0 R 172 0 R 173 0 R 174 0 R 175 0 R 175 0 R 176 0 R 177 0 R 177 0 R 178 0 R 179 0 R 179 0 R 180 0 R 181 0 R 182 0 R 183 0 R 184 0 R 185 0 R 186 0 R 187 0 R 188 0 R 189 0 R 189 0 R 189 0 R 189 0 R 189 0 R 190 0 R 191 0 R 192 0 R 193 0 R 194 0 R 195 0 R 196 0 R 197 0 R 198 0 R 141 0 R 142 0 R 143 0 R 144 0 R 145 0 R 146 0 R 147 0 R 148 0 R 149 0 R 150 0 R 151 0 R 151 0 R 152 0 R 153 0 R 232 0 R 230 0 R 232 0 R 232 0 R 232 0 R 231 0 R 232 0 R 234 0 R 233 0 R 234 0 R 235 0 R 235 0 R 235 0 R 235 0 R 235 0 R 235 0 R 236 0 R 247 0 R 247 0 R 247 0 R 237 0 R 247 0 R 238 0 R 247 0 R 239 0 R 247 0 R 240 0 R 247 0 R 241 0 R 247 0 R 242 0 R 247 0 R 243 0 R 247 0 R 244 0 R 247 0 R 247 0 R 245 0 R 247 0 R 247 0 R 246 0 R 247 0 R 247 0 R 249 0 R 248 0 R 249 0 R 250 0 R 254 0 R 251 0 R 254 0 R 254 0 R 252 0 R 254 0 R 254 0 R 253 0 R 254 0 R 254 0 R 254 0 R 254 0 R 254 0 R 255 0 R 255 0 R 255 0 R 255 0 R 255 0 R 255 0 R 255 0 R 255 0 R 257 0 R 256 0 R 257 0 R 259 0 R 258 0 R 259 0 R 259 0 R 260 0 R 260 0 R 260 0 R 260 0 R 260 0 R 260 0 R 260 0 R 260 0 R 260 0 R 260 0 R 260 0 R 260 0 R 260 0 R 260 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 261 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 262 0 R 263 0 R 263 0 R 263 0 R 263 0 R 263 0 R 263 0 R 263 0 R 264 0 R 264 0 R 264 0 R 264 0 R 264 0 R 264 0 R 264 0 R 264 0 R 264 0 R 264 0 R 264 0 R 264 0 R 264 0 R 264 0 R 264 0 R 264 0 R 264 0 R 264 0 R 264 0 R 265 0 R 265 0 R 265 0 R 265 0 R 265 0 R 265 0 R 265 0 R 265 0 R 265 0 R 266 0 R 266 0 R 266 0 R 266 0 R 266 0 R 266 0 R 266 0 R 266 0 R 266 0 R 266 0 R 266 0 R 266 0 R 266 0 R 266 0 R 267 0 R 267 0 R 267 0 R 267 0 R 267 0 R 267 0 R 267 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 268 0 R 269 0 R 269 0 R 269 0 R 270 0 R 270 0 R 270 0 R 271 0 R 271 0 R 271 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 272 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 273 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 274 0 R 275 0 R 275 0 R 275 0 R 275 0 R 275 0 R 275 0 R 275 0 R 275 0 R 275 0 R 275 0 R 275 0 R 275 0 R 275 0 R 275 0 R 275 0 R 275 0 R 275 0 R 275 0 R 275 0 R 275 0 R 276 0 R 276 0 R 276 0 R 276 0 R 276 0 R 276 0 R 276 0 R 277 0 R 277 0 R 277 0 R 277 0 R 277 0 R 277 0 R 277 0 R 277 0 R 277 0 R 277 0 R 277 0 R 277 0 R 277 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 278 0 R 279 0 R 279 0 R 279 0 R 279 0 R 279 0 R 279 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 280 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 281 0 R 282 0 R 282 0 R 282 0 R 282 0 R 282 0 R 282 0 R 282 0 R 282 0 R 282 0 R 282 0 R 282 0 R 282 0 R 282 0 R 282 0 R 283 0 R 283 0 R 283 0 R 284 0 R 284 0 R 285 0 R 296 0 R 296 0 R 287 0 R 296 0 R 296 0 R 288 0 R 296 0 R 289 0 R 296 0 R 296 0 R 296 0 R 290 0 R 296 0 R 296 0 R 291 0 R 296 0 R 296 0 R 296 0 R 292 0 R 296 0 R 296 0 R 293 0 R 296 0 R 296 0 R 294 0 R 296 0 R 296 0 R 295 0 R 296 0 R 305 0 R 297 0 R 305 0 R 305 0 R 305 0 R 298 0 R 305 0 R 305 0 R 305 0 R 299 0 R 305 0 R 300 0 R 305 0 R 301 0 R 305 0 R 302 0 R 305 0 R 303 0 R 305 0 R 304 0 R 305 0 R 305 0 R 306 0 R 311 0 R 311 0 R 311 0 R 307 0 R 311 0 R 308 0 R 311 0 R 311 0 R 311 0 R 309 0 R 311 0 R 311 0 R 310 0 R 311 0 R 312 0 R 320 0 R 313 0 R 320 0 R 320 0 R 314 0 R 320 0 R 315 0 R 320 0 R 316 0 R 320 0 R 320 0 R 320 0 R 317 0 R 320 0 R 320 0 R 318 0 R 320 0 R 320 0 R 319 0 R 320 0 R] endobj 61 0 obj -<> +[323 0 R 323 0 R 323 0 R 321 0 R 321 0 R 323 0 R 323 0 R 323 0 R 323 0 R 323 0 R 323 0 R 323 0 R 323 0 R 322 0 R 323 0 R 323 0 R 323 0 R 323 0 R 323 0 R 323 0 R 324 0 R 324 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 325 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 326 0 R 327 0 R 327 0 R 327 0 R 327 0 R 327 0 R 327 0 R 327 0 R 327 0 R 327 0 R 327 0 R 327 0 R 327 0 R 327 0 R 328 0 R 328 0 R 328 0 R 328 0 R 328 0 R 328 0 R 328 0 R 328 0 R 328 0 R 328 0 R 328 0 R 328 0 R 328 0 R 328 0 R 329 0 R 329 0 R 329 0 R 329 0 R 329 0 R 329 0 R 329 0 R 329 0 R 329 0 R 329 0 R 329 0 R 330 0 R 330 0 R 330 0 R 330 0 R 330 0 R 330 0 R 330 0 R 330 0 R 330 0 R 331 0 R 333 0 R 333 0 R 333 0 R 333 0 R 333 0 R 333 0 R 333 0 R 333 0 R 333 0 R 333 0 R 333 0 R 333 0 R 333 0 R 333 0 R 333 0 R 333 0 R 333 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 334 0 R 335 0 R 335 0 R 335 0 R 335 0 R 335 0 R 335 0 R 335 0 R 335 0 R 335 0 R 336 0 R 336 0 R 336 0 R 336 0 R 336 0 R 337 0 R 337 0 R 338 0 R 338 0 R 338 0 R 338 0 R 338 0 R 338 0 R 338 0 R 338 0 R 338 0 R 338 0 R 338 0 R 338 0 R 338 0 R 338 0 R 338 0 R 338 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 339 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 340 0 R 341 0 R 341 0 R 341 0 R 341 0 R 341 0 R 341 0 R 341 0 R 341 0 R 341 0 R 342 0 R 342 0 R 342 0 R 342 0 R 342 0 R 343 0 R 345 0 R 349 0 R 346 0 R 349 0 R 349 0 R 347 0 R 349 0 R 349 0 R 348 0 R 349 0 R 349 0 R 357 0 R 357 0 R 350 0 R 357 0 R 357 0 R 351 0 R 357 0 R 357 0 R 352 0 R 357 0 R 353 0 R 357 0 R 357 0 R 354 0 R 357 0 R 357 0 R 355 0 R 357 0 R 357 0 R 356 0 R 357 0 R 357 0 R 357 0 R 357 0 R 359 0 R 358 0 R 359 0 R 363 0 R 363 0 R 360 0 R 363 0 R 361 0 R 363 0 R 363 0 R 363 0 R 363 0 R 362 0 R 363 0 R 363 0 R 364 0 R 364 0 R 364 0 R 364 0 R 364 0 R 364 0 R 364 0 R 364 0 R 364 0 R 364 0 R 364 0 R 365 0 R 365 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 366 0 R 367 0 R 367 0 R 367 0 R 367 0 R 367 0 R 367 0 R 367 0 R 367 0 R 368 0 R 368 0 R 368 0 R 368 0 R 368 0 R 368 0 R 369 0 R 369 0 R 369 0 R 369 0 R 369 0 R 369 0 R 369 0 R 369 0 R 369 0 R 369 0 R 370 0 R 370 0 R 370 0 R 370 0 R 370 0 R 370 0 R 370 0 R 370 0 R 370 0 R 370 0 R 370 0 R 371 0 R 371 0 R 371 0 R 371 0 R 371 0 R 371 0 R 371 0 R 371 0 R 372 0 R 372 0 R 372 0 R 372 0 R 372 0 R 372 0 R 372 0 R 372 0 R 372 0 R 373 0 R 373 0 R 373 0 R 373 0 R 373 0 R 373 0 R 373 0 R 373 0 R 373 0 R 373 0 R 374 0 R 374 0 R 375 0 R 375 0 R 375 0 R 375 0 R 375 0 R 375 0 R 375 0 R 375 0 R 375 0 R 375 0 R 375 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 376 0 R 377 0 R 377 0 R 377 0 R 377 0 R 377 0 R 377 0 R 378 0 R 378 0 R 378 0 R 378 0 R 378 0 R 378 0 R 378 0 R 378 0 R 378 0 R 378 0 R 379 0 R 379 0 R 380 0 R 380 0 R 388 0 R 388 0 R 388 0 R 382 0 R 388 0 R 383 0 R 388 0 R 388 0 R 384 0 R 388 0 R 388 0 R 388 0 R 388 0 R 388 0 R 385 0 R 388 0 R 388 0 R 388 0 R 386 0 R 388 0 R 387 0 R 388 0 R 388 0 R 388 0 R 391 0 R 391 0 R 389 0 R 391 0 R 390 0 R 391 0 R 391 0 R 391 0 R 391 0 R 391 0 R 399 0 R 399 0 R 399 0 R 399 0 R 392 0 R 399 0 R 393 0 R 399 0 R 394 0 R 399 0 R 395 0 R 399 0 R 396 0 R 399 0 R 397 0 R 399 0 R 398 0 R 399 0 R 399 0 R 399 0 R 399 0 R 400 0 R 412 0 R 412 0 R 401 0 R 412 0 R 402 0 R 412 0 R 412 0 R 403 0 R 412 0 R 412 0 R 404 0 R 412 0 R 412 0 R 405 0 R 412 0 R 412 0 R 412 0 R 406 0 R 412 0 R 407 0 R 412 0 R 412 0 R 412 0 R 408 0 R 412 0 R 409 0 R 412 0 R 412 0 R 410 0 R 412 0 R 411 0 R 412 0 R 412 0 R 412 0 R 412 0 R 413 0 R 413 0 R 415 0 R 414 0 R 415 0 R 415 0 R 442 0 R 446 0 R 446 0 R 446 0 R 443 0 R 446 0 R 446 0 R 444 0 R 446 0 R 445 0 R 446 0 R 456 0 R 447 0 R 456 0 R 456 0 R 448 0 R 456 0 R 456 0 R 449 0 R 456 0 R 450 0 R 456 0 R 451 0 R 456 0 R 452 0 R 456 0 R 453 0 R 456 0 R 454 0 R 456 0 R 456 0 R 455 0 R 456 0 R 456 0 R 456 0 R] endobj 62 0 obj -<> +[426 0 R 427 0 R 428 0 R 429 0 R 430 0 R 430 0 R 430 0 R 430 0 R 430 0 R 430 0 R 430 0 R 430 0 R 430 0 R 430 0 R 430 0 R 430 0 R 430 0 R 430 0 R 430 0 R 430 0 R 430 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 431 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 432 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 433 0 R 434 0 R 434 0 R 434 0 R 434 0 R 434 0 R 434 0 R 434 0 R 434 0 R 434 0 R 435 0 R 435 0 R 435 0 R 435 0 R 435 0 R 436 0 R 436 0 R 437 0 R 437 0 R 437 0 R 437 0 R 437 0 R 437 0 R 437 0 R 437 0 R 437 0 R 437 0 R 437 0 R 437 0 R 437 0 R 437 0 R 437 0 R 437 0 R 437 0 R 438 0 R 438 0 R 438 0 R 438 0 R 439 0 R 416 0 R 417 0 R 418 0 R 419 0 R 420 0 R 420 0 R 421 0 R 422 0 R 423 0 R 424 0 R 456 0 R 456 0 R 457 0 R 457 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 458 0 R 459 0 R 459 0 R 459 0 R 459 0 R 459 0 R 459 0 R 459 0 R 459 0 R 459 0 R 460 0 R 460 0 R 460 0 R 460 0 R 460 0 R 460 0 R 460 0 R 460 0 R 460 0 R 460 0 R 460 0 R 461 0 R 461 0 R 461 0 R 461 0 R 461 0 R 461 0 R 461 0 R 461 0 R 461 0 R 461 0 R 461 0 R 461 0 R 462 0 R 462 0 R 462 0 R 462 0 R 462 0 R 462 0 R 462 0 R 462 0 R 462 0 R 462 0 R 462 0 R 462 0 R 462 0 R 462 0 R 462 0 R 462 0 R 462 0 R 463 0 R 463 0 R 463 0 R 463 0 R 463 0 R 463 0 R 463 0 R 463 0 R 463 0 R 463 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 464 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 465 0 R 466 0 R 466 0 R 467 0 R 467 0 R 468 0 R 468 0 R 468 0 R 468 0 R 468 0 R 468 0 R 468 0 R 468 0 R 468 0 R 468 0 R 468 0 R 469 0 R 469 0 R 469 0 R 469 0 R 469 0 R 469 0 R 469 0 R 469 0 R 469 0 R 469 0 R 469 0 R 469 0 R 469 0 R 469 0 R 469 0 R 469 0 R 469 0 R 469 0 R 469 0 R 469 0 R 470 0 R 470 0 R 470 0 R 470 0 R 470 0 R 470 0 R 470 0 R 470 0 R 470 0 R 470 0 R 470 0 R 470 0 R 470 0 R 470 0 R 470 0 R 471 0 R 471 0 R 471 0 R 471 0 R 471 0 R 471 0 R 471 0 R 471 0 R 471 0 R 471 0 R 471 0 R 471 0 R 471 0 R 472 0 R 472 0 R 472 0 R 472 0 R 472 0 R 472 0 R 472 0 R 472 0 R 472 0 R 472 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 473 0 R 474 0 R 474 0 R 474 0 R 474 0 R 474 0 R 474 0 R 474 0 R 474 0 R 474 0 R 474 0 R 474 0 R 474 0 R 474 0 R 474 0 R 474 0 R 474 0 R 474 0 R 474 0 R 474 0 R 474 0 R 475 0 R 475 0 R 476 0 R 478 0 R 480 0 R 480 0 R 480 0 R 480 0 R 479 0 R 480 0 R 480 0 R 483 0 R 481 0 R 483 0 R 483 0 R 483 0 R 483 0 R 482 0 R 483 0 R 484 0 R 486 0 R 485 0 R 486 0 R 486 0 R 486 0 R 486 0 R 486 0 R 486 0 R 487 0 R 487 0 R 492 0 R 488 0 R 492 0 R 489 0 R 492 0 R 492 0 R 492 0 R 490 0 R 492 0 R 491 0 R 492 0 R 493 0 R 493 0 R 493 0 R 493 0 R 493 0 R 493 0 R 493 0 R 493 0 R 493 0 R 493 0 R 493 0 R 493 0 R 494 0 R 494 0 R 494 0 R 494 0 R 494 0 R 494 0 R 494 0 R 494 0 R 494 0 R 494 0 R 495 0 R 495 0 R 495 0 R 495 0 R 495 0 R 495 0 R 495 0 R 495 0 R 495 0 R 495 0 R 495 0 R 496 0 R 496 0 R 497 0 R 497 0 R 498 0 R 498 0 R 499 0 R 499 0 R 500 0 R 500 0 R 501 0 R 501 0 R 501 0 R 501 0 R 501 0 R 501 0 R 501 0 R 501 0 R 501 0 R 502 0 R 502 0 R 502 0 R 504 0 R 506 0 R 505 0 R 506 0 R 506 0 R 506 0 R 508 0 R 507 0 R 508 0 R 508 0 R 508 0 R 508 0 R 508 0 R 516 0 R 516 0 R 516 0 R 509 0 R 516 0 R 510 0 R 516 0 R 511 0 R 516 0 R 512 0 R 516 0 R 513 0 R 516 0 R 516 0 R 514 0 R 516 0 R 516 0 R 516 0 R 516 0 R 516 0 R 515 0 R 516 0 R 516 0 R 516 0 R 517 0 R 517 0 R 518 0 R 518 0 R 519 0 R 519 0 R 519 0 R 519 0 R 520 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 521 0 R 522 0 R 522 0 R 522 0 R 522 0 R 522 0 R 522 0 R 522 0 R 522 0 R 522 0 R 522 0 R 522 0 R 523 0 R 523 0 R 523 0 R 523 0 R 523 0 R 524 0 R 524 0 R 524 0 R 524 0 R 524 0 R 524 0 R 524 0 R 525 0 R 525 0 R 526 0 R 526 0 R 527 0 R 527 0 R 527 0 R 527 0 R 527 0 R 528 0 R 528 0 R 528 0 R 528 0 R 528 0 R 528 0 R 528 0 R 528 0 R 528 0 R 529 0 R 529 0 R 530 0 R 530 0 R 530 0 R 530 0 R 530 0 R 530 0 R 530 0 R 530 0 R 530 0 R 530 0 R 531 0 R 531 0 R 532 0 R 532 0 R 532 0 R 532 0 R 532 0 R 532 0 R 532 0 R 532 0 R 533 0 R 533 0 R 533 0 R 533 0 R 533 0 R 533 0 R 533 0 R 533 0 R 533 0 R 534 0 R 534 0 R 535 0 R 535 0 R 535 0 R 535 0 R 535 0 R 535 0 R 536 0 R 536 0 R 536 0 R 536 0 R 536 0 R 536 0 R 536 0 R 536 0 R 536 0 R 536 0 R 536 0 R 536 0 R 536 0 R 536 0 R 536 0 R 537 0 R 537 0 R 537 0 R 537 0 R 537 0 R 537 0 R 537 0 R 537 0 R 537 0 R 537 0 R 537 0 R 537 0 R 537 0 R 538 0 R 538 0 R 538 0 R 538 0 R 538 0 R 538 0 R 538 0 R 538 0 R 538 0 R 539 0 R 539 0 R 539 0 R 539 0 R 539 0 R 539 0 R 539 0 R 539 0 R 539 0 R 541 0 R 542 0 R 542 0 R 542 0 R 542 0 R 542 0 R 542 0 R 542 0 R 543 0 R 543 0 R 544 0 R 544 0 R 544 0 R 544 0 R 544 0 R 544 0 R 544 0 R 544 0 R 544 0 R 544 0 R 544 0 R 544 0 R 544 0 R 544 0 R 544 0 R 544 0 R 544 0 R 544 0 R 544 0 R 545 0 R 545 0 R 545 0 R 545 0 R 545 0 R 545 0 R 545 0 R 545 0 R 545 0 R 545 0 R 545 0 R 545 0 R 545 0 R 545 0 R 546 0 R 546 0 R 546 0 R 546 0 R 546 0 R 546 0 R 546 0 R 546 0 R 546 0 R 546 0 R 546 0 R 546 0 R 546 0 R 546 0 R 546 0 R 546 0 R 547 0 R 547 0 R 547 0 R 547 0 R 547 0 R 547 0 R 547 0 R 547 0 R 547 0 R 547 0 R 547 0 R 547 0 R 547 0 R 547 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R 548 0 R] endobj 63 0 obj -<> +[549 0 R 549 0 R 549 0 R 549 0 R 549 0 R 549 0 R 550 0 R 550 0 R 550 0 R 550 0 R 550 0 R 550 0 R 551 0 R 551 0 R 551 0 R 551 0 R 551 0 R 551 0 R 551 0 R 552 0 R 552 0 R 553 0 R 553 0 R 553 0 R 553 0 R 553 0 R 554 0 R 554 0 R 554 0 R 554 0 R 554 0 R 554 0 R 554 0 R 554 0 R 554 0 R 554 0 R 554 0 R 554 0 R 554 0 R 554 0 R 554 0 R 554 0 R 554 0 R 555 0 R 555 0 R 555 0 R 555 0 R 555 0 R 555 0 R 555 0 R 555 0 R 555 0 R 555 0 R 555 0 R 556 0 R 556 0 R 557 0 R 557 0 R 557 0 R 557 0 R 557 0 R 557 0 R 557 0 R 557 0 R 557 0 R 557 0 R 557 0 R 557 0 R 557 0 R 557 0 R 557 0 R 557 0 R 558 0 R 558 0 R 559 0 R 559 0 R 559 0 R 559 0 R 559 0 R 559 0 R 559 0 R 559 0 R 559 0 R 559 0 R 560 0 R 560 0 R 561 0 R 561 0 R 561 0 R 561 0 R 561 0 R 561 0 R 561 0 R 561 0 R 561 0 R 561 0 R 561 0 R 561 0 R 561 0 R 561 0 R 561 0 R 561 0 R 562 0 R 562 0 R 562 0 R 562 0 R 562 0 R 562 0 R 562 0 R 562 0 R 562 0 R 562 0 R 562 0 R 562 0 R 562 0 R 563 0 R 563 0 R 563 0 R 563 0 R 563 0 R 563 0 R 563 0 R 563 0 R 563 0 R 563 0 R 563 0 R 563 0 R 565 0 R 565 0 R 565 0 R 565 0 R 565 0 R 565 0 R 565 0 R 565 0 R 565 0 R 565 0 R 565 0 R 565 0 R 565 0 R 565 0 R 565 0 R 565 0 R 565 0 R 566 0 R 566 0 R 566 0 R 566 0 R 566 0 R 566 0 R 566 0 R 566 0 R 566 0 R 566 0 R 566 0 R 566 0 R 567 0 R 567 0 R 567 0 R 567 0 R 567 0 R 567 0 R 567 0 R 567 0 R 567 0 R 567 0 R 567 0 R 567 0 R 567 0 R 567 0 R 567 0 R 568 0 R 568 0 R 568 0 R 568 0 R 568 0 R 568 0 R 568 0 R 568 0 R 568 0 R 568 0 R 568 0 R 568 0 R 568 0 R 569 0 R 569 0 R 569 0 R 569 0 R 569 0 R 569 0 R 569 0 R 569 0 R 569 0 R 569 0 R 570 0 R 570 0 R 570 0 R 570 0 R 570 0 R 570 0 R 570 0 R 570 0 R 570 0 R 570 0 R 570 0 R 570 0 R 571 0 R 571 0 R 571 0 R 571 0 R 571 0 R 571 0 R 571 0 R 571 0 R 573 0 R 577 0 R 574 0 R 577 0 R 575 0 R 577 0 R 576 0 R 577 0 R 577 0 R 577 0 R 578 0 R 578 0 R 579 0 R 579 0 R 579 0 R 579 0 R 579 0 R 579 0 R 579 0 R 579 0 R 579 0 R 579 0 R 579 0 R 580 0 R 580 0 R 580 0 R 580 0 R 580 0 R 580 0 R 580 0 R 580 0 R 580 0 R 581 0 R 581 0 R 581 0 R 581 0 R 581 0 R 581 0 R 581 0 R 581 0 R 581 0 R 582 0 R 582 0 R 582 0 R 582 0 R 582 0 R 582 0 R 582 0 R 582 0 R 582 0 R 582 0 R 583 0 R 583 0 R 583 0 R 583 0 R 583 0 R 583 0 R 583 0 R 583 0 R 583 0 R 583 0 R 584 0 R 584 0 R 584 0 R 584 0 R 584 0 R 584 0 R 584 0 R 584 0 R 584 0 R 584 0 R 585 0 R 585 0 R 585 0 R 585 0 R 585 0 R 585 0 R 585 0 R 585 0 R 585 0 R 585 0 R 586 0 R 586 0 R 586 0 R 586 0 R 586 0 R 586 0 R 586 0 R 586 0 R 586 0 R 586 0 R 587 0 R 587 0 R 587 0 R 587 0 R 587 0 R 587 0 R 587 0 R 587 0 R 587 0 R 587 0 R 588 0 R 588 0 R 588 0 R 588 0 R 588 0 R 588 0 R 588 0 R 588 0 R 588 0 R 588 0 R 589 0 R 589 0 R 589 0 R 589 0 R 589 0 R 589 0 R 589 0 R 589 0 R 589 0 R 589 0 R 589 0 R 589 0 R 589 0 R 589 0 R 589 0 R 589 0 R 590 0 R 590 0 R 590 0 R 590 0 R 590 0 R 590 0 R 590 0 R 590 0 R 590 0 R 590 0 R 590 0 R 590 0 R 590 0 R 590 0 R 591 0 R 591 0 R 591 0 R 591 0 R 591 0 R 591 0 R 591 0 R 591 0 R 591 0 R 592 0 R 592 0 R 594 0 R 595 0 R 596 0 R 597 0 R 598 0 R 598 0 R 598 0 R 598 0 R 598 0 R 598 0 R 598 0 R 598 0 R 598 0 R 598 0 R 598 0 R 599 0 R 599 0 R 599 0 R 599 0 R 599 0 R 599 0 R 599 0 R 599 0 R 599 0 R 599 0 R 599 0 R 599 0 R 599 0 R 599 0 R 599 0 R 600 0 R 600 0 R 600 0 R 600 0 R 600 0 R 600 0 R 600 0 R 600 0 R 600 0 R 600 0 R 601 0 R 601 0 R 601 0 R 601 0 R 601 0 R 601 0 R 601 0 R 601 0 R 601 0 R 601 0 R 601 0 R 602 0 R 602 0 R 602 0 R 602 0 R 602 0 R 602 0 R 602 0 R 602 0 R 602 0 R 602 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 604 0 R 605 0 R 605 0 R 605 0 R 605 0 R 605 0 R 605 0 R 605 0 R 605 0 R 605 0 R 605 0 R 605 0 R 605 0 R 605 0 R 606 0 R 606 0 R 607 0 R 607 0 R 608 0 R 608 0 R 610 0 R 615 0 R 611 0 R 615 0 R 612 0 R 615 0 R 615 0 R 615 0 R 615 0 R 615 0 R 613 0 R 615 0 R 614 0 R 615 0 R 615 0 R 622 0 R 616 0 R 622 0 R 622 0 R 617 0 R 622 0 R 622 0 R 622 0 R 622 0 R 618 0 R 622 0 R 619 0 R 622 0 R 641 0 R 641 0 R 641 0 R 641 0 R 641 0 R 641 0 R 641 0 R 642 0 R 643 0 R 644 0 R 645 0 R 646 0 R 625 0 R 625 0 R 626 0 R 627 0 R 627 0 R 627 0 R 628 0 R 629 0 R 630 0 R 631 0 R 632 0 R 633 0 R 634 0 R 635 0 R 636 0 R 637 0 R 638 0 R 639 0 R 620 0 R 622 0 R 622 0 R 621 0 R 622 0 R 623 0 R 623 0 R 624 0 R 624 0 R 651 0 R 651 0 R 651 0 R 649 0 R 651 0 R 650 0 R 650 0 R 650 0 R 650 0 R 650 0 R 651 0 R 651 0 R 651 0 R 651 0 R 651 0 R 651 0 R 652 0 R 654 0 R 653 0 R 654 0 R 654 0 R 654 0 R 654 0 R 655 0 R 655 0 R 661 0 R 656 0 R 661 0 R 661 0 R 661 0 R 661 0 R 657 0 R 661 0 R 658 0 R 661 0 R 661 0 R 659 0 R 661 0 R 661 0 R 660 0 R 661 0 R 661 0 R 663 0 R 663 0 R 663 0 R 663 0 R 662 0 R 663 0 R 663 0 R 663 0 R 664 0 R 664 0 R 665 0 R 665 0 R 666 0 R 669 0 R 667 0 R 669 0 R 669 0 R 668 0 R 669 0 R 670 0 R 675 0 R 671 0 R 675 0 R 675 0 R 675 0 R 672 0 R 675 0 R 675 0 R 673 0 R 675 0 R 675 0 R 674 0 R 675 0 R 675 0 R 675 0 R] endobj 64 0 obj -<> +[682 0 R 676 0 R 682 0 R 677 0 R 682 0 R 682 0 R 682 0 R 678 0 R 682 0 R 679 0 R 682 0 R 682 0 R 680 0 R 682 0 R 681 0 R 682 0 R 682 0 R 682 0 R 685 0 R 685 0 R 685 0 R 685 0 R 683 0 R 685 0 R 684 0 R 685 0 R 686 0 R 686 0 R 687 0 R 696 0 R 688 0 R 696 0 R 689 0 R 696 0 R 696 0 R 696 0 R 696 0 R 690 0 R 696 0 R 691 0 R 696 0 R 692 0 R 696 0 R 696 0 R 693 0 R 696 0 R 696 0 R 694 0 R 696 0 R 695 0 R 696 0 R 699 0 R 699 0 R 697 0 R 699 0 R 699 0 R 699 0 R 699 0 R 698 0 R 699 0 R 700 0 R 706 0 R 701 0 R 706 0 R 706 0 R 702 0 R 706 0 R 706 0 R 706 0 R 703 0 R 706 0 R 706 0 R 706 0 R 706 0 R 706 0 R 704 0 R 706 0 R 706 0 R 706 0 R 705 0 R 706 0 R 707 0 R 707 0 R 714 0 R 708 0 R 714 0 R 714 0 R 709 0 R 714 0 R 714 0 R 710 0 R 714 0 R 711 0 R 714 0 R 712 0 R 714 0 R 713 0 R 714 0 R 714 0 R 717 0 R 717 0 R 717 0 R 717 0 R 715 0 R 717 0 R 716 0 R 717 0 R 717 0 R 717 0 R 717 0 R 718 0 R 730 0 R 719 0 R 730 0 R 720 0 R 730 0 R 721 0 R 730 0 R 722 0 R 730 0 R 723 0 R 730 0 R 724 0 R 730 0 R 725 0 R 730 0 R 726 0 R 730 0 R 727 0 R 730 0 R 730 0 R 728 0 R 730 0 R 730 0 R 730 0 R 729 0 R 730 0 R 730 0 R 730 0 R 730 0 R 731 0 R 731 0 R 735 0 R 735 0 R 735 0 R 732 0 R 735 0 R 735 0 R 735 0 R 733 0 R 735 0 R 735 0 R 735 0 R 735 0 R 734 0 R 735 0 R 735 0 R 736 0 R 743 0 R 737 0 R 743 0 R 743 0 R 738 0 R 743 0 R 739 0 R 743 0 R 740 0 R 743 0 R 741 0 R 743 0 R 742 0 R 743 0 R 743 0 R 743 0 R 743 0 R 745 0 R 744 0 R 745 0 R 745 0 R 746 0 R 746 0 R 747 0 R 747 0 R 748 0 R 753 0 R 753 0 R 753 0 R 749 0 R 753 0 R 753 0 R 750 0 R 753 0 R 751 0 R 753 0 R 753 0 R 753 0 R 752 0 R 753 0 R 753 0 R 753 0 R 754 0 R 756 0 R 758 0 R 760 0 R 764 0 R 765 0 R 767 0 R 768 0 R 770 0 R 771 0 R 773 0 R 774 0 R 776 0 R 777 0 R 779 0 R 780 0 R 782 0 R 783 0 R 785 0 R 786 0 R 788 0 R 789 0 R 791 0 R 792 0 R 794 0 R 795 0 R 797 0 R 798 0 R 800 0 R 801 0 R 803 0 R 804 0 R 806 0 R 807 0 R 809 0 R 810 0 R 812 0 R 813 0 R 815 0 R 816 0 R 818 0 R 819 0 R 821 0 R 822 0 R 824 0 R 825 0 R 827 0 R 828 0 R 839 0 R 832 0 R 839 0 R 839 0 R 833 0 R 839 0 R 839 0 R 834 0 R 839 0 R 839 0 R 839 0 R 835 0 R 839 0 R 836 0 R 839 0 R 837 0 R 839 0 R 838 0 R 839 0 R 839 0 R 839 0 R 839 0 R 847 0 R 847 0 R 840 0 R 847 0 R 841 0 R 847 0 R 842 0 R 847 0 R 847 0 R 847 0 R 843 0 R 847 0 R 844 0 R 847 0 R 845 0 R 847 0 R 846 0 R 847 0 R 848 0 R 848 0 R 848 0 R 848 0 R 848 0 R 848 0 R 848 0 R 848 0 R 848 0 R 849 0 R 849 0 R 850 0 R 850 0 R 850 0 R 850 0 R 850 0 R 850 0 R 850 0 R 850 0 R 850 0 R 850 0 R 850 0 R 850 0 R 850 0 R 850 0 R 851 0 R 851 0 R 851 0 R 852 0 R 852 0 R 852 0 R 853 0 R 853 0 R 853 0 R 853 0 R 853 0 R 853 0 R 855 0 R 855 0 R 855 0 R 855 0 R 855 0 R 855 0 R 855 0 R 856 0 R 856 0 R 856 0 R 856 0 R 856 0 R 856 0 R 856 0 R 856 0 R 856 0 R 857 0 R 857 0 R 857 0 R 857 0 R 857 0 R 857 0 R 857 0 R 857 0 R 857 0 R 858 0 R 858 0 R 858 0 R 858 0 R 858 0 R 858 0 R 859 0 R 859 0 R 859 0 R 859 0 R 859 0 R 859 0 R 859 0 R 859 0 R 859 0 R 859 0 R 859 0 R 859 0 R 859 0 R 859 0 R 859 0 R] endobj 65 0 obj -<> +[860 0 R 860 0 R 860 0 R 860 0 R 860 0 R 860 0 R 860 0 R 860 0 R 860 0 R 860 0 R 860 0 R 860 0 R 860 0 R 860 0 R 860 0 R 860 0 R 860 0 R 860 0 R 861 0 R 861 0 R 861 0 R 861 0 R 861 0 R 861 0 R 861 0 R 861 0 R 861 0 R 861 0 R 861 0 R 861 0 R 861 0 R 862 0 R 862 0 R 862 0 R 862 0 R 863 0 R 863 0 R 865 0 R 865 0 R 866 0 R 866 0 R 868 0 R 874 0 R 869 0 R 874 0 R 874 0 R 870 0 R 874 0 R 871 0 R 874 0 R 874 0 R 874 0 R 872 0 R 874 0 R 874 0 R 874 0 R 873 0 R 874 0 R 880 0 R 875 0 R 880 0 R 876 0 R 880 0 R 880 0 R 880 0 R 880 0 R 880 0 R 880 0 R 877 0 R 880 0 R 880 0 R 878 0 R 880 0 R 880 0 R 880 0 R 879 0 R 880 0 R 880 0 R 880 0 R 880 0 R 881 0 R 881 0 R 883 0 R 882 0 R 883 0 R 883 0 R 883 0 R 883 0 R 883 0 R 883 0 R 883 0 R 883 0 R 885 0 R 885 0 R 885 0 R 885 0 R 885 0 R 884 0 R 885 0 R 885 0 R 885 0 R 885 0 R 889 0 R 889 0 R 889 0 R 889 0 R 889 0 R 889 0 R 886 0 R 889 0 R 887 0 R 889 0 R 888 0 R 889 0 R 889 0 R 890 0 R 890 0 R 890 0 R 891 0 R 892 0 R 892 0 R 892 0 R 892 0 R 892 0 R 892 0 R 892 0 R 893 0 R 893 0 R 895 0 R 895 0 R 895 0 R 895 0 R 894 0 R 895 0 R 895 0 R 895 0 R 895 0 R 895 0 R 895 0 R 896 0 R 897 0 R 898 0 R 899 0 R 900 0 R 901 0 R 902 0 R 903 0 R 903 0 R 903 0 R 903 0 R 903 0 R 903 0 R 903 0 R 903 0 R 903 0 R 903 0 R 903 0 R 903 0 R 904 0 R 904 0 R 904 0 R 904 0 R 904 0 R 904 0 R 904 0 R 904 0 R 904 0 R 904 0 R 904 0 R 904 0 R 904 0 R 904 0 R 904 0 R 904 0 R 904 0 R 904 0 R 904 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 905 0 R 906 0 R 906 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 907 0 R 908 0 R 908 0 R 908 0 R 908 0 R 908 0 R 908 0 R 908 0 R 909 0 R 909 0 R 909 0 R 909 0 R 909 0 R 909 0 R 909 0 R 910 0 R 910 0 R 910 0 R 911 0 R 911 0 R 912 0 R 914 0 R 917 0 R 917 0 R 917 0 R 917 0 R 917 0 R 917 0 R 915 0 R 917 0 R 916 0 R 917 0 R 917 0 R 922 0 R 918 0 R 922 0 R 919 0 R 922 0 R 922 0 R 920 0 R 922 0 R 921 0 R 922 0 R 922 0 R 930 0 R 923 0 R 930 0 R 930 0 R 924 0 R 930 0 R 930 0 R 930 0 R 930 0 R 925 0 R 930 0 R 926 0 R 930 0 R 930 0 R 927 0 R 930 0 R 930 0 R 928 0 R 930 0 R 930 0 R 930 0 R 929 0 R 930 0 R 930 0 R 941 0 R 931 0 R 941 0 R 941 0 R 932 0 R 941 0 R 941 0 R 941 0 R 941 0 R 933 0 R 933 0 R 941 0 R 941 0 R 934 0 R 941 0 R 941 0 R 941 0 R 935 0 R 941 0 R 941 0 R 936 0 R 941 0 R 941 0 R 937 0 R 937 0 R 941 0 R 938 0 R 941 0 R 941 0 R 939 0 R 941 0 R 941 0 R 940 0 R 940 0 R 941 0 R 942 0 R 942 0 R 1071 0 R 1071 0 R 1074 0 R 1072 0 R 1074 0 R 1074 0 R 1074 0 R 1074 0 R 1074 0 R 1073 0 R 1074 0 R] endobj 66 0 obj -<> +[951 0 R 952 0 R 953 0 R 954 0 R 955 0 R 956 0 R 957 0 R 958 0 R 958 0 R 959 0 R 960 0 R 960 0 R 961 0 R 962 0 R 962 0 R 963 0 R 964 0 R 964 0 R 964 0 R 965 0 R 966 0 R 966 0 R 967 0 R 968 0 R 968 0 R 969 0 R 970 0 R 971 0 R 972 0 R 973 0 R 973 0 R 973 0 R 974 0 R 975 0 R 976 0 R 977 0 R 978 0 R 979 0 R 980 0 R 981 0 R 982 0 R 983 0 R 983 0 R 984 0 R 985 0 R 985 0 R 985 0 R 986 0 R 987 0 R 987 0 R 988 0 R 989 0 R 989 0 R 990 0 R 991 0 R 992 0 R 993 0 R 994 0 R 995 0 R 996 0 R 997 0 R 998 0 R 999 0 R 1000 0 R 1001 0 R 1002 0 R 1003 0 R 1004 0 R 1005 0 R 1006 0 R 1007 0 R 1008 0 R 1009 0 R 1010 0 R 1011 0 R 1012 0 R 1013 0 R 1014 0 R 1015 0 R 1016 0 R 1017 0 R 1018 0 R 1019 0 R 1020 0 R 1021 0 R 1022 0 R 1023 0 R 1024 0 R 1025 0 R 1026 0 R 1027 0 R 1028 0 R 1029 0 R 1030 0 R 1031 0 R 1032 0 R 1033 0 R 1034 0 R 1035 0 R 1036 0 R 1037 0 R 1038 0 R 1039 0 R 1040 0 R 1041 0 R 1042 0 R 1043 0 R 1044 0 R 1045 0 R 1046 0 R 1047 0 R 1048 0 R 1049 0 R 1050 0 R 1051 0 R 1052 0 R 1053 0 R 1054 0 R 1055 0 R 1056 0 R 1057 0 R 1058 0 R 1059 0 R 1060 0 R 1060 0 R 1061 0 R 1062 0 R 1062 0 R 1063 0 R 1064 0 R 1065 0 R 1066 0 R 1066 0 R 1067 0 R 1068 0 R 943 0 R 943 0 R 944 0 R 945 0 R 946 0 R 947 0 R 948 0 R 949 0 R 1076 0 R 1076 0 R 1076 0 R 1076 0 R 1076 0 R 1076 0 R 1076 0 R 1076 0 R 1076 0 R 1075 0 R 1076 0 R 1076 0 R 1076 0 R 1076 0 R 1084 0 R 1077 0 R 1084 0 R 1084 0 R 1078 0 R 1084 0 R 1084 0 R 1079 0 R 1084 0 R 1080 0 R 1084 0 R 1081 0 R 1084 0 R 1082 0 R 1084 0 R 1083 0 R 1084 0 R 1089 0 R 1089 0 R 1085 0 R 1089 0 R 1086 0 R 1089 0 R 1089 0 R 1089 0 R 1089 0 R 1089 0 R 1089 0 R 1089 0 R 1089 0 R 1089 0 R 1087 0 R 1089 0 R 1088 0 R 1089 0 R 1089 0 R 1089 0 R 1089 0 R 1090 0 R 1090 0 R 1091 0 R 1091 0 R 1091 0 R 1091 0 R 1091 0 R 1091 0 R 1091 0 R 1091 0 R 1091 0 R 1091 0 R 1091 0 R 1091 0 R 1091 0 R 1091 0 R 1092 0 R 1092 0 R 1092 0 R 1092 0 R 1092 0 R 1092 0 R 1092 0 R 1093 0 R 1093 0 R 1093 0 R 1093 0 R 1093 0 R 1093 0 R 1093 0 R 1095 0 R 1095 0 R 1095 0 R 1095 0 R 1095 0 R 1095 0 R 1095 0 R 1096 0 R 1096 0 R 1096 0 R 1096 0 R 1096 0 R 1096 0 R 1096 0 R 1097 0 R 1097 0 R 1097 0 R 1097 0 R 1097 0 R 1097 0 R 1097 0 R 1098 0 R 1098 0 R 1098 0 R 1098 0 R 1098 0 R 1098 0 R 1098 0 R 1099 0 R 1099 0 R 1099 0 R 1099 0 R 1099 0 R 1099 0 R 1099 0 R 1100 0 R 1100 0 R 1100 0 R 1100 0 R 1100 0 R 1100 0 R 1100 0 R 1101 0 R 1101 0 R 1101 0 R 1101 0 R 1101 0 R 1101 0 R 1101 0 R 1102 0 R 1102 0 R 1102 0 R 1102 0 R 1102 0 R 1102 0 R 1102 0 R 1103 0 R 1103 0 R 1103 0 R 1103 0 R 1103 0 R 1103 0 R 1103 0 R 1104 0 R 1104 0 R 1106 0 R 1106 0 R 1106 0 R 1106 0 R 1106 0 R 1106 0 R 1106 0 R 1106 0 R 1106 0 R 1106 0 R 1106 0 R 1106 0 R 1106 0 R 1106 0 R 1107 0 R 1107 0 R 1107 0 R 1107 0 R 1107 0 R 1107 0 R 1107 0 R 1108 0 R 1108 0 R 1108 0 R 1108 0 R 1108 0 R 1108 0 R 1108 0 R 1109 0 R 1109 0 R 1109 0 R 1109 0 R 1109 0 R 1109 0 R 1109 0 R 1110 0 R 1110 0 R 1110 0 R 1110 0 R 1110 0 R 1110 0 R 1110 0 R 1111 0 R 1111 0 R 1111 0 R 1111 0 R 1111 0 R 1111 0 R 1111 0 R 1113 0 R 1113 0 R 1113 0 R 1113 0 R 1113 0 R 1113 0 R 1113 0 R 1114 0 R 1114 0 R 1114 0 R 1114 0 R 1114 0 R 1114 0 R 1114 0 R 1115 0 R 1115 0 R 1115 0 R 1115 0 R 1115 0 R 1115 0 R 1115 0 R 1116 0 R 1116 0 R 1118 0 R 1120 0 R 1119 0 R 1120 0 R 1120 0 R 1138 0 R 1121 0 R 1138 0 R 1138 0 R 1138 0 R 1138 0 R 1122 0 R 1138 0 R 1123 0 R 1138 0 R 1124 0 R 1138 0 R 1138 0 R 1138 0 R 1125 0 R 1138 0 R 1126 0 R 1138 0 R 1127 0 R 1138 0 R 1128 0 R 1138 0 R 1129 0 R 1138 0 R 1130 0 R 1138 0 R 1138 0 R 1138 0 R 1138 0 R 1131 0 R 1138 0 R 1132 0 R 1138 0 R 1138 0 R 1133 0 R 1138 0 R 1138 0 R 1134 0 R 1138 0 R 1138 0 R 1135 0 R 1138 0 R 1138 0 R 1136 0 R 1138 0 R 1138 0 R 1138 0 R 1137 0 R 1138 0 R 1147 0 R 1147 0 R 1147 0 R 1147 0 R 1147 0 R 1139 0 R 1147 0 R 1140 0 R 1147 0 R 1141 0 R 1147 0 R 1142 0 R 1147 0 R 1143 0 R 1147 0 R 1144 0 R 1147 0 R 1145 0 R 1147 0 R 1146 0 R 1147 0 R] endobj 67 0 obj -<> +[1147 0 R 1147 0 R 1147 0 R 1147 0 R 1147 0 R 1148 0 R 1150 0 R 1149 0 R 1150 0 R 1150 0 R 1150 0 R 1165 0 R 1151 0 R 1165 0 R 1165 0 R 1152 0 R 1165 0 R 1153 0 R 1165 0 R 1165 0 R 1154 0 R 1165 0 R 1155 0 R 1165 0 R 1165 0 R 1156 0 R 1165 0 R 1165 0 R 1165 0 R 1157 0 R 1165 0 R 1158 0 R 1165 0 R 1159 0 R 1165 0 R 1160 0 R 1165 0 R 1165 0 R 1165 0 R 1161 0 R 1165 0 R 1165 0 R 1162 0 R 1165 0 R 1165 0 R 1165 0 R 1165 0 R 1163 0 R 1165 0 R 1164 0 R 1165 0 R 1166 0 R 1166 0 R 1174 0 R 1174 0 R 1167 0 R 1174 0 R 1174 0 R 1174 0 R 1168 0 R 1174 0 R 1174 0 R 1169 0 R 1174 0 R 1174 0 R 1174 0 R 1170 0 R 1174 0 R 1174 0 R 1171 0 R 1174 0 R 1174 0 R 1174 0 R 1174 0 R 1172 0 R 1174 0 R 1174 0 R 1174 0 R 1174 0 R 1173 0 R 1174 0 R 1176 0 R 1175 0 R 1176 0 R 1176 0 R 1176 0 R 1176 0 R 1177 0 R 1184 0 R 1178 0 R 1184 0 R 1184 0 R 1179 0 R 1184 0 R 1184 0 R 1184 0 R 1184 0 R 1180 0 R 1184 0 R 1184 0 R 1181 0 R 1184 0 R 1182 0 R 1184 0 R 1183 0 R 1184 0 R 1184 0 R 1184 0 R 1184 0 R 1185 0 R 1187 0 R 1187 0 R 1187 0 R 1187 0 R 1187 0 R 1186 0 R 1187 0 R 1188 0 R 1188 0 R 1189 0 R 1189 0 R 1189 0 R 1189 0 R 1189 0 R 1189 0 R 1189 0 R 1189 0 R 1189 0 R 1189 0 R 1189 0 R 1189 0 R 1189 0 R 1189 0 R 1189 0 R 1190 0 R 1190 0 R 1190 0 R 1190 0 R 1190 0 R 1190 0 R 1190 0 R 1190 0 R 1190 0 R 1190 0 R 1190 0 R 1191 0 R 1191 0 R 1191 0 R 1191 0 R 1191 0 R 1191 0 R 1191 0 R 1191 0 R 1191 0 R 1191 0 R 1191 0 R 1191 0 R 1191 0 R 1191 0 R 1191 0 R 1191 0 R 1191 0 R 1191 0 R 1191 0 R 1192 0 R 1192 0 R 1192 0 R 1192 0 R 1192 0 R 1192 0 R 1192 0 R 1192 0 R 1193 0 R 1193 0 R 1193 0 R 1193 0 R 1193 0 R 1193 0 R 1193 0 R 1193 0 R 1193 0 R 1193 0 R 1194 0 R 1194 0 R 1194 0 R 1194 0 R 1194 0 R 1194 0 R 1194 0 R 1194 0 R 1195 0 R 1195 0 R 1195 0 R 1195 0 R 1195 0 R 1196 0 R 1196 0 R 1196 0 R 1197 0 R 1197 0 R 1197 0 R 1197 0 R 1197 0 R 1197 0 R 1197 0 R 1198 0 R 1198 0 R 1198 0 R 1198 0 R 1198 0 R 1198 0 R 1198 0 R 1198 0 R 1198 0 R 1198 0 R 1198 0 R 1198 0 R 1198 0 R 1198 0 R 1199 0 R 1199 0 R 1199 0 R 1199 0 R 1199 0 R 1200 0 R 1200 0 R 1200 0 R 1200 0 R 1200 0 R 1201 0 R 1201 0 R 1201 0 R 1201 0 R 1201 0 R 1201 0 R 1201 0 R 1201 0 R 1201 0 R 1201 0 R 1201 0 R 1202 0 R 1202 0 R 1202 0 R 1202 0 R 1202 0 R 1202 0 R 1202 0 R 1203 0 R 1203 0 R 1203 0 R 1204 0 R 1204 0 R 1204 0 R 1205 0 R 1205 0 R 1206 0 R 1208 0 R 1219 0 R 1209 0 R 1219 0 R 1210 0 R 1210 0 R 1219 0 R 1219 0 R 1211 0 R 1219 0 R 1219 0 R 1212 0 R 1219 0 R 1219 0 R 1213 0 R 1219 0 R 1219 0 R 1214 0 R 1219 0 R 1215 0 R 1219 0 R 1216 0 R 1219 0 R 1219 0 R 1217 0 R 1219 0 R 1218 0 R 1219 0 R 1219 0 R 1219 0 R 1222 0 R 1220 0 R 1222 0 R 1222 0 R 1221 0 R 1222 0 R 1222 0 R 1222 0 R 1222 0 R 1238 0 R 1238 0 R 1223 0 R 1238 0 R 1224 0 R 1238 0 R 1238 0 R 1225 0 R 1238 0 R 1226 0 R 1238 0 R 1227 0 R 1238 0 R 1238 0 R 1228 0 R 1238 0 R 1238 0 R 1238 0 R 1229 0 R 1238 0 R 1230 0 R 1238 0 R 1238 0 R 1231 0 R 1238 0 R 1238 0 R 1232 0 R 1238 0 R 1238 0 R 1233 0 R 1238 0 R 1238 0 R 1234 0 R 1234 0 R 1238 0 R 1235 0 R 1238 0 R 1236 0 R 1238 0 R 1237 0 R 1238 0 R 1247 0 R 1239 0 R 1247 0 R 1247 0 R 1247 0 R 1240 0 R 1247 0 R 1241 0 R 1247 0 R 1242 0 R 1247 0 R 1243 0 R 1247 0 R 1244 0 R 1247 0 R 1247 0 R 1245 0 R 1245 0 R 1247 0 R 1246 0 R 1247 0 R 1247 0 R 1247 0 R 1247 0 R 1247 0 R 1248 0 R 1252 0 R 1249 0 R 1252 0 R 1252 0 R 1252 0 R 1250 0 R 1252 0 R 1252 0 R 1252 0 R 1251 0 R 1252 0 R 1252 0 R 1254 0 R 1253 0 R 1254 0 R 1255 0 R 1263 0 R 1256 0 R 1263 0 R 1257 0 R 1263 0 R 1263 0 R 1258 0 R 1263 0 R 1259 0 R 1263 0 R 1260 0 R 1260 0 R 1263 0 R 1261 0 R 1263 0 R 1262 0 R 1262 0 R 1263 0 R 1268 0 R 1268 0 R 1268 0 R 1264 0 R 1268 0 R 1265 0 R 1265 0 R 1268 0 R 1266 0 R 1268 0 R 1267 0 R 1268 0 R 1268 0 R 1268 0 R 1268 0 R 1268 0 R] endobj 68 0 obj -<> +[1268 0 R 1268 0 R 1268 0 R 1268 0 R 1268 0 R 1268 0 R 1269 0 R 1278 0 R 1278 0 R 1278 0 R 1270 0 R 1278 0 R 1271 0 R 1278 0 R 1278 0 R 1278 0 R 1278 0 R 1278 0 R 1272 0 R 1278 0 R 1278 0 R 1273 0 R 1278 0 R 1274 0 R 1278 0 R 1275 0 R 1278 0 R 1276 0 R 1278 0 R 1277 0 R 1278 0 R 1279 0 R 1282 0 R 1282 0 R 1280 0 R 1282 0 R 1282 0 R 1282 0 R 1282 0 R 1282 0 R 1281 0 R 1282 0 R 1282 0 R 1283 0 R 1283 0 R 1284 0 R 1284 0 R 1285 0 R 1293 0 R 1293 0 R 1286 0 R 1293 0 R 1287 0 R 1293 0 R 1293 0 R 1288 0 R 1293 0 R 1293 0 R 1289 0 R 1293 0 R 1290 0 R 1293 0 R 1293 0 R 1293 0 R 1291 0 R 1293 0 R 1292 0 R 1293 0 R 1293 0 R 1302 0 R 1294 0 R 1302 0 R 1302 0 R 1302 0 R 1302 0 R 1295 0 R 1302 0 R 1302 0 R 1302 0 R 1296 0 R 1302 0 R 1302 0 R 1297 0 R 1302 0 R 1298 0 R 1302 0 R 1302 0 R 1299 0 R 1302 0 R 1302 0 R 1302 0 R 1302 0 R 1302 0 R 1300 0 R 1302 0 R 1302 0 R 1301 0 R 1302 0 R 1302 0 R 1307 0 R 1307 0 R 1307 0 R 1307 0 R 1303 0 R 1307 0 R 1307 0 R 1307 0 R 1304 0 R 1307 0 R 1307 0 R 1307 0 R 1307 0 R 1305 0 R 1307 0 R 1307 0 R 1307 0 R 1306 0 R 1307 0 R 1310 0 R 1308 0 R 1308 0 R 1310 0 R 1310 0 R 1310 0 R 1309 0 R 1310 0 R 1310 0 R 1319 0 R 1319 0 R 1319 0 R 1319 0 R 1319 0 R 1311 0 R 1319 0 R 1312 0 R 1319 0 R 1313 0 R 1319 0 R 1314 0 R 1319 0 R 1319 0 R 1315 0 R 1319 0 R 1316 0 R 1319 0 R 1319 0 R 1319 0 R 1319 0 R 1317 0 R 1319 0 R 1319 0 R 1318 0 R 1319 0 R 1320 0 R 1320 0 R 1321 0 R 1323 0 R 1323 0 R 1322 0 R 1323 0 R 1323 0 R 1323 0 R 1323 0 R 1323 0 R 1324 0 R 1325 0 R 1326 0 R 1327 0 R 1327 0 R 1327 0 R 1327 0 R 1327 0 R 1327 0 R 1327 0 R 1327 0 R 1327 0 R 1327 0 R 1327 0 R 1327 0 R 1327 0 R 1328 0 R 1329 0 R 1330 0 R 1331 0 R 1331 0 R 1331 0 R 1331 0 R 1331 0 R 1332 0 R 1332 0 R 1332 0 R 1332 0 R 1332 0 R 1332 0 R 1332 0 R 1332 0 R 1332 0 R 1332 0 R 1334 0 R 1334 0 R 1334 0 R 1334 0 R 1334 0 R 1334 0 R 1334 0 R 1334 0 R 1334 0 R 1334 0 R 1334 0 R 1334 0 R 1334 0 R 1334 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1335 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1336 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1337 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1338 0 R 1339 0 R 1339 0 R 1339 0 R 1339 0 R 1339 0 R 1339 0 R 1339 0 R 1339 0 R 1339 0 R 1339 0 R 1340 0 R 1340 0 R 1340 0 R 1340 0 R 1340 0 R 1340 0 R 1340 0 R 1340 0 R 1340 0 R 1340 0 R 1340 0 R 1340 0 R 1340 0 R 1341 0 R 1341 0 R 1341 0 R 1341 0 R 1341 0 R 1341 0 R 1341 0 R 1341 0 R 1341 0 R 1341 0 R 1342 0 R 1342 0 R 1342 0 R 1342 0 R 1342 0 R 1342 0 R 1342 0 R 1342 0 R 1342 0 R 1342 0 R 1342 0 R 1342 0 R 1342 0 R 1343 0 R 1343 0 R 1343 0 R 1343 0 R 1343 0 R 1343 0 R 1343 0 R 1343 0 R 1343 0 R 1343 0 R 1343 0 R 1344 0 R 1344 0 R 1344 0 R 1344 0 R 1344 0 R 1344 0 R 1344 0 R 1344 0 R 1344 0 R 1344 0 R 1345 0 R 1345 0 R 1345 0 R 1345 0 R 1345 0 R 1345 0 R 1345 0 R 1345 0 R 1345 0 R 1345 0 R 1345 0 R 1345 0 R 1345 0 R 1346 0 R 1351 0 R 1351 0 R 1351 0 R 1348 0 R 1351 0 R 1351 0 R 1349 0 R 1351 0 R 1351 0 R 1351 0 R 1351 0 R 1351 0 R 1350 0 R 1351 0 R 1353 0 R 1352 0 R 1353 0 R 1353 0 R 1354 0 R 1355 0 R 1361 0 R 1356 0 R 1361 0 R 1357 0 R 1361 0 R 1361 0 R 1358 0 R 1361 0 R 1359 0 R 1361 0 R 1361 0 R 1361 0 R 1361 0 R 1360 0 R 1361 0 R 1361 0 R 1361 0 R 1363 0 R 1364 0 R 1369 0 R 1365 0 R 1369 0 R 1366 0 R 1369 0 R 1367 0 R 1369 0 R 1369 0 R 1369 0 R 1368 0 R 1369 0 R 1369 0 R 1369 0 R 1369 0 R 1371 0 R 1372 0 R 1376 0 R 1373 0 R 1376 0 R 1374 0 R 1376 0 R 1376 0 R 1376 0 R 1376 0 R 1375 0 R 1376 0 R] endobj 69 0 obj -<> +[1403 0 R 1403 0 R 1403 0 R 1403 0 R 1403 0 R 1403 0 R 1403 0 R 1403 0 R 1403 0 R 1403 0 R 1403 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1404 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1405 0 R 1406 0 R 1406 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1407 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1408 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1409 0 R 1410 0 R 1410 0 R 1391 0 R 1392 0 R 1393 0 R 1393 0 R 1394 0 R 1395 0 R 1396 0 R 1397 0 R 1398 0 R 1399 0 R 1400 0 R 1401 0 R 1401 0 R 1401 0 R 1401 0 R 1378 0 R 1379 0 R 1382 0 R 1382 0 R 1382 0 R 1382 0 R 1380 0 R 1382 0 R 1381 0 R 1382 0 R 1382 0 R 1382 0 R 1384 0 R 1385 0 R 1388 0 R 1386 0 R 1388 0 R 1388 0 R 1387 0 R 1388 0 R 1388 0 R 1388 0 R 1388 0 R 1388 0 R 1417 0 R 1417 0 R 1413 0 R 1417 0 R 1414 0 R 1417 0 R 1415 0 R 1417 0 R 1417 0 R 1416 0 R 1417 0 R 1417 0 R 1417 0 R 1424 0 R 1424 0 R 1424 0 R 1418 0 R 1424 0 R 1424 0 R 1424 0 R 1419 0 R 1424 0 R 1424 0 R 1420 0 R 1424 0 R 1424 0 R 1421 0 R 1424 0 R 1424 0 R 1424 0 R 1424 0 R 1422 0 R 1424 0 R 1424 0 R 1424 0 R 1424 0 R 1424 0 R 1423 0 R 1424 0 R 1424 0 R 1424 0 R 1425 0 R 1425 0 R 1426 0 R 1433 0 R 1433 0 R 1427 0 R 1433 0 R 1433 0 R 1433 0 R 1428 0 R 1433 0 R 1433 0 R 1433 0 R 1433 0 R 1429 0 R 1433 0 R 1433 0 R 1430 0 R 1433 0 R 1433 0 R 1433 0 R 1431 0 R 1433 0 R 1432 0 R 1433 0 R 1433 0 R 1438 0 R 1434 0 R 1438 0 R 1438 0 R 1438 0 R 1435 0 R 1438 0 R 1438 0 R 1438 0 R 1436 0 R 1438 0 R 1438 0 R 1437 0 R 1438 0 R 1447 0 R 1439 0 R 1447 0 R 1440 0 R 1447 0 R 1441 0 R 1447 0 R 1442 0 R 1447 0 R 1443 0 R 1447 0 R 1447 0 R 1447 0 R 1444 0 R 1447 0 R 1447 0 R 1445 0 R 1447 0 R 1447 0 R 1446 0 R 1447 0 R 1447 0 R 1447 0 R 1447 0 R 1447 0 R 1447 0 R 1447 0 R 1448 0 R 1454 0 R 1449 0 R 1454 0 R 1454 0 R 1450 0 R 1454 0 R 1451 0 R 1454 0 R 1454 0 R 1454 0 R 1452 0 R 1454 0 R 1454 0 R 1454 0 R 1453 0 R 1454 0 R 1455 0 R 1455 0 R 1456 0 R 1456 0 R 1467 0 R 1467 0 R 1467 0 R 1457 0 R 1467 0 R 1458 0 R 1467 0 R 1459 0 R 1467 0 R 1460 0 R 1467 0 R 1461 0 R 1467 0 R 1462 0 R 1467 0 R 1463 0 R 1467 0 R 1467 0 R 1467 0 R 1464 0 R 1467 0 R 1467 0 R 1465 0 R 1467 0 R 1466 0 R 1467 0 R 1467 0 R 1474 0 R 1474 0 R 1474 0 R 1474 0 R 1468 0 R 1474 0 R 1469 0 R 1474 0 R 1474 0 R 1470 0 R 1474 0 R 1471 0 R 1474 0 R 1474 0 R 1472 0 R 1474 0 R 1473 0 R 1473 0 R 1474 0 R 1474 0 R 1474 0 R 1482 0 R 1475 0 R 1482 0 R 1482 0 R 1476 0 R 1482 0 R 1482 0 R 1482 0 R 1482 0 R 1477 0 R 1482 0 R 1482 0 R 1478 0 R 1482 0 R 1479 0 R 1482 0 R 1480 0 R 1482 0 R 1482 0 R 1481 0 R 1482 0 R 1482 0 R 1482 0 R 1488 0 R 1488 0 R 1488 0 R 1483 0 R 1488 0 R 1484 0 R 1488 0 R 1488 0 R 1485 0 R 1488 0 R 1488 0 R 1488 0 R 1488 0 R 1486 0 R 1488 0 R 1488 0 R 1487 0 R 1488 0 R] endobj 70 0 obj -<> +[1522 0 R 1523 0 R 1524 0 R 1525 0 R 1526 0 R 1527 0 R 1528 0 R 1529 0 R 1530 0 R 1530 0 R 1531 0 R 1532 0 R 1532 0 R 1533 0 R 1534 0 R 1535 0 R 1536 0 R 1537 0 R 1538 0 R 1539 0 R 1540 0 R 1541 0 R 1542 0 R 1543 0 R 1544 0 R 1545 0 R 1546 0 R 1547 0 R 1548 0 R 1549 0 R 1550 0 R 1551 0 R 1552 0 R 1553 0 R 1554 0 R 1555 0 R 1556 0 R 1557 0 R 1558 0 R 1559 0 R 1560 0 R 1561 0 R 1562 0 R 1563 0 R 1564 0 R 1565 0 R 1566 0 R 1567 0 R 1568 0 R 1569 0 R 1570 0 R 1571 0 R 1571 0 R 1572 0 R 1573 0 R 1574 0 R 1575 0 R 1576 0 R 1577 0 R 1578 0 R 1579 0 R 1580 0 R 1581 0 R 1582 0 R 1583 0 R 1584 0 R 1585 0 R 1586 0 R 1587 0 R 1588 0 R 1589 0 R 1590 0 R 1591 0 R 1592 0 R 1593 0 R 1594 0 R 1595 0 R 1596 0 R 1597 0 R 1598 0 R 1599 0 R 1600 0 R 1601 0 R 1602 0 R 1603 0 R 1604 0 R 1605 0 R 1606 0 R 1607 0 R 1608 0 R 1609 0 R 1610 0 R 1611 0 R 1612 0 R 1613 0 R 1614 0 R 1614 0 R 1614 0 R 1615 0 R 1616 0 R 1617 0 R 1618 0 R 1619 0 R 1620 0 R 1621 0 R 1622 0 R 1623 0 R 1624 0 R 1625 0 R 1626 0 R 1627 0 R 1628 0 R 1629 0 R 1630 0 R 1631 0 R 1632 0 R 1633 0 R 1634 0 R 1635 0 R 1636 0 R 1637 0 R 1638 0 R 1639 0 R 1639 0 R 1640 0 R 1641 0 R 1641 0 R 1642 0 R 1643 0 R 1644 0 R 1645 0 R 1512 0 R 1513 0 R 1514 0 R 1514 0 R 1515 0 R 1516 0 R 1517 0 R 1518 0 R 1518 0 R 1519 0 R 1520 0 R 1488 0 R 1488 0 R 1488 0 R 1489 0 R 1491 0 R 1490 0 R 1491 0 R 1491 0 R 1492 0 R 1492 0 R 1493 0 R 1500 0 R 1494 0 R 1500 0 R 1500 0 R 1495 0 R 1500 0 R 1496 0 R 1500 0 R 1500 0 R 1497 0 R 1497 0 R 1500 0 R 1498 0 R 1500 0 R 1500 0 R 1499 0 R 1500 0 R 1500 0 R 1500 0 R 1505 0 R 1501 0 R 1505 0 R 1502 0 R 1505 0 R 1505 0 R 1503 0 R 1505 0 R 1504 0 R 1505 0 R 1505 0 R 1510 0 R 1510 0 R 1506 0 R 1510 0 R 1510 0 R 1510 0 R 1510 0 R 1507 0 R 1510 0 R 1508 0 R 1510 0 R 1510 0 R 1510 0 R 1509 0 R 1510 0 R 1510 0 R 1510 0 R 1510 0 R 1511 0 R 1511 0 R 1648 0 R 1648 0 R 1649 0 R 1656 0 R 1656 0 R 1656 0 R 1656 0 R 1656 0 R 1650 0 R 1656 0 R 1651 0 R 1656 0 R 1652 0 R 1656 0 R 1653 0 R 1656 0 R 1656 0 R 1654 0 R 1656 0 R 1656 0 R 1655 0 R 1656 0 R 1656 0 R 1670 0 R 1657 0 R 1670 0 R 1670 0 R 1670 0 R 1658 0 R 1670 0 R 1659 0 R 1659 0 R 1670 0 R 1670 0 R 1660 0 R 1670 0 R 1661 0 R 1670 0 R 1662 0 R 1662 0 R 1670 0 R 1663 0 R 1670 0 R 1664 0 R 1670 0 R 1665 0 R 1670 0 R 1670 0 R 1666 0 R 1670 0 R 1670 0 R 1667 0 R 1670 0 R 1668 0 R 1670 0 R 1669 0 R 1670 0 R 1670 0 R 1685 0 R 1671 0 R 1685 0 R 1672 0 R 1685 0 R 1673 0 R 1673 0 R 1685 0 R 1674 0 R 1685 0 R 1675 0 R 1685 0 R 1685 0 R 1676 0 R 1685 0 R 1677 0 R 1685 0 R 1685 0 R 1685 0 R 1678 0 R 1685 0 R 1679 0 R 1685 0 R 1685 0 R 1685 0 R 1685 0 R 1685 0 R 1685 0 R 1680 0 R 1685 0 R 1681 0 R 1685 0 R 1685 0 R 1682 0 R 1685 0 R 1683 0 R 1685 0 R 1684 0 R 1684 0 R 1685 0 R 1685 0 R 1695 0 R 1686 0 R 1695 0 R 1687 0 R 1695 0 R 1695 0 R 1688 0 R 1695 0 R 1695 0 R 1689 0 R 1695 0 R 1695 0 R 1690 0 R 1695 0 R 1691 0 R 1695 0 R 1695 0 R 1695 0 R 1695 0 R 1695 0 R 1695 0 R 1692 0 R 1695 0 R 1693 0 R 1695 0 R 1694 0 R 1695 0 R 1695 0 R] endobj 71 0 obj -<> +[1696 0 R 1700 0 R 1700 0 R 1700 0 R 1700 0 R 1697 0 R 1700 0 R 1700 0 R 1698 0 R 1698 0 R 1700 0 R 1700 0 R 1700 0 R 1699 0 R 1700 0 R 1701 0 R 1701 0 R 1708 0 R 1708 0 R 1702 0 R 1708 0 R 1708 0 R 1708 0 R 1703 0 R 1708 0 R 1704 0 R 1708 0 R 1708 0 R 1708 0 R 1705 0 R 1708 0 R 1706 0 R 1708 0 R 1708 0 R 1707 0 R 1708 0 R 1711 0 R 1709 0 R 1711 0 R 1711 0 R 1711 0 R 1710 0 R 1711 0 R 1711 0 R 1711 0 R 1717 0 R 1717 0 R 1712 0 R 1717 0 R 1717 0 R 1717 0 R 1717 0 R 1713 0 R 1717 0 R 1717 0 R 1717 0 R 1714 0 R 1717 0 R 1717 0 R 1717 0 R 1717 0 R 1717 0 R 1717 0 R 1717 0 R 1717 0 R 1717 0 R 1715 0 R 1717 0 R 1717 0 R 1716 0 R 1717 0 R 1724 0 R 1724 0 R 1718 0 R 1724 0 R 1724 0 R 1719 0 R 1724 0 R 1720 0 R 1724 0 R 1721 0 R 1724 0 R 1724 0 R 1722 0 R 1724 0 R 1724 0 R 1723 0 R 1724 0 R 1724 0 R 1724 0 R 1726 0 R 1725 0 R 1726 0 R 1726 0 R 1726 0 R 1727 0 R 1727 0 R 1728 0 R 1729 0 R 1730 0 R 1730 0 R 1730 0 R 1730 0 R 1730 0 R 1730 0 R 1730 0 R 1730 0 R 1730 0 R 1730 0 R 1730 0 R 1731 0 R 1732 0 R 1732 0 R 1732 0 R 1732 0 R 1732 0 R 1732 0 R 1732 0 R 1732 0 R 1732 0 R 1732 0 R 1732 0 R 1732 0 R 1732 0 R 1732 0 R 1732 0 R 1732 0 R 1732 0 R 1732 0 R 1733 0 R 1733 0 R 1733 0 R 1733 0 R 1733 0 R 1733 0 R 1733 0 R 1733 0 R 1733 0 R 1733 0 R 1733 0 R 1733 0 R 1733 0 R 1733 0 R 1733 0 R 1733 0 R 1733 0 R 1733 0 R 1733 0 R 1738 0 R 1735 0 R 1738 0 R 1738 0 R 1738 0 R 1736 0 R 1738 0 R 1737 0 R 1738 0 R 1738 0 R 1738 0 R 1739 0 R 1740 0 R 1740 0 R 1740 0 R 1740 0 R 1740 0 R 1740 0 R 1740 0 R 1740 0 R 1740 0 R 1740 0 R 1740 0 R 1740 0 R 1740 0 R 1740 0 R 1740 0 R 1741 0 R 1741 0 R 1741 0 R 1741 0 R 1741 0 R 1741 0 R 1741 0 R 1741 0 R 1741 0 R 1741 0 R 1741 0 R 1741 0 R 1741 0 R 1741 0 R 1742 0 R 1742 0 R 1742 0 R 1742 0 R 1742 0 R 1742 0 R 1742 0 R 1742 0 R 1744 0 R 1745 0 R 1745 0 R 1745 0 R 1745 0 R 1745 0 R 1745 0 R 1745 0 R 1745 0 R 1745 0 R 1745 0 R 1745 0 R 1745 0 R 1745 0 R 1745 0 R 1745 0 R 1745 0 R 1745 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1746 0 R 1747 0 R 1748 0 R 1748 0 R 1748 0 R 1748 0 R 1748 0 R 1748 0 R 1748 0 R 1748 0 R 1748 0 R 1748 0 R 1748 0 R 1748 0 R 1748 0 R 1748 0 R 1748 0 R 1748 0 R 1748 0 R 1753 0 R 1750 0 R 1753 0 R 1753 0 R 1751 0 R 1753 0 R 1753 0 R 1752 0 R 1753 0 R 1754 0 R 1762 0 R 1755 0 R 1755 0 R 1762 0 R 1762 0 R 1762 0 R 1756 0 R 1762 0 R 1757 0 R 1762 0 R 1762 0 R 1762 0 R 1758 0 R 1762 0 R 1762 0 R 1759 0 R 1762 0 R 1760 0 R 1762 0 R 1762 0 R 1761 0 R 1762 0 R 1762 0 R 1768 0 R 1768 0 R 1763 0 R 1768 0 R 1764 0 R 1768 0 R 1768 0 R 1768 0 R 1765 0 R 1768 0 R 1768 0 R 1766 0 R 1768 0 R 1768 0 R 1768 0 R 1767 0 R 1768 0 R 1768 0 R 1768 0 R 1768 0 R 1783 0 R 1783 0 R 1783 0 R 1769 0 R 1783 0 R 1770 0 R 1783 0 R 1783 0 R 1771 0 R 1783 0 R 1783 0 R 1772 0 R 1783 0 R 1783 0 R 1773 0 R 1783 0 R 1774 0 R 1774 0 R 1783 0 R 1775 0 R 1783 0 R 1783 0 R 1776 0 R 1783 0 R 1783 0 R 1777 0 R 1783 0 R 1783 0 R 1778 0 R 1783 0 R 1779 0 R 1783 0 R 1780 0 R 1783 0 R 1781 0 R 1783 0 R 1783 0 R 1783 0 R 1782 0 R 1783 0 R 1784 0 R 1784 0 R 1792 0 R 1785 0 R 1785 0 R 1792 0 R 1786 0 R 1792 0 R 1792 0 R 1787 0 R 1792 0 R 1792 0 R 1792 0 R 1792 0 R 1788 0 R 1788 0 R 1792 0 R 1792 0 R 1792 0 R 1789 0 R 1789 0 R 1792 0 R 1790 0 R 1792 0 R 1791 0 R 1792 0 R 1796 0 R 1796 0 R 1793 0 R 1796 0 R 1794 0 R 1796 0 R 1795 0 R 1796 0 R 1796 0 R 1797 0 R 1798 0 R 1798 0 R 1802 0 R 1802 0 R 1799 0 R 1802 0 R 1802 0 R 1802 0 R 1802 0 R 1800 0 R 1802 0 R] endobj 72 0 obj -<> +[1802 0 R 1802 0 R 1802 0 R 1802 0 R 1801 0 R 1802 0 R 1802 0 R 1802 0 R 1803 0 R 1803 0 R 1830 0 R 1830 0 R 1804 0 R 1830 0 R 1805 0 R 1830 0 R 1806 0 R 1830 0 R 1830 0 R 1807 0 R 1830 0 R 1808 0 R 1830 0 R 1830 0 R 1809 0 R 1830 0 R 1810 0 R 1830 0 R 1811 0 R 1830 0 R 1812 0 R 1830 0 R 1830 0 R 1813 0 R 1830 0 R 1814 0 R 1830 0 R 1815 0 R 1830 0 R 1816 0 R 1830 0 R 1817 0 R 1830 0 R 1818 0 R 1830 0 R 1819 0 R 1830 0 R 1830 0 R 1820 0 R 1830 0 R 1821 0 R 1830 0 R 1822 0 R 1830 0 R 1830 0 R 1830 0 R 1823 0 R 1830 0 R 1830 0 R 1824 0 R 1830 0 R 1830 0 R 1825 0 R 1830 0 R 1826 0 R 1826 0 R 1830 0 R 1827 0 R 1827 0 R 1830 0 R 1828 0 R 1830 0 R 1829 0 R 1830 0 R 1830 0 R 1830 0 R 1831 0 R 1831 0 R 1834 0 R 1832 0 R 1834 0 R 1834 0 R 1834 0 R 1834 0 R 1834 0 R 1834 0 R 1834 0 R 1834 0 R 1833 0 R 1833 0 R 1834 0 R 1835 0 R 1835 0 R 1836 0 R 1838 0 R 1837 0 R 1838 0 R 1838 0 R 1838 0 R 1838 0 R 1838 0 R 1838 0 R 1838 0 R 1839 0 R 1839 0 R 1839 0 R 1840 0 R 1840 0 R 1840 0 R 1841 0 R 1841 0 R 1852 0 R 1852 0 R 1842 0 R 1852 0 R 1843 0 R 1852 0 R 1852 0 R 1852 0 R 1844 0 R 1852 0 R 1845 0 R 1852 0 R 1852 0 R 1846 0 R 1852 0 R 1852 0 R 1847 0 R 1852 0 R 1852 0 R 1848 0 R 1852 0 R 1852 0 R 1849 0 R 1852 0 R 1852 0 R 1850 0 R 1850 0 R 1852 0 R 1851 0 R 1852 0 R 1852 0 R 1856 0 R 1853 0 R 1856 0 R 1856 0 R 1856 0 R 1854 0 R 1856 0 R 1856 0 R 1855 0 R 1856 0 R 1856 0 R 1857 0 R 1857 0 R 1857 0 R 1857 0 R 1858 0 R 1858 0 R 1861 0 R 1861 0 R 1859 0 R 1861 0 R 1861 0 R 1861 0 R 1860 0 R 1861 0 R 1862 0 R 1862 0 R 1862 0 R 1862 0 R 1862 0 R 1862 0 R 1862 0 R 1862 0 R 1863 0 R 1863 0 R 1863 0 R 1863 0 R 1863 0 R 1864 0 R 1864 0 R 1867 0 R 1865 0 R 1867 0 R 1867 0 R 1867 0 R 1867 0 R 1866 0 R 1867 0 R 1867 0 R 1867 0 R 1870 0 R 1870 0 R 1870 0 R 1868 0 R 1870 0 R 1870 0 R 1869 0 R 1870 0 R 1873 0 R 1871 0 R 1873 0 R 1873 0 R 1873 0 R 1873 0 R 1873 0 R 1872 0 R 1873 0 R 1873 0 R 1874 0 R 1876 0 R 1876 0 R 1875 0 R 1876 0 R 1876 0 R 1876 0 R 1876 0 R 1876 0 R 1877 0 R 1883 0 R 1883 0 R 1883 0 R 1883 0 R 1883 0 R 1878 0 R 1883 0 R 1883 0 R 1879 0 R 1883 0 R 1883 0 R 1883 0 R 1883 0 R 1883 0 R 1880 0 R 1883 0 R 1881 0 R 1883 0 R 1883 0 R 1883 0 R 1883 0 R 1883 0 R 1883 0 R 1882 0 R 1883 0 R 1884 0 R 1896 0 R 1896 0 R 1896 0 R] endobj 73 0 obj -<> +[1896 0 R 1896 0 R 1885 0 R 1896 0 R 1896 0 R 1886 0 R 1896 0 R 1896 0 R 1887 0 R 1896 0 R 1896 0 R 1896 0 R 1896 0 R 1896 0 R 1888 0 R 1896 0 R 1896 0 R 1896 0 R 1896 0 R 1889 0 R 1896 0 R 1890 0 R 1896 0 R 1896 0 R 1896 0 R 1896 0 R 1891 0 R 1896 0 R 1896 0 R 1896 0 R 1896 0 R 1896 0 R 1893 0 R 1896 0 R 1896 0 R 1896 0 R 1895 0 R 1896 0 R 1896 0 R 1896 0 R 1896 0 R 1896 0 R 1896 0 R 1896 0 R 1896 0 R 1896 0 R 1897 0 R 1957 0 R 1898 0 R 1898 0 R 1957 0 R 1899 0 R 1957 0 R 1900 0 R 1957 0 R 1901 0 R 1957 0 R 1902 0 R 1957 0 R 1903 0 R 1957 0 R 1904 0 R 1904 0 R 1957 0 R 1905 0 R 1957 0 R 1957 0 R 1906 0 R 1957 0 R 1907 0 R 1957 0 R 1908 0 R 1957 0 R 1909 0 R 1957 0 R 1910 0 R 1957 0 R 1911 0 R 1957 0 R 1912 0 R 1957 0 R 1957 0 R 1913 0 R 1957 0 R 1914 0 R 1957 0 R 1915 0 R 1957 0 R 1916 0 R 1957 0 R 1957 0 R 1917 0 R 1957 0 R 1918 0 R 1957 0 R 1919 0 R 1957 0 R 1957 0 R 1920 0 R 1957 0 R 1921 0 R 1957 0 R 1922 0 R 1957 0 R 1923 0 R 1957 0 R 1924 0 R 1924 0 R 1957 0 R 1925 0 R 1957 0 R 1926 0 R 1957 0 R 1927 0 R 1957 0 R 1928 0 R 1957 0 R 1929 0 R 1957 0 R 1957 0 R 1931 0 R 1932 0 R 1933 0 R 1957 0 R 1957 0 R 1935 0 R 1957 0 R 1936 0 R 1936 0 R 1957 0 R 1937 0 R 1957 0 R 1938 0 R 1957 0 R 1957 0 R 1939 0 R 1957 0 R 1940 0 R 1957 0 R 1941 0 R 1957 0 R 1942 0 R 1957 0 R 1943 0 R 1957 0 R 1957 0 R 1944 0 R 1957 0 R 1945 0 R 1957 0 R 1946 0 R 1957 0 R 1947 0 R 1957 0 R 1957 0 R 1957 0 R 1957 0 R 1957 0 R 1948 0 R 1957 0 R 1949 0 R 1957 0 R 1957 0 R 1957 0 R 1957 0 R 1950 0 R 1950 0 R 1957 0 R 1957 0 R 1951 0 R 1957 0 R 1957 0 R 1952 0 R 1957 0 R 1957 0 R 1953 0 R 1957 0 R 1954 0 R 1957 0 R 1955 0 R 1957 0 R 1957 0 R 1957 0 R 1956 0 R 1957 0 R 1957 0 R 1958 0 R 1966 0 R 1966 0 R 1966 0 R 1959 0 R 1966 0 R 1966 0 R 1960 0 R 1966 0 R 1966 0 R 1961 0 R 1966 0 R 1966 0 R 1962 0 R 1966 0 R 1963 0 R 1963 0 R 1966 0 R 1966 0 R 1964 0 R 1964 0 R 1966 0 R 1965 0 R 1965 0 R 1966 0 R 1966 0 R 1967 0 R 1971 0 R 1971 0 R 1971 0 R 1971 0 R 1971 0 R 1971 0 R 1971 0 R 1968 0 R 1971 0 R 1971 0 R 1971 0 R 1971 0 R 1971 0 R 1971 0 R 1971 0 R 1971 0 R 1969 0 R 1971 0 R 1971 0 R 1971 0 R 1970 0 R 1971 0 R 1971 0 R 1971 0 R 1972 0 R 1985 0 R 1985 0 R 1985 0 R 1973 0 R 1985 0 R 1985 0 R 1974 0 R 1985 0 R 1985 0 R 1975 0 R 1985 0 R 1976 0 R 1985 0 R 1977 0 R 1985 0 R 1978 0 R 1985 0 R 1979 0 R 1985 0 R 1980 0 R 1985 0 R 1981 0 R 1985 0 R 1982 0 R 1985 0 R 1983 0 R 1985 0 R 1984 0 R 1985 0 R 1985 0 R 1985 0 R 1985 0 R 1986 0 R 2006 0 R 1987 0 R 2006 0 R 2006 0 R 2006 0 R 2006 0 R 1988 0 R 2006 0 R 2006 0 R 1989 0 R 2006 0 R 2006 0 R 1990 0 R 2006 0 R 1991 0 R 2006 0 R 1992 0 R 1992 0 R 2006 0 R 2006 0 R 2006 0 R 1993 0 R 2006 0 R 2006 0 R 1994 0 R 2006 0 R 2006 0 R 2006 0 R 2006 0 R 2006 0 R 1995 0 R 2006 0 R 1996 0 R 2006 0 R 2006 0 R 2006 0 R 2006 0 R 1997 0 R 2006 0 R 2006 0 R 2006 0 R 1998 0 R 2006 0 R 1999 0 R 2006 0 R 2000 0 R 2006 0 R 2006 0 R 2006 0 R 2006 0 R 2006 0 R 2006 0 R 2006 0 R 2006 0 R 2001 0 R 2001 0 R 2006 0 R 2002 0 R 2006 0 R 2006 0 R 2006 0 R 2003 0 R 2006 0 R 2006 0 R 2004 0 R 2006 0 R 2006 0 R 2006 0 R 2006 0 R 2006 0 R 2006 0 R 2006 0 R 2005 0 R 2006 0 R 2007 0 R 2017 0 R 2008 0 R 2017 0 R 2009 0 R 2009 0 R 2017 0 R 2010 0 R 2017 0 R 2011 0 R 2017 0 R 2017 0 R 2012 0 R 2017 0 R 2013 0 R 2013 0 R 2017 0 R 2017 0 R] endobj 74 0 obj -<> +[2017 0 R 2014 0 R 2017 0 R 2017 0 R 2015 0 R 2017 0 R 2017 0 R 2016 0 R 2016 0 R 2017 0 R 2017 0 R 2018 0 R 2023 0 R 2023 0 R 2023 0 R 2023 0 R 2023 0 R 2023 0 R 2023 0 R 2023 0 R 2019 0 R 2023 0 R 2023 0 R 2023 0 R 2020 0 R 2020 0 R 2023 0 R 2023 0 R 2023 0 R 2023 0 R 2023 0 R 2023 0 R 2023 0 R 2023 0 R 2023 0 R 2023 0 R 2023 0 R 2023 0 R 2023 0 R 2023 0 R 2021 0 R 2023 0 R 2023 0 R 2023 0 R 2022 0 R 2023 0 R 2024 0 R 2032 0 R 2025 0 R 2025 0 R 2032 0 R 2026 0 R 2032 0 R 2032 0 R 2032 0 R 2032 0 R 2027 0 R 2032 0 R 2028 0 R 2032 0 R 2029 0 R 2032 0 R 2032 0 R 2030 0 R 2032 0 R 2031 0 R 2032 0 R 2032 0 R 2032 0 R] endobj 75 0 obj -<> +<> endobj 76 0 obj -<> +<> endobj 77 0 obj -<> +<> endobj 78 0 obj -<> +<>]/K[2]/Pg 2154 0 R>> endobj 79 0 obj -<> +<>]/K[3 4 5 6 7]/Pg 2154 0 R>> endobj 80 0 obj -<> +<>]/K[8]/Pg 2154 0 R>> endobj 81 0 obj -<> +<>]/K[9]/Pg 2154 0 R>> endobj 82 0 obj -<> +<>]/K[10]/Pg 2154 0 R>> endobj 83 0 obj -<> +<>]/K[11]/Pg 2154 0 R>> endobj 84 0 obj -<> +<>]/K[12]/Pg 2154 0 R>> endobj 85 0 obj -<> +<>]/K[13 14]/Pg 2154 0 R>> endobj 86 0 obj -<> +<>]/K[15]/Pg 2154 0 R>> endobj 87 0 obj -<> +<>]/K[16 17 18]/Pg 2154 0 R>> endobj 88 0 obj -<> +<> endobj 89 0 obj -<> +<> endobj 90 0 obj -<> +<> endobj 91 0 obj -<> +<> endobj 92 0 obj -<> +<> endobj 93 0 obj -<> +<> endobj 94 0 obj -<> +<> endobj 95 0 obj -<> +<> endobj 96 0 obj -<> +<> endobj 97 0 obj -<> +<> endobj 98 0 obj -<> +<> endobj 99 0 obj -<> +<>]/Pg 2154 0 R>> endobj 100 0 obj -<> +<> endobj 101 0 obj -<> +<> endobj 102 0 obj -<> +<> endobj 103 0 obj -<>]/K[99 0 R 100 0 R 101 0 R 102 0 R]>> +<> endobj 104 0 obj -<> +<> endobj 105 0 obj -<> +<> endobj 106 0 obj -<> +<> endobj 107 0 obj -<> +<> endobj 108 0 obj -<> +<> endobj 109 0 obj -<> +<> endobj 110 0 obj -<> +<> endobj 111 0 obj -<> +<> endobj 112 0 obj -<> +<> endobj 113 0 obj -<> +<> endobj 114 0 obj -<> +<> endobj 115 0 obj -<> +<> endobj 116 0 obj -<> +<> endobj 117 0 obj -<> +<> endobj 118 0 obj -<> +<> endobj 119 0 obj -<> +<> endobj 120 0 obj -<> +<> endobj 121 0 obj -<> +<> endobj 122 0 obj -<> +<> endobj 123 0 obj -<> +<> endobj 124 0 obj -<> +<> endobj 125 0 obj -<> +<> endobj 126 0 obj -<> +<> endobj 127 0 obj -<> +<> endobj 128 0 obj -<> +<> endobj 129 0 obj -<> +<> endobj 130 0 obj -<> +<> endobj 131 0 obj -<> +<> endobj 132 0 obj -<> +<> endobj 133 0 obj -<> +<> endobj 134 0 obj -<> +<> endobj 135 0 obj -<> +<> endobj 136 0 obj -<> +<> endobj 137 0 obj -<> +<>]/Pg 2154 0 R>> endobj 138 0 obj -<> +<> endobj 139 0 obj -<> +<> endobj 140 0 obj -<> +<> endobj 141 0 obj -<> +<>]/K[52]/Pg 2155 0 R>> endobj 142 0 obj -<>]/K[111 0 R 112 0 R 113 0 R 114 0 R 115 0 R 116 0 R 117 0 R 118 0 R 119 0 R 120 0 R 121 0 R 122 0 R 123 0 R 124 0 R 125 0 R 126 0 R 127 0 R 128 0 R 129 0 R 130 0 R 131 0 R 132 0 R 133 0 R 134 0 R 135 0 R 136 0 R 137 0 R 138 0 R 139 0 R 140 0 R 141 0 R]>> +<>]/K[53]/Pg 2155 0 R>> endobj 143 0 obj -<> +<>]/K[54]/Pg 2155 0 R>> endobj 144 0 obj -<> +<>]/K[55]/Pg 2155 0 R>> endobj 145 0 obj -<> +<>]/K[56]/Pg 2155 0 R>> endobj 146 0 obj -<> +<>]/K[57]/Pg 2155 0 R>> endobj 147 0 obj -<> +<>]/K[58]/Pg 2155 0 R>> endobj 148 0 obj -<> +<>]/K[59]/Pg 2155 0 R>> endobj 149 0 obj -<> +<>]/K[60]/Pg 2155 0 R>> endobj 150 0 obj -<> +<>]/K[61]/Pg 2155 0 R>> endobj 151 0 obj -<> +<>]/K[62 63]/Pg 2155 0 R>> endobj 152 0 obj -<><>]/K[151 0 R]>> +<>]/K[64]/Pg 2155 0 R>> endobj 153 0 obj -<> +<>]/K[65]/Pg 2155 0 R>> endobj 154 0 obj -<><>]/K[153 0 R]>> +<> endobj 155 0 obj -<> +<>]/K[0]/Pg 2155 0 R>> endobj 156 0 obj -<><>]/K[155 0 R]>> +<>]/K[1]/Pg 2155 0 R>> endobj 157 0 obj -<><>]/K[]>> +<>]/K[2]/Pg 2155 0 R>> endobj 158 0 obj -<> +<>]/K[3]/Pg 2155 0 R>> endobj 159 0 obj -<><>]/K[126 127 128]/Pg 427 0 R>> +<>]/K[4]/Pg 2155 0 R>> endobj 160 0 obj -<><>]/K[129]/Pg 427 0 R>> +<>]/K[5]/Pg 2155 0 R>> endobj 161 0 obj -<><>]/K[130]/Pg 427 0 R>> +<>]/K[6]/Pg 2155 0 R>> endobj 162 0 obj -<><>]/K[131 132 133 134 135 136 137 138 139 140]/Pg 427 0 R>> +<>]/K[7]/Pg 2155 0 R>> endobj 163 0 obj -<> +<>]/K[8]/Pg 2155 0 R>> endobj 164 0 obj -<><>]/K[141]/Pg 427 0 R>> +<>]/K[9]/Pg 2155 0 R>> endobj 165 0 obj -<><>]/K[142]/Pg 427 0 R>> +<>]/K[10]/Pg 2155 0 R>> endobj 166 0 obj -<><>]/K[143]/Pg 427 0 R>> +<>]/K[11]/Pg 2155 0 R>> endobj 167 0 obj -<><>]/K[144 145 146 147]/Pg 427 0 R>> +<>]/K[12]/Pg 2155 0 R>> endobj 168 0 obj -<> +<>]/K[13]/Pg 2155 0 R>> endobj 169 0 obj -<><>]/K[148]/Pg 427 0 R>> +<>]/K[14]/Pg 2155 0 R>> endobj 170 0 obj -<><>]/K[149]/Pg 427 0 R>> +<>]/K[15]/Pg 2155 0 R>> endobj 171 0 obj -<><>]/K[150]/Pg 427 0 R>> +<>]/K[16 17]/Pg 2155 0 R>> endobj 172 0 obj -<> +<>]/K[18]/Pg 2155 0 R>> endobj 173 0 obj -<><>]/K[151 172 0 R]/Pg 427 0 R>> +<>]/K[19]/Pg 2155 0 R>> endobj 174 0 obj -<> +<>]/K[20]/Pg 2155 0 R>> endobj 175 0 obj -<><>]/K[154]/Pg 427 0 R>> +<>]/K[21 22]/Pg 2155 0 R>> endobj 176 0 obj -<><>]/K[155]/Pg 427 0 R>> +<>]/K[23]/Pg 2155 0 R>> endobj 177 0 obj -<><>]/K[156]/Pg 427 0 R>> +<>]/K[24 25]/Pg 2155 0 R>> endobj 178 0 obj -<> +<>]/K[26]/Pg 2155 0 R>> endobj 179 0 obj -<><>]/K[178 0 R 158 159]/Pg 427 0 R>> +<>]/K[27 28]/Pg 2155 0 R>> endobj 180 0 obj -<> +<>]/K[29]/Pg 2155 0 R>> endobj 181 0 obj -<><>]/K[160]/Pg 427 0 R>> +<>]/K[30]/Pg 2155 0 R>> endobj 182 0 obj -<><>]/K[161]/Pg 427 0 R>> +<>]/K[31]/Pg 2155 0 R>> endobj 183 0 obj -<><>]/K[162]/Pg 427 0 R>> +<>]/K[32]/Pg 2155 0 R>> endobj 184 0 obj -<><>]/K[163 164 165 166 167 168 169 170]/Pg 427 0 R>> +<>]/K[33]/Pg 2155 0 R>> endobj 185 0 obj -<> +<>]/K[34]/Pg 2155 0 R>> endobj 186 0 obj -<><>]/K[171]/Pg 427 0 R>> +<>]/K[35]/Pg 2155 0 R>> endobj 187 0 obj -<><>]/K[172]/Pg 427 0 R>> +<>]/K[36]/Pg 2155 0 R>> endobj 188 0 obj -<><>]/K[173]/Pg 427 0 R>> +<>]/K[37]/Pg 2155 0 R>> endobj 189 0 obj -<><>]/K[174 175 176 177 178 179 180 181 182]/Pg 427 0 R>> +<>]/K[38 39 40 41 42]/Pg 2155 0 R>> endobj 190 0 obj -<> +<>]/K[43]/Pg 2155 0 R>> endobj 191 0 obj -<> +<>]/K[44]/Pg 2155 0 R>> endobj 192 0 obj -<> +<>]/K[45]/Pg 2155 0 R>> endobj 193 0 obj -<> +<>]/K[46]/Pg 2155 0 R>> endobj 194 0 obj -<> +<>]/K[47]/Pg 2155 0 R>> endobj 195 0 obj -<><>]/K[191 0 R 1 192 0 R 3 193 0 R 5 194 0 R]/Pg 428 0 R>> +<>]/K[48]/Pg 2155 0 R>> endobj 196 0 obj -<> +<>]/K[49]/Pg 2155 0 R>> endobj 197 0 obj -<><>]/K[196 0 R]>> +<>]/K[50]/Pg 2155 0 R>> endobj 198 0 obj -<> +<>]/K[51]/Pg 2155 0 R>> endobj 199 0 obj -<><>]/K[198 0 R]>> +<>]/K[155 0 R 156 0 R 157 0 R 158 0 R 159 0 R 160 0 R 161 0 R 162 0 R 163 0 R 164 0 R 165 0 R 166 0 R 167 0 R 168 0 R 169 0 R 170 0 R 171 0 R 172 0 R 173 0 R 174 0 R 175 0 R 176 0 R 177 0 R 178 0 R 179 0 R 180 0 R 181 0 R 182 0 R 183 0 R 184 0 R 185 0 R 186 0 R 187 0 R 188 0 R 189 0 R 190 0 R 191 0 R 192 0 R 193 0 R 194 0 R 195 0 R 196 0 R 197 0 R 198 0 R]>> endobj 200 0 obj -<><>]/K[9 10 11 12]/Pg 428 0 R>> +<> endobj 201 0 obj -<> +<> endobj 202 0 obj -<>]/K[158 0 R 163 0 R 168 0 R 174 0 R 180 0 R 185 0 R 190 0 R 201 0 R]>> +<> endobj 203 0 obj -<> +<> endobj 204 0 obj -<> +<> endobj 205 0 obj -<> +<> endobj 206 0 obj -<> +<> endobj 207 0 obj -<> +<> endobj 208 0 obj -<> +<> endobj 209 0 obj -<> +<> endobj 210 0 obj -<> +<> endobj 211 0 obj -<> +<> endobj 212 0 obj -<> +<> endobj 213 0 obj -<> +<> endobj 214 0 obj -<> +<> endobj 215 0 obj -<> +<> endobj 216 0 obj -<> +<> endobj 217 0 obj -<> +<> endobj 218 0 obj -<> +<> endobj 219 0 obj -<> +<> endobj 220 0 obj -<> +<> endobj 221 0 obj -<> +<> endobj 222 0 obj -<> +<> endobj 223 0 obj -<> +<> endobj 224 0 obj -<> +<> endobj 225 0 obj -<> +<> endobj 226 0 obj -<> +<> endobj 227 0 obj -<> +<> endobj 228 0 obj -<> +<> endobj 229 0 obj -<> +<> endobj 230 0 obj -<> +<> endobj 231 0 obj -<> +<> endobj 232 0 obj -<> +<>230 0 R<><><>231 0 R<>]/Pg 2154 0 R>> endobj 233 0 obj -<> +<>]/Pg 2155 0 R>> endobj 234 0 obj -<> +<> endobj 235 0 obj -<> +<> endobj 236 0 obj -<> +<> endobj 237 0 obj -<> +<> endobj 238 0 obj -<> +<> endobj 239 0 obj -<> +<> endobj 240 0 obj -<> +<> endobj 241 0 obj -<> +<> endobj 242 0 obj -<> +<> endobj 243 0 obj -<> +<> endobj 244 0 obj -<> +<> endobj 245 0 obj -<> +<> endobj 246 0 obj -<> +<> endobj 247 0 obj -<> +<> endobj 248 0 obj -<> +<> endobj 249 0 obj -<> +<> endobj 250 0 obj -<>249 0 R<><>]/Pg 428 0 R>> +<> endobj 251 0 obj -<> +<> endobj 252 0 obj -<> +<> endobj 253 0 obj -<> +<> endobj 254 0 obj -<> +<> endobj 255 0 obj -<> +<> endobj 256 0 obj -<> +<> endobj 257 0 obj -<> +<> endobj 258 0 obj -<> +<> endobj 259 0 obj -<> +<> endobj 260 0 obj -<> +<> endobj 261 0 obj -<> +<> endobj 262 0 obj -<> +<> endobj 263 0 obj -<> +<> endobj 264 0 obj -<> +<> endobj 265 0 obj -<> +<> endobj 266 0 obj -<> +<> endobj 267 0 obj -<> +<> endobj 268 0 obj -<> +<> endobj 269 0 obj -<> +<> endobj 270 0 obj -<> +<> endobj 271 0 obj -<> +<> endobj 272 0 obj -<> +<> endobj 273 0 obj -<> +<> endobj 274 0 obj -<> +<> endobj 275 0 obj -<> +<> endobj 276 0 obj -<> +<> endobj 277 0 obj -<> +<> endobj 278 0 obj -<> +<> endobj 279 0 obj -<> +<> endobj 280 0 obj -<> +<> endobj 281 0 obj -<> +<> endobj 282 0 obj -<> +<> endobj 283 0 obj -<> +<> endobj 284 0 obj -<> +<> endobj 285 0 obj -<> +<> endobj 286 0 obj -<> +<>]/K[260 0 R 261 0 R 262 0 R 263 0 R 264 0 R 265 0 R 266 0 R 267 0 R 268 0 R 269 0 R 270 0 R 271 0 R 272 0 R 273 0 R 274 0 R 275 0 R 276 0 R 277 0 R 278 0 R 279 0 R 280 0 R 281 0 R 282 0 R 283 0 R 284 0 R 285 0 R]>> endobj 287 0 obj -<> +<> endobj 288 0 obj -<> +<> endobj 289 0 obj -<> +<> endobj 290 0 obj -<> +<> endobj 291 0 obj -<> +<> endobj 292 0 obj -<> +<> endobj 293 0 obj -<> +<> endobj 294 0 obj -<> +<> endobj 295 0 obj -<> +<> endobj 296 0 obj -<> +<> endobj 297 0 obj -<> +<> endobj 298 0 obj -<> +<> endobj 299 0 obj -<> +<> endobj 300 0 obj -<> +<> endobj 301 0 obj -<> +<> endobj 302 0 obj -<> +<> endobj 303 0 obj -<> +<> endobj 304 0 obj -<> +<> endobj 305 0 obj -<> +<> endobj 306 0 obj -<> +<> endobj 307 0 obj -<> +<> endobj 308 0 obj -<> +<> endobj 309 0 obj -<> +<> endobj 310 0 obj -<> +<> endobj 311 0 obj -<> +<> endobj 312 0 obj -<> +<> endobj 313 0 obj -<> +<> endobj 314 0 obj -<> +<> endobj 315 0 obj -<> +<>]/Pg 2155 0 R>> endobj 316 0 obj -<> +<> endobj 317 0 obj -<> +<> endobj 318 0 obj -<> +<> endobj 319 0 obj -<> +<> endobj 320 0 obj -<> +<> endobj 321 0 obj -<> +<> endobj 322 0 obj -<> +<> endobj 323 0 obj -<> +<> endobj 324 0 obj -<> +<> endobj 325 0 obj -<> +<> endobj 326 0 obj -<> +<> endobj 327 0 obj -<> +<> endobj 328 0 obj -<> +<> endobj 329 0 obj -<> +<> endobj 330 0 obj -<> +<> endobj 331 0 obj -<> +<> endobj 332 0 obj -<> +<> endobj 333 0 obj -<> +<> endobj 334 0 obj -<> +<> endobj 335 0 obj -<> +<> endobj 336 0 obj -<> +<> endobj 337 0 obj -<> +<> endobj 338 0 obj -<> +<> endobj 339 0 obj -<> +<> endobj 340 0 obj -<> +<> endobj 341 0 obj -<> +<> endobj 342 0 obj -<> +<> endobj 343 0 obj -<> +<> endobj 344 0 obj -<> +<>]/K[325 0 R 326 0 R 327 0 R 328 0 R 329 0 R 330 0 R 331 0 R 332 0 R 333 0 R 334 0 R 335 0 R 336 0 R 337 0 R 338 0 R 339 0 R 340 0 R 341 0 R 342 0 R 343 0 R]>> endobj 345 0 obj -<> +<> endobj 346 0 obj -<>309 0 R<>310 0 R<>311 0 R<>312 0 R<>313 0 R<>314 0 R<>315 0 R<>316 0 R<>317 0 R<>318 0 R<>319 0 R<>320 0 R 321 0 R 322 0 R<>323 0 R<>324 0 R<>325 0 R<>326 0 R<>327 0 R<>328 0 R<>329 0 R<>330 0 R<>331 0 R<>332 0 R<>333 0 R<>334 0 R<>335 0 R<>336 0 R<><><><>337 0 R<>338 0 R<><><>339 0 R<><>340 0 R<>341 0 R<><>342 0 R<>343 0 R<>344 0 R<><>345 0 R<>]/Pg 429 0 R>> +<> endobj 347 0 obj -<> +<> endobj 348 0 obj -<> +<> endobj 349 0 obj -<> +<> endobj 350 0 obj -<> +<> endobj 351 0 obj -<> +<> endobj 352 0 obj -<> +<> endobj 353 0 obj -<> +<> endobj 354 0 obj -<> +<> endobj 355 0 obj -<> +<> endobj 356 0 obj -<> +<> endobj 357 0 obj -<> +<> endobj 358 0 obj -<> +<> endobj 359 0 obj -<> +<> endobj 360 0 obj -<> +<> endobj 361 0 obj -<> +<> endobj 362 0 obj -<> +<> endobj 363 0 obj -<> +<> endobj 364 0 obj -<> +<> endobj 365 0 obj -<> +<> endobj 366 0 obj -<> +<> endobj 367 0 obj -<> +<> endobj 368 0 obj -<> +<> endobj 369 0 obj -<> +<> endobj 370 0 obj -<> +<> endobj 371 0 obj -<> +<> endobj 372 0 obj -<> +<> endobj 373 0 obj -<> +<> endobj 374 0 obj -<> +<> endobj 375 0 obj -<> +<> endobj 376 0 obj -<> +<> endobj 377 0 obj -<> +<> endobj 378 0 obj -<> +<> endobj 379 0 obj -<> +<> endobj 380 0 obj -<> +<> endobj 381 0 obj -<> +<>]/K[364 0 R 365 0 R 366 0 R 367 0 R 368 0 R 369 0 R 370 0 R 371 0 R 372 0 R 373 0 R 374 0 R 375 0 R 376 0 R 377 0 R 378 0 R 379 0 R 380 0 R]>> endobj 382 0 obj -<> +<> endobj 383 0 obj -<> +<> endobj 384 0 obj -<> +<> endobj 385 0 obj -<> +<> endobj 386 0 obj -<> +<> endobj 387 0 obj -<> +<> endobj 388 0 obj -<> +<> endobj 389 0 obj -<> +<> endobj 390 0 obj -<> +<> endobj 391 0 obj -<> +<> endobj 392 0 obj -<> +<> endobj 393 0 obj -<> +<> endobj 394 0 obj -<><>390 0 R<><>391 0 R<><><><><><><><><>392 0 R<><>393 0 R<><>]/Pg 430 0 R>> +<> endobj 395 0 obj -<> +<> endobj 396 0 obj -[/ICCBased 451 0 R] +<> endobj 397 0 obj -[/ICCBased 452 0 R] +<> endobj 398 0 obj -[425 0 R/XYZ 62.362206 729.37256 0] +<> endobj 399 0 obj -[425 0 R/XYZ 62.362206 562.0299 0] +<> endobj 400 0 obj -[426 0 R/XYZ 62.362206 423.88345 0] +<> endobj 401 0 obj -[427 0 R/XYZ 62.362206 629.7523 0] +<> endobj 402 0 obj -[428 0 R/XYZ 62.362206 558.2716 0] +<> endobj 403 0 obj -[428 0 R/XYZ 62.362206 231.16089 0] +<> endobj 404 0 obj -[429 0 R/XYZ 62.362206 747.77954 0] +<> endobj 405 0 obj -[429 0 R/XYZ 62.362206 538.4159 0] +<> endobj 406 0 obj -<>/Font<>>> +<>]/Pg 2156 0 R>> endobj 407 0 obj -<>/Font<>>> +<> endobj 408 0 obj -<>/Font<>>> +<> endobj 409 0 obj -<>/Font<>>> +<> endobj 410 0 obj -<>/Font<>>> +<> endobj 411 0 obj -<>/Font<>>> +<> endobj 412 0 obj -<>/Font<>>> +<> endobj 413 0 obj -<> +<> endobj 414 0 obj -<>/FontDescriptor 415 0 R/DW 0/W[0 0 280 1 1 786 2 2 559 3 3 474 4 4 639 5 5 527 6 6 454 7 7 319 8 8 383 9 9 869 10 10 800 11 11 607 12 12 447 13 13 756 14 14 639 15 15 607 16 16 319 17 17 575 18 18 958 19 19 1189 20 21 639 22 22 319 23 23 639 24 24 607 25 25 510.99997 26 26 831 27 28 639 29 29 351 30 30 671 31 31 607 32 32 882 33 33 575 34 34 319 35 41 575 42 42 818 43 43 692 44 44 319 45 45 1092 46 46 831 47 48 447]>> +<> endobj 415 0 obj -<> +<> endobj 416 0 obj -<> +<>]/K[127]/Pg 2157 0 R>> endobj 417 0 obj -<>/FontDescriptor 418 0 R/DW 0/W[0 0 500 1 1 556 2 2 444 3 3 394 4 4 278 5 5 500 6 6 556 7 7 333 8 8 306 9 9 500 10 10 392 11 11 389 12 12 556 13 13 722 14 14 1000 15 17 500 18 18 333 19 20 500 21 21 681 22 22 500 23 24 528 25 25 833 26 26 444 27 28 278 29 30 556 31 31 278 32 32 556 33 33 528 34 34 278 35 35 556 36 36 764 37 37 653 38 38 278 39 39 722 40 40 686 41 41 528 42 42 681 43 43 528 44 44 361 45 46 389 47 47 556 48 48 625 49 49 500 50 50 514 51 51 750 52 52 917 53 53 500 54 54 722 55 55 778 56 56 750 57 57 778 58 58 444 59 60 750 61 61 583 62 63 472 64 64 785 65 65 736 66 66 278 67 67 500 68 68 750 69 69 556 70 70 306 71 71 708 72 72 500 73 73 750 74 74 670 75 76 500 77 77 778 78 78 611 79 79 1028 80 80 750 81 81 1000 82 83 778 84 84 833 85 85 750]>> +<>]/K[128]/Pg 2157 0 R>> endobj 418 0 obj -<> +<>]/K[129]/Pg 2157 0 R>> endobj 419 0 obj -<> +<>]/K[130]/Pg 2157 0 R>> endobj 420 0 obj -<>/FontDescriptor 421 0 R/DW 0/CIDToGIDMap/Identity/W[0 92 602.0508]>> +<>]/K[131 132]/Pg 2157 0 R>> endobj 421 0 obj -<> +<>]/K[133]/Pg 2157 0 R>> endobj 422 0 obj -<> +<>]/K[134]/Pg 2157 0 R>> endobj 423 0 obj -<>/FontDescriptor 424 0 R/DW 0/W[0 0 500 1 1 527]>> +<>]/K[135]/Pg 2157 0 R>> endobj 424 0 obj -<> +<>]/K[136]/Pg 2157 0 R>> endobj 425 0 obj -<> +<> endobj 426 0 obj -<> +<> endobj 427 0 obj -<> +<> endobj 428 0 obj -<> +<> endobj 429 0 obj -<> +<> endobj 430 0 obj -<> +<> endobj 431 0 obj -<> +<> endobj 432 0 obj -<> +<> +endobj +433 0 obj +<> +endobj +434 0 obj +<> +endobj +435 0 obj +<> +endobj +436 0 obj +<> +endobj +437 0 obj +<> +endobj +438 0 obj +<> +endobj +439 0 obj +<> +endobj +440 0 obj +<>]/K[426 0 R 427 0 R 428 0 R 429 0 R 430 0 R 431 0 R 432 0 R 433 0 R 434 0 R 435 0 R 436 0 R 437 0 R 438 0 R 439 0 R]>> +endobj +441 0 obj +<> +endobj +442 0 obj +<> +endobj +443 0 obj +<> +endobj +444 0 obj +<> +endobj +445 0 obj +<> +endobj +446 0 obj +<> +endobj +447 0 obj +<> +endobj +448 0 obj +<> +endobj +449 0 obj +<> +endobj +450 0 obj +<> +endobj +451 0 obj +<> +endobj +452 0 obj +<> +endobj +453 0 obj +<> +endobj +454 0 obj +<> +endobj +455 0 obj +<> +endobj +456 0 obj +<><>]/Pg 2156 0 R>> +endobj +457 0 obj +<> +endobj +458 0 obj +<> +endobj +459 0 obj +<> +endobj +460 0 obj +<> +endobj +461 0 obj +<> +endobj +462 0 obj +<> +endobj +463 0 obj +<> +endobj +464 0 obj +<> +endobj +465 0 obj +<> +endobj +466 0 obj +<> +endobj +467 0 obj +<> +endobj +468 0 obj +<> +endobj +469 0 obj +<> +endobj +470 0 obj +<> +endobj +471 0 obj +<> +endobj +472 0 obj +<> +endobj +473 0 obj +<> +endobj +474 0 obj +<> +endobj +475 0 obj +<> +endobj +476 0 obj +<> +endobj +477 0 obj +<>]/K[458 0 R 459 0 R 460 0 R 461 0 R 462 0 R 463 0 R 464 0 R 465 0 R 466 0 R 467 0 R 468 0 R 469 0 R 470 0 R 471 0 R 472 0 R 473 0 R 474 0 R 475 0 R 476 0 R]>> +endobj +478 0 obj +<> +endobj +479 0 obj +<> +endobj +480 0 obj +<> +endobj +481 0 obj +<> +endobj +482 0 obj +<> +endobj +483 0 obj +<> +endobj +484 0 obj +<> +endobj +485 0 obj +<> +endobj +486 0 obj +<> +endobj +487 0 obj +<> +endobj +488 0 obj +<> +endobj +489 0 obj +<> +endobj +490 0 obj +<> +endobj +491 0 obj +<> +endobj +492 0 obj +<> +endobj +493 0 obj +<> +endobj +494 0 obj +<> +endobj +495 0 obj +<> +endobj +496 0 obj +<> +endobj +497 0 obj +<> +endobj +498 0 obj +<> +endobj +499 0 obj +<> +endobj +500 0 obj +<> +endobj +501 0 obj +<> +endobj +502 0 obj +<> +endobj +503 0 obj +<>]/K[493 0 R 494 0 R 495 0 R 496 0 R 497 0 R 498 0 R 499 0 R 500 0 R 501 0 R 502 0 R]>> +endobj +504 0 obj +<> +endobj +505 0 obj +<> +endobj +506 0 obj +<> +endobj +507 0 obj +<> +endobj +508 0 obj +<> +endobj +509 0 obj +<> +endobj +510 0 obj +<> +endobj +511 0 obj +<> +endobj +512 0 obj +<> +endobj +513 0 obj +<> +endobj +514 0 obj +<> +endobj +515 0 obj +<> +endobj +516 0 obj +<> +endobj +517 0 obj +<> +endobj +518 0 obj +<> +endobj +519 0 obj +<> +endobj +520 0 obj +<> +endobj +521 0 obj +<> +endobj +522 0 obj +<> +endobj +523 0 obj +<> +endobj +524 0 obj +<> +endobj +525 0 obj +<> +endobj +526 0 obj +<> +endobj +527 0 obj +<> +endobj +528 0 obj +<> +endobj +529 0 obj +<> +endobj +530 0 obj +<> +endobj +531 0 obj +<> +endobj +532 0 obj +<> +endobj +533 0 obj +<> +endobj +534 0 obj +<> +endobj +535 0 obj +<> +endobj +536 0 obj +<> +endobj +537 0 obj +<> +endobj +538 0 obj +<> +endobj +539 0 obj +<> +endobj +540 0 obj +<> +endobj +541 0 obj +<> +endobj +542 0 obj +<> +endobj +543 0 obj +<> +endobj +544 0 obj +<> +endobj +545 0 obj +<> +endobj +546 0 obj +<> +endobj +547 0 obj +<> +endobj +548 0 obj +<> +endobj +549 0 obj +<> +endobj +550 0 obj +<> +endobj +551 0 obj +<> +endobj +552 0 obj +<> +endobj +553 0 obj +<> +endobj +554 0 obj +<> +endobj +555 0 obj +<> +endobj +556 0 obj +<> +endobj +557 0 obj +<> +endobj +558 0 obj +<> +endobj +559 0 obj +<> +endobj +560 0 obj +<> +endobj +561 0 obj +<> +endobj +562 0 obj +<> +endobj +563 0 obj +<> +endobj +564 0 obj +<> +endobj +565 0 obj +<> +endobj +566 0 obj +<> +endobj +567 0 obj +<> +endobj +568 0 obj +<> +endobj +569 0 obj +<> +endobj +570 0 obj +<> +endobj +571 0 obj +<> +endobj +572 0 obj +<>]/K[520 0 R 521 0 R 522 0 R 523 0 R 524 0 R 525 0 R 526 0 R 527 0 R 528 0 R 529 0 R 530 0 R 531 0 R 532 0 R 533 0 R 534 0 R 535 0 R 536 0 R 537 0 R 538 0 R 539 0 R 540 0 R 541 0 R 542 0 R 543 0 R 544 0 R 545 0 R 546 0 R 547 0 R 548 0 R 549 0 R 550 0 R 551 0 R 552 0 R 553 0 R 554 0 R 555 0 R 556 0 R 557 0 R 558 0 R 559 0 R 560 0 R 561 0 R 562 0 R 563 0 R 564 0 R 565 0 R 566 0 R 567 0 R 568 0 R 569 0 R 570 0 R 571 0 R]>> +endobj +573 0 obj +<> +endobj +574 0 obj +<> +endobj +575 0 obj +<> +endobj +576 0 obj +<> +endobj +577 0 obj +<> +endobj +578 0 obj +<> +endobj +579 0 obj +<> +endobj +580 0 obj +<> +endobj +581 0 obj +<> +endobj +582 0 obj +<> +endobj +583 0 obj +<> +endobj +584 0 obj +<> +endobj +585 0 obj +<> +endobj +586 0 obj +<> +endobj +587 0 obj +<> +endobj +588 0 obj +<> +endobj +589 0 obj +<> +endobj +590 0 obj +<> +endobj +591 0 obj +<> +endobj +592 0 obj +<> +endobj +593 0 obj +<> +endobj +594 0 obj +<> +endobj +595 0 obj +<> +endobj +596 0 obj +<> +endobj +597 0 obj +<> +endobj +598 0 obj +<> +endobj +599 0 obj +<> +endobj +600 0 obj +<> +endobj +601 0 obj +<> +endobj +602 0 obj +<> +endobj +603 0 obj +<> +endobj +604 0 obj +<> +endobj +605 0 obj +<> +endobj +606 0 obj +<> +endobj +607 0 obj +<> +endobj +608 0 obj +<> +endobj +609 0 obj +<>]/K[579 0 R 580 0 R 581 0 R 582 0 R 583 0 R 584 0 R 585 0 R 586 0 R 587 0 R 588 0 R 589 0 R 590 0 R 591 0 R 592 0 R 593 0 R 594 0 R 595 0 R 596 0 R 597 0 R 598 0 R 599 0 R 600 0 R 601 0 R 602 0 R 603 0 R 604 0 R 605 0 R 606 0 R 607 0 R 608 0 R]>> +endobj +610 0 obj +<> +endobj +611 0 obj +<> +endobj +612 0 obj +<> +endobj +613 0 obj +<> +endobj +614 0 obj +<> +endobj +615 0 obj +<> +endobj +616 0 obj +<> +endobj +617 0 obj +<> +endobj +618 0 obj +<> +endobj +619 0 obj +<> +endobj +620 0 obj +<> +endobj +621 0 obj +<>]/Pg 2158 0 R>> +endobj +622 0 obj +<> +endobj +623 0 obj +<> +endobj +624 0 obj +<> +endobj +625 0 obj +<>]/K[507 508]/Pg 2158 0 R>> +endobj +626 0 obj +<>]/K[509]/Pg 2158 0 R>> +endobj +627 0 obj +<>]/K[510 511 512]/Pg 2158 0 R>> +endobj +628 0 obj +<>]/K[513]/Pg 2158 0 R>> +endobj +629 0 obj +<>]/K[514]/Pg 2158 0 R>> +endobj +630 0 obj +<>]/K[515]/Pg 2158 0 R>> +endobj +631 0 obj +<>]/K[516]/Pg 2158 0 R>> +endobj +632 0 obj +<>]/K[517]/Pg 2158 0 R>> +endobj +633 0 obj +<>]/K[518]/Pg 2158 0 R>> +endobj +634 0 obj +<>]/K[519]/Pg 2158 0 R>> +endobj +635 0 obj +<>]/K[520]/Pg 2158 0 R>> +endobj +636 0 obj +<>]/K[521]/Pg 2158 0 R>> +endobj +637 0 obj +<>]/K[522]/Pg 2158 0 R>> +endobj +638 0 obj +<>]/K[523]/Pg 2158 0 R>> +endobj +639 0 obj +<>]/K[524]/Pg 2158 0 R>> +endobj +640 0 obj +<> +endobj +641 0 obj +<>]/K[495 496 497 498 499 500 501]/Pg 2158 0 R>> +endobj +642 0 obj +<>]/K[502]/Pg 2158 0 R>> +endobj +643 0 obj +<>]/K[503]/Pg 2158 0 R>> +endobj +644 0 obj +<>]/K[504]/Pg 2158 0 R>> +endobj +645 0 obj +<>]/K[505]/Pg 2158 0 R>> +endobj +646 0 obj +<>]/K[506]/Pg 2158 0 R>> +endobj +647 0 obj +<>]/K[641 0 R 642 0 R 643 0 R 644 0 R 645 0 R 646 0 R]>> +endobj +648 0 obj +<> +endobj +649 0 obj +<> +endobj +650 0 obj +<> +endobj +651 0 obj +<> +endobj +652 0 obj +<> +endobj +653 0 obj +<> +endobj +654 0 obj +<> +endobj +655 0 obj +<> +endobj +656 0 obj +<> +endobj +657 0 obj +<> +endobj +658 0 obj +<> +endobj +659 0 obj +<> +endobj +660 0 obj +<> +endobj +661 0 obj +<> +endobj +662 0 obj +<> +endobj +663 0 obj +<> +endobj +664 0 obj +<> +endobj +665 0 obj +<> +endobj +666 0 obj +<> +endobj +667 0 obj +<> +endobj +668 0 obj +<> +endobj +669 0 obj +<> +endobj +670 0 obj +<> +endobj +671 0 obj +<> +endobj +672 0 obj +<> +endobj +673 0 obj +<> +endobj +674 0 obj +<> +endobj +675 0 obj +<> +endobj +676 0 obj +<> +endobj +677 0 obj +<> +endobj +678 0 obj +<> +endobj +679 0 obj +<> +endobj +680 0 obj +<> +endobj +681 0 obj +<> +endobj +682 0 obj +<> +endobj +683 0 obj +<> +endobj +684 0 obj +<> +endobj +685 0 obj +<> +endobj +686 0 obj +<> +endobj +687 0 obj +<> +endobj +688 0 obj +<> +endobj +689 0 obj +<> +endobj +690 0 obj +<> +endobj +691 0 obj +<> +endobj +692 0 obj +<> +endobj +693 0 obj +<> +endobj +694 0 obj +<> +endobj +695 0 obj +<> +endobj +696 0 obj +<> +endobj +697 0 obj +<> +endobj +698 0 obj +<> +endobj +699 0 obj +<> +endobj +700 0 obj +<> +endobj +701 0 obj +<> +endobj +702 0 obj +<> +endobj +703 0 obj +<> +endobj +704 0 obj +<> +endobj +705 0 obj +<> +endobj +706 0 obj +<> +endobj +707 0 obj +<> +endobj +708 0 obj +<> +endobj +709 0 obj +<> +endobj +710 0 obj +<> +endobj +711 0 obj +<> +endobj +712 0 obj +<> +endobj +713 0 obj +<> +endobj +714 0 obj +<> +endobj +715 0 obj +<> +endobj +716 0 obj +<> +endobj +717 0 obj +<> +endobj +718 0 obj +<> +endobj +719 0 obj +<> +endobj +720 0 obj +<> +endobj +721 0 obj +<> +endobj +722 0 obj +<> +endobj +723 0 obj +<> +endobj +724 0 obj +<> +endobj +725 0 obj +<> +endobj +726 0 obj +<> +endobj +727 0 obj +<> +endobj +728 0 obj +<> +endobj +729 0 obj +<> +endobj +730 0 obj +<> +endobj +731 0 obj +<> +endobj +732 0 obj +<> +endobj +733 0 obj +<> +endobj +734 0 obj +<> +endobj +735 0 obj +<> +endobj +736 0 obj +<> +endobj +737 0 obj +<> +endobj +738 0 obj +<> +endobj +739 0 obj +<> +endobj +740 0 obj +<> +endobj +741 0 obj +<> +endobj +742 0 obj +<> +endobj +743 0 obj +<> +endobj +744 0 obj +<> +endobj +745 0 obj +<> +endobj +746 0 obj +<> +endobj +747 0 obj +<> +endobj +748 0 obj +<> +endobj +749 0 obj +<> +endobj +750 0 obj +<> +endobj +751 0 obj +<> +endobj +752 0 obj +<> +endobj +753 0 obj +<> +endobj +754 0 obj +<> +endobj +755 0 obj +<><>]/K[754 0 R]>> +endobj +756 0 obj +<> +endobj +757 0 obj +<><>]/K[756 0 R]>> +endobj +758 0 obj +<> +endobj +759 0 obj +<><>]/K[758 0 R]>> +endobj +760 0 obj +<> +endobj +761 0 obj +<><>]/K[760 0 R]>> +endobj +762 0 obj +<> +endobj +763 0 obj +<> +endobj +764 0 obj +<><>]/K[204]/Pg 2159 0 R>> +endobj +765 0 obj +<> +endobj +766 0 obj +<><>]/K[765 0 R]>> +endobj +767 0 obj +<><>]/K[206]/Pg 2159 0 R>> +endobj +768 0 obj +<><>]/K[207]/Pg 2159 0 R>> +endobj +769 0 obj +<> +endobj +770 0 obj +<><>]/K[208]/Pg 2159 0 R>> +endobj +771 0 obj +<> +endobj +772 0 obj +<><>]/K[771 0 R]>> +endobj +773 0 obj +<><>]/K[210]/Pg 2159 0 R>> +endobj +774 0 obj +<><>]/K[211]/Pg 2159 0 R>> +endobj +775 0 obj +<> +endobj +776 0 obj +<><>]/K[212]/Pg 2159 0 R>> +endobj +777 0 obj +<> +endobj +778 0 obj +<><>]/K[777 0 R]>> +endobj +779 0 obj +<><>]/K[214]/Pg 2159 0 R>> +endobj +780 0 obj +<><>]/K[215]/Pg 2159 0 R>> +endobj +781 0 obj +<> +endobj +782 0 obj +<><>]/K[216]/Pg 2159 0 R>> +endobj +783 0 obj +<> +endobj +784 0 obj +<><>]/K[783 0 R]>> +endobj +785 0 obj +<><>]/K[218]/Pg 2159 0 R>> +endobj +786 0 obj +<><>]/K[219]/Pg 2159 0 R>> +endobj +787 0 obj +<> +endobj +788 0 obj +<><>]/K[220]/Pg 2159 0 R>> +endobj +789 0 obj +<> +endobj +790 0 obj +<><>]/K[789 0 R]>> +endobj +791 0 obj +<><>]/K[222]/Pg 2159 0 R>> +endobj +792 0 obj +<><>]/K[223]/Pg 2159 0 R>> +endobj +793 0 obj +<> +endobj +794 0 obj +<><>]/K[224]/Pg 2159 0 R>> +endobj +795 0 obj +<> +endobj +796 0 obj +<><>]/K[795 0 R]>> +endobj +797 0 obj +<><>]/K[226]/Pg 2159 0 R>> +endobj +798 0 obj +<><>]/K[227]/Pg 2159 0 R>> +endobj +799 0 obj +<> +endobj +800 0 obj +<><>]/K[228]/Pg 2159 0 R>> +endobj +801 0 obj +<> +endobj +802 0 obj +<><>]/K[801 0 R]>> +endobj +803 0 obj +<><>]/K[230]/Pg 2159 0 R>> +endobj +804 0 obj +<><>]/K[231]/Pg 2159 0 R>> +endobj +805 0 obj +<> +endobj +806 0 obj +<><>]/K[232]/Pg 2159 0 R>> +endobj +807 0 obj +<> +endobj +808 0 obj +<><>]/K[807 0 R]>> +endobj +809 0 obj +<><>]/K[234]/Pg 2159 0 R>> +endobj +810 0 obj +<><>]/K[235]/Pg 2159 0 R>> +endobj +811 0 obj +<> +endobj +812 0 obj +<><>]/K[236]/Pg 2159 0 R>> +endobj +813 0 obj +<> +endobj +814 0 obj +<><>]/K[813 0 R]>> +endobj +815 0 obj +<><>]/K[238]/Pg 2159 0 R>> +endobj +816 0 obj +<><>]/K[239]/Pg 2159 0 R>> +endobj +817 0 obj +<> +endobj +818 0 obj +<><>]/K[240]/Pg 2159 0 R>> +endobj +819 0 obj +<> +endobj +820 0 obj +<><>]/K[819 0 R]>> +endobj +821 0 obj +<><>]/K[242]/Pg 2159 0 R>> +endobj +822 0 obj +<><>]/K[243]/Pg 2159 0 R>> +endobj +823 0 obj +<> +endobj +824 0 obj +<><>]/K[244]/Pg 2159 0 R>> +endobj +825 0 obj +<> +endobj +826 0 obj +<><>]/K[825 0 R]>> +endobj +827 0 obj +<><>]/K[246]/Pg 2159 0 R>> +endobj +828 0 obj +<><>]/K[247]/Pg 2159 0 R>> +endobj +829 0 obj +<> +endobj +830 0 obj +<> +endobj +831 0 obj +<>]/K[763 0 R 830 0 R]>> +endobj +832 0 obj +<> +endobj +833 0 obj +<> +endobj +834 0 obj +<> +endobj +835 0 obj +<> +endobj +836 0 obj +<> +endobj +837 0 obj +<> +endobj +838 0 obj +<> +endobj +839 0 obj +<> +endobj +840 0 obj +<> +endobj +841 0 obj +<> +endobj +842 0 obj +<> +endobj +843 0 obj +<> +endobj +844 0 obj +<> +endobj +845 0 obj +<> +endobj +846 0 obj +<> +endobj +847 0 obj +<> +endobj +848 0 obj +<> +endobj +849 0 obj +<> +endobj +850 0 obj +<> +endobj +851 0 obj +<> +endobj +852 0 obj +<> +endobj +853 0 obj +<> +endobj +854 0 obj +<> +endobj +855 0 obj +<> +endobj +856 0 obj +<> +endobj +857 0 obj +<> +endobj +858 0 obj +<> +endobj +859 0 obj +<> +endobj +860 0 obj +<> +endobj +861 0 obj +<> +endobj +862 0 obj +<> +endobj +863 0 obj +<> +endobj +864 0 obj +<> +endobj +865 0 obj +<> +endobj +866 0 obj +<> +endobj +867 0 obj +<>]/K[850 0 R 851 0 R 852 0 R 853 0 R 854 0 R 855 0 R 856 0 R 857 0 R 858 0 R 859 0 R 860 0 R 861 0 R 862 0 R 863 0 R 864 0 R 865 0 R 866 0 R]>> +endobj +868 0 obj +<> +endobj +869 0 obj +<> +endobj +870 0 obj +<> +endobj +871 0 obj +<> +endobj +872 0 obj +<> +endobj +873 0 obj +<> +endobj +874 0 obj +<> +endobj +875 0 obj +<> +endobj +876 0 obj +<> +endobj +877 0 obj +<> +endobj +878 0 obj +<> +endobj +879 0 obj +<> +endobj +880 0 obj +<> +endobj +881 0 obj +<> +endobj +882 0 obj +<> +endobj +883 0 obj +<> +endobj +884 0 obj +<> +endobj +885 0 obj +<> +endobj +886 0 obj +<> +endobj +887 0 obj +<> +endobj +888 0 obj +<> +endobj +889 0 obj +<> +endobj +890 0 obj +<> +endobj +891 0 obj +<> +endobj +892 0 obj +<> +endobj +893 0 obj +<> +endobj +894 0 obj +<> +endobj +895 0 obj +<> +endobj +896 0 obj +<> +endobj +897 0 obj +<> +endobj +898 0 obj +<> +endobj +899 0 obj +<> +endobj +900 0 obj +<> +endobj +901 0 obj +<> +endobj +902 0 obj +<> +endobj +903 0 obj +<> +endobj +904 0 obj +<> +endobj +905 0 obj +<> +endobj +906 0 obj +<> +endobj +907 0 obj +<> +endobj +908 0 obj +<> +endobj +909 0 obj +<> +endobj +910 0 obj +<> +endobj +911 0 obj +<> +endobj +912 0 obj +<> +endobj +913 0 obj +<>]/K[896 0 R 897 0 R 898 0 R 899 0 R 900 0 R 901 0 R 902 0 R 903 0 R 904 0 R 905 0 R 906 0 R 907 0 R 908 0 R 909 0 R 910 0 R 911 0 R 912 0 R]>> +endobj +914 0 obj +<> +endobj +915 0 obj +<> +endobj +916 0 obj +<> +endobj +917 0 obj +<> +endobj +918 0 obj +<> +endobj +919 0 obj +<> +endobj +920 0 obj +<> +endobj +921 0 obj +<> +endobj +922 0 obj +<> +endobj +923 0 obj +<> +endobj +924 0 obj +<> +endobj +925 0 obj +<> +endobj +926 0 obj +<> +endobj +927 0 obj +<> +endobj +928 0 obj +<> +endobj +929 0 obj +<> +endobj +930 0 obj +<> +endobj +931 0 obj +<> +endobj +932 0 obj +<> +endobj +933 0 obj +<> +endobj +934 0 obj +<> +endobj +935 0 obj +<> +endobj +936 0 obj +<> +endobj +937 0 obj +<> +endobj +938 0 obj +<> +endobj +939 0 obj +<> +endobj +940 0 obj +<> +endobj +941 0 obj +<> +endobj +942 0 obj +<> +endobj +943 0 obj +<>]/K[135 136]/Pg 2161 0 R>> +endobj +944 0 obj +<>]/K[137]/Pg 2161 0 R>> +endobj +945 0 obj +<>]/K[138]/Pg 2161 0 R>> +endobj +946 0 obj +<>]/K[139]/Pg 2161 0 R>> +endobj +947 0 obj +<>]/K[140]/Pg 2161 0 R>> +endobj +948 0 obj +<>]/K[141]/Pg 2161 0 R>> +endobj +949 0 obj +<>]/K[142]/Pg 2161 0 R>> +endobj +950 0 obj +<> +endobj +951 0 obj +<>]/K[0]/Pg 2161 0 R>> +endobj +952 0 obj +<>]/K[1]/Pg 2161 0 R>> +endobj +953 0 obj +<>]/K[2]/Pg 2161 0 R>> +endobj +954 0 obj +<>]/K[3]/Pg 2161 0 R>> +endobj +955 0 obj +<>]/K[4]/Pg 2161 0 R>> +endobj +956 0 obj +<>]/K[5]/Pg 2161 0 R>> +endobj +957 0 obj +<>]/K[6]/Pg 2161 0 R>> +endobj +958 0 obj +<>]/K[7 8]/Pg 2161 0 R>> +endobj +959 0 obj +<>]/K[9]/Pg 2161 0 R>> +endobj +960 0 obj +<>]/K[10 11]/Pg 2161 0 R>> +endobj +961 0 obj +<>]/K[12]/Pg 2161 0 R>> +endobj +962 0 obj +<>]/K[13 14]/Pg 2161 0 R>> +endobj +963 0 obj +<>]/K[15]/Pg 2161 0 R>> +endobj +964 0 obj +<>]/K[16 17 18]/Pg 2161 0 R>> +endobj +965 0 obj +<>]/K[19]/Pg 2161 0 R>> +endobj +966 0 obj +<>]/K[20 21]/Pg 2161 0 R>> +endobj +967 0 obj +<>]/K[22]/Pg 2161 0 R>> +endobj +968 0 obj +<>]/K[23 24]/Pg 2161 0 R>> +endobj +969 0 obj +<>]/K[25]/Pg 2161 0 R>> +endobj +970 0 obj +<>]/K[26]/Pg 2161 0 R>> +endobj +971 0 obj +<>]/K[27]/Pg 2161 0 R>> +endobj +972 0 obj +<>]/K[28]/Pg 2161 0 R>> +endobj +973 0 obj +<>]/K[29 30 31]/Pg 2161 0 R>> +endobj +974 0 obj +<>]/K[32]/Pg 2161 0 R>> +endobj +975 0 obj +<>]/K[33]/Pg 2161 0 R>> +endobj +976 0 obj +<>]/K[34]/Pg 2161 0 R>> +endobj +977 0 obj +<>]/K[35]/Pg 2161 0 R>> +endobj +978 0 obj +<>]/K[36]/Pg 2161 0 R>> +endobj +979 0 obj +<>]/K[37]/Pg 2161 0 R>> +endobj +980 0 obj +<>]/K[38]/Pg 2161 0 R>> +endobj +981 0 obj +<>]/K[39]/Pg 2161 0 R>> +endobj +982 0 obj +<>]/K[40]/Pg 2161 0 R>> +endobj +983 0 obj +<>]/K[41 42]/Pg 2161 0 R>> +endobj +984 0 obj +<>]/K[43]/Pg 2161 0 R>> +endobj +985 0 obj +<>]/K[44 45 46]/Pg 2161 0 R>> +endobj +986 0 obj +<>]/K[47]/Pg 2161 0 R>> +endobj +987 0 obj +<>]/K[48 49]/Pg 2161 0 R>> +endobj +988 0 obj +<>]/K[50]/Pg 2161 0 R>> +endobj +989 0 obj +<>]/K[51 52]/Pg 2161 0 R>> +endobj +990 0 obj +<>]/K[53]/Pg 2161 0 R>> +endobj +991 0 obj +<>]/K[54]/Pg 2161 0 R>> +endobj +992 0 obj +<>]/K[55]/Pg 2161 0 R>> +endobj +993 0 obj +<>]/K[56]/Pg 2161 0 R>> +endobj +994 0 obj +<>]/K[57]/Pg 2161 0 R>> +endobj +995 0 obj +<>]/K[58]/Pg 2161 0 R>> +endobj +996 0 obj +<>]/K[59]/Pg 2161 0 R>> +endobj +997 0 obj +<>]/K[60]/Pg 2161 0 R>> +endobj +998 0 obj +<>]/K[61]/Pg 2161 0 R>> +endobj +999 0 obj +<>]/K[62]/Pg 2161 0 R>> +endobj +1000 0 obj +<>]/K[63]/Pg 2161 0 R>> +endobj +1001 0 obj +<>]/K[64]/Pg 2161 0 R>> +endobj +1002 0 obj +<>]/K[65]/Pg 2161 0 R>> +endobj +1003 0 obj +<>]/K[66]/Pg 2161 0 R>> +endobj +1004 0 obj +<>]/K[67]/Pg 2161 0 R>> +endobj +1005 0 obj +<>]/K[68]/Pg 2161 0 R>> +endobj +1006 0 obj +<>]/K[69]/Pg 2161 0 R>> +endobj +1007 0 obj +<>]/K[70]/Pg 2161 0 R>> +endobj +1008 0 obj +<>]/K[71]/Pg 2161 0 R>> +endobj +1009 0 obj +<>]/K[72]/Pg 2161 0 R>> +endobj +1010 0 obj +<>]/K[73]/Pg 2161 0 R>> +endobj +1011 0 obj +<>]/K[74]/Pg 2161 0 R>> +endobj +1012 0 obj +<>]/K[75]/Pg 2161 0 R>> +endobj +1013 0 obj +<>]/K[76]/Pg 2161 0 R>> +endobj +1014 0 obj +<>]/K[77]/Pg 2161 0 R>> +endobj +1015 0 obj +<>]/K[78]/Pg 2161 0 R>> +endobj +1016 0 obj +<>]/K[79]/Pg 2161 0 R>> +endobj +1017 0 obj +<>]/K[80]/Pg 2161 0 R>> +endobj +1018 0 obj +<>]/K[81]/Pg 2161 0 R>> +endobj +1019 0 obj +<>]/K[82]/Pg 2161 0 R>> +endobj +1020 0 obj +<>]/K[83]/Pg 2161 0 R>> +endobj +1021 0 obj +<>]/K[84]/Pg 2161 0 R>> +endobj +1022 0 obj +<>]/K[85]/Pg 2161 0 R>> +endobj +1023 0 obj +<>]/K[86]/Pg 2161 0 R>> +endobj +1024 0 obj +<>]/K[87]/Pg 2161 0 R>> +endobj +1025 0 obj +<>]/K[88]/Pg 2161 0 R>> +endobj +1026 0 obj +<>]/K[89]/Pg 2161 0 R>> +endobj +1027 0 obj +<>]/K[90]/Pg 2161 0 R>> +endobj +1028 0 obj +<>]/K[91]/Pg 2161 0 R>> +endobj +1029 0 obj +<>]/K[92]/Pg 2161 0 R>> +endobj +1030 0 obj +<>]/K[93]/Pg 2161 0 R>> +endobj +1031 0 obj +<>]/K[94]/Pg 2161 0 R>> +endobj +1032 0 obj +<>]/K[95]/Pg 2161 0 R>> +endobj +1033 0 obj +<>]/K[96]/Pg 2161 0 R>> +endobj +1034 0 obj +<>]/K[97]/Pg 2161 0 R>> +endobj +1035 0 obj +<>]/K[98]/Pg 2161 0 R>> +endobj +1036 0 obj +<>]/K[99]/Pg 2161 0 R>> +endobj +1037 0 obj +<>]/K[100]/Pg 2161 0 R>> +endobj +1038 0 obj +<>]/K[101]/Pg 2161 0 R>> +endobj +1039 0 obj +<>]/K[102]/Pg 2161 0 R>> +endobj +1040 0 obj +<>]/K[103]/Pg 2161 0 R>> +endobj +1041 0 obj +<>]/K[104]/Pg 2161 0 R>> +endobj +1042 0 obj +<>]/K[105]/Pg 2161 0 R>> +endobj +1043 0 obj +<>]/K[106]/Pg 2161 0 R>> +endobj +1044 0 obj +<>]/K[107]/Pg 2161 0 R>> +endobj +1045 0 obj +<>]/K[108]/Pg 2161 0 R>> +endobj +1046 0 obj +<>]/K[109]/Pg 2161 0 R>> +endobj +1047 0 obj +<>]/K[110]/Pg 2161 0 R>> +endobj +1048 0 obj +<>]/K[111]/Pg 2161 0 R>> +endobj +1049 0 obj +<>]/K[112]/Pg 2161 0 R>> +endobj +1050 0 obj +<>]/K[113]/Pg 2161 0 R>> +endobj +1051 0 obj +<>]/K[114]/Pg 2161 0 R>> +endobj +1052 0 obj +<>]/K[115]/Pg 2161 0 R>> +endobj +1053 0 obj +<>]/K[116]/Pg 2161 0 R>> +endobj +1054 0 obj +<>]/K[117]/Pg 2161 0 R>> +endobj +1055 0 obj +<>]/K[118]/Pg 2161 0 R>> +endobj +1056 0 obj +<>]/K[119]/Pg 2161 0 R>> +endobj +1057 0 obj +<>]/K[120]/Pg 2161 0 R>> +endobj +1058 0 obj +<>]/K[121]/Pg 2161 0 R>> +endobj +1059 0 obj +<>]/K[122]/Pg 2161 0 R>> +endobj +1060 0 obj +<>]/K[123 124]/Pg 2161 0 R>> +endobj +1061 0 obj +<>]/K[125]/Pg 2161 0 R>> +endobj +1062 0 obj +<>]/K[126 127]/Pg 2161 0 R>> +endobj +1063 0 obj +<>]/K[128]/Pg 2161 0 R>> +endobj +1064 0 obj +<>]/K[129]/Pg 2161 0 R>> +endobj +1065 0 obj +<>]/K[130]/Pg 2161 0 R>> +endobj +1066 0 obj +<>]/K[131 132]/Pg 2161 0 R>> +endobj +1067 0 obj +<>]/K[133]/Pg 2161 0 R>> +endobj +1068 0 obj +<>]/K[134]/Pg 2161 0 R>> +endobj +1069 0 obj +<>]/K[951 0 R 952 0 R 953 0 R 954 0 R 955 0 R 956 0 R 957 0 R 958 0 R 959 0 R 960 0 R 961 0 R 962 0 R 963 0 R 964 0 R 965 0 R 966 0 R 967 0 R 968 0 R 969 0 R 970 0 R 971 0 R 972 0 R 973 0 R 974 0 R 975 0 R 976 0 R 977 0 R 978 0 R 979 0 R 980 0 R 981 0 R 982 0 R 983 0 R 984 0 R 985 0 R 986 0 R 987 0 R 988 0 R 989 0 R 990 0 R 991 0 R 992 0 R 993 0 R 994 0 R 995 0 R 996 0 R 997 0 R 998 0 R 999 0 R 1000 0 R 1001 0 R 1002 0 R 1003 0 R 1004 0 R 1005 0 R 1006 0 R 1007 0 R 1008 0 R 1009 0 R 1010 0 R 1011 0 R 1012 0 R 1013 0 R 1014 0 R 1015 0 R 1016 0 R 1017 0 R 1018 0 R 1019 0 R 1020 0 R 1021 0 R 1022 0 R 1023 0 R 1024 0 R 1025 0 R 1026 0 R 1027 0 R 1028 0 R 1029 0 R 1030 0 R 1031 0 R 1032 0 R 1033 0 R 1034 0 R 1035 0 R 1036 0 R 1037 0 R 1038 0 R 1039 0 R 1040 0 R 1041 0 R 1042 0 R 1043 0 R 1044 0 R 1045 0 R 1046 0 R 1047 0 R 1048 0 R 1049 0 R 1050 0 R 1051 0 R 1052 0 R 1053 0 R 1054 0 R 1055 0 R 1056 0 R 1057 0 R 1058 0 R 1059 0 R 1060 0 R 1061 0 R 1062 0 R 1063 0 R 1064 0 R 1065 0 R 1066 0 R 1067 0 R 1068 0 R]>> +endobj +1070 0 obj +<> +endobj +1071 0 obj +<> +endobj +1072 0 obj +<> +endobj +1073 0 obj +<> +endobj +1074 0 obj +<> +endobj +1075 0 obj +<> +endobj +1076 0 obj +<> +endobj +1077 0 obj +<> +endobj +1078 0 obj +<> +endobj +1079 0 obj +<> +endobj +1080 0 obj +<> +endobj +1081 0 obj +<> +endobj +1082 0 obj +<> +endobj +1083 0 obj +<> +endobj +1084 0 obj +<> +endobj +1085 0 obj +<> +endobj +1086 0 obj +<> +endobj +1087 0 obj +<> +endobj +1088 0 obj +<> +endobj +1089 0 obj +<> +endobj +1090 0 obj +<> +endobj +1091 0 obj +<> +endobj +1092 0 obj +<> +endobj +1093 0 obj +<> +endobj +1094 0 obj +<> +endobj +1095 0 obj +<> +endobj +1096 0 obj +<> +endobj +1097 0 obj +<> +endobj +1098 0 obj +<> +endobj +1099 0 obj +<> +endobj +1100 0 obj +<> +endobj +1101 0 obj +<> +endobj +1102 0 obj +<> +endobj +1103 0 obj +<> +endobj +1104 0 obj +<> +endobj +1105 0 obj +<> +endobj +1106 0 obj +<> +endobj +1107 0 obj +<> +endobj +1108 0 obj +<> +endobj +1109 0 obj +<> +endobj +1110 0 obj +<> +endobj +1111 0 obj +<> +endobj +1112 0 obj +<> +endobj +1113 0 obj +<> +endobj +1114 0 obj +<> +endobj +1115 0 obj +<> +endobj +1116 0 obj +<> +endobj +1117 0 obj +<>]/K[1091 0 R 1092 0 R 1093 0 R 1094 0 R 1095 0 R 1096 0 R 1097 0 R 1098 0 R 1099 0 R 1100 0 R 1101 0 R 1102 0 R 1103 0 R 1104 0 R 1105 0 R 1106 0 R 1107 0 R 1108 0 R 1109 0 R 1110 0 R 1111 0 R 1112 0 R 1113 0 R 1114 0 R 1115 0 R 1116 0 R]>> +endobj +1118 0 obj +<> +endobj +1119 0 obj +<> +endobj +1120 0 obj +<> +endobj +1121 0 obj +<> +endobj +1122 0 obj +<> +endobj +1123 0 obj +<> +endobj +1124 0 obj +<> +endobj +1125 0 obj +<> +endobj +1126 0 obj +<> +endobj +1127 0 obj +<> +endobj +1128 0 obj +<> +endobj +1129 0 obj +<> +endobj +1130 0 obj +<> +endobj +1131 0 obj +<> +endobj +1132 0 obj +<> +endobj +1133 0 obj +<> +endobj +1134 0 obj +<> +endobj +1135 0 obj +<> +endobj +1136 0 obj +<> +endobj +1137 0 obj +<> +endobj +1138 0 obj +<> +endobj +1139 0 obj +<> +endobj +1140 0 obj +<> +endobj +1141 0 obj +<> +endobj +1142 0 obj +<> +endobj +1143 0 obj +<> +endobj +1144 0 obj +<> +endobj +1145 0 obj +<> +endobj +1146 0 obj +<> +endobj +1147 0 obj +<><><><><>]/Pg 2161 0 R>> +endobj +1148 0 obj +<> +endobj +1149 0 obj +<> +endobj +1150 0 obj +<> +endobj +1151 0 obj +<> +endobj +1152 0 obj +<> +endobj +1153 0 obj +<> +endobj +1154 0 obj +<> +endobj +1155 0 obj +<> +endobj +1156 0 obj +<> +endobj +1157 0 obj +<> +endobj +1158 0 obj +<> +endobj +1159 0 obj +<> +endobj +1160 0 obj +<> +endobj +1161 0 obj +<> +endobj +1162 0 obj +<> +endobj +1163 0 obj +<> +endobj +1164 0 obj +<> +endobj +1165 0 obj +<> +endobj +1166 0 obj +<> +endobj +1167 0 obj +<> +endobj +1168 0 obj +<> +endobj +1169 0 obj +<> +endobj +1170 0 obj +<> +endobj +1171 0 obj +<> +endobj +1172 0 obj +<> +endobj +1173 0 obj +<> +endobj +1174 0 obj +<> +endobj +1175 0 obj +<> +endobj +1176 0 obj +<> +endobj +1177 0 obj +<> +endobj +1178 0 obj +<> +endobj +1179 0 obj +<> +endobj +1180 0 obj +<> +endobj +1181 0 obj +<> +endobj +1182 0 obj +<> +endobj +1183 0 obj +<> +endobj +1184 0 obj +<> +endobj +1185 0 obj +<> +endobj +1186 0 obj +<> +endobj +1187 0 obj +<> +endobj +1188 0 obj +<> +endobj +1189 0 obj +<> +endobj +1190 0 obj +<> +endobj +1191 0 obj +<> +endobj +1192 0 obj +<> +endobj +1193 0 obj +<> +endobj +1194 0 obj +<> +endobj +1195 0 obj +<> +endobj +1196 0 obj +<> +endobj +1197 0 obj +<> +endobj +1198 0 obj +<> +endobj +1199 0 obj +<> +endobj +1200 0 obj +<> +endobj +1201 0 obj +<> +endobj +1202 0 obj +<> +endobj +1203 0 obj +<> +endobj +1204 0 obj +<> +endobj +1205 0 obj +<> +endobj +1206 0 obj +<> +endobj +1207 0 obj +<>]/K[1189 0 R 1190 0 R 1191 0 R 1192 0 R 1193 0 R 1194 0 R 1195 0 R 1196 0 R 1197 0 R 1198 0 R 1199 0 R 1200 0 R 1201 0 R 1202 0 R 1203 0 R 1204 0 R 1205 0 R 1206 0 R]>> +endobj +1208 0 obj +<> +endobj +1209 0 obj +<> +endobj +1210 0 obj +<> +endobj +1211 0 obj +<> +endobj +1212 0 obj +<> +endobj +1213 0 obj +<> +endobj +1214 0 obj +<> +endobj +1215 0 obj +<> +endobj +1216 0 obj +<> +endobj +1217 0 obj +<> +endobj +1218 0 obj +<> +endobj +1219 0 obj +<> +endobj +1220 0 obj +<> +endobj +1221 0 obj +<> +endobj +1222 0 obj +<> +endobj +1223 0 obj +<> +endobj +1224 0 obj +<> +endobj +1225 0 obj +<> +endobj +1226 0 obj +<> +endobj +1227 0 obj +<> +endobj +1228 0 obj +<> +endobj +1229 0 obj +<> +endobj +1230 0 obj +<> +endobj +1231 0 obj +<> +endobj +1232 0 obj +<> +endobj +1233 0 obj +<> +endobj +1234 0 obj +<> +endobj +1235 0 obj +<> +endobj +1236 0 obj +<> +endobj +1237 0 obj +<> +endobj +1238 0 obj +<> +endobj +1239 0 obj +<> +endobj +1240 0 obj +<> +endobj +1241 0 obj +<> +endobj +1242 0 obj +<> +endobj +1243 0 obj +<> +endobj +1244 0 obj +<> +endobj +1245 0 obj +<> +endobj +1246 0 obj +<> +endobj +1247 0 obj +<> +endobj +1248 0 obj +<> +endobj +1249 0 obj +<> +endobj +1250 0 obj +<> +endobj +1251 0 obj +<> +endobj +1252 0 obj +<> +endobj +1253 0 obj +<> +endobj +1254 0 obj +<> +endobj +1255 0 obj +<> +endobj +1256 0 obj +<> +endobj +1257 0 obj +<> +endobj +1258 0 obj +<> +endobj +1259 0 obj +<> +endobj +1260 0 obj +<> +endobj +1261 0 obj +<> +endobj +1262 0 obj +<> +endobj +1263 0 obj +<> +endobj +1264 0 obj +<> +endobj +1265 0 obj +<> +endobj +1266 0 obj +<> +endobj +1267 0 obj +<> +endobj +1268 0 obj +<><><><><><>]/Pg 2162 0 R>> +endobj +1269 0 obj +<> +endobj +1270 0 obj +<> +endobj +1271 0 obj +<> +endobj +1272 0 obj +<> +endobj +1273 0 obj +<> +endobj +1274 0 obj +<> +endobj +1275 0 obj +<> +endobj +1276 0 obj +<> +endobj +1277 0 obj +<> +endobj +1278 0 obj +<> +endobj +1279 0 obj +<> +endobj +1280 0 obj +<> +endobj +1281 0 obj +<> +endobj +1282 0 obj +<> +endobj +1283 0 obj +<> +endobj +1284 0 obj +<> +endobj +1285 0 obj +<> +endobj +1286 0 obj +<> +endobj +1287 0 obj +<> +endobj +1288 0 obj +<> +endobj +1289 0 obj +<> +endobj +1290 0 obj +<> +endobj +1291 0 obj +<> +endobj +1292 0 obj +<> +endobj +1293 0 obj +<> +endobj +1294 0 obj +<> +endobj +1295 0 obj +<> +endobj +1296 0 obj +<> +endobj +1297 0 obj +<> +endobj +1298 0 obj +<> +endobj +1299 0 obj +<> +endobj +1300 0 obj +<> +endobj +1301 0 obj +<> +endobj +1302 0 obj +<> +endobj +1303 0 obj +<> +endobj +1304 0 obj +<> +endobj +1305 0 obj +<> +endobj +1306 0 obj +<> +endobj +1307 0 obj +<> +endobj +1308 0 obj +<> +endobj +1309 0 obj +<> +endobj +1310 0 obj +<> +endobj +1311 0 obj +<> +endobj +1312 0 obj +<> +endobj +1313 0 obj +<> +endobj +1314 0 obj +<> +endobj +1315 0 obj +<> +endobj +1316 0 obj +<> +endobj +1317 0 obj +<> +endobj +1318 0 obj +<> +endobj +1319 0 obj +<> +endobj +1320 0 obj +<> +endobj +1321 0 obj +<> +endobj +1322 0 obj +<> +endobj +1323 0 obj +<> +endobj +1324 0 obj +<> +endobj +1325 0 obj +<> +endobj +1326 0 obj +<> +endobj +1327 0 obj +<> +endobj +1328 0 obj +<> +endobj +1329 0 obj +<> +endobj +1330 0 obj +<> +endobj +1331 0 obj +<> +endobj +1332 0 obj +<> +endobj +1333 0 obj +<> +endobj +1334 0 obj +<> +endobj +1335 0 obj +<> +endobj +1336 0 obj +<> +endobj +1337 0 obj +<> +endobj +1338 0 obj +<> +endobj +1339 0 obj +<> +endobj +1340 0 obj +<> +endobj +1341 0 obj +<> +endobj +1342 0 obj +<> +endobj +1343 0 obj +<> +endobj +1344 0 obj +<> +endobj +1345 0 obj +<> +endobj +1346 0 obj +<> +endobj +1347 0 obj +<>]/K[1324 0 R 1325 0 R 1326 0 R 1327 0 R 1328 0 R 1329 0 R 1330 0 R 1331 0 R 1332 0 R 1333 0 R 1334 0 R 1335 0 R 1336 0 R 1337 0 R 1338 0 R 1339 0 R 1340 0 R 1341 0 R 1342 0 R 1343 0 R 1344 0 R 1345 0 R 1346 0 R]>> +endobj +1348 0 obj +<> +endobj +1349 0 obj +<> +endobj +1350 0 obj +<> +endobj +1351 0 obj +<> +endobj +1352 0 obj +<> +endobj +1353 0 obj +<> +endobj +1354 0 obj +<> +endobj +1355 0 obj +<> +endobj +1356 0 obj +<> +endobj +1357 0 obj +<> +endobj +1358 0 obj +<> +endobj +1359 0 obj +<> +endobj +1360 0 obj +<> +endobj +1361 0 obj +<> +endobj +1362 0 obj +<> +endobj +1363 0 obj +<> +endobj +1364 0 obj +<> +endobj +1365 0 obj +<> +endobj +1366 0 obj +<> +endobj +1367 0 obj +<> +endobj +1368 0 obj +<> +endobj +1369 0 obj +<> +endobj +1370 0 obj +<> +endobj +1371 0 obj +<> +endobj +1372 0 obj +<> +endobj +1373 0 obj +<> +endobj +1374 0 obj +<> +endobj +1375 0 obj +<> +endobj +1376 0 obj +<> +endobj +1377 0 obj +<> +endobj +1378 0 obj +<> +endobj +1379 0 obj +<> +endobj +1380 0 obj +<> +endobj +1381 0 obj +<> +endobj +1382 0 obj +<> +endobj +1383 0 obj +<> +endobj +1384 0 obj +<> +endobj +1385 0 obj +<> +endobj +1386 0 obj +<> +endobj +1387 0 obj +<> +endobj +1388 0 obj +<> +endobj +1389 0 obj +<> +endobj +1390 0 obj +<>]/K[1362 0 R 1370 0 R 1377 0 R 1383 0 R 1389 0 R]>> +endobj +1391 0 obj +<>]/K[225]/Pg 2164 0 R>> +endobj +1392 0 obj +<>]/K[226]/Pg 2164 0 R>> +endobj +1393 0 obj +<>]/K[227 228]/Pg 2164 0 R>> +endobj +1394 0 obj +<>]/K[229]/Pg 2164 0 R>> +endobj +1395 0 obj +<>]/K[230]/Pg 2164 0 R>> +endobj +1396 0 obj +<>]/K[231]/Pg 2164 0 R>> +endobj +1397 0 obj +<>]/K[232]/Pg 2164 0 R>> +endobj +1398 0 obj +<>]/K[233]/Pg 2164 0 R>> +endobj +1399 0 obj +<>]/K[234]/Pg 2164 0 R>> +endobj +1400 0 obj +<>]/K[235]/Pg 2164 0 R>> +endobj +1401 0 obj +<>]/K[236 237 238 239]/Pg 2164 0 R>> +endobj +1402 0 obj +<> +endobj +1403 0 obj +<> +endobj +1404 0 obj +<> +endobj +1405 0 obj +<> +endobj +1406 0 obj +<> +endobj +1407 0 obj +<> +endobj +1408 0 obj +<> +endobj +1409 0 obj +<> +endobj +1410 0 obj +<> +endobj +1411 0 obj +<>]/K[1403 0 R 1404 0 R 1405 0 R 1406 0 R 1407 0 R 1408 0 R 1409 0 R 1410 0 R]>> +endobj +1412 0 obj +<> +endobj +1413 0 obj +<> +endobj +1414 0 obj +<> +endobj +1415 0 obj +<> +endobj +1416 0 obj +<> +endobj +1417 0 obj +<> +endobj +1418 0 obj +<> +endobj +1419 0 obj +<> +endobj +1420 0 obj +<> +endobj +1421 0 obj +<> +endobj +1422 0 obj +<> +endobj +1423 0 obj +<> +endobj +1424 0 obj +<> +endobj +1425 0 obj +<> +endobj +1426 0 obj +<> +endobj +1427 0 obj +<> +endobj +1428 0 obj +<> +endobj +1429 0 obj +<> +endobj +1430 0 obj +<> +endobj +1431 0 obj +<> +endobj +1432 0 obj +<> +endobj +1433 0 obj +<> +endobj +1434 0 obj +<> +endobj +1435 0 obj +<> +endobj +1436 0 obj +<> +endobj +1437 0 obj +<> +endobj +1438 0 obj +<> +endobj +1439 0 obj +<> +endobj +1440 0 obj +<> +endobj +1441 0 obj +<> +endobj +1442 0 obj +<> +endobj +1443 0 obj +<> +endobj +1444 0 obj +<> +endobj +1445 0 obj +<>]/Pg 2164 0 R>> +endobj +1446 0 obj +<> +endobj +1447 0 obj +<> +endobj +1448 0 obj +<> +endobj +1449 0 obj +<> +endobj +1450 0 obj +<> +endobj +1451 0 obj +<> +endobj +1452 0 obj +<> +endobj +1453 0 obj +<> +endobj +1454 0 obj +<> +endobj +1455 0 obj +<> +endobj +1456 0 obj +<> +endobj +1457 0 obj +<> +endobj +1458 0 obj +<> +endobj +1459 0 obj +<> +endobj +1460 0 obj +<> +endobj +1461 0 obj +<> +endobj +1462 0 obj +<> +endobj +1463 0 obj +<> +endobj +1464 0 obj +<> +endobj +1465 0 obj +<> +endobj +1466 0 obj +<> +endobj +1467 0 obj +<> +endobj +1468 0 obj +<> +endobj +1469 0 obj +<> +endobj +1470 0 obj +<> +endobj +1471 0 obj +<> +endobj +1472 0 obj +<> +endobj +1473 0 obj +<> +endobj +1474 0 obj +<> +endobj +1475 0 obj +<> +endobj +1476 0 obj +<> +endobj +1477 0 obj +<> +endobj +1478 0 obj +<> +endobj +1479 0 obj +<> +endobj +1480 0 obj +<> +endobj +1481 0 obj +<> +endobj +1482 0 obj +<> +endobj +1483 0 obj +<> +endobj +1484 0 obj +<> +endobj +1485 0 obj +<> +endobj +1486 0 obj +<> +endobj +1487 0 obj +<> +endobj +1488 0 obj +<><><>]/Pg 2164 0 R>> +endobj +1489 0 obj +<> +endobj +1490 0 obj +<> +endobj +1491 0 obj +<> +endobj +1492 0 obj +<> +endobj +1493 0 obj +<> +endobj +1494 0 obj +<> +endobj +1495 0 obj +<> +endobj +1496 0 obj +<> +endobj +1497 0 obj +<> +endobj +1498 0 obj +<> +endobj +1499 0 obj +<> +endobj +1500 0 obj +<> +endobj +1501 0 obj +<> +endobj +1502 0 obj +<> +endobj +1503 0 obj +<> +endobj +1504 0 obj +<> +endobj +1505 0 obj +<> +endobj +1506 0 obj +<> +endobj +1507 0 obj +<> +endobj +1508 0 obj +<> +endobj +1509 0 obj +<> +endobj +1510 0 obj +<> +endobj +1511 0 obj +<> +endobj +1512 0 obj +<>]/K[131]/Pg 2165 0 R>> +endobj +1513 0 obj +<>]/K[132]/Pg 2165 0 R>> +endobj +1514 0 obj +<>]/K[133 134]/Pg 2165 0 R>> +endobj +1515 0 obj +<>]/K[135]/Pg 2165 0 R>> +endobj +1516 0 obj +<>]/K[136]/Pg 2165 0 R>> +endobj +1517 0 obj +<>]/K[137]/Pg 2165 0 R>> +endobj +1518 0 obj +<>]/K[138 139]/Pg 2165 0 R>> +endobj +1519 0 obj +<>]/K[140]/Pg 2165 0 R>> +endobj +1520 0 obj +<>]/K[141]/Pg 2165 0 R>> +endobj +1521 0 obj +<> +endobj +1522 0 obj +<>]/K[0]/Pg 2165 0 R>> +endobj +1523 0 obj +<>]/K[1]/Pg 2165 0 R>> +endobj +1524 0 obj +<>]/K[2]/Pg 2165 0 R>> +endobj +1525 0 obj +<>]/K[3]/Pg 2165 0 R>> +endobj +1526 0 obj +<>]/K[4]/Pg 2165 0 R>> +endobj +1527 0 obj +<>]/K[5]/Pg 2165 0 R>> +endobj +1528 0 obj +<>]/K[6]/Pg 2165 0 R>> +endobj +1529 0 obj +<>]/K[7]/Pg 2165 0 R>> +endobj +1530 0 obj +<>]/K[8 9]/Pg 2165 0 R>> +endobj +1531 0 obj +<>]/K[10]/Pg 2165 0 R>> +endobj +1532 0 obj +<>]/K[11 12]/Pg 2165 0 R>> +endobj +1533 0 obj +<>]/K[13]/Pg 2165 0 R>> +endobj +1534 0 obj +<>]/K[14]/Pg 2165 0 R>> +endobj +1535 0 obj +<>]/K[15]/Pg 2165 0 R>> +endobj +1536 0 obj +<>]/K[16]/Pg 2165 0 R>> +endobj +1537 0 obj +<>]/K[17]/Pg 2165 0 R>> +endobj +1538 0 obj +<>]/K[18]/Pg 2165 0 R>> +endobj +1539 0 obj +<>]/K[19]/Pg 2165 0 R>> +endobj +1540 0 obj +<>]/K[20]/Pg 2165 0 R>> +endobj +1541 0 obj +<>]/K[21]/Pg 2165 0 R>> +endobj +1542 0 obj +<>]/K[22]/Pg 2165 0 R>> +endobj +1543 0 obj +<>]/K[23]/Pg 2165 0 R>> +endobj +1544 0 obj +<>]/K[24]/Pg 2165 0 R>> +endobj +1545 0 obj +<>]/K[25]/Pg 2165 0 R>> +endobj +1546 0 obj +<>]/K[26]/Pg 2165 0 R>> +endobj +1547 0 obj +<>]/K[27]/Pg 2165 0 R>> +endobj +1548 0 obj +<>]/K[28]/Pg 2165 0 R>> +endobj +1549 0 obj +<>]/K[29]/Pg 2165 0 R>> +endobj +1550 0 obj +<>]/K[30]/Pg 2165 0 R>> +endobj +1551 0 obj +<>]/K[31]/Pg 2165 0 R>> +endobj +1552 0 obj +<>]/K[32]/Pg 2165 0 R>> +endobj +1553 0 obj +<>]/K[33]/Pg 2165 0 R>> +endobj +1554 0 obj +<>]/K[34]/Pg 2165 0 R>> +endobj +1555 0 obj +<>]/K[35]/Pg 2165 0 R>> +endobj +1556 0 obj +<>]/K[36]/Pg 2165 0 R>> +endobj +1557 0 obj +<>]/K[37]/Pg 2165 0 R>> +endobj +1558 0 obj +<>]/K[38]/Pg 2165 0 R>> +endobj +1559 0 obj +<>]/K[39]/Pg 2165 0 R>> +endobj +1560 0 obj +<>]/K[40]/Pg 2165 0 R>> +endobj +1561 0 obj +<>]/K[41]/Pg 2165 0 R>> +endobj +1562 0 obj +<>]/K[42]/Pg 2165 0 R>> +endobj +1563 0 obj +<>]/K[43]/Pg 2165 0 R>> +endobj +1564 0 obj +<>]/K[44]/Pg 2165 0 R>> +endobj +1565 0 obj +<>]/K[45]/Pg 2165 0 R>> +endobj +1566 0 obj +<>]/K[46]/Pg 2165 0 R>> +endobj +1567 0 obj +<>]/K[47]/Pg 2165 0 R>> +endobj +1568 0 obj +<>]/K[48]/Pg 2165 0 R>> +endobj +1569 0 obj +<>]/K[49]/Pg 2165 0 R>> +endobj +1570 0 obj +<>]/K[50]/Pg 2165 0 R>> +endobj +1571 0 obj +<>]/K[51 52]/Pg 2165 0 R>> +endobj +1572 0 obj +<>]/K[53]/Pg 2165 0 R>> +endobj +1573 0 obj +<>]/K[54]/Pg 2165 0 R>> +endobj +1574 0 obj +<>]/K[55]/Pg 2165 0 R>> +endobj +1575 0 obj +<>]/K[56]/Pg 2165 0 R>> +endobj +1576 0 obj +<>]/K[57]/Pg 2165 0 R>> +endobj +1577 0 obj +<>]/K[58]/Pg 2165 0 R>> +endobj +1578 0 obj +<>]/K[59]/Pg 2165 0 R>> +endobj +1579 0 obj +<>]/K[60]/Pg 2165 0 R>> +endobj +1580 0 obj +<>]/K[61]/Pg 2165 0 R>> +endobj +1581 0 obj +<>]/K[62]/Pg 2165 0 R>> +endobj +1582 0 obj +<>]/K[63]/Pg 2165 0 R>> +endobj +1583 0 obj +<>]/K[64]/Pg 2165 0 R>> +endobj +1584 0 obj +<>]/K[65]/Pg 2165 0 R>> +endobj +1585 0 obj +<>]/K[66]/Pg 2165 0 R>> +endobj +1586 0 obj +<>]/K[67]/Pg 2165 0 R>> +endobj +1587 0 obj +<>]/K[68]/Pg 2165 0 R>> +endobj +1588 0 obj +<>]/K[69]/Pg 2165 0 R>> +endobj +1589 0 obj +<>]/K[70]/Pg 2165 0 R>> +endobj +1590 0 obj +<>]/K[71]/Pg 2165 0 R>> +endobj +1591 0 obj +<>]/K[72]/Pg 2165 0 R>> +endobj +1592 0 obj +<>]/K[73]/Pg 2165 0 R>> +endobj +1593 0 obj +<>]/K[74]/Pg 2165 0 R>> +endobj +1594 0 obj +<>]/K[75]/Pg 2165 0 R>> +endobj +1595 0 obj +<>]/K[76]/Pg 2165 0 R>> +endobj +1596 0 obj +<>]/K[77]/Pg 2165 0 R>> +endobj +1597 0 obj +<>]/K[78]/Pg 2165 0 R>> +endobj +1598 0 obj +<>]/K[79]/Pg 2165 0 R>> +endobj +1599 0 obj +<>]/K[80]/Pg 2165 0 R>> +endobj +1600 0 obj +<>]/K[81]/Pg 2165 0 R>> +endobj +1601 0 obj +<>]/K[82]/Pg 2165 0 R>> +endobj +1602 0 obj +<>]/K[83]/Pg 2165 0 R>> +endobj +1603 0 obj +<>]/K[84]/Pg 2165 0 R>> +endobj +1604 0 obj +<>]/K[85]/Pg 2165 0 R>> +endobj +1605 0 obj +<>]/K[86]/Pg 2165 0 R>> +endobj +1606 0 obj +<>]/K[87]/Pg 2165 0 R>> +endobj +1607 0 obj +<>]/K[88]/Pg 2165 0 R>> +endobj +1608 0 obj +<>]/K[89]/Pg 2165 0 R>> +endobj +1609 0 obj +<>]/K[90]/Pg 2165 0 R>> +endobj +1610 0 obj +<>]/K[91]/Pg 2165 0 R>> +endobj +1611 0 obj +<>]/K[92]/Pg 2165 0 R>> +endobj +1612 0 obj +<>]/K[93]/Pg 2165 0 R>> +endobj +1613 0 obj +<>]/K[94]/Pg 2165 0 R>> +endobj +1614 0 obj +<>]/K[95 96 97]/Pg 2165 0 R>> +endobj +1615 0 obj +<>]/K[98]/Pg 2165 0 R>> +endobj +1616 0 obj +<>]/K[99]/Pg 2165 0 R>> +endobj +1617 0 obj +<>]/K[100]/Pg 2165 0 R>> +endobj +1618 0 obj +<>]/K[101]/Pg 2165 0 R>> +endobj +1619 0 obj +<>]/K[102]/Pg 2165 0 R>> +endobj +1620 0 obj +<>]/K[103]/Pg 2165 0 R>> +endobj +1621 0 obj +<>]/K[104]/Pg 2165 0 R>> +endobj +1622 0 obj +<>]/K[105]/Pg 2165 0 R>> +endobj +1623 0 obj +<>]/K[106]/Pg 2165 0 R>> +endobj +1624 0 obj +<>]/K[107]/Pg 2165 0 R>> +endobj +1625 0 obj +<>]/K[108]/Pg 2165 0 R>> +endobj +1626 0 obj +<>]/K[109]/Pg 2165 0 R>> +endobj +1627 0 obj +<>]/K[110]/Pg 2165 0 R>> +endobj +1628 0 obj +<>]/K[111]/Pg 2165 0 R>> +endobj +1629 0 obj +<>]/K[112]/Pg 2165 0 R>> +endobj +1630 0 obj +<>]/K[113]/Pg 2165 0 R>> +endobj +1631 0 obj +<>]/K[114]/Pg 2165 0 R>> +endobj +1632 0 obj +<>]/K[115]/Pg 2165 0 R>> +endobj +1633 0 obj +<>]/K[116]/Pg 2165 0 R>> +endobj +1634 0 obj +<>]/K[117]/Pg 2165 0 R>> +endobj +1635 0 obj +<>]/K[118]/Pg 2165 0 R>> +endobj +1636 0 obj +<>]/K[119]/Pg 2165 0 R>> +endobj +1637 0 obj +<>]/K[120]/Pg 2165 0 R>> +endobj +1638 0 obj +<>]/K[121]/Pg 2165 0 R>> +endobj +1639 0 obj +<>]/K[122 123]/Pg 2165 0 R>> +endobj +1640 0 obj +<>]/K[124]/Pg 2165 0 R>> +endobj +1641 0 obj +<>]/K[125 126]/Pg 2165 0 R>> +endobj +1642 0 obj +<>]/K[127]/Pg 2165 0 R>> +endobj +1643 0 obj +<>]/K[128]/Pg 2165 0 R>> +endobj +1644 0 obj +<>]/K[129]/Pg 2165 0 R>> +endobj +1645 0 obj +<>]/K[130]/Pg 2165 0 R>> +endobj +1646 0 obj +<>]/K[1522 0 R 1523 0 R 1524 0 R 1525 0 R 1526 0 R 1527 0 R 1528 0 R 1529 0 R 1530 0 R 1531 0 R 1532 0 R 1533 0 R 1534 0 R 1535 0 R 1536 0 R 1537 0 R 1538 0 R 1539 0 R 1540 0 R 1541 0 R 1542 0 R 1543 0 R 1544 0 R 1545 0 R 1546 0 R 1547 0 R 1548 0 R 1549 0 R 1550 0 R 1551 0 R 1552 0 R 1553 0 R 1554 0 R 1555 0 R 1556 0 R 1557 0 R 1558 0 R 1559 0 R 1560 0 R 1561 0 R 1562 0 R 1563 0 R 1564 0 R 1565 0 R 1566 0 R 1567 0 R 1568 0 R 1569 0 R 1570 0 R 1571 0 R 1572 0 R 1573 0 R 1574 0 R 1575 0 R 1576 0 R 1577 0 R 1578 0 R 1579 0 R 1580 0 R 1581 0 R 1582 0 R 1583 0 R 1584 0 R 1585 0 R 1586 0 R 1587 0 R 1588 0 R 1589 0 R 1590 0 R 1591 0 R 1592 0 R 1593 0 R 1594 0 R 1595 0 R 1596 0 R 1597 0 R 1598 0 R 1599 0 R 1600 0 R 1601 0 R 1602 0 R 1603 0 R 1604 0 R 1605 0 R 1606 0 R 1607 0 R 1608 0 R 1609 0 R 1610 0 R 1611 0 R 1612 0 R 1613 0 R 1614 0 R 1615 0 R 1616 0 R 1617 0 R 1618 0 R 1619 0 R 1620 0 R 1621 0 R 1622 0 R 1623 0 R 1624 0 R 1625 0 R 1626 0 R 1627 0 R 1628 0 R 1629 0 R 1630 0 R 1631 0 R 1632 0 R 1633 0 R 1634 0 R 1635 0 R 1636 0 R 1637 0 R 1638 0 R 1639 0 R 1640 0 R 1641 0 R 1642 0 R 1643 0 R 1644 0 R 1645 0 R]>> +endobj +1647 0 obj +<> +endobj +1648 0 obj +<> +endobj +1649 0 obj +<> +endobj +1650 0 obj +<> +endobj +1651 0 obj +<> +endobj +1652 0 obj +<> +endobj +1653 0 obj +<> +endobj +1654 0 obj +<> +endobj +1655 0 obj +<> +endobj +1656 0 obj +<> +endobj +1657 0 obj +<> +endobj +1658 0 obj +<> +endobj +1659 0 obj +<> +endobj +1660 0 obj +<> +endobj +1661 0 obj +<> +endobj +1662 0 obj +<> +endobj +1663 0 obj +<> +endobj +1664 0 obj +<> +endobj +1665 0 obj +<> +endobj +1666 0 obj +<> +endobj +1667 0 obj +<> +endobj +1668 0 obj +<> +endobj +1669 0 obj +<> +endobj +1670 0 obj +<> +endobj +1671 0 obj +<> +endobj +1672 0 obj +<> +endobj +1673 0 obj +<> +endobj +1674 0 obj +<> +endobj +1675 0 obj +<> +endobj +1676 0 obj +<> +endobj +1677 0 obj +<> +endobj +1678 0 obj +<> +endobj +1679 0 obj +<> +endobj +1680 0 obj +<> +endobj +1681 0 obj +<> +endobj +1682 0 obj +<> +endobj +1683 0 obj +<> +endobj +1684 0 obj +<> +endobj +1685 0 obj +<> +endobj +1686 0 obj +<> +endobj +1687 0 obj +<> +endobj +1688 0 obj +<> +endobj +1689 0 obj +<> +endobj +1690 0 obj +<> +endobj +1691 0 obj +<> +endobj +1692 0 obj +<> +endobj +1693 0 obj +<> +endobj +1694 0 obj +<> +endobj +1695 0 obj +<> +endobj +1696 0 obj +<> +endobj +1697 0 obj +<> +endobj +1698 0 obj +<> +endobj +1699 0 obj +<> +endobj +1700 0 obj +<> +endobj +1701 0 obj +<> +endobj +1702 0 obj +<> +endobj +1703 0 obj +<> +endobj +1704 0 obj +<> +endobj +1705 0 obj +<> +endobj +1706 0 obj +<> +endobj +1707 0 obj +<> +endobj +1708 0 obj +<> +endobj +1709 0 obj +<> +endobj +1710 0 obj +<> +endobj +1711 0 obj +<> +endobj +1712 0 obj +<> +endobj +1713 0 obj +<> +endobj +1714 0 obj +<> +endobj +1715 0 obj +<> +endobj +1716 0 obj +<> +endobj +1717 0 obj +<> +endobj +1718 0 obj +<> +endobj +1719 0 obj +<> +endobj +1720 0 obj +<> +endobj +1721 0 obj +<> +endobj +1722 0 obj +<> +endobj +1723 0 obj +<> +endobj +1724 0 obj +<> +endobj +1725 0 obj +<> +endobj +1726 0 obj +<> +endobj +1727 0 obj +<> +endobj +1728 0 obj +<> +endobj +1729 0 obj +<> +endobj +1730 0 obj +<> +endobj +1731 0 obj +<> +endobj +1732 0 obj +<> +endobj +1733 0 obj +<> +endobj +1734 0 obj +<>]/K[1729 0 R 1730 0 R 1731 0 R 1732 0 R 1733 0 R]>> +endobj +1735 0 obj +<> +endobj +1736 0 obj +<> +endobj +1737 0 obj +<> +endobj +1738 0 obj +<> +endobj +1739 0 obj +<> +endobj +1740 0 obj +<> +endobj +1741 0 obj +<> +endobj +1742 0 obj +<> +endobj +1743 0 obj +<> +endobj +1744 0 obj +<> +endobj +1745 0 obj +<> +endobj +1746 0 obj +<> +endobj +1747 0 obj +<> +endobj +1748 0 obj +<> +endobj +1749 0 obj +<>]/K[1739 0 R 1740 0 R 1741 0 R 1742 0 R 1743 0 R 1744 0 R 1745 0 R 1746 0 R 1747 0 R 1748 0 R]>> +endobj +1750 0 obj +<> +endobj +1751 0 obj +<> +endobj +1752 0 obj +<> +endobj +1753 0 obj +<> +endobj +1754 0 obj +<> +endobj +1755 0 obj +<> +endobj +1756 0 obj +<> +endobj +1757 0 obj +<> +endobj +1758 0 obj +<> +endobj +1759 0 obj +<> +endobj +1760 0 obj +<> +endobj +1761 0 obj +<> +endobj +1762 0 obj +<> +endobj +1763 0 obj +<> +endobj +1764 0 obj +<> +endobj +1765 0 obj +<> +endobj +1766 0 obj +<> +endobj +1767 0 obj +<> +endobj +1768 0 obj +<> +endobj +1769 0 obj +<> +endobj +1770 0 obj +<> +endobj +1771 0 obj +<> +endobj +1772 0 obj +<> +endobj +1773 0 obj +<> +endobj +1774 0 obj +<> +endobj +1775 0 obj +<> +endobj +1776 0 obj +<> +endobj +1777 0 obj +<> +endobj +1778 0 obj +<> +endobj +1779 0 obj +<> +endobj +1780 0 obj +<> +endobj +1781 0 obj +<> +endobj +1782 0 obj +<> +endobj +1783 0 obj +<> +endobj +1784 0 obj +<> +endobj +1785 0 obj +<> +endobj +1786 0 obj +<> +endobj +1787 0 obj +<> +endobj +1788 0 obj +<> +endobj +1789 0 obj +<> +endobj +1790 0 obj +<> +endobj +1791 0 obj +<> +endobj +1792 0 obj +<> +endobj +1793 0 obj +<> +endobj +1794 0 obj +<> +endobj +1795 0 obj +<> +endobj +1796 0 obj +<> +endobj +1797 0 obj +<> +endobj +1798 0 obj +<> +endobj +1799 0 obj +<> +endobj +1800 0 obj +<> +endobj +1801 0 obj +<> +endobj +1802 0 obj +<><><><>1801 0 R<><><>]/Pg 2166 0 R>> +endobj +1803 0 obj +<> +endobj +1804 0 obj +<> +endobj +1805 0 obj +<> +endobj +1806 0 obj +<> +endobj +1807 0 obj +<> +endobj +1808 0 obj +<> +endobj +1809 0 obj +<> +endobj +1810 0 obj +<> +endobj +1811 0 obj +<> +endobj +1812 0 obj +<> +endobj +1813 0 obj +<> +endobj +1814 0 obj +<> +endobj +1815 0 obj +<> +endobj +1816 0 obj +<> +endobj +1817 0 obj +<> +endobj +1818 0 obj +<> +endobj +1819 0 obj +<> +endobj +1820 0 obj +<> +endobj +1821 0 obj +<> +endobj +1822 0 obj +<> +endobj +1823 0 obj +<> +endobj +1824 0 obj +<> +endobj +1825 0 obj +<> +endobj +1826 0 obj +<> +endobj +1827 0 obj +<> +endobj +1828 0 obj +<> +endobj +1829 0 obj +<> +endobj +1830 0 obj +<> +endobj +1831 0 obj +<> +endobj +1832 0 obj +<> +endobj +1833 0 obj +<> +endobj +1834 0 obj +<> +endobj +1835 0 obj +<> +endobj +1836 0 obj +<> +endobj +1837 0 obj +<> +endobj +1838 0 obj +<> +endobj +1839 0 obj +<> +endobj +1840 0 obj +<> +endobj +1841 0 obj +<> +endobj +1842 0 obj +<> +endobj +1843 0 obj +<> +endobj +1844 0 obj +<> +endobj +1845 0 obj +<> +endobj +1846 0 obj +<> +endobj +1847 0 obj +<> +endobj +1848 0 obj +<> +endobj +1849 0 obj +<> +endobj +1850 0 obj +<> +endobj +1851 0 obj +<> +endobj +1852 0 obj +<> +endobj +1853 0 obj +<> +endobj +1854 0 obj +<> +endobj +1855 0 obj +<> +endobj +1856 0 obj +<> +endobj +1857 0 obj +<> +endobj +1858 0 obj +<> +endobj +1859 0 obj +<> +endobj +1860 0 obj +<> +endobj +1861 0 obj +<> +endobj +1862 0 obj +<> +endobj +1863 0 obj +<> +endobj +1864 0 obj +<> +endobj +1865 0 obj +<> +endobj +1866 0 obj +<>]/Pg 2167 0 R>> +endobj +1867 0 obj +<> +endobj +1868 0 obj +<> +endobj +1869 0 obj +<> +endobj +1870 0 obj +<> +endobj +1871 0 obj +<> +endobj +1872 0 obj +<> +endobj +1873 0 obj +<> +endobj +1874 0 obj +<> +endobj +1875 0 obj +<> +endobj +1876 0 obj +<> +endobj +1877 0 obj +<> +endobj +1878 0 obj +<> +endobj +1879 0 obj +<> +endobj +1880 0 obj +<> +endobj +1881 0 obj +<> +endobj +1882 0 obj +<> +endobj +1883 0 obj +<> +endobj +1884 0 obj +<> +endobj +1885 0 obj +<> +endobj +1886 0 obj +<> +endobj +1887 0 obj +<> +endobj +1888 0 obj +<> +endobj +1889 0 obj +<> +endobj +1890 0 obj +<> +endobj +1891 0 obj +<> +endobj +1892 0 obj +<> +endobj +1893 0 obj +<> +endobj +1894 0 obj +<> +endobj +1895 0 obj +<> +endobj +1896 0 obj +<><>1885 0 R<><>1886 0 R<><>1887 0 R<><><><><>1888 0 R<><><><>1889 0 R<>1890 0 R<><><><>1891 0 R<><><><>1892 0 R<>1893 0 R<>1894 0 R<><>1895 0 R<><><><><><><><><>]/Pg 2167 0 R>> +endobj +1897 0 obj +<> +endobj +1898 0 obj +<> +endobj +1899 0 obj +<> +endobj +1900 0 obj +<> +endobj +1901 0 obj +<> +endobj +1902 0 obj +<> +endobj +1903 0 obj +<> +endobj +1904 0 obj +<> +endobj +1905 0 obj +<> +endobj +1906 0 obj +<> +endobj +1907 0 obj +<> +endobj +1908 0 obj +<> +endobj +1909 0 obj +<> +endobj +1910 0 obj +<> +endobj +1911 0 obj +<> +endobj +1912 0 obj +<> +endobj +1913 0 obj +<> +endobj +1914 0 obj +<> +endobj +1915 0 obj +<> +endobj +1916 0 obj +<> +endobj +1917 0 obj +<> +endobj +1918 0 obj +<> +endobj +1919 0 obj +<> +endobj +1920 0 obj +<> +endobj +1921 0 obj +<> +endobj +1922 0 obj +<> +endobj +1923 0 obj +<> +endobj +1924 0 obj +<> +endobj +1925 0 obj +<> +endobj +1926 0 obj +<> +endobj +1927 0 obj +<> +endobj +1928 0 obj +<> +endobj +1929 0 obj +<> +endobj +1930 0 obj +<> +endobj +1931 0 obj +<> +endobj +1932 0 obj +<> +endobj +1933 0 obj +<> +endobj +1934 0 obj +<> +endobj +1935 0 obj +<> +endobj +1936 0 obj +<> +endobj +1937 0 obj +<> +endobj +1938 0 obj +<> +endobj +1939 0 obj +<> +endobj +1940 0 obj +<> +endobj +1941 0 obj +<> +endobj +1942 0 obj +<> +endobj +1943 0 obj +<> +endobj +1944 0 obj +<> +endobj +1945 0 obj +<> +endobj +1946 0 obj +<> +endobj +1947 0 obj +<> +endobj +1948 0 obj +<> +endobj +1949 0 obj +<> +endobj +1950 0 obj +<> +endobj +1951 0 obj +<> +endobj +1952 0 obj +<> +endobj +1953 0 obj +<> +endobj +1954 0 obj +<> +endobj +1955 0 obj +<> +endobj +1956 0 obj +<> +endobj +1957 0 obj +<> +endobj +1958 0 obj +<> +endobj +1959 0 obj +<> +endobj +1960 0 obj +<> +endobj +1961 0 obj +<> +endobj +1962 0 obj +<> +endobj +1963 0 obj +<> +endobj +1964 0 obj +<> +endobj +1965 0 obj +<> +endobj +1966 0 obj +<> +endobj +1967 0 obj +<> +endobj +1968 0 obj +<> +endobj +1969 0 obj +<> +endobj +1970 0 obj +<> +endobj +1971 0 obj +<> +endobj +1972 0 obj +<> +endobj +1973 0 obj +<> +endobj +1974 0 obj +<> +endobj +1975 0 obj +<> +endobj +1976 0 obj +<> +endobj +1977 0 obj +<> +endobj +1978 0 obj +<> +endobj +1979 0 obj +<> +endobj +1980 0 obj +<> +endobj +1981 0 obj +<> +endobj +1982 0 obj +<> +endobj +1983 0 obj +<> +endobj +1984 0 obj +<> +endobj +1985 0 obj +<> +endobj +1986 0 obj +<> +endobj +1987 0 obj +<> +endobj +1988 0 obj +<> +endobj +1989 0 obj +<> +endobj +1990 0 obj +<> +endobj +1991 0 obj +<> +endobj +1992 0 obj +<> +endobj +1993 0 obj +<> +endobj +1994 0 obj +<> +endobj +1995 0 obj +<> +endobj +1996 0 obj +<> +endobj +1997 0 obj +<> +endobj +1998 0 obj +<> +endobj +1999 0 obj +<> +endobj +2000 0 obj +<> +endobj +2001 0 obj +<> +endobj +2002 0 obj +<> +endobj +2003 0 obj +<> +endobj +2004 0 obj +<> +endobj +2005 0 obj +<> +endobj +2006 0 obj +<> +endobj +2007 0 obj +<> +endobj +2008 0 obj +<> +endobj +2009 0 obj +<> +endobj +2010 0 obj +<> +endobj +2011 0 obj +<> +endobj +2012 0 obj +<> +endobj +2013 0 obj +<> +endobj +2014 0 obj +<> +endobj +2015 0 obj +<> +endobj +2016 0 obj +<> +endobj +2017 0 obj +<>2014 0 R<><>2015 0 R<><>2016 0 R<><>]/Pg 2168 0 R>> +endobj +2018 0 obj +<> +endobj +2019 0 obj +<> +endobj +2020 0 obj +<> +endobj +2021 0 obj +<> +endobj +2022 0 obj +<> +endobj +2023 0 obj +<> +endobj +2024 0 obj +<> +endobj +2025 0 obj +<> +endobj +2026 0 obj +<> +endobj +2027 0 obj +<> +endobj +2028 0 obj +<> +endobj +2029 0 obj +<> +endobj +2030 0 obj +<> +endobj +2031 0 obj +<> +endobj +2032 0 obj +<> +endobj +2033 0 obj +<> +endobj +2034 0 obj +<> +endobj +2035 0 obj +<> +endobj +2036 0 obj +<> +endobj +2037 0 obj +<> +endobj +2038 0 obj +<> +endobj +2039 0 obj +<> +endobj +2040 0 obj +<> +endobj +2041 0 obj +<> +endobj +2042 0 obj +<> +endobj +2043 0 obj +<> +endobj +2044 0 obj +<> +endobj +2045 0 obj +<> +endobj +2046 0 obj +<> +endobj +2047 0 obj +<> +endobj +2048 0 obj +<> +endobj +2049 0 obj +<> +endobj +2050 0 obj +<> +endobj +2051 0 obj +<> +endobj +2052 0 obj +<> +endobj +2053 0 obj +<> +endobj +2054 0 obj +<> +endobj +2055 0 obj +<> +endobj +2056 0 obj +<> +endobj +2057 0 obj +<> +endobj +2058 0 obj +[/ICCBased 2210 0 R] +endobj +2059 0 obj +[/ICCBased 2211 0 R] +endobj +2060 0 obj +[2154 0 R/XYZ 307.84253 277.67474 0] +endobj +2061 0 obj +[2155 0 R/XYZ 42.519684 195.3197 0] +endobj +2062 0 obj +[2159 0 R/XYZ 307.84253 174.37653 0] +endobj +2063 0 obj +[2160 0 R/XYZ 307.84253 161.37122 0] +endobj +2064 0 obj +[2162 0 R/XYZ 42.519684 160.5589 0] +endobj +2065 0 obj +[2164 0 R/XYZ 307.84253 464.97433 0] +endobj +2066 0 obj +[2154 0 R/XYZ 42.519684 645.79816 0] +endobj +2067 0 obj +[2154 0 R/XYZ 42.519684 317.36127 0] +endobj +2068 0 obj +[2154 0 R/XYZ 307.84253 449.73853 0] +endobj +2069 0 obj +[2154 0 R/XYZ 42.519684 663.37537 0] +endobj +2070 0 obj +[2154 0 R/XYZ 307.84253 237.1167 0] +endobj +2071 0 obj +[2155 0 R/XYZ 42.519684 381.68073 0] +endobj +2072 0 obj +[2156 0 R/XYZ 42.519684 621.0886 0] +endobj +2073 0 obj +[2156 0 R/XYZ 307.84253 263.01904 0] +endobj +2074 0 obj +[2156 0 R/XYZ 42.519684 241.6098 0] +endobj +2075 0 obj +[2157 0 R/XYZ 42.519684 607.1368 0] +endobj +2076 0 obj +[2157 0 R/XYZ 42.519684 233.38953 0] +endobj +2077 0 obj +[2157 0 R/XYZ 307.84253 402.47455 0] +endobj +2078 0 obj +[2158 0 R/XYZ 42.519684 521.60394 0] +endobj +2079 0 obj +[2158 0 R/XYZ 307.84253 552.4066 0] +endobj +2080 0 obj +[2158 0 R/XYZ 307.84253 371.3461 0] +endobj +2081 0 obj +[2157 0 R/XYZ 307.84253 420.05176 0] +endobj +2082 0 obj +[2158 0 R/XYZ 307.84253 176.70361 0] +endobj +2083 0 obj +[2159 0 R/XYZ 42.519684 691.17773 0] +endobj +2084 0 obj +[2159 0 R/XYZ 42.519684 443.69513 0] +endobj +2085 0 obj +[2159 0 R/XYZ 42.519684 196.49994 0] +endobj +2086 0 obj +[2158 0 R/XYZ 307.84253 194.28082 0] +endobj +2087 0 obj +[2159 0 R/XYZ 307.84253 705.43286 0] +endobj +2088 0 obj +[2160 0 R/XYZ 42.519684 513.97473 0] +endobj +2089 0 obj +[2160 0 R/XYZ 42.519684 125.73596 0] +endobj +2090 0 obj +[2159 0 R/XYZ 307.84253 723.0101 0] +endobj +2091 0 obj +[2160 0 R/XYZ 307.84253 143.794 0] +endobj +2092 0 obj +[2162 0 R/XYZ 42.519684 544.00037 0] +endobj +2093 0 obj +[2161 0 R/XYZ 42.519684 169.21692 0] +endobj +2094 0 obj +[2162 0 R/XYZ 307.84253 215.04877 0] +endobj +2095 0 obj +[2163 0 R/XYZ 42.519684 514.10645 0] +endobj +2096 0 obj +[2163 0 R/XYZ 307.84253 707.60443 0] +endobj +2097 0 obj +[2164 0 R/XYZ 42.519684 275.229 0] +endobj +2098 0 obj +[2163 0 R/XYZ 42.519684 531.6836 0] +endobj +2099 0 obj +[2164 0 R/XYZ 307.84253 447.39713 0] +endobj +2100 0 obj +[2165 0 R/XYZ 42.519684 423.4968 0] +endobj +2101 0 obj +[2165 0 R/XYZ 42.519684 135.3252 0] +endobj +2102 0 obj +[2166 0 R/XYZ 42.519684 707.60443 0] +endobj +2103 0 obj +[2166 0 R/XYZ 42.519684 241.54474 0] +endobj +2104 0 obj +[2165 0 R/XYZ 42.519684 152.9024 0] +endobj +2105 0 obj +[2166 0 R/XYZ 307.84253 131.56372 0] +endobj +2106 0 obj +[2167 0 R/XYZ 42.519684 729.88007 0] +endobj +2107 0 obj +[2167 0 R/XYZ 42.519684 537.1217 0] +endobj +2108 0 obj +[2167 0 R/XYZ 42.519684 432.31622 0] +endobj +2109 0 obj +[2167 0 R/XYZ 42.519684 245.5357 0] +endobj +2110 0 obj +[2166 0 R/XYZ 307.84253 314.79883 0] +endobj +2111 0 obj +[2167 0 R/XYZ 307.84253 723.0101 0] +endobj +2112 0 obj +[2167 0 R/XYZ 307.84253 517.66907 0] +endobj +2113 0 obj +[2167 0 R/XYZ 307.84253 322.86462 0] +endobj +2114 0 obj +<>/Font<>>> +endobj +2115 0 obj +<>/Font<>>> +endobj +2116 0 obj +<>/Font<>>> +endobj +2117 0 obj +<>/Font<>>> +endobj +2118 0 obj +<>/Font<>>> +endobj +2119 0 obj +<>/Font<>>> +endobj +2120 0 obj +<>/Font<>>> +endobj +2121 0 obj +<>/Font<>>> +endobj +2122 0 obj +<>/Font<>>> +endobj +2123 0 obj +<>/Font<>>> +endobj +2124 0 obj +<>/Font<>>> +endobj +2125 0 obj +<>/Font<>>> +endobj +2126 0 obj +<>/Font<>>> +endobj +2127 0 obj +<>/Font<>>> +endobj +2128 0 obj +<>/Font<>>> +endobj +2129 0 obj +<>/Font<>>> +endobj +2130 0 obj +<> +endobj +2131 0 obj +<>/FontDescriptor 2132 0 R/DW 0/W[0 0 280 1 1 786 2 2 559 3 3 474 4 4 639 5 5 527 6 6 454 7 7 319 8 8 383 9 9 869 10 10 800 11 11 607 12 12 447 13 13 756 14 14 639 15 15 607 16 16 319 17 17 575 18 18 958 19 19 639 20 20 510.99997 21 21 319 22 22 575 23 23 1189 24 26 639 27 27 831 28 28 575 29 29 864 30 30 319 31 31 639 32 32 319 33 33 607 34 34 351 35 36 575 37 37 351 38 38 607 39 39 575 40 40 671 41 43 575 44 44 639 45 45 831 46 46 863 47 47 1092 48 48 383 49 49 882 50 50 958 51 51 436 52 52 797 53 53 510.99997 54 55 575 56 56 818 57 57 904 58 58 869 59 59 575 60 60 692 61 62 447]>> +endobj +2132 0 obj +<> +endobj +2133 0 obj +<> +endobj +2134 0 obj +<>/FontDescriptor 2135 0 R/DW 0/W[0 0 500 1 1 750 2 2 392 3 3 444 4 4 556 5 5 278 6 6 389 7 7 444 8 8 556 9 9 333 10 10 500 11 12 556 13 13 833 14 14 556 15 15 278 16 16 500 17 17 306 18 18 722 19 19 681 20 20 394 21 22 528 23 24 278 25 25 500 26 26 556 27 27 528 28 28 556 29 29 764 30 30 653 31 31 278 32 32 361 33 33 1000 34 34 556 35 35 625 36 36 500 37 37 514 38 38 778 39 39 681 40 40 500 41 41 333 42 42 500 43 43 722 44 44 778 45 45 528 46 46 444 47 47 528 48 48 750 49 49 722 50 50 278 51 51 686 52 52 750 53 53 278 54 54 500 55 55 917 56 57 500 58 58 389 59 59 333 60 60 389 61 61 750 62 62 583 63 64 472 65 65 785 66 66 736 67 67 750 68 68 500 69 69 306 70 70 500 71 71 556 72 72 708 73 73 1000 74 74 778 75 75 500 76 76 1028 77 77 500 78 78 750 79 79 472 80 80 611 81 81 750 82 82 584 83 83 500 84 84 833 85 86 778 87 87 278 88 90 778 91 91 750 92 92 833]>> +endobj +2135 0 obj +<> +endobj +2136 0 obj +<> +endobj +2137 0 obj +<>/FontDescriptor 2138 0 R/DW 0/CIDToGIDMap/Identity/W[0 99 602.0508]>> +endobj +2138 0 obj +<> +endobj +2139 0 obj +<> +endobj +2140 0 obj +<>/FontDescriptor 2141 0 R/DW 0/CIDToGIDMap/Identity/W[0 23 602.0508]>> +endobj +2141 0 obj +<> +endobj +2142 0 obj +<> +endobj +2143 0 obj +<>/FontDescriptor 2144 0 R/DW 0/W[0 0 280 1 1 591 2 2 356 3 4 591 5 5 796 6 6 591 7 7 532 8 8 414 9 9 650 10 10 591 11 11 944 12 12 356 13 13 532 14 14 501.99997 15 15 621 16 16 297 17 17 487 18 18 532 19 20 591 21 21 414 22 22 400 23 24 591 25 25 385 26 26 532]>> +endobj +2144 0 obj +<> +endobj +2145 0 obj +<> +endobj +2146 0 obj +<>/FontDescriptor 2147 0 R/DW 0/W[0 0 280 1 1 332 2 3 510.99997 4 4 460 5 5 510.99997 6 6 422 7 7 307 8 8 358 9 9 460 10 10 464 11 11 460 12 12 537 13 13 358 14 14 562 15 15 256 16 16 486 17 17 460 18 18 307 19 19 562 20 20 818 21 21 307 22 22 678 23 23 755 24 24 653 25 25 510.99997 26 26 409 27 27 510.99997 28 28 460 29 30 307 31 32 460 33 33 743 34 34 767 35 35 729 36 36 769 37 38 743 39 39 678 40 40 562 41 41 386 42 42 716]>> +endobj +2147 0 obj +<> +endobj +2148 0 obj +<> +endobj +2149 0 obj +<>/FontDescriptor 2150 0 R/DW 0/W[0 0 500 1 1 944 2 2 750 3 3 778 4 4 759 5 5 778]>> +endobj +2150 0 obj +<> +endobj +2151 0 obj +<> +endobj +2152 0 obj +<>/FontDescriptor 2153 0 R/DW 0/W[0 0 500 1 1 249 2 2 204 3 3 527]>> +endobj +2153 0 obj +<> +endobj +2154 0 obj +<> +endobj +2155 0 obj +<> +endobj +2156 0 obj +<> +endobj +2157 0 obj +<> +endobj +2158 0 obj +<> +endobj +2159 0 obj +<> +endobj +2160 0 obj +<> +endobj +2161 0 obj +<> +endobj +2162 0 obj +<> +endobj +2163 0 obj +<> +endobj +2164 0 obj +<> +endobj +2165 0 obj +<> +endobj +2166 0 obj +<> +endobj +2167 0 obj +<> +endobj +2168 0 obj +<> +endobj +2169 0 obj +<> +endobj +2170 0 obj +<> stream -xœûÿl{ +xœûÿ þ#ãø endstream endobj -433 0 obj -<> +2171 0 obj +<> stream -xœm”M�¢@†ïüŠÚ‰sp�A�1Q”ÄÃ|dœìž‘.]iÿý†~K�lö æé®î·ž†Öýñqœnt}âiøÓ§Oîê¡-xš¾æ�㺻º*6ý³f}Ÿí–Ô´uÑqOéaw0eï¸îÁ×Aó½æ%[¾”æY0fP:t}]9®ûUöW^Òd{¢ƒfÓ—ý�üÇuqÛ•µYRà¸îÞè´®Ææ:Ç“ ò>Úº8rOçÒèV’è4æ:�"]½�ý.ª¼±‹�·®çê`Î5…¨ÒC#•DDÞ'_Ê®oo4±�½�æ3fÞ[Ími.4¹7ûmò84Í•Ç&É·£l´ýõFù·¼bòDø1*–<‡¾n ËÞï×Zß!@‹E­¹kò‚ÛÜ\ØYù¾ï¯i•eY¶ÿ™�æXv:òÖ–kZùþÌ_[R–âZJ(Â\š�f •!(±n@sKJ–"IØ ]öÜb—9(ÉÜë$o�ô=(Cel)À÷ã~q¿~³$~Hà— ë~q -‚_Œs à—HübœD¿Dà7“9ñƒQ¿XÄ/~ŽÀ1ÞZRpŒp6 -Ž!RzUp ±‹‚cˆÎCô£àÂ_É3„¿‚c(yâ(ypŒdO8F’G…ç¤äâô#I‡ŸBz?µ°/±¼­ãë<ÞÚÇý)†¶eÓÛKk/Ëx3JÃ�ÛßÔ͸Ê~ì߯ýh¤÷ì/ÏœJÓ +xœm”ËŽâ0E÷ùŠšE$zÁÛ!„�xEbÑ5­™5Ä œ(�?Šo­Ñ,º®rꞪØá��ýpiË#Íψ>¹)»:çáúõPa¸)óîÊ®}c¶lïÑfFU]æ ·´Þmv®hƒ0ܹüÒY¾çü/eÅçÂ=ú´îš¶¼aøU´žÑ ä=Ñβk‹öFÑK†¿¸nŠÒÍHa¸uv]^{sM0’4ú¨Ë|Ï-� +gk©DǾn 4Ù"oEùg~=T~óþÖ´|ݹSIY¶«$“ˆhôÉç¢ië ¼±²|Bä½¶\îLƒ»ÙoÁ}WUîMRäWÙYÿ?êáßW¦‘?V…’ÔséëV±¼`ôûµ´w¡`1/-7Õ!çúàÎÌ£(Š4ϲ,[ôÿ‰'ÛŽ§üÏ¡öéjAó(G ¯´W‰‚2^¥*F,†C�¡d¨Ô+³„šx¥¥ÂÔ«X*,Q]Þ¹Â[&Pk(‰m°OêmQ} •!3ñJõ ˆ¢d +¾$ƒ¾ øð)ჟF>× +|ã +| \+ð¥ð©À—‚] ŸìŸ‘êÂ'>Á§×PàK’•FÄ5S‰ #º¯Áh@¥…´Ì}Ó`LVP2Ct_Ož$œœœFbà4Rœcq œ¢À9F48cLÇ€Q‹c ?FóóèŠk,JX“Ç7dÀ›¢3Ffйᕽà�áËÈ7 #ß,X�°b¬1æbÀª¥‚ÌtꦜÀþˆö7ÑãNÈ»ºf×ú‹È_ýi/?n´ª¬ú]þç¯Âû­Ú«÷ì/ŒYt# endstream endobj -434 0 obj -<> +2172 0 obj +<> stream -xœ�XyXW¶ï¦­ª‹�FºhœDS Šâ†à6IŒÆ\â£QÔVAA!( û¢Ð -�Ý}šfß”M£€´,"”[\bb\�“èh̨Œ1†L£ß)rÉ›÷5./cæ½ïýSõõ½·Ïý�SçüÎï^¹¬_?™\.w\?ká8¯™›ƒl~Ò i08Òër¡¿LðôéÀ þõ|ïáÁ\Öà~Û™×e2¹³“ííüºLö¯9 ±ýXíàa{-u&SÈ嬲vFPÄúàyAÁá1¡1‰#f�tï3î-¯ñ>ãÿì6#<&"<4ÚmYtÄæˆ-¡1¡ÑcÝ–…„F»ÅGD…¹…F»EoŒr‹  -Žr‹ v›óÁÒen~á1n B7‡G»yy¹¹E»…ÄÄDNööމÝ46"j“÷ƈð˜hïÍOE{Ûþçå·xÑ2¯ófù.Zê;6&!ÆmcD”[PpL`èæè±2™�L.óé¿ÔºBé½|\[YPÈ -ýû-‹p°Ïv诈ýë-³93Ëœ›ëà€ì+bΑâœBK^V¦ƒ£ÙA©�Ée2™lœ-Š.f³-j¯Ëd²©¶ ¸ªdãl{ô“m�=‘ûË¿°›g‡ -ÅE…¢»ß–~"3ˆIeþÊz²©œ3×@�Ýä‚ý,{±ÿàþ‡_ {å’Ãh‡ëŽ›Ï*§(ãœú;¥ 2 Íy°s²*RÕÍ�çßçvIu¹¡ö¡”8ÅróZð¸(GOŒ�è¥è –RÕh7âot²Ð;I‹²Þ*Ó²�–Ÿ Zá'h4‘Ê0}9Ô ù -í5ÔËu6¼CG ô5<ŽƒXÓfi”ïé­[D\\‹c¬ªýhÿ:ãDdù8éÄ>uU -¤‹ÌP UøŽÙ—èÈ÷©rׄu ûkë*�p0¡Ô�ùQN�¹È× �Ó4¡/Î6ÐÜ�@‡Áh�ø%ܱeïÂÇm†5ÿޱE°OC× §æÅÑÞ)QþŽ -€2_rÂB”ÔÃr»Ä«ê ʨ¬íQÉ×K‹¤~ê�?8°Bð… +c@†á/ÑÉi e.äB�†o¡ü´ª,L#\«�ˆqwµ•?CBRøúºøÂ�ºÊf�²Òæ´¤°â1/^Õˆ.˜�,.ýä*r Ó¡IÒc†o¥¾/ŒÓÜʦ9¡Ã‡L îôOß�ÁšÏàØ¾Új²ŠCy¿”Äõ˦U #\…cpº¦éÀ± ÎAsØÞp¢ôÑ7ã‚zœlUÕ¡ ¦=Zuœ,íD{uYdI`ÌÐ'Ç­~?l>ênÂWOjð »·ök V}å‰�Öåe~@èĉÔ™¾öó(œ¤Áñ­OJ5¼4_SÓ46³É|¤²¾¦¹¹è´‡L:ÐB”ôM½UT§j¼6ûæˆ�×øiº©¡5¾!䣠a9S‹7æÎ/.<„r üï¡1<{™y›9£È>È®ÒÜâÊ!?QØFJØZ’³¶ @†ŠÈФæ�Fý�í„¿nÕ�ïÁtoˆwØz=µ5£$¶‰}Œ†Ê¹ØY&äB&Tî&5¥L{Ø)¨R‹c‡k”ô þ }ä·ÑG�;$;ukHC@@HH@@CHkkCC«@[úýaLIS•˜"å=_¸Š£8šßÃ0ºšTH‚Ø•–žH¨¹÷Œ~›!m�8-‹ö¦ ÎÂa9Åš¬¢舕ð)>@Ë4ZA+4Ã}h0zÓÕ4�ÅZF‰ó-G¥›¢üˆˆé8ÓDE�QòSÇ¡þ·IGõ"0ùÈÜF 7zoR/-{Ú‚ -8çpÚV¤^ÒÍo8 -oP–(u b·(—¾ugst -®F{teŠKrrïÑÛàa-SEÆ¢ô]z0îÚEÞð‰\3€H ÔÎÚk€>tH=™¤„M°h¿·t@d²s4�™›G:QŽvÔ5@‹FI§é›ñ_¢ÖªêFV+b‘¿.©p€úc­?7',:b \¬ð9þŽKµ,ßbò\5b„�lú–îçÜîÅ^¾i;w]£DDl± �µ†à$Qѳ ÷«µl,"ÆÓ…@èl8ÃþX0C#â!-;cgÀ(aà'AÍÌ=Ô‘ˆ°õ¿Éme½w¨ @Û£FY âS’@YÊvc²|½D]E?,âÐä)Œ†åïoõ'€2QLÿD�*ˆ~Ì üîñÏUÆMhÕ3QêJã¤a¢\º!¹«E.ÊnŠâcûå i¶83vJ¨…òXsÚö (kíEÀY½LQT1C)X -* NF‰É)ÉʽÄ–â6üyñÿ@Õ·èC•(ã[°ÉU\(yrüu:Ó÷|õô�UZ¾øK©ëß*A°¥½F o–x«ÜF*¹è¢�š0\�¯ywÑ7è›>Ãè«tÀ}O|§ÜëB'�ºª'•Qžò€²{´ÁÉÒCå‡�T‡#p$yoHe(,ƒÕD‰_òfd1Y¾á×·ŸºÛ¿ÅCð�+·®‰èm †‰ðþÚ0;ËþÊïƒÞúƒ~ïø…C{ãßyôÿs’QJ_Çõ åƒû™{xµÈEšBŒk!Â& ÑrÕp è�Þ»”úJk™`ƒ@L� ÑÞåZÜé`úø�ÆôßäLNrÖŽj¨†¬‚œ=gJËqfïò—F“¥˜œRsf­¥"³ö±¥ä‹ž‚,Êp#²üí—RÒIl01áC–ïü]{é=ñÿJÈJº\Dw«$ÚUO�ÅltáoK�­ÆW}~ ÂtX¾9&‘Àqæwöƒ5½æ—ì/bZCŽîh‚£~ù;j4ÔÅ•ºû®˜/l-¬=­+3” á Ül.)!üíæ–S9{�\9»ˆªú(Z”:ú<%(ãc»«è×3ˆã¿üƒWN6àz«äò,4OP†…¿Ãíõ&Ì„•áÑñ°ý÷Àƒ4½�"·§¼´lOy^Úñ:€àÈîgxA ˶…¯_£ _:½c7Á,nñg•Ô|‰RGµVéQ¾9ųBŽ6nyQÈ Bª&#•rÔDy¬z©†÷ºÇ¬fbW‡Í™ ÛÁhN³ägU•@©�Þžä/®ÿT¸Ÿ\ØŽ`˜´†É-±ôÕ·›¾Yrj–Û¤ÅLtVHQh¯.Ù;„D03R¶.X�à„j=ÛϺ¢ZÓ¥†bCb²1v’ç -ë.%9K蜔£„¾úË>ˆ$WPbèSvêiïs*ƸÅ d´Í©üã&� ½É@†Ð|ÊbýY›c¬� „ÀÌd3Ñžç*W‚võ­šB— Á¾EN*·€ö< Wo&7éyÎç–’'Xô„ýûzà&»8+ï¼­t¢ô_­ª‹�£DÉ®sòqþ¡t ש“oû”:¬–Ù�¿ô|­]øßq ºB«èà¬h0>—øîS�‘&“nY»ŽYÒ>?5Ð10k4 �ùõk*c�¯:”Žòèû ¥†½q„¿±{[õVX«·ú …i@ÇT¤áHSdÇ>×óŸjŽ|ÈÌÌË%ííÌåUõ¶üë2]Ï$a#÷‘ Ëkmµû\ʾž¾û?‚p)÷–ÿò™ïªO ü`À^šÞιݎý‡P_AûM2•SÒ -ËQì1Ò*G{”u “Bš'õ¨�lj›:’Ê)# Ÿ®ýšvÿAÃv#'RÇ.í¾É¯²!r�Ûti �@jÖU¶Ê�û„ƒÍº­¾6¢ì:)zR¤uëÆ£S…X’¡Ýذb�P%Lûk<_ìçdA§gÌÐ�h·Ÿ­ç¾Ã^¤N]rße• y�eÊ1½ ƒ^mQŒ‘Û`#$寕¥wºù'î»Ë�„ZØ«/Üõ\áàŠgTÀß�¾s“¸0d¥ì!t*nB×.¦¤0?÷ -‘‹4DÁV ”³%vX yi„.¡#AZÃðÿ@6t|æÏ¨ÊË©(!]TÅä§î‰Ù™)‚–;˜yÏœ Ä -‡L‡ŒD‰¥}dÖw¾’¢§¢çü»#é(TÓUBïÌç',i‹+q:á‡êiV•§.[»z΄äE¶“�Ô�ÃÙËp'á¸oƒÞK”qÒ~t®‹W]SE�ùNéLÏŸÔ4€�꙽=+.Ë”o, Z`øÇ!4W¦à›ââ¬ÌœÛP -§²‰± q‰±£i¿YtÐÄ÷,äì³/ í³Ëó¯Tžoh!è�s™‚BÈ„†>…Ñ' §*ÐR«þ"ôÜ’5›C6ì>)ÔAE©Å’Fåó—Ç%ê3 Г…;òv?¾‹A kÅn+®jRC¡³æKU¨F·áש£ K!p)çÙÊÖÜÝûÄÓÖÏ  ªb/mÊHŒ„-$´*¢©i_Mã…ÀÏm=÷­I4[€)ùo·ø‰Ó¾‰¾ -5?~‹¯iøÇtp¦z8Ì.¬€È¦´=ÉupÊ:ŸÏ�`º§Fè,Å‹ªsè�~(hEþ¡T‚Qj-kˆßùîöt�~:$¡ž¬Øtªµ­žð]È~wQ„G¹¡¡îÔÑká¸÷ŽmÛW×¼÷„§÷lË:°¯±Ð -w¡}L¿à9¾³ ]C™”T0B!¿ø´Q–j|§Œ`]Ù‚›ëßñÆÆuïà BQæo³8ñö}´Óð7†â55¬Œ ]»…P¯¿1ÉùáeÐ@®}ŸÛ"Ü=è§á;*{9õ¤ÑÉK<—œº%À­ÓG­5S™¶­�q°Š(tVé�UÞ†.˜`¤5ø•ºÓ„iTOõ&ºsä3MF=¦gbÒÏu)RÃRðI_±}ì‚í[`,Ý›r}ç)ø'œ�¯àRÞ©¢Ž«û€†–„òu¥ÁÞ"ÊQ.J+ãUx�)‹,ß%5¹Š£PÞ)rüÃQÒ[ì~cv„&Sê6Bé�Z†ïÂ*§ýl·% H3ܳ�ìø‡t®SSüœQÐ5VÓyÕŠov­·ªêî.DÅØã‰ŠÌ»¾wùîJéŒä®®JjX#l­a]áé‹+gõ€eSÁÖW¬¯‹!ü/ºˆý‡GøÇö¬UÍa)†™q°€øŸ™ŽZô½yþªÓ>Ÿ{héG‹a4ØúX†™ð¿T®ËÕ5BdíÎÉk�>i¬ô€37á*ˆQõÛöÜS™Yh&Æ ãNГ¨ÒÄ¡¦´|Ÿ††�Q¯…¹þ‘n\ikƒ+÷ïÃܵ¾ãcdzÕ›‚> ®ÔaU{û§ŸC‰€öl-2°P£D-ˆØfÅRQ…ž8OãÊ¡'ÿ¸G�‹Ôðã¤+ô•|ÂK(+¿ô)Ü ßO¼E' ½~/ø1ˆÅÉßÝÿICgâmõ;«Ž¨–â|¶«lÊ ÿx?½¥NêB=ºÛp9[" - õ�°tDù}zfüTTô ì;3¦ù%Ññt©‰ì:•Ã*XÃ!ØB^ì~•{Áz¶kƒ›¼5PJáϨ~ÄÇÒoè¬øÕê*R™Ô�2‘nåbAº(BÓ{khºTÃèªu}ý2ʶŧ,ÄšL†hƒ"€DÙ2è4Á8-"Fq‰�dNÌ ·˜Ì°—H¢™ÃêÞŸ˜üø¢ŒB¨€¼ÝYùë$j°áµ].X-¡Zà[�Ub¨N”~UÑKJ;m㸞\ŒWW¤›“„Ø ¦¤túu\4¦ÂûV|¦=—ú5üÎZPöùåK—ïžïÉàÑ´½”·$˜�U@jÌ™5þ!†=UU;ÁhLK'óÞcž˜[6ͤ^t$ ¤�èEGb¦#ƒNÈi”tÊ:’Цª³ Ù „ïŸÿY ºlýkê÷ppåI‡v¨Ø½Ûl†l(ÜaIƒ$ܘ˜D¨õŽ¡ À¨s=µß¼L¹sË�äX -û ë;ø-j@çÑN{gr>W v@`,MFu9D}�ŽºÒŸŽ£vTA¶m3™ t†<(…#‡ËJѽ«p’Í9p7ó¼L&·,}FÚ?/“ýë_¶£¤mÇH_«lÇÊr¹RU;×?lKÀBÿ€Ð¨à¨øqóÆ;Muá·©îSÿæ474*,48ÒiudØö°ÁQÁ‘“�VG:ņE„8G:Elð‹ ðwŠõˆpŠ + +pš¿fÕj'¯°Ð(§ÅÁ[B#œÜÜœœ"œ‚¢¢vΘ2%*:prXDà”ma¡Q‘S¶÷/Šœ"ýÏÍkÙÒÕn‹Îó\ºÊsrT\”Ó¶°'ÿ€(¿àí‘“e²2¹l ÿW­+—çᦊü%?hàê0[›,ÛA`ûŒ0è¨Åb6gdšsrlmQùŒ�}¢(»À’›™akg¶U)dr™L&›-Eq¨À,Eíy™L6K +Š£ƒl¶ôŽ�²­²'roù‡@…»âEE¹¢{àŽ�3‚Ifþ®tU&³ö,°�ˆYCºmVÛœ4{ÐÇÏ,~¦í™Ú†ÛVØ ³ËPMì¿’ªö¾¸¹>îPm]Å ŽÄ•2g×™ �|Rï7[ÌrÂëš“ +t,LÄ!ñÁmš+O.f;øü“?¥,/„ꃬš&NIŠðÞZßÊÊpjñi QÑ1–Vì¬OPFe�hƒ*T¨~kMÍ:Þ¶®�ZL†{ŸÎH-s9ü5\3½ì¥eÑÁÂ4À°ˆ0 ç±×ªQõ=Ä%„n©‹­ákê*š4ª +éТŠ+„ÜX‡ŠY¨äBÅGŽ»d4j@|Ìp-ÔóׇÓuìúÆuÙo¡.£^¤ÎôÙo&á8Í»pê@m5ÙÀ¢|`Rü–Õ³�ªÐ®Á)8w°±æ„�_  )¤2”¨ÜõM¸ø(ΰ:ÔáPL{¸¡�{,¦¢�z¤ó `L×ïŠÙ¸2dêlÂçÎhð¼²2i VcýémÖµû½€ÐiÓÆP{:üû 8]ƒS[ž”j8qWÓ4eF£ùDÅуMM…5@Ð2è0sQÑ—õVqD�CÃõ×Oa¶àw�ë_D'5´ÄÖö¯ ÉžU´-gQpù>”áî ¡Y«Í‰æô +  «Js›-ƒ¼x~/Á?.¼(({+ì:€ƒ­å;Ëš‚ô7wî†UÿI,‡9S œ!üFrKzq:ì ú( •³q�ºŸÏ� ¨ØG–2m!g¡H-NtѨèY@w|ÝåwÐ]�)âuKP½¯oP�¯o}PKK}} O›þiLE“U˜ ºòž… ,ÍëaÝÁdH€0Ø›¶'žPsïwŒ>Ñ�6HŒV‰6¦Þ†ã"°0Š4™…§Ñ+àê2 @Ë4XB 4ÁWPo ô&îQÓ8@%Ö2*\dio òîÁQ˜&(zŒ¢—:õ?O‡ª€ÉC&ÿŽr³÷uÓ*ÏYP—à ÎIEê&Þú”ÅÑ�ÿj>U•ìäâgŽÂë,�‰Ñ™¢âìœÏ�€0Å0ÆZ¦ +�…{öêÁ¸w/yÉm<²Mµ3ÐÐ^t§‹é0êÊ$ÄÂF  ýÆÒm�‰�ËÊÖ@FN.éD9 ŽþZ´µ0*:[ß„ÿ´V‡nTj$p7D¢~KëÍΉ [Wªx¬A�åÞ§.¸J«äšM®Æ�3�ÀÏè!ÖéËèëü§'/ÜШ�ëÌïC­Q8]Pôâ!µVMù°©t : ëÏ+¿Í÷�«ð˜V97Õw¿óq’§ÃA�jGØŽúŸåÒe½ŸS€¶ÇUý �JTRe7† ’;*RGÁ ÙÇk]ù‰°ve¸7”I@1ç/€ x`ƒò»í靖™ÍÓª§(¢Ò•ƈc¹xStV l„1ÔÄ]º€²CšGcz +¡„Z(‡ÏIW0Ä@•Ð^œ×kËFA”B¾%¿‚à ™ìâÌœ+@¤—öŸûº;|†îT…2®…%¢+ËÝ Òv/õAOßfU†+úHìú]%ðRÚkT¾ú&‘³Ê%PÉÁ¡ +±CÕ8|J}‰¾ì>–>G‡|劯âÌ/»p0O‡¨§•QŽr€²/y8 gJ�•?QÑ'àĮʠŠ`X ‰ +ïÿ1ÈÛQ‰~¨äêzµ?ÐÝÞÍcxwX·>Ü'öþKLƒ•›B$t–1ÜÕzË_ýËöËÇ*c_û5èÿu’‘€[ú¶Ÿ  p(wGìÄH5>çö€Žå=`}hd,lc¸ÎÃïÞN�-)+Ý_R–›Ö¾¦Žïþ5:Ô´°:Ü?t‹�.<Áÿ\Ê>‚™ì¡úS5çUÔ<‰ÊIß$n’K\á�ö +1mÔÅ»!…�“1=)|ñÆ8o TèÚö6 #ª5õPj(2Äï2Æ@*ù…2ïR’½‚ΟKYJès&ádüÛ$9|?ê=E¼[<•/¢^¸+Çd|F!¦: +ÔI¼ÁVB~8¯#¤îym别-W–Àyó¥2Ò[L5Z¶ÕòÚ¡ž@«�¨Ä‹bzFò‘Í=œZ`wš‚Œ› –@ …hÙj85:Bçô®¢žâ¦o&Àà¾à3!ÈB´wÙfg:’>þ�Ó¸çg9“½+3¥ª!3?»„ ‡¸=z×þat—ø€É.5gÔZÊ3* ˆTÖ¿ò2*Q†ÛPÉÝùCY{¯ˆ &¼¯üýŸþu÷÷•^_V£/*9­ô‚_˜_ÆpGé¿1ÿ*öﵯªÏñÜHÀ˜ÞÍ:݉þŽ?CÛ-2‹UUе:[E¹Òäð•˜õoIêþ€ŽáçÀÚíQñÚÿ=G4½æ?`)ÓÔšÒ 'üð4Y©³çºEünЦsº"0CÁ ÊÌæâbÂÝij>›] äêÛK©ƒF…ÁýÖÓÖ`QƦH %Ëk7é4`0èM2ŠæQ%}[Â/¥�ñ¿Œ]f¢½ÄV¬ìí[5“®…}‹¨ÜÚK€n½ƒ˜œ„_®4§”<ÁÂ'´ð÷c8Ÿ0YE™¹—€¨|íÅXÁáŽA/äµw_,ƵViˆM}c÷�~ìB]•BãÙ–“G ×…Ê/®ð� ;ú¨3µs[òÂòS‰êš*OópÖ¯$1³æ@C�îBÛ|˜^ó=_'Ô‡ú1IÉ`„X/ÎeUhÒ�èQ6ɯ ›8WÒÐÃñG5xî�êçµÉÃ3z ,�Ã[>}órÚu¸ W,ÿhý°ùݫ՗€\��š6¼‘ùÌ'è–¨Æít,Ž¢4žjè8º�nE':c1y‹~¿Õò,ZjÕ_Xá³=Èwë¡€3|”—Z,iT¾hmL¼>Ý`=I)HÉÝ÷ø.jø>^ëpgŸúÅ7pØh”q�%eê%NdéëzÓ^ê +ðˆùâÆoø¨H¯J"œ˜ ß™[‰M¸õxMuóÙÍWè:„Žñ§6¼ëí'´[Ê­Áü4·ºq0ÜãÙ¤¡öO2}´$vôÕAwÛ¯ž,÷ÑŸ +MZ®£Z«xQ�W"«xÊ’‘Æ¿²$�¬IO¦,5Q«þ@�Ëz�£62ÑCæ¿»ÁhN³äeVC=9y 8,4Îß[Øò.^>t�`ˆèÃä[úɳ\zïÛOÙsÚŸ@w*C'`¹ŸDÃý�.–ö[ +•°ÏX¹’ †ô:³ëç-ãwAZFbfYqVì#õá‡"v®|'ø3þîóO4\iÏóÿŠeu-x¦3N¡µC ŽÂç¨ Žç¾«p�·ޥck ×ùQÅ� p—Üœ}™:Ógf½ñªoíΆ“ÕuÇy¸¸¦`¯¹þpkn-|9oÀ›�”N‡�Öo1M馓1ô$>öó­J®å™ã½µ{ǎքúÌ�GŽæUWC39R»Ù/h§÷ �ËPÍãüßßÓ¨|©�'u^³âË][¬uw— br;îDEÆ]Ï»\w…x^tVW%Ôûð‰ 5l"ÜC]|¡_Å\ c`õ,ð„-å[ê¢÷ƒ.,Hÿ&Äîñ[�˜¹¡)$Éà‹‰÷ù9¨EÏ[—®ñp$í½ÇV^!| ÝL¸*6çè`?ägîËÎm‹îà‘ºŠfþ©ÇuOåwSüÂQH`}Á�™TBè, DÇ.¦¸ /ç*�Ýiˆ€p ”•’¿ +,†Ü4BWÐñ ú0Üý?U�­ížV#ÇÔTåf—“.êÀä%—Ä‚�¤ÆC¯e�d|iÎb…c¦cF ?” âúX¼�öT‰J®Klt& ¼S`¹ûÄW”‡ŒYa| ˜Lɉ„þл´ ×…ÍTNJ&Á=hÒ_J wŸâf5@À÷Õ[¿À�\‚Ÿm(»‡ƒ=Ib�ºe[ë,>V¤kwøÕ¯‚h TEGÐAšÿ€/Ûh·—$Ýÿx�îb‘ý"s?Ÿ ™¦lÓO¯5« Æ�‰° rbö'gºýûÅ ÷øZ¨Ôì%}ÕøÀú5©‹u¸€Ã_~ ßÄQÜ�b1ú¨‘�yŸŽãCa­ABðYöpz�ñtù™êcA²Æ&Ðí]½Vï ³amYx^ªÅd6Ù º]z’�‡¤J¾22srÛÎå7é€_õWj«‰„ÅY~÷“5£æ4“·|÷E&®K]ú·[Ã!šAÝèxêGýÐ�ŽÇ .C#«Q¡Qg�nÅZ+Ö +}–à1t{ pgz¾{¨®”Z’0¥˜’Òµ¡Ñ;`¤^O?a,HûÈ”µûÚêZß$Gμ�.:à ºœï¢E™’Kê³Y«å‘Ô£¾j¥2¬E¶Z¡6¸.âÀ†kÁ„;Ó}yo3�‹¾.ÑPA¯^/ÓWxJ°)qzƬ¥•Ú§'ä8î-ñg´Wüdu¨Lì@™@ÃÙhÐ�.‚Ð=½éñ £«ÖõáJ„äh¾£„h“)Ìi0B� ¹.@#œ€³Ðl B æøŒP‹É •DÌ,V÷>bòb Ó  r÷e欩@:›äåX-÷¡šá�t:MF�Ny^TàuôPwÒ‹X–>ݱ+އ¡«¢ç"~­Æ�tªé¾×ã·Uœ§Äõ8ã›êjV•'¯ Ù´qþ‹»–J.¡Ø�.Êàó¸vÏzïÜù0ùi?ý›¸ üMàýôÓ¤ÿ.ðzÿJàñ À#eyð¦Ö•RÛGY:!ˆÚý'8üŽÿ¤ú½ +¤½=ÃÔÛ’Ò¶Cè,¡GÈ+tÇ#öë›u·ù +8`8”HTt‘¦Ö>sG!º: +T)v R`·ƃøÀxØb!ZTövP¥–m¶<‚6hƒGÒÕ©p¾¾iûSÞvxÊÛ%¿xNI:ì—«©s—ò¿póÿ‡àãè!–;£“\)É”âÿÊÇÚ­ë7²LtÀŽT DåMâ@«ü”€7…¸7©µÊT—]Ké³@¦ÁŸ™Ü‚Œ È#»!žïU@2�'“Ý]kíò u½2&µ¯#'ÑEPÆ‹C”ˆwN}îý1V}ìý9wA¬Âej@ÙK†Ä»yÍZÛ +Éî'©’jL†çpeÑ•{ܣǥjøvúUúLáD”•½ÿÜ$ßL»M§ó½^¿–£¿g|ñÕ# õÀ;ê×6.™ÀSw,ÅEÊ®ý3}4ÜãCô¶:èP:†§‹ñ$®U–ƒÇ|ÉQ?X:¢ÂF��ƒñ‚8¨ì'à¨5ç¸öž»¯Ø Ã4ð‡0ÖH‰=Æ……8õ� +O чÀt˜^³á²ïù¤ëpp`ùÅãíM§oÿ'üøgäÿ3î{\œ—¿5Ñ)Ô•n¥q,�‚š£Ð�qú2ájw¾ž¤Šöi¸vôaU˜Ùoäô|ÞéA Œ�Ù +ëÀ_rqòþÉØkH7ÝÔ»‘‹‘ú½š‰}^ÍK}^Í7,Öü,grã3ÓC AÁ‚eâ#&»(3ï†Ô®ë¬â=«ü$Å8ÉZ<ˆ«;M˜FõTo¢©ãÇ™é.Ôãž Løž§C Õ° +Ü÷¬Û=yñî«*“n¤ž…ÂyøÞÏ=[Øq-ÿ�ãÐW¶¹t8Ã+Dõ?¿A ¾#(z†õùÄi^ t*�dŒØ�#åf•ÃØ . ]ÀÓk¿Æªèl”u*Dš¬Î2dzhÑ»~84üïÉßÀW€ëÏà 8ûö™Í�)–4H¿mñ „ºÓ)Qt18µ?J/@Ù{ Ê€äæ[ +4Òƒõ� +¼‡5 ý›8@ûùBdݯÆA¤@é`,Ý…tè1ê ô ë½é tU�ÄD“ ÒA—oÈ…R8q|)ºã”*\ Oˆêÿ‚àŽ endstream endobj -435 0 obj +2173 0 obj <> stream -xœûÿþAÆ -ó +xœûÿ~M· î endstream endobj -436 0 obj -<> +2174 0 obj +<> stream -xœm•MOÛ@†ïþÓƒ%8Ðì§MP ,å@Aµçàݤ–ÈÚòÇ!ÿ¾Ú}'IUõèõŒ÷�gfXçßÞ¶w�®ýôwú» w?´S_û»ÕË®Ëò|ÝÖÓчñ‡÷λstx ®oëÁ�´Ú¬7¡³<ß„úkrþœó¿”'hÂ5!zÐjÆö˜åùG3~ùºÁJ5ÑÆù06ã‰Äm–ç?}?4mx ™åùsp«ö‹²{Ðì­oë­iß׳}FßL*rM=²J¿ëã®K/oOÃè�›°oI#ËMgÍÞý¡ÆþD7©°[r~�Èkï|߄ݜ‹ý+¸�ºîËÇ"I¤§>¸ôwá쎞f |yÊ”$¯�>N�çf¿^Zw%Ö­óC·«}¿ Ÿ-„bI‹ªªªetü'~oñÚç¾þ½ëSº\ÒBˆÂ,“RPJ'Uj(ƒØÊB•PÔ3T™”P÷ˆPs¨ -ê -ê ŠkY!ój�û=/i¡„äÌ*Å4N‘±Bh¸Kði¸Kð©5ø4ΔàÓ`—à³| -óI(ð•¨L‚¯dæcðè Ÿ~„b¾ó±{ä¢dwð)d*ð Uà+1>ý¾¢¸LM�Ñ o -Œ•+0*ÌP�Ñræy†—é(æd'pT¯xŽè–§á -À©Ð;NÅ1pZtKƒÓ€Zó9NƒNjpœ©™Ð`TØ7 Fç€Ñp ŒUkððiÞSöŸ­f>ôM¯¯=G_5ï*sDFÇ•aFl £ÅdMdTq¡’â]££…ƒ±WO¸ÞWÔk˜“ϧ§§ELäTq’â}ÅÄ 85×ÃsD æhÙ�ç* F‹ÊmdTrŽZ,Ï{n™½±|ߤN^6Øòÿå<]o|�Å‹.Þç—›µžúÞ‡1]çé�wfüå»Ðµ]|+ý¤ÊùÛÕkõÊý³Ó +xœm•IoÛ0…ïúìA@rH-nR¼ ð! â =;âØ`S‚–ƒÿ}A¾±=ÄÁÓ�œ÷ÍpI¼oæ®ù¢ý3Ô7cWÑÃòe×&iºjªñD~x%rä.ÑþI´]Sõ4ˆåfµñõ�¤éÆWÇÑÑeÌÿ†,èPûÛ€�C,Ç~hNIš~ÖÑžÄ>ˆèIlù¡Î"»OÒôu}Ýø'!“4]{·lNÁ\ŸL8‡˜¼wMµ¥Aìkï:Î$¾BÞD*áêj`«Ó®�“·ç~ ÓÆï¡1Ê�-�BˆÉê~èÎâ.»Žöˆ¼uŽºÚÄÝÅì·àvlÛ#“"‹_É»øà_w'¾~eJ!oŸ>Ï-ñ“ß/�» ‹Uã¨owu; ä9˲l&žË²,g!ã?ñ©Â´¯}õg×Åár&ž³ÌÈYT*ªBAé¨r e ¡,Ô*Ç<U f¡c5�JePsŒäì ¨5ÔŠ×\A­ ÖX“W)[F%C²,/¡À—çPÌW@�Ïb ¾´|h%óñ*àÓs(ð)ξœ30ê)ÁW f’ùòk% ˆ% ç£Bm æ©À¨@T`´àP`4p§À¨9FF³€£A×58÷½P`ÔœŒ– z¡ÀW ÛŠ{ÈÙ¹‡È®™´š÷(\kðiÉ{4¿î> F‹ºéÀ¨B[¢£æ3‚C3#:®Á¨±74ÇÀ8çg£EÝôúÖcÎ8Uh~P&pªÐø¨˜{ÇpQsÃ}„F…º>‹¨¤aFTÙ7œœ†× œJNY1'jkÀ©ÁbÀiÙ85¨ 8-»C/5ÜYôÒÂ�£Å< Æ|zuoÁ©‘Å2'Xp®8Æœ¨�gF•’ ²‘1Œ¨ÀhÐ=Ëû•½~;“|.É;¾-Ãu^�ëý]�]G~ˆ�F¼¬ÃÍ\{º¾>mÓ†Yñ/>[—0¨·ò/5#Çé endstream endobj -437 0 obj -<> +2175 0 obj +<> stream -xœ�zwTT×·ÿ`æ^QÇÈ8¶è½{ìiš¨c�=ö. :R”2ô™Ù3C¯ÒaPQQl±¡F�k�=Æ� Ösñ û·.æû}oýÞ[o½õøã²fæ”}÷Ùç³÷þì­ NND¡P|0Í+tÌÔ!ƒ|çµ2Dç¾Vþn¼ô¡ÔZ RW…àLD€÷ÏV\–úve•¥‹S­²+! -sùù¤mWB[}$¸Üª‡üïL«žÄÑ�(ˆŠ¨Âèþ^“Vxùû‡÷ã®óòîëúÉà!_ødð'_¸Žö ö÷ó r�ä¯ó_ãì4ÐÕy¶�o�k¨ÿÚÕ®¾A®k½t^îA^+\CüVx­u öñr�0cʧýÆ�s0À5ÈËËÕ'88 è«AƒBCC®ô è¿vå �¯§—_�WР•ºÞî�}‚×èº�÷÷ æéìëïçê<Ö7(x­¯GHó§ÿø¾ÏØq}å�ÝW¬ðZáêëç:Ë˳ù‡¡®þÞ®¦Í 0Ðu–——ë”÷û¸zûê¼\ƒ}܃]=ý×x¹†úû¸:{¹®øÏ{ø{7Ëïíï4�YO³%C»Æ®Ú�iL>õËJ-Q ÎN³ý[µHmå ­ZÚ�K̳ÅbN³XZµºÐ²¼ÖšjIµZ­)­Z;!„Ì•�®�À,UWBÈWòI¤º�¹ò‘Š�¼®;MpúÛé¹r¾ò´j¬j·±Å‡-úµ`Γœq~ÒrO+ÏV·ZûµYûÁˆ¶¤­®miÛ».K5Žš¹í>j·CÛN{¦}¿öÇÛÿÞar‡¿;VvêÑéHçÎ�¯}8¸Ka—g]÷^¢³¸Id®!®?t«ùÈôÑ›îºîïzÜêéÑó^¯-½­½/÷ñës½oîÇ£>>ÙoJ¿Åý6õÿ Zÿ«–Ltpp÷Á™ûɺOÀAiÒAÅÁƒ´î ãÁöR©º©‡ê`Ó­4‰Ö5MâÔõ%z»Ô©„¦„ºTߢ/hP£^ûv�5›ùIÉœ&¨˜e¬v½ê»˜¬�¢$™¸xÖE¢.›;”ã-²`nÕΥŋ€gíú3œEÄØ‡�,ï�ˆyß Y{Y!h¤•h½!0/¨lÕŽˆ¸ 6ó±â=%ûV„ÃP»¦|!Ïâ¹ø�eF¯fî16ª+¦n¥ŠŠÛ4¶ÞQêH9íêA#cƒÀ‚rB -Ü·†…|ÝÁm´Xó)÷íM¦bêÏ]Yk&¼H9êü=U§RcÅxHJJŠæÙzFÛ�ÃÚ{Š“Æ¥ÓŽ¿Tï”wK�)•>-rÙr`Q9µÚ‚h~¤¿KcµŒã"�I1ÑÆÔ¬¼Ä§Lñåê•+𨫌E2PzƒøìQ,’Žœ -‰¢CÔÞ^sɨb‚øøt’îË býçn0 &z}Ã>€Eà»1 ñÎÙɈ×q5bãù(Déú^Ä×b ðˆoVÌGlº³‰£O.’¶ÆŠTä�´ÅSV$Ždh¦ ¹�HÒ» ¾}ìˆÄAwñ•ömúJ$-¶F¼óÉ$­½_ >®ß„Äu™�d`LŸ$]¢®Â9Øg‡“°oUùò\Ä{ m�ðçó÷µy†D5x…{¯y´ªpÍØu[.Ñv°*¢K7ØÊ‘8îu³"á&ÔŸšbŠÕMØ%eev(íöÂåÊËqõšŸ¶´·s?æ#!Ý�ôØS'ÒjIÿ$7=UXÝìJM-uÍ‚&g%âõ&U":¾9*è¸`Xn]¼,0ÿD:©Õ䨋KìÕ���ë}Öæù׈jæQFgœ¼UJ¡T_â²épè/3*é²_¶^v\óX/�“hWÂò°@^ó\Xä»q1ô…)ßÀhX^¼ºÌŸ×ü¡_¿6:t0g«.ÃÀkþ¬CÜq(ñÁ¨(Äg£“/ÿðcÉü¤»Óè‡H¦¸!™·³ÉðÂ*$ãÜç°#Ò\:ÿÀ—æ‘ÀOWiþ(ÖÚ·‹�Ÿ–Ÿ™eÛ°9r7<„Kõp¶Fn °!i#\ÿçÑ�®AÒá·žHzþ±R ÕÀÇâca¿!7:·(3w£€ÄåègˆÏ£ÿB"Œ<‹ø«ÞE`°O´0?.0t]DÔúh�_q—ù8âË áH&?ðD2¨S�‡ÒlEʹ…ٛɷa€Ä¹À€XÓæW@¼ÔЛ×Ü«£MÎÚEÓf3Å�»N -°kå®tžòªïÿŠûZT”Ï×UæB.P¯ š½ÒzTÛ°xÇ a4,^4›‡—JÍUÖMºPž1#þµ� þ2“ˆ¬ˆs×›& -:®ÎZË nr€·s“¢‘8˜‘ô -Z/ÒiœfoÃ…õû6!þV厸gv‘À’8Ä«Cê¢ß�s›$¯a>jÙpÙ˜câíáÜœ$-ÛôCÒ±ágQÍ.¨Ù“f[¤)¥ -êpîÑoŽtü%-½IUvÖ‚¶àîÜ¿³$'„$S` èø˜äU;6m,°íô¬\0ú«ïz Œ¡»Æ®s+¢ „�ª ¾·î�=pØTiäí‘Ü}ÚQc“–Ȱ´ù¶#µæj§Ã’Úðz{ÒV8gÒwUT#Ñ~yIçï·"éy¢ ’Nã¾Û»`_p�_…oº;Læar’�^¾p©Çtài ]¬¥íz¿b­YÛ�™‚µeÚýh Úºá)Õj–Ù|ú—Wõ¯~Óì}õ³Ö#Æ8AÐq6ØÛ,á)C‘¬Ö�!å�x‰ƒÛ�iNÏ£½®æ–B -X!Å ¡|@qDÉÆŠÌêêUÂdðœ6“ê ŸŸøþü² ɫ̉°zòj–c“Ú•W¹<¿¸¶œv.×l¡'©Z{¢|7ñΡºˆ[›¾A´�}·öç³-ç4g¿(QýnÝùÐü”/Wi¶°É´ûjNS˺˶qó -d4º¼O@2$fMùáÐ{;ëXJ½«2C]¨¢žú\Ôü‰ŠÑše¨øÞ©¯É¢G -lþ#ý�Š/?k ˆ¥Î'xÇ!Þþª®ñÝq·ã‚Ž ‚ï¬Ë!Æ¢dµ qpj H4Gž‰â‰/ðH¾$$êÆ&$¤ -‘ô�ý©H#¸ëA[ žA« ‹™R`“8ÄŸiˆoß-¼$è¸(e ‡e0ß,ïðsrònQ*äöêÓ&6ÃÛ Ú# Xƒ”{"™Lú qrꋤ“f(ð꟤U -É»ÊQò6h»Ñï?bû”ˆo÷×!6 ˆ�cÝߎ%ñ€Èð. ¾^�†D῱)ÃŒ¤EJÏ:I¼ñuÞE$�‡5">ö„Dq’ó”%ʆ y°ŽXK�×qºD69ñ^m²ñ)«4‹¿Ñ„pyWz·I»×ŸvuGâ8¦—‡‰âò1Ä7÷|«yDó”HÏH”Þ!!OíH”«fåО<’N�ö#þѾ -I»ÆKˆ7[Ø;—HÆtÑ�´ý]v(³ä™y·ï)Üß冸ò]âoßÖñ¬ {"kο3¢tµ/â«”ˆïz_1ÊšKrÄWWË�(|ßwE¢Êâx5}A3J4ÃéMDÉ»•~¥¯ž7Ø]¾¤ËÚ4/¥/Û[ Ëé ~­ - jXL<"{è�ø¶›„øwÇBDv‹wž�‡O(‘”¾FR8º‰»»3’Ïêî’ˆ·zž:#–:̾‚h|»…)G2pyet©ÍVXm*!MÕ.eU¯‘(—�ã½ñz®IVðæ½Éˆ'ÌDÜzºñÀ¥mˆ»".òl ›§D”‚ Òì,D„MˆM>« „W‡”6v(Qì¸CSî8R5½ª¥Ÿ[è§Ô…º˜Ù§ìsö¹‰}Á\˜‹‘~A?†µg}íl�H'ÚéÚ—öÕÑ9l"›¨csX_A²Iú¨äöÛKíê}ÿÛûš&z¼±�VŽ -Š’ÌIÀGû$zø­¯°ì6ï»:4<ç˜0~ŸöÓo=æÎöEòñ‡uHúÝs`ÿ¶ßé¨4^ÍZ7Ç -ZN3´#ä«ùÍ}$ÎÊ ˆïÒ?DâxäKùÎ@¢öÐ#isi -¾½+ͼZ$äÙÞÎÂbƒ7xƒ¯%D†½}€¤Ãí׈øp’®Îˆw;V!黦ñôš$}j—!ž®]†¤ï-‚xúá—ÎTÎýLqñˆ÷f½C|x* -ñ§-ˆ�¿ˆ]j”Ñœ¸¬$HH AÒ;ý‡d‚¤‹²5 qù¼� úª±©‘!éàvRv�÷•Ÿ�@BÏ�(ZlFBœÚÉÞÿš‰Äab&âËk—‘8ÏŸ H>ø¨�´r}‚D5¥3’6Þë�¨! IÛ]ðn?`­•Ha"›´‰CO?Ä7[¾”�ûÏy<âßE>ˆ÷»^BlŒÚŠH‡Œt\³–ÂvØa(‘½Ôw€Ø0�"qè^�øðb $â�…ˆ?=|…äË49Løý ’aV_ÄËŸMC2ô–Ä_}¢Dòù=’1Ÿ$¿ÖË!&©¶òv.ܴʸ -V�—%XÖ?õ¬ˆ¸`õ««ˆ¯œFâÓð‡ò­Ž©@ܱ§-baE½l²_§PíHGID‹øº%AÒq¯‰Ë+‚¤åý^Yê@˜j€pXd^káu©Ü^@ÒfŒ‘=¹ƒ¤Å�PÄGí\yúq“R‰ÄáÖˆÒóÏ�8vk@ll}62‰*â$õv$Nq�´üd.O?¤¿+‘´Ø³ IÇ®=�h'ÎDÒóî�ò¶aÆ@Óˆ€)ÖXÙ¥ï�éÍMÄ;­_*»#þööß�å(ÕtJ³5ëK\¨ÃEêy‰z\l×EsCêHOjY$‡xÍižñ «œ/lЩrવ6ÃICðöõÜ}Êv‘Îàþ¬?ua¿mÕh�Åržú¤Æ^´ÀøÁP* 5?ì"MäžÏÝú¥0¯ð›svQÕ7àKÜý¦òð¢9³P^N�]*²JÎgƒa‰¼Lœ°Ân8Ö¼%7)öʉÄã³§/ -ËV%¾çªl³¹l¹Mç=Š,ÕH4E¯¥.Û©*?5Ñ+Æ�ÑhŒ›¾bÉ70¾< ´?Ü(Ø¿­¶¤¤4»ö®Ȓ~…1Î"ø€üÐJ[aAå¹;F3í'LÅœ¦¸ÅœiA΂_QÿWŠß^9ÒÙ’Z»9¨ÌÏ/(Èϯ,hóæ²²ÍûÕé¿|ל;7*š^ÇzÍ:îªvYšQ/×Âb«'xÂRÃZùíÎeY·‹Òfîûˬæðr;í‘¥TŒ)�¶KNU«w¹PÅEê}^Ó_ªjÏzèá¾rÝ"O÷ À³NaH&vC2έN¤K¸ ™3„(»J“O{g»§\�±ü*’1�z$C­z$_ž©Câþ™›@§q5aéKMyx—æáo”éÑ'…L•z ›k§ýK¥Žö›ËË‹4ï¶æÏMRíÅôÄÅbÓ*Î3ÊômsÞ÷‹5 -ડ@~‘‰zØ'Ò±\ÞÊþGå|mžÉ/=’ÏZ"±ª_‰š´béçn/-§ë„° nÞLꕚ?Y½’öf7µ4ŠÓÜ(Û³wÛ�ÀŸº8žuD\ÇÚ}‰xÐéÏéâð¾œTÈË -~¡ K©›£Í¾†dÚî}H'MØsvŽ÷¿ �ݱrÓ22øFøÍX¶ñ]K=âÛ—uòC�ØÔÒ Œ|’%>]�už±NZB}*³C]^ÖÓÂÛš^ÔMj­¥M\<0­>x]·Ï˜“�dN‚ f¶›7±Ó6ØvJ¹g2’OײX$=,J੊’Ç´=mßç)s¯ùÈÁ™l·ØE³xúA“ƒñáùw€øó`‡T/$Ÿ ¢4¡‰ó7Dº× ‰óé:Èçi°ÌÅîC2dX ’ñ‘�L?™ŒdÐÓWH溴ôdƒXftóX¸:x™Ì{µf_Þ–B슢ŽR'ú@Ûƒ%3ŽV)M“’ùÜœ¬Ý‚ Wc qàžï}Ò~(„\�aK•kÆL˜ až›QfÍÛ%|UPVø†À˜UîÛWNCí±²ã<#�PìÞQ¶]6ƒ8Óc¬†eÖ0‰j @¥“�‹¹­£sþç›Öä¨İ‹nc¯œžˆ­àãS’R²._§üfãÏs|Âó¶ HÚ¶q$|C]Jz*ÿžÌ�ɪ.·”¸¼œtNskS{;w>ÓR+Òk#ìר’£ÔùÐH¦¬¬CòYŽÛ²Y«ƒ#‚à -+ȰlÛkF|õ¡¬ú{uˆ÷ïÕ!iÿ¡Û"à' sï#2GÚ…Ûo>š\›Æ79pËÓ ï¯ô2«xÀRC ¬ÍÍ‹é–="-ãöE¦.ØÇÌMÅúsËÓ�Ø<˜÷€%2 ¦¦ï�BA»þ.¹48nµku’Š££hGú9G§°.ô 6Fd#�ÍX­¼Znò›ös~Q Ÿô—öŸ«–3*Ei?gËJ± /UôëzÖ‰MîÉÜ™^T'ƒ�~o§ßW*è‡÷élýÈæH_µ_a™`ž 3aŠÉ×ÈÛs¹Ÿó¬»DÉÎ’.Žé1Hœiù $ıï+Çu`—öÁ&²”áT >Ê>¾·jz,x‹,ã÷tgò1߯ -7ÊkxÌ/D¼±q²ubîŠIH�½ø9,YµrcüUa«ªXßÛBÝ5õO`—˜]±ÕFÓïÑt›c»aôëö;à¦õ"<„ †MÀÛÃ8Ö"6½F”*¸jkÊ¡XEûA¶g.ÈÛ ”†4Qe!6PæÒÁÙ5´ðŪºäD_±©‚óŠ�c-„0•z»¾6Å–Û44‡µÒJ'B8ïOL¦�=Ë÷�•ó$æ0ˆ}ÈúÒÖ#ïŠaǶÍÛy6™ó��Ÿ/„ëT6Øg­…ZØk°ÉªgÝ)N¢™Úï�l©�xp–âöþuˆ'.»!YÚ²‰ßKw$º­H¾ÖoA2/ý;$ßEÏ.ÁÖ)y3x5«ýçÖw¹çHOÙµe:•¯eŠyÌ‚©Í7ÒÆ]³fm�æÍWV²¿‚ìªJc½é(TÂ3s‰…×q_ÇÇz‹Àò�å+WZõ75Ëùû„#B­Z _l¥â…gH\*ôH>L#H´ ôH:ù‘²R$­Ç$­öê_O�¸LЇ/Y‰x­«ñò<7Ä;wõˆg7×Mﳚµ&ªX›“}x$bW£üÓ-9 •‡´oei8’ã�å¥ÌˆÏ÷½–—*E|, ÐÉ� >]ð�(¸Ÿh�x“Öˆ7ð)¾ú…ÌJ2KŽ‚†�t¤ëß¿„Ç? Û´®<¨4*'£ 4|ßÊš¥öù'ûTýK¨Õ¬Íô>¼n¾×Ò%+ƒÃc rrÊJ‹Ë 7]x¶•ŠÍ« ©j\árÔx–ž;;·\sõ]¡íÆs‚$³Ñ’d1¦B24—͹Ù|qµr÷ò“7I ×i@2+ï $ËÌJ@Äàéä_#6%è‘�çuˆëÖë‘tkí–`A<¿)É]£ž¿ÉÀ»±p -®œ=r^‹Âj&’áÓöÉe-‘(Ô7©b'ųˆMï¦f@$[,É< ãrã-‰B ¾Ý!g2cÜßM•˜u†¤•Ó�FÞ¤ò­òÙÞ­ûdNlçøº>0A7ÉÔº ˆ§~Y†ä‹Qƒÿ:¸  ž¶óÏ‹GÒaâßK[ écóC<·{# î²ëîþˆxeîNÄš®ÃË*§ÏÔðɹ8ÚŠ×<2(qíP$-$WDf9…Ä�»¼Þ-ªƒþ±íïÓ;6GéÍÒ®Um`ÃçNgÝ€®Z+ûšrnGzÆa³]µÂ8ÁÔì‹Ì¾^WÍ�̈\'6Õp>‰1£… �T R‡lºBiÏ2Ê7œ*w©¥ŽÃèð‡nÏÛqDÓH¨Bº¢­:x>{TÉuðŒ 3? ñyÛ:Ä7çõˆ/þrCÄÁúUć ¾=¿ñu{O$Nƒ3!’ÌIÉFÞbÚ¥2K¢¿±}l?Ö}ä:/qŒ.^}4Ùd+ðöò’ê-Á¥¾>¡Á+?{0ž:Ròò*uáÊÌëLQz½üä%¸ÎßüôX¿nc'�]]´®²º t³P]¶@P³CJ ŽRh•¢–:Ѿwk©ƒ6—Kµån¾º$H·*0ØWLR!qzZ‹øºG’n‡û!^¸îƒd¬n²�šyIï­c�|›K�¸­$¢äÍÕĤ÷ü쪭Æ+ÍqÛš¼nçfô›¼8ÄØnˆÐ"ñâÆ:ÄïÄš�€Xm8ƒ¤ß®Ùˆì«Hæ»…W7€�ž²Ój»ŒRôð{ úÊ {±“ÁÌ7mätæ¥Ïšs¾EÖå°½OÿLݹU”Ò¸*c²›h§3TiñéÑt$3ót"Ëi¤rkÂÎ�;gƒý†f¼±Úä7êiZªÕ“þ] -yŽ®¬wl×…þо 4—‹5Ã7ßttEâ„[dŠù’Þd6O¥¼�µ”Ó`‚ddÁŽD¼|Wȃ¬¤4CTÒúHâ HˆÃEO$Þ*Ë/°íô¨\",„•¡!ë*¼òfߣǔ!"k-g¸Ì·ØÝÆmÔάQü14 ñÇOï¹$h®þtfþ7"ÌZ:kv?j¦–ö ÔüP\ä>ü¨¯ç�öõñߨÛt`ï¶üœŠ|^ÝÐèZ¥ t§£ô›t[˦6­gs¤4e|¡¡øw3oçV™f‚L´úÊ uŠŒÙ«xª«TZcSs!R¬Ötž†K ÊìªmÇe€ˆ‚©†…VyÖSµ_”Ï?4ž�n Í�[bè´“ê5×$�ÌĬ¢Žª¿nïª4�3 6Åòš±Ib �×\ƒðôˆüžÍsb7ÿãûÿÄbhƒôvM¢ÜY°¶Ó;#¾µx"¢î�x͵9 -q˜�x éðX«ùÌAdJì®qÿ4ç”ýC -ìù)`Ù-Ò«œš=aî¥Ò5{V(íöÒåÕˉç4?峯µSItð‡ŠÎ¥Ï¸mÁéÿj•ëŸMqÊϦ|û b5+F¼ýÀÉÇÛõHzõrCÒÑ\‡¤íàEHÚåë‘t}z‰0ùS$êýS•Õ!>º¢d¡»È±Q£ñ© ŠçéüÆ -mE°m� Éñ õ_PXY]´©\TŸ€*z¶„î¨r¡ÂmúÃÓ1 IZJµOŠ.ž€«üÃ�רG{¥S�¯†ÕbÓ).iCl¬ð‘:ê¡¢¡° -­T™î6üÊÃ�·˜*‹gÑ´B›“•Q"ÐÞªú¦Œ1vâp‘ P±�èúwçÈÈa#§� ²ŽªÀ É¥¢ìÒvû™P øÙQÚ^®7"äâe¬â›x‚ÄÉ[®©öïPÇKõ\¶!E/ĪÖ,÷f -Ùfäì¼÷¯·�h{uòXÂ355j5oº÷íÉ8¦¹Õ�òâyر¿è0¿�cÝ<ç+À#3ÔÆ§©*óÊJK7”èüׇùw£m ¹´×Kú%ñ‡�沕¢šjé†� -I¹ÓQRÒ ZVÃ…d&fff¦”´†Öp%)™™™‰™!"«¡5ª’˜”˜˜˜Ä�ÕÈ#cbbRbJDµTKïªlÒ”rG©ŠiËU&Ö >©i9·.rEÈA¢�zÈ�_/‘(¹H8ÖŠ§³è1º�žeŸ‚’õÏoâYëæ03DZ,‡™ q"$ bÖDöM""ó…(\Ï7unzK‡ƒ’Ž„-7Óh?^-µ‘!¯ÑŸ:k‘äí ¨¸Ñ±Nî/£òã…\ìÿª�¤óñ8Ä[¡H:=¬D<}…ì\œ2TÂØÞLÒq¾°02rÏæ5=Q&E£!Rvß‹�´ê3‘ÅÙ�´¾R˳¡°‘MJSRBRĘ-Æ4žy6ª•ñ¥ió€�²«lpذöÃ1Kµ™×ÉåÚÓC!þ}ûcƆ)ˆ=|ß¼å? Ù8±©v0ÅÔ"Ä&§(ž~Þ¤§¢4:ºF~Üšé|Œ]µÑtÝXàŒy“,1âaÕ Ä§;[!¾èùñO馼&»· Ùy ‘�º…D‘Ö‘}Bù…ÈeííÜÏ€¤Ÿ±IÇÇï�tY7ÉÇZž¥ l&Ý®Dü»ÓX$�¼Ìˆw@â�Ž¤íª™ˆ÷ çi6_i±R €D»{×!Õˆ·&U"évnÖrŽ7›åXø{k™ ²+¿5ñÆÌ®ˆÄü€xkëïr“ÐêÍ.+‘´÷5 Þ|¹Iç¬0¹F˜yFžin �90Ú¸JNÀ¶Ëps �´„Ø0 I‡-<û”ƱžL§DüSNHï¾õD"†‡ Þt(Ø+¯³Æ<ß² –ý«âó¢d ¯õ¬¥á¡.šÁoÆÐdmLª>9&eБ™×CxOUöð}_œX˜š�’˜–ÄkÜÞ&™”ÑÆpQãžgÌ4u�I&åú˜°uÂÜè&Ç|ËïT5¿þ🙒•œ™*gœ1¥´E‰Ôµ„¶(UµQr‘f_¤¤ÜñmÐ2:�  c„§p{ãéâú’òHçÓãÍë„Õp�nkÏÜ]édÚ�Ρ=h:Œv§ÃXk¶vPb߉b$„¥ó4™+ó*A¯Šƒ9.+²pÙ‘QÀ÷é3”ufÝ^Žx$ž†½5e[ÒÜkýÿ¨x†–°Å/Y{6� fý˜‡H—µ/ã2¹V¿ÀÎÉy~—KJMLŽ·òo[«ôI ó¹N“Ÿià[«Ò¬)ÉÉ©|.gLILN´Í`ç�g—T±Iñ‰ çßwF8îq¯¤Ú[JåâÊz:õÁ·çÚ©‰¦�H©@ E–LKzTòºL¨[AáfXŸ°q9ðÓ‡-,2Žúr¨¿ þZ£DÒã‡öH\ûeœ(=ü•ºÿ(8Šÿ«‚cWn¿ùÈÿ\p<¤ÿwÅ1J®8öc¿ªV¤&>tܘo•mo¡l{v®>ÛºS¤×˜7»Â-ɆúæÅ–þçÅÎg™÷ŠôŠ\,mÍÝ~ú¹pv¬+ñý÷5§·s¤·¯héBqïñåH‚:ïBÜçàƒ$hÞ!Ä}Æ! ²Xþ”‘ õkÄšéH8¿£#ä- -LwŒ›àÔ›+e¼ùBn&ÚýñÞ¥×H¸I�_pæédv�Ž åJ$ê^½‘´€Øôë¯HúvH™&¯Sb½Vû­Óµ·ÞËol ›»·Ÿçz/gÇ1ž¾~Ô¦[›Çm7¶­~>ˆnâvçÎ×Û­kwø›Íß„~óSûq:u°uìÑñ]§_:oî|¥‹—�]+»¹t+îög÷´çzNìÐ{aïG}Îô½ümFÿ”þ,`P6àÊÀ­K]„£ÒģУGiÕQû£Í¥RY]ÕѺÍZi"­ª›È©kÀFؤ% +*•Û×®°iÇâ«+ý‘ØÍ¬A|×ç’6;¿â™om %bÝ$ ’ªßͪB¢Xë²¢ñW…øi7A¬“\é Â3Ý'¢ Z­Û´ÐÈGêTpÔºO¨€³ÆBoãæúŠ€¤Á‚凑ÄçòTHœ<ß#ž7d!é˜öbZP@ü0¡1�øL· ñ—¶¯ÙÄ0ÄÆ :U™å´ù(…“†àYMÕ2U9%»©b7%¼Ù¨„èøˆ÷ì<¯fÉ`“”6}žÓûê÷¿i*ßÿ¬]n/è¸"¨´€pÞ�¼�ëqÇÄë<€dsbít+=âÀ +q¦�Ȉ0à½sƒó¶'—•­)^!L·y�3y vJÍ-&êA—Ê#†Cìs4,‚޼š¹†ÑÒ§»Šœv< óž‡äk$§×R§ÝT•m�#Áh4FN_¹ä{˜CŽíw³ïªÈËËO-ƒJØ’çϯ4F®‡`Þ;3 ¤(;«äòð=£˜ö[¦bŽSÜgŽ´� î6©e>õ(Mp¢ŠjºúšæOTŒÒ,CÅ!‡®Z$‹ž+°þ‡ôpÈA¼ñº) æ;žåY$‡ø`hUâç3.gç ?X—CŒ5„Êë‚ÄΡ! QÐ4¹ÿe$Š—žÀ#"HHÔµuHH)"é:»ŸHƒ¹;¾; ^NËN“ÅL)°‰âOŠâÄOŸ^t\(Œ´Á2˜oð“Gø96v¿(es•ú„ Ø”š»´C +°3(z$ƒ¬z$C.V!qíï"Ði\y`âAÓCnÞ¦¾ùGåŠÄ°sB²JÍ®ªÃói`nñÓÊ|§ëOéà«ÓÏß(Ô|’œéN-TÄ”{m÷Øí–;ø™ƒV~/ê²6mÛ–•µ½`S®§PPR¹uð'N.*®à؆-S{ÃôÒï<œ‚“ÛóΘ·N9|ɱ¼âé ?póüôaAi§„qµý&­˜;ÛI·ÖUHº?vàð®ßéȾޑ”¶Ð'jw•º_ÕTJKèImÍâ=½…Q°x�ïlÞÉßî‹Ç_4#þµ‹ þ2“ˆ,‡sÕ›&:..Y îÒä>1 ‰�9I'ßM"�Æi*k®ž­>¸ ñ·RWijsÃ!Þê[†øù²ËDÙ†ù¤ådÁ cš‰·qs¶ iؤ;’–5?‹ê^ú<½Mj•GãœÊîÓ­W5G¨Q¯}û.™ÍüÄXN TL1 V›^õCxÊ^Q’L\k£Q—ÊË÷™·fïÒÜEÀ³f=aŽ"bÄ‚cH–÷@ÄŒâï‘l¸¡4Gò´à“á[°fOp9<‚"ó©Üy�–…ãP±®p!Ï¢¸(ÿeãG¯¦SêÝSŸçDí®Q·ëtŵfm4w¥–ôœ–…pˆ·æé?²’ùÂf�* nYó`;œ3oÛÄ-ÐÇíé îÏêóW­%°ÎMóOÛkÖ,8§ ùò²Î÷›H£¹7swÆÂâ•^3Ã/-*ý\`‰«×TÞÖo“òF|ÄR‘•p«7–Èf +à¬5öéú!¹‰aP)ïÊ‹K® G ÖÕ½Ø\í‘/µ´9½»F3hþÜ&uÐ^KŒ^,Ö­áÜBM“ê·ùk:dÁ-C–lh‚Št —±ê¸çI9BnÈÐ#Yç®G²àuC$Võ{Qs—6@ÌàòÑr¡JX "çÍä¡Z©ù“uÐ+igvOKC9ÍÝ‚•»~þüµq¬%âFÖlâQ‡?§‹ÃÁãFLö—8X«¨�øöÕš«tì-í²£_7Àb«¸ÁRÃy^—S¬»Ei;wÈß2«>î¦R”êð"©Wžb¶?°§ÖtítXRtDo‹Ù ábâ¾â2$Ú!�‘|}h'’ŽgÛ i5ö‡}°úey{&ºÂd&ǬÖë‚.]1xêCki³ÎïYcÖ´S°¦Lû¶;m@×¼¢AÍ^Ö —¯ v—ŸÿfOÇ]×Ò{Tec hîÑÑÃ{óÒ¢@ˆ1ù„ƒŽ�]³gÛÖ¬¢½n% F ý¡“ÀøáºÛì·2Ô°@–7òPý ~ÜTbäm!Üh}ÂQý“Ô¡T!y”ÚKm;zèvP‰øépbí‚X;ÆñÓˆ ÉPcmÅú%ˆuIf$ â +xÖJ╈2®!ùzp-â ÷ÞHç87yESáGCì†Ö|àuœÎ‘MöA|\k|ÅJÌâotK�<*}”ƒ¤Ù‘Ûˆ¯Úº"±Ýi…Ü?ÓtØXùpܺMî�ø@yñ餿Yƒkˆ/6ý*b]Ó»HÈ�pD„DqãâÇ' +¾†•?§J$Š7 +$ÊÍŸ‘�W6$Ê53‰rPGI«ç‡ÿh^ФYíuÄ{ l‚�K$£Ûè Hšþ®X2̼Ž[ˆ�.ˆ¿ïsA|÷¹ +ñ·IUüv’Æo_ToCâ<’Ȉ!¼Ë$mBoÁe8hƒsppMáòtÄÇ5M�ðW26y�DÕg¥k¯y¾&{Ý8U;®Óf°ŠÃò7"±¯t±"áÆWg +Õ¬q½s*h!MÒŽ¨Q"Jß?Aâ¨�ø9±5ûCäÓu…õ +=’&×§ á›;#ÑÌ«@B^?ämœ,6x€xZüå£u�´xðŸÍAÒÂÙñQËR$]×å!^X—‡¤KÅ2Ä Ë�t½O/Ü'üҙʹ“À…øxÖgÄgçCÚa‡øb`ÄR£ì1ÄiAB|’Ήo‘ØÅ$m”�‰Ó€ +@Ò{h b]-CÒÂåœ _»u•Ÿ_ŸFB¯‘(lGBšÉÿš‚ÄnB2â»Û7�8Ο H¾ú¦�4r~‰D5åk$M<6"QC +’¦û¾â¯>xÊ+‘ØÃf97YˆÄ®£âÇCdçøs�øwÎjÄ'm¯#Ö†îD¤}Ç +:®NYóa7ì1äÉ‘à@¬éE‘ص/F|v­ñîBÄŸž½G2$A†ê¿E2Øê‰x£ÿ4$ƒîKü­—J$N葌þ– éõ�^F ¤ÌÊÛ¸ ÓãXî?yý‘¨gµ@Ä3�¨ßßB|ï0\Nö‚žâßáUˆ{4EÌ.®æÕ¬±;)”ÙÓ‘Ñ"~hH�´¬Ô#qzO�4¯¯”gíS Þ‹Ì,¼.ž«$MFÙˇH @|ÞÌ™§Ýê”J$v÷¿B”ÞôGbß®±¶ñ¥TH$ªà?�ØUÛ�8D¶@ÒðÛ¹¦u S¬rØÜ#oÒÇ{ˆí#¾S¶GüíÓ[¾=KSÊ—ã{ºþ½â·÷öt¶¤Ön÷-ðòòõõò*ðݾ½ `»À~uø/ïɽÞ*è„|{êÀæhSo#™¶ÿ Ÿ‰ã÷…_šã±ÙË×?M·GÌ‚ô„¤$~ ýf,[‚ø¹¡ñÓ;9~§G¬kèF>Æ•(¨ÙŠ:ãÜý| +ùú<§mÇ~™QB—ý²óø²3šzi¬´@» +–ú¬ç5oô>9ž[CW˜ò=Œ‚å¹k Öóš?ô›6„yƒæìÔ%xÍŸUˆ{Ž!>ŠøzT â�Óoñ7ËO|4�¶F²eŠ ’y{«� Ë.E2ÖuŽ{Bl~ùó� 1�~ºJóG®ÎǶ[„Ì„Ì䔢ÍÛCöÃ3¸^ w`gÈ.ï"$M„;ÿ<ÚÓuHZüÖIÇ?*!âM|„!*6ó›ÓÃÒs’Ó· +HœNöG|öaÄ%Ä_õNûŠ}«…ù‘>ƒC7…­ùŸDÜg>ƒøÎw’ÉOÝ�ôn•%±„¢œÜôôìÔí€dÒŠ@@â˜e@,oò+ ^¯éÌkWQ»:Gí¢i³™¢ïð}ç8¼³d_"OyÕ¡¿"¿å\&i£4(�®.I pzWM³h:Q©±–^3qQÀ´ú(àuiÜAcÚ–X_s l6³ý¼‰](‚]ç•&#é·�E é`QOU”¼ Íió.¯˜ƒˆx{u ^Lu‰X4‹§_ÕÙiŸ]ù ˆ?÷±‹wGÒßH¥ñUHwÈ|H¥ Ç U�ÉS?9ðGDÒw°’q!ã‘L?‹¤÷«÷Hæ:5tc½XftY±ÄÏg­ß2�]‘ y ùÛ9wí¥Vô©¶‹e-Uš,&+Äòéi)ûÙG"Áܾ„ÛÃ� é«eÇS.U®ë=~2BPRDR�5c+äñ¥¾)A›}Â׸î^yR¸§ +Îðt´4\™µOÁnÙÉ"MKŒ¡°–Ye'+‡,]¨îŸÄHNºÛ<Û‘çôî2�úlâeÍýmÍmÜ•dK…Ho·Ý¦JŽÚQÇc ™²ª +Iÿ4—e³Öúûùf �dÙUiF|ßZ^˜ÇUˆOW!iÞÚeð»v™=mÃ6ŸŒ­Hàëì¸å †/pv™u¬€¥y‚š{×-DZÀ ‰_,°nÌEÅzpË�×çSߘ_Kät^M¿€dmû»äTc¿Ó¦ÕI*ŽŽ¤-é:–Namè@6Zd#€ÍX«¼Uhòës^¡[¾è/ͨ–“JDé0W”gÞ©èwÕ¬›Ü9„¹2½¨Ž=d£‡J´õ:»ˆ~SdOß7_iož 3aŠÉÓÈÛÒ¹Ÿ3¬ûDÉÆ’6ö‰áHiá $ľï)_HU`“Â6æ¿”ATô;ɺßY5=~ Uÿ5ú©YÅ?ëÒæ±==oÓèTž–)æY0 ¦ÖϹˆ»mMÙ%Íš©,3¤|mªcµé$”Àksž…×ùrßEExˆÀ2�e*WYõw5}K“ò4ÉácpÞç`•º—imŸb§“E>—èåKs 5·NÑ•Úv0ˆ1-1c<ÄB=WkNJOÍâsË”û—Ÿó¾ H|Ü/’Y‘,3+‰·/ƒW“ï~—‹X·E�„¼©BܸI�¤]c—-Ä+Û¶ ùªm(âÏó7#éõ(ÎÃÍK'®ÈùX`ù@2lZeßtÖ‰B}‘*ö"Q¼î€X÷yj$@¬ÅËÓ@.=Ê-Äâ§=òõ:ÚñóT9Ì©2Ĭšn4ò&•géêÝ=ÑzPÃ{ÇUu�¾€º H¦V]E<ÿË2$GöAüë肬(Úl}F’ ÿ^ÚI—"/ÄËû·âá—‹±êÑ�ˆ7çîE,o;± d*ðL ß^ޤ�xÍsS–7 BÒ@rFd–óH츇‘ÀëMQa¢Ú÷Ÿ]mý„>,²—nÓíÕf6lîtÖøaª5ò9,äö$&=¶ÛT+�ãMõçÔìiáueܹ¤��b]9·::|”à«“²Tjÿm7©=íX@ùšó…NÔ~p öÌåM3Žhj UH7µ¥G¯¤î‚28»úÃhïÑóß4­BüxE�øö/Dì£_c@|öGâ§+k?4wCâÐ'‚!Ækä-¦=�¼æ£þîî1ÝYûÝÅe0*wíÉX“¬ÀÛ +óÊvøå{®ð[Õÿé8jOÉ»[ÔQ„›3ï0Eþ�Âs×á¯ß©îíÆL³6gcIYVþv¡0¬`� f­ýóOS{) TQAh×GöÔN›ÎÅefï¾,ÏW·ÆÇÏSŒQ!qxU�ø¡C’vÇ»#^½³ÉøYíä4ræQ$�wŽF2)� qYEDɃ+Oì!xÙT;�7ë½ã�5xÝfÎ5иZ¬sç#Vº Bƒ8Äk[«�ðˇ×b™á"’îûf#²¡Ã‘ÌŸ´ƒWSí¿í£=ýƒ¢LH*;¶ÁÏ·F¢¸ý±îyT|¸5¢x½)ðAÇ•˜ÏXŽA.üfL•Ùö`sÜš†Ø¹¿N{Ê3%ª 訤„ÜÌ»®| õ³�à +®–õòå±=só&‚#"""¢¶°“¬Â`äMF ×•åñ‰÷„ ›ÊÓ8Ý4æÀŒúã’Î=Œ�X%Öåp #c†Ö'm–³æpΙʌ¼-˜ëoBÒ`Ý$�JE:Š«Ï ÏÙhÙ—p¹°ˆv+²ÙKè�¶ˆfù¨¿ÎÄ ‹hŒjWªy—@[©h;Ø=/ƒ}ÃÂzê_w@§|´Õ´L¬+ä˲Uj°ÑV¥ÒÆ=~ÞVIÕÚºÌÀI}Ø|­m7˜¼[|ô·­³´’K‹‰Õ a€øy}".=‹ø!nâ뼡<;Sç:š ÐOéi %lÔ© +áœõKÍżú'°I̦ØYDÓÄ"ûfƒéwÍ÷À=ë5xW Û€·r¬ADb¹(seÖ¸B®Šv‡T·tÖ‹/‚ê_G!€ù�2�öI-§Ý�ÏUUÅF{ŠuÅœ{D$k ªÔTë_T;¸Pqò�´ü�½ÔÒ_KÇÑÞ´/u£n¬íÃÆ±q¬ëÃܘíËúÒqb‘C'Öì9M¥¡Tûäï¿©¶/ e©¬Y¯Nr’+3{=ó¤.EŠhÆ5{ik¤•ÎúsßšLþ–¶”§÷Õš[R¢M»âÖí`-蔈ôÔ´êSvɩdžaFo“t°È¼ÆÂëb¹ cR8„@ht„^cð\ç¾–;$?¥æ¹\AÐ)Oå‹uÛ9½AæÅ ¡ÒºöÂC¡lnL”倯/žü‹‰J$üÏ?#¾M›‚¤a·îˆ®ª=¦�Ø ÷4Ÿ°”ÃV¨2&™x¹‚ù%<–Hƒ·ÛKµÒOÚº“\HLL„ ³zØý,E/bÝOC‘Ø]KŠH¬ë/Õ‚™¯ÛÊéÌK_×ÅEÖå°}A•ÆïÝ)J \©1ÖEð±Ñª„¨Ä0:‚™y:�e‚4B¹sËÞn‚�+‚Æý°[‹d€ÜÑ´T'ªò/­íaSœ|+ùÚK]jçhÙt6€õcË™ÄúÒi´ïÚ’6Nƒø°Pˆ 3•ugÃ� †)ÈŸtM£�oQ…Hð¸Oëd C:ð[Áš+Òp.ôB Ä@d¿`”Òc×ü£ÝëËaÌe#�ó?g»�uöZ@ ¼æ2ñæ…€ø×Jo>*.&.åÆÊo7þ֚ȫý·Õ6)tª8ê_I øÓ\¥çégí‰ÂËÇà.ÿg§£#:û¾§}.xìðçsC"c”¬ùä-}Á›x»?möU\§�Ø¿©ríÞ¯À3×µˆ÷ݪ�Y®ìõc@ +ãÏ\8p]ÐÜúéâüïE˜µtÖlo~äL-íÔS©9]ê—³Vó#¿›?bðwg~ jÛ‘Ê]™iÅ™¼Ú?¿¶EžbÏC÷Оªé--`¡ý¨u2³~l`b™s2Ò�t€0¸9ëjc è:ÁFЮ´«ŽÎa؛ú +j–ö÷Y{Ú—Zµ@î¤âÕ×HœŠõHZ'$Úz$­¼HA>’Æã’F•zć§ñz¿ %«o·Õ#Þ˜ç‚øð‘ñÒöªé]Ö²&À`B)kr® �Dlk”?º/króo®ÊBÒ`œ£lÊŒøæàÙ¼Ÿo>â y­¼â«ÿL Òñ.¾Bb·@Ê�DÍÒ˜%MAÏÙÓM_þ�ÕT÷:pÛÆBßü䤬ü ƒ«Ê—ÚæŸëRú¯y­eM¦wáuóÝ—.Yå‘•–V�Ÿ[˜½íêë�T¬7\ão«mñ%­ùXb/ÕR-}ÍíKÜõB°q›`„ÁÖÀT«¯ì¤ƯX-²¿8Ö±ãÈ>“;ï£cé+n§%ù·ÿÁþf ÷ëšsLCíXÚŽ6yó�: ê‰ÿV©zw™®ª¶oÖ†žn^GêëMÇê}€ë ˆ[:#qÀ2ÁýIg2›§¿RÞÆÊ\A2¢‡`C"Þx$d@JL‚!4fSøò$Äîš’žŸzdfí]Q²DX«¼ý7»g̾C‡)}EÖX™7¹¥zÓ¿$+_`Gýý91L.8Þ¤ì6çf˜(ü*%i™ó¤÷ôþÁsB>ìŠÎ Ù>à—–cŒ4EգDZ’ðžÎ PÜÈ£.yò­1K[´©x•!á[¶˜ŒF€1b¡4áÆ…ó©©`+Ÿ­<’9N/XºgVq‘@ç6ÿÿë(Ïáá{�òe+¤ß´ç|+—ÿÛ5”éÉññ2/ ´„@ ¬�=ejXÁðå:Ê3øÿ먦müm©é›ÔÉætò­4àÉÚÍ‘ÚojçhåÌ¿ÎíâçC?fND”Æšø¹#•co}Ÿ(×Ìg±îlû�- ½Ù0:W¼·¶—œæ5ÁIAû–¤gIéDn»%c·PRªbjƒr–iœÅÃÌëq×Ã6ˆ+9XQÆH^s(rÁ”åƒ�_Í6n¥—i]¸óŒHµð+k’Ê-a`J‡$°‚ÅÂkŽXb!hsž6á2�ÖpAýÈ?ßd£Çóéq›~£;ß¾,Ò„Jèsí�Ì×ÇáOþwF³ž»¬+U�Mý§Ôlõ%kûÑ,—Úö™öåRÛdCúvÑD“.ƒò'ˆ�TãÁð=IK­a`6OƒÉ`±†ðšc9íh“˜l¸¥Ç,gùTc\¸Þ¶Ì{¹»à Áùúãå/"rùÐSswÏËä¯ÑÁZ˜æ±Îñ²­ÔC¦=¾•ÓßœöÀÏ y*s¾%á,ð—U´'¢ôH®R¤=‘¹ßÛƒ^Ö#>zÞFËl2cE�!­†d¼�A�³ n•A?\NSdiØ)8gêSÐ@®¾ PÐåvkÚ]!Û¦ò7v3­ƒÙ0ͼÎÂë²¹»Ö0O‘…±ã4’ûÛü‹éúoÇâÔ,hãÇàm¸×þ%óÓÒ‡)‘8üX†¤q³îHTƒf!iúòçÀ„ÀÄÚËÿjÖ‘ š…øæåÏ<{ñy˜2:tKPvTvT|V\¯f/™k¾tÛ–@Û½szÿnÂeÍO™ì;­Å”V +ü±œË§D©?·Ë/ñ_*§)P©ì?eÒ÷ˆe,ñÁSg$Ývë‘tê䂤¥¹ +IÓ>‹�4ËÔ#iûê ar?$êÃSŸT!>¿©ä*QYóW«G|µšøFñt~m±¶Ø¯h� Ió X¿Á;ß§¤,g[¡¨›´Ûv1@A½¶—5—kj†`¹@á‚ø1Š qð�ë†=ZTñR5—jˆÓ ªuÞË=˜BÚ2Ù×ù×ûH4n«�8vú —™Î×ep2½þL¦ÝÂåú}Fâ–P™O +©âu\üe-‚ópÛ°K–2}Ig¶$Éb?:íéÄjÍmI##Ô5Ô^õ׃}Õ‚æEö^s7"fs8øðšÛ”œ¹…góؽÿ;S9ô2Í �>­‹–Kâ²1ñkÄO7DÔ=5¯¹½E^3û^™ˆGê®ß“5šÏìD¦”cømîÙaÁ?©ã�¥Ž–ý"½Å©ÏB)½”G÷”:Qá=ýjt�F’–ÒZíËœkgáÿ¬×mö�ÀÞëTWÊ`­Xwž‹Ùá|ˆŽ®PÑØÙVªÌ¼ü~åáîðûL•³0Z¬MKIÊhgUõé)#†�™0Ld=UìGº‡þÀ=<1bðˆi£zЬ¥Êgsl¾¨¦oó6Ò  +· +à¤éóq4�Õ†ÇëcÃãzŸ˜y' †wS¥;8ðìÂø-qÑ 1¼ÆåSŒIl 5ÎAÆTASUcRn +ܸ9Ðu�nrø$~¯*§æÌ�g'øä¸”Øäx™á¢›÷*$å^{II7kY9çŸ�œœ—'ÐrZÎåÅ%''G'û‹¬œ–«òÂãÂÃãýV.·Œ� ÏÕþÛ¨ÃK:%Ÿ6¿ïtôÕ’øDó�¶””Z:�+ŽØ³ xÚ⋤8c|¤óׂA°Ä¶ ø-Ƙ‘]áBc£„ˆ5Ç%&¤”Ÿý J $ sM¶GÜZCíZåëïì.sàþΔgD¢z¨$¢^æ+‹ +3ÊÄíþ9Þ¾>¡®ãN0Úœ~õôåiÃA·fÀ +//^-5‘S”ÚõÔQ‹$cAÅÝ–U²¢’Ê�·ri}h;É×g"ïØ!iõ¬ñÂMN°q9pÞP{`w=ïÎyÂÂ��e<›W÷Rfˆƒùº +^„¤Q—)ˆ,²Iã›<$Q—¥4Ål‰‰„ps´Å˜À3·Zµ2*?8að¡6U7†ÃpÊRfæu²Dë çˆ?è†XS3ñ¯g_ä(ë{"![§#ÖUôA¢˜šƒXçÊÓuz*J£ÂÊ•Ag¬É.À‡ÛT[MwŒÅp.š·É3F<®z‹øjo#Ä·ß þ)Ý“m²Ç‹‘�½÷ÙùûH íÙ·Tý»Ÿ>È“:9U<™$'Hš7’B¢í°è»ÑL ´õ¥“fÚ‘6ÂÏÌ¥|êÚØ)°„gjjÔj>¶ïÚ‘qLs¿=åÅ+°çpÎq~3Ç’Ú¹ÍVŠä€">AU’Q�Ÿ¿9O·~Sàúv´©!�vzG‡Ðáh:[%þ‹d(hnã~$Ý�åHZ¾øŒ¤ÍÆéHºiíx:ˆna3én%âß­Æ iånF|ôô» ¿irÚiƒ“†c° ®XvÊ ëZ¹”™¼ ±fÔ$M×ÌD|rlO[°ùJ‹•ªè$Úýó�8÷-C¼?±I»Ë³–Ë‘b»é˜QFŸ‡¬rÈ”/Ï;¡ÃïÎl‹øGøiÄû;çY+¶€z°J$Í= ˆ÷Þ­EòuJ \¤N¾(w 1³øÃe\#óÄ»åÐ{�4ä‹XÓÓI‹-<ëG#YG¦S"þ)‹½}rC"ù#޳˪”í¬3Ï·,ƒeÿÚØXx' zV�ïtòYÞ»IE/h>Òmÿ¹ô$È\„È&Kgµ9;•^„;°}-Œ†ôÁþ¾ˆ— ž³•#Þ.0"î˜3†ð³¹KL² ù�lE¢œH�4>@ÄJh$ËB»#™Ö× I›ðŸ‘h.g!éºk’6®#QϘŒ¤sƒ^PÆ#ñ«Š@4ß™\7ßÝîçþß!þØùâî[Žr7 ŸGÒ¤õ $­V!i&ºûeû 1`‚h¯v‡2és©âj‘4¥Ð^*¥9ÚB•ɰ™%ƒ��©[ÎmÜé"¤!QHdÕô;$Jî.Ž5âé,zŠ.¤—X?P²àö}k\OÜùK‹eânK¤[1å"û>‘yŽD”Žnâë¾®ûD‡�’Ž€÷h÷g¿üfOÜÔÒ…bå™åH|¿Þ‡xÐn5ßyÇŒÄ×bA¬ùS>»ñß!¾ÕLGÂy�.Ÿ¨,ÓCã68ÕæÙGÊr—ýÏ_ÿ€„›Ø +ñ]OGžNfGépZ¨D¢îÔIóçÞˆu¿þФk‹¸i²�<ãqÓ^ȃ‹–4ù”ËìÒÏ®#ÿXèŠøGö)Ä;E9<[À~¶L‰øënRˆˆ3ȯ‚Žó4O·Ì…¹0ݸV>Ž�ÓÍDõ¤ÿzÈÿJãíü4ÞDÖx;�ÿ‡Æ›ü�·æ*úÉçÿ ¬vÿ_ÀªJVä¿V.ÿVX¹ÈÀÊå ¬âþ3°’.üº#@q²(¾èÂSûǵƒµ…ªØµ6 K�§_«`#"ŽÙ„ø)§‘ŽF|·¼Ÿ¦+O•W!Ú^» ¾žæ‚íDqe]�XGLˆ½ÉRÄG º"þ]|IÓ.Ç‘t¼9J$žÎÓ�|S½Iüõj$K¿MDÝè ÿ–va<(L*•%DûæÀ–HÄš¿]ÿX%Ë+¿ªB¬ +•µøµ oò1sæt9j:EÂ<ÅÉ tf!~žÎ,²¯ÍD¯ê©ET{}ÊăËF!È=>f¢aÛ4DÓü=W‡ ™x°ÉÄq~¼‹$PÕS@ Ù‰HöÖÞÐ"8“…˜ �üÞ·x¥Ý&ć÷£gmC’¼à’ Å=w\FÒo'ŠøyÌëRg-’þËZƒo™Ž»¡|Î$Ä«¯!þ>®÷Œ$i½‡#ñ¹zzÇU$ýrÿ‘à¸çL¡E2¦e/8U˜žÌ›MJv¹Î�µf®ˆ/�…’­?õŸˆX×ù‰IF„Ÿ~ùN¾èºžŒ FÒ4x„Œ"OA¬‰ýðcH”ä<îÔ÷Hâ )¡ä;@ñC »¶$äÔ$ö*™�µŸ¦ä‘4èë‰ø±•IÃÅgߨ> ˆµ�ÆB”±¶$‘š�#~š+»©ï¨(=âëÑur�øûÄß5Gç¯D´)ËŧϲxèÔ«GH:¿Ü„øÓÄB$�lDüûÇMHÔK¾Ú¼ñž%‹÷pÁ£ä­Ù=Ñ4ýÂ�!H& 6!nãù»H6§?‘¿àÓãu¤-´¬ÜÿP’áÏ®É: m­C†6==4-<™g éBº€.T&'§§…¤§‡††‡Ëña�ü‰2<94-8$$$=$9œ§ Yý¯2<<$4=$#$--9™§²ùÝäð´Ðôôz<ùõ^Úk¯â$­ÑBxBtb4O¯«hµüs¬%>Îǧs1ñѱQVþSc•>fËçÙ\OˆJ6ðµ�U Ö¸ØØx>�3ÆEÇF[�f°+À³ëªˆ˜¨hÐדíÿžãÑ~l�öãP.² 0Dø<” �ÀœÈNämÚ«¨u§VmN „u/U!�� ¢ô‡*#rD5Õ>¢ +j¤öR ݬ½=éÒðá“& ~iÒíÛ—.Ý–Í„çÓyRÛ<Ú _q²ˆ’k4õ%…ö§h�–­ ½XO:Zx¶^È­Î+LƒD>1ʼQðW [ÑnCoà™«3�L»Ó9´í@Óöt0kÌ6ôŽî:A �ÀxÃqžÆr¹`^#èUa`0G¦„d/;1ø.]±¯Y»wß‹ ²¼`G‚k…×1àß/Ò<¶økÎÆ³>¬;[!ÒeÍ ¸¤ç'nWÃ/°wr†Ï\9¿%ËN~ƘjÚBT×0¡HQÁmѧîËW츖PíÓù²¨hU5�útÒåfj¢É&’FÊÒBŽ%Ù’»1J¡(+{;lÊôÞºøéƒ—ôG=¹#t½ öZ§DÒáts$ÎÝ“Îæ_þfÕ¿K�Äÿ•©-wØ|â– Óÿ›)TÖ ug¿ªVÆG?tÜ:˜o•aÑBùª±qÕ©Ö½"½Í<ØMnI*T×[ú�]I1WŠô¦,ŸjÌ=ysá�pölÌóäÕÿ/]8 endstream endobj -438 0 obj +2176 0 obj <> stream -xœûÿ~M· î +xœûÿ>Z£ å endstream endobj -439 0 obj -<> +2177 0 obj +<> stream -xœm•MOÛ@†ïþÛƒ%8ÐØû‘BH$!R¨={¡–ˆmÙÎ�_ÙϨª’èÝ™�™gf³›~{x:»)›—xæ¾gæ1öÍ¡+âÙên×&iºnŠÃ>ÖÃ�ËX­ý¥i»¦èã`VÛõ¶®†$M·uñ~(ãÑç.ËøVÕ_c³:ôC³OÒô¹Þã¥9aÁL5™më¡>Lvš¤éÏØõUS_š™)‡™=tMñóZÕe§LæeÌ›äÖ”U1HMßÅ~×N›Ÿ>ú!î·õkc^å¡•§1ÆÌã[Õ݇9™ -;5e|Årß•±«ê7sr,ö/ãÓ¡mßãX¤É¦ÕX—Óïl„ÿ±ÛG3ðçª(MþµôüÑF˜ýºkÊ£È)±hÊØ·»"v»ú-&WY–e׿j³Ùl®ÇŒÿØ/,Û^^‹ß»nrϯÍU–ÍWד²¨ ”CY”ŸÔ"Cl9jŽMž luŽ -¨ <êfRöµÄ&ÏJQÖxž£n±ÍQ2%�e†\|”ø–(ñ%‡Ï—Ãg×(ñÝ Ä'›ø(ñIÁ·P-âƒ!‡oN'røæ²ÁT5|�Ê,| ,|–ùYøTfá[0# Ÿ“ >§(ðYòYø´>+Oø‚|ŽZø‚2Àç”>§ ðYe€Ï3i'>ˆ|Ž“åàstÉÁdŸ¢ˆ�ž9øspðyΙƒ/h|^ÙG>;�I‰Z_PLÍ"Ÿ—§øðôð9æîu>©ÓÃdƒÏÓA¯ó ­‡ÏÓOŸçàÅÇôðyí3òÌ/È>§(ðYØ=|žÎ{ÍOûÄGÕA|t0Àçá â#JÐü˜CÐýBö0òÙ ¢0òÙ ©�O²øœ¢è|jŸîÙàóÌ(Àç—ÓE©q¼2Ç—áóŽ.]ëaz¦ y¼}«:~¾0mÓŽ»¦Ïô4_¹QÝoþ B‰ +xœm•ËNãH…÷yŠš…%zÁÄ®‹Ó „�HYôEMkf삱DlËv¼ý¨ê;¡[£Ytü_¿úëRüñýéò®žã¥û³4?â<œ¦&^n¿ÆUQ< ÍéûåkŒmlÏÖùÚŒÓÐÌq1Ûýþï–UQìûæíÔÆ³Ïÿ¹ÜÇ×®ÿå�j˜íi^†ãª(~vË[¼6|0¹'³oc¿tË»)?­Šâ¯8ÍÝÐ_›jU�}»Ž©¹yµV ³þ> ÍS\ÌK×·“*™çTwUYÓvÍ"•›ãaÌÁOïó�ûþe0¯ö4ÊÓcÖ?âk7/Ó»¹È�}2m|ÁòmjãÔõ¯æâÜìoƧÓ8¾ÅÔ¤)óר·ùÿ:Á=£Y øã«(MõëÓÏ÷1*Áúï/C{-6CçñÐÄéпÆÕMY–å­¹Ùív»ÛTñ?ö«+ž_šSv¯nÍMYúp›•ÍjS£\Vµlõˆ +xzT�Up¨ 6Å}FYÔYTáU¡îQʲE}F= v¨GrÊs—•¥³*-@Y*Tðú¬à«é¥ßߦDÁW+>+|ö%¾;”ø®Pâ“§ø6(ø6RðYø*ø,ì•øÈiá«ïQâ#ÎÂçèÚÂçX% ŸƒÝ ²ðm”SóS=ø,«dáÛ@dá ŠƒÏÉ_�‚ÏKÁÔ |ž]ààó08ø6²i².>ËÊ;ø§,ð9U‡ÏCëàsRâcšNó“M󣂇Ï11Ÿc¾@Ÿ—§øØå>ÏT¼ÎóâƒÖÃçÙ>/Oø,+áá³Ðzø¼:K|6m”¬tþT¾@\}ø,D!ñÙtÔ²ÒùcF¾@×Aç�êAóSøÕ|�Õ ðÙ4?hƒø`ÚŸª®ó§ÎÄ'Í�¬µ?‰«Ÿ šJ�ù‚¦RÃóU¬;7]ÊéíùxšÓ4Å~ÉOO¾òÓýÞõñã ‡1Eå¿üø�ßѤ¾íþ]íÕœ endstream endobj -440 0 obj -<> +2178 0 obj +<> +stream +xœÝ¼ tTUº?úíéœSCR§*U©Œ•TŠJÀ�™)Â<# d1 !(DDššËdÁ„ J¤mÔ4­4b `„ "*¢¤mÚö‚v£¹vD…Êæ¿ö9U!Ðj÷z÷½õ_ë%TÕ©3|û›öïö€À«�@úœ–Æ_ª•Íð +N/Y|oÙÝxÁ§¸ öÝ»pyI̬^ï�½—–Î�Uܳâw3òÄ3½KKçÎR1ÈÝJË–>˜´É= ¯Þ\X>gÖáýx o/ñ½lÖƒ‹%#-Ȉ_4«lî§ÝI@~<€cËâò%KÇ©«ºܹ®-¾îâ¼×�Éã÷ÈÑЧµWÚ +MPM°¯ÜùÛ'à4`í¾&tmB‡á4ì?œ†µð-2’×Q¬…f(¢n8 Õ°K{²šü*È+ð>œ‚�¡‚üå’Ãp½nô)4Á¦›£�fØ'H3¬ ͤÅ¡2x=+  •Ã*¼šðx¨†·éYx6ÀØOC9¼­q¶m…óp*¡vàK0Âa8IÝÀ!TµÂUX…p?\•pªa'ìDk¡–P@FàðkÅÉð4„]0v±V¶C胵²Vvž€X©I²Ëô¾¦·½è”�ÇÀûÈ+`"™Fî#£uÔC—‘KP��Ì„ùp†µJv¨–=P-• åt¦ö»Bȇ—Ñ™¨.Ivy6ùV`7œ„]šÄñx6†�¡g¡vÁ.í½Z—Tx›\ƒµ°s4Œ!P +è(Ø{ Ê œdÃ|(‡l³þ Ð)l3©Á+tm ,ÜváT Õø*¼ ådä@ IJ˰”ìòJXÂZìð²,1J0‚žñê~ì^¼ßwgQüSÜ)=oû¯ÊñûaÜþ�åñM7nŒ+¢ÑlÊ~³Ÿx•ýÔëùìç.~–Òs丢ø&ä<(@vðÌA)=GN(Ú�½âÛ )nwJÏÁƒ´kbÔýÌ»Ÿy‡ÏÜ?§4þaõaOÞÃêܼÀPÊkh){È÷ +Pd Ù‘ÂÖb +i-çÚ2@=×v®-=Ìê¶zÝVw)ÿíÿœ×È¡?|{¿ÔCÌ,'èÖ +&èá —j)®…�J-ý£1C†L\�eVým™þ–– HkkkoS/§‡¹nkàu’<Ð1oé¸õ/c­ |HCÇ…�v1-£3A‚¹¾ÄIÅN„™ø X 9¤BLà8“ÁˆÑá0XVÛÛœ¹�v137}ä„"Ye_˪x±¯§$H¦ûBe�‘BJöb<MË2 ,ä)>Š£!Gùý_Ó™þ‰dßõzÀ°âÆZDW€b`°/ 2V6Ká�Hª +A¯FV…5‡ÔÅ£Â%cS‡Åªímþ¶«-77Ô‹ímÕËêeµý²-ך›Žn‡=<+³wŽ#yâÁªBV¦MNEžI¦Eþ GŸ/:V6ïÄÝü:ÿÅ_ùó÷MtëÆuûT¦$P0BÓ}Fá:2&2FÓa:rh6sØÃã� 9ìÔ“�˜”íBY™½³{%¦¡T”Ý«[Vf8iŸvhFõÞ²Çæ¿wŒÿàŸwnÁ’·Kë*¶.|÷òé¤f¶çí¾ùëî›Sê‰Èxï¥Ö¿¥¤|0|Ц‹ˆ�Lk®ã’„}o\ ÑtÆmö­3¢ãfaÛX;þ¶LmìfͲjΖmͲz²C‘'!ñDÓ‚Å¿yøÐ¡ŒçîÛ÷,zVX˜/¿¾çÙYż€Ð|º–®+Œõ…I²‰€•Ô†6ŽËFIŦžkÑGjo;÷–UøµÏ«‚ŠTk<Ä£xk:¤£,5Ýê¨ú¬ã`§Ž³Ú¦#]k7ýÍI×ö}hø¾ÆC‡R�®ô@o²<õŽ¿¼Õñ>�ùñ²U ÝtLþ�$Ù! zù¢˜ŠÌJ£„ªàñP鸇É``RˆÅt«Ç]Ìô·©—3mÂåž) ¿óËîÕ;‹&��3­á«š„Ã=÷¢d¯»óÞ9Õþ4r¶zL“·€aÝ� ÔCWœƒE¾;Œ2DÅ›#,24FÈU6weüÑØªnͶºˆA#C�’yP<•ýïPÛ…‹e +‡Ï€´–‹í~áaêe¡+[®Õ–›î‹M�K�Ow§'ÔC=ªÇõÆzÓžðzg}D}d}Tèô€s ¤ÈÎ /»Wï¾([‹l]}.„›û=õÛ‡îx:Ô÷ÅÕ¿ëú?¿Gë·Í8vwÉ‘¢Ê“ýãqÖ}‹ç.~ÿp�Qkößóêž#Çc×/ïÝ«))iüøÌm߸@½tDBš/Ê\rL…ÊÈcáUD­24“º¨0›¤!Qj{K¦ˆÛÛTÞ~Yýîrº7ˆdîÌpgç´œSïˆ'Æs?ÿy½sç˜áÛ¦ÿáð‘}3w ÈMNF}�ÙQŸ;z¾: ïoïžù¬oÿ ­£5[ߊe�ÿ4–µý$–Yÿ –Iöާ50ðéÆ:L³±<¾0©ÊUfaSƒÍ2ˆØý#´‰ùr: +J—Ý+1)ÓfU±'[U.­z䑪Í�<²ùÒ÷W¿¼tõ*ùô£?·~üqëŸ?ÚÅßãÿÍ?ãï£$Ð"UÈÙzã•è +H‚ +ŸOÃ3gΜ®8Wa¬+BC4T¢cÔ^é8Qe¥UÞfk]÷XÒÆFK¡ÃeÉž0¸»ÚÞÒv¤©ü;a›Ss·.°@µÛLê +`iXàWo�_o�­Ÿ°rÅ´—GlÚÜöÞ„Æù÷¾2ñ¡õß)ƒw?úÑ›S÷Ò܃©©wN9µkåÞ#Osvöœ)«3phܶUO½àÖðäþeµö` TÙFZ[:ʲzÐ%¿eð3šæÐ¬$åë�¼˜`ðJ@ÄʨäeŒC‰D †ÓXº¬úõøßösñß+‚<Ê¥螳|äw|äÙ–|-Y¤rÒèÖ +2ÄøB%‘ÍP”G\�Çõœfü´ö¶L=…ñXÝit O9Í“YkõV–¬cÅ«"W�ì`‚H÷EE˜ ¤Êb¨rÔYš£�›mh„Y’¢†ˆü SwÞ6Í£´ ¨»®æWHø®îW‰I�5‹Ôl\·þá‡×¯ÛØq¡çã%oþãÒéâºÔ¦&œöç�>n=÷ÉGøÁñEüÿ†ÅONž°Y¤©ê¨QŠƒHô…€Í'I##É4Ê*{K�Íþ6�‡[b2^u÷”.ýî%þ!úÕüê×»Î'?>šù$:‘®b ËÃÀUj¯RšCëÐ r<Öj3 uR�pIÕ‹;Eõvb@Æ[d_vèPj]ñé¿ÿãÍ’:teƒ.÷†Í§$cõ„Éü5þ%ÿ†ŸšŒ¾ ˆ®ë^äi6É®ñ”î‹êä©.´=N4n†Zm¦@n–™y«î;ƒ£˜ÆY™á(8½= ™ÚÔ$ºôå›swrËÆuë*+×­ÛHÎâ�?¶mž0õEdE9“¹éÏ}r®õã�„¿£OI^ ¬�P�)þÞ¢e×é" ¡blèø¯}œ *� (òŧ¬ÈŠ\¨4ÂqÒÈ i°QÌñ@@iko»éåZvÛG)Š£7é£ &#”ɤH™Kæ+ä!Å8=Ìí£f#÷’Ò±¯õÁk;£³ŸõRÓ@¼šÐA’ ©¾H¥Ì-ÆÇḄ)eB#�r¨z.ÿb›?_óßÌ‹"Ʀ#-©Ïv[Ý(ËáA€>@eþE¨Œ†âššèLZu5)Ä/ ¿, åt$Ào}I6‹�Ê-ÉsU©Ê`µ(£¥1ÖÑ–11££{Ôö‘ûÍGî·N¼»èDÝ8ÖgŠ?ß/üÖ&Ô‘Ÿ±=_èÆš›+r8ßÐtšÎÒ¥t9]I7¤ÓMá΂ˆ‚È‚¨‚肘‚Ø×j²š®f«¥Õòjeµaµqµ©:¼ÚYQYU]S[íò éÚd �D]‚Ëí3~:aÞˆ åÏf×÷ÙÜÃsŸyÆ=§`Ô\ryØà³üÓŽexÍ×KV|Þ± +¯¹²X|Ò™3ó †h~ˇQ[ ¿æKŠ4»¢“ ÄVn¨r…WYHU·f—åD÷ãw$E‚d*Ùlîawè`/¼X½x±EO,ÔÖËüoÇ’0;¾ ëýp'wfg‘©ÖlØR½qÕ†¦ËµwÂìú��mì¹£¬åË/[Ö¤5áÜ3|pæÌ‡ñó~î�‰nLí¹g¿²rÆ4”‡d¤ ¼IS·ê¹гt&˜‘Õ7Ø„ 6Œ„`£ÉH50F%á×ÌI™¢È¢Ž3ƒ9Á\hd˜(D‚ã&Ål2½¢6É�ö–çùíù-Á¬üVD—UvJV•À‹�š’pÀb^•É@Mq‡bq‰¡‘,ÆmŒ2Å™!õÀ‰¬‡ÔCö(=͹¸7Ë•rå,%ßžÇJ�+ð*²œ®¢±_W#¦£éÖ¬h$þ!� yOÔ?±çD#¿¶ÿÅûéLÿ2\ØÑL6]¯Ç%;u]–Ü(¤9¬DøLXD"`S+ÐÛ[Ò1²¼Ùù(ký± jàß’ +É!ç³H»àñЈM‚£ÀùL�ï úÛÒ‘„v›Ó“ˆ³{ÙrHźµk×Õ×nß^+Ù.ò¾_|Î󾸌NüíSÔÒMãß’¤ ]YЕ‘I�j3…ªçü8Òèf…Ûv,{zÛ²{a4M£Y¿níZÉÖÆó?ýŒç·ý½vá"zMÈØ@h±ò¤/Oq&1Ù)IÌà$XqÊ +ÁÌ)1L0u�… r7J +£D2 IƒÍj{[[¦Uw”¶‹Áâ-à'Ê×2 xŠ"R‘BŽ—°¢„ã¡dã^¬·2—à +ü3ˆ¢D'�`‘R”¡t'I¬»Ü—ô¥9,WÎQF�áòiм€Ì£ó¤yòrò [.-—c¦Ãô°,+Ê2 ’­ž¶ftáo|(âŸñÙ›š%»:˧v Á…kx�˜+<–žåe@Àò4! +8í­@dp¸³éÙëi¼lÜ8=®•¬d­€! +}q(„„!!…@Lr-Cd£™�àR¨d1w Uýþs ŠêGó>˜!ä&YÖ,‡G+ƒð§¼úÀýå믟êØÀbý_‘·ýYOó]¨ø¨°‘È‹sØèC}= ¬Öa¬4ì ©•â*ã÷ÆÔzj¤:Çs=ÂÀØ#]‰ª‹$ÄÙ q=TK‹¿íœÈÉ3@më,­Õ˹é?Q''£ìÎP«W4Ôa—ˆ²mÿê»{ÿ|oÉk³÷8°cçÎÊ][×Oi.]þ§á!¶‰Ä%½þØ;_%v;•Ý«fóoêö>T¶dE÷î‡ãã?~i…hL‚Ôî@ Üéë1^&±ˆÈ(âŒöJ+T­Ï„ÔÚk(ÔbP�]Εt‹Uý-þ¶cÇô¸Ò–ÙÞÖþVЫ´–ûÚ™9%Á+Â]oÈé�ng]E¨?ï¯8‚"²‹‡l[}÷‹ï}}ÖÇÈ4¥¸OkCCÃI”Úÿ¡Ú±+·|+#óÒŸf[:à akÑS«`­ @$û"P­ +µ†�6Õ¨` +,3¤�\†,»ÖWÓÕ›Ö.zuÈР×-8 ÉÈêAÕ_V=±k3…^C˜ß¸þå›,­ãÌöuë¶í½ðñùÿîxV×Ñy~€´Kvè%¾ìH‹Ã$y Qª#ÖÄâÝL�hD¯^w4†½ä5Œ¬[x$ÄY¶C|d?£…yo–ÜV­®Õ^$hù•Í)ÊÝ΋éLä—ýPÐðý�(hõêÜ.É.+r/Òîûñ¹UýûožÿÜ�¾!Uwݽ¨|ê]UÍ�l?ÿMíÒêûk®œßV=yËOl‰ŒÞ²ë‡êIš,¥|]Cg‚ ê}ƒCœ¡!" ™°Á,Äê´�MuZTV5$4Äâ )TÃB ÔP%Ÿ°†³ª–Ñå±)¶á¡ƒÃD¯û±ÖQÉÍ Î§‰Dºoh0ÓÇ&`&Šª¨X±*¶HsdHbh¢¥�u¸u¸mJˆQôŽ$Ë¡H×AŽ@Ž,25u蜄ԇ§m/»sÚP>ö…†ž@¥GùycÆìœÑBÇø·“…zfÿ ‹Ò€‰A~ {$BQ,H"F¼dñ„¡«þ“;ç ´3| Zu»Íçûrâ¼Ñáf‹mrDZ(‹'Ù �ž×¢_·4Z_òÆDF9,ˆ‚åðØ(DÅ9ú©`¢šÕ3ýùZ“óçì®§#ºíÓ�c礢Në‡ cß´~2:?hó¤©å‹îž´yPÿŸ]°ÙçÛ¼àÙû7mþa×–èÈ-Oü°¹¨zÛù+5÷W/­ýæüv]gø·d™d\> m”^Æ�ð’ÂéŠzÎß"Ò$¿H˜‘[S7:ƒž½r…IöêýÕΡ™t9Ù£õÆ£}fZ/ázP˜�\µ³/è_Q÷õOÅ ›÷áBѸFh*ÿ–à ¤½ÌÐOÂTQÏéM�«;[Tó¢+W$û­–¨àÁ$ºœµ‚Öùz*˜`YdQ#ƨÐ(c +�ƒ&fPDçÛ(I4Í(Azˆª%Ã�H˜Ù™2i9’@zkŽÔ�)FƒE§aHD=H¢ÒÃÐå’ÞJC¨E¶È#½q·‰Šy�Õ3 …"Œ(²áWëøw‡Yk‡‚¸–Ì’ü_“°kŠU.‘²Vƒ˜ïëcPdb”¼„楔J„:j öÚ��&ʬ቉bB‰12“Z3ìF—9=V_;òXE>¯´|v¶\Û-“0ØÊp!g˜‡¤"�}V ¥NXÄ?ŒY4þô‡F¯˜óêiTIOu¼{~ë–ÚGð«öò½¼-jzÇ&Öú×÷¶ÁS:.ÿfý±ç û´Øk„¾�îö2*{]ƒ—/1"¯‘Á‹�c!¹í' Ì È’¶@d`iF“ú‰Þ¡È¿èo Êý_—# Id¢!‰,P Õ@ 2aJÌÔ KÊz´ +Iˆ[KÝăK[QÿãwèÔÙEß-<Ë<”<-­ã+´>¯Å+t÷9¤ZÃF¨µH`1È$Óœ!»Ôt›îFzI+2½ñ½­j¢Ûc Ó|=�„ožº3‹Ñ$4ˆ¿ÂøÙ½×Ñ04òúõe,�oç«ùoøc¢¯‚aOãè2­'’å‹éì‰4[ê¢OD�Õ:"CÁfë³2ïÚ+èZ‰£[‹«�Ú°nݦMëÖmÀQ)usO]úÇ›Åu©‡ádÑhýø£Ž‡'LA9ÈŠÂQÞä Õ?^ÕæØ(ºŽÎ<çÆœ’,Q'¡²ø`aDœ#“�#&£¬È§¢È…F™"ªÀq†Gx¸"‰L´Å¯ÇŠ´ü‹™ÎŸ�·­IÉÚlt�$— yòh¹,ÉH–%£Ã˜G{§ÒIFe:šnÀòD¤ 3x9jjåMüP+jâåo¡(‰ÎìøGGz•àa8‚/@�éX¿� Éì +XaŒ¯§bj6ÈS…<Á¬æ'Œ(´Æ¦˜�Ä Y ""-Ôdˆ ‘lªH?ÉoË8®…Àvq¦Eœ°9sÓQ’E#‡%¥ fͲzi2߉Jrùþ%|.*á;sѸ%hýôø‰Ù§ù´üôìÇçœFËù†Óo—hktó…Ö醴Ä^Ft?ìº|è¹DÊ;¦á¹�Ÿ+‡Ã:r†ë=FšÀð¼öÑ=C±:ˆkëüfi b­éè÷Ó$~–çj­G °öÆ:†]x ú¼f¨M’j])µ¶W]Òséænw¸Ý\ƒË�@\w\ºêoió·úûÁ´W|Ü¥[ì öTEžÈ…»e÷êÖÅËqé†m�­Û¸í1þæš­ß¾{öÛ­kjvs~ñ"¿±{ôêå­^µbùj|²öá‡ëj«6í˜è>¸êÀÙ³Vt»ßØýæÅ §êO¡Ùþú×>´z�.Ó^z_`ÞÇC?Ÿ'j�†ZÛFTkücœÕ¦`¡®p–“a—-Ý­'™Z¯C�f­U¦3Jº$�7Åé�Ð}ßì¯yòÙo¾ztíší|:üùk×n{†_å?ò!øTÇù›Ý„KxÿÅ+ï+ÞûÚK›ž´‡Ÿ©?}T߸@SX+ÄB?_•c£jek¥ºÅ^"<#¤Nnp8]ȘjœK$êBç`j ÞbËÕE¤ãà°Ã-ņHÔÏàöŽ–äÉ=ÿ�T~áûe'ÇL;<ë™—ŽþíÌŒý{êx½�u Qì +D@7¡A9.²ÖW«OQ%l¡µá5j�7Á‰! ²ƒÂ⼪¿­Í1°jÐÞ¦~®{GºÞuǃ¢`öÄc}ᤫ£�O ¶Œ>öZzâ¿|Ý~žû¿Ed±�Ÿ_³}ûšõ7²ƒ‡½IüSþ÷âüû~˯¢ +´=„6Çu,<üôÓ‡ÿø‡çÅWèÖ +FHôÙ‰˜cl£ µÅ#¹x�I='Ö©¥Î¹¶t¯Ûʲ½Yb¢¡P”ÇŸGãÞD½ýo4ЊQM#®µÖèE,9¥­ÿ×úÜŠW¯ß½Ó u8¨Ó‘¹ ]Û +  &'·tsD]NE˜%³pæTY¢ÒÁwáÉJ‘¡?Ä–++ ¡˜ *3�°,nJb=¥d9QÉ#ÃÉPi¨|7™ÌФ"y²²€#¡‹äXɨžóŸók¹u å©¥×Dàˆ–b‡øý�,›¼ÎÏê}œ t»±�ïsGFA8‰ª� ¯%!/š¨µ†R犎t™¡wX¦”í‘·EÀ]À_ø9ÑϳæþÄâš{‹EA7wçéøEÞ‚|(vƾqw6ÜsòäÑ×Îíñ!Ú¹bähT‹ÊÑ"T—ÝûÌèáü ‡Ÿâ­qn´u¯fÇ"¸�.§ÛÀI>;7×øƒ¶ÉÊ´…u9&›6ì¬Û°¡nç†÷¿ïè¸ú½¿ã{| GQü þ2oâ_ H4œ?ÄW£_£�hZÍWkv© Äa”ùbd„–d©P”ó&![i†l…tc°ÓCen—þ-ºu"¡ÛD'Ž�{àD¹�<ãùr~H6FH‰¨‡4 —&£{Q©$ÊÃ0·XÜt#«§úáÄ àÊ Öz=‹¾}-™¾}= |@ÅŽFÑK2ëKß.’½$1SE'¹-=LK>8�Ïú‹Y«– ÎúX�)¾TâÕjc}Ÿž—`äPÄ>½ƒ– £HšÏ\�/´5õ‚ý⢽¾7Ïê±z?@ Ñ‚¹[„ï2¼Íªc6~²3¦å"‰>»R‹ÿHa£Q’‰‹eÜL?ô�QùbO�&�(¬n-5èqútÇ‚ì.\|-¿Þ‘«ç8b¿Y»‘�ꋊ|Ü»£Â2Í�!¥G ê7Ó¼ËW/ÿBĦ£÷ÜÍ?ã'Pн{Ïè “N¶´œ,znxv�¨-¡ºG�3ý|ü]þ6‡¿ëë§ñr +€JšŒ^_˜QÛmIk%†rˆK†ƒY¬ñè"Š<.°Q!°${Šô㯣\ÿ ”Ë_g­ ×W54P­6Àhc/Ÿñ2ꫤ7ksCègcýˆXM?�‰XGmô,^®­ëáF Ò;>ÄLëzxùfÀp˜O"ªd‡°B_,d¬RP#Q[B'Ç•6“‚ ØB¥PÐÛ0--�­ÄZå¡7Ƙ–€‹¦¹XBÐ&T?�¾{ñwüË©S›šìäóêëU'ÞßµRúÝ%�?ò˜à5ßHƒ×h2*^Y1‰�`ý&±ÛqÜ&�ã¦B“Â0%pÐ ™™a´¯”}�^/ú/v¶žÿãÖW޶»O£¦–„º“,ÑTdz/gËL&ñ7 +Å¢Øq”âÆ^–¬xMÙø.ý�?Ê+>Bí¬µãmœÕQè¿„×v¬$NÑ£wÒËøªT¼>: ‡(Ê'A«ŽÜo˜8r¨¶ÅÀ×gJ[º �ΣÈ�üˆTÂ7¢±\¬ÏK�í‹dbš¦´BV  #ˆ¢ˆûÑZô8~ûV±ǃܕd_Ç—ïc¥#›µNº¶Fì"»ÍwÅy^úƒŒ0t§ÂwÛ2;}×€…ëÒåþçÉx±A^åÇø‰—Ñž½h� µíÆÚOë§ôð9-Äj#êT´ÑeqY3° ÒõI\ßr7 ;µ†‰XÜ_Å’¦h£¤¢m§Í–øå“&?趘ßÌRØ0^ÃÀÁy¸Ùá7—M�웟ß7rÚBrÕ_úù«}óòúö?ù{ÄdB. ·îiÐTí%¶fÿ×bSƒ´M`Ϊè-oë}} aÔ†ÿ^ ©4oQk¤Ú˜š„:o˜DP\‚ÁeNŒm—‹mZÚ¢'rWÅHÑu �{8ÑúA6«ÖNÎîeËŠ· ,OHÄ×lÛ¶fýÆ Ÿ‰îËÉ4Ñ}ùç_ý–Ÿç_ ߎk‚­•ŽM‡½IÈ�"‹ cûÿ ˜—ó |Iœà9÷Æz’]�; ^ôͶªØfqšCÌ¡Î�sJOœš,zË™Y½²{³Œ'BP˜c6HˆÉ–Œ�H& rF­-Áí­uÕØêäèŽd‰¸Í +ÍIvGt�¶¸i˜ÒÝæŽVBÔèT›£�úI[‹¿E}M/ŒƒË­bN¸ßk¯^þó;"jKr?¹Å½Ë*¦Ø$ ÊÄ4”%ÚzNO’ÐdkKw�hعqTE��¾ýQö ú¤D‚ž1²¡uÌ„Ãóÿ¿x¸23ýõ? X;¤ïÊÁ³çõžv÷�ß1­g´G%•g3jJã‡ÈIOý³èó²!ƒPÈ[›¾,öðКç#œ/'yïÝwÍ™á¿�Vûz„Óâ Ñühݪåу}1Š>÷dE.TÄÜ«{åMIäÑ¢ÿ3{å±WN,¯tÙw€œò·“¿ ßÇZw^/oØI·ŠÚ¶™¿O§Jv� Ò Ø—«tïr�êµ§©Î0ä­ì¶%®{µË^¶U‡º]*•º)=˜¤è0Ö=>”€ìrZˆœ®žsæ·93ýùmªÿâea›ÌmkÄkWõfOÀFéȪUêIVM¿ÚÊ–èb:­š r²³œ.$¬�dÕº2>¸èW‹ÿZZQv®•œš]|zÎo¾´�Ý3ñ®§Æ½ó—K¯àA| YZ<ýÞ³è ºxƤâëÇP +Kíq°!FoÆ5Ö íæ³vòu|6zR¼êИ�hwÇëü€Øëxãy…µ‚zúœfYAp€ÖZ”ZãFK�Í »L�mSýç‚}?õòÕwDtÏ ´¬µ ¯ŠÞAÙÝÇK«ïãkñÐ÷¿ŸÙܯíÎ;;M5ømü‹/{jùŽàÇp“ôª–'‚(ûˆ5ËŠ#êëëÉBr—ÿ÷×'’‰þ}z.—�çÓÇH¸~oX¶Û‘•ívä£+ËÑ<ß¾}ûÁ0ò,ݤŹŸ‘Pe‚˜¬ž{K Aoeæ +̰�_Eoà[ù£Ð¬µã}TÉ+pŠÞ�À3šF'½Mî±äqz*ùyǯs‚Ÿ×7v Ö¬ä€",­ý ¹ŒÇŠ^Æõ�7ì¬YïltùAô,”ÂI(†pÖB%œ@óᬃp6A+º¹�¯B=4à +T §á”‰c(A hšØƒ%° vÀI8¥hœGg M…IP ^Ø›`ì…K謅½P ëà*lBÛ ŠÀ í°* >ÐSh<¦ ã° * «plÈ •Pâ¹U� » ŠCŽ€|¦É“a'\F‰h&ú+Ç3ðN¢’‰¤Š¼AûÓ‰t ÝO`ÝØ"ÖÈÙ[’* ’Þ”Såsòe%J¢´(" Þ5�‹�_˜¨i¡i‹i¿é=s¦yG„ä…üWȵÐa¡‹C õ[ÆYž´\SÕ-j“Õhím=lKµ}öë°f»ÙÞݾоÃþW‡ÑÑÓ1ÞñI¸¾4|Gøþð'8ã|Úùžó‡ˆþS#FÎŽÜ5-êpÔ•h):"ZXQXªwƒdX`P¡NTl2²ªýuðà pZÀ‹ø;YýA<4Ž1(ð×À1éržv9f_Ž%ˆ8V EŽM�‡òÇ!a‰è�Àq(”ÚwÁ@(‡Å°î‡yp/”ÂRˆ‡î0z@> æh÷•Áâõ q·Î»îIsµY£{pB/IÐ,'ž-Ö>…ìå0æÁ¬€|ºÎ� +(Ó¨^—vê§æÁ€wïäñæb¾ þ—²€Ÿ‹oêDœY ÷C9C…ÆçMnŠ5 „ÅæÁl¨Ðø¹©‡ŸAPz˜ ¡B£¢ëd™æ¥Úœ_ÐŒ°÷­éßôð%�ÜVh:ìÙÅ:âXè妭oÎß%Pª�ù)9zvÊ™¦áN¼FYŸ:íy­Þjý_–:¨9�[ÝÏt oõº›-ÓôQö�œ %f. +H¨ûˆNMxŠ�¤\ãf‰¦‰ù0æhôô{ºúñÂJ-$$Ìë´ÇÈÓfçÄÀS³`6”kÈpÓ]±è¦þ D¼t…—-¹åÞà\Yü“Ðõ9!•˜Ûº¥Îßêkº6t$Ÿõ ö”u¼¶/Ó>oâÇb‹¥°\ã·DCA;õMýÒ³B'Ë;ù£ �‹¹D4Á»ðÓû;Ïèœ +�¥öìêuÁø%FÑõU µo7%Ï +ÍÞðB]Þ{a¡&MiàÜý]0TH§s²¤sŒÛõ³äßÊÔãŠoñ0!éOsðËœÜ:Þízù)u-.ÒF’ü<ªëVï‚¿²[èÏ,éôÌ༹=ŠÌ à�àøæHË4©Šµç~".&tÊ}ûâþ`ÔMèâmúÜu[œzÑcS�Wá »Xâ(‡y?¡±¹ð ¦gÝ{gÁbXˆbbö‹h|¢«ýužyÆ;ŠkâsI€G1Î/ù‹.ÝOa¸¸Z¡Ýu«†~1_I¸Å†ÿOç¬ÀØ…�1ûæ¬ Î(‘AèsOÄ%=¾ÜNQ<3À,íAY�‹Â«þ5ÿøÿ±~^*=ÓÒµ(âbI§¦†Á`mœ±0&j㌅!0&ï] ÆÁx “`8 ‚Á0H³Ë트ž ÍÆÉ0\£8îÒhé4ÆÃ�öˆ×h‹LuŒöm$ ‡10H{v0ic † Õ±0^£=ÆÁ(®�©ç˜c` Œ‚»`�v‡8 éHP4Â]0Jû&ÎÞãaŒ… š>åAnÇh2 �ñYkè–Ð9caLÑî +Ã`¢Æ…ibàNñ}¢&�°ÌhmÔ‘ÚY�³±+‹ã›TD ÆÔùúŸÔ9òMþQ0JÓ­�w‚6Â`£;é}g¨FAð­kã.M>ÁßpMBAC\ZúÕyçø.V6 ÙMp.ž’�LøII‚ÔnµÎOyGpAKØMhj”v÷ƒa FI?£ûãpMÖ�Ýê4u¿×}B¿WçIØgŒfÙ;á®À:½Û¥Ðgˆàÿ¦ºÞvÑÙMë Š‚ç ?›…—ý«VÄüœˆ»„ˆoA-VÒ9×ç‹>FÐŽwü38óÆÜ¦ßà< +Þ÷Ÿ`‡N+8ö­úzÒ9Ô‘dÌDWÇ®Áð †{‹qmÉ¿Dî®Ùãͬ´kþÙ³ ÖvÍtªÝ[vÛ}7Ïêø¬Ç¬›5O×î§"W°Jîy[öÌ>tìÖk£®Ùo±–§ë¹à’άD�å�™‰ÈþõŒ!XµˆjPÄ—[ë½%Ú¸ºdúXÿJKÏ/Å}úhK~B›¿¡n¯/BG‚ò2íXô’ôŒa–v�¸Wœ趪X—á?·AP–§qv‰EE.:OÓ°È'Stïï¬ÏnêDh@ïn•Ýfõ›Þ'¨åýK*tpoÎE}§ç"¯cÿýµ4Mß `¤i<Šg„T- _ i·e”b-@û¹ñ˜ÜAxËO1t‡)0 î„ÉЊà. ‡ÀH“À +¹�ƒµ¸6Tüÿ.0FÁ8SÁ&0Ã('„ƒ²`L Ì„þ>H‡lèI0î†è± ^ˆ€H°Cª¶o8Tôs! ò 2 ò!¡§ÖIöÀïáèn0‹0úÂ@ˆ‚8èè2Ì RÄßôÀ¹bѼÌì^ƒŸáÕÈq`ût6 9 rÀjíÿÅã@P˜öndÕŽUíÝ;� Pí8äÀWCÙ/ +�•@�¼@� 2� £FϠݥ@($kÇ’vÓŽ©vžhg°vù¦pÂ9éXIüœ\çäZ&ùñùa%ùþjûž“ï�Ñ«ßMaW«ÈÕÕô»öDöÝò��¶'’~›Æþy�|›Fþ‡“+œ|“I.ÛÉ×5¤í«¡¬�“¶¦g}7èWCÉ——ŠÙ—5äR1ù'ÿ"šý�“/¢Éçœ\\@.pòßGÈg‹dŸ]#‹$ŸÖ�¿rrž“O>v°O8ùØA>ª!þÅÁ>ää/›Mì/òÁJòç<ÒºÙÄZóÈ9NÞÏÈÞçä=#9ËÉ»œœyØÊÎÄ�wÂÉÛœ¼UCNWzÙiNÞääÔJò'¯sò''w†°NNprœ“W99¶ÙÄŽÙÉQ3iþÓÖÌÉŸ^™Îþt„üi5}列½2�¼â£G¼ä0'‡jHSõö2'�ÕXã5òÒÎv�“‹É�bòÇP²ßF^àäyîë àd'¿·‘Nž{6”=—Iž %Ïìµ²gº“½Vò»§SØïV’§SÈo9ÙÃÉSœÔïŽdõÅd÷“*ÛIžTÉF²‹“Çw†°Ç9ÙBêv¤²:Nv¤’Úꬶ†ÔŸ�°°B+)lÂà3оP6ÀB4¡c>õ˜™/”øšÐjŸ�˜ ¬ÀL +š�ÏWLûsÒ¥°~×H>'}»“> +stream +xœûÿÿ?ýþ +endstream +endobj +2180 0 obj +<> +stream +xœm“Mo£0†ïüŠÙƒ¥ô�ņ@Ú(Š”’ q臚j÷LìI)ØÈ˜CþýÊ'­V{ôÌŒ=ï;6ìÇûa¾Uæˆóü'‡Íd%Ϋ—vHÛ9õ¨Ý+¢BuËŽ+¬‘#:¨š]£;—0Öhy™ÞjþWòŒçNøPM£3}ÂØgç.¸‚ h‚F¡v�»Hû…vìŒ^�HÛkU™Þ‹“4ö€ôÝy@§N+;ÁÑ÷MDª“.Rx˾ÂâÃutØ7úd §*5 ± ýÀs7:{…Yö +O”y³ +m§Ï0»‰ý–> +stream +xœÍZ XT×™~Ïý†AÆAQþAĨ£hŠhÄHÂ?ƒ‚ `”X£$±ÑƒénIê&†ÐÄ›&45–Ô¬ÍÝmÚ¦©O´ijÓ,Ñ´Ý>”¦­Û͸îsÎ�á/ÆäÙî>]F˜;÷œóý¼ßûýÜy‚½ dVßÑî<™ÿíÙ^ؽu-õMÛ²r7ŠÀ7ë;ꖹʔ! Ôç«­¬IÛùT^ +`¾¸aìg¿Â0Ã×Ô¾3T1dá}<�ÍÕ•³^KÂ/‰ÏM•;[T;ý°88·V6Õ~˜ô0`YD½ÖÒÜÖN ôUÀ= ¸¥µ¶eÕÚÇçÉE€ú1›Ç\ì9v§ ø_ì›°‹.0›É\,†¹X06©¡Ì…7±É¿kv¨1ô2s±y8€*æ‚Æ\rµ* I EºÕPDöáIz“î‘+)]Ê›JôBÁ=xßÃ1<‚—чËÌ�^¼ÎÜl.޲ æÂGPX;ÏL,Uô&Šq +½x?``O2ÚTðóxƒxÃü<½üÅ�_Óç]»ÁùòĴÔ)�– gŠúÂ:œýW¯mPcyYŸÞGîà>Õíü¬ÅÁ´Ô‚¢ ξWò–û¥æU,OK-(ÙЧ¸Å§åe‰‰i©yËåšPÚÇÝ}Ü�_Ñç¬ö9ï·ÜïZx¿¥va À§u«>þ$A˜æ UÏÀp†ó*EEÆÀ¹¡¹°œ:7”iM´º­‰>#m;ò¡ÖdþøÏ­†0Ô›ùy„`¶7:¸Å ¶(5¦Ã^¹ FΦ‡ZF†<#‹.ÍEÆ¥¡ËC–áÌfM ¼Ô›Gn£ÞÑ}Ê—F_UvñóÚˆvFÑ:À xÝ©VÀ€¯Û®rÆÀ \!E%RÜœp˜¸ª0޹”dŠÎz<¹™%‚,ü� K°ÿ—¿Q–ôÂcå^;ñu\1�©¡†�XGëÔ2Cp9Ê�,‘%Æ2µS{T{X{”¥üñ#µâ“µb¤›6‹l¹zQÝ­î‚ ±Xåu…F…Ù"ª+ÌêC…=èðPëô(;BÓÍöX»!eºedhhd@ yéòÐ�å7Öa˰5"7ÓòVè[ao™ßŠz+–—»ÍÌå„Õ‚yX£ãÙ<ÏüJg®$C�ZXwîí·Ú;,þ?ÿ‹EŽÙ‘¯×lùXik©Ý’3{6Kc±,‚-ÑÞÕ.ï¸÷î…_b í;vm󀚡îB$2½1FbæP0ƒÏz ô°‰)vä„í!Ù6¡¡¹ÈXtéò�5W¼r3E�²’]I†(§¨%lžÇ®fhï容U{�¹º7?Þ«¼3š¢œ¸³áÁ»F÷¨ÇËëÞ™ÄÐ ¨sùQDb®×ÁÍÁáÔ+;Üe +1**ì–³ÍrnÑÀÈ¢O®ˆÙÐåEC¿â¨D«Í~‹r%%g[³­Ê l�Ö™¿nð¯#/h°x~T{mpt¸`éà¿TÔA¶ �W/ªëùG˜‰ï{”m›‰™æp‹5‚‡E3Ý‘¶™PMñ�q͉-ÓL�Ææ°ÃÖi­³fFÚ0Ãbœ5Ã�`±ÇQÒ,ËÈÀÐÈÐ%kDn®0N„N0÷¯ÃÖh?³ÌoX£sýoeI,�eg͘ç±G[eÈ¢löÏ¢ljbRòL«ŒfvVòL¦½”©ëZ{û@Å–Ö’{÷œ<¡ý‡öçæ÷}w¼×Ø´³h•ÒµîôÑÿ.#]û·e+Ö-Y;7óÆ_ûàýì,6·´¸ºlYmœÀ·çêEÕ­VL�k«‰1ÍQì�qòÈ„�«Èg›]$âšmf®¤äæ^¾fMsk¾ÖÐó vŸòîhi‡ï�»”{>é9V^wŒ¹u�ä~Š×f¶‘Ñé\ +3Úe97 k»o†Ñ€˜„hs¨!õцzk‚Ïyÿôz×kkô¾P¸¦G9ìé d·¥$[F†~22äñX£uƒüô± Lô™Þ´¬øÌ„LgVâv¥5ªÕ¾ûÙ~SgT§½=¬ÇØc:f;ÕÕcï‰îqØË™dÕ<ÏüÅÌ[vÖüÄyÙ2Iƒ²—°yN»òò“wo\ݾ—¹´÷²�Õ|ÿ¼ÆL?®¯iøÁ–ʶ0ë{й´nÕÆç³míÜ}sÑ�^8=we~öÂ+³’-ð>.yµ1H÷N‹9ˆ»�†vXda›iEŽ!{š(Có"s¬~%NLQÄT÷Í'ª´QíW,™ÑƧK¼_/î:r¤kÝ?Þ˜;g[Àl,ŠåÎNÕ.çÜpêøñ“¹ ¨[2îé^‡Ùj$Ùj¿vÙôÎ͵N­ZÖ U«h㔪Õá;´{t�Á6ú¤([ +ª®^T›Õ]A4\ÞHƒ/ +¾ðQ­£="�ì¡)™OºÛ–á̱ˆdg%3½p[àJ‚âkع³aóÎ�£‡Y̆YŒöÛá?h¿¥C/=õÔKâWëeál±öºö'íOÚël±ð÷êEuŽº 3ñ”wqX¨bIˆ6*A¦ø„x·Ñ¤šBâTuÐÖáðYÙAt¨>÷kë¬xcHBl–ÄÚÍ9Av[RöuK—àÝ„êå/^/ÌJd(÷ÎqÂÉf()Á)ÆSJHJhJXŠ952Ö•jO™ž—Ÿ’�✕8cfT9 „8AeÎàšXæ2d9œ?Ïc§÷×>¿¡ªæç!¥=_»x©ö\Så·Õo.8´öø‘ߟ»ýŸÕÅW‘•�» ÈC¶ägë[.CÎÂE¹ %E$÷îï=™Æ^ÈÌšA°œÂ…™ ZÄx’1”É­‰ìU¶ZûkÖC. >ÂÏÀ&¯›)¤À" *W œ«npVC•¸ËA–Ë}Á%}¦µ7¼Ï"˜×,šœbb&âªjPËQîsKT|,ˆ¥h�iõÚc,å¯êûWæðó‚Ïu?¯OZ¥E­G8±`˹�‘�¹È¸<䛋”í]mŸ§íSßfЦAA÷Õ‹êƒ !˜ŽùÞ¸ÐprÄÉnôEµ†ˆm��=Âã5اeƉ´ÐóbHÕ_p˜ž’±LäE„Õ¢ˆ¸‰ +IÅ�·ß¾uëí·7j¾ù]ÿõ�s¯Ôv§k¿b3”´ÞþþÞÞþ~e~y¹ö3í’ö;í'U•Ç ¶€_+ 6„Áí�àf]Ô®7yÕ®À,½ÓÛûˆÈP‘žÙþÞž˜”ÌîÑÞQ +‹«~¯ œ=ÅÞ¦ŠÁO¾ïÖÁ_è3´8õ¬v„ðq7S¡düD¥¢¬g«g?ÉÐ.œ9#C�.@=dè„a˜í� +5wá°Aé2µ©Âì¦ ³h¢Kk†<—‡,?ÍŒMIX”(˜ôž²œ5Ž(¬Id ¢ª#�ƒƒÊQR´ºM½€š«VÀˆÞi¬�âʨ‹T6† “HB1ËȹfhœF‚>‘‰QL(e‰ß£=£ßQV�4(…£?R+>­¸ð¡Ò#ý©Ò6¨7©»‚YÈ÷ΚgLžIF_\äÁˆ»/œ|®3‡?—’{¨'Â`w¦¤XF†DôçÂ2 ºŽÈûr§PÀbŸM3ÑO«%BôçìDrµ–oÚ¶mSy+sÿEû㦓å%ÏÞúå;æwŸslçziiäŠÀ ó�3IBà|àtÚgè©“ë: +í~Ä*íhÁBd 5þýw`;ÒцflG+ªQ+϶JDÒ±µhÇŠ Ò"õéȈ5�ºØU…Z4¢;�"ãô¿ƒ¾ˆ£éššu +áœdó§™fBÚßðÚÿìu~ŽÏ ~EÌÅc�Y+¶À‰fÔ}®-³")¯IJg£.Û'×jý~ÕK-eaŸ�S'WkÇ´éÖÙ$ÖÛ%ãÅq¾ÅÏx]C3ªÐî�°ø­÷ûRíG: ³]Z1™ã•¨–ûšÐâ—� vë¶ëLª•ù£38iK’däÄÙù.|oF5Pé÷Oç`5¶£IJ¶¶�áS‡4úy[ƒ�.p¨F#¶K):&;$|2çÛýȈxOö( œámcÖn—¦NˆŽ¸¸ŒÇz<Ûà“w®åGꘟ²î8¥d=tÙ ~T'Gÿú^�Ó­Õy¦{8™uãí�x4}! �l¨“5s«ßC�#º4ÁáI³´¦M"±µ¨–òô=yÜ诒� Ï… cñhÃB™�¥þS•¨B³¬ ã1˜X‹Æøt%�CÈ,k›´7�+-׬Ï ¯Dnë‘u~2×t4ôJ^y�x +Éz½±o’ïãõã‹Ä¢ÒÞ:Y„ìôIH]ï¬À¤cÌ~¡]`.r9Pф킧­cwtK¦/E<'².п„¯íh”ŸÆ=g²õ~êþÖ£Qzãóßk�PC…wº%mc:¦âÓö¹>M¬q5“&<½¶×·d²¾©¸\ËFÅ­R“ð䳫º%ñWØ×4InàNÛ3y3µ‹Ôúë�°x\ÓéU�<Ÿt�¾˜4æ÷Ôb ë&M`›ž;«¦ô�‹Þ›¶ +64NˆÄhFÃ5«ÅN‰³ÎÞJ´ ÅßÅDö‹n811þºÍ×ÏG±&ÞÛü6 +=×ã‹îݵj¸XÝ.wMF8éºóJÒ¤þOs¶Í?ÿê={<ë%&=÷D_ÒûËT‰âL5¶ Rî’õ¾¨ÏçSç�ÿ‹ŠõÙ^é“–Ž¢è‹ucHÝ„<©g +Q*õ¬Á +”â,E±\ËG œ(B1Ö`=ò±yX.ã²T®ˆõ$™�· _J\ƒuR–.£K¥ì28¥l1©ÊOÈG!–˳yØ uä¡DJ]ƒb){5а +ùR§>cbVa–Ëë•rÕõb�|ûWK[tKK±f‚ÖÉV É%c–­Fб 7ùW—âFäKyÂ~¡…¼.³s…ßÒ¥#!YÈ\†uX%?‰»ëPŒ"¬A‰ÄSH^î·¶Pú°Å~_ò¤z$t‹–a ŠP&w¬ÄM(•VM¥þ�âs©ôGDfµÔZ ïê–­ñGY\�KÏB§n‡Àý˜æéÿ*¬’Ø +K¤†<,Åê1¹„Ý:뤾|é¡�!ÖŠÏUc;‹'DEÄt©Œ›°\œžDJ®éI@Úäè\‹ B–ˆ›@j•Ü]‚"äa™”¤ßÑù˜/}]æÇV—©ó^焾W·IħPFv-Öùwèò¦z¡gˆ°Ü =Ký—MÀl<úB¢°9`�`³`Ù§Qù',» ħ +‚c"Jºåz¾è:q\ççg ó +§àÈ£À¾/R;tYÝ“#(ð8éꕤð ÉÕkWvʺ×âïkmŸêܧÇñ©tâü™:¡ÖNœô*¼Rîmš²oü®^Ÿõž5þÌ3q†»Vç +<%§N™~Ó‡^»õg£‰Óo�œÓõY°ml*ÑûGóØd"¦}b<µˆ§AÑ_&?ïµI½ºgº®OËÒçK±O×Öv 4¯×¡¦>! +[FBòy-¾KÒ'†J¹Gì÷ïœòT¬ûðÅcðåóðwÛd³hƒDXÌ“é~¹­cÏgã˜ôo·š¦D}œ}BÚÂOÍ¡ƒú –‹ç;}Æs…Ðiú¾_Ë�xoA2¤�âìN¤Ë)¼S&Jñ�»ü¹ú ùmñÔïšñ¿²f¡ 7a-nÁ|lÀ:YW ÅX+r‘ƒ<¬‘5½7ýÊ^oû�NŸ¦ï�¦—®Ðw¯Ð©WòS§éÅ•tÒCßé¦<ôíêë¦çŸ‹äÏo¡ç"éÙHú¦F'ž ã'®Ð3atüéøq�Ž¿¢>}¬Œ?ý=½W=öT2?VFǼêSÉô¤FßðPo=ѳ’?¡ÑýWÏz¯ª=�/å=+éñ¥tô1?º…óУýÓ:r…¾®Ñ#ô°›ºô}U£‡4:¬QW­…wí¦Ùøƒv:ô€…²Ñ:XÏzèþP:°ÿ4? ÑþûÊùþÓ´¯zßWÜü¾rºÏ«~ÅMûî çûbèÞpº§›îÖ¨³ÖÂ;=´wO$ß[C{"é®nÚ]C_.¡]uñ|—‡î¬µð;=Ôá �uñ|g íÐè�ÚÛÂx[µnKç­mK§–ºxÞ²›š´µé4ߪQSc9o:MM{ÕÆ7o,§F¯ºe mnpóͧ©Á{ÕM¾ºxî«¡úºx^_Cuµ^OµªÑ¨Z£*�*wS…F·ÕЦ[m|“F·Úh£‡ÊÒ"x™�6ØéDð[4Zï¡u•–„ðR •„PñZ;/E¾ÖNEZ£Qáê^ØM«kh•FÝœÉoî¦üHºé +­¼B+vSÞòHž·›–GÒ2-5Ó—<´ÄJ‹™ùb�Ý™é†Z˜ÍvSM¹{Õ!´À«æÌw𜚟Áç;(;ËÁ³ (kž�g9hž�> +stream +xœûÿÿÿ ÛÞ +endstream +endobj +2183 0 obj +<> +stream +xœm“Mo£0†ïüŠÙƒ¥ô�ņÚ(ŠÔ’ q臚j÷LìI)ØÈ˜CþýÊ“V«=z<3ž÷µöãý°|RæˆËü'‡Íd%.«—vHÛ9õ¨Ý+¢B5GÇ5 ÖÈTÍ®Ñ�Kk´¼L +çœÿ¥<ã¹Ó_ ¾TÓèLŸ0öÙ¹ ®aA 4A£P»Î]�ß%ŒýB;vF¯A$ŒíµªLïÅ�I{@ún�< ƒS§•��àèû&"ÕI)¼eß¡øpö�>È)KMCÌH?ðÜ�Î^a„Ý�ÂEÞ¬BÛé3,f±ß‚‡i.èE«¨Uø¦ÞükÛ#¤Ñðm5ºñµôy0n�þ~1jA¥Q8­DÛê3&Î9ߦ®ëzë;þÏ +*;žäŸÖ†t±… çy¶ ”ÊöD9ÅVD÷D‚hhuOT*ˆJ¢X÷@{r¢GŠÅODqÏg¢QEôH´#*‰ö�ʨº&¢~ÂçEEDþÊœˆü‘È_Q‘¿’t +ò—‘ýDÑyä/�1òWÆXô—…ˈ§î¯ÅOßmäd-j†/\º¿áNãmŠ3øªð„ñŸÿ$Ooõ_rj7 +endstream +endobj +2184 0 obj +<> stream -xœÝ| XTW¶îÚÓ9§j¢Šb,(°@ƒ ‚ˆ(J‰#ÎQ£h¢q@Ä1*Q´m5^JŒÔ˜–xÓÆÐv´�!84Å$jc”¤3Øv®&Ý&ÄxÓÄ$Zlß·Ï)ÍÐý½ûÞ÷¾ï�Uuêœ}Ö^Ó^ë_kX’g<¾8êz•l€c89Á¬Â‡ñÜ+¸ öÍšW’ß+¾ä ² %¯`æ´¼®Å¿Ÿ�æ€3§)µ˜ôè� +xœµ—{pS×�Ç%ìk~ˆ`U›lIYH iy.�„z“ðL�0(8ØØ`aË’-ÉÉ–mù%Y_I–%ü�ßøý’¦6ÆŒ±‹CJ“nhHgH“mRh7i�œK³Ù‘�¶™ÎnÈìì?çνsî=ç|~ßïï÷»BAp°@(Šw(²6l_±|Éz�*q‹>A•|8ðx3?—Ÿ‡p)?_( È€{cøÌy,úî‰y!Ýó‚�Ü|�@øèìÀ˜=g¾@ðÍ7áÿ¸‰_¸ì R$†ˆzÖ%j)¶$*Ôúd½añ†ÊW._ñÜ’•ËW®–¯Së5êd�|·N£Ò¤&ë“uK廕É:y–F›"OÖɵ +•"A§H”g¨Z¹^©�¿´'z·|³F­—oK>¬Pëò%Kär�B!Wêõik—-Óg$-Õh“–Ѩõºeª{“tËï-ÙüêŽÝK¶mÙ°iGô¦¥úl½üˆF+OTè’Uº¥Á �PðÏøÿ¥ÄXÏï³S­ßi‘†ïÖ„Ïr…‡"<Ìê/·;œv§Ým¿vÙÙS娶W”»Â#ìᢠ�P æ þÀØÐæ ‚¨“y‘‚¹�%‚מž±bÆ� êàG‚ÅÁïp!é3çÌì'« ?+-tVhFèÛaÞ°_‡"VGüR´Uô)».2§2üÔê§ä§ã-c4ÔO¥Bñ(ÿ%©ÍC®T›­4×tè�â¤`{E��ˆóºŽ8”½ò–¯»–Æÿùò› ¡Ák.,`+²g¢v¨;FgÚsús¥¥(µZ-¥Vk)JaqZÖ +³«D�°jWq¾½ãÝ�ètæÉ3†—¤°–>ypÇ�mÛ•qz¶qY"[Œ\"5ÕÀ+mF9ʽDœ_7V_{>ôfõfñhoVÆiù º²C&bÐEB*¢OѶÿ�ôk[“½¦Ø¨ÇÐ5<ü³©ÈdºJÅt©ÿ6�%¤aôqM#‚øË´FBƒ×­’ª (�Õ’nÆ9«�vÀ‰ŽÒ áãÊ›6ÔVW¡®Òcf˜-ä€1¦`âëIh0»Êì Ìl5“ØEÎVZV‚Rá‚°{½£çÇ<]D¥™P¦2IF Izá•"=â l3�L¸Π�ø5^mÆÎ¼/oøÝN9*§ó¥"FøWF„|a—¤Á“ôhˆ +6¤9 }’¿Æ9*ìåpÂYV› 3,¥%%ϯxQÿri‘ÍhC:ŠùÍ í8{l¨‰°à»Ñœ%×fË(3•å"Db0B]êÈk’�Çç(ÆpÑz¤ª]2�Æ}Dx§ÿzÐ�|¼¤“náU(Hcr¤w/„dÀ‚âl²Ÿ-䊳ò“QC*ѧÝÝ4Bçuž ´Â�:a î^å,6µ%$.Äh€ÚЄrÝã¤û­œ·] Fäʆd"Õžå TÎ_âÊkí¨µ“þ�Û-†m2¨JËÌefXd×£ + ð ¼�ˆLE~þ1?Ý>ÉÝÊú²u\’x²ZÍRknñºgŸ‚ ÑP÷áÚÜÇ|äTÈ$�•NFû×±u,Œ=Ê–2Á[/Ó9²\ržok@5ÙL[% Ùì�z_¯ìîêíô�®ó� ¶¤¥ÈD,Z4< -òÚ$|ÀìÅïË,[°esÖÄ«Õ)8mr^VzB^4,ÄTƒZi+ìvw›§¯á=ªá.‚ÙeE–²‘�|€NÕšß 7†5£ä2µsî®ñ›…î^Ì…·¾¦S“A<{°sl¶bmÑQËÉÈG±-htxÊëÈ[´…+¯…åh16j`„±(//¥/Ñ•¢±µìqöá¡÷eo`¤·µ±Q9œ3€´ùjšÚkÆQNXÑk’ø] ¹*è‘ìÅÆúéìªNß틵íèGgFÕa²“peŰڊˆˆ~ö1ÿ›ûˆÿräû"þ b’“3Þ_ŽÌoá&ÏÞøGÞä{×ZŒñ$�Eq%:óaß›]�WÍyòµq›Ÿþ`J«æ¤tdðô�L´žÅùéZ?ÿˆ?ò�XOâ›üO·J¨€ßÄ•×ÁÜ“I -/�ˉx G® 9¯hÆ´ì#£¤ÛÅ÷ÂiKH„f¤¿Qó§μҙ¤ßplZP´hñŠF½ 0b4-X8sA¯7OÈ#çÈaPçÔW Ú u�u°¯Üö[§à`u\:‡6 #pö€ÎÁøéÉ›(Ö@=äR7œƒ -Ø©ÞYAþÅä\„3ð “¿£ rΡ‹àFW 6´ÏBêaœ"õ°œÔ“\‰ -áô2,‡:T+ñr¨Ãc Þ¡àXëa ¼EðŽÊÙ´.Ã!(ƒf؆¯Ã$8Gà4u“6j„[°×à>8Êà4TÀØ�Ö@#,¢€ôÀá3Öˆãá8;`:ìd�l›Ðkd�ì&¼R�d—cÐEUo{Ð1Ô �„‹HËay„Aki ]B®C2æÀyÖ(Ù¡BŽ� -)•Щêïr!^B§¢¸.ÙåéäXŽÝpvªÂcØH6’^€|Ø ;Õ÷ -í]²À;ä6¬�͘£!tÉ‚ -XN‡Ã6Ø qPE$ æ@,gµ_¨�H`I%^®i¥â>°ç£2¨À·à(" ò!‚Ý€µè�d�WÀ"Ö`‡×d‰Q‚t�²Àžœ¼Þs£ÞšèNèzß×(‹uF(‰ª»{wt. c°ðÄ£ ž˜Ï~éâg ]‡�Î�ªCÎ�üdN�ÐuØØÜØ#¾ ˜èv't8@½&f=À<˜'gê�¨QOZžŒéõ¤ef¯ÀPÀ+i{Èy (‹FBöZ¤°5˜BRÃ¥¦n`¹Ôt©)9Ðê¶zÜVwß"æûœWʦ¾](u@P @õR$@¬7PD_k`injð5¥¤XUh¸Öìl -F­¶ «-#Ù‘™•ìNŽ®†jT�«õÕ†ÝAÕÎêàê�êPÓdÔÆ{zZº5&Í„„çtïÑ¥%Škrš5ÈéB¸¾Ïóÿ¹lÞ¶—Ñáý_Yõ‡·ïüó{´nË”çÍ-;Ý'6 -§>¶`æ‚‹Gº oY½'ïÑ×w=±®¤G÷º¸¸1cR¶¨þŒæÐ5t9Xa”7P’ ¬¤ÊT¯;)ë% ›åRCSJ7HºÖÜtém!G²×c ²X£ -EY“!¥Z’­^ð¢þ¯u4ŒF£-£­¶ÉHeØÑfŸ ']Ó{YξÚÇ�¯ðxR’øÀ_Þn¹H§~²det'M÷Ëï^¥6É®úC²7´Í¶›êѳDõ„ÁV›aˆð¡ê{ýÓšê×¶Zl©)AHUešêdR]�p†ë_ž�¹ƒ›K×®-+[»¶”\ÀýlÚ8vê�ÈŠÒ'pÃz©ñ“�[ùÉ¥ËÁá0Ð AéËt¥ Z$• ×CÊë¶GnÑI�n³øyk°Ú„X®57]N`i¾aúC·ßÂЉ«RSl²f^šë»züåÜ…³O=Ìïð�QÔ;¯£›K×î³à)“¤×Þê™ñZ|<Ê@�Ȉ¼ü¯§w¿t`' h¼{•Jt9ÄA±×`Ä&ƒ3ҥ谬wº"]Ù®`½ÁIP†NP{™ãDp¹•–{ê­Û;Gè ‘a2Œ -“L9²d�ØÙÒÜÐäkº&hÁ¿»aùî†Í©ºñ°±¹²ÅôµÕ™!«ï£‘C3·=(¹�Ã.‰°�æBªú“p"JëÞ#5%ˆ¼3ªzìŠå�¼6tÃÆ¦÷ÇÖΙulܲuß)w=ýñÙI{hÆ¡ÄÄÇc -ݹbÏј˜ú´´WuæÈ-+ŸßïV}V²ÏQ7 `©…ÝEµˆžÔ”ŒR­1èºÏ‡ºñój¼>u÷* £S ºzƒu&IåÖíztÒ “ C±ƒìš/ /Oin²:32’­©VÍwÚ\*öTÝÜOÚͧL)>ÿÙùÞˆâÙïøÉŠ–êßLß²çžZ�úB5ü[R,Ù "½fi'«×®Y#Ùšxæ•ÏxfÓ?ÐW¯¡7D<Ùp÷*¢æ'Äx¥r”E.ÑÙ̈ÍÑ7XuFÿ¢»‘ŒZóAZ÷Ø8ŽÁ ”?õTùƧžÚxýû[_^¿u‹\ùøƒÆO>iüàã�ü}þ_ü3~% ±…_6ЃjÞ?íí¥8u -“˜ì”$¦s¬8e…`æ”&˜: ÁÙ -Äœ„gõ’Â(‘tH’À@£¥¹©)ŪÚ7³éšó^ +_ËôëÖo£*2B“½c$¬(A8˜+i¸;ë¡ Âù¸?ÎŒ:¢(!ÄIƒYˆ*+�Ië,÷&½i:Ë�Ó•¡$Gž(M”ç’Ùt¶4[.!KY‰T"‡O†É�©V”ªC1H¶Æ4Õ£«ãƒÿŒOßP/Ù}ËÑ>©eÎ^ͳü¾'Ù!º{C™•Z •ó&é¤Ê cR€Ùp謁–"°UŠMø�#&ZŠ@bmƈ5)"�»0cZÍWÖ‰EùÒ+’}ûƒ³fTø’È…Š‘u¢d„òÐRˆ×k-TcŠDœjP‘f² ‡ò°®å{¼æ@p€ZètÐA®7œ8eEVœXØ¡N’Z¦ (¤�z›ý£©¹©]ÿêÊê©�;z�žÊ@2T™@r•™dŽRL–)úÉ�n‡˜5 ¹¯’„–x�ï(^Óò �¾×÷ie ñ¨ñ«€Ñå ÿé� ¶™uT†ˆ0IvË£H}ØÉ‹ V³2Bia<"t`Œ¥yØã¸a¬ãÎ= ¡wOôœèËô ükS%óZs¦àÙš‘auf${'Ód–,%ËÉJ².YŸlÈ -Êrfg…d…f…e…gEd¹V‘Ut[%­’W)«t«ô« AΊàŠ�ŠÐаŠðŠˆ -W š¬‚РÔa‘ß¿Úñ ѳ‡®/Ú›6htï½Cs2^|Ñ=#køLrcÈÀ üJ˼úëEË?oY‰Wß\ >éÔ©™YƒÔÜχP›ñÆ…]aq:b+ Ò•»‚Êͤ¼S½Ë|ªóÉâB@2–l6÷�´ä)�‰åÚµ Zoˆdz?†´ãö4Ùw.δT2iýêõ›*JW®¯»ñ÷á{ÆN¯îÿLi×m… _~Ù0¯2©gœÿðÃóç?ú˜_öq_xXmb×Ý”SA½�ŒÔkü¤ÍZNz�N#²z0Áz�ž¬7è)£:ƨ$ü�9)SYÄv#‘ŒÙz†‰B$8iPŒ½NѪ>ƒ Iok! 9³AXT8á½Q^¶°3²Eñ¿Ø™‰ÑÍzõË :jˆ„H�CI8 aáº0}¨!Ò ±¨ Že]¤.rŒÒÕ˜�{° )CNU2�•Ý ÃPãx%W7Á0ј�g“|:›ÍÑÍ4<Ž—±¥Êot f}Ž$‘4„Eê#õñ8žõÅ}Y¦~ˆ~¢~žÍ -ôËñJRBWÒeì7ú•úàÉh²55 ‰(Æ€bjOU?·ûT-¿}à•ƒèTßœÝRO6Ü©Æù-;4]ðñt5� -6¨ö pš„: XgQÓê´�Íâ4[@|X-¦³Ód -ȶ€IW.Ÿ²šNZ-æ½ ²M±å˜Š¢A�¨}dd´â«ŸhTÅ[Zhíi =”Z·`ŪØBŒ!±¦XsOkŽ5Ç61@?&#IƲ Å£46E´L%“ψN|ò‘­…>2˜�:…†£Á§PAñqþC¯‘#wLi #}[É zY‘uNE‘³õ2ET�“ û�pŽ"‰ ÒàÓäMʼ–âüEYïà ²ê=.’ä|4[~•È’ŒdYÒ;ô½hwý$:^¯LF“u8F‡btBZ:…¡ºF^Ç7¢:^ô6ê‚âèÔ–´Ô¡×y‚ƒù\ô ´áD]!�ä 5–œ°@Yȉ rb)×Õ“í¡�6#HƒB-Í )­É™7 DœìiEñî” g -*§ž¡Ï�á>þ ò úàŽ‘9[&ÿñÈÑ}S·õˈ�G=‘ÙQϺ¾Þ¯×ßÞ;ÿYï¾ÐŠsÔ\u/ÎyöçqNÓÏâë¿À9’½åè A/ðB `~êœô¶?C9ÜiôÂ�$^8z´V›� Å¬„xo0ª²@•®ÔfÑ+˜K èi—.Õnñ î47‹ž -²ú‹0�;…:ì4YcPÅ—åÏíÜȇ£C·æwï|y–%µœßºví–=W?¹ü_-{5»\æI³d‡N�ïM 1; ’GjqDX”›€¡Vµè Ý›ŽÚÀW=F�žu -�= Ävˆ -é£73O{Z·ª5¸úR¡›Š°lNQš·]Lf{÷AÝcµuÓ‰â[CvIv¡QS’fï�/Í-ïÛw㜗~ô*èáùE“*¯jëåoªW,¬¼yyKÅ„M?<·)$lÓÎ*Æ«x` ÿ ‹’€A°×@þvK„¢�DçéíV0é 1�è–ïôŽ(é<_�VÞ¯‡9ÞôHOX�Ѭ„!fÊ¢„Ô†AmÌaošk­¯zÂCBfDA uÄØ(„F:úXÀ@UM¤ø2ÕÂô—t¡¥)MÉN¿ÒQ›F‚„Ú5�.Ø8~RÑü‡ÇoÐ÷ǽs7z½çîý±o}îÆvn - ÙôÜs+¶\¾Y¹°bqÕ7—·j¾´��¡‘t‰Ú‡Jõ†·õ¡êÍÛÃN…œŒP»PƒÁfëÛ^éwìût¬ìѽ‰Óƒ†¯_»vƵk×ãЄí3Ï\ÿÇټ퉇ãxÑçiüäã–'ÇNDéÈŠ‚P¯ c+~¼¥ò”‹¦Ò²[í†y�´ZÂÕ 0¡IUum=BÝw.uß¹"^ظgך�i0Ð�Ǩì–TCïÓ -HŽ�ô–K¾K>ÕÞþö�jr"ØVÍàóù-OÞä„íóïfÓtÖDø †RŠ\€]D½jã²¹!Y$0RRï»qœ5þXX«‘iœ_–¼öÑÝCš0jµ+ð£(˜­þ¢Ùç£qüÏPKg@� �Ѻ çµÃó:TÍž—‰‡RWBõ’QR{‘þ«¢d+>†þÌûïåÐ1º…¨5è tžK–Hv�Áå5ÓZé5\ ¯* ‘>‚7ŸªhŸÊœ[4ntí½y“çJöŠ}m=ºáì&C'èã�‘#Cªô‘Uý(*ƒM´*¨Ò²Ýí‚Ø€hY -G�‘‹¯©Éwͼ››,Ÿk�¹d­s�Õ˜) -¹˜(¬óŽ¥¹’µiĉ7’kæÿåëæËÜ÷-ŠAö¡[ùåÕ[·®^WZÊñÄñ+üïysù÷ÿü–ßBÅh3Z†6F¶Ì;ò GþôÇ—¨1`ÿ–àVÙI-z� Ù¡�„©b¹¤%Mv«[¸rÁ<÷æMÉþÃ_+$*dGp €b� ‡X¯�TQ\ÅJe¨Ò)1’‹@ 2X.‰>¤Ú"V°ÇmeižTadB½øËhôYÔÃ÷V -^7ôv£p]ÀÐ,jv\ ˽ÙñÉÌ(yBBCŘyBCC²#B¦Rˆ"-eæ�ƪÀÊÈun)*$HÇ„Fé€ ]— 3Š"‘1‚�K—ÄŠU£�Õ–¡7n‰ÔùŽ5#£5ýS Ú¨£QLZ÷Y¨/Ò–·¿çé°KfdBˆ¤:bð¾ú>ã&õMZS8òàô)Ç Ž\î?%w`ìésçÐæÓ7Œ™Ø;·ÛÄùËèyâùákŸ=®Oÿ–«úÛƒ‡Òxv¬0ÒÛÕ`jÔÉS…<ǬÆçôÈTiSŒz¢“¬ !fjÐ…H6uá|šÙ”"⤠-ٚřqÂæÌHFH i—€Ò™5Õê¡ñ|ÊÏàñ(ŸïÈ@£¡ÑôÊÉSÓÏñõ¨äÜôS'gœC%|ý9-.Ð1j޵Bg¯CªÒ•B•Y³N&)Æn²Ë’l³¨µ•Ö›S±'ª‡Õ뎱ª@¤ ²¢Cp÷ì™»05�Gø1^Ã/칃† awî,aI|+_ÅŸàψí„m<×K@Àö‚W)P¬5Ô,¾·Ud�ë÷I?€"Ül 1ì&„ÃÞ.îa  %Î0�$±l‹õÅ€*{%…* =Fz—3ÚB:EX| ¾¦'´šP8fóÛ­ØO5>ûÚ™21Ú#*Ô�~¯ñ©Ã.É&„Šñ˾â£(8-oЖU¿µ`Ö›Ó>A†‰y=kjjN£Ä¾ËªF­Ø”Ýÿín)×ÿ<õÄâ~_Xs÷*Én‚b ú{=ÁF¨Š“ª\ U¶J×ö¸—’ƒ��p9:¹Ì:—#,š¸ÌîÈd‹¯¡É×àïß [k9ÒrCdŸ¡ÁÓÚÛ<ŠÖ'�‰î”Ö½G`‡ … Öoyfmé–gøÙÕ›¿}ï·›WWîâüÚ5~w׈U%ËV­\^² -Ÿ®zòÉíUå¶�sZyðÂ…ƒ+¹Ýoí:{íê™ê3húÒßþvé²U«µµZ!j:Ö�s¼=uŠLô’‡PÂ<”’l‰‚ƒPG•Î^Pj Ì¤“ˆžX!ÜDô!)ÔÚÍ®w“#T¬Ö ÀšUÔæV­ªkºÔ$–jÇB¤µÍëBÎÀ’ˆ†³ªpNjƒwø‡‘óÇœûèàˆå3^?‡ªo!éù–÷.oÞTõ~Ý^´‡ ’ç'·l`�}ëQ<±åÆëV‹Ýß|Ñs`�€!²½‘(€!Ù@ rC¤T‡Œzp)T2;™,>ŸXt*«×ÄQ+›Ô_iê�›¤ZS1j"ÂWxô¡ûË7ß<Ó²žEø¾"ïøR_à;QÞqá­yDíÇ!½7”ü^g«r«‚+]Û;9\a’¢Í.wd'‘@D0kíÍ]º‘ìý ’PN"I4‰%IIr’’¤KÒ'² eá,’E³X–”%g)Yº,}–aŒB£ð(ý(ãð(z?ªÔ° v¡]xÙEw±]Ò.y—²K·K¿Ë°ö£ýx?ÙO÷³ýÒ~y¿²_·_¿ßpŽ£ãø89N�³ãÒqù¸r\w\Ü0è—˜ñOE¥�²G¥GåG•Gubâ_"Ôi2šŒZ}:&:6Ð�®Dû±c]Ñ£çèQ=‡­ÝXV¶qSYÙ¦o¾ûî›oš›ñ�ôÑ£Ó{ŽŽwò÷ø~–¿‡’Q”Ž’«ùR¾š?Á—¢Rô[´•ªþ¼òîU:HµC'èí�” *è–€2ã&KU´T^½Ý(­scCD:¿Öäkjk”Þµ˜Èæ­KÎa"jcÅfUáaZw[j”MdöèX bÇ^ÔF�„‹tƒî’ðd±zš’UPðá9|Á—ÇU$€P½€KÔš×E�äïZŠÁ¢&Ä%AÝÆA‚áÞ.ȃ �D|  ŒJÆh60tˆH” `8‰%ËŸ¶?ÐôKûfQ*.@�^àþãÃ.Ô°øÛñj®ݧæÇ‰ÞDâ{mOÂC° -ò(bOâ�„eÂ(’�f2dÊ:Uo~$ò« hûÖ«çC4Íýˆ»Ï±Æ–B¼Åw¦e:þ�ê¯Gøxb‘ìVèé�`€ôå -ª%–Ó³ä¤rÊfP°›©d 644ø·UÔ¯1LÊ¢ˆ¥¦Y8—ŸBß½ò{þå¤IuuvòyÅ�òSw®�~]³éušËA�¼�~Ÿ£RR®»^s8 tt·˜ë¤¨å<³åÙsÂÓ†Ô´¤«ôÎPIõa�7P¯z0­’J'.ÒuF±¤í�‰”íw8ÿãgHþ&Êð�BüMÖXsgeM UóR÷9ÒÙMè ƒ½] °Ê¡/Óí ¨’"Ë¢ö„WÅTJÛ/u -bqÅZ\$:Ò®‹ìbñ54øš.iÍ5áú×ö�Œdu[‘vÜVô—è÷¢lÙÉ¿únÖ³òߘ¾çàÁm;v”íܼnb}AÉŸs>Fl‰Œ{ó™w¿Šít&­{åÆ'¶ïYV¸hyçÎG¢¢>yu¹x¸rÒø–”<^:‡)ÊT+.ú†Ð�vÀ¤¶»¼='6%‹­t…ìàG¥|^Š–‚²»WéPvâà?¼}Ô=Y�Ë¿)뉌te«;²ÈñŠýÅà*+z^¤UžJëöÎ.mC66,ÃÔÍ.gD'w¶ø ¿gCVSLk÷Õ¿Ûš¦*XôE—ɘȉ~à}[´÷ê2 ÓIìÑ6?rxJÅžÂg漂ÿà›}iî¢w -¶×ož÷Þ1pe|=ÛýNï̵�Í(ˆ îöþ«�KHø0gÀ†å � -Iª¯~ë¿ã·­S zØæíç�ØÃ¨ìñG�‡èÑ#�žèÁƒ�}6¹ - ¥:¦SdI}ÈHÇ’ô˧ZÔȼækjMøþf¡ö¯Ã‘Ú!•‰Ú!Õƒ* (AL‰‘êdIY‡6`Eh„¸ÕÂÙMbpA#ªáú�¹0¿å»yXL %/ߎGkùrQ×�!5d¥ZƒËá51´�B©KTnMŸªŽ‘Ù”¢fñKV¾|áeÖØÒˆãÅ 0ß½JiÅÞ@]•™T9*ÍÛƒ!ÅØCJ±¥«È¾}ï¯5Ϧ¦Ø;“ ëwl_¿~ûŽõ¿oi¹õ½¯å{|å Pþ�×ñ/PÊáËø*ô[TŠÖ£U\<õ‡ Ì_7ê Ð.#Œ°$KÙ²– -“�Œ­´›l…d}k�¡vS2:à+*:ùN$TëÄÁr+÷”'à<}ü�þ3»|„v,6U¡"4mOëq~D?Ïßågxc¤mÞ+î �ùk³(Ñu�*½®ÊVŠªôŠ´B"ÌÀä -b)áÝtà²%»µf¨Ñ´rM]¸ê£*Zä"š£í%D„ûæ@åïö~óÕÓkVoåCÑ‘ÏX³fË‹üÿ‘ÂgZ./ßøôœÏû.XñXÞž7^Ýð;{ÐùêsçÁxZÂÁk½]L°,v{F:ŒQ¶^\!pÈÀtŠØµ×KMÒK� ÙÝ¿»›Ò¶µ£îåhÖ¿g/§Sô:' -&N%X‹º�X¥‹®Ê =”ž:“Y6Ëf=ûún(Å kÌxdBQd>Êomçßa�- -þáv<‹ó}MoÔº_¥òÿ™w“˜N¯£zÑôaƒA/yÄv•GÛ®òhÛUžÛU‡~i»Ê÷ËÛUÚ6•¬íZMŒ>¬>>El'Eê¢Ž× 0ßS? Ob§!?@<ôÖY‰×y ±Æ4Ü‹¤Ñ^¬·ÒK×ÝÐÃ8Æ¡I8—L ãÙÃÒÃòe‚.×0Ù8K)0–�Çi‰îqCÌdhÛ"bÿétEÍÅ}§¯?wö8k¼3€Dû>¥õ·ãÉßµ·‘–Ð% �ÛkO\£/£?ÊîL!N¶ø2›ÔžD“3#90 9PR[”d y½–OÙ缆ú�z~‘N’ì A$Až7CéÜ ä.ÁÔcO²8‘§¬Ó¦ÈÎ.{Yà&Tar»,Tê¤te`�ÂYç(Ùå49ÙrÉ™ÙäLñe6Y|×ntƒ¤7RÔtýÆ-mAŠ6‰è$#«ZÐÇYMÈaR+H‘zœVµxNOKuº�X¯qVõá!šÿ›-(.¼”‡òÏLÏ;7ã­³�ç¡Q»Ç=ôüèwÿrýÀG’Åy“g]@ÏÑSÆçÝ9�Xb—C}³1:Y[ùÜQÓW_1Wèã“(0òäŽïêüØ€Q1O¬×®Tá?Q(ÕK2q±n:ä‚d µ« 'S< ¢dÑ$µº­1Îá.çε|( áNœw;¿Ù’¡ÒÅRƒëÕ!ú(¥ú(¢C«öQü AãCàƒnì&„@¢74ä8xO¼ L1B7)9TpÓÞ¶¼qëÆ¯D9ÚmÄî‡ùgüÊBï1´fü醆ӹ/å¤ué‚*ÑÞºt9ßÇËßãïðwù{Þ>þ^oßb#`äeé�2ÂЙ -ßjJió-®EK|/“1¢N^ç'ø©×Ðî=h·ÀÆaüuºƒ-t‡Þn�ã;…:‰É¨ ëNö;uû�Ö¸Nûã�®ýÝâÓâ¤ÎF�âí�h´»ã� ‰i–O›| žÙÐÚ8ì–†KâTCcƒÚ€ië'&{ü� ®²¯rH¢ní°¿Þþ¥º5 .ijë‘îlסÍAM«æÎ]½zîÜUåOyÖŒyþâÅçG¯ët`ó-~ E¶ó2’³–÷âÏòÉh)ʼp»W?ûìê'ž}–ÿ£(kÀ·ÇŽ};¨OÖÎz¾šæ¯ñevÇú�`ô>*GëлüÖß� € -ß0ÀÞa:�Þ W<²bÚ³8¢wv=cƒ¨g Ù…aJà�N2(2à ho) zë…«^ó]k ÙÿövjºúVÏF Á,u&]X¬!×°—°%ƒxNÞ¬˜;UÜØÃâ�! ?„'*¹†™x¶´>¬¥nµÆ ñ¡!häG¼ºòšŒšYcË;8µ%Ûw¯iYAœÂǶܽJû°Fˆ€.^§™Ø€"]Uðv *u™]ÖnØÉjfW�]{Ïÿ žSE©¢)(¾ŠV À®‰hË9£9ªdü„¥n³ñlÆ ìš9³kúì…û�m>cáä�Þ™™½C™Gnù ->=«w¯^½ûžöïm &ZBŒ±^2P3˜huˆ ª á`Š ·¨àâ’/ó’è/©L<­'fíðn�Sø-V1jÒcA†³RGmÌùôØ´¼¡FÇâɃ6ã˽ÓSõÔÜ;ëåÌA‘LŸÞ0æ'p�ôºŠ'AÀ#bMµâàêêj2�<äûÃ�qdœoŸÖëËÄsè3$H˜æv¤¦¹™èf º‰çìÛ·oŸÛ‰=" Ò¼!LÔâª3e³*(%Œ Š¢!R,ã ×Ýÿè�x8(¹ËȾ–//b¥%�5Ž¿½ZtTܽJT»õñFS9"´J¶–Y6Ù«D½°]®q8]H –H—hôо©Zìúë7Þ`ËÐjG¡7pØážâChô¸c�¨X7[ÌüÆ—MüfTÔ;)Ýì®>èñ‚!d/Ý ÊìÕŠ T&ˆÉ–Ko«áê플d¿“â×Ñ[ùfþôAôkl¹ˆÊx1N`à Ø60@(¸àÈ€ñÞ$WzDçèpsÑ=mŽ~:}Kç§7›·�§ƒÃÃâ#]’Þa½Ñ£ïnˆÔÇ{YDwÁrƒkÑIÝæ¸që÷?ý­n}ܳi)k1©õYY‡œž_¾Œëf�1wÞð‘³××­ó.JLL9²¾´–‰¡¶.>çìQ#æÍ1bîúºõÞâ„„nGÖ­¯CY#çÌ1bÞœQë''&-ÎZ{ä5:ížúù¨ÙsGŒ˜;gäúÃÉ ÅýÖ®ÓÖDáÝ«äk+tõ:�²‚à ­2+UúRóv›Nv Ífñ]jÅÝ–·Þ]Œt¿Õf†Ep[øðÉ‚ŠÇø<øâ÷Sëû4=øà3çÈ€Ÿ�ñe×ö|F�‡U^w{ dÚƒ‡pÈÿÜ!jOÈ–÷E¹öç ©ÀjÃeÄœJ,‹UzàAø!ó–,ù–M–#ÖPk¦õˆ-Ú6Àö¶÷;. ü‹=Ï^êPÝÃO;ꂲƒœzçPçTçBgµóF°%xkðÙ�Õ“q'ˆ‡¹  l;`l²ªþµß“@Q+Åß½iÇ¢ ÞŒA�¿ú�I‡ó´Ã1ƒ`øÂ,A8pÿ±Ñ(Ôl€^(Ó‹÷› À¾úC,€X³aÀbˆ‚Î0º@¤@2$C*DÁt(�(ȆٰÁbX3aBWˆ‚˜3 ¢ ̃ycÚh-R¿Í„E0Âã0ò ô0f˜㡢`À4˜³Ô‘Q0M¥³a>DÁ(†é0fà ˆ‚<(‚B˜¦^»ŸÎX•Š 0Š`>A6AÌ…ñêü‹`¶z^H–©�~Ïý­w·Þ{/í|õ¬&Ñb¿ôBÂŰzA’¨…üã‡bH„EPŰfÀLõÞ…ªt‰0fÂbÔ�Z«´­Zÿ©–Å5¡A1j:Ì„yPK ‹ªóÿ3š6ÑÿìÌšæ¦AÔ=<ÿÔkô�ð?ø³ÿ¿ðĨ!ól¿…ÍÅac¡³…0¢ òÿ%/B²Ñ*½B•Z»j´ Ôk3ýrÍRgZü :ùêÕ™m³iÖ¼I\_ E*‡óÕûøý\›¡¦Ãb¿…Åk–_–~M·Ò\¬rq¯�Oƒê¸BXà§ÞJAŒÖxס�Õxö©ZöAxÈ?B£w¿Ú -ü·K¡Y Ÿÿ½�µ[_P<·ò#¼YxÙOµ"ÖŸàDŒ^ ¾µjAø˜°’ƹ¶^´9Zíø�ß?[WÞÈûôÛºŽZÇý;±C£Õ:÷½úzÒ8Ô"ÉÈ‹®»ÂR5î-ðçµE?ÉÜÑc;*íˆ?»vˆµ‘€…«c ï×~V‹ÏZÎj¯y:b¸ŸË\­Ur×ûÐo+úÐb·VuD¿y*N×°à¢6T¢å�¢6d"п†Z«Q Šüro½·H�W“L›ë§´4|)Æi³-úmþZ†º¿B¼ ÊKÔcÑKÒÃ4uŒ+Î/»¯*Ödø÷mÐ*˿ҿ8»HÍ¢‹ÎV5,ðd¢Ÿî¶ú¬]'BZw«ð>«·{Ÿ Öë'8Tè`VÎE}§a �+ÄœúÿA-IÕ÷\˜ I*�âÞ¥�¨¢ð�t¢ûdêÏÝ¿À¤Ö¿z¾çg2ŒR£õX©ycÊê9ùó±ÉìÏGÉŸWÑcG=ìØdrÌK�zÈNW’ºŠ~ì5Nj+ú±ÚÛäÕì'¯ä‘ƒyäO&rÀFösò2÷¶�?r²�“?ØH '/í5±—RÈ^yq�•½Ø™ì±’ß¿�À~¿‚¼�@þ““Ýœ<ÏIõ®V�GvýÎÂv…�ßYÈsz²““gw°g9Ù@¶oKdÛ9Ù–Hª*ú±ªJRùÌQVÉÉ3['³gŽ’gVÑ­OyØÖÉd«—náäiN6?åa›�’§<¤¢ÌÃ*ú‘M l“�l4�ò2+Ï#eOZY™‡ÖæªFýw‡;3_Ub"â©”g4�åW”7ЉA×¹3oXrß±"öF‘Ÿÿj\øéLÑ¿sóTÎ_|¸ô/¹[ÇYrPŒl�<:þíL!b¡÷Ðý–F$¾Oçõôûâ_ó +ú¬Ç‹}GºÔ-Z×ÞÊÔŠCœpý+TÂ�_S‘+Ñ6æ´µ�h:w1º/JúoP¦r�j²öB…Õ{Jˆøw£æ fÄø“!p´ÃílÄr2Åc<¢TÇl»’t[Ú‰_ØÇN’ÑMo¢�*N m~M…tw@[ÿXnr±ªL` “Š:F®^®íF:l�Yd¿Á +Éõ¨»û‡Îz–íz‚­fÿôÜÈ3t‘´—cM÷ ЃX«… +ƒøÏè ÉŸ6^aa,fû ›³ð�WhÝÿ]K#¥l[%Qo‹>ºJ¼V�w0ÐNƒÛ.t½9Þwý8kÄF$¦³`í^"2±½#T6ýõk“têë þ&–а5?{AºRtÙÙýqu‰ßË$,ônêt‡” +’k„:¿²ä˜¬½Î.ôÃoõ�¸›ÐUo´›e&ä ÏNéó\¯Æoi¡QÿI×ÒÇel û±$ùéݹéPBs¼à¹D 8‡N»ghb°¶-ð™° {”lv�†ˆX4Ýð¹ð3z6ˆ£q’ò:O7œdºÙc©!ØÊJô¶bóaXIvj¥49¤vgE+±çç'G¨Ö/̦ôí@_Û'H2r w‹”=5î#†.7ÝpÕ£Vö6Ý�Ù÷–äí·æšö�“§¯ÞÓƒ:Ò‘Õ˜š¤TŒy=õ’ôn4�ÐÅ5C®FÏi2I¯ÑàêÐØžlAˆg¤•Îs{]5µÕ^”ÃkógätÐ�´¦ìî~÷°ôo­™ð«éJý·¾ìÿîÁ½,•KéL*×ÿp“qˆþ—ŸÆ DÒǦhå6ÐÙbž—¶I^×|ÉæI³±û¶vÑ�¶SÇ»º&NýÍ8^R™EÄS‡‹2�BMô^½¿ÿdÇeÖøé&ÈÕ«˜à)id[KXйé,)}ù�t%}TÆÖ°•’ÜÄÔ˜—@öå�îè÷Ñcô1™˜?]¯M‘!.‘-(PK+òó܈�F|įyÀ36[QNÂâC: +‘oG÷}žïÒ³¿W¯Å6É6¢ñÎÝ^už{%.Š’ü$‰=µ\àŸ`Z(W±‰õÞÏpdÛDò$�ùK*«—ÞÏ �ˆÒà[Tûuïû«÷âpÄ�cžÖüÃpáÝå +“�3a¹J_ø�¢�I§]”´pga6¶Wà…¯¢½î �ßÝæîq·ÃQYÕÞ{ªí$H‹Ï´KöW3ý7÷F+ endstream endobj -441 0 obj +2185 0 obj +<> +stream +xœûÿÿÿÿÿÒÜ +endstream +endobj +2186 0 obj +<> +stream +xœm”Moâ0†ïù³‡HôÀÆq>hB‚@$ýP©vÏ!ØHĉòqà߯âwh«Õ=ž™Ì<Ž�ÿãí8ߘæÄóè§¢wî›±+yž=­çû»¦k¶Ã ³as�öKj»¦ìy ì°;Øjð|ÿ`Ëëhøžó¿”-_*û•0õ l쇦ö|ÿ£®¼¤ÈÍDÃv¨†©Ï÷q×W�]RèùþÞš¬©§áz/�¼uMyä�Ε5�t¢ÓÔ× 5™ª„ÜwY­+>Þú�ëƒ=7!ËŒ­dï|©ú¡»ÑÌ ö@†Ïˆ¼v†»Ê^hvö[ð8¶í•§!I¹U¶Æý“üKQ3"ü¹*–~-}ÜZ–¿Ÿs‡#–�á¾-Jî +{ao¥”RkZåyž¯§ŽÿÄc�²Ó¹üSt.=\ÓJ©E¼v¤¥9(BL�bÄ4(… ™[8Òè$OyB]Ú î´E,eˆIæOÙ�öÈ܃rú…Ó(µxÁ/]€àm@ðKS¥RÉGt ÅQbpLàÂ1ƾ…pŒS�8JLaŠ#ŒCq”:8¦BpŒ¶ 8.°ßŽ)bŽ1vCÃ1Æ;ÔpLðf4üb©ƒ_ŒY4übé ~Ø} ¿~1öEÃ/‰Ý�““5½é†}žõrì:¶ƒ»`î`O§¸²üySÛ¦�ªÜÇ]ñû¿ÅD¯ù_ÔÕ:œ +endstream +endobj +2187 0 obj +<> +stream +xœµY x“Uºn 9@§HcÆqáO]FaDœ‹ŠãUYd�EºÒ†¶é–¶iÓlMÒüÉ›¤YÚ¤[Ú¦tMº·ÊV†ÂèèŒ( 3zÕÑ;×A=éü�ñ>IAǹ£3Ï}ú<Ÿ,ç<ç¼ç}ßïýNQS¦D ‚™kR‹–>}ÿ‚y+䉙Òäð[ËB·„nE š-`§GI€ÉgÌÔ¿¶ý%íÖè¨[§t0³£¢q3ÃÏœY³£¢¾ü2æöð‹-1w†ÿ­‹ùq”P ˆŽ ,IÉNJ]‘’*“KåÅs–Î�_¸àþ‡ç-\pÿ¢ø%2y¶Lš¿1?;3;K*—æÏ�ߘ.Í�/ÊÎˈ—æÇç¥f¦&槦ÄÈRRóâåé©ñË7mØÿd¶L¿Zšœ*ËO�Ÿ7/>>?55>].ÏY|ß}ò‚´ùÙyi÷íÊ–ÉóïËœüRþ}áqóž\»fã¼Õ+–.[³aÙ|¹B¿+;/>%Už(ÍÌŸu]” j.nýU\à-õÐ|[�?š�>ecvÌ4gÌtÄÌðO÷Û­á¿�ú˜˜£3úë­V—­Æêˆù�0JuOÀ¬€5 Øì¨¨¨‡Ã€ÜuOxþ)Qƨó‚Îëž¼î}a®°KØ;%~ʘuѳ¢WO�5ÕM6�?O[<ýÎ÷Í8³+棋Å̸™¯_¿ñúOg­šÅÇýAÔ~ÃÔ&Ä—øþ�úÉoÚxÓ™››ù÷cù­?tS@@g§·÷iˆÞ!®ÒÃÀj‹¾¸ø¹äÇŸAЂ:¸Pçj'¿¦‡.�©÷ Á[¥äôÑ9LE lp ©Ä¡Ct…¦R’Â?°L‰ +M%ê$ç±”d,&“*²µ+yôÓ¹66^ëÍó èuç¨÷œ0ä¤ñGëÎþ”¿žó·ówÝyòé ôz*¦·Ó»Ùeb$Ü™±V¶qËŽmHÁ®^Å¡V* ¾Ðwâ@ç¢}'Ö‘ØxþÙ½72ó…núæaÈEÄ_¬=¸–݆„4™Tsl‡7i(’+³Éê5tUp V«ÃO|çÐ +È …JC6á£'v0úL=—b!e( Žr—¤ övÑÍ@œÕhðê Úg•NWkEé¥w1®’–¤Q* 3è:WÂÏäŸkò–mÁzè.Á »Õns“ýt=ãî~åͱUÏ ‰9ü Ù*Ë/Œ€DE:õ%šsFÚyu;©�J3È9ÚÅXí¶*•F»tÎBæè�Ek�‚h«PsÀfÐqœ +ìœ cátIH%>¿ü5:…N£?¡w³×¬,»›ð:öVötÐhרj;û»û¯¬�|µ¸+øÉåÎÔt Cþ¯d± T«¥ÈÚPs…Ac´åo“ƒÔR®ΨËá…ütþ^þ®E#Ë^—ôboÓàA϶¾Âô¡©¥6àë÷¿\!Ög<½x#¶Cÿ2:q(@§ùžo91rh{Ѳ‰€Å`6’X~KU!}ï|M„ C…!Uh¦xt¥g7û$Ò¶* ´GRvG&(9…IIç�0f£¥´îÒFtÂÕas“Ó´–±Ú÷Qñ;p �ªt-—�r¢®T´ú5l, +Ÿ (C…B*ß/îRµf$ç¥Ë2šuMl#ê<ŽŠÒÿܺj{²Fc±ÀD8»Áå£3éè 6–¯Ž°€æøé#~�>KËû…¡ôËb‡Þ£gM°Xôº%óšJ°Ú_aýUû\md ºgìÞ â4\dd×ÀªÛù;îâ—Ý`ÑGô† ¬e¨[|ÿ0v³…ÑÃô¡?Ù«@›s¤’X~¾Ö_sžæž¯ñǹ¸©‹þêâê.ѧ¡ÒkO¸\™ó[SQ„/F±Ç3ìî&Ýt6S3Úöâ~ØÃΡ"¢/’uH¾ãðz¯ŒÝˆÝÛtŠ‚ãÛÜy woâo”‡‘—TVG]Û;£ƒè@ƒÉÈ×l+ÛEä åÊÂGÖ‚C>Jjˆh¼Ó]Ù‚ZÒZèTçåîNy^>Â@W}gã3µ­ :Lon”Lb×ÿÖð¤Ñ$^úvè6!oŽáHÛg­zOêÙö™k“ÈV#¢ô(›Ûöøû&ô­ºjPs^^ñ»I cký¡‡Š*ýqºNÔ¥†nхб*‡WécsZ°Ã£¯-B 2³žüœßÇ”Ã\¦Vd˲vbÊö¢ 'ºÞks79jÐ@D‡í$ÌOççÃÀ±Õ°Ú*jÜ�¶�ŽÁ‡\äërT)¹Ër—`3~zLöÈtwîéôm66\ѹ"ªX¾š®=*x‡:…ô"}Nls:ý¨ a›gùÍÑE0ä6ËaR»ácéÆè8`÷’Ø “.p¡›6õ�è +C®n\ ˜M¥úœœ¹ëQK-jÐRÕêÞÓxdˆNÁ1¼½Ñ¿ äa<·V­0”v&ü[bô¢Þåj ƒô§ŒÃïlrø«ÃZßQ³gßëo`/l;Pˆ§wò3TÉj)W†B²³=íÀÝ‘ZÄÿUë¬Hðáñ�;„´ëêÊaWN yLÉ2Ebö×§WëÜC衱ïá¼ü¬ ޱ˜Ê‹PŠg{w@?Ûª[ê{jNÁNbùñ‰þ‘ó#_©¤Û¢ö¨èsmèNüIˆe¬NÛß3e?c�%¯]÷­ùDû/�d�=ÕÕ©üÃKvn]Ö>§êwH«Íe 8¼m¨#MJO±&Ÿaó�ôEö2ÞÿSˇ+Ÿè§«ü¡ÛÖ‘3FM¿}š¾ZL¡•LE-l¨˜4} äz“LÕ‘ÓžÂOá§òsøÛí_þºä0†‡¼^Rø £Ùj,Ì–&oOF)ÔÈDއˆ>kt{›POªËr‹3U)É}ÅÙÏðÆ/Þ JºY¬ÏXóèFÑÇ›Ìox$8ÐI§5íoyaï¾�ã•ê%œŠ‰1Íþ<Ž2T�M¢K4ï¤Ø¯lÊÎØ�µ;×WÒÊvº:%¢³oÑߊ›•�ÙÒÌŒLy]Q;Û$±øD¿ï­×ÂɉÞñV»?.ï"ýàøè¥�Ý¢/ÿšùÿPòoЗÂéˆÇ€–×Ã÷Óø‡ê“4íØÏµäÑ—Ú�eH¾â™þv_ÿëütä±<Á’”þ6Rÿ<îB—÷èþwQÁº è/ôâ7ð�åܳz¨¬hCkuWu9Ecª:ln4�S[vÍ“òs˯ÑbeWë-–CŽ|­Ì  YíšÃ¨RMZ‘”=I£ì‡]ô'ÑŸÛBéâ¦RVFFfFA]a ‚’ØãÐþVz2 ÿáï„ã²PP|qù)^ÀòG¢•aЏÑhC‰Ì&áo™dôR£~9Häsì’~ Ù0ˆ!nÄå…¯Ú€¢õpWJP[E 9A­ô>04ŽÞò)�&y MŒ¢Ò¹©;ÓS¶*Ö‚ðq£mLe¯ ] ïDŸ;œ»JKg#0.ÆŸº ¯ ­Ó;Ê‹Yþôw[Y˜Üôx4]O7P)-�<Ô/Æ:¿vÑÝÛžy"ñ�;£3awIà´WY�„²¡CLe�ËÚòqô[oîào”Äö @÷èH@0þ`P8¾9”+n¦Ë{«… ÏE—èP¬v¡ñôezS)áožèú;Œñ¼}#ØÏí©ô^i‚tÑ?‡»z¦Zr’:.€9]Yš#A‰IiRš 8h<¨€v«£�¼Hsk…Õ<pPÕ?¡Ì{äQœ¢^ÆÝÙ�ý…?óI ò9''%ž‡­�µÂ滿#­AÔým·0ü?íç_¨Yëy5SÈiKüÎéü©B±>sÍ#ßšÎÝh¶ø²«”U2(8‹)ÒSµþ£oÑùgúãÞ¸d8Jã^Úþšè‹Ðlz¿ï>êKªÌv¦¹1Œ=Mcçц>�'…ˆ>Ï0és #ZgÉžúVïðHÒÀ +~ZÒ=›YüçiU»‘ˆþ¼W·O‡MÈÈyäN`½Cyø°{Ò ®.”g—ìXs,íW,ýI?�‘\ æ¥+1&øýbÌSHOÏÌ"¯Ñ}ß'Ì™x—±˜-ZÚÊÒ4¡ºÞî!ÃtÉ›ªìµÚ|ÁÆàðÉsÿ4ÄL6‹‚ ]ƒ'jŽÒû +CºPŒ¸G�Éóärj—ÁiØ“kÏ@!ÉÞÎWðS7iu±8|·íUG�³ ä»E†ÌEEk±�,8%{™þl„þ¥5l’‘ÒRè¼·|~AÞEúÊEah˸H<œÔœÄÎEÿCƒ”ò×­£Ô¢H[…ZÉ+øùÄ4Fó´.ÉX¨—ª7§A…][û�¶c¸°�½Œ!ºËÕKÎQþm0õUðUëQ"yAºˆq÷yŽôÂ�&sµ2«\•�"RàÓÕµ´4ö°×”ýÏò#U_vD\�Áäkº0�Ûf-^’üÌæ­åœ™ƒ‰+Ê�¬�N£q4F":{‰þîÛ¿ðë 5& -á—Š¯Þ°pÙakšþŠz-ÒÒ•ÙšŽÜžç¾!­!ôµ¶·=jEÑÏ´ Ó* Qk¡(sr.I=ö.t¡— NÞ9H–ÐCßWoá~hìmo˜ëqtðØ–S/_ÚÜ-)þÖ<Ô[Ëw"æf4¢ÙÕnuÑŽ&XQ�±em«+›kššÚ£/ŒBÜ ÑPžýW¡¥µ¸{û,¥ÉϬP*ÓKȧ¡{ÿmtÂÕn«$‡éN¦¯üT!Öç"¢qS Ðaéþ¼>ì…¿ËÓ@üý5'¹šÎºR„>_(v›el&vsÈ€Ôž R®†B[i©”ŒšÝE(D™Ù`Öþº‰UüÚP=c¬³X AøY_U,× Ð‡ëÇéÿ£‰6Fµq—úf¢×ZŠ#‡Ô‰n{AôD.†¼ðy V�䛹µ°ÃYQG¨8ä¥2ŽR«¹T„ ��`êNµwX‰Ýk­—ÄÒ¹­ãµèœ÷B©—„ã¡ubúÐ�?æãøD>™×ñ¥ü=ÆÓ[évº‹P#»èy1HãozvÁJ^¸pÝãX€ oƒÎÀH �?Gc�|øþkïâ·xu-ø›I,ø Güt4*þhõoºßË ŠFÆÓþKì2;Õ¬–rNS²!+#YàÚD«}�½•ˆ +íÍŽ½hÂÑôç·øèí½cïâ5òæcgùY,_ÿ}‚Ç ½ÿ8xœ¦u4 +Ìó£Ê¥¨¸2Õ"Ñn7©  KNdœd/v~î–ˆÞÚ%†Ñ4Ó¦,ÙVùr�»pšÖ1®N·ÍesÙ‚h9õ æIbé=W±µAì-C›fÕ¸4ÂßHÿHWÓyLsÃØK¿tº­UVÔÃerPˆþxïeÍŽ6»‡X�Œ§ FV³ÙTJ6ó©ŒÙ`áÀÁè09á‚Õæ®òx[ƒUÕ}›N•Ž‚Æ� +éÍ q$–/�ôú~ƒØ«GÑÓpyÂêrMª«�q�•gAšª"¿˜$ÉHä’�„{HXºap»;"1«-áo�bô»ËôO�–MJ;Ò† aÃÜ ˆ+œb?A Äté@¨o4Üie‰;érÆZÇÕK¼‰ªè¢ð6ŠÉ:~9S®†)\T½°£°Ûm6«µªu?½­½‰ŠAº£›a2H`0—™�áCîfÊÒËt€(£#÷^8$‡qÄ~£x�;iˆ=:”JJ£•È´qȶ—‡yd�È–Þê‰x±Ûê¶µ¡—ѬÂÓÐ@¡[gÖFV£ßö‘jØìv‰ý_Dw8½ +endstream +endobj +2188 0 obj <> stream -xœ;ÁÁ +xœûýý endstream endobj -442 0 obj -<> +2189 0 obj +<> stream -xœmQ=kÃ0Ýõ+®ƒ RÛIéB ujð�âÐÎŽtv±$$yð¿/’夔’¸wïôÞÝѧSµxãꊋÕs -g´ª7 ù¾Ö„Ò�b}‡Ò9ò)k× �bäå®”ÂJKÉn=ljóå[!¯yo�ê¥án¸†Ù@ð%Gé„ �J?ÑX¡ä2Bé‡ä¹ê¼9K’¨ÉÉ(V¡ƒFHn¢\½.É–Às1 -7ëjŠ«Á:ìJÙ(X�,ÞëÈHÎØ -ë̳`l›1s4��-Ì&³¿’U¯õ ½IHŠ’‡7ñÍê!‰ ßÑØ%dè2hŒ$_{ŧ -2ÅÑêš¡©e‹d“¦iº…MQÅÖ+þÉǪkþkØÙ6ËåëK`GÜ×ùñÜ�²Þ”.L'¸ò„Äû˜µÒ¾*œ°ŸiÕ>:?”¢ÊW +xœmRM‹ƒ0¼û+Þí¡k¬–R„VWðжe÷l“×n &!‰‡þû%QÛeكʛ™dfÉËá8[suÆYúJá­ê ÃY±mtDH©X×¢t;DŽ|dí´QÌ¢ƒ¢.k)\DH-Ù­ã8jþ“lð*äSà= è¬SmDÈI¸.aÒ2AÍQ:áî@§!Ÿh¬Pr IDÈ»ä…j}8ŃÄ£Ø\„äfp‚³÷�’9pÁÜ0…7kïÖa[Ë‹‚´WñNJ€ø¯Â:s‡I6Ž—žÙŽFÈ+Lư¿Èc§õ }H EÉÃ7öåwM‹…èÐ’'tºk6ˆ¿¶Š�CÒGdŠ£Õ CÓÈ+F+J)ÍaUUU•{Ç?|Ö¯:_Øwc‚:ÉaU¾¥YY,Öy@æ$]ôHšÃŠÒù¦ŸO>ë‘,ði‡½½·?âGYÖƒÒ…Í| !ñqUZi¿*<áŽÇßÅOûêјØC endstream endobj -443 0 obj -<> +2190 0 obj +<> stream -xœcd`aa`dd”ðÉLJ-*ÉÌ+-N-ÊLÓ JM/ÍI,ɹý�þ!ÓÍ#÷C–‡AŽ‹A¾»Bò°Ë<¯ý=EFBú×OVY†½ü² Œ\‚² ÿÿó(�„>ñ¨€¨W<ª ÌŒŒl|ÞŽ)ùI©ž)©y%™%•Îù•E™é% -–‘�¡‘®‘�‘‰BHFªÂY -EùY©É% -Ž¥%ùEÅz L Œ õÓ8Θ;ý»ÈÔu3VNeûÝ4�]Ž‹yÚÎnîõ\·¸oMâáý±O”¤¬‡‘Q¤§»»ä�Û4@Ä!!&FFßöÐî½?^îcìÞûCe/s7Ë�ŽŸ~:Ønÿ•ýn÷}Òw»ß“Xÿ°ýØ+ -býñ¾W}Ÿ–cý3�ýwÌ÷ý¢¿í¾Odýþ‚�¯`Ú.Æó?L™¿}Ÿ úýâwö3{VÖN”ü#õ[±©ª»zJ�ä©ßJ3'v­¬›)ùGû· è©]¤Øø¼»– +xœm�oHwÇïŒ9Ó4²ÚŒ5wë,¬/¢­°Á¶n¬uu(­í¨Ûº®üsjPsÎ\4î¢5^Œ1Ï™?^âŸD£âÐj×vQ, 8¸vcŒÂôUaP„µÔ��ßÙ vܺÁ^ìÍó|Ÿçû|yàC™™I’¹UlWÙ™3µ|³å$ǵh«r%_)ƒY)$ÍÙ ð¬²ö®üa(ÈU +ömë ‚ÌËÕ*ÿ|!A<}j8¤ — EZ«6&t$I—N4pulEkã­|÷+eG˜Ò£Ç^·”-}�9aã9›ÕÎTÛ¹V®ÍÊ[íÅLu³ÕÎtq-ŒÕÎt°­l­�m`¶¶ƒá›Yæ½ÎW3åœ�gN[ëY›�e,†±³,ÓÌóío””ðަb®£©¤‘³ñö’ÖgGö-g)?[Um9]Qvªêü©bÞÉ3�\ÓÀòµÖV{1Ad$‘¹›8 ünrÌ*e“Ø£ ”Z'e™³3«9ÃsaC6rV²g#¡`p,6VrVÂÁP8 ‡ ûtIAiH’­� ˆã“ƒû J{ñ2™CndddÜ×1/AJ©H‘©ÞLéR/(Eʵt•J &¥o¦+²Œø�¿s·x™|’�ëžœÛ=nrBeÀ .Ep�;ìóåŸê ºERcé‹ÍÊgúÁsá5@3Q9õÒxÛç'Tœ€\¢(¸#Þ½q�ÔƒÂ慨ô›MŠKïûb5|Фüwì2퀷þ󈮾¨7A¯Bph¸ ­¥ÓKÎ÷Œ‡rL’@ á¤â¾‘þ±¢‘'¾§léç7 ߆¦†C!˜„¨7Ô=ð‰¯�Á2~™¼Ž_Õá€i +nûƒb÷J6©' + ˆD¦‹ók‰/Çg¤À#õ#'ƒõàENG�³_4*æ9ø90Óp×7 (6"Ç£ý¡nšƒÏ‡½o`0â�¡³ÊžÞ—�äN¸ îO/R¦Ò·}\ß~qPðÿ xâ°ˆÂX¹kZTï¨- 7âÎuR!×u»UxÞ¤&q’Š‹² ˆ¢`V“j2KS‚,ÆiœT“”¦â²7ã$Nfij¼o´—6b ,ã¯ñüò~åñãšGyÛ»>eÅôàØÆ³zŸâÝ~^˜–ÆèEÿÔPÈòûÀ CþŸ©Ó z¯ÃÝëòz†ºý­€:Ý¢àŽzghü'õ=lM³Œòvîܼº ·á^åú»è—™�à!RkvLyÛ?^o©¤áBWm­µÖzI¸H¥a {ôò5€U@¸D}dÊÛ™àGxè€�ºjêë?íþ�šO¹ÁƒÒø†!¤àΛû5þ§©›p¥g²ñ‹·ð¡[än•¢3¥÷(A]. ‰²GÅe96¦!PÏeþ�5Þí¥�‹$� endstream endobj -444 0 obj -<> +2191 0 obj +<> stream -xœ½k�$7ñûýо \n€õúý€#@.A%H(÷�ð!wbˆ p,Bù÷¨g¦{Úõ²»{n¿ììîØår¹\.×Ë÷ßüë»ãë×÷_¿ù݃¾ÿê»ã__ýåxøì³Ï¿xóì߃ô ‡;3˜ä•ÖÁ )&•c~xÿýý{=¼ÿÏ ‡ÿ¼?>ûüí ‡·îô`ÒðöáÚwüxûýŸ^}«µþVkspvúÝ^>ÝåÓ_>Ãå3^>ÓéóÝéçãâ÷ã¡ÄWus†õ°hõáòM¼ŽlεÕ5ÖLm/[ý -ãáðç·¿öåÛg|öå×oî—„4ˆ~Öe}qCrE•`#K>3#’o7éŽÕ75Á®pSÕÖ.y¬G¼ô‡=gÎ’Ï›šÔÓ÷Ö1ÿ÷<á-"|´ÊEku’.Êæì$ÆõJbÝ£‰Ö±Ú„%õ?­#�¹0�E+b‘xF« a6O¿Çj9~J›•w&&3±žo6¹À¦¾‚lbkö#œpŠWì�®�9.øzæygøþ¦c~ÍI ½ÂÂY%öPÒ0»Í“3çgwÁäÒÁ]À¹ð›†e-Å/9Î$9'H‘f0—šë~%â¼~ÛÇÝÄ­s“Àìö±M\á*lÙYÃán"Îãõ× ß<¼˜{²ÒÇKÒ'Eå¢va»øa·MHJg­sçLpR*� Ò�oàÒ9}–H’Ál03±G|߉ç'YÛ ˆ�ø¶ˆ„E2Ã: aÇ2\´"¸ç(åÝ¢ågEO”D�ÏÊÅäwˆ¨–§�mîðB0&j\xÆÅ¥ª„èBaù’'V’ˆe³ -%l–Ó?C›êÎe“ÏébQ™4)hÂà{Pw6Øfiú˜`yˆP7‡79txر{†k¹Q§Äï(±1,¯Ñ‡eÌ||V²M!á‚�_h{x�ÊàþÊ©ÜÎý¬4Ž_/¶=ïÃ=qëCƒpý+7ñ:D 1(‹‹Kí¡“Î½H ÂSfä CˆÅÚ²R#KRÃxJqi³Œ½XzÁ¤Á.D÷uÆ|B©âAy­�™ÎJd)»nuÃuª­+WÜü‚Ö±c/Dȼ™ŸóH›ÞM๼¼¹Uàf¶–„È=©5NC@€1ô;ÐÒv õV~áZ£&n¾–úY�˜è:ñ{zoŸ$¿›+â`8/]Z,¦´ó‹´óµVQÛ¼C»š‘Ÿ:’VÚ;ëIYÅ4Ë´R“mÁœU_qÙª–‘“Ÿ²cn¬ü¨ÝÚtÝï*öÙ;¤8G$hÛuXŒÆþ±ì”7¾‡kì�•Yæ€}|Î¥½7û9_Õ¦�j)8ËX½^›üvhÍ¡]\z¡- BXt‘MfÞÚ¼Í`Ãj? ]u�|¬ú>®ºDI,9CŽ*ê¸çVØE]§�7‰ l8Ü]£€¤cVòkUÉ6¦§ŒÐ*e­ o -z.Ñ& -ÑW&ŒsŽ®cʺW©žãijnÃ.- � †&hUb6ö˳ `ˆ¹|¬FÕ®ºïã;5öâªÄP‡�z1E0M±+æ6¬¾¶>B¤.]GËH‘%œç39 SøŽQ4LkrÜô¡Eƒs× D‰M¬dj!< D´Ê¶±û 48ÛžTënÖ£ÑJF¼ ³*9š¼y'ƒ9/�ΪcŽQÜÀ÷ЙÁ5ìó Rp€ ÒÄEq©ªÀé û+¡”ÉSÛòð)¸k¹Ÿ¢ãÖ^gP'Ia{¢Éo1ëð)„*o�GT“í÷á{Û �þéòRE2úâUÉ%î,ërš^Ox …æ³v—‚å©'‘õèãç�$€íkï0só¦5N ØÿæÃãß¾{ÿøúõýÛþõ—û¯¾ûáŸÿ}”¨œ­ -¹Äé¶òæ›Aß¼ùà•þ7øáëSŸï=˜¨U2& ÿxöͺáJT:«ÃÓ FÎ4Î?ÍhÑ*3Òò<9—KÌ­á|ÒªcÒ ÷Œç²QÖ“žjEs‹%Ò©k'àÔæ©òÁž¡c™°Ó¦¢¬Õæ#­Ø¥ó)SïaâRW›%°¥¦Æx'ѽ䀽®�æìw9憳Êh.K'>ÝbÃUí¤ÖUZBQ¸Ró|Eñç­²v”Û9Ó-)£]Œg´OgM⾅܆¤ î!xº l":µ9¯à/: îH¦×౓€EoàmhuÃÏúc"×s2äŸwŸÚýŠ…*Б8Àc\ -WáFÛl`Úë%á�Ò`³Õ~¬§µ„v¡.¥;i‚N*fïsǦï�·™ó¸ç|n%lm6]îôÛÅÐõ<|ûwfà"J@›•µy‡Çªe»_0E6Êä’ˆP¿ú+&ýo -g"!H�RX ¼|NÁ‰ƒF„ËdªT¨uSB¡½´Õ®Žü�–ÿ"#Ä«F?ãËÅ4›nç�NË/Ñ -{6nÚ´¯rë©ùkrå°òÃWMÐâv6^Yg·UK¶YXdy3{ð",L6*Ò(àšhõhüŪœ�Në÷XÝ“×ðëvU$ £áÈ[w(Ÿƒ»à 0–°Íª– ã¦# »„!KƒžÐ‡­Î[•]ôûyRtrM8lUpÑ)ƒÛϼ/iJ}ÊÇU°, ý'v'‹sQl�[‡¸®lÄ—0zA¥œàÆŽ¸6çEy©µ².ÌÞ”-‚É4â�*!…|•8Ž6éÎÈÃ]éâǪÂQ"(TTkñ0Ë¥‘˜)£Jã±4"R Úóë¾à6AsXóí¡v¦Ó˜ÝÎ{ àƒÄ@F˜0Á�µgÄA!¯®üƒ–ž«ëøÒ¦v9V>Ä-î¬Y°]ÝYs*ËõH†oIÈ„Ð9Óœ¯(Âz2¤ü5rÁ2|û¯ÿF+�’rEÎí„Á°Æ©ÎBp¯‹=ŽV‹Ò. -wꦣ…ò‚ŠV*n–D‘‹iDZ#„ŸÇr6t\•¿4k|'÷—^‹¡­ƒ·Þ:V»k•£S檺I/¡H›Ã ~àÚE¸P6QSt«%n·  žÿfLŸ¶1 .ä̼PJ¸ª!šô¤ýöät©.=5~§fq§†0þzƒÐˆZ sž›t°a¼4‡Ô‘„ ›^wÌòvŒ²(è4ÙدgAq?ÿFŽYgl4fçêM`!œ‡[¥+Ýͪ«‡ÜèBSô•gOMf±CŸ_�ê��´Ëx­Îà{?f?FjqÔ]áIä·¯hÞv~üÕïq5¯sYŽCû\‚\áÐ’9;©ë8akµ¨«óì(ßÛ­Ý#ß« ŒFÙŸÈa®óª bºgýyÇb¯8�ÙSô BÄYµPÒñ¶ÐŒ¯Ù)Ú¾�MËPÎ-[½mpPö î]¾�×3E5þ`Šëµ„Í˾*ë\TWUƆ#®¹_wY£¹qÀ5ùÒþZ~H¸gz#Ę;m•MÁlŽä¯“£ñ5Æ�¥‰²…é$uHÂB¼‚öLíchO„‡:>p\vÓÂñoR’‹D~µ,o°Ö¨\‚éXµ–QàáZPßÙëïÖ˜aß‚õZÅTnÀF"¯ÀO_¼±ð¯ëø¯û0·r·“ï¶•#@ {=Nœa0¤ÂÍ©,[ކM @ô…àµJxlú±+xo.Í5øb[¼”nos¡ß ·@(7î3YÄ–‰+¸¸3`ˆ>(ø ÃÄv/¼Æ¬ä?ÁÂwõ³ ”�ó3\‰6÷.•¦Ôu /+®ÂÙÉvÞo-8~5‡Þ€"þŽí3ØÁªÐÁV¥Xˆœ“ y? Ò­§Ó'ÔµeÆè–È€àZ÷,lÖDcexñ"UİÉ+›í¶x¥Kp¥iJ ?Ò ¼Ø{pÀÞ)4J?°ó°¬gtáüÎ\uÇŽÝÜê»õ(‡8 -ÜžRqMê` f8h™µñ)ŸhTôÎØÙáÀ鄚ì-îÑé™í´xà{Õ³Ù›aŠUÅeÛ�]_• k”óöRÑîŽZ‡éýŽYLKZ«œõ¾áfî¡]š„�àq -DÔ—KÊ—¨óǤ\ý¬®ÓôÀußü'‚³ö=4á�8â^á�Ò.ø¸5«÷À/¼%ì„H\&¢S6z—ö¯(~¢†G+�6¥]öù<Þ€¯LÂe+ŠZ�;/~\YÞ(„zú$=>X]Oÿ¯®Ç¶$ð[£~cª¾‹¼àº15F€9, -»žç+"ìöXùÎlÛJ>x“²J»*.­yöÎø ’³®£`4j+DRÖ-�‚Öä{ ˆS |Vet‹¡3/øAPÌ&ºô}b¦û6V/>òrñŒŠ5tç� -%õðiK¼^(ög àÉÔïlc¸Á¾!ˆö%%ž€À¨:ì|T@«¯G ¨Ó²»ÖÏV¬D;Š*1Û>pPŠDŠÒs®&z•vVûªnÈŸÐ JÛn�"BèÚ&*“²¶¾c2ÝnëI†ŽêÉFBÿöFùèÍMèÜCŸDDƒû¬´Î¥ì'Ï©¦¡[éE;q¶³©f r( -°Î*–�Ì“‘”Ð}MPÖë`oÁq‘H׫™ '*Ž6¨�]Î7ÙÛ'MM9ý%àNŠ¥&ÓKªî­@T¡§|ƒ¶Uý<ðõZ4;ú®§t èÒ?»j‚ZZʺ•dq>;Æ(m’Û±5òWHþ$êò£OÙf!de¼Ò2ºHÚ~XoËIEŸ�ïÀé6â «j%(�±·$Ë\/èæ"ž( -pR|œËOFCB«NY?ÖÞODp^KÙô_=B±Èn|ñ`<�ö“蜈æUÐ6”ÅSº+œ©°? ðK"Õ©ÝúÈæÀ 3hð«Óýtä¨ëìPÖC¤UV†•LŽdˆÅŸQ�j3Ê -9›±émP%è”÷ï…)/sòËþŠŒu?ï�2ÖúôT‚!cÝÏû¬¢ ö‚a¾aNäHt؉éAk‚>ûÑp�ÝÇâNo(>¨N~옧¨¸8^™÷Øt$ÖÜ9§¼÷®�Ý`!0ÝñÂR OŽ;ãF_—­� °B U‚�õ>;Ž�^¬-¹\>¥˜€�³Ó–Ö™q¾F˜v=UÈÚvðjßmªà’)(v½UÖÅÜdï¤n.¦Ÿ VÇ}ðLKû6G“Ú¸¥Í¯Ç�œ±&ì‹Q9™”ös ¬É8‰§(˜!¼€,QºÛ�Nïr)ܳ¹¤èßÑŸÄ"–kM�u�¿­t+T¹†¤rJ£ý²‰ôMWˆ²Äc=×TJ¸á,-‚áP( Ðu¦PÛŽé¯ù$?¾YÓ~’^3NÍø˜ÕS¥Ú9T>'HE>Ô}IG�÷œAú?hGô‚`l�äc`� vVÍ`QÊ]!ÂV8 v©ªyV_1iŒ–tiF�a¨†1\¿Ý®æ3Æký÷üÞr’ŒÕY%WŒÛ¼¹N{<Í?ÊõXMFùâµíи¦ø+)‚쀮ÞPË$Æà#ÕaS�?ÔÚÕB¼µKÃ~ä!Ò‘ø je¢ ¨-·Sc…¾½X�d«�Wæ~6Ðkn ª+È—Ó7Zr>hu�°AH4j,1è¼ÀUAÍBºŽ¯’·1m–¿=Ó á.”\í¶¨ CÉ\g=¨$;Ü™pÓÃ&Ëü+8tw<Ý�Óœ8dÖ�€jPL�7&P_¾O>pKÁ ˜Êß<Ä�å{WGÊ‚s” 5BÒ}o8Ìti™©Yvzµ–¥>_‚—(R¶BÖ*�^™=µ¡VÚDí)óØyýÁ–\u”‹º‰Ñ{ßÖ\صù/JžØµ)�q�i×U¾}.nDDÍóÿ€p¡’ +xœûññ endstream endobj -445 0 obj -<> +2192 0 obj +<> stream -xœí]Ý“$·m×_1’íÓ­œã‘?mY±%;©¤â‡TîÍʃO•MUª¼•È÷ÿWªg¦»ID÷ ›³r•_n÷îú € ?¼ÿ�ÿýóË×_¿ÿãwÿòû“~ÿo~ùï·ÿõòôÍ7ßþþ»ÏþïdNú¤OïÌɃB ý)PÖŸ~øËûô釿žôé¯?¼|ö퇓>}øñý³>­ÜéÃóz÷ôãÃ_þôö{­õ÷ñúÓ^~êùïîúw¸þÝ\ÿîžþóÿ~ö‡Ÿýûgøãwïs’ME©ÑF�Ðv›T³EªOïÀ•´ÕîüoïÎÿñ©¤Øœ¿@•Ÿ§C›l¨Éƨ’qa›h8Ee%ö^Ù -3û.??µ©Áš—”±Ö¥xˆa›¶¦À¢3ýüоänòÃÕÔĨ´Át0?ˆn}¯Á·ÿO_”íåú·yñ˜æ%ýØ~"Yp–_˜Ü½f7¥ó7áåçsc‘Ã&wæ/q[Wžö^¸wYÁËÏ—>Hbœ™~µå›>åÿ¶KQü¦ÌßDÏÒ6?m6!�Wi){iïpQAò‹§!¯UÉà âÛü{©‚i¿\±Ü}áþ›Œ1Z…h4úó…_üãGqù¾kšRir6´·g¢BmÝ6‡·wgÂ/CÌçòwÍ_•Äǧw€*iƒÞ–wÿ„PÈh^©=ž@¬˜Á+цôwHÛtjIJºèó€5§…±þ¼ùØsFÓås“b¶®Ôç%Õ&cþòç�-§¾óyÃâmÊ'�çúÉÍ¿ÏoX¬õOƒðœ­­´ºçTó¬©ÛI²©Ö*ÔZ»å‰ÏÍ¢2)ŤÍùÆ÷Ó?ŸþЭK¨ÒάäUûÂð_ÅÃæÃ%U%WÎOYV�Ùq϶Ã%ÑóÂE -×g>·ŸQøŸò_¶Ù‰ K™7u†l(ü‚¿�y³sø -O«K°|œ æëz™Eë9ë×"Œó×6]ÄVù6ÌiGÊZ2"¨j쉔DÏË5ÃãðôÎy…ÎYûv1&g+kWì ZÞ4îÿ‘§s{yƒ€J¤>׊æNOï¶‘i(”â¥!ørmêÕøÂ…ìú’ör§Ï*ýµ—œ¹ë2#ò -=Ì_’ …»ö‰¦æ:u¤[·ùJö,Kº‰ -|É?ip»#µ¡ó¸è±»Ã¨ɨ¯Pî¶*­­wÕHc•C´f‰1Jïxöüè'¶o̾ºyIý_Ôugô…ÜB“3ÕËÜâXÅÙržßÿÕbÏ¿}½¨ï[Ï">»º_š„”—WF¯ñaëmY¡¯Wq›º+îxA–¢¹A–Ο·¢_IMé50iˆBÛO\†™]œô=‹ ¨wÜ—ÒKÙü(j$K’%РÐh×£T{ÃÊk•ô°|†&‹r74׺"ÝfÚO\m;“;*—ÈiÞý(ÙÙ.H_&¸ŠÕ—n L`RË‹¡—6ü•Õ/yþµYi†ˆi§YŒJ¹bÄö¦qg™ºk‘ÏE²"{Ræ„�äºÚÕ)¯ÿݬi¿�þøçé�¸©:MÇ­q!›¹¹óë©ØæTÛ¨ `T|Œ -�5÷Ú”S;\-¤VR0±•Pj\PK€^Èm~µ•˜ï§îóñšÊ© %f×ЬÌŽV>®È`®iüæÛ!³Ò\~¯´9ó¿Ó -¥Y)_òe‡”g®À~O¢|ꋼÅT*’F¼ Aɶ»‚wrõü¤…]øÚh”Nƹ텽yÈv¸P”&áü×ÔÀÎeѦn«#­¸vœÚ&U:nóÁ*4ÁÝ€U"•­\GV`ùV¢Äá¶Mˆ£¯>ô¸˜•U§×%Ó:Üúö%ŸLéÇ–îzÖü¿EÀðR™%%h…pT˜©‡³Ê™ŽJžÅ£&†æÂ‹g�¾ú°.�Ja›¨=5;1*oM´~«d§X�å]¿,¯_M]lscé�©sŒŸRÀ>¥í¯ß0}ózµ¬D´ Œ{>_tùÏËB½ní`ŸŒ× -¼Nô�²Tø/„R§:ï Ö¨à½=BÚ{ª‹ ÎR� -œ1¡›å+'Ê�ûyåfÉ/˜=ö"m‰õuLŽèUB·gõÃúÚƒG Êš±ŸõW–gܨëˆ�²Em¶hµÝ†P5i[åÝ&„£ZûF6z�u¶_  m.¼«$V!Ò•yÓ�îÚQr8•ò¥‡-… íÅV+g¢�+ßb]®®«QZ¢ þ€èvóôÎ:£´5¸<6�]\þ6I±k‡À„ ¬N wðæù0ó¤ÙxvKzåó‰0—µN�RŒÌ.ìAùð(þ"³ PÆxs€úoï™ý(Ýïì´O žÙK”�ÝÃ8Èl¦8qÀ¥¡‹ý‘äîª8’Ë[Ìwx Uïi˜‚ŠÎOÎÙƒËlj:(ˆúÓ\¸)Ù!ä™\ï~6D¥£Ù¥9Ç0¹Þÿl Êigð&g.ÔÆR®7§AùàãÃxQï5΀2Z‡#XA3}WoJè¿Ð‚wô–X©”ÀƒW¦ä`÷þPà-ѬPXlk˃ò6¢ÝA×1¼© Bå�ñý¬ùY¹Îû¦@ Ó¬cPŨ�ïgÈšqǤÀ9�˜q/¯’’fìåÔcmšóïjf÷óˆt>h©ËËç�KÓBëeÌŠêDæç­ki -}T#7Y¨Ém¾Öç›MZ)Òš"¿ã»É*ò‘öàÅ£þo}fO²¸3˜MNì¸i>¼Òü¶1p’Õ6 ¸ž³’²Ò¶Ì°e]*E›Àâ¾#Þ= [[ûNœŠ)jØþÄŸ¾ohk¿Ì$¯|J& ÂJ(Ô*/™ør12õxåcØZ´ò’Ò\6´'/HÙÑ‹a™F£„ -  exÿ)bšjwMT£=b2¼öKþ�>"?”yi¿†;}¾Ñ -°mO­K˜ºréf9yr»$„~ìÞBzãÍéíÎg©IÀ¥¨Dwžt«³#<½3ç„F‚MîUW.�µ´xáRÞA«þZUØ ^xkv2uâ�/¢u8²ub{²É“KîÎ9/ÒZ)qp/¯ÃR3'0f0ì$˜¨ƒÚ¹:tDŸ”6!…Z{\fÁ1IÂh•‰Éï!d…ÌÃÝ›iûêXÓ‚WÁFï03_£�=‰áÌ�˜ -´·ñ†ïÒ>&yœÒzÊ­À°›“9ŽIö%§£Á¿-”Ë\œ -×ÁÎÓNed±tP1°Û4߆ÊEÉnÇ¿mr™ó.t“±šÖN/‹ïÏ·ùÚ™ŽÏmp`#þõ×3G]Z«€Ð^é”°Içñz˜®Ir£ý5õ®Þ( FÇ1‘]ì=©ºj…-ÌäÊ€ -è�¼¿M �¹Æö;©ýY£´—yä{$g”ŒðšT0{™_fß´.ÜÆYiô`¨‘† Â}éì²d�Ô4³––Ë7ú?–'7 ~à0¡W¾5ã̳*CZ9}2½Uçîäm¤ïÌ ß_l7ÕÁðVû®ár­§�´ïÙêXÚÚ¤¼µ°½vVçê@€èP ¤Õq³ I¥\ìX�³_ˆOþ§�ÿ'&çÍ/õ²/™ó‹U´h0€?i€’5Ó¯ñü»»/e²¶E @…uøÂ5cIç-Â[ÒÊmþ0…: \2Æ gm¸FÙ×ó;�U(åŒòŠ+â™Z°¬ ,³&î˜à'f�±á¤U˜~jgw&›!z’=ŸƒHÀ˜ `¯C… e"Sࢲ6¹qw&ëçmš˜Ó|�Êip(/˜ä§�ÊyLÃ�iY˺•áŠý£ - ¢ɦÈ$7ªè´é|í©ýN$=E•t@ú­LÞP£JÞ:wÿ{¯2.û¸\ØÚ‚IÊ ‡ÞñyûÓkó -`•IhÝP–×–`*ÿÒ¾Ã.–[Óì‹ÀöÚ6‚µÊL)ðQl¯�ؤlH“k6�íLMÐÔS†>v¬0r”òqÛñеUŸTÐSâbÏÓY¬ -ÞMYœqD;u)±ÉiAœR�ÉtŽ .Y¼_¹më§Ä +ø(I#Lu^FùÌ‹R:Ž7 ßš«>Œ£/^r™î¦`#ogL�ü¥‡|xœ¢~†¡ýemÝòû$&›sï,ÛòG-¶�SÃ@ -yç±ì!ùÜ­B½O¶æK{ù"x@ �à4Ç)¤£˜2—ã6œ�½«^ò.úÞÜNZ‰ ¼�ð ö¾¶�¶2 ÈUæxPÖy{wa�PãL8t´0–®¼¢=Í�>©,šçp§É=íÚåâ2fN}5;@¥®ý[¬Ã”N tT~Ò‘Maílƒº9;(a¥sqDT!Z�úÕ‹a|QÝ[Uï‚w7mœZQÙi+Ü&}{¦åœ¶,umî«öIA4~Á®þß)Äú:ó �J)%¿”Öþdˆ&ý²�5Yõ³(ÈÞì­ çëmÙš  Äh‡(KÞDK×ï,âø5jŽçu]Ã�s¦ëc“žV }yÝZ;ǃ ˆê-†‚6*ë&|Ãû;Âv7dX•ÀŰÝèK.löTsÃÊ›çAU¤à³,ˆüöcÁ$§aÜÚîaæÎ“�O»Z¬¤X›Á’TÐÎÄ~¡×]Ñ•ÈÈêÉe-tÝ"nJ€9l7{¿#9¡•Õ0 qÓ‰vÔèðˆ%T3�Ÿ°Gv¹j,k’$)½…yë¹°âÙÖ€¡[)‘&SÞm6ö�½¸§�¾¯í΃/qO‹·a 3KŽVY�ÖÞ­†L÷WUPNËÑÏŸóO¼‘•‘X8ÅÞY>¾îŸPYÍò*Ôœ�Ù"49¦!ŠÁÇPå”�ëÄdI“¤YÎV£ -)�8’Áª°5Ó¨ðo È•ðùÿŠ¢uvaìÌõÅá�F»HeêFô!ܽVZ8€Ztp†Áè¡J랪ɡêJjßY‡Ñåí0Z¦á…ÕÄÂþ9?㇂.A'(ÿêž¶ ÎdMžä -a]qe=‡îMë‘à¸Ñ.„Ô&A^èþ+�Hj¾ƒ`µØGh ƒ´96°«òþ5ÊåJ§ÍNÍ­%LöÕj¬ì �F�Å U :ÂRÉÙIIÙ„àŽÅ‘$wßæœOy{ÆÖ“ßm4ü—WÓ´Yk€hîîä@uåçÍåöÔ|8U}Ò\Þhô~Ù&šßÚx;m¾–gzî`UÃYà n>S�}/óž}{”àDH€úŽY’Ÿ7®�ZºC¼xÙNHÓfÑyƒíh‘ý$Ìh¼hö‚|{ÓÌݽ†ä¡Ô»Øé;ËÛ{c`Y&P"K(IFRö 1¨˜ n1»w ƒq�Ñ^i§}GꞢ Zw­£í§a9f¯I4ÕI¢4!#8:HÚqRÈ€Ô¦ ‚‡�È‹�>Tÿþ­@‡ø*‚ÖÆ fHGûµ£1ÖjMÛ”%ªEõúbÃ¥Cgití�ìåÜS›o VäZ0ËjR˜ôȹ“VZcã|:i…o ë+�*Ši;¹ñKáµLõp@åÁº¬ì2 j£ Qù-Œ]5 VãZ4|Õ…k­þænÄ:®U*#×JhW©¼ÞRaЇ1)ôÓ™^^†~>I²HBµî0¦üi�ñ•4 X õ4ãÌw¸ÔÓŒ3#,.áµÛq¨a äÖ–Çñ{¯X,Ë€j¡Öj:}t#mšµ¶=¾š±`ð®Ö¦ÈcŒÅ>gž�tBp -1¤†ÙÚ´ØR1:¯€1YËgòáë÷£ØªÖ(ƪ¦Ô_³g/¥Ã84V�ŽÆg PœuÓ MyyÃ`­qÒÝd]N^ÚËŒüÉã E"þ”ömÈ *ã#¡ðñû6‡dQA{Å&ìÛ�� -Ñi*µ££êwUšÏXª”@•„õÃR5.�µ£n†ƒZC¬,H‰¶(à°¬¹Îç4e!î]»Ö/xd€%Åj—“‰6|@‘¢œÐà>J¥¨§_½·c÷AÏ(Û»i ðä$j©¢è“w¦» ÜÚì£,=w ln‘ -íd%MÚ¯®å¼ÜÊóhQ„R.’Vz&Îï✔Šï&�`Éî¡4QÈÄßMѦV Yø%Ñ£ãQÐñ‚ăwé÷÷È0 þ×ùyß9¨eOçø>4¼]ZDK4Î�ùUšñ>òÕÞµGPÞ¼„Ë´½C¸¥šØÈQ®e“sêÇþÈù¼�{ä ¨¸†ÁãÊÌôä{F胴O A²Ó½4ÌE‘®né¯5jJE?ŽAî2&¨”Lè zË–3Ð]kO{çkåô)ƒß•ÙènM¹ÛÑ` µÖÀóH i‡ƒd‡‹ox p4݈WÏ×p6IX…e^ò\�d–?H'àòpÜý’ ýC€”œïU´º–p—H¹ÉnZ©c²ë,%Tt'n2Ùbï¥{à J¶ØÁ®·Šf8À±Õ æ/ÊB|…=Õ?À€�eºí¬J)öà6íPn``Ç2åî¦�´uäÖ@H»ƒ0¶Fwµ©º^Jn)�{ÓîÀ@Œ­i÷’‡§Ý�Á![Óî�bk§ÝA‹aÇ“£‚Tä”[`M¶–%=L¿ö©lõ|8®Ž=a5œ Qš—ëµy¨á20Û8¬`gk€Äâ]!«Jf}ÚMyIžcvÀ$I5Û`QçF“h@i÷è¥aż·â]{ Ö¶¦Žï§qk3QÖŽÏC][÷Ž‚¢Ã÷Ž3ugÝö[»‡Ò¶†�}òvšm9�d¹sÔ.ö¨¹Ä0iöF'Ãd—›Ãf›QÐX–=¢ŠXl¶"m,?¤ ýkžì};{€d›¡aÇŠè�À'+àöó©Ì7‹!2‡Õ¶ÀÔŽ°Ò-z+«±¹&â/Q —k¶©ð€²»Iæv“-q@Ô8A)ÛÖüTŠB@ ›@j3øZYü>€™Gý”¾ÉJ[2'”±F•Âq,mý(à"1_!�\ óŒíPÚv¢Î|×–b#vž@¢ÐѤÅÌŸ~®�³’ø¤Ù4&9bÄÁA.ƒäµšØh¸^¯ÅìÝOÒ¦±�í´ì£„!õi–4ü$#ß³KtŠJŽFð�5Þï|se,�ã|ì÷JÛW·zJ͆õÚu;lÓ¿6OvrªÝæLüš.+ ßN`>“’¸�-VMý˜ù…)1cùŸ„d@¿(îl鎊‚`º××.Ðq¢`š×W¤ö¡¢`Ô×XÿQÌ~Þ’[”˜/µ_Žå‚fNv¾¹�WL×ú2ËæH7f‡;ô¢¯í¥¬;32÷±e,¬2¼ö¦$씣ܸ_íðå˜&w„imèpˆ?¹§œé\GÔ -“ƒÁ:R›cD§¬ƒ)ÛÐùñgûðëå·sêçk‰ µ=E¯UÀjk8˜ LC7z§5œ6JF1 Þ½Ò`»Vö–÷Ïôwc2JG3e† -¨¶¡˜¼2˜º\] - :ã H¬¯�ª5F¡nΙ~lk¼B0˜¿aÚÈD6x) Œ¶fùë�B¿öO‰§Rï<Ùto˜æóÌÁ»›êM[”$ï®ïµò:亾׬p·ê“úzæ‚0Ï -˜~ä5i:”)Üäñ¤{Oæ²|éËQÊÑýt+9²ÇëÃ&Ì00½Æ+îÞX^xÁ�íWƒ¼`e;ðbÚ‚Ñiå§êı\`œjç”�fJ“cÈ—:c‰Œ?=�q¨q(�ijÅàT2ØÊ>ÝÚ†ƒL¿kæ?å3sTe’ŸR¥ýŽR1æjG™vV FYp£U‚qœÁ++�xå”2}­Öå�»‰"ÓÚj­WÁ‹´Èô¯fÎý4*ÖÃuŠÅß�û}Î=2m·™sßËÓ;,¢Xfr7=;´œé¯Í²æÝÚÕ€^ÖÂJGÈuÕ®þPÞ€*~”–�ÁKó¤m�(8÷CÙÁd°ÓÔÔl=¡*ƒn7Ä"2�£YÒx(O¼äÜ÷¾Y·�-‘éù\�û¡Ÿ,&©û÷£›k‘éæÌ<ü‘¼`º9×yñâ×v£˜Ï,Ê#&OµJ>Ø0v‰0�œYb~è7[)7ÐÎAz[wÀç Ó»™ù’Ó,sÐ0¶™žÍ¬Z«Ÿ†ÛíÓC¹–3ÝMÐFÁ2­�kgȃD!v†4<}™®È‚¥ê¨¢”onV¦—2«½êÓÙÄ Ô32Ttí.uÛ!DzC$пœî/ÔG¦/«/f^˜¼~¬(Är¶W³*b©ÛH«ò�÷«Nj7ÛŒ” Ó€—õÛ¼–qašó²$ÉP~€”$é7.7´ž Ó²·ÂoŒ3'Lç]v°<”ùN:[~-sÂ4æeY©QTUÈÒ¯‹½j>…�⦛/‹Ð+¢Û½�Ls_tÓ`‹â¸ÛûéÜZxNô†û^+/ÐM¬2Ä<~¢B‘ ÒHœ»)ÜJ½0½Ãkڣﭲ“Ìô ¯ Ên�˜T*ˆr7Û.ždz‹×I #¹ÄM�^f%ôs‰ÎÝ ;üÈ(ûCy!…úý¼(-l9„± œ%ñÉ -‘ð�Fï€�™éJ^c¶‚Ô‡ 7ó£H¹‰Rl4—»‘§R°üD~˜Ñ¥9ç–óv¢>î�¢€bËvçFò#B8‰„ 줽$÷]"#¶QÌî¦bk·d–—󑹿wÐ2mÌë&Ztàœ¡3¹¿m3) €}’iÂ$Á·qœ8ªLçÛÝËMKž/úø"ï›Ü¼äåè‚cÍ#f!7Ky©¼ÊiXrÿ"æÎ D­™ÍPé5SLóóêãuóèZ[q�’0gj ®X¦zÍX¼ÖÒ±L#4è¤4˜Ʋš§Yoü—k«„eZ ð¼N< °Â¡Ò¨ýö¡¯.»®À'œþtŠç—Û~¡eŸ× ‚ãÍØ ²P9dzJ‘xstâÊr](OŸ(tJȧ;t† ?8õ††.+¶…{§ü=©}KìlÅžðŒ¡Éh“Ì5mŸ9…Û'K"‹ÄI\ܘݻÆÍó*9Ì� ‘ÄÞ<+³}ü?ôg +xœmRËnà ¼óÛRrHýªzˆ"K­K>䡸jϬ]¤àCþ¾ÛIUõhvgwfútªWo\]p•=ÇpF«ÃpUìM(Ý*6ô(Ý‘#Ÿ³v Ú(fÑAQm+)¡´’ì:pœ9ÿQÞ±òAðP Ö©žPú!Ü×°ŒAéÂÍÇÞž�x­´¯ ++¼Ýü <:–?ƒîÏÏ endstream endobj -446 0 obj -<> +2193 0 obj +<> stream -xœå]Ý�·‘×_ÑrÅ›‹¸¬â÷EðÁ^K‘-+ÀÁûåA¼A‚x“S6¸Ëôl±X¬îN�qw/Ó»3l²X,þêƒEòúû¿½¿ñâúíÍ7_wú‹/¾úúæÉuÐéNwÏ¡ó¨ŒGÔ¾ Á(°Îu¼þ¨»�ït÷÷�÷O¾ºítwûéúNwQÙîön~¹ÜþøûÏßi­ßi´ãóê·ß>yyûä?Ÿ¼|{s½$ -*¼wö ÀãüãSC�,‰AyM2~yuûg¾IS4™Pí\8'ï×ÇÀt€*†€¤ïp DEkÀô�VˆÆa²Ðÿ»#Xó¼ÊW’¨’±6°$õ4@ð=I‡?lè´ -ýSûc÷r»Àø’DŒ -´‡³N�K=ë EGQ•|ß?n,¬³ICòäÏè1Xw žM#¡‡áyÕ„Œþ»Çÿ Ãöøí‡á¿¼¬ÖÃà‚ž‡ßŸ’Bnùã;m†ï�Ë¡e¨Œoê./[«›~¯ { -�`fuà»6¾cù�ô{PF›}¡?IÐßJ@>¸‹‘8ZäS ZÑP*ÀTðÒeFPBÿF -Æy ˜_jC@£´� ¹`@u¬„ãËvϨýö<,™4ÎþiðÌ–1ZÒEemrÐȲ§u~1zÏå´f×q*Õø¨œ7é x5Añ£¤Þ L/q¢QÁ8owí —£ - ã9ð2>|ìd-™çA¬_‡-,q!)0M3D›ºNÁ^ÑYåŒ�ͰvU,Á]R.Y̓Zè!͹N+­MBp>uZ™G[:ü_õÒÀ•x‹>© c³øüÛ±)!CR1�Áè˜ =#PPâ'Ƥ’ »Â2V|²*%ëÛç u´Çù3yQDáÀž^�ÑÊ$·«€%ºã”uhÛQí�ÀÆhæÛå¯Ä±¸[Sþ¦ÄaµJ>ô.ò~œ2%ðšè•Fpo{I£¡Þ—À y­É›¸+ƒJìµÚ+tÆ77«]½ÙW-€2Ьº["ª¯Œ�:œÉ p‚ã<ÂʼÔ±)Ñ×:¯"ºv3™M�\m›”õð¿)Tt©§0ŒQ2B—Ý?&UÆsò(й´ÊðQ¦ï× K’ɾȿŸ\ÆÜŠÏfOöÝøfƒf"”À̳ˆ–§\†E•t0»‚…3’uÕÌj¶|¦�:qLè“B§ý¾rÁ�ŒUFSåuÉ9„‘LRÆ;Ø0G²VYD³¯öU«‚ 'QŠ~îÈ›LÑ[å`³>ûTmÕk! Ú8YÇ¥©eXO,Bls׉¢±ñ(…>9ô��ÚµøU­A§Œ iWÈ[!Èڊ8â¼Ñä{–Þ|‰Ã&8•@£Û•]^ -µþ4£B”±•ºÎ -1kŒJ¦§`üÿ��6ª¤G¥$döeŸWj‚”MÂqênx�²MöäŠ~øÉ-Os(·!I8,ÿU –C0‚g¸'˜°€5 -Cˆ¦™=|ùmÿñºÿ¸ºï$/tÏþ{É Ýu²6v’º';¢äˆ¶Ï–ezdÎgÎìVjcí‚U§hGÞD-%~4óf´F¦µk\G�’/¶'/PrÆÚyA‚÷$VйÑHf‚F¥“ÙÅK|§ ]C9Àßo²Ñü°!;Ö -šrÙ�3�¨%ù X ÑK)¢;2#H»v”¢o†Ãh™Déú°$HLl‡™”$ÓáÔv‰a5'”Ûœ5ƒ9NÖ…œo-eOîÇ¥RöäžÓJ°/ß5*: »òÂHaåf‰±[£ÉÖ ˆ]GD -%'™gbÎÞkã¼; -‰çmæƒÏøðPMö" )J©œ;r†±{£U1jüÉ�´"¯ûñ4H±ÕV1Y3@×MsÐ(…X÷äŒb¬­ŒAn/Ø +Œ ­`¨ûˆÊ Ä:�½!¨\•ÐÁ—B…qÈ‘þ ¹E£=¡ÃUìÍ]H!³Zý2—j,Ÿ³ÆPÖŒµ�…Ô{(èi…ÞÃïÝÕ󡟯H¬›6?Z˜ÕÎ oÿ|dÛÔ-$“°RîÓ4@s’ä2— ½(™1—5º¨ù¾F+‘ê*­„>vNÔtÜÕ1p¦ôøÿ?ñò(9{¶w�,>*ƒÎ´.º‘‘_.½Î4Ä´ÿpìŽD¹¼ÎUÊWñÀµç}^vΛ| èŽÃ±A,¯ -ñ µ<äbü8„Oë�V%£F^Ù÷d˵ð¾<Ó>Û"MŒ5ꬲÞ×,Mkû®3l=’¾Í„ëèuLÐÌ&Ô -Aï&ayf/!’lÍCÇÕ KI#;ѧ†�–‹¯V©: `ëSáaëÜÚ0YJºOyî|¶�ï3ÿ±’R¤½³Ê`Œ¾A°FI~_äÔ»ù¹ºÇ#G7ë„úë‚-LË.•Ô}½Fa‰"/™ ç'a=XÌj`0Wœ ËAQ¦bsL–—0øã©ã(�ó>Î*8dêS÷wš»t)Î0²grËÅá@T)æ0ªÜéºã59Wx:�ñ¼;ú°(`}JÀ†¤ËPt^%ck#ͳ V)ý+™ÕOÙm&eì[Å?•q'š\EkõÄC—~Þü¢™J'Y.b9˦ãÍ(+kT7éÆELWI´V)¢oXŒ›¹µ2¢ÖE6÷kÑ3Ë;’�=oÈ�|•Äý˜ÂP¤j‹èY+)pƒ£‹‚È1"Ë *b�T˜×=;ò=¹ã�šÐ´¿›V âÖêf„ wÔiþïñ®ƒ[—k'×(Ÿ3_~zøÓÝû�/^\ßþóo?\÷þŸýǃ0ulŒÊ-¦ÎÍ÷�ù]¿_¬ûïÎvoïüØé΢W½‹Ý_ž|\{ý9 ÚZ.Õ^LʱöœA•ÀØÿ›ÍyTÐK‹;´g"z¿Öž Z%:ÝÔ^!]¬=´Ò˜À^ª=´*zëñBía…³ñ¼{sN«¤µ j¯?™!Ø^Æ/ÓžïÃç^ŸØ½Ü¾1×ß½¿ÿãç?Ü_• † ­ƒÏæ›­²bèŒá¸z>%¬L†€="qm�äþœíðÛIÎN’ZÑ@ÏÒC£òŸ’…òï‡ÀÒÝ–>:¡�ÉŽJ¦½�£¡„°H#ŽšæÒ„…œ¸¤´–²šÖ³Ó–٨徎çz ‚ÝÓ eëA Zr´LimX5[[c4‹7Ááü9®|}ã_òƒÐ2,/úã…ßèýiE™ŸUL}&ɳ6dùkÕ!aj*Vô–[:¤W«Z_Ìx\¡Ýæ^i6Y§4€0½×Ók*ùÄÔ³^ö½VÖ¢ dá‰f„OÞ[õÝ1Hw~Ò0 kiOÇà=sqÎL£6J#‚¿ô4ŒušLLJ£kIM®%W€p.:0w£8ç•Ö6I4]JÔ˜ Qf–—¡Žg¸ž4§Ä4æ–”Ù˜0.¨èBË"ËÛ.³©rÁ{¶¸ò²LãïŸoss�ÝQØtëàÆ¶ž³v�ÛÓõ'çŽq˜Â�2›×ó•4”(Ø9ÍCùzÙç7‹oD‚¹zf¶sÎ ^EQvè­d�1wÐÌÓñ rŸ§ðÜK”p:T² Rr1ÜT¤±FE V M*(³æÆ ŠÑ`ÊÂìÇS¶rÔˆD—ä @å“�Ø¢K4ýPÇ5‰RÉ5j t -[ÕO†b®·Y C+“^s°pðÊ^K7‰ O3Id›¤˜Ø!�(ø?˜R™ß ·(NÉ&Âx¡áè—kLÇnk¸Î£wµ�sÒ}ÂÀÜŠ3Ϲþ&СÁHž´p®âLš-]äôs¶ �M{”òÝ«åfˆ"ž%Aü®ÿÐ ?~Ý�º#ÙÌA3˜œ>[°Ä XÒ:øJ²S”¤#”˜K‚æ㓊)è“·—Ò´–Q`$cŒ¹ȹ¨À¢Ù@ÐÚé PXá\¶b|‚¦fÏ|Á\´ UÀ¤Û£H+™aåúKŽEFÿc�LÍÜ¥DÒ Y$¿/Õ,x™UsSÖU44nº¦3COdbŠy¾’`¶ùf¬n÷ H&?s1Ó Ú‡½pFLÆQ‰2”¬•²Ža‡¡#Ñ$)ü30‹Oϛ֡$Ê×­¿´ÚÄ(rëRsCÓÌ9´*t-®e#[²™[œfÚàp¦€>Ùgz»…A+Bò*˜äN«b¦D §Q9LÆK¤\LŒ¥ ñ°yW·,óA%¾Î1æ2§™"³D¬ ¨qæ§Y�ƒå‚›2µÎ±Ò‘YéG,WäGãhž¹RjViÍ,x3iIQ0×EÍÊ«™†2î8*VÉ“g®’š%µY2„­òü˜ \ˆî «MbŽÌUS}ÌÑx“$J.}Ì�S3£Ðô Чx*ÅQ§µUƒ-‘æzª™F�”‹ýw1Ai$§\Lú ‚ìÞx�*溪Þóô�$ª.%mÌÍUÏ"(—Ú¢uÔ&Ù0ŒÌ�VI>d)Èç8†°²ÓŸXáÿ"˜¨a +xœeOÍkÓ`þ½ËG±‹ÑÉkÖfqÎ!ÈtF½Š ˆˆxñâ\¶F$IÊØts çJSmÚm]+Ї"´õ_‰ø§i�Dä _æÅ,Û¬ endstream endobj -447 0 obj -<> +2194 0 obj +<> stream -xœÝ=ÛŽ$·uïû½Òjµ-d8¼_„�aïZˆKrí[”�à Dg=A࿪§«š<7²ªz'/3»S¼ž;oóññßîüéñíÛÛýóŸn¿þñ¯ÿùß�¿úջ߾ñ_sÐ}¸1‡h•‹ÖêxH©¨dSð‡Ÿ~¾ýIÞЇïß{ÐÊþçàßœúü|ЇPTˆæð/¾ñÝ‹¯¾y;8™×Qiï]úE&ËE¹™É‚³ªçÿ¿Í­2y„ÓdÁ¦Ø›Ë'­J0&ô®é¬V.Æ�Ó}ÿçÞ¾½ýæýï{з_ÿøð¯oþôpÄÓ¥…ø§Z?O÷Ó_úð—Ÿ^¼ûpЇoïõÁhî/½§_~þç7?h­ÐÚ>ý¶üŽçßSýƒv¦iß~µç¯æ©×]ÓÆÔÿ[æ‹Çùð�/¾ú@,Ý ›à•v%õlº N ¨ËÿgÀœ>ÚÜ"çaúùrnÎÃm1Ü¥(�íÃmYyì‡vW*`Yh‚Æš¢|±Æ\�îL/z†¬¥“‡>Ö<†3X•R�³ƒ¶Ç8ç§ß¯yX†%9¥GNZc.Ì5@U˜t}œE§ÓFÅ õ~R[0®%¶ekÓV’L�~¼:ý‹‡) 0?_Å;aúìDw `ŸŸPõ¦CŒ…¸BÓù -�à™nÆíԨǩÍh’à” %ì8g:„�rE¹è´ß�›Xøcäò¿ù’8_ .oÀµñn‚­°j‰bõr~w†xæ: ÜÜóa—Às’ü­µ -%l&Åϫ͟Ù5d<éxã•-:;“ÒgîiÛö¼�˵þm ýÌÔúp,ì�ø6søv4 ÿ‡W–å¹Í#×âÓö‚Ñ9·qm„%_Æ—±ž¿ÜIãQ¼aF¥ �I�ñ:xø~ÓYýrñÏ„pP�� 6fÁÁ.˜¾_‹I*ßÇß ñÎãäæ,…bJž'y�'ÅU(y�xÛPÆrDÓñÆh•\É¥Fd#¸pãæàó£°.ž©†Tàš}…c´—nñ»šPgÕo¾XùÔÿeo¾G“+q×µS¹å6=õÔg¿Hgß{uÙaº”NSQÑc/tp qÝ€c#M�‹4í�P|àÔ´M£‘AÛý¥ê\Íž7Ô ÑBÍó"äv9z`2RüÂSŸ‘ó�8*ò;lÙv®¨btò}Jí9Rf� ZüfÐÙöºÁ‘b±Üå ¼ )\á„AÄŸ ˆ"]‰Ú)BcÙv®f•o¸�âŸ�OðˆÅ–x_Œ²&ÄýÑ:Ä/G·²Šb©ÅVø`� -¡<ÃŽ3€*Ñ紊ƆðÌ^H _X=/V%Boc7AùlÐÕ)y_]êO Ì  Síþhì'm3?|h�f¡ìÛ° 8WíylWò Œî¹§[Í,‚Ýk…Ìû"_Cû�×wY4‚ô] \€mçº'o@Ø»µ=tÐÀMm¨D(îÇ%€>Pø×õŽ¾ßº!²âÙ£Ê1n%f¢Šf��ýéüÏ‚Ú"Q‹™ž‹uZp‚½Ð©BLÈÚZ¾˜›FH÷P Åñ1¸�æwc“hÒÒ;FùJÙ¸=î¬6ÿ‡€ãè0n÷AôE»»" -F6îbu×A1t‚‡Qcª�ÓÒ!tY™œxÍ@ŒWIHÏ8 ŸÍƒD™³ôu$V<·l�wÀfØjjóW Lœ[i#À9Dhî;ó‰¸8.” u¶ÃHÛaв!l�ßû‡0Ê©Q1z—§@ƒ’ûB0h¹Zs¥çá(³TrÄ ¶äl^É•"Þé±ÏÄCˆ°e-þ€oÛ¬^hœ�"±åß9¯¼�ÖPäX Êϼ̀Ù5s[2¿°§À• Œ±%ï?\iއû¼ÕÊ›èÓ~Ĉ›yÛñ&åSÖe]âÑ•Ý^aÙØ=á‹U.ûr�="O"³¾§Ó¯°|�ÀZ¿>ɶtPÖ¦œ®o»t¼IYYŒcìî=—EÓý@«s5GzÉÏL¿‚&–ÿt1ë.¤`�Ýu\T:i$íÀŒU€=9Úsß>òdNWAV>¨öP(uœjâG÷™Ü®ý¨%vÈbÎw‡°>ò—¯� Y5|JÊ:¿CŠS¤\TÅg�žGfòn«¨�qÏ[eDlëç†ýµ˜Û�¹ã¡ã BÖ…[“ÌŒ|G�ßZ2´-…Ì϶å:Y¸í+¨àžˆjLN%£} æ]š`T)؋иFlºÊŠ>5ª¼Óo”6GðN@4Qo@'å½±v?ÛPK�‹•ôÈÃ+ ðÑ)ë²Û#V·”QeæIÍÑn û¦k†�åÎZÏj»6³Ú{YŸvP?µ¾]<àÜ”i³��&ß9ªÄ>ˆJBŪœS¡’n@*Œ Œ¼Ûâ|P˜�‰F¥˜�Y=ËëWk�‰ÍvÏ·$@öœ¯×"ìaßÊf*ÍRöYSÛ�ÈÃÑ•ŒËYdE’YËû¢¬7y·¢6à×ëbX®ŒÙ« z#ý¥Mô$4³rÎÆêË‘1Ḛ�²¢Õ-2LÈ£<P_!òã‰110T8Þ&¤­v#}Ââg ®¸W.àâ^àQ·�ØØmå1))›4 -:Uüc3ñðPð"ƒTjx)RÕ» ¬n³–%º(bŠ ê‚↳¼æ:¾7Ç*• žøZæààãB“k;bÛ÷Sp°)#M;™¤Ö.æa¥€ØX€ox¶ñ’Ÿ §!\(­Ä0dyÐÖŠhc˜)ã+‰µÆ NØ[}·ãÏ6ÒO·�Ñ =YLüzIžújúñûé‡ -6ûúä'ÝϺýl€&r’é^ìR|mL¢ - ¶*$­ýn¼Ítón µ$åÌÙT‚35Há$Þe}ü.l'о=~øwfV¬FǤ¼)Þ LºÆÍÎ/kÆÉ©ì|AÞ“r)Csæ.ï—?—e‹þ(‡²Éèi‹Máºì… ƒ‘‚²MŠFd±3žãYCç�#¦áåPø×)´Iá†ÝÌàj¨#²•ˆùMOœjÿ¿XA¸ð�æïRÕ^©“×Fù”¶sÑ:C“ϤŒV鬎ÏT,id - T8Ééø (?ÛÈ„gWp;´ôó± -hؘ” P"¨¸(9š©:äy>N·Íä-j Ž•,»5�§�ÝA†c¬ñð§Œ(ÒäœÒ>Y?pʺ浹ÚQ—É;e¢IûO9…Ì* fHЙ¶~nßWÚZÛ\?+*)ÑÉå¤|ÖÛ¯æO Ïr«ÞGe‹³sTW•‰Á#ª9áu^Û±¾ÄøõªžR¿gò áŠh1^ü‘I´¶´mq",?.Y†í%‹ ÚQL -”A)…*jEÒH~‡æKÂÇÕîôÇÀ°nÔg  'RŒgÕux×›fOƪ€`:é¦1õÁKF<¾MÚ†Œø›IšˆºM†|pÄrÈ„„OŽƒ¢%ܾ<ÑŒ¸ÎOùØf»‚×�4YVsþWå@_EŸ@bþFš”� ¼g¶^!c ìàáòB{â¸v0ìèBàD¸¬pa¿‚q0Z‰ªlPÉ”íרíPå‘tLVe­�á‹9À]†ò¼ô±ÌA”k€-šãL@¹*õôftz¦Ý có(\Œãƒw0hÙ­ò°ɬÒ´ƒ¡…mÔ8Úu -Õ�Yº`¢'x$ÁÃNËöØ„À�#F_lß„ µÈ–�xþ#Å®:cT²Îo÷ššæø¸±ðCZÂe§­ÊVkúàŽ9¤£ÒÁ†üT3`yŽØ¿cLP^kcŸ…§¿žbÓ¸ z¢¬ô,8ƒZwGb†½ÑS¬-WdMPŽ1²Ù/PÓ‚8ÜpÂ+P�ÞµA�öàV&^I=N œÛd}r]gí�l,Ðv¸L Evbx —¸fþ Ias¶$•lÜ•_ÛÐëß„ý(Y+�] ð Õ‹®Iuôö6Öß­7Ñ+C±‘ؤ¨L26ìß‚‹…DÛ¥L6ª8w”#KÙbWÔTÁ�Ý·ÐòïõÖ ŽÍ„_듊izpæT}r'Zãs°Jñ`bMÊ&­l.Å^ó ™i~™ôʸ&j£Ø Ê)hëyp½0AºX¡êKÛVÐptxiO@–P»Z²&4×|½‡η!E q¤%ñU6¦$­·Ù©äô®<{ºhl- 1vŒ§K2•RÊl®-xoc„‘bõôòzÔÆ àbôõ‹…ƒÎöªÆd¼JÎN„œ]²²)˜|•Ý#v€¬ˆßÛ%Üæ±(Z›TƆò\–ˆLÚƒˆ–´�!±ã-Y¨~Ñ2*ísÛBIšìD;‹›ÁD+ò·^sÃõÞ€äŠ('Ç¢’seó9�¦Jº6!å>2YekŒ[êÎ �/²6/ìk‡%BG༕Ï~Úy8Ðp˜}À&¬q -a vÖ@Ð¥-eÊ/ºr[ËÐQ‘B(Ñ›Õ]ÐÇ­‚ Ó­6¥£wGÛy7ðãzê„æR� ÈoîÀ[<�‘Bm*¹´+)¢Œ²P˜)U›Kúô¦0I4 -40÷«fÁ!�ß˰K—ñq]{Ñ�mëÏW/)x|>ö¡‡œ�cV¸+¬pIó{\“ˆÁ_oý|TÊPg¬¾q˜Õr:¹Zæõå®G‘™É5¼Òà[1г+ ‹g!RÀ¡õF%¯óvËù:ÇyÏ‘œ¥x(kŒÒ)Ƹµ„ô6 e*0)n«Œf3¿J¨–v9c»Q1*¤òx;ªú˜“wïòº|_�ä×€ÍFƽÑv?ŽOs— B¶©øÚ! U*!N®íŸ–×п`ZP¢A‘Á°P È™(Ç2eÓ»éñìýûÎ<à;Ë©3c�¿.‚¨0¶‡ØT”Î%ûgÙcR†MÛhÄûî‚€h99Lðæ›�"8Ÿù-ÀW¢ -&F·Ÿf* -9Ó‰³0D¾‘‰*ù®BÕ­Œ¶bŒ¥ -†o¬þÏDV|ôʸ¢Óÿ6�Š ³²Ú†ç:ÁaÍ3+<äE’.LÊÊøó3¯À3ücª}èF¢ÒΞŸÑ¸YkJPô8¢`Z²’pÛnì¹Sil>‰˜†ý¡×Ù,[Q¼ŸY Ç#b*ºȯêçC]øOƒÄ >¨`t´W8"OžˆÛ}ÒE…2s¸ ¾…¥9òªqƤg9_Lv*ñFƒó*éd~1æTïÒJìR¥è‚²©¸ølÜ øã.OA�ö(X -6©¢ñ;P¾Ð:[Ô¤H®3IÁš­iÞW<]tô‹?º¤ìTeŸ8Õ÷4™5¡÷xð‚ͧz³ñe6�´®ƒ;:|§~¦ ¡óK–>à—W…êq´7ä«ÅÃßk /Š¥rä©ÕZ%^=ˆA™¤½ ™¡Çéµ -9Û¹XöZö…ÅB—½rF›Ý$½Š}=ȄůÀh,zWTq%>ŠG|°K¥• ÑQÚÆaÞ;d) N´�­Lq�V0àsx -6×S•Ò¶dNxyI·<Û> 0™0Á/"Tz -”;nÒ~µ0©Ҷ† dÁ ¢˜ø ],:·CÑ¢öà^ÑhãÀ5Žî)°9@óþpÅL†ÙwìÏ?tжCÖidj–²L[‚�–òÈŒ‹Ê„bþv´�^ô•´RÂÖí�²)e7°Ò±GŒô$Àz3~×À.¬º.,ŒHnÉeª©®±ƒkâÝ�ÝHrðMX¦�WYçžiGä·â¨.+e2£±†cSžeGÈìúRÙº-Áj’³E¹¬ŸiGšl´¡¢¯u¬(ØÝÆ2Ô}j—ë>B7l”òÐ�%lÒÁ*ŸÊdÙO"\�” ¤#­‚6J]l~Z!Š1ñ;ÈnD›´µÊD+x¼;€¿"H¤gU °Iã½ oz¯ ½fú¼Ý6æÅE‡ŠÌÓ5{`f~£Z�yY[0pÝ3LdÝ8æš�>i�É1RºŽ2J¢deþ9 ¨øRW¤adÈvˆ/)Äš°\g`äWY+Àp\°mEû‚êˆyÒQ©žØ[Œ»œ£� àË6JE4%ër¾Zt;8�NŸd¹ÓF~Æôž«1k&åbòv…«4 -Tø %[I€b…ÀZ==[s•�æ°Í„¿]P�Þí[cˆ�ìHÔGAß·Ü*ZXÄX½ª!ªðrèazóqûváb·ý9ê°ô…‹>pggݪ!žA™‹ê��Ò¥ô°â•‰e—£ÊPãÀ&°ïUJ6Ÿ‹"bnÅYdFl†ÈsZÅb¼ï/Wö1=²&cˆ4.Tò9îÁòi^Þ±e Vٌת”¡iÅÕ¾fÅ:ŒñS*|(»)Šxr£!9°iú%­ú€2ŒLèzE¥h’Â𠻌Ù#¶˜¼p&óÒ�qÒ 415cìâ”z^4Ý ¥!@ÖçÍ -žxoÄz¥M nûy`3kU| -‹§¨1�cññJX�%ü`Sºña€e éÒ[�Ð^…ÇÕ ébuW@€$xg­Lr{|™Ýà;ê”U‘8!îê¤ršêÇwïì\ýP•›˜«;ß“¿fnÞÕoð¶Ã¢Ú’]Ç�/Û¶ôôŒÄdKˆò>ª’\ û©¾è13€åïFü^Û,ÈמD¹Å²›3I¥äónò¢¶å;~g©|pÐf]жãç:ßlâc°�™é“ýÙI‹®c2þóö�ÆPË*+ƒ�•Þˆ‰Sýš÷¼A£—¿<Ú¨¬uÚ‚'I´åíƒÍ0߱ó[ŠàWâ”``àÎ>oë׆l^ÏÖ ù'WƒKH>$NÖ…¹1«4ÕÌíøPêÄÊ Ÿp‘嬊·á'îo÷,}dì/<Ä+󕒱ؗ‡ÌšMú_ß9l +xœÍ=Ûr%·qïú +îÊöŠVâ~±e%ºæRqURÞ7ÛZEtœŠ�œ5]•ü} +ä™ oÀ™9ܲ–2 ô4�Fßqÿ›?wúì³û_õÏ_ßèûýîô‡O~8Ý~þù—_õÑÿܘ}£oîÌ�ÉFÙ”òMJEå¢�¿ùþO÷ßë›ïÿr£oþòýé£/ßÞè›·ïïô�‰7o¶¹õÇÛ?ýö“ßi­§µ¹uvùo{þéÎ?ýùg8ÿŒçŸééç»§›ÿ>Ý–øI?Ç<ÃzhF½?ÿ%n_6ϵÕ=Ö,cÏÿß茇Ûß¿ý—�¾yûÑ¿ôͯ¿ºo iý¬ŽÊÄhoRð*¦’K?s“U ‰wkó'SÜòl�RCàAÎÇúJÖž¤g(ïû/›ËØ~aÅ1ô[rþòcÿÛó=‚¹žX\×¶™–£¿¥–?9+ïu 7É•ƒüÞꛬ,¿·ËZû]z{wÆÞúö¯6ð¸;á\Û¤Œ‹ÉO oÈß«¢KÚúó>ÛmÏOî`_<`e fÀ‰›ØÞöML ¸Ô¾\éÞóç˜Ïùu-,Î/ð5M€…5zT¨¶™üÀœú4jËw`B+³QáO9p6½p‚Œ¤�–÷ùÄJÎ ¿x)Gž¡8vy‰d~K�ð}·ï¼8{{·ñ½ÞþÏ™9œ»Íî“m9žeå’Ÿ‘©6¨’KÙ+•&ÖvFÓJÑÅô´¿F[dcÃD‰™„îaÛØ~·VK€à“:OÞÇ8›,B¸··}ÏÀü‚W½Ê^†ürõ¹3IÜ™pŽþÐMG^÷í@px¢ƒ†¶½—�ëÄ•­L}Ï}�S<ü`//c2 ²;»ˆKpbWÎY•d½W—mí�3@í[{Q#â°|‡Ž¯³ p6'N@qèùhú,±¢/̈>•±Á¥�²ïu{”>®ÿü䌖V!äàlbø²Sú{Z[@êÄ|ÁÕ›‚¹²#ÜC \¦Ì°E±k£¸eÝ/Œö6x½(õÛ¬Ò$�Š&ÒOŸÿƳãkÚØLÓþœÉ[įy±/WÕâéçÏHÉÅïæâSw^Ü8¹’‡=Ë}Óöùîy:#O»mýs:�^ÈFkô ¬!cS@£³ÁJ˜8#atT6êäwJ˜å¸óF�¬¶P ÐGz»ÃÐLV‹D#¹y�°ýt†«§Ù� ¥”�Ûa¸„Þ8>É;¿^‰�é–Ù”¼ùЈcG ­ ¯)IF*Ð27-Àùt3_l{ßkÁžûZ­NX8�jàÊ[ï�� àõw� ¯aòf‰¤Ûe§”eè`³r)MÈ¥X‚ÊÞÆÝ¾(¬øßy«’w¶¿©ñï·­§OCbæuæ‘ä’%xb0«Û¢™àÌŸ;k0�?úWìp—3ö€[«ŠñÞL쮽ÉÊ\䇴æ1+<ÿYŸ”-ɧã H2Àz ù0‚Æ(F«BÊ:^ƒŠ|tê±N@Ò-Q%ã�} ‚ÒÚßä9¿#ØûÝø`‚‹�'ŠE´p!ª”Òõ¸ÿ± u8hyÈ ñ—£Ò.Æü"GàLÀõÎB÷ÑžÞ4 r’wõ¾â#*]ÄÔøÞYáD|ˆù�or|uš‡iÌAyëCÜ}wž¥|{lÔ/*Y“ØPvc=DXÇ€çôööŽ’ìÅYÕH¿wq¤OG·Z=®²ø:)ŸH�šÕëx +Ä…âUÊÚLðÍ@.=PÖô€º¸Ùnæ¾ [g²òÖf{üðÌÀs ­ƒ3¶bîÛ¡mÔn§ 6èQEàL+›·› +8u2º° G|jœ`À‰›Gg*"lF™Š ú\¦â$vsÙ|,Õ9sßg$UÊz‰½uRFG¡ˆbXw·¨àœƒ^ïþ·s¹‹`ÎUR§°&.RP6%â=N[ä ãìÅ)ü‡¹‹ÝøOVì�öŠqhø-õ³U”C÷)ÏËAàåP¢²!¤Ý¼Üä\ôÝ;£J1±¤.¥ÿ-d%ÇŒ‚öJ¶N0äÒû³ñÐ +ã‚|Ö= tÂ&¾#°`óדà‰rŽŠ�¿ç™%ïwâÍ´§n>xÃsk”¸µú…upn·ä%Œ|­tÎ9"vš3`„·ŽU¸Àœ†…r�fM Ff™T¨Í«+£/¥|6îH¸}\î­<4é,‘°G‹c9[âú$q}ò*_ös=týNxò©’Á•?ú³´�P&[+¹\áËB‡Š¢n³Dí�TÉ£ØOíQ¶šÏ*hŸâL X?ú¾±ÙÁ\¿grà 8˜ +Ö�BâPŠ*t¶>ˆ0<ˆàO“‡Î&3VI=XÊ%ñ-t¢ ˜é2׋•gIÉ3|T¥èÃEÇÂ÷�P=^=Eѹ`Ljè"‹¦±˜ø‹þ`Ô°(E•*F‡)óx£ŠÉ»ÀÝsðïÜ¥Æí:ôpN"6½/¾³üÇ9~;³L´Ê»’&8n”åÔe¡,Rx¡VS¼5V«à}ج±…n޲@¾$tèžU†Eˆz�2Ø·eƒSÆÛà®Ïq¸,PίIrhÕo“·ºì.ðæuˆÇÛ;c•qιµÐ JTTwNR/‰ámƒ’„Ð÷X5Žœª^Fó^ ?¼Ã5Ã/‡¬yn!VÀø4¼I‚ÂJ|‰YE�… Êæ"—Õ‰wÐYÅ@QçºgT.ñY�ó›£þ‰e¿AÍ"i[¸~¦OÃŒökçh-“;Ïm^‰Lžs'Îøú82†1$òí5JúÐ�æD—„ñõδû=txw•�¹¨íÖ(götÁ‰„`àQ‰òF¨æéË9±§ŒY TÈÃP>¢§§q¶ „ŽRIµœhÅMOã‚0¡GÞ\æ ž[EW‚v*䜄.¦ùÏee²ëuÕÍžo¯5º �˜ € IŒ¥¶çÓõÖÅÔä\qýW§Ú. IÕ§O4h\q0ôâ¨ü,,߬—ßS}íê7«i3›³ì$W‹/V%—ýaÇôBcœ½mksmçåµìeí�iÁS…˜a¥ì&»³•äQŸst¹|ÎF²S«"þÀÄÅ„U¶�â‡ÓXý»H¶„«?¡ÅÎó¢äcñÙ¨ä½Ûoò.Çû|.‡~")ÞãìœUÑ6Íà548ïLV9çt.ÿ¼—ºE4JZ7i2mL2 k§áTfÞr!�}—c +Î2ϰRT›¨¢7ƒúå�Ö:mû&æfõõhH¹„’Ø[” Ú…¡ j,T5¤l2/9ܽ®æCØ]ÿTliµrÅF¶_.ø;¸GР>Ò -Ð+’IxÁPÙš~Ü•ûN; ½PDkrQ9Ù'¶u� õ°=fáÜößÖ˜a÷¥ÕAER8Ìgh^¤(Ë3qpÈÌöêÜú«‚¬ËÕœòioL¢ïi½­8×|.mi:êÒ°de Ì<‚ýÈ3èÀÊõŸ²þ³v«@I™ÑÿÇ´fA&¢ qœ#ƒ‰ŽÃ*ÙÞÃÔ˜ ùZ]ªæ“?P1îøš‚2&‡Å¹:õnWÞ�Ó�PôlÆ>Àeà�@]ñ¤ûÀfƒO¬3oÑ”aÂÍý[2­x•¸oBWëiæ!! dÑ—‡éÄAr©ºX­)w ž��¥„¨²Ž[e}I �/rÛm‘)úc `PŸ_#!MÖ®êhðl© +BqµŒ/ê¾÷ò§H30y–\®µ¤/%ïv¹â2F¯ƒvW³!HQ‚�±ÆOvéW”÷)» Z�âêÀÂX¬¹•‘İOÇD§²Ó¹ßDbÛè‚U8¬þíÿÕöDê¾óšÈ°b†ûï´ÙPpÍýL‰O$�…óAE_üÛÅÚì°Ó/þ~lúÉôw6«â‚Û]HÆÉW¢*6䯗8ö3åâ38ú:ågþDm)…'5s&dBÇÆ hÃf9Îõ~6'.íµcRÅÆ8Ã5“ò´‚tB3— %F¹ÚÌÅk÷"“ ú®ß ¨ÖÙ’ª„ô©ˆíùb”Í!O ?òõß_߯öXI*ºâû5p“• _Ö²€/Qõå¬Ê×@ø³±ÀŒ¢Z¥�ÒA‹å…—5sr‚r½ÐÌé8*K@ +©�èÕ,"óÆz×ÝðÚ¤æñݾ=ÔÖí_o:‡”@ +)öes©Öš=ÐsáÒ‡D�ŠÑ·‰ò vFŒÒs:Iyï²›X¨ìÓ½F2ô3trï•€,V*ŒIÊfg�vô€ÊÛƒspg‘Â�­s‘ð’Áá[*  ¼gù®Ë}¢÷~‚óð{sÝfÒQЭø­´Í'Òí» ˜†07n2,¥„{›r Š ­þf;WµÝf¬¯�wÄ ádÚî„_� +bÉÌÜõUa낼+ž|°MkQàwy쮨m9p¾þÄ¡ò`žÏ%�µªÓ©X *�ìæØ÷ÎØ¬\,[‚+ŸqF +ð)¸—±<AhÕ:‚À4.µýC-û@ÝŒe� +õÞ`¤ì¹ly¡ç$Å%mˆªx«ˆîtý>^2Iç[þ�rúâä>Ÿd�‹¸(½#¢o/ß%­ô )—= "ì§ð¶R³±^+çƒ5{S ˜¾±·w¦˜ZD„;õ#£CX¶³SÖkgPŸ¨Mz®tÚÊÍæ/˜EÅd9޶m¦Ñ?ÿ=ÛxtÄÅM Ç1(ø®á5‘c8@®+r5‚£&�UrTÆx—%fZ€|ñýã_¿ûï·?üïãgß~óí·ZñõçOÜöŒöíSwì_Åà&å%ÛÚÛ¹ø¼›ÑÑ>­" W뤲�q`uoìÛApêkÒH¡‡`?gúïýú¯ÚdóÎF·Ñø9UÎÛûväD�¦$:fLmg¦…L¥áSt€y@v+`Õ¥!.íÐÀ˯F +2:¨bBt3+ß+©¨ŠóçË5=7'~%„·ÆGem(áøÁ)س ãðó�„™§‰­!¢º¨hMy‰�ẀS‹U‡•päì¬ßWA]œU˜DƒT'ƒÞ�™µHÎMº+­ƒÙ^}¿ÌR¢µùöòèÁ£/úþÅ*ü„}ÊâÇÝm-âf{•™Xß ¹ï†5qMmލŸƒ=D5`ãP!Àà ›X2LM‰*†h÷æQl¯ÐU‚Õ9Ñi­‘÷÷ÜæBpl×�+sþ�*Öñèú—‡³™ÊÙÄŒgŽ‚†X3Áp~Æ…˜6[G¤@Ån6B0}Ì›Ã>–mnÄÓ¿7.ØX¶5¼äœNÎÉrur�ûd)¨jrPY‡ýiNbg…-ž’U)ЧŽ~ÍWƒMÍÿ˜½#î™ï]^=ÙÄÇ ,tÖŸWq¿‹°T‰qiq�ɶ`Ä’ÿh+èbûiƒAs¬ÂÉ7É«lLúîÏÇ[§t6±ÈOE?L·ÁÙmê~AsRˆxš)�Þ©¬6cš¤ãcßQð“�p� ó/fe\¨‚òàNÄž<¹h×9“b´›¤Ê &Ì;0M”³ÿÜ…9½YÊl|J¢5)îµff+¸“y*á.—óÆGÜO&‰dløã”O·‰%Óòhg‹„P!sµJ*¹Þƒ®_DG©O§;(P¦¢d`…·ã´ ~ j»q¦üz]@›äÊqAWm«`U‰iúâƒHǧ6VE� å6AY0‚ ç�!“;ÃäíR“néL_øt§ðvÈM �²„጗7gÜ‹`ÙEˆ]#Æ,)Õo•11ÿmÅ¡oï\PZGí·–w£î`Ɖƒ„ºÎÂ̬^‘Æiô¼WžÌ~ÛúW7N±Iˆ�4à±` g¿!ElàÈ+=°ËR”çg©±v +¯¾‚ã)q «L^%ºþ�ï¿|Ü%?p=Ù$Ÿ~q3™Æxc‰ÒCÀãdù§Ìƒ¨\¶ 2 •�, �ÍÁäT&‡ T'2¼²Þ•Ìæˆ¢Â5Ú�L›+ù¬Öþju_¼á€Ëú1|wVÝæUÃ4àSm_”bχâ†h{€ÇµNˆiWD‘¯FWÕ»Ø#À²bÓÖ÷¢>?]ð´Ä/aÿ)=ˆÓ +À¨…`TE`ŸòÂC�+¤ˆƒwƒé•Ðd ÀOÞ©5Ø„Mœ¼}ľ ß«Ä‡RÉd©ÆAÞ�”òÅ*“^oÎÕÇ[«ŠvÚ¦b\"vB`4íÐù‚v2HÂXðë#ˆ°§œ×]Ð'&^§Poå3„ø˜\`!ñ¡äÊÕ"È~rè(ÕëöïÄfØÆ*Wœó°+ÐæöyCÒ  È%K®açRÞ›ßèóôÒú¾BÔÂ'ò&Ñ"ØŠ8ðÒø>pY|ŸÅ“l[½P™V…ÐÅ3¹T:“ª™�¥ŠÀ!—˽,È·LL(Ê“¹–»}­oã6`ç‰MÁYãx8ã2>¡a!Ä‹2[MÂá�Ž­«¼`ú®ì"nùzí ö?'+Ö— £OÊE_<¶Â[(#‹c%W}ÎBêɹí yÙ¾ é–{§ÅP€èx`ANøðXŒ^qPWqvVÉ¥Š0÷4‚9hûÓ¾Œ!€†uȯÆíÛ0îb´–¸ÖEŒ? v'ýgó1¦ÖgóÈ8µÜd£�´xSTñ~wow¶…HM‘qÖD˜—!¿µfq–€=è:ŠÆO¨Äø«ý7X)ݹwò½z„i/t¿ +ÉÛûÜÈË_EÐ~A3§ôØ²ÑØY‚QÞ%%~ü5F;é°è¢tqéÀC艇5ѺÛé§F�Àª 3ö`´¿Þ¥ýu ^·þÄ'cý'̧.öïC¿;©úÒ÷fëh‚úZ’’:�Æ¡žªÂ‰ðÓ…’«·q÷ó\¿Lã�ŠÎ�žze)=‰™Á�q¹¬ï™ c󠟰 Ø“¶˜oÒÏÕà§Z@²—5ôÓç¿ñOÀ¼f +b`ë—Hãy¡rot�1§Úù@Ç÷]ÕÖk& UÁgük­ã¡ùóW«n’Vaöuýg{Yñ5 äЯ÷ÈYbäJ¤— +:YÂaí9o:¬äÏŽnìgýZ …È4;E‰�RT5†²“›Äw£>Ý”o¾x0yesÊæ2†˜Ø^bÆ}?ñ4B„®ë ³DòS.U­´©©�CòOæ­¯Spÿu5b š 7ºA¸Ì’S1¸r”…z�€ÚdyHƒàŸß4ü™XOêÔÓßnÛ__ç2Zòº„”×~/˜·‘[§±S÷·CÄæ(õÃ$å´´L‚¿a¥~ i}YϹ5Ô³ªüÉp+�%j{¸è'¶t�&±ùÊÜíݹ¡ÛkrªD–V­õÈ¡f:ŠÎÃÄJqüvÛ ++"LO딯ià _çE %7'ܙܯ%ܰå‚U)¡ö¨°ÇÑô^y£ã~�4:�¸�Û]ñªhí¹WÙ;ß׿ý³è÷&ûxY¯zb|ß!0Pl;$l:•â”KÕÞ“_fÉWdȾx[hÌb ‘l5/®À›ÔŸÛlI¯‚{á/µšzÌÝß úU«N£ô�¦I:oÞ)oân÷ÒæïfôDåLÌåyYBZc°&æªë¼dãǨïí)Üoeß©½ñQ-v�_cˆòz]Tª/õÝHg&ÅŸ´„é�ÚuÿY¢ dem,kZhþgGöüÆJ«ÂzJ°O­÷à lÜÚl»^¾äØ ¨[I8«|킵{C`‹ýU+ì7g×q±„§<•œÕ¨Otñ°ª³5ßH¤±Ò±ÍÚzLwBíÉA9«uºÝ/6�ÚmLß§­�|7_±„N²ò±Xÿ3ýð�±„B‘Œ +6–ãDGM‚@^Ÿ�·w5?[}6!~Æý¡á¯°eTÂ_‹Iúƒ¶Íh"*Ö(o³ð*ìÐ(�ƪí1ëhÔ+>ý.`Æ…òyÚ­DÂÑc´²Á8?AÙªrÖ²¢wE{•ŒßÝ[_î,6Ê$kL¹0³bs…˸ŠD~k‘>‘­ª ¨ò�&U¿À©ÝÏÅŒ¶(×Y@›ª2‹Êø¢óá} "Ñ7w¾!ÿþízP~2Ú´HnZ¯-ÇœêùÌ9�iŸ#lˆ¤íøâT²Úì/“’^FO*x=låkå©©Ã7\3õÍ8éã9JÆS&Û›‚i'|&·‘] еN Ê$£Š5.NpÂðÖ³MB3ðlÝ×€…„(ñ†‡ŽJ‡Ãq–è}šfH¾f'Î�%@ + ûlUŽ%8³GJ_7ÑT;>ÕtîtY.áF€vÁ¯À‚S¦‘û²ª×‹³ÖoˆQ +yÎ}€>ëÔ¡ BÄ;㶬ç{ÐlLJ¸¶©„ÀêR¸Ú'«´)n7«Ï«m¶ÆM½3_ó•TÊÁl�ÏèrO\<ïÂÎ1D#À7}@#¤¶¶ K̺Ñêå€5Ì<\g×�‚û6‹@Ë£OÛûÁ’&t*É~ôѨ'ßÕñ2ÒÄw¬—4ŽMÊZi]Âs0UvŒû)£´!ª³ƒÏÕSM º‰8Ãfø¦4Êñ�øvyb€ç„Ö0mò?C:qþâx/K¼´r&‡ébTK4VË«îlÀ_LýÈÓï/È“L} IKæQ‰¹>7ÌIiÿ +«Õô Ìô”˜²:gÍ"‰‘$ŒwNy›ßÏe�ö}, â O‡õʘt<º×J¤Ž•�GrÝT0ê|˜Ä/Anßùë[T‚7ÁÚښ5»„¯D•ž4^ªMñõR ÒKtW�HÅÛú^ +Åäµècab˜DØ“Ž¸~Ž‹c€òv¾$â]eŒW&y3Cœ¡íƒ2Î¥5S’™ëýGsæ7Jáã‡]•ÀxÔ–™ÃsOµ Äm±) ó­ËÍk/Õ›TÿwåÇydìñ¶f™˜´˜ö  NÄмU.3�ù•�n Ê陃 �)/{l‰ø›Ï*'üñ-EÛÖ(šD’ÌÊ‚äf£_£ÔJ1Ñ“Jã'@ÂS ´s a2ÃZØɶw¥¨¢ó~7Öê=õµ/¸µgÔ>•+ÐÕ³K¹]“戕#—É `KTìà½b@¤Z?mAÇLcvâ`Ñ#õ Üù^ÿLš™÷JF¾ËYãäâí)çsQù�bŸ ß<ÑýYjâ×ìÄ>ntÈ@åú"R8¢7<½¹<\r5%5ãÝ�IVñT DQQÇ&v.X‚’q¡=×�aÏ©îv¼£JK,|òó~þ^nL6ò¤¾.Õ«Ãn xQa×À3>ž€�0¤|QÑç2±�¡2¶Ë’Ð%"Á9תS:Nø'Åܦ¤�EL¤¦€¿¯"gÁÌì0„ /Sˆ“`…µãŽôr\u‹¢v·6ý0«ÈÆ’9äbR:i³¿!9ÚÂ;kb}³mrëÉïGðY`䉃0�ГX,…‚uf$Ùn“Ęˆô0¹¶˜h\ŸBs:sj"þÊL UžÆÒo®m®ÄÐR}� QYSö?v'Åþ]xj’ÃH’Íq�èlc¸å,â‘e4ã[®±:σïñÅB£0†È¥¿á/Ó�Z¨=Œð„aøïŸv®sÔ_àY)åÃùPŸžÜßÓDìWñ‡u즽SÞÛ°ºè® pêÒ}±ç .e™·p†e¯#†ñw�m�†\�œˆäì˜UÈÞ¤‰]¿P…\B—0"ê×sVºÔº³Ã|ÈpÑå%ƒ˜è0™p|t9Z}f•¤ ¬s^¹ÖAÞB-Šó:ÁŽsý¯ñíƒ3FÀŒýû�×8úr ˜.P�¾»Ûi ­/®·w€Ðo’À…WÚÐÈýUö´ad¶�ô@W´N”¢¸®–µf³¿…/’v<"‘Šâj¥­3ˆì•dë­ug³WÆ•­{ÕµÍçHXû9Ô\Ôx˜ÐO-B“2Qû3þ#�§Mv}íG0º7%t-ø¦"‚Ùš1÷¹åp—1÷pË‹J & Öa&�²é]mpœ“¾^6=°³¶ÿó t˜¾«O’Úèã³0ú§‘•Ö˜ +ýà>iÛj")‰ttTp¡º£†¤”%Æ œ(¡E=WYT19Ûã;Œ)Ⱦ:_öÌ0ù *ÒgaË"î;«b‚¡àfE8[TÛ\R±¾_}�'gVKQxÌyxf +\²”hd­VY‡°¿Àe}\cKOp1|�Ë7jÃCf„µú¨„áÒª²Kk—®@¦óÖ-Õ,‹FL¹€Ïs�³Ó¹=ï�á)¬QJq±º(ãÌî—î`“W¦ìÃ�·µq±ÏËsº'ñ�¥n¬Ô00JR´*d[ÂÄJ¯ßeë(wRÍ:�2ÚÚxxç–ÞešÜ|5õ‰À6À`.ª½æ, dÄJ]йºšœ;ÌË#†O‹x#a‘ÈVÀ&js|3§Âe«lÁ&•³ þ +Çã¬ÙÓ¯ðÊ~çLgãÖëþÃ�¦H×¥©6lí�»—4@çYͪ¥ë¡ž Q!.Q—ª,0iÃkЈº+�ÒÉæã$\ƒZŸ¯¿—$.ºd•s6| +÷OÊÊé>L¢…Þ�ñ Ìçú²²³% JvtÉõíÞx JX²à+é°‰M—¨·€]õ×ÄÅ9„‹Ý;•s23Lj.0¤y&ÒºŒsR)Åè?á,ñÐâS“,©ôH·±mÉF;@¦Ëæ “–bøKðÃÐRŠï›œ”ñΔë¨ÏD“4 3B²;û”Ž7�Ø5HC••r­:Nš¾êú0|¯>¯&·„'!ùS©Å-Å} JQ’ß)ŸJ½{®ÅDkIk{Ó«í鳨’˹Œ¸¼UÖGó�hGÜ!T„¹&íPÅÝ0ÿËïù¬kdÇ~ ¾2¢ ?LºÝ_+Û¯ÅeFó„’¢˜OOýj�d+úN–^}æÞµ­véžIyK–xÊÆÕ×í]öqbI× *¥úGM°3L²çJÎg¢l€¨Ì“¹øA(Bdáh£|Éá8AºXB¢é–xÅ»Ú$#W)»›4ã袥4ÙN^(Ê �®^Ç e‰GGœµÊ'뇉0w}Ô`{üQl©°Ê˜‚P&#[Ã(ºµt_ñâcU$mmë%tˆ¼m•¯žÉ£{1Qço‰Ç@ê4>ê’“c¿;Ð/}Ô^hÙ{mÿFy”é® +ÒȾfúòõQБöù·_U!—ú׉¥|SC`õê¼Ê³P]:õV�Tß¹Ž1€—®aJÈ¡o2Áüž†}¤ ïÛ°<´Xê⑦ñÑ.ÌæÒüá7Ø8Tæöí#©ñ öÐÅÚ3%žya¯^&Üsfo½%ËAà`)Ú¸¤BÑGêÐÜðUÞ¬\IÖJq}¶G!IKƒæž†í¿ùë\–xǯBíx0¦à¤S$‰¶ñ6Mm@¨uÍ}:¼§�fdµÇvÜ}÷wŸè!-B\g¾ºå¥Ù-ݾq uo]‚ZMÀ%Ââþ^ú +�éXâ1�æ4Úúj_ôîð�²ÈäÞs1y¼æt"B?öEùu˜YÈˇ-õ:NÊ*™�®AëÚ:Le£½ U$ ®»÷ðèÁ‰–o.9šÌõNk½â¾‹ê(Øð×]ÎÏúhù¢#Dæ[½K$¶%žjΟ *j[³`öñÄJ S›À«‡— ýýkâ ¡nô3û;tI£ç‡/W¯� ª +Àòd%`FžOyë•+>ÌìˡÍ-õ4N¨Þ×> ‡Y‡$+ºˆà¢2•JÎ> y�ìMÄŽ¶\ *Ìe¥ „Ê‹F˜ö*êlÍá· †Ó7´iÚ}d•Šwi¶12š!H0²¯µ$"„ܵ˜9vDêœÑÊEkÓ i?XÄŽxÆ{ÿä&�Bt¦éIËöüÀ�ç$)ÞZHk[Ø~løå�.²¾É£ "µ>-NEãµP‰1Û@ ´ÓXû›«U)É%R™|j�$Ê3tÓ�òG +Þt“dgÝaó‚‚}ÑЧȎÀ·¤v»øº{;“‹¼ÉÔüõìÀµGnmd¢xÿð/Oºÿ‘f¿ÅI4²O[G«à‹øœH}_Ò¸#9[°x Õ‚o�U9Õÿͺ,Á¦à‹„?v´Œ‰@Ÿl¸ˆÁáݰ­¨ âIxÒ†ßnªÚú� ²A6¢½™Gïp£3dŸ8´~Fq×MXâ9˜�¹£Q:Úb÷ËäIUÙ!Ø¿jlV.;:ÔíÒH-„ $E¥¸§£=D› !ÂLÉDzÀœ¨5ÙLÒƒ°ÜÛï¨Õ�¡N….¾xÿøÇ‡ï¾üì³û/¿ûþ?ø�ßÞùããã�úýýoþúîñÿþüÃý·?þøøÃûû·õ¿ÿí»?üñôÝã¶%(ïR¾±¥¶05æroéîß¶ +Íÿpºí[ endstream endobj -448 0 obj -<> +2195 0 obj +<> stream -xœ½=Ù’ÇqïüŠ)‚X›[¨û°(ОK�?0ôò&V@„þÿ/ãmCô¶myމլ¿·¢êæöá½› ‚Z[Ã[Hœ3„€iS[wäÞAg%Àõÿåö¿ÞüáƒoÞ|ðãß¼þêe,9–±“½ a|,ÝM5Q<•?v0°¾’<~kß¾búV^´'‹N¾×|*ûyÉvjBƒ/h\-URKyÇRrOA'ªYùÖÓ¯=±bâLèÿàåï¯ú7ð\nÝ ÷ãÊ$#š-S"hžX�›B­Þs˜VòßÖSÈ!ÜX‹Î"Ð><·À‚ÖUx°Š9�ý©Ýîv·þ;ØhEï-:hBß—£¿u1G˜‡{.¤¨$_Ln)ŠçÈ߸hŠrŽüŠØ{À¡ÖK7/ó kk_nï‚_wŸX<ÝóC &Zq½RߊóõŠÌ·ë<÷2“F[M >\Ì�>ô_nïœÏ&ûœÒ –”tÙ„ö?BæñH‰x€°6<áÃI'y<1*Ýi‹“å ÜŠhøPoç2H#Ü]8ázêY#€uãˆT”_~ÿç‡ÿ~ñ—‡[BNÁk q‚œüèz;9y-….Á9“­­a?EïÅô• Ö˜¤4Ýèøy�ÜâT.Hé»#t_¾PxŸ­“aBU´*l.·hJH5^L«Z˜Á= -rI¹õƧ”cÚ8 …:n"nDª!�q¯0åÙÌŒ¬ù"¿�ñÙܪÐþ°ªÛ‰ç¿<íx·ÛOëIíüù;g4O¸Œ›1¤a–ea¼0dz°çÂŒ˜'À_¿?n4 ¿?#u¯B+d/r�¦q�jM íb‚ò~¥Ùd—šÍCBE 7²‰§ àó€ð ¸¡ZÈÆÜ -ÍŒÆÜɳÒ=ˆmuhœw €˜ƒŠ&Ç“qlC8_kñ'žƒ¼_FâiÏú„�32±)¿ph=ºHÖ³1¬®î“nñ¢–èØŠIÎ×ñÉ×-^_"ÒYuÅõÿp«Š�ËQ³\ -Þ4âîtyßü�0¯×ØbΦD_wHÛ -uʰ 6¶ê� ¾´‰%é[�@åwe¤e;j­p¶˜àjóñp²å¶8“�­å<­ô¢·*ôd怭=ŠÜŽM÷�¨ ¹T°@$ŽHÙ+j' oÏù.¼�ð­rõW´ÊuLNÌ•‰Z u©Bà3~��)P^�r01Ô±à�Ñê!5œæ–Ï5_Åì�sÞï>ê¼�ð98ò½©Ø¡™vÖzçYeKYáä—3Š V^4Vž¼)1‡Ëud$‘ʶ¬ZØrëªq©¥r¼×©Í5è%ü!ÇÕVÄ.Ç•ôfëŽ]ݪª†´²®‰L -Ôjáj4%Ù<ƒ�¼¼þ�›÷·Ç}–wXÛŸ–�o—�º||¾|ü~ù&ý—ÛoO{„c lzlŽ·Í?¡ŽÀð]w˜Ö^+—XÑç8RÝ-¤lrË!]ƒ² šïO$&CŽÆÖœËnœ¾9¡àii\b:Ã\ÏÆ¶X„¾ÎB­Î�l�¯5¾d\(µ xŽß̯s~_¯ -Ê¡š[Û/'oú+2Þ¯Ê*6G2ÂU¨¦FWZÙ©—£qX²Jòê�ØcÆ~ã©VàZ3%ù4†¿~ì¢Ä -»Iì%lê?;™àëé"YÞLE”ûËÜ ·õ&:!*Ïç†4TÌ�š|®&Ø\&èb(3Ö -ðØ©õÛáuÀÀI$…c âjØœ‰x¦ -Í8[jÝOóFÆ/À'’µY �‘Ædó tJdDbJ‘Œ¨ºƒ¾9™å˺ ð5:^…|éÉd¡DRɦպC�Õ’9bΩ†E;H#ÇO3ýg½Ó¤'4bo=›“f�qóuž2N/ó’9F{Ú‡Rð÷Yww:üp8§qû¨Ûõ÷«.1‰Y¤¢T åRÅšKPˆc– ?â²°8¿e#FÝÄ‚|C«�F±Œ%Îlk*gÌ'3 -m&eoÚâGß›5³n¢Óÿs2¡–êéƒÞ ~ÓˆÈQ²ÕS ÔmÝYgS“×`Su qR=“Z> ð‰þò¬ÿ¹0ºœï7µZi1@èU*S5©ŸLö!¤1uíð"_(t�áMÔ¸Z6ÑÇ®ppâE×¢ãɆÍ -‰ã¼/qî¹@ñ´ ã›ëY’NÖ˜W¬¦µT/€o„sèF¢™tN04v¡³0Hxóë�³—§š‘G8¦°lB ëfSo9V{4v€T=Å´ ‹äª ÁårqÞp©/™\³MX™�û„)ȬÁ刮ž+LI dá¼Òf¿ßrÕP–[p·Á™ÖZË+B¶œ¼$6YyN¾;âN‰Ô šÁ-¶f¢µ®^zX?Þî}èÁ9q½æL�i¬í¡vœ_z‰+ˆs1#³©bŒ²ñ ;À£x]Iy&n ë ÔŒ§¢7?ó ݉¹˜ÀhLæš�Qü¥=b{uô”}ê\»·{‰LA¢T¬@"&ã• cHÉãœQ° P›kŒÙ8¿ÄÑ�Oô@¼ë£þºš>¢fëPZ²…À..EË~¦öÂä�iÕù+°)–H&@�ƯªJԌɱ8mqe;ÄU*�þÓ뢋zí’ßr¬³WrRqGîp#P¡òU¼Q ‹R¨åª(+•ŠcI~¯yæBÖ†’¤1µCåÁ ÇÓ h,Xh88›·Ó/À¼L -Ž ¥9 [PêH vCRåÅYÙâ7tü9q~ÚKÇçŠim<™iÉ\A³ÇTLt6ï’F>È2r=Å�'C-ù‹[ý1 \j”k}tÐ Åd[tS†Ý¿_—ÜNÿFžìÃ&>UMÍ Î.âró6N,vF^öÁxk]xºÿ€‘×ר.aìfÄ£pyù ë¿K©x%2!R륷K8s € £|$'æ#Ej*ó6™’}½Æž(ˆÞ!¾“1?án5u] ‡ËÇïàñ¸æ~øÂ$¨ f]C‘žÌÁEŽóû£ž¹Ö~þ<hÉ"+„~½ÇÎϨ’:€ž�{?Àé僩Z*}0Ù¹=&À[ɺ--‘#Á¤Öb*|(lT…#Ü!R³á1Ý�ʽµïâ˜Pû×ÊN¦UaÁFW�1Bm%½�iK"féŒÉžsÁ”ÖBš ³I÷(Ëyâio…„6§Ô¶ãsíBqW9 -MÀª<�m®�ÂAeTœX .$Œ_´)žþìg<ý!S}R °Oÿ�P¡@Bñíg¦°×ÁA�ßþxySªX\ŠŠÚÔž¢a-öz™©9Sf#„2ÆS¼(è-Õ£¤,ZZÎÉ{„Í®ò^îÕêeñì@Y'/óHÜ¢Ù„lKÕñ4€ˆÙŒ -NçÝ **´øy¸c�¾¦5Æt%n !“)v3Ó&5 Íhë�|›š!kÏ‚×Lõm‡YFö^ݹŒwÉÇ‹ ˆ{_FxÍ;,Î8¶Iúîñ„¿ð�¥®A�¯ÀÀiâ¸SÒ„Ç_áÂ{!H1ZϽ²?�Ý÷exZÇn’t®DX«dÁ 0ï\æZ¹Á`“©Áç‹S¢ñý‡Bwɽß{X–ØêÝl²_v�ª¯ù€gáVg8²ôÅŠâ)Wï-–h÷ �t¾­ê{i¬€aÂÅî£GÇž‰9L#Bê<1›”üâŸá@ëYñö´ ÛÁø¤{Èù Co”%2ïèÕb¬³if‰3e›>€½£J Ä2 FÁd‹Ïçþ;‹<çìÒÒÆ;†Ì„LLøý²fõKF_È»Bž¤èÌ»ÚLËmÝǬ‘ò¹0À³wc—0Êâbzô|ÆÆÜcEà 5Ry[Œ-KÂæ3ú‰ÿd³º<}(�c2Ö_L°Ö•kH/+@òÁX&d„ ¢ Ô7™P[1¦€ �T®)Ç’Ô™!¨¿ànÀ •ÐþâxÇyHm7Iâ×ÖÍA7Æ+T#gæZ-4((VgRi5¿_ê&7�D±ø'Vž¡È—·¬å8ùRL m—¡’§2ì®Û¼”+‰¾„Ql3Ð1PŸãÔoåãJ‹¨ÇÛA0§óJ;¹€‘Î¥Ó1]ù -ûÄR†'$LLÒÁp¿sx–=� -I푬Ì(3¶êrfVá¹6\ù°Nl>~˜4ÿVÏ2fI‡˜r;‰ÌE4³™ÏÁÔÜÞçMÅ¥u>4¡¾:¥¨dl(©•"ehg麘„�X`�;®"ø©ržý¡8¤bMÂ1Æ;)¿òÎúè�gR+9D{²ÒöYM>›heF½Ç+ ?a.…âÅHqWžÕ’©7,ß…4yÌð&d3¾çB6)”Ó\Ê?jÞË@ø'"Y!~¸½[ ðI0¤âæÞ“èÁð\ò“­ä5“QIvtºfe‰i\�&ˆãËg !i|LV>_­r:0®'%í�Š˜b-re±é†ßm…yæ#e“j+{Žåa}-�ÚLbަ¤:3묗ê›x›³±©úíñ΄+!ÉðiL°_‹&ÅE‡Û  jD1Qy­˜œ­ Wá˜qöd®ä¥ñ…Äškçr25—Ë}‚?,‡u¹ík(,�⦄Zžyµ•Þ; £Ï´a~ž‹s—®}îI8ºÞ¶2`�)W�¢Ù]�ã·KΡ ™°™0K5Î…:CØ:[ûÃþu WÿtùHÛÿMYWß«š´¼è±ûÔ €󷤂}8<ãÒC3•IúM\`Æ+ƒ_{¾Ò%gjq;*î9TL�Hæ¸/ïþÕrVMA¨•V¯Ko9 éy8:Úצ½àÐylb,Yh† Kp�‰õ ûö®½Q�­åDé,4´Òq-tñSY¿2¦Á÷\êxVéÈNA7:¥¶ÏÈÛM+ØçB1µÄò~‹2뛿°ò† 9!\æ1A|¯†ŸÞÝ[0uù›7òSvÒ^—'Q¦$Ä~£ÛMd%|+Ôh{gåæ÷oÔ\ø^ç¹6ZÆTá†ÏÞŸÍiu¦˜ŽsuI Ìi5�þå_˜FyÜ»RR§5¶¤NÊÎÏ,q굜hŠM¾±ØJÍuì #ðàG½˜ùaBÊ€Hó+¹vÎ2ÑΚZª3@Ù -6s\¼�Ñä|”ô~Ã~{2:“¶ð6H�Àÿð–ýúì·¿c¿-ì·üF¿g¿ýw #Ô"‚79—v*Ÿqû8Ë„–„jª‹Ýj?Yx9¯x”ݳ_WmýLJ ƶëÀ�9©—(¸Î2îòæ\k‹õk/˜¿+Y�xŒÍÖVÅh¶Þ™RS¸6~ 4iTûy–œ™ªØyyi%\…ÇYø—ò(«pž®Ò<úw*cÔjZñÕí‡L£Ø¬æ}š9�n=u'¹sôVw±™œZúaéèMæR66;ï¯AjG–{/ÐZ÷fl6餾ÉoƳ(àœ£ª÷Ù”êc¼Fh°Œ3Á¥Fbeˆ°Ô”þ1Ô_ÎnÆQÔÜÆÒ¤QÇ\Û6×ZºÁlþJŠÄN|ƒ?}.|ÿ•LNøémçŒ$�ÚR¨|Ž-ï¦�ë$.Á9/m¤‚=H߬}¢Ð9¼”{\løW`“ò9"ñ`’•†ŒÐ™/ñØØD+Äïuy5jD žñ)XÕÄŒf�-5—ÞU�ºãMÍÁZPEƒME¤TÔw\™˜ï6Ê‹9b ï«KÎßçòJ:òP°CE-ï’‰5¥:��Á‘ A9rLÁ)oMŠ~fâK3%œç -L%Sšß½_9 -Ãy&-$Ø%‘Òú÷º[FØÙ8;7ïåZå‚z™ê<½Ý}v&ÔìÊ5x”PêéEës1-ä²—þCÁ½ä|ñ¦µÐò/·u&y½4“bL{·þ±²u&c½F“­+齆ÇÕfjš›÷âCð±†îÑÚd\(µþbd˜÷g½3¹Ö¸‡;l>K%�1m.JPLS‡ïl]އ+õס]4O1+úp€"oC|±½×PÞ#»�œ™±šä¦¦½˜RŸiðgÌ�)˜jãû¡S.¿E±íb¾˜½Ì¾©ÑCèÿ´z8ú +xœÕ}Ù’$·‘à;¿"I]¬• À—Ä¡Ñvf¤µ]ß4z 9l�Ö¤¢¦§d;úû5DeD‡Gd £ºõRG&ðp8üvÇëÏÞ>ýéÍ7ß=}òÉë¯ÿþ×ï_ÿ替ÿð·§O?ýüË/>øÏ‹¹è‹¾¼2k@i Þ^BÒ*zoÜ廿¼þN_¾øÝE_~÷Åÿºh…—ÿwÁËo§gþrÑ—äTL^{wùóŸ zô�OõåÏÓsþà?>øÝÿçƒ_ÿö‹×lùûßýõ›ÇO>yýÛ/þùË‹~ý›oÿøñ÷�-ìè”�.¹KˆZ¥äâôïþë¢/ÿõÝãŸ}Ñ—¯ß¾~£/AùË×oÖ‡ó¯¯ÿòû�ÿMkýoo¦Ÿôóoí¦ßß>ÿgñúÛ\¿…yÔþþ—~ýõõ­J¨M lð +Œ¸ ¬‘�}Ê??|þ³�m!ˆQ¥ÁoC`/N¥>ÚøAæŠpÂ+$ºy×´ 5êáŠd€úÕn'çà•I)lQsE©‡(Z( )4Áà%xTh"ÀaTë@¨ÐÈ�)uA+xB£öúyèS +2´Š*AÜ¿¹xe÷ëõtM5×|*âüZóK_©e~éå%lœÑ„=ÏövùþÛêI:Ï‚âý4G‘èî””g/Ái¥“±î( ÌÀÏ«)aþüÛŠ…'·i·¯“œ +ù%¶_gIüóÿ +  ½ÒhÂ0B�Ýþo Ây$$z3¢ï[LÚ¤Ð'ï.¼²ÉùÔ±žûùœÏ2Êñü@ÚóÔBêP ì�ô†M¿‚BÉ8à©f_óë,’ÆÔ§]Äš¡dT"„;àßR€:û@ÅÝu?žª—�Eã¢_¬¼sþðqœSF7Á ‚KÆ_‚µÊc�$–/C¶O›½o½R®D±†Qr’VpûUî¦L+ýAk ¸;`s–‹¿Ê¶ŠéÛ}Çý&:aôL +µ�î´S�"w}³ÎÉ^Êóï²ç§m5ê±Ò!~vÕ <‚ ƨv²Àé«©`õDoYf9¤Å"¨�œ ᣠ¼òÞÌÖ—5ÊF †ÿpÔ$3®o“E«R€éüE­Œ‰sÕ—°mcÌC0À~Ìškóϲä7'¢ÕŠ<(ã$!}P.ZôÒ‘�'QF¯¯ý–'¬g²ü—ë?þÁù�+ùàX=z¢õ/×??ºbfõ#vÈüí/ò�BSÿr�­jçµ²é™j\T6`:ŠECäíôû§D yþU7XÝ£'‰•½®†Ô\¥YîŸJè?ˆ0-üóCÌÃx§tp.]B°Ê˜-Þ�Ùq£-ân¸²¸uÖf3¯ + l†ž~íßÿA_þýbTŠh�½huB7ñ²‰C)ÌÞ$(¾ÊlË(ŒÑÄ”9ZûpfiŸ¡ÉVM7™ù”Ý%!»âqwÉL•‹¸"–õ$Ýy«¶›�²t Þ+tÆIRÚ ž¦¯ò�â¬,vsÅšnÑEKg¢J&š›�¥8«œ·ÖðŽÊ8+øÑk1…g‡Ë»UÂÍ’i&‹Ur-²íM!Ù'å�y%F˜ ¶5A¡Í;6(ënXÅy µrh*å�áØ»žgdÑBg°è$%xÄ›t}f±Ôî"3mké ¢Š1ÚQ‘ÉË÷M¤õt +CH°ÒÐ>‹¶Âé¶!”¯3êE_¹b7´JA›—Šh¼�ÖÿC EËØ¶T²ö"ñEœo¶µa!e­Îª¦÷*�P<î>"µSšøkæ�üÁ=ðú­WBRÚ��KpAAÈAŠÃ¯ï +=`v*ÏªŽŒ‚Ú¶{¤þü×÷R-0{Šƒ÷;T‹¤0¶ºûé°ráå"¥ÚK°I%ƒ‚—{K»xxEMä+íý„hZ;@j�UBæ&*pÞÖX¡òºgº-RÚ— +Á*zjZ´D #Vb-¹uh4‰�DU#[ðßZ.ˆFï2âuRc8†wŒ#ζÆùÕp®.Êëðqeb´A9F?Ø6¨ ¿rà=ñùx,Ôq�®¶oÞ *V+pˆ/¥hØÌ7"ºDE£µa!¢²1„x Ö)«ý¨ñ½t8~¡�0Atʆì&7ÙbòaÄ�'àkª„�Ù‹7¯”\2'øOG¥$0�*p +RŒŒŠöpVÅ›‹¸˜9D ¾VSt³¾ÖQO«7`âS“6ßÀH½ɪXÉ¥á�‹0¬ß’pÐYé.…Ýì{¼€³zå ³w~ÀáL Z…Áú‹wIiƒÇÍ�ÅG;kè~ûCa™XöUgûq&Q¶¶ñðEàZ И1h¬ŠèÜT|×Bزq¯Y`SÇ�Ã5Q¼:àRiœò˜üÅcTÑ›»‘Á|ˆŸK„À$ÄxPh´Ùê(Ô‰^DÙ"ž©=¹U[ôÂZß‹YjûTyT%£RòÑ_|Âg�iSþ«4¢ p P‘Œ²ÎwñqòcÞâ’YuœEíù˪ =+LóˆEuºŠ?î὚«X­ ù(úË1),ú¬Ñ¡Ï‰hF‡£¦·9 +bŒÂи|6ôK ¨µ»îžV Œ÷ˆaÂJŒÆ…ŒMû¬TΕàUÑ\f9€·5˜¾ŸÌ(c +±Š›óŸ«9­å�ô!]|DØõœ*%°æ‡‹²Sñ„ü¯Í«¶¶ªKÊyƒpñÙQj�šª¤§Y³G¿í‡(A•Ø=ÀNÙD8"¸¨0·‰>ÐZ_>¨¨]Nkð”˜Ü±}©LrKpy‰RáÑ%W�d­ÞÅ^¶N(öp‘‘+êj¡îˆeŽ#[KÑÇ,²Š +1Ž€5å¹öpTD%dH74ѩÛ!õC{=€ÓÖH ^Ř&âETèR<ŒÓqƒ§6a„PuÁ™ˆÛ‹÷liaƇœ£ˆñâó©ÞöÅ#:•Œ5xñ*8¢ƒÙ_t«V‘“T<$ï£r¨í-ÊXþ†”2FE‡1ø¬I`°à «^ëßZy�ZÛìÁ²J骲•óÐ,¬vÊX—�ßAÐ+~˜ ·Q+l¶þ3�Nà/ŸD¥Ñú¬^hòß‘Ä� +bvû¤RLM gŸ¾{Õ»ŵÒf gàõOvà~½­9Þ­Å„¨¢qo@�Ò/²w-n '·|‹¼7¯™T6êa'þùÚ“C•¡þ‡q¼xT4ìÀñ½sŸ*È÷KÌF£Ž8¼ûÚ^U a}Æ�r€~Çú;�óNS�)ø±õF€†\Ê`î€—í¢ +ä‹s"Äì£Æ I¶yÆÓ‡\Þõ:D¶5:œ²Î…—AÜ€¨BJîÈš5`ÃU Fšc‚A«àc6ÕÎGškÕ:¨ÀX;Ž4’äµzòW”ÍÌôMÅR‰µ±ÄÍæ˜Ä-«œI>³0�="Á­^ë=\õJTkŸݯ²ÿ+imC-X¹`ýíÏ —æG1)‡Õš_¢¤^xbp7³íci‘Ã&ûv­`3è Ñn÷ö•9I°¹V°™|¼,ša¢`öˆ$U¶#··Ä¿L½Ðí«ÞôØ–Þt!}žÿª_ hdä3x•Rޝ ïïá µ +D&pŒ26špÿ­®{# ¢¿ª8GùÎÛäp��J;Åh 6[O�ànGñ¦£"äÒ×Ü+á¬="öÂ{s(û\‚¸„–úÌͽo „œæâðÖ¿P ß·êFŸ»i8ûNöê}^ýOvOöÉ·:—³�ƒYöÝQßêQÎY5Õ9Þ€ÿ_ÿß΢Œ1Ù9ðâ’V>çUÞØâU¶©qwsÙn40êÆ%2ä`�åæà ÷j{¢ÖÔ‡Zð5XšÝ°+ħÜ)ÀÃU& §�ÉkϾn4aaŽóÔÑ aõ.­‡:¨ÁÇáCFv®©êvL<¤ù)¿­œ75™S_ýRÝU†cÈ \Èwôfñ‚Iédϲ”WeÛ¥¼°‡WèU4­ÛY±­x&®³Ç:¹í×Ù åj/oˆÀbJB�Vv’‡£hæI…~3¢—ÝÓŽ¼O¼”�±â`œÉаgæ5ˆá|}­Þ¢�½yF÷nõy¯³ú%òMÒqÌásˆ ŸÇ¢(´‰æRJ˜ ¾W&—Ž8ƒõÇ=*iÖmÞjf¹Zétö®ýÒ lZo4á϶D¶3Ù[Níá&\\�,®¨Ý€LjDVª’fÛMjÖ:¦Ùt Y¾î÷ÝÒ9»)ÿ.¥æÝ¶H`lTmŒ~Šû#5¥m’bs§ÿ&¼Ó´AÅdµÆ0¯ðÜG¥ù¶åDµœ”îHJL‰ÌÀ) KéÌ%Ư¥G¬ ôôð*8åbˆ¾çË¢úü¬Ø©ÑØ3xkÎÛ’r7Þãú &jƒkÆë4p:Ž¿��B9Ï`¿n0ò¦÷šlQá¦Ï9 D¸•·Géö#¹¾Ž¯ÜøÕôÏL÷Ó£? o�­ˆÊy¯3_‚žÔ +pl„¶u�ØQ½«5eŠŒE´ŽtÌÕÏKŽÓ­ïøó.[i½6Y[§‚ö~å¥GT™¶•+ô¦áûg'm¨V(dº–!ôF0ª¹3Ò·FÛžvm”ÒŽ®Êjë|fôcwMÞ°z`-â¦Ä½ÐÜx¼ÕðI¼%´‹)(«Á¥]n£}R&†D­»:šœÛÈPýyi¹RRjF‹tëõ~´´ùÅÚ£‹ÂÔQÇ™1ôÓÙL©œQ«pªß¼®änMX‚§®öOÆñÛOF-=Žéî­¾±‹$ ŒKC£N�>AF‰ £WàìaU•v·ìymúmu×Ó ‚[4‹‡Uˆ¶¶�‚ëŽG†´Ú­0,¹Q Ÿ`C’�¨%w¼¢év.UOßëØÁ'06ž$w«�E¾øõ {7ðØlm "Ñ«ä‹Á¬êãúÍm�[²Âisáº÷}ñØù¼öjl•ò2+“öÃõÅÌø›ø:C õ|½\¢fÙÝÚCðºI(·ë¥� +¾U }õºé׿u 8JÞô¨¼³ú0w¤©µÊÔans{µoo¦F9mH€>a{]X°»�†m.R(œÍмò'¼ çwãj6˜µJVθ«¹ãGhKè®Ò.¸4YgÅ]ç“×JB¿ë˦G1‰¦¨¨�àÊ#´�Ä¿ÿKua:Œq·çþ�(¥ˆ"¥cpö°³óŠ�N¼èêœ~· ÷v„Ì…‰*L+¯®¼2!J$5OòÙwOûæÏ_ÿßOŸ|õ믾Òú³/?�fÜ ¹g@¦D ß~Ñ�ÍIÇÔz¥åâÍ [h—}b•Gk�ÝôËôÒû4<ûÀ/…jÝkÉçR>½ 2«úÉí·F�ÈÛZWŒ ˜+ #Œîö'Wà7ÍïÈu¥†\j§Ã(¶¿>¹riý\€�¹2ÆX0÷’>ÛU"‰¹ÜÅDe0Äaô ”g%ÜuVG÷"haÚNaȽNÓÐrßê¬ÄæÌډῪ¤h§tJâåm»hèÞÕYÉ Š×&ÐwÀ#ìTÏ=޳—,ÎJ “wF—«Ü^‘ ƒÏmycwò»âi.˜šm‘ºé.îÀsÇ@2ÊYôwÀÓÓë”O^³mj‘ô¦3¼ (‘·^A´Î[/)êLCݦä²ïÂ@åÀ¢™'ýiâÌæ*~öa‚K5dÐ%›” hUÐîkÿJõYó{¸¬£‚ѳýOMÜÆ¡4”ØÌX« D©^R_:�Q�RÓÇÃ-¡þ&u¤—…CAÙ}+û¼43Ì•·Ö8e1jØA7…*v†Îî%Ü[-ØFP.ߨ2JíŸìÖÅ™KP‹Cù¦ <œŠ@Óרåâò=ŒÆ¤nš_3 gB Ý ×‘F—=ÖÑîxQ½7#wnW9óNr­rcÒMã/Ï”ô�/ŶÆ/>±Þµ¦ü­dTÖHHÃÜRj¼WÁkã†7¾Ñööxe—·Aãc$ÅslªvkßÓi72;v“bw cõ@ìËã ^T©m¾ +�=Å›µl™Ge‚ �"¡ùïwßj»ª tY™kƳMCn›så®Õslî;�¹¢ÐN+¢èªúË-T7dIQ.”¬6È’5ï-êÚ„oi…Ì6AK¯`tî…ôÒÓMƒŠfMüÙ{‘1}pÇ�ƶ³d¿o…mÛ—×ËpcVí{�Š6ZÜK¶s_Óªÿu#I 4Ûž¨Êiï>6¿¿iT;Õí2yK‰6åÛh`Äìi.©ž˜`­¹Õæ'¸PC›NO%~gýÝI’äq>ÚÓ,2¿ ¹‰ Ûÿ«&†7/·V�ÐR3�r Íõ¬°ú¸N½ ç\æ5Û£œ´»#qK‘5£BkÓq]„ìiÏ$ç·Še uøæH¯*¹/rçÒè쀻l‰ýnûƒTÒÞZæ±ç²iîiNèTLÆÓ8Jí9(ËHÙçºp<öàÛtÇ4O0žA:Vå-g©\�È4þg '�þÒ‹�ûÑù SeØ<϶WÚ®ˆ2ÜÍÒ+Ez£´ÎW’ ÷W¢å?ó½cWW&Ãe–÷mG3¯Ù›üC~’ÇÎÜó!zŠKðA nkÄ´Ôˆó•‚ô@6K ¥TM’üZ„ 0kõ +m¼ÒßÈÅ"E¤wˆ)bl�VÆ™p<Ýp• ̶Ü6z5_ÕUŠfm•óv£\…Œ’·ª”Ñä9V™bÆõ´&Ø…°z5†5‹£Im¢†º=:$Rj Õ Ý:ÙzX§.}˜ÍZZ�n%徭¤@›‘4Y+zÄž´r ÀÚðì_ú|&ïÏò�ÿy\õ Oúk$g²5£w{ºè“4 ŒîÿºC±»€$žZÑ@Ã&u8…{éÚÇXDÝ:OÔ=눓Œœ¯º¶íMØÌÍ2¢>13×µÄl£B§Ú§Ö–N÷°�Q!hXšQ‘œì¦ãßø"{‚§yµ†ˆ�E<Ô&J3ÛÛ[�k},;aÑoéN1¿.žvÆüšç9ƒ‚ÁŽä•l‚ÔTbU ŠD.•\[”×z ,²¡W¾2A¡×^¯ž>†M5£z�p +ë¿~¢§ƒtæå=Òm‡ãc«ìM¨o #ö£f�'ã¦þ÷Ö©B!�F|*+€ýÇIîÛç­ûôÍÜZ +Y�ï¡Ôa ©ñ—Ýîh*Y)ìLÁaž I…t¤–G볬¦ª…Mc¾¾QëÔ]èfA\žÙ¦ëPmðqXi|{x«úL¾í):bÞl¬ZÏU>CN=µ�êÓ·ƒÑJQ“œŠ)�êÜ*�XT¥âÒ.Q�ès§…}Ó"ª/îvvÂ)v¦~âq‡¯¤˜ƒ Ii_åm™¸?’qBî6 pòÝ)˜¢ŠÜ�»žnMÁm$;m16}úÓ%Yd%Ò"¶ðÂÀ¥%�<ˆùþcÄ£5?ÛÝ3±Í¥_D¼Ï!—<îj!\�–ò΀©/ÕNÙ`LÜñæ§ä� 3SŒê£ò>ÞMšrFpÝõÐ� ÜÑØ>A3¥«é™§çt§Óñ]]–ˆ«(™Kí‹Ãå¼ÂˆËÕØÇ54Ú]¯<'¥ø©ëJh�„œÇz'|”Ó7ž¨›J»@X¾bÝÖËÅeA»”—¯â~¾s=ß\Œ³®úsšó»‹EPÉ<ßsnóÕ@ÞùpÑÓß×oÖÇÖÍôðü_ô9E5šëˇQ�°˜7†ëÀuáå£ï.SºýùÁ|ð†¥9ƼõJÃ3 +!(Ýsé•yÆ`D0¬¿he-8›Ðä?ãô·£L/ˆâ®qÙ ýŒ s¹K*W3fÍ „”ç¿<(«/­ws™a.õö  Ä€þtÕ+ó1[k$„µ:]tJ;›ok>aL‘HR OSC&WTk5ý�!ßxŸkº†E"s¸1NAr ܹXãÊa“ + ÃiTvõ öab”‹J{‡á\d0šIv(ëÜtöì3GªògEt9ƒù0—}§•έ!‡˜[¾s£Ô‘rÇ3F»�ͦ¿ÄÏ™ë¼M0¹ãUÇT0Å´t^éh½?•¦¹›ªcæxíàÂ?Vu’€Ï—å8'Ú-"Ü÷~K[ï%‰: )¬žýK|Q‚,H2ó0d`Nؤ(I̱e7ˆ²e´ TÎñ3dá3”›Ú¥E®¿Û%¯™ë„W©y&â˜+~ Ñ8¶²pœ™Ë{ Áxê 3|¬ +Y-g#u�Í+•v%Q{ÚQeîX-í©À¨¦Ù7ŠàÒùgõùTJ\�¹|4û�Ѐ±Ã˜ùXÀK‰S7$IšÄ¸›Ý¿;ÚÊæ®A“Òѧ3s¹ I Ðš`ÏE>wõ_ +*8�‘? ÷²2i�°)LGN£sIe¸ƒt’Ìæjµ\%­“sŽÇNÈœÂe˜Ö�¬q>ûµàSñüL}Abþ‘$Ü´|4§ 锆ôÒs­™«î¢|^àéÕ™v4Êh4x&£ä® ZM3¹¬µ{YS€¹f¦0†aºòNZc¸§=sHa +†l‹£2W<¦Àز2Ge.OXM�já÷Ó`îPXM�S‡’)0¶²` +0]ô SàÔö}S`œ�Ün +pæWS༣%SàÔ H’)pöYÝ6˜nò…)0ˆAÂ1íâWSàÌ aúĦÀø™°L~Äš íHjòiGƒioµU:ëNÞ F¯Ô^¡õº#Æ^Þ.`š¾[“¦Ž¼Æ;ÛL›vkAï4%‡»Û)ÿøHBKóa¬Ó”ÝBn}“`ä�ì7 ,×v�BˆéD~i™žê…E £J ÃËZ–éz^X£0u®ˆ˜-ƒ%Õ®¼#¡®Œø™;Ãmsfµõ#èÜ`¶–iðmŒQÚF—™­eúd—Ñòjé÷Òh°L“lƒF¡F'ãŽQZѩɦѕûVƒåz-/G>%å<â'~„#´ì9fÁ¢¹Ä7›–iœr°�á8<[‡œiý›ëT!å ÇV•é”éÈ»ð´jÝwaiX¦=oÁ�ñ"HN¦-ïâl8u7œàk> ·÷G¶Fb ç�‰yžŠÿ(¸^æ8ürõn™žÆƒÒ!žz$¬dôŸ¹'V°úG�ÄÍ-²-Ónq�Ä�v ˜žxk îTä£à`fH;)žé*µÚî¥ÔHö›•,ú�\�”9­  q/l¿1­YVûm¦ºÒ‚dÎöZÝ[êúåE‚9IÊÕa˜·-Ó@aU­ÆV•-SÔ¾ZŒõÊïÊIf™zåBÓÅŽà$³L%êXa±óÂÁsË”kJ×0nú®2ËÔgŠ×àʧcê.WN—­A ñeùS +¹ò¹QˆØ&„•Wª{¹§rØÜQ�7™\ê3¹¡5eÇÓ­,®\÷ýôK1uY9E\ûü©h³gZWðI1V+S9óe¥Òa¦r»c‰)œZ-¨³Î'Wñ4[Ogâ>ÓQÜoû�¸Ê¦ˆ*8kâ½ä‰d0pN‹½6†vAŠ2åI« &w–~ak�©[Z¥è(DÛR”Ó—vFÄV¢G!Þ:¤\ÑÒ,D‡Ö”)W²´ÑrÝ÷Sˆ2µO«=m’Oth]Aˆ2¥KëÙö&ï�}¿â:\ÕÓ×øvéË•BÍa�£àl�k®|ia&C‹ÊÊU/Íü¤\ö�Ät¸§5¦3†A8rNsHçÌ­�²GG�ÁÂD«;¯FãÜ~C:l¹Ó¬"Ÿv*¼�XyæV!­òEŽÅÔƒøÇÒ~ÅNç� ©ÐéÄý`‹šæŠé;KˆkóQõl­Óé8ë(0eNk"器!�’9 +/ïfKœæ�Ë f$ï0[¸4§P2˜yiß0[Þ4'3ŽâEð ‹UMwÒ?%‹š©n²˜TàÃ]YãÎNT ƒŠh\\Ô¹Oú÷¢•ñFùd nEe¼Uv’sëÄù³ç‘ëÒëg¹Õú_Õ�jýXëbé^?*ËÔƒ�1 +¼uöâB6¢œyaS 6æûX]¸L[5j…·‚6µ�[»[¡§…e*Ê X¥1Ù”nj L=Y±•Þä{'_8fÔˆ;9 +Q¶¯[ø!i)L‚ÓËõbe'AÒRz£V +ÂT¦™ñ}£í½uÒÞ:«Ü9Y¤&¼´·ƒ ÕiãÕ�¤'ïo+Uaª�pü¥¶·7JÛ‹ ŒÉRýe÷7Iû;©Y*SßhTÐ.ŽÀ²e 0åm�}|¨½]V6˜ú6pZApH~?=ÁL¯ âÉxãØs¶ bˆ£+ ¾`¦®� +§¾°øÕð!]˜ª¸× Ëœ¹a†ÍÃÉVL®\ëOF9s§ÔäË @W~g~+` +Üpr®?Žž¾$®Âmò®z<÷èSdžSYn>z.ì¥Ù⹦8 s–êq +íŸ `ªÓpò Ÿ+²€©MÃɉœ(úÏèlÜh×ÿ§Óò«#»Ç$ÀMiå!ÙSP�TAåc–¢ïS`˜·BO‡ùæØ"0%l0å9¤0ÐÖÑc +ÕÀ b]V>|Fô9T+¿‹#0Em…^6ˆá41åj…bvꎠ ˜�‰·m×íÖ"m마¯�ñÌóá%¥äÔÝ’Zò2çcSgcjÝ0§ÏN=#Œ>;eÏ8wî¦0l…‚4zF*ŸH‚©gÃ) �ÙÚ>í40…k8• ¦“YS»†SÚ+þ,¼x°˜*7œ"ÑÆÏ‚n¦´ §DU0Nq¿€ã/—¨£$K™Ú7œââïp`…�#0åon +Ž7JÆ91G`jÝÜTwh²us‹dêÕ +¥Ü¢²ÜËúg�)f+ü³ã0Õ—1€L¢Ãô0L[œ$oéØª2'e*×À¡ +Fº寧˜Â¶B/?u¢Ëtlå¾§˜º¶U-?õ}9/in÷ zähÜâ©�\¦ç�NÑ_z&ι Þ¦tzkFvûFµž»òkµ=î%<$±É™ásÿÐ4¾‚ØdêÌ +±iPE ö…Å&SƒVˆÍa˜€Ër\.�–àr‚ ; ÖÖÙdŠÑ +6¶ìÆÙdøát¿&GV~?E'SÑV°SQ—6¶° 9™Z¶B€�ù¾LÍZ`g#·»—™š´ÂvÚieªÒ +ÿÙ©[€’ ;û´nû—¹[·V 7ˆAÂ1•g«ËîÔ ‚ÓnôHì¸y˜b²%ÌxÞHBŒñLt3uc…‹pœ•ÉjK78ûL=N"àï¨YI:%S4VøÉÎ;q^Ô)ufÂ6½¬JéE•r¤¹R¦ïÀ}Z”SoV�¾uŽ™‚³%=lhÍ�S$oG¹ðû©r2Åh…%}&â’dH-,hœL9ÚzÔ19•¼õïW�5É:óõŽøöزޔŸû¯Ó¦úm±,Ž¿Å ˜Ú·Õ²\V¦k¦¦mµ,ê•ß]X˜)€[ß(zI˶­FιÛ;gøí:QH&<ñ$$ÁŒ9åLÍÕbÈÜ�ÕÞªß25T‹¢7Î/©·QΟx渂©UäE¯ØSÚŠH¨�ÔÛ;ÀÔ­±[5Û]J°Ü'¸ª«Îyü%¶4W$µhœƒËnè ¸'ë•ßOe—©‰*ÄÞ©¨K’àPw™"§Bê�ùÂL�Óêݻà ¿ÝÁÊÔ?-î­óN+Sµ´&¤�»ý¤ýqüo»O™ú¥Õ½u7±#É[¦ŽiÉIż o¹Ú¥UÞ† Œ~aiË”5Òv ¢*UøCî~¦¥z6®^‚$dØsã|¢–òë‡O'2EM«“¬\÷þ’ô‘ÜM^k<$÷û¹ÚEÜRd+¨10„ÞþD¦Lª8‚>(LxÑ3ˆL UqA"Þ™ëïǽ‰ÈÔY­!ûÃÀm=/„ëÇVÝ8{Aà3åºï¥‹\iÔâQ;mIò© -ÜWa‘+€Zϲ‹Ê¢ÿ^ylшÛq˜oV~‘+–ZHþ0@['œ+•Z…ëØ²2­r¥R«x­V~ÉÈTKFü fÉÈ”K&ü©;"tA?º:«J#m�ù:ñLHq¯Sw€«�Z}Ü™xñ"äê¦V'ù(v„"äJ§V?ù(�îÒÐJçd‡ž‹!×§!VB)µ=ã(í×ö SqµÔöÜMr d +¯ÖÚž¹,SNUè,U4F¿°ÂZÈ8LäFû›m¦«°A÷Ŏ¹2¬Õ[VfÇ\±Õ¢’U ¿ŸfW˜µôSg%;dleÁá +³ÖÀþ©/Œý›;œÜÛ­ ¦Ðª+œvV¹ ÄVgÒ©;$µdx ¶í®Ìj‰¥ßK H¢”«¸ZヸD)SoUˆRÈüF‡—­÷A¦úª¥ƒ0º^zÉJYÉßp íÍý¬6©ä4]÷AX—±î�S>]öaU©a*Z­œÕ¦Øè0i“<ù3z›UïÛ’ô\ý{¡SWæ¿, L=wølEЇ…åÏ•eä,è^[T¢ò=ÿÿ �ö�! òY×od#W~¶´ñF­Uˆ‚—Ò\¢ŠÝ}øÙÂl¬X÷z3Õ5›òéá•Íž.í}¨-À2m©µlÂU*÷æažœo¶&É/Ϙ}¨f½®¢ˆYÞLiýƒóuÐ(pú"hù¥K—}é™jUÄF�QxÇ…ä49CHf²|fA +3¼q�8³öœÀÀAâ”^Ú¸ ¢MÞÖÉsôô‘çWôÒç—[Ö�Äë÷×y¡Ç?µ8ûðjùç:Û· ÕÔÍAØlk³Q…¹Ãlb;cCttÄb6…M>!Ì�…±wkŒ­�SßöÇq©+Á†¨Û§X{‰Ê¼ô¹È³:£X½è�c–yˆ½…zß«Tàа¨ê¡ÇÞ\FEufiùk=§À ë� Ï«Î\þͬPóƒÇò¤ö0°,°ƒþ wWy&úO7)�«E^)Ý•0_zqwÖŒNÙd¬ð¬”‘µäIž„f¬o`ôÛï;~°o¿¶¹ií•…hÆ·¨¿ô%jÙÉP`3Ooó…W…׿ùæñ�ÿøÀ¤Ñ�ÂÜÃaó•õU.–@-®µã‹ + \@oï¿öPzXOhöfÞ½Ê_½X+UT¡Ý rq!‡?ŽÚ+ ÏÄ÷ºÑ/&]À+çŒ3DÐ�Ï×~Æ×£ºÎ5k‡Ï˜dÔžz¢…c*ÚZøºmÜŒ…âd æßbvbcå«ûœqƨ˜JFa#P…«»IU0¯`¥bº…¢2*G¶­¦ó@AºÙ¡”¢?.g$ذ…ÖŒf•ಟ& õ^Ð`3}PТë�·©õÃTft�é+NÝ9=ÿõÙNÝÂFåHZ”�’ŠkàUô·iæÝ¨LÇGgUp.§òY€u½‡LŸ‰âp�Ë·ì9{ôtÕî†ÅÚ”6qP·iæé+ñd¤dÙPÓß<®k†vÊFgã4î$¸Ã—í"×E0ßñ”Üø6—ž6‚ÞÍ-Üzr!*1Ú'LÇ D$& Ak­R’ªg�¯Øª lÛ¾aº‰nêP‡Ç ºJ]މm·Éûè•¿ÃOY�ÜÉÝéü¶w°ûæ3™¡£G´r·ŽpïÊë8Øs�mદ¦vüãNø7׽㉠(; ZÑݬwùéáU�9"u�®Ü¢7¢ç“§ã‰¨&XÞ¡Hp-o’žnGq[Ú`ëï0ûŽi†“‹¾-c·�Û´ÆX##¦Ÿÿšt7²ÃĶI¤`^Íœ%BkS$ Ñ ÑVYïÒ@¼„S¾© ]Œ'Ö§BŠwÀ¹W)©3uæxŸÔt™ö>àµ2ÑÄa6´–ƒh­EÉ1À�«g*”2²£@4óÉ +DW`5ól°@:~WTmûý¥™±1I·á.Î%�O“¨I²al2ʇÔ1°ÈªÕÖ6¨’‹f‡IOGÚ-" OÔöטfT¨�Âh"n£ó�ØÎL/ªœÁÀ�·Z³_­÷‘Œhâô®³:N5�ƒMŒíÂ1S?_ ¼·�hw£ˆ +» ™66jeC¸ÝÇ<Áñ˾ƒë©eL!Án¯ºE®•p[®U®9C]”ü.Õ:¦Ëš˜Ý²&ß�g\\3¨ HëKQëA9/6l·þ!ÖñÓ%ãgM_À¨Rò$úø¼‹¯›C[^vBt×w)œ™Ò÷ŽÝb…­føo7yž¸òþëÝ–×%-å¼·q{s7Ú»º™l’Šc&Yn-PŠó…S�E¨y’Ͼ{úÛ7þúûÿ~úä«_õ•ÖŸ}ùé4á.oñs†ëo¿èÁ&j[ùâxŸ\‰WqUlzvžbRΛùüÐXÛÉÿ"g‡h$;bªL'¹ÜgËú]ˆ¸IòÐÊ9Óˆë)§µòÁ�oQƒ^)±ŒMb«žÿpמ5ßv²”ÃFã‹ÒFa¥A¿º`©EXOÅ£óîM|HAÔÉШ`’8­·y_y£¬MÞà®@c3º«7#�KLX�—¨Q3€&_€.Ñ‹ôòÑñÀÆ‚ #ÞŠV§„œZ•à…N`NxÛαZÂçý­èx¶÷0XÉ9mA«`1 Xö$Ntu1ò‘�§‡W •€Öm’c=òùÇGù‡áüëêe×ÒwèmLWKÔAoîÀ⦜j.$—`aüÎÖ¨�ìP¶µ­ ÔíÕ.„`”`çUHNÁe·Ù>t3‚É©¨Ñ„; =Äùb{£½ÂÜ݈L×Kg�rÆeíú� +™šcœj^u:©cÔÈ4ß\à7¡¾12½6ÁE¥mÌ?ж£¢!ÓŒ¢Í½MÀã%› Fíiäȹ�‚r){_ÑÚ­´Xi±Ž¼�Šqz�€è ຃¯Œ±ÁŸ±†ÃÞ’º¿}2]ÜÀ`Ú™¬(§ÜEíŽ'÷¬<Ú׃$Œ”ð´/¡:ÃMº&�âŠúš$—bØrÆE�}ŠW‹g3_œ¬LÓfDß,×vÙ(“¬2ÞÃqI|êå=ÈõŠu^y=î~G‚.T3i¸Æ¢ž†fZ7 ûä�ÇÎO–¥>ãÎ×a‡ e1;‰Zö ›Ðó[v‰ÖqÍ¢W¢ Z�5Çõj>ðDÎ/©CØÖÅ×i]nw½Oi¼GûoÇôІˆ¹7eÀaì>=`RÉHc‚‡ÌBÔ¦æû›jH(„„od®îÈ8qLcl ÓUk.ÓBH™[e@+ ÇôÉv:)gc<�Ú²±y“z›Ò3$šToá¥Ø½qIa0ÇùM/ð+ðJoµÝ/ò›§>ìLöØ[„ȵGÚݨË›y¶ÅóP›'ߌ觲ґ»ë³£f?òj­“fšõa]bæÖï’óî¸ï+¥cT�‰í#\“Ó\!br»³åÉ#ö¦º±äÞq�窀Ñí@Ç-¢ø'wàÄÜeIQyˆú›Çn ÑLlÁISä5Óníïuçˆ[NÈøéRãâV’ËxÊÒiºS;ÍÒ¤H…¢éAa{Øô¢¤UéÜuèi;^}[º0¡!Fí㉹y5ª ì<íôb×%†­¿MÈBtÌ= RJãûøÔçp5&gRäwÅg`M+”’Iž`üŠÂ_9…ûÅsՈà²�‰ã/3HlVÉ8æ.‘•«¤˜Ájíß�dnYÕˆmø6dò*é�B7’ A§•¿wã¨í[q8úc4ƒù�iÄ–8Ê›�Æì·…‚š§e¡¼uìËQå®™½§šÐ�‹û¶“bÐyoRþ¼Ã@bn–YX *ÁqòmŽ +�Gk¶0BÆUšË²µRû^ºÐmm+d0i,¬-»•Û°žW"ÊÛ·ÞöC¬³¡ÕižzØÀJeöÞZÂðGeJÈ�:3 'ß8æ*£•ÔƒW <ÐzEEx=(Ä„@ ¯Þ:‚²pÒ\MÊT 3µBhê8ðFï=Ø„yYIØîâ«R´··ao:fÕü©Ì.š}5’O†¹û 1(câ(…Û'bÍðüÎöJé{ÛÞëyÐlÞnBb2U(AÝ%èÀ]tµUïTB{<�‰ïîL;sÐÀŒCe  çp¦C;–˜#M¹;WÏCæhÂ@Õ÷u +‰{w î˜báñ†…¶f΋}³þrƒjbÒês$ßå±IL›-½L‰Ôoix˜äg–Ókà¸Ì–“æP%Œ ·ça›“tº�î–2p*ov€(oàOnîXpÃ\€†•ņÑÞi>ðÙÛ§?½ùæ»§\óüôôÍwÿñý¿ÿþõç?<=ýð—?¼þÝß¾}úû_¿ýÕ?<}ÿöõ×ùïÿýÍÿôøÍÓŸ~xl`µÉåëx±)ßÄaÌÑn›ÕÍDÿá†OÄ endstream endobj -449 0 obj -<> +2196 0 obj +<> stream -xœ½=Ùn$Grïú -j$S¢$&ó>ííj%ËcÉмI~X -–°gùÿ0ªÙU�qVvU÷¼ ‡¬<##ãŽÈ‡_ÿ÷ïOß|óðæ»ÿrcÿüç×ùî£ÿ»q7öÆÞÜ»›ìMÈÞÛ|SŠ7!ewóÇ?þ°7üóÆÞüó�§�^¿½±7oß?¼³7ΚtóöÝ©÷ôãí?~ûüwkíïÖ…ÃÏw/¿YüYîî«)%…æŽ C>ü|:6ˆÇ¿º—Ÿ¾ûúñ] s§²Œ÷Ÿoÿõ£¿¾ýè?>úë›ïúý9²?ç½)Ö¶¶¾?wSMd¶wœßÛ»·ÿÃOë™i‹)©z¿¬‡y“8i “†`¬­ÞíÝë{qÒÈLZ�Í)Æ E•yø5#ÈòûŒi#’èJ2ÙÖâ·Cíeýßgwý-ðiþ)¯)“5ykMm±¦ë¡Oa&ÍÆÅ¡Iw"d•®)8ƒmõ*ص�œ#Í)õ5¹Ð(Gì ùîÞ×ϹÂ>ƒÉ‚0î½ §Fà|0ÅÕTî¥ãšãݽK¦æ”¢M3¼¢öÑ+­ÚSž3(ÝœüñW…GP&­5ÉÙ=Äú·s¸eSÑc]¹òÝSE™X Þ{Â>¯ðD¼#üw?‹/wéùîx×>žÿsºfÇ&wgœe¦É&­õe'3ýM¤ÀŽ2Ó䬉©¤…ˆ”&—MËÉí@ÄÃ~’·MÙ]òÞXïC¼»s”ß%_Œ/C³ª[ýEž”2´4Ý)ßò‡:á¦Iü©š�ZÊÛöýV–‡=£˜èZ “n=bOÉjñ&çKÌz€²81%�%™KÝ âo§¥D«4sž0zß¼·ò¤”hÕh’i>Í(íã�óÁä|ÖØ™ŒðžðíÄzÜ•q€R5çœI±…²gæs.»§4ιb|Ì%¤Ý§ó3PîgVë�„¡ ¥†.“šuפ -”ºäM�C³^$!È ”PºŒ«!æ…5�1šÔjjµ5| Eeu”¶z׌÷Ñ�P×É4WZ¨P‹™•:Ç*ŠÖæ“Ø¹¨!Pr}FÊä²Y]¹„«!Rp¡sÜ,zç÷H¤Æ¢tfA‚óI6Ç çBêTX`£9~¡CIs‹ J®!0åƒ1Eb®»eœ Ⲏò˜š‰6º+ ¦b”éÅœ�Ϲ•KpåY=/—>}ZðTi’Öš<¯�qD¯Qެ‰¾1š�'åv/PT̤ ¹xKÎ),aC(íHÙøjS]³ÇË ¦,º5SK*{@vXΗò5b8´�¦?Yí·CiE0ˆ ïuÖ¸:6­N6îäivë’ñÑN΂½8Á¡ÁÑÛr¢òQ°?‚~ -¹‰Œ¿#Zš$†�7ëK°È°Î”Ž–½ù{Ѿ?hì9²ìùqú÷'”çø`Mµ1]ãbËë ,ÈGkŠ id!•ÀHù��É”b'•÷ -8ü"l|ßJñÆÚÉ_µóŽ['‹‘q¯”blvùš$�q To‚šUÝí÷ò¤”|ûZLȵ^€É zõ(-÷-×&SȾ�Ë^ßD)¹oÕø4$_l äH±@ß?�úÒžÎP»…Oöz¢lD°Ø<4?¯J$tÛÑ _£œOÕå$­gáoI‹¦V7ÙÁöaÜg2Æ1šNk¦…z%ºûÈø+/ f¦¨‰æÁš�óÅ Ç ,*x¹»÷Ñø|˜}ˆK›0Іù¶ ÅØ‘æ QÝ ¯puž/dûEO)~kz>y¶Ÿ{×þ³S¼ÎËšnºÑ°“ŽùÂc£‡¶ã¥­|6ÐZ1Ÿt‡Ô·Òü‰Ìãjë“c¡PKÔcA§fÒý9.:F$¼bÿ>nAòÊ*+"Ÿß@W“F\6¡X»ƒ4ôW¹³�ðÀ–ˆ�ÓýöôÄM¡é¡G?÷¨úÞ˽µËÿM?ëqTÚ�×·C!*Ë"@ 7>ƹM÷Àš7‰m‘I˜}HGöìo"ôËa½[[9½aF^B ÎñÓ(àÄé—°Fˆín be¥s"¹`4ÏЬ²I5¿YoB i‡¬¢±¼h�Í­®§¼xÆ“f“)Íå2°ø½ÑzYÂc|dΚ§Ð®ý@c€3’'pŒ³]h¦¹.£àDeÆp£iÅ¥«É„=\´¸Ñ˜¹¤³H$·¼„6öz}Ñó7$Q?õ½UºcQ–ÕIçÁƒ²� ˆG¹¿ƒ^8‰>œ¼të'újõ¨ÐV7!Ú84˜#Ϋ!Ìâ�ËxVQéI`â¬÷ d¿§$;;…/äZM˜b5¶^^”R0›¨²Ây«i)”S¬*Ö¼d�ö^•~ï¹µBŒz%/¯S¬�6³œ¿¤Iy ‘-«{¿6Y¯û ù -€4ÇENƒá‘ÉŠ#£äl”Û�Ú¢5±B:K£øl0ÅÃÌ )uY]g§r(ç1ßt2Ó˜ZoÞ“`ÙHáêM'pô€<í é…/À¼Ã—`@þÊ^#%šPmJ»-DC!�Ë1�ˆ×—ª¡{d Àñf\‰ÎLjP®Æ‡XÚôV=ßKJKGÞ¡`³õ³aáÏ#ô•­0þ-_Mó.¹ýˆ`VÏêí “d T1�ä´pdô“›Íèý¸ ²ì°�`\®âƒc˜70°ä¨*% pû~!‹ÁÃÌȉeµ¨ƒz5›VÎÖ„mØŠÃ÷œd!K$lí´mŒÏ-¸åãzYAA-Ÿ $ÌøÉþäHwtt½µ¶-‹éLlòÃôÏaò“0s;pÚŒÇ9[S¢ aà´×ÔbÙt�—sžl%¥ýhÆA‰oœr³�V³z˜Á± :“Q´,Ñ1J«˜˜ä㧜Ь)9¦ ¿•³2“Ø’©Þ•+?YăXÅ(b¿R"­LY=U(û-"µ,þ|×gÑRðsÌ&Ô²'f4ô¶p‹•bê³i¾ù¶fgy‘A°PÐ"€Æ&8ÇfWÆZÍA“!OÚë•Q>Ñ%´›¥Ô‚iÌ3ÞMçÔ�Q…‹/±ÆÛìË:ªèRüëþ ~îÓh‹q9æðáÀö¾é`ì~yäð‹’êP¨JÒ”€gãð^Æ8tÏ�½á¾,=È»b"2& ´õy÷—þô+,ªcéÝ’YóZ˜áÈž˜°ö\��ù"Š^s倦7H½&ÆßK�ª*±OœÍÙí -ôqŸ�ØÄò�˜ÉV Á¸BØàÈ!;g¼®,–±,~RÎ7…^MÄ~eÛó¹’銃ðÄÝ0}ÏÌyg†X ,f`´æÐžå^uÌ�JQhÀÆÏ9|INZƒÐÚw¬è&šš9{^Œ÷flzéXœºƪ2ÔG%eíÒ�*¹)UhR2¶^ÚGÙz²Ûc¤ù:‚ÓaA¢‹8ÎàJú´“8ÉÉnËñ/øNB:nB ED] ß¹Èg7Ì]Lµ¥ œö*‚²çî*(æØÂ(™.ÛB�@ÁŠŠç�1Þ*…ìÿýýé¿?ÿ¯§;åò¤VMjN1$û5¿ã]Ëp�’õ•²2*Ý×d¬³“@¼¾ �·Å—ôâ·Er� áÀ>¤T›ït¤.4öº7æÑ ÅE”3@®¦RǾÉ~<ð°9©!Žyà‚—ñ®s+�^oäM¯øU3‰€¦^Ôe-ÃF˜�rtÔð‘÷„É ÑІqáž ÉîX_Éclsê�$ ùÒS%8¹bJteàÎë4ý{o·ž [ƒF§ª-m-Cõ4§,‚*›¥˜B­ëk!‚>[KJ/’Ø@!¬J•¿Ú¦¬wƒe¶>Í’P}¼‹É„T‚zž}›q03 -_lÆ•Xòn8¿€Í Œ¤‰Pý“ @Æ]•<i–¡KÊ8R|1Õ¥é–îCš1Û'ÐÉþÂ2õ$è§€6Œ7c¢WÖûx�ëI¬¶Œ$®¢ ŠAmÇ“°Óñ&|4 öDé¥-ôÉÏ V‘W…�õ òwhqÙ×ízɥƈÙdïvCùÃ}5![P0Õ@óí,« Mc -´dZô1î=‰^ø³�–ø,l�;ÿÞ7‡-¨ùß?’r ñHÉõÆ•£}Ëê3ßéñÚ¢Ï!ÕÍô³¬¡éy«YÇ;áuù‘¸eRÇONµ&øjÃH¾"À ޾lÍ“ô^Ùž–r›R6Ùúdßz¹ î��]!»Ïx˜ÿb)â³eAj�BêôæFõ¹¤*Êš‹â4JgXö¤î˜fÑu�ŒÜõE^!Ô ~…1®Û6–hÕÒ�Í-dSZs›MnÓm›i±*�WÌp�*F-›d«__á�¯3D“cjGó`—½ÍÞǑá‘’€ò-ÅÀô9f¦¥\‚œèD¹Ipyÿ¦¬7Èy¢ÐP´êø•WËFú1:Eúñ‹=+ºTÙ,¡º 4Œdû]»ýÝze™¼}TžðÓeÂo§øé¼“ƒÀ¡‰§šGk* -]­ßa¿ãI¬p¾D:D‡”]ŠEˆÆM†€ã-Fº””‡ÅÅ’Á‘îGƒ~È�C¿WOÒVËû‘÷÷¬&-Utáó½É71|”ì¬Oà‡õBœØËÏ4J6‚0«šlÞs D1¹ d�üà|ê'!_ÌÊ–·±¾…�{¹j›-üòüÔN�l4Sjq¼aP¡ŠíüÐü¢ÉØÎjÁ�ÉVSmÞQ½¢ -±'…Mæ›òcZ«À¥EîäB³˜98±¯ˆ¶'yˆ“\Ó§‘ L�Œ0[] Æ€sý "e8qFÙ&¦ÞYzŠ‚Ò˜àæ¿©ñ4,‰?9X pϹe±òKfE>¿¹!ãÊY7påˆú{TèÏÜ ñ‚æð8šõ+"æ¬�–±„„£ ’WÄgµÜæC}0ÛRÙzÿ¿æoÉ!.=9“Z«¨²Å`ÁÙÒg£á±ÈíëŽê•ÒmÖœ�ÜfSÍ‘ Ž·îk¾Ðæpf±¿(ŽÒù˜ :Ê•‚þlÙ’ñRɇÊÁi?áµ³×hK~*Ò¤íYy³ëÄ¢øH““ƼÂ&†Þg±Z¦ód€¬.Øzq—Èaÿ¼øØ5èXk25Åðb^ýŠ'"øÌ{Ûwÿš;ó¹œAäû0ªŠ_/^V„ÁFCf<ÅÊ|Z ¥X`4˜ äh¹º§E�‚|†ó†á4ì�À tÙ¾t8„% A’®ž„‘pÎy"<\_¾à…EÜéé2AªÚ…×¢n¦ªýÕå]O~H‹B')©ä}ì\6¾¹z¿a@=¡¯z©·ŸvB ìø#jyöƒ·â_ìW½ú€Ú(eÑ�eŠ„brñÕíXÝ…™÷ápj ­¤b9ËÔï.ÁØâEPPD!|cˆ¡SJE£l-ºÇaÍ-Bãy}iOÎŒ‘m4+—Pß•EX¹V3?3z®TC¥|ZEÀ8½Éä­Ý¬19T Óë~yN9GpA&è û¯Bó‡-¾Äâ -ÑO×:^ÉäâkÞV)\Þ‰hÆë �ÄØƒ>ò¬X²£­¨Ì™û…x™zŸ¨÷$ÚfÊ”zº~�V¸ !åÇÓ§P¦YÝŽÜqÌjÿÑ  Øz�eŒÍÅšèBô¡ &}ªuF±ÄöÖv¨…½ÅPMõ!mWòøPÝÄà­‰¥Ô‘L±)ýÄ_MrË䱇#`3èRЋ%¬C»al·swÆ4$U±±¼½2¶`Hå`…%ý3ÀŒiÔž c¶ އÖ[@ý©ãB^ âÅE  `p+´ïB4X�[$Ðõ¾¹FÓ@•Ç { n-ZúÜY ïIÆÉ­Uá0zAåÐkÓ‹9�œùZñXù-Kç©B]4Éû¯„m°ö 6ò0Ò­@‰x×9h»?¥•L¾?×Ôy-\,ä`lˆŠYpc…ÇÐLtå8¤äÄ9OuÖZMu-¦�^äiW穨߲±1æ¸H_ŽÌO%2g³q©Ù¶+où:OÅ*眉vlÞŽt7²s¦h„w&ù�büpçϼÅå«)±–°‡%|ª€Ÿy‰+Smj—ÆýûRŒs!ŒXGPÓó^XZ"׆*3Á¹ø—=µõ.†ö -ÛU±€yeºLE9¬Û‹¿?ÉX˜‡¥«75O¡»±àV€Ie•ªðLH,‡³ ð?ñØÅ·‚¤9Y |®Á±� ¶ìç�/(¼•¸JêéÅ$m‘ÌkÏ1š\(Œ&A=9“Ó«sH½A¾Û mÚ☚ÁÕ™ÖÚ.ÙK�Ëp}6ei‰4»Ôp!°5 ¸*·\ 5ù©¤wñ×ÓP¡q´<̧ëXwx“6$¹`Z-µF¦$ÍŸÖh ê êh³ ôg)`Sòæ¯[ï„â�ÏyOÂÆ àœ©±MrÈêÙ¨œïVa| ¿uÙ4[\¹JÀØ|.”¸ÅIAäT4‘ 5¢“ºj¸‡ôÎ ‹Iò0b�JÒ�­þ íšù(ÄJh«,çEÅ35‹¥ÙWÓBÂe.%j˜˜EÈaÓî@â¶|U£æ”!ݧ¬ô°˜õ �E]ž§àæeÏEúO­^Z#“<®µ‚‰ 6‘—ôá�5ê `œƒ»’~}Ý;­ï°·|Á¬ÅOÄ¡ø‰à’ñA©vä‡\6ãg±rÉ ] ™ˆLÈ„-¦¤PÜú*Çž4r&æVÒ‹Yž$$! áp¨Ó“K°d#ó¬c ¦´Ã:4®à¡ˆ‘÷™x?)ÇW8!®ð.l6e^‰¸"‡5£;ˆLËõø26çÉ$(¦Fõ�¬ , È#CQž“½™c“<ÀeñÛ59`ÏûíY#¯ÄÈZd¾µçe‚uÆG¿Ûá‹ãP…ÇrŠI¿ )gt±'vÿòïHx¡¿Ù‹æárVa!+)™<‹C - qb�ç^ ¥Ã)‰×fãUèH_ˆýX�pB?õ�<‡LÄ@»3î%‚^-„\ îÑ6p"J ñЦ ‚rï–Õs• h%2…h!Ó+>¦¯¾6E+ç0ò$'ny"%üµãÕZh…àõœOû -<*‚O•»ÈDŽ„fjöqà(W¶_:^'å�‹Q¶Æ6.�[,å³ñKéÑ Ÿ¬œüñ™áB -¨˜÷lÒA‚-eÿá¡ -»¨ôm¯€+p<ÂÒ)0Cú(¡Ìr‹X'ô,>1a)—ËÕp§ôêØW´G"I2AY ý²Á&-PcÊœö±í‰Ý_Èvýjl+(Ë—­ÙÅ4|”>+Ô5äŸb¢-Ôa†”“¡ï¬ñ¾…2p2CJ1¾Æe p&Q]݇dÒô¼Ë†95h oúõ†t£ÄhúÕš”‚»”ÿŠì-É£Úš¨l254®cr •`1Mƒæy¢¢b˜^óŽ¥î=rñ’Òô 8ûí(7ÒvÃä,û<É&%^âÐ@T €ú/OKÇ%Ÿ¨˜›Äܲ”þ骔±j<+M/ù]ÎVúZ”æV! -í�ÌŸVi¥ K°^¯tú¡«T ª}ÂÅ€�冃�îØ1Æ�¯ª(·B¿oVô¸â/ä°W(Ê)?›vŽË‹h7NIC•!Äío�݆£¨ÖÞ¢JÌ¡CN0ìöËZ -aЊø˜ŒOÙ^Âö0â2ÍTÿ(Ó+˜>Ù4°˜­Ú3¢D€—‘Ôƒ°SÌÌ#<ÉšZl»¼§to¼mÙ‹oT“´6nröCÓÂ|¹bR_£O߇­½L‰š«ŠŽàåj00ôÆì¤§,yã†�‹BÇy7œ“Y>±¡@ÜÌÄBúd¢Íõy GÄÄEÆdrHC+Úq¿ŽHöÑÒžç;sev%M�–ö�ÎøÔv„ J�L—®þF1Ñû¹Ò/ê¨ÞaÔT3á3ÍéC:zéYmqÌèT aÍTL10R]„LJ®N+7áÖ"‘ Ï.é÷jDŠ4µãÈå *To}‡%�Å?�yç¢qÌ6‘kþ=Ÿ=…Öh~nïŠñ9ìŠ!]qWÛrç¼ ÁÚ‚Ü>Ïk VÙpÇócœ…™;Ž•,±ïJ8ÜÚëáüV:­/ÕËŸÎòý­LŒtdغŠKžðô¢Þ'šx+\ÆC+6ÏÁ×¹bšòq^`ç=c±eF…Ú¨>uŒÏy�/kIš[cü®šXSˤÌI×µb`s•£á¦ò¹€†K]#»ü//¿_áò?#/²ºÌx½§ˆ¿Xý°WÔè´Â;×3dz¼üüN5™‰m´ã¥5PÉæÉ-_c“ö'?ö [~¼4Á/>¨¤@³Ø¹ìŒ/e»/åÛEû¬óêûïÌ+Ñ„äRÃvJ‚*¨áX™ƒÂÔ3 -Ù¤œœØ�Nèêj\ÔT~Ë'Ób±ÇR:¿@4]Þ=ízý¤í‡©ŒÔ¦Jm¡¶ý§ÅÀù{þõ &臜±»n=i…+�bÌ5˜S ×?yX€F (L<º�ÆÚPÒUNT+E[©­ gbˆvd1›ÊÓÁû±Èðôƒ¶nªJ†œMhµÄý@4"fÓ7沫×C²9È·€sL ¦C¬«·Lº±ç8<À%]´]UA?ã«ÝáÇîUHT‘dóz“Cð㥲`E÷F ‚ /h B…ô€ -²gˆÄWÞ"Èè…Ÿ°åcÁ{h×à Ž°€<ãPpÃ{­ÈÈ0GÃú7Rh›sú¼ JÙ5ëÛgÔR “†QN/p€ŽõXÖ•{óDÄ ¼’Ç>|×jµUíe€©‚£¯q»Óù›Óʦ¿Ö,¶!‘°„Ô 9?„RS›v%ðÁ'óAÃ÷=L}Àr»Héï¥ý -ö¥A†—¼•ü0af̨Ű  ­$=L95¢óᥠ^íÇ!E+McK$ƒë¤äŽq å�­ùVh—ÏÉr×÷¯¾ìª¤ÈK -)ÑbåœkÆ×²Ã[†¸þ@9éý$,`x,Äty²tn�S# ‡PyQìèï°°skµÌcE*l¿!1¸¥å*.(�<ãZa¥šF -+µfb�isa¥Ã³¾my>™ îî­™Þ�*éÈ.çŒæÍ�ÇkûDá»°8 ­0ÖÉJá-º…]÷…‚8Ì<;>¢ã°ïðZìZ‘¨J­:Þ¶©>wië2O:[ŒÞ½lâ‹ =ï†Ç%Ï"Ü%÷|\}ê”+¨�FéCUÀíbJÛ§3ƒbĽb?çørQJ⦠¥WÀO™ÃÂ(€U¬…Lá±¹ž*ÉjUYk2±ÖífŒo4çóêâÁ;`¦xWŠ m)í®Q8€²üÓÄў嘔æÆûSÄ�0ß�8ù^n YÍ�Ôðþìq•ÕÉ:9€ŸÙ‡8N>Þ�5øÒ±Å*�"4ŸÅrùÉsqj`¡oEM"-:ù­4¶POP­VµÄÝâLln»aë“Q¿S.Æå˜+ˆë -*êÊùÍp\@ÎpWÄ4ˆïÁóéåñ¤d€ëý€\6¬";ÃC?¹r¤€-iú«ðwŽœ¾Z;‰I3 t“^ËD]�EÓÞ˜<{©+Œ‘8\r‚ · ™4ÊŠV… ±ŠØÿ’˜™¨ +xœÝ}Ù’$7Žà{}ETu÷H9R1yšíêhíΘz›z›ž‡ªZå´Ö¤PoN¶Ùêï×N'Aîá •íK<@Çý¿üõÝñÍ›û?}óOßä—_~ýí7/þÏAäA^«ƒÕ©ä£=„hEt.>ü|ÿA>üÇAþãÃñÅ×oòðöñþA¢ˆ‡·sçüëíÏÿú韥”Ÿå–Òž‡Óï§»×&ZŒ1öÔìÏRë;Ÿÿ>žÛúóoS¾™ÆAú+�ö=Ïk$ÕOºsCB¦N¿ßŸÛ…ö7=²Ñw¯gÀÏÍ¥©À<©‘3y^Óâ<Ö „œíX�tOï +³C$ÿööŸ_üñí‹ÿùâ�ú澦KÅÑe°BJg7ÒåŸÏË�` +†­ÑÂ+CÝþH’$$¤yÇψ2†™ |Øj¸&à-ÉÙ 8m—Ÿ×J®á¸ÈWçAN�þŽœ×²;SQø*HâÒqy#¤Rz+q1‡M-TˆÊZ�9ïº_$³Ð¢ïØ´¤>s:Ðw¢¡Ç†£N׎6A¬jýÂÈL‹e~Òöø<Œ‰«º%`¦“:!¦LéÆ_Q};Ò4q™Ž¸´"iïW—:D¡â:oÌûvÆÑàXŽÖ�Rå¶ÂótžÿŒ-=ñ¿Óï—ç/mC—Oƒ7jÐza®[˜RR„¤µ]³2þ¿É?tÙ{i…•FZ¿|ÆaÓóB��U–\@ù÷ç3WEä9)Öå")¬ð€™%w6w[wÐè~ç¡iFÈpvÏQ»UBy¶ÒD!eÈó"lZ&Ç^‰m£ŽÝU¨lùV¯âÂÍ`F�J¦3$’’_d+87 +G )°¶¼ûZ@ž‹N¦TƒŽ¦^a04±n¼Èÿ$9Ž�Fy³� .4ßò* +­lTæS|ßW8ôØ Cq¿®á4�ëXE¬°«ÏuÍ[‚�B•Æ;WM äù»örè†sRŠb„óðï‘3ÿäû¶+%Þ“Ô9êTIXï’Ý�YJ¡]4f&³ýÖéÆjå‹;Tž…„HBVøÅçá)#@�n+¤Œ[n¹:8 O`ëÐreZ³  „­q‡Œß EóA&©9qÔ,£ðÊm7Lk=òP–›¡ìUðÂ:Ÿ !ŸÝE‚5V;Óô¨n²æ�L]88�÷z™¢§•~öî“$bü¼Ùehsg‡õ)ˆà�ÚL½¤±Qi¡Œ1F1";w¿>Põzöz‘” f»{äuEkŽç½uR¸(8OKrk×Ï´×y§aÞ#÷Ž';.7é¸S–P~c/ Èè…h›%”cÖÚZÌ*tÝ¡a!—#…ºÆ4©G,Þ+ˆ�³ÙúàDÒÒ_®J��yoÿ71ko̳N$«cô+f½©õìÁ¦ÛæÿV�pÍÌ_² ú{3bÖÀ�SÁ  ¿2¶ÕB“‘Ù`j½žÈDߎz&ÿÇ Õ‰rÞë6d¨Ov|ŒÞÇ +ï„ Ro·/ÀKf¾lµ:9•¨Ü"ʬ~¨ZYèìWí>e³q‘½fªjç‚Úr÷ˆ +AÑR»±öÓ�(”®Ó\°þ¼�ÚÌü"Ñr6Zï¬Ð*Å£X‹Ú#fÿ,ï&_ÑND¥Uû Þ} Í=¿}7òÒ ÁóZG$Pƃóó-ÿ«Wgéf€‚i× ?€klM&- +d`ŠF´–Þn� óZ¹÷=æ)™3ïzk„ñÉn¦ä"`%¤ vzÃln7À!º í¼‘bôµô˜oän,RBìÆ ¬Ñ�„AëuØ¥a�’SKŽC>Ï,̸Žåt£ ËnõâÒM¬œµ×›ÓŸr³…�P‹µJE·ÊºÞ7‡ÂÀì–Ââ±Â5ß[áΊaj BÖ2Ö¾a ŸA…äÝš­) mz³-íÊͤ.»@ù4‰r&\¯•ð>úí&\èîG€}&š �(_­�m ˜Ë­Ÿ–¸1ãÑÄÚ¥!HÔéCzÅ[γµúŽÞVä¢ÉÚhÆá8¶^"g$¿oþ›}Füf“™Mù÷ïÛU}õøôãûOoÞÜ¿ýõ¯?Üÿî×_þöÄ`Ù¥$´ Í…/’·Aº”Wy�uø9÷Ö† œvÍŸ§1?´5")cÒA~:hkó3œyúûüÍÜmþP�:—ÿ¢ΩdσLŸ*¡¤Õº8öÜržzþìÃAUÿýTO=,«�e5õ‡yñ€Q bÌñBš3¥ð*�­Ýê�Ñe‚ö)´6N'«òŸñô·ƒV—À3¼V´fè¹7¹$¤5))nÂA™1Êò¤{ƒ‹7„¬ßEOË!Ý›‚ÒF÷ÅK¯=/L4’ œŒ|FËéR„ü[úËèfb“¹h2Œ÷�îU„dDÐZ©q4M½´—¢�ÎÇp…í±ô¼ý®¤&)oG'v䬦¿ý”ô"èñIYb4ým§”Ò+ö;¤'¸ TÏ\•rÂ$—ŒÛ›uü¡º§[gÜr|”g|g{v«¬VZ7În?¥Q†Y·�&é´/!N¬N +™=ÛÇ'戤g£Êe?Ì8~¯1ç´ç†*‹‡Ö¸�ñܳC•]±CÜÿHÌÆL±˜ÍSÌ©@˜jLBFŸ®ÀßL Ñf¾šŒ°Z½ë~Y„±f'çm¼Á~5ƳbUàœì{ž«•29çöEm�=÷Œ4ÃÆßѳZN~Ϫ@ŒñòY‹€î©ßÌ8N^n@Ú�b–mÖ3òòfŒ­ �ÀÈË£ó2¨í9n”"äš+¬—@mÏ+£ÒeÍ||bEóJ×óʘ„‰ÁØkœ‰ó}±p‡ÔöŒ—˜W¯Ô9„íš ¤OÁïJ¥Nsrß¾§÷ôüƒÃÉh{"År2ÚŽgÈ9NLÛó yNL»ñb("pbÑžø‰œX4~ï^Â*z>«sø�—á +§‚‘I½äD­�£Wœ¨µ³„½ '�ºqÎãöË#ªuš8|¤ßÐ(3œœ”.ºÛʉžµ«6 í 'ž¹Lq¸«ïk^ôŽ“k6£rÙ{N®ÞÃ3%¡+$ƒ‰ÀûöDDäŒ}{QγˆB•Xcߎèˆm@%L–ŠFçe‹€È¨Ú +é�½ÂÄŒTUGa¥ÔW <æÖˆj´C<ªW<`N‘œ‰$ ÒlÎã´Šûªã¬g»žÛ/i û¹›gŸÝx\L4¿d¹ê6æ†åv™™›±Bš ÍÆM¸cæe™ªr"»E^ö +ÉîP¹Ù7 ^™N‘ ›ßI+üì•âØò®˜qŒç†˜+zÚ7¹+`üù 7¥BÐç[b£ÌË…Bâ…«kbx/™ þ¬Þ.Æf¦Ý}ÐWîž=I‰†«.Ÿ½õ‚_¾BbãªÛg_V3è—¯�ˆ-僰Ê(}…CÈ¥H–Ê�66¤�‰±/dÔ~o"úÏ TØkVf¸ÁÂ�ø=Ù¾¥¥V{©vÞÎÀYÇf¦=¥ˆU aÒ‰`½¹­f‹ÄhU"O Òoâ)­�h®YâÙŠ±‚DsÍÏ༜X€lÍ/«£ëe„$تzZœ˜³4 áVÕÛê虸�©‹ÜšEŒ=©ÔsRÄ®§wÉSZ¡ñ]åµsO¤Dî¹AÊÕ¬ _1@%î-t”¡0ç ÛšCwÜ,lk–‡&f®X$øj¾bmò9ËþGåJ£�¨­Âñ·¼‚á#a[3Ü—ã÷HlÖÌïG7èflÊrœwtÛ¸S˜'™zÞÛ=‹)$¼kæô{â"1ŒÁÅ-ø<ù5óù!\0Fe$T«bzÑ‹��ÛC¢¼f¶×€üP(,lzªßŽð4�Å�Çó½ñÖ'55Ën +‹ +öÆAdq‡‚óªµ!ëœÙãPp¨Œ¶½¦Å×$V¬Â…�Bj'oü¦Ž�U¸h`ú¨‚otVIN›‘¹âð#Qg•ì4:3g®F"ÆJê¢}—lèÜE»É’Àã9ï°}ÑÁ¸ˆ�Ǫ HäWuïŒÎÌ) Xˆ×Õ4μ®å2¨°˜°Y4Cí4¨°¨¯YúÞ“&°¨¯YþÞ“&°¸¯Yʾ1IŒ¨‘HÙü1ˆÂÏü&nzpZF$C¾*1ÄEá’ {˜g¸àrÅl‡iÍññ´oßðÄìé ô›Êø.ÜððDÆ…b|ë˜k «TšfæŠ%HŒWu¼lÊ[»ÇûâÀñCâÃf3Àf�W<´!b•?:3wî‘0°JŠ�™;øH Ø,ÅÓÇí>X6KZÃ{ÇQ�ç„­fæÛÅ+,”lÄvÅGä1 ·píEËæ7¯q®Âœ0$ºl~ôÚu'�(²âµ/aÒ¯ + 1›Ý¦öE‡a\£veï‰Ëm°+{O\rƒ[ów®²ƒg’'í‹!ÄïK›\OUŽìÍ–àW…D“iãEðÉ Ó £ƒ!Ád•�h²ë³U·uBÓHœY¥ƒm†iù°j$ÖlÖÁF'f(Q#e³6¼ 7q{ÒHpZñ�ß5&Ÿ=�V)~ÍÌ{³„×'�…ž�]Ý÷Å‹güÐP¼Ü@ÓXÐÙì*1†šÑi$è¬ftRiâ­ûæÕÀô¾zk$x­Ò\7£te#ák³æ::1óð¥‘`´ùýilb†v±RÃ3íj)bæ%×~&ÖHxÙ\ÒÈJ)Ÿ?œ-p¼­d +xæµ�Ú+³ ÑB°\ÆÌ(aóù:}òuþñÉd;*u”_•�_áýVTï{`5¾«ºÛ =U† Œ +Ë„-T‡½ç^€E€rÆUé»f„/¸�D„~�D !Ÿ¤1ÚZÐÔ"z1"êC4ÂiéG‰–½f€àj7š˜DJÁl„⿞¡€å« U&á­‹Ö¸OÛªigšz ( öë+,w-ˆR|HK¬âf 9îj†Rã—1�EŒÇlSióKì±Tôµ5ÏtYî*@,Ô1áC/IÁØC‹ÿ¶‚¨a +«Îv„¤ÐÁ‡É£­ +ÿPêo2ä}†Ž#$ ³:–!e[—ÿUND¼µÃÛòêH»ª(%,É6†˜a¾Vžx0¹‚ârUÝTÏü§ch°º³GŠ�W…MQ�º+•œÙyä _jáâª�¿[@^û�Ñz7L™=ÂF¸BG¶ÿHùÇŸòO�Ó•ÒîgZ–°98�tB”z~ úqì:[¾�,ñ\·úîôñéÏ8€5êÚfèÏsôg¥pù•sëQ6º/´~Dn¸¼¥„IRªE] /FHå”ZòÒ�0Ùh§; WaP¹À(ü²ù°ÔÜŠ±w`'’ +N�nT�ÀFL'Hâ_Cθ‰Ëð#RíZÙ舉žZÖ·a)Ó~Oμáüp…Ô�NÙWIm¾ ÛÛÞy¦ÁŒÉ6ç„�¾Â= ¢Î¯_/ZÿU‹uw×»xú€…g:}ìñÀjÃj-LL2�ïA�×3p�PÄÄ„ƒ~RÉ‚ 5±ñŠèÐßø +V^¯°3�o@G(#¹•0#~ÂCš;„* +/¥Ó+¸]9ûP:‹Ñ¸¤×û@,ÇÎ4çŠóˆ½*!•Ì´¿´ô¥ãxV‹`Z[°T÷ZI#¤³ÖçÀ«åk<_T¼T�¤½Ð2 r~´Q‚èHà%AÝ�-k«…–w7j(Ù(;¶Þ‘$±ª‡ Y´”zœ•›j±µ‚6)ÛL¶B�¤à^xçRXââFz/¬t¾ÜÿàtÂÛ¨È +ëÿº^x÷ƒwAÓñ% °±öÐR÷§å-�þ»¼Š^�¢˜z;õ‘|TdÄf™Mж7ÊÁ»mÍéÌéÐ)G…¥Í”‡™ßtIz_L°e÷+Û¢¦ZMê,�èw¤Æ+Žs%hܽk¶ò="±¨Ž÷ß¿;þû§?ï=Lj �‹Ë8× 8‡š`™„ÈÛDBÚ”Â01ôèFÝS–àF¡2þ¬ÌµÍ?#N¸ý€eÐ-‹ô[lˆ3<¨c´eÓXG’ëTG0f—t·ù‡ì³[ŸîrÀ–U1ðJ)huVWޣ܆2%ˆª#“�IjµŒ’í>åý~“‰I¤œ)äÓ3º�EýXíœfHçc©Ý®Û”¿QÿlZ;äÄ”rÓêLPõêÈ«c ¤-š"2U/m’s&2[h¸³¬V¦°q}1O7ÉŠ•²Ð"™œ­ˆÚxµØ<!yš”´BF£ƒÆç‰Q+'’”6nÒºî„Ñ»oy¤F Ÿ"©¡.1SôÒ0m,!f)Ò}=6=.‘óA×Sa–/ŠÁÿÍ2!sæyí�ˆI:¿ý¼Œ(N6g§Š;òØXí&hÍm&Æ”f³f;ñžg#�k°ášgnØm™�‰ ËLýE"Š%àëGö¡ 6¯øìøþ¬‘VXÊ!­ÔÛ•¨úó^èà?˜rRÑèÔéàmìÙNÛjJ•P�!z&0LÍ5w¤ÃeG»CÖzÔ.†Æ XB RÄ@ o½– ˜ó̯¯*¦¸�€—LÞ-gš¶´m7§uÈ"ìÉ ñó áÓÐ(Iz|ôuWÐö+8¡pÇ<ÁÀ,ÂqVd$bµûÚ �¢£ß•ô!ñù¹'Þò¼Â;$ïaŽNÖF³à ´èsV{V ³ÞD “Š[,ÜÜ*åU铇³º>[Ö3É…gÖ<»Ǻb½3·7ÖúiÔsðŽZ«Väé×HªÆŠÊTŽŸŒ*mä1´ÅÇeÏU+§÷ ô[—ØúbC99Bí ¢mPì¨ ¸ó˜™h‰Û¿¬:†wwñŠ$ÿ¶¼ð\Ø�׆CÀ0Ä%«>C¼¬Y@j‚UÛm;�RTOÆ’Aú_{'œrzÑ»I¥0:I½bAê#á´Xˆ¤)¨0¾-o «�ÚMN4H2Q•‹’…hÕ8Ò;DOÑ+Eиùóùû{˜ŒµÙÃÖ/ÔÕϛ܂‘P«DŠ2ŽŸšnG +l„8Tį¡z\çfÖÇÉÍ*)‘´ÙlÜâg܉í…é¥<€uBnk~-ªÚiÅ…Þ,`jìRi[,«tݘíupÄÈ5Bu3ºÅ†zÞ�®Z @ˆÜˆs,Ìh¶ÌâþÂ0 +½´-kî6NüWQ Ìv×XB„zºÓZ¨ ÍL Ýûomú�„j‡=²C8Z&Ò‰zÏ¿!!Q^®´Þ‘Î Ý« œt~I‡ÂÄ=àzpÍ—º Ò�㸺›mΧ7Ñiß»yÆ*’3¼ñXk%؉ú9é+¼m©ÈT‰¨=¬œ¤3¥ˆ¸�Å1Á)GYÀÕ:24o>Êå‰wÝ‚1ñ`ÉèVÄŽ¶Aeð‘Ôâö·¯Ÿ~|x÷áéÍ›û·¿þõ‡ûïßýúËßž84Ú(¼oÑ(R.„ëRFçAÔáçÜOX‚rÚ5žÆü�ß·ERƤƒ<ü”ß�…õ·ƒ<ý}þfî6¨N�§ÿR–6’Òç1Îz¡�ÊšL6ùs»yÞòчƒšÿù©žµ|*ç1å<ç‡yñ€Ò!—XY)¤‘Ö Õ©5’�}®NÝ‚zå(H +ö9yÌîHÚ^0A#ÜçT«û¢,2 kP”]¯nõŒ$Xj÷9õý®˜Ãr»O¹½v£µgƒ0”bò~í‹Í$|Þÿìµ7#Ð.ÖÔÚÕHÆ÷9E쾨³LŠØÁ™éB€Éë^])Fž,n›k^–ƒ70�©È:›¤Jü½öÒb»ÿ] WH6æó·ßäßæœ� +æž“˜‡y ‚‘@&ŽîÛFÔoM˜}.€ûZ©ú¤ýª4… À%_q¯­HÞüФ´ʧxk’ŠI�ÃDüªâ„ÏÏÄGÌËð%.UÃqyÞÅeÛß¾ÆÓÍA�túSÇÁIy†‰¤ÄŸ³8¶3ּ߮FòåÏY÷…ŠIχäÒŸ3ÁïÕdb$ÿœ(|_òrt¢ð½1V|u8Ìx&s÷¾˜ Læîá™™bI£_Ý,Je‚fœh‹ÊI²_*wŒƒÜ×r +9’r¿¤ÝÇÒ値Û/yAgå‰K©_jϵ3ßðvÀÒíOuéöй°Lü¥fÝnPµÏ—Â'�+t%.�×_Ö€¥÷/µGFiîS�\!Õíɤ¥ý¸¸’éæNã0ÓÏ/�D0Qxz"eÁçJ[ À¿ÈÖXQ{p^–±ɈÛíÄ{6>íÔïò�ßÓÛb�r•¸9Šú ¤\À|“1Ĥnªy$ûÿ¬yni–èÒ Éþ_j6ŒÎ˨£K64óîAŸ�‚QܾxÆÚ¾œúhÖb�2³-~×}‹´€¸+>hK¶A +ñqO\ ügéqw"ÞP=Þ ™ÿËCÄ ¦~«8~�°6úó[ÅñÛq� GÁ9åD%¸/ `Œ�+Æ,[? Rh zØÚ¼˜éÆ Å +-�Mºpê<#Í5ßNe3H2ûYÔÛ(Ze3HrûIÜ ¤qcžÁ×ǵ= Kø^ÞÖvFØ +[žA’¢ÏVÎ]£i#ç輌!Ï ©¶ç{#X!U4{Ü÷ +’*»hÊï6ã$Ít‘Ò6ƒ±t! ¹…‹66éyF k&¾á…€åð�lx»ÂÄÜHòÚÙ„·P{Yð –š¶Xðé�f‘ì³3SòV¸’û¸˜’/µ0¥aˆË�^‡ã¶AØ+¼³ •ØôD››�\bY†�Ǧ噖a¤çfÞòÃIÖ{u‰ÉÝ8wÝÄȰ ½VóÍÕ…º®tXû‹D+)‚—aô°r¬KXX¯sB¹àÝO' *� }³!a;L+,·H–½b¹ž˜£z$7\1ݶÿ¶¶[$+Ül»Ý ÐÆ[$Ül¼Ýwë<­ì‹Æ|‹¤�*zþ؈Œâ°?%o1à"9jŠØ0Š«–ó® ÍÉY¹£ÕÎB’"z¯ÜGšãƒ01'R©Cs|ÉJ›S.–q}<7œ'.åØœòO›S>•Õ §¿Ï=‰àƒ¤ö1¹,‰×NBÔBëo|‡!‰zŒŽ"Hå€iOêâdÜEãq+ÐVƒ!·‡VÆÁd8¬ŽÃ´Æ™¸D†º¢v·ßçˆÊUeê ’/%×Â4Ù8»}QKz’àÄœ..#Ýè´ü�ä*1§Û)t ¾¡u I(bO7—ö;Ũ�X�“É+˜¡º‚þˆf~È�7JÅ}© Ó~¡Iû£lÍ›¹oOŽênçs‡D×Û¬Ó[Ù‘ø•_�0îê.ÉÊ�úã2ð!¿&kÆVz7òúg$œ×œâym€cér@ByÍIOapÒí™°9iàÖÆvâÞ H�®9içIê½áÍ€„Þšà…ÉÏx;�´Û³ËkO&cÁ®_Õö…DëV\É‘ŒOWBÂ|+®4 r›å£TŸâ2 £Ø +ÏwB"v+.14+Ïž�˜ÝŠÔïæÌRµO¯R1 ã…;G�;GÖ +'ým£N uZ)Š[AZaAFBA�1BoÜðÄi"Á Æa‚R`öx7©-È�̾ ¡£ÆIarÒª½€œ`d.S$vÔ8+‚’§WüwÍpÒÝžaŒÇHèçÌÓ÷D†ãØúÎ$¼ÉtŒ„‚šÓ{ˆÉÆÛ1T1 7² ×8!ƒ7·e¸‘e¸5H©¹‰­pª�ðÖ¥­Ïwô^"Ñ¡&gÜLÁŒNû†™Uq¯2ÜI«ÖTU–ɧRënNŠöO¢Öõ�¡j IZŸN™ÉÎÚ6 ×LÓ·ìjwsÁt¬m·ùjÁ U­›–j½Ô£õ‘Ÿkƒƒ™±¼ýt�4Gýç Ým“x +�CÁ?}b‘@㙆}Êl8 ÔÄkËʺ½ïr8ª³9¢Œ¦Œ€UŸ�ÀÎᕌ©Úv`°$G㑚÷’„ÿ ¯%’»²€�4 sW¢`ÖµèÆ\!Í®xfÉ ´˜„Ò ±ã¡ÅìÍ•ßL.¥´`_0<ÔÚ» WL;žG¿-XÓ&oŸDþ†8$bª³Þ+�Øäӝжº$Œ±RÁ‰{•r"$oü8ºAÂñr”8é ?™Å¤Ç†¤6Or“‰¸ÉkPÛvÐâHöÄ–7%8ÚöSµ�¥½oÙé·NU+ÂÃ$Å'ZbY¿™ �# &§PÚ-—íÝkeƒˆ>I¶"lÕï/³>D†vQhë¬[\€.çE-5�÷h*ÁÙ%l£We‘¸«½p*J;ºk=¾×Ò.¶—Øî]J‹)Ÿaû®ê]ié k•ù{Eé‹ä<¨N•×B«è6—Ê(˜ÕVhëu<7*žªgu’Vh C�q/Vwª¤æ`(â€Ã…\P"éÝ–Àƒå÷(˜ššëM}† äíö ++ñ>u^[N²¢ÍÉ¥ýf<%,’9Ã9u*éæ9ò�FùêÃÓßÞýôö‡ÿûôæ»?~÷�”_}ûåiÄ5âçÙ2ñ§o(àXÑ×eûHÈÇJÂÃÊ‚ænV¦&ZP2¥¼�Ï…¹>›› `@ó^e.Šsé£U2º¢_�DºÉ°Bg�ŽÞƒX©^Ýœ/â~:¹Ô”æÕL�0è Gî@Ë ¨eÁ2-¿¿ÛjêaΕåH׿G»£°eŒÐNZýÌ-NÒÈ+ü;Nð$[†uóv,”¿!´áÊ8Æ;.ãÉk¬ŒÐR®À;/þZgý»37càB²„Y-¬—z”0´wG‘}Ï–A¡5W�3ÒUã>ðýØ2Bp5díÏå*:ɲSK�„ªkÖ~w ÃÐë`…2X™Ú-›DߥjÅ ý‘bµfî�6ÝŽqéµ¢¶[Á=W™^Rl÷W¶U,‰²jºŠ"¿.nf¤Ú­„3nâï Ö #föƒ0¤ç�DçɃkmE*‹¤Œ2É +MŽ`\DÛµõR$É”}~©È¶ÈÑ Fö+¯g@J€B]·Q”ôÀ qAŽA¤˜G·êkbh9à…H¡­Q~�Ý�—`yDµ€5Xf•'™ë '5 ƒ¶/T8T¦k„11z^‰®Þl@ûžF¬_j`K^~® µA(Úž] ²ãÂ[iýšÕöœîv½|ˆiÔ��<жE{Zq§­Vk»lXÒæ”+—¼ˆ:ލW bc_ƒ�Ð�Av8ª°àÇÓY,Š„ƒ'°69£·}C�àÜ5½G¿ùfÅ­¬CV`U>XB®ÝjÇmu”B»h¶š€(ãA®�¬¬§ÈÂO½Õ£]Ët †íu>Ѐv2�#5R6.Æ!¶±v˜ÃÒ$‹à®Öt–FâŽT߸eKÐù¨k+TN$»aEÁ> íí@âó^U{ƒX›äÙ?i¡r}¾S—/¸• ʳ3©—²²Æ{ËsŠSÝùÛÖDÚ>ïM›Fœ_àZHø”Í�³˜3ÏåIÍh}~À×ÚŽnóä` Ñ-[¡Ø3`#‘’¹6¥—&oêÈ»'’»Ñi)¼5vüØô(„ÒÈ™p�¨BGüÜ» s1gyŒ›�=ÕæG‹i}J‹ ½÷/1Ü?¬‚ö+JÌà3,fåH•²éûüã뉡}^.êOà) ÐT‹#þù«/qÜzkŒ>ÐW�9Æõ´<³µ÷„yÆ×‚øz× ‹ä­ˆÕárÉà;Û¼ñhµz'Œ!qfª¶ îÁ³tbá-Ëì¾_îx�F ë÷£PôÛ¡Lªh±h¯ÏX6@`‡t�©´­ÙwEbŠå$§lE»ÆçZû½´E-d´ÑÍo® W1ì¤ò¾éw^îºQPF:âOÖ´¶œ¡^Ýe°ó…Ʀ®;okêö�²ƒt qM\oXÆv"¨ šþ›êò¸ú¿Bÿ«Û×—Ò—è–ð:>’Û¸ZµLB'½yÙßóbOL1'� t¬hn€1÷õÁÉ·s8Á§”­ÄA=‹T] RÄ€-Ë;™_qΫ§únœön SwzBáTi›¢°V1×úÚ)¥¬�ƹLgÅK'Šþ�Y^ÛN‚ík5>k\6:ˆz¯gØbY|=XÑrrö xo^¨è¦‚|“|d‰�{ÊÏïI.)¦pö…BkÇ 4u±!I[abû"Ý=º¶�.Á +ìÚ9Ô@©v˜àŸU%¢AÏ/`CCÊtŸ%ðPÕªuëd†R¸u¼p)Wиr�E’³Ï4ËÓ.̶d ÆK‘Ÿ·ã©"å¡Ç I×n¢VÙ4€Õ7Q&bbIZgÌoƒ_6ª©ÎIìn�Ê&mn„T¬D‡6ÿ[mE¦/ѪJG`p1—`¾½�ÓB{ïo„xN +Φd/�¶7Å<µ3æ[ÎÑäžœï9n±X¸›ÁfÞ±¸Ök`‰n³IDâo„lÀ'@ŒôdCÇ\‘^òù“ÊåÉ`#2Ä“íHñ;+¼LŒ§ÿâ~³\÷"êÕÂd“ø�h>ÍÇ.cÂ9Öl – _åGoeì +¼\ag�tø9 4騇7æ=c¯DÓ6ý'Ç»‘Äù6Wmð9uË(–ºè^c³¯^Ü ¿¸H‘FÆÅ4˜Gü¶;klíZ/[Y‘ÕµF—%„Í„Œ�@u +�uB©èVs€W§¶ >lçœÕ58ýV½Ê ¡Êýè}V Þç¬@¡8ydg¤Â¦~ß8`´£М +äl£9Çn’Û=�º…öb ì½>¿Ï­|dm›‰QVXb`§oj^t2ibpZ±‡k¹�}µÝwÁge®h¾*V´¯ï”zæi¶|öŠÂ´B0´ÇÊæZçd/L}œ †ù΋ëDNoiGª…Të�N›S¸þÿ¡c UHfcq­Ë$9=–îLo˜‰µkAž=Ø7€¡“t0^7¨l.‰’ј’ÿ¨÷¸}FdpÐ[ »ÐÜIŠ­°"•cæ“‘IŒÍ9Ñ6[“;{kðÂÛ¤ËK;pä!0ßu<ã�µŒöÍ™–¤H>4¨©£�ùöaÁêÒ,Àæí-cˆV•_IO{åõu…„�øÂ-¢Uʯ ž;Þr3¤ÌŒ“Z()ã8}dµ>ú +)8ã´6y=Œ9‰Š`Kq�ÍÄ. `nBiþǪÅÌ%aœóÛ-Š«‚û_+kEŒs>{þ¨^�ǃl,{kê”GryÕ¥Q©Uc�Ϊ®°“•Cc‹w2V³iUiúÓz>o�Án"OÁ°ÅB|TëwV¥’$fªCDÑ™ZƒK_œ›³¡¯F£c3œ§´ÿÚ´®O]@º#ÚW®¨M¬7�ƱÆQ;E`F×°ÿ¢M„ú®ÙÄ–n”ðÑ[¿bOϼ[sÀó|>[ì¹F\öƒ.äZ=ôT[Gš�XðHºxcg—cæu¼–4$2øâŒôÆçtx£‚lέ»f�²`Jb×êHtFÓõyœ!L†+K¢¢•×�>òŠTϪΞ“© 7oõ¦,×Ù‚"ì«�4'„鯷k÷¡+`ä› +n¿_ô¥}x; á²�ÚÈg(†ãÑ1!°%Z~�±ñÈW¬=dÒ E³ú‘É©xr…Áü@rƒ\¿¯^x­Cg„êÒ3`†lØ� ¦Ä*¼ù$”7jxõ¯ˆD½V­û|Eò{n%XÊ­ìç‚^JúØîÏ¥v +¤²œÍuj¤4z¬cA¬m,Q[ÞšÅ/Š=EêÖ9)…O^�ïr(.Kº +OÇRê§ ü ®¹+Y[®ŽÂí¶¢ãeyA×"W¶JtjØbAê¨_ç×õÄ èÓPÂÔS]44^¦]º×v,eeû5}'Ku‹X` ài\Èc~Mòàa`éÅ”�—Èw¬n¥r¶žh¯–™Žƒ)‡hT)¦¬ -B²À"W=êÍç zn±€+¼€“sYFa}Ÿ²mc:f”sÓˆ<û3Ù·Ú˜NjTB‰_!¾¢k¨üñÑ%B‰�ÀHñBéýi V–/fË[®°7s6¹/CZ¬ö%½ðÑš�Ü»½�c2]vƒˆÞ®|E63m…�:6.-�ÉFÂÖaðM_øœ U¦n Éq˜pz¢%¥…­­7Ä‚1Ïg¬õÞAÚ +¢g�P[ê¶É…ÐŽÞEß9:›G?œÓ•N¹R˜ÔJZgÓKNÄ\ë ÒD7ÏZi$éâp…8 Åœ'E¿WKL¢Õ:Oå-zR±5;3y§ƒßÝ·p÷€]W�â¯ÖE¡¤»ÆÏ[Óïa^Œ<7¤œ¥<¤=�M±�ê½nû!a+c1(à¤\­ˆ&möÉZ#‚3Ih´Š§jq­ÌóÜŸ)»†w«|–Úï90Ø{¯{dkk€º4ÅfqN>)–jq4ãç¡ðHã°Áù­­k¿œu+^nbNOˆ;6­�øë\ŽÁ¼—(4½ —1"÷Ñ·5¹¼_õZøˆ7ª5È\eƒ¹d Ò岌io„2I“c”9Ø5Êhaœtú°ÁdAµýl)“k°ÝÅÎJdÄ饽QT´¾sÅ¡že‹¼Ðwb[ñÓ8–é<ƒ1ý�‹"¥T›,ËÌ2TP°ÔÂ*&&KS*nUl?¹¨$•r§m[+hõC´ö€.·l=Éb\¾اæ©ë[É™7¤�†è‚™7v{Û„—ÆúÁüh’]ïúG%¬rüL‹Y1O!lUC° ^Hc0oéT;ÁWA–E žhƒS ±ÊòA m¢õ+V´& ¾¹ÄX¼(5ìv$>'ëú!Ïø 'ôÖοÉÕg]5o¬çeþ. )piv´2"ڑļcM�åí•IìC6³J4áUÖáͲÀ õù„ Ѧ�Z;¿ª‡ÔŸÞ�% ©V ÄÝf÷C0ÖË¿¡zƒóݪVhÈ È’OìAúã²ÈZ› ´gûO;ÒKv9Hœz¬¢:êí�tü¹<ù$­µ’³¸¸�aöè˜Ùïçw|:À…ªÓùuö\U´ë'¯¶˜Æå‚‹\@—Á”CN/¥b(éH»9»8˜˜—¡gNgVÁ GRÙC×<3&DÖ^$çœ]Ù¥÷L[ÈŠ»iöŒ¨r=?ޱ¼½R$c”�¡Ùi‚˜�¨1ðª8,_ :ˆqºÀf\¿©œáúmõ”í€Xí¬´aó(T0Èéû*'(3q@ í-ÖyÌ$WæÏ_é ÇÄÕLxjûñIÉë€,¸’»–þÎ�woÌ©«~�Þ>xÃ,ˆkåa � ¦ï®}ëuÓæÁÞ²†`9£€rFXkµÚSB™3 ý¨ÍU…\ŸÌJc€WÂF§×¬d‰û7¯ÓlyüçŸ"@øÁyá'kØßí{Š%�“"è¤Ýöt6‘Ö{ö²9v]\,¾-‰`µºÞÆ¡A÷íîmð”lȳ$æÖʽ,«\ÀHuõ2a2æ¬C]ÒJ‚í$¢[9™5÷° õnòž“ˆˆ¬pÊ7ŠÍq• ÀHÀ…™Ü©• éBA¼YX\ßuHŽ{gSÚ‰`rQòAu~uÜS°Qx%5°¨¶÷f¿ÖÚ�˜R±q/Ôñ f›™Å0f/�d»ÎŠÌ–ʽdg6’A›­ò%îlÖ)_L”µ“Hb]}ÊÎ~rƒ»ôÅÂùaÀdå5eE°*m·rÑÒpt^Yt£Uç§ïÆÔwÀÇßX�¾íünƒ©Ñ`Úu2>Œöb¶ q]ŒF¤de\±]W99­'à'ý7}Ä7ïgàd óðŽÓà´²/Òˆ`CÜ™}f>�=z§,”Ó]ÐŒÆ+5+s}�¸b�W@4RÞ$#|²ÉÝÑ­F1¹;Öl¿Ü–à²�oÏš ÔƒrétZþÝŸœiümp�¤ NV¨ô�h¼×ô8p{!Á#T’r_Ùû!Da’Tl¾<Î2÷\Ž!ì,ÂV1wâ8VYÚ)Œ–+à^�ü…±«¸ ¢tÆßq׿Á�ÁV{!CHv«YÚKB9ÃäÜl‰~ P{B7J÷Ç$=K·Ààcèܺb…“¥BîÆ�JädH#¤©;¢ÉšÅhfmÈ%ï¢0Ù굸´E ìiÀü +ýÖXRê8×±±!ÚáXFdo4oÛ·�ð¸µ�_’s6sQÝëëmÿ^ki9~ãò¸°rÊϬ΃¾@{«Óœ8¶t�"É4PZ£�&h©¯OGœD²Þ»îõ¤ 2øïx——ÄHkœ8á܈×P£ŠÒ pÁPGâs<Ç8•�ÿ¸:L.äÂ1Tÿ><¶ÄjJ“pph;%.WˆÍl‘h/…t1n/iN¹U+l÷‚·� ¢â/m÷i7Ú÷Ó¾.CÛëÔ2öÓšë��Ù{/\ò}ûÕNI gk‘ï¨ðMYíÌg@ï%¼†$¿z|úñá݇§7oî¿zzz÷á/?ü¯½ÿú—§§_~þ·ûùÛû§_ÿúÃýw¿üòôÃãýÛü÷ÿx÷ï?ß=ýøË±#ßuiMˆ�²ÿwNó¼) ðïë,Àÿb"zò endstream endobj -450 0 obj -<> +2197 0 obj +<> stream -xœ­ZK�7 ¾ï¯˜mÚ͈e½¥Aƒ ÍA‹¦EÑí©é!»hzŠÑ&þÿ(l�4"Ejd{/3°‡¢$>>R¤6¿ÿûqûüùæýëß òÅ‹Wo^_ý7¨ArX«Áka¼ÖÒ!haœWÃÃç̓¾røú°½zu7ÈáîËæ“”n¸û4�Þ¿î>ÿyûAJùáËá)¥=¾•9¼?Mÿ†ÕÚŒbtAš#¹”þðÞ1ûÿdÈ�‘ã³Ü‚íôA߯Âß: 4åÿ7 ·Èü?LÌBžLM“M“ÊD—~{ð{ —6‰r¦V�{Í{ƒÓ#‘yÍ*jâ2ñ6z¥ã-ÐmÚ¬Ì_æ¥Öì°òîÌãX‚ñ«u½Ö<°¡6ž,Èò@—~–AÞËj×Ôb83þëî§«·wW¿]½}ÿzSººj¹º‹Â¸Ñù³}ý¸ðû¼ c… Á·÷’‚lûÓodã…Y­3RôT¤v354Ôl[Èg­0ÞH{�Ï(¸ä£Œ “«µWB3Ú[RJIŠ-Y (ôƒ˜C&�á}Ažâ"KhÜ0C¹˜‹&(Ñ{&Cð˜ój¼^u².'ÃÝ•ûÁaç‹„ç#>¥¬Œ'Oˆ`){ª–l~(ö˜t�¼Ù?¾Ý?äå´κ~]ES§­/3ço8{ÉãcК‡’Ø‚å… R^rx@˜‘T@aã«JëkåÅè½sÇýyîC��çá”ÔãD¢æÆ{ AËW0%0ì-ÉÒZ•Ì3…'”,ÑpÆ -–Àü#¿Uc7–•õî1¤uÜ´'„©n2‡k�¬9ðü q\/J»Ü[•Û[nø¡ºønÿx¶hk¸øÆZÒ:§‚ÅV\‡ö;ê%c b¤&wAzÈš”RÂ9ÊØ•àÍ5&¤ØŠQ£¼€§D§–{ú@›t}œRÀܲŒÌÿEĪ=æš_.1›®eÅTYÎ2XÙVC³z4Wåu@Ôø “ÎF¸öT"ãŽ.ûq ª�µ”'Lnë÷åwÙ¸R«Äs²ª›X˜p-É9-Ž ¥îùrr«uäc&„ó[G8yrZH+Òð SNŠ ¿sÅ?T‘F¶B÷b˜?Z1FBÔhD•A�Ñu„䆫lÙõ¹êZ齈Wÿ{u„å™èA‰² -ÜfQ^çÂäsß#ÇF¥�Û« Ý—¯¨ùRd'�Ók9�Éì‘xoCrWƒªÕ¡òÁ -¥sB -µ“µ•btc t+ }n¤µˆð”øSÇ} -h¾ åƒ{Ö€÷|ˆDB!p€Ø2,ØhI–éôáL�C©ib¢¿�¦iéÎÖ‘kç, ÞxUNDCqdæ:éxÆV�Øò—®»•}¾?hª�¶rèlChc–_ Lt L¼&Zi.®u4›‰E›M ïŒöí6[IT׊+§Mp©ÊUÄ4Ý]Q8Ž Ì†Q$àËwí’ÖStƦ‹¬õúpn;�ükǘt|n^ÁP¬4J•D›ºÀn§ÊØF²bŸÏ”Ål¹ùøîûIöõ²ìZ}OÓÄ’æM³.À¡ ü!i°¦Ö ÃÙ5Ï7Ä~ƒp{ÔsMoGdC~ªc!×±@LŠO¨�È©æ*ˎNYÔÂŽëÕœXS=òÄêÔºžˆ)a�ÐsÒ¬Ôl]’îÊ<Š‘áp´9OàÃwb¸µ.šs!³qML÷óQÇ�þe/]DåÁ£uuÄ-̨äÙG’9ãÙ qQÄ0ú)[çD¨Î~çõõåöÍæÑêŽ�w]|›I%OÌWe˪¯¨à„wιËBˆu¾þ$‡«7Ä�È��›IË„¹Ì[â*àÙvêã«KÄúQI¬y`Ä[?áÀˆ†¶p®K6y7GYLgà�E°ÏÞdPù¸#ÀòÔºOâU¼ø6Üxí„’R¥M·oªAZ> ºCŠzÐ’eU·¶‘ž p6Ùh…uÏ©ó^3âsz@½¡YM�1æhRW’æå²[¤kTLIšPv›¦SÉü­×,ž$\\fíØã)MP7?Üþsû÷vUß–sJø�íp6½ØÅ �ÖA¨nÍÚ …wAö¬¥'tr {±ÌùëÜŸl ü%þf¯JÚKïЕ¹Á,2ÿê+b¹á~~uBP•û0Ð"*Ó§»Iî soÆì óÿQºsï +xœí}[—$·�æ»~EÊ^�TÖ4‹÷Ën¯ŽeYòÎŒí�÷›=ݽª±÷Ø%oOùœõ¿ßì ^@ fµ÷L?tUeF  ðáþëOxxûþéõëû7ýó÷÷¿|û×þòôÕW?ûù7ŸüŸ“8ñ?½'-™Áz}r�3o­0§÷ºÏOïÿãÄY°ÚqNÿñþñÄOâô§xÓÚ9a¤©~=?óýÉðÀ¤²òÄO<ÿá¼�ßÁ/Ÿç{Òg"Þ˜þPŠiΕ�(? FZ—Ÿ)”¾\—ÆÌ½?‰â¯?–£æ�yñØçß/7òûO>ù—O¾ýÕ7÷¿ùóÛÇׯïõÍ?üüÄöyŸµ>˜“ó’Ié¼>³O<³O¸üê­tÚD¦~ò³7'~zóáþ�Ÿ“§7ùÙñÇ›?ýö‹ßqÎÇ•ÁJþâüLJËWºüêwœ›òÛ˧ïÊϸ«®ä\U×>ÖÏS²¾êr×C5Êóg_ c.÷-O­^ärÿSñWÔT +‹Ü™ÞžC.èrTø•Wßï~aÈô‡rÐ…pøBàEéËxàŪoßÝýÛ›üäÛ7ˆ¼ +J^�d.8c^D^»ï^‰Úÿˆÿ}ÿ{ÿû,þ÷ñ¿o/€oç¿Yúï›ôÁGŠŸæQSËC€7”‹Ô(0»€#rùÜUÏy@—P|«%ž—3þÞË·OÕ2�ïà�…Œ-ÝÄ›¾tJJ:­bÊ:ù2ÂùT¼ÎåU•ªô¥Áõ%Ô9pJ6¦Ÿ+Š -üå9¿>M“ÄE®÷¼$ËõµX�ßýsp©«•2�±,¡àózí$‚°Í K{ýú`ó·ƒôd…½ ¶36Ë£/Ša£XP6¼�~íè€ó”¢L†›N¡‚Kݸ\²ˆ5GÅþŸy(„—¥ÕHβ‘'aB-\b451Z3í-'Æk%”“q6¤TF-â¯þüûu3sÑ\÷©3 uA0§¬V5qÏFø¶ÑÓq÷æã£ÚvT˸åHd�pö,ŸñíNœ¹ø“Û+%µRVŸõ4bŸ]®!\Hɸ . óKõùå‘a-ÓB›é2´ÈúbÒ.jX g³}Úgbh߯ +ƃ´~¦Ð‰ÖgÖ0-�ÖûÇ]ØÔ³é “¸µ‰…—ÌYkÄ LŸ ­©#¼c‚ 鯙ð¬7ÿû{âÅÛ½JrÎ7ÆßXœÅŸ7‚ìV“K)˜sAOÛVEKi™Z¸éÜzÖ�–`J«Ê¥ÒÌùhn kD×çJ«‰¥ +L¯ÍÔÙhU±ÔŠi£¤š>ãóCe)R¦Õ´Òræ¤õXÉVÑJk˜�Ž�®”¿ëJF”aÜY5¨Ø°Ÿ„S‰¨ZΙæ&Øš¢ ¢rÙ�ŸòZ-,x`Üö3lƒt´jTÅ´à2Z3ƒF”l©Ž9m¸´ã"Òx•ÄV"[­)gN%†Y úZS"ökÍk>0çIæ@$HI‚ˆI«=ãÞ+?•ˆñi$ÓR �.Ê㜠�UøªŠ#ÿ x§— áü#¦m0Ì9îg®(%(CnTœVmÕ*XÉ=s.ÚÏcoýEÿ�ãU(&xðv¦ÚTˆõ)ÓZ[u¯¯ö^”¡ ¿iÜ·”Ù7‘ûŽ2ûµÁƒç*ÈxÆhÔù_Ô ü]×ÌÍ?n ß~z¾’˜,Äjuй`�W}m«Ôy&”Pf渱@½dÚZ¡fЉF¢·ÌsîÝl“„¤ w}LB„u«N•�Ìó ýT¦µU Ç„Ö�µu¨ƒ[E“Ó‰¡øt«ˆU<Æ÷jT´^÷Yd(çFfµ ú¦Þ�¶”wS‘ô"î�v”{³›eÄÙ“þÍèÈ„9¦éà ‹É5Žá”‡3ÊÂÀ7‚rq†yp��c$åãÌä�¢œlmþMx9F^ÎÄue åæ ‹ÔªŸc,áç ¾wßÔ6Žrt&*Pã)OgœÝW»:&P®Î´ °œòu&N€”³ƒ©�ÙÞN™´æëXIù:Ãj¢¯u­¢œ�™kÊÛ™)(†rwÆîGú­¥Œaa™ÆÎ0†÷ËÖ‘–YEó‹XË–Ì0ØÍÓ "(;qt`bSwœ²±9ÊFzµ%ƒ—HATúÄLŽI*bxÝ]¿/;E%P s¢¯p�¦’(F& {‡aÁ´²ü€)ØAJ›ðrɒɨ7L¿�­Ÿ3ëJ�‡…y]¬ñ˜ªbñÉ„”núîðOñ¿/ –P9yâÉ$‚qmS¦Rä¤eBïzAYYµ�—”•5s +ƒ6&”±fîùË-©‡Ý¢Q諲¶ˆ(OñHPW;¦}³Nìlº2‚y)UgåëjUÅZ0¾'`Nµ¨jL: þÏwšo÷-$3<ˆ¹âÜ*w-lT¥±äp––=ÓõUŸ¨Võk)˜pÞÊ—#*P·Ìy£^ÂÃÚï�ÒÜ~¥ë�× ¨ín‚Ö—A@Ìxc™3.H0ðí$.(Ê©y)¢4åðÌœ"ÄÌ·�9+;3txü�(’½lÊ b¯©Ý\Æõ@lA@a\¾5R½²/PˆX×â‚ê9ô…I¨WryyáG´XñòdXª F åÏ0m=£u£—o?t2H:Õ°MMh5“?]ÙäC»É®˜QAŒl!ëÖrhwyÃóN5:pÿ€>xbÇ´Á1e ÿ¸b’�<-®iþ³UGvt­˜3}a?Ë·Ô â\w6㪚èe gmA1Löw•A~õ†àTaªõŽ©ÝM3W×DêÊn’ž)º#8A%ñXç™ú¶9<‚SI<Ië¾àŽ0{wsÝÃܦ-Æã¿}_ðÐ/•å5¥™‘Ï\+‡ñú^„@ê<¥Š @>¨—#J9 S§H'ÃZpO.rzn,ÓÆ9—¦Y1±°DÔýňBNel`Ú +?y†¨´ùùªA}‚H¤�¶p +z€«Ù7¿ÔêóçÇÂmÇÚjVUágQÛR«­gÞ +>W°� +YíÁø[eH­vŽÅ_’( +™ÉÚÀ¸Õâ£r«VW[øÕ%Ís´Fµ þi}UWQ­cú€F$”Ï6€á5ÚeMÌG+ÃÈz@Z4_0ܲ:>ðû(�Û„B�ŸÕã D&ûTêVJ§"åÝÚ[&9ûì<¤º[‡¸ ´ÐkÑ ¤�»ÐO&0«"ˆçÑ¡¤Ž[ÇLS¬9Ym˜v¢Á'ÇlW‰ü,ÆEP¡ëXBþ·øßw—;ÜZ¶¡@ª¿½dÜeõ:ýüä™è«uR·<û®•9Nî"CG ¬ôÜæ•WvtÒ’ÕsV_S|}|ðšPÇÖwÖQ‚2Y?;] AÓçˬ@„8µ*:H©»´‚É`��µm’¢ K2̺àõðì>¡k|*Ô�ô`»kVñb#ËZºë‰k|ù4ÙÕyœ´Z–‰.ïx‡[ —IÂEè›§îÑ#J%‰·çÕ¦¨±û*Óz¯ßÝVWCÚ±…V�ÖpjÑ’8å“{¦>`\½Ëúì©Ry�×èozÉI$P.ÍÉ*Ärv·ââ…8%ë Z7ˤ¬o->ùüÒ˜MÔÒ +âú}ãñŠ �x6By ¡†ù¼@º,•FGœ�ò`è—ä¸O œÂÞ½ê)–§Í—¤£ðeKq¥É®ªõ»¼Xµª‹ëè5S\_âôfÊC%Ïä?Pê…,cÐ2ïBŒ­�‰÷öƒŠÀÖ$VÞ2¡B<—=FŠ�x-?0‘\®ª[õ¦dÛ^Hi~À½Ë�wØ^„írèÎWKsô„ˆpDo+<3Ö‰~\Kœ<ó[*|%Ü�Wve™’&4 x‹AÑÜ\ïrµ@3©woñ¥Z²•”®~­Å‚®Td­V�¡ú&¥7*L&¨LñGÀ�_aà=‹ì ¤’ó”sÇœ°~¯W†Ñ}Âں\dî*ªçq�žXM×SPïePéµËïðÙLZƼG4`í¡•frí¥=lQ-�˜Û¶HcúÚ]ž]¨Xa¯ÿ„ÂbmÝ@ã™ Z{5NÈrPÂ[�˜Ïð¸´\/ÑSèOcŸb�]Ô€°wJÖ»8¹HfÝ ØÞ°{k2–kjn_Ý´ÇxÃ7F�5퉕æA(Î]{¤ÖL[snŠ¿|“oËÆÆ=Å_ž3cDЗ‡,Ÿ +&¸–²xpüìùÊ=VUH¢®0L”™­B>kñ�–D‹|ðF,=J0͵W¼}Ô��GiÜ +2L"$þe8ãNóñåú@Œ‹”ؘۘÏ�oqÄbE`�D´aµ2³�äÂZŸóó—E'•Zˆ.õq#sZ°}gÊð“ðš9#ÅäÁ~>0îm˜h >T!ú4(¦¥pp&f@u^]²/0,(Á™³Ü¹ÉˆÁDƃ1p‰�$(¨Â±°.ö&½1P¨@P¢ +;~7M¬0)ÊÆØ‘2r|`¦E xOÙ3�…N~5HºE`Ê;¥G¤bË2„å_ |´û’hà ¯`&70è¦ì ŽL8rSr Ɔ%r£x¦BÇ<·SpÚˆ•€ 7º¨¢éãê=… ?yø‘Æ�0s‹ÐZJyŒŽÌ%1²#”ÇÔWö„‚˜%$«Z+PÊc&;(¦¬>&´¸TÄF_˜Ú¾Ä¥2n0¬ºÒq€Ù}ÊŽ`3Ñ�1æ:ÁI*tºöL9;†bi_V4½¶¦@�²5¸›g[®#âÄ£SqWÕÈsæ„ób|`jËÀ�‹ò69:2¥3€¡b›_ëõéø4LÖ2ŸvÚnÃôDÊA1‹R lX (îJ*V6s ¡ÈD)V6K»œ û5A–*è˜JÈa~<ã"¡R'˜Ónx¨·µÔ®£< : ~Te`MV�ɇ�Ñ­Ïž§´ãn>nذ™B;ŽÜ·!%“]ˆÑ¦Ú "1Ù…–Ø:¥žj³ˆ�¾d#z�ýS$‰À¾”&ô(ëÉî³XƒÑÀœŠ§HØÊ»qßUFHÏ4çROæ ÖvT2wÄŒP�W1ìå·ÁÙÞ¸–øJv“¦*€@£ÎÙêìµÅÄ[L¾Kª(üº|[ŤîñÂ�d;n€Õ�K>z™)mÞJ>z™˜s TE¦WbòÈd¬Uf„™�²5Ï€’.KÞAßhh…P4JÑí%zƒ¶Ap[ +=7È­²ž …Y|Ë¥2�œVd/p–q/í8‹¨ÍO�iåÀ3ήÕõ€}�'Á1g¬öSY…À“H.B”U.2ÊÄ|^‘,„±1IW=ocþ +º¾&ˆÂšM[¦¥åb˜”nE0HŠ�lhà~¤UJZ£sƽ¹m U"ÐE µ$i†a]‚` ЀÝäÙÙ^VnYGäÑÙàÀ” „àCd·wô… sÁu(öì±q©íO’gcˆd—]¹T;ÿ„VQ,h:ƒI"p…1Q„<ƒ"st`b.‘jó"ÈSw#u[…Í>sÝ"ÕYRzÆCÄ­œª»‘ê¬ì$Œ LëHiVa¬sÁ¤ÔîÆÆ:RŸUëM—…£�\ ~õgÌð+mõ嘳Bß¾/ÿXÎýn²™`~À¼ð`õ ƒx@ÐnÃÞ^¾«º’·ƒgvK)aË£#fÐZÝV`� +¶,05M·¦|­¹àu- +/×¢´TSðU)À+æÒK¦¬&�ñæÌ¥¤æ²¢éeòÕ�:¾T¾°Ÿgl(¤X/•/ LíÆX±Ý’»;þÆ„í†ÕÚ¥àÇðÈ”í†ÔÚåàÇèÊèî”Ôù~ÑÙøäSœDÆìTyÇêìrÎìlU0Ü‘Sb{) i�s„E‡TëJÝIöô`ÒéHu^¡ÓK’^F¥#e|Y¥ïåØ '€ËFÇ¥:R�—úà¸D9šD +ð +}>80¥Î±¼¬Î‡×ÄZƒ°tɇ~µéP²ˆ`ã°2a®"ÕrEDqtdjKGêåŠ�"&³l¯„V·(xG‰ +…Ž>Ì6ÊCŠâŠíSkfµô3¶Ï�í©§Ë¾Øn’7H7ROWl�£#SÒ�ÔÛãè4Mvǰ¢¼ìŽMe›¡ü±jäIÈ1õqM‚¯,þˆrI®¤i© õ(#I5á(5<25…žŠEO9Pá豑 ‡F“Y&…¼mR¥Bêï +¦$éEÎàR¦—•{9¶ÁÎBjór0rp\j5"µyy—8ƒWHu^±ÿ Léq¤¯Øþ†×Äæ3øµ([êîM «£ö¤Q©¡˜è)dæ2 ”;2I}¬œ4+¬ÍZöTƸÑ?ÓUXµ^vS^Šdx¢d2Œpcþ©»2Tlx¢RÇêïrêÃØÀýCweH›r¢N7¤I9ªÓ·ºcÕvùðŒ}TW…ÔÓe`°™k)§Ëà_ã|?&ЊTÞ‡7I¬ÍY>ʰù‘j¹Âæ—†9/omô#ut…Ñ_Ñô2V?Rn—­þÝ<Û²‚¨„Ñ�©-)¸ËvÿèÀ”á�u=ˆÿèÈÔ.�”Œ–ÿøÊ¸6ý6£Íñ�r´Â.‚ÓXGµl‹Í\OXOµdqÍR4k61RªU˜cƒì ÌO¤R«°Ç^Œ†2Ö¦ +‡¥â?nà# %^EhpæàHkupdÂMp¤¹:sð¤½:¼lw�š¬ i0U5#eYÒ`pdÂ*õ¤U*,S&Ö>ßÔ*õ¤UZÑt»rmáàOê (ˆ3Í- RØë‹àŸI¼#féü#Yä–×'å]$ÁHb\ÏD—7Ÿµ„àø;™C “ éµœ8J¢.R!U`Á2a¥ÞAœ<óÔüò‹âš…x:ùìèU9'±„SúåšÚñͥŸ)À3“ªÙÓÒR_ß~¿ÐQ³·¹.Á1òÊv(irpÎÔ.ýà E¼¢áðSÒwIqÕèVÝK¤î¯X/Î2áƒØ/k ¶a™géY0J©°Æ6párˆœ þ²L&�£#£ªúÊÀ2÷øÀ§ZnEý Ûy«[b$i ­a”{xÁÒó³yiŒ!Ë"³ÅwPÐ +êIG(âùé u »†§= Ÿ T<çÉu9”œ[ÔòÎì—sü}ò<¾Ê3¥¹S_lžœ§¹ûU%„ùëaϰ¢”!(©í“C¦…ª®±…fF«˜CÀgÊ52nÃùåʯHs ×ú"‹”"k4SZ{WEöÀÙ)ß8&”ö��<J¥Ø>àצðhóÐ-“�”ÁbìœÒ1éŒÜÂbÚZ<cNauÓAD§ß¨q1€¼Ý¨ZdobVW#¸ÊQÚÀ…pÿ¯Ÿû¸>ÓŠZLZ1´Tû «é’[‚Š¡ö$ÍòíQò2CÛ¦¥^ûk/Cí×�w÷*›¸½qwi”ŸŠ”âr!-sž‹»@ �ñ>T2M«Ö§;o™r‚K¸ÆÊgå…¯†8ø>™jØyàºdˆ•“F°½KH÷µà’éèÄdâðž[­6Læfuþ‚-µ— �bÁ;.¯í¬Ë4±uá^W*“ôÖ†(ñBŽZ�° Èe®»nÊÓ�µ,!ã,mxÇ×�~YåyÜdU‚çlsr +#«¾�O`×ÀO�Ý•×xEφ7V]äv¡%’v¸ï¼?�²ûÖ0Oboˆp¶!€Î„ƒ}ªf䯫Óä"NY¸aÜEÀ¶½"ÞlŽ)éœPNÉ(F–.x;¢!€õ²H�êÔsÿ› ×¢õ  -šH‚Ô¼„\1]Á �=’ë}k‘©Þ^x¹íÝÓûJ„f—¶w2W¸ 5=!ØTTEÛ§ì^Ĭm°[.-C +Cùójy¶�±§MMùô…5÷+£>:q�š$´ËŠË»<•‹ÅW_’šî¼i‚GÇ�V†õy[³“`бwž ëų^ÅŽa^ p´ ¬ÒŠž2èW�~ÓA*œàJƒ‹ðÀå6yûÄFüÄŠXê΃*¥ bzÝ»@yÝ nÇ]›dÚyξ'±¤H_CkæE»¥Ÿx­˜J�¯þ´Œ0AÖßwî  ðêÊ H W¡»ñ"F é$ï4aÝëjó´È‚š=íðþÚ7ÆŽ1ð)(CE ©±©› c(‰Eú +‰U*fðÝE7IìÓv#a‹ì5L›}͵- &¨Þ"“F¨ó�`<î\C º<•§@ç>V¨6_R„´c¬p Z{u!ÏG§6Wì30á‹ËºëÐÊîNÁs˜ç;ÿKýaê�T§½C÷`>&e³äPTn)‚—úgRÌ¥Nk%7ÌhÄΕó%ªãZÝøJ�sŽyÐ]^Ñ?ÍWÂFLPתÓ¢1¦â`T¿~Ng«øPËð´€ÊŸ[l/HanÉ´6X°m�É@¦AXªp'¹¤N‹EÐÌåÕþ€A Ÿó¡L?\&—פ+¢ºT#™^0£¤ñh]�XÉ —²—7XmÇÅñM}Wíª5vj}5n£ @Šj´dA;7:œlvæúûÇÎ]däJš d`{pÛ?†¢$”rv…“ÌñCOŒû” �µÆ² E„ +X§dcŒ|WM�Äðå®L¯Ôح⌷ÉaNç¼X©óJZMÅ^:W÷uJseé¼aFÒÀ“ëXøCÏv]»ÿŠ5Òæ´O½B–©S>asÂH±W–{Ô54:e HñBüœ²NÞfQÔ1ÃÙIßÍï€\•1oÞ¿…A¥ŸŒ{I RgŽ/àFE]Ôç +”Ò.9M‚csA7º†�õtéT¸¹×œš$jÖ]¾þðô‡‡·ïŸ^¿¾ó×?ÿË·ýá/OÔò1‚ SË' ±Ý„(§'~§?Åû˜Ö.J²©~=?óýIjÅ‚P*œøé�ç°®¶Æº?ÿ~ù&ß–?ç›—¿B<Ĉ…k,?´L*¡EñØ`/×åqÓGïOç²Ëå9ŨéSžŸÉó˜ï?ùý'èúÖDQltMŒ°áãÌׯwjÍS‘},*‚F¾öÍîqáéüv+"ŸÛ#ýÆË…Ѳ'¸é à©ìô&ã,ñ{v¢�×8“&xÆkœÊ�"ˆ^ãQ"¿bl¢‹@ƒgØ–ÑÉ"à14‚ ž‘Y†ù’"�éA +Ó'§‘R˜xј*Ô†ÀИ¼Ã,ŒÒ[d©�P©ˆ|cÅMA74‚þ�A7jšflÏËI¸Æ*Grp‹zª‡ï~¦n»V‰Åœ”BŒLiF<ÄÄëÝb4ª/HñÜÐf˜1”ÂDÀÂsÓšÃ8sLÏhpãâCqIhpSW 2žÐàþ;bTRíJŬ±jšŠÛß&¥öUÓV¼Ö%N#(äY=T¯{$ê­Æ0ȹd*{C6ƒá\H)(ŽJcÍcTžõÕÍY†¥\¥øÁ>ø|”}¤B!Ñͧ1pÍ»#¡ÏçRõÏUøùÜI¢ÐϧKy�Ì“Ï^)y¶äLu@õ»B3) ‘ìù »4±žp‹k’o‡Ùw ö>ßÕ"Ic@N}>|¾æ_ê¯.�YjÕ–H>z±¹&¯I].JåõÈa ¿ó>ÍÓÎÛb»CžtUûB¹f.K¤q¾ˆ,"ð÷Ykšo Y…WjpxÒ¹§r½ê<áä W“üz»¼œ3)DˆrøÈUž. +!¦fJâ SÖ(û‘ —ì WEñíDë27U\ê€úøîÍ‚iÃ4Á° ^y<ŽÖsñnc|`5ªvK/û jÎ(”[oXP†lbFôÿª(¾�˜UBRm›É¹ªëÆ7×/6ÈI–Ý*™ a Êó_Á#ôËa >ó®ŠDºVdqs–®ÝG&m¶/m%Á/$l]‹¨ª ®o¶ Tõ½Ë9Œ¦›be¨ÛX¡õÇOê6fP/ìû!¥ò}u‡�þ 9 t#.§èªgñùqQLòüÉ�&bý?ÒIôIÿëþ‘›'ñq-D‚õIçÐ3i"$X[�t =s~4>›+ç4㲡f³�fCŠOS HÇ�¬x�cVkÁgx¥'¨Ôý4­�þk¤]HÊO¶ß8B#ÍBwÌk•˜ðLK¥í¼b K`Bz•hn˜rÚÙ /¾š§šaBT`ÚYSÕDÖÐÎõ†¬pÁ+ç:»¸ýïÀ1<}S"^@7‘Á;ˆ€©C‘€wëÐ7^Üÿòíã¿ñýã%ÅÜ1£-ѨC®U¨¥òôY¾Þ݉À|TÖMå0pë,óÚ[¢Œ¥ÃÛ#¬ˆo»áÆFF·á…W«¯„cRicžÓ�?E?%r�á…I†íFt�f$˜FÝ\°¿ &ç”7dÜòfÔkrÊáÍt½l÷®:èRϹX±FLÌû%Oc/€4[ØÒWÆDPÛ~ù¸ ®¾{¬5|VFDHTÕ÷#÷ô„¹ÿpTÔj‹ ¦ÀÇÕ©î–!ÒÕãR¯Àº‡Ž´Âˆ×.ì¸ÃÖ%q¡@Qöd_nbP"í¨ +éä�qaù4¯¤µÆ4¤Ç’R˜ßÉ ®8|d*i ™œ9h¹]…”†ôTRÁ1í•0p}¸-Ö)Ž«¹ÝBÁš�yÊõ�ж"0[N2­¬ñ7b+’»ë As[ªMRÝFj ÒÄÃɤµö&Ü5H·Ž¬¬tÌŠ‘‚p(¶Áé$UÎöêÏ'r±ô’ ,]‡&v¬º±ôÎpŸ¦srÓ«$ÛØm§àºÌÈšë/×mG:ͼ1ÜUív¤3Ls§LñàøÙó•yèüY츓ÿªZîä�yñ`^ Ýëºc�®;J¦¬4ò¤¤ŒÑ7-KX‘ˆ#"œ=#:Å_´;qæâOn¯�Ñøl >Þ }w +Ê…bÚDÝqŒŠÄ ŽI²Jr} lªD»ˆ/Aâ+Ç”b„+ùtºë™# t”Ìqãùh°ž¯ª‡8BÁœsG§òi¡£ gÊ™#DŠD[Yf9È[ègÚÁE:í¨sÏ +Ň¹ö‚eˆ�xÆs^á"䢙¨¾9WA +c£NVÏŠÉ_AU ¨Â²cÂ�´£¼ø{bT$Í�[æ¤SÃÃÐIi’£…`ÜJ¡&/$�LA›üÜCe€¯ìBH_œó)=~æBA’ÇÏP`ºQ.·\(tDÙ#’SDfBLÒìFŸa뽟Ȓßf·=PÄ!8Æg4¯ÜD‚ô¹ÑçÆ`Jè™:éY£}ˆe0a²‚ô¬ÑgH~iÝd¢©ùo•ª†ñ0n„ZihcÎ�ÖM~C%�t�1ç&l±câRð”·À5ãRz5×[@ÚÇÞÂ8 t­EÒ`¦°ÝwSv&Ìô'iSçN +ö“ÚL�~1ê š¨µ„Ý~3AÚÆDˆ6÷¹±©"ÖÒ&Vú8á]‡#‡yw?%¨BbÞ0nbåÁ\ªA8÷ô¢f„T +@É’£¼ŒÉ+p£ÚDZÇÄ̧L€4Þ¼]G DŠw0xS¹>Gzq¤còê _âe�¦RÇÀÜéÓ’·?b±KŒœNÝþ�¶+yû¦þBX›èOQHíGû)\Û‘–!)RtäÜl™#do9£H™Ñ‚ ¤÷¥Àß2wT [zÄ¥Ó=��pCIø8 e‚Kª§¦è±D0x?=«òìkòÈ©Ø#àžðŠ´ƒ÷f~íÜÂàº÷þ$Þ él�wr”û·è$RÁ(B·éùª_ƒ—”-€88»zW†ªþm¿¦$(%æb3á?ÆIéŸO%%tOoØzp—$=2y‰V/b ›û­‹ž¢DÏZ¦¸ kÿSô0žjJô*žÞ^ôêz⿇ÉhðÖv±LF7Å ê¼^˼ÒÏ5Ð�¾ …�6LäSÝ‹»õßÔ–ØÒ7DÍœà мfÄ!mC”‰ K:˜Ñaé>Ö:$™hÕÀÇû °ý/Ȥ˜FuMW}8°ä³�!†*º dÕ¾\ �FŠõK5“ó_¼ ÖÓ( ¼©ô')“”²›²µUƒ5CÈ„'åÚˆ6vŸ"Ú5a·�hcðð—ˆöàTm œ=;XGŽrÙŽÝ�\Ï©1³k§ùƒ€“æÆá[7Ã�–ÙX9Aæ@çÔ�â ŒU>‚lÚ—˜Ë�ý'ÁS¿»�µŠ§74µëÂ4jŸþþbGƒÓ'$¨´%Ð�áDg ”>âD«É¶†5œm‡qÈÓp,ýë]»Ø—¦rä{`DZ’qä/×, wÙ3œÀ#~Rv ‚˜«#h±µ¾Èííú¶P*cSKX&Þ­Š­2ceÆ’[µcxµç®´ÆàDe-¥²yl}R“Gd#ÞFÅYNå$¢L»Ý¦„œŒn3lª�è êíÉMF& 狎ú˜~R<%ƒÙOoŸ Ò§Ö4ø¢žkÖÜz… blßBÊx`N77³Á"�¼…)3NCƒÐ`û(Jè™›ŸÞÖ*²¨nNæßÏ’«ÈbP´"Ä#kG‡ígÚZ A6[1Õ°7·b,‚¾ZèŒ1žô­‹ kf+cÉ ¬‹À+f+fQCvŒÅõ +‰ jnb‹Å°Ô +7JCBTª"è0 ¨>5¹µBÃPÐR¦ä~¬)4 ,—c9ûÏŽxÒQ«¹½ŠCШJ749„ŠCÀ¨ +‡°ä…}"+¨º±£¥iM§Ê9¸w çbJÕ`âm„`u¦ë*ªbÛb`RI¿‹À™7!Vðÿ§›´]¶L¤¬jžÞ0Ѭj +–zèÕXd°uXÇí¦RØ.Ÿ.ãÄÜÅI£¸')‰ô‚iéÝd‹ƒUIÇ4çRåîã•“~’V¿‘ +Xö¿æz–…Åà3–"•zàÛ¡5X O#áÌeu¾z˜¼m1SiyZÌÞ:!å©î˜I Uñ¿CðzöÚ¦ê ÇiX yKžìÉ!±‚Ç~2×,~ {#å{5C”¥�¡p,ùWŒQI Úù¦™Q„ò›™˜›ELަÔ E·V tŽâ™é;+ƒ<#\$ Ôc ¸Ô£î"qD7^“sc1lë˜ZŒKµaðÉ5B¹v‹Íƒ÷p–q¯¬œË Û#Ág ¯º:hYJ”``pÁ1g¢¯0w~¢[A0ªU݆KÆc*Â4º fÏÖ3ÅBêØ&öC7ÖOFo³XHa‡ ÓPÇ-Ï +[PÔ¢Xo?5kæ’e�œì'e† @!Ù ©è:<Þâ?¢hóDÎÏà\›) rÉùA9rø^Ú$ d™à†.’}«1nÑ0’ ÛPŽÝbEÀEòIÂ\v(â0ax­_¿�" "Zz¦¹~òüÐû(‚+RÄg¶k#ÅÀFòFjÊùÉ)‚"Rl¤£4ÔmL`TD^w‚€‰ÞõnR×vY #US K¯y u#m££Só€�×’{±‘ª.†A¹îÊFµV#¼}ê#AèÈ.G®÷ ëÁÜ(Ô$ÆÒ[á�ƒŽú[˜LŽ]@€6Çb).P…­B+æxlN4UcHY�Ò°%°Œ�~HÏܹÂy?kŠÃóPŠqe•9”ùk?)Ö�Ê”F|T—¦nš:–£ÐŒcSGëd ï"Åq«��÷hvWÞZ Q"›Ñ7�9Š6E©dŒ¥·¨¹ƒÝ.ëª;xN¾R|�vÎ…K ·]â)°(!5óÚzûQ@ôY7Aô°†h }ÈþB'@£ Hq”ÆßOÊÚNƒ"äøÜ‘ó°e>q4YÓ2+õ¬ù¶·Ð‰wÀ ²+0ƒ¡W0ØS‘#”Á_éN�ÝR!QTˆT©©U6"@©j"¤Nc¨KT‰v¡ƒcÈJŒ"Ÿþ?Nr£xCô—¨Ys+Iû +lÈu¡2<ª_ÛE¦`þ +jhå͜⠵MËŒ1D›ÍC6> š"m|Ã$¤L…L0 ŠäYí&a ÄßzjŸ–ŒXyj£fy§wm´V”®ÅЖ€Ú[ã�7Èð-3'C +H']ÓQµ$jˆåÏωA�’­H<þAÎ- �Ïyn£:�€\…7Ul ¥Äæäpó´ÓÌX¨[ô1€¥ÅÏT:¢Xc–\×A¥ YBñ‡ÊVšÊ *!i\7]‘�è°JÿÔñf"RdŸÞ¼øfé�zõÔ‚æ î_©á©,#ï˜�ÆŠ�Øë ^�t}µT}BÐmÛ=ü�§`Û†§tÉsYZ?ÿ}lûŽâ…ë[ S™á c\¾¯†6sXÑwªäDßWqXYwJ$™9Hqv·�Kc MNl©ÐÖÚ0§ùdÄê¢ gÜ™Ñ÷ÿ Þ‚fƒî§lOå‚!l q90{LÚ f R7mgΊ‘é[_®ŽØ–�c6õ7º-#Öy[~µ¼2_¼hЬÐ?hÝMÕ†u�7§z™qv¤ªkâÕ±ZàeožúêTšç±’�âñ±3Oå•ä9¾Øa®äo ©}Í›åT^Øîf9̉ú4†´Ò�b×�7õõ}w~ÿ‹}p ªC +QsÜ™ŒPT¸`�§rA|¾a5 §†[¦¥ n.$a}T¿h€ËaÕ¦É\±žIk¸0›ö +VКì•ñw»B)!õ©Ù±ßOÉ9´DþÛ8v(&¬òt±Y沂 +( ꥋRãD®Ÿó8¬59ÛSÙ‚ .µÓ‡ Hòta䙬5�ÌeŠì[ˆJr¾¦�§CÛÏ/Ûú\–hb_ù‘×§sߨ¥ÐÃRºçdcŸš}*¸`<óJ»�²qâ†Ý«;M»uýn/k2a•ªi3ß? +6M›ùèÈÄÄzÙ/–¹/,‰˸¼‹g¹¬‹;‹*¦Z¾þðô‡‡·ïŸ^¿¾ÿúééíûßÿ¿~{ÿ³žž~øÓ¿Ýÿæ/ïžþúçïï¿ûᇧï?Ü¿‰¿ÿóÛÿÃãÛ§?üðØ¼… †i›ñæ¸â9!{ qòÌ÷ØWáöþ?’zê endstream endobj -451 0 obj +2198 0 obj +<> +stream +xœå}Û–·‘à;¿¢dÙ¦Ú6Ѹ_4�uójgä=ãw_,?�µ¥=RKÛÓ>{ô÷{P• $�ȬD¡Øöð�Õ]�"#�@\o?~xüîîõÛÇ—/o_ýüÓ7·_¾þùÇ¿=~ôÑ'Ÿ}úìÿÄ�øá…8hÉŒÖëƒ œyk…9¼ýáö-?¼ýÏgÁjÇM8üçÛû?ˆÃñ9¦µsÂHSüxœóíAjÅ‚P*øáûƒÔšik¬;ðãÏÓ_òcùKq|xþM +ϤSrš#é¥Ñn1¯á40/œ¾z{ù—ï‹eçoùbÒÓϧ'Ÿ}ûìîÙŸž}þÇOo¿úéõýË—·üô¿và5-ãê„@/™”Îë#âž}òêÀ¯nïøÁ1yxu—ŸŽ¯~øó_sοæÒΟ7yõ¯Ï>…@ *ŒfŽ)\?�§õ•˜àP§O®ÚðÈ +'×^úpŽÐ˜›Wÿ_SÕkZ¦¼â×"‚®ð†q#�ƒˆÓ>òZ å¤=p&¥22hôÇŸÍHù})¦†)0å�ÒaZã›øùÞô¥>~ÞOÒË!_sá¦ß'ìsSŒ6ëŒh©­é$sÁ3”+µ5»!˜‘«ä3jÃõÄvÙ ÖÊ 57ræ„ó¢wÍ# ZË +NíÌbáëíL!¨­9 +¨­|sÅ-*$µG­bÊ:9öôTÔí†`˜<áâ‘€£>A¬cN(#÷ƒ±²%…!ŽêË!,µI—�LÛ!ò¿p6n‡ãQWc.~r{˜¿*ÉS²óth=ÌìLÀ_ z! SÁeF ò ÔÖ²^(Ǹ ΢¨ÕF.‚?z+�>WИò3Á»�*Ï\ßT��½Bh>³üš¶œ(äYže–~ósó÷“¸ºÛ ´%´ŒbAÙ0�Øïú“Pý9ÅlKÌ\‹Ù%Ñgæ8±ÖKÀºØïà°;�ú-Ê4ó 7ÅÁ +W�Ùšº; е´f†[;ô<”’:{!à3fr“J¢ŠMHˆ\©¨#c/œ+¦Ô”ÞÖµ(©ÄÊú Š9)…(—t‘{?þ÷K‚äUKÆ�UcÙ–¼jõB0mè‡ò\"� žP­ö‚³Æ��¸Ö]ƒмk-!p«�ç?Ÿ.'J×âVpÉT¶eëûYÕBVpËœì^ö×í5k�)„`ÜŠž²á]5©÷òeqh—çï†óVÕ‚UhÁ4×Fõ"åƒ6Jjq)´aN†’¹¦θÓüBèX@1³~¦+ä®â,ã^Z;�µØ>Þ+�–#©¡eÙkæŒþûÂ,?Ÿ–kDÇœ±Ú}ýZJ.BšÞuoÚ«*JS‘†YmÂ`W�¦T•nÒ½×-·Ý´'1Ig3ínØVMšúV¥yÐQú|/)æóG"$ȑنºA©Ôˆ|”šq}•Cñ„ˆH陿\^d× ê5!± §ŽÏ�h0‚:?ûÑPØæã3«¦ø=Y)N({M7¢øDYNØwC´~°Úú` žqc½L �Ú0IÊ64Š0u³EÍó÷[hJïìCÅoÚx0”Ò‰q…‹_’:äŽâsÚAú ‹=”Y c‘+¨\´þS:øÑ0¥7+wCGÙà˜2†ÿ×ÔÁ�"dV‰š+ßÅ@xY‰l0ÆˆÌæ”xU2ÞÃòÛ“�/Çž¾¤€H¥î|Ö;¤vc÷¹!ôí^J]’”x¶À¶ŒásmÅþ‚Èß@OX0 H®dêaÜìÆù²™‡â‡ÊOè^Y+c©ÒùôíÞ³Ó�[%*TÀr¨sXâÐ�ó5—ÄeÍ–Nú@7ªZéÜÐñL¡NQ‰‘ Œd6/NÃ~Ñ¿[‰CòŠ¥’L+m/°±(n!Cé„dÚËý?/P¡—BUõb7ìì HnJ/-Ö}JÖ$Kwé#ïDµë�lÜlc@Ðu ’©»01ŒD†¤4®Þ W�(l’ð¯¤KßÍGîH !6«w¾‡OObñ¿Žÿ]Æ·Ôûêùþ–ºV�Ü/„JŽ%êæ[õHlxêRݽ_@~Ä,IA‹|� Šcå׈#$Iwá×èÅÑ„ƒ"ÄwSyi$sw¡[pŸáb%éût $ß6Õ Û êUIf]8M– ?%]ÉM¥ú {ŽÉͺ‚¾kèHéB·èd&R8J·ÈL/&žq©Aü�´b8ÒŠ1I¤£›y‹”)PÃ0ý9ËŒ%“æ£w ¦°lÒ|ôŽÜ\„"âÉ ½‘Ø ã7zù†¨Lþ®í6XÈÌM³R\¢"ÿSù¤PA½ûQ±P—žcd¢7;åàÜ Ûö¦ü›ÅÂ×Ü잸 +ÅF ⥻YàYT„Éú5Õ¶…ry(©È2pɃ̢ùÒHaK‹³öìú¢ɤ”Ê2gƒòï¡È‘£\‹~AUI¥/s ¯™â�LOå“By¸,QqE9ÚÕÝÔÙ EåOíg‹¢Úr#g~›­“L Š¡ÐN�éG�Þê’“H‚g¶9åZÒ™)‘üÏ|÷¹‚Îh8*‘4Îì»È)IÂÌ®Ë^LåË@ìÿ&öÑÔ:+†:!/‚¨pœ{ +”KÕ8Íœ½D]Œ§òIa‚²G-1q½£PbY¡óQ¸“6[ø4P'aO<Ö[8Û™¨}TLÍ@\`)¡ùR×»?æãsZä6};ìpIÍ ‘ø¢Âeºñ…;Œ«D0x›šÿJá ¹HfÄIͼóžJ·ñLÐçÛøß«äøùUú”Fã$˜H§Ë4"l€R<óÄÞ¼y¡gJp +tLŒ +ü!þgñg1щY"ɨByf� r¾Hµé>ß‚xätpŽY¼|˜Ç‘þ!õõQ"¹gAø¸1;ñ ´Ó7t“�Só n +Ä•í- :ês½¤áНò.’ìºZ1ïµsï·çñ;–+ ³6¢rý•h¤N-€ÕT áFhæ‚T>ÿú °æ7�5ý²@ç,aà, %ðT-k×�U; ˜çƒ5�yc¾T÷AßH?óT fPÇ'‹?¯KNo‹¬?OUVDT34G6�ïL0†¤x])Æ•Vvïn»ƒN"-�7/D`^yo]S^™ìÚŒë# ¸æÈYáÈýï�€jÂ^´|OÏÂ6 Þ�õYrè´CÒ´M$À’ž™…�(�g¢#‰ +‚½ÅÞR2áUØÍÞ\¥m—ðñ)à-™gÆã$�&ÕªÄPùdsÕ™`…‰2÷yþßOMB®©‰°·¡D<2`—J0·Ê/sñGQ‘Ôæ¹a2Hצ¢<â2¯O%7)@�:ñž¹ uô�v2²˜TÇò€Ÿì?<~w÷úíãË—·¯~þé›Û/_ÿüãß é˜s²8àYˆ‘6&Dü ?Äç˜ÖÎ #Mñãqη© B©pà‡ïRk¦­±îÀ�?OÉ�å/Åñáù7é5“Þ(7M’¿õÊY³˜Xz3�ÌKçïÞÄâ·ï‹¥Ó×|1ñéçéágß>»CILYÐtL(­G5ŠÏ¢qVGˆdB‰õkŒk¼ ^Ø,€”[°Š)ç�¶ã‘„Ç«dÓ1…²@„<EÖ×=%Œ (»T€Ü¸ï:ù°ÑÚí[üXý†db‹LÙ4¤㾕ÈK‰•oHfÖ±ØÐDöñ»±T @YÿA1’!\ cQgÛ>ÐÎ…M¬J•ØÑ^0Çõ�˜Š¤Hï…)k®¡]G~?ÇõÛq.)Ù�*Éw®J³R±!Eý— _|Ó~�4HX-"&5Uó},ªˆnŽÃPµ&ï‘R¹‚Ò`æ=�ìV¥ñ±0Ìc¶Èb¬*CŠ�ï¥r»2©Dê.,d¢“L:å¯,‘𠙨“Ü ‘Š Y*îaU*ý5:W¥·:Rl!KÅbá'"±Z Y*E•$¤â(T­IE¬Y}–Šc 8P*"ÅR±“Ê„TDj3,¤¢•{#ÊS¨°”P쩬LwU’6Ž{áY�H†…!¡kÕ•]OÕ›,±¿–ÔI4"hƒ•_H2j –° Y ÂÒšlÄÊ/ä*Cѡڙªƒy¦�¹¹Qúó’%Í}Ø"D®%E®QÑ,!¯+r-)r»A‰~в9#µ²b¸’5a‹ÔaȾkÑ•mCZ‹— ?Y‹µKÏšè@4a]Ò“�„¥5Y‹ÔTX#‘¡H!?–iö [¬ªB¶^÷á‹¶Ž¶Ñéç¥ÑšŠB)m»aÅë±Rì+I,ªøØ~×İ#J�u®º²§B»Ée¹ðS�ÃXÍ„…‰(¬jBÒóF!jMcURI’±è *ª�fœý²Ø“ƆN„ÂØ“ÂXifŒ4WÆžÆý0I*óø@æ(Ýp7`k2©J°ý}Ë®l¦@Hÿbá§ „±:Yÿ‰'¬ÁBü�ÂÔšÆ:6§£a(>%þ3Î~!¨î½#„p …°4LH)¯lò ¤î… V¾.óãÞœC3Gˆ¨Ý`®‰d$�?k§}«®ì¬@]ø‹•Ÿ€HVXž’@ñ¤°Dþ,�FáiE +$›¡œŇjGÏ æšÝòXam–Sqª^tµå±â¤<Ž9TA#ä±â¤<*¿#ɼÜJ‰¦éè(¡µö!­°Ôûd£ì[ue³J-V~ +BKÌÏRi$¢°ÔüY*�BÓšŒÆº4g«èPl(Êù5˜möKi$ï>×WèE!¥É¶Íš[&­°×�S‚”ÒÝ0áq×'™<'•eéîê4ÓTvµòŸÂRæ“ú¶ûmÖä6Ú§9én}Ë®ìÀ@IÂbå§ ¸±Íù¾:QXny6WŽBÔšèÆÚ2g}q(>¨æ£g¿èF’,sω^„¢[R¢[˼êÊ¢[R¢»¦zHÁàÚ�¿ûAX“·Hf_Šïè\ueׄvŒG¹ði[ÕbJ5Ó !}Éî3UXÞ]ºBu®ÜÎZRXÒÝ|…û¾TLq÷n|XÈM¢”€Â²ì’&Òûþ„|Ä’ã’2ïíÊ­ý2°êå§ËJ˜›Œ dcä‘4¡jƒ�¥ Uü§eÑB"g¡ŸAMUH$Ÿn¡GxÇ4ÿ{*}Ia˜Šmë}SPXi®O¥V*�*,Go;AZ'9´Ö·*¹ ±¹dœéfµr â-NÉŒ…õ0ÎBb5\;—z$1<Ñ ÷BÄ(«ß�CŠ@îˆ$�.îì[÷†XU…¢G¾­$ú½/×½¢ë4,ÓŠ«’ãóඪ¸X‹âTÑz˜Ü sÜ”s,8î®{ÿ'sÜzAzlw[z¯¶ÐnÒ¹�¸Å�p/¼k¶$._»]Ùí”K­Xøâ[K·iÇΦš¥L¶#h)‹í(®ÉKKlÇRI.ÓPO¥‘(R*ÂxÚIJBt")r Ñi=3šÇbg—>ç‘9ÙrZ›Þe_«R%"”̺àŠÈmtàÑáXþè­túLQÿÃÇÿ¯=iïË ~[®Ž×t:M;¯5Ûqʰ½ûíÅÚ’S¸¤+gRýnèú¯8]ñŤZ”­]`4Mu“Cq~‡ÇÜL£È¾ præ�¿ÂO‹Ælâ��Þl!¾§ˆ¯8ó"ùNˆ_åjÍ߃f5Ûð [Ú”T\R8ðþ„/·—¯‹•„€x™goDƒÁ©A�¼©Ü9Å �â)˜òe²Î6œÈ»|ƒLêÉÖ=Å#ŠÝ*$�w�!X0Sùûw\T!y¿¹þhé…õW$¹wq=Œ¢ýÕGÙp}(Â4‘5„!lxíQ +K†¸VŽD’¥®•ƒ¸jíV„µUÏÙDCÑáÛñ^Ã÷XwMQ…µZÏ—ë�xÃÒóåºkaÂ9�dþ.N .™‘þºUK”'¥t/H¥:%7p’ œ#Lö‚³f#Cry"ÿ2tù õÒ¦�U,?íb÷°‰åß9WA +ccMwu:ü8ùßN,8ÓEˆß‹’&R�/:J.r³xêðéBÊŠ¸ +ä31kÇš¾;>±�%ïæ —R”¾—)´• )Þ°ß|îtâLQ8S”áÁ>Êyą̀㿈>~¼¶‰s�ö§¥ü…M9Ä{qÓî!¬°ŽÜÉ'Þ·.aFòu³2 ƒb<¸p]eIÍÍÊ@?HÀœÁ‰&.*!®»!YÓ�\ݤ|ô-J +4�$Ýfý£Xø]{IÍMêÉP´È¶ +2 +++gžÆÒr“†2º}áÆ�q1ñC&CèC�ÈhKNMöÏ–^1k�ÑW�š“¢³&±·ÿ‹Frj²e7`+¢T#µÙ˜Õ¹,½Q�äØ,­Š…'¿|M¤ÝŽÅ%•Z; ™k2˜L»½Üþ§Ë¡MŠ|'MÚ$�¤Ðf5¾—Ú±ë�bø{×%D=’ϺõN3©œ¼²¨G²]¢¾¦ •Ã×Hnl„»Á\üHŠkVÚúV¥e–áš5³bå'� +¬±Ô×Ùx2OT©¯axZ“éXbkJ ‹Ó>[GsM+t‡âK<�ˆ"$±$%±1L9k¯[òL“.K˜Þ±^#™³Ùž±{Øki™lƒQ”› ·ƒäÉ.. +C1#‰óÃÌ¥ÌÈ)² ›Ø &å€1 +‡T Ýnfe�åÛγw]¡ˆu u1é[™Pn°dÛ|óʬŽð”ÛÇGÀ(2xê0-€÷ùh,Cw6ù¥#–x›áclGœ�Fvs¢p¿hi{e4–@s&š//¥püæä�&Þ_ÉrO™Ã‘´[©$ÓªR�.Íáµh–Ê2gƒ‚t{'Þ $ïVjôò£·~-Â¥Ì9e!+]Òd„¤Þ.wmXÐvH{ºÅIÈÍJkòåYFÍ,ÕžTi‘´ÞìªÙ�𠆵¹Ìþ˜Þ•)} ks™Tú—&Î ´™ålHéfï³#Ø4Ö…2«¦�Ï«�©Õ ¥'Þ:)щ4�#y» öP.¥*ÑŒeRªÍ„ð¡ËÌÍ*hÚ±}K¤MÙ—�˶ó5’F»8þ”eF˜pÝ8�dÑ.ìV»aZõâ ° \HǸ7×Mâ×HrìK�ÆçÁüâMÒŒìD)…cÖŒpÈ!i¶)¹·oUj/!6`ÉŒ8¾ëñræYÞÝ3±nP�’ÐPoXºûœh3ÌáŽ$< +É™ÑFÛ o*žyÂ.zóBüB~€$ß­ÑHžÐšqå½|Z” ^ë…³^Õ�ä9‘¯Bö/€ÙÞÞH_Üj:Åb“i´‚læ +ì!OK>}ŠU +#y{Ò:¤µ—ØkçëµH>ŸŒÕfTT´÷SkÞM¡ƒäã-„Ž·,Hé;¸DÞ¼¨è_¦ÃT�ÇDêzÍ8·Ö,÷pý}Öʪs¥ù„.ùeyWñ×™Ló=`;xñö%?#ó¥Dè"Ã6Ù¥JL•u + è&8 ¾ÁîšÖl½=”¡Õj)õ™ó·±Il M±fÔ©�4b7kfªe�ÝC3 Ù÷¦7šö¶P É6+c‰ +Æ0ýRÇ·µ ÌKÀY €3-Vm8p'Ýá4n 0`®ŒY¦ígº—Ãò +ÍEà[GàV„.š±p €!XÔP,j “RîçPX6!ÑΔ¯$Ìã� Œ¥Œ¢¶n9h‹&|”Oþ{ÊÝùÕÇÁxhï·ù…€Ï?ÕYÀgüoË·KôÝñòd ,VQM ެdš.8£¨¬¸ÐÚþ“ZŠI�fÊ ·ŸI—Û¾:Œ‚cÆ;o!Ê`×ÒÁë‡=|ÀÒÒ Ÿ&ã\ô( YX–�7ÎH¸†CÙ¬R)Ÿõ³¼l½v‰à•×/c%òÍ +SI…jà\ôùË $Ï’wamÎØý7°s,zHã˜ÐÁÈ  ¬^V…޾ å¦2O/ +U8C…oŽ<÷ZŒÕž„¹U.›‹Ö�,Q«$;¹R±pMäMã)÷†Sù_È©9`žl:ºgÂm[=–¯ßPÀ¡`fB'.ÄÅ^Xý†¼çUÌub7Õ*Ä–¢ñ÷ –› Yþ//v�ý­ÛNU *¡4º#T’”u)/±À˜0Ì Å÷^ÞKœR‚ f×H¬UÜò„©×Aî#pDªÈ�Ÿà»AD,8åÚõ¡haáJw!`ÍË%|ZòÅÓˆ­,©Àëg&RÜCÆÐså„ÜÀ «6­Fѳ¥‘x2 +óã#O½‘¦Œb»ù{Õ(†T Yì+®™>ìW™I{äÃêfìb¾‡’Ðè™ +­•íì�VîëåðûÖ,�ãªVÈÁƒeq̼ÏÁ°Ä]87…êÄ�“½×XâþªµJ¨ª•ªö–u[ÏàÃ@ð¡WL³àv2â­�-R +ÕGà"Æ6móíAÕŸÁ�}·~ßx ‡Pf^²tŒTSlòð””ducÐÞë» °ÕDi+ÔGÎúüvÍ”¯ -hRj³6Rd*³¶“LHÍýnÖÎÛX$Zw±¤åJ¦NKÏ•åd×/“H+˜^„ ïµÑƒ_4®n}÷­Îa Cr–× ®õnX™+Ç´ãÎô A>´Ò¢‚4í¦u· V.+ðÉÅÝK�TëF3á£�ðô5rÏà†)+t @èl_¨v6x�½G¦V4]“©ð�IÖ½¡DËÇ�ßݽ~ûøòåí«ŸúæöË×?ÿø·Ç +·JJf¼´îàgÞZ1ßá>ýêÀ_}ú?b-sø}øãñ¡ü ¤cÆnß;M·@ô[~øþøÜ÷Ͼ}öÕnà¼dA¡W�+Þ´`Í‚æöË×÷ýà›û›Z"óè’çM4–KuÐ,Pò¶·“N(«[ ó3Às¶´r=)¢"$ßÅBšÇ"²ÒÙ�TŠ1ëVJnެ´•JEÔa`Áë(Ÿ®´`~C¡Ù‘¹ÖØÐzÆ•ÐAïYOÉ0¯çLÜRbåý$“F·÷ýûlËzñý\`Êqî;v™mï2=¡Â\¼¶:Õ¿Ç ö…ÍKö Ò(Ô1>k­`Rz¥ýxr+ÍÎçÉšhŽ�R2a¹ºÕ +É–£~@ë�*¦ÇõR—[,O­çqtkŬQF_€º›Ö»y=E^Í”×ñìã�éµ>˜À9Èæc2}»�œÎ1e´ÒW°‹ëš›5Ô ±Žs×±ePWg¨ð½ çÔɘ*RV:¦”ôûßl©ˆÉÃY!8`Ù“]o"( +¦¥W'<óÒ‡'ô"…î`˜Üèƒ5šy®’[˜P3F«íÊC±žfò¸/¬1LkñÒXúÏá‡ÿˆ“0«œ‹²õðÃA0ÇyÌÀ9&Ý|wÉ?/¿<]i|öí³OÎ|�à™ÁÉ]ï±€eù>Gá“„¢´²Þ–/ÀÁ["Sl}‘š“k;ˆR� ¸?Xm˜’œ G1$ —åN +Û{ Pšm�ÂSR)R a󱬙·ÜžÁëV2)Œ Ûï£ÅzŠo¸ü;gvm-³Fü£ð:’§!$ó.xóÄxýÃ^w’ù ÅÕxÝ f…5ï¼n”fÖK÷ÂëHâ‡ç,¡ž¯ÿ²ƒ×m´7èÕá³mSz6F©ã�rb;+˜ÕÞw\ ³ÎEc‡ðLfÛÒ6¶Ï$ÎtÿaÉèG¾Iƒ,4Yeñ)vÉøô"±‚‰Úù“�9CRˆmæÆ`ÞÁõXÐQ`’­N[æ”f7×'ÛFïåiÁ™7&Ø­<ók�æÖö3µµŽIî¸úûçj!™P1a÷ï™±-ÁØÖ;&…Pö1v i¥x`Ž[y°J1»z¶½€ŽY*XAxácý§›±L§Rjr¿ÍŽÃ#Pü¹ Õ]D”“ÌÆæûeÂ"ü”£>0 ‘Å\‰9C¼˜áy›ƒ…WA°Ë–ÃUL-^G—î[Ë”�¦$Œ¯Â|ë)MíZ®%x¼Šš†ÁÈ8M>„¡Š ·¼AšYdö—Š«å~ö/ÓZ@š/È™N�Ù)`P3£ ävg¦,£üŽ¿½lÌ |•eX[Þz–‰˜0M±FòÙÈ Ià†VêÆ§ Â 샄É9Á´5a�}Ö"n. ´ #]MŒäL*í.�¬�к’PÅ€\;3',”®Gv¤©IÞÌB3”Þ¿™a³û"p_ò|JŠA_5�éÖ5�‰xÊÎýYi&³¨™xœÄy’8s0fùíKXJ|2³C­b-M¾1þ°|y +ßIÅâ m¬Ž<^R +fü”(†1J«Aa醓–+æMØ­|U±¯’kf…w³C¢U=0`”zžEJWü˜Ã±”… +¢2˜³ÁÐe¶‡\G2bF䜹`¼^Çòæ¡ HdĤ4 *;¹� ÿ"«¬9ˆ€+YÆ£'ßvcø¸óÞ?Š@ +äÂåtôík3ysشٺ¶FqŠÍ»]ômÑl�>II4™ ˜‘fˆfÒçpã"”î7ËY~—¾9¬”Þ6HŸ%åe´Ù(¿þRëLà¸9à´ÚaÕˆfe8ríBØZ{‘ûF¤“¯Ì]æ$T0uãø¨Ó[ ú Út¤�r²ƒéŽðš™�é/¥½‹í.‡0EU±­&ÝJ �M¤}•‘>zŽB'2Éš¸éCeTìÜ)µëFè2¨DÛ™• ÒµJiÃlpÎLLq +Bì•p äwU i)ÆR`ˆY“_HK+åC¬B"7€¼Æª¸Ì*�º  ÄÓm·È³‚ñ1ʢׯV)ð.ص äR% ÒßKqÇbÜ™:«¢Àâûmªj)½œ‚©ÖT ÌÒ1�u¦õÄVùL n×ò9àÈûÖ ûÓíÀLU&È™ç¾Ùœ�½oyŒeNáÍ'àž1”ý>]ë+óWIí(î +noª¢[i¸šRCp¬£v‘‘,xÛQùÄ¢¶‰†º5ý&R1×ãO‡¤íÐIÊ\cµOj•E9að6ú�Â%–5n˜4Rô¢Ô¶Ýf]ú§øß¿Í¢ôøù>¢÷EfðFöÂÿrƒ@G:Ú-XQYÆQûZQüýåáþ>b¼”:šH»ax±Ù u€$¥F¢c¹qÀ­¬Uò>t÷•ÊPã¯CÍfùYi. ”wð*BáWRD–š¢UÍcïïq1JË8wÊE~Ê�j=@¦EîÓdžâ-8ÚÕláÈyħò9--’Q×5±p†c3¦š«í4]¶W>Ç.ßüì*—éÒ¸`i¡˜DöÈj±t¯A0K—©Õ—zxƒH—!ÌyÄ  p>cÒ‡�&ˆ¦C¬²HúÝ(¢} +¶nAA^ V´¡©_1-·®+–NÒ½çÀ„­Í×%ýw7;ýwÕ*®1ù-Á%zå �e£z?0kYi«:€'ŽbX꺫½e2öáÛ­¨Ë3ª~òé�UBíÒJܺÂòF†ˆ©VØZš´u:ƒ^ª±Ð/6y!×êkÖð߯`n›‘±~”ÇpãÅ7 :®Vt#ØÓRìébl8éõ_aÏæ9¶`Sš;Õë%^ ¹"Çž<Þ¹ ¯¦ÉR€1¥³iÂEºðÆ„?Uä^”"¨Cë„h©¯‹ÊÅrr—ÀŠlcé´«¼aÁKïû&,êHË\4s^{Ù¿°&FŠ­rÍ„ôúoÜn‹m�&¹ZO¼±Œ�ô¸�éöÞ{!p~e³ƒ;ƒðBñCÖ¤‡£/áÉ[¶C*¦ÇCsë²–¸yËÅé¼�ùV3ß«DŸÂt£b~©µ®O_hM‡*Õ�R¥K¤š£ÉDp­2ž«e¶i­rŽBŸ…¶� +©(gÞPáW¨^ZAªûAÊKp-©©Åœ–»¯ë�Ða)£¾ ÔÒ�¯š&‰r0!}àÈ�QtK³\«Q-»`£»ÕBÕÈë&,ðw`sÛ2ÇE2ð¡ÕlúýÄ—³¶º®4Ñ»¹‡Á„”ÛEëc.ªl3:½NvJ&e°Bo.ï Ÿh3j5÷y2>ߨep•ÊðÕ|׳¤fëéûÕq�_  ®Äp¶mIÀî€O.õ—*ɸ6Ôƒ—ý²e3¬¬ç(ËW“–£s¢@¥álSR¼(^–"Fíì7PP÷F(Ï>û+iÔ6”5^u4Yì…î#c}ȶiÐ$‡å4ÈýÚXf¹·zúõÖ9‚3k¹ª*#kªÿìÈWͲvJšŒß‚o#çÃÇ)Ž\�Ö7ˆÉÁzæ¼4¶›“@yâ Ò‡"¡¬°GÒÔojK†Q’ çÍÖ*w~ƒÝ�—b(• +ŠYiCwñw©NÍQ'‰›´ä•Ó>wެîÈ^3UO½زË9`äZÖŠÙ˜®N‚ :�¾Œ»æ,gé²ÕÓE[ÃD°,eŠQþÎíŽï ÜÁ1Ï6”- ú±šwÊ÷�çàEƒâ\Â2UÕ¢/×i0Ýô{Ù˜Ÿ!_¶öÀ‚ª6rƒõ°1ÓòF"í�&ƒ’Ž÷[úc$Ûr%7”]H9[´‹Ž;9G4rpe¹ßF–3¾…¡ÜëáÞÕ® :.éœB`Ú2•ºLo?+%8I¬Ðª\Œ5S€ K ôÛ«Õš $|¶üê£�i¨Á¾è.ìPýèj\üF9«Xr�‡Tµ¿C;®„z°‘é ­qb«›)B(ˆS¥¸dg+lµ4,g¯œ�¥sqþýf;»Õk\HÀ’|CÙ°dÌ ³³�î‡í"‹˜!„`Ò'×Wý:¸‘zÜ3\Ðû±RºfæR’3«UHBI4ÿRÞÇZÍKPN§•T-0àƒp¦›'2ðÀnZ›‚‘-r}›3Ò²Àµµ½äCè‚×DLÈÔ}IúØÛa‡›mm¿;Ní÷µUŸØ~ÂJQ᥊Ѻ~?Y–‚ Ú’ãÆó!D¼MmäŒ]” ½,XS¢Å”›[«QïA™'ced!BØC}2ŽáüªYÞfÊíí§ýލâÚ@èsvA‚'K¶ÆíÔ: + ¸Â§· ¨º_§9’Tb3Ê ¿Nó�Ö‘2« (8'*|�—ä:‡Ên’3é©wC²O”Œ1DºŸ�›d>32×rê‚e-3VÄÓ�…ÝðØÔêúœÃj8ÓA)<•:GÑõ0èpkOv8Ѽ˨1˜¯P>ô!í[Íá7èRuìx5à³( +CGò0 :¦1Ì7¹�±–*`ÕŒ…â_Av=31’d�ÍuË”,]©XªáñoÇþι)ßéË?ÄÿŽ¥:Žÿ5l¡?<~w÷úíãË—·?>¾~ûí7ÿñçÛO~||üñ‡¿Ü~õ·7�?ÿôÍí~üññ›‡ÛWñçý×ïî_?~÷ã}… Ó*æ,›` q~¥æ#þmiñþÿÇšõž +endstream +endobj +2199 0 obj +<> +stream +xœÝ}Û’d·qà;¿¢hÊK6µƒÁý¢˜¥=¼ÌÊZÓa-çÍôÃÌ,Û–CjzÇ­°ý÷T×9Hd&pªZ +¿tÍTá’H$€¼çËþõÝëW/¿ÿæo¾=ɯ¾úúÛo>ùÿ'u’'yz¡NV §’�ö¢AiiOþðòƒ<}ø·“<ýÛ‡‡O¾~{’§·_ÞËSñôö~ë�?Þþá¾øQJùùÏ�R†§OêÿKsùôçÏÇ»Ê)¡’ æÜýG)ÝÓ§¶T uCËÎXÔZU#¼¿Œ£«ñ¤¤ ø¸ô¾{¡ãÓW÷õ2ÍÓçãÝ?¾ýÍ'ß½ýä·Ÿ|÷ý7/Ë=PÍ(/… F…�=P§(² õÝÛÁçÕȼNX›BœÞ{Õ€ÛKbÈ´�Æ(´ñvAO€þeAË6¯¤¹�¬ZÈ`!7Ú-ÿwôl³m•ˆ!©ù“ö£4r/ὑlE¿ëaº¬ÛIY­›Ù9ÇÝ/Áˆè•7‡W½RÐÏrüU!cørÚÛÍE¢‹ãýPSÊëf ÈèrIs·"w½I@çb¦—EëŽÚ ^ÛÅYï9Ì6�…j€{j᪾6/ó~Ä›?£ €ܤ˦Çp32õ™z-¢Î'S‡nÄòCµÜ&ˆd‚ó Ãë›.­ Ù.WœÁa2ä°õ¸ OÎÿ€Ž{éÖ݈€¼IFDmÓÈFðWýŠøÎmoʳ¿Q£¡¡ŽíÝ.ƒÊZ7O>)Ôôó°Q¾��9µ #¬”îˆ^y=[ ò}…ÞË&h­ªåNµÂÍŽåöfÖø­)x9�4”Š»<\~ã�½þÝ0Nõ[pf}C�w. +c‚Öð|£Çw�Îí|aêYîÑ÷a{¯!Lõlà÷_1{�ð´.é‚Õý½ø3áUËîj%E�R™Y‚zu!†€!öÄaÇèØ—ûî៾øéá¹f¼0ÆÚ�³¡{g¾ó¾¼a–Õè;ó´‚O—f™E´¼«A(óí3y®!r†Q˲*V ©�: +Õ*,X'dòÉÙƒR2èß•’‘)/'å¡`•©%Þë¯ð;’×<ü5?&�”À%>À¼ÈPSPkÑ´o4 ÍÎè�#’ìh‰¯icáú°Ò486R¨ }¸Ö“ùx>ûÛ?Wä}�+ð�A‘Êò|3=5�óê ÔØ[’z?QI²ZñŒcÒ”-ëuW+¸Ü Gó/M`È%r䢌ðRk3©¤Ûº^›ÄGa}´±{�š–Ôu´l¹#zî“î™þ$Ú t-5V¼p-˜—ª§zŒ‡.ê.´ºÑ¦.VÈáÖ ù$ÒÎôð9CŒˆà½PÊšÈã2Èë�|÷û·?ýÇã«7ß½y#åëo¿:�ء֧ÅÝ�•¹ßC(tYm¾Ô"8�”ÏΈÉÎçÿ—Ûáy¼3N¤d’ëž�¦á€Îë僦�ÁŽåv¯ÚBÉø|¿d_X +¶õ„ ¶Î1 •=À'sŸ^›ß #½Õ kNpöI‰àB˜ïk¥dÉHVjÝ…£!ï»Ç�}£‰H¤Ê‰$uŒn`•óŽQ´y±šh)BH6�ÌÜ“`6|Õo倖O·Æe­°Æ¤+ ÄÒ(iE:e“0J^ !˜ÎE*�HßnþEï°ž}•xÁ5†®™PÀâq<›æÄT¬ˆÞ{FðOB“˜þ¼VÌF†×­¤á²ÁÚhfÁøQš…i¬1\ë97UÐòÕœúå÷O+f<_Oü›¥n²ÒÊûú¬$žûŠ�?÷åú¯rßòr.·è€âJsŒº÷F¨¤íQõÛ‹üçeþóvÃ×FÜg0¿[� cË¿E%œÑ.€ØÕê)”1ZQ¯põ»_QaTœ¦eò”ôÂË$ý4’—ýš@jZ{ºq®´1qƒIµžÞ¨ö„Û4$¡ ë7Lûæke…“AÆ+lѦ è阫K`Ñ¡1×¥A4ÏYO™üŸàt¬ò#èÕlNýXqûb¹ûÉiaìŒFc¡5B‡hœÜ%í;è ºïùæ�QÖçÇ‘,q!–À•Ôê Ux:.š ž +Ž€/ +ñŒ€Ön{´L +>§¯WÂ2Ñy›¾¾‹ON0¿¥¦YqÀСct >‡Á°Œ›UÂØ�üœ�¡aMWËûBoÊf^<*MºÖPrpӳѴ-*Á~ó‘í€�Ö+1røf6ÜšPûÙ4Ý.êk†¤lj`Dr&ª�mëÚókï=äX?öYqƒ9ID!“”i‚°Î~KJ|Q×)-l’ù)¾!^–Ÿ>öÝ,âæ`½ðÚ?äì<$4  +õÇHÚû‘»¡Ë×@Qfåo�V+†¬I�a–ª~|$ÉÊî–4RgâÁ[Îú £…wÞÛÀ¹¥ÀVœ¿hIš¸S_3Ç>F¦íÌdÌþbš¢(’7É÷Ý=¾'ýôýÂ_ú». iUzzú¿Ü-õý(u ~úUþó?Á ”¢zPmýüpÎjç>Ò™ù˜#ÝÂDOÏ2* ãR8Ì„ß#þ̼U/Õöà,åžïÀé@TÀwÜ©�QSïw`:GöØÑ XÌyÒ‹¨dŒ˜½¡÷“bä~)o§+�Ã=b‚2ýÌçÖ:ƒ°3z`=K $cæ¨y±±‚H�Ýxµ£تo‚rš¡`—mAÉéI1²jJj¡‚�Nq¼ Y݉ò ²Dó&Ô`y^Z�º#Àh›PÆã�ÙÅ�«¸Žíªò=Á'ÐdƒZýÕb’¦DIj¥pNIÕ§œ®D¾ë9¡r¸˜¾8Þý²¸3›_3˰Œätøô'Ç„¹(E6Í~íÉpP›Ð$ðuÖÍ4š¤ÓvjԾȨȵh[ß'”Ök;_3úæãÅ:Q-ça=0(Fà�WB>‰ aÅè~Å_Õ£» ºz‚yeJ‡]¨.¦\ɹk€“c\�B¦ã¦’®eó‰°¼õ"–Ok¶ÉêÉ›.­a &©NÔX3yå^@Ë_ÕH…(–üÖoŸ®ö‡¾| Â¥P4[±j=X‘û‡^è‡ÜaÔ¶ÑÔ͹h;—„’r&ªi̳r wWå)‰´ìG8êÛL\:˜µ<à•ú¬ôãßÉŽÎÀ!Ò·ŽÂ:ãÀ>w¸àβ2eÅUü9oÓæ4‘ÜCZ«i +�=çlë¬&úýlàz“”GDï�Mô:…þ¼Í(¾•–\æ½Nnz±’YlËk;/|ÐI_kW|j÷âËÝ· +ìO)íÑ9º: Ð}.@øX4Ÿ;€Œƒ¼��,?¼Œ«ÿÐüN½žs$qF “ôá ¼Ò²LÛ¹_œ¡•MãSj-÷=ÑÁl1n©ñˆo­²ÂKåR'Ç,5‹©�ªÓŒ1Ú·Ì»rFxeí°ý‹ßx 4ÈÖ_Ciºh™}Kƒ©¸\V)µ­ üé(`À ÄØ„Õ¨W*C CÚgšµ´ñâ5ƒI8r6Oº¾"ÃÈØòg_s·¶ÃuupÂLß•f¤QFš–9C¤�AD›Ó]Źi`h#ŸReDÒºöÃ/Oqý; NÒäi¿¹P@‹AS{yg¡ki,³»!Aî6Öö=¬ÛÞ: Ûbý¯}K¨uÓ{³Ä ‹}�ó„´Á‹”¼�Èð™Ëûg›„ÖÉ«QƒoÓãh*�v$Èã V~Ø£^_Û¢Ž²^ßñ $ĸ”±¥EÖ¤¢Œ]ç[ã<\>ôç0œS^Ÿ; œÂÆú{‘&ò�’Ñ ]ÏZ-µp2š~¶Å¦åXꄦ_·ïÐö¡‹VGçùÊnºÎ9ïðÛKôµ® +Š:çUËÆ iÉó.Ë_˜E!º¡ä…±Ñެ¥Ï‚7;rugj†íÀÔ4~Ïî¶(N³c�Ú§ÃY|@>®,Ú!$ˆ”Ôó@ï(ÎG¡‰Yª‡l­é¬Ë]Àٛʦ†€ÜRWœ�÷Üüœ3òFK8*ëõ’"EFÒï¤lVË$ã 8§¶±Ö +œ:leIL ž±åžÆirñ†A– ÒG`†mÆ ŠÊùÍ"næ?~XË‘Pù(…3ùþžÝÙqdÕ:M²×È‚ñ„fR`̱ìÂåéi²K×¹*t}Êï‘lÛ./~C¾Í\18døAxY�„vI®ÁøDVÔQN™âQ­ÅšëÀ¡’Ûã3\÷¹±ò‰õø”‡�G,¦K +¤@#Íl‰LÈà)0Ä„é“:¥0½Á÷à™ÚGÖ'6Fs\¶ ëˆ�²Inu\Õò²½Dý°j+8ã%N’Öj'Õªjz·Ö¯Îø�õ ¶o„’æ©=ðïÛ)»“rMWsD%G¾Ü¡˜_o³D¼ðA¨ß‘€«ÚQ”&VNWfB®‰æªO6ä� ©#p«DßèðZ74¢�Íæß±…ƒ0/\‚‚²ˆ™a9O”ÁxíB� ³+7ïüúÞ3ú±Ä2žÎ #ýaÿ3ìHg�0i¤:z¢×Û „¿Ap6ʈ•–š†eŠ˜1¿ ¶Ç‡©ï‹J;פ2h€’q�@¨†€ÄB Û5Ú7(®e¸¦{I´«‚n L©2.ÈääÖΛ Àoír¼6é4‡ÊhÕÈ››´x z.µñÐ;ââBÆ)�pmGQ•�€¾ñ5d�J Ö·ñÕ×Ê4h%Ä�5¶úzÅX |mÃ;‹h¨–¸.‹¥qΞaL®}á§}Á¨ó)Ökó¹" Bx½7•\Á„—§q« 8×¶ƒÒ­áU/ÕUñ£c|=Ì"`b +áK@ÁÐäKŽ´ñm†Ý!­Ò«=ò qs~˜Fkᕊf`’v•QBú\Q†*¸rZ-ïÞMª$V"'� n`¡S‘ZkL[õ±9/‚×af':!®JbÖ%”×ÁÜ/�#XÁÜ’¤°Z[5OŸ !‘f=ØŽKúZ/öÚ.‹¶ç ^Mó¡{c@ݦ$çªd”Á«Ãf@ÊYç¤iNõƒc»-IÅ-ìÉá÷Žó$²Dïk£ªÎ?r‘²ÍcÙGÚ~»³‘gŠÕÀH)’š,ÿ‰yDRN]œ?¢åëÖ�.kýŠøµI#©aWFØ £x$Æ#5o%«VwíÔ«§–HŽÇ°·UÃ�:¦q­$\š·ªˆrB£H PÆ�2§þÑI +éåá´×; °bL˜ž�bÏù¬7­à…4Gh +©�^ 0a¬?jÌ�˜UˆƒEцŒ.(쉶•˜œºE×{]!Õ£V�f‘W‹Wa¹| ¸Â&Ū²ãÖ�°ô& +é¼Ô·@ý@( ìaÉ’§Ø¦�4Ôs…àb3•F$)ÝüÞîw.Ø}˜ª ÂY3½£¯€Î•£ùþ­ æ +HÜoˆWHáè5uù,��l%bíµ0NÛ C|Iõ8Y•&¤æ@eêœõ!½®�b´Ñgßëôü¨b¢}R…Ti'œÓÑ<¦�¡FЧÛ{S—ä#,:È„¼'.³cÞ»gB ¦rB†kÐN�]”½†5á,ÍÐ�¿&µOÈ®�J«+àB ×’ã` ø[dL×ÀRÿ¥Òxq¡¢þs»˜‘”ú)Œ)óɳÈz¼³J¤\ò¡V@@ÑrpÐ,Q�3õЫCªS/¤S¸8‘7´ŽÙèÊ´0�0wû +„ ”vÒ €=X&ª¢õžy ¤&Úp œ®j«…ÕÎ\r‡Óžã›Æö U°%™Í¨öÁz–Žõ¤�´YK£í-È5—¤6u~ � +ªi߈²°E¥Š!òÐKb(‹ i:gµ—aÆ¢‹k<ªìHhê»* )ƒ‰«•�I‰†‡…7§¿Ç*Wï§„ÒQCð�¾©ÐQ-×tòM FßÝ´Ä•uEâl5÷l]¢nò Ä0,ûïútËåsȾÑÞWƒs–äÇ;­²,-Ìc†GÈ ¸ý�ùR)?‘¶¾ö ÌÁ†thZÝ�w«�÷•,`É€É?ñTæ1Gîué¡—~B›\®ö»X½•8ðý‰1Bjåü4•ŽQ +–&æa�¬Çï{ 5H‹C©¢Q†éÚ92³)C Cô~Ÿ‘´´;Õ#P.A`ž>Ë{ >íÍ‚vߪè( FsùD ?ô +¿-n�…Y "ÏqY­£º9äïô…ÇP3«ë“A$#�sFÀ}¸xcøZHÎá�4j$ä¶¥°Úáá ù4ÀvDƘ摂ý ¡÷àbˆJK`u£Fé%­¡·eªe·µ¿nnà EsöV•‚�!¥é ê:‹ÉBÑàß±‹©}Ošã_îÛfä¦V™±îìjHaæaQ +cÐ×Èßñ9…bxÌBÑ$ð(c+®î•òK2‡xŸ®›¼ÜT@òº¼_ýu§…DJA»å£ð.ÎxàoŽ Ùíú«6ÁÙM»VÈ-¸úyuKø46ÜÕóatD<±|Ó‹Pãߘ¦ç¯€$°^tµHÃ`‚DE*{YY6Œ¾¼N¥€ÅìT¡À1ƒæJÕ½«ÙãÊeHÇUê� +ð/T®_fµAñÚßÁ¥ +.ìNü2ê'Ú:í³PÞwFÇÔ'{à@ÚCVqp< Ëå}Oé A{òdµJ±=[I‡’1¤Ìéj”õ"%�òdFÓ¡ŸÕPéFùV�)€øs³Ñ 5ïBÝâ ½ø �9¤H 8TƲ_ÕaRKÊÔ*Ø\�m¯ý‚™å”>9‚/i«›t´Î�t9�§NÄ3¥U�Sü¢÷!#�ðÅI3^È(ý‡¤óv %‚‹†cšÊ»½n¾ƒÏ(;þfýÓ�yfªn0—q‘N—ós#žóu\A C£à`WÖ 2¤ÏiM²_�ŒÞï×y>ñŒ å'&™PÚ+¤®„N%eâÊ*!)—TH9$c¥´Âz­õúˆÄB)ˆ /åÐȇ´l´çÖý�„!õ¸CÒɬЧÛmµ»õHmP}‘Ad×êv¦àæ¯!ÇEêæ²lÚx=ã\¿¡¾âº_Ž1üÆ åtvÝÖH¬íË0°+u!ºb΋žýp9êZ†yýáñ�ï~ÿö§ÿx|õæ»7o¤|ýíWç!‡8 »ó;ðý7t¬è,�ÐV«‰\ÿ…šŠt_y¼ËÖG#mTc™d·âStB«è¤XN×ráT`øùÿ¯9ÀßH™ÎGëš7k ~Jà• `Ãå…‚Fòõ¼4#Ïè4šÙGD²¡ã·%h4”zÂx¸Þ C#\²µ¤…I*Lh±0;îEƒn"¯ýgK '|‡YJ–_?©¾1ÒÞþ]P»'1»í+Ȳnõïx¯N´$³6$/½VyM2Ìî×Õ1>C®é�Ù¤•XVÈ$WH q•²{¹Žnzß6>ÛŸ&WØc2óØÕAÉt<­©.vLÍà‘cw½>7ýâS:P@…´’­tNwCÕ�âêÙ÷8¦ûÀw]×�ùsu1¨ÞùÖçâŠG‚[:iⓈֹÙ}ƒanÕvìå¼ë¾Ð‚ÙÄA6îHeÿ¾#R^\[#¤³:MS¨Ò*ÇmîŒË%¯†ƒe� +àktÞô!z‘¢ÞeUvác2ŒÍìÈ‹ý75Ò@4Íš/Œºå2¢V&•`¾ÖZÖ!3f>¦0�4P7$@üضY®Öú‚¾»®¬0°NE5Bãv½ÚÆ-ð†4íÕ ¹ªE›ïD_óŠUV^X£¯°�{bæ�â¼úœ©G;=�ðÑDjBØðSjŸ _Z¡ænd#ÚçÜÈ\ÆU;s +ÑŠè\¼V­ŠåH5š«„Ï›85>ðÍÇ2×�)èÌu á*‰“ÀVŒ•ö±x]GÖZ$_&š£É"q‡:úÞHœhCNû^·½Öˆê»´õC�­™+¤tÅêk}>HT8êŠ ½7–�¦­¸L9 e©ØýŠtžº*˜"´ÍÈud’¡Úítv¯{ãù� ˜Òw€0íË߬¼Í~Ä�¯(†’-GÉÞålš<¼+DyÞ +üמøuš ¬Dp†õøŽµ•@ÔH,�<æ]\OXT\m•›Ä ‰_ã.$íBÐTÈ=s*¾à8ʱ^¸hÝ5\F�HÎ9Ë;·õ|`»ž‰¶oNhqÐ{�š:ó‘™í]ÏÏ*t=yk­¥ÀcuãÿV‡”3ã–O¾?°%Ð�5«}Buo×zØê–xRHQã‚Ò�.I;‘Ë‡Ö +ÕùG¶¬ °­Ll¬Ï|³€u îW.6®ÓêT_rµö±8h><˜ºŽÞËG�q½,å6lÂ=> essá +-ÀÍ¡Éh„ì.5FQÅPTäÈ_[lN 6�–baèœÖ+¥;ñ@M¶KÐoËç„— +96RlŽ…;;µW&)Õ>Óà¯"hµ‹7n²3LˆB±Žá÷càAd9™Ä¸ôB*M+ôII4H¨t¸@çEJ,•su§'æ}:X3ZE UÁH\ QEÓF”÷wKŒ÷ªþèMù~sÜÆ‡aNœö©½m›�)Mó‡‹3/Qv›O ¢µÒV(®ÈÇÊsz¹KV|C#T}ð +Sÿ\v ìY[Nš[;§ +ñ!kgJÂÕ¿¥ÓÀƒ&Àdü”ñ|¢À{yƒã·�‚ÌVJaSV‘w×?P¯Ï ï³[>)¥À`É S³0wès×.Õ�‚‘ ›¶;Z•ª¹F†Ø • ºöÄ‘JÖE{/\ +ê¸J„À$‰›ñ}�A¨°l Xy“Úùª‰.ìÆ,®˜•G=_¥‡iÀ‡œAo˜å¾…ñ?¾Ó­§¨r0´Ix°-`ÜéÊmÔ:ñ‚¯¤+C¥œPê�~"ÃÂ@qƒ~f¯\ÒPEI ìo•õ”Ü…µBæ¯ü‹.¾ÇH3ü|6Üú@ðRúÛ:+bÊ/Cg‡ºciioßB¼ÐM¾zè­Ìç¤Þ7K\Ê«W¾~›­áóç¯â3Á,–ãÿ½µÂ˘¿á“*‘3ÁøFšì×Y?àqŠ”7QŠ\ÝÈ ¢C³e9M +„¯OVx/]œß€Qη )¦�Y;—OMcãOÃÚÞøÚ@ª};„ÑóçèÔöÔ碛VŸš3�ycr0à„5ê¯ÈM¸Œýx—¼0Ú­š»â¬Wf²Æ|P�ÛŒ£ý�kÍñC%è�__%“‰»�4QãU¡ÛCe�Dy0`2YXÕéõB~"�¸À\f*ý%\Çù\ýz}ÄA©_k. äíQZ]½|rº9§Œ-�ðÙêtüÞ +±Š‘ O,c}á¨t–Ø2°ëQ?‘m‹6w?ÁÞ7Õ„ú\.ƒ#\é¸6ƒÂÇ 4€,>ú3üúWÛT@[ +<J›�h€glŸ$7Ý`€L­¶×Ô^Ñ OË"`†ÐYYHfWTãÇ>W¾'`ƒ`P(¸+q¢K<=EºÛÆÅúèÀjõ0ðzûÔë��¿»÷áñÕ«—oÿó_zù·ïþóç?>2[ã’Ì•5–�ù懓<ýðÍß�¤°§?ÙÓ÷ç.8ÉœfBH§’:ýþ“vN§µˆòìŠü,Ó…œ zû<ÓYe„2J>ÓtÎiœKÏ„K„·ËÖ9¹br>m�0ʘt’Só…(Ì–:çæÓy—³û\óe¯ü×ÚY7ŸÏ*a¼}¶étÒÉgÃf¶ožkºœFký\˳I %£ÒÏ5_H9Ùé³mŸÍîBZ#Tn>ŸsBåÔ¤Ï5ŸÕ"ºÕø·sº’ÑÒRҙ،’"é3�F%LÌ1x‡óï˾ÞNKÅ£�ˆQ»+ÀÒXÕÜ<´Ô lQŠ(£ŸGT×eáî9H ©U^h¥¤¹Æ–Ö}+�Yj€¥Úú.•ƒ�6‹#ɧÜ éF¢>‰ìß²ß tž÷ 饥cˆÑ'!ƒžp-Y…"ÀØ�xÙjé9Z<Œ‘'ØÞœø‚›>p69ýq’ÇýJÑü÷ÛJÛœŽ×J �Õg“©S©‘Âï;/’Ôª€=�÷CÁ‰¡`ç³eÁ‡ãÕ)Köb0Lù<�T¼.¨ö0 +ªý’›]qD;9ûC¢E*lDk­p1êýª˜3x¿ i)ˆ½Ñ¬µÂZë�=A¦¬­jJIÄk!ÍUÄ¡ÉrD}MQ'nvÇõäìÿ‰ÚsDm´Ðá@Ï3t_24š6Z(#ílR�•”¡Ë›âÐ9â=ŠŽ‚vÁMž8Ú�›¼ tÜT· HHáë‚K[£”Á±õ³³¥ Žu¶¡2"î$ŒïÂ`Xç<©s)Mw‡ÁFh2DVƒ�Tœ,Èå(rF¬:àF.s³¤¤þ^A-. +Óáô]±©»·Q�‹B[Ç\¬£ªÛ õTÞÌkÖ»åsåS8p9žú*ÈbÉ+X¶‘ÏäôGé‡cb­õBwØÕ7Üì /ksu“ckç2n,E$�罩# �»Ï"Æq°sÌîU0ǽ\X£�–&g?JKœ÷¤Õ^(V›ÿÕª˜)/…LJ¯9²†+$ÞˆŸ0lA{ ‚–u¨e“H¿æ‹¢kU3�þÕG!jCé 4ñq+<­°Ñ;@ƒ¼Åj©à®s‹é6¬×ï±\«Ì’�D«Ÿ×qB_Ü£I& )YS0å„öru’˜/O|÷"ǵKï"ªKÁ¢9‘) ·Ÿ¦d av…s!6;Ê<õ óÙ'7¥”üª0‡÷$t0°x%Vn5·àDT.?޳›MmLYGføi4÷ lH¾, fïSm´5K.Ñæ•á² ©KSœWi³_÷Dm�õ.®#©š +xE /t²&˜})a/¡°e�y· ŸÆ/"¾Ë$”t>à·Ë?Öö¾U~¥Õ(‘›Kÿ¨®Aä>µI›ý1½‡-“8² ëP$ú@6P-ˆÙ”ÀM“Œ°Ê›ã›R&�,‰~É*°àÛ{¡’1`±õ·T2‰ÕðH<˜øík<Ã`V‡¦_sWP™íjZ]î`ûAamª¨ö0°ï‘r®fÉÂ�MéCì6îÆôh¤ÀQAÒQŸ] çóÙ´ù·½ðÞÛµ†^©þØiFEt°(¬Ô,¬œ‰ÃKç-'à«cž`Ø·'ë¯ÔÃ�±síÍdKÐH©©û£q¶Oe7õÖô¸T–ƒçæé¿_&D#%–Š”°:N”!ÜíˆM29MƒÍuKúð\#ˆ¹É¡¬2×ÀÈÕ�.�ªB&èœ]O#ì)³ÅãÝ „ÖFK6Ý#¢3M^Xg“¿Noງ2NXâô%Y£­d Ú1ol DZ¡…i‚¡nÀ½ܦ´ °sJX4KFÏQŸV#—Š›ÔKá„]f„Aº«0¤ŒRF3Ÿ ¥ª› ö ‰š™M6zd0z%ı7¸¨Dh ¨Û@2ˆàãWÄéìÌ Âœ$.–ÞØ$‚fƬ×Ö±�RLê€ÔÝàÊP‚dp<Ì•‡Ÿ#X=˜Kʬ‘’\ò2¯àüáJXŸïÈYyÎ"¢D”Þ[B “E` +�2à >Qݺiß+GEµ¯²�® -Ù�^¾¥Sð�”mo2¥ Ò +]L+¢ <ö×™ÄO£‘âmÉj-”×ñz¦×^ΡFWpÇÞ‡!ØÄ”¥Eñ¶Ì·NhÌ;élË'@Ñè,™}@òöå\)Ú»�mx6—%¤ˆ˜õN¯ã<µ´Ü+N •¡lŒBJ;ÞŸÖ©MäŒ!ÈdoˆÛ=w—¾×(%”�Ç#¡–d—¼°¤xˆÜ¡¤V¡^W ¾Ý5Z¤LQ ØÇøË ûSÜ�kn{سîQ†J!­Ë§¤b lÏ"ËW+Ø�/iY—Ïh’!¶K€W1tèF¹âêçQGå0§† +YiOJaÔqCw]ˆ§N�ߩж â\è1˜ød'VÓœq3d×^Tض뎛‡¿J¢ïfЀ¦Ÿ'+„ÁþL‰?¤5^‹¯ñ«j‹iQ…Å(K®œ¼§cFÛ Eóê¿FKO“�.« <„u�¹*_}Õÿ�WuüØêöØi»Ç#¯°,�c�Îá ÚW ÷`è`pçŠT²Ñ iRðtÃ@U¤ØŽS2[N¢¾m˜ �";N9‘¼wn7ûC—�Â7.+¯ƒN#{5” µ +ø8#�“²y<Ôëú>øXÙ•¥paÜ:Ñ˯Ï�`U6¼6©¯ˆÄªÎ#vUŽ=X¹ i„�.M p9 �ö>EŠE”æ•îp aÊHžBÎòçÕ°÷ è=5ü =Î[”ñE’ÈšŽüFÃ�8hôÓ·ï›çÚ*÷)°ô"ô¢hàirÞóÛ‹”=1lÅí¤H2MTϹð­¥ÚŒ×“ KØÄjk@£97Ê QpX:ê�¶‘nh‡švÎÁ²;›í€GQ‹‚Ĉ1Ä”Œ +·QÉ –. ðCÊXÍ–�”­ÒÅtåbepW²¦Ö©¸¦d'iÐS=�ÂvpÌãnrCêç»Aòo #„¬FµÞô#!@ˇNpU6#¸š +ìÁºVêœù‡°^ CÏœEë$´ŒÇ‹ w³ƒÔËh¼37S™£*eO鬌½p¿Ð*-SAdp.Î×úÃ6½w•Cnçn'9>f-¢Æ+®ÐjÝêuÒp`žë dÒÔiª(Œ‹ê¸z‡ñ‚oõ„. ¥:ʾª ‚˜ú÷FáQ+ÈšŠŸuoƒ–ª’òojS0o˱á'\4ohÍ#mGõrcAeÁx¤ÞìÊ„ë÷p8 R–« Üsj °&hŸWLÉ5¬™åíˆðqK?7„w` l¨S'‚6!]SâAÆ%½öH@š(*ªbò�-ëÅþ{QEeõ»üJ ÅÃò[‚MetôamX–�uì× 59½÷…ˆÙ„%`¡s*�!‰‹¼ntqΟÎÿ:­ÇéDõàm8¥»?èHDéÔB,03#ÔìTÍTÕX$<0‹´kHEâ2´ÇUTSÑeÞ!ÆãTuñ|ÇÞB%[£´Ð.?'9"#í.M#©‘�0¹¹ÚˆçiBdjy –êÍJ\¬M¨›'úã7ô ‚/ÒÊ9�¨`ErÞ&Õ{W\™£ªœÓUëšó¾¡Hº. +é™t9ú”„¦Ñ°(7Î… + §­P99Í, ¹Ô,ñ{}ÀÛQù%¯ÿ*1ÕôGwëóqªæ©�ø“Uª”ÓÂYW=å"y¤K9¹ÆÑ¹Ò‘6»WåŒå?Ïc~È “DZÊkäTEÂzçÃIžÿ}ùeë¶}©Î�—ÿ%'|HJ_Ƹ|é…6ʪbØÌœÛmó®_}8©í?¿/g]¿•Û˜r›óÃ'ÿüÉ=JSˆnZåš?9ñ’ÊHŸž¹zB`´F�£3dq:噄Žç;h‘ +Ñ5r‡fâÈŒDt�Ú +ã}„ âÖ1–Mü¢LNFbùÅ¢R¹p{$Õ™e`ýd®š‰‘ˆÁIa‚³7Æ":+‚’ŠÀXF‘ +>cìüNR„ü)ý|ýe¿Îõ¯±/—O‰¼<Ì+i�j%VåÂ…Z™ãËé„I™�¼U>ÙI¨�(;•D0Êûã±Hk+².ÎÞþ”®Œ•Ý[ÇÜ õBrÖ¹œõ +(3Ê�œXNæ¤mÏ€0C„žqoRSä\,?w󘢽 VN$(lŒ7¾J‘Â6x!£ñÓÏÞÿ`¦5c`²WŠ÷û§]_~O}r»o¹wx&x„+9¼(0!¹æža¤l‡ñAemœ€»WÈ å/Ö=ÔNHëMzæ= ÜNô&¤qãÀÕŸq 3X•Œ�w9 a·XV\9ᕗϼ[X…Œm·fašp5X�•µ= Ø.GïVc£�¹iùg)†a\ÒÚäÀÌW¬_ööéãŒÕ­H¹âpNÇ0‹Jæ˜a…+Öc–r•4ÇVç¹Å)óÌ)«@úSËàHm c•ÒEéµÉ¹”sza+½»5Šxx¬ÒÅz1Ü1XM‹õ^Às-A»PÓߎ»~oˆTÇ0Ù­=F3�?NB+`,×A Âh§˜:�Çû+Ÿ±†i˜An–³Åêf¬Ôw’Þ3ŒÌ0ÞR˜�µsÚ<ÃÉ×3_[çb¨�[9h!mÒ�†ÿ¼ô“X�d²�_ºodb¸�£ŒÔ?2˜B +llʾYL1ÜVZc½C1Ùøgv;bå5ÖÛqd¨Ræ,v)µ±]�‡AéÝŽX1�åvœ›”?SX�õr¬&~Æ»«�±Þ�·ê +W#RYc»oº‹‘¹o‹°Ccb.ÆI<1÷"Rza»}Ög¢V)+˰J‘‡AÐ�;äÂÍYÍL®d:;1GªHm‚œ7Ä¥À&\�TŸÛ̈åð_¯ƒ›âØ1×ÁÜÄŒõIV¿¯ü‚«nq¼fŽ_`ØŽ +ä<4«§Ö>ŒÒ +J 4;1'Ú#‰˜W>h–†öÉXBæà…‰vzùй–±¼ËQŠ bˆ(A^O34j…)?/Ù¹žœŒÕn« ’ãÙš d�á¶D†øX%¬tÄ}HØ@22[g…• l÷•Ÿ,¯²‹"Xëìm'F>¼2$sÓ I ¼:!Üø¢�k\Av@–J~9×ÑÚƒƒr [‘Îæêw$Ÿ«³*—ä[´›4^UtQø¤¾~||÷áŸúÿðòëŸþÃ?¾üá�ﳣꛟ~üéã“Óêß¿û§ß=¼{üÝÏ €:¹œ÷:žtAz¥–×­(÷¿6 ‚¼ +endstream +endobj +2200 0 obj +<> +stream +xœÝ}i“dÅ‘àw~E¶èDTTÜÖ`Ö1Œ�iL½;󡻇’XƒB[S2[þýÚËÊw„_ï½Ìí—ÎêÌ8=<<üöÛ—�ßÞ½~ûøâÅí«ÿúÍíW¯üáo�Ÿ~úÛÏ?{ïÿÌAôáÆ¼UÁ”˜ý!­rŒ&Þ~ûVÞþ÷A«}Ò¡þûíýAÌáû¡Ÿò>%l¨þ<�ùö`½SÅ8WúðÝÁz¯| 1ôéïó/s·ùKsê<þ/&år6å<ÆôeñÚ§°7æsÃyâé«·3ÿç»å´Ó·z1èéïsÏ÷þòÞÝ{ÿüÞïþðÙíŸþúúþÅ‹Û?|ö�Ÿ4`TÚ=0[emÊ~ÀpïýöÕA^=ÜÞéCRöðênî=|¼úþ?ž­µþZÛ¸õóøŸ¯~ÿÞï^+6hÅY«dR6`ÁæéijwÆ%ZYë‚-Þ æÓßaÝ~œ9}>ò«³xuE¹œœßÏ œÇWÿ›ž×¡y‹SÉZcöOëøi=ž6+bNôa Ð7)‡qúçƒViøÔqÍQÜ>­ÌœñŸê|Οº>´ÖÑ´ã’Ò±¤¸†&±0Œx^o”×>¸ëñkmíÓ§>ÃO‡3È<ø~üô<øÞF2*ùT®Š�™˜6*�mÜsjg=œÁtÞ¾ üö ^G.JçXö_üçìî ¦ß¦8å­IöªôÆ`2lJR)DŸ/ðph€€N@<ƒi®5Z¥¨Sºêñ×`¢kMPº„®;1&»Öå�v溧Ži¤µN¥t¾Þë{Z×?ñ‹ÂÔÚ¬t)—áaºgÄ`Bh}PÞåÝ„P€A–ø¸dU*)„Ÿ€�ÛÁç�·¨vtyFï ý,êTý*Ð$KÐe땎Á§ ž‡I +í¬J.ìXÀiþ÷Y4´Vâ¡ÞͶ1Q6>¨äŠ-û—pßâ;……a¢mbRÞ8c÷žˆH´m8³k]¤Ó²~ï)JlÛ5�A°©y࣓ßs +[ ›%6êšpÀÔÕj«t1v7}øúÈ‹©˜.Z•·Q›½Ó¾àg5Òë�r1ÙŸ•’ÃYA˱\ðÏðísÖQ%°¯  ™_ˆó:mM GÂ}ÞÍ™œ['ìÆKâùVÜi!l�68U\,W@X ˜`{O9ù¸}EMr‘$8¸ tŠîÝÂ!KpX®èŒÝ>ø¢M‰àÏmòkµ�¡þ„È> +ë/DmPчâ/~�Óý •KÞ‡½³ò·ÈÉ: ƒJÑeé±ÉÊHèã4äMýþ�ÔsbOGóë%Ñ�*Ž'%Ó—Ô—ãçJ…¦'¸q¯•¦˜Žý›CV™Ýÿa}<Þ„0ÐíäFÍMÛK\C€µç‡æ®úõŽîüP5z8Ú<^{¼™þ3ÎÀ¯u¼H¾ JBÕ¬Ê:sT:Ëw~8 ƒ•”�žP 娊wy÷�­�n‚)7.¯R,yóü“¶zD­é²ÍpþíñxcLÎj`-Ö6~ëJR>ĜۛhR RB˜žã�Q6™rjÿ?Nb÷ëâò¤4C(B™¥ÝZöy÷�âCº'Vßs”¨@•ûŠ6IGŸ%ÎN¥DŽC>ü‰8.™éGbëó’ˆÇfËÛñå…‚°s„¢BÈ.t¨#BQ.–ø¼ZÓ�DBE»Dvÿ ÷Ñ“h«k©�ïP7Ïõ¾FÏñ,h +†aßJÐÐÔâŠïEç %tNV6Ö¸(0ŽŸàŒ¦O³ìOôŒ‹rP¶;^éIt°Ÿ 1Ž´| êûvÇ� °  Ãt'ƒ€“Ĩ£Ð9­xÉáàöñƒk…Hƒ‰!ê±`�&Æ3Jì” í>°OÃ6qRâÊc´Jûìãf¤„\™ YhCwýNÌ ˆ01B76:�“O°n4£Ql4˜O6àèt{ûÕëû??ÿæþHÐ金Ž)t@ß¶ !7¢þùÓÄ£³Ï—ŒþÛ™¨ » ¸p­•�.ô,¾ýøÈp†\hþ@¢PM�&@ØDò±Q²¬ÏzD.V^ºfÁ(S²Þ~Ímv€˜µ¸öx¼ô$:¹f± Q(¹ù6€Þ5"£Cz> hY� q¦:áz�o8ÖÌqÏ­’�bÀ{4Á||+¹3t¨w%‹«ˆß�Zà8Ò½æ<šŠ£×Êj¿�A©ù¹G -, ]ÇnÔ|Üx€�kÓ@XÍœWZ{_šÀjê .¦2„�-fe¼Nqï™>ò^-—{÷1�Oµ‰WOÏ#s6¶c�¨·Ã¸± S´r!ç´L�ÇŸ¬r:§šwyÂŒO™FZŒ˜ß–¡�O9¿îïŸW�7Ñ9e£5Ë;öfùt»&®Çù^=Üs¾U˜Çì‹øý(€‹ðŒ^ŘíUЛ‰±·à}æì¤ëm¬Š®øÍ fØo£ +6™¦6ljIä„䉸ÄìÁ1‘ðï5? +Ô¶�-=thÀHS„œò"°Am÷Ø´"Ïh»€1ƒa@À³q�—oÿöú»WßüßÇ_üî‹/´~ùù§§ˆ¸4 þá3fm"¨�J¹ìT¤òʘÚ–+Ø©¢‹ ÷Kú‰ØgbìQÈ1½Qo]ÔÈMjçI+­Ö-¾�UÇÑ?^Ïk¶ ¡�ß%R­ñ-ã1œÕ µÂíMû”$[ W¢€�¡hU\ŽÛññ,DÖ#�º#âJºŒSý:9üïá*$BÎÅ긙^œ—Ó4»$I¾ !+£õý¦‹Ç<½e,?ñõãÑ•¢?{G(ò›õçUcâÞÀß9¶¶Š <­¨ezÄ­z”´ ý +îÜ[5-é +²ÂzÐ÷€t&Û´µ˜ HBrÉÒ|RF‡r ޵ë�8\£}¥¤Cv¹ñ$cÜã\7wŽº Cª~Ž+KF£,=/A�‚e·O0.Î�ÜM»ˆXOíUp:–Žcm>lK7� +×Õ›ÏÀ³ +O�‚d~X„�ñ£9)ïœN;ðwä$8ïä$òø.*†8â�~µ<ƒ4˜{·Æ)mŒi«QKY?x”•‚‹cfÝvÀŒw„áµÏƒƒXýx󱞮"PêäÖ[¯ð~ $õõ #h&¥›-•‚ Êë`v`^š­"XŸ e`k•+Îù'=#¿mq,ukÂ%,mZŒ€ü¨aÇžq¯^—6ô’ƒ�h^fûf̤L<ýu˜ððÀõØ®éo+äR‘ÐÕxÂ�ìh3íEßR`¤¯ ¾€]¨÷Ï…+ã¯îú|ö«½Öº14t�58FzÚµÕJ#^k~ÅYE[y–ô¾$£ ¼°eEо=]N«ù¥° +¬Y¥â}vûW1ú��¤`ļú)vHë�ΨRJ‰ +èʱÉ5:¼?yí!eæù¬§¸ÐñboP—ÚSjbzõò´ÁÂο à¶Ò¡ç J.›åüßLÿüó™HݤÁ�¥Ì[ƒ®Â±Å_ÁdC~ßD�­1*{cmèØø%#„URÁ³A•4„Mí=š-�k|A´œÝ ëN÷}˜Ðõ�8f=ÁSG'ò¼d8ÔHux.dÉjëSPÆ•š-þR?t3�:/Óbœ¨Û'1´öb˜¨e›3j5>ÔrLÀ¦#?³úíô>Ÿ‚^4½{N>™dОvb!ï oÒ +LîpÖ!3kŸé(/û´å±V²íúè•Í9nÆZ.€C{弞£NahC°ÇIâ¨0#K¬šÞP @’ “Êèè”èþ¦ç£à°Ä WŠHÓ’¸-Ñîô�ø ­¾–P}Èc§¤òÁ 9Äìöp­[ /ò1zUŠccâ¶ÖZÔY! ‚³A·.æ÷YC +~¯5á î…nµæ£o2œ“C ظã ð=n\Z¹M¾SZ=|– ´÷;>ÉÍ®ýYR3ùáÏœò~GÄ5y§BÔžu¨ +xMÁ+42´µ†›Hºÿøù†Ã`»LÝàžéq]òWß¶v¢8ÉÜÔÝÿaÙø•ôîúŸÚ-³Ö3<€u»mÉ’þÉ;£’’¿lÃã_tqª7Æ9¥unÖ­:¸U8î†b þÁD³uIÏÌþˆ0ìZ±Å¬¼fG,3xm=ÄQÌðY$Ýwƒ·ÃW¬ˆj/«UÎIoW�6éÿMŽ*9çŒQ·A1âœîu”®xF2bžúxï¸fÝΩÌÂf-Ã¥VÎdo[»Ð›ó­E,/Ïc´!î�VÚŸÃ>EzD±�SA;Lî½ ô£`ÇÛ òò£M‡�7XØØÅn–]á@¢èJÌK§ÔàÞK®;Rd`ð-tv%0 j\2VIo=xûíVÿ }GgòmÝ�,O)…:L™8?£‘¯¶çܬƒô¶Â9+OÉÚ­€»@»nî‰Å!‚MO7Üï"YH¸ÕìóMëP®ÉÙÑe«¢ BæóÎl3ó“’²Æç¶³jxU=šƒÎ²  A›÷ ³¸¾î “ð€dJ]±‘×½ãÖÖÒ%ÁàæH(+¹.º!Iv {\Îéxò 0£¤jôà ì’ñ5B-êp¼ %s>£¿¾%Þ˜É� ’•êÜ©}sèŽ[Ö÷«±öi�ÁéH9ÜŸ¸™V ÷ :öiñ¥òŸd‰sQ«bÂK´”"iA¡´^õHÙhk'çOÔµNnW5=œVØð×jrÁòšj¥^§ð„íà5gàÙåë¼ë´û3›É0ç|QÅê>ä@É! ŠUC.�¶¨P¼I©cæÀïãŒÓGAZÃÊÒ\/CN*D*o”3C’Æ6\/�ü³Gîó€LŒÁ"_J¸b<ÉE¥`+… Y¹à{´.N¨>¶÷™¸HœuIº8’rÚ¹¢tÒaûÅ´™æ×I`¤ý†Q‚7“lãMÁ ]‘ÔÛ�§°t/Q·RpCL«GÀ2)LÀú¨¼v›KðÐJ•…=@i“Ë9ï¡ LGvUÂsƒ D.8• Æ” ÈŠí/ëhZiD9$§UÉFÛÝgÑãta Qi|H’†J{¡hYkúì5†Èaƒ +.› € ÖêËdˆ‰9(oCÊ»Á÷I &`Ö˜)-�¨x2°T9t�îJàÍ™u¸7´'¹ýÑ� ” �T)JHnŒ(㸠Bpv«F[ kÏ*Â…°§4�œXèóp€ïF™òÔïå„«‡u™Ùr“UÉvO‘É0±¼Óƒ‡L1ÎÁeXCzÆ\Ükµë÷Ü$Põë-pR18ÃYÆu– 8(k#[¹Ã¶¨;#Œ™ËAðÓ~p4oÍ›RtyîÁAÇY¿#ü¢Aê 9áÓ}£lB>›òK¤J=\5â˜÷ªÑ¬=ø=qê“%‚<7vúTk8F†R3§¡ÿ¥jÐ1þ% PbÓP ûЄv^˜b•1%ÁP”þ�ÕZ€x%YÝ�ÖÖI.™®0Sóì%Œ¹øÔ#Ñ…‘ÕnøÂ¢¨ þ8­äbbQɤ‹Õ™™¥™øL.yÊ´ýlUJÚ%é�­Û¬c˜`_-ÿÞðÅaVÒéíÏí#‘ÉÀ{l `ÄÑl¡÷&ŒwÀ˜ ’Uà¤Ü�Ur1!«“Ù¡®±T6C‚ÒÖkŸWe¡½0GFe2·Ð‡PP3ÈÏJfm%zËÚÚÙR÷ƒÕ»1[O³ {Éd» ´�9)ÙÙ_#­P—IÀÇwQ—ÉXÉ_Áø¬´‰i»H‚Q¤ÛƒµfÐîZ7r¢lH(]ög¥˜&;µw¬ý¯se¤=€Ñ tÃ�‘A«Þ»·ÒPð±ž�$ˆÝ_•ŸgÖ/wIRú—”‰q³!‹ðÁµˆÑÊ©ößóN°¢é")�ô¡Fín¨I±ê + øBd¤«±›JF\Q>�ZK®aKÙ^Í´îT‡¡ÞœdÑöRµ’BÕØ8Dk”͈KeÑåÔÔ,‘Õ‚’—SâaæFOÎ?h$åžÑEyëÓeRú~ -# 9}w/£v�µÈ¸úÑt{Ý”UwÎøkp˜ëĦC¨˜ÁiF¡ŒŽ±’¾¢$•Ë ³ïȯ‹ã'€}UM­ùN|j#\e öÆV|Ø ¶8OØÁáõÊQ ÷Ø# ŽX½ìJMv-ȉ5ñ¾.å"…Ö-û»ó+‚ +˜ÇO@d)R*'¥½+; �ÈHT6Ö86Ø4¨yˆ‡†jFìÜ[t-±á’¸HâJO_³¤hø¦M“÷iÁ°4)~�VKƒá\4t@%?+Uò3¾Â +jiÅ»Û3R 8/ι¼–<ØÑ'îœJ<0>¨Ì·Ð`‹“ ¾ÐÕð©dšMÈV©œ«gŸèÓ ]"Jú´”3q³¿c·Ì:$Ù :Æ•ä@¶l¨†Å}ÚÛ¯^ßÿùù7÷G´á28"›ŽíÚÞ7#¹€7¯Þ‡ä6äˆê¶(«sÜ},Ä×efÀýÖ^Ðþ¡!Å`çu0_�×§§BëC `cÕýs‹B)7sŽÊeø†„bnó¡Ó:«ÁÝ#«l½ ëÞÈ¥SK5£Í‚³@®�k°pú!¦ÁýhM`:»bkÉm�­¡æz~"f쑎…GŽatí@У!Ú‹N'•½ î�²W9„¼cWÓx^—fœQ1y ÿÁ÷ˆ{A)K‰G]ÈäF‡ž÷KC=V÷Ø\†äÅ%¬»üqøç«Yµ@Aƒàá@‹I_ͱÕS@ì1¤ è%tLC°Èf®ú¤ÂÑsnÆSõ£¼�1L7ÏUƒ·˜`ÈÅ!‚ZŠçmk´«r + û´@"¼0 äì2ïÉ‚ 3  áäŽ{Á©1›4ò²5Eꪙ$4�N™à7‡ÿ™�‹*:Æ1áÑ=óý�~ÛGŒÝñ$Á.]¡Q¯u4Ý–»«á@Ëyõ +Üô»ׂР°ÁÇ@@Þ,!o°Êi¿#ú˜t'Xé x8B°¼åm±÷0@Ézn ¢ˆ;ž:Ï32¸"oªÝV�ÌÔ‹óÏˇÇoï^¿}|ñâöÕ�ýæö«×?þð·G i¼QÉ×H£JôI‡2 ÏAÌáû¡Ÿò>%l¨þ<�ùö`½SÅ8WúðÝÁúA^ +1ôéïó/s·ùKsê<ý/‹qæ<Èø­UƒkcY <|÷ÔržzþîíÁ,þ÷Ýrêùk½X/¦~ûÞ_Þ»#oV�;c”‹6ØCrnPµÛ§daæ �>ø¢M‰àÏmòz¦&±råS`Çüù”>év‰’'—ÔœþÛ,õ½(f"N|êíò—€å¦fL^®?ï�^(aúW½È 1®9V7¶æ›¦^O3@ +|ÏÒêµt¸Ö©˜By§gÛÚ)T-Ûúóù†±FC t1«ÊõÈ#ß7ªßê=@ºæ�^KG®¯RÃNû«-”u,Œ¤ÖóF:bã•õç\ïìˆgò^Þ¼4µö¤Xx`piêcáCôANÙ$©#¡Ç­ž1�c”'ê ¾ÓÿþuIyÆŸzK–uo¥ÓÔ^esvš~‡§É ó„æKh‘×h ¨ªÀtM©ò@Ø1ž‰«ªPB¤¨¼,°eêÙ¤pï„sŒ%(w‡|wÇè(ês6Ô;›(#¤k V™¢gDzV%¦+ xlôÔ²$ žS0zõ^�ë¾ì~¥¿—ÎØKg<2>¾óSö$DÀ‹®öÃüï�vä›:T¼Rô:ZQñeú!èõM!”6’˜ðÓ7ćLÑà=A÷ý“4=m”¦�Iéâµ”;Y>Õ ¬èS¡ü±Y%mBk:ãaöθdä³Ö[¼þ̧¿W"b‹¤J 'ÿ •KÁï€åìBÄŸ"!ó¯’Ñ&]`fËφ<¥½/áº{Ÿ�r©\IžÜ„EìNL*ïó•ÁA<ÐÉ*틽ÄE®)ší¸ �xMrQ.g· +` ÈhqCªL}|tF˜™ ¤%+Òþ $LKjk«\±qï¬/„Y³ôl„¤;ü;~6B‘ž�jMWx6:bê#A+]R.³hÄ!´rP)ê�!h¶Ü +if‚ ú¨´×1\`ÏÂË�ð_Ø^zŒ¯‹‡'«öo$ «yU¨Dée¤ ’˜„A‘­µ+Ö„8h§�7&EŸ×@ååôfýêô_ >ÒÁweeá½¼2õB2£¡"<8×P"‚uTÉ&¯KJájŒÒÑš+#E"Ò¶› \‰™¹5ãrÿEX4kŠJÎÄkãåÿ畎ÑûwGEþWó©OD¸›7Êë® "˜lHï5ºž?-IIâ }VÖ9rƒ™²ÄËl]r•ŠÀÊì�Wi²–8™�KXœ�¤UXN|iª÷©°(+°N׆“8'— y§u},,ÊKê†kBƒ``STnˆÅ¸b,â8%Ö:G‰u¼&H’À9ë?9K|ÚN8˜$À¡HlÚî'Y -ñ)WÄ…B+eåµ14±¸àó¶‘I`!ØY—†ò}×%¡TÕî‰{÷7„ªÈ<ÄÚ{¿ûAT£T Ýè”’Ð]“‹ ÊžÆ¤¼ñ溇NhF‡j×>_ñ­h1D%NŸ¢Jæ\÷ݤjhf­¼É.ýT\„¥j,f¯RpC¬á¾‹ùQ›£·Tí¼â”·6Ò@yÒµ¥Jµ•¤RlêWDKÔî +Ú*�Xº+ŠÖ±¡ ³Di§`ŠÎe?ã™ Kê Ö©ã`XºæÄ¢…Þe•õàû--–(α°´l]RÃUÁÕ0€°E¹dóÏJc`‰Ê �ÁÖ%OÑ(<¬ˆ‚³Æ`ï¼²u0ƒåÄg2PP“âÉmèD7Ò@X†OWúmŒ“Žp0DEŒ…L½@‚*ǵ.ró^$†Â¼$­îƒÀsaÿA�UwÂ]zÛˆ ú ¿€] Ìš$Ÿ€ë9 &šk¹H’ÿ^ä^-gY+Já»ÁËYÖŠ"ø^Ú Z–H´ê�UÉ…rÝs§’¨Nb=Aô/ê®7…/Jˆà%«È5!CÈÜA+�Âu1�¹‡ìºäën7IŠ�+aBCä¶Tr¿IïqM`AïAÀâ]ÜTR­Y'² ¼�š¥2(ÍJ�«=ƒÎJ*�+ž<‘™h¡ÖØÍäUÑ@í´@‰ì9AGåmLX{é‘ IP‚Ñ*E³›òNŠ–Êu1 Œf¨EnÃÏK`¤òÌãÖ%w¼TÌú$0îœW¼*¢çúrâ‹óŸ6 z– +6� ¾W„ 9 §LÞÅËAòÍ‚ë.hWX;3Z#Ô8üI®0Eµ¸Â;—¼B§"Šf'Š­ éÁß(ݛż?Á•N"_&™w[ @òNn4Azó0uNy',„M Í7Z¥³*kþ$Wš +5š¯tµæw�bAqê‰4PaJóÑo>„æÙKÔ<£b0ág/� ¶ŒíkZŸ¡ç•â=C¶ÊZc¯0­”ÿ-D«œ+f{a¾:éÎ"½?Š�­“÷Àòg(m‰t‚M7ʺ¡8xsKÍlKS"­!·lI±<• +ãŠë¸aԌΙ8'tƒ­ÉÔJ]É8ÑH0+)LU•Ià×Ïg}mÌK@×)—¨þ(ã)j3âÃ¬Ç Š»]QÇR‘ló…Fùœ·æç쨲�ʹxs‰´äx´EN|qÞÓ¿§Á_1#éj}ð÷{ê["Ÿ5HÇ?oÈ.§Á‰�ѯ ˜¹oÜϘ]ä,»£GA‰ŠáïÜU©Ú}°ÌA覨¤ÓoBÁwKÅ7Θ굊.ï¨?yZɇÂüTø¢ü‚³íù�X¥MkA7AE/Z=¨­Âö=§¹ N“a¦RT\³NHQ—¥ì.pVMöŒüöÃáŸ2üó?‡¾˜oj•µ^5HHl æ°¬1,èÕԋɪ׵!âÚ¡0îÌR3v“Ýyš-¨:ß.[Tô~{a52S,ª™�“´û¢R eªÑHäˆÓŒ‚˜Šh\s9/'®n)À—Љ뤂v:·á+3žÿ6nNšŸ0š¤lêåí>ßé> +  *  f(£ýe x^["üv° )Îó·ß‰@°¡™H5"Ä8ó5|×�X�eÒFƒ™Ÿr�Þë_Šã€û ¢�”Ù8˜¡âšÛÁoB™Ê‘J'nå–åN’zþ Ym£Lõøb¸Ê‰ù[ÀÓ®@%„í•,¸œîÕ\dÎ?X©%ᕨàðk'ÝT„â|ë*ÔÍEâ  r¨N*#Q¢‚uw$ûÛ¥U“ÑêԳ݆šd ®{ž‘i+ABα4@ówL¬¥¢ÚCVqÈ´¶÷`Ñ!|,-„2Òk•OÞ™ÿŸÁózÁçAiëýn ¿èæˆ0úùRûb†â«‚OyS²YÅl{ï• Óm…5a©Ô˜D™*0 ++§£ö¼gG¬ÔIUá�i22¥8UòŽ›eæ'�òŽh *�V +°º„ßB�XR«9x0eóŠe=HïÖmÖ÷ž7^ç;©,§w¯ºÏU¾ôOÖ'ò”öL¥ *ZlÇž;*ƒ™8$$¯«éÀ×OXÁŠ›Á¼ïò%ŽdcVTͤó¯Û"›ñ‰!þ­œ–SÚ?QhØe}ÔîçÓQ@ö�+È +[“Ø4?÷¡šòfîþ2¥jëz,§½¬ùk…ÔTêeu¡Ž ®D‰ØQ«�ÿ2e/3D>›Ê´1`°*&�ì¥ùl;ø…Ïj¯j‰åŽëÖÏ_‰=¼5ªø\:vùwù*)E‚.ClBÏ–ÛD‡9 h‘‚õ'ƒŠ°rI39„±Çãz6±åB¥1FÙ’í˜öïIv©œ\¦’ßjZ0¸1§àŒR¢\ʬnEXI¹Ëúu‹~¶�Í*ôèÝU÷›~{6=¨.)VX/•T–Ñ;­²�Û™\ýz)o,ƽõ–9]©)WÈ•¶ý³>­ú׎›êåT+æWÍ-JPà± AaµI ñÉR�ðOÜ<§ÿÀ/ØÖ‰ÞÛ'²±µ¥² ͈;øá¦°™Åì/(œÒ%–Ð[ˆ÷hpƒ]VaÄ p´{ú{3Ë6ä6{ËuwMvñ‚«ÕèO& í"»›žR¢ ÖØV©U3Îꢌ ~· Ÿ©6�"eÕ_Y”�åçÁ”Í»Ú÷ØRŽ]¶¤çªgZ`*Ù 5¸³ë®¬Šú"K:ðμSÝ®â +Àc¶,Ó±¢p³%Ò˜ÍH;DqgÜV! f$¦n?Ì7Ú`]©@š¨¢ÎÑw,·CÀÑQYS’Ûnj\zV£ÕT·þí·€3Gk‹ó ¢’²ù¤¬×i÷�¾C!…HñtP)j½ÿ¤o¡~~±`!ôȵ¥±ëÇC<-Iìu9©ÁÁ~³uä~þ‡ëí´2Þ™�·Ô’†�»Šö�0}x®”#ŽRJÇÔ×^ºÖLäD%Á³V=Éî>ßù©€°þÈÄñB›=ÿ-”¯l&|�[ëÀÜèü&SœÐÌÃÔ� áI80Id‚™†Q7sqw„�ññ½Š1Oõ·¡©fµ\P¹ â«7i…µ4T¯i‡Huè²W:çØ¡k™v�<_±D¢×•CÑ'峿ęhS(0 p5*ð®Ö Yƒ÷îuáðͯ¸/€úÖ´&HÄ%‘ÿŒAÙb·k¬iuZ…ãå²V…”R©EÙê[žŒÕ�9{ÜŽ¢v  ÃŒíkœ‰…Õ¢µWÄW�ïB²%G‚Wɨ=Π솩.u½ÌOä] oðÊ–·‹üz +÷%¤Éù’W¹èÙ�“” +�œ 5h%¸ÔÓÑoGÕ¦¾,3úô ÄBÞQIUmRÉé˜Û�ÿù=BŽÌÒj”±1Úݨ„°l ÀÔ¶Xx×àX¡…‘5éÑï¶}È¢tä�ò>o÷çî¡2e—câ>«ÆMñ‰5…3î·P2¢}•[šçzª¥ã�à\Oý8e þxŠÝyÑö?pD +Þűڨ’ó›ù­//©sD"Þ¡¦œKÅ÷,u�¿è7OG¤ìui+ƒÂg'0¿~<£J.!º=.“`�~Ó,ìˆTÁ�öÀµá¤vVç—W³KDà¶Ëð¿ó Öô@÷Üø²þ mr£úÉQi—ç›h‚Ê!íðä’ÍÑ«“C–š9|4XòCøW’›¦‡ a´˜N_€ÓíW¯ïÿüü›û#©�*Aç6 m§´5Gg'ÄŸÿ/ô›á…’ÁXRÄ/‰´-"ÛàEnÜ ^Û‹7Ž>”/I½ÈŒæÜYÏáÜ Â!](jØ0@µ;6B ¡…´?ÔÊQÙ·ç+¬ƒÒ:–í.tDÐã1žRYÇ2�¨”¾¸XÙ€Rä†R›¹ap¤íSÓÛÍ;¤yjh»‡E/Gul|l€³~×Ï}kҞ˾“�VÛ²•÷}By[¼2!†‹EÒnÒ5ëÞ¿ðÓpƒÑ2ƶ—lè;„xdÓƒ¬2íÀÎ]¦¢ÓJÓN=BYIž¿A3ÕzI Î"¬ŒÒ <ð‘ +†;ζ߫£j ̸œ�²©lW€bÚˆj-NSP“ÖG�G£ŠNÞë3Y;ðó:Œƒ��KÐóüÓK +%4v� +0Ò™ÛOŸf®î%„%:c6q0@X“{СÁ(ÞUÒÈÐ7G«LqÙ¤sŠ—Y®¿þqÇ›3'éèö³2¬Ž«Á �Û�ËïŒãA²¸!É*¯mÙÌ:ŸyÍÊF>1=Oûæxc­Ê);äØ8]#˜èKw0áxÔ1qßj†¿8­'Vr.¿bëÆVÏé@†#6;?ôÂQFò2ôf—Û'¼Ö½¨\LWÊù£×­$_]Ð’OVX9ª|ÈŒñq(é·'[WíÅ‹¼^OYãMЀñƒßKièPÕfž�9qš«r†Eú;Ô`á¿îñŒ�FpI^œËªpÒD”dòƒÃtœ´¬]ü}²*'ߘJæ´‰÷'—–—Ë=þ{u’µÈé�ø½´/"ú1X•B(ÛxÔHrÁ<ލ³¸7A«²ßž$‘Ò�FkeRH�„ß÷\9؃Ÿ³vïªÆ¤/ü¬’ì]=Ç%®= N¥ð¼î_Ak<‰³­ážãHÑAÖÎùl;Aû,’I§c]Q!z·C/ËÉC¤¿�ãÁ%ˆœó*ybÇz»éC·~9ýõ1õüKR1èœ[׫n‰7¥º:t)ÆK•#£;p×½zóxÓ=Yèp@Õ›UiAD“T,nO¿.ÕeRÉN¹ïž‘ßä³NÍGgøÆÃPŒ &›hrr2ÆH»Kj(5”JFŒÚ±Q u“UÎ1ùÒ©†íy5lˆÔP°AŸVŽê«ékîTRuYÁH¼ÀU,R®¬¦® j�€kÿ¬ŒÊ9§ci¨ «f¿¢M=£ƒœðòhÒZ�d'Âû~Gó‘¬>�Äèüï¥#  ÚZ9«}àûTPm[.UL/dUL.«ø9Ö-pD-¾`­rÆ÷l©ùôºÈœ ¤„=‘@Ôlµ«¯#ÊûÍwÓ” ’öJѽƒCþP ¾6¶Ø‘´íd ãd=��­6d¼8¯>U½@ˆ�{}{‹ÝyË´s9¢cÐI¥�rjcIë﹇XtÎ*ãuÙ�½b0Š…KQ¾qqϲW9ø –ª˜Ù¾pеî’{�]Ò.r| É€tì º­ @~>¾„±v€…2rvÓ? ÔrUÆØÜo ]£·ðñÊÃn%k–Ð5ôðn&9URÖîâ,³ f¹•6Ï\wø5 ÌK»!æ­WE´ÇŒ6lA[RÁ²€‚RZ`ãÄönŠï»0Ý¢1hƒ,7'wœžÅ.<‹N-”Ô’Ñldá'Ï'@Å+Öþ@9ˆLð¯lk ,Á€È°R´ +Y›öÎ;ytizɼd¼Wn(µ.€Þh¾œÙ‡¼Y‹ªkâàhìžyŸv^ÇÜÁ‹wtF•RJœ'Px¬xÈ[AS/ðŒšQL:wªDí x;Dê yaÿÖN©Œž£jÖZ¯ŒIû×1¹®šž0ÀXÍžß5¡¿�ebd}[¶dèQ=GRÀ´:"KJÛ��up"×9™­1o\Þ"a=”v=¢jîYŽÌ¼ÿšC‚™DÔ#ØâTHÞÇýÀzÊÅ­Œµ³O—ì4Éh f ‡<2ãö2ú´¢ËéÕ aôRÍÀcæ±™pWµ÷3déÉí‘YÒëÝæ‡I ¶jú´Ò¸DURž¯£öª„ìvxOð"ëì[.M�)÷ôל‘¤¸©ò„Ìqõ`3¶¢Î•0éêîø=WÙ½ª¡¶.�‹- Û‡B,ô~êöBZ–&W-›“‚ ß—~³-Uœ{Âå!@@§íÕR9—ATrÎùÝR4©!F/Ó?ž8�.h�R6Ó’xdÞ!’YøϚEáWÖ^;£‡u#ÀÖÇîªïdky‘ngbÆÖCM@e)­äàRØïÓV)‘éœòCÚ3§\,³:!¦;Æ>ü�-j/;ÄEÌÀ5Àkhä²­ÀGžM.¬Ééo/ðò³gµ—«Y"oH& ¶$P'«lt;¦rÞ>ÕM¯)Æc3cèmG† #ßü¶ùgSÄùãÒyA8Âàè³²¡çZÖŠÉN³L¦2o`ü/Le†’‘ú¥ ¦ Ÿý~´©Ð„u9ûz¾«ûèW°ãåÃã·w¯ß>¾xqûòññõÛ¿|ó_ÿqûÛøþ?oÿô·7�?þõ›Û/~øáñ›‡ÛWÃß|ýçoï_?~ûÃ=‚Ž-Ay—òÁ•t4ÄÄpþ´ÔQý?‰mèl +endstream +endobj +2201 0 obj +<> +stream +xœÝ}]�$·‘àûüŠY^©×žh’ÁOŸ`À’×8/ìÃ-,øgûA£Õ¬½�Z¾Þ^üïÌ®Ì$ƒÁHVfUÉwóÐÝSÅÌŒ Fã;ñüò—_}ýòÙg�_þí¯ß<þæ«¿}ÿŸ/?ÿùç¿üâÍÿ>é“:©Ó;}²œN>ÚS R2úôõw�_«Ó¿;©Óï¾øo'öôNöôÛ| ¨òŸq:ø õ黓:¥Fúö¤£«"ûYïß +÷ÿöÍŸßüîÍ¿¼ù§ß~ñ(½Xþþwýêé³ÏûůyR�¿ùêéß>ýæé¡yqïAGŸÜ)>ëÎ/þõœÔé?¾~zóù—'uúòùñƒ:°§/?¬ç__~÷‡Oÿ¨”úã‡é§Bõú[¹é÷ûóÿìù·©¾Uæü£_kÿð§/ÿùÍ?}y~Ëò-t¼V Brzz-Cÿ’¾}ý³‚iA0´A7‚99H}”yEÒS‰8sF”ÂAçÏçß�ü&ëψ~½§Ò¸nÕ?æ?©¶ç¼ +çMšVÿ´FÇå å=`tqC 0Ì.–ÀâèÁyÜ)8Ñh-�’—vQU¸V*lQ•m`IœW™–6�‘‰êÇ#Շ̵ô®#Ä€hí1<™-¤x–Õ&<Ž•eSf–›‘òTK'…}øB ŸEˆN§ƒ˜yE…ˆ´Õ§Xñs¨Ÿ_ëCuÅ,zç53w‡qƦ/›—\ÌLjõ¢‰;™gÙÿ`â§<àˆåëÍQWT+žK„Íû­<{ï銟õ7=1DéAÛ0ðâ$ù¡v})·K +uµä,VŠÏ&?U/ø¡æu³üŽYÕ¾d–�ˆ:íÝÞé?{øòß÷‘›n�þ�@ygœ=ôàBTþn$‡–åº-âÑŒö <$­�x‹ÛÐÏü2ï«5…Ì¿mýåLE¯_N÷ù„»lC�k�`(7{;7õ(¡µGq ‚õîŒÌŠÈN2ó¬8&Â~Vf%b¹j> 4G�çO?Œ‘!s¨^Ç‘—ܠ‰Rf:¬”Mòæuªú†( .FeÐFe÷îÞQzjµ„U@w +Ú�טnCOü·ÏÌÚ„u‹“�36žP]wÜSŸ+:?K�…&¢ã"´·úIÔ€Z)w +ÊAð.†]xÿUþá+ÞS¾~º®•T\ͨÊ‚¶JÙ@7íÉ´'br/¼)Ø»Œ.`Xg|6¡²&„åÄ%deÔû™&>° +±ã>r4“‡ÔtV}÷a?Õ™VKI +TÀ“O\²ûì�Ÿæj=d%÷§a8@ã¶AtNÌ|KJœ°øñù?A‘9õQ�Ž)CSåÁ‘ÔN_Žœúhåu½•”ˆ)Ãþ,t'=D­u_“ñ€À§ ¥”§ä¨-جøñÆUxxæË™Ê³gªSdöãwÔùÌ/Y+ÓdËêsç³É?8=¿Œ!ªn)—OQïò^¡ÊV�æTŸLêz¯þ€'ü“:ýëI�L„¤£›ÁÇüU<0.@Œ:ÚhÐAÐÞ¡½4Ñ@4>Ž£ Ø`¬}õ‘_וgZ…Ö$„ ÆSˆÙûˆÎî|�ù‡]DËÿÌ?~™ü:ÿøruã %§ýDmS?OÚ@§½ƒWÛ]c,8åý)¸ÊQå´³ÏçMV¬UÉfú×�¢5Μ4ØL²)?æ?×…ß® å€ÈrÁ›?¿ù|‘´Æ� Q…SðÙ[½›Fþ+9 gÉñSÛp³³]R¿ZóU¶tZŸ<˜ñóòW…ÎZ ã'bÓÙ�D��0(=‰Ô ÛØä‡Ë%Zt’ñ`1¨Æé Q}A¾+ñ ™¯O^y¬˜@ÕlÒÜr7Õ·†� bD} +Ú¿—è õ +}€|zÍ·FªÑ™mˆ/¤øx]boBL­ñd‚cÝd (ì~õÎmjH\á}G9¬±°¬*u»Åw^¶.º#U¶Ïú!õåÏÞžÊ5¶Ö˜ ˜–„'-8‹v¿v]éм;oM“9ÿ½nÕrdÔ(%*î‚´Z96…Û¿ ÿ6»R>tŸn# õ6N•@.‹º�…ž·ð*ëøOï™.�¦Ÿ/aÑ�J.Å)Þ�¦ïûP'q#0»fÖü4•·ƒmJ‰TÞ×ÖÛìR7›àjÜÖº~b-í:nò̺x—Øåæn-g«X7éE*û|wRhJ� ³ ž´Ö‰µbÈÞö-PõÉ ›RzjsøÊùO+G_ 1ñ›�é©ñ·ž±ù:á[åÚFc´�Ç÷ãì’ *ãxZ½Ù¦•Šû‘>�óË*P=´ÕµœR€cÝBÉ&™�S_†ðSbùÉ%g²•ªò±‰n'™2šÃ’‚Pÿ>¿äD¡ÿ¼þùØfÏ4¡Ð¬îáù\|bSô–Eˆ2ðrv÷�+ÁØÜ}zêãу×jÆbØ{òšÁc©:cùOïyòZ!SÑæ e +Y£ŒÊü=½¶U7­sà]Òqà}‡oÅ/�Év‚ùGÕÉ`¤H«Q+w³_„Ø6ö`uu8cÀÚxT~~^†‘Ö�Wl°ùeô¡‹X£ÉdÔáC" »Ÿ³)>l£:¢…4â)hÕnYaÂÔ¾‚*= úUÉ­„(+¨=Ð{V™¨·è®Þ¨NnÆô­º]ìô¬ç‚ò`’ÆÿïŽF‡F�ãc6{R"X#¨V¶#\—@RW¥³Œ^‹:Eo·Ÿ;"Ô‰"ÇGÁ¤ÔaFÓu˜ h?€™MÍÛ¬¢É< Áz¼¥40ÛªÍ6ð!‡˜®‚ã߯¾¨?= &9EY&eN>F0ÊïW“¥xõÇE|½Õ?Rc¼gŒ@N®uYZ§cØ~» bªEþ �¢““¹öx‘Õ#Y¬ŽÑDzËU�ïÔøÁP›%CMι³NòÈ:ø.òÐ5àõ1ÄäÂɇ6¹½îÏ·«Â9[åêÁ¼z>ZôÏ�דÊýäp÷KNËt¿‚LVYó~½ryÔô„êyo—‚–õ~Y}8Xºâ½¯üÞXU{kˆ1…\úå´ƒÞðÞó8uØ·²L¶²¢Á“w\f|¡l;<‘½|³UE²ƒ‹ýmm”EVzý,ÿ椒:XABêÄæZ÷^sãìêÉAî—ê�Ú½ŽÅ ˜ùïxUÈã÷¼Î>[™Í‹­�›ƒ´êdµïô]Al;3-\«n; jº§·RÌrDeÁ1á„d=&1õל�g̵áôš)]Ǿ?›EÇæQɤQçÔcTN·ÛÂÆP6?S·1ý~Àâê¦bÌåÝ{4ÇŽzвcª¡Œƒ`ûûo†¬Àe·@4ñzØZ| +v4ØZÁkRN˜Àž¿¡•»&yHî +b—”—pzçµè¨•Έ \¼)êZáŒVçXÍõdó3OL¤Tô}i·×a�:êÚõK ˜à5xkõ~ fÄܨì“똟•±ŸZÙìw™Èd~½¤~Ò+I¾¸ÙÌžÜÉ?>/w§áˆºw�mŠí‹\° J©7S˜šëI¿“…£6U×ļ¹nS«=oõÀóÿNN™‹X^²ãT“ÃÜ;>õ[ø|’IùÇ/–¿Š˜ÿ'K�û£¢1C‰‰õ3ŸNˆùü!âô”é ˆ�¶*k¾ØŒã®0YòÊôGÒ4®IO¬·°““4"”%r¨êÅVI¬5¼È¢öÅ +$=;†e£Ža�LÎ#Û¤›¿W=‚QÉb€4eºd†!éÃ(gIZ´Ç‘z5�¢nqǨ^µÉñÊ;àL+F 3b�à–ÓE–” t¹äÁzƒÿ¯1’VŒn¡‡Jo¿•L\ÖnyÞ)éÎC£J$­0ehö²™IÁô²ŠmoW«ŸÄ_Ê?g(s®B§€bÒú‡ô®aË©V.‘£: ÑFsœ)V+£.°oUê°'4zó;�ùsWçÒ~}WÖ)΄F^k»FQ«Vg Öé! åÝ™”D³8ã$�Á¶ô>§&ÇݹØ\lÁ¥U«0$!àõ1ñ{ ¾ý]š,¹+ bûæZݪ8å ]²©™·¹ßµ×Fiw‹’Ýw;l÷)×\[ €Éçóñ ¾šN€õW|.AÈœà.BPñ�hÚrªi¦q¬ö +\@{ÌÔ�4ÅbòAƒ1)Üöý¹Þ«¹Rèï¿+q_%á6„bMÍôe5JƒÓoŠ%ÆRËa×àõU°Dû4WFÓlq™9r#þ"‹é=j0€Q)^Y#�Š«P;ÉŸšóÉV’JLgPD¼-™dœ¨Á§¯!’æ$ü¹|·Œn¦õgNJ*^㸯 ŽY£çÇ#tüGõ´ROW1�€s¾‘, ÐÊ™v¢«V (eÌ~ô÷v�U÷š�ªÆ¼ÄÙ¸›V �>•h�Û=m ­¶*íGLât(€ÄÌaís‰VT¸½cwЦ˜¦‰:&ðN]�¤^DÞì[ïOl!MãhÈ¡*—ÔžãIÖ×2'iѬ.âñàšT Õ\øá.$×kñâé…Î4‘\Å‚Ï~ÚýŽNÒ•F +kò]—Ç‹´8ŒßmÇ¿f!¦�ÕÓhü&Fõ®¶>µ;õ ž³QèXC ñ*cMçÒÝàQÝç5Í�wå�ÖzС ­¸�vá¨k~× w‰/²ëã{kßûØÜ®çäkÖ„ÕëëEÙª¹ÍEÊ-¹øŽÊ-öÏ ¼u~>sU ³ giÄo"´¹æó>"¼ÈÖz—¬W‹Hb<ô{j®NÖ5!×M¥žÎèY-åúÞOì§DV.ïádø-º ]{öN5»ò~–Ë=Š¡ë¶LS3T‹éìðú6×Êx¥ÖèAåiÕ;‰õzÞ¶Ì™ÎiÛ‘³…8®×1lAWô·¬^y>_í�PNÀ¥·Ü+WK³} R‡4ü(j%]A²OšïJF¥†�”°gók‰€½DÀÁ�X!±A¾| •€/y~fndë>ÝÌñ kÛƒ½Y° +f3t økîFLä^zIs¥-² #d[ù°°¹/)u€›Wozì¼:·ê¶Ky5{ Äœ4™ž�„�à0Ù¡X^½x³ü›Þ›Pz'‘©•¢f1¥Æ¡{Ñe•ªD�HŽ£Ž"¶ºßК¿K'öNóèe¬½Ñ�ãH´%ZÎSLÚ­gÏÛê…[7BÔF£­ÕIòñ5ÜÓä–DâS‡D ÁFܲ¹`ƒÝ®þ†…Tãpº�Ž.â÷ŧümž‰ƒ^�&ÉÍ\XéÚæY1^QF⬗6™U(¨¼ï +:º€÷¾µ2kÓ}@žÛ×¥Éó�ó ¹åÁDÌî«:/�ã½@Ô¾ ”3¹Éåœ�Z§plB4br#6VjÎSmLÜoJ)§x*ÚèG• º¼Èux*O…¹¹EqtÖ—~DvxŽ•šÉÂK¼2 ðü�{†º�É;À@3¦54Y0ÌžÔüÝœtÒÀˆd憬¬´l¤�SÙw«VΙ\pá]nYjç `w·¾ ÿêõ:ªtÐ(6éNÒxšÜE¯Áüzz>’ï7Dpµúcù½éE®½:ò¤ŠFzïÁ§“5îéÎË“lÆN!ÓŽ}e2ù„³˜ÁÀ¢8A@&ò�£�àêÈ=F=E,å‘LYs’|þè-N•õSG{§•Jà£Õ›2²]Éרñi�jþ°‘d à‡é¹o58£ô~6õ_Å¥ÑÍ›¿öa�dJCn`”¢9º�ÌNÐJj²ƒmExkÙí'ƒ£ },2#à +6° ‚±n¿!‹z¾`¶>†àÀ+ëÔ[ÔH?×Fxs¬QW$ uIᆎ©”g‘'wŸ9÷$Xð. ‰ô>ó-üj¯˜”;TdÖ�'4a‚ŽO©91é�zÙ4äyö·Èg}¸gÇkü ;(¥3"Fx 7§IkoíÜ[ Ì{H>†m=Ôë]9ª�uOÁzI§ÏP›Â.½,ÓÃ&ˆyöíáí0w™qÚ%ˆ1êëc›o@Š Kqô–“Ká ^pžrSé0@Òã}ÍT%Ø�7Ó�Ý6“Àe*¢‡dñ>èbFÎrÊèoŒ°š" £H¼=ËcÏšt€û˜kÏè<3º4ÀqzÍÊofì�vtÔ `s íŸUWП֠½K;·Qm8·z¶tr =G³h¾\/9™¨ô6äyD_TÄ�Ñ8}!Uü#yU©�Kv…Ñå�$‡N$#Ä{zêb�˜õ�>ñßÎ +b& ´¬äáfû­ Ý 8ïÐØìNÇn5�ðNþñ7_=ýÛ§ß<=pòVYqàÝ”øn?>ƒ³Ô&Jµ +FŠ€~ ŽáXÁÙ'—Öt‚ë0Ó0>ô;²�Í +êçו_ç2ùÆ‹Í>p]ï]Ié˜Î¹ Í‹õ¸r¢‰ L²)þàqWf4äw݆rÛÊ÷ RÖ$»ùÜíŠînÑ•½fT+Ð+ˆW�Îa Ça:–#ݾb'g„èdçu¦s›Ž¿¢yËKBÇäj®‚ãý0³0@ f ¬µnRä¶zŒ6K·›íÐk–úÓ=F¯ÙHâå3Ú'3Ýê€TäNH©^õ$£h¡0“ôÚ P0 ßÑP¶´q;ºId,%�À%«÷“q¿–×;pÊûä:^uA!Ùš˜KoIvhÕRÈBô«Q ôû+eÿ¤Uw$ ê€ðªÞ@¹BõúšæÖÝ¢Àj™Z"c?a¿ß…4]†ƒÁ móà +Âj•2ÃŒó,†ý!ÖMÞÖÊA´Z-ÉõLMF?ß³¹z8ˆß^Y ën©Js]Íî®h;MÕ+¹dmÒ¤ˆßÜÃÜЍíûÆs÷Õ»¢hÛPt m®êt8êáè‚ wf4vÁ&ÖALhw»ùHÖÖ"zL¿˜ D•úõ‘tA­9"¿Šd‰mfTч4ÕÅ ˜Ý|s +.U‘i‘Dóèjéa­wלòÞ¤o’›öZ/^¼gO"P¯Wÿ\ÜÈzªT'ƒzèŒ�|£yj†²x %~Ûf „„HCÄÝü\Íjþ- R.öLó,N=.j(érÝù‚Zp¬eÐ\t¼º©p¬¼¿Û~­eí¼ 5M×ÙÞË…í)N‹Ä¡7aº–tvÉcj²(€Â·ÅSYÃb8rïðm­ÞàïÑ´B¨¿â¯0gû¦Cu¯me¸¬jA£dMQTÛê„É»þÎ!ÐÛ¤º½�kÚ]Pôn+º"ÉJ©‘çIÝ»ûT5)2kñerïŒc½·¯ßü†©EÝbµ˜->%+x¥¦{#Ý[ñÔ½òbSkfÖ�ó0ê;Û�$J½ìáÁ'û¶wÛ75u¼#JY¬F@Õ`] 1`ò¼Ó!«V! +Ö©øB­ÿ?O±>¸�÷Ùpÿ³ÓpæT†ªzî<ÇêüÖÉ5øLRªÕàu²ñø~´èí¸¨é&ÁQ±G×vä4©~”Q§Þ[3òær˜1ïA;ULÜÄç/%p™lƃ„òàþô¿8«×!¢ÙäÆšÐ ù°tÒ`Sð»Ñ°Zç1ªEÉÑ¥qÂö¥F%Ä[ê'AGTÕ7ß#«¥—Ür¨5W.YT¢ýÌà„9!¹u£M8è•=!#ß¿ÑWz¯KSêœKIr.åÉÞsàØã�;ÚF©&'c!Xkm7¶O<õ®Ûã®nn"‡îšå|P‹.ky5®�{ Õ¬÷äÄèá¼ÿ¢›Ò`¿Ó↠+cvo)Çl@;Dø’cIû!ùCmp¶âÕýž;±Ÿ’_„J¶÷ƒ¹[r2F’|ÚHÊõ2ê”Àtqò�¤Û³”œZ3O�NÖF< +Çrþ¨ŠvÞ“Do%ëBÒP*{6FWé�¼ Ê¥ þI�ôé»|X‚vÆUN÷üúd,æDrL'uúöd¬ëóÔ5ý}þf½lýPOÏÿ‹ŒÍy÷ßÖF¥U²Å}c8/\¼|ôõi2)æ�]>UÅM§¿ÏW¾ùó›ìÆ3V˜…¯(̹à6à+ +õ+ +£E�Áø“cЙduþ3N;jÔ‰0ÚìBiœOjí+—²;:e­@å ,–U%ëæ §Ö4òyêu ÖßIgQ¼”pÕ—d3ÕfÙDÊìMQf“)Ÿc@Û¡«Œ#|FÙô‡ §\™Ö_‚°›Íž0Š)�3Y1TæÖØä¾ènG~X?`b·aåSð7ÆWV¥Á*ëðöìØÒìÔd—Q\%V]:Ž,”�Å ƒñ*äáíûŸ¼`cz{2Þ·€©Æ:?öoÞ¯ò0Š«²ÎýÉ�¿1�êVìfËÎê`ÍÑ'ÿƒðX-�å&ê z 7kßû-ì¾6ÒÑy&:ؤþý,Á…Òɸ®â°ÒÉX=ùŽÒ\·25!c´¾1:Zɘ"(—+‚Yl„Œ —uu¥0í|VÀñUgˆ—�Ï/ò³üõ3‰\)ª`r)‹ÑƒúT@O”xZçb^îÍÓ¢:|&S¿He9Æ´r6xÀ˜ËW÷Ã1@´†Ñn3e†¨ÉƒïÈÂÆH"í¦è@I¤qø¸,‘ £±*ÁøãøXØ8‰…U–í…ê±›°°ñ „éTžÊB°1AàßÝ@Œlø·zð=ù7Iü{Kt0ï þåðq þýÙô_�\P ü{?ÿ2îWþM\rѸûò/3~åßã0�‹3iï�¥Š™¨x?d#Tì*®Ÿ|G®fæÓ.ŠõmÑúŠ5‹�›ñ´¤X3ó\ Åú(‚$®NWçá�Þ9¼/S3ÓVW¦> +R§ŸÑ2¿s ¿HJNÝÝÐ23ta¢ê¹wdlfvèÂ_7ŧÅÀ¤=Ù„[óµ¤k3c³ŠPÆŽÀÔÜl·…©Ãä07÷fê 0õq�ªì‡ºŸÙ3Gá Þ Ø'AŠT¾#73SM sKd0()ÂáâfÌ,Ñ03Q 8¤¢G`gfDÀÊΙ‹”�á¾ìÌt�_Ùù0Hu¨wn‘rþýDrà¤À/Ó0|!çÝPŽ�³àÍ­ž{OÖÂ�ySdľ½Ê!ãf¼-Œ^7\/bcAygÉ–eíÁ T¢5ON#„TM~ÿáx´×à“Æ:ƒG{f +â®7Ο½®\½~–sxÖÿUI<ëǪ¸±*ÝKã1L/äÜǽqæä”íľ€7�”Lßc4‚Ò.\¤E +®Y{oIJgÙ¢i›$AÞw (›Ìd ¦/NQ[?öŽÂ“é׋“«4†x[\´'Nú)fÉÀ`ãfÒó�O­0Çt¸=‚bÄ4¼]ÙÝN oîÍíQàöÑ^­KÜ9IY‚«=ÞÐ)Ààìn°¨—ép‹ÎBÐJ×è¸#+3ÍkqJ ´ÉÝŒ8�tæÄ¢âv|,¦—¦í*鎡Gàd¦mÁÉ97åt¥»ò2ÓR´àåÃ0ñOÊ‚ƒkäÁK[ºûEFh>§Wýä{JFHO®—ˆ7F#…'{•ãÑqC) %’áúj Ê}G‚`Ér xˆÞà�}L»°B‡©jhJÏv®«éu_—x/u<âö¢Ä—»_i€˜¾:+ïU¾£@`Z¡|ySt0¢yò…>n©H®4®OÁäüÌÕjGq$I„(I?�C§JHÏ}žËÈjíÀ§€GûYÿ©Ir`XÁmÿÊ� S‡Ÿ›JçfàÒ+³LÅñ�Qt ‰©x^M°[âËJ6ƒ¯kU 5S¹Hc�úÓ +�’(®o‰JQZ߈ô¶/®tqv½ÜLJöäÖHü÷R}ÄÔ/ÚÉÇïíqŠ��©?´SÅœÖqÿƒ‡k��©B´¹F-("®[z„L¥ �*ÔÜMù™:Ak]áw˜'úuGÈTg7&ÐV뻚.È”gåQ�æ±9´îˆßJ 2…�…Sn/ #¤¯\ùÜûItdJÑÚÚx[d$!âÃaã‡(BB¦¬p5!�a§o‡ SUXp·IY¡ºo2E…w©N”|¹<™ÚÂBñÜ Þ3¥……0)|G¦f* WAsKdxI˜0Èø!j’�),,RÇÐ#1µè\0™Ô}K�©*,™úHeWŸ§ Žg¦Âpµ]÷Â4@¼L�a)BŠß‘“™ÃR¼Ü(‰�7ãäI¤"eJÄŽÀÉLuaÁÉZAtê¾Å…Èœ| $Òxo‰(dÊ 1ç‘ZåÝ~xFè6 +Ò£|î=™8I’å†È`Š éÁ`ãf\ü‘@+L�ᜂu93†+ V§xß$d* &>SÞ›Ã}¤û ŸèÝÝ ß!;‰�«'ß‘¯™:ÃõŒ¼):‚¤gsè¸cG‰^¢¤hD�ÄÛIàmŒœKwNÖC¦Ðpåíã0­FrÉÛsR\J毟[ñ>â`”Hg˜¿Uö2�Ö;ˆAisñÅ$»j>éÌdø¬“’ê-$Sê‰vË,§£CTo!³²ž–—LG¦„±`VƒàÐ 2bs¸ 7ãgy©>‘Ëc„‰AÚ ´£@^ÈL”óüÇ30¦¸`DA¦—B¨§±4ƒPØU#ïÄÏ7­jŸ„q:â´äÊF×�V¢Ña/ }²=Új{ˆru¤™µ­JÊòë†g—?ÁäF–Gñ:ÍŸBvg5+ÈD5:>°3¢½�JïûÔ…h•9%­¶Cäêëª ×ËîpùÓ»r„•í¸Àr8ÿÐþîsž [™ä ½_~²£ÕÊÿ|à¦U +/ ï½8ï©^³5�»#ù”Ã8ö¥o-éȪ�ç-ffuúÐB�‹îHG¨>5C(Ϩ,�ûBW=@góaθ@µZ¢Úè@•öR-Çý�‘± +ÞpÞ$ �#˜Ñ´™Bø[D¤¨ëB€Tªúy�‹#M~Gª Û˜$èLðBòæÔغqh­•·Ã‡�ãâŒàRëXþ.>E|˜×–Zvu“ÎiZ*¼ +×Þ‰NÀÝ}† Fybk�Õê]×έ)‚ÙLn.°ºÉÅøÒ†u|¬hOlKA¦•FA³Î䘇٭aöÖ+ÂÎ#yxgÐD•–9äT7­_3Še}‡Ÿðò`Ý�æ�…ÑÖÐ_ØF1Y�L´nÅî^ê4 .¯ås B&ªŠRŒúD°È®&ÁeÌqÍ2êyfþ±‹`”RáN˜àè!m¯€‰¥ +²:‘ßJÝç�0“™!NÉ){'„µzœS0„ë Œ´µ«CâÇ ÊÍÀzïï„7Q[±LŒFïÅÛ ÁZÕ è-#èIãäÙï´TïJò‹ëKâ= ×Ñ ¼Ê¦­@» ,Êã×ø¸¼GÿZi~ÓMdé,)ƒiR’í�¤uÙúÍC?tðÖ7èÊÊ5ÅŽo'zßùN�zT¨Ž•‡K÷�¬½ò$zDÕÈærÆÃ·íè"\†Åˆ’ƯF¸œëë²r9*°(ŒSÛÜéÚå\Î4 ;öò�R~˜¢Á�å¥Qq™áþV¼…г ꋟ:ÐT\)™4—GÏaH]¥½»5Û@ƒÝd}— ëuµ`ŒÇ~ó $²:àEšµÍê.{ÜŒ¦/o¸„èˆåUíä#¿–?È¢á'‹>õ®}!ŽÎcIE-M–X¬¥úòU«J7‚ß`³Á·–±_;˜è‚üZ-¥ûcqKʳNÓàš?­¤«b~€ñÇ6=ty÷Õ’M¢Š>:©uì‹®å­Þí$–•�,q�ª NY_ÛÄÒ–B¤/HmZp¦È¨-É_Ë5µÂ*ºï2=90·±�Bžjp‹5s¬¿·æ…"×Ç*'f;¯ýq`•Ñ$‡‰ˆÉöúEv—ž´!¤Ò°Î2Ý·V‰¡§¬À¤÷khT†�ÿÿÀK�Z"J½£�NnMFMj®§ + qÛ�äËQˆ–³¨Kgˆ[Á8êò™ tž·»¹›b©tBõ²2é¡^UÜŽŒ ³\[³C²V‡+ÔbstvÓðßÓ( +ûíB ~Óü«Ù‹À™mÜG@¡d4éèÁ+·ß<^¥s-©(bˆŠ&P‹°‰=Ô×=ñw«’_ˆµA� ++dÙ¬‚"qçŠ/3ˆ³L¯»|0*—‡ImîßµœÉ–éogóô 8É“ƒd4ŠÕA’ x�N¹¸KÈu|°tM:#Oé+ß=x¨™AáÐ/¥LëàÀk•v+"|†ÌÊL=³È0ʹª`|ýiÇž$‹è“+½Måk®ïÛ£dá°€Êú@Ñ5&l$£n{ Éaô9èá'èÅÌÖ£ÜÎhã)�J¹òá0¹íß½&!± ]Ò êã�¦ØK(�B +Ú[ð:WnÕeâé/´ÑÞ®aüбˆtÖÖ¹îŽÎƒFÂÀªˆs¸ä™³g\î¦`ˆY®'dr m˜æßÝ�õt¯1Ý(‚t'zt[¯«"Íi°±Ûd}�çÒs*Ó{(A™\¤V}Q'O{Æ�d�¤¤Lí‚Ï%œûÝÿÝãü]T b\¬¢ê-$ÍfÔÐ\&r¯ž¢CÖ ¹jÕº¦Z¨Éçáße.OÚûÜf A'}¸ñµW÷y;„ðÈ&îT!c1½ÄWxê ôóB ]ѳB0WˆÌÈwµæ:x½|¹Y]ÔѹÿxqY{ÎØ$¤1–�² †¤mZ>µb0ЛÓ2�e­AJix—]*ÔJÁ¨&{ÚÛÛj/¨ûh+4$¦f[®ÝíJÓÆƒQ:y·?¥rB' Jye7%]ÙKí®çÜã0™¿äÊn†p’Æ,êÃÞÀDDcÿY¯ßüœ,' H8%—sÙÌeò-‹šÙîÜ!y�R¥*Ôi®¼Óî€ñ<Žó� æ!ÄÇ[ •®èS9YIqµÐÎ$@Oì:§ª•”! îºþ©wŸcãöv$j@ªÿÚ²FÜ¿]l®N™*Ð÷¥ä%òU6;õõnòm¶É ëtœß‰–Ï5éíÝT�V\Ñ{K7bÕôú¥±{ÿfÇèŠ^ ]w­òææ¾�B\¯˜¼¿È?Nk!!÷¦åÐè«’ó#!x§Â�ÉzùVÆ¿¼>÷æéõ!GåÓšÕT)mV \�Û¤sÐÑÛ�ÙçüÍ™Û5ˆÅ2ß§ûúÝ]’A&¼Ýjd6ô޹>‰r�ò�¢RèöÛT|Ø­.¶e˜“�¸vEàOefe�) +Zëz{Ú›;`-©Wòå#Ü Ç“fÚ«ï˜Û^Ë÷žj=êë7ku-77a!à³v`„¿ÑôŸÚ¯¿Œƒ’ò-7< ãü`Ç“ò-7Døc¸jjEõ“"ÿŒž÷´cÿB€ÌÜL…\»1Ü kÜÈE *g<Üi�²�%…L� ϳ©dQ…x'Ü1Á5À­¯€»-²*F¯`D‚� ¹yPÌ��×ê|N!8¥œ¹+öVál?,Ç®>3ía©Aº þP: ¼‚˜“zÜQü‘ì©R±ÜO{Ü8—S:¢· _yüÀcÐÜw´}é4‚CÆ[­ó¬n—î…CÞ=mó ã8ì3åø›é-˜C$äh"š6hmfxçh‰úãÁBéæ*5’¿»ú;éÕb~ç/ž_þò᫯_>ûìñ//_}ýçoþõ�ŸÿòòýwzüݾùÛ_¿yüÕ÷ß¿|óüøeþû¿õoyúêå/ß?5(ÍM¦j©<ðMù=þ£ æX¶dý¿ ¹û + +endstream +endobj +2202 0 obj +<> +stream +xœí}Ù’l·qà;¿¢©�lRû¢àÐ&)Ñ�ä�C×!ù�—öå�ššv+Âúû T×Á9È ¨sªx¥‰yé¥ +K"‘rÏ׿ùÓ×�Ÿ|òúW_ü÷ŸßéO?ýüç_¼÷îÌ�¾Ów¯Ì�·*˜³¿KÙ«B¾ûæ�¯¿Ñwßüç�¾ûÏoßûüÍ�¾{óôúAße•ïÞ<¬�ë¯7ü퇿ÓZÿ´þÐõÇõÇïžï_ÙèUtÎ�¾ÿ�¶æå·¶çÿO­§�»·ùÜFÇóï´|wúýÀ ib?äÒM¿tÓ:œ§ò`Øóoç˜��½µÕ�úxúù–�tþ⥓ÖÛµz®‹ßv9Ï@à³áoB��ÇÃæm‰çùÞ‚Vpö}ó�ïýâÍ{ÿüÞ/~õÅë-ñ‰ø’WZ¿“ø¸ ó=jÊE¹”Rù°oµÐ Ód»Ç©±ÜÅ{ •Äú²ûÊ´;Ÿ+ Ãr8z}’WßÈ |¿@cõº"k™¶‘Ú Û_ øçÏŸºÖƒ-:3»dØî¡;’[´ ä»!Y[‰¬£S&ø’vÒõÇ÷¹�v»ÁûB™¯B¨Ç&­;vsÝÀ±»ëoÝÔn¥¶Eb�mH’ÖÙÑkôÍ1œDäBAÌËÑ–À¢‹¡s +‘@ìæ:ïÛÿˆ�p8Ñù›ÇKon'‘x°Êiö’øÜ irP1y�àZé¿Áê¹¼[–¾õÁ³Ð¤Þ0زß`s{gP¿\¡/{�ðÙA-Ïå[ÏK¨^"To”ÞĽ„ÚÎçJ ç'¯˜&"í{�`·^%¯u`a7wYá�Ù@rÆ€™á¡‹ºdTJÅ… àˆ}¾wN¹à=,.Ømûß7GLøšã ˜I!× €§ÍÖ]±ÌÃ;‘çLü0{Y'´S¦8å£ ñ0�÷¹-+ðàdŽ5V•˜¯O7àyÝl"™@.§kü(rŒË½È¢¤H'Ý8•s‰»‘ÂÓ»1ÊûìsäB!šíxúâSòY‚‡u}Ýà(û7OOð Ú–• RÅÌLl·ÐÝwZï~müQˤ�Ò®Øýô 'ã�åùÛrÏœü"þtý eÑäDß‚ÃÊ/éÔ0<+8‘Ë”�ˆ}ñBÄåŠ,Ž‘T@1Gål0n¿(nÄeJY%-ã£S±èlÂhCö«gšÕÞB � ŠU%hŸg>¥¯‚WÙhoÇ„>�/«W:Äšƒ~¨Gò}ÁíZzB¨âþp îzE-�ï…{¾eYh1jŠuËö$Õ·@+»>aãûY’’b +Ê9³Ÿ)ãõ<@áõ/¿~ü·¿}¼G�…¤LtÁÙ Èì²ÅZp†P�v -îdíÜPTE`ç”NÎËö^º£µà ’6` ÇQCˆ Á)S¬KØÞ¥� rÛßïwGù +*Û&ˇ�‹jè–Hˆ56*çM¸�ÜÕ?¸ÂÞÒ²�`¦€œ3�Iª‰Ñ+çbÙ}©t +`dVK–ÑZÅ”"R3S�ã´‚󳩨œlˆã…]JÌg°6tznòÄSûó†×Y(¾™ç„…òœjk’;¼ep7h³æÇL2Ê–hßÒ›�9à& ‹é@Ó…§k²™°XÌÛÙà•)9^}€§ÝîÐ( Ï4êð‹Ù‹à”ó6ïgòá£(J«PÄÉǯŒrÑ&g^ „ï׈•µFURÎib�úÜ{œ1Óbê›M±Ç��üh6õúYÐc@*lE¼K«˜ÆZ[tºm¨˜Â¾p$Úbè!éSÈæ›î¯EË÷üþ'éày«|±þÀÁ㸠Ÿ”³·Ï +q¨­é÷„3L‚^HäƒßÏBÚ_½†}½µ¸ÎõFDP lÎ}{ MFÂ2ÙšQB®ž´ õÂe÷ЬÉm–&à :KíŒò%»Šòåņ’xbUsÕYÊ(›Rvö-¥fÃÏ[…Œ‰¸)£�Yñ@¼ �›ÙdЩéy[ÖüÒ&¸r,oX“ULƺ ⸊�Ù(&4¿Çþ›Ç&¼mBÁÑ‹6GU¼ËW øv“ñó‹ +Z«UРrÁ½×c-Äü„‘ûÔwh'”©Ž�.êê|ðq¼Î+à3üÆe“µßš—›NL¢JÌÑ›è•ÍÁFÖó}r*']Ò.1ÑŒx®&ãâ÷…ÉžP×NýER}T¬®òÃmPŠ8 + ñ+G±yß�ÖÈ“#ñÏ �‘uÕEÅàö:¦#ÇEàÁG°ë®>R9oüãÀ3÷e½QËq ÖŠùšéò@»Á3þhØóY_h ?å¾–4“}Ë9]�fºÓ·ÝŠÓ’ Üì$±©¡dÃnà +�h¼‘µUÖhÏXÄv;9Ìa娼÷¦Œ—5ºZ‘äÒËìÍ[Mðõü’1A%ír<Œv­›³[ðÔZ +®££1y›ÐdÏ øI¶õò Nh£NŽ*ŸÚݽzX:†žB,…ä*±´!'cØïmœ!Æb‹ÇœgHJ§�ý 8Ç5ÛÏÛ/Þ§4ØíHžþÿ@X ÅÅjå“Iå +¸}¾7V墋“ß­¾7eb…�¹®ä;Ñ©8{àuSó ¾E¡4Ì‚ûÓî +Ç›§ IûRTÙ„Î_´—æz‡„¤¼ŽÚõF§—µ½F×ÇÓ�¸+Q Þ©´äïz4&téè<Óø›?l½L𪻠 2âÁÐB¡B·{Ít=ø�®æ4!«{É1?Ä JôyoàâiöÿU|¹¬Õ¥uÔÞ^®a=Ù¨X ð$Vj4^yØ·¼\’f[‰‰œe¸êšž_ìVG z\Ä­ÀÞ[k%äÏAÛ¡mÒG‰^CPÆx¿?ж�a¤AÅmY«²±Æyî^… ÖÑ ½8�£Hа7+=É�ŸhQ�0 Ø{ªÂš}~ÙÈ¢µw©Ð†Õ úcÎ ì{í¾­PÜ>ÌÊÕ^R +ÚÅý(xC°ÃFwËŽøf#,$“ÞÚmgV2â“¥¬à.¿vXè¯Ú’œ�v‡‘þÕ +Å`¾w–v‚. º€TJ6Ø^ŽoºóÌÄ¢ti“*Q0ظ9CÂJ¢�d¥} Ú“Ò6År0&Bsíí}hæb·ù»i\7ŸÙMÏ…73ØÆÄû�U)¥i™ÛS±q·6o4Gû® VhLàA²…`‰ãèVêèݯ÷<ÚrN�X× ‚Ð,'Y ›,EfULÎìÍ=À¼8É©”´ ázi]Ð�½È„°Ï!7Ò0Í0é™o#x8­}@³Bÿ§fK:ÿ[€S�{z @âLˆ¥ñâ%B)â  žjE�®ÁáEÛë;*å ’ÍYOHÞçu˜ö†XhÌVeÒò W(¿¢'(Ok,�9ýõÂ7ÐXet6&Ç9FtªÙÜË+`š0Œp±ì¶\”l‡ÙSéºbó’ 4€ñ£ìJz#vQê£øW™á°¿P(F»¨£@b‰ô]1¦:¯¶ òÙ7Ïþúo¾ý¯çO¾üÅ—_jýÙÏ?=�8ŤݟX´_}ÁÀ&™è|ñ*f_öËÄеtÕÀê rñÑH¯ +�‹a8ÝBKQp¦yŽ?¿1*úäÊnf }͆»¤ÂX.]�­�[ï¢ÒNûx|Ûˆí!#í±yQ‡b•ò ¡neg·—iÀ9ÀS?Ðüž�Ö¢Õ†1€‚î™Ã°Ï®%ÉËç—ØÝ>\=g�%žþ‰X¤�e;^¥¤¢·ÑNvÀ#€ðaÓIYM™@€|nÙg?÷U/˜-_  †B• Eçã{…±�Sõ É�Ýïôj_�£_v.`÷ÎùŸð ÍÇf|Ø¡¿È&Îûiš¿� ïSeuµßÏÂ/�Ø@V^»àUÑN›p‰#?êF?¤pìÎWêaJbâÀkl Ñ�ˆµß¼FØžy&f½—ñÉ sïÅGRíÂñÀ(wým`�DÄ’eÏG£²�^(4À“îUÉÙ,ZªGòS ²7ƒ­¥]‚ð,9f UCBÂ_D =›L�Z7¤0Öu\�"!§tIÞ�÷{êÁ„1å/HÚŠ.^ëP¥ �VX!i£¢‰)¦[„{ô˜ÎÑíH·Â¯N”Õ‚V¥„C²¶Ë‘ñIÛ´Œ*D[Bžvc€= \ßÙjh‡¸�uÌÐ…»6­ÁDJK°2?XáX’ð†D@‹KYº/5k¼½úhY‰¼Ž§D¸“Ð/�§c)!‚÷ZimöÇO˜/…�X®ŽQmÓ dã¬|YådX›÷õ#Ýg‹4P¬‰ûnLŽ n– “ö�Iǘ‚©ß _ORu?¶ñCR'lþÀG�Á +�ÛU!˓Ԉq~jÄ$åúö¶(Ì‘kp×q©‰{¦{Í•wYç=L5ì<{i"5ÉËï{Ž»KœòÿëÁX„/âÉnñ"èå P!¦¼€q¿§ÏnœøœAø^å=Zý¥èã•d'ö&+’Û/®se8`1€ß»¤|ò©Ø]A +Nº£àÜÄ"Fê™”O0Yß÷¸–6YX:¦ËJï-¥è’r.ëxý­½¶QjÚ¼É`>i2hSù÷r¿‡ðò©ž@w°î×|^o(ô�Ë`î±hš$§M¯“r:–½ò%¾¨�Yê½Ð‡�iCx:ô-ç=.`?ôâœ÷¼÷ÝEýP�ÃO¡†Ð±I-Ùig™KØï2vö–ùÅ®õßõtߟE¯¢I5#$ó„,9mº•?Ä©ÜO«øûíÅjrú xêøÝêîÛšú>8^fã†O°Tí†Ã<ÎÎ2Ê Úw¼kÏÑúã…íúMýñ?ê��˜)›î©ç7{ôrÀ_ª�|89Rce`C&â\¯-%³ã�u¥ÐÖ$é˜\ÊÇ©’À±¼wÈ/†ˆÓ *åbóÄZæì÷?<Ñ‚�埰Ôk­¼)Æ@èØ7Kf`³ÊÅjweÈWF»¢ŠuX*æ�¥ Õ™Ìí[¸þºëEý‡Nâ깜·bä»!i„]ycz)•÷²œÇß%ÉÝ…¤rIuåX&e$v�鯘�n" €gÍú" Š0Ö‡¢B¨©‡Æ¨ë�Ž*{‡Œ]2%¬_�RNð;@Vhƒv8Ó 9Ù}ñ.^ëY2±;Ÿ”ö)—ÙÅÔ{OÊÄ‹‹mìRî> fÃ^sRåf´G®îÆqw0ä8ÂÞ%M6ÙU¾ÉêQ¶o\žž%k»sQ™’öÆ*pVð`×ÊCƽÊüÝ} I/2݆@ØA +ÑïŠfSt² \ ]BþšöÖ jôÀ¡ÅÁ‹Jï0 X˜_žÎÞóâøËöWýn)èñÏÛèì²ÝQôrßKÔ-)pœ Êù˜v[­ÉÛkó7¾™š ‹®ð YMgœ•—VÕ¬5j�šh{¿Nœ½ÆÁq¨Úôè+°CeÏJÓesÄâ7YԌ謢³»ÝOëù–d-ŸÆqƒ‹Æ)ªj2¥1”ã8‘¢|p‰½úï—™‡˾Å)WŠÉáH=ÑÇÏê�"Ôs£„߬´­ñ7À™à„!y>ÐáÛMG?¨¢(t|¥¶’(›ÝÍ H]$(ôdcƉ.²t ÖJQÛ2ßÛqnà²]$e…-IE—�(G §£VN[”wÒ¼žKÐ9…�ï�¾�ôËeÆæÒU‚èœ}�ã�DŠdCf]fî{º¾úu^ÃÌ8Ã\«5¬÷ÊÛÓ˜ †Q ô- +õÞ袴õš-^THnl-Dù‹œ”wNO¬BÖ>³šÃ"É×Õå$UÆý§ dJÞÞzõkUtŒãÜ‚ á%Ê}Ü�´#«èôÈÁÜ]0èÝ`=ûÀ0»«^ö½/�R‡Î­ð +ŠþTHò²MA—öKX@¹}*I#“?XÏZ8»QDo%yÑ <“ÒôYÎêVœá’q5¶Î;›¾ ÎÝ¿ |”2êyÞ xâ;Ý?*H-mæ˜T%á×F_ØïNo}Ìè�sEÅdõ°×®'µ'ém<Ęß# +áY­¬®ìC¼¼£\:…ÈV½Ò%øã›‰7ùï€ï¯í9ÆÇåÜûï�ôo…$™ð�Z�…À”—dPã·Ir/·Á©âËó½ë•1i¢h5h(ª¤îèÄBü¨MÊQSâyR9›âª¦ªu!>ï03-�LTæó±FOg{xs§}yŒ&BŒkuZkC9Œ¸/ú�?À%ýÞDp£ KeöJgkãŽÀšowŽô±È +:>2ã¡è6)S¦€ϗѵ^«l‹›¡¹ŒîOîRþ;öËçw¨˜·µ1©£9¾q Þ»°¢â™_ã«sËá2ö�†~ìG‚ìj] £*éVøsÆ_pñ/¹~ œ΋{ ÿÔ‚žaË _ØcS„ªåÅÁ²Á¡GÒö +ô,Ù¬³Êúœ®@ÏP£õª>¶)äÕÓW6çprGTU´àãš&ZÙKX¼Áô .¢–.]~9 Ï(gøòÈ d„sj"JiÁÆénª”<1­5Ê•|<2eJÍßžÄ7Û­çÏ^�í�À%”Œí§/µMËç�«@¯uë]€Õwá*ÈÈ^ä0î’iMÑôÓØNj´(ÙèSÖ§¿Ê0c°¤Q¢2Ñz3|ì8gMõ‘´ÂÃ×·8Å}Dú»î-ù +Þ[`&¦2ªð=°0_ÿ€û˸ýå™1VÆŽöÈÍÒÛ)Q]z�b$‹`MN‘Ý�Âgse<�yQj#™Üp-¯_ÐX;`:)ÀÓnÞz0NëAå3œ�ûXCH¯ ‡ÝŠ@©ˆýZû¾?kq/ñ[§ÿüŸë�OÄÉy¬½+Œ‘ܦM¶ªdw¤”WÏŽ"‰âU &4ß°N'‹lð°9&j¦æ_áÄ”U 7”§WƒìÔ+aÁˆ »Stó/�ÌNHþÇõëÒ‘U7òÿ$?]B OàÒ[&¾8 J¡Ë&YeÜ•uÚekÞlkÇ”öÌ&⇸nx+ûÀì@òUKþAÇÍÑ[0ÆÞ� ëÞv_D~Jì8†ŒrÄà³×gù!6ö†Ö[c$÷�2.ï£xyX§c«qPÅÛœãÄ´C.A�,é{౉$^Ö©sŒ3+”¯„O&ä#)RŒ÷Êúy¦ÍÞeY>Úê×~(A�ç�U*ÞgwŠWýáJ�÷ó¡+‰@».™üÒ¹�R¹·W3~]Í@=[?{zþýÃ×ß<òÉë7ùÓ·¯ùõ_¾ûó³´.«dS§«¾’¥bðNß™»?Ö~Êû”NÅì¶žÆüæ¤.ƹr§ïþðâ“CLwúô÷ù›µÛú¡9u^þ˱jå²9�Ñ>ÌÚèjBnãætn¸NÜ>úæÎ¬ÿüa;mûTo=ý}îùÞ¿¿÷@!¢×¼Ìgj¥s9‹ºæ…Ù;ã’�wZYë‚-ÞÔ?óéï/¡$ÐæÆ…?KâÁ«¤‹5 H_ƒ4M È_ÀKt¡(í«ëøÌN𙍸â”K9ùx`æ¦JÛÞÔíâ—ösnY«dR6Çaø�`c‰<1Aé`Cb¨³’£I±RçéŸî´Jõ·Ž—ai›én{¾Ò•óýAÂ&‘CÆfåu­!u[º"^\gUrá +óJÛHxϸ¤t,)ÞxfÂÆW+�îÆ·‘Œß•\±åvë °â¡r„EÐJ'¯�ܦç33[FÍ8Â+":¥S�L~wÈ!œ$bRÞ8coK,ŽðHFÕ DG'þ‰0«—Þ~«kÝu-yµÉèbbÿ%ÊÒÓ×Át~¤S¯¾•ÀÄ7iŠÊe§�`n†R’ðÝ2ࢠ+½B÷ÍÕÅKÂqó£t4•o»)Ѝ2(A¹ª—ØÍhèÙǨ/³õÏh“æ"è¶q°Hý¿(%$нp®w£nxh$ãaMv”’}‡FB”`;Ü ñ¶è%=k\)N+h¾‰Tëþ¯¢�å¯m +¿?Ä·p@ÏךƒˆØi‘Ú-»Åã—ÜR¹�š-öV˜ð5–Êt¤£JöÖ0ñÒ¼¥Ò5_¨›î�—”=ÇfÉð WÓuyÜ]*Z廼¬ÿ?<m ~Þœ1ÊEì]ÊVY›²ÿ›b +-‘ŽÉ�,+%�%½COK$Ùq'Oï3G?˜îôü䔯°ãÍJ1�ØÅY<Üé}I•±;†‹À#‚HÁáO¯‹�W£ü�„eS N—¶5ަÓtâÅë5¥µ+Ö„Xï÷âã—/�ïG-²[(V`ɘþªºŽþ0‘þTØ*Ž»Ê(ÆäïῦKh!ŒÉU[œªwË£KEìžL!Ü/#~ˆˆ²õUùäõ¡s4ƒ"ºö¤a +ß•lÅô&²g òó:NK„ëú“§q­¥w¡¼ k‰@Í '�¬J%…ð·Å ±�+'pxI—ú&Y"àsóôî†gì›d‰€ÏZjUW9ånæ«Ç9Â4·/F€WtBÐ>1F+®.bU|.ŽbUð±DôèæÈTB‹BšÄwwbŽœ¨ ‘ðÁ%ÏzG["ÎÔ�€œû¡˜9GIbUïxç½>&ˆÇâ$€Ç|`?FZ7KŸn(¿¦åu±ü¿EùDdë†ò·K~çQ8ç߀ù™"`vÃêïÝЉ;”ˆ‰ÝÜÞZ¯ÆÓþVŠxR^ÌÌ%&ï+ºùY2¶:…wtׄÓOÄÐú“Ó‡7‡iEbø‰ XrùÈUirp[¦"BÖg¯RpæøQ‘' +G/ÝËs,^¢*}ôÒ0A„ºnžïUÐñ]ô}?¿��rÊÁ¸«Vk‹™k¿RÛÔëb�Œ%Bi7<ËÞÝ›xkˆHZ²½›H#çûxkˆ¸ØÍË{”–w¨c½¤e:º;Ò¡IÇ&3"Àus—¸ tËÂþª=ˆàÙUí±wE3;HHµV‡ñ>œXP\¡²› ´AE_‹hý5¹X"ÆÖU·¯c8óÈIá´.håRð‡§ÎI»Ù"UµeÜb‹¤- ”76«¤MH`o�hq­ž9¸[ÌcyÌQ-TO–¿î¤™@}å®`S_"oqDiii…àeÉ-„[•�9扅K .yˆ_• ¼ Ñ‹ ‚ûF|vZa5TÙ‹êáäíÕ¶íê9è,ì“WYWh�nÆ+(üS°·JçܶôA�ÒvñÅÁ8•|ÍÌtx?ú‚­ŽjêpP9ÙI*",ysîkqX-zè ²ü2 í©eg‰¨dç�*)ç1¤cš1ú¤-�.p¦Ø‚;®¨e§º}šl KSPyà3È Ÿ?­?tûñAûâãv O_|F�qjðú;ÝÆÿ˜nðU›Ï#l±“¨1¥�uJi›ûüR`èõÇ˧oθ5­¤ààxµ«=•«¶§ '쌩Œ�JDGƒ9½Ë0ÿzÉR‰]£*ÞÄ â6 }J*—”&¶[®/ø#&Ju[ðcýTâˆPü`´ +%‡ãDIl"H‚Þ`Äí6iôÉ��M2o‰`ýÍÁ ^i—Ï¢ky?ß¿ +F9§­’Xßð‘ëïåž(‡¹cV‹H+à’W%ë]µó™4–§ÀD&fŠaÌ*Zgãá�&v¤/&ÆÕåÝÜ:s; =�:'›ªˆÔxˆ‡£Ãʉtñæ>OÚæ…˜™SØ¢"ÆZ0ÔûݜΊq\lkÍ™ßS”�:k7Y¥´dÇû×N.|¹4,�ÃŒ„¡Ùrn ¸�P‹÷‡{Dä±ð¶T¹+äñ øæ>hlPÚÆûWîTK9…å A%^º;„f"}Fж:¥g{˜$©Me‚›˜€+�Kg•ZïçNA¡ +X#qy3CP:[„)P{æ‰i­'ëå�~Dqî +Ÿ¨Åd‰, ®Deu±3˜ÊÉ]=É_ n’ £ì-•×›lZ=$@ØWlP>{—¯†P6fwZ~CUQ<'r†øX”K.§k¢Ž£hšré^MØ—œWôyJ‚©¡Ùº¤ÃÛÄn�QÆç”_.yý—^;œ"sc�2é€þ•/±Vqu«®r hM/†uÎ@HK2,¤²êŒÙoUØôyŸV_—i]y-~糤þì•9é¬G ý °X#ÀEñ6^åT9¹ƒIc–Ûd.ÁÃÂ�ÅbÅ£•IÅøýoKgÈi{¤Óv4—G&n´5R½4K¤¢q!ª¨sôà_˜U—j½òÎn´ƒÜ7m+6bQ÷ÂP£ î¶}Ç2U!&gݪŸaKX“n]˜�®ËÚ+ížJ•Ýx�ìC)ÊÙœöÓͲ�›µâ òù>WõxŒéæV¿kJñŸ´Ø~ -í¸PŽ}û8 +ôX‚ÖÀÁê\‹Ö÷'ú‰b er«çÈ´[˜‚Ró«°t8`ö'rÔK:Dê§ æ¬|Êû‹Ï"^¬QA‡ÁË©†Ø½‡^oöÔT\+^ ã“u[å=ߊ5ê!Z®¨íà5¶Õ“ÞP` ú'º�àhÇF|ó$Å|¨îÍQ(Í$S΀L€‚P}ÛZÊÇ (|Õ³ÀY"׫•K +G1VŸ*«ŒÏÑ"FÕ6ÂPµ>˜¨%|rû¬TN¯ÎBÃ3xl|G´{´g�`Âai‡P ÏïûªY½u>h%½2½šM€¼íš@y’lbõœÖTx¿6¬Ø¤\.!'ÙÞ†ÍH½¹ ÔïI£pØÌYªD}/4ƒ÷ÏÃ[ÖjÞßë© øÈÌs¾ƒ¡Ž‡|$…Í¥òã«böfboå;ð¨?‘èÎg£JÕ¦;bç}¡Ÿ#V=>‰‚ï„Ò«ÉĽ“ßà‹*¦¤ý粩]6"ÀêáUÔ1ÊVß§,Zl=6úa.3õA$aë sÅ9'%��X=šÈã™vrÄ K®QÁ•šŽZp ÊЉ‰½Ÿï_e} +µÜó`ÕH{¿è1?æø8s/ªõJËöHnXn@F1l”ØϽU�|–m£ÁN¢@YØ?G@ˆèhWTÀج¬Ée·ü ä¹&  +†\Iß�“LÈPv•np™ #�UÛpŸ@Uí#*m�¨š0FEŸÃÞ­aO²ñÕÙ{£aêïC(¡ÛÛŠÆ#|wa‹ž;å þa4ãÆO¶€˜ÖÖ=c%mÀý±‚™Á>º+°“q×sz{§<.±¾9l7T«kµP¿_ÈŸÃ�QÅgühûÆ?“ÖEèY‚S!ùh'Ö52Ѐ0ÏÆA·(Æîßàaa…YI¶ ++÷î@±DŠK*D_3µFõb2êîtÆF;!\ŠWGäá­ë(QÇãgÓ<¥øê[¤($¯Ü¡0îˆÜ¿ëPÓĨ÷+Ô×›+ŒÆÚRGäv6*c½õc஥-}ÿ¢¼ŽH\½›sÑ.Æèóý+_”�IïóOwD&aWªÇ{réûFéÑòŽH ëk¬zºRIvTŽòت¾né8N!‹úvsv)ñœ• $’©¯’s%?æ«c†8!²�ÄŒ¸,ÐüòØNGåâ]/¹”UŒÑ^Ïl¸r=¬1ûÍGe“[äCÈ™Bu hN8wƒø‰�#È› à–xþktµÏ%Nàö]EÎ:*-q*˜ óqš v@°Ê°»ræ÷ñKÏM²„Æ&Vœ‚ï>çÚ_~–ˆtÊ›³“Ê&äÃ*ˆ~ÝP\m×�IA…`|ÆGÁ–‚lyúæÓ¡Ëèfã}JÕÔ1Hÿ‹F¸(ô†ayX¶…ËÄr÷n”‘áá«jer,nÃö¼7“HÁ’ Ǩ²5B2ÇYžwãë•31%;[‘’»º¸X‰!dCN"¥�: ÕC¶âð-K°›9«xJ6t×PFˆ^¯Ãiî˜Êüm’rQÛÒxw�Å€VA):œ¤)Ì"~µ`œ;ÝÞ<ùÀ�5ð‘ÇsïxÖ’t)ø ²ê¥9Ä)sÖµ–¨‚KéåÂ;e øoÌÃÞ7�»‚·oèN:"€ˆ¯‡yäRŠ*ªÃÆÈÝ}ªTO‰†òs›È'í•AÔQ›|˜60ò© n‘Ù×·n8¦#Á#ˆÊÿ¾žSéjô£n½ii QÌjÏ+µ”G¡Hšqè�NGLwÙ+аãL =dQ¡*Y“ý¢èÇÞí®aìÌ Èm}vÐn÷õAäóÎ)ëK½>†ä5­`Yv Ù9%˜0‚U)„r€â—+�Ë.æˆ$ü›“f�Ê®VŽÜyÒž„pôš6¯Ø@[Š¡Ï7áÇà %ÝN ¡|rˆˆgl-~}ƒ��™gkÓÜ4�$¢ö��Ao´ŸØp™ö¼¹ZÏj�&[Žï¼šÒœ¨w|®‰aýÌ2¯dËrTÁƒõxÕ°Ôhã^0X+¯SцÐÒVA‡,OöíÙøñ í�M|(¡‹F¥PôÌÊÁ„Ï÷ÎnoŽ^ÍSoÚjð*¥Äv]¼ò·÷¯¼2±”`›þ×nW÷“>¯lþc @¸Á¨Ô7ÀªÞþ.hËKn9òƈz¦%TÞ@ +v†eä3,½Ó7`ÿ4*�çlˆc=Ú¨óÞìR›Â ¦QI‹&’*;ª:†3ÊdmÆPM°å)+´ïCéPú è[ƒtõ`œõ˜I×�]%Ð8CuÙ¶ýûÒ²`@â"Jð¸'ÊtøàTŽÁÅ1òoêÄí¨"Ù)cÃíÈIkT+bÉÐã�{.h™~\@‘¤uE«R³å¨92$ø>×%e´5i ßh{´�À.È¡©Q¨ µKmïD‘SÂ\C¾6Òù*GÛ7à£8ûS nB+ꪟ¶½õ^@¹8¼�ô4r*ž)7NGÔ>ÙŒäkn¤½šŒ¯HRÏ~ãX2U4Úp‘€Ï/ ÎA½÷‡ª£‘g&8ë܇üO]dTqJ§l(!Œ÷ëÆïUHƪ`j¤ƒ´´õ|È"$'@BKœ› w?·O +ô«Å¼.å +îgí9­À­µc„À–òì�‘ÚH'™‰“°’ÞÈ]{Òw›XÓÂÙÂ00‚M¶?Т‰6VÄ…Êå +¶‚¼r/"­å~³±x�##6ŽÁé„hMOZI;XåMÈ× +a­ ºR˹¹¡ŸjÉŸõÁ˜˜A{YH G}�Gn$BmøÆá&ž:Tbœ¾ÖQ¥Ÿ‚WÅäÇ;´fŦ·XrÙÖX”`kvdxÌðW�àDA¨â”9¬9L‘ê5ª0T)*yìQÌiòJ˜¬‰úP¡æ!ÕÆLìê„ØH¥G&�ò8 ˜# +Hm.˜ZJª +KW¦C”K6!…úNK�Lv-AULöcÈç½Q�ÓÊä˜;þ¼çÄ¡�¸Û©N4£ÐÁû3L¾€©D[ÎS±öø·Ý“Dq¢ÊU•þ¬³>Þ,j3èT “èîÝkEÔRVý¤tLG«w؉êZ¾ç“óî(–Õ�‡eâ×f7� Ó;U°k½ÓœVþˆΤ†àùþU´*'OäoAdÙµlÏ��äwh‹ïçÆ]¼²TLÍQÞÖãaŠòÎùýïÁ‹Û8� ç Ã ÜØ ¦ß¿4-•¨š#*Éy­•/Ùø‰Å�_¢¬•�våЦÃhQl®–Z5ÆÛx…}‘ËU‘ÙBŸa=¨ýõ¢QÒ®ÚS\Žù +ˆßzŸwô¤øonÑ×Új(áY&oØX4%*’nˆ3ÕR±¦ì%Î_^êgŒ2Á¹ÀÚyPîÌÆ-�I4ÓùâŽ$�Ë©M× F„Ýkº(xàÀ<Œå ºò9‘ýD³÷®b|¼–N"`I·bCQΤw¥}§JÒ6Ÿ!lÁØ®ÚH<ŸÖ +4`¹ma „l^=µ­Ó‡Ñ{Ô!ÒY?L¶·À®µNhÓHFa[°Ÿ«<¶˜•PÈÑ@û'êÆ‚:«UL¸�¼ ¡:�¬QR&ª†j0µ<‘�Â^.ƒ|öÍóŸ¿þÛoÿëù“/ñå—ZöóOONE¦¼Ô½ÿÕl’ó”õY9ëâ�ô°ýcN_¢•e­Æi´?ûN®EŠ™ê“<Õ´ñÁê?GI–¯`{�Z«­³�÷»¥À‘¦r5Í¢â¡Û¡-—9 r:KÊD�Ù`�ò%Ú 2œµšÁ‚È ¤lU#ªÇ¶ø±ý'e?&VDµ.)ŸÜþ\y]×Õ¢™Ï»ÑéÕ°�5öJC}FªDØžsvsý`WLŠÍ™¯�‘ÊÐØ|¢µsÌ<—ìÃÞû‰?:DÅØ ÛX­ãæ€Óà€ +´J.¦°”ˆ&Zí' +MÀ<+3fÙ¦Òª£Ù�e Ï›C1ìØJ%×}�Ó^efñŸ·yz3 1äüƒ»Ñ7îaÆ+™ªòÛ !…ÞVÔ"Šï®/ÏîÓ3|y¨Jµë©5AE›÷ !ÓÎÿ#ߺ©�å"¾D&•�Ž`è÷ôy"3œ¬ Jò®[`JVɥȟ¶wEYv >Új*>�  ÜÖ¬WƤ ¼Ìœ=HêÂööl€¬·As†:ÿQIH:)©µö‡QÙÈã-Åy^Íb–·³¡´(ûËË@z“6ÅšZO¾Üvm±¤šÔè«KÈ’,£&WŸ+çw{Ì¢T˜K˜H¼j¿%€©ÂW¦Æô ÀÇ‹W;ª¸oHÕ ,ÇØRËï-S´ú„"Ïd¶>ýNÍÖ‡T›rÈn&ÜÜlP±x�c¶ÆÆª^Hð×nÕ)@ï{E»(;Õgý©¾„5פÎ)\‚FT¶ºÁ&sXß`U½RÜ*íòL_TñÛõ6ªvœX”g¼÷I¹2—ÖÄ`Ž™è²PãJ¶¾™8ªÚk1ÊmâQ“Y»Î¿?8àžD­wŠÏµ<áá �¸¬‰Ò’Þ9¥«»ÿqDAÇ»¹²Žgà�jªË! óJ¼=²”Æ­Þë!ùïg²�#êð4'^ÃîÄkZÒµ™”NAðfØ Ó®ý#jòU§ó’ržuÂÉßX•¢ÕËítéÃLÔåsÑ+­K¾ +.;a­w¨„Ž®—á•xª+ ”âìM "äqCIÛ:¿cİ@ Aj."®°O¯ã5UÈ�p⯾‰”q‹¨·¹I“çË+A$ÊíÁ+��ÙÍ– +"óûÆŸŽœBè�FùìÌfˆŽÃA~+à�*?ë¨ÚÒ‰Ië•,¡¦–Ç1:_5�Ø–W¬IÞsËêÙk¨˜V‚ƒ1h S£l´ÁX׋�ùÌhN†nT$¶2À‘û{°ç�¨‘¿>² ¶k`WŒ=€�t> ØS¨ZY+­ÚXkƒ\­Öòó½)*xïÇqê°!Ò²±!§X0 +p=_^T6jÊŽ�b³ðî_&ÈãÃ6œucý õ3hþw€ê9 ÷䯦½tO§äï禘‘m‰Òiš¯Öˆí�@ÊØ~üîù>¥Z}õoRþšÖûsº)U=91÷}€c0gtE! Œ*óR—�o.ÞxÕ‚ïÙ“Úµ‡„RQû \ט+p·ÙUàj5Y—lއwwUC U†s¿ž*·UíULv?÷‹ÎÄ‹((xàyªÀ[-»îsI�‹ÄG}1¬3!ø^,Y \©è!.+wæ©r5n.ec�ïgÏé7J°0zªVœñ*_ÏÿÕ7hèÐÚ£àa%„fÑeŒÎþ0¦¿¸Þ…KU—«áÑÚÝàDÐ<5³¡þž¶K6Oâ1Š;ÕdÙ‹”Øbb“: *­Cót9ÊìÂñ¨däŒÛú½Ä0|é +Ô²[‰* ¨Œ$Ä‹@‚jÑ\xÎk·9äb/TOPØ•¬á½IŒ¡f§ìX�HÖ’Z;[U’qiw‚]Œ=`Âà�ÇúÒ¶ÆÊÂ03ð9òà\È ¤èÛFÛ£`‚‚-ø k D�³í�€ñE½¤Êi4ö¨Ò#ý§¿âø|ñ’y%j;®$š¬2V—ýaÒÁ×Ã|6=Kýbè@N hk||?(WVN�„¿4ñ ø{Õ@ïTG4áCpÓÆi²@gš.L$)vèëUòŒN�u*=*k{±Äf4Ê&v“o» �rÆfìå"%îM¹uÀ¬F¶ûP ÇfCy=‘^µ§tc«—¿zpò-çÙгO »õãéÛqŽ<Šhu”N:"ÿìéù÷_ó\ÃОŸ¿þæß¿ýß¿}ýùwÏÏßýñ__ÿæÏoŸÿò§o_ùÝwÏß>½~Sÿþõ×ÿöûǯŸÿÝ#:¶„ó–ïlQIÇš¤q—Ëò¯¶Ëÿ<*�* +endstream +endobj +2203 0 obj +<> +stream +xœÕ}k“$·qàwþŠ^›29^-fÄ’\Z:SvhugÓ¸{[ÒP7EœþýEuW¡€Df]ÕµËû2�n<‰D"߸ÿÝŸxüâ‹ûß~ý›oNòË/¿úæëOþÏI�äIž^©“Õ©ä£=…hEt.žÞÿéþ½<½ÿ¯“<ý×ûÇO¾z{’§·O÷òE<½}X;O¿Þþéß>ÿ^JùýãùçóÝ+m„ ^}ù\ªP|/¥Ÿ_>•RSý´�[غ‡ªs=—¹ÜhÏe.-«–ո،K{÷JÇe°e:CNçzÏ}óó’°±L½È¥GFFµ#{ÈÓT z£}¹Ñ äó¼mû|[3aÔ„Â �$¡Òûß?yóö“ùäÍo¿¾/σâÎC°BJg·ž‡¬¼ ± šî2¡h Åk‘œ1¶>AóHx£G~€«ˆ…àñ={ Æ=˜ ŸDçÌ ]Ã"µ_Ö08ÓÒÇÕç к¾€SSÂ#Í+àé$)Ws”ë�PΦ°�t™E*S|vÁç»jY€ÌB‘VèdŒqs_£!¿*Ñ‘'¹o xÀÇ[QF¶#.�y#�ÂÇYÛ5ë\ybÝç)Ÿ±‹6 Ÿ7ºÀF‰š~”ÔàõUõyÁ,i‹@yMÏëžå#ΰáØia¤uÛ ø*vw¹8´NÞ6[ˆ†`�Mÿ.±n!Õ{Á+8(™°1 +[ÊÜ1É£ìüTvmªîÔqX‹’w¬‹\NgÀû½¬ö±ªáÈ—õä3žjµµZ%¬³Êo¥Ö‡ +¶G�B‡Bf + +#õûf[ë^]­lžðApÞg·=& ïíÛ™L$…�VŠ�Zl&œj“À]² òÔ½1x‘…8z=ŠuÅ)¼4a3Åfâ[Ð2£ù²ðçΕi½Ï­CcòŽ\‡:E¡8AgÁ®Áùþòù²+Y¸YÚ-Tzþþ×scSQÁù¿…|À=b Ôd…Cñ®�­Ð`Ky+¬™„Á>¶:»þ|ç´ð*D_ CÜuSú¸�vWk›å�ƒ1s?D-NޝQwøÑꋎ9¹‘£xi„4IoÞÃeÃaÊ(“*ˆ¨´ÚfF$øzµ‡,äÈ$óm|\pµÀÑIb‚ ËCàÅ¥ îPØ(+)õí¾è²± tçêíXziu@dSz…µpBÜí}âN qû¤…´NíÐ[;b¾òIh­ÇEÌxÕÉF}l­v‘³4=¡ÊªC×€ÕZt•qÂyåã>;×ã38Ĉ&†¬5­)—D²6˜ýÝ⎽‹h([3ŠŠ^¸† BßÓÊýO $­>¬¥2Z�À¢6‡ƒ4=c¸oŒ$O¸$µJì͹iDvtn;95ÍG%T²;„Þ†ëѶ– xc…±2˜1¾Ý´Ôï@7B7k€�ÚÆrdLgüZª_mõåÅ…°¼F÷ë`§PÇÚ–ø\Ù@LÚÊà²^ö¬(D£^Ä-زó®QLqZ›RkÍ xE²ŽÂJï·@ïLƒ�ٳܰB1æ.fú"`:5TO:¾8àw"_kÄlŒ}èx�˜ˆÁãPCÞ‚õl8h8Òbîûn‹Wqš¹wIØdÒc¶²<¢/ð��lQ`´ÈÇ¡À}wõrZz˜‚ë­Fäf%àúÎÔ�³®i|ëðQŠkâgYÀJ][ð¯Ù{”¦×ÀÑ«�¦”6“ë¬I,XÌž–VžïLI:¯z< ´c=KuÛkXùŠu4Fß77h‡(¸ë˜ôεœp:†þÆõ,€3¨ Ã^IeŸû´oxS­b«eÞÛwSWƒG`Z-I/›¢më¾Ìwk5œêÄ�ì¥]ڇͅ#y„“.l�¿Ø`ky¥TÁê”1×xaš´ØÑkyÍÆ Á(Ùh=Bí¢î ²WÓ²0"ãe:¤C“Dk6ÐF •¼÷$qµBŽ‘úg x­½@{/ŒÑÉí§XlŸ©`†®x¦9»×^8¥¼¿Û\Èà²Áz•Á¶ +Ü,9Ì𘠻ý:÷?;TÞäϾŸ™ÕÛòŸ_÷™³níÊYᣠ+nÛ&µÐQ…‹µô›îÞ´jžò~Úøpƒ­™�*(§1Ýåÿ([_K)bˆÓ•ö°Ö +pZi‘LJ· è, YÌ‹waa ¿` m…í”HN�¿­Ø÷jWïk¿WN¸éHmÝÜ« +ÙËdµbâŠZ¡"(B2® …õl›¸{û¿‰i¹PR—¢ˆÒØZ’«ë`€ðÊÙçM{“E/¬L* €Û¥Î(Œô)ïï#зÊx·V¶‚í ¥ZwÚC3ñ=kš%…8´ �ïqCkíØZð×™‚ø·ƒ1/@s£ŒËx†“L\ "Êdöe!p +R�£ŒÊÄð9«?¡ …[°Ü�½µÆÁÚß¹zk¨` "ì©7 ½ ˆPäƒPѹþ.ðüêM�·,2� âPôÂ'•öÄ5¸j)¢¯¢•1�= (|E¼c5ÊwõÛÊ™e]ð"¹˜ŽŒT,b¬pÉÝõñ€†×yšYÜ�À¸*T¦7éàõŽ·­ÀT‘漃a‰p™±`z­¸1÷ v=ô†³Ú:ï…’Ñí%çó.|‰¯’ âlS^)ÂPá%ß´†ã¯Ž„K‚L®f°ÔŒ�Šjé zÎ÷þ»Š°Ûv”ºnUq€ Uþrúñ7$ž¯·‡Á•Û+àk%Hš€¹p>çœÐ.êÝ^²õ + RDm5ÈLcŽóEŠå–À(½�М'jè:h&ÉËé@¸.Éõ–v]²X,­iM�~¢µ8®uëÏ]< ÙA4)sÊ¿³VhoåŽ|ÉÆ9묰S¹ÛDƒq ažÚõUØÖ¬�&žàœXúrBY$èN'a•Šû÷AwgÀÁŠ¿†š£’›Z>#àÞu£§1[DgµtCÖÉÙŒ +½ô˜G—Þp. Ô#¬2~û‰„ºÿüÿÝu œ�}×vôÛgÔžC(^(sàÏ k�P“g€Áš>))4e%|Išm«p;%¬µ>†³Ǭºèæî•ÑDîÎê”a0ƦÙDHNÓ:º>%ö—µ±C{ŽV»uQ„dm4»áÈñΕHž�}+/b(TúV†¥.£< Âþ2Ï¢V©b¯–›ìrZ«/cþ¶œ¾d£J+¥·_Tç³D·ùžªÍ�Éza/ñŒ«Ë`[Üt—Oþ†Y+b¤Ð)ú4°Ö‹vB—²]¸Nï#/„ºß/3Ê>£nŽ$P¹ñô”: �IP€ËNeè�Õ…¼AâITƒÇ÷u$Ë –68¡tØ#X‚`gT™Á4:½ÎßõQTí¨Î«ûD´€(tÂ'– +¢EòRê$úæ€ fÜ•§ynV§�eÇža¢™Óf¹äë­PFïpÀê + +‡éA 3¥úSøõáão-Õö¦ AÙZé׋öB§pÊSë 4™ù±62 µ¢žrN8ÌlÃn¡�ø„Û˜ð‰„É:®ŒIÐ c‘ƒDã¬`$ˆK&�€«Ñ£ˆ¢w”•M�ÊÄͶÐ(oÔ¯‹õËÂüe}Ž«{ !6[³âW›/DhU^xo”@ %©:ešº¤ÃÍDq×Jp*Z‘Œóa÷Ö¶ûwSéÓ·‚“ž…¤×7Ù Óç &¢´Ä52‘Gd"—„I.è#Ð>¢}1ƒÝ= ž3^Y;Å¥E»x ¤û"áGŠ(½Ÿ�ômŠxý}/S²*ÉÑ'€ŸÃªÍ,x°O©WCÏq ¦�}ÄÒ0�J[¤ +öƵ(ÚIƒ€$ûÔ-®È¥CC£bù†ÕÑäÌ­QB'»3¬é\æêœøb"!2,¤Ä͹rá˜Û‰‘Fl¤¢HÉOv»r:,K]~¿ܶ´µ‹`‚ý=¥DðS°ÿÞ EÑ^â¶6î@NR–m«FéæÈ{$ª[yá¼U»7bú©¦ÅÅn¾’…¾U <¤ÐÒG}Ä®\/áyÎãoµÖš´ÝšµÔ´Y¢ÑlxXµlz«[®BÌb‹w±†=iDÐ)n×árêð÷-R +>v YÔ ñ�Ÿ¨"œïгÙÎ?dDË4#c1…T`�Xb5Œ^è8UéïÐñÂ/úýSßcÑØ¦)çGÝ€òÖËìÏuÙÏps¡îº†°îÁ§Œ,cqis~Ýúf¾0lwÞ~2Vו½Îá•’n`'ŽMIˆö½ˆA³ŸLÈm ,¯Âý¦íÞ´%›ØÙvø:á+›}).q9²:žáÔ›/GÈ}Mš^§ö +¼¹°¬Fœ‚æíc+°AãÔoÄá; +Ó$Vçns�¹®^À'±bÂkÊ£e„,0_Ñ”÷ôYÜJ×Ê/ 2áZ Q +§p¸hœÙOÁ$ ïÜÎwøÁ�ñºIÐP@¯•óþè …÷S¥ÖEÐ4v"ü�á7{@žy ©FÏæW8§¡jç…L› ƒ{Ò7Hï×¾-B6OÝÖ³myŒÆR}‘6��î=´{B;SÌ¿é­HpªÊ\Í=ÇËOXÉÉY[ÍZ7Å;å`dÏã´Ym§ ¦ý¥¹ºŽÓ‹¬a-»§¯Úuлs*`kê‰SØòqp>Þ2‚ôÇ“üPÏMƒq÷�!Ü�žë¶oTŠÎˆÝÅŽ>7Í7§©’Äý!U%YMÕX¡mH›OÞÐëÛN §Sê‡ù7-±ÒÂõ®=P]™¸?Ø}Ÿ<×U3:rÖMÞß�p % oÑ߯«jÍ\Y`bÑÑ«µUeh²µ¸�¹õZ&Üb;ÙöܵJ²Š¥6¤à6®`pÂiºA ©ÃAŽÏW7‡­¡+¢-äÒT{Üîڿ؇¢]!SpN0³=Eµ{Dsˆ9AœgyzÌÃJcof"aàh]g…t:Œ€ñAKåÌÝ@ÒUŽHÙàAU)A`½2¿ +M!Fá‚Õº{ÇV ÿ[pÍHêm¦,� xÄ3 /!wn[]ȼ¦žlY-\*áœÙS'«#öª`…”¶}‰‘z¤*P=‘w8a‹Æ‰Z+Í…+ìT«—õ6¤Áz±ÃÈ5*Z¸³;ã‰~4\ã*›â20•‹BÊà·æ¾©ºNï‚ÕSÅû‡œ#¼Ôð�V˜¤Û×�ñŽº5ñðjõ”çô㊶ b{$>TωI"�wÁ·ýÜUKjûÓAõeÚkóä x­=+‚ r¦8eƒ�ÊøÍœ£‰Ê(¨eaˆz£¯Kµ¦—(…Öa òÿ¯ +ƒ+ÕÚw&®.¥÷qÿ6Í+ÈäDäS¹í1!Ü�ðàó7Öp¹ã’»µ£ÞF'”‹Íó*WTglÆh¯4Ђz€Q’#’ÂpÓrc¾§Õ4¼ ù­`˜¯®7À¨¹e[ “öA$íýùl5Œ2iÖf ’0Ò1Núc„ÓëÌ­È›öÉ åµU+è ›“áZjß<”Uœ­ºúŠc޳ä¸ôù-©€xäÄÀ9 ·l‚ß»P æ¶n_ݦçà à/„†�4ãÑg(éƒøô5W±GÉ(ŒRzG¼;éFD©Ê»öù$ÐæÏ”L‰ +£%†ê½˜I>ɇ�W¼$6·C-Hœ·óSýt­6d0Ëw-æ¸èæËùî�Ì*—“ͲL‡ÖW±Ö³Ÿ˜´Öº²A$w€ ñHvX*Ðw¡P˜ÚíN �Æ ¦9�l +Âz·ŸÚ¾ýú¹ñx-îM¯göV™Yr§ñï_H;è\Bx¿-¥¼Z?¯XÅœñ7÷®¡îxCÖ¥:ùr�£Žˆýd5 ÍUó�RÄ$·*ß š^9©EL~àýG¢áõ/½#ƒ9ê xаå2›á§2 ³Šˆv|98ü5!n`‰c.áWŠæ4Ùp®m³ÝÐr±øÉ,ÚŸJû¤�̬c´ºÂ¾[õB�(/ÿmÀÐwlpKHåÁ9 _q¹m† ð>jFJ,d¨Š}Ã6 “Oa»o9�k4~�"Iïû.9Øòê@kÐ кîñ€½ãÙFÝo_ˆ=Ç#s›ñ¡“—+)ȈÜ`\*ªœk5ò–ý5˜.‹¤Lk12ˆhµ3§­ˆÎíÓ(;Vúñ|I¶ïA‹àâØ¸ 1P¢Ð8ÅÆèF»<áUÅ,Š^h_u5ÖËNbXXMÙPÒ]Å9•ÈÕn3èbæ!Ÿ{'AÅ‘`0"zåwT}ë +{J*¡£Qum–úÉ�6–cͪ©'xÀ‡}ÑÇCk…11éb@CW­.¤-�B ª�Á •…5ÕáC︕ &ë„ Qéìè€;Í N]§D´^ëÝÈTt4ôg œÆ QµÍp9s0Ì«Á?·ÞÖ®áŒÁF¿áíÁï8<Ïq¯EôÁm7ÿT-].�*1QÙø‹½žøüBýµA¨ÿ=>ŠytZDÕUçç2“ÀKwß’É©º`¢ !#�¼²ÜÝäèTß?â½PkÊH–õZ¬G¦žóhÿ{\å?Z 1‹'ö„ìø8Þ¦‡'2¸¸n‡'±/ïu¡c‡šFHM�=}¬ˆh¤PAûö³ŠÏÖና|«”Ñ©„c:æ‘ÚE9:ùýp{ÖцQB©%¬U3Ï…#5;ؼº49¾ÇÀ}“ƒÈ{¡ºšÚò!pXÜUETÒd3p„©’P!î�ݰ¸˜V:6Z é“ô}`nvIý¢ÀìÂÍü°4l[iØ„$\œü/;z¦+¤ +ü}\tÉd�ÏàðÃ9éMOhR®£ØÚ� ØÏÛ¾p¨=YãíÂj=¼ot¬ú!£PÑÚ퇬 óf AŒ‘Z +é]�¤k…SYÃü:ûå׀ߔ2Êeÿ¿ç€E,†SމQÑí@ÛB�–ÿœa6̧é±T¦Ô£>%¡{ÑÀ¹¾'ýºVú5Ú +¥Ân0êwòi]Ž 4fù«Ÿ]ìPµùïS$ï +„cùpä +®:³.Î=� 'ÐûI±RIU¹b¦1”:\ñ¼*ZÖ!f˜‚�Œ+¨ð®¦ &È$ß¶ÅU�XÁÞ£Áð‡º +RÈô�„—ñ§5UÔFáH‘-Èäá¦% Œ†g(…“ø¼�ÂëÛf: ´�^Z�:°ÛðV†]×Crú@[o¶ÀÀ@W‚nýˆÅË!¶a£E +z:Â��é^Í›r‹Î‡÷UÂi­fËÓËòk¾-Ô÷'$9IJl”^ê�¤ÊAC·ã¥F<'êyDÖûýz~½‹uö6å ^ˆ_9a\pÁÖrïè» {Ÿâa‡Îs«J;ÖQºèÛ° É_Q$ ýa +'m2oë·4˜aáȲ!oj€Ä‹S6{²i¬ ï»÷\ ƒ·^$mõfª&Ì0É -½÷�L“òú­{� +™®O@µ¤cÃ@¢Œ{“Ó«"KŒÿò”¬)ÝŸ³®UëovÙn²¯zÍQ¨ñB³ÝìÒDzâ¯z+Ô´–ʪ¹‹GÔà�ûAo°ÃØ«¯p–ΨD‚N½9ä#HÔšV! 4@} xãJAø$„¿ÅA¿þѦv+]0$+°Ví„Öf‡YˆªB?ÊAô�buf6tµ¹&mœn¸¢0;Œ­Fj‹µy¯ŸžÿððÃûç/¾¸û×?ÿxÿÝýé/ÏÜ&(+¼¯7A$oƒtiÚŒ“<©ÓŸ¦~Ó»A9íª?Ïc¾?ikDRƤ“<ýñüìšõ·“¼<Ávùfí¶~¨Î�—ÿ´ŠBOoŒü|µ³¡W«ti¸Nœ?zRë?¬¦]>•Å —¿/=?ùÏOP:F¬TJ ãµÓ'/½�Ú© +Õ…ÖÙ$UòàÏèu°ê5�³b­õ\ÀïK6éï«£P–h[ôCW69wYs©Oú–·º>bw¡Å_4ÊÎêZXªÓø=%{þ‡¾k‘íŒU˜Ø²Îfæ³­.yá’�g[³íÝg,û±ÿ­Äk6E!;k„Ö">ˆÅø4Ïýz*ß–ïú²HYùÙ›<é«’ô^q›é¹ÍŒA(o?Ò^.$Ü}]!rpgg?$öjk„™%ξçÒès®_•ORf—üþ +Bê0`¯T¥™N—mè·ï!ˆ`æÔ÷yߣ5Ê=m¶ÖÆédÕôçd%ÑéÊ�¯c̬KâàF<ÁÆi¼q5ܸ1 ,o'íúñˆ÷Öa‚RzûÄ@&C/"±*¹$¤µéXlDñ÷F˜�ÂaÔsQl˜0·`AY�E"¯O±»6é�zXž¯ðD KQH¢Ù�ÅxKRÜKês¥±œ˜«íÖŠ�¬}„ 2K9�e•ÎÌ„/Æz%ˆÓÁI!ÃÑ�wÜ ÉS�IÈ8½0±o‹¿ ÷7Jî–õQi¢ù(âÕ7•õɯ±œ«½h=µPÄ–F²íÀ0¿nþݤŒà#‚‚î <¾Ñ¶*«Ëü)1xÐ!¯“¡£¨¸uq +¨HÚXÝt3¬zGâ”Ó½HÊÅK¹¥kN!¼N…<]½ò¹cak´ªg´èž)LX*›'YŠ@x¼7B†´›",±L|ÊœÑÇ©±0Ú‘³@ BOÝÆûXzÛó¢‘'{‹åXm5ò«qR˜àì¡ØB»³"(©jlÝÚ¼ÊyŒ‹êÅ�žÇa«9k…ôÞ‹Vì-Š(¬T*E„ñOc�„-ÂôÖÆ¡¸àL‘-*„7EHiÒm;… «Tð6^C€…·”#‘@›D +fŒ´ÂÊt,}$Ä è¢ÖË‚°Gßò=Ú¢bZ» +þl�8ÓD˜ˆfú-ý5ÔQû¬Á^ì +^Loaî¼Èèk^#/.×¼ÔB'~÷¼F^J*îùÒ[Ó�å.úƒQ´ã¦G^ˆYoú#ñ幫~ëÄ tÓ3!W¾@­~Œâ­‘ÇZ +;Α(‹Œzs…õ®qä+½:(2°‡6Ô”²®•9ü¼å*�4• ¯hX•D0ÊŒÍ ™;'6ÜĆ“2÷žèmyU ËS{qAÛ‰4ò À*P¹ùž“š�Sq;ùöWÙ_Ì©@Xö ¦Š‚“ íHÌDNn:˜_d‡Bã åP…°Öd„ÕSÁš#Q…ïµ)ˆ)oÉLDk0ç¿N?ƒ¤¾«SNÈä÷mF6œ lÓTroNöþØ‚°áá +Ò[S'�¢‚°aáCñŠ›'Î’Jùž bÍš³¾«Æ¼ ).ºzETdä'ÿ"%8³L~$2�Êš«Ü}ô)±*’Sökò1‹ÍÈÝ{'æÄ_¤Îã*wï>ÇÅ_¤ŒãT2;Xyúä`¤’ã"wºûž–»±Sñ¡Å_¤ê›õAXeÕ±x‰ŒØ}4»Ø$ýZ. ôHT!EÃV±ûXú4—Tä_¤–˜“^X==³3ŒäëXÉ7a�’þg!ú:Vô­@½5í°²ïÑHÚ!ü:Nø=c¬ô»yfX¢o‘l«ˆ>Žõ¼ÍŒ”uZ%ÀCQ9ð(bë ÄHå Õ.{(>�@…Èyôá�‰‘j.…„x(j4cšÝ=3'#µV7ÿþó½Q,F2çW1q7:±Éí^í³Ç’€§ýÚèḅXs†ë5'Š V>šWlˆ=+‰¬ÀJÄGÑN¡V½^“ý!9³NI¼š”Î}bãÀ +ÆÁŠ) ãç!V0®@½5 ±‚ñÑHÚ!N0>c¬`¼yfø>=ˆ9~*dà²X?KU�ŽE?E‘³DET=É„-ÌÔGâɆ]5‘£ÏØ€ü‹ävªÁ¡˜Ñœj°wfNþER sôîc¼QþEòûr^ü~t0ò/’ÞWØ£%O¤±³qc†3èaéy«Rp(R"£Í16IÁH†ž�Sìm4áX\!ùv6ZœQp—>’aÉË[ã6vↀ+{+\�Öü,àÄ +À¨·¦V>I;àÄ À‡bŒ€7ϼ¾�TH·…Ô “0é¯rò.‡½ÀÙh÷¡Ï1¸‹œ¤uè®%NÒ:šÎAµðµ~ÓR®”Ù+ƒ¤êåÌ #qf�d»U; +e5ÆH–«;'”•êæç$®`¢LEÃ1H¢ÝZ½b;ÒFhaÓV‰ ]„¨¹­†c�„:c½�Vzw BÙ¤á$�n-V·´Šc�\ºµ8Á±4�Èâèá¸�|×fO�ÔºÕPt$b�œ¹ÂPt4ߨ¢ç$㮸ÁE–ánðcéh<üÛ ùv…cq‚heÇ`érëµ8™2&âÏ@×1X‚ݪë”�Þš€wKŒ¢íšŽAÒëVMçH|%NÑÙ:ñŒ�‡º¨N©È¼ÀÊþ•Õᮎ…1lRÞ>ÒÚŽASí2Ç:pç°T»Õ¸{0¥ïÒu°\½¬r‰16ù ŒõTÍòtã…É}XMÉã+xh ÒÇQt�4¾BÑÙŠ²Šœž³sbNÍA²ñ +5g7‘lÓr�L½BËÙ‹ FÉA’ï +%çÀýÇð9 ˆÉ`™vqš:†x,.°"pNHgœ9œIlÒj�»ÕC}$¦>I;”$oUjÅWÊmûÌ�ýäúÓ 47(…Ž—ôUX1nð˜øHa7ÃçþŠf®´Ûa„Ù“¨±ì¿Õ8s(>˜ònǢ㟠˜g(ö…�²£åb?ömsÛ i}Åm£¦Øï· ’XÜ6¨7&b$ï¸mŽFÒŽÛKÌ·Í¡3Üm³yæÊˆ6_¯Ÿ/"¾¸ÂޢƱ Ž›c9ú¡[Ëqô£ÏÂew/Oáp›BWí<7LÝÎÃpÓ»üËâe)Fç?¬= É ,8jÓÇ1¨a/½¯µÍH !ìïÕ¢¶wfΤ†½Ã¼šÔöÊ6¹{Uwµ©íÆcTÃ~]�j‡Ò€§­jØá¸™Y�ƒ)pfµC±9»ÚÑÌb“a K̆µ#q…å®–µ£hg,°ÞÖ1¸-ŒŽÃfN¹@nzŒá=oûƒ¿Ë'vÑ7€gy`·5º˜Vàý€Q? G=/G¼N¶”qȱ·Ðº³fðÅÛvæÿ¹³…eMæ—êMˆ"ª4¿Ç�Q‘‰½ìm¦£º¥`F3Æ#JcÈ*Xµ¢Zd¥hÙz +#õú¨ñòú÷xýžbž/W !!›GXŽ x80‰j~rÖê|axìåŒ[VNÅvW¢÷A$ŸìV¢GV¿®ÅKa½ÌÛV³¯G°6µùÚÁˆæMÃG¢ý(È ÚxÃKçe~&Ž˜g!µG‚qÔ|FË|t©iñu èš9³�’HA—…kÔAEÔ à�Ó F(Öqë¼HÁ»•bkAUH³yß4N$½H:zwŦÃ[p?t4Àý,a�&¼³vKê…Ú¨s}�7v öäóôüpRúdÐnƸa šÙ�–Êä³6Û‹³W^üà¨_3)ƒºÈ� ë…Ò–y ²ÇóÉ›°­‘V +;ùÐBEëàœµívœU|ÆëΪ¦àb¡n¹¦{þ=ÞàïJH~MN·RÓô”dMY3ø÷TçVB«[üíú:qÈg•Z9¼L '”‘ÑoäOç0 �£2λpyÙ<[ü` ÄÞw^x½€²wRò•ã¤5ÎX<$V_"|¨þžpkg¥¡¼@á;�]·®Å}8#~5¨ ±¼mç“�¬P|·Q¥y×-€çÁ¹íƹ.öâùJñÚ +¥ÝNK�ˆ´H*$’lêïñ^3W€²/>Ç#ª¹V옎b˜¨Ä�wÕ joç+’‚»UÚšþ;»ßUÔ¨Àü£Øâ·¶ž âî�˜£ºSj¬^Côš#z5Õê’r«zy‘!Ÿï^9)‚1Æ”ÃÏ›¸,ã\ê åxb�1l6½pšYõ!ؾ0eú*v*øä|«�©nqÙra“Ò4P×-‚–ƒZ¤¶<£)Ìž[}Yo^ÕeD§Dª÷d*µpaznd/oÎ�$!“1n¶>bôH™©ê®+1;†Qg>™-pž:«Ö¯*ò',O„ ±^¸@YCoØP <�ÝÕò¨Xi¶nWëO(ýõѽÝn‡TvZ)V'%\t.ì¦Ø|©1�`Y5Q(¥•€¤ki°”�¡}f;“¹©RœÙ)e¼1D}L>Wœh¦~@—PŸwÿÖ0�†¤ÂTAÁNް°u-ŸÕ9˜Ö gŒqûcÓ `Ðàÿ¸Òa¾$T€@^ž,MÇûï~xü�Ï|¼CÌhçx3ªY„þ*rL*<VwÅ +%'»ÞÞFö¦14 ÞµüT]šVöߌr ÿ¿–~ŠÑ[}3¡ P(k—Ô€éÍFJ‘ÇÙ[¡ÌTm¯¡.Û +ŠúøÅÆb6Ø–SÛk‘caHq³bÍ6ˆ³ìê5Ÿ—ü?i_>ZÜlªºÓÈ´½[ìø÷ Vó,è9Iy;â2gPI™T°ÛXa´¤…5QïÇï‡Í~GJ¥M…(¢±Ê€é5 +B e��ÝÓ7ÛT`1˜ÖÀ_lްÊ(0ÄŸ“’PÒÞ§ëÅ3DÅu#�‹ônÀë�“ݤÈ1#ã'«‰R;œò­§WyÄâ™= ¨Xºô9–ÑùP»Ò¿BBòst�ÏF|þR±HÉ·)îQE7qŸ½}¾{•P^;’@ƒGôcžÐî0hÝå�g¹u²4Ì7‡ÂØõv?‹•º›Š¨Zmo@Mpçk‘wc1AkwÈÞÕc&aÜa@RÈÚ`$ðƒY½áX‰v“e+lg%P¨�ÿ¿Ã¬#y×°ˆ/’�*ªz‡ }ææ ×‚\M|OÚvzíÀ–RýZçÕÊTИKÊ%^ übçj A‚c½Fˆn2ÏPE‚1§yŸú´}*+‚N;TÆï…–Þ¯!žÒ<ïI´4ƒÀÀ3ÔìÐô"|›E0ì1¾¬Ö��û"žÞúˆykcèø +›–/ȉ¡=§W ~Ic詵ïa‰×qÄ+Í”6§Í­Œ°­‘½ |É~÷iVmiÀi@ã3Ä&剨úöüéçèwh „csà., Ñ ÒdfZíð‡f�€Pt\¸e‘¬NEá§4‚>¹ñRÕ/fp]e|®ãìŸFÄ­V�sN‹à\Úq$º¢ì7«Æš�¢šK�6î8Š•Ó‰Ì–Qø‰ÓÔj%H†hšÒjĉ‹ÎƒõÜÁ\ĶQ0aAŽuË+|ºÍ$•µ¦Œ¸¬Ûž�AÜ­ ×3­Tü~¤EÇ�<’±ºÌ+“ôR¨˜v¤C¬†fÅ™b°'62·JVD¹+î‘ô*‘¦Kª¦¶ÐFP°rêQ®“h:£ ßôMÿÎMß´´Võàî›{šƒöž¦_#Zv`éciÄ�=Ê’I5‘잸ºqß©QÂÚh#ðƒ�aÖ24y{¢(ÇYH£¹€îê«›$‘/`¹^”!µY —ì ?àe(óQ!P‰è†ž Ej›7 R@¡XN²FÈ”&ïÐ6’=°IÓ�ßN?²Ok)œ×‰)~XŸøªçBCE´T!Ñ� ~nÚ7 —xË—|.<5ìza4-0#•.z+Æ4H̸Ioa!"`ë-å5Yâó ñy=%0NñØ·rV3%zuî3`cÏKk‘B´zìQ�·†h¦¸Æ<¶™€ÜfM¼ œç~8öpúó¨)Ü uœ ¼  'èûzÒzž…ýr…#…ýõÓó~xÿüÅ÷¯ŸŸxÿŸ?þ¯»ÿê§ççŸþôï÷¿ûË»ç¿þùÇûoúéùǧû·ÓßÿüÃüáñ‡ç?üôØ�‰NFhâI'¤Wj«™í|-ÖÕ�ÿë°í +endstream +endobj +2204 0 obj +<> +stream +xœÝ}[“%·‘Þ;ÅRÙK ÷‹4æ.)Š+[’-…&a¯ö�¤ÙZ9¤Cy¶±ú÷8}ª +Hd&PU§ºG~™K7€‰DâËÛýçïÿøðõ·�oÞÜ¿ýÛ_¾»ÿÕ×ûþ¯�Ÿ}öÅ—?ûàÿ�ÔIžäéµ:Y-œJ>ÚSHRDï•;}ûçûoåéÛ?I‘¼ Ò¥Ó¿{>É“:ý9÷Ö† œvÕ?/c~{r2 m¼>ÉÓŸ.ÿ 1æß:%¯?_úÌ?S¹ãôŸàETÑåîó�‚0A†¸ ÂS£ùsÓ¾=©ùß*?6ýP.£Éù[ß~ðo<|ðÛ~þëŸÝÿî/_Ÿß¼¹ÿõÏþë—'Ù,:¡�Õñ¢Z‡h/ôROôŠÖ(´?I¡µq:Y•ÿ/ÿv™Œ|ñö$OoßÝ?ÈSúôöa;ÿõöÏÿòÉ凜¿—Ú>ý-Ãõÿúî_ßþ·~þ™§j晢Ð&:8ϧ}›È4÷öÿâŸÕÍg•ÔÂcìñôQæò÷»+•®ÔRþúGSË´ÓÖVhT8”\ùn.ªD�+ÓGŸÉuù‡ ')Bþ[úUÄ:_þüæJS‘ìLʵvRh籄òÈw­pI:s_]¦õ)9§€Ì)Ф›)Ý–±ý¬7B{)ý3±ù0UüªÈµÔN?&¡}ôþP²©Vr«d„—Æê½þýUÅÜA‹�‚+ïו‚§þf.VDWSzé+ ‘ÊÊ —ÅÝâ ° +"–UIi«Ž'Ñö[L!ÒÙ*á¢×æX‚!âÙ:‘´º“_/¥RàhW_\ŠcxDN%’NþX¢ ‚:x¡½5ðÃÏwg)LüJáRRúPjhDüF+’11¦ŒºüýHs‰VÜõp$ayœ‚H&ª´ÿÆùp+^µÔB‡LÝ[]ˆ1px™mhE¯ÖF$ëC¼5M�Vvj…ŽÊ{ÙèVvj£…WÞhü`„|,\~kKi’VΧ“æé=WÌëCzR­ÌÔÆ‹ä”ŒÇR£šÚ*¡£Ë€Ä¡BÓГj…¦¶Nx�5î‰aZ™©mÉ9pbÜêyøÓRlæÝÜJMíµH.ùƒIÓJMíC^¿5Ç_'“Ú¡gõcú9C*DÜF+¼1öØ#e‘£H>jÿ>ܼ‘¼)oÂÁti¯‘J¤ �?ôâ5­p5Ò #Ã-ýJp€èÌŠ2|¡W¿U=ê«ËÚ€,ŠÖ +oã½HÉJwèåmZm‚F7ºå�á»V@›à„÷Æ…ƒïnfR­L6!‰”¢qÇR£•É&ZaŒQ/vuÛVêš…÷áhÖh¥®IFH©/¦‰ïî׃w·m宕^HéÓ±h�m¯U—CÞϫ۶Ûê |”áX-ǶÕ-¤rc>J¾<‘ê£üÇ¢´2×Z)|tkÅ®ºÖ:!UzÁ'‚k…®µIg³ÁíHj´B×:#|LÊôÜrƵ"Øz/|2ûßohºXx÷Fô³âîÎq¸{9£†Ý�ç`÷­¤àäÀ¡îh;èî"ºH®Dcî;ùû±x<=¤ÑýkÒ#¯Â%¥„W Àßt`v¯9ìÿ@RÞ?ø õŸúÞrÆßÉâ8pçw™—¾÷¶¿ûÚ„°{ÚUIx¥åÉÑ£ûÈ!ûî}â€}äH<¼$‡ùG‹ 8XÿHQIkÎAsˆÿ�´0 ¨��⹡õ€À¶. +¯m:”,ŽCô¾A¶<Îb?‹y»b8–‡ç¿øUq›Œ0ÒÈcÉ’4ÿ¸›6JÌßyÓ¾$¦4×ká]ÒáÈë:jË?Ž¢a ü—¹­£åPþIá8 ÿenëèŒÿ@RÆ?ð¶¹"‚ÔJ%|ÐêPí6&į넠·Úc}&Å!ø‰•'2ý$ÿ‘‚ ®ñÂ8¹© ‰áðû—‘.ÉrÐþ�´pzÿˆ—„˜Ì¼ÆÅ½�AO�AÐ�Éøô¬zŠ‚^ÎèºKÖÙ$UòàŸÑë`×*Ü®þ{š¯ +ðofþ‰!¨µÂIïŸ7@JŽ¢å”^:@*Î(±•x#Q#š³JL¢¡Òpf‰# fi»Ä^&QGÕ3êUMœë yê²üýÔéŽt,Z|$éX¸ø ^ë ÈÀ¹‰I Á�^xÂñ'¯°(‰ Ê ‚>–0H$[�àïü0ƒ±($˜­€ðwß`›Ì +‰g[pܽĠñ…D§ÎÙGn? [¡“T€ïn ‘|ÎLÊ1hõ‘Äð\}°�ØòþUH8Z�WIªÈÖ//O‘д±>�0HhÚY(N‘�³³Þ)N_³VHL[Z(”‘˜¶µ>’‡,[)“iØZ!!mn}$1<\©ÇÒÐ’BBÚäúHbDºFˆñÜØ’BÂÛ +ìú@Ò ñmxý>^ÞH¼ÛŒ^I(ÍÁ׉—1øZ!qm~}$Q,`¿�”A‚Ù +ûHjxÂ~Ä ö¶`Ø»(CcØ + I[0Wã„ Þ‹�ß#°µd]|÷î—bÊe ±jƹ›2s¾Á°ö¹¤‘€µS*ë }+ò*J,DÂ%¤nvé¤ "ƒC>…\RõPª 28Z!M¶‡¾”¸AbÛlŒ9_ȱh€F‚ÛlÒ"H­ &ynŠF¢Ôœt"Hß<ÐohAÑHŒÚbAQ^äã±uKî˜Ï" Ñ“p>É|îû.·ZÇU—ZHgèäïê„ç-©Kj‡ÙSר�ß’OñÓ;•„5Þ9ûD³‰™CW³DâÚ‚ÊIïúK‘§(‡4V³\4`˜Èbº®g6ÖqÖH8œ2VxŸRÜ»/YK¾³¦õ§µýG%_â½Ú�;èzç�$0úŸ€ÞAuíèT: ŸF}(þl–;¿i,ñ­©ÁuËÏ`“‰/?”´úŒ%J¨™NWH™×•%µ¬˜OSÍeÕàa唦îׯ6 Jl~>-K%êjÛ¾Áf0Ȭa„·®“=·+®…ÔjG:tF«œ2TMÝð#QyÌSC�Œ!Õ,–?¢¨åï”RDû_L?ž˜ðòû� èÈ\6>)TÚ.Ôæsë—3¹x&�1Â+“´®�oèCÙö¬Ò²hpîSsP0ÂãÜ {—`òsyXOù�²Öó|%Îk¢×=Úþ]% +ÎÕ—ûP(V|SK‘C4vrRg“ #(}=ßyÍ0·Xر`_j•v1‘ð¡3q pq|†R)ŸŽê~6›ÕD‚Ygb܈4dm´R{¥÷ j›0 +#{)eÒSjI3Òe„èo–‡ÙdŸcûRûƒUÔÓ"y á>ï3lOAh“¿!M*Ñ8 JnZ¤•ðRËý|»‘_ÇÙ—u³ˆºïõ«.Œî ÎÙð“ù�$´Á�ÊÝùE„JÐÎÿþ¦œÂ´>f{§ÓD-Œ�ŒŸg�ïíúµU"DíŠöåŸJ}<ÿ ¶¤ï›Yñ$ú‘¼ÛÕ¿ŸÑ­á-[ÿþL¬h\àåÅiÇ`¬ä?b)k«_£ —0Ì?Ë�îêlpœôyU—újž€p­wÜ!T¾}X<Ó#^ÄÍÛÐß9ÿ § 5dÜˢêg«bg8 Šù K°›ØÓ|�`l¥'¤ÊšwœGóz°¦0l]¡ Ô +5n¶Ø›•ZÎe÷-IQ€çdqTXÍ>:ƒ)„h·¿:Iñ¨“ˆÒK³æ*m¤"\Œ Z 9„54OZ˪éòŸbÓª¯Ã6 *¦ɸÁ‰”×õ�µP|‡bWŹ#&ÜݦÞ5V7Ÿ~�#%ÈÈL­~TOX)>Áßàò†Ú‡Ž +Üesý-¹n (YâRåÜJAr‚emô–yx27­yp¯¦£Äß@Æ7]g’�°ç¶�ú5¼Æ+Ä™B /ø::B‰úN~Ço_¥�Ü¡ÂÊ÷e<æ i„4Þ™“÷9iH¶RnVÂmåÊ­5CAv½¡@›YðÒ·k)<¦½0œÁIUÜtÖ‹˜4c0U§("eBü_´ IåŒÞË>ÛÅ$ û\ê¶:p‹‹*æ’zå¾ú©4õ¥#‹r`9¤Œ!©¡ÅR4žŸó¯³÷°VÖxZðÔ-ÎdÏêz%ŒT³ý±+5#.¢±M@y Ÿè´éeWTGj¡8=‹>e&b¶Øpül¼�V»Í[|&n/Y¨Ü×—€€y¼{½Ð2{“’ÌÒ´¨PF·ömqÍ褖[ZåÊëœb•ÑxǼÓÁ� +›wæÜWÚû÷ ’Ñ­`]í„JZmeÝT´_ç?~y��J"$cüÓR†�ÔM€9¸S—G=äwtøã¯ÊinŸ!Ž-µf訌×ÅðŒñºšÄ ºšNT+ÄÔ\6h.ºFá­Éå9YËp†ñÇxÊ +•"«€qŒ÷ªÿùc÷^X—=¦ú_ïh �µ“›~¯„ŒÎî'CJÆœI%‘R¥npF,Ø7äþ.lX£™Ä§—ssÅf±{íž– É‘ØÖ¤„ qÿÖƒX�Ykç‚—�´…Ze°ÖÀàäÎNK„Gºò@vR‹`¨_$PuyE 4Ïbº¼ir!É É!s8@v:ÝH®•`mvQ&ð”%t¶ÆA„ê¿î)\êÄp¼Ö-Žf±6Ü �X¹r +ßó™oèÒk£ÁûhúN@4áÀд'àþÖ¦U +–ÿ¸×«KZØ\ðc/ÿ½#`Ùê‚­·õaàÅ�äæ\^ü.*á¤Ñ7ð#‰9 ’õ¾Ú¹.;8 ,àsW#0¯ +�õ®ä«WåsêcÜ=‡}ì#É>UNïç£Xè€$×ZXœ¢¯sØ‚|¨À–¤)½7f9®žåŠ§Ìø™]r÷¿úúü‡O¾;ß!Aà^¸FøR÷Nr}§-¦D½¨æRê;óä_ðŠ›1Nƒ°ÆÈ|5±}nwnƒN&ɹ*t„›ßq›ñã�¿¸æ[ÞÊ9C uëA[Ð]œ/š6BôHcEÐZ çæRí¢²äP.;êKÓ_p«Àò­\®›vï1<ø` �6°,ø)7¦^0Ncä¬.Düë¿›A „µ€ÖpÕKª£ÖŸ'¥éŒä´-Î’Ë1èq‡ÖJF1är6.$¯ÜÁoa\ƒ£Uá9¨†¸À'žúvãø¬1¸5�aÞ6dÖ3ÁÎÓƒm~À�ýšjÐÝÒæ½#^V…°+À—V_¤í›d`ÿ†ä0.8ÙFeܬä´/Á×9¼QzBQÿx«,¹[ãf>Y �Ìà×YøåÝ$ýdÛL¢ú†šßÞ½vj¬ì“ƒ9j¨çÑ9ÄzÆ$ÙuÁ &ˆ”±ž›‹Ú×>Š`Œ±›odh#žóD’¸ÔK \pk +œØ*çHšæE yÒ€^ÄÍZwÄj¯˜ŽðF^ýìcY¡dØlLD�)å»7?X¦I;+¬›÷qß¡lÖªŽ­såÌ ]Rýþ¢Äà_À%FÕñ$NÂô´Ö@„ ï åÕ:n!_lHˆT…“âåuë*IJ§,f×ZXʨœ r+*ö+vi¯ƒWÂDSyÖŠ-PZõ¹ìÁ�3[3&®r-¦xJ´‚Q~nð$1ïqë21z\ÆdNåÑhÎ5„á.¡€MI8»ÃA¦°oÀY’÷Uã¾> Õrpe“XD×¥©!�_°6UæÇ Ò¬�Ž]�i"å”�ÂÆ`Ó ögäÁ�-Î +ç­¹q¹”° g¶¯ÝË®�§Å�8Ÿ�¼¡æÃÉ:¼xŸq@L„N ™¼óϳHa‡œ/S3rH‡À.ÂØ5D ¤üÃ�n'P!ÅJ;,Â+FálŒ›Òµ¤ÞƒKByçäÈl†lÛ åûÓ.ÕR$m8Õ²‹›?b>Qú‚'˜ýÔ|¼sFX�‚Ûj#ü㸾Q¹σöe²�æâ} ¾q;…vßê«ý™�¶£ž—(͵ݑJ¿ú§qÅj!Ÿ¢Ýn®X÷â•"øä£‰ô›69¿x�¼EÉäH­Ï%þ™1k[» +òaÞ4E-o>þ)m‹3ñsàüöD‡û¬?Ó»mšé·!Ò×]šBþ%»®ÖµN�X-g'º&jÆ„ ýàr5á†7:7†Ç‹:æ·ÝæÈ„ôÙ»L¿;k¤*WˆÂ%ÕÀ¤»HÅe’Å#¦´ju bS8»+âÓ=r4VÕ+(¡´÷zÿv=Þåì­Z;7; ±Á€û@ÓÚõg>Lˆ•~£±³Â�Ð'F:SýñxvÚ킞#ñ,`y•Å.T.ê°Û» „’4®:³½Ü9a�Šv—+46)aË�÷)5óÁ8JðÿÐ�¸ :kiV\UÛ)FÜt�šðÌ¡,ÏŽg¥.F¿ý9D¬|ržWe­P2ÉÀ†fÕ­@`ö;T-䤎2×½£š7¶§ºÁ™è·†‘ˆOhžXeÂä…Ô.ø�}å/Î_—†)!ó4ÿ E~ö4Çë¯.MÈÙLØ— RÒP»¬>« oÀ¦E2¢šâ« *fúè`’˵·4Ã4.µc' �2ø¾Â 6ÑX’€ÐZÃå”_¢ÖYÚ!æ2�„‰ÎùÚuU@µÅÈÍ1®ù(LŠÖíßk¸¯ÐŸV¸_¡Mš_ ltëŽY@OC³#¯\ðq> ³ÜK'—{÷RÁ�<û¾³v�ò-�†DþÛ„3°ýø-€TÙTJ ë“W”èžK™xKS8áÔð–w´ô§1\ŽænÆ(‡zËÎøÏ§µÎ¯Ç%Zâég¿˜­}Íÿº(G¿íï<÷Éyjƒ“›£W¯Òé æ Jx«M¢³XÀôM[4ß]@Ó›w“ +´?“ãìNÓŒèVÓ¼uiÂ{ð¾R…’_÷ÿ°Ú–—¹7ŠñZĔد[ý�•·Âz¹äL_˜þFÒµß}‡g Õ>˜6 08T‰KŒ^ï®Ù;Ù5�!Š˜u|3™*Dº½—›Ýæ/ô�Ç5Rà¸`Üìod“ÞñjÙb}­µÆé9ËSë@ ³=ÁÚ9¤å }¦>IÄIÌ8:ˆ°Á…2yèšn YoFb0Rص‘´Îݽ{ ¿Œ_d#%?�’ÕZ%¤sj³ +8½ù—b¬ó‰QN˜œÿªóÍç,rMÏ™—�7Ô̸jÙ@›ÈœÞ_ü+ø�ë ç]Ûº¯È(¬·(¯V)S‹äh…3Ö’1—TúÊñ×”½@ʈÜj¤ÐÒn�ÖX¬ÞÑç2vK$ÓW;X ýÖ*ŒMÿŽÂH̦ë"B”0‡Œ·#ƒŠ$m$‚®Í‡ ¡(ït;¸#&U¤ô|Á¬êRuÇsr£õD›KZ; >?cÝ)°n¼‰ô€—\›Àª )C2ÔÔ£‚j¨ƒï[6ÝýL»’�›þ=ûbÛ£»Ã­^BYÃV¤åI,Š&cN‘‘¶ûÉAÕ¯ U•2˜èF"•êÆ¿—ÆÍ‘G/�L¤aÈ�$l&±ñà‚qV¦[‡³€z=\i�R¹546ã­µ­#g»¡é0”i$—ZY§�Ójlwe_¡¢‰†ëû˜ë‚ö'r¦Ú*áÜô‘üÉR ë¼L7 ¤®¼ëŒSq sh9+øõTg(¯�_XñW¬æÞ„íñ=(tdëé$Ä¥žÍž¸Z‚Þ (ˆÔYg˜…ƒÞtô»doà-�…ˆL¤i §„NNsõE…vÛß¡B&DstãMQÿ0aÊmñ6ÔèxÑJ Ácà—V+ÚFr@šND™ö8í×{ |½ÁÅ.Þ‰èÝG +ëà`$‡ÒäÀÀ¤B¤=…õ)1ß'$â2­�¸Y´ ƒ‹"$k£Ù=‹I1½kSV”œîAs÷úšÜ­-G¢C§­˜$Œ*ºídè¸\aŸËŒ§ìryw—KÊ1’Ã4´ B[¶Cp/hQ5qvPR„¤sÌþÚ†ríæQ’¬àÛ´¨¯@?QÞÈ`d½¡{’ºA=þ‚C·…V³Í™°Bšr/;m¼Ðv‹=í2±Ÿ�¡½F!¼ÉêýÀgÿ?sF0 +qð¶1ëˆÁìØ…'r¼©ÄÖ%[Ið1± [·i]{ˆß—I]šo4½vÙëÁXldÙ–5÷ Â3ÛÙ>«²~¬”5‘ÛÎi”Ï¿}üë×zûÝ<¾ùêç_}%åç_~vq(¢þîrì~ý3jr¬¯�ÐÎíΰÌWÿDÐ#´‹®I:ÙZ~AC:±nÝ÷†æ ‹ê‹|éCØœ~â ¡jIØpûÆ>£0·ß(B�Qõ7ºkìòM³qÙUFX)½ÛÍs)½ ³‡ð�‰W‡ï5òDpæÒÀîç(¬•V:µ™j„¢âµP*ew?.’¯<]wbmÙ¬ru»+†I7€·@-ýË”yÕ¨;ŒWÛ–1[ˆé<•T…Þf¬Æ(PõÀ\ñR‚„¬+ò–£œûŒÈ¾Y¤ÎÙ°ãÊ€«ð#ÙôTÏ;¡uš<†ËˆnZÌ  �4~öºì¿ê¾D ¤|óÅÛ{Ï7ß@^I }ÛRc•nm ãî…QŒt1j‚Ïål¯*iá¼ô›ž‚Ùe ͋䵩QÝÂÃ~ÝñªY·Ø’QHŠqi„²*ÇDô?˜À«ëª³Éþ Ÿt„4J†ý×м͖ÁùòÁÎ\§áÄv¹ +‹/‹,V`J½‚™ÐÑèUÃ= ôõÿ“rÓ7…iÅÂ\FÁçú¯ûŠŽ½VZkÒœKq]Þ Ø{oøo]©�²ë?Ì%ýÞàGhˆNC�éúPx‹n™²p¥“ì²Ø½G¼¹¶–ÔIì=/ƒªHÁ9øåïŸ×²fqòãæÚ¾µ3B)Õn>ݲ)_\'}ùuÿ²ÓœýC)‚òj‡Íֲ±M)d2 ޾c øj£Àv×Qá1‚½øð)Øz]Ö‘ºw'À“óCG/1¬ Ek:`´oXþþÈw1ÃÒœÁK¹”˾lÎoŒ{³#ºn¸øè-aW–ìÕ\äUןp+mßôI ´1 íèjhÁ·–Vó%ðjÎ}wùÝ[nÎXŽZ+¢rÒÝ`wúyÚŒÆrÔJ“Tñ ²Õ5‚à• ž…èÝôª5Ì× +j�kÎ[މHëü�‡.Ö'âL”¬=ùoe»G ÊÇü§�û“@{Ãd°wÚf–³€³­€;&•ìl’ÑÃyPùë‹òÅqYïÃêùF åÂfýéÓ"쿃 ËtnÅwd¥Ë‰£YBÁZX¶€Þ’U`Ø·j¼ìÔ˜ +#X¿Aù.Iç½r›²*7Ö쨅I¯2�qeBý·¾> v iþAù“¹Å"¼½¤bV~7¢[Ø ¨{ g‹ÅAŒe †ÌŠžÅ`hXíL+¡¼Ùáý=‚§¬V›"{r²ØS‚Ô°BãÌÀBV’ÚYiöGš„3ôgQl˜/Bå¸Õ¼yw$À7Qð’VÅ÷o0²q|äR!�A?[æY÷Á“tï­Æ$†È,F­¤0J§ž¢‚4Ù¡›t¨>ûýrwÔp�ç>9�±„%/Týáó›úˆð®ÝD +ÓugO€ðB|ɱQšŸWI{~‰· $È0 çé•’0*¦í6œ š|ý, Ñ‹”‚‰Ý§´m_Ñ +�Œöýåoõ˜ÍE­ùßùÎÌb`!G’hµww&°€¹Š˜ø<5%kk;¡ú3l±M£ÐV‘æ{´ „c–o¼ZDpGÈr‰trYE�}£od�<*ÝãÑ*ÉÁ“¤Œý©¾ÈñøŸÜjðÚï!ziöR~>Ü÷98(œ5é6Ùç+k^®½Äv¢]G®)¤8}uÔÖžÌÄ�$²}w$ç#u¢Ûµ_ocíë=Kw*0RÞ‹ gr^b>WÐ�†ñû{©ßuýÛß)…±éê½ôiù2‚¿œo¯��ÅúïÁÕuQŽÑӾδ×ÂG§ÃÀÇh23hŸ.Úçú_Á¸Ý,öCì…|âfƒ ßùzÏPÉ~r X#\«xD´Ú™“·^Ä´½\ÛfœK{qsiÕF£ƒ06¦00—[P£Õ‰Œ1§vãŸKž˜Ž$È`;UËM²U†Œ"(eü~2=Þ½N^8e=kÑe¦×j7&¦†mõn"þp¥=]Hï¸é ÊŽŒÙ±(Ý‚§J´�뼆˜÷NMä +A@²P.z·9į°|#DÖ„vfíSûFÛ¯1稆ÀÕ�–Ç<'µŒÒêi§¡îÖÆc(-¤tJÎûk7Vjö;S㈩Jq…X�`ïçh UûWë&‡æR½ÍL€±™N|8HE`¥RÍPã¯�”�ãbí„´Ñú%O/ÃÖEð°²È¨iÕ—OÏ^d>*{uæ3R浆÷kî‡ÁÖRH—Sì÷ ~ó« 1MÎ;é÷ï»[mº¥ú÷´#.¾C�TdÇk]È«Ö3êÂWcïL¹cû�Kà8ijêpöMQî8’ ¨gëÈ>†é`�R46­9¬Í#ZxÐÂwSàC>iޤÿ´Yë'ÜôXS&q‹ý{CnÖêd‰ 7Y"ú=®¢õéiû3?Ž5 ¼´)‰{¯ŒäÄò†;†2g‰Ûnî�Tåº6‘ßâÎS”Uq"),ôC*¾Ä.y,¹$œÑÚ÷:˜9`£•ãÞ„vüâ*šÔ ¦§sÇíàŠ<ÃLÎEi÷îhs¯�MÁ „­‘¨¤Fšl-­©•þ:S¾´H*zÁ×Qžu…_²çÞL.å$^fgnŽÆL˜˜0µþoC:ŸKb܆-ÀÜÈ8Áº­î€ïí‹÷ÄO®·H&~äÇØo¬Ùþw‚:•FZuMä »r�#—#[¤ÙŸŸ­ÉˆA0Û’oDçª*Æu½xꆟ—¢²"7°öZ!ü+ç´Íp6¸{&nÒ,Už!»î¥&š„+uR"È èJ$G�S‰Ï¹ã˜5r̤ðÙ¾q‹rŸ‘:eþŠm²K“Ú…ÃXÝ“JúÆgôEÐrS¶ß©ƒ8;.VÔªVöi‰‰VLJôæ Œ;,¥éÞ£C ñ‰ãY—D”Ú„{/Rˆf'œ¯\M¬eÚ‰&õ³æƒ™R±GÒ„b>Xâ‹Æ¯vêMsC% è.­>“íÀÈ04¥ z«�é±ð¹¬t¬)RXOÏN¼•q6ŠäÔÖÇà skSîÈ\ˆ32�ä¤5Ø ¥g¿Ùý�ž©n ŸÀ—všh|&'Ôð:Ñrº°=Zð«éånF†�ªÄÍš[— :ÅGÿÍÇÍ& +%•ÛŽ1bþÞ�åFnÙ¼ñ r@vÊa� 6z²ï«îζ@€›Y) Zå@“dÌŽèZ’1SÆ{ ¬SDÈ·³€ÀÖ m¬ó é¦J^é�ÈÍyËF)L�Ñí'yKÚVÙª¬|¹�Á[� ~}”väw-]^ªA‰§6Öˆå­ö%ç”ÁioÀ[×éMk!r Ì÷Õ|á13Ž­BïœÁ¹´ƒÅ&#5GNó²) gw9ƒé±ÚrÓ>{=PZ´`]Ð~M@J�/ùêìt�™Nú©Zà(ÓWCko\êÖûJ#3£èÃÛákùS'ô\ñÒÁ!N½µ1 +gãžj7Ç%Ï©J¶&áU”°ÆFápZ N,p ª–é;¼Ç+j YXÛ91ü=êz)Ñ ax§}68…WɰΨ.²'+B´ô±�¹û„ãU§¸½ðáHZÕåpä0n¥�Ë5 °¨¤”ø°A#â;¢ÈluÆün­' 5~“D±‘ÜìÄÖøJ·—+:d‹*Q[Ò(¡-…[¹3^i.r�)ësÕP»Á W#"O¥„ö.è�Ïþ½uq„GüNe.?’ÝAøñˆÏÈÙz¬ "… hÅWŸ”Dë]&àé­Op¯¼>8½ç»uÚw¢¦ÒtXçÜ"sU£:ڵũr ¥óE%y_Ug¨:lá€Ä>?Œ2:æùÁ¡š¤úbg&‚èŽÚ +• ´ì�G›6£N84…ÈÕæ_Íü”*^c�];;�;¿W<:Å9!YÃ1 9E«RÜ;ôoü�ótùýš›âP¨s:ëfeXÅIJÅuDuʬä]¸=Js¥Z�Ä¥DQZQ‹¶T>-2Cjä:�VØ”TXq·&É\œ(ÓϦFÁ/h-ú(L¶sÝb¯púŸÑŸ®ôÆ%˃½æ|.Zƾ2õ3±õ6#@eäÝ\ï­‰T`/Ä‹ŒÂªôVæš”™ÚŸŒKÓ‰Í^bsûóÙ›k‘:ÉÆh.—Ö^‚ÔZä• s³Aîr§…Ö&¸g¢r‰» \ÒrdGQßE³aŠEªé昭UÒÏD äÞ ™RÛ[°G%l¯49sóA®Ã„LÞÇg¢’K#—ÃöNÞâÀ@ <—Ø´õ-êE´Q>u�'ÙÅjéoÁ.ƒLw7$0‡¥[¯o*HdyÏç‘-ºŒ·j¾hÔµÓ0—�¥ƒû7íÙàÐÆO�&:R±s¹]ó9u>ìð.‡x Ay#’4séÓÉ’LXµ?•õËê‘ �|§6Z4„Ç{/É+ŒÝøRÎÿÕ¸¯ð�ËHûûú�#>¡¯\J•êkr”Û¦r]áÊ¢5À8è3f‘šªÃF/\0z‡ÄL§6a3|-Á“œrÚ¸YäV&buƒ©MLÍH&Ô®ýÃ"%MMJBÚdâ5oY°WgI²w… +ó#¸•Ì2�YRÕi‘”‰z? ;³é-Ýî/D€OrH�‹[/±ñ;e’faÈì�åÎhpÂ…´¹–ÍGäA­ì"ïETEÀ9†ëÃæ†í˜ˆp‹<Í ‡¤’n`]݇F…ùÍOä2�ÙrµxpŠ68MZ´©N§|Vvî¬O vce|°ÅªU:'’wñhÒ“ÀR-”ölD«ƒ;m…6ÑØÝ�œ,FÝ`µ÷¨E*‡Þ[t ; 4˜ nêßVÕ6x烡Z =½4ÿ +Ú“Z%û†)‹ÔL,Èg£P.šYh—¤š6'—~~EMP²ªèæÄ#1<Ëùí…uÙè¾/gJ/9“yòj4Ý °åh^<Ð�p ­šêìm2¥í3€h:ó'Ì—üj û:ù\aö¦q'kZô ¡óc™”3šå„õÆÞ0œƒ·O*¡U +~ÈdØ4rè®ÌñÚ¯ƒ… ¸V héVضXÁU#EŠ*Ä�à_WZÕ‹¯¯¶vàD¸ªt:(ZÈ™�Ò›NæšH2¹°WqÕÖ[7Àøžö¸ÝKõ_4›ŠÏTçæhŸÛíÄ›c”ãˆEêa2AZasTÛv]‹;Ô 10{·!è�Iñûõ?%Ìd�“Æ¡C n?‰¶vAh©‡¥9R<Ѹ\›D‡½4úKAìJD0¹Ô-R6ÐD%LpÁ捻Nﶨ¯v0)TøÙêœWÅ(Z…¬î»²D4Æ�ªÖ ä!W†ÏcĘ‹ÔP\DˆNFØàôþdQýÄÔ©}hTÊY'G&2æµs "gp¤7ykõ~Bæ°'“‰:š§np&ú�äAMÖ†ºy[€¨mk5G¸Šú=�«éÄó˜0Žq=¢SÈ(¢÷v?¯¡êÄì/Ÿ¾u^†åÇLžK念6*Æ3¼3ûJuœ‰²R6‰ìXð$zБ¦ÅšžRÑ8'Lòa` Ã^”1ë`Á……šß¯`å…ËV~IÎDÅ#cÿÖ®ª¾»@ìÛU6áC÷»>J| ƒë`DåòéÏ0ghÐA od8 ‚±9f2j”…º›ÎÙŽºÓ�!§u�íé) ²ŠàÓ"Ro•M`òËú(À†] ‘Ï­!UšÛù¼þ5e!¦™)WY0c.\åv³ÍLr$_.Ä´ÞUÛ­@¬`­4ìYsRزX·µBz¿Y?úb-ü³¨MQ m4pík€k.o&:?þ"ԪߕòÀÉ„~ú­WiEL1 �¾ë±©zî¬ÕÁ¬”íWˆÎÁ-ñÕ‰&W¨–û™¨³YmÖË`óaAš ÏP‰ó¦ÑÆôví + ’ŽÊYXñýía Ž=¾œÐ>Ø�2ø#–Ï Þ\ƒL¾FWÆ*;šìÜì�ß¿�UÔ|µUðé~ÏE^£í¸'nv±�íÐð ¼iŠØ}Qÿ�e=ö5¦/‘{”Ë~‚‹Â|ŸªM4×I7ëèȧnùrÐâ +FÅkMÍä«kÙ’© Á§ù)àCHEü‚|B‘þÔ†u토C£ž2ŒÊ ´Êh¯ºe>8fgJ§|'µƒ6]ÏþºI¦šµb6 q[ôƒmTh5a—µ/ 9ÙÚF™²·FjêËtÜ   ˆ4”b‡¢Ê÷�ýGÖ²Ú¼;"d‹!sÞ¾ÑCM(ˆ’Qh½_G¢¦›$’¹4 ïŠB +·Í‰d÷ؘüÍ�Ià>è°Š-ñK@L7 Q¨¨Í÷(=àVåªÛÀl‘ÂÂ.ǘ}Ÿ1�>Dê φîÍG¦kçÆj Ï'UÅ$œöqûÍÅ,¢�ÒÄgÕO×ú 6�{ˆ°&»â ûÚ†L1›ª!¡uÇ-r V êô ÈjE¨ˆÍ¬»YñÀð˜É·w.‡IÇœΨªBÌEÊ‚z6¬¬ôœ€ ?�¾iÒ|ІðïbI…¼ÜmÎÂ<4µn"�‰½.{ö›Q˜ÿf²Y¬†rtBgÏæèù a`‡¦ï™Ú#<è +7¤o†%œ7é†; +�î¼½�Èõœé øxÈ9TñÈóSÓÛÕmOl†`œŠ¬|ÎD¿õ͸ÅV9;¤³Ár8/ŸÅŠÎ).).ûóëªJõ)ƒ·3ˆ¬«OÔ}€4·ÜbILBÊàýnj£„æMAE‰Åºç?ÖW' 3ÑTáDˆp‚+|ƒ0w“6ŒY=¨ï‰Fm4,:W°çŸî”™SÝ÷ORc·8QÎ £67.å”PÁ-AÒ[]š‘fŽ IZJÓÕ}%þžÒ»`;«,«îA*¬ÐÙ‰¹Skµµn[3‚+Îhó´A�Ùnñžg8œU~­Ñ„íÒ6à¿è–Íg•97¸lÂìåØŽ€Û¡ ølqÛრô3â‚ݪ(WÁ"kfÓ.òt‰¼V¶ya�žŒvÛ¸ãÇT¹Ù…5æ0e„U>§©ÒJ|¢‡¾�æ�`&06ZöYp¾šCkÐäìµ zÜ„›Ì»f3æYC¬ì‹©åÅáFÂrx˜/RK»à>u±ª¦ÍåÑ×¹˜k/tð*Úí䩇¸D³üvh(i¨�VÊë\ª‚ÊÁƒŒ� ØÍnµÄóÖ¢rR3X]­¦W1OèÎã:&¿ú;¯0½2,Ç¥W2«uÚüê¼®þ3·›�Ñ_êö’MÓ€Ðå—“ :Ü*-@5l?(©um•Nªä¨{ëdŸ)gl�RF«o°Û#"ËŠ'…²)Ü€"ëÓ9"H­KÂ;S‚t8P û&"|‘ˆ¼»¸¯SYyŽuQ½SþãKŽ œ'eòÂêíÕk.3øj˜×°B‘Z‹èrƬîTº ÀþêD`@‹w´AhãwS¨ »ÉIùmšï&(¡±¢ÉpHŒôŠú@w +gª'’Êa¤×È Sš`Ú�n­Ô�vUßjº�Ї©µl÷´‰9'ÓŽ +8Ûk¬,Åñ¼p^'(øšz YcâÛ2ÎOpö�‡e4Æ«Má&ÅøÜ]xWD?ôá�ÚêÜȇFÔÓÏßàC¬Í…†T=]83\2mÉ�œ™�R(�ÙXãtÇj½¯ón$1ÁQÕv­g :rµ¬šÆü¯\­§&oºÔœZ%mìg¢Åç;€²õi½Á˜Ãó8Î*rúê¤÷½/©ñðãŠ6jç²H²zG÷²¤ÆI9­‘sa€ÔÛI°zýÑ»-RB7gm´>Z{®X¼ÆÐ"€¬fK®zde‰.�tÝMsp\ü½3þˆq©}[VyÁ¤(·“zÜù\Yµ·a Ñ[€g,kl0Vì –¦LØ»1¶×-V`ûÈàtþ²º%š%Ì�ÖOâe +…¡:4/ U…mðÂzX�¯òäàR* 5‡�”"ÚÜ~%ÉÕdǃ{j¶]¥ z[­üów�|øúÛÇ7oî?|üúÛûîÿüËýß?>~ÿç½ÿÝ_¿yüÛ_¾»ÿêûï¿{wÿ6ÿû7_ÿá�ç¯ÿøý¹¡RNa Mˆ'�D�^)¿‘H$à«j¢ÿ q4Z´ +endstream +endobj +2205 0 obj +<> +stream +xœÕ}Y“e·‘Þ;Åmjdu‰ê,ì‹ÜÛKPcÒ!;d‡GtsXM�E¹§¶þ½÷Þ³ ‘Hàœ*µ_ºÈ*H$�‰\¾¼ýäÝÃ_îÞ|ûðòåíë¿ÿõ»Û¯ÞüýÇ¿=|üñ«Ï>ýàŸäIœÄé…<VFÌÉGÁ9iOßþpû­8}úÍIœ¾ùô¿Ÿ˜Óÿ9™Ó×ç>?œÄÉ{p.ªxúþd¥¥�"'Nߟû|ÿÁ¿ðÍ¿ýàó¯?½åæ•þþÍ_ßÜ¿|yûõ§¿þì$n¿zsÿççßÝßóVJ‚6œ|�àcp×YûŸ'qúÏoï?xõú$N¯ßÝÞ‰“?½¾[z¦¯øÃó? +!þ(”¸ü”îòS«ëOyþywù?1ýõòómú÷Ÿ®²ùÓÿ }ý©²�¦¿še ? +%³>oÑSEõÍÆŸÆ3ù¸óâüjâ×âϼ]Ow¢Žš>moþôú_?øüuÇfÒLæ=(C‹ÇÖ,´‹ud1 +A\8Õy�&˜*ÇÈ“…Xç™»œ~ZgÓÜ_1³H�þê­r:¯W¦Š•I!À[-d{iêd!Ô—ö€øQelðnÕæÜþëu'�wº_uy·úÍ2¨Yzÿ:ûút$'Æôä_§ó†Îî½þÌu"B‰›*c› ±êë³åúœc„‘ÃÛœÿ›l{®; L}f®œY°é~8’î_Ìÿ\~ ¹ïq×Çûùòï#¯OžþüZYNýêƒóÞ;’Þ·Iÿ}@\&øÏ…ïš«>òÅni©@…¤Ù ßXaeZžXhÕ'Ê ^(7Î_ÓD¦«~Ú@G²€EŒcn¦=œz< åê¢Å$.xÖ™XTeŠWNÄœu6Óõw͸„‰Œ„1œ°r÷(µÌu¥ºßbbfä¿!·j9½WBÖùJŠbUÁ€uV(Û^Vë$ìátYê�1€÷ÚŒÓqø•Òo+BÞ74&žm×[: uee|Ð*RóòÓRw ÚèûÕY¸¡²y+ônJd{iCœ²iÎnM‚ý'O*æèiAk³WùËÒ>Iÿü$ýóáü_•½ZËÊÃ|¸–§ôÏ«eà ËXùƒà-¾�›‡¼TŽ¥1µ4ºƒ€­S~e©YîMònþî´«Sࢌ=Së9ïϲ}Ì.W–h„®é#}×5•„3ÓLýܶ0ÑÝæÓ†Ot��Yþœ½[Óë Ù8¤cÖ]j²ÊhÐÑÛƒ6dÑ —ëA«EÌÕK|ó±—îLºõÓyRA'Rà'l¦I0„!”Fa Úààl“˜/Æ&! ­PF�Î˃¶ê_›D)µ"­$ãò ÁJƒ3‘RÄɨÀFßµsME‘à§ÕéT´ª’ñh‡%N•RQi Î@Ì6•òPi Áú$“�ࣳ!ð÷³Gé“ú\t)•ÕàdP‡²mÄ]ý…á8]Ê:å5DŽ`¸ëŒ°=Sñ}ˉ8]Š8-hmâ­šÝ +ÿí–QŠsk-GýR˜i­ÀXcŽ;îHk{vìi×¥°Ñ^‚Uþ°Éýh®½K[^,…q«b„�“4F´n&] +8£=ŸÚG\Lg{I¼ÎÐÏ�ÈL©”{ÆðQÆ�½»\O7¯ÿc§#Ñ3 +ž´g©9gÁ;¥ü~5. ÷ÂóÞÑc @ж3ÑôL8ñ‹ëŠJ]¹v3¬¢8_çžyTVGü\åo}]„ËÀHÐ&…U°†ص„‚â(á¤:h Úä"ô/ÁDeÕá|›?o·j"Ù´ wBrjº¾]>†t„WAðƇ#H‡¶Ë÷“UÖÊå7§ÏáÓ¥ò=H«ƒ2'g è(ÇRGÔJ£Ê¸#‹ºÙ¬r9I;;•‹²=ñ­ñK+So¡d1i•j$»Tá^Èe%áÑ)ÚÐv’Š˜fxÜ•Z…’dðéŠ:Œ–gJö¢`ÝRGÚiáíÉ2¸�k-"Ÿ82�tF¯Ë +·³2å·�CP¾½�Ý¡xtÂÉUK?¢’ ò�2RùžÉ5€Ž5æUÔìùÍ!J8z~ +uÛÏ_¥šDRDžÓbPÃF»÷Ñü�é2°ƒ¼ùtVROÇ8Ñ(iζ7ë•}›€a%"ð%JÐ6DÕži¯ÿ‰ù>µ D‘dÍ(¡æ8&¬ûÛæ´ˆ©À9} UºRÖnxïhÅ4‡Çžã}óouì™’|ñBtP⽌ò¥>cbòÈk¹/çì¯ñT&kaMºª¼…èƒ}K)––=aHK$=Äa£n¯�¥ígUw_ ¤y4`û>ºÁµA¦˜¨aêÒiÏèäù‹Õ{úcNÈ·ÌJIfE¥…<¡C·_(åŠvt�újÈ=[Â)Ù-;‘ê(ã„î˜f?ŽHæ ÿÚÏ‚sóü¥ÕèHºŒø²û4”2ËœS"ÕäÚ5:J½ ÿd&X +$ã xa“ºòtûUŸa,¥— +œÐ“cˆ„[bÏ"!º¢å„ Õ`Ôq$žN)l!¨àOÎêÁPüëBMe?¿{�GêIe X]Ç +ƒO"õ Š Œ¶a˜~(Øøw³Yÿ¾‘“©(ü¸aºæÙŸ“SÄ=d€)8†ÁS€ ùIÏ�#fçtyË!!{"º©3øž�BÀZ2Ƥ8 JªÝvÑÅý�ß~ydo@œ#¬?‡¤È^q÷åœôxZ *æ <>á¸C˜fs%0i€oZ{0~Æ• ÷äX®h€{½ÇKÒš³![k5³´#uy¢Ð ™~®yèžLDWÜQËÂßçÉÔx´ç £íh: "¡OÙÆY£:èü؆ÂN «t‚rÞŸ|òSŸ#R›ÖÆ{g´ï4±¢ï‰ôÁ“—”´Qˆþôþ$Nÿvà�ÑØ“„L +¹ùá$Á„ C¼xæ"Á‰ §'÷Õ9ù$^m�Û�`®™GOA/£xaþ%—Šd”Ξœ ½c¨% ‰Á^©ÖkÀ_.DðB'OŠ‹à•0n ½V‹þaYöL±ïWÄ›~¿4¼ºº–?ía¯9,\Z�24"åÂY£µ0kødò—£¾¸Pe+Ak§âÉ Úq©/|ÊÄü â’hT·…F3ç\H¿üw–Œ±`Ô¬…ÒjP"Á¬ðå*ÔŒø<š $X)ÛWË8Ô±õDQ›œIA¨Gv®@ª "n-H0Òk}Àô t«�.ü��çüóg’!@¦%H“à�š+Úé½¢´è�jmån¬{N"y‘QîJ¬”õú+ŽŠ"¤ Q&eÅÝöŽT,çF¸“AÙ±)ÉÉI¿ßZ´%yI2R.Ïp‡o|}‡�¾ÖªóUYî6Wµ1 +ìÃõÛwíáËNjÆêÚ-©´ßù£âD¾í©)”¯2ÇŠ^È~rÌqqI ÖOrXæøªÌâ³yY¢çŽLZ£ÒªemL!hÙ—®cf´��\æËøˆ',>YD+.l�6-›†¶/bç8Wj=3_äx·WŠ~´ä:4Zòd¿áî "¼ Ñš6—l„!èÆ!¯‰tì®B0ùÝeç81¿ DxäÐ1˜×Ö¦ w3�JºzwÝ8cI@`¯nëÁ +Þ‡›…Mpâµ Œ-@YU{�‡(Æ}˜Î(�­œ�SÜÐð.õ$�)R¾ãDÚ|ÒàFtÝt€F(�/¤£_§Ý‡=# f-„ØÃ"� Bl¢²%}>F‰ƒˆüËÌÍ‚ êd¶ù‚ÓÙìã—#×§óBÙø¤/¤}Y„‰1’@d^ÝÜÆ�tN¹÷ÁˆÑKŽË‰<Ô„gl­T‹ÝÀç×›ñ9®“;—*ù~¸„@ÊÂ^HDÊkŠZ*Ý÷ƒ{»8ô׈+�Œ´Š¼”�ýì°o#ÑS€ÐÂ&;§<šÍ7â4àã:Bf½Ô,± Ã`šù‡rú‘EËF.mîy ,H§ÃÞK…>{(W+ç1þnyGðáÐp6<ãTãn¶ŒÎ`M—>Ü-N4$×ÓC#I¢”Žœ³j˜­J‚7ä[ÞzÚãür,NÛ¼yº7�HjÏÕ›.:ÚËìòŠ.”ùY=Å£.ƒ›:áÍ Ä9tøXÄ>>)ý2¹~UµA÷õÓxÇ·G®ØùˆŽ¼á¶:µù.ï\ä;ºyÍ•A7ÃæÌ,ª»¬'טbm‘ô‰Ä~¬ªd&§N9¬ò„ÀÕ»#á'•{ ÆÊEÒMå�a¹åâˆtWÙyÚíZåôØÁÈ-‹N@‡¹ê{lsbäε2 ¼ »÷»Â«åîHÏÿÐE9A•Ù�[B�æÓC%‹0â�µ–\…9³J:XË�O†•DÑ¿Ï20�ìâAUyfÚÊÉËÙÓˆ²çJâÆ=:�Yýbö) ¨½é·†~áeh7ZBŒ1º ³ü—vcz]pàÞ½:žÃÅ÷Û +øj 3£Ñ©¥¨0ÍN‹QöNJ>*e:VØâAyÆ(ó¼!p�5ö#¯bf•4€7àC‚òÜF”{»jøM¾¡ÛΦ±'R®;ý™7³�´ QNM`�à †÷½'…¨ž·:ZÁ€•Þì¼à÷†ß,¹‰Òz.@P©0Fs~ûy†›Q©&ŒUa¬¦ØÃÍ‹è!h¯Ívu›(¥'…ƒT|ÅŒÒêOéŸ×é1[•žÍúéÿLÿ|6]üsZæ‹ë–ÂKbþ0 !*æÉ`À+çÔá$~Fþ¾²œÆsÃÄæ/Æ–|Ęþùfþ¯Xcƒü^Ámæ Šù­º×`•Þo¥¨ºÇ^È”l½�H¶øòÛ´°á=1A±Õ¨j§Å#ággr®Îf¾�T@ôÇÑ&èïϪDÀ¦;fÇ©ªƒ©F’Ží çoƒÿ•‹ÔyOóäòÌ:8#"àWMå%>µæÈ™ŒSu«ânóÂ.˜²|z¥ºã%xµmÏ®}‰©Ö +#mCÖ.Ç÷¨ÚbìÝG¯øff1\Žn‡ß}Øš0›ˆQϲ€ã�g[5ú™«o¥å»\ÈDŠœ‘ï¨ðþ—¤­¼Ë‰–œÛ­»ü�$j.®N�•`õ\Pkë©ù|uþß­¶C¹™€"†Ë³/W©óÃ7«Õçpc»n�`UòÇÙ¹Ï/¸¥¥u +êÕ¦céÝÚ¶W�°¬Ù,o³ÛÌ�Çi<è9n žÁßÄ*þ{íšÁsÀ +“&Óm=“†ÛzÏq½`Íþ�?;}UÈ}Q�¤M 0ÆPþ/ì]Æ­‰}!šÌA»kŸY­ù}›Ê„�ÜHÚ©6•�‹<<_8/”HXújhº"Ïï�é>¹ÞÜʨ2å¢Á�ò¹3YŽÄJ•­ír‘XKG¡V7µyKz-C9¢Tçêä©. +·ÿáaª—ìŠñMåƒ÷¡©ùà–Õ@ü꘳Ÿyà«t�Yù’,úíÖÓŠ¹òùÚÔc©¾Ž%ܧ>ãœÙf±sÛ�xÀû^Òðõà9~fÒ XʰX¼]î~„¥+éÊ‘Øa„2øÖÉØKa�fF>“ãÌ;‘*5?XD†ü’ÁZþÂmë +¤r{ïæâ9aƒo¼Ï¬DÕà-Je_n%z¬Öß; íD X©L +õtm‚´ÄðÅšGÀ aUÕw”+�9¹UQÈÂ>éìÖŽî3Anä m!:ýÉpÀåÊ%··ü=ö¶áÃÞŸS@•À�ʹ&ƒN¸r»ã<ÄE@¦°¡ «PËH ÖÏko@néÔËO€ŠÁÅŽ¥w(p¢Á:º]äT\Óˆ“0%:Øf“RÍêÕИÆZ¹¬6%‹†¶ M*ÍÖ¹!‡è²-€*.¼œ’ @jöÇŠÔ‰— |�+3VsÅ;'²Ó•Á._ù¸qu ^wìÞMjCÛ…GÔE–Æ€rVÚŠn59q3!ôB4«ÀK¿WÍ«…�¿H +*:‰t®\&¶ãÜï*ã­„{†w0¿r Ë~q,ñ˜E OùØ z•~Ö¢EUBUVE§Z—ó÷œW¶iø¢DãæV7Ì mÏ+Ub|aa#Á§¯í=÷+‹�„„îA,ëUGºH1b®f Œ“_Qª\J Æ8ÝA‡C ¹»•  í‡8´/‹Ë/Ì<ú|0&Œ¨¤žà�¥4>޳Ï*Òow%»à +*!_,[°*¶¼bBZ»“½(KÖÚ*œ<!D:g­Ã–SŽÁÙxQKd^ÇÔ+Ú7’ÒÊö¹òÙž ¾#ðíš¿8\“… Ù?CÆ +�Ç,^Ñ�*ÑÝ;Êðwàø[ J ¤Š4]œ¥×E&²ZYÂ`DjíœQ .JSê#b¬j½¥½Ó«éÃS�roÖÞð©*Æàà^ ’p*K Ê +Û3Ù¦"�‰Œ£�˜™QÙqŒÑ©.à(µô€’„ž<›ØÃ0U±Ï:#ñϺ�Æýàxim¹v°¶Çë-7úr#¶ŽÙî�m!ŒŽp=æe-  èy¢RYžæT·D•ÐN-HYõjyYæ«Ù¨2íìyw^M¿­vï}…ù¦ZSYÊ1w”#ă`ŇÙäò¥¬ŒO8qœ�+[©d¨ã^Ȍ̈¢À"A›*«ÏµTd û�/¹rQF{7£½l¯ï¦U48@ˆíi7Sx§ghp¾)Šô-æVnÞDÙœ  š&xIX@¥|ª¡nŸ{xw÷�˜GL¡‚…ø,Buö-7¾+«¡ÃݤwöœJ`ª3 eÆÄ.Óå¥-Do#rØuh³¢Ä¬´Ô5µ `u:­­…>­Â®JÕRGÖÊ0¼'•׸1EÔ_Þ”ʨáFˆÜ}OQÏb®EÂ=µîÖLˆx=ìûD¼žCg¹Cg%h;àâÂé﮳÷Гø:§cç‰ëð+ÇQI'O8ãžlª l‘ë|Œ¨&úæ:zJ• +ŸÖœs±cÂ-… |œÎ×á‹s½9;#·ß½Ü*©H+ì0Õ j7c"q�"(ýým3ôèmò0žm%?kfô«E¡>k,¡EíK� °V~m�z;¢¬”bõs¥Á‡8 Ÿïq5<ÜÄÊì¶}Y”ͼ{aeÍÿÜ4¢æœ°«ª›“³Ái©…§[Ùb´èm2}[æÑ~ÔÔ÷ÍtºúÖ´§É—;9®Ö‚ãj©ÀG; ùÒÏ�Åà‘ŠŠïíìÃa¾¸A/ˆDÙq¡öäM§½:K ¿²4^Cyp{Î>®‰ /)AJÛÄ}ä�—üĬ LÔç¬všÀñtÆ s &.—Í0æYeðþ´¥‰w]ª #ý¹XVƒ~üE“É\«ÚU¢Gi"À)!ü[3¾¹‹+i€|Dx‹…öv”|+4™ü¬Îo¼ËôÖç�»ª ¥p®"ïœ{?¨IT¼·"@pÎüCˆÙ!¨ gcÖ>B`Š2óSFŽü)j‘› aV´ Ñ7'ÓÜàùrœQVÑÐÅÙ_·ãQäÝ‘ó� +ÂVN45Ø¢M0Ä7·_½¹ÿóóïîoÊsfx£dsDŸÃ=·Y¤è»¿¥Á‰ÝeZQ{{uà´MuÓlø‡l—Â56¸eŠ«s � ¶cÍ ó¼²áZÏa2­1ó!b |§½¦*!MJ#Ô2n”HTW¤I¶Mè“Æ‚ú16á°H�bØÚ­ŽvÇDÔ¾@†c/å1ó^QÀ|u²•�ÜnïË*ã9.žþõsŽõ|Ò‘ÐÊ3½³X,ú]¾”Y)L/6Çe;n×nPå�ÅàÿîÌ„#¹ y¿uͳTD­jl +9ý÷*�׉úé«-Œ´Þ]— +Â{�aæÚR +÷,zÔ b‹ž4(Vp‘Û}ÞA�ù¨ÕâbÅ8ò€Êâ­‚RTxôLÌCQ™}á%=%ýû¨°%~˜¨Æ®½KÁ"´ÕVÛ¼‹àÝRÏq{Ê3Q�ÝÂ9uÕw#ØF_Â\l ­ô(y;HWªªÆ¹T9[¸÷Šc‹‰ùŒ¿ˆÖ�Q~6W±·½çNh8´7�J•'¦µÌbvÝ&_Jù¸,ÿŒ¨ñOù´×»GøHEƉó߯£ƒ ƒtäÒêÆuk?Z?OØ–…%=Ã{fÊ5+¢uŠò^ËŽýëð®(R?pqñ­ÊÛT�Ê<Íu4¹Â`Ì*ˆâÎÚI0ÁªžUt<ÖS‘xëÖû¯sÆ‘—¸ý}mœtxVqqýRÏ�tÑ­ÏhF¾úµ®0Ê6ÓPµ‡V‡ò¦9îÎô²¼¬äðáˆb¾6Õü²6ðÓÄNUØ>¢bîê4:FD›7˜Ö9PbΣØ+OTÆM%é�Ö´gß´x°{™É„¥©8³Q�VÚ +Ñ1éæÍ‘µé›BÍ·ßçD…Y#=8åR5�Ñ=Øq�oÚ _-™ð¢z„­Èã„‘´á³qÐ2�ÄG†¢cƒAî9bº…•)eV9†ZÓ Ÿ|ûð·7ß¿þîÿ>¼üâó/¾â“Ï>>Øu‡Ýœï°¯?­Í�Õ*l*½fÕ~¨<üž«îÖ©EÕJ€Vg ¡9£æÁ@ ‘³Â¿ãÀp« ¬?^Ct>@ׇeÁJ%–`zdΠî+ý®†>|8hÏVÙ½Šxw¦”âæÒo¥ª¢:ѧRÜã q˜S¼ÚMñÒ:ÿæCn­šQgv³MS�! +P®®�„PÍaU<&„Vg£j„” 1ŽR©øNÉ7"Ð}L–îÏyÅÔŠ(K{â*�žþnÓx[t4\”%ú¹™ sÉ’Ý»‡’ø2à6rR}&Uöá²Ù•–à�Ù/ç®$AEGJWëÁŠPÃ1,H:®ƒ*àQ¢”¢±¶º­qwÞk�›w`3p•˜N¹˜ÁiO½ 4µañ~-N¬2;{¶}ÔŽÓÚ°Ù¿#��¨çºâU% D³÷2^ÐpU‚èpÎuÄíá¦Tõ€ë†3##%£*»{²)ÞpU‹9«6�üm>ín4Fά8ô¸É}½ïcŠ=†!Yë¶ ŒÞé5â/œwǦzMÉÖÃ阨QQ{‘ó†¾&ðìÊÕ‰?Sìxu˜¯qC +ñ„Â'&>‘—ʈ(º\ozÚóY›ã}e4&¨¶:瞊f8F–áâÀp±Œ„‰;°0•KQ0UK ÊY¯:>ûø9é§�_)¿Ÿÿkz-¿æ ±D�V HdÏ27VÇL¡ä„+Þa5"oVÞ“—«ç²Úì6úyu„-²Œê_†µVZ¬äj�´ÍüíÁ�€(ë™MVYp>UiîŸÛôS$ªtååì‹D…Ùù?½êCÔ»²ÚAp:>Æ®cÃ73.L˜™ÚSÄ k¶úU€uÎ$ø†Ý®‘ZŠ”÷•’Ë´·Æ–üï÷t/ °¨8LIT²¢6¸¤ã·ˆ«¬Ý—³]ñ´È¾�¹ÍÒEn¹s}U­·Õý¹›�ªŠ6st4 MѸúˆ¼ñ²º­Lø²B¶Ëã†e2Z�4ÇT¿„]"¸A•ˆ­ÚÆy‡Ýú=çžþ5oZ ]üWYR×SŽ\VÍóW[ÄVgÃÍœ¥<¤Ä­ws3Yâl‰N�œW¼QžŸƒa\¥õ@�KN£ÍiÝù3ì®Ú>ßòú,눨%Wï“ó¾—R+ëþv†áDfJU¢†«*aÐQ‹Ì±öqNýv6œf+ü¥äX—RËÍÒTµ½94«ùÕ÷áCáK¹1"»ŸntÐ3 â)å (´¦ã™~ŠxhÏŒx¿$h¸Ä0}Šsè(¢ÃÙQTõ„ŠœãFÃR¡€ÂA=¶ºIˆÊp)˜Ä­ÍøiØ…w"³ß¿cœ&)¤•YZ¨Ç“�Ý.L8™fK’9R†÷-F¶Ç +B/¸rº£W�ÔU¸Œ»ìŸ(ÚbRK'ÙK=§¸ò��ÕdœöŽl!ó5mw*`·ö'ïþr÷æÛ‡ôxxóí¿÷o¸}õãÃÃ�?üéö›¿½}øû_¿»ýâǾ{wû:ý÷oÞüù/÷oþòã}Y×:jPéñ¡"xá¤ÜË"_œçú<›éÿ&Ÿ\ + +endstream +endobj +2206 0 obj +<> +stream +xœÝ}[“d¹�ÞûüŠê‘64åQ³IðÞnOxFו%‡š‡W«pL�U»rX5rmíƒþý³òœC‚È<'³{ä~¨ªÎäI>¼ùý_¿y|÷îÍo~ò�?½Ó_|ñÕOòÉÿ»3wúNß½6w”79$w“SÑ€vwßþåÍ·úîÛ»Ówÿöíã'_}}§ï¾~zó ï’2w_?lµË¯¯ÿò‡ÏþYkýÏÜé÷ãéçûógþå·¶/¿M8_~[Ó|þtþÖV-i½¶qÿǯõÉϾþäwŸüì7?ySÏÌt33F«˜ÜÌÔÌ]R‰ŸÚóýkZ¹Cø¬ÔyÒ}‰G¶æ™Ú¡éÁé÷[ïLBÐ5©‚.­˜Ø�}{®«�åzYJ€™dÛªâiTza…3Ñü:³,sG«R0Áîå ÐÎz¥SGc¼²:÷Ióe…´µL�‡z;hš_ˆ³þita~=ÃŽ*´%³bËtO£Ö,Ü¿†´¢ù"ÓÆ]Ó1?àG´í4„úͺp[ƒeZ+1m°JûyVÜ>)“bÚö “¤=„s-®¿î�å<Ã;×x¤?ÿEÝír(XÍ»_šv½ßs;7ÅŸl¸¤ —n„»ßØÎºý8N{¤>êï3|yÄæbbyÙI¼ìAi�ßÍÌgö|O^wËØÀ*ãAÛÜœ¤Í™²°ÈJwT‡?Ú‚'Æüqùq*÷ù}²Kf=“4Ó»T¸ uí ˆF¿lÇ�¹Û5[ä©á9仵 JÛ¤ÃÄÚ %C[�æ©:±×ƒ]£í.ˆz¡ª3J{�ýa6ó#ÞX–ÁS‡�åjr[-›o(ÙDic•¹ƒ5»÷ÕEb寍Aí<@<ŸåÛ‘°þ]}jí¶AÖÃ5‚Ï£¾Ú›}ÝœÄIÌI%¨¡'ú ÔÕJ¾©ÚfZ¡åvÔK; £Ô£w Ýé�¨á¿áz—ï¦G–[S¿?cT!ænž$Ëùùdi»X­4¤l÷Žà¹9Ñ)EÒL{¥;€ÒŒ�ÍP1µ Œ5àÃ%×$®ÔÝhן÷ñyøÂáoÐE×þñ@-Â}±w&[0ÔÇ7ƒ ‰>g-x“ :ˆž›´ì¹9E«-øŠ[bC,qWì±Zñ‡Ù›ÆiLL»E¸UÞ7�U6Þ¼ŒôËzGýºL§U%ª{g¡ð{’ûûš³ìßÕD+؊ʕ҂êµ÷ÁÃÌŒ–Îû{o›Oí³îÃOåVÑEQ¢“Œšš¸ÏãHÍ^&fÏ;Á +׬éõ=c“ +ÎD`E–ñÅû¯ÿÓ±X!d£ [ÞhwYK˜….vÂü{EÄ'³sÉFF:˜Vk_m2Þ¾u�Ùc¹l–%…fÉ—ºA˜¨¤„˜U �vî<-Z«« ¼2fd:{}Ö&– DÂ2ƒ¬¤Û)w(á.ÙC•ë¶s^ I…P”º–!Ü·ýöÒ‚T¶�÷�±*VùZru ¼Esqm¸y ís]“ÅîñBÁwh*<«&‰UCR1¤¸÷HÑ‹‡í¦Á�+gÀgÛýªí©Ìˆo[©ú†PÆ‘ý¹©L‹°o„±¶Â°ªèwb5*#z¨xùýy'‘Ó–ì 6ÇÚÂ( ÕET�Sz-<¨¤£7œ2§§.V-D‹ÕÌá‡oZ½ò)¨ìl:ÌÎ3û $} ø¨²IöÀ£Z»ë‹X™µr.ÔÖý^‡Ã¥Ð¹~æøÆø¿Š§�ëi­Ý_:øñ£“™×9r>Í©Ë�†µåã’%¡-Í) »{GowÄÐëB¨‰éÆçRI7.¨ ÂN.ýízbÚkuIYgá|æ=Uâ~¨fs&DWáí�ØmûÝ™ºŠ­ÔJÕD"ÏZÝ(|ßô3ra@|Å/¡Ûd­¢`&Œ;|·Ç9NuBgÉ^ i¦cQgù)ß+õº¢•¶�àøtµ0]â­D;•uvpxct²@Ë2 óû†ãîÏ“RÂRj•´ð‹rûÈÅn°‰7ižEE¥Ç¥£Ýo +;ýüÍ:j¯¬Ó²ä S=ža!»ré@-1ö˾äégâši�£èyO:ªmó-7›7èá–³ £”g*ÕêaE¬Y¯Ô2¨H½4¯ï7o˜úôuÌó¤¨Ý€W:æ¸ÿ!k¤m¡'Þý>(— X¿of½4ž’rÑú<žØÐÔÕ=.oÖõ3W|*xèô"¸1QEk!&:¦Û+™šØ�Š]ª(âê�$²œ/ºZ‘QCrn2äõ! Ùm–õ$À ʘdÏ;ñ™¶ó,Ì)L ,ÁEe­1×_{fÏàEê®4ÚÒÂÏIr9 Æ)�ü?ID\i ½,æAÅ”aj C œ–ÃÁb/Ûã‚b{�.bŠù Ç–o„µó9*»ßõåKÆ8ÚÊX²kØë*¤ϼúÐ躴{áB﮲ ¡‚OÞP¶øŒJ{o옶n| �ªelVú¿¬—Àòÿ¥6s#|2À) 1¤£|C­ï¹yî JØE·‰Ÿ1žílVòñ)(f·[ÕgNÇö¨ÓïU*°'qÂו¤ÌVí³Òº½Ûõ�Œ[Xܬ ¨&cdjK©ÉAÐx’£}qÁg:ܪ Üÿ+O/ï;+…æøè•‡¸?~¥SËv�î”ÍÌê7Ö¼ø¬*�aŠw‘%]{Õ& >�ª‰7pÃ&Âæ …¹Ðæ,KÒ3hå´K0Á#ùh�ÜãyØ?þ8—´ß·‹»…ŸÙÄü¤%�Ù§B aÿÖä�xç•>Y™¦,ý]i'ã/¿» Ü éšúÐXé†ñM¨MüDÜ»¹ +Ü|™K§›vSZ•Y*f£­ýeûðƒIÊ×$ë^ŠÂcZ)Ã{«Ø«å}ZË­'Oþ¯6†X�>…ÁI<ÞFÞ§½féÏ™íäAY}t—\"Ûúàê«:©™��G{ÅÎð_7ä–˜œ'1£Ïá®õ6HŒÚÊLŒµƒ,Ô½«vÃhEÊ݇›Accæß~¥½&jå³ÛJ„}‰_g«À�k,ÈÝÇ—©µ…»m‡ éwo¢:Z°)y¨øj³�¹‡¶¡Ö»‘0Äãq;l{Ó˜ RÎ9Œ`(¨…6¤å×ÖüOrI×èc_§ûÆNxâó¡ÚDeÙ ÁŽ|ò)�]Ç' 9C3ÏÏ’SšÓA“÷ÃÊ oËT‚`Òö�æùŸ¯2¨ù^ ‘X{¨àžc�ï�ï‡n@”ñX�’�*H�|å–SÉp½d¯ ¨‰�‰xî–¬Û6{Cöû¹{ìé´:|pö%žø+Ѩ„긹Ê-_µ»7ßÓ«[ì¶ÂÏËú«Ú�—|0ŠÙ=Ð5ƒmÐÌå§I\ítÉüžnñéðÌi™› %;�W$[·-É*¢w»�kŒüë•Óà'ü»’ jªíî>À%†«Q<ý‡~š¥è�Îä±A°§ ªL—p�Ÿm•wÖ‡ Þ=S ®±iõÿ_¡ÅX~ X{�ð°KQ9ku<°c–— .Þ2HVp­ŠÉ|ˆPÙ�…PÙá8>f¤lÔ|¤ìA¾;šGÉ2a}T`öûU±¼ñ¦ÀØCám#< kÛ—ðò�5v k+Ly‡wÙq«!WvãC˜xE hÇÓJÊ83Ý6R €@xŽ•ìÖæ€3 Bÿåãn<¨˜T†à–/Ͱf䦢p3E +ðÖ+£mÎSz#-§YË‹Ûÿ¥SŒ€�‚¬\ŠÚ^•G ‘šxbC%8y¢ÚÍm þ�5„¶'Ñœ…EtëÄ™™.ñ‚÷¹\“>±™ƒº”÷4õ2< —{9ÒœQx?A¡Cè.Ié´% ™_úšgz?�0º¶QxäHȦ:(€�s +±¸86`2f �hà �nq”Øf'"M¥lp + ¶“ñ3S«|æ vƒaˆä¿­t§ßèt[o�Õ¬'Ð¥—ú�Ë*é‚H2¦ËØËe]„½ àÚêúû%V+'´ñ×Xó8ºô…‘Qq÷ rrEC¾>Ù/ò1ã«·«Õñº Ç!VI”D�U`ä[À•ø�`äf»¾ªòãïÅɘUÎ.úñð/‚w>vL,�®s%BR QÅhc8¼jÅÉ×*9£×� +ù–‰ÅégºVNt§ ušd&B5ñ²"z¹Ì…�_Q’Ô ÀéýÆÞ¡§ $­@§ ´5¢{ÌàP7íºk‘”e«øfãø”DȘ֪àfH7´Už§TýÜîëf—þò…Ëñ¦FN€‰%PN'Ž/:AÔKÜ¢úA}aŽ… +8Å�—it.�1�À“W’!eóqw£±¹îùþu"€Á»±=¢*£ +#×f®ýN½F/•åð°ZU›(Àqz#zi`H.d~Ún +\aP`h¯ÀÓø9 â\�<‰‘%ÿå²�¼Í{�P¸Dqóö\t’¾^Ǽéȱœ©_S°ülªÌo…øßn2$ªÙ §L�ºPèjÌK0ÝöÄ .öhìÜ"vI£3g““êM—¨m&±ãô›[&Ô@”7Z‡ñ²Ë²Ç¯ÛÃs9¬×Ó¨±\9FÄ�Là´¯ “޲,厅Iωü€%w¶’—'—´<ÇSsÉ­XµÍ{,œRWÙ ]V»z3hµŽ©Í2?î¥{È¡ïé±Ë~çÛ€{âS tcâcüq›XËêB-jÝ PümŒÍPgË’ÿ8PÜ }¸Ü�ì‚k{å‰cäiOÐM6e\`qœœx›Jê0€W`­¿Jöƃ�R7ÄÛûÜ%­e —½³Zë6ý¡0); «¢¶®*”p,„$rr£¢:’/)Îù‚5T +±¨,gâËfŒð[yA ±@SmÇøšýÖhÑù›ö™ Ñ>ýòéùÏß|ûüîÝ›¯ÿö×?½ùõ7ûîߟ%NÓVg•ƒ‹ÚçÂqwúÎÜý¥ÔSÎÅh<øæÏS›ßžв±6ßé»ÿ{Î)|ˆwúô÷ù›­Úö¡9U^þçRÁŸõáÜÆùÃ\’4$ç«v]>Ü:^?úö®@Ÿ¬ UÝ®ŸêªQ½õúí'ÿúɵW cEPÚž(h²Sþ„è[(h^(è¼ËÚä€þL¢óXÎŒÂQT¥G\A¯åóU~Ÿ + Ö蜥©§ò\ì\¨§ž +7E(ó°²3åÏtúû¹Ÿ·Ã"ã,ï$«û��R÷:_±¢›r+´ã¦…z`‹0Â'ì2º×:"¨Â é@ÇèQ}¥ˆD�^…(Ý{ðÑݘ½.�JÞëhÝ ™æåæFÕ‹„Ù cnL"×HRÚ‡o½…~�ö|{E?µ’¨ÄK”¯HRNk¸/Y‰x„Þ‚ŠÖÓ´Ó9i›îŠåíô¯�QŸ^s é~uÍc¶û¾9Ò~%Q¾¿{LŽ%Ñ¢KW ¼�£]ùu5(� TüÁžßñÝ-Ý8Ñ«àÜüôÃ]¶ømm]Mœ¼õCNôÐüþ -.Ÿ›¤ï¼Ïn&œÒùýÓúWªŸ¶·ï~¸Â¦K§…1Ò†âliSÜɳOëô7ž×ÊFçüánµ¶Šé/<ïTÔLD=Ÿy¶œé&†£–?\¼Ó*–ß:\IJÈÏàå÷$kIK(]–GI)�÷Fº,wwü‡-òìÇÒ¬‰kN!`¾ý~ »&J×óÑå’d“øëùàj-Çš �"¼q¦dXñöðÄ?ã§Mdu7Ϋh3äÛœÈçn +JRtš9j®-!¿6ìyi.“oˆÌß&e¥SÈî¦+H%°¶Ê�‰pãt’DwxÓ\ !RY­�V1èo{aRi­�W:{•ƒã"•(Dx†;¯œMWØæ’ðM$ +—‹`8¼4‚ðMdñ­$7TLÖß@r#Rôn’ÛÁnEF$ÜV’[Óó߉äFd¼Ý$·£¤”$7"-í&¹Q”¼šÑç¿ +ƒ"[]’‹L×ý䘔'‰,³Æ¥ƒá¸ë{"PijW±î(Iw#•ÂÕF¥CŽáðrÍ”DžÕJ°;8uA!R™VbÝ-INä­4ÞG×di½¶@Ùòéb£\70×’(I¤[¬DÉÛ­‘S±%oºvV%o—öS 0D¶ºJ”¼å N¤¢«DÉkÑ`Èœm(À£Œvæ8_ŠR¤“Ä×c B$‘¼j{p66¨¬ÍîÌœ¿¥@þþh Zésa�áp¦ü?°ùƒr‰Ss`Ãaš•¸Ï˜ç°Èõ÷'Ðr½Rûwç²§�ð£óD=Ýpçv޾?³¸!6M2€ƒh¾r|ÒEÔúœ·¥'qhÖ� P ”÷FÃ�@¾.ÂÁD­|€¼8ó‰ËÓ©*tB²- 7?ñò8 ‚Ïv%+§¦õÙ®žz WÐV%RL¿gðUÚš­+å ÄÕìåñt&ƒìp½¯°}ëÆ"QŨåNö! /†AŸ \‚2�C1%ˆ ¨rûYK…Žn©Ìe’~†³¡Ñ(¶´�èYv(öalã�7!fãyar„B�òÅ"r|�' ÷ ‘gÆ$­L²®J^*Þäa†D„¼§³rÎ^�Bû¹›÷Ý÷át¨¹šAÀ ”|c�v%K‘?�¯½O㔕÷Æ›1²".yvÐı)‘ˆÇûÒ$ߣØk™t_¨>Œ"Û¨j3;§>$¼-ÿžúØQé±ðÊ`äz¾$!9›‘ç$õ([e}0°ö{Iä<‘7Ö(Ö‰2MÌÆa×ã’â.·SýxžÇ�U!ÛØ¸[­Àd[5lõRrëzû¬8oÿk¼�·�uÕ°®ºæü��—že¢SÆÇä>´K“Ôš¸<ßÓrŠï»•ÒPVV’§"ÄóqËz—@À2Dr Ÿ•v6'ÛŽôÊÖA"cB°ÊÆ]¸9‰<>Èø›ãòM æ¿åÝ_ýQšŸ6&«X�toJÊ Ø©"‹¡Ž? Ww�\ŸnJ Þóé¶{ëZ&G’Ø[ðÿð®üPL +„#gJÀE»«ûtO_N¯„Ãì™ò¥•Þ§,ã6ë,>ޝü +@ Ò‚ %ƒM‡;–žf<ØíáãûsžذMÜŽîá‘„‚']åïäßù…À Ýä—f¤W>a XÎM~¹5‰È/æ&¿Ü”`Y�_vw|Å—P +2Òxe³ÏÖ$�çéBàA®Ó-׃`ܦ[1ðÈ#Š@UܦƒL2tˆ¢À Wÿžïñ–& MÁpÖçÛ²P„´[lì.E¡ä•´ä.Þ˜,„ðCA†·ã#B™4zYUràí]ÌZ¥š”ÆÏꢲ)™Ü½BTÙé"ülí†t.¸u¼~TL^ë‹×ú©®=ý}®ÉÙ»P5kŒ²<ÜÅ +`1xí¹ÉÖð¹î·Àꈘ…¤¢6>¢1}oU>Ëžd9—ýºNè|ž•= s™!Þÿ': ‹Un[cÒ(.(_Ò‹}"‡ã=KjT.ù¨ñZ$1”r§K5Û£”4>ñ§:ÔÊfç,ß#þ™¨�t¬ó.³=¥RÁ[•mÈßC�ÀA±Ö*mƒõí¸¯+é�cbmT6ƒv{Ju:¶¸X0¦ ¸(ö¤�&{[7Ï) Æj´rÎj BŠ=ËÄ[Sƒ¸Nñ0n¾ýV=�Iˆ«ã0æÆt!®„“e"¤t¸cA0ßÄ�l& Ù%éNI=Ò‚Î +%ÆiÌ0×"€@q§€˜ÇíõD PJN +¨ Žå±X¶F*¡ˆÉÆ›î\Õ´�ÞwÛsô¿ ƒ"ÄëSÈOÌÇ—ÁX¡câ=Åü@8º Òt½$=¹DÂU‹�€Ì¨ÔâzH]±[f$èk@ iØnœsi?…g¶!Ÿpã +©(:^m{}! Š�zcPöäuôq†D oؤU4)Þv�à {zֱŬwxィ �Jr:Hé¢&€4*Éé(]®õÈ êF%ÈÜ’oÞ˜9øôÂâ¼»íú$Áô!—‡wα‰~7$�ÎQ‰~‰aäŽJôûXƒI,¼å§ð)¼˜ +ÿ‚ LløJ$[®=áþXõ:(¡¼(|¬å'\eNQðæ#žö¦†?ÈÛêVo²:��ˆƒUÊaÞ†‡¤ŒÖôhÊ2ywzU}Y¨2Nwùàþ焬B@røâ°õÍ(÷B8~1 l�Jó´ 6Ø›ù!09`¥:@ rT#†‚#ëó‡}C¯£R–›1ý]hËêG¥-ï¦ñÄáB ~Tê2EÉqÔH«¾üÑÆ%…ù¦k”$�ùø<ª2À •Ê|”2’ÎL }T:óaÊ\Mi¦ '6¥ù–¼Ca ,ZóÇÚID\ÿ¦6´A9Io¾éyAq¾Ñ]6SˆÐ{w +-q†Ó•머öíÑèc‘,IV…c Ä{ÃÒZyIj¯b°i/¨Í/Å\¦§$¯NiÐè°úÈg¹® 6±_×W pó„dÚà¥÷Û p«ó9¹� +�õ%/fv”æäþp.%TdmÑlmŠá0‘KG$/FËÀ¤M“fÂNÙ•@­´àÂ��ýÅìV’i yµ� Ò<'`0ÊXw:çê Lx i¡P˜‰ôbà³V6ï¤Ý'sÊ»¨²OÞO =}ùõZèSÌtIèk¿²,rm·WYJ¤,å*ŸÀ¼†„½êÝO€-9ˆÊǯ°ÈÄrr9/°�ˆp ‘ ¾ÌgˆÀ9[;2VðìJ¶ Å�k>Y‘°>Ò3…�Yy›Ó�MØç ÁZõ¶ñ 1ƒ œ¦Naˆ2M­k\ÑßÍJ’}kõ¬‹?÷úêò' |Ϙ6˜„�éÃÚû´æz’Þ¢(Ì®m‘¨ò�´c4˜Ygn@Ân¹A’2%Ÿ1/®´i¥)ØÙîqGxÝÚ£¿–2„vèBOëÚãL(áà´È¶–�Ñ, ÷fÂvC¶Æa¹Ú|níó;rÀ3l#�¹e¶Jà°U¼ì�ÊÑÐW.P° \6 ¦äätib$³Ù«.»n©·­²s®@�êMÔ{•³ËèY¯ý¸Sˆƒ‘¹N{�¿mZ�KñÎV]•ýÕßW»„¨(l£’/7çqF¸Œˆ§‡”K9_ÿžJ³Ï×ÕE%²PŸ+qŠÏ��‡ÓuÍ»pR¨vÛéR ïÁåÝô{ ¯›³‰¨8¡F1EþÍào޶à#×ÁuÅ‘ò¸®WìÞÍU®MßÔ–ÇÅ—‘n0Ä™0ò7�IÏ�ü"µï#ò'3W¥ôâä­VÆê²=iÕq`ÝëB6•^œ]IÓéŒÞϧxräû1몱\õ!q9ýküý�S‚ôjk œöŠP®¼£é°W=aÙÎh{‚¥kýÃ,';½úõ]ѶF!º”TþÀ”îï&ÜÚ~ˆªµêèܱ9HËcî:º¾&W¨{ogÔ°‡]ØSzˆ=ù÷‚†^=§”$3!‡M™¢N¦ŠšòÔU¼¾zŒºŠ=[»’G<G5$Ù¤t‰ûiõÖƒ•}{êfjmFýIqrúâ5XX +tNˆ$»õ¹cGgÖ +tÚlߨ��ñï42aö4ò¢ÑÂÄìg=b[¬ÏW× "´DÖ +Ø.æ™I UòfÞJ¡bþŒ²:F÷÷CM +B̩쵋‡©ùNälì„Káý�¾§��Q¡e“ÐQq´7ÔÄE(ZJÀ …-ùð‚±Ú·i°TÖ ë”OÚÂÄ`g�–°n¯EÇ–ž£_4lß§,Ä€Âhë dÜ#Ò‚8TNÙ¸k,ãzqye“÷°áÈ—µúÎÅŽ„‘kJ¡G%Q[›Ó¢d¬£z¤ñö Û¯•˜©µDª”jçi§¬-9ö-Ù0¤Ä S,hå½5½Ë[éËš '—Ü}Ò?XnH7Ò°)¬ç¬¬Ép˜h/FÖSJ¯3Oʯ”©7± ¡„f6Œ¾ìGDXŒqÚ¥-zDþkÐgÚf9ý±-vEúAùñiù�5�;¤GÑ’oÓ׿™~¡ÆQŠ–Htc!¨ 6¤‰Q Ÿ’&a©ödʵDBµJÆšxœ¢/`bÖ8ƒ=Ó�šÛ™hO²{á€ÜÙÛD;Ôu«sDø[8ÜùjÓGE¯­~öÑ%�ÈQ%m¯?Â�Ñɤÿ³4ÊiT°à¯0€Çûeu«;z}{ü�¹2¶FÏE7uH»kžnÚÊKÑbÇ„æ¼\ZÕÍP§ÇA´ëêñIz"ÝjÅ É+ÑïÕ~‰n)æ‚]ï©’>'™°­w�v1Ú!—&Œ’å1‚x»ãôÑiô ÉmÛÿ{ÔMÊ™¯ïaU€Ú–»‚�ÜTZYªõ\àU�¶É”HÂë¡È«�Â?/-æùuµê‹{E2;Ct%¥­ÛŸfl å Jeºêøñ" "yï–Ûm<›i«�ÑIe뎘Ȉ”¾¶øãÆ™�€,69¨ì’÷úÇU¹Å•f�é”á>_ž~Y˜—…óÀß¡§…nQ»¬ ¸y#`=l p³øt$¯ã.Ì+Æ/‘c¸ÚûÁªàÃþ'¼Þ8ꂊ\,…?­¸êû€™Cõ:ŸÚ5 +ÕêÖ˜ãŒy7˜7¿þæñ_>ûÓã=±¯­2>?AT˜%j¼·€? ëgçç÷{oUL69¬Åc ð&¾¿T—fJ¥|p*dcÓq®A+ð‰¶Ø”€™gdzè˜�G>bye5Ö"mï[×Ô&Þ{°~E3ö§íễ’GxPI0Çbž�ÃV+Wøb¤ˆÎ°âlcÞQô—|sk l½µ\�Ëó¶¹:A.7Æg·›~«c·Qª4lNkn[“¬Ý"ÙQŠ&Úš�Åj6l¯ ¦üØÍžÈ›]q¬3*{Ÿvslwï¯ï[�'vqµÑ¡ .F ƒç×»–k‡CðC½IÇÐ}{P3®D£›ðC(V�ÃnG*ÅBí-Ñ”Å6ƒµÕ‘kèö´ÑµCgž£¦-y¡w¥qzoŠF øZò´«U怋 æë3Ui3 6ë-K^µIPǤ�a¼øì؈ß4ѽp‘õO ,4 <ø@·9Hô�J³§'n•¢F?ŸËÄñ¦¶d5 ÒÜ{£•Ž&N°Ð Ñ%srör Q˜M”v„É*‡èó�ì î³Q�Ò9=6 9,ÑÙ�<åï-zLZå·)»L„à.’«×Êe°Ã·;T®³y1í]Èòmíq<úÉovTªŸ#Ë7éþíl¯ÂâKok ³2&ÀÔ`ìÐø'Ÿ ¨Âü4j<£ºò�hoaíxô` ¾Ó §sE¬…ð\*X§¶¡M“�ŠwíD]Ü�…ûÒà ¿äN?íQupë]Ô".`æ³Â"€¯ó ƶ)qs”LG&—p'kv î½[CV!'}â’»^Q€kÇzvÂK/'Þ·±rt†ŠnØ—q3ÕBïÑ–à’`âvâ4Ð#‹ZÓãÝà^p$ŸX¨­) PÑÏ.c½é³K”«MŠÊ@>‚Õ°ÿÙ%ÌŒž04ù ‚NÁMŒ~ÎáƒÅ! ºåçoÊ�ÿ6>³cCv +rÖ3£žõhÛñ*‰P“TH`�ó4®ŸòŽm‘Â×±JÏõzahÝ­3lžÆyü˜dÓÕØ EB"”ÐÖx>ŒÐ Œ,2T³èYvh‰’㡉�â@ŠëiÀ)›`ÖŒšÉéyàl»"r5×û²çW~¾NÿòCf¼éÃSUï­ü˜W¥Vm»{‹o�£÷BbW¢™ÓªÀÓnr„âÁ*©6ÆáÒùV\;‘­g4Ù‹øD¥¹wIimL˜˜‹|*þ±üøEùñ“òãõr€Hã¡RÎ@�8NÛçû˜”7Ù„i¯sa¨„L“­Ê1ëã¤û‡z8oW˜Óß­d=›®Ê“ÁÙÍýwÒX-n›\¹ûsìD¬‘°¼@Ry(=G‰õ³ÕQíÕzþlýñù ™¹%’™Ÿ@¦�N×$b=Z! õl+>LÝK�-«sϸÈòOä"¯ÎÂbY5n¿b<|öD·n;7:¶´†é4ÓÝ»Ï4&ÙóŒû2Î0B{åõÈ’€Eä[¯ÖÊ‚2ÎÆ½§Ÿo.+Tõy¸G)JÂäiEڶõÄ¢Ôp ObÈž8²åÅ>λ#s�(f¯À vY¸Ë3^ã¼”Þ-dåt´pt< hЕ¸r}o<VeCXÙ—¢õ˜é,¹Â䉜òÕbh«J~á¢ä¸¬­rAã¼ì—øgô­ÐN1lŸTª/Á9ÍÐ: +7®âx]C a�Z¨ÉYòé§„¢NµÄÑ%�>>ýù5Û:YûØm\|ó–®ü,e=È "¤à¢—¶:�¬QYGK�wp'>®ôÈ´Õ©`dÒ<€n p«,W¢‚ãˆBj],Ò KtÓ†dX΄¤7Êãõ>nV¾�g}ùí×öO ˆð|—TrÖ¹£<� Â($÷±dTŠ1ÉS0ÿôQ&RIë ì;_ºú‹{·BwL× +ÿRÆ”Ä9ŸÎ +Rg…Bõ¹ç$ªß{+?Rå€wóÚn”c{ ¬…Äë¡,p¯ä$�Òa¿÷½„Ì%wz¾�­ò)�­ªÛ½PÓ|ÿ(ÖBêÃøÜG­ {;R²�߯' A�îN£gϸ”4¥_Qþ°få߯[Ìpæ…ƒÁ4.• A+ãµÝë³Ôn³/jwg}šrmÙ +>2õwä½ÄMôáqØÓ˜tì¨'U¥½ÇkEM 3Íæîð>ðè Kš=Zn¤ƒU`©G¡*Ћ(0dä#mJöd½3æ0ÎÆ ª²4ºÄƒ”„½»e1Ö×O¹_>=ÿùá›oŸß½{óåóó7ßþëŸþ÷Þ|õÝóówùã›ßÿûûç¿ýõOo~þÝwÏzzóuùû·ßüËŸ¿yþów�Ý![6¦;ÈÅ=º> +stream +xœ½}Û’d7rØ;¿¢‡\-·µ �¸S\Ú\’£µ¼tÈÁ ?XTØš-­CÛ”G½òß;Nu� òœsªùR=S…K"‘òžo¾û×?ÿüÍ·_ýç¯ïì_üþë¯>ú¿wîÎÞÙ»×î.€‰®¦îr ¦ÄXî~üó›íÝ�ÿvgïþíÇÇ�~ÿîÎÞ½ûðæÁÞSîÞ=¬�§?ïþü¿ùÞZûýãåÓz·ýŸ�Ï�þ áú}ºü}º�Þ””RþMó½µs»Ì·ss;hÚYHí¼0ýÛ~ÿ¾™�Îê·0/c§m_4ã:“wneWŸaù µ}âøA˜¾mõ €·Ùl�PYD[˜ÇùÇw÷Ñ7ï>úo}óíWo¶ôë4úÍÁXÃQú•QðÚ…j¸kKÚª&=z b‹áÙÚ–3å¹Üí)mHÛs9­Þž‡–œÐ®…óFWc[hçõ{;¨ÁÞCAxÞÃG´R‘¬A#ëä�‹¡æãtí×5´�-ûì|<_!Ö¯ØqÁT›R¼Œò[‰M³•Ìæ�š‘z=÷øR&ý6€/¤¹ù.‰=ÞÆÍ}EÚ¿�>Ò–ä¿›>êòÁM±œ6ôÂ!:çÐ2_w–oñ_$ßÿfóIG|dq^²q"]z�.#—ìav¡ó’dã]*5 +gýÚðž +"¶aÀ–ÔçM\ÙÔ|á_è}@`š&JÉ+3ÒtÿÕòñûûgòv²]®8ãœË©¿]î®§lW{h—›ã‚¸?L»ð$_O¿»®ör¿™>¾Ú|½ÜÕó½Œsùø×o.->›>>‘—É’¡xÁ¦³ú9ºE’vH‚3ÞÙ<ÉÌMz0µ†Ò–"qË-vì=½Ü�o3f¡Hé~p‡°4/] / +HÞ^ë&®¸’ú€^Åù1JÔÊ�//PèwÖˆÛ[ÒÙûT:xjˆ&“‹ÿ‘ mYÞÉWŒ=ŠÜ„õYFGÑÐἉ©”(bîª #þf{#N²† N%à¤`l ¡ž‡æ‰òhwø–¸÷ÎÔZkzÆá¯›IèË]ÓÒ`{¯ÿóþÞfåf2QðáTý†c} ñüeüÚA2ÕEç§‹™ÂõÚ{#tm™à–-á&¢¼#jq½Oy¶ +·~”FÙ))*(AÒéûæÆ£r&†½P±Ëo Tê\ÿ +ýýk�µÄ@+ô 0ÙNSj¤êŒ Á§Ù(ßt ål\�<(Æ¢Ö*�G¦ÛÆËh �yv¤?¢!05ÊȦ¢¶‹ÉävÙ:�üuK:“ìåï§ +lTÜr% .»ó„°œ1y~F~¨ÖdWýYÔü¯íÖ.œ^hvm›¼!ÃîÃEkëi abå¥j6*@ð&ÓYôýaÂc‹¼™×Y.¬™^C&åí d¼·ù82g\¾û?¬ •Š5®—–8…£GLÖøÝ•+~Ü ÷�ÕƒY¡'~µY1óBJ½GÕãAÉÃæ•ZnÎÆ#ƒ1Ê»M×unHYÏŠqÐ×Ü�>DÓ†•7¸ÇCÿ-�ó]Õ(7Uãj=­^AoîL"×_šrÞŽy¿m{½jg(£ì¬3ÖW ‹ÑÏ!fº’3¹X OÇèÔòÄ�WÅ"­,t`Šωîž]S€G¯ú•ø¸U^ÿ: Pþ�ÌÏ`—�ò�à‹I)Õtž—[ ½>4"^ÖiW�´·ç(ùõÞÐôÙ)6æâaÿ"%ÏC—­Ê”åIí@Öwùw�¾(M`(ÿUª©©LwO÷Ù›”sI=)>é¸#Q磣º m_(»ç¢3®ïNoÌFëÁ?,ÊÔ¹MLÙç�x_ˆÊß¹b Ô˜ë ðòL¥€œ?}UV°p%ú_kž¾ÇÅTçs8�5tÂgô4‚û*®†³ÈôN»Üc³w»{ÍŠžB6àÜa#úNaÐÓ—&9«ËÐeðiž÷­Q;]Å¿ x³]e´L-9åÓè}º r*é%_SO°IVàü�¼¬úÇ3/J©&gá%<üºÒ­á9×�ã êqƒ÷Ö3ÚŠ Æ;?ñçvÎbŠ}Þ $Sƒ/§÷hä�PePŸ ¸ãÏįž›ÅëãWG ¯[I&Øêjè>a»L²à_òâôMs`M�<±5ç#åhàTÛÁÔ¦g÷æhž9Tb.—dôÛ9HòY‰vp8u·79ß]�Îp‹¶�|D`Þ®Nn-ñôq_^(áÀÏlRg.…¤q‰GñßcƒæÐ� ÆBü"w}s²7Ö^T]�_JÒ ô)ªyÒºÆ|LN<Ää¾óê¸û‰Œ–Óe3Xs˜i@rMû™Ç$8Sct/€¹WBw^E‘y^r5Á¦\nA†³â9,^Y�г9W2Àô¹WLp5¥!ÐÙ—ÑIK|<„}i]»¸�5̤ö÷ê3˜1h÷£ Ægo�ß�ÈÇnzm0Å.1#«tZ,Ïr,Áq^ê¿Ãqõ4/ +ð¸Ð�ùÚ‡[|Ä9‘õ›)HR×°‡ R {v‹…Äß°1…wÄaš—)9j”l½ñ¹Ô[Ç +­+™§×àÀ$çBЇ-PHm9ÿìzþ2–ÓõiqBÿï×qóòõÅI¿(ØaTÉ™dK»É](×>¾)ˆç×��†Ý_X˜å$4Frìkt\øKŒ½cù|HpšÞ¸]>.–á±—×PËìä+¯[óõŒÅ_í ­ºôff™\ð~š(çÖð·K°Qâìsæ]ß*n²I²­3­9é‡á° Ôóƒ2D§P?…Ƽ+<^ºÝpÃɶ‹¶�Oê��ÖÂYâ\¹ào°ø$>•�“¾ Nh¼{3›¶–¿Ø™:Ý;WLŠÉæ5Bã_)Ëd¸?ëM­~`•ç͈™sÿ,&O—æY,Ë(úŸ¤dF-ꋱ<œÆÊÓ}Š&A͵§NFíÄcŠÇÃf™Z=+êÝrlD$Çsµcâß…•(9-h†qÙŒÞ@Må&$ƒ‚W‰“×ó3¥‘�W J‡É§gPÊšÂ4†bbwœ¡‰´o!α¯ì·{¸vÒ— )@úºVåJ†às6|ŽwÂb¼&ñ8¢qú�&™ø­£ß1“!©ù=êµWB¢õvJ$…›&³Êéúl²=á ƒi^ßÿ1åó)Ã)ËÑkqáYK'GgãMÕœ9Ëaága1[ýJ@.Rܶá²Hƒ�¬¼F~ãq�ß7­æ»©�éÀþW +æTîÓVã|:�öûöÔµ¢ +OiÓÚmlG¡g}^ã¦×ÄèZÃMe›Ó^|d†V±:Ÿ+×™GlЉO&ãiÇ›O•2 À͌˸·&úRkŸV~ysxaT®©š])gi›Û#”ŸFŒsOƹɧRlãËŸþòÿ¼ûéߟ>ûÍÛ·Ö~ùõ—‡»û c÷íWhšKy¨Å@Ig’ûhÙz6i¼’I6%)÷Rïn]ÏpO‰óˆŠãÁäÉRæ]ÇS�!€Õs†_©'wæUËÒz-á1©SÀØ£"0õ í¥¬`¹EP4Rò)ÓbßÂENŠÇÏ¿ùzJA–›ˆ_žçÔs8Pyƒ;4+ÕX=c +b�ûɆyäÇ»#ˆ¦Sõ�ŠödS°ErÆRè8‘]¸G�U£UM¦9™X"¦UÂ.€­ÆŽäI$-QN#DŒ+êOr$YM¤£Óãû< ùõl½I/ÝÃm·äúÖGl'¼/ƒðo(˜ôhO¦L£šìR4Ù‡r"m�¸L +Ö Ù=I‰æÇË‹£Éèz·»Þ(íµ@‰€®AO•¹>ᤧÆ0íé“/ލ³(€Œ@œQ‹Gº#Ié(³M‰b0¥œH¯Ã%á O€q%—…ÙÒ?0-ñ³FïÖuâÎLµ�wCCS/¼îäJªÉä-M+äÎoÃéÄQ/aèˆÉQO¿¦£ð%O>§ñ(³,ùïo ;W#‹ºñ=òê¨t[½‰µTV×µn,ža +ŒÃá4Áùº „Lj1³¥ãž’“j'rv¸Ý|RÛŠ]r›^ÿay>÷óÛ¶²þù ˆnZþƒ1—°uÂl^šð ¦F™‡ïó”ÅÙºлÀäêä½Gz#œÓd°9Ò—›í� 1 +š¤UDbWg \{‹WozÒ©�%Ypù‹l܉¾ØE¬å‹?/‘™¿¡E{H÷Ê`Èd¬yø”Œ�5TùöþuÊÆÚâQ* +Än´ +šV}ˆ ø‘±mÛgVcHÓïcAw‹ ¬B…�¯/<|H‘¡»–p�8ìtòÀ n £ªM ÙA½`Õ„H£;|žÍ™�BVpºVÞ¹èé>”)Ó¸x+¦(l’äüBº³Xwðß«vѲ³œ¬vX'*ø¸KqÃè àv™¶ "¼ör`µ*>ãc©‡éqVÎ0rÎqyâK¤Žaè^#¥:ˆd|9�2~’qWö™À�D6˜4D·ÜrtÝÚ8¤UuâQó÷Ê�Ûª°1_EJ±uIêpÄ*Ð7=:«iž¼÷&N!æ7g�}˜T|h“L ¹ä=»»¨ëÞ7wgíds�2vù%Þ¢[šg\›Èd=›2îЕ$™.’9 3bPœZPcEd£kë C¡Z͙Θ‹;áL‡p,Ô@�úèÔ©*g5M¡·ÉØ’OdŒLtÆÊË%P“[®î9Ô›wez°f>Þƒgc_Á£t˜lR^î»Ì�»×S-ˆG×:[Œ.\e$^ËY…é(”—æÁa‡ÄTM·W¡RM3 5ðù¸‚d×Í}¯x¶.|5ë×–ëTI¼¹Ÿ¤¸‚„{öÝÿÐa˜-?¥âx€ +ª Ñ[¤í.…Û9›0jÈ«Ây.ª;¾;»’¢³šW”`üdœ;|Cò4’Z´ûLG'{OòL%¯°8«yÙLÖââÒqÃÁ˜FoÙù&{ïCÏã[ó“4€îM‡š·&C:š|QCÁ…õ)Ô€ õ¨A*<Í<ˆUiN#°Ÿ<@¼%¨u½#àó>b´ÙÓ¦©Û!&SS:¡ntüëán& dM©ôƒÛi|jˈÌôÂÄ®@íü��æ‘ã\Øo£ì'1°ª´Í�€å‚39·"…‚óÒÅŸBëšNÂ’Oèä5‰Œ½n‘†NVm¬¥{æ»D[¥¦Í¨ Ü6Tõû{­ø©•WNC3«æ3°•°pRÔmˆèÛ®•Ñ‘£µULÙN-ñ +.›Tƒ;ª±3›\ª[Æœ×lørD~Ï(:fAC×ÅÏõßÑ%MG.=R^¾å²@ý«ßjõ’Å¿£Àò5½)Wy‘_ +ïctéFš�ÈoAÔ+Íñ!™¨<š×Ô=`“É“aâ„ÕhgâÆ§û�ŒÏÎZ�ÜŠ¥‚f�v�rÀt-{â@ØëÁ» ‹ ±>%ïH�L®Žq�ÆFŸüÀÎ�–-À™×áp6JÇT>Ì&4qßÕ¨EþÞÊÂ\Ùã9õÎËãóùû9׃VP˜+�¼dà9ŽÆnIa¦’ñzÕL¤WBM‡·¯UWéÒ¤|¶a9eí1Ür¬½4ñx07T5�ê�)PÃÑV,sÚå«3|OCL�¤»RáæÏ\Þ¤&¼¡mÀ1CXúþ«‰®<—,?牢x>ÀÜLñ¶´ù>Ç“0ƒ4’¤ºl[5lò}Ä1‰ñ NUæw«œâGߘ�œã©µá˜òÌSéŠàäÓu±t\eaÆúìn± cÉ¥¯¨Þ†ÿÌtŽ6gSb@Y“Vqø8J»o“ZsØeoJrþ°J|ï½ÄTŽÕ@‚Tã4ƒù?—ì/³“L G¦Úà—EGÛf„g +ÿ¢“V±ðòp3÷ÜæBþ5{28hÎÝAÊÎhÚà© yÉNØwZïS˜"¾ëT:Ý»Ýù PGÑWQ�¡ušd‡Ö¹×y,%¥²uì˜êá®xãÝTA±¿‰7cšwÜ/Ì@³%È£É`c¹Y÷•`j5r¬)¹Ú®Û`Ð&d7ž'6™’s E=š^±=Òƒà_ò<ع‘“2¥ÞL”æRÏ&tÈÛDû.:xÁë…µ4®t\a…\˜äÕPLu>��K÷££ûŒ»O6ï÷ó7M¾vòµ¥MÒ:Lù]I‹©í¾9qPM)ñŒÁ¡ãÚ2UðLø„ˆôwl¸g�ŠYì=ø°vfeZt´q´}k¨j“POh�†¿¾dë“\)¹¿ù/t~F5ÚºTݶ+SJ‘züm_nïÖRgç=eç “'P¤�T²®š’c}=�ÄÞŬ!nÊiä (ñ·Î„_9ª‹¸äƒòOƒp‰ùþaÎ8ÿj[tMóbˆš ›ª7¥Àáûð·|´e{À?V÷‘ßáz“ÜšÃFÃÚÆ|–T¢áC�Z§>¢IkS™Gõö[Óï¶sGýgñìs.Yòjæ`¡]ô^ã¹ü5Ïp™~“Õè·€)¥øÃÉÊ÷fï @ ÖS{íór€o)ÕŠçZ +¡Ê*.¨îs05Ú�pÑõÀ|mÞöU-¤@ȘBm0à¬;¿Y‰HψÜ.12VÞÕmo�œ³·dmŒBldŸp6fíot‘æyµâ€$Á(­jY.’¦õH¦Øªz\„ìòý© î’Ô¡ÜK›¤0Íh¯¤9ö +'déÇ ‘ƒé{V©¹wvä·Ý�)ð}ßÞÍž%c¬y‘v@(©¥…nòpÊph^8{¢BôšÞ$MñS…ïCºÏÏdÕgbYç ¤)wOwÖÎ]�ôh óШ‹—— ¡li=smKž²)sp +6•ç�þ{áû/øùÄB%OÑ$ùÒ7E±óv4ÙVlOêHtkÖö¦÷ñR²ÙŸ›`µ‡·À“Kà–玕8ï0·™S»j)v¹è4$7êd¡‰y)LE®üiq†è}ë4©µ»7爗M„YQ9V\F•0œBw¦R m�÷œý™Î�…!%„Xã9qAN‚+¶€6lŠÖõÄ ý\¶ž©hdë½Ià�«u�U~VZ Ú Hó#6%4Oàó-ÐJÛ‚nŽnÔáql%R}7Éÿ 'U·C~OTwÀ Ó–ú¡9Þù·BB›.DsˆñQuG0)EápEAÓɲ¹ß{ñNYè¾”#?Èþ¾[¥1ÑS<4ù™±ÚÏÒáÔçaZ1M¶Á2ËàÙì±l(-î›4—ÛI†ª]“•ó²óªR’V".uõÐÝO?ÄJ>YU>9g*ÀáÒ¬Ý N�Ó]ÿh|̾ÎÙØ…ï{î0¨=JvЪ5Vµ ž¤ ÑÓð�´J™é�…rÏüýñ âQ‚“ÐoñëÕåêdÂ�Óé£F¤œkŽ›Ò>AÒˆtåËŸþòÿ¼ûéߟ>ûÍÛ·Ö~ùõ—‡œwî/ì·_IÀi<É:c³+'NP‡¡œTŽ5 …Ãäv­,¸°w´4#Ÿh�´ï²ŒPQ-ÆÚÚzš°Oïõž#Eµ°^”àÔé`aã ‚àÅ”�d¢IcÌð¾c­P"-êD¢~…05=cÌõ’{óö%ay¢ÀË#þݪæ&kŽ9qÊ�cmz©(¹×Å[S�bZu¡®Ê¦äƒ>½AQû -_/‚�!V)‰ÎMM|édíå»îßã®ýá)™–²�”Œ)µÙ$W—‹µ &W—®[|ÝjÖüt&ö«F{Øý—Iã=¥£tÑyK¼Õ9çi=h”WÒàŠlÛ²Õ +ù¶‚w“ÍÀEV_eÚZÎÐÙ¢$´>”JCyeƒŽ�(¯è¨‡q½T›_òõ]~³¦.�aJ]V�—XäWèëTö ,g¸qD÷kSe‘$:G#ÉG÷ ÛA gFÆai›Ä,£¥R‡ ¤3^2ܶ{¿Æc  µo tx} dl=xuóTà~nd×i¥Hþ÷~]Ó�Go „zÜ£W~· _ª F\À�”oÃZÌIea@Ù³ 7Ô¼Óºã2Šþ¦ùa^¶Ñm·àÝ=¼ôêÖ~Ð.Œç36^†§÷(�²¿¢ƒBüš +>ºj|­Ç G»„ –ú¶UiGßm=F•¿HÐÅÙÀ«éõgRdýÓ±-Õ´‰NøUðþ>íÅz@Ò*šÖ:Zk’ªsö…öZ)Šâd_è[ÀОåÖüž½Ñq®æµLÒ†ßö÷¯¯Öb�ÎÒÿ{�¢7~Õ¦Ì>Üß?høÓ§¡]<|ðçÕΡKÆåâƒäà€Ò8au_ÔU莂4a0ŒÃ¹UÉ⤷‡Ì°OXú³3bJâíÕýÇ£ø7üáñŸ~óÓã=uË­S­ó1Á¨!W Ž(LÊvMª¹ŽÀÐõ >Šô�Ôª­0*†e…¿ˆ!¦h +‹�³I)œ°åwŽïFñäR4¹D—Ø›ý,Å~´ùAš4�¨ÿbgãsX´î-[…‡WÑmQ)Ò„ ¥Hü¸%’M4GVäïÁ-�g´ö¡ˆƒ’†T�®X9Lšæ"¤dŠ 'L›s¸€“w¼�\r9X³‡ ¡eÈ.Œ,œ£ñ>¤óXùXu�Pð7ü¢�ÏdiwÎ$WC9 þçÂŽHz8¼oØ*Ö˜:6´-ôÛ\a¨Å¬1Ù”»àš1:_<�˜žŽ%›ý6WqÓ _Åò6VMÀ1ššüq£>r¼X‚ÔZõÊÏR�µ1®6 +”ÙXL=ˆÓёȳBZˆw8i©ëÙH�Wç§Ü¯ºF#\Õú\êŽ.T\úâãóË¡s—ª±:�°C4ÎùpºŠ„hlDóý~ ·>Mr +—Bž/t°%6]GQUñЉÁ¶uã›Ìšè«¯åYìþv¾�™¢õmK) mE5ÃKhg+;`WK<ŒâßÑ<^£ãÂ' Z&èq0u +FrZ"GulçûÐCr«ÅIÒš¤›„`[P;jD«y…Kùk8ì ÇÙ)Ú�õJõÀ˜¾°�¦i#£ +µ{¶3þna?e›}öcÆ``2å—’Þ?´P¬×G­lCŠÒÖˆ¾ýDÞ£ä¦ +Åj.EÁ&“jQÊÒKý­y=#¼D„ƒH!™mLR,› •¢d.hÂS£�ì×Ë Ñµ`ÏŒd)‘ŠØQçîŒúO7q¶þiP ‘Ñ�M•@§ŒØ}:ìf.hiâ=Mü¾J�INrxü°Ì +J1Ò·jª_§ÊpÜ!uoÞßÊ%±™’L”!hº[…ü–nc9lŒ54Tニå<¦žö˜›bèò\ww6ï~NdTíú~È›V“þ}.¦Vë�²îÿ‘ãß_OÞ¡qŠ|õ¢W¸g��“zê:Ðÿ$NÈßµBû¾ +b£¦%s‰²0n)Äǵ/kb8ŽÒRÑK‚e +�gl².ÐD7�m›thÖÍvèö$Ì‚Dcð_ÊÖ $‹ÙG¹vÀRa5N׎‡|šàg­¥2»&uøTŒóµžõá#A.²ÛQæÔd&×mµÊjÔwÖG͉‰AoCĨ!*[Œ–-DšS‰ è˜_ù™ðê ©’™g„ Ä�¤ª>„åT¨X“Dü”/y²F—Dü®Ç{µÕ¿ŽÎ€M4µúž–– ¤€{P…>nÑæŸÆX%Øöü8nq»¾tœ{_¾¶Û+òc ¶& /~ëzY2�HëdB¤ó@z?ÈJ¦Z”ŠŸr$ûꎒéªÚwÖ¸k2{bCÂ-Ö,N¢‹[ã‹¿Ml¶w. +"T›¦xh¶dÞ:„¯ZÌ�:VML×·o‰r1Vf0-ÉŸ$(öû…SÁjþÞGB<\�BLlSì…Ãë¡…[4î½ÇЖy§b-`dµƒÕ Q¶æS|¢Å¿k«£"t˜ +c•�Ëé½Ü¼Z˜.}^Л/ìæ‚=iÖaõ?êøØ;¶`JÙÍkzÒfÄÒá<”$u'r�ÔstH¶¨8±‹m|&Ã×6ÿoó0 ù {;7«!6O$xsö¿GÔ0à NUUØhJÊŠ=·ç…¼X翌ó0f×5oU•�úò¯ÚaZßãÅýÍ7­P™¬b»NÝæ�^ÝÖî½3µÖº¨~ßê(4ù5ãA}ÝCŵœfe‡êMöÙ7£qÉð›³ôtÿ‹ä»Ñ(¨Ý­s,“ ¤Péaa=yÀpè…5"1‡éâ¹HiôûNÁ ½Jh4¶œLÛ«¤õ5\(‘+ðÆ 7h@’pš‡09@…ÛE;ëL(ຆpÒP¡Ö~‹@fi|bÜÖ$XÐU°Ë(Ū5À ·k¤@‘øñ]8Iö1Di¨ØŠ.‹îR[$Õ™„ä�ËSe©Ó$„ñÿê,!.  W³‘è’ äúTÈ}W¥ MÒ‡ìL©�"÷3Ö£‡3ƒ'Ä]çbŠÏÖ×éæMadeÒ±ÑÖŠ™`q7é0ãt‚H0"}�B ‘?‡ùù Å›¦Tº`Ñ9;°�8´‡ïÕŽ�ܨ{;Œ‚àûÁièê• ià’aNQ~ßI;8ƒù:ÁÏY�ˆ µ±$5=pI<Í6\ÉY"d(HTppE�„SLÇjV+†qE<¥É‰ +²‰CI‡ið2!ËÞÚíø,Wè?'[Í[¢5®º|˜n cñ&yïwdŒh7A¬:4 ®Nµ-ÛÖ[2e}…Å5b9sÀ¸o ívÈ(;Œjí¬¹bsýã +¤n¥TˆTËYþâ²âo_¡þ…ÄTDý�{bÐö0¢¯©rÀêSæáŽÆ«^¯ ‹d È^¨÷JQós£1éÀ蘦Ä:Ñç>¤]=�ñSJÛËÿÑÑjB"‘oÄšw;€ @£6œá{€ Gãhî"À¸‹kK@šWí&™äjÀÔ'‚dRHpz·÷Ž!Ôª9xÏmZºéæMo—UÝj!;|м^ÀMâYpÇUøô.lÖCdˆhJq±—25S+c´m‰G›x¶›â%í 8Žh­)Å,Ž@€0²0ÂL ?ý J²sËA"ù9`>Û‘Š™L‹„öõß]{Íˬ5uÂÞAªý»ëô¯C5Ù[ŽŠìÌŽ~Î�-°ÃV‚eš‰© +¡�Xª­ Κ“¬�ØAƒñšÊðŸ/.$Ñ_}ègà²vˆÕøRm8MIà˜Áæ~ƒ�êð(¢Tªãh À7XËFA¬& ºRMµ±5µÏe˜·¦:âŠD—­ 1YœM‡—8ÚÖ\žüöâc& ¹Ú�œ4[©¾yH 3ΠΈɥ ë'ïëvÃõCs²@óµ&¥@€)ÕJ § €ÝÍÍeÇìÖ&&ïR{„pÚ¯{ +I’vƒNyI;Òs€×,ë.Ws‰À> +¹§ÖUÙÈíÃÜí,ù/³3 FFÎ�Œ»£€‹�â\ „\|~Û׫Çy½ZW¼–³z8�\àGh£5emŽB}š�›Ê@ÚèŽSŸÆ¥RGôlÜFýuTüjÇ’Rõ9£àzàceH <ºiܶë½/ìŽ?Xk¿À¶âEÕ¬.n÷yßöå™pÖ…53¿‹Ùøò Jé�†«¦Lr¶ã…9ÒàQèG’à(¤Ë¥+@ò!^é"$lmq�à²Üdñ».WÜ›&DÝŽKƒÎ)¦ë‘÷yïaÿº·ª¦rq!ŸŽWh áC8îëX 䯕æ?kX[ò³ôL£VׂµrB³I%É4“.ÝÒ‘ë„qùgr +{ qÛ@~�õ¢> +zQ;qч·þ™ÇW¦eÄb?žmñgWû^™– +�ÁGS]ñ<¡3Ä�s«µ¥!šûQ†_"_%Í.O†B‚…bAÊ"Ñ-Ý—0½f,s>šè,¶¡ì«Ç6¹!ã¬ÏKu¹ƒYrè8Û`®H éq0!â™ÐÎãYÆð3ôcø‘^âQÆ "ã,bdŸJ¸‡QF·KÑK墶†à ˜OTß�«-ó¼3ÒµúdJô>'!K�âÞ ÝÄ`1Üp8m î¸×Š¿É‰´k‰KbAÔmÔŠ@0£Û:p˜e?ŒéýºLÂAÕó8oŠ=^|c~0_á½'¾Îø­",Äo–½Ùh× mÏ}%Á¥üvØš†Á[a¤J€øIFˆÎ£­þ¤= øýþ0맬IB¸èeÇtÀ$-5Ø¡x ªêÇ‚©±Ú3å`ôSûÚglJkebêÅ÷^jË*‡ˆßîÅ$#Å0 Ë& nV›4¡xAˆS%Þ§¸NæÆ?Ü8Iƒ];Љÿ}Çu88Ôã³ãT®i•ª35…”}aƒãg ÉL°Ýµm€ãÇ[ƒé&òôÏA“T ŠŠ)=—÷à`;Ýl3ó Bª”翟j€1Ò»·&»èÊùm“"*‡²Èê÷Ú–˜§f9ךó^ˆ#Î{S¶òTÜAß½7¬›}ª5‡½Àø½ûjbŽºà Ø }2¯^ÛŸlñ7¿Ð™mJ��‰v˘à–˜“¶7â?–‚ìø¦–€¹Ò uÝX:Û®øýX•Çh]ã�=üTjm[Mv•Ñ evPåÍ5!]>á¿¶Ç'aãÇf¢õ°6›¨ ½ÉËŠÑãŒçç`�ºk Âki¶ß±µj"܈ÓsNL÷7[Æ 6CðÄâgêEs(¤­—d4)eƒ<È'üÙ.Gœq|»·†öÝ Ô¶6C;T›�%yo“t<‡Ú�?ëD_²Vg²NÄøáÐ]ÄðoÞ24©µíLl2Ž‚²ÓZøuK²_~xúÓÃ?>M¥•žž~øñŸúßÿðæ÷??=ýüç|óÝ_Þ?ý¿ýéÍÛŸ~úéÛwÓ¿ÿþ‡úÓãOúù‘�÷”–|.wPM¶É¹£ä}qªþëÐÿÁÝ&ä +endstream +endobj +2208 0 obj +<> +stream +xœÅ}k“·•Øwÿ +R¶KYñ~0´6’(ZöÚ.¥ÄZ§bykEE³qʾÞpÇUοOáÎínༀÛ}©|™!§ñ888Îû<ÿæ?¾;½|ùüw_üúÕýé§Ÿ¿úâ'ÿç‰y¢Ÿè'ÏÌoU0%fÿ$e¯rùÉ÷}þ½~òý>ÑOþóûÓO>óD?yóîù½~’U~òæ~ë\½ùë?úVkýí»óOmõ�Í�Ñ6nÿÖéüûáÎE•³ �¾¿|´çß§Çÿ9séî/ÿ§º>þMëÐŒsÍ,ß.¿-9õöùqˆmHwþýöò¿(Àÿ”Ø< mí¦¿½®t™‰m§Ëð÷’бƒ~vžDÕ]‡Ôu4=‡µÍVën}�À=ÜýéÍo~ò囟ü·Ÿ|ù»/ž·”h$JLNåh¢ÛIŠ/:l]VhÜ +ìÛ†^-1y‹NfBó$+Ö¬GÚüô²+g¼Ÿüv%¯å!tBcœJÚÅÃ8|Y<îåÃÝ3ë²J%›¸��íŽHÄÁß7 ªÅB‚ë6vô…Ú#ºçG„#w¿9€–S§ïžÁ«m;ŠhNp#,PRÐj‚îFB×"K^:dѪS; äó~�—;[ûæÊ_öbô™Ë*x <íbqôœìò”(Û6VÀS ÎQQ)E—'ð$õ3~^m?x("†Âe³Íöðn}«�fpw!uHÅ+­®›D=æ`°þY;‘×Àv#Q$O(ÑÐ�‹gyÉ×»þ9‚ùž%¡îÕpm§þ9D}ïÁìËÝÁît’Îe0*'ç¯ÞèǧïîÍÿ¦çÌxÎ Š·9Çñœ£×ÌõjØ• ÍÆå-7 “…E±�GdÁGFå\În?"ñ‰±N¥’•ž­®Å‰ëIó]ˆ¾@7šÀL�Zl?øR„ p�ü:WÀ Û¢?ª'ü¼ÌÑ7¸zXˆöÝ/<*ÊCÞ(m�ÙM?š¿8×-t+kdbQɺl!�Ñ—aC{°'¼ é‹õš¾ÿšíƒ¬" ¹wô{³\⇇9ˆeItÝ]Í+ÁË®~9— pø�ê{¼½T}ó/‰kqL�¢Œä´2ÉÆ´›<ÁOèé¼ÀW”‹1–Ò³þLóV&<!¦¡~øûvó.("žwâ²ÙpO�ˆîÎõ®Ë{üûsp³DžÍ<�oÉÁ–ÛÊŠŒ§g€€�„mvIëÈðÎ�3,põR¢P+Q¨)ÊYkwS(ÇV=‹A%ç\ÏsBÁ°o©}[lìÞK¦×&MRsã~ðŽ»l5¤Xñ‚Cò�=5?â(à´ 8uS­ä TEĘ‹h»9Þ ¶m[ûö‘-ùÒo²øv2†¼·qÍ꬜ÍÞü?”Š +%d»ªé:ToŠØªA¶M@#†vNË4cÏVßî×CxD% +îù›áö`�…‰^é’½oÏHN±‹Àm@U¦Ãa6l¼ù—µ<Ë¡€×5GdôÃÓKi¨÷‹Ž\ûHÓ:KmíQ8—±$åSÚÏì¬(h^Ì—ˆ›ÕAid +ñÍ×·üõÑdî›h�î4`™F#^±Íh‘Ô6ùæñŒy߃Tв`ͼƒÛ+ʯ¤Ñ‰9ªhÓ^M<$Ÿu-U>-±¿ë˜ƒÞ Z=Žr¢6A³J¿à©qpIHˆ7m1GÓ¨à|‘]‹6T#^i<`–@K.|?�¦�ƒ•£Æh–È:�³l–:tIàêžÅ¬¬Ëz¬)† ¡­w’V-P¾eº¶OéË �ý*�Å�Åçd»mù�ÇŠUk¬ŠÖf7±ñ2ö)@yË¿í¯!Á8‹UwÖG•B1öiž|Å*Ñ-VÉØ`”™œu/¿§{=å*°nS¨ +“oqJ5w¦ �ÉC(É\1z•²Íû!d­«õFŒ1vú(øWñ$÷M…#¸°A;àœ2ÖáÂþ�ô¸Þˆ†�JËß2)lÝ™öYWØ ¾)à;¼‚�8»·òõZÿw€g¶üNi#ÿÑŠyê—Ìð18Uœõû¹ÃÉg¾UúâœO»_a0Àª�¡èknÇÁˆi¨o}ê·¬½ÍeÐb±Ñ[@ö.‹¾§¶k�c> ìw| ¨ã 9H„ì­*.»ý +=øH:�Iym·ímÏ?plØ@Ð ²�‘løë[Îy”³„_H4*foÜÖ§ÑVsû ,ìšl”):¤ãd0» g´¸ ¸¸ÿô¶-› +ÈGdýFËiš™á’DiËY¥s*€à�/Õ;` 24�ÉWªFÅPlÉk +âÃñZ[±=QAÏîÕ]š‹±}ýا/_¯p ×x Ð}¡2?2O„hEÀæÀö§!ý‰~­QÖq`ëÙ?ðF¾¯äT41n{ß¶–œÅ § 9à/î²#ˆdµ‚Ù·×[à»8ý-p�Gƒó 1v݆#ƒ§“¾VW} +}1àQ;ž‘[É¢ºæìƒi†ÞçNrü‰F+ë÷;Ž­ +L㼊º0l-ܪU´â¡ÆÚ‡”Õ>›ý@Ÿaû¬ÆÃaåB¶Jc'æœ7Ç­wà3ë�r1‡Õ+a¡»‹šH@ío³51Dз<†‹¥IÊ•dl8LL„)’UëÀÃ_a¨ã9Âà‹úã“úã§õLJÌŒuAÒë�†šuð�šÇ/kÇ•O_’HJQÖ—ý¢yì3áÉçœ*);'jnÚ íÛOY³x7ŽØâ;cöl­|äLÓ›FHDÙªèu +ãM“/¥ß¶‹þ—ú#¬öÑÏÖÿf8,Y[)�S‡º.ü�uètwýÆNy–ò˜‘ä›�³r%—ýìáÀ‚žµJU'z' V8j¬éEq%Ûa†C±'Ô’·j [ŽØE0ÂÓáÀý²Þ‘Ó@=52ê#T\eÛÂ;·µû”˜FëåiT’aBJ*ø|@Ã%kâSQ¹êéo<ˆ§½€’—˜P  §£g£¼7i©ë3·+jY ˜³z€�ËîF Ø¦(ïÎ\(z¼ˆÀ‚›ø?I’ 1ªXö{«�ÆîÏ@B†~ö¥ºÛðEÿªÛ˜(ž�é†3+WûD„fU²´š„w㘄MsWAÃÃzÜË{¡tÌàÝæ¬´xøñ‹FjlVà‰RŠ\!¨ìÓî<w¢M ±Hø´Ÿëö™gTx�ÿY?(÷÷B*O‘×¾½ïùi=_K!þ–ÙŒ§N6íuC«bm F¿Ê�wíÿàCß� ‚PˆÑ�7õR$Cð^•rÀ�ïfÏ€õÊh—dùpÅw×ú§ÃPk08¡"ì�î f…]Ô çÀwh™ö²€ ¼:|l»ú�oÏ,� U„)+à¬<ùJ6Áyeüßi™ÊœQ©$ëÍm‰}»“Á´²Bqü9D#öëÙòWôvSÎ+Æ%E‘ø†Á¥Öjã]‘À;>ǘS‚`�^±ÎÔæ¨Œñ.óôº ñÙ÷ÿî/o~øÇÃË×_¾~­õg¯>=�7¥É½;kr÷™¨´NÙ€0ÖœpúB[8ÍF?…î88`Ó¥9’Ž´L¢¨�~ŽAF'øÚ^Nä5ΗÿÑÚRB´Ì[->ÿíw§ÿè‡Ó�´¯:ª�%',+nìûjîž9Ë0p+÷,¤3ðXw˜¼ +Îë2ãXwX‚2Î%6+M«ä�J�Ä Ây¯·F ;‚XñÑkÐÉŒÛ0/dbu£IÕEÞºllW‹:!Þ=«£–’õ#Sû ÷á ÷¡ñò%¾­i0¨~–ÿ¶>(èÛç܇$Œf¸oŸ­ø��”j¾rvvçNü©,f¥s¼p[ß ù~°Ò$dŒÑvži‡i~~,W'hJ<Œ�õÎGñ܇NDE߬‚Å&£­*6¦p“õ°[U’v¢’‡Ž0ÛGK¢;ŒçåÎVµÅ\m„G)cŸU)Öç[ÒÀä1W3-‘± ªðùµѲgN®˜Ðò¢îZSº|Óã6÷ãQZ_®Mw_“m~Ï5ø¥€ÊH2Å¥òÄ?Ê­$1úìU(~wpËWÝ1y ðT‹OçÊ'^xˆþ«µÂZˆÇ_¥]Ñvf17À&•›*)[t,‡±¹ÐßÜ©¨äÍ;«ûßR›ž• Ú|3jðDHm§I—a—cN‡wyŠ—¢¼‰‰ °Ç¹,ÔSÂ+Tòbãz7˜À¯F"¶¬¾ +¶è| ‚1ìþ�¸/¿æ><3}QÒ¨úäTÔÚìf¿é!Þ=óYEï.�Ü@˜Õžë‚›€çJ´Qt¹±N¬É×N QÁk袼 >؆åXÖ-.RI5½rÆêtߊ™ÐÚN »?¥ŽÕLÜÍŒ… �L¡Ôg‚�Œ˜ +ZóA÷-E÷ªíM.xBÑÀ?|£”ÔÃG«¢Žz/OóO{ÎÖ½€ì’Цä W6ô ‰˜7ÊAåìJšX‡xŠ„#„™˜¢UŠ:”È„8GÌ·T�KÔ!]évÒDðZ+í’}+MøÁ5Ú+ï’)áÀZ'äC�sM„Hm³ÊVÛ Ž3®‰�š]M&�îÿ?ñ[A¼R51¶·Ñü8‹&^+ïTÊÙ^öK~Ù„#keg¢¯Š ÷F÷Dü^pJGè2› û—ÂPÚÚ¤¼ÏÚý8@ÈkÙÖÀÙtøŠÊge¦£¤\ÒUè?¸lÈ•öoÞyG~Yüt `úE«§v(* ïoÖþg…òk$Áö_ ßYòò5ô̘²[@‡Y«BP)‡ Sb¶1ZÛ÷óÇ�©/£�‰rEŤ� ëéç+ž~ðe]%E“Ÿ™uç ’ñu�³*þ¬õݽԉ ä+ÿ”¼á”³%''L@Ð\ߨE�ÛÇ÷N¦Òþ'lu‘;H7g¸ØM$žo•צ^=ï�tˆ{ÞG•µqåørµ°\â~¯Ó¥÷AWÊû°?mç`æ¦÷ýÈÀHÞÀ�º‚®F]Ó0¤˜X'åX˜I‘<¢½×*š”$ナ¿Õj +–¾üÝÎÞ…ïL*„ÓÜǬU‚"¯P6>£Š×ÉÇgM˜ B.ÆÛkéu„þI°JB3Óig¶Ž�™RÍ ò‘½{„û#öN+„Ø”¢ÒÎú™iw^á…2ùiåÝÙ|ù^ õ#a ë™ö*›npì!1# p‘—§¼§Qp­ÌÕCÃpÎÛ<þDëŸ-*Úª�¹ÙµI¤ÒÙb®a  +æ(R¬ÆôhÂàŽÂ%þ`Ž"•c±US³cƒÓg4~ßrT¶No°^žƒ2?PÅ(ãÃYIr”DîrÁøÕ³ x)2a3Ó—´Ñ”jM«œr9€¸©Ö +î F¯‡ÊFSÕ@ï��(7”¢âä´ðñTB•~Ý+íƒ6·'2`ѱ`—¿C= + S1êžN­f nhAÛ`4ñÜùZJÐù4 3sl¬·‰¶re±ƒŸ>d»¬èðÓÏ׿ +&“U´%î\¨ÚàªÊcq üôß%PñS�*Åéú�@=®Ã4?I1+_B¨š™c¸Ýp‚_³! +m[/Þèãåá®å¨*ü›˜Q“ÓpŒ&R‰iòv8†(µSymRÚÔDïûw‰vN²Aéc–và¥Û´ +Ö™#Û{è�ª©$åJIù(1 ‘ªÎ„¢R))]íÇü´D±Z 3¦èÊa¢ èä«ãÊ8÷Ý$Í)‘œÊ«L +þðð Òwöï¤]%¾kéãÀÙ§2Ã^̯%$P5£j²ÏXâÍ�¶†^¾ƒ4,Ëšd’åå;xh¥—OÌ/ârPÑI45ò¿¶_ö±:@ëêw>lä—åŒà[;Vé-Æ]žDrˆT…·ÝÁÅJ“â;%Uœ›Áð d$"íAuµ®”ƒ‹þ=¿h"×A.Êk¯ùœ¡8©âÓª {Q ”œšM‡È�M$Ä-o–ïm )¶€d�xœ;þu™Õ§¨Žxjbª~É$;á~'r?ØdUòÑ¿g©-ç²; ˆ… äU*q>l�Z}V!Ú²öÅSp;œžúN�m¶~4´ƒ³<þ‡³IáþÕÆãR)‚¶ +1 ’Ûñ�b¤¿%@–lva^>�ðD¤Z¬ýñg@¿jØožËrj4¥ñ±<:|Àl8ªÕõ’êßÀ†³i«@ÇëÒVñݹÜ5‚˜¬D¥]nªoõCœFH»hæÒ½Ñ#Ý_ÕJ±Ä®°•¸; üƒõ"Ø"žÌµ3=«‚b1ÁÄñ% [vZ˜Ÿ_臼žQ &©Söážç)7<¨îÙÔqGyùð›WƒŸªÇ0‰pè6» ”9_ EÉ'Éy«Œ¶¢ÅZp¶MŽÏdtZ–hµòɇ´Æ~ô ’Û¨~Ê�À<¨pqS´š´h•±¹ê–†h©ŒÀ.Ê´˜”Öáøv2X„YŒÇ #•E§¥MIñèdôH»ä]|ª�\�phÿÊ&�I©Û½‰§àînDÍ7ç¦ë€1uŽsFÙ`â�Û….CrˆlŒYR5™ÛÎlJ[âP8ŽcÂ{¶rŒ�M’ºwî®"Ë€q¼Ð “)œ€Ú #�· `—Ë­u­_€Ýê^s'Ž?Sc`F@$`jȺ:]+æíŽ0qWf³Wź´Uœ�äz�½iZ;bƒÓt⊺:ôŒ§*]›VžoŸ†er:6§µXã*½5·2ìÁ� Ôå ܾ“�˜ZèÛµ&Щ¨—©É³tÚ]RgÛUâÚÚ’,[_ó½mɤ±df™¦[Ý"ŠŸ�­U[û¡îG%xº ^ Ki?R5MÇ‚|�sJG¼Ý@@ì¾`âDÔ¹hi´bba�Áâ~$|ìRR1•ºº?ÎD"‰ùºlÉÊ·;5Ls(C/Ò¿zï˜Ä¯Û&€�Ųb”Í!Oe†?S’rF/—½žƒ.‚{A^ Õº2\Ï~ð®…7Áü0,³ÓÑ‚hfŠaK³«·Lµ˜ÉÃy]ì¨!R¬5_ Â&çÝ¥ª®Boójº Bp%¯:(XÀâPô¾Ã�^�šœ¾vèŠbpó 8± +-JO–íÇ*ÔY<žÈfŸ¬?C~]ülýñrž¯À:[ ô媶Óš(¾ÈËîDn=›Lµ¤�8F2ä ç«"#_sST¾¦x1g•):š©È/¢›/XÁØÜ�xe¦{Ce t¶»HeŒ¹Q°ˆ "³FUb9¾c¢{o0­mec°Q�XÀa‰1iUr²~�õôE;ø}Ê„8¤)ïB”ö…‹Aù`u[r–av[C¥¿,Ne£ušØæ!ß×`ﯶ`,›•óÖtFøÇÜþ¯A¢ßþÿçí”´(*.(*£¿í²$ןôÈ´¢Ya5’ÉɆš·6†ë¯xÄ©vÑpO�j˜-Wý.õv¿œÊù©CµÉU'�ñš†Ï‡�*S‚è Á2d@�Û_¿“´ã¶�¥Äã;¹¾0í*ì�°›D*Skk‚}Üí7óé‘-F…ÏàÃÊ©·„Õ‹¬«w*ähð®43MHø)+íc\Ež^Í‹œè?_—DO KAŠxg»7®VÝÙìùÌ„AôLÀi49Ëf‰8zÄôsZK¹%C°AQcãÿ \´µ@VÉΦDêÚ†v�U¡Ø +ïåð­Jr¡)ÑJ,“싽×Ý2µB ]œçÿ@mYë‘*d¯²dÁ ¯R +ºŒ±0¼:M�ÿKÁ5ÓˆÀC&t¢µ[u£°ìlH»öм%Òø0³r +¨¾ù‹� o4)æ‘®–(MÒ�¹Iʹ�Í�W‘, P¿ìcwÐßÒîüÈÍÄìÝ"�¨¥*¨x§\6Ï o�7ײ, ,)¶Ä訜7µ€èACßæ5Ö³�BiK”‰Q]=¤' ›‹Ùb»j(�ËÉ‚Ü0ÐYz"-öî¡ÉuHŒ±ÕWÇp×’jÐ×€%ÉIÃ4ìßÙV�Ñwÿ·¶x9§Ê�UѸ¿$Ü¨ÚŒŽ ù¯@CÜAØ'I©^‚r>…²w—¾ÂF˜Ž„^Õµ&UO7»IðB Óò”%&ðµoÝÑK'Ï}Elö̼½YFÂ^ÿ¥§;¡;°Dië­JÙ‡8±‡†f孵ɤ>OKÿç�Y¤î¤…IbEöÊ—dö§'ƒ~"«’ladbP%Ó’Âg™l9ŸXwd²‹õ"Ùù:·Ô€!„¾k(em?=­¤&""ZÇ– a |gUTp."¢OÔ;²7¼"^öœÚHo‰¸íŒãJLªhív;®hmî` 7vAZxQ‰y"J*Õú².Ù<†r|Ç8[Ýs³�É®¬‰•x‚5ãù‡#×´�ÝTçð�H~JøQÁ€"5Ȭ#Ûd�qJgH¬b‹3>¢öÕFÅ!)íKù±ÂXÏvÐZ‘Íã(ç6ìè$ó‚(ë‘Öô *ûßp�‘~Kì<‹0qžêó†cq¡Y„Y?å�‹iál?,—Úºçÿ\œ—»¤�r¢”Ògí#âìMŒ¨tšLöÚ�NK@]>QжÝä]:jrr”� {Å‚öºBùâS¯$jÌ5†ê­ãÃ~—±w;½†óQt*Y—2òiéŸÑ´‹Xáè\²Ò®Å; gsÃ�“Êe%á‘à¯Kê?gÀíÁKlˆóë�‰J© %úêG£w?Õ#.ðúYÖÊØ´„�¼ƒÌ°�1šÍ¬ß·>pXà{E»³ãá˜äœàéÞÐ�cÊЦ^áj‰z´>E•Œqq¼¹CëÍ¢µ“ôåDeZŸ£ò±Þo‡é‹CjÇõ��\F÷×ðqDæ>¸õ€~%D`7€Œò.Yi/–A>ûþáïßýåÍÿxxùúËׯµþìÕ§ç§B×îÎ>M¿û‚ƒÍI—€ÓÊï¯T˺碬^-À=‰±æ¶}ÿU³ÕÞûýÁ¹˜¹[£�Z3±Ì:Ò¦!�ÜÆHPXÜ.ªQ5µ‹bž¶ÿ 2º 9‡ÁI-ý¦�a3²$QG¹¡MSTÔê(#A¿—Ë]…ª!"]PZ—°ê{`ö7hMói{·#ÐM²8‚8Š»ª—âúQ‘"Èiõ1éu·I·pñPuÓa‹AÙG½B¸ñÊœøʆsUŠÚ6�U +å@Ž¥9Œùs±·²VD|wsÚ#æÄÊU{] #Ô„§Â‘÷eP¼»®xœuuYfïÆ’È=Ñ(­X Yó/Kèr«÷z¬é’KYKŠ»XªÕj¿M‚.ЬÛûŠé7�¯uÓrV¾„‹šó >÷Pça鞊VZ­ Ù é!Ü0»eæ¶#ÍÛ ô¸)Ý‘^„`8¤>ì©uèi<ô‘h€1šÍt+�¥¤¼‹9)ò�,ôÔqƒIÈ—äÇS².ñÂþôP'ìîóv¤^…}¥  ·%ö>Ãôq{ìŽdFAPïRQöB0îiç~ Ñ­àD·þáNÅ1¥Jн˜b� vîGr‚…nŒÅ«¸å‘§ÃuºVBMfk ÅQ´*Å’':—ú|Y%g®Zöœ‡Òa H­\tÔéû±Rk‡Zàï ù*ËAÛOšû~k;žì%x›¦©³þLÄÕºyNgÕŽ ó »i¤Óƒ²f¿O ŸÉ´K–©Ž)úC–_MØÜð$ý5ŠWØYÞà@~-ªßXxͣ棬 ,€‰ŽÔÆ$ê\Þþ[¼·"ð ã¬D×Á+kjï{È¢m½ŠµT[wóP&ì‹& +~JDƒrédi…‡8RðA‘LÔ225§TðaÉò‹õi“ævy–ò¶0.—“Ýf têŽpKNÅèS¼¹4Aï`Vá¸÷Ÿ˜Ð÷½’àQEM„€I7kXM<�Á‰IWZT1Æh�^üyÎ��óžxDpÒJxŠXøÃ;=½n1¸a/‚—jb\Z7KÌÆs8Å ö/Rð‚î-íŠ +˜š¿Ð”#öX>ÛÑãZFp«fƒ²½W2m‰CñQý¡�­T ”Ë.Ï=øM>È9€Æ=paw‰wao¶ÙUªßNö—”óúÏ(µÑÏ ¡;!Ù†š�¤�çÌØp¡˜¯×��ß™õÈSlá=7¼@.Ý £¯A8+1u÷ªžû|9gŸ³¦Où€GÍSºùZÉÖIN!Q¥“Én!OÅÙc�uÌ…²«�ûwôg-»lÐâç+&?{ðy»æè< ¬¯{ø Ô¾/ç%À/>) äåÕ—Ã:7Ök�˜BñJ§”ö[pi‹xójµ%8 oÍ–cÚ«T’1�œ®èÕÏúæ÷c<%ÿÊÁKoÐ==Ɖša{‡àh1)Å“Qòü;²±¤Fô„P9ç…Èv‚:æ}”z¨/eÛˆºJ0®<.«ƒó‡)úåß×·—ÜyBvJgw— Úe±� +É#°QÒüÞ)A¸œúOô<¬5„J™Ý.0Ð�êO`¤Ý·1„z¦�ùÇå¬'45$¨Ä4AÃB4½%o“¤ l�¥ô ½òÚÙÃ4Š©ÜŒ›-ˆ(=IÑÛ€hÐm)¬[’ðB²Jç²?8æ^ŠÚëV`½ÊÙè俢»7û츯¦E,ª%`“(±¤ƒ²¹˜ dŠè9!œ'’é­L éø.è�a @’F|0Ú% ¯”Šf:” w41ŽÇ’H¨¹Xl4áÀ‹UμW%{½†K·XÀÌmpNyïóq2˜1Có"ÚÆxAŸLèÁ ù\óè•8OÿŠ?~Adq¢QºÔÓû–ýñ̲ ·T“T(!Íp‹�'ÔÜÖ)ç¢9€�óü¿O<_6+klºå²Ÿ�â�ãÌ@#îRÂ\k}˜*ËÑγi5{Ç&h:ÚŽY ¸ÝÄKç“ÒÆt¿ëý.l9ñÔ£r®Én°ç4š×*Th;I�yú‚Áh±›Ô!2ûÞi¦���x>…³ ’Ú-­Œ6{]j>%­‘½SÉåt FB—ã�*)ç1€7¸ó"á⓲:Ô×îúUGv6ûº�„¿Ê1¸‰­§·¬ÑÐÙ\ +01idÄK™‹Ê©ºLÞe_B ‹ !‡ê¢\¶·ÇÒSwsÕ`ç‰b›>�Ù´µl lÀ§…#µª—wbcàc<[�ÜFB<÷I¹²Ÿfþ©‰˜»Á*gŽ÷-îÉÑ8¸jOÙ_UÅ�!kR­y Ä@×ꋞW›SdE¶Í¶®QÛò)7__ µ*D+%F`œ )ºàp ¿°¦Q„ÒÜÜrIWp +ôç�®`K7HK,ЫänlV^ëÞ.ñÊE&+“BZss!6`�W@C!€¤a#ûN¿à®TÁT®Œ=j<•¼‡BE§3g1†l‰hÙE†®‰¡ï—áLP–ê½”w±ÈöV¡:èýe) .š ºn„' ŠÒ�üA’žñ Z«ì¢v;ýAžõÚùõºYv &«‘Ô‰bÀƒÊÙ;èLE/­¬¿`ø„s íZMºÕántàϘ¥NDFU‰Ö† $¼sJ"¼[]@rºý¾°.R]¾®“n�ôßÂû;ãù;IDÃQ°è-žÂ¦H±ë>Urˆ¢�d¨žÝ|Ç…Dz«ŒsØ�úD=­z£o.ÇË¢O™a¦3Üõ_ì ˜± à¢écîÈ0F!üŒ-ÅsôUz;ÄP†g2§çÄK›$ÏhŸŠ2.¨´Æòq)©l¬�Æ,”[«ËG÷or ‘©DÏ·×›�>²!ê°�œê +Ÿп§ñg÷ Ütæ®w €Ü ÛÍ>د¿Ãarä Ðþ‹6„wL–Z2¦û˜•ñÆì·¿Íð0%€Ñ5—` +”ÓŸ¥R ÷®¼k#(Ѝ±.¤r%Ò[-†\(x±­o7á–Gî=dÀÆòÎ ô�õ9A;es¶~‚>&‹/-üÂË&@„%ö`­Jŧã ùô*ê™:o’¨æCRƧýlæ5…²ç½©øÕdšŒJ1—<±š¡sAK½ èÉ޹œ;èŽé· „�UˆÜ˜ãq�ø� 7L_”ÓÆß×ZXϨèÝêÌc2®««FCIÁd¢GË=¡Ä=ãæL"CáRȧ÷Q™`ËqVl³5ÔP÷PÒ¸´hH�EZÇ$dE3Ïrò›V1Ê•h'p7+÷[¿ÖÁ�aß2A¥Tl:¾«— ³Ð5RWºëg¢s0J»bn‡Üoº+k�}•À"J‘¥P³�›ü0 Ý­�8Ì„£Ä>oýϯb²Š¦ÄàAÞçÐè +39I˜%“ƒwA™ ¤ˆš|3‡O� Á£¸¤Œ¶&�œÏø¡•-Æé¸';ìŒÄ#ЀV.£f[’�¹‰f/_´à®ÒÔå\Â5-«WYǸ¤e¥ÿL[ EªÃÇ9““^S&ô ¤W8ç–ÎÍyY öwÝâ3ŽxÇA9m³°Cä÷ƒûkì�¥Kv)KMK{k·†&pÉVÇðBM’_f=Õ@?A¢l«TÍÍF´œõNÙ?™.Œh<3íí ¸ªÊ-fÜB åGœ¥k§%µ� Y¥äÔ™è‚â:0Ww+­Ò&¥(%ô­$ç÷®åkB«Ÿ®ÿ2«Ûö�•iSÚ# ™¦¨”´‹š/"%ÿ?í¦diušpõEé#û9ˆ–všp Ny§µ}ÏHy˜A •�ĪXÓ“§r–ˆ‘v‘1ëá‚Z‚úöE¼ ö»\��–<øj(d±>Ü ðŒô<µ¡6.)­Ó’ –»úVÒí0jÉ¥E=%÷žœÌ{€s²±ÀIÞÉþGBÕ=ïs¥4?€b}¨�qZò«¶7�ü�¸½.5RÒ±Þ–ÏJQ6唇¾oŠ^p5ÏúNÀŸ~åkÁa�26è�ŒÈó…oB.€]GEߘ%²ãñ„Ü7Î¤ÈØv¦¯¨WTèØ¨t®Ïäÿßü„ÏJPVÛU+vÕÝÕîJ7 s �¹xE>h(ì1h({Nsêø î�JUQ”7!Ú‰½”�®¨^gjøŠpñ»xTHÀR¹�œr:˜pœðXš™IcAÄ·Îî5¿^# +3&(k�TrEñ„¬|´–.’Ò,¶…ôíá•L}Es œ »ÖOwLÛüÌ PÿÆ|_é|G«ð{ÂÙxꕆó{¡*/hë>¬»Q@ËÏW‘³jl–>f «‰Y²‡;í•uá#ÎKÖ¨¨SŒ±£PÂ`îêÝâê•8t˜`ÕÌ8lÙx¶Žêrx´Žþ¡Û×ÕíkiU”�Ü«bŠKÇÑ�Ðü%®�‚Ÿ@2B´˜Ýeæ¡ÐCHÔ±(­cq?Ú¦ÃÊ'’ãƒ3T:Ь´)ù=ì§�‘Ø"‰ñ*Dü pG¦‡£³qß$è /ÒêÈïìñs±¤&f—,N¶8år8 "Û¯g#ÒŒôñŽnÎy[¢Q§Â’é±3æUf Øyœ�ˆ˜ø­ÂŒ¢š]ýT64rõXì×4PÇ\mœ‘ìH6[œO7Øæ“ô¶oKõ�1™>Šºÿ3¡ÊI¤ +ÙÝÁ8'v|~ÏQëR²ÄÖ¼ÒÏÑ+`º*/ȉásôE³_ŠëÆeà,¼h®åtͳ’ÌdkR>T¿ru"” õi½u…Žü´\³ÇÏg±Y³Cqî==å6w3Ÿ¨÷9qc è»kýÛUPø¤7`uÄÍ-—Óc¥«A¾îN¡¶ Ä� +ä)IA6Ö´#î@¼þû¤!+}º¨²u›O”Eá÷>"¡÷ÚV z¡‡�•¹rp&d�nµòA° àÞ}*¯]`£v½]ëžîE—&G‘P×.´‡*<úÖ ®Î(`œÐY)^ÓÖbpÙ…”ÎÛy|Í¡�!šy¹pGNå¼1ª ­¸fsä0p¦SÐòðKÇÈt�M§}ë­|Öò�ßrèÁÀº'®íG×inÍ�`Q²d;¼Ø­„µRâtëj +gŽ»¯RÅ»87ΞRÎé–”É)åGÃôßqæ϶ô|ž¼6uUß :~ B5§�‡ªkÙ=Ú›òdXz‹Ÿ‘6ì2vÓ ê†{)ïb>à`7‰‘pVŠ‘°6ךu3I²~$Üç�KÁÈeÒe¥½É‰¿†ÑÓÚ÷À�(‘ɬÄA0 ”ÍäÖõÓW—·‡`ÎùÈ6^×èg�Õœl½Ò7 s�.M$2ZG™cô4v ¥ªbX“â¢�•¼4¬©9>|Ù ýˆè�N„—tù6 ÛCÀ†–”ÔdÁ)›Ó½˜½¨i''ÙcCÈѾ�"±Q†œÊ>›|t»ÆÄ&†udÞee�±t4_¶»—&ézBînoçHAir£,9K¯¾ãÖ‚R€÷n‚�s¢^HGe”8¿çy¸Î¶y©ÎúÏŒV1XWFUœàqZ޹�wâí:çëxÊ>J~¿[¿Ç[¬ížmÇ +{â#[ +àC­�[«îû�D³’²È” œÖûÑ`BÈ£$Jc¿é*¯Ê;7åãߦõ*Ç ò/iUçáu´½KóÞQŽp=wE…ìKœØùMÑà¬@7Ms4±‹s„{®•ÆŒNÇéo -�Á»:�‘ŽGÿµ9t ì�ã�$ð˜|¶C¸™/�UWOF…à4 +Ú£¾ÇÁ²�ÎnòŪ¨K Ø™bv/À­É«�êv¹U¾îsý]—Îoéµ> +‰‘PÝÒ_Âáë ‹Š>G{*jL@D±÷æþ$ñtu~Gç$Ï^“œò!ˆžâÝ bÑÉ!£Ï ´w €Q¥P�ðäÈ×¶y�O¹‘ê‚]Í!ÄÑ�ÃÑ!ϼ�Ì4qæ‹�Õyz È~†Ø{‰V‰¼°Á(ï’�­.£|öýÃß¿ûË›þñðòõ—¯_kýÙ«OÏ#΄Ž}{wŽûÝp’[D mŠúˆ³#زÓÈ4]… å7& ´8±=áÑ9§î¹þ³á¼hÞÖëÊ{/`¯´D kfBºö‚:ís-¶³¼o†–”Ø‘X×*4#ÿÖ =^’™MMü9§uÔ»ò½¤<ÛX«J_N8îôÐZÛú~ÌuL˜�@¿9¹e¥=A4Zå2K�BÐ%޵W! µ>¶ý B®9A}Ã:箉”(JÂ^«¢}:`„£ïQtærR1y�.³În�Òî…É{žž†Ë¯ÂÀ2 ü7^‹Ü¾ãʳÆDrXÿÄ5Ä2‹D$!ƒëëVv]Ew‘|%_c‹*F‡ý)V;ã,É4/8é1Æl5ôC鯄¶¬×¨¼†€*A«R£Æ(ëû\šÈg}‹‰l…W©¨xäv„Â@œQÞxO»™ò’Yù>÷�35ÖŒqÊ”öÖXû„¹ß´Ø<" çž0Øy«\1n`gƱÙ׈!ïòļCÓÓòvB›sOnÄ;ñ1.èH}"!•2«•š«/„HÝ”ˆ9ªSÓ Unó¨tÍOÜ(0¿:}‡± t@N“Z +âåûŒm�7ÊgNDÂz§´¶é¦'€Dá@ÛüêvÛ[a^�÷Jâ�à.“QÖe'–2K•MM£/ïæÒ|Ân'æï¬×iã z0ï6m?IŒcsÚ¢3¹.2[v›j AàI�Pªôq�ãó$®euvf'­?Ÿ%õÇà¶l•ö‹[ãÇ ŸàÂ’QE;�ÇËš°…Z©^Ûaa Œ3)«¢öGñËEvˆl8 ôé$Nÿ0^kÂQ¾$±7›‹ê;�¸±ÈÇž8¬ÈýNP.8>”QiávF ’SWÖÊo�[8šf­R°)VÁߡŮWÊ̸´öã� >âF쉨‚qªÁ‚HGùa=l (öÒ-Öy›ôÁèfñ}±Ê&§Ó˜læDv^‚�ª�gT¨ÌG)!U¼Å ®Ôf¥c9Š"6 +<©`2´‘Š^�Qâ[cQÖj}=Ûú¸‘¼ˆ ÓX•L-5œuò¡ÈE9*—ŠTÑ{ÖHXÝ0'ft&ñ R{âh0Ð N 5ïc#8=ø`U|6õ¾á/;Êâ�o¤À¶ÁÏÎýˆƒá(>½¼ñú'> +stream +xœÅÙ’Åñ]_1` °µu +‰‰c¬íă$k°ˆ`„—ñƒÿÞÑ3}T^Õ== ~™•f³²²²2³òªÚëï~y±üøúëÏþt³ÑŸ~úìæ³ÿÞ˜�ÞèÍ•Ùx«‚)1ûMÊ^%cµß¼úùú•Þ¼úu£7¿¾Ú?xv»Ñ›Û»ë�Þd•7·»it÷ãöçï>×Z?×:ö?}ÿ3¶WÅ+²³á¹;~îÔÝé¶ûF?ׯ c÷à÷;jÀóRfNsiz¨¸µùá µÃo8h­íé§“èÕÖ@Èi…¶±‚Õi7PöÃíŸ|~ûàÛŸýÙu½¯†ì«ÕVÙŽŠûj6Y™Æ¾Úaý==�Q¦ÇRz¬Q)—X.—3ŽÇPâ&^3;ÖÃrû¼T~ÉuµäŽÐ"W\Kû’Sɤ×kŸ[ _XÇš˜Uq.FÆJ¬žfkè èŽ3=é>Þí>>ì>ཀ£W¼ÅX­vsäÉ™¬UŽºä4«F¾ZDo˜l€›gzŠmÜ^9íTŒÅ‡ÿ£V=×›;¬ûøóya�ê£�*™ïAòé©WŠŒÙ¤~xÐÀŠˆÅUÅ–>E«’ueí¢öȼ›j× ÂsŠvØ^E«ŒµÉ …ê5O Æò'Õ3< ™Èáÿã÷A:€ÉüK5}í +MÜ^Ih·POzªw3‹¶ºÆ8î×5C}\zæ&ªS!+“Üá»gS!Ò˜[ŒJ¶¤{ó ¦ÃÂÕttö*4(�—©.„ê•×Å”DÏ«X”-ƧQÚõA>?'ã…�4dÞ1†qR=Ó<Æè¬-‡t�^d‚_½r�ع{-=3xÀÚqáû¥§…i?^«œŠs÷è~õbï™ûU=»‘@g­gžK�%šë¯^ì|øz¿m­Õze³ òZíÌZmÚ–ˆöª÷E^n�IÊç‚8zv{pw\ÜfXéÖ�Üœwx$Sä6°wÄㆡ•ؤ ³K5`Ý2>ìnƒá4Ð1Ñ*ïc4 øÜ›ðXÖ×e±�[/è#8vnº�á7$0ê Ñ +å+Ä œì9A_Onäº�OÆ�z4²¸ù–¸i«B6iõ2²‚ó~±«0ž.'>¶WÖFeSÎ…Êè{+[ ²?}à�‡ö�™ÙÏ‚¬@Hf KëJ[ûôË\ŽÀN^ŸÄÌ)ªÃ•§2òFbe[d[‚‚‹QÉ™ <›F4Ï<Èåãþ?SÒ¨sß³.§óøVt¶ÐoB’Î%”R"ƒÇU»ó^;x3:¢ðú¹$×;H…‰â|éQ FP[¥ê>WÉdcV>Cè�‚ù,u+a»L[Ö«6È\õÜéÖ•’òQŸüÕ¿³ßr�e!›ç;¨€v¤ +k ø^ÀÒ�ƒ±#Üq9YÏb̨~Pva³_HÉb¯DKˆP„ŽŽŒ–䥖ä%­´ÓaµäÑS ¥uvN”.Äèçâ‘âc!¸™$tFøE<½D–½%¶¼MÆN@Óðí–-sO§Å!6µ %òo™“`¦ ˆ(”,î‚D+!CQ&k»R%1³Êeçœqgø™Ì°††!A@8rÝ.Éš ¸M[ ;‰ ýñÕZ1”âFöx77Ùtª`Þ¾a ‰=Äp|–©!ž¥%ž>+“ãývÚ ¨’È©š×#KsyI«| èœËk£ƒhd¯s Šh ¾hå¢1—3î° V“|M4:Ü çОÁ¸ŠB]G<¯h0`3ì!À6ÁÃ4"9}dRghfâdŒ‚œÝ“½ÀNÜ™{3†¢B‘zLóÎê€m)«Kʹ×F} ûœ´ò.é1+ üÁ!»JºløýÝ 8ës¨!¹ÆÑ�BˆOLrÒ™Ið‡|áQy8Ÿxf�Í?†Þ Ô7mô…æE˜’û³É­dl´Q9oB¸D�ßöt\uKÐukï~¶h¦½TñaÉsçȠ飛<9™W>©d� cjWuÓ|Ãl!QuFr0�3ºYa1>XËÛµ*¿K¨;� È@vŠP Ü6ja¶™3A9Aé�œ{tV¹|oãÖ袂™R„ˆâ(ñnÎ5™tƒ ø«4s¿Š;ï5xJ8&•¢³ó,�ó§T¿V!ÜÚgù_‹]Cì²BÆ÷Ü ê6Ò7•xTÎù1ò5SÚ+_Òêš“µN9=•÷a"-.¸…RÄzf(†º…znÃRåö÷À^`ù[Ï]TŒ¥9sŒ—;:¤–x®­,H(NEã=;®W�Cœƒ‰¸–-t¸bMä�M±‡f›jÉ#ßj¿ †\¸C$©o�ࡹJ2Ó\ÀCq6‹ o„ �M¿Ïµ cK†å)â«mvlØùtD?ï¶² ![•J´«å³"�—¼!ɉ2“£ê³‡-[y§B´}›Žá¿¦A“] ¼…F˜„ö12ŸhÀ0¤Tn×íÚYeŒ–bH—´�"#çJ´tÁ­nB}6›ä»­î¶�Ì1¨¹¼f©ù²Ö*£cG0ðƒMöžý,~†ÝSY�šjcÔ`‘ª™òHì—SC2µ2øc+Å7È–q~êŠÍ1]^KË=†·]¦s@–EÓ’Åh”.Á¯¿èÒÌ-&­ŒsUka©¦–€Ï…; ºÉ�¢ïQSV“Iñ¸vÇ/ŸS¢TáƒîrRêìÀÂÕÏÅ>©›ÙjøöÂê/a1`ɼ¶Ò£!heýúŠïÂæ”-¸øR7ÂDGwØ¢lÈr1ì…q¨gË´»tªÓ—Ÿ¾ê‚X4=‰�€Kƒóshë·|9øa×[FDdAC6¿zìzp®*jŽÉ‚ÙÊ“W”+]ku3è�€Fâ¼òu¬gÝ�ÊQ�{i$Ü4Wí/¼ç˪ •–’à™Z˜©„£ÅK™%y±Â=ަ+]·»¼Üå$(��øS ±ª”¹J"Ø+e-n5›UðÞ®á%-ÔÝÚY~\UøÇ� ã:ïpãæ‚s`9žI ŽÁµ€¿Š6uCbâ�|ÁrŸÌb>]‡ÉR‰÷•Ð,|Ä)Ël«LR±¸¼Þìž' 7í–Eg� £~²¹(Úè‰Ç-{î�jõÓŒ‚'ô¢M±Á +e‰ƒÕB¾�M™�ÛP Å»üVŒÀLJ‹ß톄Jí 9 ‰7ˆë®lo—,à­îÚ £ÊÞùµå&K×-Ǭâ)ä««4&„ßK¸8—_ûxñ¥Œ›¤9ÇlÑ“Á•E9¼F÷ÚN\ÌSƒœ5=¨AX\:h�EΟqý�\‘ÔjÕ²| ª§ïW¤M‰*—hOýOë:ôfÌ +ô`ìQ5gøl’Ï_™XDË‚iÂ7¹èS.‘¢<ÐR( q�nßôEð˜vðHG,¾ìZå(Ÿƒ2ÞÆ ºÏKdÃëVzŠò)fÅé@âe±�)œ3'3Wl]í£„â…Ú¼Xâ’ˆ#:‚{ Èå9B,u"þ²_Rd�¬¾ØàZµ*Ÿ¼2>¯¾€lF“5ØÊ‰ ±{Iϸ;êB›$?ÚC+ÆÄîα� X°´xôÑÕÀa4 n¼Q.g/Þ†½�2ÜK…'•040únù¢" +þ +Ge©8¬ù¯ôdÔmœÌ´Ë/þÉ›ÖJûè”-ù’»”K#j¯•Õ1¶ç@P­«3 ¢ Gñ7ò± ¢“Íë±àõŸ¾BºÎL,í¶]™ELµíAË+³^±o%�}螟Hií-" ¨P>Ó†�²Šnº8ϾI¬8²«C«Æõ>=‚ÄÆü@èå©ã/>¡ÆÊËFk3ÓhQ¨X^a=­îðžÎ.ï¯,£~ÉK4Þ’.võK48Þº¢ãi0j¥‹³9, eî.’3ëUÝ¿ÖÉYæÍÿhiz«Ô¥¨�2Úé“Â<™¸Ñ?uprv†.·ã0]ÿæÃ…ïyšéj˜.¦¼`± Þs4A%§ž@Ãÿæ/ÝÇq›þÖ}|Ñ}|$!A™,òv�çŸ)€PgwxP�|¦©벊1–¸€ÑËÐÓÂ�?ÊÊ{õ7C©¥ͨÐ8•£M„DTr' #¿?Jc´h•¶Ñ¸ôHòz$çf{û“0)�¿bT~ᤠ?ËÆ¶�+h€Q´ÊÇ× ×³âDÕcœ#²±(¯í©8òÞhÊ>bÿA­–_² ìµi€Ž.”ÕHc_ÒAcÕFöØÖcŸyïxUOî9ôÈâÒtöµSÚØÕ¯‚šó Ç§Pdx˜¹ZDˆ*ç¢|uÜËMXŠ–ˆ ;7(&±%K\6Ş͛a�ëó#‹�qî$À‰LBвôóltZ½!®Xe¢)ë(¸Ö[åHð™CWèïÉŠÍS$Á_V†HϨ†�q{ãñ0U~D©1‚7rã�˜Œ«¤”¾¼J{®X_®IïóÜëMÔÑYž½Z}.ebÊ«£¾†U-hNéäüð׎Ÿe<‡³×ìUC øÜ‚R¡û;¯ÌÌ…O×]!4:-`^Û‡zY¦SQ´A ô§JIÁ\¾““Á—  Ñ€‰Zù�Ü}qã®~·‘ËÔÃ7©¨]þ]X™¢K÷H·u1^ÌŠ?Ö1ä:�}¨V&2ÅjB)÷ úT !Û?ß'©ò‚Cô¢¢á­ôykC[u×õâ'wÁån¹äwŠY[£|òÖØuÝ}‘ºÉ�:Xm\X@~[Ÿ ³Ù?dß †ú@¥t‘gÈ—ó’°ŠV`ø®Q:pö!52“p]Žt'\¸›ôÌ6Ù(�r°ÿ‡Ý¤‡ ÕZ…ìKü-¶slu·Ò°æ“­YwÃnMšñš°ÌÏVW— E9ë.xˆ|jaoKÑʘ\Y_±]¾çáÛˆdWÂqÙq 纪É@U:¦aeÁúÈ¡èZSõ'³Ø5Ni•u<¸,Ühι_,•­.0ç³òÉ®�[qSf£P?_´ìسÅü‡3€À*©Ð.V�ü�´,ža.Q„¢ovãÁ:¥+׬(ÚðbO%ÊŸÞÞì^¼:<~|ýôpxñê_¯ÿùýõ³·‡ÃÛŸ¸þî?/ÿýåõõoß^ß]ßvÿþæÅ�oö/oÞîé9Pœ².å�-*éhÌÚ[­ÇÊ ÌÿÀ¼|s +endstream +endobj +2210 0 obj <> stream xœu�±JÃPFOŒµUªv¬DœD@¤`]\Ú @@ -1704,7 +7770,7 @@ F ̵Ò8+¤/<°ŠÀâa­RkpÂàØ´óA¾mWÓQ”Ihÿ㌠œ%–Álþ½E5VW~ªüdžµþX€ì)ôÏ´þ:׺öôŽb‘ˆ�k#�&¼_¤ 3÷0±÷ ØIì endstream endobj -452 0 obj +2211 0 obj <> stream xœ}�¿KqÆ?×U–X 94%MQKS�†Nø#Ô¦óüQ vÝ÷BšË¡©hˆFk ¢ÙÆú‚ ! @@ -1712,478 +7778,2237 @@ x ô’0l+ϽߣÈy7½®Ó;¯×«É¥º;QŽ?V.ø_îtFÀà3LËEÆK¶)y ðëz”80eÅIPö¤Ÿkñ‰äT‹/%[Ñp” å:8ÕÁ…ü¶¼+%¿÷dбÐŒ"ÂÿG¦·™ `d_¿{Ù¹ÙÖ–gzžçm\‡Ð8rœÏSÇiœ�úµ­öþfæë ´½Ô1\íÃÈCÛóU`¨ ÕS·ô¦¥]Ù ¨ŸÃ@†oÁ½ö ãâ_± endstream endobj -453 0 obj -<> +2212 0 obj +<> endobj -454 0 obj -<> +2213 0 obj +<> stream -Typst 0.15.1en2026-08-12T14:37:41-03:002026-08-12T14:37:41-03:007application/pdfcWkS3h769t04PhZUfewewQ==cWkS3h769t04PhZUfewewQ==proof1.7 +Typst 0.15.1en2026-08-26T21:28:07-03:002026-08-26T21:28:07-03:0016application/pdfWvx4a0CBvmf3gi1aUBP6hw==Wvx4a0CBvmf3gi1aUBP6hw==proof1.7 endstream endobj -455 0 obj -<>/ViewerPreferences<>/Outlines 10 0 R>> +2214 0 obj +<>/ViewerPreferences<>/Outlines 56 0 R/Names<>>>>> endobj xref -0 456 +0 2215 0000000000 65535 f 0000000016 00000 n -0000000117 00000 n -0000000193 00000 n -0000000279 00000 n -0000000371 00000 n -0000000453 00000 n -0000000568 00000 n -0000000650 00000 n -0000000754 00000 n -0000000856 00000 n -0000000922 00000 n -0000001143 00000 n -0000001833 00000 n -0000005028 00000 n -0000006510 00000 n -0000007552 00000 n -0000008746 00000 n -0000010276 00000 n -0000010462 00000 n -0000010533 00000 n -0000010594 00000 n -0000010661 00000 n -0000010743 00000 n -0000010816 00000 n -0000010886 00000 n -0000010973 00000 n -0000011055 00000 n -0000011127 00000 n -0000011211 00000 n -0000011283 00000 n -0000011401 00000 n -0000011471 00000 n -0000011541 00000 n -0000011611 00000 n -0000011681 00000 n -0000011751 00000 n -0000011938 00000 n -0000012006 00000 n -0000012078 00000 n -0000012196 00000 n -0000012330 00000 n -0000012398 00000 n -0000012526 00000 n -0000012598 00000 n -0000012668 00000 n -0000012738 00000 n -0000012808 00000 n -0000012931 00000 n -0000012999 00000 n -0000013071 00000 n -0000013141 00000 n -0000013211 00000 n -0000013281 00000 n -0000013351 00000 n -0000013421 00000 n -0000013491 00000 n -0000013561 00000 n -0000013631 00000 n -0000013701 00000 n -0000013771 00000 n -0000013841 00000 n -0000013911 00000 n -0000013981 00000 n -0000014051 00000 n -0000014121 00000 n -0000014191 00000 n -0000014444 00000 n -0000014515 00000 n -0000014584 00000 n -0000014653 00000 n -0000014724 00000 n -0000014794 00000 n -0000014924 00000 n -0000014996 00000 n -0000015066 00000 n -0000015136 00000 n -0000015206 00000 n -0000015276 00000 n -0000015346 00000 n -0000015416 00000 n -0000015486 00000 n -0000015556 00000 n -0000015626 00000 n -0000015696 00000 n -0000015766 00000 n -0000015836 00000 n -0000015906 00000 n -0000015976 00000 n -0000016046 00000 n -0000016116 00000 n -0000016186 00000 n -0000016256 00000 n -0000016326 00000 n -0000016396 00000 n -0000016466 00000 n -0000016536 00000 n -0000016843 00000 n -0000016913 00000 n -0000016991 00000 n -0000017131 00000 n -0000017200 00000 n -0000017330 00000 n -0000017456 00000 n -0000017578 00000 n -0000017651 00000 n -0000017726 00000 n -0000017799 00000 n -0000017872 00000 n -0000017998 00000 n -0000018088 00000 n -0000018166 00000 n -0000018236 00000 n -0000018398 00000 n -0000018508 00000 n -0000018594 00000 n -0000018688 00000 n -0000018782 00000 n -0000018868 00000 n -0000018970 00000 n -0000019096 00000 n -0000019152 00000 n -0000019222 00000 n -0000019316 00000 n -0000019470 00000 n -0000019652 00000 n -0000019802 00000 n -0000019940 00000 n -0000020094 00000 n -0000020180 00000 n -0000020322 00000 n -0000020396 00000 n -0000020470 00000 n -0000020580 00000 n -0000020730 00000 n -0000020804 00000 n -0000020886 00000 n -0000021037 00000 n -0000021109 00000 n -0000021165 00000 n -0000021318 00000 n -0000021429 00000 n -0000021562 00000 n -0000021901 00000 n -0000021974 00000 n -0000022047 00000 n -0000022149 00000 n -0000022229 00000 n -0000022302 00000 n -0000022379 00000 n -0000022477 00000 n -0000022559 00000 n -0000022634 00000 n -0000022755 00000 n -0000022830 00000 n -0000022951 00000 n -0000023026 00000 n -0000023147 00000 n -0000023261 00000 n -0000023349 00000 n -0000023485 00000 n -0000023613 00000 n -0000023741 00000 n -0000023905 00000 n -0000023993 00000 n -0000024121 00000 n -0000024249 00000 n -0000024377 00000 n -0000024517 00000 n -0000024605 00000 n -0000024733 00000 n -0000024861 00000 n -0000024989 00000 n -0000025066 00000 n -0000025202 00000 n -0000025290 00000 n -0000025418 00000 n -0000025546 00000 n -0000025674 00000 n -0000025747 00000 n -0000025887 00000 n -0000025975 00000 n -0000026103 00000 n -0000026231 00000 n -0000026359 00000 n -0000026515 00000 n -0000026603 00000 n -0000026731 00000 n -0000026859 00000 n -0000026987 00000 n -0000027147 00000 n -0000027235 00000 n -0000027308 00000 n -0000027379 00000 n -0000027450 00000 n -0000027521 00000 n -0000027683 00000 n -0000027756 00000 n -0000027877 00000 n -0000027950 00000 n -0000028071 00000 n -0000028206 00000 n -0000028294 00000 n -0000028473 00000 n -0000028545 00000 n -0000028617 00000 n -0000028689 00000 n -0000028761 00000 n -0000028889 00000 n -0000028964 00000 n -0000029076 00000 n -0000029148 00000 n -0000029220 00000 n -0000029294 00000 n -0000029366 00000 n -0000029438 00000 n -0000029510 00000 n -0000029585 00000 n -0000029657 00000 n -0000029832 00000 n -0000029904 00000 n -0000029996 00000 n -0000030068 00000 n -0000030140 00000 n -0000030212 00000 n -0000030284 00000 n -0000030356 00000 n -0000030428 00000 n -0000030578 00000 n -0000030657 00000 n -0000030729 00000 n -0000030801 00000 n -0000030873 00000 n -0000030945 00000 n -0000031017 00000 n -0000031089 00000 n -0000031161 00000 n -0000031236 00000 n -0000031308 00000 n -0000031381 00000 n -0000031454 00000 n -0000031527 00000 n -0000031600 00000 n -0000031673 00000 n -0000031746 00000 n -0000031819 00000 n -0000031892 00000 n -0000031965 00000 n -0000032038 00000 n -0000032111 00000 n -0000032182 00000 n -0000032603 00000 n -0000032703 00000 n -0000032776 00000 n -0000032848 00000 n -0000032920 00000 n -0000032992 00000 n -0000033064 00000 n -0000033136 00000 n -0000033208 00000 n -0000033280 00000 n -0000033454 00000 n -0000033526 00000 n -0000033598 00000 n -0000033698 00000 n -0000033808 00000 n -0000033880 00000 n -0000033969 00000 n -0000034043 00000 n -0000034115 00000 n -0000034187 00000 n -0000034259 00000 n -0000034331 00000 n -0000034476 00000 n -0000034550 00000 n -0000034622 00000 n -0000034694 00000 n -0000034766 00000 n -0000034838 00000 n -0000034910 00000 n -0000034982 00000 n -0000035054 00000 n -0000035113 00000 n -0000035185 00000 n -0000035244 00000 n -0000035316 00000 n -0000035556 00000 n -0000035630 00000 n -0000035703 00000 n -0000035776 00000 n -0000035849 00000 n -0000035926 00000 n -0000035999 00000 n -0000036072 00000 n -0000036145 00000 n -0000036218 00000 n -0000036291 00000 n -0000036364 00000 n -0000036437 00000 n -0000036510 00000 n -0000036583 00000 n -0000036656 00000 n -0000036729 00000 n -0000036802 00000 n -0000036875 00000 n -0000036948 00000 n -0000037021 00000 n -0000037094 00000 n -0000037167 00000 n -0000037240 00000 n -0000037311 00000 n -0000037382 00000 n -0000037453 00000 n -0000037524 00000 n -0000037595 00000 n -0000037667 00000 n -0000037739 00000 n -0000037811 00000 n -0000037883 00000 n -0000037955 00000 n -0000038014 00000 n -0000038086 00000 n -0000038158 00000 n -0000038230 00000 n -0000038289 00000 n -0000038361 00000 n -0000038433 00000 n -0000038505 00000 n -0000038577 00000 n -0000038652 00000 n -0000038724 00000 n -0000038796 00000 n -0000038868 00000 n -0000038940 00000 n -0000039012 00000 n -0000039084 00000 n -0000039156 00000 n -0000039228 00000 n -0000039300 00000 n -0000039372 00000 n -0000039444 00000 n -0000039516 00000 n -0000039588 00000 n -0000039660 00000 n -0000039732 00000 n -0000039804 00000 n -0000039876 00000 n -0000041889 00000 n -0000041963 00000 n -0000042038 00000 n -0000042110 00000 n -0000042182 00000 n -0000042254 00000 n -0000042326 00000 n -0000042464 00000 n -0000042538 00000 n -0000042610 00000 n -0000042683 00000 n -0000042756 00000 n -0000042899 00000 n -0000042974 00000 n -0000043047 00000 n -0000043120 00000 n -0000043193 00000 n -0000043266 00000 n -0000043339 00000 n -0000043412 00000 n -0000043485 00000 n -0000043558 00000 n -0000043631 00000 n -0000043704 00000 n -0000043777 00000 n -0000043850 00000 n -0000044088 00000 n -0000044163 00000 n -0000044236 00000 n -0000044309 00000 n -0000044382 00000 n -0000044455 00000 n -0000044528 00000 n -0000044601 00000 n -0000044674 00000 n -0000044747 00000 n -0000044820 00000 n -0000044893 00000 n -0000044966 00000 n -0000045039 00000 n -0000045112 00000 n -0000045185 00000 n -0000045487 00000 n -0000045562 00000 n -0000045633 00000 n -0000045704 00000 n -0000045776 00000 n -0000045850 00000 n -0000046500 00000 n -0000046907 00000 n -0000046944 00000 n -0000046981 00000 n -0000047034 00000 n -0000047086 00000 n -0000047139 00000 n -0000047191 00000 n -0000047243 00000 n -0000047296 00000 n -0000047349 00000 n -0000047401 00000 n -0000047524 00000 n -0000047658 00000 n -0000047792 00000 n -0000047915 00000 n -0000048049 00000 n -0000048172 00000 n -0000048295 00000 n -0000048444 00000 n -0000049011 00000 n -0000049219 00000 n -0000049371 00000 n -0000050286 00000 n -0000050497 00000 n -0000050637 00000 n -0000050853 00000 n -0000051092 00000 n -0000051252 00000 n -0000051458 00000 n -0000051671 00000 n -0000051799 00000 n -0000051927 00000 n -0000052055 00000 n -0000052183 00000 n -0000052311 00000 n -0000052439 00000 n -0000052567 00000 n -0000052648 00000 n -0000053299 00000 n -0000058210 00000 n -0000058292 00000 n -0000059114 00000 n -0000070956 00000 n -0000071038 00000 n -0000071861 00000 n -0000085825 00000 n -0000085902 00000 n -0000086333 00000 n -0000086805 00000 n -0000092369 00000 n -0000100471 00000 n -0000105171 00000 n -0000111698 00000 n -0000118844 00000 n -0000126997 00000 n -0000129046 00000 n -0000129389 00000 n -0000129796 00000 n -0000129909 00000 n -0000130982 00000 n +0000000206 00000 n +0000000296 00000 n +0000000398 00000 n +0000000502 00000 n +0000000614 00000 n +0000000705 00000 n +0000000809 00000 n +0000000916 00000 n +0000001014 00000 n +0000001113 00000 n +0000001263 00000 n +0000001382 00000 n +0000001486 00000 n +0000001609 00000 n +0000001715 00000 n +0000001805 00000 n +0000001922 00000 n +0000002025 00000 n +0000002147 00000 n +0000002245 00000 n +0000002365 00000 n +0000002479 00000 n +0000002577 00000 n +0000002714 00000 n +0000002805 00000 n +0000002912 00000 n +0000003027 00000 n +0000003120 00000 n +0000003267 00000 n +0000003367 00000 n +0000003474 00000 n +0000003601 00000 n +0000003712 00000 n +0000003820 00000 n +0000003954 00000 n +0000004050 00000 n +0000004165 00000 n +0000004266 00000 n +0000004392 00000 n +0000004493 00000 n +0000004583 00000 n +0000004722 00000 n +0000004827 00000 n +0000004928 00000 n +0000005011 00000 n +0000005133 00000 n +0000005226 00000 n +0000005332 00000 n +0000005446 00000 n +0000005560 00000 n +0000005650 00000 n +0000005806 00000 n +0000005895 00000 n +0000006006 00000 n +0000006111 00000 n +0000006179 00000 n +0000006388 00000 n +0000006866 00000 n +0000008768 00000 n +0000013050 00000 n +0000017252 00000 n +0000023294 00000 n +0000028200 00000 n +0000031186 00000 n +0000034023 00000 n +0000037903 00000 n +0000041629 00000 n +0000045589 00000 n +0000049945 00000 n +0000052933 00000 n +0000056542 00000 n +0000058801 00000 n +0000062086 00000 n +0000062725 00000 n +0000062797 00000 n +0000062859 00000 n +0000062928 00000 n +0000063035 00000 n +0000063148 00000 n +0000063253 00000 n +0000063358 00000 n +0000063464 00000 n +0000063570 00000 n +0000063676 00000 n +0000063785 00000 n +0000063891 00000 n +0000064003 00000 n +0000064091 00000 n +0000064189 00000 n +0000064271 00000 n +0000064344 00000 n +0000064415 00000 n +0000064486 00000 n +0000064622 00000 n +0000064695 00000 n +0000064766 00000 n +0000064871 00000 n +0000064946 00000 n +0000065057 00000 n +0000065148 00000 n +0000065248 00000 n +0000065323 00000 n +0000065414 00000 n +0000065489 00000 n +0000065562 00000 n +0000065635 00000 n +0000065708 00000 n +0000065781 00000 n +0000065854 00000 n +0000066018 00000 n +0000066093 00000 n +0000066191 00000 n +0000066265 00000 n +0000066341 00000 n +0000066415 00000 n +0000066489 00000 n +0000066563 00000 n +0000066637 00000 n +0000066711 00000 n +0000066785 00000 n +0000066863 00000 n +0000067075 00000 n +0000067149 00000 n +0000067223 00000 n +0000067339 00000 n +0000067454 00000 n +0000067528 00000 n +0000067602 00000 n +0000067676 00000 n +0000067750 00000 n +0000067824 00000 n +0000067898 00000 n +0000067972 00000 n +0000068046 00000 n +0000068120 00000 n +0000068194 00000 n +0000068307 00000 n +0000068381 00000 n +0000068637 00000 n +0000068726 00000 n +0000068832 00000 n +0000068938 00000 n +0000069044 00000 n +0000069150 00000 n +0000069256 00000 n +0000069362 00000 n +0000069468 00000 n +0000069574 00000 n +0000069680 00000 n +0000069786 00000 n +0000069895 00000 n +0000070001 00000 n +0000070107 00000 n +0000070272 00000 n +0000070379 00000 n +0000070484 00000 n +0000070589 00000 n +0000070694 00000 n +0000070799 00000 n +0000070904 00000 n +0000071009 00000 n +0000071114 00000 n +0000071219 00000 n +0000071324 00000 n +0000071430 00000 n +0000071536 00000 n +0000071642 00000 n +0000071748 00000 n +0000071854 00000 n +0000071962 00000 n +0000072071 00000 n +0000072177 00000 n +0000072283 00000 n +0000072391 00000 n +0000072500 00000 n +0000072606 00000 n +0000072715 00000 n +0000072823 00000 n +0000072932 00000 n +0000073038 00000 n +0000073144 00000 n +0000073250 00000 n +0000073356 00000 n +0000073462 00000 n +0000073568 00000 n +0000073674 00000 n +0000073780 00000 n +0000073886 00000 n +0000074004 00000 n +0000074110 00000 n +0000074216 00000 n +0000074322 00000 n +0000074428 00000 n +0000074534 00000 n +0000074640 00000 n +0000074746 00000 n +0000074852 00000 n +0000074958 00000 n +0000075400 00000 n +0000075474 00000 n +0000075550 00000 n +0000075624 00000 n +0000075733 00000 n +0000075807 00000 n +0000075881 00000 n +0000075955 00000 n +0000076029 00000 n +0000076103 00000 n +0000076177 00000 n +0000076251 00000 n +0000076325 00000 n +0000076399 00000 n +0000076473 00000 n +0000076547 00000 n +0000076621 00000 n +0000076695 00000 n +0000076769 00000 n +0000076843 00000 n +0000076917 00000 n +0000076991 00000 n +0000077267 00000 n +0000077341 00000 n +0000077415 00000 n +0000077489 00000 n +0000077563 00000 n +0000077637 00000 n +0000077711 00000 n +0000077785 00000 n +0000077859 00000 n +0000077932 00000 n +0000078005 00000 n +0000078384 00000 n +0000078496 00000 n +0000078578 00000 n +0000078664 00000 n +0000078737 00000 n +0000078810 00000 n +0000078883 00000 n +0000078956 00000 n +0000079029 00000 n +0000079102 00000 n +0000079175 00000 n +0000079248 00000 n +0000079322 00000 n +0000079396 00000 n +0000079470 00000 n +0000079680 00000 n +0000079754 00000 n +0000079865 00000 n +0000079939 00000 n +0000080013 00000 n +0000080087 00000 n +0000080163 00000 n +0000080303 00000 n +0000080403 00000 n +0000080477 00000 n +0000080591 00000 n +0000080665 00000 n +0000080753 00000 n +0000080876 00000 n +0000081035 00000 n +0000081194 00000 n +0000081289 00000 n +0000081432 00000 n +0000081535 00000 n +0000081658 00000 n +0000081753 00000 n +0000081908 00000 n +0000081987 00000 n +0000082066 00000 n +0000082145 00000 n +0000082288 00000 n +0000082443 00000 n +0000082566 00000 n +0000082713 00000 n +0000082808 00000 n +0000082927 00000 n +0000083050 00000 n +0000083141 00000 n +0000083236 00000 n +0000083367 00000 n +0000083490 00000 n +0000083569 00000 n +0000083644 00000 n +0000083715 00000 n +0000084015 00000 n +0000084089 00000 n +0000084163 00000 n +0000084239 00000 n +0000084313 00000 n +0000084387 00000 n +0000084461 00000 n +0000084535 00000 n +0000084609 00000 n +0000084683 00000 n +0000084903 00000 n +0000084977 00000 n +0000085051 00000 n +0000085125 00000 n +0000085199 00000 n +0000085273 00000 n +0000085347 00000 n +0000085421 00000 n +0000085495 00000 n +0000085683 00000 n +0000085757 00000 n +0000085831 00000 n +0000085905 00000 n +0000085979 00000 n +0000086053 00000 n +0000086201 00000 n +0000086275 00000 n +0000086349 00000 n +0000086423 00000 n +0000086536 00000 n +0000086610 00000 n +0000086684 00000 n +0000086758 00000 n +0000086832 00000 n +0000087016 00000 n +0000087090 00000 n +0000087163 00000 n +0000087290 00000 n +0000087384 00000 n +0000087517 00000 n +0000087641 00000 n +0000087747 00000 n +0000087856 00000 n +0000087957 00000 n +0000088060 00000 n +0000088131 00000 n +0000088187 00000 n +0000088322 00000 n +0000088445 00000 n +0000088548 00000 n +0000088635 00000 n +0000088710 00000 n +0000088841 00000 n +0000088932 00000 n +0000089075 00000 n +0000089178 00000 n +0000089265 00000 n +0000089336 00000 n +0000089580 00000 n +0000089654 00000 n +0000089728 00000 n +0000089802 00000 n +0000089876 00000 n +0000090004 00000 n +0000090078 00000 n +0000090152 00000 n +0000090226 00000 n +0000090300 00000 n +0000090376 00000 n +0000090450 00000 n +0000090524 00000 n +0000090716 00000 n +0000090790 00000 n +0000090911 00000 n +0000090985 00000 n +0000091059 00000 n +0000091133 00000 n +0000091261 00000 n +0000091372 00000 n +0000091447 00000 n +0000091558 00000 n +0000091657 00000 n +0000091748 00000 n +0000091855 00000 n +0000091966 00000 n +0000092065 00000 n +0000092168 00000 n +0000092275 00000 n +0000092350 00000 n +0000092461 00000 n +0000092560 00000 n +0000092651 00000 n +0000092758 00000 n +0000092833 00000 n +0000092908 00000 n +0000093136 00000 n +0000093210 00000 n +0000093284 00000 n +0000093358 00000 n +0000093432 00000 n +0000093506 00000 n +0000093580 00000 n +0000093768 00000 n +0000093842 00000 n +0000093916 00000 n +0000094032 00000 n +0000094106 00000 n +0000094180 00000 n +0000094254 00000 n +0000094328 00000 n +0000094402 00000 n +0000094476 00000 n +0000094550 00000 n +0000094730 00000 n +0000094804 00000 n +0000094878 00000 n +0000094952 00000 n +0000095026 00000 n +0000095100 00000 n +0000095174 00000 n +0000095287 00000 n +0000095361 00000 n +0000095435 00000 n +0000095509 00000 n +0000095583 00000 n +0000095657 00000 n +0000095917 00000 n +0000096034 00000 n +0000096108 00000 n +0000096196 00000 n +0000096303 00000 n +0000096410 00000 n +0000096517 00000 n +0000096624 00000 n +0000096735 00000 n +0000096842 00000 n +0000096949 00000 n +0000097056 00000 n +0000097163 00000 n +0000097296 00000 n +0000097365 00000 n +0000097434 00000 n +0000097503 00000 n +0000097572 00000 n +0000097684 00000 n +0000097823 00000 n +0000097953 00000 n +0000098089 00000 n +0000098183 00000 n +0000098268 00000 n +0000098343 00000 n +0000098478 00000 n +0000098561 00000 n +0000098632 00000 n +0000098835 00000 n +0000098909 00000 n +0000098983 00000 n +0000099057 00000 n +0000099131 00000 n +0000099205 00000 n +0000099333 00000 n +0000099407 00000 n +0000099481 00000 n +0000099555 00000 n +0000099629 00000 n +0000099703 00000 n +0000099777 00000 n +0000099851 00000 n +0000099925 00000 n +0000099999 00000 n +0000100267 00000 n +0000100382 00000 n +0000100541 00000 n +0000100644 00000 n +0000100755 00000 n +0000100870 00000 n +0000101005 00000 n +0000101112 00000 n +0000101267 00000 n +0000101422 00000 n +0000101497 00000 n +0000101572 00000 n +0000101683 00000 n +0000101830 00000 n +0000101957 00000 n +0000102076 00000 n +0000102183 00000 n +0000102342 00000 n +0000102489 00000 n +0000102564 00000 n +0000102635 00000 n +0000102879 00000 n +0000102953 00000 n +0000103027 00000 n +0000103135 00000 n +0000103209 00000 n +0000103283 00000 n +0000103391 00000 n +0000103465 00000 n +0000103539 00000 n +0000103651 00000 n +0000103763 00000 n +0000103837 00000 n +0000103911 00000 n +0000103985 00000 n +0000104059 00000 n +0000104187 00000 n +0000104302 00000 n +0000104409 00000 n +0000104520 00000 n +0000104595 00000 n +0000104670 00000 n +0000104745 00000 n +0000104820 00000 n +0000104895 00000 n +0000104998 00000 n +0000105077 00000 n +0000105249 00000 n +0000105323 00000 n +0000105399 00000 n +0000105499 00000 n +0000105573 00000 n +0000105673 00000 n +0000105747 00000 n +0000105821 00000 n +0000105895 00000 n +0000105969 00000 n +0000106043 00000 n +0000106117 00000 n +0000106191 00000 n +0000106383 00000 n +0000106469 00000 n +0000106583 00000 n +0000106667 00000 n +0000106738 00000 n +0000106897 00000 n +0000107008 00000 n +0000107095 00000 n +0000107190 00000 n +0000107265 00000 n +0000107340 00000 n +0000107427 00000 n +0000107530 00000 n +0000107605 00000 n +0000107712 00000 n +0000107787 00000 n +0000107886 00000 n +0000107989 00000 n +0000108064 00000 n +0000108155 00000 n +0000108282 00000 n +0000108401 00000 n +0000108504 00000 n +0000108607 00000 n +0000108663 00000 n +0000108734 00000 n +0000108829 00000 n +0000108904 00000 n +0000109047 00000 n +0000109170 00000 n +0000109301 00000 n +0000109424 00000 n +0000109515 00000 n +0000109594 00000 n +0000109675 00000 n +0000109763 00000 n +0000109836 00000 n +0000109918 00000 n +0000110036 00000 n +0000110136 00000 n +0000110209 00000 n +0000110324 00000 n +0000110397 00000 n +0000110494 00000 n +0000110567 00000 n +0000110684 00000 n +0000110803 00000 n +0000110918 00000 n +0000110974 00000 n +0000111109 00000 n +0000111224 00000 n +0000111351 00000 n +0000111470 00000 n +0000111577 00000 n +0000111692 00000 n +0000111791 00000 n +0000112299 00000 n +0000112373 00000 n +0000112447 00000 n +0000112521 00000 n +0000112595 00000 n +0000112719 00000 n +0000112805 00000 n +0000112916 00000 n +0000113019 00000 n +0000113122 00000 n +0000113229 00000 n +0000113336 00000 n +0000113443 00000 n +0000113550 00000 n +0000113657 00000 n +0000113764 00000 n +0000113871 00000 n +0000114002 00000 n +0000114125 00000 n +0000114228 00000 n +0000114303 00000 n +0000114359 00000 n +0000114430 00000 n +0000114501 00000 n +0000114572 00000 n +0000114643 00000 n +0000114754 00000 n +0000114881 00000 n +0000114988 00000 n +0000115099 00000 n +0000115206 00000 n +0000115262 00000 n +0000115413 00000 n +0000115532 00000 n +0000115607 00000 n +0000115682 00000 n +0000115757 00000 n +0000116089 00000 n +0000116163 00000 n +0000116237 00000 n +0000116311 00000 n +0000116385 00000 n +0000116459 00000 n +0000116607 00000 n +0000116681 00000 n +0000116755 00000 n +0000116829 00000 n +0000116903 00000 n +0000116977 00000 n +0000117090 00000 n +0000117254 00000 n +0000117367 00000 n +0000117443 00000 n +0000117554 00000 n +0000117661 00000 n +0000117776 00000 n +0000117883 00000 n +0000117990 00000 n +0000118097 00000 n +0000118204 00000 n +0000118311 00000 n +0000118418 00000 n +0000118525 00000 n +0000118632 00000 n +0000118739 00000 n +0000118846 00000 n +0000118953 00000 n +0000119060 00000 n +0000119241 00000 n +0000119372 00000 n +0000119479 00000 n +0000119586 00000 n +0000119693 00000 n +0000119800 00000 n +0000119907 00000 n +0000120045 00000 n +0000120119 00000 n +0000120196 00000 n +0000120289 00000 n +0000120413 00000 n +0000120487 00000 n +0000120561 00000 n +0000120665 00000 n +0000120776 00000 n +0000120850 00000 n +0000120924 00000 n +0000120998 00000 n +0000121072 00000 n +0000121146 00000 n +0000121302 00000 n +0000121376 00000 n +0000121480 00000 n +0000121581 00000 n +0000121687 00000 n +0000121761 00000 n +0000121835 00000 n +0000121909 00000 n +0000122017 00000 n +0000122093 00000 n +0000122169 00000 n +0000122245 00000 n +0000122319 00000 n +0000122393 00000 n +0000122545 00000 n +0000122617 00000 n +0000122689 00000 n +0000122761 00000 n +0000122833 00000 n +0000122906 00000 n +0000122981 00000 n +0000123127 00000 n +0000123200 00000 n +0000123273 00000 n +0000123375 00000 n +0000123489 00000 n +0000123562 00000 n +0000123635 00000 n +0000123708 00000 n +0000123781 00000 n +0000123854 00000 n +0000123927 00000 n +0000124000 00000 n +0000124073 00000 n +0000124146 00000 n +0000124328 00000 n +0000124401 00000 n +0000124474 00000 n +0000124579 00000 n +0000124652 00000 n +0000124725 00000 n +0000124798 00000 n +0000124871 00000 n +0000124946 00000 n +0000125019 00000 n +0000125180 00000 n +0000125288 00000 n +0000125361 00000 n +0000125434 00000 n +0000125507 00000 n +0000125580 00000 n +0000125653 00000 n +0000125726 00000 n +0000125872 00000 n +0000125946 00000 n +0000126020 00000 n +0000126139 00000 n +0000126213 00000 n +0000126287 00000 n +0000126361 00000 n +0000126435 00000 n +0000126509 00000 n +0000126583 00000 n +0000126657 00000 n +0000126731 00000 n +0000126805 00000 n +0000126879 00000 n +0000126953 00000 n +0000127029 00000 n +0000127265 00000 n +0000127371 00000 n +0000127447 00000 n +0000127521 00000 n +0000127595 00000 n +0000127735 00000 n +0000127809 00000 n +0000127883 00000 n +0000127957 00000 n +0000128031 00000 n +0000128105 00000 n +0000128179 00000 n +0000128253 00000 n +0000128421 00000 n +0000128495 00000 n +0000128583 00000 n +0000128694 00000 n +0000128793 00000 n +0000128867 00000 n +0000128941 00000 n +0000129015 00000 n +0000129089 00000 n +0000129163 00000 n +0000129319 00000 n +0000129395 00000 n +0000129540 00000 n +0000129616 00000 n +0000129761 00000 n +0000129837 00000 n +0000129982 00000 n +0000130058 00000 n +0000130203 00000 n +0000130291 00000 n +0000130358 00000 n +0000130495 00000 n +0000130569 00000 n +0000130698 00000 n +0000130835 00000 n +0000130972 00000 n +0000131060 00000 n +0000131197 00000 n +0000131271 00000 n +0000131400 00000 n +0000131537 00000 n +0000131674 00000 n +0000131762 00000 n +0000131899 00000 n +0000131973 00000 n +0000132102 00000 n +0000132239 00000 n +0000132376 00000 n +0000132464 00000 n +0000132601 00000 n +0000132675 00000 n +0000132804 00000 n +0000132941 00000 n +0000133078 00000 n +0000133166 00000 n +0000133303 00000 n +0000133377 00000 n +0000133506 00000 n +0000133643 00000 n +0000133780 00000 n +0000133868 00000 n +0000134005 00000 n +0000134079 00000 n +0000134208 00000 n +0000134345 00000 n +0000134482 00000 n +0000134570 00000 n +0000134707 00000 n +0000134781 00000 n +0000134910 00000 n +0000135047 00000 n +0000135184 00000 n +0000135272 00000 n +0000135409 00000 n +0000135483 00000 n +0000135612 00000 n +0000135749 00000 n +0000135886 00000 n +0000135974 00000 n +0000136111 00000 n +0000136185 00000 n +0000136314 00000 n +0000136451 00000 n +0000136588 00000 n +0000136676 00000 n +0000136813 00000 n +0000136887 00000 n +0000137016 00000 n +0000137153 00000 n +0000137290 00000 n +0000137378 00000 n +0000137515 00000 n +0000137589 00000 n +0000137718 00000 n +0000137855 00000 n +0000137992 00000 n +0000138080 00000 n +0000138227 00000 n +0000138359 00000 n +0000138433 00000 n +0000138509 00000 n +0000138583 00000 n +0000138657 00000 n +0000138731 00000 n +0000138805 00000 n +0000138879 00000 n +0000139063 00000 n +0000139137 00000 n +0000139211 00000 n +0000139285 00000 n +0000139359 00000 n +0000139433 00000 n +0000139507 00000 n +0000139581 00000 n +0000139749 00000 n +0000139853 00000 n +0000139956 00000 n +0000140079 00000 n +0000140158 00000 n +0000140237 00000 n +0000140328 00000 n +0000140384 00000 n +0000140479 00000 n +0000140582 00000 n +0000140685 00000 n +0000140776 00000 n +0000140903 00000 n +0000141014 00000 n +0000141120 00000 n +0000141199 00000 n +0000141272 00000 n +0000141328 00000 n +0000141401 00000 n +0000141474 00000 n +0000141702 00000 n +0000141775 00000 n +0000141848 00000 n +0000141921 00000 n +0000141994 00000 n +0000142069 00000 n +0000142142 00000 n +0000142291 00000 n +0000142364 00000 n +0000142437 00000 n +0000142510 00000 n +0000142583 00000 n +0000142656 00000 n +0000142815 00000 n +0000142924 00000 n +0000142997 00000 n +0000143100 00000 n +0000143173 00000 n +0000143278 00000 n +0000143352 00000 n +0000143426 00000 n +0000143500 00000 n +0000143632 00000 n +0000143712 00000 n +0000143786 00000 n +0000143890 00000 n +0000143991 00000 n +0000144067 00000 n +0000144183 00000 n +0000144254 00000 n +0000144325 00000 n +0000144396 00000 n +0000144467 00000 n +0000144538 00000 n +0000144609 00000 n +0000144680 00000 n +0000144795 00000 n +0000144938 00000 n +0000145121 00000 n +0000145196 00000 n +0000145375 00000 n +0000145470 00000 n +0000145565 00000 n +0000145644 00000 n +0000145719 00000 n +0000145790 00000 n +0000146018 00000 n +0000146092 00000 n +0000146166 00000 n +0000146240 00000 n +0000146368 00000 n +0000146442 00000 n +0000146516 00000 n +0000146590 00000 n +0000146664 00000 n +0000146792 00000 n +0000146866 00000 n +0000146940 00000 n +0000147014 00000 n +0000147088 00000 n +0000147162 00000 n +0000147236 00000 n +0000147310 00000 n +0000147502 00000 n +0000147576 00000 n +0000147652 00000 n +0000147732 00000 n +0000147806 00000 n +0000147880 00000 n +0000147954 00000 n +0000148034 00000 n +0000148108 00000 n +0000148182 00000 n +0000148262 00000 n +0000148498 00000 n +0000148596 00000 n +0000148707 00000 n +0000148814 00000 n +0000148921 00000 n +0000149028 00000 n +0000149135 00000 n +0000149242 00000 n +0000149349 00000 n +0000149467 00000 n +0000149575 00000 n +0000149681 00000 n +0000149787 00000 n +0000149893 00000 n +0000149999 00000 n +0000150105 00000 n +0000150211 00000 n +0000150319 00000 n +0000150425 00000 n +0000150535 00000 n +0000150642 00000 n +0000150752 00000 n +0000150859 00000 n +0000150972 00000 n +0000151079 00000 n +0000151189 00000 n +0000151296 00000 n +0000151406 00000 n +0000151513 00000 n +0000151620 00000 n +0000151727 00000 n +0000151834 00000 n +0000151947 00000 n +0000152056 00000 n +0000152163 00000 n +0000152270 00000 n +0000152377 00000 n +0000152484 00000 n +0000152591 00000 n +0000152698 00000 n +0000152805 00000 n +0000152914 00000 n +0000153024 00000 n +0000153131 00000 n +0000153244 00000 n +0000153351 00000 n +0000153461 00000 n +0000153568 00000 n +0000153678 00000 n +0000153787 00000 n +0000153894 00000 n +0000154001 00000 n +0000154108 00000 n +0000154215 00000 n +0000154322 00000 n +0000154429 00000 n +0000154536 00000 n +0000154643 00000 n +0000154750 00000 n +0000154858 00000 n +0000154966 00000 n +0000155074 00000 n +0000155182 00000 n +0000155290 00000 n +0000155398 00000 n +0000155506 00000 n +0000155614 00000 n +0000155722 00000 n +0000155830 00000 n +0000155938 00000 n +0000156046 00000 n +0000156154 00000 n +0000156262 00000 n +0000156370 00000 n +0000156478 00000 n +0000156586 00000 n +0000156694 00000 n +0000156802 00000 n +0000156910 00000 n +0000157018 00000 n +0000157126 00000 n +0000157234 00000 n +0000157342 00000 n +0000157450 00000 n +0000157558 00000 n +0000157666 00000 n +0000157774 00000 n +0000157882 00000 n +0000157990 00000 n +0000158098 00000 n +0000158206 00000 n +0000158314 00000 n +0000158422 00000 n +0000158530 00000 n +0000158638 00000 n +0000158746 00000 n +0000158855 00000 n +0000158964 00000 n +0000159073 00000 n +0000159182 00000 n +0000159291 00000 n +0000159400 00000 n +0000159509 00000 n +0000159618 00000 n +0000159727 00000 n +0000159836 00000 n +0000159945 00000 n +0000160054 00000 n +0000160163 00000 n +0000160272 00000 n +0000160381 00000 n +0000160490 00000 n +0000160599 00000 n +0000160708 00000 n +0000160817 00000 n +0000160926 00000 n +0000161035 00000 n +0000161144 00000 n +0000161253 00000 n +0000161366 00000 n +0000161475 00000 n +0000161588 00000 n +0000161697 00000 n +0000161806 00000 n +0000161915 00000 n +0000162028 00000 n +0000162137 00000 n +0000162246 00000 n +0000163351 00000 n +0000163427 00000 n +0000163536 00000 n +0000163612 00000 n +0000163688 00000 n +0000163803 00000 n +0000163879 00000 n +0000164009 00000 n +0000164085 00000 n +0000164161 00000 n +0000164237 00000 n +0000164313 00000 n +0000164389 00000 n +0000164465 00000 n +0000164541 00000 n +0000164713 00000 n +0000164791 00000 n +0000164867 00000 n +0000164943 00000 n +0000165021 00000 n +0000165194 00000 n +0000165284 00000 n +0000165409 00000 n +0000165506 00000 n +0000165603 00000 n +0000165661 00000 n +0000165758 00000 n +0000165855 00000 n +0000165952 00000 n +0000166049 00000 n +0000166146 00000 n +0000166243 00000 n +0000166340 00000 n +0000166437 00000 n +0000166534 00000 n +0000166611 00000 n +0000166669 00000 n +0000166794 00000 n +0000166891 00000 n +0000166988 00000 n +0000167085 00000 n +0000167182 00000 n +0000167279 00000 n +0000167337 00000 n +0000167434 00000 n +0000167531 00000 n +0000167628 00000 n +0000167705 00000 n +0000168032 00000 n +0000168108 00000 n +0000168184 00000 n +0000168283 00000 n +0000168359 00000 n +0000168435 00000 n +0000168511 00000 n +0000168587 00000 n +0000168663 00000 n +0000168739 00000 n +0000168815 00000 n +0000168891 00000 n +0000168967 00000 n +0000169043 00000 n +0000169119 00000 n +0000169195 00000 n +0000169271 00000 n +0000169347 00000 n +0000169423 00000 n +0000169499 00000 n +0000169575 00000 n +0000169925 00000 n +0000170001 00000 n +0000170077 00000 n +0000170153 00000 n +0000170229 00000 n +0000170305 00000 n +0000170381 00000 n +0000170457 00000 n +0000170533 00000 n +0000170886 00000 n +0000170960 00000 n +0000171034 00000 n +0000171130 00000 n +0000171205 00000 n +0000171282 00000 n +0000171357 00000 n +0000171432 00000 n +0000171507 00000 n +0000171582 00000 n +0000171657 00000 n +0000171732 00000 n +0000171807 00000 n +0000171882 00000 n +0000171957 00000 n +0000172032 00000 n +0000172107 00000 n +0000172182 00000 n +0000172455 00000 n +0000172579 00000 n +0000172654 00000 n +0000172729 00000 n +0000172804 00000 n +0000172879 00000 n +0000172954 00000 n +0000173029 00000 n +0000173104 00000 n +0000173299 00000 n +0000173374 00000 n +0000173467 00000 n +0000173542 00000 n +0000173617 00000 n +0000173692 00000 n +0000173767 00000 n +0000173843 00000 n +0000173919 00000 n +0000173997 00000 n +0000174180 00000 n +0000174256 00000 n +0000174332 00000 n +0000174443 00000 n +0000174551 00000 n +0000174680 00000 n +0000174793 00000 n +0000174938 00000 n +0000175039 00000 n +0000175148 00000 n +0000175249 00000 n +0000175338 00000 n +0000175419 00000 n +0000175516 00000 n +0000175641 00000 n +0000175730 00000 n +0000175819 00000 n +0000175932 00000 n +0000176029 00000 n +0000176110 00000 n +0000176191 00000 n +0000176268 00000 n +0000176341 00000 n +0000176596 00000 n +0000176672 00000 n +0000176748 00000 n +0000176828 00000 n +0000176906 00000 n +0000176982 00000 n +0000177058 00000 n +0000177134 00000 n +0000177210 00000 n +0000177286 00000 n +0000177364 00000 n +0000177440 00000 n +0000177680 00000 n +0000177756 00000 n +0000177832 00000 n +0000177947 00000 n +0000178023 00000 n +0000178099 00000 n +0000178175 00000 n +0000178251 00000 n +0000178327 00000 n +0000178403 00000 n +0000178479 00000 n +0000178555 00000 n +0000178631 00000 n +0000178707 00000 n +0000178783 00000 n +0000178865 00000 n +0000178941 00000 n +0000179017 00000 n +0000179093 00000 n +0000179397 00000 n +0000179473 00000 n +0000179549 00000 n +0000179625 00000 n +0000179701 00000 n +0000179777 00000 n +0000179853 00000 n +0000179933 00000 n +0000180009 00000 n +0000180214 00000 n +0000180290 00000 n +0000180366 00000 n +0000180442 00000 n +0000180518 00000 n +0000180659 00000 n +0000180735 00000 n +0000180861 00000 n +0000180937 00000 n +0000181013 00000 n +0000181089 00000 n +0000181165 00000 n +0000181241 00000 n +0000181321 00000 n +0000181397 00000 n +0000181477 00000 n +0000181654 00000 n +0000181730 00000 n +0000181810 00000 n +0000181886 00000 n +0000181962 00000 n +0000182303 00000 n +0000182377 00000 n +0000182452 00000 n +0000182527 00000 n +0000182602 00000 n +0000182677 00000 n +0000182752 00000 n +0000182827 00000 n +0000182902 00000 n +0000182977 00000 n +0000183172 00000 n +0000183247 00000 n +0000183322 00000 n +0000183397 00000 n +0000183520 00000 n +0000183609 00000 n +0000183712 00000 n +0000183787 00000 n +0000183862 00000 n +0000183937 00000 n +0000184012 00000 n +0000184087 00000 n +0000184162 00000 n +0000184237 00000 n +0000184312 00000 n +0000184495 00000 n +0000184570 00000 n +0000184645 00000 n +0000184720 00000 n +0000184795 00000 n +0000184870 00000 n +0000184945 00000 n +0000185020 00000 n +0000185095 00000 n +0000185302 00000 n +0000185378 00000 n +0000185454 00000 n +0000185530 00000 n +0000185606 00000 n +0000185770 00000 n +0000185850 00000 n +0000185926 00000 n +0000186037 00000 n +0000186113 00000 n +0000186189 00000 n +0000186265 00000 n +0000186341 00000 n +0000186417 00000 n +0000186493 00000 n +0000186569 00000 n +0000186645 00000 n +0000186858 00000 n +0000186970 00000 n +0000187046 00000 n +0000187122 00000 n +0000187237 00000 n +0000187310 00000 n +0000187383 00000 n +0000187456 00000 n +0000187577 00000 n +0000187650 00000 n +0000187723 00000 n +0000187796 00000 n +0000187885 00000 n +0000187994 00000 n +0000188052 00000 n +0000188177 00000 n +0000188326 00000 n +0000188475 00000 n +0000188624 00000 n +0000188773 00000 n +0000188882 00000 n +0000189003 00000 n +0000189112 00000 n +0000189233 00000 n +0000189346 00000 n +0000189455 00000 n +0000189576 00000 n +0000189649 00000 n +0000189949 00000 n +0000190025 00000 n +0000190101 00000 n +0000190177 00000 n +0000190317 00000 n +0000190395 00000 n +0000190485 00000 n +0000190560 00000 n +0000190636 00000 n +0000190712 00000 n +0000190790 00000 n +0000190866 00000 n +0000190942 00000 n +0000191018 00000 n +0000191193 00000 n +0000191269 00000 n +0000191344 00000 n +0000191420 00000 n +0000191496 00000 n +0000191574 00000 n +0000191650 00000 n +0000191726 00000 n +0000191884 00000 n +0000191960 00000 n +0000192035 00000 n +0000192111 00000 n +0000192189 00000 n +0000192265 00000 n +0000192341 00000 n +0000192478 00000 n +0000192554 00000 n +0000192629 00000 n +0000192705 00000 n +0000192781 00000 n +0000192857 00000 n +0000192989 00000 n +0000193065 00000 n +0000193140 00000 n +0000193216 00000 n +0000193294 00000 n +0000193370 00000 n +0000193502 00000 n +0000193578 00000 n +0000193716 00000 n +0000193825 00000 n +0000193934 00000 n +0000194047 00000 n +0000194156 00000 n +0000194265 00000 n +0000194374 00000 n +0000194483 00000 n +0000194592 00000 n +0000194701 00000 n +0000194810 00000 n +0000194931 00000 n +0000195093 00000 n +0000195185 00000 n +0000195383 00000 n +0000195581 00000 n +0000195656 00000 n +0000195880 00000 n +0000196105 00000 n +0000196358 00000 n +0000196435 00000 n +0000196600 00000 n +0000196677 00000 n +0000196753 00000 n +0000196829 00000 n +0000196905 00000 n +0000196981 00000 n +0000197122 00000 n +0000197198 00000 n +0000197274 00000 n +0000197350 00000 n +0000197426 00000 n +0000197502 00000 n +0000197578 00000 n +0000197789 00000 n +0000197899 00000 n +0000197975 00000 n +0000198051 00000 n +0000198127 00000 n +0000198203 00000 n +0000198279 00000 n +0000198355 00000 n +0000198431 00000 n +0000198631 00000 n +0000198707 00000 n +0000198783 00000 n +0000198859 00000 n +0000198935 00000 n +0000199080 00000 n +0000199156 00000 n +0000199232 00000 n +0000199308 00000 n +0000199384 00000 n +0000199460 00000 n +0000199536 00000 n +0000199651 00000 n +0000199727 00000 n +0000199944 00000 n +0000200020 00000 n +0000200098 00000 n +0000200174 00000 n +0000200250 00000 n +0000200326 00000 n +0000200402 00000 n +0000200569 00000 n +0000200673 00000 n +0000200783 00000 n +0000200859 00000 n +0000200935 00000 n +0000201011 00000 n +0000201087 00000 n +0000201163 00000 n +0000201239 00000 n +0000201315 00000 n +0000201391 00000 n +0000201467 00000 n +0000201543 00000 n +0000201770 00000 n +0000201846 00000 n +0000201922 00000 n +0000201998 00000 n +0000202074 00000 n +0000202150 00000 n +0000202230 00000 n +0000202409 00000 n +0000202485 00000 n +0000202561 00000 n +0000202637 00000 n +0000202713 00000 n +0000202789 00000 n +0000202865 00000 n +0000202941 00000 n +0000203137 00000 n +0000203213 00000 n +0000203289 00000 n +0000203365 00000 n +0000203441 00000 n +0000203517 00000 n +0000203785 00000 n +0000203861 00000 n +0000203937 00000 n +0000204036 00000 n +0000204135 00000 n +0000204211 00000 n +0000204287 00000 n +0000204363 00000 n +0000204439 00000 n +0000204519 00000 n +0000204595 00000 n +0000204671 00000 n +0000204851 00000 n +0000204927 00000 n +0000205003 00000 n +0000205079 00000 n +0000205155 00000 n +0000205288 00000 n +0000205364 00000 n +0000205440 00000 n +0000205516 00000 n +0000205592 00000 n +0000205753 00000 n +0000205840 00000 n +0000205949 00000 n +0000206058 00000 n +0000206171 00000 n +0000206280 00000 n +0000206389 00000 n +0000206500 00000 n +0000206613 00000 n +0000206722 00000 n +0000206831 00000 n +0000206975 00000 n +0000207084 00000 n +0000207191 00000 n +0000207298 00000 n +0000207405 00000 n +0000207512 00000 n +0000207619 00000 n +0000207726 00000 n +0000207833 00000 n +0000207942 00000 n +0000208050 00000 n +0000208161 00000 n +0000208269 00000 n +0000208377 00000 n +0000208485 00000 n +0000208593 00000 n +0000208701 00000 n +0000208809 00000 n +0000208917 00000 n +0000209025 00000 n +0000209133 00000 n +0000209241 00000 n +0000209349 00000 n +0000209457 00000 n +0000209565 00000 n +0000209673 00000 n +0000209781 00000 n +0000209889 00000 n +0000209997 00000 n +0000210105 00000 n +0000210213 00000 n +0000210321 00000 n +0000210429 00000 n +0000210537 00000 n +0000210645 00000 n +0000210753 00000 n +0000210861 00000 n +0000210969 00000 n +0000211077 00000 n +0000211185 00000 n +0000211293 00000 n +0000211401 00000 n +0000211509 00000 n +0000211617 00000 n +0000211727 00000 n +0000211835 00000 n +0000211943 00000 n +0000212051 00000 n +0000212159 00000 n +0000212267 00000 n +0000212378 00000 n +0000212486 00000 n +0000212594 00000 n +0000212702 00000 n +0000212810 00000 n +0000212918 00000 n +0000213026 00000 n +0000213134 00000 n +0000213242 00000 n +0000213350 00000 n +0000213458 00000 n +0000213566 00000 n +0000213674 00000 n +0000213782 00000 n +0000213890 00000 n +0000213998 00000 n +0000214106 00000 n +0000214214 00000 n +0000214322 00000 n +0000214430 00000 n +0000214538 00000 n +0000214646 00000 n +0000214754 00000 n +0000214862 00000 n +0000214970 00000 n +0000215078 00000 n +0000215186 00000 n +0000215294 00000 n +0000215402 00000 n +0000215510 00000 n +0000215618 00000 n +0000215726 00000 n +0000215834 00000 n +0000215942 00000 n +0000216050 00000 n +0000216158 00000 n +0000216266 00000 n +0000216374 00000 n +0000216482 00000 n +0000216590 00000 n +0000216698 00000 n +0000216806 00000 n +0000216914 00000 n +0000217028 00000 n +0000217136 00000 n +0000217246 00000 n +0000217355 00000 n +0000217464 00000 n +0000217573 00000 n +0000217682 00000 n +0000217791 00000 n +0000217900 00000 n +0000218009 00000 n +0000218118 00000 n +0000218227 00000 n +0000218336 00000 n +0000218445 00000 n +0000218554 00000 n +0000218663 00000 n +0000218772 00000 n +0000218881 00000 n +0000218990 00000 n +0000219099 00000 n +0000219208 00000 n +0000219317 00000 n +0000219426 00000 n +0000219535 00000 n +0000219644 00000 n +0000219757 00000 n +0000219866 00000 n +0000219979 00000 n +0000220088 00000 n +0000220197 00000 n +0000220306 00000 n +0000220415 00000 n +0000221623 00000 n +0000221700 00000 n +0000221814 00000 n +0000221890 00000 n +0000221966 00000 n +0000222042 00000 n +0000222118 00000 n +0000222194 00000 n +0000222270 00000 n +0000222346 00000 n +0000222534 00000 n +0000222610 00000 n +0000222686 00000 n +0000222766 00000 n +0000222842 00000 n +0000222918 00000 n +0000222998 00000 n +0000223076 00000 n +0000223152 00000 n +0000223228 00000 n +0000223304 00000 n +0000223380 00000 n +0000223456 00000 n +0000223532 00000 n +0000223798 00000 n +0000223874 00000 n +0000223950 00000 n +0000224030 00000 n +0000224106 00000 n +0000224182 00000 n +0000224258 00000 n +0000224334 00000 n +0000224410 00000 n +0000224486 00000 n +0000224562 00000 n +0000224640 00000 n +0000224716 00000 n +0000224792 00000 n +0000224872 00000 n +0000225167 00000 n +0000225243 00000 n +0000225319 00000 n +0000225395 00000 n +0000225471 00000 n +0000225547 00000 n +0000225623 00000 n +0000225699 00000 n +0000225775 00000 n +0000225851 00000 n +0000226077 00000 n +0000226151 00000 n +0000226225 00000 n +0000226301 00000 n +0000226376 00000 n +0000226505 00000 n +0000226601 00000 n +0000226676 00000 n +0000226751 00000 n +0000226826 00000 n +0000226901 00000 n +0000226976 00000 n +0000227051 00000 n +0000227213 00000 n +0000227288 00000 n +0000227363 00000 n +0000227471 00000 n +0000227546 00000 n +0000227621 00000 n +0000227696 00000 n +0000227771 00000 n +0000227846 00000 n +0000228023 00000 n +0000228098 00000 n +0000228173 00000 n +0000228248 00000 n +0000228323 00000 n +0000228398 00000 n +0000228473 00000 n +0000228635 00000 n +0000228710 00000 n +0000228800 00000 n +0000228890 00000 n +0000228962 00000 n +0000229034 00000 n +0000229146 00000 n +0000229219 00000 n +0000229360 00000 n +0000229505 00000 n +0000229643 00000 n +0000229719 00000 n +0000229795 00000 n +0000229871 00000 n +0000229999 00000 n +0000230072 00000 n +0000230201 00000 n +0000230326 00000 n +0000230427 00000 n +0000230485 00000 n +0000230558 00000 n +0000230695 00000 n +0000230868 00000 n +0000230941 00000 n +0000231078 00000 n +0000231261 00000 n +0000231337 00000 n +0000231413 00000 n +0000231489 00000 n +0000231609 00000 n +0000231685 00000 n +0000231765 00000 n +0000231841 00000 n +0000231917 00000 n +0000231993 00000 n +0000232069 00000 n +0000232145 00000 n +0000232221 00000 n +0000232422 00000 n +0000232498 00000 n +0000232574 00000 n +0000232650 00000 n +0000232726 00000 n +0000232802 00000 n +0000232976 00000 n +0000233052 00000 n +0000233128 00000 n +0000233204 00000 n +0000233280 00000 n +0000233356 00000 n +0000233436 00000 n +0000233512 00000 n +0000233588 00000 n +0000233664 00000 n +0000233740 00000 n +0000233816 00000 n +0000233892 00000 n +0000233968 00000 n +0000234044 00000 n +0000234339 00000 n +0000234459 00000 n +0000234539 00000 n +0000234615 00000 n +0000234693 00000 n +0000234773 00000 n +0000234853 00000 n +0000234929 00000 n +0000235005 00000 n +0000235193 00000 n +0000235269 00000 n +0000235345 00000 n +0000235421 00000 n +0000235541 00000 n +0000235614 00000 n +0000235715 00000 n +0000235791 00000 n +0000235869 00000 n +0000235943 00000 n +0000236290 00000 n +0000236388 00000 n +0000236463 00000 n +0000236538 00000 n +0000236613 00000 n +0000236688 00000 n +0000236763 00000 n +0000236838 00000 n +0000236913 00000 n +0000236988 00000 n +0000237063 00000 n +0000237138 00000 n +0000237213 00000 n +0000237288 00000 n +0000237363 00000 n +0000237438 00000 n +0000237513 00000 n +0000237588 00000 n +0000237663 00000 n +0000237738 00000 n +0000237813 00000 n +0000237888 00000 n +0000237963 00000 n +0000238038 00000 n +0000238116 00000 n +0000238194 00000 n +0000238269 00000 n +0000238344 00000 n +0000238761 00000 n +0000238869 00000 n +0000238944 00000 n +0000239022 00000 n +0000239139 00000 n +0000239247 00000 n +0000239319 00000 n +0000239394 00000 n +0000239499 00000 n +0000239580 00000 n +0000239661 00000 n +0000239759 00000 n +0000239835 00000 n +0000239911 00000 n +0000239987 00000 n +0000240063 00000 n +0000240139 00000 n +0000240215 00000 n +0000240291 00000 n +0000240367 00000 n +0000240447 00000 n +0000240523 00000 n +0000240762 00000 n +0000240838 00000 n +0000240914 00000 n +0000240990 00000 n +0000241118 00000 n +0000241203 00000 n +0000241290 00000 n +0000241366 00000 n +0000241442 00000 n +0000241553 00000 n +0000241654 00000 n +0000241743 00000 n +0000241852 00000 n +0000241930 00000 n +0000242045 00000 n +0000242164 00000 n +0000242240 00000 n +0000242316 00000 n +0000242427 00000 n +0000242503 00000 n +0000242579 00000 n +0000242698 00000 n +0000242812 00000 n +0000242888 00000 n +0000242994 00000 n +0000243072 00000 n +0000243148 00000 n +0000243224 00000 n +0000243300 00000 n +0000243376 00000 n +0000243452 00000 n +0000243655 00000 n +0000243733 00000 n +0000243807 00000 n +0000243881 00000 n +0000243955 00000 n +0000244030 00000 n +0000244105 00000 n +0000244180 00000 n +0000244255 00000 n +0000244316 00000 n +0000244391 00000 n +0000244452 00000 n +0000244527 00000 n +0000245919 00000 n +0000245996 00000 n +0000246074 00000 n +0000246149 00000 n +0000246224 00000 n +0000246299 00000 n +0000246374 00000 n +0000246449 00000 n +0000246527 00000 n +0000246602 00000 n +0000246677 00000 n +0000246752 00000 n +0000246827 00000 n +0000246902 00000 n +0000246977 00000 n +0000247052 00000 n +0000247127 00000 n +0000247202 00000 n +0000247277 00000 n +0000247352 00000 n +0000247427 00000 n +0000247502 00000 n +0000247577 00000 n +0000247652 00000 n +0000247727 00000 n +0000247803 00000 n +0000247879 00000 n +0000247955 00000 n +0000248035 00000 n +0000248111 00000 n +0000248187 00000 n +0000248263 00000 n +0000248339 00000 n +0000248415 00000 n +0000248476 00000 n +0000248552 00000 n +0000248628 00000 n +0000248704 00000 n +0000248765 00000 n +0000248841 00000 n +0000248921 00000 n +0000248997 00000 n +0000249073 00000 n +0000249149 00000 n +0000249225 00000 n +0000249301 00000 n +0000249377 00000 n +0000249453 00000 n +0000249529 00000 n +0000249605 00000 n +0000249681 00000 n +0000249757 00000 n +0000249833 00000 n +0000249909 00000 n +0000249989 00000 n +0000250065 00000 n +0000250141 00000 n +0000250217 00000 n +0000250293 00000 n +0000250369 00000 n +0000250445 00000 n +0000251334 00000 n +0000251412 00000 n +0000251488 00000 n +0000251564 00000 n +0000251640 00000 n +0000251716 00000 n +0000251796 00000 n +0000251876 00000 n +0000251956 00000 n +0000252157 00000 n +0000252235 00000 n +0000252311 00000 n +0000252387 00000 n +0000252463 00000 n +0000252652 00000 n +0000252730 00000 n +0000252806 00000 n +0000252882 00000 n +0000252958 00000 n +0000253034 00000 n +0000253110 00000 n +0000253186 00000 n +0000253262 00000 n +0000253338 00000 n +0000253414 00000 n +0000253490 00000 n +0000253566 00000 n +0000253642 00000 n +0000253908 00000 n +0000253986 00000 n +0000254062 00000 n +0000254138 00000 n +0000254214 00000 n +0000254290 00000 n +0000254366 00000 n +0000254446 00000 n +0000254522 00000 n +0000254598 00000 n +0000254674 00000 n +0000254750 00000 n +0000254826 00000 n +0000254902 00000 n +0000254978 00000 n +0000255054 00000 n +0000255134 00000 n +0000255210 00000 n +0000255286 00000 n +0000255362 00000 n +0000255438 00000 n +0000255899 00000 n +0000255977 00000 n +0000256053 00000 n +0000256133 00000 n +0000256209 00000 n +0000256285 00000 n +0000256361 00000 n +0000256441 00000 n +0000256515 00000 n +0000256589 00000 n +0000256665 00000 n +0000257082 00000 n +0000257159 00000 n +0000257234 00000 n +0000257312 00000 n +0000257387 00000 n +0000257464 00000 n +0000257665 00000 n +0000257742 00000 n +0000257820 00000 n +0000257895 00000 n +0000257970 00000 n +0000258045 00000 n +0000258120 00000 n +0000258195 00000 n +0000258270 00000 n +0000258453 00000 n +0000260678 00000 n +0000260725 00000 n +0000260772 00000 n +0000260819 00000 n +0000260866 00000 n +0000260913 00000 n +0000260960 00000 n +0000261007 00000 n +0000261054 00000 n +0000261101 00000 n +0000261149 00000 n +0000261197 00000 n +0000261245 00000 n +0000261293 00000 n +0000261341 00000 n +0000261389 00000 n +0000261437 00000 n +0000261589 00000 n +0000261748 00000 n +0000261909 00000 n +0000262068 00000 n +0000262226 00000 n +0000262381 00000 n +0000262534 00000 n +0000262690 00000 n +0000262729 00000 n +0000262768 00000 n +0000262823 00000 n +0000262877 00000 n +0000262932 00000 n +0000262987 00000 n +0000263041 00000 n +0000263096 00000 n +0000263151 00000 n +0000263206 00000 n +0000263261 00000 n +0000263316 00000 n +0000263370 00000 n +0000263425 00000 n +0000263479 00000 n +0000263534 00000 n +0000263588 00000 n +0000263642 00000 n +0000263697 00000 n +0000263752 00000 n +0000263807 00000 n +0000263861 00000 n +0000263915 00000 n +0000263970 00000 n +0000264025 00000 n +0000264080 00000 n +0000264135 00000 n +0000264190 00000 n +0000264245 00000 n +0000264300 00000 n +0000264355 00000 n +0000264410 00000 n +0000264464 00000 n +0000264517 00000 n +0000264572 00000 n +0000264627 00000 n +0000264682 00000 n +0000264737 00000 n +0000264792 00000 n +0000264845 00000 n +0000264899 00000 n +0000264954 00000 n +0000265008 00000 n +0000265062 00000 n +0000265117 00000 n +0000265172 00000 n +0000265226 00000 n +0000265281 00000 n +0000265336 00000 n +0000265390 00000 n +0000265445 00000 n +0000265499 00000 n +0000265554 00000 n +0000265608 00000 n +0000265663 00000 n +0000265718 00000 n +0000265858 00000 n +0000266010 00000 n +0000266174 00000 n +0000266314 00000 n +0000266478 00000 n +0000266618 00000 n +0000266770 00000 n +0000266934 00000 n +0000267098 00000 n +0000267250 00000 n +0000267402 00000 n +0000267530 00000 n +0000267670 00000 n +0000267810 00000 n +0000267938 00000 n +0000268066 00000 n +0000268218 00000 n +0000268953 00000 n +0000269165 00000 n +0000269320 00000 n +0000270337 00000 n +0000270551 00000 n +0000270694 00000 n +0000270912 00000 n +0000271154 00000 n +0000271302 00000 n +0000271525 00000 n +0000271773 00000 n +0000271931 00000 n +0000272346 00000 n +0000272572 00000 n +0000272726 00000 n +0000273305 00000 n +0000273526 00000 n +0000273680 00000 n +0000273911 00000 n +0000274122 00000 n +0000274285 00000 n +0000274509 00000 n +0000274725 00000 n +0000274889 00000 n +0000275053 00000 n +0000275208 00000 n +0000275339 00000 n +0000275495 00000 n +0000275627 00000 n +0000275759 00000 n +0000275891 00000 n +0000276023 00000 n +0000276155 00000 n +0000276311 00000 n +0000276443 00000 n +0000276575 00000 n +0000276731 00000 n +0000276863 00000 n +0000276995 00000 n +0000277078 00000 n +0000277788 00000 n +0000283962 00000 n +0000284045 00000 n +0000284892 00000 n +0000297615 00000 n +0000297698 00000 n +0000298553 00000 n +0000313297 00000 n +0000313378 00000 n +0000313922 00000 n +0000319941 00000 n +0000320023 00000 n +0000320575 00000 n +0000323810 00000 n +0000323894 00000 n +0000324519 00000 n +0000329668 00000 n +0000329746 00000 n +0000330209 00000 n +0000331394 00000 n +0000331472 00000 n +0000331918 00000 n +0000332503 00000 n +0000343401 00000 n +0000356679 00000 n +0000369966 00000 n +0000384296 00000 n +0000397464 00000 n +0000409848 00000 n +0000422222 00000 n +0000434551 00000 n +0000448126 00000 n +0000461316 00000 n +0000474994 00000 n +0000486957 00000 n +0000500159 00000 n +0000511590 00000 n +0000526141 00000 n +0000530338 00000 n +0000530682 00000 n +0000531090 00000 n +0000531204 00000 n +0000532279 00000 n trailer -<> +<> startxref -131169 +532602 %%EOF \ No newline at end of file diff --git a/docs/design.typ b/docs/design.typ index 8b93614e..d322920a 100644 --- a/docs/design.typ +++ b/docs/design.typ @@ -1,54 +1,144 @@ -#set page(margin: 2.2cm) -#set text(font: "New Computer Modern", size: 10.5pt) -#set par(justify: true) -#show heading: set block(above: 1.4em, below: 0.8em) +// Diagrams come from cetz, which is the one thing here that is genuinely +// painful to hand-roll. Pinned to the version in the local package cache so +// this document builds offline; `typst compile docs/design.typ docs/design.pdf` +// must never need the network, because the PDF it produces is a test fixture +// (src/pdf.zig and src/pdf_pane*.zig open it, and `zig build mupdf-check` +// renders and searches it). +#import "@preview/cetz:0.5.1" -#align(center)[ - #text(17pt)[*Pardes: A Text Environment*] +#set page(paper: "a4", margin: (x: 1.5cm, y: 1.8cm), columns: 2, numbering: "1") +#set text(font: "New Computer Modern", size: 8.8pt) +#set par(justify: true, leading: 0.55em) +#set heading(numbering: "1.1") +#show heading: set block(above: 1.15em, below: 0.6em) +#show heading.where(level: 1): set text(size: 10.2pt) +#show heading.where(level: 2): set text(size: 9.2pt) +#show heading.where(level: 3): set text(size: 8.8pt, style: "italic") - #text(11pt)[design for the rewrite — 2026-07-05] +// Listings are styled NATIVELY rather than through a package. codly is the +// obvious choice and the cached 1.2.0 does not compile under typst 0.15 — it +// still calls the pre-0.13 `pattern` — and a document that cannot be rebuilt is +// worse than one with plainer listings. Six lines buy independence from that. +#show raw.where(block: true): it => block( + width: 100%, + fill: luma(246), + inset: (x: 0.5em, y: 0.45em), + radius: 1pt, + breakable: true, + text(size: 7.2pt, it), +) +#show raw.where(block: false): set text(size: 8.1pt) + +#show figure.caption: set text(size: 7.6pt) +#set figure(gap: 0.55em) +#set table(stroke: 0.4pt, inset: 0.35em) + +/// A figure that spans BOTH columns, for the diagrams and listings that will +/// not survive being folded into 8 cm. Floats to the top of a page, which is +/// where a reader expects a wide figure to be. +#let wide(caption: none, body) = place( + top, + scope: "parent", + float: true, + clearance: 1.2em, + figure(body, caption: caption), +) + +/// Diagram labels are code more often than they are prose, and the inline-raw +/// rule above sizes for body text. Every canvas below is wrapped in this so a +/// symbol name inside a box does not outgrow the box. +#let diagram(body) = [ + #show raw.where(block: false): set text(size: 5.9pt) + #body +] + +#place(top + center, scope: "parent", float: true, clearance: 1.5em)[ + #text(16pt)[*Pardes: A Text Environment*] + + #text(8.8pt)[Architecture and implementation of the rewrite] +] + +#place(top + center, scope: "parent", float: true, clearance: 1.4em)[ + #block(width: 82%, inset: (x: 0pt))[ + #set par(justify: true, leading: 0.52em) + #text(8.2pt)[ + *Abstract.* Pardes is a text environment in the acme tradition: columns of + panes, each pane a tag line plus a body, the body a live terminal, a file, + an image or a PDF. It is one program with five thin shells — a terminal + over libvaxis, an SDL3 window, a browser tab driven by vanilla JavaScript, + an AppKit application, and an ESP32-P4 microcontroller — where the + prototype had three parallel implementations of one editor. Two seams + carry that. Outward, the core is a state machine over plain values: + `Event` in, `Surface` and a queue of `Effect` out, and nothing that cannot + be said in those types exists in pardes. Downward, `host.VTable` is + twenty-one optional function pointers, every null one answered in-process, + so the zero-method host is both the test harness and the browser. A + session can also be a daemon: the core keeps the ptys and the disk and N + frontends carry only a screen, a keyboard and a clipboard. + ] + ] ] = What it is -Pardes is a text environment in the acme tradition: columns of panes, each pane a -tag line plus a body; the body is a live terminal, a file, an image, or a PDF. The -mouse carries meaning — left selects, middle executes, right looks. Everything on -screen is text and all text is equally alive, whether the shell printed it or the -user typed it. +== The acme inheritance + +Columns of panes; each pane is a tag line plus a body; the body is a live +terminal, a file, an image, or a PDF. The mouse carries meaning — left selects, +middle executes, right looks. Everything on screen is text and all text is +equally alive, whether the shell printed it or the user typed it. -It runs in four places: the terminal (libvaxis), a native SDL3 window (the -steamdeck), the browser (a freestanding wasm core driven by vanilla -JavaScript and rendered as HTML/CSS), and a native macOS app (an AppKit and -CoreText shell over a static `libpardes.a`). The rewrite exists because -the prototype grew three parallel implementations of one program. The rewrite has -exactly one program and four thin shells. +Two acme mechanisms are reproduced rather than reinterpreted. A *dump* is the +session as data: `pardes -l state.zon` on every platform reconstructs the panes +— terminals by replaying their raw VT streams into fresh emulators, files, +images and PDFs from their bytes. Editable tag tails are stored separately from +their dynamic live prefixes, and image records retain PETSCII, palette, and +ASCII renderer choices. A PDF rides the dump's `image` kind, carrying its path, +its bytes and the page it was on. The reader validates weights, scroll ranges, +pane references, and bounded tag tails before constructing anything; invalid +base64 fails instead of silently becoming empty content. A PDF record whose path +cannot be opened — or a build without MuPDF — falls back to an ordinary file +pane with those exact embedded bytes and its original editable tail. -= The shape +That is what collapses the prototype's *two* applications into one. Its browser +build was a separate 800-line read-only replay viewer; here loading another +instance's dump is a first-class feature of the one application, and the web +shell is that application with an embedded dump and `spawn` left unanswered. +Same core, no viewer fork. + +The other acme mechanism is a control filesystem, `src/acmefs.zig` +(@fs). + +== One core, five shells Pardes is a *library*, in the way ghostty-vt is a library: you feed it bytes and events, and you read state out of it. Every platform owns its own event loop and its own renderer; the core owns everything the user would recognize as pardes. -#table( - columns: (auto, 1fr), - stroke: 0.4pt, - [*core*], [layout, panes, modes, selection, click semantics, themes, text of the - UI. Pure state machine: `update(event)` mutates, `render(arena)` returns the - surface. No rendering, no event loop, and no IO that has an effect for it — - but "no syscalls" was never true and is less true now: `look` reads a file - a Look opened, walks a directory for Find and reads every candidate for - Grep, `fonts` walks the font directories, and MuPDF opens a `.pdf`. Those - are the ones that are cheaper done in place than round-tripped through an - effect and back; everything with a lifetime — a pty, a window, the - clipboard — is still asked for.], - [*shell*], [one MODULE per platform (tty is one file; gui adds the CRT and - gamepad files, a C font loader and eight GLSL shaders; web and macOS each - add a host language). - Owns the event loop, translates native input - into core events, renders the core's surface, and performs the core's - requested effects (spawn a shell, write a pty, open a link).], -) +*The core* owns layout, panes, modes, selection, click semantics, themes, and the +text of the UI. Pure state machine: `update(event)` mutates, `render(arena)` +returns the surface. No rendering, no event loop, and no IO that has an effect +for it — but "no syscalls" was never true and is less true now: `look` reads a +file a Look opened, walks a directory for Find and reads every candidate for +Grep, `fonts` walks the font directories, and MuPDF opens a `.pdf`. Those are the +ones that are cheaper done in place than round-tripped through an effect and +back; everything with a lifetime — a pty, a window, the clipboard — is still +asked for. + +*A shell* is one MODULE per platform: tty is one file, gui adds the CRT and +gamepad files plus a C font loader and eight GLSL shaders, and web, macOS and the +board each add a host language or a host repository. It owns the event loop, +translates native input into core events, renders the core's surface, and +performs the core's requested effects — spawn a shell, write a pty, open a link. + +The five, and what each one actually is: the terminal (libvaxis); a native SDL3 +window (the steamdeck); the browser (a freestanding wasm core driven by vanilla +JavaScript and rendered as HTML/CSS); a native macOS app (an AppKit and CoreText +shell over a static `libpardes.a`); and an ESP32-P4 microcontroller, a +freestanding riscv32 *object* that the `zig_p4` package links beside its own +`_start`, linker script and UART driver (`src/esp32p4.zig` header; the +`pardes-esp32p4` object `build.zig` emits). `pardes.Platform` is +`enum { tty, gui, web, macos, esp32p4 }`. Native shells share `shell_bin`'s OSC 133 startup snippets, but not their files. Each host owns a private `mkstemp` pair for its lifetime, writes and @@ -57,206 +147,1799 @@ in child argv, and unlinks them at teardown. Concurrent tty, SDL and macOS launches therefore cannot truncate, source, or replace one another's startup files. -This also collapses the prototype's *two* applications into one: the browser -build today is a separate 800-line read-only replay viewer. In the rewrite, -loading a dump of another instance is a first-class feature of the one -application, the way acme handles dumps: `pardes -l state.zon` on every -platform reconstructs the panes—terminals by replaying their raw VT streams -into fresh emulators, files, images and PDFs from their bytes. Editable tag -tails are stored separately from their dynamic live prefixes, and image records -retain PETSCII, palette, and ASCII renderer choices. A PDF rides the dump's -`image` kind, carrying its path, its bytes and the page it was on. The web shell embeds -a dump and leaves `spawn` unanswered. The reader validates weights, scroll -ranges, pane references, and bounded tag tails before constructing anything; -invalid base64 fails instead of silently becoming empty content. A PDF record -whose path cannot be opened—or a build without MuPDF—falls back to an ordinary -file pane with those exact embedded bytes and its original editable tail. Same -core, no viewer fork. - -The boundary is two data types, both plain values: - -*Event* (in): `key` (which carries typed `text`), `mouse` (press/release/motion/ -drag; button left, middle, right and the four wheel directions; cell position; -and the `ctrl` flag, because Ctrl-left-click is goto-definition), `pinch`, -`touch_scroll`, `resize` (cols, rows, and — in a MuPDF build — the cell's pixel -size), `paste`, `pdf_scroll`, -`output` (bytes a pty produced, tagged with the pane id), `eof`, `command` (a -builtin line arriving from another process over the nested socket), `tick`, and -the answers to an ask: `lsp_resp`, `pipe_resp`, `file_changed`. Touch -policy belongs to the shell: web maps -a one-finger tap to right-button LOOK and a drag past its tap slop to natural -scrolling; native SDL keeps its two-finger gestures. A joystick cursor is a -motion plus buttons. The shells do this translation and nothing else with -input: one event vocabulary, four dialects translated at the door. - -*Surface* (out): `cols`, `rows`, a grid of cells — grapheme, fg, bg, attrs — a -cursor, pixel attachments, and a bounded list of plain panel-transition tracks. -This is the *canonical interface*: it is literally what the -tty shell hands to vaxis, cell by cell. SDL rasterizes the same grid through a -glyph atlas; the browser reads a packed copy and patches native DOM cells. If it -cannot be expressed in the surface, it does not -exist in pardes. (Pixel images ride along as a list of PLACEMENTS rather than -one per pane — a PDF pane contributes every page its viewport intersects, and -pages can be arbitrarily short. The SDL shells blit RGBA, the tty shell falls -back to the petscii matcher, kitty -graphics when available. Transition tracks identify their pane by slot and -serial and carry only phase, effect, frame, and from/to cell boxes. Canonical -layout is committed immediately; tracks are finite presentation data.) - -*Effect* (out, queued): `spawn{pane, cwd}`, `write{pane, bytes}`, -`resize_pty{pane, cols, rows}`, `open_link`, -`save_file{pane}`, `save_text{pane, serial, path}`, `write_dump`, -`set_clipboard`, `read_clipboard`, `lsp`, -`pipe`, `watch`, `quit`. -The core never performs IO for any of these; it asks — and four of the asks have an -answer coming back: `read_clipboard` returns an ordinary `paste` event (or -nothing at all when the shell cannot read the clipboard — most terminals refuse -the OSC 52 read), `lsp` returns `lsp_resp`, `pipe` returns `pipe_resp`, and -`watch` returns `file_changed` whenever the shell notices a text file or PDF -moved under it. The tty and SDL hosts currently implement that path with Linux -inotify; the native macOS host watches both each file and its parent directory -with debounced DispatchSources, then restats the exact path under a pane-generation -guard. The file catches in-place writes while the parent follows rename-over -saves. Text snapshots are filtered by their content hash; PDFs use bounded -inode/size/time identity and commit it only when equal stats bracket a -successful transactional MuPDF reopen. A mismatched transaction gets one -bounded self-retry. This catches rename-over saves without reading a large PDF -merely to notice it changed or spinning on a malformed one. Web has no -filesystem watcher. A PDF response retains its -reading position and pane settings. This is what makes the browser build honest instead of a -fork: a wasm shell simply -answers `spawn` differently (or not at all) — the core does not know. - -Platform divergence inside the core is a comptime tag, used the way the stdlib -uses `os.tag`: +== A shell need not share the process + +`pardes --detach[=name]` runs the core with no terminal of its own and +`pardes --attach[=name]` makes a frontend of it over a unix socket, so one +session can carry a terminal and an SDL window at the same time and outlive +both. `main.nativeMain` dispatches `--detach` before it switches on the +platform, because it is not a shell: the tty and gui builds can both be asked +for one. The two flags together are refused there rather than resolved by +declaration order, and so is `--fs` beside either of them — only a LOCAL session +mounts the control filesystem, so `--fs --detach` used to be parsed, stored and +served by nobody. Both flags take `=name` and never a separate word, so +`pardes --attach README` opens `README` in a fresh session instead of attaching +to one called `README`. See @detached and `docs/detached.md`. + += The seam + +#wide(caption: [The core/shell seam. `Event` is the only way in; `Surface` and a +queue of `Effect` are the only ways out. `host.VTable` is how an `Effect` +reaches a resource, and every method a host leaves null is answered by +`host.Fallback` inside the same process.])[ + #diagram[ + #cetz.canvas(length: 0.995cm, { + import cetz.draw: * + set-style(stroke: 0.4pt, mark: (fill: black, scale: 0.35)) + + // ---- the core ---- + rect((6.0, 0.9), (11.6, 4.3), name: "core") + content((8.8, 4.02), text(7.6pt)[*the core* --- `src/pardes.zig`]) + line((6.0, 3.78), (11.6, 3.78)) + content((8.8, 3.42), text(6.2pt)[`update(ev)` --- one dispatch, mutates]) + content((8.8, 2.92), text(6.2pt)[`render(arena)` #sym.arrow.r `*Surface`]) + content((8.8, 2.42), text(6.2pt)[`emit(e)` #sym.arrow.r fixed ring, `limits.effect_cap`]) + content((8.8, 1.92), text(6.2pt)[`nextEffect()` #sym.arrow.r `perform(e)`]) + content((8.8, 1.32), text(6.2pt)[single-threaded by construction]) + + // ---- native input ---- + rect((0, 3.0), (4.3, 4.3), name: "in") + content((2.15, 3.98), text(7.0pt)[*native input*]) + content((2.15, 3.52), text(6.0pt)[termios+vaxis / SDL / DOM /]) + content((2.15, 3.19), text(6.0pt)[AppKit / a byte sink on a UART]) + line("in.east", (6.0, 3.65), mark: (end: "stealth")) + content((5.15, 3.92), text(6.4pt)[`Event`]) + content((5.15, 3.5), text(5.6pt)[16 arms]) + + // ---- surface out ---- + rect((13.3, 3.0), (18.0, 4.3), name: "paint") + content((15.65, 3.98), text(7.0pt)[*paint the grid*]) + content((15.65, 3.52), text(6.0pt)[vaxis cell-by-cell / glyph atlas /]) + content((15.65, 3.19), text(6.0pt)[DOM cells / CoreText / ANSI]) + line((11.6, 3.65), "paint.west", mark: (end: "stealth")) + content((12.5, 3.92), text(6.4pt)[`Surface`]) + content((12.5, 3.5), text(5.6pt)[cells, cursor,]) + content((12.5, 3.16), text(5.6pt)[images, tracks]) + + // ---- effect out ---- + rect((13.3, 1.1), (18.0, 2.5), name: "perf") + content((15.65, 2.2), text(7.0pt)[*perform*]) + content((15.65, 1.78), text(6.0pt)[fork a pty, write a path, mark a]) + content((15.65, 1.45), text(6.0pt)[directory, put text on a clipboard]) + line((11.6, 1.8), "perf.west", mark: (end: "stealth")) + content((12.45, 2.05), text(6.4pt)[`Effect`]) + content((12.45, 1.62), text(5.6pt)[18 arms]) + + // ---- the vtable ---- + rect((6.0, -1.0), (11.6, 0.4), name: "vt") + content((8.8, 0.14), text(7.0pt)[`host.VTable` --- 21 optional methods]) + content((8.8, -0.32), text(6.0pt)[`push_` #sym.arrow.r every host, returns nothing]) + content((8.8, -0.68), text(6.0pt)[`pull_` #sym.arrow.r exactly one host answers]) + line((8.8, 0.9), (8.8, 0.4), mark: (end: "stealth")) + line("vt.east", (13.3, 1.5), mark: (end: "stealth")) + + // ---- fallback ---- + rect((0, -1.0), (4.3, 0.9), name: "fb") + content((2.15, 0.62), text(7.0pt)[`host.Fallback`]) + content((2.15, 0.18), text(6.0pt)[every null method, answered here:]) + content((2.15, -0.18), text(6.0pt)[a virtual filesystem over the]) + content((2.15, -0.52), text(6.0pt)[embedded source, a virtual]) + content((2.15, -0.84), text(6.0pt)[clipboard, silent ptys]) + line("vt.west", "fb.east", mark: (end: "stealth")) + + // ---- answers loop back. Routed through the one corridor that is free of + // every box (x between `fb`/`in` at 4.3 and the core column at 6.0) and + // below the lowest box edge, so the canvas stays inside 0..18 and cannot + // bleed into the page margins. + line((15.65, 1.1), (15.65, -1.6), (5.72, -1.6), (5.72, 3.58), + mark: (end: "stealth"), stroke: (dash: "dashed")) + content((9.0, -1.94), text(5.8pt)[an answer returns as an ordinary `Event`: `paste`, `lsp_resp`, `pipe_resp`, `file_changed`]) + }) + ] +] + +The boundary is three plain data types and one struct of function pointers. + +== `Event`: what comes in + +Sixteen arms (`pardes.Event`), in declaration order: +`key`, `mouse`, `resize`, `output`, `eof`, `lsp_resp`, `pipe_resp`, +`file_changed`, `paste`, `command`, `pdf_scroll`, `pinch`, `touch_scroll`, +`pointer_leave`, `tick`, `fs_req`. + +`key` carries typed `text`. `mouse` carries press/release/motion/drag; button +left, middle, right and the four wheel directions; a cell position; and the +`ctrl` flag, because Ctrl-left-click is goto-definition. `resize` carries cols, +rows, and — in a MuPDF build — the pixel size of one cell. `output` is bytes a +pty produced, tagged with the pane id. `command` is one builtin line arriving +from another process over the nested socket. `pointer_leave` exists because an +out-of-range motion must clamp onto the last grid cell and a pointer that has +left the drawable area must not: otherwise leaving the window previews whatever +is under the final cell. `fs_req` is one filesystem request from a process that +opened a file under the control mount, and its answer leaves as an +`Effect.fs_reply` in the same update. + +Four of the arms are answers to something the core asked for; @effect +names the asks. + +Touch policy belongs to the shell: web maps a one-finger tap to right-button +LOOK and a drag past its tap slop to natural scrolling; native SDL keeps its +two-finger gestures. A joystick cursor is a motion plus buttons. The shells do +this translation and nothing else with input: one event vocabulary, five +dialects translated at the door. + +`postEvent` is a value queue of 64 and asserts what cannot survive the trip +(`Pardes.postEvent`): `output`, `paste`, `lsp_resp`, `pipe_resp`, +`file_changed`, `command` and `fs_req` all carry a borrowed slice, so they are +`unreachable` there and must be handed to `update` directly inside the host's +borrow window. That is also what keeps the pty read path copy-free. + +== `Surface`: what goes out + +`cols`, `rows`, a grid of cells, a cursor, pixel attachments, and a bounded list +of plain panel-transition tracks (`pardes.Surface`). This is the +*canonical interface*: it is literally what the tty shell hands to vaxis, cell +by cell. SDL rasterizes the same grid through a glyph atlas; the browser reads a +packed copy and patches native DOM cells. If it cannot be expressed in the +surface, it does not exist in pardes. + +Pixel images ride along as a list of PLACEMENTS rather than one per pane — a PDF +pane contributes every page its viewport intersects, and pages can be +arbitrarily short. The SDL shells blit RGBA; the tty shell uses kitty graphics +when available and falls back to the petscii matcher. Transition tracks identify +their pane by slot and serial and carry only phase, effect, frame, and from/to +cell boxes. Canonical layout is committed immediately; tracks are finite +presentation data. + +== `Effect`: what is asked for + +Eighteen arms (`pardes.Effect`). The core never performs IO for any of +them; it asks. + +```zig +pub const Effect = union(enum) { + spawn: struct { pane: u8, cwd: Buf(256) }, + write: struct { pane: u8, bytes: Buf(64) }, + resize_pty: struct { + pane: u8, cols: u16, rows: u16 }, + open_link: Buf(256), + save_file: struct { pane: u8 }, + save_text: struct { + pane: u8, serial: u32, path: Buf(256) }, + write_dump, + set_clipboard, + read_clipboard, + lsp: struct { id: u32, kind: lsp.Kind, + pane: u8, offset: u32, arg: Buf(128) }, + pipe: struct { id: u32 }, + watch: struct { pane: u8, on: bool }, + theme_file: struct { + generation: u32, on: bool }, + dump_themes: struct { pane: u8 }, + fs_reply: acmefs.Reply, + attach: struct { + pane: u8, name: Buf(attach_name_max) }, + detach: struct { pane: u8 }, + quit, +}; +// doc comments elided; see the file +``` + +Every arm is a fixed-size value, and that is the constraint the widths record. +Unbounded content is never in the union: `save_file` names a pane and the shell +reads the bytes off the core, and `save_text` carries the *path* — bounded +exactly like a spawn's cwd, so two saves armed in one batch cannot cross — while +the bytes are read at drain time. `save_text` also carries the pane's `serial`, +so a slot freed and reused before the drain writes nothing rather than another +pane's text to that path. `attach_name_max` is 256 for the same reason `spawn`'s +cwd is, and reusing that width is why the arm costs the ring nothing: +`save_text` is still the widest member (`pardes.attach_name_max`). + +Four asks have an answer coming back. `read_clipboard` returns an ordinary +`paste` event — or nothing at all when the shell cannot read the clipboard, which +is most terminals, since they refuse the OSC 52 read. `lsp` returns `lsp_resp`. +`pipe` returns `pipe_resp`. `watch` returns `file_changed` whenever the shell +notices a text file or PDF moved under it. + +`theme_file` carries only a generation because the path lives in the core's fixed +request buffer, which keeps an already-large ring compact. `fs_reply` carries no +bytes either: `payload` says where they live — a staging buffer in the core, or a +range of a pane's live text — and `fsPayload` resolves it during the drain, so a +megabyte read costs one `writev` and no copy. + +`attach` and `detach` are the two arms no host method performs directly. `attach` +must not tear anything down (@swap); `detach` is served only by a +detached core's host, and the null case is the point rather than an oversight — +a local tty or SDL shell has no session to leave, so `perform` reports that on +the pane's message row instead of quietly quitting something (the `.detach` arm of +`Pardes.perform`). + +The watch path, in full, because it is the ask with the most machinery behind +it. The tty and SDL hosts and the detached daemon implement it with Linux +inotify (`src/file_watch.zig`); the native macOS host watches both each file and +its parent directory with debounced DispatchSources, then restats the exact path +under a pane-generation guard. The file catches in-place writes while the parent +follows rename-over saves. Text snapshots are filtered by their content hash; +PDFs use bounded inode/size/time identity and commit it only when equal stats +bracket a successful transactional MuPDF reopen (`file_watch.Generation`). A +mismatched transaction gets one bounded self-retry. This catches rename-over +saves without reading a large PDF merely to notice it changed, or spinning on a +malformed one. A PDF response retains its reading position and pane settings. +Web has no filesystem watcher, and nothing in the core waits for one. + +== The effect ring + +```zig +pub fn emit(p: *Pardes, e: Effect) void { + if (p.effects_len == p.effects.len) return; + const tail = (p.effects_head + + p.effects_len) % p.effects.len; + p.effects[tail] = e; + p.effects_len += 1; +} + +pub fn nextEffect(p: *Pardes) ?Effect { + if (p.effects_len == 0) { + p.effects_head = 0; + return null; + } + const e = p.effects[p.effects_head]; + p.effects_head = + (p.effects_head + 1) % p.effects.len; + p.effects_len -= 1; + return e; +} +``` + +`emit` REFUSES when full and never evicts (`Pardes.emit`). Byte +order is the reason: a `.write` dropped from the middle of a run would reorder a +pty's input, and a `.write` dropped from the tail merely truncates it. + +Capacity is 4096 on every hosted platform and 128 on the board +(`limits.effect_cap`). No capacity can deadlock the drain, because `pump` empties +the ring on every iteration with an unconditional `while (nextEffect())` — +including effects `perform` itself queues — so the only question a capacity +answers is how large a single-pump *burst* may be. The one producer that can +burst is `emitWrite`, which chunks arbitrary bytes into 64-byte `.write` effects +for a pty; everything else queues O(1) effects per event, and the input queue +holds at most 64 events per pump, so 128 leaves two effects per queued event. A +build with no terminal panes has no pty to write to at all. + +== `host.VTable`: who serves the core + +The seam itself is one struct of twenty-one optional function pointers +(`host.VTable`): the `std.mem.Allocator` shape, and the generalization of +two vtables this codebase already grew on its own: the tty shell's +terminal-query hook, which this replaced, and `macos.Runtime`, which survives as +the C ABI that shell's host still enters through. + +```zig +pub const VTable = struct { + /// The ONLY place the process may sleep. + pull_wait_input: ?*const fn ( + ctx: ?*anyopaque, + timeout_ms: u32, + ) void = null, + push_present: ?*const fn ( + ctx: ?*anyopaque, + surface: *const pardes.Surface, + ) void = null, + // ... + pull_tty_taken: ?*const fn ( + ctx: ?*anyopaque, + pane: u8, + ) bool = null, + // ... +}; +``` + +A null method is not an error: the core substitutes a default backed by ordinary +data structures in the same process (`host.Fallback`). A `Save` lands in a real +file under the tty host and in `Fallback.files` under a host that never wrote a +filesystem method, and every path above that behaves identically. Two +consequences were taken on purpose. The zero-method host IS the test harness: a +`Host{}` is a complete, deterministic, in-process pardes with a virtual +filesystem, a virtual clipboard and silent ptys. And `Fallback` lives on the +`Pardes` instance rather than on the host, so N cores driven by one fan-out host +each keep their own state and can run in parallel. + +The fallback filesystem is not empty. It is pardes's own source, embedded +(`src/source_manifest.zig`), with `files` holding only what this session WROTE, +so a Save shadows the built-in copy and reading it back returns the edit. That is +what makes a host with no file methods a usable pardes rather than one staring +at an empty buffer. + +What is deliberately NOT in the vtable: whether a capability EXISTS in this +build. That stays comptime and stays next to the code it shapes +(`pardes.platform`, `pardes.hosted`, `pardes.pdf_enabled`, +`pardes.can_attach`, `pardes.terminal_panes`, `builtins.capabilities`, +`PdfSlot`). A vtable cannot make a field zero-sized or a builtin absent from an +enum. Comptime decides what a build HAS; the vtable decides who SERVES it at +runtime. + +`pardes.can_attach` is the sharpest of those, because it exists to close a gap +the coarser gate left open. `Attach` and `Detach` were gated on `pardes.hosted`, +and macOS is hosted: it has a unix socket and it compiles `detached/`. What it +does not do is POLL. `takeAttach` has to be a poll rather than a host method +precisely because attaching replaces the core the call is running inside +(@swap), and `src/macos.zig` never calls it — so there the word +parsed, queued an effect, stored a request in `attach_buf`, and then did nothing +at all, for ever, silently. `can_attach` admits exactly the tty and gui +platforms, which is exactly the pair `main.zig` accepts `--attach` for: the same +question asked at the command line instead of in a tag. A capability that a +build cannot serve should not be a word that build offers. + +=== The naming rule is compiler-enforced + +Every method name says how a fan-out must route it, and `Fanout.isPull` makes a +wrong name a compile error rather than a silent push. + +#wide(caption: [`host.Fanout.isPull`. The failure of a forgotten `pull_` is +invisible in every unit test and obvious only to the user: one Ctrl-V pasting +twice. `Fanout.all` synthesizes one wrapper per field, so adding a method to +`VTable` needs no code in the fan-out at all.])[ +```zig +/// How to route a method, read off its own name. A method that is neither +/// is a COMPILE ERROR rather than a silent push, because the failure of a +/// forgotten pull is invisible in every unit test and obvious only to the +/// user: one Ctrl-V pasting twice. +fn isPull(comptime name: []const u8) bool { + if (std.mem.startsWith(u8, name, "pull_")) return true; + if (std.mem.startsWith(u8, name, "push_")) { + if (Method(name).return_type.? != void) @compileError("Host.VTable." ++ + name ++ " reaches every host, so it cannot return a value: whose answer would it be?"); + return false; + } + @compileError("Host.VTable." ++ name ++ " must be named push_… (every host gets it) " ++ + "or pull_… (exactly one host serves it, because there is one of whatever comes back)"); +} +``` +] + +`push_` reaches every wrapped host and returns nothing — a push with an answer +would have N answers and no way to pick one. `pull_` is served by exactly one +host, because there is one of whatever comes back: one value, one sleep that +ends, one `Event.paste` for one Ctrl-V, one `lsp_resp` per request id. + +The six `pull_` methods are therefore exactly the six places a single answer +exists: `pull_wait_input` (the one place the process may sleep), +`pull_tty_taken`, `pull_gpio_toggle`, `pull_read_clipboard`, `pull_lsp` and +`pull_pipe`. `pull_tty_taken` is a pull and not a pushed fact because the +`execute` that asks — has a program taken this pane's tty? — must choose a +destination inside its own update, and effects drain after; a pushed fact would +mean every host probing every pane's processes every frame to answer a question +asked when a human middle-clicks a word. + +== The frame, as a frontend writes it + +```zig +pub fn pump(p: *Pardes, h: Host) !void { + p.host = h; + const v = h.vtable; + if (v.pull_wait_input) |f| + f(h.ctx, if (p.animationActive()) + animation.frame_ms else 0); + while (p.nextQueued()) |ev| p.update(ev); + while (p.nextEffect()) |e| p.perform(e); + // A quitting frame has already freed + // what it would draw. + if (p.quit) return; + if (v.push_poll_frame) |f| f(h.ctx); + _ = p.frame_arena.reset(.retain_capacity); + const surface = try p.render( + p.frame_arena.allocator()); + if (v.push_present) |f| f(h.ctx, surface); + if (v.push_post_present) |f| f(h.ctx); + // ... +} +``` + +`Pardes.pump`, eighteen lines in the source, and the order of them is the +architecture. Input first, in whichever host owns the sleep; +then every queued event, to completion; then every effect, to completion, +including effects `perform` queued; then one arena reset and one render; then +present, then post-present. + +Animation time is not spent in here. `pull_wait_input` was told how long it may +sleep, and a display clock wakes faster than that on input, so only the host +knows when a real frame interval has passed. Each spends it by handing back one +`.tick`. + +`push_post_present` is split from `push_present` because it must observe a frame +the user has actually seen: panel-presentation acknowledgement and pointer +refresh both depend on that, and a hook that ran before the pixels landed would +acknowledge a frame that was never shown. + +== Platform divergence is comptime + +There is one deliberately platform-divergent file by design: `look.zig` holds +path and `:line` resolution, URL detection, and the per-platform outcomes. The +divergence is a comptime switch on `pardes.platform`, used the way the stdlib +switches on `os.tag`, so every platform's behaviour sits in the same screenful. ```zig -pub const Platform = enum { tty, gui, web, macos }; -// in look.zig: -if (platform == .web and target.kind == .url) - return .{ .open_link = target.text }; +const platform_has_fs = !pardes.isolated and + switch (pardes.platform) { + .tty, .gui, .macos => true, + // The browser's filesystem is the embedded + // source archive; the P4 firmware's is + // whatever the serial host answers for, + // through the Host vtable — never a path + // this process opens. + .web, .esp32p4 => false, + }; ``` -There is one such file by design: `look.zig` holds path/`:line` resolution, -URL detection, and the per-platform outcomes. The core's one `pointerOperand` -primitive owns click-word expansion and is shared verbatim by right-click and -the delayed hover preview; that policy stays beside input because it also -observes live pane selections and wrapped grid coordinates. +`look.platform_has_fs`. An isolated build has no filesystem *by construction* — +the option is comptime, so every libc path below is dead code the compiler +removes rather than a branch that could be taken by accident. + +The core's one `pointerOperand` primitive owns click-word expansion and is shared +verbatim by right-click and the delayed hover preview; that policy stays beside +input because it also observes live pane selections and wrapped grid coordinates. Pane implementations are similarly flat and direct: `file_pane.zig`, `term_pane.zig`, `image_pane.zig`, `output_pane.zig`, and `pdf_pane.zig` own -their kind-specific storage and operations. `pardes.zig` keeps the layout, -input dispatch, cross-pane invariants, and the small calls joining those -modules. There is no pane vtable or callback layer; the kind is already plain -data, so a direct switch/call is the shortest boundary. -The large end-to-end PDF cases live in `pdf_pane_integration_test.zig`, keeping -pane-specific fixtures and raster assertions out of that core file as well. +their kind-specific storage and operations. `pardes.zig` keeps the layout, input +dispatch, cross-pane invariants, and the small calls joining those modules. +There is no pane vtable or callback layer; the kind is already plain data, so a +direct switch/call is the shortest boundary. The large end-to-end PDF cases live +in `pdf_pane_integration_test.zig`, keeping pane-specific fixtures and raster +assertions out of that core file as well. -= Data structures += State -The whole state is one struct, fixed-size where it can be. Panes themselves are -heap-allocated on demand and their contents (file bytes, the yank register, PDF -rasters, tree-sitter state) grow with what you open; everything else is sized -at init. +== One struct, fixed where it can be -User-settable runtime choices are one plain `runtime_config.State`: booleans, -theme index, owned bounded shell/font strings, requested/effective font facts, -one panel-transition enum, and scene-effect booleans. A compile-time `settings` -array generates each setting builtin and the rows of the single `Config` -query. It has no callbacks and no parallel query registry to drift from it. +The whole state is one struct. Panes themselves are heap-allocated on demand and +their contents (file bytes, the yank register, PDF rasters, tree-sitter state) +grow with what you open; everything else is sized at init. + +```zig +Pardes + ncol + col_weight[6], col_terms[6][16], col_n[6] + panes: [16]?*Pane // slot array; id = index + active: usize + drag: Drag // none | border_v | + // border_h | move | + // tag | select + settings: runtime_config.State + rects: [16]Rect // where each pane + // landed, this frame + panel_tracks: [16]?Track // live panes, + // serial-guarded + presented_panel_tracks:[16]?Track + closing_panel_tracks: [16]Track // dense visual + // tombstones, no owner + presented_cells + panel_cell_diffs + effects: [limits.effect_cap]Effect + head/len + in_q: [64]Event + head/len + fallback: host.Fallback // per instance + fs: acmefs.State // inert until --fs + +Pane + tag: TagLine // live prefix (cwd/path) + // + editable tail + vt, stream: ghostty-vt Terminal and its stream — + on EVERY pane, not just terminals, + which is what lets the same keys and + the same parity suite drive a file + and a shell + file: ?file_pane.State + image: ?image_pane.State + pdf: PdfSlot // payload presence is the + // kind; none = terminal + vweight: f32 + mode: enum { normal, insert, tty } + cursor: absolute body position // rides the + // scrollback, not the screen + msel/vsel + sels[63] + nsel // the primary + // range, and up to 63 more + ovl: ?term_pane.EditBuffer // ONE typed run, + // anchored to an absolute row + undo: two stacks, not one — term_pane.Snapshot + history for an edit buffer, and + file_pane.State history for file content + +file_pane.State = path + bytes + line index + + Syn (tree-sitter bytes) +image_pane.State = decoded RGBA + petscii cache +pdf_pane.State = MuPDF document + continuous + layout + search/selection/outline + + bounded per-page raster relay + + frame placement decisions +``` + +`MAX_PANES` is 16 and `MAX_COLS` is 6 (`pardes.MAX_PANES`, `pardes.MAX_COLS`). Sixteen panes +is also why the jump stack's depth is what it is: the depth that matters is +"more visits than you can hold in your head", not vim's hundred. + +== Cells + +```zig +pub const CellStyle = struct { + fg: Color = .default, + bg: Color = .default, + bold: bool = false, + dim: bool = false, + italic: bool = false, + blink: bool = false, + reverse: bool = false, + invisible: bool = false, + strikethrough: bool = false, + ul: enum { off, single, double, + curly, dotted, dashed } = .off, + font_role: FontRole = .body, +}; + +/// One surface cell. `default = true` means +/// "never painted this frame": the shell renders +/// it as the terminal's default cell (vaxis clear +/// semantics). +pub const Cell = struct { + text: [7]u8 = @splat(' '), + len: u8 = 1, + style: CellStyle = .{}, + default: bool = true, + + pub fn grapheme(c: *const Cell) []const u8 { + return c.text[0..c.len]; + } + // ... +}; +``` + +`pardes.CellStyle` and `pardes.Cell`. Seven bytes of `text` because a cell holds a +complete grapheme cluster and not a codepoint: keeping the cluster is what makes +combining marks visible and keeps ZWJ, modifier, flag and Indic sequences in the +same screen cell that cursor and edit maths treat as one unit. `len` bounds it, +and `default` distinguishes "a space was painted here" from "nothing was". + +That distinction is load-bearing twice over. `visuallyEqual` compares only what +a shell can present — bytes past `len` are scratch left by earlier graphemes and +must never manufacture a diff, and an unpainted default cell has no visible +style or text either. And `printableAscii` returns `' '` for a default cell but +`null` for a painted multi-byte one, which is what admits a cell to the ASCII +transition walk (@diff). + +== Columns and panes are arithmetic + +Layout is arithmetic, not objects: columns are weights over the width, panes are +weights over the column. + +#figure( + placement: auto, + caption: [The layout model. Each pane is a narrow gutter strip (move box and + scrollbar) plus a tag row plus a body. `col_weight` is 64-bit fixed point with + 32 fractional bits, so every possible column split divides an initial weight + exactly; `vweight` is an `f32` over its own column. `col_terms[c][k]` is the + pane in slot position `k` of column `c`, and `rects[id]` is where that pane + landed this frame.], + diagram[ + #cetz.canvas(length: 1cm, { + import cetz.draw: * + set-style(stroke: 0.35pt, mark: (fill: black, scale: 0.3)) + let w = 7.6 + let h = 4.5 + + rect((0, 0), (w, h)) + line((0, h - 0.28), (w, h - 0.28)) + content((w / 2, h - 0.14), text(4.9pt, style: "italic")[topbar, execute-only]) + + line((2.8, 0), (2.8, h - 0.28)) + line((5.2, 0), (5.2, h - 0.28)) + line((2.8, 1.8), (5.2, 1.8)) + + let pane(x0, y0, x1, y1, kind) = { + line((x0 + 0.26, y0), (x0 + 0.26, y1 - 0.26)) + line((x0, y1 - 0.26), (x1, y1 - 0.26)) + content(((x0 + x1) / 2, y1 - 0.13), text(4.9pt, style: "italic")[tag: #kind]) + } + pane(0, 0, 2.8, h - 0.28, [file]) + pane(2.8, 1.8, 5.2, h - 0.28, [terminal]) + pane(2.8, 0, 5.2, 1.8, [output]) + pane(5.2, 0, w, h - 0.28, [PDF]) + content((1.45, 2.3), text(4.9pt, style: "italic")[body]) + content((6.4, 2.0), text(4.9pt, style: "italic")[body]) + + line((0.04, -0.3), (2.76, -0.3), mark: (start: "stealth", end: "stealth")) + content((1.4, -0.58), text(5.4pt)[`col_weight[0]`]) + line((2.84, -0.3), (5.16, -0.3), mark: (start: "stealth", end: "stealth")) + content((4.0, -0.58), text(5.4pt)[`col_weight[1]`]) + line((5.24, -0.3), (w - 0.04, -0.3), mark: (start: "stealth", end: "stealth")) + content((6.4, -0.58), text(5.4pt)[`col_weight[2]`]) + + line((5.0, 1.86), (5.0, h - 0.34), stroke: (dash: "dotted"), + mark: (start: "stealth", end: "stealth")) + content((4.2, 3.0), text(5.2pt)[`vweight`]) + line((5.0, 0.06), (5.0, 1.74), stroke: (dash: "dotted"), + mark: (start: "stealth", end: "stealth")) + content((4.2, 0.9), text(5.2pt)[`vweight`]) + }) + ], +) Horizontal layout weights are fixed-point integers and geometry rounds -cumulative boundaries. Splitting a column replaces only its weight `W` by -`A+B=W` at the same position. Therefore every boundary outside the source +cumulative boundaries. Splitting a column replaces only its weight $W$ by +$A + B = W$ at the same position. Therefore every boundary outside the source column is bit-identical before and after the split, including at awkward non-dyadic screen widths; only the source and new column can receive movement tracks. Vertical splits apply the corresponding rule to the source pane's weight. -= Presentation animation - -`panel_animation.zig` is backend-neutral data and math: five transitions, -their easing, exact endpoint progress, stable per-cell noise, and a POD track. -The core detects opening/moving rectangles when it commits layout and publishes -only active tracks. It retains the last successfully presented canonical grid -and a typed old/new cell diff; unused grapheme bytes do not manufacture a -change. A separate dense closing-track list is presentation-only state for a -pane whose functional lifetime has already ended. Pointer input inverts the -presented slide/zoom/vertical rectangle back to the canonical grid, lets -unchanged dissolve/ASCII cells through immediately, and rejects closing +`splitBelow` shrinks only the source pane; `absorbVWeight` gives a dying pane's +weight to one sibling; an emptied column hands its width to a neighbour. +Minimal motion is the invariant: an operation on one pane may not move panes it +does not touch. + +== Runtime settings are one table + +User-settable runtime choices are one plain `runtime_config.State`: booleans, +theme index, owned bounded shell/font strings, requested/effective font facts, +one panel-transition enum, and scene-effect booleans +(`runtime_config.State`). A compile-time `settings` array +(`runtime_config.settings`) generates each setting builtin and the rows of the +single `Config` query. It has no callbacks and no parallel query registry to +drift from it. + +Requested and effective are separate fields on purpose. Resolving a shell name +to an executable, or a font name to a face, belongs to the native host; the core +retains what the host actually chose and marks a changed request `pending` until +the next spawn or the next atlas acknowledges it. A rejected request therefore +stays queryable without claiming it is on screen. + += Modes and selections + +== Three modes and a boolean + +`Pane.mode` is `enum { normal, insert, tty }` (`pardes.Mode`). + +*normal* is the helix motion model. *insert* is click-and-type: terminals get +splice runs (shift right, never overwrite, anchored to an absolute row), files +get real edits. *tty* is raw pty forwarding — a Ctrl-key toggle, default Ctrl-b, +`--tty-toggle` — where the mouse is still usable, entry does a +`promptClickMove`, and prompts become visible again (they are hidden in the +other two modes via OSC 133). + +Select is not a fourth mode. `v` sets `Pane.select`, a bool on top of normal, +which makes motions extend from a fixed anchor instead of replacing the range; +`pane.mode` stays `.normal`, and insert/tty transitions drop it +(`Pane.select`). It is *displayed* as a fourth mode name, "select", +because that is what the user is in — but nothing in the dispatch branches on a +fourth mode. + +Since the helix motion model landed, a traversal motion SELECTS the range it +crossed. That is why there is no verb+noun grammar and why `i` after `w` types +at the selection's start. + +== One primary range and up to 63 more + +`MAX_SELS` is 64 (`pardes.MAX_SELS`). helix's `Selection` is a list of +ranges plus a primary index; pardes keeps the PRIMARY exactly where it has +always been — `cur_row`/`cur_col` plus `vsel` — and the other ranges in +`sels: [MAX_SELS - 1]SelRange`, document-ordered and disjoint +(`Pane.sels`, `Pane.nsel`). + +That split is the whole design. Every motion, operator, renderer and mouse path +still reads one selection, so with `nsel == 0` not a byte of behaviour moves, and +the differential and snapshot suites keep proving it. The extra ranges are driven +by replaying the single-selection key handler once per range (`replaySels`). + +`s`/`S` — select and split by regex — snapshot the selection they were armed on +in `sel_snap` and re-derive the preview FROM that snapshot on every keystroke, +rather than from the previous preview. This is what helix's `regex_prompt` does, +it is what makes typing a pattern one character at a time land on the same answer +as pasting it whole, and it is what makes Esc a plain restore with nothing else +to undo. The snapshot also records whether the range was a *user-intent* +selection, so restoring cannot silently promote motion residue into something +the acme chords will act on (`Pane.sel_snap`). + +== Three buttons, three meanings + +The mouse selections are `[3]Sel`, one per button, block-shaped, in text-area +coordinates where `r` counts from the tag row (`pardes.Sel`). Each +has three states, `none`, `dragging` and `done`, which is what lets a kept left +selection stay highlighted after the drag and still be distinguishable from one +in progress. + +Left selects and pins the cursor. Middle executes: with no drag it expands to a +file-ish word, then runs a builtin or sends the text to the shell, and it does +not focus. Right looks. A theme owns the SELECTION — `sel_bg`/`sel_fg` are one +pair per theme, and the three per-button tints and the dimmed extra cursors are +mixed off it, so what stays fixed is the distinction between buttons and not the +colours. + +`Drag` is a `union(enum)` with six arms (`pardes.Drag`): `none`, +`border_v`, `border_h`, `move`, `tag`, `select`. `border_v` carries an optional +`corner`: when the press lands on a cell that is both a column's vertical border +and one of the two adjoining columns' own horizontal borders, the one drag moves +*both* boundaries — never three, and when both columns happen to be split at the +grabbed row the left one wins, so the gesture that existed before is bit-for-bit +unchanged. + +== The tag is a command line + +A tag is one line: a live prefix (mode indicator, cwd or path) plus an editable +tail, and the tail gets the full modal editor. The coordinate space is UTF-8 byte +offsets into the *whole* rendered tag, prefix ++ tail, always on grapheme +boundaries (`Pane.tag_col`). The prefix is live chrome, so it is +selectable, yankable and executable but READ-ONLY: every edit op measures from +`edit0` — the first editable byte — and does nothing left of it. + +`tag_tail` is a fixed `[max_tag_tail]u8`, and the bound IS the storage +(`limits.max_tag_tail`): every writer — `appendTag`, `tagInsert`, +`restoreDumpTail`, the acmefs `tag` file — refuses input that does not fit +rather than truncating it. The schema limit and the buffer therefore can never +disagree, which is what lets a dump reader reject data before copying it into a +pane. + +A tag edit is always insert mode, so it hijacks the body's mode; `tag_mode` +remembers what it hijacked and terminals restore it on exit, which is why +clicking a tag never changes a pane's mode. + += Diffing and presenting a frame + +== Eleven transitions + +`panel_animation.zig` is backend-neutral data and math: the transition +vocabulary, easing, exact endpoint progress, stable per-cell noise, and a POD +track. `Transition` has twelve members counting `off` +(`panel_animation.Transition`), with explicit numeric values because they +cross both GUI shader ABIs — GLSL receives the enum in an instance `uvec4` and +the Core Image kernel receives it as a float, so spelling the numbers keeps a +source reorder from changing pixels. + +#table( + columns: (auto, auto, auto, 1fr), + align: (left, left, left, left), + table.header([*id*], [*name*], [*frames*], [*composed by*]), + [1], [`slide`], [12], [backend shader / tty grid], + [2], [`zoom`], [14], [backend shader / tty grid], + [3], [`dissolve`], [10], [backend shader / tty grid], + [4], [`ascii`], [13], [core], + [5], [`vertical`], [12], [backend, lifecycle only], + [6], [`edges`], [12], [core], + [7], [`fall`], [14], [core], + [8], [`wave`], [14], [core], + [9], [`curtain`], [12], [core], + [10], [`scramble`], [12], [core], + [11], [`typewriter`], [14], [core], +) + +The split in the last column is the design. `composedByCore` is true for every +*character* effect: the core writes the finished glyphs into the published +`Surface`, so no backend owns a byte walk, a stagger, or a noise threshold, and +every renderer presents the same byte at a given frame. The geometry effects +(`slide`, `zoom`, `vertical`) and `dissolve` are the ones a backend evaluates, +because they are transforms over rectangles rather than choices about characters. + +Easing follows from that too. A sweep and a typewriter are constant-rate by +definition, so `curtain` and `typewriter` are `linear`: easing their head would +make the pass visibly hesitate mid-pane. Character walks and per-cell locks read +best with a slow start, a fast middle and a slow settle, so `ascii`, `fall` and +`scramble` are `smoother`. + +The core detects opening and moving rectangles when it commits layout and +publishes only active tracks. A separate dense closing-track list is +presentation-only state for a pane whose functional lifetime has already ended, +which is why it needs no pane owner and no serial guard. Pointer input inverts +the presented slide/zoom/vertical rectangle back to the canonical grid, lets +unchanged dissolve and ASCII cells through immediately, and rejects closing pixels, so pixels and gestures cannot disagree during a transition. -Pardes core first composes each ASCII-qualified diff by incrementing or -decrementing its printable byte. Short walks move once per frame; longer walks -use integer ease-out skips and finish within twelve movements. Style-only and -non-ASCII changes pass through, and every backend receives the same -presentation cells. -TTY copies that `Surface` grid into a compositor scratch grid, clears -slide/zoom destinations, then paints moving, opening, and closing panels in -order. -Cleared geometry uses the theme page color when it is explicit and the host +== The semantic cell diff + +```zig +pub const PanelCellDiff = union(enum) { + unchanged, + visual, + ascii: AsciiDiff, + + pub fn between( + old: *const Cell, + new: *const Cell, + ) PanelCellDiff { + if (old.visuallyEqual(new)) return .unchanged; + if (AsciiDiff.between(old, new)) |diff| + return .{ .ascii = diff }; + return .visual; + } + + // ... +}; +``` + +`pardes.PanelCellDiff`; the elided member is `changed`, which is +`diff != .unchanged` and is what `Surface.panelCellChanged` calls. The core +retains the last successfully presented canonical grid and this typed old/new +classification, and it is the *semantics* that matter: unused grapheme bytes do +not manufacture a change, and a style-only or multi-byte change is `.visual` and +passes straight through. + +An `.ascii` diff is a `{ from: u8, to: u8 }` pair, and the core composes it by +incrementing or decrementing the printable byte. Short walks move one value per +frame; a longer walk is crossed by eased character skips and finishes within +`ascii_max_movement_frames`, which is 12 +(`panel_animation.ascii_max_movement_frames`). Frame +zero is the exact old byte and the endpoint is exact, so an intermediate frame is +always valid UTF-8. `Track.frame_count` carries the core-computed duration for +these data-dependent effects — zero selects the effect preset, and the ASCII +composer fills it from the longest eased byte walk in the pane's diff. + +== What each backend does with it + +TTY copies that `Surface` grid into a compositor scratch grid, clears slide and +zoom destinations, then paints moving, opening, and closing panels in order. +Cleared geometry uses the theme page colour when it is explicit and the host terminal default only for transparent themes, so a light theme cannot flash a -dark gap. Slide/zoom change the copied rectangle. Dissolve changes only diff -cells from their old value to their new value; vertical raises only an opening -or frozen closing pane -inside its own clip. SDL supplies old/new glyph data, diff flags, -final/presented boxes, and effect parameters to the glyph and native-image -shaders. Pixel attachments bypass ASCII because they have no character byte. -macOS passes the same records across its plain C ABI and composites -old/new panel images in Metal/Core Image. Scene `Crt`, `Ripple`, and -`Glitch` bits share one full-window pass in each native GUI. DOM web is a -separate platform, not a shader GUI: retaining selectable HTML/CSS is more -important than duplicating the renderer in canvas, so it exposes neither -effect family. +dark gap. Slide and zoom change the copied rectangle; dissolve changes only diff +cells from their old value to their new value; vertical raises only an opening or +frozen closing pane inside its own clip. + +Every effect's last active sample is its exact canonical endpoint, so the +compositor returns the source surface unchanged when no track is +non-canonical — keeping the real cursor and attachments in that sample rather +than suppressing them for one frame that is otherwise pixel-identical +(`panel_compositor.compose`). Kitty placements cannot be resampled +through the character-grid transform, so a moving pane's attachment is omitted +while its geometry moves and placed again on the last active sample. + +SDL supplies old/new glyph data, diff flags, final/presented boxes, and effect +parameters to the glyph and native-image shaders. Pixel attachments bypass ASCII +because they have no character byte. macOS passes the same records across its +plain C ABI and composites old/new panel images in Metal and Core Image. Scene +`Crt`, `Ripple` and `Glitch` bits share one full-window pass in each native GUI. + +DOM web is a separate platform, not a shader GUI: retaining selectable HTML and +CSS is more important than duplicating the renderer in canvas, so it exposes +neither effect family. `EffectCode ` writes the actual backend math, host submission, and -shader/grid source segments embedded by the build into an ordinary output -pane. This makes the implementation inspectable -after installation and makes sharing explicit: several builtins can quite -honestly print the same shader with different uniform bits. +shader or grid source segments embedded by the build into an ordinary output +pane. This makes the implementation inspectable after installation and makes +sharing explicit: several builtins can quite honestly print the same shader with +different uniform bits. + +== The ASCII fast paths + +Three loops in this codebase run once per character over text that is almost +always ASCII, and each asks two or three general functions for what arithmetic +already knows. The guard that makes elision *correct* rather than merely fast is +the same in all three, and it is a statement about Unicode: an ASCII base joins a +following combining mark, ZWJ or spacing mark into ONE cluster, and every scalar +that can do that is non-ASCII. So a printable ASCII byte followed by another +ASCII byte, or by nothing, is a complete grapheme cluster one column wide. ```zig -Pardes - ncol + col_weight[6], col_terms[6][16], col_n[6] // columns as flat arrays - panes: [16]?*Pane // slot array; id = index - active: usize - drag: Drag // none | select | move | border_v | border_h | tag - settings: runtime_config.State // theme, shell/font, display/effect choices - rects: [16]Rect // where each pane landed, this frame - panel_tracks: [16]?Track // live panes, serial-guarded - closing_panel_tracks: [16]Track // dense visual tombstones, no pane owner - presented_cells + panel_cell_diffs // acknowledged baseline + semantic diff - effects: [4096]Effect + head/len // a fixed ring +// ASCII FAST PATH. Printable ASCII is one byte, +// one cell, one column, and the general path +// below reaches that answer through a UTF-8 +// length, a decode, a freshly constructed +// grapheme iterator, a slice validation and a +// width lookup - per character. +{ + const b = text[i]; + if (b >= 0x20 and b < 0x7f and + (i + 1 == text.len or text[i + 1] < 0x80)) + { + s.set(col, y, text[i .. i + 1], style); + i += 1; + col += 1; + continue; + } +} +``` -Pane - tag: TagLine // live prefix (cwd/path) + editable tail - vt, stream: ghostty-vt Terminal and its stream — on EVERY pane, not just - terminals, which is what lets the same keys and the - same parity suite drive a file and a shell - file: ?file_pane.State / image: ?image_pane.State / pdf: ?pdf_pane.State - // payload presence is the kind; none = terminal - vweight: f32 - mode: enum { normal, insert, tty } // helix-modal; tty = raw to the pty - // `v` adds a fourth thing to DISPLAY, "select", - // which is a bool on top of normal, not a mode - cursor: absolute body position // rides the scrollback, not the screen - sel: [3]Sel + msel/vsel + sels[63] // per-button block sels, the line and - // char modal ones, and up to 64 cursors - ovl: ?term_pane.EditBuffer // ONE typed run, anchored to an absolute row - undo: two stacks, not one — term_pane.Snapshot history for an edit buffer, - // and file_pane.State history for file content - -file_pane.State = path + bytes + line index + Syn (tree-sitter highlight bytes) -image_pane.State = decoded RGBA + petscii grid cache -pdf_pane.State = MuPDF document + continuous layout + search/selection/outline - + bounded per-page raster relay + frame placement decisions +`Surface.print`. Its own comment records the reason +it exists: this function was 26% of a keystroke when profiled in the ESP32-P4's +configuration — 40×12, no tree-sitter — which was the largest single item there. +`\t`, `\r`, the C0 controls and DEL are excluded by the range test and keep their +existing handling. + +The second is `file_pane.fitEnd` (`file_pane.fitEnd`), which decides +where a soft-wrapped row breaks. It asks `modal.nextGrapheme` and +`graphemeDisplayWidth` once per character, and a 640-column line asks 640 times. + +The third is `modal.nextGrapheme` itself +(`modal.nextGrapheme`), and it is where the argument above needs its one +correction. GB3 is the single UAX #29 rule that joins two ASCII scalars: a CR +takes a following LF into the same cluster. The two range-tested loops never see +it, because `0x20..0x7e` excludes CR — but `nextGrapheme`'s fast path admits +every byte below `0x80`, so it excludes CRLF by name. It did not always, and +`graphemeStart` did: the two then disagreed about a CRLF file by exactly one +byte, a head stepped onto the offset between CR and LF, and `graphemeStart` +repaired it back onto the CR. Everything else ASCII is still O(1). + +Because `fitEnd` decides where text lands on screen, a fast path that is off by +one column *moves text*. Both of its tests therefore pin it to the general walk +it replaces rather than to a transcribed expectation: one sweeps every byte +below 0x80 against a set of neighbours at every width and start offset +(*the ASCII run in fitEnd survives an exhaustive byte sweep*), and the other runs a hand-written case list — +including `"abc\u{00e9}def"` for a hand-over mid-run, a wide glyph a width +boundary can land inside, `"a\u{0301}bc"` for a cluster the fast path must not +split, and `"e\u{0301}x"` for an ASCII byte followed by a continuation byte — +through both routes (*the ASCII run in fitEnd cuts where the grapheme walk +would*). `Surface.print` has the same +arrangement: its reference implementation is `print` with the fast-path block +deleted and nothing else changed (the test *the ASCII fast path in surface +print paints what the general arm paints*). + += Detached sessions + +#wide(caption: [A detached session. The daemon owns the core and every +machine-local resource; a frontend owns a screen, a keyboard and a clipboard. +Counts and tag numbers from `wire.ClientTag` and `wire.ServerTag`; the routing rules +from `src/detached/server.zig:34-59`.])[ + #diagram[ + #cetz.canvas(length: 1cm, { + import cetz.draw: * + set-style(stroke: 0.4pt, mark: (fill: black, scale: 0.35)) + + // ---- the daemon ---- + rect((0, 2.0), (6.4, 5.5), name: "d") + content((3.2, 5.24), text(7.4pt)[*the daemon* --- `pardes --detach[=name]`]) + line((0, 5.02), (6.4, 5.02)) + content((3.2, 4.74), text(6.0pt)[one `Pardes`; `update` is called here]) + content((3.2, 4.38), text(6.0pt)[every pty master (`host_io.forkShell`)]) + content((3.2, 4.02), text(6.0pt)[every file (`host_io.writeFileBytes`)]) + content((3.2, 3.66), text(6.0pt)[the inotify fd (`file_watch.zig`)]) + content((3.2, 3.30), text(6.0pt)[the theme dir and the acme mount]) + content((3.2, 2.94), text(6.0pt)[16 of 21 `VTable` methods implemented]) + content((3.2, 2.58), text(6.0pt)[ONE `poll(2)` --- the only sleep]) + content((3.2, 2.26), text(5.6pt, style: "italic")[pane shells outlive every frontend]) + + // ---- the socket, drawn in segments so the labels sit in the gaps ---- + line((9.3, 2.0), (9.3, 3.02), stroke: (dash: "dashed")) + line((9.3, 3.46), (9.3, 4.72), stroke: (dash: "dashed")) + line((9.3, 5.16), (9.3, 5.6), stroke: (dash: "dashed")) + content((9.3, 5.78), text(6.2pt)[`AF_UNIX`]) + + // ---- the two directions ---- + line((12.2, 4.7), (6.4, 4.7), mark: (end: "stealth")) + content((9.3, 4.94), text(6.2pt)[`ClientTag` --- 11 tags]) + line((6.4, 3.0), (12.2, 3.0), mark: (end: "stealth")) + content((9.3, 3.24), text(6.2pt)[`ServerTag` --- 8 tags]) + content((9.3, 2.48), text(5.6pt, style: "italic")[a frame per pump, per client;]) + content((9.3, 2.18), text(5.6pt, style: "italic")[never queued, only skipped]) + + // ---- frontends ---- + rect((12.2, 4.35), (18.0, 5.5)) + content((15.1, 5.24), text(6.8pt)[*frontend* --- a terminal]) + content((15.1, 4.86), text(5.8pt)[`--attach`; vaxis paints `grid`/`cursor`]) + content((15.1, 4.52), text(5.8pt)[screen + keyboard + clipboard + a link]) + + rect((12.2, 3.1), (18.0, 4.15)) + content((15.1, 3.90), text(6.8pt)[*frontend* --- an SDL window]) + content((15.1, 3.52), text(5.8pt)[same protocol, same session, same screen]) + content((15.1, 3.22), text(5.8pt)[`screen -x`, not N sessions]) + + rect((12.2, 1.85), (18.0, 2.9)) + content((15.1, 2.68), text(6.8pt)[#sym.dots.v up to `max_clients` = 32]) + content((15.1, 2.32), text(5.8pt)[the 33rd gets a `refuse .full`, not a backlog]) + content((15.1, 2.02), text(5.6pt, style: "italic")[NO FORK HAPPENS IN A FRONTEND]) + + // ---- the message tables, full width ---- + rect((0, -2.8), (18.0, 1.5)) + content((9.0, 1.24), text(7.0pt)[*every message, and which way it crosses*]) + line((0, 1.02), (18.0, 1.02)) + let row(y, body) = content((0.3, y), body, anchor: "west") + row(0.72, text(5.6pt)[frontend #sym.arrow.r core (11): `hello` `bye` `key` `mouse` `resize` `paste` `command` `pdf_scroll` `pinch` `touch_scroll` `pointer_leave`]) + row(0.32, text(5.6pt)[core #sym.arrow.r frontend (8): `welcome` `refuse` `frame` `quit` `detach` | `set_clipboard` `read_clipboard` `open_link`]) + row(-0.16, text(5.6pt)[BROADCAST --- every screen must show the same thing: `frame`, `set_clipboard`]) + row(-0.54, text(5.6pt)[ORIGIN ELSE PRIMARY --- the answer belongs to the human who acted: `read_clipboard`, `open_link`, `detach`]) + row(-0.92, text(5.6pt)[`read_clipboard`'s answer is not a reply message: it comes back as an ordinary `Event.paste`]) + row(-1.40, text(5.6pt)[the gaps `0x13`..`0x17`, `0x1e` were `output` `eof` `lsp_resp` `pipe_resp` `file_changed` `tick`: deleted, not renumbered,]) + row(-1.78, text(5.6pt)[when the daemon took the disk --- a decodable `output` let an attached peer forge a pane's text]) + row(-2.16, text(5.6pt)[never on the wire: `pull_wait_input` (it IS the poll loop), the two informationless frame pushes, the four `pull_`s]) + row(-2.56, text(5.6pt)[that answer their own caller, and `push_fs_reply` --- with N frontends, N#sym.minus 1 would get an answer they never asked for]) + }) + ] +] + +== The daemon owns the machine + +The `Pardes` instance lives in the detached process. A frontend owns a terminal +(or a window, or a serial panel) and a socket, and nothing else: it sends the +input it collects and draws the frames it is sent. One core per session, N +frontends attached to it, all looking at the same screen — `screen -x`, not N +sessions. + +The argument for putting every machine-local effect in the daemon is one +sentence: a unix socket means the core and its frontends are on the SAME machine, +so there is no question of whose disk or whose process table is meant, and given +that, the process that must hold them is the long-lived one. A shell forked by a +frontend dies with that frontend, and a session whose whole promise is outliving +the frontend attached to it cannot keep its panes that way. So the daemon forks +the pane shells, writes the files, marks the directories, and drains the pty +masters in its own `poll(2)`. The pane shells outlive every frontend: attach, +detach, kill the terminal, attach from another one, and the build that was +running in pane 3 is still running and has been scrolling into the core the whole +time. + +Of the twenty-one `VTable` methods, the detached core's `Session` implements +sixteen and leaves five null: `push_post_present`, `pull_gpio_toggle`, +`pull_lsp`, `pull_pipe`, `push_fs_reply`. + +Nothing blocks indefinitely, and that property is what a detached session is +*for*. Every descriptor is non-blocking; the single `poll(2)` is the only place +the process sleeps; and every queue that could grow without bound has a ceiling +with a stated answer for reaching it. Frames in particular are NOT queued: a +client with bytes still owed to the kernel is skipped for this frame and its +mirror is left alone, so the next frame it does get is a diff against what it +actually has. A slow frontend therefore sees fewer, larger frames instead of a +growing queue, and coalescing costs no byte surgery. What is left in a client's +out-queue is control messages, capped by `out_backlog` and checked *before* an +append, so a single oversized message still goes out whole and what gets refused +is a client that has stopped draining: it is closed, and its peers are untouched. + +== The wire + +```zig +pub const ClientTag = enum(u8) { + hello = 0x01, + bye = 0x02, + + key = 0x10, + mouse = 0x11, + resize = 0x12, + paste = 0x18, + command = 0x19, + pdf_scroll = 0x1a, + pinch = 0x1b, + touch_scroll = 0x1c, + pointer_leave = 0x1d, +}; + +pub const ServerTag = enum(u8) { + welcome = 0x01, + refuse = 0x02, + frame = 0x03, + quit = 0x04, + detach = 0x05, + + set_clipboard = 0x10, + read_clipboard = 0x11, + open_link = 0x12, +}; ``` -Layout is arithmetic, not objects: columns are weights over the width, panes are -weights over the column. `splitBelow` shrinks only the source pane; -`absorbVWeight` gives a dying pane's weight to one sibling; an emptied column -hands its width to a neighbor. Minimal motion is the invariant: an operation on -one pane may not move panes it does not touch. +`wire.ClientTag` and `wire.ServerTag`. Eleven tags in, eight out, and the shape of both +lists is the argument. + +Every `ClientTag` is something a human did: a handshake, a goodbye, and what a +keyboard, a mouse, a trackpad or a window manager produces. Six numbers are +missing from the input run — `0x13`..`0x17` and `0x1e` — and the gaps are left +rather than tidied away, because renumbering is a change every deployed frontend +feels. They were `output`, `eof`, `lsp_resp`, `pipe_resp`, `file_changed` and +`tick`: the machine-local host's own reports, which stopped being a frontend's +business when the daemon took the disk and the process table. Leaving them +decodable was not merely dead weight. `server.zig`'s `apply` routes any decoded +non-resize event straight into `core.update`, so an attached peer could forge a +pane's output, forge an `eof` for a shell that was still running — and unlike the +daemon's own `paneEof` the wire path never called `closePty`, so the master +stayed open and the shell was orphaned for the life of the session — or replace a +pane's text with bytes the next `Save` would write to disk. + +On the way out there are only three effects left, and which three is the whole +design: what a process nobody is looking at genuinely cannot do is put something +on THIS human's clipboard, read it back, and open a link in front of the person +who clicked it. The eight machine-local pushes — `spawn`, `pty_write`, +`pty_resize`, `write_file`, `write_dump`, `watch_file`, `watch_theme`, +`dump_themes` — were routed to ONE frontend precisely because each has one real +resource behind it, and every one of them is now performed in the daemon. Two +frontends can no longer fork two shells for pane 3 or race each other writing one +path, because neither of them writes anything. + +`detach` sits in the session range beside `quit` rather than among the three +effects, because it is not an effect the session performs on the world: it is one +frontend being told it is done. + +Four `VTable` methods are named in the file with their reasons for *not* being on +the wire. `pull_wait_input` IS the server's poll loop. `push_poll_frame` and +`push_post_present` carry no information — `frame` already arrives exactly once +per pump at the same place in the order, so two more messages per frame per +client would say nothing the frame does not. `pull_tty_taken`, +`pull_gpio_toggle`, `pull_lsp` and `pull_pipe` are answers the caller waits for +or work dispatched off the loop, and a round trip inside `update` is the one +thing this transport must never do. `push_fs_reply` cannot be a broadcast at all: +the transport that asked is the one holding the request, so with N frontends, +N−1 would receive the answer to a request they never made — which is why the +acme mount stays in the detached process and `Event.fs_req` has no `ClientTag`. + +=== The codec is architecture- and build-neutral + +The frontend on the other end may be riscv32-freestanding while the core is +`x86_64` linux, so: every integer is an explicit width, little +endian, and no `usize` reaches the wire; no native struct is ever blitted, because +`@bitCast` of a Zig struct puts this compiler's field order and padding on a +socket; every union and every enum gets a tag chosen in this file and never +`@intFromEnum` of a core type, with exhaustive mapping switches, so adding a +variant to `Event` is a compile error here rather than a silent protocol +redefinition; every variable-length payload carries an explicit length prefix and +`max_payload` bounds the lot; a bool is one byte, 0 or 1, and any other value is a +decode error rather than "nonzero is true"; floats travel as their IEEE-754 +binary32 bit pattern inside an explicit `u32`. + +The codec is build-neutral for the same reason: `Event.resize.cell_pixels` exists only when +native PDF placement is compiled in, and a frontend must not have to have been +built with the core's options, so it is ALWAYS on the wire and dropped on arrival +by a build with nowhere to put it. + +`version` is a `u16` checked on connect and refused loudly, because two builds of +pardes are routinely on one machine — `zig build` replaces the binary under a +running session — and a frontend decoding another version's frame layout would +paint garbage and blame the terminal. `ClientTag` is exhaustive on purpose, which +is the opposite of `fuse.zig`'s `Opcode`: there a newer *kernel* adds opcodes, +and a non-exhaustive enum is the only way to receive one without undefined +behaviour, whereas here both ends are pardes and an unknown tag is a corrupt or +hostile stream. + +`max_payload` is 16 MiB, derived rather than chosen: a full frame of the largest +grid this protocol admits (512×128) at a worst case of one run per cell is +512×128×(6+20) = 1.6 MiB, and one paste is already capped at 4 MiB by the tty +frontend (`wire.max_payload`). + +== Connect first, swap second + +```zig +if (core.takeAttach()) |req| { + var attempt = detached_client.attempt( + gpa, req.name, core.screen_w, core.screen_h); + switch (attempt) { + .greeted => |client| { + attached.* = client; + break :frames; + }, + else => { + var mbuf: [256]u8 = undefined; + core.setMessage( + req.pane, + attemptEnd(&attempt, req.name) + .row(&mbuf), + ); + }, + } +} +``` + +`tty.localSession`, and the `takeAttach` block in `src/gui/gui.zig` is the same +shape. +CONNECTING IS NOT BEING ATTACHED, which is why this asks for a *greeted* client +and not for a socket: `Client.open` writes a hello and returns, and every way a +session says no — `refuse .version` for a session built from other bytes, +`.full`, `.quitting`, or a plain `quit` from one that ended in the same round — +arrives *after* a successful `connect(2)`. A swap that trusted the connect would +already have SIGKILLed every pane shell, unmounted the filesystem and freed every +undo history by the time it decoded the refusal. + +So `attached` is set only with the welcome in hand, and until it is, nothing has +been touched: a failed `Attach` costs one message row and leaves every pane, +every shell and every undo history where it was. The teardown that follows is +the function's own defers, reached by leaving its scope. + +The ordering is enforced in three places at once. The builtin only asks +(`builtins.Attach`). `Effect.attach` is unpacked into `attach_req` and +`attach_buf` by `perform`, and what the frontend acts on is what `takeAttach` +hands back AFTER the drain, not the effect value, which dies in the loop that +read it (`drainForAttach`). And `takeAttach` is consumed from the shell's +OUTER loop, beside `takeRestore` and for the same reason: both END this core, and +nothing running inside `pump` may destroy the core it is running in +(`Pardes.attach_req`). The unit test *Attach asks for a session and tears +nothing down* asserts +exactly that — after `Attach` and a full drain, `p.quit` is false and `p.panes[0]` +is still there. + +Because a failed connect is cheap, `Attach` is the one word in the session group +that is safe to press by accident, and it is the one that gets a leader chord: +`SPC s a`. `Detach` takes `SPC s D`, a capital because `sd` has been `Dump`'s +since before there was anything to detach from. Both entries sit behind +`if (pardes.can_attach)` in `src/config.zig`, and that gate is not decoration: +the leader table may only name a builtin that EXISTS, so on macOS — hosted, but +no poller — the two words are compiled out and naming them would be a compile +error. Which is the good outcome, and the reason the predicate exists. + +`Detach` is not `Attach` backwards, and the asymmetry is deliberate. Turning a +live local session into a daemon needs `setsid` and a fork; a word that pretended +to would hand you a session that dies with the window it was typed in. Run +locally, `Detach` therefore reports rather than acts. + +== `host_io.zig`: the machine-local half + +`forkShell`, `writeFileBytes` and `writeFd`, and it is now the only copy of them: +`tty.zig`, `detached/server.zig`, `gui/gui.zig` and `macos.zig` all fork and +write through it (`src/host_io.zig`, module header). + +They did not always, and what the four copies had in common is the better +argument for the file existing than "it is shared" is: ALL FOUR were missing +`FD_CLOEXEC` on the pty master. `/dev/ptmx` is opened by `forkpty` with no +`O_CLOEXEC` and there is no flag argument to ask for one, so in every shell +pardes has ever shipped, a program in one pane could read and write another +pane's terminal. The silent half is worse and compounds: closing a master is the +only thing that hangs its shell up, and a master a later shell still holds open is +not closed, so a pane delete or a respawn left an orphaned shell that never +exited — never reaped, eventually blocked writing into a pty nobody reads — and +each orphan pinned every earlier pane's master in turn. The startup drain forks +pane 0 and then pane 1, so the arrangement existed from boot. + +`nested.setCloexec(master)` after the fork fixes it for all four callers at once, +and the file states the window that leaves rather than papering over it: `fcntl` +after `fork` is not atomic, so a thread that forks and execs between the two +syscalls inherits the master anyway. In the detached daemon there is no such +thread — it is single-threaded by construction, which is what putting the pty +masters in its own `poll(2)` bought. Closing the window in the threaded shells +means replacing `forkpty` with `posix_openpt(O_CLOEXEC)` / `grantpt` / +`unlockpt` / fork / `setsid`. + +`forkShell` takes the core it is forking on behalf of and nothing about +terminals: no vaxis, no `Loop`, no reader thread. Who drains the master is the +caller's business, and the callers answer differently on purpose — the tty, gui +and macOS shells hand it to a worker that posts into their event loop; the daemon +adds it to the one `poll(2)` it already runs and makes its own copy non-blocking +in order to. + += The board + +== An object, not a module + +`-Dplatform=esp32p4` emits ONE freestanding riscv32 object exporting the C ABI in +`src/esp32p4.zig`; the `zig_p4` package links it beside its own `_start`, its +generated linker script, and its UART driver (the `pardes-esp32p4` object +`build.zig` emits). Not an +executable, because the entry point is over there. Not a library, because +`addLibrary` bundles a `compiler_rt` the firmware already has. + +And not a module exposed through `build.zig.zon`, which is what it was first and +is the interesting part. A dependency in the OTHER direction was built and +reverted: nesting this package's roughly 30-package graph under `zig-p4`'s broke +every build in that repo, not just the firmware one. `std/Build.zig:2091` +exceeded its +1000-branch comptime quota through ghostty's `SharedDeps.zig:874` `lazyImport`, +seven cached tree-sitter versions failed to compile because their `build.zig` +uses APIs removed in 0.16, and the fetch materialised 2.6 GB across 42,736 files +into a repo whose entire claim is that Zig is its only dependency. This direction +is free: `zig_p4` declares no dependencies at all, so it enlarges nothing here, +and its `build()` early-returns when it is not the root. + +The seam stays bytes over eight C functions, because bytes are the right seam for +a serial line and because that is the arrangement the board was measured +through. `-Desp32p4-firmware` adds the other half in this tree — the firmware +executable rooted at `src/esp32p4/app.zig`, the flashable image, and the steps +that write it to a board and talk to it — and the image it produces is +byte-identical to the one the toolchain repo produces from the same sources. It +is opt-in and not out of timidity: `esp32p4.firmware()` reads ESP-IDF's register +headers at CONFIGURE time and exits non-zero when there is no checkout, so a bare +`-Dplatform=esp32p4` must not call it. + +The object is also the compile probe. Rooted at `src/esp32p4.zig` it drags the +whole core through the riscv32 backend by actually calling it, so `llvm-size` on +the result is a real number to hold against the board's 1.5 MiB factory +partition. + +Where the terminal is: on the host. The board writes ANSI and reads ANSI, and the +emulator at the far end of the serial line does the font rendering and answers +this program's own capability queries. That is why vaxis works here unmodified — +`Vaxis.render`, `queryTerminalSend` and `enableDetectedFeatures` all take a bare +`*std.Io.Writer`, so the transport is a parameter, while `vaxis.Tty` and +`vaxis.Loop` are termios/ioctl/SIGWINCH bound and are not used. Window size +arrives as DEC mode 2048 in-band resize reports, parsed by `vaxis.Parser` like +any other input, because firmware has no `TIOCGWINSZ`. + +== The memory budget is one table + +`src/limits.zig` is every board-shaped capacity in one place. These numbers used +to be nine `platform == .esp32p4` tests scattered across nine files, each one a +separate place to forget — and they are not nine decisions. They are ONE +decision, how much memory this build is allowed to spend, taken nine times where +no reader could see the total. + +```zig +/// `board` is the ESP32-P4 firmware's budget: +/// a 384 KiB heap and a 240 KiB chunk of L2MEM +/// shared between `.bss`, `.data` and the stack. +const board = config.platform == .esp32p4; +/// No OS means no address space to reserve +/// megabytes out of, whatever the platform is +/// called. +const reduced_target = + builtin.os.tag == .freestanding; + +pub const board_heap_bytes = 384 * KiB; +pub const effect_cap = if (board) 128 else 4096; +pub const wrap_rows = if (board) 128 else 256; +pub const cwd_buf_cap = if (board) 0 else 1024; +pub const undo_max = if (board) 16 else 256; +pub const max_tag_tail: usize = + if (board) 512 else 4096; +pub const host_path_cap: usize = + if (board) 0 else 4095; +pub const embedded_sources = !board; +pub const hexdump_row_bytes: u32 = + if (board) 8 else 16; +// ... `arena` below; see the wide listing +``` + +Two booleans derive all of it, and nothing outside this file tests the platform +for a capacity again. Every cap says what it is measured against, and +`board_heap_bytes` is the number the others are measured against: the 384 KiB +chunk of L2MEM at `0x4FF40000`. It is unconditional and not profile-derived, +because it is a fact about the silicon rather than a budget this build chose — a +desktop build that wants to know what the board affords is asking exactly that +question, which is what the memory tests in `pardes.zig` do with it. + +Each cap is a different *kind* of shrink, which is why they are not one scale +factor. + +- `cwd_buf_cap` and `host_path_cap` go to *zero*, and zero is a type: `Text(0)` + is a zero-sized field whose `set` refuses every non-empty path, so the three + producers — the resolved shell, the picked font file, the watched theme file — + report failure instead of storing 12 KiB nothing can fill. This is the + `PdfSlot` rule applied to a capacity: the board has no filesystem, no processes + to spawn a shell for and no font picker. +- `undo_max` and `wrap_rows` shrink *gracefully*. `pushHistory` evicts and frees + the oldest once full, so the smaller ring loses the deepest undo steps and + nothing else. `wrapWidth` reads the array's own length and refuses to wrap a + pane taller than it, so a taller pane renders unwrapped rather than getting a + truncated map. +- `effect_cap` changes *behaviour*, and the file says so: `emit` has always + refused rather than evicted once full, so on the board a burst larger than 128 + effects now drops its tail where 4096 would have held it. That is reachable + only through `emitWrite`, i.e. only if a pty ever appears on this platform. +- `embedded_sources` is a capacity spelled as rodata. The allowlist is empty on + the board, because the table is about 0.95 MiB against a 1.5 MiB partition. + The API is unchanged — `all` is a zero-length array and `find` answers null — so + every caller compiles identically and simply finds nothing embedded. +- `hexdump_row_bytes` is the one that is about the *display* rather than memory. + `hexdump -C`'s sixteen needs 79 columns; the P4 drives 56 of which seven go to + the line-number gutter, so a sixteen-byte row wraps onto a second display line + and the columns stop lining up, which is the entire value of the layout. Eight + fits in 46 and keeps every property that matters. + +#wide(caption: [`limits.arena`. Three tiers, because the address space +differs by four orders of magnitude. The board's tier is deliberately ALL +FALLBACK: every buffer here is a `StackFallbackAllocator`'s static, which lands +in `.bss`, and on the P4 `.bss`, `.data` and the stack share one 240 KiB chunk of +L2MEM while the heap is a separate 384 KiB chunk. A megabyte-shaped reservation +would not fit, and every byte that did fit would be taken from the stack's +neighbourhood to duplicate memory the heap already has. Zero is legal and always +spills, which is exactly what an arena for a compiled-out subsystem should do.])[ +```zig +pub const arena = struct { + pub const pardes = if (board) 4 * KiB else if (reduced_target) 8 * MiB else 32 * MiB; + pub const frame = if (board) 4 * KiB else if (reduced_target) 4 * MiB else 16 * MiB; + // ... + pub const tree_sitter = if (board) 0 else if (reduced_target) 4 * MiB else 16 * MiB; + pub const image = if (board) 0 else if (reduced_target) 64 * KiB else 32 * MiB; + pub const pdf = if (board) 0 else if (reduced_target or !config.mupdf) 64 * KiB else 64 * MiB; +}; +``` +] + +What does NOT belong in this table is capability switches. `terminal_panes`, +`board_memory.enabled`, `hosted` and `font_picker` answer "does this build have +the thing at all", which is a question about the platform and not about a budget, +so they stay next to the thing they gate. + +That division is also why a build option selecting the board's budget on a +desktop cannot work, and the file records the attempt. `-Dmem-profile=board` was +meant to let a native test runner compile the board's capacities and boot the +core under them. The dominant term in a boot is `@sizeOf(Pane)`, which carries +the ghostty-vt `Terminal` — 1.1 MiB of it — and what removes that is +`pardes.terminal_panes`, a CAPABILITY keyed on the platform rather than a +capacity in this table. So the option shrank the rings and left the boot six +times over budget, producing a configuration nothing was designed for: +`zig build unit-test -Dmem-profile=board` deadlocked in a futex rather than +failing, because a hosted build with the board's effect ring silently drops +effects a hosted test is waiting on. What DOES test the board's memory pressure +natively is in `pardes.zig`: the grid-scaled cost and the allocation-failure +sweep, both platform-independent and both running on the ordinary build. + +== What the board does not have + +`terminal_panes` is false there and nowhere else (`pardes.terminal_panes`), and it +is a platform gate and deliberately not one derived from the target: `web` is +freestanding too and KEEPS the emulator, because the browser shell renders a +replayed dump. False means ghostty-vt is not in the module graph at all — +`build.zig` never even asks for the dependency — which removes about 400 KiB of +flash, a `PageList` of RAM spent parsing input that cannot arrive, and a pile of +freestanding root hooks (`os.PATH_MAX`, `os.heap.page_allocator`, a cwd handle) +that the core itself does not want. + +Tree-sitter is refused outright: `-Dplatform=esp32p4` with anything but +`-Dtree-sitter=disabled` fails the build, because the grammars' parse tables are +megabytes against a 1.5 MiB partition (`build.zig:298`). MuPDF is refused for the +same reason (`build.zig:297`). + +The board gains four words nothing else has, all gated on +`board_memory.enabled == (platform == .esp32p4)`: `Peek`, `Poke`, `Hexdump` and +`Gpio`. Each takes an address or a pin, so none can have a leader path — a key +path names a builtin and can never carry an operand. `Gpio` is the one that goes +through the host seam (@seam) rather than reaching the registers directly, and +`pull_gpio_toggle`'s comment says why: driving a pad correctly is not one +register. It is the IO MUX function select, the GPIO matrix output route, the +pad's drive and input-buffer bits, and the output enable, keyed by a per-pin +table. The firmware already owns that code and checks it against ESP-IDF's own +headers on the die; a second copy in the core would be a second copy nobody +tests. + +`board_memory.zig` makes the target the *witness* rather than the gate: `enabled` +is keyed on the platform, and a `comptime` block then refuses to compile if that +platform is hosted, is not freestanding, or is wasm — because whatever else +`esp32p4` means, it has to still be a machine whose addresses are the bus's +(`board_memory.enabled`). + += The control filesystem + +== acmefs as a pure transaction + +plan9's acme serves `/mnt/acme`: a directory per window holding `addr`, `body`, +`ctl`, `data`, `event`, `tag`, and a program that opens those files IS an editor +extension — no plugin API, no embedded interpreter, no rebuild. `pardes --fs` +serves the same tree over Linux FUSE (`src/fuse.zig`), and `src/acmefs.zig` is +the whole of what the files MEAN. + +A filesystem is a request/response protocol driven by other processes, which is +exactly the kind of concurrency the core does not have and must not grow. acme +answers it with a thread per in-flight request — `xfidallocthread`, a `Channel` +per `Xfid`, a `QLock` per window. pardes cannot and should not, so `acmefs.zig` +is a pure main-thread transaction, `handle(p, req) Reply`: no thread, no waiting, +no callback, no allocation on the hot path, freestanding-safe, and unit-testable +with no FUSE anywhere near it. + +Requests arrive as an ordinary `Event.fs_req` and answers leave as an ordinary +`Effect.fs_reply`, so the transport is the queue every other host/core message +already uses. A backend with no threads at all is not a special case: it either +never sends a request, or sends one from its own frame loop. And acme's blocking +`event` read — which waits for the user to do something, parking the `Xfid` in +`w->eventx` until a later `winevent` sends it a message — is `Status.again` here: +"nothing consumed, ask me again". The waiting lives in the host, which is where +the kernel's request already is. The core keeps no waiter list and no wakeups. + +One divergence from acme is deliberate: acme counts RUNES, pardes counts BYTES, +clamped to grapheme boundaries. Every offset in this filesystem — `addr`, `data`, +the event records' q0/q1, `index`'s lengths — is a byte offset, because pardes is +byte-addressed end to end (selections, look spots, LSP offsets) and a second +coordinate system would mean an O(n) conversion at every boundary and a lossy +`addr=dot`. acme pays that cost the other way round: it keeps the document as +`Rune*` and converts on every utf read, in a function that carries a +"BUG: stupid code: scan from beginning" comment for its cache miss. The two agree +for ASCII, which is what scripts compute with. + +`Pardes.fs` is `acmefs.State`, zero-initialised and inert: a core nobody scripts +pays one branch per edit and nothing else. + +== The nested socket + +`src/nested.zig` listens on `/pardes-.sock`, where `` is +`$XDG_RUNTIME_DIR` — a per-user 0700 tmpfs the login session already cleans up — +or `~/.local/state/pardes` when the session has none. It accepts exactly one +verb, `Look [:]`, and nothing else, which is the security property. +That is how a `pardes ` run inside a pardes hands the file to the outer +session instead of stacking a second full-screen UI inside one of its panes. + +It arrives as an `Event.command` rather than a direct `executeBuiltinLine` call +so that it gets the trailing sync and the ordinary effect drain: `Look` on a +directory emits a `.spawn` the shell has to perform. + +The detached sockets live in the same per-user directory under a different name, +`pardes-detached-.sock`, and both sides derive the path from one predicate +in one file so that the side which binds and the side which connects cannot +disagree (`server.socketPath` and `server.sessionPath`). A frontend that finds a socket at +a derivable path which is not a private one of ours refuses with `NotPrivate` +rather than reporting "no session": a planted socket collects every keystroke +typed into the frontend that trusts it, and the two cases need different answers +from a human. + += Build + +#wide(caption: [The build graph. One `root_mod` per invocation, rooted at +whichever file that platform's host enters through; up to three more compilations +of the same graph beside it; one `pardes_config` per distinct *frontend*, which +is the only thing two of them are allowed to disagree about. Line numbers are +`build.zig`.])[ + #diagram[ + #cetz.canvas(length: 1cm, { + import cetz.draw: * + set-style(stroke: 0.4pt, mark: (fill: black, scale: 0.35)) + let row(y, body) = content((0.3, y), body, anchor: "west") + + // ---- tier 1: inputs, full width ---- + rect((0, 6.76), (18.0, 9.5)) + content((9.0, 9.24), text(7.0pt)[*inputs, generated or read at configure time*]) + line((0, 9.02), (18.0, 9.02)) + row(8.72, text(5.8pt)[`highlights.scm` from 29 grammars #sym.arrow.r one options module]) + row(8.34, text(5.8pt)[`vendor/themes/*.toml` (214 helix) and `*.json` (11 zed) #sym.arrow.r 225 generated theme modules plus a `list.zig`, straight into the build cache]) + row(7.96, text(5.8pt)[working-tree `.zig` sources #sym.arrow.r the web shell's read-only source archive]) + row(7.58, text(5.8pt)[8 GLSL shaders #sym.arrow.r SPIR-V through `glslc` --- or `-Dprebuilt-shaders` embeds the committed `.spv` + `.glsl` pair, so `EffectCode` cannot lie]) + row(7.24, text(5.8pt)[`@import("build.zig.zon")`, UNTYPED (`:8`) #sym.arrow.r `zon.version` (`:1920`) and a `comptime` check that `zls_version` matches the pinned sha (`:36-46`)]) + row(6.90, text(5.8pt)[`gitCommit(b)` (`:1855-1867`) #sym.arrow.r `?[]const u8`, null when there is no repository, no `git`, or nothing to say]) + + // ---- tier 2: modules and the options module ---- + rect((0, 3.8), (8.7, 6.6)) + content((4.35, 6.34), text(7.0pt)[*modules that compile* `src/pardes.zig`]) + line((0, 6.12), (8.7, 6.12)) + content((4.35, 5.84), text(5.8pt)[`root_mod` (`:306`) --- its root is the file this]) + content((4.35, 5.50), text(5.8pt)[platform's host enters through:]) + content((4.35, 5.14), text(5.6pt)[`main.zig` (tty, gui) | `web.zig` | `macos.zig` | `esp32p4.zig`]) + content((4.35, 4.74), text(5.8pt)[`hx_core_mod` --- a headless second core for `hxdiff`]) + content((4.35, 4.38), text(5.8pt)[`isolated_mod` --- one comptime bool: no filesystem]) + content((4.35, 4.02), text(5.8pt)[`gui_mod` --- the same `main.zig` at `platform = .gui`]) + + rect((9.3, 3.8), (18.0, 6.6)) + content((13.65, 6.34), text(7.0pt)[`pardes_config` --- ONE PER DISTINCT FRONTEND]) + line((9.3, 6.12), (18.0, 6.12)) + content((13.65, 5.84), text(5.8pt)[`platform`, `mupdf`, tree-sitter tier, `zls_version`,]) + content((13.65, 5.50), text(5.8pt)[`esp32p4_cols`/`rows`, `theme_animation`, `zig_lib_dir`,]) + content((13.65, 5.14), text(5.8pt)[`version` (from the manifest), `commit` (from `git`)]) + content((13.65, 4.70), text(5.8pt)[`ShellConfig` (`:1874-1890`) holds every field that is a]) + content((13.65, 4.34), text(5.8pt)[property of the BUILD, so the two modules a default build]) + content((13.65, 4.00), text(5.8pt)[cannot drift apart in any field but `platform`]) + + line((4.35, 6.76), (4.35, 6.6), mark: (end: "stealth")) + line((13.65, 6.76), (13.65, 6.6), mark: (end: "stealth")) + line((9.3, 5.2), (8.7, 5.2), mark: (end: "stealth")) + + // ---- tier 3: outputs, full width ---- + rect((0, 0), (18.0, 3.3)) + content((9.0, 3.04), text(7.0pt)[*what one invocation emits*]) + line((0, 2.82), (18.0, 2.82)) + let out(y, name, what) = { + content((0.3, y), text(6.0pt)[#name], anchor: "west") + content((4.9, y), text(5.8pt)[#what], anchor: "west") + } + out(2.52, [`pardes`], [`-Dplatform=tty`, the default --- vaxis over a real terminal]) + out(2.12, [`pardes-gui`], [`-Dplatform=gui` --- SDL3, a FreeType atlas, SPIR-V]) + out(1.72, [`pardes.wasm`], [`-Dplatform=web -Dtarget=wasm32-freestanding -Ddump=`, plus a vanilla DOM shell]) + out(1.32, [`libpardes.a`], [`-Dplatform=macos`, then the `macos-app` step #sym.arrow.r `pardes.app` (Darwin host)]) + out(0.92, [`pardes-esp32p4.o`], [`-Dplatform=esp32p4` (target forced, `:171`); `-Desp32p4-firmware` adds the image and the board steps]) + row(0.46, text(5.6pt)[a bare `zig build` emits the FIRST TWO together, because those two are what installing pardes means; every other spelling is one invocation each]) + row(0.18, text(5.6pt)[beside them, from the same graph: `pardes-isolate` (tty only --- the filesystem is not compiled in) and `hxdiff`'s headless core]) + + line((4.35, 3.8), (4.35, 3.3), mark: (end: "stealth")) + }) + ] +] + +== One primary shell, sometimes two + +`-Dplatform` names ONE shell. Absent, the build makes BOTH native shells — the +tty cli and the SDL gui — because those two together are what installing pardes +means, and asking for them one at a time is two invocations a person has to +remember are two (`also_gui` in `build.zig`). Everything else derives from `platform`, +the PRIMARY shell: the one rooted at `root_mod`, the one `unit-test` runs, and +the one the snapshot and harness suites drive. `also_gui` adds the second beside +it and changes nothing about the first. + +A bare `zig build` with an untouched prefix also redirects the install prefix, on +two conditions and the second is not the obvious one: no shell was NAMED +(`-Dplatform=web` keeps writing `zig-out/web`, which its docs name), and nothing +else has already said where to install — no `DESTDIR`, no `--prefix`, no +`--prefix-*dir`. It cannot depend on which *step* was asked for, because +`build.zig` cannot know that: `build_runner` keeps the step names in a local and +resolves them after `build()` returns. That is why the dev binaries install to +`/dev` rather than `/bin` — `zig build perf` redirects the prefix +too, and a 200 MB Debug benchmark must not land on a PATH. + +The other three platforms are separate invocations: +`-Dplatform=gui` for `pardes-gui`; +`-Dplatform=web -Dtarget=wasm32-freestanding -Ddump=` for `pardes.wasm` +plus a vanilla DOM shell; `-Dplatform=macos` plus the `macos-app` step for +`pardes.app` wrapped around a static `libpardes.a` on a Darwin host; and +`-Dplatform=esp32p4` for the freestanding object. Any other spelling of the +combination fails on purpose rather than building something silently wrong — the +web target, the web dump, MuPDF on a freestanding platform, and tree-sitter on +the board each have a named refusal (`build.zig:292-298`). The esp32p4 target is +in fact *forced* to `esp32p4_target` rather than taken from `-Dtarget`, so a +`-Dtarget` cannot discard its CPU features, and a wrong one is still an error +rather than silently overridden. + +Because `platform` is comptime and `pardes.platform` is read from +`pardes_config`, two frontends in one build need two options modules — the gui +shell `@cImport`s an SDL the cli must not link. `ShellConfig` gathers everything +`pardes_config` carries that is a property of the BUILD rather than of one +frontend into one value, so the two modules a default build makes cannot drift +apart in any field but the one that is supposed to differ +(`ShellConfig`). `hxdiff`'s headless core and `pardes-isolate` share the +primary shell's, being the same frontend. + +`pardes-isolate` is a third compilation of the same graph whose only difference +is one comptime bool. It cannot be the same binary with a flag, because the point +is that the isolated build does not CONTAIN the filesystem: `look.zig`'s libc +paths compile away, so no runtime mistake can reach a disk that a `--flag` build +still links. Only the tty platform has one — gui adds a GPU it would still need, +and web and macOS are libraries whose host owns `main()`. + +== Generated inputs + +Native dependencies are ghostty, vaxis, uucode (shared config), zstbi, SDL (a +pinned fork, lazy), FreeType, MuPDF (`-Dmupdf`, on by default everywhere but the +web and the board, lazy), ZLS, mvzr (the regex engine behind `s`/`S`), +zig-tree-sitter with 29 grammars for 28 languages (markdown takes two, block and +inline), and `zig_p4` — all pinned through `zig fetch` and wired in `build.zig`. + +Generated during the build: `highlights.scm` into an options module; the vendored +helix and zed theme sources into the generated half of the theme ring; +working-tree `.zig` sources into the web shell's read-only archive; and eight +GLSL shaders into SPIR-V. + +The themes go straight into the build cache and are handed over as a module, +rather than written back into `src/`. The output then has no freshness problem to +own — zig re-runs the generator only when a vendored file changes, and a cached +run leaves the compile's inputs byte-identical, so `zig build` with nothing +touched still does nothing — there is nothing to gitignore, and no stale `.zig` +can survive a deleted source. The INPUT list is read from the directory rather +than written out, so adding a theme is dropping a file in, and each file goes in +as a content-hashed argument, which is what makes that new file re-run the step +and nothing else. The generator is compiled in Debug on purpose: it runs for +about 40 ms, and every core module imports what it generates, so its compile is the +first link of every cold build; ReleaseSafe cost 16 s of compile to save 47 ms of +run time, and the files it writes are byte-identical either way +(`theme_gen` in `build.zig`). + +The shaders are the only build input wanting a tool a stock machine lacks +(`glslc`), so they are also the only one whose output is committed: +`zig build shaders` refreshes each paired `shaders/prebuilt/*.spv` binary and +`.glsl` source snapshot together. `-Dprebuilt-shaders` embeds that exact pair +rather than shelling out, so `EffectCode` cannot describe different shader text +from the binary on screen; this is also what lets a gui build need nothing but a +C toolchain. + +The tutor and the embedded font are plain `@embedFile`s, not codegen. Debug builds +are incremental for the seconds-loop; release builds are the product. + +== Versioning + +There is one version string in the tree and it is in the manifest. + +```zig +// build.zig:8 +const zon = @import("build.zig.zon"); +// ...in shellOptions, build.zig:1915-1923: +o.addOption([]const u8, "version", zon.version); +o.addOption(?[]const u8, "commit", cfg.commit); +``` + +The `@import` is UNTYPED on purpose, and that is what makes it possible: +annotating its type would demand an exact field match and reject +`.dependencies`, `.paths` and the rest, which is the failure the hand-synced +literal that used to sit there was working around. + +```zig +// src/pardes.zig +pub const version = @import("pardes_config").version; +pub const commit: ?[]const u8 = + @import("pardes_config").commit; + +// src/main.zig +const version_text = if (pardes.commit) |c| + "pardes " ++ pardes.version ++ " (" ++ c ++ ")\n" +else + "pardes " ++ pardes.version ++ "\n"; +``` + +Both halves are comptime, so `version_text` is one string in rodata and +`pardes --version` is one write (`main.version_text`). + +`gitCommit(b)` runs +`git -C rev-parse --short=12 HEAD` at CONFIGURE +time, so the binary carries a string rather than the ability to shell out. That +is the whole point: a `--version` that runs `git` itself reports the tree it +happens to be standing in rather than the one it was built from, and on the board +there is no `git` to run and no process to run it with. The `-C` is not +decoration either — `zig build` may be run from anywhere, and a `git` resolved +against the cwd would cheerfully answer about a different repository. + +Absence is not an error and must not be. A release tarball has no `.git`, a +container may have no `git` binary, and a source drop is not a repository; every +way of having no answer lands on the same `null` and the frontends print the +version alone. It is deliberately NOT `--dirty`: marking a dirty tree would cost +a worktree stat on every configure and — the real cost — would change +`pardes_config` on every file edit. Every module in the build imports that +options module, so a dirty marker means editing one line rebuilds the world. The +commit alone changes only when a commit does. + +The manifest is read at comptime twice, and the second read is what keeps the +one string this build cannot fold into the manifest honest. `zls_version` +(`zls_version`) has to be spelled beside `.dependencies.zls.url`, because the +semver half of it — `0.16.1-dev` — exists nowhere in the manifest, and ZLS's own +`build.zig` needs it: that file otherwise shells out to `git describe`, which +fails on a fetched package with no `.git`. So the duplication is unavoidable and +a `comptime` block beside it makes the two AGREE instead: it takes the +short commit after the `+`, takes the sha after the `#` in +`zon.dependencies.zls.url`, and `@compileError`s unless the pinned sha starts +with it. A `.zon` bump that forgets the line beside it is now a build error +rather than a `SPC l i` naming an analyser nobody linked. + += Testing: the old program is the oracle + +Before the rewrite compiled, the prototype got a harness (`test/snapshot.zig`, +the `zig build snap` step): it forks either binary in a pty, feeds it an *event +script* (one line per input: keys, SGR mouse, resizes, sync points), and captures +the rendered grid — text, cursor, and per-cell style runs — through its own +ghostty terminal. Goldens are generated from the old binary +(`zig build snap -- --update`); the new binary must reproduce them byte for byte. +Eighteen scripts covered the checklist in Appendix A at the rewrite; ninety-five +cover it and everything since (`test/snapshots/` holds 95 `.snap`/`.golden` +pairs). All pass. + +Determinism pins: fixed workdir paths (they appear in tags), a controlled `$HOME` +with `PS1='$ '`, `LC_ALL=C`, and a grid-stability sync primitive instead of +timing guesses. + +Two of those pins were doing damage rather than work. + +== Deltas, not screens + +A capture used to restate the whole screen, so 82% of golden lines were a copy of +the line above (`test/snapshot.zig:59`), every golden carried the topbar and a tag +row, and adding one builtin word to a tagline rewrote 78 of them — 3,758 lines of +diff for a change no test was about. A capture is now a *delta* against the +previous capture of the same kind in the same script: the first is the whole +screen, the rest are only the rows that changed. The corpus went from 16,700 lines +to 5,722 and the same one-word edit now moves 337 (`src/CHANGELOG.md:9-13`). A +capture whose only change is the cursor is the empty delta its script always +meant. + +== A click names a word + +A click used to name a screen column, which is a coordinate into that same +chrome. When `Newtty`, `Joincol` and `Changelog` were added, seven scripts began +clicking the word next door. `snapshot.zig`'s own note enumerates six of them: +`tutor.snap` clicked `Grep` where it meant `Tutor`, `exec.snap`, `respawn.snap` +and `tagalign.snap` clicked `Newtty` where they meant `Del`, `find.snap` clicked +`Joincol` for `Find`, and `windowops.snap` clicked `Tutor` for `Debug` — and +`--update` blessed all of it, so 256 golden lines were green while asserting the +opposite of their script's first line (`test/snapshot.zig:817-825`). The seventh +is the changelog's: `tagbottomimage.snap` clicked blank space 176 columns from +the `Del` whose effect it asserted (`src/CHANGELOG.md:9-11`). A click may now +name the word (`press middle @Del 2`, `@Del#2` for the second pane on a row, +`@Save-2` for a column beside one), so the word is either there to be clicked or +the script fails. + +== Regeneration verifies itself + +Regeneration was the other half of that failure: `--update` captured once, +serially, and wrote whatever it saw, which is how six wrong clicks became +goldens. It now captures in parallel at the widest probe settings the harness +has and then runs the ordinary verify pass over what it wrote, so a capture that +does not reproduce is reported instead of committed — 77 s to 41 s, and the retry +machinery serial update never had (`src/CHANGELOG.md:14-16`). + +== Deviations kept after review + +Three deliberate deviations surfaced by the oracle and kept. + +The greeting `ls` waits for the exact OSC 133 B input mark after the real resize; +the prototype raced bash's startup and won only by allocator luck. Shells without +prompt integration omit that cosmetic greeting rather than guessing. Commands +which create a fresh shell are owned by its terminal pane until the host reports +the actual prompt capability, then wait for the same mark when it exists. + +Dump files compare with base64 pty history elided: it encodes prompt-redraw +micro-timing, not state, and the cleaned text fields are the contract. + +Typed insert runs do not survive a dump replay. They are an overlay, not pty +bytes, and the prototype's replay viewer had the same semantics. + +== The shell suites + +Shell correctness is no longer out of scope, and that is the biggest change to +this section since the rewrite. `web-snap` and `web-e2e` drive headless Chromium +over CDP with real DOM pointer and touch events and diff `test/web-snapshots/`; +`image-harness` and `pdf-harness` snapshot native PIXEL output through kitty +graphics and SDL; `macos-e2e` is an offscreen AppKit snapshot suite over its own +seven scripts (`test/macos-snapshots/`: boot, cwd, drop, font, keys, rotate, +trackpad). `esp32p4-test` runs an on-die suite on real hardware, and +`esp32p4-image-size` and `esp32p4-image-check` answer the flash-budget question +with no board attached. + +Two suites are differential rather than golden: `hxdiff` compares the core's +motion and operator results against helix case by case, and `hxparity` compares +editing a file against editing the same text in a pty — which is what the +per-pane ghostty-vt `Terminal` buys. + +What remains untested by a snapshot is the last hop — that vaxis diffs correctly +onto a real terminal — plus each shell's inline unit tests (the FreeType atlas +raster, the trackpad and rotation maths, the gamepad replay). = Style @@ -272,126 +1955,33 @@ asset moves. Measure the current tree when making that comparison; keep this document about ownership and invariants that should survive the next edit. Two things that number is not. It is not one program's worth of growth — the -macOS and web shells and the PDF and language work are four products sharing a -core. And it is not licence: the rule that survives is the local one, that a -change should leave the file it touches no longer than it found it. - -= Testing: the old program is the oracle - -Before the rewrite compiled, the prototype got a harness (`snapshot.zig`, the -`zig build snap` step): it forks either binary in a pty, feeds it an *event -script* (one line per input: keys, SGR mouse, resizes, sync points), and -captures the rendered grid — text, cursor, and per-cell style runs — through -its own ghostty terminal. Goldens are generated from the old binary -(`zig build snap -- --update`); the new binary must reproduce them byte for -byte (`pardes-snap pardes/zig-out/bin/pardes`). Eighteen scripts covered the -checklist in Appendix A at the rewrite; ninety-five cover it and everything -since. All pass. Determinism pins: fixed workdir paths (they -appear in tags), a controlled `$HOME` with `PS1='$ '`, `LC_ALL=C`, and a -grid-stability sync primitive instead of timing guesses. - -Two of those pins were doing damage rather than work. A capture used to restate -the whole screen, so 82% of golden lines were a copy of the line above, every -golden carried the topbar and a tag row, and adding one builtin word to a -tagline rewrote 78 of them — 3,758 lines of diff for a change no test was -about. A capture is now a *delta* against the previous capture of the same kind -in the same script: the first is the whole screen, the rest are only the rows -that changed. The corpus went from 16,700 lines to 5,722 and the same one-word -edit now moves 337. A capture whose only change is the cursor is the empty -delta its script always meant. - -And a click used to name a screen column, which is a coordinate into that same -chrome. When `Newtty`, `Joincol` and `Changelog` were added, seven scripts began -clicking the word next door — `tutor.snap` clicked `Grep`, `exec.snap` clicked -`Newtty` where it meant `Del`, `tagbottomimage.snap` clicked blank space 176 -columns from the `Del` whose effect it asserted — and `--update` blessed the -result, leaving 256 golden lines green while asserting the opposite of their own -first line. A click may now name the word (`press middle @Del 2`, `@Del#2` for -the second pane on a row, `@Save-2` for a column beside one), so the word is -either there to be clicked or the script fails. +macOS, web and firmware shells and the PDF and language work are several products +sharing a core. And it is not licence: the rule that survives is the local one, +that a change should leave the file it touches no longer than it found it. -Regeneration was the other half of that failure: `--update` captured once, -serially, and wrote whatever it saw, which is how six wrong clicks became -goldens. It now captures in parallel at the widest probe settings the harness -has and then runs the ordinary verify pass over what it wrote, so a capture that -does not reproduce is reported instead of committed (77s to 41s, and the retry -machinery that serial update never had). - -Three deliberate deviations surfaced by the oracle, kept after review: the -greeting `ls` waits for the exact OSC 133 B input mark after the real resize -(the prototype raced bash's startup and won only by allocator luck); shells -without prompt integration omit that cosmetic greeting rather than guessing. -Commands which create a fresh shell are owned by its terminal pane until the -host reports the actual prompt capability, then wait for the same mark when it -exists. Dump files compare with base64 pty -history elided (it encodes prompt-redraw micro-timing, not state — the cleaned -text fields are the contract); and typed insert runs don't survive a dump -replay (they are an overlay, not pty bytes — the prototype's replay viewer had -the same semantics). += What is deliberately absent -Shell correctness is no longer out of scope, and that is the biggest change to -this section since the rewrite. `web-snap` / `web-e2e` drive headless Chromium -over CDP with real DOM pointer and touch events and diff -`test/web-snapshots/`; `image-harness` and `pdf-harness` snapshot native PIXEL -output through kitty graphics and SDL; `macos-e2e` is an offscreen AppKit -snapshot suite over its own seven scripts. What remains untested by a snapshot -is the last hop — that vaxis diffs correctly onto a real terminal — plus each -shell's inline unit tests (the FreeType atlas raster, the trackpad and rotation -maths, the gamepad replay). +No render abstraction over the shells: the surface *is* the abstraction. No +plugin system, because the control filesystem is the extension point +(@fs) and needs no API of its own. No async runtime in the core — the +shells may thread, the core is single-threaded by construction. -= Build +No 9P. The library boundary is exactly where acme put the file server, and the +FUSE mount is already that server with a Linux transport instead of a 9P one; a +sixth shell could serve `Surface` and `Event` over 9P without touching the core. -Every `zig build` builds ONE platform: `platform` is an option defaulting to -`.tty`, so a bare `zig build` gives `pardes` (vaxis) and the other three are -separate invocations — `-Dplatform=gui` for `pardes-gui` (SDL3), -`-Dplatform=web -Dtarget=wasm32-freestanding -Ddump=` for -`pardes.wasm` plus a vanilla DOM shell (any other spelling of that one fails on -purpose rather than building something silently wrong), and `-Dplatform=macos` -plus the `macos-app` step for `pardes.app` wrapped around a static -`libpardes.a` on a Darwin host. Native dependencies are ghostty, vaxis, uucode (shared config), -zstbi, SDL (a pinned fork, lazy), FreeType, MuPDF (`-Dmupdf`, on by default -everywhere but the web, lazy), ZLS, mvzr (the regex engine behind `s`/`S`), and -zig-tree-sitter + 29 grammars for 28 languages (markdown takes two: block and -inline) — all pinned -through `zig fetch` and wired in `build.zig`. Generated during the build: -`highlights.scm` → an options module; the vendored helix/zed theme -sources → the generated half of the theme ring; working-tree `.zig` sources → -the web shell's read-only archive; eight GLSL shaders → SPIR-V. That last one is -the only build input wanting a tool a stock machine lacks (`glslc`), so it is -also the only one whose output is committed: `zig build shaders` refreshes each -paired `shaders/prebuilt/*.spv` binary and `.glsl` source snapshot together. -`-Dprebuilt-shaders` embeds that exact pair rather than shelling out, so -`EffectCode` cannot describe different shader text from the binary on screen; -this is also what lets a gui build need nothing but a C toolchain. The -tutor and the embedded font are plain `@embedFile`s, not codegen. Debug builds are -incremental for the seconds-loop; release builds are the product. +"No config files" held until the startup file (`docs/config.md`) arrived, and that +is the narrowest thing the phrase could still cover: a list of builtin COMMANDS +run before the first frame — no schema, no new vocabulary, and no key remapping. +The keymap is `src/config.zig`, compiled in, where a wrong binding is a compile +error rather than a silent no-op. -= What is deliberately absent +#heading(numbering: none)[Appendix A: feature parity checklist] -No render abstraction over the shells (the surface *is* the abstraction). No -plugin system. No async runtime in the core — the shells may -thread, the core is single-threaded by construction. No 9P yet — but the -library boundary is exactly where acme put the file server, and a fifth shell -could serve `Surface` and `Event` over 9P without touching the core. Half of -that is already built and unnamed: `src/nested.zig` listens on a unix socket at -`$XDG_RUNTIME_DIR/pardes-.sock` (`~/.local/state/pardes` without one), -accepting exactly one verb — `Look `, and nothing else, which is the -security property — and that is how -a `pardes ` run inside a pardes hands the file to the outer session -instead of stacking a second full-screen UI inside one of its panes. - -"No config files" held until the startup file (`docs/config.md`) arrived, and -that is the narrowest thing the phrase could still cover: a list of builtin -COMMANDS run before the first frame — no schema, no new vocabulary, and no key -remapping. The keymap is `src/config.zig`, compiled in, where a wrong binding -is a compile error rather than a silent no-op. - -= Appendix A: feature parity checklist - -From the prototype survey; every line is covered by at least one of the -event scripts in `test/snapshots/` — the original eighteen (boot, tty, edit, -scroll, modal, look-file, look-dir, exec, tag, theme, tutor, windowops, dump, -load, ttyonly, syntax, fileedit, images), and sixty-nine more added since for +From the prototype survey; every line is covered by at least one of the event +scripts in `test/snapshots/` — the original eighteen (boot, tty, edit, scroll, +modal, look-file, look-dir, exec, tag, theme, tutor, windowops, dump, load, +ttyonly, syntax, fileedit, images), and seventy-seven more added since for everything below that the prototype never had. *Layout.* Columns by weight (≤6), panes by vweight (16 panes in total, not @@ -401,6 +1991,7 @@ the source (cursor row kept visible); `Newcol` takes width only from the source column and cannot resize any unrelated column; dying pane's weight absorbed by one sibling; emptied column hands width to a neighbor; border-drag resize on a pane's own trailing edge (v and h), hover shows `╎`/`╌` glyph-only hints; +corner grab moves exactly two boundaries; move-drag via the gutter box with preview; Alt-n new shell below, Alt-c move pane to new column; Ctrl-w h/j/k/l directional focus; body-normal Esc hops to the pane you were in before this one, alternating between two (the same @@ -465,8 +2056,8 @@ reports `img petscii: palette: ascii: ` before the ordinary tail (its renderer toggles are builtins under `SPC t p/l/a`). Topbar: -`New Newcol Find Grep Help Tutor Dump NextColor Debug Kill` — execute-only -(left click inert); +`New Newcol Joincol Find Grep Help Changelog Tutor Dump NextColor Debug Kill` — +execute-only (left click inert); Colors and Crt left it for their leader paths. *Panes.* Terminal: ghostty-vt, 16 MiB scrollback, OSC 133 prompt semantics, @@ -522,7 +2113,19 @@ parent-directory/rename-over semantics. `DumpThemes` materializes the compiled ring under `/themes/builtin/`, providing the schema and a copyable starting point without adding inheritance or a second theme vocabulary. Colors toggles -all recolor passes; Debug stats overlay; Dump writes state ZON. +all recolor passes; Debug stats overlay; Dump writes state ZON. Eleven panel +transitions and three scene bits are settings, one builtin each, generated from +`runtime_config.settings`. + +*Session.* `--detach[=name]` runs a core with no terminal; `--attach[=name]` +makes a thin frontend over a unix socket; `Attach [name]` (`SPC s a`) hands a +running frontend's screen to a detached core, connecting before it swaps; +`Detach` (`SPC s D`) leaves a session that carries on. Up to 32 frontends on one +session, all showing the same screen; the pane shells belong to the daemon and +outlive every frontend. `--fs` serves the acme control tree over FUSE; a nested +`pardes ` hands its argument to the outer session over the per-pid socket. +`pardes --version` prints the manifest version and the commit it was configured +from. *Web (replay viewer parity).* Embedded dump; focus, border/move drags, wheel, scrollbar, Colors/NextColor, and link-LOOK opening a new @@ -547,3 +2150,11 @@ surface styles with CSS. The browser `.snap` harness drives real Chromium touch input and reads both text and per-cell styles from the DOM renderer's packed surface. Joystick cursor for the steamdeck is *new* scope — the prototype ships no gamepad input. + +*Board (ESP32-P4).* A 56×14 grid by default (`-Desp32p4-cols`/`-rows`), 384 KiB +of heap, no ptys, no tree-sitter, no MuPDF, no filesystem of its own, and no +embedded source table. vaxis unmodified over a byte sink the firmware supplies; +DEC mode 2048 for resizes. `Peek`, `Poke`, `Hexdump` (eight bytes per row) and +`Gpio` exist only here, and `Gpio` goes through the host vtable because the pad +sequence belongs to the firmware that already tests it against ESP-IDF's headers +on the die. diff --git a/docs/detached.md b/docs/detached.md new file mode 100644 index 00000000..b4dde59c --- /dev/null +++ b/docs/detached.md @@ -0,0 +1,324 @@ +# Detached sessions + +One pardes core with no terminal of its own, and any number of thin frontends +attached to it over a unix socket. The core holds every piece of state — the +text, the undo history, the layout, the pane shells — and outlives every +frontend that comes and goes. + +The model is the ESP32-P4 serial console, which is why it is worth naming. On +the board, pardes runs as firmware and the host side is a dumb wire: keystrokes +in, bytes out, and the console performs no effects at all. A detached session is +that arrangement with a typed wire instead of raw ANSI: input in, cells out, and +a frontend that does almost nothing. + +## Using it + + pardes --detach # a core named by this process's pid + pardes --detach=work # ...named `work` + pardes --attach # become a frontend of the one session there is + pardes --attach=work # ...of `work` + pardes-gui --attach=work # the SDL window is a frontend too + +And from inside a running editor, as ordinary acme words — type one in a tag and +execute it, or press its leader chord: + + Attach # hand this window to the session there is + Attach work # ...to `work` (chord: SPC s a) + Detach # leave the session, and leave it running + # (chord: SPC s D) + +`Attach` switches **in place**: the window, the terminal and the process stay, +and what changes is where the state lives. The ordering is the feature — see +[The in-place switch](#the-in-place-switch). + +`Detach` is its counterpart and the smaller of the two: only the frontend that +ran the word leaves. The session, its pane shells and every other attached +frontend are untouched, so leaving is a success — the terminal prints where to +come back to and exits 0. It routes ORIGIN-ELSE-PRIMARY, the same rule +`read_clipboard` takes, because it answers something one particular human just +did. + +In a session with nothing to detach from, `Detach` says so on the pane's message +row and does nothing. That falls out of the design rather than being special- +cased: a local shell leaves `push_detach` null on its vtable, and `perform` +reports `NotAttached` for a null method. `Detach` does NOT turn a local session +into a daemon — that is the true inverse of the in-place switch, it needs real +daemonisation, and it is deliberately not this feature. + +Bare `--attach` and bare `Attach` mean "the session that is there", because +bare `--detach` names itself by its own pid and nobody can be expected to read +a pid out of `$XDG_RUNTIME_DIR`. With exactly one session listening that is the +one meant; with none or several, `detached_client.resolve` says which case it is +rather than picking one. + +## Where the socket lives + +`nested.socketDir`: `$XDG_RUNTIME_DIR`, else `~/.local/state/pardes`, created +`0700` by `nested.ensureSocketDir` — never `/tmp`, because this socket carries +keystrokes into a live editor, and a world-writable directory means both that +somebody else can plant a listener at a path you will derive and that a file +they planted cannot be unlinked. `server.socketPath` spells the name +`pardes-detached-.sock` and refuses a name that is empty or holds a `/` +or a NUL, because either would move the address somewhere else. The prefix +differs from `nested.socketPath`'s `pardes-.sock` so that nested.zig's +sweeper, which recognises only an all-digit pid, can never unlink a live +session called `work`. + +`bind(2)` decides who owns a name, because on a unix socket it is an atomic +exclusive create. `listen` does not unlink first: a name whose socket ANSWERS +is a live session and the bind is allowed to fail, and the only file this +process removes is one `alive` proved dead — which it says only of a connect +that was REFUSED. An unconditional unlink-before-bind is how a second +`--detach=work` used to take the socket away from every frontend attached to +the first. The window `alive` cannot see is stated in its own comment: a +session between its `bind` and its `listen(2)` also answers ECONNREFUSED, it +is two syscalls wide, and the loser of that race loses a NAME rather than a +session. + +Both ends vet, through one predicate — `server.zig` `vetted`, which asks `ours` +three questions of the DIRECTORY and then the same three of the SOCKET: the +right file type, our uid, and nothing granted to group or other. A frontend +that checks only one of the two has checked neither. `Client.open` asks +`access(F_OK)` before it vets, so a mistyped session name reports `NoSession` +rather than `NotPrivate` — the latter promises that the socket IS there and is +reachable by somebody else, which is a different sentence to say to a human. + +## What the daemon owns + +**Everything with an operating system under it.** The eight effects that were +once routed to one frontend to perform — `push_spawn`, `push_pty_write`, +`push_pty_resize`, `push_write_file`, `push_write_dump`, `push_watch_file`, +`push_watch_theme`, `push_dump_themes` — are performed by the daemon itself, +through `src/host_io.zig` and `src/file_watch.zig`. + +This is the whole design and it is worth stating why. A unix socket means the +core and its frontends are on the same machine, so there is no question of whose +disk or whose process table is meant. Given that, the pane shells belong to the +long-lived process: a shell forked by a frontend dies with that frontend, and +then the session has a pane with no shell in it — which contradicts the one +promise a detached session makes. It also meant `tty.zig` had to reimplement the +shell's own Host, so `spawn`, `writeFile`, `watchFile` and `dumpThemes` each +existed twice in that file, and a second frontend would have been a third copy. + +A consequence worth knowing: the daemon forks its pane shells whether or not +anybody is attached. Start a session, attach nothing, and `ps --ppid ` +already shows a shell. + +The daemon is **single-threaded**. Its pane pty masters and its one inotify +descriptor live in the same `poll(2)` that accepts frontends — +`poll_slots = 1 + max_clients + MAX_PANES + 1` = 50 descriptors — so there is no +thread per pane and no thread per client. Pty output enters the core as +`core.update(.{ .output = ... })` out of a stack buffer, so a chunk is never +duplicated. Dead shells are reaped with `waitpid(-1, WNOHANG)`. + +Two capabilities exist only because the ptys are here: `pull_tty_taken` can +answer whether a pane's shell has a full-screen program in it (so an `Exec` is +typed into vim instead of at the shell), and `push_poll_frame` reports each +pane's live cwd to its tag. A frontend could do neither — it had the pid but no +core to report to. + +## What a frontend does + +Input and screen, and exactly three effects: + +| message | routing | what the frontend does | +|---|---|---| +| `set_clipboard` | broadcast | put it on **this** display's clipboard | +| `read_clipboard` | origin, else primary | read this display's clipboard, send it back as an ordinary paste | +| `open_link` | origin, else primary | open it in **this** display's browser | + +Those three survive on the wire because each needs the human's own display and +cannot be done by a process nobody is looking at. Everything else the frontend +receives is a `frame`. It never forks a shell, writes a file, or watches a path. + +`read_clipboard` and `open_link` go to the frontend whose event was applied most +recently, because both answer something a human just did: the paste must come +from the keyboard that asked for it, and a link must open in front of the person +who clicked it. The fallback to primary — the lowest attached slot, i.e. the +oldest surviving attachment — covers an effect no input caused. + +## The wire + +`src/detached/wire.zig`, protocol `version` 1, checked on connect and refused +loudly, because `zig build` replaces the binary under a running session and a +frontend decoding another version's frame layout would paint garbage and blame +the terminal. Every message is `tag:u8, len:u32le, payload[len]` — `header_len` +is 5 — and `max_payload` is 16 MiB, a bound derived from the two messages that +set it: a full frame of the largest grid the protocol admits (`max_cols` 512 by +`max_rows` 128, worst case one run per cell, about 1.6 MiB) and one paste, +which the tty frontend already caps at 4 MiB. + +**Core to frontend: eight messages** (`ServerTag`), and the split between the +two ranges is the design rather than housekeeping: + + # 0x01..0x0f — SESSION CONTROL. Not effects; the session talking about + # itself and about this connection's membership of it. + welcome = 0x01 refuse = 0x02 frame = 0x03 quit = 0x04 detach = 0x05 + + # 0x10.. — one `push_` method each, in Host.VTable's own order. Only the + # three that need THIS human's display are here; see "What the daemon owns". + set_clipboard = 0x10 read_clipboard = 0x11 open_link = 0x12 + +**Frontend to core: eleven** (`ClientTag`), and the whole set is a handshake, a +goodbye, and what a keyboard, a mouse, a trackpad or a window manager produces: + + hello = 0x01 bye = 0x02 + + key = 0x10 mouse = 0x11 resize = 0x12 paste = 0x18 + command = 0x19 pdf_scroll = 0x1a pinch = 0x1b + touch_scroll = 0x1c pointer_leave = 0x1d + +Six numbers are missing from that input run — `0x13..0x17` and `0x1e` — and the +gaps are left rather than tidied away, because renumbering is a change every +deployed frontend feels. They were `output`, `eof`, `lsp_resp`, `pipe_resp`, +`file_changed` and `tick`: the machine-local host's own reports, which stopped +being a frontend's business when the daemon took the disk and the process +table. Deleting them was not housekeeping either. `server.zig` `apply` routes +any decoded non-resize event straight into `core.update`, so while those tags +decoded an attached peer could forge a pane's output, forge an `eof` for a +shell that was still running — and unlike the daemon's own `paneEof` that path +never called `closePty`, so the master stayed open and the shell was orphaned +for the life of the session — or replace a pane's text with bytes the next +`Save` would write to disk. + +The format is deliberately **architecture-neutral**: explicit little-endian +widths, no `usize` anywhere, no struct blits, a length prefix on every slice, +tags chosen in this file rather than taken from `@intFromEnum` of a core type, +floats as their binary32 bit pattern inside an explicit `u32`, and a bool that +is 0 or 1 and a decode error otherwise. A pointer-sized field would be 4 bytes +on a 32-bit frontend and 8 here, so none is sent. It is **build-neutral** for +the same reason: `Event.resize.cell_pixels` exists only in a build with native +PDF placement compiled in, so it is always on the wire and dropped on arrival +by a build with nowhere to put it. Today both ends are x86-64 Linux; the +neutrality is what makes a riscv32 end possible later without a format change. + +Frames are diffs against what a client actually has. A client with bytes still +owed to the kernel is **skipped** for this frame and its mirror is left alone, +so a slow frontend sees fewer, larger frames rather than a growing queue. + +## The in-place switch + +`Attach` is a core-side word that emits `Effect.attach{pane, name}`; the frontend +drains it with `Pardes.takeAttach()` beside the existing `takeRestore()`. No host +method performs it, because attaching replaces the core the call is running +inside — so a shell that never polls simply cannot attach, and `host.zig` needed +no change. + +**Connect first, swap second.** The frontend opens the client and, only on a +handshake that actually succeeded, tears the local session down — reaping pane +shells, closing watches, unmounting the control filesystem, deinitialising the +core — and enters the thin attached loop. On any failure it changes *nothing*: +the message row on the pane that ran the word says why, and the editor carries +on locally with every pane and its undo history intact. A failed `Attach` is a +no-op, never a half-dead editor. + +## Fairness, and why no client can stall another + +* Every descriptor is non-blocking; one `poll(2)` per pump covers all of them. +* Frames are not queued (above), so coalescing costs no byte surgery. +* A client's out-queue holds control messages and is capped at 1 MiB + (`out_backlog`). The cap is checked *before* an append, so an oversized + message still goes out whole and what gets refused is a client that has + stopped draining: it is closed, its peers untouched, and it may reattach and + be sent a full frame. +* The TABLE is accounted too, not just each slot: `session_backlog` bounds every + in-queue and out-queue together, and a drained client hands back anything + above one `read_chunk` (`idle_retain`, 16 KiB). Its value is *derived* — + `2 * wire.max_payload`, 32 MiB — and the derivation is the fix. It used to be + a literal `4 << 20`, which was by coincidence exactly tty.zig's + `max_paste_bytes`; since a client's `in` grows to hold one WHOLE message, a + frontend assembling the very paste `max_payload` is sized for crossed the + table's ceiling *while still receiving it*, and the session closed its only + frontend mid-paste with the diagnostic for a peer that had stopped reading. +* A PANE is not a client, so it cannot be closed to reclaim anything. Its + shell's input is queued behind a POLLOUT on the descriptor already in the set, + bounded by `pty_backlog` (1 MiB), and past that the write is REFUSED and said + out loud on the pane's own message row — dropping input silently loses half a + command line, and killing a shell to reclaim a megabyte destroys work. Before + this, one `write(2)` to a master could park the whole daemon: `sleep 3600` + plus a paste larger than the pty's 4 KiB input buffer meant no frame to any + frontend, fifteen other masters unread, no `accept`, no inotify drain. +* `max_clients` (32) is a **refusal**, not a queue. The listener is always + accepted from even when the table is full, so the refusal can be spoken — a + level-triggered `poll` on a backlog nobody accepts returns ready forever and + spins a core. A connection that never says `hello` also loses its slot, after + `greet_deadline_default_ms` (5 s), the one number both ends of this transport + time the handshake against; a slot held by silence is the same denial as a + queue arrived at from the other end. +* A peer speaking another protocol version gets `refuse(version)` and the + session survives. So does a peer that sends a byte the decoder does not know. + +## Limits + +Stated rather than papered over: + +* **The screen is shared, at the smallest common grid.** Two frontends of + different sizes converge on the smaller; the larger window letterboxes. Same + semantics as tmux. +* **No LSP, no selection pipe, no `--fs` control filesystem** in a detached + session. The daemon implements **sixteen** of `Host.VTable`'s **twenty-one** + methods — FEWER than the tty and SDL shells, which install nineteen each, + everything but `pull_gpio_toggle` and `push_detach` — and it is the only host + that implements `push_detach` at all. The five it leaves null divide cleanly. + Three are real losses: `pull_lsp` and `pull_pipe` want a + worker pool this deliberately single-threaded loop has not got, and + `push_fs_reply` wants a `/dev/fuse` this process never mounted. They fall back + to the core's in-process defaults rather than failing, so the features are + quiet rather than broken. The other two are not losses at all: there is no + moment "after the frame is on screen" for a process with no screen + (`push_post_present`), and no pads to toggle on a PC (`pull_gpio_toggle`). +* **`--fs` is inert rather than refused.** `main.zig` hands `opts.fs` to + `server.run`, which imports no `fs_service` and mounts nothing, and `spawn` + passes a null mount directory to `host_io.forkShell`. So + `pardes --detach --fs` gives a session with no control filesystem, no + `PARDES_FS` in its pane shells, and no diagnostic saying so. +* **Frontends are the terminal and the SDL window only.** `main.zig` dispatches + `--attach` to `tty.run` and `gui.run`, and refuses any other platform with + `pardes: --attach needs the tty or gui shell`. The other three shells — + `-Dplatform=web`, `-Dplatform=macos`, `-Dplatform=esp32p4` — are entered by + their own hosts and never link that file at all. +* **Linux.** The code carries darwin branches (`sun_path` is 104 there rather + than 108, and SIGPIPE is per-socket rather than per-write), but only Linux is + built and tested. +* A pane's shell is the daemon's child, so `Kill` in a frontend ends a shell for + everybody attached. That is what one shared session means. +* **An attached window does not get the session's font.** `Font ` is a + core setting raised through `takeFontRequest`, and an attached SDL frontend + has no core to raise it — so a window that would load `MartianMono-NrRg` + locally keeps its embedded Adwaita Mono when attached, and its cell metrics + differ from the same window run locally. The choice belongs to the session but + the fonts belong to the display, so closing this means putting the request on + the wire; nothing does today. +* **An attached pane tagline is drawn in the body face**, not the condensed + tagline face. The compacted band is positioned from the pane rectangle that + produced it, and the wire carries cells rather than rectangles: in a + multi-column layout two panes' tag runs touch, so the origin cannot be + recovered from the frame alone without bleeding one column's band into its + neighbour. Painting and hit-testing therefore agree on the body grid, which is + what keeps a click landing on the glyph it was aimed at; the visible cost is + one row per pane of looser tracking. + +## Tests + +`zig build unit-test` runs eleven tests in `src/detached/client.zig`. Ten drive a +real core over a real socket: a frontend is greeted +and sent a screen; input comes back as a diff; two frontends share one screen +at the smallest common grid; a frontend that dies takes nothing with it; a +wrong-version peer is refused, loudly; a peer that sends an undefined tag byte +loses its slot and not the session; the session outlives every frontend, keeps +the grid where the last one left it, and lets the next one take it over; the +three surviving effects route as documented above — `set_clipboard` to both +frontends, `read_clipboard` and `open_link` to the origin, and to the primary +once the origin is gone; the client table refuses rather than queues; and a +frontend that stops reading is dropped. + +The eleventh builds no harness, opens no socket and touches no core, and that is +the point: it pins the DESIGN rather than the behaviour, and `wire.zig` has its +mirror, one test per direction. "A frontend is never asked to fork, write, or +watch" walks +`wire.ServerMsg`'s fields BY NAME — so re-adding `spawn` fails with the name in +the failure — and then every one of the 256 tag bytes, so a session built +before this change cannot talk a frontend into forking either. "A session is +never told to do a frontend's remembering" is the same argument pointed at the +other end, and it is what keeps the six deleted `ClientTag` numbers +undecodable. diff --git a/docs/ghostty-macos-notes.md b/docs/ghostty-macos-notes.md index 556831af..3974d0d8 100644 --- a/docs/ghostty-macos-notes.md +++ b/docs/ghostty-macos-notes.md @@ -11,19 +11,22 @@ which records where pardes deliberately went the other way. ## The seam is small, and it is a hand-written C header -`include/ghostty.h` is 1207 lines for an application of ghostty's size: about 90 -exported functions, four opaque handles (`typedef void* ghostty_app_t` and -friends, `:56-60`), and one callback struct. Everything is a `void*` on the C -side; the Zig `export fn` signatures carry the real types, so the compiler checks -one end and the other is untyped by construction. - -The header is **not generated**. It is maintained by hand, with `IMPORTANT: Any -changes here update include/ghostty.h` comments scattered through the Zig -(`src/input/key.zig:83`, `src/apprt/action.zig:75`). What keeps it honest is a -test: `build.zig:359-365` runs `addTranslateC` over the header and imports the -result into the unit-test build, and `src/lib/enum.zig:91-141` walks every Zig -enum asserting each tag equals the matching `GHOSTTY_*` constant, failing in both -directions — a Zig tag with no C constant, and a C constant with no Zig tag. +`include/ghostty.h` is 1207 lines for an application of ghostty's size: exactly +90 exported function declarations, four opaque handles (`typedef void* +ghostty_app_t` and friends, `:57-60`), and one callback struct. Everything is a +`void*` on the C side; the Zig `export fn` signatures carry the real types, so +the compiler checks one end and the other is untyped by construction. + +The header is **not generated**. It is maintained by hand, with reminders +scattered through the Zig — an `IMPORTANT: Any changes here update +include/ghostty.h` comment at `src/input/key.zig:83`, and a six-step checklist +for adding an action whose fourth step is "Update `include/ghostty.h`" +(`src/apprt/action.zig:69-78`). What keeps it honest is a +test: ghostty's own `build.zig:359-365` runs `addTranslateC` over the header and +imports the result into the unit-test build, and `src/lib/enum.zig:91-141` +walks every Zig enum asserting each tag equals the matching `GHOSTTY_*` +constant, failing in both directions — a Zig tag with no C constant, and a C +constant with no Zig tag. This is the single most transferable thing in the repository, it costs about ten lines of build plumbing, and it runs on Linux. pardes now does the same thing in @@ -32,8 +35,9 @@ well, because a hand-written header drifts in signatures too. ## Zig→host is one callback, not N -`ghostty_runtime_config_s` (`include/ghostty.h:999-1027`) holds `userdata` and -six function pointers: `wakeup`, `action`, `read_clipboard`, +`ghostty_runtime_config_s` (`include/ghostty.h:1018-1027`) holds `userdata`, +one capability bool (`supports_selection_clipboard`) and six function +pointers: `wakeup`, `action`, `read_clipboard`, `confirm_read_clipboard`, `write_clipboard`, `close_surface`. Only `wakeup` and the clipboard ones are what they look like. Everything else the core wants from the host — new window, set title, toggle fullscreen, desktop notification, @@ -51,8 +55,9 @@ comptime { The payoff is that adding a feature adds an enum case, not a struct field: hosts that do not implement it return `false` and the ABI never changed. Worth copying -the day a fixed callback list starts growing. pardes has two callbacks today and -does not need it yet. +the day a fixed callback list starts growing. pardes has three callbacks today +— `wakeup`, `set_clipboard`, `read_clipboard` (`Runtime` in `src/macos.zig:153`) +— and does not need it yet. Two userdata scopes, worth noting: the runtime struct's `userdata` is app-scoped, and each surface carries its own. Swift round-trips both with @@ -64,7 +69,7 @@ lifetime stays entirely on the Swift side. This is the surprising part and the main thing pardes did *not* copy. Swift creates an `NSView`, gives it a non-zero frame, and passes the bare -pointer through a tagged platform union (`include/ghostty.h:448-465`): +pointer through a tagged platform union (`include/ghostty.h:448-460`): ```c typedef struct { void* nsview; } ghostty_platform_macos_s; @@ -168,10 +173,16 @@ routing shaders through Xcode build rules. header-drift test — runs on Linux. `flake.nix:63-66` excludes Darwin from buildable platforms entirely. -pardes skips lipo, the xcframework, the Xcode project and codesigning. All four -exist to ship a signed universal bundle to someone else's machine; a dev build -needs a `swiftc` invocation and a directory with a plist in it. They are the -named upgrade path in `docs/macos.md`, in that order. +pardes skips lipo, the xcframework and the Xcode project. Those three exist to +ship a universal bundle that something other than this app can link, and they +are the named upgrade path in `docs/macos.md`, in that order. Codesigning is no +longer on that list: the `codesign --force --sign` step in the macos branch of +pardes's `build.zig` is reached by `b.getInstallStep().dependOn(&sign.step)`, so +it runs as part of an ordinary `zig build -Dplatform=macos`, ad-hoc +(`-`) by default because an unsigned arm64 bundle does not launch at all, and +`-Dmacos-identity=` swaps in a Developer ID and adds `--options runtime +--timestamp`. So a dev build here is a `swiftc` invocation, a directory with a +plist in it, and one `codesign`. ## Small things worth remembering diff --git a/docs/helix-keys.md b/docs/helix-keys.md index b307ae30..81bc0248 100644 --- a/docs/helix-keys.md +++ b/docs/helix-keys.md @@ -1,21 +1,34 @@ # pardes ↔ helix keybinding plan Living tracking doc for the helix-parity effort. Every key / table row in -`helix/book/src/keymap.md` (checkout: `~/05-genizah/helix`, HEAD 278b24389) -appears in exactly one of the three sections below. As of phase 5 every -helix-equivalent row in A and B is differentially verified against real -helix (see "Differential testing" at the bottom); the two waivers are -listed in their rows. - -Code map (line numbers approximate): `src/pardes.zig` — `Key` ~296, -`handleKey` ~1171 (intercept order is load-bearing, see comment there), -`setPaneRange` ~1566 (helix range → pane state), `handleNormal` ~1736, -`enterInsert` ~2171, `handleInsert` ~2278, `normalDelete` ~2600, -`normalYank` ~2686, `normalPaste` ~2727, `normalChange` ~2799, undo ~3386. -Pure text math: `src/modal.zig` (the `hx*` family is the helix-semantics -layer: gap offsets + ranges over the flat text). Differential harness: -`test/hxdiff.zig` + `test/hxcases/{cases,goldens,waivers}.jsonl` + -`test/hxcases/regen.sh` — see "Differential testing" at the bottom. +`helix/book/src/keymap.md` appears in exactly one of the three sections +below. The keymap was read at UPSTREAM commit 278b24389 of the genizah +checkout (`~/05-genizah/helix`); that checkout now sits on the local +`pardes-harness` branch, whose three commits add only the harness, so +`book/src/keymap.md` is byte-identical at its tip (694e7dfd). As of phase 5 +every helix-equivalent row in A and B is differentially verified against real +helix (see "Differential testing" at the bottom). Five of the six waivers are +named in the row they belong to; the sixth, `wiX-edit-drops-sel`, belongs to no +single key — it is the anchor-only divergence every insert-mode edit shares — +and is described in the Files list at the bottom instead. + +Code map, by SYMBOL — line numbers rot, names do not. Body-normal key +RECOGNITION is `src/normal_input.zig`: a pure state machine over `Role` (one +per bound command, matched against `src/config.zig`'s chord lists by +`Pardes.normalInput`), a `Prefix`, a `MatchSub` and a count, emitting a +semantic `Action` that both the text and PDF adapters consume. It knows +nothing about panes or text. `src/pardes.zig` then EXECUTES: `Key`, +`handleKey` (intercept order is load-bearing, see the comment there), +`handleNormal` (marshals `Pane`'s compact fields in and out of +`normal_input.State` through `paneNormalState` / `putPaneNormalState`), +`executeNormalAction`, `setPaneRange` (helix range → pane state), +`enterInsert`, `handleInsert` / `insertKey`, `insertTab`, `normalDelete`, +`normalYank`, `normalPaste`, `normalChange`, `replaySels`, `multiOnce`. Undo +and redo are per pane kind, in `file_pane` and `term_pane`. Pure text math: +`src/modal.zig` (the `hx*` family is the helix-semantics layer: gap offsets + +ranges over the flat text). Differential harness: `test/hxdiff.zig` + +`test/hxcases/*.jsonl` + `test/hxcases/regen.sh` — see "Differential testing" +at the bottom. ## Global semantic divergences (read first) @@ -41,6 +54,15 @@ layer: gap offsets + ranges over the flat text). Differential harness: cursor/range endpoint is snapped to an extended-grapheme boundary. Rendering, mouse input and vertical motion translate those offsets through terminal-cell widths, so combining sequences and wide glyphs remain single cursor cells. + Three functions in `modal.zig` do the snapping — `graphemeStart` (repair an + offset back onto a boundary), `nextGrapheme`, `prevGrapheme` — each with an + arithmetic ASCII fast path over the UAX #29 segmenter. All three carry the + same exclusion by hand, because GB3 is the one UAX #29 rule that joins two + ASCII scalars: a CR takes a following LF into the same cluster. `nextGrapheme` + and `prevGrapheme` did not spell it out and so disagreed with `graphemeStart` + by exactly one byte on a CRLF file — a head could step to the offset between + CR and LF and be repaired straight back. Fixed; the test "GB3 keeps CR-LF one + cluster for every grapheme step" is what holds the three in agreement. - **Terminal panes: shell output is immutable.** Edit ops only ever drop/alter typed insertion runs. All "To implement" edit ops follow the same rule (motion/selection parts work on the full motion surface; the @@ -97,7 +119,7 @@ language-backend queries, and the shell pipe. | `Enter` (normal) | acme **look** chord: EXPLICIT selection, else file-ish word under cursor | pardes-specific, keep (helix normal-mode Enter unbound). Covers helix `gf`. Implicit motion residue falls back to the cursor word | pardes-specific | | `Tab` (normal) | acme **execute** chord | pardes-specific, keep; explicit-selection rule as Enter | pardes-specific | | `:` (normal, body) | focuses the pane's OWN tag as a one-line editor in **normal** mode, parked at the first EDITABLE column: motions (`w` `b` `e` `W` `B` `E`, `0` `$` `^`, arrows, `Home`/`End`) walk the whole rendered tag, `y` yanks the selection, `Enter`/`Tab` look/execute it (else the file-ish word under the cursor), `i`/`a`/`I`/`A` enter insert, `Esc` hands the body back. `h`/`j`/`k`/`l` are NOT motion here — a tagline is a place in the LAYOUT, so they run the same `Left`/`Down`/`Up`/`Right` builtins and land on the neighbouring pane's TAGLINE, still in normal mode (nothing that way = stay put, EXCEPT `k` off the topmost tagline — see the next row); the arrows keep the in-tag motion | helix `:` is command mode (section C); pardes' commands are acme words that live in the tag. `tag_col`/`tag_anchor` are columns of the RENDERED tag (prefix ++ tail) — one coordinate space, so the live mode+path prefix is selectable, yankable and executable, while every edit op (typing, `Backspace`, `i`/`a`/`I`/`A`) measures from the first editable column and is inert inside it | pardes-specific | -| `k` (tag normal, topmost tagline) | focuses the TOPBAR — row 0, the global tagline (`New Newcol Find Grep Help Tutor Dump NextColor Debug Kill`, plus `Restore ` once a dump exists). It is its own one-line normal mode: `h`/`l` and the arrows by grapheme (row 0 has no window left or right to walk to), `w`/`b`/`e`/`W`/`B`/`E` and `0`/`$`/`^` by word, `Enter`/`Tab` runs the word under the cursor through the same dispatch a MIDDLE click on it uses, `j` drops back onto the topmost pane's tagline, `Esc` leaves. No insert mode and no selection — the bar is chrome with no tail to own | pardes-specific. The topbar is not a pane, so `focusDir` can never reach it: this is a fallback on the `.Up` branch of the tagline hop, from a TAGLINE only (a body's `SPC w k`/`Ctrl-w k` keep their pane-to-pane meaning). Its whole state is one global `topbar_col: ?u16` (row 0 has no pane to hang it on), cleared by any mouse press and BEFORE the chord dispatch, because `Kill` up here frees the session the way `Del` frees a pane. The motion vocabulary is literally the tag's — both call `lineMotion` | pardes-specific | +| `k` (tag normal, topmost tagline) | focuses the TOPBAR — row 0, the global tagline (`config.topbar_str`, today `New Newcol Joincol Find Grep Help Changelog Tutor Dump NextColor Debug Kill`, plus `Restore ` once a dump exists). It is its own one-line normal mode: `h`/`l` and the arrows by grapheme (row 0 has no window left or right to walk to), `w`/`b`/`e`/`W`/`B`/`E` and `0`/`$`/`^` by word, `Enter`/`Tab` runs the word under the cursor through the same dispatch a MIDDLE click on it uses, `j` drops back onto the topmost pane's tagline, `Esc` leaves. No insert mode and no selection — the bar is chrome with no tail to own | pardes-specific. The topbar is not a pane, so `focusDir` can never reach it: this is a fallback on the `.Up` branch of the tagline hop, from a TAGLINE only (a body's `SPC w k`/`Ctrl-w k` keep their pane-to-pane meaning). Its whole state is one global `topbar_col: ?u16` (row 0 has no pane to hang it on), cleared by any mouse press and BEFORE the chord dispatch, because `Kill` up here frees the session the way `Del` frees a pane. The motion vocabulary is literally the tag's — both call `lineMotion` | pardes-specific | | `Ctrl-w` + `h/j/k/l`/arrows | directional pane focus prefix — normal/tty modes only | pardes' own window handling (helix window mode skipped, section C). Runs the SAME `Left`/`Down`/`Up`/`Right` builtins `SPC w h/j/k/l` runs; kept alongside the leader because a pane in raw **tty** mode never sees `SPC` (the shell owns it), so this is the only keyboard way out of one. Insert mode owns `Ctrl-w` = delete-word-back, so a tag being TYPED into swallows it; from a tag in normal mode (`:`) it moves focus to the neighbour's BODY, while the bare letters `h/j/k/l` there move to its TAGLINE (next row) | pardes-specific | | `Alt-n` | new terminal below (any mode) | shadows helix `Alt-n` TS sibling-select — skipped anyway (tree-sitter) | pardes-specific | | `Alt-c` | move active terminal to a fresh column (any mode) | helix `Alt-c` is change-noyank; the pardes window op wins (do-not-touch contract). `Alt-d` + `i` covers the behavior | waived (`alt-c-window-op`) | @@ -122,13 +144,19 @@ Status: `todo` → set to `done (phase N)` as rows land. All rows verified against keymap.md. Edit ops on terminal panes obey the immutable-output rule (typed runs only) — same as `d`/`c` today. -Phase 2 state added to `Pane`: `count` (accumulator, capped 0xffff), -`pending2` (m-mode sub-key), `pending_ch` (mr's ``), `find_op`/`find_ch` -(Alt-. repeat). `pending` also holds `m` `[` `]` and the char-arg ops -`f F t T r`. New pure text math in modal.zig (`findChar`, `matchBracket`, -`paragraphFwd/Bwd`, textobject/surround ranges, `replaceRange/Chars`, -`changeCase`, `joinLine`, `indentLines`, `adjustNumber`, `deleteSpan`, -`advanceBy`) with inline tests; `zig build unit-test` runs them. +Phase 2's recognition state is now `normal_input.State`: a count (accumulator, +capped 0xffff), a `Prefix` (`g` `z` `m` `f` `F` `t` `T` `r` `]` `[`), a +`MatchSub` (m-mode's `i`/`a`/`s`/`r`/`d`) and one `held_char` for `mr`'s +``. `Pane` keeps the same four fields it always did — `count`, +`pending`, `pending2`, `pending_ch` — but purely as compact per-pane storage, +marshalled in and out by `paneNormalState` / `putPaneNormalState`; the +comments on them say so. `find_op`/`find_ch` (the `Alt-.` repeat target) stay +on `Pane`, because the parser emits `repeat_find` without remembering what was +found. Pure text math in `modal.zig`: `findChar`, `matchBracket`, +`paragraphFwd`/`paragraphBwd`/`paragraphRange`, textobject/surround ranges, +`replaceRange`/`replaceChars`, `changeCase`, `joinLine`, `indentLines`, +`adjustNumber`, `deleteSpan`, `advanceBy` — each with inline tests that +`zig build unit-test` runs. ### Counts @@ -154,7 +182,7 @@ Phase 2 state added to `Pane`: `count` (accumulator, capped 0xffff), | Key | Behavior | Notes | Status | | --- | --- | --- | --- | -| `r` | replace selection/char with ``, newlines kept | terminals: replaces the typed-run byte under the cursor if any, else no-op (`runByteAt`) | helix-verified (phase 5) | +| `r` | replace selection/char with ``, newlines kept | terminals: replaces the typed-run byte under the cursor if any, else no-op — one code path for both kinds, since `normalReplaceChar` goes through `Pardes.editText`/`setEditText`, which route to a file's content or to `term_pane`'s typed-run overlay | helix-verified (phase 5) | | `R` | replace selection (or cursor char) with the yank register; pasted text becomes the selection, head on its last char | uses the INTERNAL yank (`p.yank`), no clipboard round trip — `SPC R` is the system-clipboard twin; empty register = no-op; terminals: no-op | helix-verified (phase 5) | | `~` | switch case of selection/char, selection kept | terminals: run byte only | helix-verified (phase 5) | | `` ` `` | selection to lowercase | terminals: run byte only | helix-verified (phase 5) | @@ -331,7 +359,12 @@ case corpus. Both sides speak the same contract: a case is `"file"`/`"tty"`, ignored by helix), a result is the full final buffer text, the mode (`normal`/`insert`/`select` — select only for `v` extend mode), and the block-cursor positions of the primary selection's head -(`cursor`) and other end (`anchor`), 0-based row + byte col. +(`cursor`) and other end (`anchor`), 0-based row + byte col. With more than +one selection the result line grows two more fields — `sels`, every range in +document order measured the same way, and `primary`, the index of the one +`cursor`/`anchor` describe. Both are omitted at one selection, which is why +every golden written before multiple cursors existed is still byte-for-byte +valid. Files (all in `test/hxcases/`): @@ -354,14 +387,33 @@ Files (all in `test/hxcases/`): (one yank register, not one value per range), and `sel-regex-caret` / `sel-regex-dot-newline` (mvzr is not the Rust regex crate: no multi-line `^`/`$`, and `.` matches a newline). -- `test/hxdiff.zig` builds `pardes-hxdiff`, which drives the core - headlessly at 80x24 (22 body rows, matching helix's 22 text rows). +- `parity.jsonl` — 80 further cases, used only by the parity gate below. +- `parity-waivers.jsonl` — 13 named exemptions for the parity gate, in three + classes: one deliberate pardes binding (`ctrl-b-page`, since `Ctrl-b` IS the + tty toggle), eight pty VIEWPORT divergences (a terminal's view cannot scroll + below the vt's live grid bottom, so the cursor snaps into a different + scrolloff band — no text differs), and four case texts a pty cannot hold + verbatim (a literal TAB the emulator expands, a file with no trailing + newline, an all-whitespace last row the dump trims). +- `smoke.jsonl` — 20 cases referenced by nothing in the tree: no build step, + no script. Either wire it up or delete it. + +The pardes half is `test/hxdiff.zig`, which builds `pardes-hxdiff` and drives +the core headlessly at 80x24 (22 body rows, matching helix's 22 text rows). Run it: zig build hxdiff # cases + goldens + waivers, field-by-field; # unwaivered mismatch = per-case report + exit 1 zig build hxdiff -- test/hxcases/cases.jsonl # results to stdout, no diff + zig build hxparity # the SAME binary in --parity mode: cases.jsonl + # + parity.jsonl, each case run twice over the + # same text and keys (once in a file pane, once + # in a pty pane), both result lines must match. + # No goldens — the file pane IS the oracle, so + # "editing a shell pane behaves like editing a + # file" cannot drift. The case's own "pane" field + # is ignored; exemptions in parity-waivers.jsonl sh test/hxcases/regen.sh # regenerate goldens The helix half (`hx-harness`) lives on the `pardes-harness` branch: diff --git a/docs/ideas.typ b/docs/ideas.typ index 3493f758..8c83ab55 100644 --- a/docs/ideas.typ +++ b/docs/ideas.typ @@ -1,9 +1,23 @@ +// STATUS: live scratch notes, deliberately unfiled — nothing compiles this file +// (no `build.zig` step, and `docs/design.typ` does not include it); kept because +// the first two ideas are still open. The ideas themselves were last touched in +// change `xmzpwklp` (2026-08-01); this header and the LANDED annotation below +// are 2026-08-26 and changed no idea. + - grep shows treesitter context around - this could be implemented as a separate binary - clickable file:lines show somehow (as highlights or something else) - - is line above of below middle one on the editor + - is line above or below middle one on the editor - is this on the same file as open on the editor -- clickable things for the mouse should be navigatable with the keyboard +- LANDED: clickable things for the mouse should be navigatable with the keyboard - like if I get some search results I can just use tab to navigate them, the editor should know: - semantic boundaries (maybe just jump to the next clickable thing?) + - it is `n`/`N` rather than Tab (Tab is the acme execute chord): a MOTION over + look-able text, not a jump (`Pardes.lookWalk`, `Pardes.lookSpanIn`). Enter — + the look chord — on what a step leaves selected is what opens it. + - the "semantic boundaries" are the pane's GRAIN (`output_pane.Grain`): in free + text the largest whitespace-delimited run `look.resolve` can act on + (`look.lookableSpan`), in a results buffer one stop per ROW + (`look.lookableLineSpan`). Written up in `docs/helix-keys.md`, the `n`/`N` + row of section A. diff --git a/docs/lsp-evaluation.md b/docs/lsp-evaluation.md index d9fcd243..ab502422 100644 --- a/docs/lsp-evaluation.md +++ b/docs/lsp-evaluation.md @@ -1,19 +1,22 @@ # Three language backends, measured -Same core, same seam (`src/lsp/lsp.zig`), same 17-probe harness (`zig build lspbench`) -over the same corpus. Three jj workspaces, three independent implementations, -one function each. C landed; see **Decision** below. +Same core, same seam (`src/lsp/lsp.zig`), same harness (`zig build lspbench`) +over the same corpus — 17 probes at the time, 22 today. Three jj workspaces, +three independent implementations, one function each. C landed; see +**Decision** below. > **This is a dated report, not a description of the tree.** Every MEASUREMENT > below is the one taken at decision time and is deliberately left alone, > because renumbering a comparison nobody can re-run turns a record into a -> guess. What has moved since: `lspbench` now has **22** probes rather than 17 -> (the dot-completion work added five), `src/lsp/lsp_zls.zig` has grown from 540 -> lines to about 1,400, `test/snapshots/` holds 87 scripts rather than 56, and -> the differential suites are `hxdiff` 481 / `hxparity` 561. The one number -> that IS kept current is the grammar count in the Recommendation, because that -> is an argument about today rather than a measurement of then. For what the -> harness does TODAY, read `docs/lsp.md`. +> guess. What has moved since, re-checked 2026-08-26: `lspbench` now has **22** +> probes rather than 17 (the dot-completion work added five), +> `src/lsp/lsp_zls.zig` has grown from 540 lines to **1 432**, +> `test/snapshots/` holds **95** scripts rather than 56 (`.snap` + `.golden` +> pairs), and the differential suites are `hxdiff` **481** (`cases.jsonl`) and +> `hxparity` **561** (`cases.jsonl` + the 80 editing extras in +> `parity.jsonl`). The one number that IS kept current is the grammar count in +> the Recommendation, because that is an argument about today rather than a +> measurement of then. For what the harness does TODAY, read `docs/lsp.md`. | | **A · stdlib** | **B · client** | **C · in-process** | |---|---|---|---| @@ -98,7 +101,12 @@ real binary through the helix keymap, and pass `hxdiff` (360) and `hxparity` **C landed.** It is what `src/lsp/lsp_zls.zig` is, and ZLS is now a fetched dependency (`build.zig.zon`, pinned to commit `3e0d0820` on the 0.16.x branch) so it sits in `zig-pkg/` like everything else and the build is reproducible -from the .zon alone — no path dependency on a local checkout. +from the .zon alone — no path dependency on a local checkout. The same commit +is spelled a second time in `build.zig` as `const zls_version = +"0.16.1-dev+3e0d0820"`, for ZLS's own `-Dversion-string`; the semver half +exists nowhere in the manifest, so the duplication cannot be removed, but a +`comptime` block beside the constant `@compileError`s unless the two name the +same commit. See "Which ZLS, and which stdlib" in `docs/lsp.md`. A and B were not deleted, only un-worktree'd. They remain whole commits: @@ -118,7 +126,11 @@ second process, and its weakest number is a cache that does not exist rather than a limit that cannot be lifted. The one thing that should override that: **pardes ships tree-sitter grammars for -28 languages.** C is Zig-only forever; B is the only implementation that will +28 languages** — `src/grammar_manifest.zig`'s `all` has 29 entries, but +`markdown_inline` carries `.exts = &.{}` on purpose: no filename ever selects +it, and the block grammar re-enters it by name for its `inline` nodes. So 29 +grammars, 28 buffers a language server could ever be asked about. C is +Zig-only forever; B is the only implementation that will ever answer `gd` in a Rust or Go buffer. If language intelligence is meant to follow the syntax highlighting, B is the strategic choice and its 721 lines are the cheapest multi-language client anyone will write. diff --git a/docs/lsp.md b/docs/lsp.md index 54ffd764..1adb397b 100644 --- a/docs/lsp.md +++ b/docs/lsp.md @@ -29,18 +29,62 @@ core shell worker ``` The shell already ran this exact pattern for pty readers, so the async part is -about thirty lines per shell: `tty.zig` uses `io.concurrent` + the vaxis loop -queue, `gui.zig` uses a detached thread + the mutex queue it already had. The -web shell compiles in no backend at all (`zls_backend` is off for wasm), which -makes `lsp.supports` empty, which makes `lspRequest` return before it emits — -so on the web the effect is never even raised. `web.zig` leaves the host's -`lsp` method null and exports nothing for a response; a host that links a -backend would add both. - -Three rules make it safe: +about thirty lines per shell: `src/tty/tty.zig` uses `io.concurrent` + the +vaxis loop queue, `src/gui/gui.zig` uses a detached thread (`lspThread`) + the +mutex queue it already had. A FREESTANDING core compiles in no backend at all +(`zls_backend = !freestanding_core` in `build.zig`), which is both the web +shell and the ESP32-P4 object: there `lsp.supports` is empty, which makes +`lspRequest` return before it emits, so the effect is never even raised. +`web.zig` leaves the host's `pull_lsp` null and exports nothing for a +response; a host that links a backend would add both. + +**In a DETACHED session every query answers EMPTY.** +`src/detached/server.zig`'s vtable implements sixteen of `host.zig`'s +twenty-one methods, and `pull_lsp` is one of the five it leaves null — a +worker pool is precisely what its deliberately single-threaded loop does not +have. A null method is NOT automatically a dropped effect: `perform` decides +that per arm, and the `.lsp` arm's answer is to synthesise one on the spot — +an `lsp_resp` Event with `rows = ""`, fed straight back into `update`. `.pipe` +one arm below does the same, yielding +`pipe_resp{ .success = false, .outputs = &.{} }`, so a null `pull_pipe` is a +pipe REPORTED as failed rather than one that hangs. So `gd` jumps nowhere, +`gr` finds no references, `SPC l r` renames nothing (an empty edit list parses +as none) and Tab after a dot offers nothing — all of it indistinguishable from +a backend that found nothing, which is exactly what the seam's "no rows is a +legal answer" rule promises. + +**Tab still indents — still, not always.** The empty response reaches +`lspResponse`, whose `rows.len == 0` prong performs the indent the Tab prong +skipped, but only while the cursor has not moved. That +guard holds on the ordinary path because of `pump`'s order: `pull_wait_input`, +then the queued events, then the effects, then render. The effect Tab emitted +is performed after every keystroke that was ALREADY readable in the same +round, since `pull_wait_input` applies a whole batch and not one event (the +tty shell says so at the head of `waitInput` — "block for one event, then +apply the whole pending batch" — and the daemon's poll loop drains every +readable client `.event` straight into `core.update`). One keystroke per wake +is the normal case and the indent lands in the same frame, before render. In a +BURST where the key after Tab was readable in that same poll round, `cur_col` +has moved by the time the prong runs, the guard fails, and the Tab really is +eaten. The local shells have the same race over a wider window, so this is a +property of the late-indent repair rather than of detaching. + +None of the detached core's five null methods silently drops a reachable +effect. `pull_lsp` and +`pull_pipe` have the fallbacks above; `pull_gpio_toggle` is +`orelse return Error.NoPads` (`board_memory.zig`), which lands on the message +row; `push_post_present` is a `pump` hook fired after presenting, and there is +nothing to notify in a process with no screen; and `push_fs_reply` is the one +`perform` arm with no fallback at all, but it is only ever emitted in answer to +an `Event.fs_req`, which is not on the wire (`wire.zig`: it has no `ClientTag`, +because neither half of that pair may cross an attachment) and which the +daemon raises none of, mounting no `/dev/fuse` by its own vtable comment. + +Four rules make it safe: - **The worker never touches the core.** Path, source, arg and root are copied - into an `LspJob` before it starts (`tty.zig`). The user keeps typing while a + into an `LspJob` before it starts — one per shell, in `src/tty/tty.zig` and + `src/gui/gui.zig`. The user keeps typing while a query is in flight; a borrowed slice would be a use-after-free the length of one keystroke. - **One query in flight, identified by a monotonic id.** A second press bumps @@ -107,9 +151,11 @@ to end — the enum one level up (absolute row) and one level down (`inner/tint. a stripped path that still has a separator in it), each with the `n` step that selects it and the Enter that opens it, plus a right click. -This is the rule `look.grep` already follows for its own rows (`look.zig`, the -`shown` computation), written a second time; the two are now the same function -and want to become one. +This is the rule `look.grep` already follows for its own rows, and it is +spelled TWICE: `lsp.rel` here, and an inline `if` over the asking pane's +directory in `look.zig`'s `grep` (the `shown` computation) there. Same rule, +two implementations — they want to become one function, and `lsp.zig`'s own +comment on `rel` says so. `completion` is the kind this shape changes the most. Every other editor answers a dot with a popup of NAMES to insert; a seam that returns locations cannot @@ -166,17 +212,29 @@ asking would stop the replay dead and collapse the multicursor. ### What it costs, and what it cannot do -Per press, measured by `zig build lspbench` on this repo: +Per press. **Both timings date from 2026-08-09**, change `lmlltvrx`, and have +not been re-measured; the line count beside the first was refreshed once +afterwards, on 2026-08-12 in change `vwtlskzr`, and `src/pardes.zig` is 16 466 +lines today. The ReleaseFast column is `zig build lspbench`, which is pinned to +ReleaseFast in `build.zig` and always has been — so the Debug column came from +running the installed editor by hand and the repo records no harness for it. A +completion parses the buffer once per placeholder spelling it tries, so the +first row scales with the file: re-run rather than trusting either number. | | ReleaseFast | Debug (what `zig build` installs) | |---|---|---| | a switch arm in `src/pardes.zig` (14.6k lines) | 8.8 ms | 87 ms | | `std.` — 91 candidates, each alias-resolved into the stdlib | 26 ms | 204 ms | -It is a worker thread, so the editor does not block; but the second press of -Tab joins the first query on the UI thread (`old.cancel(io)` in the shell) and -that wait is real. Pre-existing and shared by every LSP kind — not this -feature's to fix, but it is what a fast double-Tab feels like. +It is a worker thread, so the editor does not block. On the TTY shell the +second press of Tab then joins the first query on the UI thread — +`old.cancel(s.io)` on the one in-flight future, and a backend that ignores +cancellation means waiting out a query the user already abandoned +(`src/tty/tty.zig`, the `lsp` vtable entry, whose own comment says so). The +GUI shell does not join: it spawns another thread per request and lets the +core's monotonic id make the older answer stale, so it pays memory instead of +latency. Pre-existing and shared by every LSP kind — not this feature's to +fix, but it is what a fast double-Tab feels like on a terminal. Known limitations, in the order you will meet them: @@ -194,6 +252,46 @@ Known limitations, in the order you will meet them: - **`error.`** is not handled — the position context is `.error_access`, which no branch claims. +## Which ZLS, and which stdlib + +Both are decided at build time, and `SPC l i` prints both. + +`build.zig.zon` pins ZLS to a COMMIT rather than a tag — +`git+https://github.com/zigtools/zls#3e0d082084be43e36865136a138c1fe2023b33ca`, +on the 0.16.x branch — because master requires Zig 0.17-dev and no tagged +release both builds on 0.16 and exports the internals this backend calls. +`build.zig` spells the same commit a second time, as the top-level +`const zls_version = "0.16.1-dev+3e0d0820"`, and hands it to the ZLS package's +own `-Dversion-string` and to `pardes_config.zls_version`. The duplication is +unavoidable rather than sloppy — the semver half (`0.16.1-dev`) exists nowhere +in the manifest — and it is load-bearing, because ZLS's build otherwise +derives that string from `git describe`, which has nothing to read in a +fetched package with no `.git`. The two are made to AGREE BY CONSTRUCTION: a +`comptime` block right below the constant takes the short hash after the `+`, +takes the pinned commit after the `#` in `zon.dependencies.zls.url`, and +`@compileError`s unless the first is a prefix of the second. A `.zon` bump +that forgets `build.zig` is therefore a build error, not a `SPC l i` naming a +build nobody linked. + +`std` is the harder half. ZLS resolves `@import("std")` through `zig_lib_dir` +and through nothing else, and this backend sets `zig_exe_path = null` on +purpose — asking the `zig` binary is the subprocess the whole design exists to +avoid. So `build.zig` bakes `b.graph.zig_lib_directory.path` into +`pardes_config.zig_lib_dir`: the exact stdlib pardes itself was compiled +against, which is what makes `gd` on `std.mem.count` land in the real +`mem.zig`. `zigLibPath()` (`src/lsp/lsp_zls.zig`) reads `ZIG_LIB_DIR` from the +environment FIRST and falls back to the baked path, so a user who moved the +toolchain can point the backend at it without rebuilding. With no lib dir at +all every `std` symbol is a silent miss — which is why `SPC l i` reports +whether the directory OPENS rather than only which one was compiled in. + +That same `zig_exe_path = null` is the dependency-module limitation above. +ZLS resolves a relative `.zig` path from the filesystem and `std` from the lib +dir, but any other import name — every dependency in `build.zig.zon` — it can +only answer by running `zig build --build-runner` to discover the module +graph. With no zig binary that branch returns nothing, so `@import("vaxis")` +is a silent miss by construction rather than by omission. + ## The keymap is helix's, exactly Verified against `helix-term/src/keymap/default.rs`, not from memory. @@ -299,12 +397,14 @@ hand it. `zig build lspbench` — same harness, same corpus, same 22 probes, every backend. The corpus is pardes's own `src/`, plus `test/lspfixture/`: five of the probes -point at fixtures rather than at real source, because on clean, already -formatted code the correct answer to `diagnostics` and `format` is nothing, and -that is indistinguishable from a backend that has neither. `broken.zig` carries -an unused local and a misformatted fn; `dotcomplete.zig` and `dothalf.zig` -carry the two shapes of half-typed dot. The 22 probes cover 15 of the 17 -`lsp.Kind`s +point at fixtures rather than at real source. Three of them do because on +clean, already formatted code the correct answer to `diagnostics`, +`workspace_diagnostics` and `format` is nothing, and that is indistinguishable +from a backend that has neither — `broken.zig` carries an unused local and a +misformatted fn, so all three have real work. The other two are the half-typed +dot, whose two shapes are `dotcomplete.zig` (a switch prong that parses +everywhere but at the dot) and `dothalf.zig` (a line also missing its +terminator). The 22 probes cover 15 of the 17 `lsp.Kind`s — `definition` three times, `document_symbols` twice, `completion` five times, and the two introspection kinds (`status`, `explain`) not at all. diff --git a/docs/macos.md b/docs/macos.md index 940d5a58..2b50e6bc 100644 --- a/docs/macos.md +++ b/docs/macos.md @@ -27,10 +27,10 @@ Pardes goes the other way because its frame is *already* a cell grid. `pardes.Surface` is `cols × rows` of `Cell`, and CoreText is the native way to draw one: attributed runs, the system font stack, and Apple's own subpixel and color-emoji handling, for free. The alternative is a hand-rolled glyph atlas, -and the SDL backend is the measurement of what that costs — roughly three -thousand of `src/gui/gui.zig`'s 4,700 lines are a 2048² FreeType-hinted R8 atlas, -GPU pipelines, transfer buffers and shaders. Writing a second one, blind, buys -nothing the grid needs. +and the SDL backend is the measurement of what that costs — a large part of +`src/gui/gui.zig`'s 6,348 lines is a 2048² FreeType-hinted R8 atlas (`atlas_w` +and `atlas_h`, `src/gui/gui.zig:140`), GPU pipelines, transfer buffers and +shaders. Writing a second one, blind, buys nothing the grid needs. Blind is the operative word: this backend was scaffolded on a Linux machine. A Metal renderer written there would have been untestable code — compiled at best, @@ -65,13 +65,28 @@ removing; it is the encoding being right. The one real difference is IO. The browser has no ptys, so `src/web.zig` forwards every effect out to JavaScript as a numbered code plus a byte payload, -and JavaScript performs it. macOS has `forkpty`, `read` and `write` right there, -so almost no effect crosses the boundary at all: `pardes_tick` drains the -core's effect queue and performs each one in Zig, the way `drainEffects` in -`src/tty/tty.zig` does. That is why the runtime struct is three callbacks and -not twelve — and why two of the three are the clipboard: the pasteboard is -AppKit's, in both directions, and is the one piece of IO the Zig side cannot -reach for itself. +and JavaScript performs it. macOS has `forkpty`, `read` and `write` right +there, so almost no effect crosses the boundary at all: `pardes_tick` runs +`while (core.nextEffect()) |e| core.perform(e)` (`src/macos.zig:1154`) and each +effect lands on this host's own `Host.VTable`, of whose twenty-one methods +fourteen are filled in (`src/macos.zig:1761`). + +The machine-local half of those methods is no longer this file's. `forkShell`, +`writeFileBytes` and `writeFd` live in `src/host_io.zig` and are shared with +the tty shell, the SDL shell and the detached daemon; `src/macos.zig` calls +them (`:1807`, `:1823`, `:1848`, `:1863`) and its own copies, together with the +four `extern "c"` declarations they needed (`forkpty`, `execv`, `chdir`, +`_exit`), are gone. Two `extern "c"` declarations remain here: `setenv`, and +`pardes_host_watch_file`, which FileWatcher.swift satisfies +(`src/macos.zig:40`, `:45`). The bug all four hosts' private `forkShell` +copies had in common is the argument for the shared file existing: none set +`FD_CLOEXEC` on +the pty master, so a program in one pane could read and write another pane's +terminal and closing a master did not reliably hang its shell up. + +That is why the runtime struct is three callbacks rather than a second vtable — +and why two of the three are the clipboard: the pasteboard is AppKit's, in both +directions, and is the one piece of IO the Zig side cannot reach for itself. ## The seam @@ -426,8 +441,11 @@ and it answers both. `Look` resolves against it, so it has to follow the shell rather than stay where the pane was spawned. -*When* it is read differs from the other two shells, and deliberately. The tty -and SDL hosts poll every pane every frame; here the drain has just finished +*When* it is read differs from the other three shells, and deliberately. The +tty, SDL and detached-daemon hosts poll every pane every frame — inline in the +tty loop's frame path (`src/tty/tty.zig:1228-1231`), `pollCwds` in the SDL +shell (`src/gui/gui.zig:4048`) and `pollFrame` in the daemon +(`src/detached/server.zig:889`); here the drain has just finished saying exactly which shells produced bytes, and nothing else can have moved one — a `cd` is a command, and a shell that ran a command writes at least its next prompt. So `refreshCwds` reads only for panes flagged by that tick's @@ -446,19 +464,25 @@ against `getppid()`. The socket half needed real portability work rather than a second spelling. Darwin has no `SOCK_CLOEXEC` and no `accept4`, so the flag is set with an -`fcntl` after the fact — a race only against a fork on another thread, and both -callers are past that. `sun_path` is 104 bytes here against 108 there, so no +`fcntl` after the fact — a race only against a fork on another thread, and +every caller is past that (`src/nested.zig:81-93` names all three). +`sun_path` is 104 bytes here against 108 there, so no buffer in the file spells a number any more; they are all sized from the field itself, and an address that does not fit is refused rather than truncated into a path pointing somewhere else. -Identity gained a third sibling. `bin/pardes` and -`pardes.app/Contents/MacOS/pardes` are one build installed twice and share no -directory at all, so the comparison is made at the *install prefix* — the -directory holding the `.app`, or the parent of a `bin` — and the bundle's name -can never carry the `-os-arch` tail the installed binary does, because -`CFBundleExecutable` is a fixed string. `zig build -Dplatform=macos` then -`pardes src/foo.zig` inside the app's own shell opens a pane in the app. +Identity gained a third sibling. `bin/pardes`, `bin/pardes-gui` and +`pardes.app/Contents/MacOS/pardes` are three installed frontends of one +program, so `samePardesExecutable` (`src/nested.zig:142`) compares BASENAMES +and ignores the paths entirely — the GUI may be system-wide while the tty +frontend sits in the user's own bin directory. `familyTail` strips a leading +`pardes-gui` or `pardes` and requires what is left to be either empty or a +valid `-os-arch` pair, which is what keeps `pardes-snap` and `pardes-perf` out +of the family; `sameFamily` then accepts two names whose tails agree, or either +of which has no tail at all. The bundle's copy always has none, because +`CFBundleExecutable` is a fixed string: the bundled `pardes-macos-aarch64` is +called `pardes` and nothing in the name records what it was. So `pardes +src/foo.zig` inside the app's own shell opens a pane in the app. ## Fonts and zoom @@ -717,24 +741,39 @@ synchronize and no mailbox protocol to get wrong. ## Building -Two commands, because they are two different machines' problems. +One command builds everything this shell has. The two extra steps below exist +only for handing a bundle to somebody else. ```sh zig build -Dplatform=macos ``` produces `zig-out/lib/libpardes.a` and installs `zig-out/include/pardes.h` -beside it. - -`-Dplatform=macos` is the one platform that overrides the repo's default -target. Everything else defaults to the Steam Deck (x86_64 linux-gnu, glibc -pinned low), and that default is not survivable here: swiftc links this archive, -so a Steam Deck build hands ld64 ELF objects inside a GNU archive and the app -link dies with `archive member '/SYM64/' not a mach-o file`. On a Mac the -default becomes the host arch at `macos_min_version`, which is the same triple -the app's swiftc link is given, so the two halves of the app cannot disagree about -how old a macOS they support. On any other host it stays plain native, which is -what keeps the Linux dev loop below runnable. +beside it — and on a Darwin host a signed `zig-out/pardes.app` as well, which +the next section is about. + +`zig-out` and not `~/.local`, and that now needs saying. A bare `zig build` +redirects the install prefix to `$HOME/.local` and prints that it has, but only +behind the guard `also_gui and prefixIsUntouched(b)`: `also_gui` is +`requested_platform == null`, and `prefixIsUntouched` refuses when a `DESTDIR`, +a `--prefix` or a `--prefix-*dir` has already chosen somewhere. `-Dplatform=macos` +names a shell, so neither condition holds and every path in this document is +relative to `zig-out` unless you pass `--prefix` yourself. The `/dev` +directory the benchmark binaries install into likewise never appears in this +backend; nothing here is a dev binary. + +`-Dplatform=macos` is one of the two platforms that override the repo's default +target; `esp32p4` is the other, and pins its own riscv32-freestanding query. +The tty, SDL and web shells default to the Steam Deck (x86_64 linux-gnu, glibc +pinned to 2.38), and that default is not survivable here: swiftc links this +archive, so a Steam Deck build hands ld64 ELF objects inside a GNU archive and +the app link dies with `archive member '/SYM64/' not a mach-o file`. On a Mac +the default becomes the host arch at `macos_min_version`, which is the same +triple the app's swiftc link is given, so the two halves of the app cannot +disagree about how old a macOS they support — the arch is spelled rather than +left null so the CPU model resolves to generic, which is ghostty's +`Config.genericMacOSTarget` workaround. On any other host it stays plain +native, which is what keeps the Linux dev loop below runnable. That archive is also *fat*. `b.addLibrary` emits only this module's own objects; MuPDF, tree-sitter, zstbi, ZLS and ghostty-vt's simdutf/highway stay in archives @@ -749,23 +788,37 @@ through `ranlib` first, because ld64 otherwise refuses zig's layout outright missing, which links almost far enough to look like a source problem. ```sh -zig build -Dplatform=macos # zig-out/lib/libpardes.a + include/pardes.h -zig build macos-app -Dplatform=macos # ...and a signed zig-out/pardes.app -zig build macos-dmg -Dplatform=macos # ...and zig-out/pardes.dmg to hand over +zig build -Dplatform=macos # libpardes.a + pardes.h, and on Darwin a signed pardes.app too +zig build macos-app -Dplatform=macos # the signed bundle on its own +zig build macos-dmg -Dplatform=macos # ...and zig-out/pardes.dmg to hand over ``` -All three need a Darwin host: the app branch is gated on -`builtin.os.tag.isDarwin()`, and off Darwin `macos-app` and `macos-dmg` resolve -to an explicit build failure rather than a bundle that could not have been -signed. A plain `zig build -Dplatform=macos` still gives you the library and -the header anywhere, which is what the Linux dev loop below uses. +The first line is not "the library only", and that is the part worth stating: +`b.getInstallStep().dependOn(&sign.step)` puts the bundle on the DEFAULT +install step, so on a Darwin host an ordinary `zig build -Dplatform=macos` +assembles `zig-out/pardes.app` and ad-hoc-signs it. build.zig says why in as +many words — the app is "part of an ORDINARY build rather than a verb to +remember". Only the dmg is opt-in, because it is for handing over rather than +for running. + +What is gated is that whole branch, and on the TARGET as well as the host: +`builtin.os.tag.isDarwin() and target.result.os.tag.isDarwin()` is what decides +whether `fatArchive` runs, and without a Mach-O archive there is nothing for +swiftc to link. Off either, `macos-app` and `macos-dmg` resolve to an explicit +`addFail` — *"pardes.app needs a Darwin host and target; drop -Dtarget= or pass +-Dtarget=native"* — rather than a +bundle that could not have been signed, and the default install stops at the +library. The library and the header build anywhere, which is what the Linux dev +loop below uses. The bundle is assembled by `build.zig` itself, not by a script it shells out -to. An `.app` is a directory with a plist, a binary and an icon in it, and each -of those is one step whose inputs the build graph knows — so the app rebuilds -when a Swift source or the archive moves and is left alone when nothing does. -There is no Xcode project: a hand-written `pbxproj` would be a second build -system to keep in step for what three `addInstallFileWithDir` calls already do. +to. An `.app` is a directory with a plist, a binary, a shader and an icon in +it, and each of those is one step whose inputs the build graph knows — so the +app rebuilds when a Swift source or the archive moves and is left alone when +nothing does. There is no Xcode project: a hand-written `pbxproj` would be a +second build system to keep in step for what four `addInstallFileWithDir` calls +already do (`install_app_bin`, `install_plist`, `install_scene_kernel`, +`install_icon`). - **The plist.** `plutil -replace LSMinimumSystemVersion` reads the committed `src/macos/Info.plist` and writes a stamped copy into the cache. The source @@ -784,16 +837,24 @@ system to keep in step for what three `addInstallFileWithDir` calls already do. `defaultBG`, Glenda `defaultFG`, the strip above her the tag bar, the block cursor at the end of it `ansi16[11]`), and there is no binary blob in the tree to disagree with the app it ships in. -- **The link.** The same swiftc invocation as before, with the optimize mode - following `-Doptimize` so both halves of the app are built the same way: +- **The scene kernel.** `shaders/crt.ci.metal` is installed verbatim as + `Contents/Resources/crt.ci.metal` and compiled at runtime with + `CIKernel.kernels(withMetalString:)`. The same file is also an anonymous + module import named `effect-source-crt.ci.metal`, added by `build.zig`'s + per-shell module wiring under `if (shell == .macos)`, which is what lets + `EffectCode` print the exact source the app executes. +- **The link.** One swiftc invocation, with the optimize mode following + `-Doptimize` — `-Onone` for Debug, `-Osize` for ReleaseSmall, `-O` otherwise + — so both halves of the app are built the same way: ```sh -swiftc -O -target -apple-macos13.0 \ +swiftc <-O|-Osize|-Onone> -target -apple-macos13.0 \ -import-objc-header src/macos/pardes.h \ -o /pardes \ - src/macos/Sources/{main,AppDelegate,PardesView}.swift \ + src/macos/Sources/{main,AppDelegate,PardesView,ScenePostprocessor,FileWatcher}.swift \ /libpardes.a -lc++ \ - -framework AppKit -framework CoreText -framework CoreGraphics + -framework AppKit -framework CoreText -framework CoreGraphics \ + -framework CoreImage -framework Metal ``` The arch is derived from the build target, which defaults to the host — `arm64` @@ -846,10 +907,13 @@ for letting something other than this app link the core. This ships arm64. Three layers, and each one exists because the layer above it cannot reach where it goes. -**The Linux dev loop.** The Zig half of this backend is plain POSIX. `forkpty`, -`read`, `write`, `ioctl(TIOCSWINSZ)` and `/usr/bin/open` differ from the tty -backend by a string constant. So `-Dplatform=macos` compiles on a Linux host, -and its tests run there: +**The Linux dev loop.** The Zig half of this backend is plain POSIX, and most +of it is no longer even this backend's: `forkShell`, `writeFileBytes` and +`writeFd` are `src/host_io.zig`'s, byte-identical on both systems, and what is +left that differs is `ioctl(TIOCSWINSZ)` (absent from `std.c.T` on darwin), +`/usr/bin/open` against `/usr/bin/xdg-open` (`src/look.zig:33-37`), and libproc +against `/proc`. So `-Dplatform=macos` compiles on a Linux host, and its tests +run there: ```sh zig build unit-test -Dplatform=macos @@ -897,7 +961,8 @@ product. Scripts are `test/macos-snapshots/*.snap` (seven of them: boot, cwd, drop, font, keys, rotate, trackpad) and speak the tty suite's vocabulary (`start`, `wait`, `stable`, `text`, `key`, `snap`, `command`, `mouse`, `click`, `wheel`, `resize`, `draw`) plus what only -exists here: `fingers `, `force`, `rotate [gap_ms]`, `rotate_end`, +exists here: `fingers `, `force `, +`rotate [gap_ms]`, `rotate_end`, `drop `, `scroll `, `haptic `, `nsclick` (the AppKit-event path, as opposed to `click`'s direct entry-point call), `font `, `font-size `, `zoom`, @@ -983,6 +1048,20 @@ used for the table and is not folded into the direct-path claim. - **The `pipe` effect.** Selection filters need `pipeRequest(id)` copied into a job, a worker to run the command, and a `pipe_resp` event back. Same shape as `lsp`, one more response type. +- **Detached sessions.** `Attach` (`SPC s a`) and `Detach` (`SPC s D`) exist on + every hosted platform, this one included, because `Builtin.enabled` is + `pardes.hosted`. Neither works here. `Detach` emits `Effect.detach`, this + host fills in no `push_detach`, and `Pardes.perform` therefore reports + `error.NotAttached` on the pane's message row (`src/pardes.zig:6837`). + `Attach` emits `Effect.attach`, which `perform` turns into an `attach_req` + the shell is supposed to consume from OUTSIDE `pump` with `takeAttach` — and + `src/macos.zig` never calls `takeAttach`, so the word does nothing at all and + says nothing either. Wiring it up means a unix-socket frontend loop beside + the AppKit one, which is `src/detached/client.zig`'s job in the tty and SDL + shells; see `docs/detached.md`. +- **`tty_taken` on darwin.** The pull is wired (`ttyTaken`, `src/macos.zig:1838`) + but `look.ttyTaken` has no libproc implementation and answers `false`, so an + `Exec` here always believes the pane is still at the prompt it forked. - **IME and marked text.** Only finished characters reach `pardes_key`, so a dead key composes nothing and Option is Alt rather than a compose modifier. Real composition means implementing `NSTextInputClient` *and* giving the core diff --git a/docs/web.md b/docs/web.md index 0f8c068f..dfbc295d 100644 --- a/docs/web.md +++ b/docs/web.md @@ -37,14 +37,18 @@ core ticks, so 120/144 Hz displays do not accelerate frame-count transitions and a returning background tab has bounded catch-up work. The `web` platform is deliberately not one of the shader-capable native GUI -shells. It exposes no `Panel*` transition builtins (`PanelSlide`, `PanelZoom`, -`PanelDissolve`, `PanelAscii`, `PanelVertical`, `PanelEdges`, `PanelFall`, -`PanelWave`, `PanelCurtain`, `PanelScramble`, `PanelType`) and no -`Crt`/`Ripple`/`Glitch`: applying those faithfully -would require a second canvas renderer and give up the DOM renderer's -selectable/accessibility contract. Theme fades and the delayed, -side-effect-free Look hover remain grid animations and continue to use the -fixed 60 Hz ticks above. +shells, and two comptime capability flags say so once each. It exposes no +`Panel*` transition builtins (`PanelSlide`, `PanelZoom`, `PanelDissolve`, +`PanelAscii`, `PanelVertical`, `PanelEdges`, `PanelFall`, `PanelWave`, +`PanelCurtain`, `PanelScramble`, `PanelType`) because +`capabilities.panel_transitions` is `pardes.hosted`, and no +`Crt`/`Ripple`/`Glitch` because `capabilities.scene_shaders` is +`platform == .gui or platform == .macos` (`src/builtins.zig:41-42`; the words +themselves carry those availabilities in `src/runtime_config.zig:155-168`). +Applying either faithfully would require a second canvas renderer and give up +the DOM renderer's selectable/accessibility contract. Theme fades and the +delayed, side-effect-free Look hover remain grid animations and continue to use +the fixed 60 Hz ticks above. Build a replay from a dump: @@ -60,10 +64,12 @@ The output is in `zig-out/web`. Serve that directory over HTTP; browsers do not allow a useful WASM module load from `file:` URLs. The browser shell has no argv: every command-line flag `src/main.zig` parses — -`--tty`, `--tty-toggle`, `-n`, `-l`, `--nested`, `-h`, and the positional -file-or-directory — is native-only, because the wasm module roots at +`--tty`, `--tty-toggle`, `-n`, `-l`, `--fs[=]`, `--nested`, +`--detach[=]`, `--attach[=]`, `-h`/`--help`, `--version`, and the +positional file-or-directory — is native-only, because the wasm module roots at `src/web.zig` and never links `main.zig`. State comes from the embedded dump -instead. +instead, and there is nothing here to answer `--version` with even though +`pardes.version` and `pardes.commit` are compiled in like everywhere else. Web LOOK's source archive follows Git's working-tree view: tracked `.zig` files plus new, nonignored ones, sorted by path. The checked-in browser replay is a @@ -77,10 +83,18 @@ against the build-generated source archive rather than disk, and no language BACKEND is compiled in (`zls_backend` is off for wasm — which also means `lsp.supports` is empty, so the core never even raises a language query there, and the host leaves `lsp` null). + Replay terminals retain the same core shape but use Zig's failing IO value; this keeps the module freestanding without instantiating POSIX threaded IO that the browser can never call. +`Attach` and `Detach` are absent for the same structural reason and behind the +same gate: both are `enabled = pardes.hosted` (`src/builtins.zig`, and +`pardes.hosted` is `tty or gui or macos`), because a detached session is a unix +socket and a page has none. Nothing in this shell speaks +`src/detached/wire.zig`, and `push_detach` is one of the null vtable methods +below. + `src/web.zig` fills in six methods of `Host.VTable` and leaves the rest null, and a null method is answered by the core itself rather than forwarded to a page that could not honour it. The six are `present`, `write_file`, @@ -102,8 +116,12 @@ against the core's virtual filesystem, which is also where every read comes from — the core reads files itself and never asks a host; `watch_file` records the request; `lsp` answers an empty row set at once (`zls_backend` is off for wasm, so the core never raises a query there anyway) and `pipe` answers -failure; `wait_input` is null because wasm must never block, and `theme_file` -and `dump_themes` are not reachable without a config directory. `quit` sets the +failure; `wait_input` is null because wasm must never block, and `watch_theme` +and `dump_themes` are not reachable without a config directory. Of the +remaining nulls, `post_present` and `poll_frame` are per-frame host bookkeeping +a page has none of, `detach` has no session to leave, `gpio_toggle` belongs to +the ESP32-P4 board, and `fs_reply` answers a FUSE control filesystem that +`--fs` is the only way to ask for. `quit` sets the core's flag, which `pardes_should_quit` reports so the page can stop its frame loop. diff --git a/next-steps.txt b/next-steps.txt index 86e53908..3546da42 100644 --- a/next-steps.txt +++ b/next-steps.txt @@ -1,3 +1,35 @@ +WHAT THIS IS: a wishlist, in the author's own words. The items below are left +exactly as written; this header is the only thing kept in sync with the code, +because a list that reports shipped work as pending is worse than no list. + +SHIPPED (verified against the tree, not assumed): +- "version tracking" -> done. `build.zig` reads `.version` from build.zig.zon + via an untyped `@import`, `gitCommit(b)` reads the commit at configure time, + both reach `pardes_config`, and `pardes --version` prints them. +- "markdown treesitter highlight with nested codeblocks" -> done; markdown is + in `src/grammar_manifest.zig`. +- builtin `Changelog` -> done; `src/builtins.zig`. +- builtin `Newtty` -> done; `src/builtins.zig`. +- builtin `Joincol` -> done; `src/builtins.zig`. +- "only create a new column if both panes get at least 100 columns" -> done; + `pardes.zig` calls `p.columnFitsHalves(from_id, 100)` on the open path. +- "in a tty pane if the leaf process is a shell, ESC behaves as SHIFT-ESC" + -> done, in c3d0b84. `pardes.zig`: "A shell prompt is a pane you can leave: + plain Esc there is Shift-Esc", guarded by `takesCommandLine`, which is + `pane.isTerminal() and !p.hostTtyTaken(id)` — exactly "the leaf is a shell + and not a full-screen program". I first recorded this as PENDING by reading + the Shift-Esc CHORD handling thirty lines above it and concluding from the + absence there; the note is kept because the mistake is instructive. + +STILL PENDING, checked rather than guessed: +- `.patch`/`.diff` highlighting: no such grammar or extension in the tree. +- the tty escape-scanning `tests:` item, and the tty-colour bug: neither + confirmed fixed. The OTHER `tests:` item — "clipboard integration for tty + escape codes should work" — has shipped in both directions: `src/tty/tty.zig` + mirrors a yank register out via OSC 52 and reads the other way, the reply + arriving as an ordinary paste. ("neither" covered three items here until an + audit counted them.) + meta: - version tracking - special coloring algorithms are aplied based on the title of a pane. implemented as just ifs and else ifs. one of those ifs will be the one that choses the color algorithm for tty panes, other will select the treesitter coloring for sourcode files, and the other one will be the new one to highlight .patch and .diff files diff --git a/src/builtins.zig b/src/builtins.zig index 2c52b5f9..0e92f5ce 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -280,6 +280,58 @@ pub const Restore = struct { } }; +/// `Attach [name]` — hand this frontend's screen to a detached core, the one +/// `pardes --detach [name]` left running. Bare, it means "the session that is +/// there", which is the case worth typing: one detached session, and one word +/// to walk back into it. +/// +/// Nothing is torn down HERE, and that is the feature rather than an omission. +/// The effect only ASKS; the shell connects first and swaps second, so an +/// Attach that reaches nothing leaves this instance with every pane and every +/// undo exactly where they were and a line on the message row. Absent where +/// there is no unix socket to attach to — the browser and the board — and also +/// absent where the frontend would never NOTICE the request: see +/// `pardes.can_attach`, which is narrower than `hosted` because macOS never +/// polls `takeAttach`, so the word would have queued an effect and then done +/// nothing at all. +pub const Attach = struct { + pub const takes_arg = true; + pub const enabled = pardes.can_attach; + pub fn run(c: Ctx) void { + if (comptime enabled) ask(c) else unreachable; + } + /// A name too long for `Effect.attach` is too long for `sun_path` several + /// times over, so it can never name a session: reporting it here is the + /// same answer a failed connect gets, one round trip earlier. + fn ask(c: Ctx) void { + const name = c.arg orelse ""; + if (name.len > pardes.attach_name_max) + return c.p.reportError(c.id, comptime word(@This()), error.NameTooLong); + c.p.emit(.{ .attach = .{ .pane = @intCast(c.id), .name = .from(name) } }); + } +}; + +/// `Detach` — leave the session and let it carry on without you, which is +/// tmux's detach-client. Executed inside an ATTACHED frontend, where it +/// travels to the daemon as an ordinary command line, is run by the core that +/// owns the panes, and comes back as the effect that dismisses the screen +/// which asked for it. Hence no argument: the daemon knows who typed. +/// +/// It is NOT `Attach` backwards, and no word here is. Making a live local +/// session outlive its terminal means setsid and a fork; a word that pretended +/// to would hand you a session that dies with the window it was typed in. Run +/// locally this therefore REPORTS rather than acts — see the `.detach` arm of +/// Pardes.perform, which finds no host method to call. +pub const Detach = struct { + pub const enabled = pardes.can_attach; + pub fn run(c: Ctx) void { + if (comptime enabled) + c.p.emit(.{ .detach = .{ .pane = @intCast(c.id) } }) + else + unreachable; + } +}; + // ---- the message row ---- /// TEXT onto this pane's transient message row — the row a failed save, a @@ -814,7 +866,17 @@ pub const Last = struct { while (i > 0) { i -= 1; const j = c.p.jumps[i]; - if (j.pane != c.id) return c.p.focusPaneLine(j.pane, .{ .line = j.line, .col = j.col }); + // `.keep`: Esc is a RETURN, and the pane still holds the view it + // was left with. Recentring it moved the whole screen to show a line + // that was, nearly always, already on it. + // + // Not `line = 0`, which focusPaneLine already understands as "focus + // and touch nothing": a background pane's view CAN move while you + // are away — the wheel scrolls the pane under the pointer, not the + // active one, and a resize recomputes geometry without revealing any + // cursor — so `.keep` restores the recorded cursor and lets + // ensureCursorVisible pull it back on screen by the least it can. + if (j.pane != c.id) return c.p.focusPaneLine(j.pane, .{ .line = j.line, .col = j.col }, .keep); } } }; diff --git a/src/config.zig b/src/config.zig index 15a21866..8d186435 100644 --- a/src/config.zig +++ b/src/config.zig @@ -228,6 +228,26 @@ pub const leader_path = paths: { table.set(.ThemeFile, null); table.set(.DumpThemes, null); } + // ...and the one native word that DOES earn a chord. Gated on `can_attach` + // and NOT on `hosted`, because this table may only name a builtin that + // exists: macOS is hosted but never polls `takeAttach`, so the two words + // below are compiled out there and naming them would be a compile error — + // which is the good outcome, and the reason the predicate exists. + if (pardes.can_attach) { + // In the `s` session group beside Dump and Restore. Bare Attach means + // "whichever detached session is there", which is the whole case worth + // a key; the named form is typed, like every other builtin that takes + // an operand. Safe to press by accident, uniquely among the three: it + // connects before it swaps, so nothing to attach to costs you a + // message row. + table.set(.Attach, "sa"); + // ...and the way back out, which is where the group runs out of + // letters: `sd` has been Dump's since before there was anything to + // detach from, and Detach is not worth breaking that muscle memory + // for. A capital where the lowercase is taken is what this table + // already does one group over (`lS` beside `ls`, `lD` beside `ld`). + table.set(.Detach, "sD"); + } // The bare-metal memory words. Peek, Poke and Hexdump all take an ADDRESS, // so none of them can have a leader path for the reason Theme and Msg have // none: a key path names a builtin and can never carry an operand. diff --git a/src/detached/client.zig b/src/detached/client.zig index 8d118782..07c85945 100644 --- a/src/detached/client.zig +++ b/src/detached/client.zig @@ -1,11 +1,21 @@ //! THE FRONTEND SIDE of a detached session: a socket, a grid, and no core. //! -//! THIS SIDE DOES NOT OWN A `Pardes`. That is the one thing to be clear about, -//! because the shape invites the mistake: there is no `update`, no `postEvent` -//! and no `Host` in this file. The core lives in the detached process -//! (server.zig), which is also where every `Host.VTable` call originates. A -//! frontend's whole job is two sentences long — send the input it collects, draw -//! the frames it is sent — and this module is exactly that and nothing more. +//! A FRONTEND IS INPUT AND SCREEN, and that is the whole of it. Keystrokes, +//! mouse, window size go out; frames come back and get painted. This is the +//! shape the ESP32-P4 serial console has always had — a panel and a keypad on +//! the far end of a wire, performing no effects of its own — and after the +//! machine-local IO moved into the daemon it is the shape EVERY frontend has, +//! terminal and window alike. +//! +//! THIS SIDE DOES NOT OWN A `Pardes`. There is no `update`, no `postEvent` and +//! no `Host` in this file. The core lives in the detached process (server.zig), +//! which is also where every `Host.VTable` call now lands: forking a pane's +//! shell, writing a file, watching a path and reading the theme directory are +//! all done BY THE DAEMON, against the same machine's kernel it shares with +//! this frontend over an AF_UNIX socket. NO FORK HAPPENS IN A FRONTEND ANY +//! MORE. That is not a simplification of this file, it is the fix: a pane's +//! shell used to be a child of whichever frontend forked it, so leaving killed +//! the shells of a session whose entire promise is outliving frontends. //! //! `Client` is NOT a renderer either. It owns `grid` and `cursor`: the cells the //! session is showing, kept current by applying frames as they arrive. Whoever @@ -20,23 +30,22 @@ //! seconds before its terminal is even in raw mode. `attached()` says whether //! the session has greeted us; `refusal` says why it did not. //! -//! WHAT ARRIVES, and what a frontend is expected to do with it. `next` hands -//! back one decoded `wire.ServerMsg` at a time: +//! WHAT ARRIVES. `next` hands back one decoded `wire.ServerMsg` at a time, and +//! there are exactly seven of them: //! * `welcome` and `frame` have already been applied to `slot`/`cols`/`rows`/ //! `grid`/`cursor` by the time you see them. They are returned so a frontend //! knows the screen moved. -//! * `spawn`, `pty_write`, `pty_resize`, `write_file`, `write_dump`, -//! `watch_file`, `watch_theme`, `dump_themes` are the session asking this -//! frontend for the real host work it has and a daemon does not: fork a -//! shell on a real tty, put bytes on a real disk, watch a path. A frontend -//! with none of that ignores them, exactly as a null vtable method does — -//! and only ONE attached frontend is ever asked (server.zig's routing). -//! * `set_clipboard`, `open_link` and `read_clipboard` are the desktop. The -//! answer to `read_clipboard` is not a reply message: it is an ordinary +//! * `set_clipboard`, `read_clipboard` and `open_link` are the only effects +//! still on the wire, and they are here because each needs THIS HUMAN'S +//! DISPLAY: a daemon nobody is looking at has no clipboard and no browser. +//! The answer to `read_clipboard` is not a reply message: it is an ordinary //! `Event.paste` sent back through `send`, which is the same asynchronous //! shape `pull_read_clipboard` already has in-process. //! * `refuse` is followed by the session closing the connection, and `quit` //! means the session itself has ended. +//! The switch in `next` is exhaustive over that set on purpose: putting a +//! machine-local effect back on the wire is a compile error here, and the test +//! at the bottom of this file says so in the other direction too. //! //! GEOMETRY. `cols`/`rows` are the SESSION's grid, which with several frontends //! attached is the smallest common one and can be smaller than this frontend's @@ -45,8 +54,8 @@ //! //! BORROWED BYTES. Every slice in a returned `ServerMsg` points into this //! client's receive buffer and is valid until the next call to `next` or -//! `wait`. A frontend that needs a path or a payload for longer copies it — the -//! same rule the core's own `Event.output` bytes have. +//! `wait`. A frontend that needs a payload for longer copies it — the same rule +//! the core's own `Event.output` bytes have. const std = @import("std"); const libc = std.c; const pardes = @import("../pardes.zig"); @@ -104,6 +113,15 @@ pub const Client = struct { pub fn open(gpa: std.mem.Allocator, name: []const u8, cols: u16, rows: u16) (Error || wire.Error)!Client { var path_buf: [server.path_max]u8 = undefined; const path = server.sessionPath(&path_buf, name) orelse return error.NoSessionPath; + // `vetted` is one predicate over two different failures, and a human is + // owed different words for them: `NotPrivate` promises, in its own doc + // above, that the socket IS there. So ask the cheap question first, + // because the commonest failure of all is a mistyped session name — + // until this, `Attach nosuchsession` put "attach: NotPrivate" on the + // message row, which reads as an accusation rather than a typo. Spelled + // with the `access` this file already uses in `resolve`. + const F_OK: c_int = 0; + if (libc.access(path, F_OK) != 0) return error.NoSession; // Both ends vet, and this is this end's half: the session refuses a // directory or a socket anyone else can reach before it binds, and until // this a frontend connected to whatever it found at the path it derived. @@ -113,10 +131,10 @@ pub const Client = struct { @memcpy(addr.path[0 .. path.len + 1], path[0 .. path.len + 1]); const fd = libc.socket(libc.AF.UNIX, libc.SOCK.STREAM, 0); if (fd < 0) return error.NoSession; - // CLOEXEC before anything can fork, and a frontend DOES fork: the pane - // shells it is asked to spawn are its own children, and one of them - // holding this socket would keep the session believing a frontend is - // attached long after this process left. + // CLOEXEC anyway, even though a frontend no longer forks pane shells: + // `open_link` runs this display's browser, and a `xdg-open` inheriting + // this socket would keep the session believing a frontend is attached + // long after this process left. server.setCloexec(fd); if (comptime server.darwin) { // linux says MSG_NOSIGNAL per write and darwin says it once per @@ -166,8 +184,11 @@ pub const Client = struct { } /// One message on its way to the core. Everything a frontend collects goes - /// through here: keys, the mouse, pty output from the shells it forked, a - /// paste answering a `read_clipboard`. + /// through here, and after the IO moved into the daemon that is a short + /// list: keys, the mouse, a window resize, and the paste that answers a + /// `read_clipboard`. `ClientMsg.output`/`.eof` still exist on the wire but + /// no frontend sends them any more — pty bytes are read by the process that + /// forked the shell, which is the daemon. pub fn send(c: *Client, msg: wire.ClientMsg) (Error || wire.Error)!void { const want = wire.clientBound(msg); c.out.ensureUnusedCapacity(c.gpa, want) catch return error.Closed; @@ -266,7 +287,28 @@ pub const Client = struct { // The session has ended. Left for the caller to act on, and the // descriptor stays open so `deinit` is the only place that closes. .quit => {}, - else => {}, + // THIS frontend is done and the session is not. Same shape as + // `quit` and the same non-answer here — the caller leaves its loop + // — but the opposite meaning about what survives, so a frontend + // must not collapse the two: `quit` is the session ending and + // `detach` is success. Guarded, because a peer that has not greeted + // us has no standing to dismiss us either. + .detach => if (!c.attached()) return error.Ungreeted, + // The three display effects need this process's clipboard and this + // process's browser, so they are the caller's to perform and there + // is nothing for a `Client` to update. Listed rather than swept up + // by an `else`: an `else` here would silently accept a + // machine-local effect returning to the wire, and the point of the + // rewrite is that it cannot. + // + // Guarded like `.frame`, and for a stronger reason than drawing: + // these reach the human's clipboard and the human's browser. The + // protocol version is checked in the `.welcome` arm above and + // nowhere else, so a peer that simply never greets us has had its + // version checked by nobody — and until it does, it does not get to + // open a URI on this display or read this display's selection back + // over the socket. `Ungreeted` is the same refusal a frame gets. + .set_clipboard, .read_clipboard, .open_link => if (!c.attached()) return error.Ungreeted, } return msg; } @@ -337,6 +379,201 @@ const poll_hup = server.poll_hup; const poll_err = server.poll_err; const poll_nval = server.poll_nval; +/// Which session an attach meant, answered before a frontend opens its window. +/// +/// `--attach=` is taken at its word beyond one `access` on the socket +/// file, because the connect is the real authority on whether anything is +/// listening — and a typo is the one failure worth catching earlier, since the +/// alternative is a full-screen flash on the way to a one-line message. Bare +/// `--attach`, and the bare `Attach` word, is THE session: bare `--detach` +/// names itself by its own pid, and nobody can be expected to read a pid out +/// of `$XDG_RUNTIME_DIR`. With exactly one listening, that is the one meant; +/// with none or several this says WHICH case it is instead of picking one. +/// +/// Both the directory and the filename convention come off ONE probe through +/// `server.sessionPath` rather than being re-derived here, for the reason that +/// function exists at all: the side that binds and the side that looks must +/// never be able to disagree about where a session lives. +/// +/// It lives in this file, rather than in either shell, because BOTH frontends +/// now ask the same question — a terminal for `--attach` and the SDL window +/// for the `Attach` word — and a second copy of a directory scan is exactly +/// how the two of them would start disagreeing. +pub const Resolved = union(enum) { + /// The session to open. Borrows `requested` when it was named, and `buf` + /// when it had to be scanned for. + name: []const u8, + /// No socket of that name, or no session at all. The caller knows which it + /// asked for, so it owns the wording. + none, + /// Several are listening, and choosing between them is not ours to do. + ambiguous: usize, +}; + +pub fn resolve(buf: *[server.path_max]u8, requested: []const u8) Resolved { + if (requested.len != 0) { + var one_buf: [server.path_max]u8 = undefined; + const one = server.sessionPath(&one_buf, requested) orelse return .none; + const F_OK: c_int = 0; + if (libc.access(one, F_OK) != 0) return .none; + return .{ .name = requested }; + } + const probe_name = "0"; + var probe_buf: [server.path_max]u8 = undefined; + const probe = server.sessionPath(&probe_buf, probe_name) orelse return .none; + const base = std.fs.path.basename(probe); + const cut = std.mem.lastIndexOf(u8, base, probe_name).?; + const prefix = base[0..cut]; + const suffix = base[cut + probe_name.len ..]; + var dir_buf: [server.path_max:0]u8 = undefined; + const dir = std.fs.path.dirname(probe) orelse ""; + if (dir.len == 0 or dir.len >= dir_buf.len) return .none; + @memcpy(dir_buf[0..dir.len], dir); + dir_buf[dir.len] = 0; + const d = libc.opendir(dir_buf[0..dir.len :0]) orelse return .none; + defer _ = libc.closedir(d); + var found: usize = 0; + var len: usize = 0; + while (libc.readdir(d)) |ent| { + const entry = std.mem.sliceTo(&ent.name, 0); + if (entry.len <= prefix.len + suffix.len) continue; + if (!std.mem.startsWith(u8, entry, prefix) or !std.mem.endsWith(u8, entry, suffix)) continue; + const name = entry[prefix.len .. entry.len - suffix.len]; + if (name.len > buf.len) continue; + found += 1; + @memcpy(buf[0..name.len], name); + len = name.len; + } + // A socket file whose session is gone still counts here: the sweep that + // unlinks corpses runs when the NEXT session binds (server.zig `sweep`), + // and probing every candidate with a connect would put a phantom frontend + // into a live session's slot table just to count it. One stale file + // therefore fails at `open` with "no such session", which is the truth. + if (found != 1) return if (found == 0) .none else .{ .ambiguous = found }; + return .{ .name = buf[0..len] }; +} + +/// How long a frontend's attached loop waits on the socket before it goes back +/// to whatever else it owns. It lives HERE, beside the `wait` it parameterises, +/// because both frontends need it and both had defined it for themselves — +/// which is how a measured number drifts from the thing it was measured +/// against. +/// +/// A frontend cannot hand `poll(2)` one descriptor for the session and one for +/// its own input: vaxis delivers the terminal's events on a reader thread into +/// a mutex/condvar queue, and SDL has its own pump, so neither has a +/// descriptor. `wait` takes a timeout for exactly that reason. +/// +/// 8 ms is half a 60 Hz frame: a keystroke waits at most one of those before it +/// is on the wire (4 ms on average), and the frame it causes needs no wait at +/// all — it lands in the poll the moment the session writes it. The price is +/// 125 poll rounds a second on a frontend nobody is touching, measured below +/// the noise of what an idle pardes already costs: on an i7-11700 at 100 Hz +/// jiffies an idle attached frontend used 0.16% of one core over 60 s and 0.18% +/// over 120 s, against 0.11% and 0.31% for an idle in-process session on the +/// same screen over the same windows. Reach for an eventfd and a waker thread — +/// fuse.zig's `pollLoop` is the pattern — only if that stops being true. +pub const poll_ms: u32 = 8; + +/// One round of waiting for the greeting, and how many of them. The COUNT is +/// derived from `server.greet_deadline_default_ms` rather than written down +/// again, so the two ends of the handshake give each other the same grace out +/// of one number instead of two that can drift apart. +/// +/// Rounds rather than a clock, and that is sound rather than lazy: the only +/// message a session may legally send before its `welcome` is a `refuse`. +/// Everything else is refused by `next` as `Ungreeted` — a frame because it +/// would be drawing for a connection that was never accepted, and the three +/// display effects because a peer whose version nobody has checked does not get +/// to open a URI or read a clipboard. So a peer that is silent costs one whole +/// timeout per round, which makes the round count a real wall-clock bound; and +/// a peer that is NOISY but ungreeted fails on its first message rather than +/// spending the budget. +const greet_round_ms: u32 = 250; +const greet_rounds: u32 = server.greet_deadline_default_ms / greet_round_ms; + +/// What came of trying to attach. A VALUE and not an error union, because four +/// of the six outcomes are ordinary answers a human needs different words for, +/// and because the two frontends report them by different mechanisms — a +/// terminal prints a sentence and exits, a window logs and unwinds. `resolve` +/// above returns a union for the same reason. +pub const Attempt = union(enum) { + /// Resolved, connected, AND greeted. The caller owns it. + greeted: Client, + /// No socket of that name, or nothing detached at all. The caller knows + /// which it asked for, so the wording is the caller's. + no_session, + /// Several are listening and choosing is not ours to do. + ambiguous: usize, + /// The session said no, and said why. + refused: wire.Refusal, + /// It accepted and then never greeted us inside the deadline. + silent, + /// Everything else, already reported by the errno it came from. + lost: anyerror, +}; + +/// Resolve a name, connect to it, and WAIT FOR THE WELCOME. On every failure +/// path this closes whatever it opened, so a caller that gets anything but +/// `.greeted` has nothing to clean up. +/// +/// The waiting is the point, and it is why this function exists rather than +/// each frontend calling `resolve` and `open` in turn. `open` is not a +/// handshake — it connects and writes the hello, and the `welcome` or the +/// `refuse` arrives later through this loop. A frontend that treats a +/// successful `connect(2)` as proof of attachment will tear its local session +/// down — reap its pane shells, unmount its control filesystem, free every +/// undo history — and only then discover `refuse .version`, which is the +/// routine case: `zig build` replaces the binary under a running session, so +/// two protocol versions on one machine is expected rather than exotic. The +/// contract the `Attach` word owes is that a failed attach changes NOTHING, and +/// that contract can only be kept by a caller that has the welcome in hand +/// before it starts destroying things. +pub fn attempt(gpa: std.mem.Allocator, requested: []const u8, cols: u16, rows: u16) Attempt { + var buf: [server.path_max]u8 = undefined; + const name = switch (resolve(&buf, requested)) { + .name => |n| n, + .none => return .no_session, + .ambiguous => |n| return .{ .ambiguous = n }, + }; + var c = Client.open(gpa, name, cols, rows) catch |err| return switch (err) { + // `open`'s own two ways of saying "there is nothing there" collapse + // into the one the caller has wording for. + error.NoSession, error.NoSessionPath => .no_session, + else => .{ .lost = err }, + }; + var rounds: u32 = 0; + while (rounds < greet_rounds) : (rounds += 1) { + c.wait(greet_round_ms) catch |err| return give(&c, err); + // Drain whatever landed. `next` is what applies the welcome, so the + // loop below is not discarding anything it needs — the state it wants + // is in `c` afterwards. + while (true) { + const msg = c.next() catch |err| return give(&c, err); + if (msg == null) break; + } + if (c.attached()) return .{ .greeted = c }; + if (c.refusal) |why| { + c.deinit(); + return .{ .refused = why }; + } + } + c.deinit(); + return .silent; +} + +/// Close, and say what the failure actually was. A session that refuses us +/// writes its reason and closes in the same pass (server.zig `refuseFd`), so a +/// read error here is usually the far side hanging up on a refusal we have +/// already decoded — reporting that as a lost connection would throw away the +/// one sentence worth telling the human. +fn give(c: *Client, err: anyerror) Attempt { + const why = c.refusal; + c.deinit(); + if (why) |w| return .{ .refused = w }; + return .{ .lost = err }; +} + // --------------------------------------------------------------------------- // tests // --------------------------------------------------------------------------- @@ -346,7 +583,6 @@ const poll_nval = server.poll_nval; // the transport as the core actually drives it rather than a mock of it. const testing = std.testing; -const host_api = @import("../host.zig"); extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int; extern "c" fn unsetenv(name: [*:0]const u8) c_int; @@ -382,12 +618,17 @@ const Harness = struct { errdefer h.core.deinit(); h.arena = .init(testing.allocator); errdefer h.arena.deinit(); - h.session = .{ .gpa = testing.allocator, .core = h.core, .cols = cols, .rows = rows }; + // `io` is not optional on `Session`: the daemon does the file watching + // and the theme scan itself now, and both of those take a `std.Io`. + h.session = .{ .gpa = testing.allocator, .io = std.testing.io, .core = h.core, .cols = cols, .rows = rows }; h.name = "s"; try testing.expect(h.session.listen(h.name)); // The pre-loop drain tty.zig has, for its reason: the startup spawns are // already queued and a session must not open its socket with panes that - // have not been created. + // have not been created. These now fork IN THIS PROCESS, because the + // daemon is the thing that owns pane shells — which is exactly the + // property under test, and the reason no frontend below is ever asked + // to fork anything. h.core.host = h.session.host(); while (h.core.nextEffect()) |e| h.core.perform(e); } @@ -553,7 +794,13 @@ test "detached session: two frontends share one screen at the smallest common gr try h.pumpUntilGrid(&a, 50, 12); try h.pumpUntilGrid(&b, 50, 12); try testing.expectEqual(@as(usize, 50 * 12), a.grid.items.len); - try expectSameScreen(a.grid.items, b.grid.items); + // CONVERGENCE, not a snapshot. `pumpUntilGrid(&b, ...)` pumped the session + // while draining only `b`, and the daemon owns the pane shells now: a + // prompt arriving on a pty moves the screen between pumps, so `a` can be + // holding an undrained frame and comparing the two grids here would compare + // two instants. What a shared session promises is that they agree, which is + // what this waits for. + try h.pumpUntilSameScreen(&a, &b); // ...and input from EITHER moves that one screen. Both are drained on every // pump before they are compared: one pump sends every attached frontend a @@ -592,7 +839,11 @@ test "detached session: a frontend that dies takes nothing with it" { defer d.deinit(); try testing.expectEqual(@as(u8, 1), d.slot); try testing.expectEqual(wire.FrameKind.full, (try h.pumpUntil(&d, .frame)).frame.kind); - try expectSameScreen(a.grid.items, d.grid.items); + // ...and it is the SAME screen the survivor is looking at. Waited for + // rather than snapshotted, for the reason above: `pumpUntil(&d, ...)` + // drained only `d`, and pane output means the screen does not stand still + // between pumps. + try h.pumpUntilSameScreen(&a, &d); } test "detached session: a frontend speaking another protocol is refused, loudly" { @@ -674,7 +925,7 @@ test "detached session: the session outlives every frontend and keeps its grid" try testing.expectEqual(@as(usize, 90 * 30), again.grid.items.len); } -test "detached session: the seam's own routing rules, per method" { +test "detached session: the seam's own routing rules, per surviving effect" { var h: Harness = undefined; try h.init(60, 16); defer h.deinit(); @@ -686,31 +937,93 @@ test "detached session: the seam's own routing rules, per method" { _ = try h.pumpUntil(&b, .frame); const host = h.session.host(); - // The eight `push_` methods with one real resource behind them go to the - // PRIMARY only — the oldest surviving attachment — because two frontends - // forking a shell for one pane gives that pane two shells. - host.vtable.push_spawn.?(host.ctx, 1, "/tmp"); - try expectOnly(&h, &a, &b, .spawn); - host.vtable.push_pty_write.?(host.ctx, 1, "ls\n"); - try expectOnly(&h, &a, &b, .pty_write); - host.vtable.push_write_file.?(host.ctx, 1, "/tmp/x", "body"); - try expectOnly(&h, &a, &b, .write_file); - - // ...and the ones that are facts about the SESSION go to everybody. + // BROADCAST: the yank register is a fact about the session, so every + // display it is being watched on gets it. host.vtable.push_set_clipboard.?(host.ctx, "yank"); try expectBoth(&h, &a, &b, .set_clipboard); - // The one pull on the wire goes to the frontend whose input caused it, and - // it is asked ONCE — two frontends answering would paste twice for one - // Ctrl-V, which is the rule host.zig states. + // ORIGIN, ELSE PRIMARY. This is the "only one frontend is asked" rule that + // the shell-forking and file-writing messages used to demonstrate; the + // daemon does that work itself now, so the same claim is made about the two + // effects that still travel. `read_clipboard` is asked ONCE — two frontends + // answering would paste twice for one Ctrl-V, which is the rule host.zig + // states. h.session.origin = 1; host.vtable.pull_read_clipboard.?(host.ctx); try expectOnly(&h, &b, &a, .read_clipboard); - h.session.origin = 0; + // `open_link` follows the same origin: the browser that opens is the one on + // the display of the human who clicked, not the oldest attachment's. host.vtable.push_open_link.?(host.ctx, "https://x"); + try expectOnly(&h, &b, &a, .open_link); + // ...and with no origin it falls back to the primary, which is what a link + // opened by something other than a keystroke gets. + h.session.origin = 0; + host.vtable.push_open_link.?(host.ctx, "https://y"); try expectOnly(&h, &a, &b, .open_link); } +test "detached session: a frontend is never asked to fork, write, or watch" { + // THE INVARIANT OF THE WHOLE DETACHED DESIGN, pinned as a property of the + // protocol rather than of one code path: a frontend is input and screen, so + // the set of messages that can reach it is exactly the eight below. If a + // machine-local effect is ever put back on the wire, a frontend becomes the + // process that owns a pane's shell again — and a pane whose shell belongs to + // a frontend dies when that frontend leaves, which is the bug this replaced. + // + // Adding a name here is meant to be an ARGUMENT, not a formality. The bar is + // the one `quit` and `detach` clear and `spawn` cannot: it needs this + // human's screen, keyboard, clipboard or browser, or it is the session + // telling this frontend about its own membership. Anything that touches a + // disk or a process table fails that bar by construction. + const allowed = [_][]const u8{ + // Session control: who this connection is, and whether it is still one. + // `detach` is this frontend leaving and `quit` is the session ending — + // opposite meanings, same shape, and neither is an effect. + "welcome", "refuse", "frame", "quit", "detach", + // The three that need THIS human's display and cannot be done by a + // daemon nobody is looking at. + "set_clipboard", "read_clipboard", "open_link", + }; + + // One: the union a frontend decodes into has no other variant. Named + // rather than counted, so re-adding `spawn` fails with the name in it. + const fields = @typeInfo(wire.ServerMsg).@"union".fields; + inline for (fields) |f| { + for (allowed) |ok| { + if (std.mem.eql(u8, f.name, ok)) break; + } else { + std.debug.print("ServerMsg.{s} is not an effect a frontend may perform\n", .{f.name}); + return error.MachineLocalEffectOnTheWire; + } + } + try testing.expectEqual(allowed.len, fields.len); + + // Two: and no TAG BYTE outside them decodes either — the check above is + // about this build's union, this one is about the bytes on the socket. Every + // other byte must be `BadTag`, including the eight that used to be defined: + // a session built before this change cannot talk a frontend into forking. + var accepted: usize = 0; + for (0..256) |i| { + const tag: u8 = @intCast(i); + // Payloads are deliberately empty: what is asked is whether the TAG is + // known, and every known tag fails later (`Truncated`) or succeeds, but + // never with `BadTag`. + if (wire.decodeServer(tag, &.{})) |_| accepted += 1 else |err| switch (err) { + error.BadTag => continue, + else => accepted += 1, + } + const named = for (allowed) |ok| { + const want = std.meta.stringToEnum(wire.ServerTag, ok).?; + if (@intFromEnum(want) == tag) break true; + } else false; + if (!named) { + std.debug.print("tag 0x{x:0>2} is decodable by a frontend and is not one of the seven\n", .{tag}); + return error.MachineLocalEffectOnTheWire; + } + } + try testing.expectEqual(allowed.len, accepted); +} + test "detached session: the client table is a refusal, not a queue" { // A small grid on purpose: these thirty-two peers never read, and a full // frame of 80x24 each would push them into the backlog rule that the next diff --git a/src/detached/server.zig b/src/detached/server.zig index 6d13a4ae..93c964b4 100644 --- a/src/detached/server.zig +++ b/src/detached/server.zig @@ -1,53 +1,96 @@ //! THE DETACHED CORE: one `Pardes` instance in a process with no terminal, //! serving N frontends over one unix socket. //! -//! THIS SIDE OWNS THE CORE. `Session` is a `host.Host` implementation whose -//! methods encode wire messages instead of doing IO, and whose -//! `pull_wait_input` is a `poll(2)` over the listener and every attached -//! frontend. The frontends own terminals and nothing else (client.zig). So the -//! `Pardes` is here, `update` is called from here, and the same screen is on -//! every attached frontend at once — `screen -x`, not N sessions. +//! THIS SIDE OWNS THE CORE, AND EVERYTHING UNDER IT. `Session` is a `host.Host` +//! implementation that performs the machine-local half of a host itself — it +//! forks the pane shells, writes the files, watches the paths — and whose +//! `pull_wait_input` is one `poll(2)` over the listener, every attached +//! frontend, every pane's pty master and the inotify descriptor. A frontend owns +//! a screen and a keyboard and nothing else (client.zig). So the `Pardes` is +//! here, `update` is called from here, the shells are forked from here, and the +//! same screen is on every attached frontend at once — `screen -x`, not N +//! sessions. //! -//! WHAT THIS SIDE SERVES ITSELF. Every method this vtable leaves null falls -//! through to the core's own `host.Fallback`: the embedded source filesystem, -//! the in-process clipboard, silent ptys. host.zig says in as many words that a -//! zero-method host is a complete pardes, and that is exactly what a session -//! with nothing attached is. Everything a real frontend can do BETTER — fork a -//! shell on a real tty, put bytes on a real disk, reach a real desktop -//! clipboard — is asked of a frontend, and the routing table below says which. +//! WHAT THIS SIDE SERVES ITSELF, WHICH IS NOW ALL OF IT. A unix socket means +//! the core and its frontends are on the SAME machine, so there is no question +//! of whose process table, whose disk or whose inotify descriptor a call is +//! about — and given that, the process that must hold them is the long-lived +//! one. A shell forked by a frontend dies with that frontend, and a session +//! whose whole promise is outliving the frontend attached to it cannot keep its +//! panes that way. So this file forks the pane shells (`host_io.forkShell`), +//! writes the files (`host_io.writeFileBytes`), marks the directories +//! (file_watch.zig) and drains the pty masters in its own `poll(2)`. THE PANE +//! SHELLS OUTLIVE EVERY FRONTEND: attach, detach, kill the terminal, attach +//! from another one, and the build that was running in pane 3 is still running +//! and has been scrolling into the core the whole time. +//! +//! Only what this vtable leaves null falls through to the core's own +//! `host.Fallback` — and host.zig says in as many words that a zero-method host +//! is a complete pardes. What a frontend can still do BETTER is exactly what +//! needs the human's own display, and nothing else: put a yank on the clipboard +//! in front of them, take a paste off it, open a link in their browser. Three +//! messages, which is why the routing table below is as short as it is. //! //! ROUTING, and it is not "push means broadcast". A push reaches every HOST //! (host.zig's rule, which `Fanout.isPull` enforces); this is ONE host that //! happens to be backed by several frontends, and how it spreads a call inside -//! itself is its own business. Three rules, one per kind of side effect: +//! itself is its own business. Two rules over four messages: //! * BROADCAST — the frame, and `set_clipboard`. Every screen must show the //! same thing, and a yank in a shared session is a session-wide fact that //! every attached desktop is entitled to. -//! * PRIMARY ONLY — `spawn`, `pty_write`, `pty_resize`, `write_file`, -//! `write_dump`, `watch_file`, `watch_theme`, `dump_themes`. Each of these -//! has ONE real resource behind it, and doing it twice is not doing it -//! twice as well: two frontends forking a shell for pane 3 gives the pane -//! two shells, and two frontends writing one path race each other. Primary -//! is the lowest attached slot, i.e. the oldest surviving attachment — a -//! rule that is stable while frontends come and go and needs no election. -//! A pane's shell therefore lives in the frontend that forked it: when that -//! frontend leaves, its panes stop producing output and the session's text, -//! files and layout carry on. That is a real limit and it is stated here -//! rather than papered over, because migrating a live pty between processes -//! is a different feature. -//! * ORIGIN, ELSE PRIMARY — `read_clipboard` (the one `pull_` on the wire) -//! and `open_link`. Both answer a thing a HUMAN just did, and the answer -//! belongs on that human's machine: the paste must come from the keyboard -//! that asked for it, and a link must open in front of the person who -//! clicked it. `origin` is the frontend whose event was applied most -//! recently. Effects drain after a whole batch of events (pardes.zig +//! * ORIGIN, ELSE PRIMARY — `read_clipboard` (the one `pull_` on the wire), +//! `open_link`, and `detach`. Each answers a thing a HUMAN just did, and the +//! answer belongs to that human: the paste must come from the keyboard that +//! asked for it, a link must open in front of the person who clicked it, and +//! a `Detach` typed in one frontend must send THAT frontend away and leave +//! the others painting. `origin` is the frontend whose event was applied +//! most recently. Effects drain after a whole batch of events (pardes.zig //! `pump`), so in the rare case where two frontends type in the same -//! millisecond the second one wins; the fallback to primary covers an +//! millisecond the second one wins; the fallback to `primary` — the lowest +//! attached slot, i.e. the oldest surviving attachment, a rule that is +//! stable while frontends come and go and needs no election — covers an //! effect that no input caused at all. //! -//! FAIRNESS, and why no client can stall the core or another client: -//! * every descriptor is non-blocking, and there is no thread per client. One -//! `poll(2)` per pump covers the listener and all `max_clients` frontends. +//! `detach` is the odd one and is worth naming as such: it is not an EFFECT the +//! session performs on the world, it is SESSION CONTROL — one frontend asking to +//! stop being a frontend. That is why wire.zig gives it 0x05, in the +//! 0x01..0x0f session range beside `quit`, rather than a number in the 0x10.. +//! range where every tag is one `push_` method that reaches a disk, a clipboard +//! or a browser. And it is why this side does nothing but send it: see `detach`. +//! There is no third rule, and the class of message it used to serve is gone: +//! `spawn`, `pty_write`, `pty_resize`, `write_file`, `write_dump`, `watch_file`, +//! `watch_theme` and `dump_themes` were routed to ONE frontend precisely +//! because each has one real resource behind it, and every one of them is now +//! performed HERE, once, by the process that owns the resource. Two frontends +//! can no longer fork two shells for pane 3 or race each other writing one +//! path, because neither of them writes anything. +//! +//! FAIRNESS, and why no client — and no shell — can stall the core or another +//! client. The property the bullets below add up to is worth stating as one +//! sentence, because it is what a detached session is FOR: there is no path on +//! which this process blocks indefinitely. Every descriptor it holds is +//! non-blocking, the single `poll(2)` is the only place it sleeps, and every +//! queue that could grow without bound has a ceiling with a stated answer for +//! reaching it. A daemon nobody is looking at cannot be made to stop looking +//! after the shells nobody else is keeping. +//! * ONE `poll(2)` per pump covers the listener, all `max_clients` frontends, +//! all `pardes.MAX_PANES` pty masters and the inotify descriptor: +//! `poll_slots` descriptors, one syscall, no thread per client and none per +//! pty. Putting the shells in the poll set the clients were already in is +//! what lets a daemon own sixteen of them and stay single-threaded. +//! * one read per pty per round, which is `receive`'s rule for clients +//! applied to shells: a `yes` in pane 1 gets one turn and the loop moves on +//! to the other panes, the frontends and the frame. +//! * EVERY descriptor is non-blocking, sockets and pty masters alike, and a +//! pane owes its bytes the same way a client does. A blocking write to a +//! master was the one hole this file's own comment used to argue was safe — +//! "the peer on a pty is a shell this process forked, not a stranger who can +//! stop reading on purpose" — and that was wrong, because the peer is +//! whatever program the human ran in that pane. `sleep 3600` plus a paste +//! larger than the pty's input buffer parked the WHOLE daemon inside +//! `write(2)`: no frame to any frontend, fifteen other masters unread, no +//! `accept`, no `expire`, no inotify drain. So a pane has an out-queue and a +//! POLLOUT, on the descriptor that was already in the set. See `ptyWrite`. //! * FRAMES ARE NOT QUEUED. A client with bytes still owed to the kernel is //! SKIPPED for this frame and its mirror is left alone, so the next frame //! it does get is a diff against what it actually has. A slow frontend @@ -96,11 +139,48 @@ //! collects, so the client checks the directory and the socket before it //! connects, exactly as this side checks them before it binds. const std = @import("std"); +const builtin = @import("builtin"); const libc = std.c; +const linux = std.os.linux; +const posix = std.posix; const pardes = @import("../pardes.zig"); const host_api = @import("../host.zig"); const wire = @import("wire.zig"); +/// The machine-local half of a host — fork a shell onto a pty, put bytes on a +/// disk — shared verbatim with the tty shell, and the sharing is the point: +/// `spawn` and `writeFile` below are the same two operations tty.zig performs, +/// and having them in one file is what keeps a daemon's pane and a terminal's +/// pane the same pane. See host_io.zig's header for why the daemon is the side +/// that performs them. +const host_io = @import("../host_io.zig"); + +/// ...and the inotify half, likewise shared: `applyEffect` is the mark-then- +/// reconcile transaction the tty and sdl shells run, and this session runs the +/// identical one. All that differs is who waits on the descriptor — a thread +/// there, `waitInput`'s poll set here. +const file_watch = @import("../file_watch.zig"); + +/// Host-lifetime storage for the OSC 133 rc files a forked shell sources, held +/// by `Session` because a Session is exactly one host's lifetime. +const shell_bin = @import("../shell_bin.zig"); + +/// `shellCwd` for a pane's shell, `ttyTaken` for a pane the core is about to +/// type a command line into, `readFile` for `run`'s `--load`. +const look = @import("../look.zig"); + +/// The "saved " / "dumped themes " message row, stamped the way +/// every other host stamps it — one clock format across every frontend. +const message = @import("../message.zig"); + +/// `Dump themes` writes the reference set out as .zon, into the core's own +/// `opts.config_dir`. +const user_config = @import("../user_config.zig"); + +// TIOCSWINSZ: absent from std.c.T on darwin — _IOW('t', 103, winsize). The +// same constant the tty, gui and macos shells spell, for the same reason. +const TIOCSWINSZ: c_int = @bitCast(@as(u32, if (@hasDecl(posix.T, "IOCSWINSZ")) posix.T.IOCSWINSZ else 0x80087467)); + /// Diagnostics for whoever is running the daemon. Every one of these is a /// `debug`, and the level is not a judgement about how bad the thing is: /// main.zig's logFn drops this scope entirely unless PARDES_LOG is set, so what @@ -136,11 +216,11 @@ const sun_path_len = nested.sun_path_len; pub const max_clients = 32; /// Bytes of un-drained CONTROL messages a client may owe before it is closed. -/// Frames are not in here (see the module header), so this is a backlog of -/// ACTIONS — spawns, clipboard mirrors, file writes — and a frontend that has -/// not taken 1 MiB of those has stopped reading its socket. Checked before an -/// append rather than after, so one oversized message is never the thing that -/// trips it. +/// Frames are not in here (see the module header), so this bounds a backlog of +/// the three things that are still on the wire — a welcome, a clipboard mirror, +/// a link to open — and a frontend that has not taken 1 MiB of those has +/// stopped reading its socket. Checked before an append rather than after, so +/// one oversized message is never the thing that trips it. const out_backlog = 1 << 20; /// One read per client per poll round (see `receive`). 16 KiB is two orders of @@ -148,16 +228,73 @@ const out_backlog = 1 << 20; /// 4 MiB paste arrives across several rounds, which is the point. const read_chunk = 16 * 1024; +/// Bytes taken off one pane's pty per poll round. 64 KiB is what every other +/// host's pty reader uses (`readPty` in tty.zig, gui.zig and macos.zig), and it +/// sits on `readPty`'s own frame rather than the loop's. Nothing is copied out +/// of it: `Event.output` borrows the buffer for one `update` call, so a daemon +/// serving a shell that is printing a build log asks the allocator for nothing. +const pty_chunk = 64 * 1024; + +/// Descriptors in the ONE poll this process runs: the listener, every frontend, +/// every pane's pty master, and the inotify descriptor behind every watch. 50 +/// on a full house, and one syscall covers all of them. +const poll_slots = 1 + max_clients + pardes.MAX_PANES + 1; + +/// How many times `reloadWatched` will honour `file_watch.reloadChanged`'s +/// request for another pass within one round. See `reloadWatched`. +const reload_retries = 4; + /// Bytes of client traffic — every in-queue and out-queue together — this /// session may hold before it starts closing the peers holding it. /// `out_backlog` bounds ONE slot and this bounds the table, which is not the -/// same ceiling: 32 clients each a byte under their own cap is 32 MiB of a -/// daemon nobody is looking at. 4 MiB is one whole paste in flight plus every -/// frame queue a real session builds, and past it the fattest peer is the peer -/// that stopped reading. The mirrors are NOT in this number: a mirror is this -/// session's own bookkeeping for a client it chose to serve, not something a -/// peer can grow. -const session_backlog = 4 << 20; +/// same ceiling: a client's `out` tops out at `out_backlog` plus the one +/// oversized message allowed through whole, so `out_backlog` alone permits +/// 32 * (1 + 1.6) MiB, about 83 MiB of a daemon nobody is looking at. +/// +/// DERIVED, and the derivation IS the fix. This was the literal `4 << 20`, +/// which was by coincidence the exact value of tty.zig's `max_paste_bytes` — +/// and `in` grows to hold one WHOLE message, so a frontend assembling the very +/// paste wire.zig names as one of the two messages that set `max_payload` +/// crossed the table's ceiling while still receiving it. The session then +/// closed the only frontend it had, mid-paste, with `.backlog`, which is the +/// diagnostic for a peer that STOPPED reading. The documented maximum paste +/// could not complete. Two whole `max_payload`s is the smallest number that is +/// headroom rather than another coincidence: one peer may legitimately be +/// assembling a message of the largest size `framed` will accept while the rest +/// of the table holds frames, and past 32 MiB the fattest peer is the peer that +/// stopped draining. Neither the mirrors nor the pane queues are in this +/// number: a mirror is this session's own bookkeeping for a client it chose to +/// serve, and a pane is bounded per pane by `pty_backlog` because it is not a +/// peer and cannot be closed to reclaim anything. +const session_backlog = 2 * @as(usize, wire.max_payload); + +/// Bytes of un-drained INPUT one pane's shell may owe before more is refused. +/// +/// A pane is not a client, so the answer cannot be `out_backlog`'s: a client +/// that stops draining is closed, and the thing at the other end of a pty is a +/// program the human is running. This refuses the write and says so on the +/// pane's message row instead, which is the only honest answer left — dropping +/// input silently loses half a command line, and killing a shell to reclaim a +/// megabyte destroys work. +/// +/// Checked BEFORE the append, exactly as `queue` checks `out_backlog`, and that +/// is what makes 1 MiB enough: any single write lands whole, so a maximum paste +/// into an empty queue is never truncated. What gets refused is MORE input typed +/// at a program that has stopped reading its input at all — `sleep 3600`, a +/// stopped job, anything blocked on its own output. +const pty_backlog = 1 << 20; + +/// How long a connection has to say `hello`, and the ONE number both ends of +/// this transport time the handshake against. `pub` because a frontend that +/// waited longer than the session is willing to hold its slot would report a +/// timeout for a slot that had already been taken back, and a frontend that +/// waited less would give up on a session that was still going to answer — two +/// halves of one deadline, and two literals is how they drift apart. +/// +/// The `Session` field it initialises is a field and not this constant for +/// exactly one reason: the test for expiry would otherwise have to sleep five +/// seconds. See `Session.greet_deadline_ms`. +pub const greet_deadline_default_ms: u32 = 5_000; /// What a DRAINED client is allowed to keep. `in` grows to hold one whole /// message, so a single 4 MiB paste otherwise leaves 4 MiB resident in that @@ -206,36 +343,52 @@ const Client = struct { accepted_ms: i64 = 0, }; -/// A pane's shell: which frontend was asked to fork it, and where. +/// A pane's shell: forked by THIS process, drained by its poll set, reaped by +/// it. /// -/// WHY THE SESSION REMEMBERS THIS. A spawn is the one primary-only call that -/// has to survive having no frontend to serve it. Every boot layout creates its -/// panes before the socket exists, so a `--detach` performs its startup spawns -/// with nobody attached — and dropping them meant a session that opened with -/// panes whose shells had never been forked, forever, in silence. So a spawn -/// with no primary is OWED, and asked of whoever attaches next. -/// -/// It is also what makes `pty_write` reach the right process. A pane's pty -/// lives in the frontend that forked it, which is not always the primary: A -/// attaches and forks the shells, B attaches, A leaves — the panes are re-owed -/// to B, and then C attaching into A's freed slot becomes primary while the -/// ptys are in B. Routing a pane's bytes by its OWNER rather than by the -/// primary is the difference between typing into a shell and typing into -/// nothing. -const Shell = struct { - /// The slot that was asked to fork this pane's shell, or null when nobody - /// has been. - owner: ?u8 = null, - /// A spawn owed to whoever attaches next: either it was never asked, or the - /// frontend holding it left and took the pty with it. - owed: bool = false, - /// Copied, because `push_spawn`'s `cwd` borrows the core's memory for the - /// length of that one call and this outlives it by definition. - cwd: std.ArrayListUnmanaged(u8) = .empty, +/// There is no owner here and nothing is owed, and the absence is the whole +/// change. This struct used to record which frontend had been asked to fork a +/// pane and re-ask the next arrival when that frontend left, because the pty +/// lived in the frontend that forked it; and a `--detach`, whose panes always +/// exist before its socket does, had nobody to ask at all and had to remember +/// the request instead. Both were one problem, and forking here dissolves both: +/// a startup layout's shells are forked during `run`'s pre-loop drain with +/// nobody attached, and they are still those same shells when the tenth +/// frontend attaches an hour later. +const Pty = struct { + /// The pty master, non-negative exactly when this pane has a live shell. + /// While it is here it is in the poll set (`waitInput`), NON-BLOCKING like + /// every other descriptor this file holds — `spawn` flips it, because + /// `forkpty` hands it back blocking and tty.zig's streaming reader wants it + /// that way. + fd: c_int = -1, + /// Kept past the fork for `look.shellCwd` and `look.ttyTaken`, both of which + /// ask /proc about this pid rather than about the descriptor. + pid: posix.pid_t = 0, + /// Bytes owed to this shell's stdin, drained by POLLOUT and bounded by + /// `pty_backlog`. The same shape as `Client.out`, for the same reason: the + /// thing on the far side may not be reading, and this process must not wait + /// to find out. See `ptyWrite`. + out: std.ArrayListUnmanaged(u8) = .empty, +}; + +/// What one descriptor in `waitInput`'s poll set is. A tagged union rather than +/// the bare slot index this loop used to carry alongside its `pollfd`s, because +/// the set now holds four different kinds of thing and a `u8` cannot say which. +const Source = union(enum) { + listener, + client: u8, + pty: u8, + inotify, }; pub const Session = struct { gpa: std.mem.Allocator, + /// The Io every filesystem read this host performs goes through: + /// file_watch.zig's reload of a changed pane, and `user_config.dumpThemes`. + /// Required and not optional — a session that owns the disk work cannot be + /// handed a null disk. + io: std.Io, core: *pardes.Pardes, /// -1 when nothing is bound: an unsupported platform, or a bind that /// failed. A session with no listener is a session nobody can attach to, @@ -258,9 +411,33 @@ pub const Session = struct { /// needed rather than sized from `wire.max_payload`, which would be 16 MiB /// of resident memory for a session whose frames are six kilobytes. scratch: std.ArrayListUnmanaged(u8) = .empty, - /// Where each pane's shell lives, and which spawns are still owed. See - /// `Shell`. - shells: [pardes.MAX_PANES]Shell = @splat(.{}), + /// Each pane's shell. Forked here, drained by the poll set, reaped by + /// `harvest`. See `Pty`. + ptys: [pardes.MAX_PANES]Pty = @splat(.{}), + /// The OSC 133 rc files a forked shell sources, staged once for the life of + /// this host exactly as tty.zig stages them for the life of a terminal: + /// `shell_bin.resolve` hands a child pointers into these buffers and the + /// child holds them until it execs, so they must not live in a stack frame. + /// The default is the empty one, which `resolve` reads as "this shell gets + /// no prompt marks"; `run` supplies a staged one. + prompt_rcs: shell_bin.PromptRcs = .{}, + /// The one inotify descriptor behind every watch this session holds, and the + /// last member of the poll set. Opened lazily — see `inotify`. + inotify_fd: c_int = -1, + /// Which directory mark belongs to which pane, and the generation the core + /// has already accepted from each. file_watch.zig owns the shape and the + /// transaction; this host owns only the descriptor and the wake. + watches: file_watch.Table = @splat(null), + /// A reconcile pass is due: a watched directory had an edge, or the last + /// pass asked for another one. Consumed at the end of `waitInput`, which is + /// where a core change still makes the current frame — `Pardes.pump` renders + /// after `pull_wait_input` returns. + check_files: bool = false, + /// False during `run`'s pre-loop effect drain. Read in exactly one place: + /// `Pardes.loadThemeFile` animates an interactive theme change and must not + /// animate a startup one, and a `ThemeFile` in a boot layout is a startup + /// one. tty.zig spells the same distinction `threads_ok`. + in_loop: bool = false, /// How long a connection may stay silent before the session takes its slot /// back. `Client.open` writes its `hello` in the same call that connects, /// so a peer that has said nothing for five seconds is not a frontend that @@ -268,8 +445,10 @@ pub const Session = struct { /// real frontend out with a `refuse .full`. /// /// A field rather than a constant for exactly one reason: the test for that - /// would otherwise have to sleep five seconds. Nothing else changes it. - greet_deadline_ms: u32 = 5_000, + /// would otherwise have to sleep five seconds. Nothing else changes it, and + /// the number itself is `greet_deadline_default_ms`, which the frontend half + /// of this transport reads too. + greet_deadline_ms: u32 = greet_deadline_default_ms, /// Monotonic milliseconds until which the LISTENER is left out of the poll /// set, because an `accept` failed for a reason that persists. See `accept`. accept_paused_ms: i64 = 0, @@ -286,7 +465,19 @@ pub const Session = struct { for (&s.clients) |*c| if (c.fd >= 0) s.close(c, .quitting); s.unlisten(); s.scratch.deinit(s.gpa); - for (&s.shells) |*sh| sh.cwd.deinit(s.gpa); + // The pane shells go with the SESSION and not with a frontend, which is + // this file's whole change. Closing a master is what hangs its shell up; + // `harvest` collects whatever has already exited, and the process is + // about to leave, so anything slower than that is the kernel's job. + for (0..s.ptys.len) |pane| s.closePty(@intCast(pane)); + s.harvest(); + // Every mark dies with the descriptor, so there is nothing to unmark. + if (s.inotify_fd >= 0) { + _ = libc.close(s.inotify_fd); + s.inotify_fd = -1; + } + // Unlinks the two rc files staged for this host's shells. + s.prompt_rcs.deinit(); } /// Bind and listen. False when there is no socket, and a session without @@ -362,17 +553,31 @@ pub const Session = struct { return @ptrCast(@alignCast(ctx.?)); } - /// Thirteen methods, and the seven that are missing are missing on purpose - /// — see wire.zig's header for each one's reason. `push_poll_frame` and - /// `push_post_present` carry no information a frame does not; the four - /// synchronous or dispatched pulls and `push_fs_reply` belong to whoever - /// owns the core, which is this process. + /// Sixteen methods, and NOT the fullest host in the tree — that claim stood + /// here, was believed, and was copied into docs/detached.md before an audit + /// counted the others. The tty and SDL shells fill NINETEEN each (everything + /// but `pull_gpio_toggle` and `push_detach`) and macOS fourteen, so this host + /// is the only one that implements `push_detach` and otherwise the least + /// complete of the three desktop hosts. What is true is narrower and is the + /// point anyway: it performs every MACHINE-LOCAL effect there is, and the + /// five of host.zig's twenty-one it leaves null are null because there is + /// nothing here for them to do. Three of those five are real losses a person + /// can notice — no `pull_lsp` and no `pull_pipe`, because both want the + /// worker pool this deliberately single-threaded loop does not have, and no + /// `push_fs_reply`, because this process mounted no /dev/fuse. The other two + /// are not losses at all: `push_post_present` marks the moment a frame + /// reached a screen and this process has no screen, and `pull_gpio_toggle` + /// wants pads. + /// + /// `push_detach` is the one entry here that is not an effect. See `detach`. const vtable: host_api.Host.VTable = .{ .pull_wait_input = waitInput, .push_present = present, + .push_poll_frame = pollFrame, .push_spawn = spawn, .push_pty_write = ptyWrite, .push_pty_resize = ptyResize, + .pull_tty_taken = ttyTaken, .push_write_file = writeFile, .push_write_dump = writeDump, .push_watch_file = watchFile, @@ -381,6 +586,7 @@ pub const Session = struct { .push_set_clipboard = setClipboard, .pull_read_clipboard = readClipboard, .push_open_link = openLink, + .push_detach = detach, }; // ---- routing ---------------------------------------------------------- @@ -402,16 +608,6 @@ pub const Session = struct { return s.primary(); } - /// The frontend holding pane `pane`'s pty, which is NOT the primary in - /// general — see `Shell`. Null when no frontend holds it, which is a pane - /// with no child: the core's own answer to that is silence, and so is this. - fn holder(s: *Session, pane: u8) ?*Client { - if (pane >= s.shells.len) return null; - const owner = s.shells[pane].owner orelse return null; - const c = &s.clients[owner]; - return if (c.attached) c else null; - } - /// Which slot this client is. From the pointer because every caller here /// holds a `*Client` and not its index. fn slotOf(s: *Session, c: *const Client) u8 { @@ -422,84 +618,202 @@ pub const Session = struct { for (&s.clients) |*c| if (c.attached) s.send(c, msg); } - // ---- the host methods ------------------------------------------------- + // ---- the host methods: pseudo-terminals ------------------------------- fn spawn(ctx: ?*anyopaque, pane: u8, cwd: []const u8) void { const s = of(ctx); - if (pane >= s.shells.len) return; // the core indexes its own panes - const sh = &s.shells[pane]; - // Kept whether or not there is somebody to ask, because the pane now - // exists either way and the cwd is the only thing that cannot be - // reconstructed later. - sh.cwd.clearRetainingCapacity(); - sh.cwd.appendSlice(s.gpa, cwd) catch {}; - if (s.primary()) |c| { - sh.owner = s.slotOf(c); - sh.owed = false; - return s.send(c, .{ .spawn = .{ .pane = pane, .cwd = cwd } }); + if (pane >= s.ptys.len) return; // the core indexes its own panes + // The in-process host's reaping rule and its reason, verbatim from + // tty.zig `spawn`: the core reuses pane ids and there is no close + // effect, so a deleted pane's shell lives in its slot until a respawn + // lands here. + s.closePty(pane); + var cwd_buf: [256:0]u8 = undefined; + var cwd_z: ?[*:0]const u8 = null; + if (cwd.len > 0 and cwd.len < cwd_buf.len) { + @memcpy(cwd_buf[0..cwd.len], cwd); + cwd_buf[cwd.len] = 0; + cwd_z = @ptrCast(&cwd_buf); } - // Nobody can fork a shell right now — a startup layout, or every - // frontend gone. NOT dropped: `reconcile` asks the next arrival. - sh.owner = null; - sh.owed = true; + const child = host_io.forkShell( + s.core, + pane, + &s.prompt_rcs, + s.core.shellBin(), + cwd_z, + // The SESSION grid — which `reconcile` already made the smallest + // common one across everyone attached, and which survives every + // frontend leaving, so a shell forked into an empty session is + // still sized like the pane the core reflowed. + s.core.screen_h, + s.core.screen_w, + null, // no `--fs` mount in a daemon: nothing here answers /dev/fuse + ); + // A `forkpty` that failed left `master` holding a number this process + // does not own. The shells get away with not checking because they hand + // the descriptor to a reader task that simply ends; this one would go + // into `poll(2)`, come back POLLNVAL, and be closed out from under + // whoever really owns it. + if (child.pid < 0) return; + s.ptys[pane] = .{ .fd = child.file.handle, .pid = child.pid }; + // ...and the master joins the rule every other descriptor in this file + // obeys. `forkpty` hands it back BLOCKING, and host_io.zig leaves it that + // way because tty.zig streams it from a thread that wants a blocking + // read; a poll loop wants the opposite, and one blocking `write(2)` here + // is the whole session parked. Only this side of the pty is affected — + // the master and the slave are separate open file descriptions, so the + // shell's own stdin stays exactly as `forkpty` made it. + setNonblock(child.file.handle); + // The pane's starting directory, for the tags. `pollFrame` keeps it + // current after a `cd`; this is the one before the first frame. + var lbuf: [1024]u8 = undefined; + if (look.shellCwd(child.pid, &lbuf)) |wd| s.core.setCwd(pane, wd); } + /// Keystrokes and pastes into the shell, QUEUED and never blocked on. + /// + /// This was one blocking `host_io.writeFd`, and the comment defending it + /// argued that "the peer is a shell this process forked rather than a + /// stranger who can stop reading on purpose". The peer is whatever program + /// the human ran in the pane: `sleep 3600`, a job stopped with ^Z, anything + /// blocked writing its own output. Any of those plus a paste larger than the + /// pty's input buffer — four kilobytes, and a frontend is entitled to send a + /// four-MEGABYTE paste — put this single-threaded process to sleep inside + /// `write(2)` with the whole session behind it: no frame to any frontend, + /// fifteen other masters unread, no `accept`, no `expire`, no inotify drain. + /// + /// So a pane owes bytes the way a client does, and the answer is the shape + /// this file already had for exactly this problem. What differs is what + /// happens when the queue will not drain: a client that stops reading is + /// CLOSED, and a pane cannot be, because closing it kills a program the + /// human is running. See `pty_backlog` — the write is refused and said out + /// loud on the pane's own message row. fn ptyWrite(ctx: ?*anyopaque, pane: u8, bytes: []const u8) void { const s = of(ctx); - // To the frontend that forked this pane's shell, not to the primary: - // the pty is in that process and nowhere else. A pane whose holder is - // gone is silent, which is what the core does with a null method. - if (s.holder(pane)) |c| s.send(c, .{ .pty_write = .{ .pane = pane, .bytes = bytes } }); + if (pane >= s.ptys.len) return; + const pt = &s.ptys[pane]; + // A pane with no shell swallows what is typed at it, which is exactly + // what the core does with a null method. + if (pt.fd < 0) return; + // BEFORE the append, which is `queue`'s rule and gives `queue`'s + // guarantee: one write always lands whole, so the biggest paste anyone + // can send is never truncated on arrival, and what is refused is the + // NEXT one typed at a program that has read nothing. + if (pt.out.items.len > pty_backlog) { + var mbuf: [256]u8 = undefined; + const text = std.fmt.bufPrint( + &mbuf, + "input refused: pane not reading ({d} bytes queued)", + .{pt.out.items.len}, + ) catch "input refused: pane not reading"; + return s.core.setMessage(pane, text); + } + pt.out.appendSlice(s.gpa, bytes) catch { + // Out of memory for a keystroke. The shell is fine and the session + // is fine; this one write is not, and saying so is all there is. + return s.core.setMessage(pane, "input refused: out of memory"); + }; + // Try immediately. On an idle pty this empties the queue in one write and + // the descriptor never asks for a POLLOUT at all, which keeps a session + // of keystrokes exactly as cheap as it was. + s.flushPty(pane); } fn ptyResize(ctx: ?*anyopaque, pane: u8, cols: u16, rows: u16) void { const s = of(ctx); - if (s.holder(pane)) |c| s.send(c, .{ .pty_resize = .{ .pane = pane, .cols = cols, .rows = rows } }); + if (pane >= s.ptys.len) return; + const fd = s.ptys[pane].fd; + if (fd < 0) return; + const ws: posix.winsize = .{ .row = rows, .col = cols, .xpixel = 0, .ypixel = 0 }; + _ = posix.system.ioctl(fd, TIOCSWINSZ, @intFromPtr(&ws)); } + /// Is this pane's tty still the prompt we forked, or has a program taken it? + /// + /// Answerable at all only because the pty is HERE. While a pane's shell + /// lived in a frontend this method had to stay null, and a null one means + /// the core types every `Exec` at the shell — into vim, into a pager, into + /// an agent waiting on stdin. Lazy by construction (host.zig): it runs where + /// the core is about to type a command line, so the /proc walk costs an + /// ordinary frame nothing. + fn ttyTaken(ctx: ?*anyopaque, pane: u8) bool { + const s = of(ctx); + if (pane >= s.ptys.len) return false; + const pt = s.ptys[pane]; + if (pt.fd < 0) return false; + return look.ttyTaken(pt.pid, pt.fd); + } + + // ---- the host methods: the filesystem --------------------------------- + fn writeFile(ctx: ?*anyopaque, pane: u8, path: []const u8, bytes: []const u8) void { const s = of(ctx); - if (s.primary()) |c| return s.send(c, .{ .write_file = .{ .pane = pane, .path = path, .bytes = bytes } }); - // Nobody attached, and a Put must not evaporate. This method being - // non-null means the core did NOT reach for its own filesystem, so the - // obligation a null method would have discharged is discharged here by - // hand — the same shape `readClipboard` below has, and host.zig's rule - // that a zero-method host is a complete pardes. - s.core.fallback.writeFile(path, bytes); + if (!host_io.writeFileBytes(path, bytes)) return; + // Our own write is about to come back as an inotify edge: restamp from + // the bytes we just put there so the reconcile reads as "no change". + // Only when this IS the pane's watched file — a `Save ` must + // not silence a real change to the file the pane has open. Six lines + // shared with tty.zig `writeFile` over the same `file_watch.Table`, + // which is what makes a save in a detached pane behave like a save in a + // terminal one. + if (s.core.panes[pane]) |pn| if (pn.file) |f| if (std.mem.eql(u8, f.path, path)) { + if (s.watches[pane]) |*w| if (w.serial == pn.serial) switch (w.generation) { + .text => w.generation = .{ .text = std.hash.Wyhash.hash(0, bytes) }, + .pdf => {}, + }; + }; + // ...and say so on the pane's message row. AFTER the write, not beside + // it: the early return above is a save that did not happen and must not + // be reported as one. + var mbuf: [256]u8 = undefined; + s.core.setMessage(pane, message.stamp(&mbuf, "saved", path)); } fn writeDump(ctx: ?*anyopaque, bytes: []const u8) void { const s = of(ctx); - if (s.primary()) |c| return s.send(c, .{ .write_dump = bytes }); - // ...and the same for a Dump, including the part that makes the bytes - // reachable again: a real host reports where it landed, which is what - // puts `Restore ` in the topbar (pardes.zig `write_dump`). - s.core.fallback.writeFile(pardes.fallback_dump_path, bytes); - s.core.setLastDump(pardes.fallback_dump_path); + var pbuf: [1024:0]u8 = undefined; + const path = pardes.dump.outPath(&pbuf) orelse return; + if (!host_io.writeFileBytes(path, bytes)) return; + // Where it landed, which is what puts `Restore ` in the topbar + // (pardes.zig `write_dump`). A dump of a detached session now lands in + // the same directory a terminal session's does, rather than in whatever + // directory the frontend that happened to be primary was started from. + s.core.setLastDump(path); } - fn watchFile(ctx: ?*anyopaque, pane: u8, path: []const u8, on: bool) void { + fn watchFile(ctx: ?*anyopaque, pane: u8, _: []const u8, on: bool) void { const s = of(ctx); - if (s.primary()) |c| return s.send(c, .{ .watch_file = .{ .pane = pane, .path = path, .on = on } }); - // No frontend to watch a path, so the core's own record of what was - // asked is the whole of what a watch means here — exactly what a null - // method leaves behind. - if (pane < s.core.fallback.watched.len) s.core.fallback.watched[pane] = on; + // The path argument is unused because `applyEffect` takes it off the + // core's own pane, together with the serial and the generation that make + // the reconcile safe. That is the one thing a frontend could not do — it + // had no core — and it is why the frontend's copy of this method needed a + // second table of pathnames to go with the watch table. + if (file_watch.applyEffect(s.core, s.io, s.gpa, s.inotify(), &s.watches, pane, on)) + s.check_files = true; } fn watchTheme(ctx: ?*anyopaque, generation: u32, on: bool) void { const s = of(ctx); - // Dropped with nobody attached, and that is the whole of it: the core's - // own `theme_file` effect does nothing for a null method either, so - // there is no obligation left over. Same for `dump_themes` below. - if (s.primary()) |c| s.send(c, .{ .watch_theme = .{ .generation = generation, .on = on } }); + if (file_watch.applyThemeEffect(s.core, s.gpa, s.inotify(), &s.watches, generation, on, s.in_loop)) + s.check_files = true; } fn dumpThemes(ctx: ?*anyopaque, pane: u8) void { const s = of(ctx); - if (s.primary()) |c| s.send(c, .{ .dump_themes = .{ .pane = pane } }); + // A session started without one has nowhere to put them; the core's + // options are the only place that answer lives. + const config_dir = s.core.opts.config_dir orelse return; + const out_dir = user_config.dumpThemes(s.io, s.gpa, config_dir, pardes.themes) catch |err| { + s.core.reportError(pane, "dump themes", err); + return; + }; + defer s.gpa.free(out_dir); + var mbuf: [256]u8 = undefined; + s.core.setMessage(pane, message.stamp(&mbuf, "dumped themes", out_dir)); } + // ---- the host methods: the desktop ------------------------------------ + fn setClipboard(ctx: ?*anyopaque, text: []const u8) void { const s = of(ctx); // Mirrored into the core's own clipboard ALWAYS, not only when nobody @@ -533,6 +847,254 @@ pub const Session = struct { s.core.fallback.setLink(url); } + // ---- the host methods: session control -------------------------------- + + /// `Detach` in an attached frontend: that frontend leaves, the session and + /// every other frontend carry on. tmux's `detach-client`. + /// + /// A `send` and NOTHING ELSE, and each of the three things it does not do is + /// deliberate. It does not quit — the whole point is that the session + /// survives, and a detach that took the daemon with it would be `quit` under + /// another name. It does not touch the core — no pane closes, no shell dies, + /// no frame changes; the grid is retaken by `reconcile` from the frontends + /// that remain, on the ordinary path, because a frontend leaving is already a + /// case this file handles. And it does not close the connection: the frontend + /// closes its own socket when it reads the message, and the peer-hangup path + /// then frees the slot exactly as it does for a frontend somebody killed. + /// Closing from this side would race the frontend's own teardown for no gain. + /// + /// It is therefore the one vtable entry here that is not an effect on the + /// world but SESSION CONTROL — one frontend asking to stop being a frontend + /// — which is why wire.zig numbers it 0x05, in the session range beside + /// `quit`, rather than in 0x10.. where every tag reaches a disk, a clipboard + /// or a browser. The module header's routing table says the same. + /// + /// ORIGIN, ELSE PRIMARY, for `read_clipboard`'s and `open_link`'s reason: it + /// answers something one particular human just typed, so it has to reach that + /// human's screen and not somebody else's — sending a detach to the wrong + /// frontend takes away a session from a person who did not ask. Nobody + /// attached at all is a no-op, and correctly so: there is no frontend to + /// detach, and the core has nothing to record about one. + fn detach(ctx: ?*anyopaque) void { + const s = of(ctx); + if (s.origins()) |c| s.send(c, .detach); + } + + // ---- pane shells ------------------------------------------------------ + + /// Each pane's live cwd, for the tags. One readlink of /proc per pane that + /// has a shell, per frame, which is what tty.zig's `pollFrame` costs — and + /// why the far more expensive question, whether a program has taken the + /// pane's tty, is a pull asked at the `Exec` that cares (`ttyTaken`) + /// instead of polled here. + /// + /// This could not exist before. A detached session's shells lived in a + /// frontend, and a frontend has no core to report a cwd TO, so a `cd` in a + /// detached pane never reached its tag no matter how many frontends were + /// watching. The pids are here now, so it does. + fn pollFrame(ctx: ?*anyopaque) void { + const s = of(ctx); + for (&s.ptys, 0..) |*pt, pane| { + if (pt.fd < 0) continue; + var lbuf: [1024]u8 = undefined; + if (look.shellCwd(pt.pid, &lbuf)) |cwd| s.core.setCwd(pane, cwd); + } + } + + /// Drop a pane's shell: out of the poll set, out of the process. Closing the + /// master is what hangs the shell up — which is true only because + /// `host_io.forkShell` puts FD_CLOEXEC on it, so no LATER pane's shell is + /// still holding a copy open. The pid is left to `harvest`, because a + /// `waitpid` here would return 0 for a shell that has not noticed the hangup + /// yet and that answer is worth nothing. + /// + /// The core is NOT told. Its two callers are `spawn` — a respawn, where the + /// core is the thing that asked — and `deinit`, where there is no core left + /// to tell. The path that does tell it is `paneEof`. + fn closePty(s: *Session, pane: u8) void { + const pt = &s.ptys[pane]; + if (pt.fd < 0) return; + _ = libc.close(pt.fd); + // Before the reset, or the queue's allocation goes with the slot: what + // is in it is input a program that is not reading never took, and there + // is nobody left to hand it to. + pt.out.deinit(s.gpa); + pt.* = .{}; + } + + /// Push what the kernel will take of what this pane owes its shell, and + /// leave the rest for a POLLOUT. `flush`'s body, on a pty instead of a + /// socket, down to the `retire` that hands a drained megabyte back. + /// + /// The one difference is what an error means. A failed write to a SOCKET + /// closes a client; a failed write to a master means the slave side is gone, + /// which is the same event as a read of 0. It is NOT the same moment, + /// though, and that is why the error arm reads the pane before it ends it: + /// linux's `n_tty_write` returns EIO the instant the slave has no open + /// descriptors left, while `n_tty_read` on that same master still hands back + /// what the shell wrote before it went — so the write fails while the last + /// line is still retrievable, and ending the pane first would throw it away. + /// That is the very thing the dispatch's `.pty` branch protects against when + /// it takes POLLIN before POLLHUP, and it has to hold here too, because two + /// paths reach this arm with no read of their own in between: the dispatch + /// runs POLLOUT before POLLIN, and `ptyWrite` calls this during `perform`, + /// after this round's `readPty` has already been and gone. + fn flushPty(s: *Session, pane: u8) void { + const pt = &s.ptys[pane]; + var off: usize = 0; + while (off < pt.out.items.len) { + const n = libc.write(pt.fd, pt.out.items.ptr + off, pt.out.items.len - off); + if (n < 0) switch (libc.errno(n)) { + .INTR => continue, + // The pty's input buffer is full: the rest waits for POLLOUT, + // and this is the case the whole change exists for. + .AGAIN => break, + else => { + // `readPty` either takes that last chunk or reaches the end + // itself and has already ended the pane; the guard is what + // stops the second `paneEof` from being a double-end. + s.readPty(pane); + if (s.ptys[pane].fd >= 0) s.paneEof(pane); + return; + }, + }; + // No progress and no error. host_io.zig's `writeFd` says why this is + // a `break` and never a retry: looping on a zero-byte write is a + // spin, and a spin in here is the whole session at 100% of a core + // with no syscall for a signal to interrupt. + if (n == 0) break; + off += @intCast(n); + } + if (off == 0) return; + if (off == pt.out.items.len) { + pt.out.clearRetainingCapacity(); + return retire(s.gpa, &pt.out); + } + std.mem.copyForwards(u8, pt.out.items, pt.out.items[off..]); + pt.out.items.len -= off; + } + + /// One read per readable pty per round — `receive`'s rule for clients, + /// applied to shells: a `yes` in pane 1 gets one turn and the loop moves on + /// to the other panes, the frontends and the frame. + /// + /// Nothing is copied. `Event.output` borrows the buffer for the length of + /// one `update` call, which is the same borrow window every other host gives + /// a pty chunk — tty.zig frees its duplicate the line after the update — + /// except that this one never allocated a duplicate to free. A daemon + /// serving sixteen shells printing build logs asks the allocator for + /// nothing. + fn readPty(s: *Session, pane: u8) void { + var buf: [pty_chunk]u8 = undefined; + const got = libc.read(s.ptys[pane].fd, &buf, buf.len); + if (got == 0) return s.paneEof(pane); + if (got < 0) return switch (libc.errno(got)) { + // A master that said POLLIN and then had nothing is not an error; + // the next round asks again. + .INTR, .AGAIN => {}, + // EIO is how linux reports the slave side going away, which is the + // ordinary end of a shell rather than a fault. + else => s.paneEof(pane), + }; + s.core.update(.{ .output = .{ .pane = pane, .bytes = buf[0..@intCast(got)] } }); + } + + /// The shell in `pane` is gone. The descriptor leaves the poll set BEFORE + /// the core is told, because an `eof` is what makes the core offer a respawn + /// and a respawn into a slot still holding the old fd would leak it. + fn paneEof(s: *Session, pane: u8) void { + s.closePty(pane); + s.harvest(); + s.core.update(.{ .eof = .{ .pane = pane } }); + } + + /// Collect every child that has exited. + /// + /// `waitpid(-1)` and not a pid list, because the only children this process + /// forks are pane shells (`host_io.forkShell`) — so "any exited child" and + /// "an exited pane shell" are the same set — and because the pids a list + /// would hold are exactly the ones it cannot help with: a respawn closes a + /// master, and the shell that gets the hangup exits some milliseconds later + /// with its slot already reused by a different shell. + /// + /// Nothing here waits, so a session whose shells are all running pays one + /// syscall that returns 0. Called once per poll round and again wherever a + /// shell is dropped, which is what keeps a daemon that runs for a week and + /// spawns a thousand shells free of zombies — the one bookkeeping cost a + /// long-lived process pays that a frontend, which exits, never did. + fn harvest(_: *Session) void { + while (true) { + // 0: there are children and none has exited. -1: no children at all. + if (libc.waitpid(-1, null, libc.W.NOHANG) <= 0) return; + } + } + + // ---- watched files ---------------------------------------------------- + + /// The one inotify descriptor behind every watch, opened on first use. + /// + /// Lazy for two reasons pointing the same way: a `Session` is built as a + /// struct literal (client.zig's test harness is one) and so has no init hook + /// to open it in, and a session whose panes are all shells never watches a + /// path and has no use for one. -1 on anything but linux and on a failed + /// `inotify_init1`, which file_watch.zig reads as "mark nothing" — the core + /// then keeps its own record of what was asked and simply never gets a + /// reload, which is what a host with no watcher has always done. + /// + /// NONBLOCK because this descriptor is drained from `poll`, not from a + /// thread parked in `read` (tty.zig `watchFiles`): `drainInotify` must be + /// able to stop. + fn inotify(s: *Session) c_int { + if (s.inotify_fd >= 0) return s.inotify_fd; + // The whole body is inside the comptime branch so that neither + // `inotify_init1` nor `linux.IN` is even analysed on a platform that has + // no inotify — the same shape file_watch.zig's `watchPath` uses. + if (comptime builtin.os.tag == .linux) { + s.inotify_fd = libc.inotify_init1(linux.IN.CLOEXEC | linux.IN.NONBLOCK); + } + return s.inotify_fd; + } + + /// A directory this session marked had an edge. The CONTENTS are discarded + /// on purpose, exactly as tty.zig's watcher thread discards them: a record + /// names a mark and a filename, and reconciling every mark against the + /// generation the core accepted is both cheaper and safer than deciding from + /// the record which pane it meant. + /// + /// DRAINED TO EMPTY, in a loop, and one read was a real cost rather than the + /// coalescing this comment used to claim. The descriptor is level-triggered, + /// so a queue left partly full makes `poll` return ready again immediately — + /// and each of those rounds is a whole `pump`: `reloadChanged` over all 17 + /// slots, every watched text pane re-read from disk and re-hashed, a render, + /// a present. A `git checkout` can queue the kernel's whole 16384 events; at + /// roughly 128 records per 4 KiB that was ~128 spin rounds and some two + /// thousand whole-file reads for one command, at 100% of a core, while every + /// frontend got a frame per round it could not use. The fd is IN_NONBLOCK + /// (`inotify`), so the loop ends on EAGAIN. + fn drainInotify(s: *Session) void { + var buf: [4096]u8 = undefined; + while (libc.read(s.inotify_fd, &buf, buf.len) > 0) s.check_files = true; + } + + /// Reconcile every marked pane and the theme file. Called at the END of + /// `waitInput`, which is what puts a reload in THIS frame: `Pardes.pump` + /// renders after `pull_wait_input` returns. + /// + /// The loop is `reloadChanged`'s contract. It asks for another pass when a + /// PDF's pathname changed between the stat before MuPDF reopened it and the + /// stat after — a save that landed mid-reconcile, where committing either + /// identity would lose a generation. tty.zig posts that request back into + /// its event queue; this loop has no queue, so it is retried here and + /// BOUNDED, because a file being rewritten in a loop must not hold the core. + /// What is left over is picked up by the next directory edge. + fn reloadWatched(s: *Session) void { + if (!s.check_files) return; + s.check_files = false; + for (0..reload_retries) |_| { + if (!file_watch.reloadChanged(s.core, s.io, s.gpa, &s.watches)) return; + } + } + // ---- the frame -------------------------------------------------------- fn present(ctx: ?*anyopaque, surface: *const pardes.Surface) void { @@ -590,19 +1152,37 @@ pub const Session = struct { // ---- the loop --------------------------------------------------------- /// The only place this process sleeps, which is what `pull_wait_input`'s - /// comment in host.zig requires of whoever serves it. One `poll(2)` covers - /// the listener and every attached frontend; there is no thread per client - /// and nothing here blocks on a single peer. + /// comment in host.zig requires of whoever serves it, and the only place it + /// waits on ANYTHING: one `poll(2)` over the listener, every attached + /// frontend, every pane's pty master and the inotify descriptor. No thread + /// per client, no thread per shell, no watcher thread, and nothing here + /// blocks on a single peer. + /// + /// That the shells are in this set and not on threads of their own is what + /// lets a daemon own sixteen of them and stay a single-threaded state + /// machine — and it costs the shells nothing, because a pty master is + /// pollable and a pane's output has nowhere to go but the core this loop is + /// driving anyway. fn waitInput(ctx: ?*anyopaque, timeout_ms: u32) void { const s = of(ctx); // Push what the kernel will take before sleeping: a client that becomes // writable while we are inside poll(2) would otherwise be a frame late, // and a frame late is a frame skipped (see `present`). for (&s.clients) |*c| if (c.fd >= 0) s.flush(c); + // The session grid, retaken BEFORE the sleep as well as after it. A + // client can leave OUTSIDE this function — a `set_clipboard` broadcast + // whose write failed during `perform` closes it — and the minimum across + // attached frontends would then stay sized for a frontend that is gone + // until some descriptor happened to become readable, which on an idle + // session is never. That is what this call buys, and `regridded` is what + // it costs: a round that has just told the core to reflow must not then + // sleep on it, because the frame carrying that reflow is the one + // `Pardes.pump` composes the moment this returns. + const regridded = s.reconcile(); const now = monotonicMs(); - var fds: [max_clients + 1]libc.pollfd = undefined; - var slots: [max_clients + 1]u8 = undefined; + var fds: [poll_slots]libc.pollfd = undefined; + var src: [poll_slots]Source = undefined; var n: usize = 0; // The listener is left OUT of the set while accepting is paused, which // is how an EMFILE is waited out without the core sleeping (see @@ -610,7 +1190,7 @@ pub const Session = struct { const watching_listener = s.listener >= 0 and now >= s.accept_paused_ms; if (watching_listener) { fds[n] = .{ .fd = s.listener, .events = poll_in, .revents = 0 }; - slots[n] = 0; + src[n] = .listener; n += 1; } for (&s.clients, 0..) |*c, i| { @@ -620,13 +1200,43 @@ pub const Session = struct { .events = if (c.out.items.len != 0) poll_in | poll_out else poll_in, .revents = 0, }; - slots[n] = @intCast(i); + src[n] = .{ .client = @intCast(i) }; + n += 1; + } + // The pane shells, and note what is NOT conditional on a frontend: a + // session with nobody attached still polls these, still reads them and + // still feeds the core. That is the difference between a detach that + // pauses your build and a detach that does not. + for (&s.ptys, 0..) |*pt, pane| { + if (pt.fd < 0) continue; + fds[n] = .{ + .fd = pt.fd, + // POLLOUT only while this pane owes its shell bytes, which is + // the same rule and the same reason as a client's: asking for it + // unconditionally makes every idle pty a ready descriptor and + // turns the poll into a spin. + .events = if (pt.out.items.len != 0) poll_in | poll_out else poll_in, + .revents = 0, + }; + src[n] = .{ .pty = @intCast(pane) }; + n += 1; + } + // Opened only once something asked to be watched, so an unwatched + // session simply has one fewer descriptor here (see `inotify`). + if (s.inotify_fd >= 0) { + fds[n] = .{ .fd = s.inotify_fd, .events = poll_in, .revents = 0 }; + src[n] = .inotify; n += 1; } - // A detached session with no listener and no clients has no event - // source at all. Returning immediately would spin the outer + // A session with no listener, no clients, no shells and no watches has + // no event source at all. Returning immediately would spin the outer // `while (!core.quit)` at full speed, so sleep the interval the core // offered and, when it offered none, a frame's worth. + // + // Nothing is owed on this path. `check_files` is only ever set by a + // watch, and a watch means the inotify descriptor is in the set; and + // `reconcile` posts a resize only when a client is ATTACHED, which means + // its socket is in the set — so `n == 0` implies `!regridded` too. if (n == 0) return nap(if (timeout_ms == 0) 16 else timeout_ms); // Zero is the core's word for "sleep until something happens" (see // pardes.zig `pump`: it passes a frame interval only while an animation @@ -639,37 +1249,87 @@ pub const Session = struct { // idle, IS the denial `greet_deadline_ms` exists to answer — so the // wait is clamped to whichever is due first. if (s.nextWake(now)) |due| timeout = if (timeout < 0) due else @min(timeout, due); + // ...and two things are due on nothing at all rather than on a + // descriptor: a reconcile pass a watch effect asked for (`watchFile` ran + // during `perform`, outside this function) and a regrid this round has + // already performed. Both are consumed before this function returns, so + // the round must not sleep before reaching them. + if (s.check_files or regridded) timeout = 0; const ready = libc.poll(&fds, @intCast(n), timeout); - // Expired unconditionally: a slot held by silence comes back on a - // timeout exactly as it does on a wakeup, and a poll that returned - // nothing is the ordinary way this deadline is reached. + // A timeout is an ordinary frame boundary and EINTR is a signal we do not + // handle here. Neither skips anything below any more: what used to be an + // early `return` here is why a client closed without any descriptor being + // readable — which is every `expire` — left the session grid sized for a + // frontend that had gone, until the next readable event, on an idle + // session possibly hours later. + if (ready > 0) s.dispatch(fds[0..n], src[0..n]); + // AFTER the dispatch, and that ordering is itself a fix. `expire` frees a + // client slot and `accept` — which runs INSIDE the dispatch — fills the + // lowest free one, so an expire that ran first could hand a slot to a new + // connection within this same round and the dispatch would then apply the + // OLD connection's `revents` to the new descriptor: a POLLHUP from the + // peer that left, closing the peer that just arrived. The dispatch's + // `c.fd < 0` guard cannot see that, because the fd is perfectly valid — + // it is simply a different fd. Expiring after means a freed slot is + // refilled no earlier than the next round, which builds a fresh `fds` for + // it. It fixes a smaller thing for free, too: a connection whose `hello` + // arrived in THIS round is attached before its deadline is judged, + // instead of being taken back with its handshake still unread. s.expire(monotonicMs()); - // A timeout is an ordinary frame boundary and EINTR is a signal we do - // not handle here; both simply come back next pump. - if (ready <= 0) return; + // Unconditional, and not only where a shell is noticed to have died: a + // shell whose master `spawn` closed on a respawn exits after that close, + // with no descriptor left for anyone to see it on. See `harvest`. + s.harvest(); + // Both before this function returns, so a file that changed on disk and a + // frontend that left during this round are in the frame `Pardes.pump` + // composes next rather than the one after it. + s.reloadWatched(); + _ = s.reconcile(); + } - var k: usize = 0; - if (watching_listener) { - if (fds[0].revents != 0) s.accept(); - k = 1; - } - while (k < n) : (k += 1) { - const c = &s.clients[slots[k]]; - // A slot closed earlier in this same pass (its peer hung up, a - // decode failed, its handshake expired) must not be touched - // through a stale revents. - if (c.fd < 0) continue; - if (fds[k].revents & poll_out != 0) s.flush(c); - if (c.fd < 0) continue; - if (fds[k].revents & poll_in != 0) { - s.receive(c, slots[k]); - } else if (fds[k].revents & (poll_hup | poll_err | poll_nval) != 0) { - // POLLIN wins when both are set: a peer that wrote and then - // closed has bytes still worth reading. - s.close(c, .peer); - } - } - s.reconcile(); + /// One pass over the descriptors `poll` reported ready. Split out of + /// `waitInput` for one reason: everything that must happen AFTER it — + /// `expire`, `harvest`, `reloadWatched`, `reconcile` — is then stated once, + /// in one order, where no early return can skip it. An early return past + /// that list is exactly what findings 5 and 6 were. + fn dispatch(s: *Session, fds: []const libc.pollfd, src: []const Source) void { + for (fds, src) |pfd, source| switch (source) { + .listener => if (pfd.revents != 0) s.accept(), + .client => |i| { + const c = &s.clients[i]; + // A slot closed earlier in this same pass (its peer hung up, a + // decode failed) must not be touched through a stale revents. + if (c.fd < 0) continue; + if (pfd.revents & poll_out != 0) s.flush(c); + if (c.fd < 0) continue; + if (pfd.revents & poll_in != 0) { + s.receive(c, i); + } else if (pfd.revents & (poll_hup | poll_err | poll_nval) != 0) { + // POLLIN wins when both are set: a peer that wrote and then + // closed has bytes still worth reading. + s.close(c, .peer); + } + }, + .pty => |pane| { + if (s.ptys[pane].fd < 0) continue; + // What this pane still owes its shell, which is the whole of + // finding 1's drain: `ptyWrite` queued it and stopped at EAGAIN + // rather than sleeping, and this is where the rest goes. + if (pfd.revents & poll_out != 0) s.flushPty(pane); + // `flushPty` ends the pane when the slave side has gone. + if (s.ptys[pane].fd < 0) continue; + // The same precedence as a client's, and it matters more here: a + // shell that printed its last line and exited reports + // POLLIN|POLLHUP together, and taking the hangup first would + // throw that line away. `readPty` reaches the EOF by reading 0. + if (pfd.revents & poll_in != 0) { + s.readPty(pane); + } else if (pfd.revents & (poll_hup | poll_err | poll_nval) != 0) { + s.paneEof(pane); + } + }, + .inotify => if (pfd.revents & poll_in != 0) s.drainInotify(), + }; } /// Milliseconds until the next deadline that is kept by the CLOCK rather @@ -802,16 +1462,30 @@ pub const Session = struct { } fn apply(s: *Session, c: *Client, slot: u8, tag: u8, payload: []const u8) wire.Error!void { - var scratch: wire.Scratch = .{}; - switch (try wire.decodeClient(tag, payload, &scratch)) { + // `Hello.version` BEFORE the payload is decoded, which is the whole + // point of wire.zig putting it first at a fixed offset: a mismatch has to + // stay diagnosable when the rest of the layout is the part that changed. + // Checking it inside the `.hello` arm defeated exactly that guarantee — + // `decodeClient` refuses a cols/rows this build does not like and refuses + // trailing bytes, so a v2 hello with one extra field came back as + // `.protocol` and the `refuse .version` the frontend needs to say + // something useful was never sent. `wire.helloVersion` reads the one + // field without decoding the rest, and lives in the file that owns the + // layout. + if (tag == @intFromEnum(wire.ClientTag.hello)) { + // A second hello on one connection is not a resize; it is a peer + // that is not speaking this protocol. Judged here rather than in the + // arm below so that a repeat hello is a protocol error whatever + // version it claims. + if (c.attached) return error.BadValue; + const claimed = try wire.helloVersion(payload); + if (claimed != wire.version) { + log.debug("frontend speaks protocol {d}, this session speaks {d}", .{ claimed, wire.version }); + return s.refuse(c, .version); + } + } + switch (try wire.decodeClient(tag, payload)) { .hello => |h| { - // A second hello on one connection is not a resize; it is a - // peer that is not speaking this protocol. - if (c.attached) return error.BadValue; - if (h.version != wire.version) { - log.debug("frontend speaks protocol {d}, this session speaks {d}", .{ h.version, wire.version }); - return s.refuse(c, .version); - } if (s.core.quit) return s.refuse(c, .quitting); c.cols = h.cols; c.rows = h.rows; @@ -847,7 +1521,12 @@ pub const Session = struct { /// Settle the session grid and greet whoever arrived, once per poll round /// rather than once per message: three frontends attaching in the same /// round are one resize, not three reflows of every pane. - fn reconcile(s: *Session) void { + /// + /// True when the CORE was told to reflow, which is the one thing a caller + /// has to react to: the frame carrying that reflow is the next one + /// `Pardes.pump` composes, so a `waitInput` that hears true must not go to + /// sleep before returning. See its `regridded`. + fn reconcile(s: *Session) bool { var cols: u16 = 0; var rows: u16 = 0; for (&s.clients) |*c| { @@ -858,6 +1537,7 @@ pub const Session = struct { // Nobody attached: keep the grid we had. A detached session is not a // session of no size, it is one nobody is looking at, and reflowing // every pane to nothing for zero readers is work with no reader. + var regridded = false; if (cols != 0 and (cols != s.cols or rows != s.rows)) { s.cols = cols; s.rows = rows; @@ -867,33 +1547,14 @@ pub const Session = struct { // safe too. for (&s.clients) |*c| c.need_full = true; s.core.update(.{ .resize = .{ .cols = cols, .rows = rows } }); + regridded = true; } for (&s.clients, 0..) |*c, i| { if (!c.greet) continue; c.greet = false; s.send(c, .{ .welcome = .{ .slot = @intCast(i), .cols = s.cols, .rows = s.rows } }); } - s.flushOwed(); - } - - /// Hand every owed spawn to the frontend that can serve it. Runs at the end - /// of a poll round, so a frontend that has just been greeted is asked for - /// its panes' shells in the same round it arrived — and a session that was - /// started with panes and no frontend (which is every `--detach`) is a - /// session whose panes get their shells from the first attach rather than - /// never. See `Shell`. - fn flushOwed(s: *Session) void { - const c = s.primary() orelse return; - const slot = s.slotOf(c); - for (&s.shells, 0..) |*sh, pane| { - if (!sh.owed) continue; - sh.owed = false; - sh.owner = slot; - s.send(c, .{ .spawn = .{ .pane = @intCast(pane), .cwd = sh.cwd.items } }); - // The send closed it, and `close` put its panes back on the owed - // list; the ones this loop has not reached are still owed anyway. - if (c.fd < 0) return; - } + return regridded; } // ---- bytes ------------------------------------------------------------ @@ -902,11 +1563,12 @@ pub const Session = struct { const want = wire.serverBound(msg); s.scratch.ensureTotalCapacity(s.gpa, want) catch return s.close(c, .oom); const bytes = wire.encodeServer(s.scratch.allocatedSlice()[0..want], msg) catch |err| { - // The only reachable case is a payload past `max_payload`: a save - // of a pane holding more text than this protocol carries. The - // session keeps it (the core's own filesystem already has it) and - // the frontend's copy does not happen — said out loud rather than - // silently. + // The only reachable case is a payload past `max_payload`, and with + // every effect that carried a whole file gone from this protocol the + // only payload that can still get there is a yank of more than + // 16 MiB. The session keeps it — `setClipboard` put it in the core's + // own clipboard before this was ever queued — and the frontends' + // desktop clipboards do not get it, out loud rather than silently. log.debug("message {t} not encodable: {t}", .{ msg, err }); return; }; @@ -918,7 +1580,8 @@ pub const Session = struct { // what this refuses is a client that has stopped draining. if (c.out.items.len > out_backlog) return s.close(c, .backlog); // ...and the table as a whole, which `out_backlog` does not bound: 32 - // slots one byte under it each is 32 MiB. See `session_backlog`. + // slots one byte under it each, plus a frame apiece. See + // `session_backlog`. s.account(); if (c.fd < 0) return; // the fattest peer was this one c.out.appendSlice(s.gpa, bytes) catch return s.close(c, .oom); @@ -933,13 +1596,23 @@ pub const Session = struct { /// peer holding the most of it is the peer that stopped reading. The next /// append asks again, so a second offender is closed a message later rather /// than in a loop that could empty the table on one bad frame. + /// + /// `items.len` and NOT `capacity`, which was half of the bug in + /// `session_backlog`'s history. An ArrayList grows geometrically, so a + /// client's `in.capacity` crossed a 4 MiB ceiling while it was still + /// assembling a paste of roughly 2.8 MiB — the peer was punished for the + /// allocator's rounding rather than for anything it held. What this is + /// asking is "how much is a peer making this session hold RIGHT NOW", and + /// that is `items.len`; capacity above it is transient by construction, + /// because `retire` hands back anything over `idle_retain` the moment a + /// buffer empties. fn account(s: *Session) void { var total: usize = 0; var worst: ?*Client = null; var worst_bytes: usize = 0; for (&s.clients) |*c| { if (c.fd < 0) continue; - const held = c.in.capacity + c.out.capacity; + const held = c.in.items.len + c.out.items.len; total += held; if (held > worst_bytes) { worst_bytes = held; @@ -998,8 +1671,9 @@ pub const Session = struct { } /// Free one slot. A frontend dying takes NOTHING with it: not the core, not - /// the listener, not another frontend's frames. Its buffers go back and the - /// slot is reusable on the next connect. + /// the listener, not another frontend's frames, and — since this file + /// forks — not its panes' shells either. Its buffers go back and the slot is + /// reusable on the next connect. fn close(s: *Session, c: *Client, why: Closed) void { if (c.fd < 0) return; log.debug("frontend detached: {t}", .{why}); @@ -1013,19 +1687,12 @@ pub const Session = struct { if (s.origin) |i| if (i == gone) { s.origin = null; }; - // ...and its panes' shells died with the process that forked them. They - // go back on the owed list, so the frontend that replaces this one is - // asked to fork them again in the directory they were forked in: the - // alternative — which is what this did — is a pane that looks alive, - // produces nothing, and swallows everything typed into it. Migrating a - // live pty between processes is the other answer and is a different - // feature; a fresh shell is the one this transport can keep. - for (&s.shells) |*sh| { - const owner = sh.owner orelse continue; - if (owner != gone) continue; - sh.owner = null; - sh.owed = true; - } + // ...and that is the whole of it. A frontend used to take its panes' + // shells with it and leave them owed to whoever attached next, because + // the ptys were in its process; a pane that survived a detach looked + // alive, produced nothing and swallowed everything typed into it. The + // shells are here now, so a frontend leaving is a screen going away and + // nothing else. c.* = .{}; } }; @@ -1038,12 +1705,14 @@ pub const Session = struct { /// core's own `pump`, exactly as the tty and gui shells run it — this frontend /// simply has no window of its own. /// -/// The pre-loop effect drain is here for the same reason tty.zig has one: the -/// startup spawns are already queued, and they have to be PERFORMED before the -/// loop rather than left in the queue. They reach no frontend — there is none -/// yet — and are remembered instead, then asked of the first attach; `Shell` -/// says why that is the only shape that works for a session whose panes exist -/// before its socket does. +/// The pre-loop effect drain is here for the same reason tty.zig has one, and +/// it is no longer half a promise: the startup spawns are already queued and are +/// PERFORMED here, on this process's own process table. So a session binds its +/// socket with every pane's shell already forked and already in the poll set, +/// and the first frontend to attach — whether that is a second later or the +/// next morning — is sent a frame of shells that have been printing into the +/// core since before it existed. Nothing is remembered for a later frontend, +/// because nothing is owed to one. pub fn run(init: std.process.Init, opts: pardes.Options, name: []const u8) !void { const gpa = init.gpa; const allocs = pardes.allocators.init(gpa); @@ -1066,13 +1735,23 @@ pub fn run(init: std.process.Init, opts: pardes.Options, name: []const u8) !void } const core = if (options.load_path) |lp| blk: { - const bytes = try @import("../look.zig").readFile(gpa, lp); + const bytes = try look.readFile(gpa, lp); defer gpa.free(bytes); break :blk try pardes.Pardes.initFromDump(allocs.pardes, options, bytes); } else try pardes.Pardes.init(allocs.pardes, options); defer core.deinit(); - var session: Session = .{ .gpa = gpa, .core = core, .cols = options.cols, .rows = options.rows }; + var session: Session = .{ + .gpa = gpa, + .io = init.io, + .core = core, + .cols = options.cols, + .rows = options.rows, + // Staged before the first fork and owned by the Session for exactly as + // long as it can fork: `shell_bin.resolve` hands a child pointers into + // these buffers, and the child holds them until it execs. + .prompt_rcs = .init(), + }; defer session.deinit(); if (!session.listen(name)) { // Loud, and on stderr rather than through the log: a `--detach` whose @@ -1085,6 +1764,9 @@ pub fn run(init: std.process.Init, opts: pardes.Options, name: []const u8) !void const h = session.host(); core.host = h; while (core.nextEffect()) |effect| core.perform(effect); + // Past the startup drain: a `ThemeFile` reload from here on is a human's + // and animates. See `in_loop`. + session.in_loop = true; while (!core.quit) try core.pump(h); } @@ -1137,7 +1819,14 @@ fn retire(gpa: std.mem.Allocator, list: *std.ArrayListUnmanaged(u8)) void { /// Zero on failure, and every caller treats zero as "no clock" and enforces no /// deadline at all — a session that cannot read a clock keeps every slot rather /// than dropping every slot. -fn monotonicMs() i64 { +/// +/// `pub` for the same reason `setNonblock`, `nosignal` and the `poll_*` +/// constants are: this file owns the transport's conventions and BOTH ends of +/// it, and the clock a handshake is timed against is one of them. client.zig +/// times its wait for a `welcome` on this and against +/// `greet_deadline_default_ms`, so the two ends cannot disagree about how long +/// the handshake is allowed to take. +pub fn monotonicMs() i64 { var ts: libc.timespec = undefined; if (libc.clock_gettime(.MONOTONIC, &ts) != 0) return 0; return @as(i64, ts.sec) * std.time.ms_per_s + @divTrunc(ts.nsec, std.time.ns_per_ms); @@ -1254,8 +1943,8 @@ fn alive(path: [:0]const u8) bool { } /// Unlink the sockets of detached sessions that are gone — our own litter, -/// which `--attach`'s "the one session there is" would otherwise count as a -/// session (tty.zig `sessionName`). `alive` is the whole of the judgement. +/// which the bare `Attach`'s "whichever session is there" would otherwise count +/// as a session (client.zig `resolve`). `alive` is the whole of the judgement. /// /// Bounded: one readdir of a directory only we write to, one connect each. fn sweep(dir: [:0]const u8) void { diff --git a/src/detached/wire.zig b/src/detached/wire.zig index f2030e26..a84c2a68 100644 --- a/src/detached/wire.zig +++ b/src/detached/wire.zig @@ -39,8 +39,23 @@ //! frontend must not have to have been built with the core's options — so it is //! ALWAYS on the wire and dropped on arrival by a build with nowhere to put it. //! -//! WHAT IS NOT HERE. The seam has twenty methods; this carries twelve of -//! them, and the eight it does not are named here with their reasons. +//! WHAT IS NOT HERE. The seam has twenty-one methods; this carries FIVE of +//! them — `push_present` as `frame`, `push_set_clipboard`, +//! `pull_read_clipboard`, `push_open_link` and `push_detach` — and the sixteen +//! it does not are named here with their reasons. The five are spelled out +//! because this arithmetic has now gone stale twice in one day, once when the +//! machine-local eight moved into the daemon and once when `detach` arrived, +//! and a count nobody can check against a list is a comment that rots quietly. +//! * The eight machine-local ones — `push_spawn`, `push_pty_write`, +//! `push_pty_resize`, `push_write_file`, `push_write_dump`, +//! `push_watch_file`, `push_watch_theme`, `push_dump_themes` — are +//! performed by the detached core ITSELF, through `host_io.zig`. A unix +//! socket means it is on the same machine, so there is no question of +//! whose disk or whose process table is meant, and a pane's shell has to +//! outlive the frontend that asked for it or a detached session is a +//! promise it cannot keep. The `ServerTag` doc below carries the whole of +//! that argument; this line exists so the count at the top of the file +//! agrees with it. //! * `pull_wait_input` IS the server's poll loop, not a message. //! * `push_poll_frame` and `push_post_present` carry no information. They are //! per-frame bookkeeping ticks, and `frame` already arrives exactly once @@ -114,13 +129,11 @@ const run_header = 4 + 2; const frame_head = 1 + 2 + 2 + 6 + 4; /// The longest legal payload, and therefore the length prefix a decoder will -/// accept before it refuses the stream. Three messages set it: +/// accept before it refuses the stream. Two messages set it: /// * a full frame of the largest grid, worst case one run per cell: /// 512*128 * (6 + 20) = 1.6 MiB. /// * one paste, which the tty frontend already caps at 4 MiB (tty.zig /// `max_paste_bytes`) on the grounds that anything larger is a mis-click. -/// * a `write_file`, whose bytes are a pane's whole text and are the only -/// genuinely open-ended payload here. /// 16 MiB is past every source file anyone edits in this editor and is still a /// buffer the receiving side can simply hold. A larger message is not sent and /// a larger prefix is not read. @@ -151,6 +164,26 @@ pub fn frameBound(cols: u16, rows: u16) usize { /// /// The numbers are the PROTOCOL's, grouped session/input rather than derived /// from `Event`'s declaration order, so reordering the union changes nothing. +/// +/// EVERY TAG HERE IS SOMETHING A HUMAN DID, and that is the whole set: a +/// handshake, a goodbye, and what a keyboard, a mouse, a trackpad or a window +/// manager produces. Six numbers are missing from the input run — 0x13..0x17 +/// and 0x1e — and the gaps are left rather than tidied away, because +/// renumbering is a change every deployed frontend feels. They were `output`, +/// `eof`, `lsp_resp`, `pipe_resp`, `file_changed` and `tick`: the +/// MACHINE-LOCAL host's own reports, which stopped being a frontend's business +/// when the daemon took the disk and the process table (host_io.zig, +/// file_watch.zig). No frontend ever produced one — tty.zig's attached loop +/// swallowed them by name and gui.zig never handed `Input.post` one — and +/// leaving them DECODABLE was not merely dead weight: server.zig's `apply` +/// routes any decoded non-resize event straight into `core.update`, so an +/// attached peer could forge a pane's output, forge an `eof` for a shell that +/// was still running (and unlike the daemon's own `paneEof` the wire path never +/// called `closePty`, so the master stayed open and the shell was orphaned for +/// the life of the session), or replace a pane's text with bytes the next +/// `Save` would write to disk. client.zig's header says a machine-local effect +/// cannot return to the wire; deleting these is what makes that true in BOTH +/// directions instead of only core -> frontend. pub const ClientTag = enum(u8) { hello = 0x01, bye = 0x02, @@ -158,40 +191,46 @@ pub const ClientTag = enum(u8) { key = 0x10, mouse = 0x11, resize = 0x12, - output = 0x13, - eof = 0x14, - lsp_resp = 0x15, - pipe_resp = 0x16, - file_changed = 0x17, paste = 0x18, command = 0x19, pdf_scroll = 0x1a, pinch = 0x1b, touch_scroll = 0x1c, pointer_leave = 0x1d, - tick = 0x1e, }; -/// Core -> frontend. 0x01..0x0f is the session, 0x10.. is one `push_` method +/// Core -> frontend. 0x01..0x0f is the session; 0x10.. is one `push_` method /// each, in `Host.VTable`'s own order so the two lists can be read side by /// side. +/// +/// There are only THREE of those left, and which three is the whole design. +/// The daemon performs every effect that needs a disk or a process table +/// itself (see `host_io.zig`): a unix socket means it is on the same machine, +/// so there is no question of whose disk is meant, and a pane's shell has to +/// outlive the frontend that asked for it or a detached session is a promise +/// it cannot keep. What is left on the wire is what a process nobody is +/// looking at genuinely cannot do — put something on THIS human's clipboard, +/// read it back, and open a link in front of the person who clicked it. +/// +/// `detach` is in the SESSION range and not among those three on purpose: it is +/// not an effect the core wants performed, it is the session telling one +/// frontend that it is done. `quit` is its sibling — same shape, opposite +/// meaning about whether anything survives. pub const ServerTag = enum(u8) { welcome = 0x01, refuse = 0x02, frame = 0x03, quit = 0x04, - - spawn = 0x10, - pty_write = 0x11, - pty_resize = 0x12, - write_file = 0x13, - write_dump = 0x14, - watch_file = 0x15, - watch_theme = 0x16, - dump_themes = 0x17, - set_clipboard = 0x18, - read_clipboard = 0x19, - open_link = 0x1a, + /// One frontend is done, and the session is NOT over. The `Detach` word, + /// routed back to the frontend whose keystroke ran it (server.zig ORIGIN, + /// ELSE PRIMARY, the same rule `read_clipboard` takes and for the same + /// reason). Every other frontend, the core and the pane shells are + /// untouched, so leaving is success rather than a failure to report. + detach = 0x05, + + set_clipboard = 0x10, + read_clipboard = 0x11, + open_link = 0x12, }; /// Why the server hung up on a connect. Sent as a `refuse` and followed by a @@ -254,6 +293,23 @@ pub const Hello = struct { rows: u16, }; +/// `Hello.version` alone, read out of the still-undecoded payload. +/// +/// Separate from `decodeClient` because of the guarantee the fixed offset above +/// exists to give: a decoder that validates `cols` and `rows` and then refuses +/// trailing bytes can never deliver it. A v2 hello with one more field would be +/// closed as a malformed message, and the `Refusal.version` byte a frontend +/// needs in order to say something true would never be sent — which is exactly +/// the case the field was put at offset zero for. So the version is asked for +/// first, on its own, before any of the layout that may have moved. +/// +/// An error rather than a zero on a payload shorter than two bytes, so a +/// truncated hello stays diagnosable too instead of reading as version 0. +pub fn helloVersion(payload: []const u8) Error!u16 { + var r: Reader = .init(payload); + return r.getU16(); +} + pub const Welcome = struct { version: u16 = version, /// Which client slot this connection got. Carried because it is what the @@ -324,28 +380,14 @@ pub const ServerMsg = union(enum) { /// The session is over. Sent before the listener closes so a frontend can /// exit rather than report a broken pipe. quit, + /// One frontend is done, and the session is NOT over — see `ServerTag`. + detach, - spawn: struct { pane: u8, cwd: []const u8 }, - pty_write: struct { pane: u8, bytes: []const u8 }, - pty_resize: struct { pane: u8, cols: u16, rows: u16 }, - write_file: struct { pane: u8, path: []const u8, bytes: []const u8 }, - write_dump: []const u8, - watch_file: struct { pane: u8, path: []const u8, on: bool }, - watch_theme: struct { generation: u32, on: bool }, - dump_themes: struct { pane: u8 }, set_clipboard: []const u8, read_clipboard, open_link: []const u8, }; -/// The one thing a decoder cannot put in a byte buffer: `pipe_resp.outputs` is -/// a `[]const []const u8`, so the outer array needs somewhere to live. Sized -/// from the core's own ceiling on selections (`MAX_SELS`), which is what bounds -/// the count a legitimate `pipe_resp` can carry. -pub const Scratch = struct { - outputs: [pardes.MAX_SELS][]const u8 = undefined, -}; - // --------------------------------------------------------------------------- // primitives // --------------------------------------------------------------------------- @@ -796,10 +838,16 @@ fn sendCell(cells: []const pardes.Cell, prev: []const pardes.Cell, full: bool, i fn clientTag(msg: ClientMsg) ClientTag { return switch (msg) { .event => |ev| switch (ev) { - // Not on the wire, and not an omission: see the module header. - // The acme mount lives with the core, so this event is raised in - // the same process that answers it and never crosses a socket. - .fs_req => unreachable, + // SEVEN `Event`s a frontend cannot produce, so no `ClientTag` + // exists for them and this arm is where the compiler says so. + // `fs_req` is the acme mount, raised in the same process that + // answers it. The other six are the machine-local host's own + // reports — a pty's output and its EOF, a language or pipe worker's + // answer, a watched file's new bytes, an animation tick — and after + // the daemon took the disk and the process table every one of them + // is raised by the process that already holds the core. See + // `ClientTag` for what putting them back would let a peer forge. + .output, .eof, .lsp_resp, .pipe_resp, .file_changed, .tick, .fs_req => unreachable, inline else => |_, t| @field(ClientTag, @tagName(t)), }, inline else => |_, t| @field(ClientTag, @tagName(t)), @@ -849,26 +897,6 @@ pub fn encodeClient(out: []u8, msg: ClientMsg) Error![]const u8 { try w.putU16(if (comptime has_cell_pixels) rs.cell_pixels.w else 8); try w.putU16(if (comptime has_cell_pixels) rs.cell_pixels.h else 16); }, - .output => |o| { - try w.putByte(o.pane); - try w.putSlice32(o.bytes); - }, - .eof => |e| try w.putByte(e.pane), - .lsp_resp => |l| { - try w.putU32(l.id); - try w.putSlice32(l.rows); - }, - .pipe_resp => |p| { - try w.putU32(p.id); - try w.putBool(p.success); - if (p.outputs.len > pardes.MAX_SELS) return error.Overlong; - try w.putU16(@intCast(p.outputs.len)); - for (p.outputs) |o| try w.putSlice32(o); - }, - .file_changed => |f| { - try w.putByte(f.pane); - try w.putSlice32(f.bytes); - }, .paste => |b| try w.putSlice32(b), .command => |line| try w.putSlice16(line), .pdf_scroll => |s| { @@ -877,15 +905,16 @@ pub fn encodeClient(out: []u8, msg: ClientMsg) Error![]const u8 { }, .pinch => |v| try w.putF32(v), .touch_scroll => |v| try w.putF32(v), - .pointer_leave, .tick => {}, - .fs_req => unreachable, + .pointer_leave => {}, + // See `clientTag`: no tag, so nothing to encode. + .output, .eof, .lsp_resp, .pipe_resp, .file_changed, .tick, .fs_req => unreachable, }, } try finishMessage(&w, at); return w.written(); } -pub fn decodeClient(tag: u8, payload: []const u8, scratch: *Scratch) Error!ClientMsg { +pub fn decodeClient(tag: u8, payload: []const u8) Error!ClientMsg { var r: Reader = .init(payload); const msg: ClientMsg = switch (std.enums.fromInt(ClientTag, tag) orelse return error.BadTag) { .hello => .{ .hello = .{ @@ -928,29 +957,12 @@ pub fn decodeClient(tag: u8, payload: []const u8, scratch: *Scratch) Error!Clien if (comptime has_cell_pixels) ev.resize.cell_pixels = .{ .w = px_w, .h = px_h }; break :blk .{ .event = ev }; }, - .output => .{ .event = .{ .output = .{ .pane = try r.getPane(), .bytes = try r.getSlice32() } } }, - .eof => .{ .event = .{ .eof = .{ .pane = try r.getPane() } } }, - .lsp_resp => .{ .event = .{ .lsp_resp = .{ .id = try r.getU32(), .rows = try r.getSlice32() } } }, - .pipe_resp => blk: { - const id = try r.getU32(); - const success = try r.getBool(); - const n = try r.getU16(); - if (n > scratch.outputs.len) return error.Overlong; - for (scratch.outputs[0..n]) |*o| o.* = try r.getSlice32(); - break :blk .{ .event = .{ .pipe_resp = .{ - .id = id, - .success = success, - .outputs = scratch.outputs[0..n], - } } }; - }, - .file_changed => .{ .event = .{ .file_changed = .{ .pane = try r.getPane(), .bytes = try r.getSlice32() } } }, .paste => .{ .event = .{ .paste = try r.getSlice32() } }, .command => .{ .event = .{ .command = try r.getSlice16() } }, .pdf_scroll => .{ .event = .{ .pdf_scroll = .{ .pane = try r.getPane(), .delta_pixels = try r.getF32() } } }, .pinch => .{ .event = .{ .pinch = try r.getF32() } }, .touch_scroll => .{ .event = .{ .touch_scroll = try r.getF32() } }, .pointer_leave => .{ .event = .pointer_leave }, - .tick => .{ .event = .tick }, }; try r.end(); return msg; @@ -984,36 +996,7 @@ pub fn encodeServer(out: []u8, msg: ServerMsg) Error![]const u8 { // encodeFrame directly and this arm exists so the switch stays // exhaustive over ServerMsg. .frame => return error.BadValue, - .quit => {}, - .spawn => |s| { - try w.putByte(s.pane); - try w.putSlice16(s.cwd); - }, - .pty_write => |p| { - try w.putByte(p.pane); - try w.putSlice32(p.bytes); - }, - .pty_resize => |p| { - try w.putByte(p.pane); - try w.putU16(p.cols); - try w.putU16(p.rows); - }, - .write_file => |f| { - try w.putByte(f.pane); - try w.putSlice16(f.path); - try w.putSlice32(f.bytes); - }, - .write_dump => |b| try w.putSlice32(b), - .watch_file => |v| { - try w.putByte(v.pane); - try w.putSlice16(v.path); - try w.putBool(v.on); - }, - .watch_theme => |t| { - try w.putU32(t.generation); - try w.putBool(t.on); - }, - .dump_themes => |d| try w.putByte(d.pane), + .quit, .detach => {}, .set_clipboard => |t| try w.putSlice32(t), .read_clipboard => {}, .open_link => |u| try w.putSlice16(u), @@ -1033,14 +1016,9 @@ const msg_slack = header_len + 32; /// once instead of guessing and retrying. pub fn serverBound(msg: ServerMsg) usize { return msg_slack + switch (msg) { - .welcome, .refuse, .quit, .pty_resize, .watch_theme, .dump_themes, .read_clipboard => 0, + .welcome, .refuse, .quit, .detach, .read_clipboard => 0, // A frame is bounded by its grid, not by this: see `frameBound`. .frame => |f| frameBound(f.cols, f.rows), - .spawn => |s| s.cwd.len, - .pty_write => |p| p.bytes.len, - .write_file => |f| f.path.len + f.bytes.len, - .write_dump => |b| b.len, - .watch_file => |v| v.path.len, .set_clipboard => |t| t.len, .open_link => |u| u.len, }; @@ -1051,21 +1029,12 @@ pub fn clientBound(msg: ClientMsg) usize { return msg_slack + switch (msg) { .hello, .bye => 0, .event => |ev| switch (ev) { - .mouse, .resize, .eof, .pdf_scroll, .pinch, .touch_scroll, .pointer_leave, .tick => 0, + .mouse, .resize, .pdf_scroll, .pinch, .touch_scroll, .pointer_leave => 0, .key => |k| k.text.len, - .output => |o| o.bytes.len, - .lsp_resp => |l| l.rows.len, - .pipe_resp => |p| blk: { - // Each output carries its own u32 prefix, so the count is part - // of the bound and not just the bytes. - var total: usize = p.outputs.len * 4; - for (p.outputs) |o| total += o.len; - break :blk total; - }, - .file_changed => |f| f.bytes.len, .paste => |b| b.len, .command => |line| line.len, - .fs_req => unreachable, + // See `clientTag`: not on this wire in this direction. + .output, .eof, .lsp_resp, .pipe_resp, .file_changed, .tick, .fs_req => unreachable, }, }; } @@ -1103,26 +1072,7 @@ pub fn decodeServer(tag: u8, payload: []const u8) Error!ServerMsg { } }; }, .quit => .quit, - .spawn => .{ .spawn = .{ .pane = try r.getPane(), .cwd = try r.getSlice16() } }, - .pty_write => .{ .pty_write = .{ .pane = try r.getPane(), .bytes = try r.getSlice32() } }, - .pty_resize => .{ .pty_resize = .{ - .pane = try r.getPane(), - .cols = try r.getU16(), - .rows = try r.getU16(), - } }, - .write_file => .{ .write_file = .{ - .pane = try r.getPane(), - .path = try r.getSlice16(), - .bytes = try r.getSlice32(), - } }, - .write_dump => .{ .write_dump = try r.getSlice32() }, - .watch_file => .{ .watch_file = .{ - .pane = try r.getPane(), - .path = try r.getSlice16(), - .on = try r.getBool(), - } }, - .watch_theme => .{ .watch_theme = .{ .generation = try r.getU32(), .on = try r.getBool() } }, - .dump_themes => .{ .dump_themes = .{ .pane = try r.getPane() } }, + .detach => .detach, .set_clipboard => .{ .set_clipboard = try r.getSlice32() }, .read_clipboard => .read_clipboard, .open_link => .{ .open_link = try r.getSlice16() }, @@ -1144,11 +1094,11 @@ const testing = std.testing; /// Round-trip one frontend -> core message through the framing too, so a /// length prefix that disagrees with the payload cannot pass. -fn roundClient(buf: []u8, msg: ClientMsg, scratch: *Scratch) !ClientMsg { +fn roundClient(buf: []u8, msg: ClientMsg) !ClientMsg { const bytes = try encodeClient(buf, msg); const f = (try framed(bytes)).?; try testing.expectEqual(bytes.len, f.total); - return decodeClient(f.tag, f.payload, scratch); + return decodeClient(f.tag, f.payload); } fn roundServer(buf: []u8, msg: ServerMsg) !ServerMsg { @@ -1160,26 +1110,25 @@ fn roundServer(buf: []u8, msg: ServerMsg) !ServerMsg { test "detached wire: every Event variant round-trips" { var buf: [4096]u8 = undefined; - var scratch: Scratch = .{}; // The tag space is the protocol's own, so assert the numbers themselves: // a renumbering here breaks every deployed frontend and must be a diff // somebody reads, not a silent change. try testing.expectEqual(@as(u8, 0x01), @intFromEnum(ClientTag.hello)); try testing.expectEqual(@as(u8, 0x10), @intFromEnum(ClientTag.key)); - try testing.expectEqual(@as(u8, 0x1e), @intFromEnum(ClientTag.tick)); + try testing.expectEqual(@as(u8, 0x1d), @intFromEnum(ClientTag.pointer_leave)); { - const got = try roundClient(&buf, .{ .hello = .{ .cols = 80, .rows = 24 } }, &scratch); + const got = try roundClient(&buf, .{ .hello = .{ .cols = 80, .rows = 24 } }); try testing.expectEqual(version, got.hello.version); try testing.expectEqual(@as(u16, 80), got.hello.cols); try testing.expectEqual(@as(u16, 24), got.hello.rows); } - try testing.expectEqual(ClientMsg.bye, try roundClient(&buf, .bye, &scratch)); + try testing.expectEqual(ClientMsg.bye, try roundClient(&buf, .bye)); { const key: pardes.Key = .{ .cp = pardes.Key.page_down, .text = "ü", .ctrl = true, .alt = false, .shift = true }; - const got = (try roundClient(&buf, .{ .event = .{ .key = key } }, &scratch)).event.key; + const got = (try roundClient(&buf, .{ .event = .{ .key = key } })).event.key; try testing.expectEqual(key.cp, got.cp); try testing.expectEqualStrings(key.text, got.text); try testing.expectEqual(key.ctrl, got.ctrl); @@ -1192,7 +1141,7 @@ test "detached wire: every Event variant round-trips" { for (std.enums.values(pardes.Mouse.Button)) |button| { for (std.enums.values(pardes.Mouse.Kind)) |kind| { const m: pardes.Mouse = .{ .button = button, .kind = kind, .col = 4200, .row = 7, .ctrl = true }; - const got = (try roundClient(&buf, .{ .event = .{ .mouse = m } }, &scratch)).event.mouse; + const got = (try roundClient(&buf, .{ .event = .{ .mouse = m } })).event.mouse; try testing.expectEqual(m.button, got.button); try testing.expectEqual(m.kind, got.kind); try testing.expectEqual(m.col, got.col); @@ -1202,7 +1151,7 @@ test "detached wire: every Event variant round-trips" { } } { - const got = (try roundClient(&buf, .{ .event = .{ .resize = .{ .cols = 56, .rows = 14 } } }, &scratch)).event.resize; + const got = (try roundClient(&buf, .{ .event = .{ .resize = .{ .cols = 56, .rows = 14 } } })).event.resize; try testing.expectEqual(@as(u16, 56), got.cols); try testing.expectEqual(@as(u16, 14), got.rows); // Pixels travel whether or not this build has them; where it does, the @@ -1212,48 +1161,18 @@ test "detached wire: every Event variant round-trips" { try testing.expectEqual(@as(u16, 16), got.cell_pixels.h); } } + try testing.expectEqualStrings("clip", (try roundClient(&buf, .{ .event = .{ .paste = "clip" } })).event.paste); + try testing.expectEqualStrings("Look /x", (try roundClient(&buf, .{ .event = .{ .command = "Look /x" } })).event.command); { - const got = (try roundClient(&buf, .{ .event = .{ .output = .{ .pane = 3, .bytes = "hi\x00there" } } }, &scratch)).event.output; - try testing.expectEqual(@as(u8, 3), got.pane); - try testing.expectEqualStrings("hi\x00there", got.bytes); - } - try testing.expectEqual(@as(u8, 15), (try roundClient(&buf, .{ .event = .{ .eof = .{ .pane = 15 } } }, &scratch)).event.eof.pane); - { - const got = (try roundClient(&buf, .{ .event = .{ .lsp_resp = .{ .id = 0xdeadbeef, .rows = "a:1:2-3 x" } } }, &scratch)).event.lsp_resp; - try testing.expectEqual(@as(u32, 0xdeadbeef), got.id); - try testing.expectEqualStrings("a:1:2-3 x", got.rows); - } - { - // Including an EMPTY output, which is what a filter that consumed a - // selection and printed nothing returns. - const outputs: []const []const u8 = &.{ "AA\n", "", "cc" }; - const got = (try roundClient(&buf, .{ .event = .{ .pipe_resp = .{ .id = 9, .success = true, .outputs = outputs } } }, &scratch)).event.pipe_resp; - try testing.expectEqual(@as(u32, 9), got.id); - try testing.expect(got.success); - try testing.expectEqual(@as(usize, 3), got.outputs.len); - for (outputs, got.outputs) |want, have| try testing.expectEqualStrings(want, have); - } - { - const got = (try roundClient(&buf, .{ .event = .{ .file_changed = .{ .pane = 0, .bytes = "" } } }, &scratch)).event.file_changed; - try testing.expectEqual(@as(u8, 0), got.pane); - try testing.expectEqualStrings("", got.bytes); - } - try testing.expectEqualStrings("clip", (try roundClient(&buf, .{ .event = .{ .paste = "clip" } }, &scratch)).event.paste); - try testing.expectEqualStrings("Look /x", (try roundClient(&buf, .{ .event = .{ .command = "Look /x" } }, &scratch)).event.command); - { - const got = (try roundClient(&buf, .{ .event = .{ .pdf_scroll = .{ .pane = 2, .delta_pixels = -12.5 } } }, &scratch)).event.pdf_scroll; + const got = (try roundClient(&buf, .{ .event = .{ .pdf_scroll = .{ .pane = 2, .delta_pixels = -12.5 } } })).event.pdf_scroll; try testing.expectEqual(@as(u8, 2), got.pane); try testing.expectEqual(@as(f32, -12.5), got.delta_pixels); } - try testing.expectEqual(@as(f32, 1.25), (try roundClient(&buf, .{ .event = .{ .pinch = 1.25 } }, &scratch)).event.pinch); - try testing.expectEqual(@as(f32, -0.75), (try roundClient(&buf, .{ .event = .{ .touch_scroll = -0.75 } }, &scratch)).event.touch_scroll); + try testing.expectEqual(@as(f32, 1.25), (try roundClient(&buf, .{ .event = .{ .pinch = 1.25 } })).event.pinch); + try testing.expectEqual(@as(f32, -0.75), (try roundClient(&buf, .{ .event = .{ .touch_scroll = -0.75 } })).event.touch_scroll); try testing.expectEqual( std.meta.Tag(pardes.Event).pointer_leave, - (try roundClient(&buf, .{ .event = .pointer_leave }, &scratch)).event, - ); - try testing.expectEqual( - std.meta.Tag(pardes.Event).tick, - (try roundClient(&buf, .{ .event = .tick }, &scratch)).event, + (try roundClient(&buf, .{ .event = .pointer_leave })).event, ); } @@ -1270,44 +1189,91 @@ test "detached wire: every server message round-trips" { for (std.enums.values(Refusal)) |why| try testing.expectEqual(why, (try roundServer(&buf, .{ .refuse = why })).refuse); try testing.expectEqual(ServerMsg.quit, try roundServer(&buf, .quit)); - { - const got = (try roundServer(&buf, .{ .spawn = .{ .pane = 1, .cwd = "/home/x" } })).spawn; - try testing.expectEqual(@as(u8, 1), got.pane); - try testing.expectEqualStrings("/home/x", got.cwd); - } - { - const got = (try roundServer(&buf, .{ .pty_write = .{ .pane = 1, .bytes = "ls\r" } })).pty_write; - try testing.expectEqual(@as(u8, 1), got.pane); - try testing.expectEqualStrings("ls\r", got.bytes); - } - { - const got = (try roundServer(&buf, .{ .pty_resize = .{ .pane = 1, .cols = 80, .rows = 24 } })).pty_resize; - try testing.expectEqual(@as(u16, 80), got.cols); - try testing.expectEqual(@as(u16, 24), got.rows); - } - { - const got = (try roundServer(&buf, .{ .write_file = .{ .pane = 4, .path = "/tmp/a", .bytes = "body\n" } })).write_file; - try testing.expectEqual(@as(u8, 4), got.pane); - try testing.expectEqualStrings("/tmp/a", got.path); - try testing.expectEqualStrings("body\n", got.bytes); - } - try testing.expectEqualStrings(".{}", (try roundServer(&buf, .{ .write_dump = ".{}" })).write_dump); - { - const got = (try roundServer(&buf, .{ .watch_file = .{ .pane = 0, .path = "/tmp/b", .on = true } })).watch_file; - try testing.expectEqualStrings("/tmp/b", got.path); - try testing.expect(got.on); - } - { - const got = (try roundServer(&buf, .{ .watch_theme = .{ .generation = 7, .on = false } })).watch_theme; - try testing.expectEqual(@as(u32, 7), got.generation); - try testing.expect(!got.on); - } - try testing.expectEqual(@as(u8, 5), (try roundServer(&buf, .{ .dump_themes = .{ .pane = 5 } })).dump_themes.pane); + try testing.expectEqual(ServerMsg.detach, try roundServer(&buf, .detach)); try testing.expectEqualStrings("yank", (try roundServer(&buf, .{ .set_clipboard = "yank" })).set_clipboard); try testing.expectEqual(ServerMsg.read_clipboard, try roundServer(&buf, .read_clipboard)); try testing.expectEqualStrings("https://x", (try roundServer(&buf, .{ .open_link = "https://x" })).open_link); } +test "detached wire: only the display's own effects are on the wire" { + // Two guards, because the mistake has two directions. The exhaustive + // switch fails to COMPILE if a machine-local effect is added back, which + // is the direction that matters: it would hand a frontend work that must + // outlive it. The count fails if one of the three is dropped, which would + // silently leave a clipboard or a link unanswered on every frontend. + try testing.expectEqual(@as(usize, 8), std.enums.values(ServerTag).len); + for (std.enums.values(ServerTag)) |t| switch (t) { + .welcome, .refuse, .frame, .quit, .detach, .set_clipboard, .read_clipboard, .open_link => {}, + }; +} + +test "detached wire: a session is never told to do a frontend's remembering" { + // The mirror of the test above, and of client.zig's "a frontend is never + // asked to fork, write, or watch". That one pins what may reach a FRONTEND; + // this one pins what may reach the SESSION, and until the six tags below it + // names were deleted the protocol was asymmetric: server -> client carried + // only what a display can do, while client -> server still carried the + // machine-local host's own reports, which server.zig's `apply` hands + // straight to `core.update`. + // + // Adding a name here is meant to be an ARGUMENT, not a formality, and the + // bar is one sentence: A HUMAN DID IT. A keystroke, a click, a pinch, a + // window resized, a paste, a command line executed, a pointer leaving the + // window — plus the two session words that say who is speaking. A pty's + // output, a worker's answer, a watched file's new bytes and an animation + // tick all fail that bar the same way: nobody did them, a machine reported + // them, and the machine that reports them is the one already holding the + // core. + const allowed = [_][]const u8{ + // Who this connection is, and that it is finished. + "hello", "bye", + // ...and everything a person can do to a window. + "key", "mouse", + "resize", "paste", + "command", "pdf_scroll", + "pinch", "touch_scroll", + "pointer_leave", + }; + + // One: the tag set is exactly that, named rather than counted, so + // re-adding `output` fails with the name in the failure. + inline for (std.enums.values(ClientTag)) |t| { + for (allowed) |ok| { + if (std.mem.eql(u8, @tagName(t), ok)) break; + } else { + std.debug.print("ClientTag.{s} is not something a human did\n", .{@tagName(t)}); + return error.MachineLocalReportOnTheWire; + } + } + try testing.expectEqual(allowed.len, std.enums.values(ClientTag).len); + + // Two: and no tag BYTE outside them decodes either — the check above is + // about this build's enum, this one is about the bytes on the socket. The + // six deleted numbers (0x13..0x17, 0x1e) are in the 250 that must answer + // `BadTag`, so a frontend built before this change cannot forge a pane's + // output into a session built after it. + var accepted: usize = 0; + for (0..256) |i| { + const tag: u8 = @intCast(i); + // Empty payloads on purpose: what is asked is whether the TAG is known. + // A known one fails later (`Truncated`) or succeeds, never with + // `BadTag`. + if (decodeClient(tag, &.{})) |_| accepted += 1 else |err| switch (err) { + error.BadTag => continue, + else => accepted += 1, + } + const named = for (allowed) |ok| { + const want = std.meta.stringToEnum(ClientTag, ok).?; + if (@intFromEnum(want) == tag) break true; + } else false; + if (!named) { + std.debug.print("tag 0x{x:0>2} is decodable by a session and is not something a human did\n", .{tag}); + return error.MachineLocalReportOnTheWire; + } + } + try testing.expectEqual(allowed.len, accepted); +} + /// A grid with something in every corner: a default cell, a plain ASCII cell, /// an indexed pair, an rgb pair with every attribute on, and a multi-byte /// grapheme — the five shapes `putCell` branches on. @@ -1481,7 +1447,6 @@ test "detached wire: a run is coalesced across a gap only when that is cheaper" test "detached wire: a truncated frame is refused at every length" { var buf: [4096]u8 = undefined; - var scratch: Scratch = .{}; // Every prefix of a real message. The framing must say "not yet" for the // ones that are short, and the decoder must say "truncated" for a payload @@ -1494,22 +1459,21 @@ test "detached wire: a truncated frame is refused at every length" { // ...and the same bytes handed to the decoder with the header's length // left claiming the whole message, which is how a decoder is walked // off the end of its buffer. - try testing.expectError(error.Truncated, decodeClient(copy[0], copy[header_len..cut], &scratch)); + try testing.expectError(error.Truncated, decodeClient(copy[0], copy[header_len..cut])); } // Shorter than the header itself is not yet a message at all. for (0..header_len + 1) |cut| try testing.expectEqual(@as(?Framed, null), try framed(copy[0..cut])); // A payload with bytes LEFT OVER is refused too: it is not this message. - try testing.expectError(error.Trailing, decodeClient(@intFromEnum(ClientTag.tick), "x", &scratch)); + try testing.expectError(error.Trailing, decodeClient(@intFromEnum(ClientTag.pointer_leave), "x")); try testing.expectError(error.Trailing, decodeServer(@intFromEnum(ServerTag.quit), "x")); } test "detached wire: an unknown tag is refused, never guessed" { - var scratch: Scratch = .{}; // 0x00 and 0xff have never been assigned, and 0x0f sits in the gap between // the session tags and the input tags. All three are the same answer. for ([_]u8{ 0x00, 0x0f, 0x1f, 0xff }) |tag| { - try testing.expectError(error.BadTag, decodeClient(tag, "", &scratch)); + try testing.expectError(error.BadTag, decodeClient(tag, "")); try testing.expectError(error.BadTag, decodeServer(tag, "")); } // A tag NESTED in a payload gets the same treatment: a color, an @@ -1518,7 +1482,6 @@ test "detached wire: an unknown tag is refused, never guessed" { try testing.expectError(error.BadTag, decodeClient( @intFromEnum(ClientTag.mouse), &.{ 0x09, 0x00, 0, 0, 0, 0, 0 }, - &scratch, )); } @@ -1536,14 +1499,13 @@ test "detached wire: an over-long length prefix is refused before it is believed // An INNER length prefix, past the payload it sits in but inside the // protocol's cap — the one an outer-frame check cannot catch. - var scratch: Scratch = .{}; var paste: [8]u8 = undefined; std.mem.writeInt(u32, paste[0..4], 4096, .little); @memcpy(paste[4..8], "abcd"); - try testing.expectError(error.Truncated, decodeClient(@intFromEnum(ClientTag.paste), &paste, &scratch)); + try testing.expectError(error.Truncated, decodeClient(@intFromEnum(ClientTag.paste), &paste)); // ...and past the cap, which is refused rather than read. std.mem.writeInt(u32, paste[0..4], max_payload + 1, .little); - try testing.expectError(error.Overlong, decodeClient(@intFromEnum(ClientTag.paste), &paste, &scratch)); + try testing.expectError(error.Overlong, decodeClient(@intFromEnum(ClientTag.paste), &paste)); } test "detached wire: a frame that lies about its runs cannot walk out of the grid" { @@ -1664,56 +1626,44 @@ test "detached wire: a cursor outside the grid is refused, not painted" { } test "detached wire: values a field cannot mean are refused" { - var scratch: Scratch = .{}; - // A bool is 0 or 1. `2` used to be "true" in every hand-written codec that // ever silently accepted a corrupt stream. try testing.expectError(error.BadValue, decodeClient( @intFromEnum(ClientTag.key), &.{ 'a', 0, 0, 0, 0, 0, 2, 0, 0 }, - &scratch, )); // A codepoint past Unicode's last: @intCast into Key.cp's u21 would panic. try testing.expectError(error.BadValue, decodeClient( @intFromEnum(ClientTag.key), &.{ 0x00, 0x00, 0x11, 0x00, 0, 0, 0, 0, 0 }, - &scratch, )); - // A pane the core cannot index. + // A pane the core cannot index. `pdf_scroll` reads its pane byte before the + // f32 that follows, so a one-byte payload reaches the check this is about + // rather than tripping `Truncated` first. try testing.expectError(error.BadValue, decodeClient( - @intFromEnum(ClientTag.eof), + @intFromEnum(ClientTag.pdf_scroll), &.{pardes.MAX_PANES}, - &scratch, )); // A zero-column grid would collapse a shared session; an over-wide one is // past what this protocol carries. try testing.expectError(error.BadValue, decodeClient( @intFromEnum(ClientTag.hello), &.{ 1, 0, 0, 0, 24, 0 }, - &scratch, )); { var hello: [6]u8 = undefined; std.mem.writeInt(u16, hello[0..2], version, .little); std.mem.writeInt(u16, hello[2..4], max_cols + 1, .little); std.mem.writeInt(u16, hello[4..6], 24, .little); - try testing.expectError(error.BadValue, decodeClient(@intFromEnum(ClientTag.hello), &hello, &scratch)); + try testing.expectError(error.BadValue, decodeClient(@intFromEnum(ClientTag.hello), &hello)); } // A NaN scroll distance. `pinch` multiplies into a zoom the pane keeps. { var pinch: [4]u8 = undefined; std.mem.writeInt(u32, &pinch, @bitCast(std.math.nan(f32)), .little); - try testing.expectError(error.BadValue, decodeClient(@intFromEnum(ClientTag.pinch), &pinch, &scratch)); + try testing.expectError(error.BadValue, decodeClient(@intFromEnum(ClientTag.pinch), &pinch)); std.mem.writeInt(u32, &pinch, @bitCast(std.math.inf(f32)), .little); - try testing.expectError(error.BadValue, decodeClient(@intFromEnum(ClientTag.touch_scroll), &pinch, &scratch)); - } - // More pipe outputs than the core has selections to produce them. - { - var head: [7]u8 = undefined; - std.mem.writeInt(u32, head[0..4], 1, .little); - head[4] = 1; - std.mem.writeInt(u16, head[5..7], pardes.MAX_SELS + 1, .little); - try testing.expectError(error.Overlong, decodeClient(@intFromEnum(ClientTag.pipe_resp), &head, &scratch)); + try testing.expectError(error.BadValue, decodeClient(@intFromEnum(ClientTag.touch_scroll), &pinch)); } // A cell with the reserved attribute bit set, and one with an impossible // grapheme length. Both are bytes this protocol has no meaning for. diff --git a/src/gui/gui.zig b/src/gui/gui.zig index 05008d0b..c42e390b 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -5,6 +5,26 @@ //! FreeType-hinted R8 atlas. Test modes: PARDES_TEST_GRID=1 is headless (no SDL, //! stdin escape sequences in, text grid frames out); PARDES_TEST=1 keeps the //! real renderer, drives input from stdin, and captures frames to PPM. +//! +//! ...AND THE SAME WINDOW WITH NO CORE IN IT. `--attach`, and the `Attach` +//! builtin, hand this window's screen to a detached session (src/detached/): +//! the `Pardes` lives in THAT process, and this one sends the input it collects +//! and paints the frames it is sent. The two modes share every line that +//! touches SDL — `dispatch`/`keyDown` translate an SDL_Event once, +//! `renderFrame` rasterizes a `Surface` once, `putClipboard`/`takeClipboard` +//! and `look.openLink` are the desktop once — and differ only in where a +//! translated event goes and where the cells came from. `Input` is that seam, +//! and a null `core` inside it is what "attached" MEANS here: every function +//! that reads pane rects or theme colours off the core takes an optional one +//! and falls back to the body grid, because the wire carries cells, not the +//! layout that produced them. +//! +//! An attached window does NO machine-local work whatsoever: it forks no shell, +//! writes no file and watches no path, because the session process owns all of +//! that now (src/host_io.zig, src/file_watch.zig, src/detached/server.zig). +//! The only effects still on that wire are the three that need a human's own +//! display — `set_clipboard`, `read_clipboard`, `open_link` — and those land on +//! THIS display. const std = @import("std"); const builtin = @import("builtin"); const posix = std.posix; @@ -29,16 +49,25 @@ const nested = @import("../nested.zig"); const fuse = @import("../fuse.zig"); const fs_service = @import("../fs_service.zig"); +// The other half of `--detach`, and the reason this file has an `--attach` +// branch at all: the frontend side of a detached session is a window and a +// socket, and this file is already the one that owns a window. Just the one +// import: client.zig owns the frontend's whole side of this transport — the +// name resolution, the handshake, the poll interval and the decoded messages — +// so nothing here reaches past it to server.zig or wire.zig. +const detached_client = @import("../detached/client.zig"); + pub const c = @cImport({ @cDefine("SDL_DISABLE_OLD_NAMES", "1"); @cInclude("SDL3/SDL.h"); @cInclude("font.h"); }); -extern "c" fn forkpty(amaster: *c_int, name: ?[*:0]u8, termp: ?*const anyopaque, winp: ?*const posix.winsize) c_int; -extern "c" fn execv(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8) c_int; -extern "c" fn chdir(path: [*:0]const u8) c_int; -extern "c" fn _exit(status: c_int) noreturn; +// The machine-local half of a host — fork a pane's shell, put bytes on a disk +// — is shared with the tty shell and the detached daemon. This file used to +// carry its own `forkShell`, `writeWholeFile` and `writeFd`, ten of eleven +// lines identical to that file's and one line short of its zero-write guard. +const host_io = @import("../host_io.zig"); extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int; // TIOCSWINSZ: absent from std.c.T on darwin — _IOW('t', 103, winsize) const TIOCSWINSZ: c_int = @bitCast(@as(u32, if (@hasDecl(posix.T, "IOCSWINSZ")) posix.T.IOCSWINSZ else 0x80087467)); @@ -786,12 +815,12 @@ fn takeScrollTicks(accum: *f32) i32 { } /// The SDL boundary owns touch policy: two-finger scroll and tap-as-execute. -fn handleFinger(t: *Touch, core: *pardes.Pardes, f: Finger, win_w: f32, win_h: f32, cell_w: f32, cell_h: f32, tagline_w: f32) void { +fn handleFinger(t: *Touch, in: *Input, f: Finger, win_w: f32, win_h: f32, cell_w: f32, cell_h: f32, tagline_w: f32) void { std.debug.assert(cell_w > 0 and cell_h > 0); - return handlePairFinger(t, core, f, win_w, win_h, cell_w, cell_h, tagline_w); + return handlePairFinger(t, in, f, win_w, win_h, cell_w, cell_h, tagline_w); } -fn handlePairFinger(t: *Touch, core: *pardes.Pardes, f: Finger, win_w: f32, win_h: f32, cell_w: f32, cell_h: f32, tagline_w: f32) void { +fn handlePairFinger(t: *Touch, in: *Input, f: Finger, win_w: f32, win_h: f32, cell_w: f32, cell_h: f32, tagline_w: f32) void { switch (f.kind) { .down => { t.updatePoint(f); @@ -803,33 +832,33 @@ fn handlePairFinger(t: *Touch, core: *pardes.Pardes, f: Finger, win_w: f32, win_ t.syncPair(); if (!had_pair) return; const res = t.noteMotion(f) orelse return; - emitTouchScroll(core, res.center, res.ticks, res.delta_y, win_w, win_h, cell_w, cell_h, tagline_w); + emitTouchScroll(in, res.center, res.ticks, res.delta_y, win_w, win_h, cell_w, cell_h, tagline_w); }, .up, .cancel => { const tap_center = t.finishPair(f); t.updatePoint(f); t.syncPair(); - if (tap_center) |center| emitTouchClick(t, core, center, .middle, win_w, win_h, cell_w, cell_h, tagline_w); + if (tap_center) |center| emitTouchClick(t, in, center, .middle, win_w, win_h, cell_w, cell_h, tagline_w); }, } } -fn emitTouchScroll(core: *pardes.Pardes, center: TouchNormPoint, ticks: i32, delta_y: f32, win_w: f32, win_h: f32, cell_w: f32, cell_h: f32, tagline_w: f32) void { - const cell = gridCellAtDimensions(core, center.x * win_w, center.y * win_h, cell_w, cell_h, tagline_w); +fn emitTouchScroll(in: *Input, center: TouchNormPoint, ticks: i32, delta_y: f32, win_w: f32, win_h: f32, cell_w: f32, cell_h: f32, tagline_w: f32) void { + const cell = gridCellAtDimensions(in.core, center.x * win_w, center.y * win_h, cell_w, cell_h, tagline_w); const button: pardes.Mouse.Button = if (ticks > 0) .wheel_up else .wheel_down; var remaining: u32 = @abs(ticks); while (remaining > 0) : (remaining -= 1) - core.update(.{ .mouse = .{ .button = button, .kind = .press, .col = cell.col, .row = cell.row } }); - core.update(.{ .touch_scroll = delta_y }); + in.post(.{ .mouse = .{ .button = button, .kind = .press, .col = cell.col, .row = cell.row } }); + in.post(.{ .touch_scroll = delta_y }); } -fn emitTouchClick(t: *Touch, core: *pardes.Pardes, point: TouchNormPoint, button: pardes.Mouse.Button, win_w: f32, win_h: f32, cell_w: f32, cell_h: f32, tagline_w: f32) void { +fn emitTouchClick(t: *Touch, in: *Input, point: TouchNormPoint, button: pardes.Mouse.Button, win_w: f32, win_h: f32, cell_w: f32, cell_h: f32, tagline_w: f32) void { t.click_count +%= 1; t.click_button = button; t.click_flash_frames = touch_click_flash_max_frames; - const cell = gridCellAtDimensions(core, point.x * win_w, point.y * win_h, cell_w, cell_h, tagline_w); - core.update(.{ .mouse = .{ .button = button, .kind = .press, .col = cell.col, .row = cell.row } }); - core.update(.{ .mouse = .{ .button = button, .kind = .release, .col = cell.col, .row = cell.row } }); + const cell = gridCellAtDimensions(in.core, point.x * win_w, point.y * win_h, cell_w, cell_h, tagline_w); + in.post(.{ .mouse = .{ .button = button, .kind = .press, .col = cell.col, .row = cell.row } }); + in.post(.{ .mouse = .{ .button = button, .kind = .release, .col = cell.col, .row = cell.row } }); } fn normCell(norm: f32, win: f32, cell: f32) u16 { @@ -1451,6 +1480,24 @@ fn windowGeometry(window: *c.SDL_Window) WindowGeometry { }; } +/// This window in whole cells, which is the grid the core is asked to be. The +/// one derivation of it: `pollFrame` follows the window with it every frame, +/// `refitFont` re-asks after Ctrl+/Ctrl- has moved the cell under it, and both +/// attach paths tell the session what this window can show with it. A window +/// that is not a whole number of cells across has to round the same way in all +/// four places or the last row lands off the bottom edge. +const GridCells = struct { cols: u16, rows: u16 }; + +fn windowCells(g: *const Gui) GridCells { + var pw: c_int = 0; + var ph: c_int = 0; + _ = c.SDL_GetWindowSizeInPixels(g.window, &pw, &ph); + return .{ + .cols = @intCast(@max(1, @divTrunc(@as(u32, @intCast(@max(pw, 1))), g.cell_w))), + .rows = @intCast(@max(1, @divTrunc(@as(u32, @intCast(@max(ph, 1))), g.cell_h))), + }; +} + fn windowPointToPixels(geometry: WindowGeometry, x: f32, y: f32) crt.Point { return .{ .x = x * geometry.pixel_w / geometry.window_w, @@ -1485,30 +1532,40 @@ fn compactTaglineLayout(g: *const Gui, origin_col: f32) CellLayout { }; } +/// Where a tagline cell's compact band begins. `core` is null in an attached +/// window, and then EVERY tagline cell takes the last line's fallback: the +/// wire carries cells, not the pane rects that placed them, so there is no +/// band origin to compact against. That is the same answer `gridCellAtDimensions` +/// reaches for the same reason, which is what keeps the two honest — a click +/// lands on the glyph it was aimed at, because both sides map through the body +/// grid. The visible cost is one tagline row's worth of loose tracking. fn taglineLayoutForCell( g: *const Gui, - core: *const pardes.Pardes, + core: ?*const pardes.Pardes, col: u16, row: u16, track: ?pardes.panel_animation.Track, ) CellLayout { if (row < pardes.TOPBAR_H) return compactTaglineLayout(g, 0); - if (track) |active| { - const box = active.contentBox(); - const tag_y = if (core.settings.tag_bottom) box.y + box.h - @as(f32, @floatFromInt(pardes.BOX_H)) else box.y; - if (@as(f32, @floatFromInt(row)) >= tag_y and - @as(f32, @floatFromInt(row)) < tag_y + @as(f32, @floatFromInt(pardes.BOX_H))) - return compactTaglineLayout(g, box.x); - } - for (core.panes, 0..) |slot, id| { - if (slot == null) continue; - const r = core.rects[id]; - const tag_y = if (core.settings.tag_bottom) r.y + r.h -| pardes.BOX_H else r.y; - if (row == tag_y and col >= r.x and col < r.x + r.w) - return compactTaglineLayout(g, @floatFromInt(r.x)); + if (core) |p| { + if (track) |active| { + const box = active.contentBox(); + const tag_y = if (p.settings.tag_bottom) box.y + box.h - @as(f32, @floatFromInt(pardes.BOX_H)) else box.y; + if (@as(f32, @floatFromInt(row)) >= tag_y and + @as(f32, @floatFromInt(row)) < tag_y + @as(f32, @floatFromInt(pardes.BOX_H))) + return compactTaglineLayout(g, box.x); + } + for (p.panes, 0..) |slot, id| { + if (slot == null) continue; + const r = p.rects[id]; + const tag_y = if (p.settings.tag_bottom) r.y + r.h -| pardes.BOX_H else r.y; + if (row == tag_y and col >= r.x and col < r.x + r.w) + return compactTaglineLayout(g, @floatFromInt(r.x)); + } } - // A stale/closing cell without a live pane should still remain legible. - // Its body-grid origin is the only safe fallback available. + // A stale/closing cell without a live pane should still remain legible, + // and so should every cell of an attached window. Its body-grid origin is + // the only safe fallback available. return compactTaglineLayout(g, @floatFromInt(col)); } @@ -1639,11 +1696,23 @@ fn setTransitionFields( pub const run = runNative; -fn runNative(init: std.process.Init, opts_in: pardes.Options) !void { - const io = init.io; +/// `attach` is `--attach[=]`: empty means "the session there is" (see +/// `detached_client.resolve`). It is a parameter rather than an `Options` field +/// because it says nothing to the core — this process does not have one when it +/// is set. +fn runNative(init: std.process.Init, opts_in: pardes.Options, attach: ?[]const u8) !void { const gpa = init.gpa; const env = init.environ_map; - if (env.get("PARDES_TEST_GRID") != null) return runGrid(init, opts_in); + // PARDES_TEST_GRID owns the process when it is set, and it is headless. + // There is no window to hand to a session, so refuse the combination + // rather than silently dropping the flag a harness meant. + if (env.get("PARDES_TEST_GRID") != null) { + if (attach != null) { + log.err("--attach needs a window; PARDES_TEST_GRID is headless", .{}); + return error.AttachNeedsWindow; + } + return runGrid(init, opts_in); + } const test_mode = env.get("PARDES_TEST") != null; const capture_dir = env.get("PARDES_TEST_CAPTURE_DIR"); @@ -1865,7 +1934,40 @@ fn runNative(init: std.process.Init, opts_in: pardes.Options) !void { _ = c.ui_font_raster(font, scale, ' ', atlas_stage.ptr, @intCast(atlas_w), @intCast(cell_w), @intCast(cell_h), asc); g.pen_x = cell_w; - // ---- the core ---- + // `--attach`: this process has a window and NO core. Everything above is + // the window and the rasterizer, which an attached frontend needs exactly + // as much as a whole session does; everything below is the core, which + // lives in the detached process (src/detached/). The branch is here so both + // leave by the same door — the GPU objects, the glyph atlas and the face + // are put away by the defers above whichever mode ran. + if (attach) |requested| return attachRequested(gpa, &g, requested); + // ...and the same handover arrived at from the other side: the `Attach` + // builtin gives this window to a session mid-flight. `localSession` returns + // a CONNECTED client only, and by the time it does every pane shell, watch + // and mount of the local session is already away. + var attached: ?detached_client.Client = null; + try localSession(init, &g, opts_in, test_mode, &attached); + if (attached) |*client| return attachedLoop(gpa, &g, client); +} + +/// The session that lives in THIS process: the core, its pane shells, its +/// watches, its acme filesystem and the loop that pumps them. A function of its +/// own rather than the tail of `runNative` because that makes its teardown a +/// scope exit instead of a second copy of the same twelve defers — and the +/// `Attach` builtin needs exactly that teardown, in exactly that LIFO order, +/// before an attached loop may draw on the same window. +/// +/// `attached` is how a connected client leaves: it is set only after a +/// handshake is in flight, which is what makes a failed `Attach` a no-op. +fn localSession( + init: std.process.Init, + g: *Gui, + opts_in: pardes.Options, + test_mode: bool, + attached: *?detached_client.Client, +) !void { + const io = init.io; + const gpa = init.gpa; const allocs = pardes.allocators.init(gpa); defer pardes.allocators.deinit(); pardes.image.start(io, allocs.image); // stb_image allocator for image panes @@ -1891,9 +1993,9 @@ fn runNative(init: std.process.Init, opts_in: pardes.Options) !void { var pw: c_int = 0; var ph: c_int = 0; if (opts.load_path != null) { - _ = c.SDL_GetWindowSizeInPixels(window, &pw, &ph); - opts.cols = @intCast(@max(1, @divTrunc(@as(u32, @intCast(@max(pw, 1))), cell_w))); - opts.rows = @intCast(@max(1, @divTrunc(@as(u32, @intCast(@max(ph, 1))), cell_h))); + _ = c.SDL_GetWindowSizeInPixels(g.window, &pw, &ph); + opts.cols = @intCast(@max(1, @divTrunc(@as(u32, @intCast(@max(pw, 1))), g.cell_w))); + opts.rows = @intCast(@max(1, @divTrunc(@as(u32, @intCast(@max(ph, 1))), g.cell_h))); } var core = if (opts.load_path) |lp| blk: { const bytes = try @import("../look.zig").readFile(gpa, lp); @@ -1905,8 +2007,8 @@ fn runNative(init: std.process.Init, opts_in: pardes.Options) !void { // construction: argv image panes no longer freeze the startup capability // into their PETSCII preference, so their first render emits attachments. core.native_images = true; - observeGuiFont(&g, core); - syncTaglineFont(&g, core); + observeGuiFont(g, core); + syncTaglineFont(g, core); // Private, complete before any fork and retained until the last possible // spawn; children borrow only these stable in-struct path buffers. @@ -1967,7 +2069,7 @@ fn runNative(init: std.process.Init, opts_in: pardes.Options) !void { var shell: Shell = .{ .core = core, - .gui = &g, + .gui = g, .io = io, .gpa = gpa, .lsp_allocator = allocs.lsp, @@ -2003,17 +2105,46 @@ fn runNative(init: std.process.Init, opts_in: pardes.Options) !void { // it a control pipe that CAN wake it out of poll(). fs_service.wake(fs, &queue, wakeFs); - _ = c.SDL_StartTextInput(window); + _ = c.SDL_StartTextInput(g.window); if (test_mode) setStdinRaw() catch {}; shell.threads_ok = true; - // The core owns the loop. This owns the one thing a pump cannot do from - // inside itself: Restore swaps the whole Pardes, which is only safe - // BETWEEN iterations. + // The core owns the loop. This owns the two things a pump cannot do from + // inside itself, because both replace the whole session and are only safe + // BETWEEN iterations: Restore swaps the `Pardes`, and Attach retires it. while (!core.quit) { try core.pump(host); if (core.quit) break; // a session that ended does not restore into one + // Attach builtin: hand this window's screen to a detached session. + // + // GREET FIRST, SWAP SECOND, and that order IS the feature. + // `detached_client.attempt` resolves, connects AND waits for the + // `welcome`, and closes whatever it opened on every other outcome — so + // when this returns anything but `.greeted`, nothing below has run and + // this instance is exactly as it was: every pane, every shell, every + // unsaved buffer, the whole undo history. It says why on the row of the + // pane that ran the word and the session goes on. A half-torn-down + // editor is the one outcome an attach must never have, and `open` alone + // cannot rule it out — a `refuse .version` from a session built by the + // last `zig build` arrives AFTER the connect. + if (core.takeAttach()) |req| { + const geom = windowCells(g); + const outcome = detached_client.attempt(gpa, req.name, geom.cols, geom.rows); + switch (outcome) { + .greeted => |client| { + // Greeted, so this session is over: the `break` runs the + // filesystem unmount below and then every defer above, and + // `runNative` picks the client up on the far side. + attached.* = client; + break; + }, + else => { + var mbuf: [256]u8 = undefined; + core.setMessage(req.pane, attachFailure(&mbuf, outcome, req.name)); + }, + } + } // Restore builtin: swap in a core rebuilt from the dump; kill the live // shells (their detached readers wake on child death; gens bumped so // the stale eofs close the old fds without touching the replay panes) @@ -2041,7 +2172,7 @@ fn runNative(init: std.process.Init, opts_in: pardes.Options) !void { .{ .text = 0 }, ); _ = file_watch.applyThemeEffect(core, gpa, inotify_fd, &watches, 0, false, false); - clearNativeImages(&g); + clearNativeImages(g); g.presented_images.clearRetainingCapacity(); g.prepared_images.clearRetainingCapacity(); nc.native_images = true; @@ -2050,8 +2181,8 @@ fn runNative(init: std.process.Init, opts_in: pardes.Options) !void { core = nc; shell.core = nc; shell.surface = null; - observeGuiFont(&g, core); - syncTaglineFont(&g, core); + observeGuiFont(g, core); + syncTaglineFont(g, core); } } @@ -2066,6 +2197,243 @@ fn runNative(init: std.process.Init, opts_in: pardes.Options) !void { } } +// ===================================================================== +// --attach: a window, a socket, and no core +// ===================================================================== + +/// What to say when an attach did not happen. One function for both callers +/// because it is one set of outcomes: `--attach` logs it to a terminal it has +/// not drawn over yet, the `Attach` word puts it on the pane's message row, and +/// neither should be inventing its own wording for `refuse .version`. +/// +/// `requested` is the word a person typed, empty for "the session that is +/// there" — which is the whole difference between "no session called work" and +/// "nothing is detached". +fn attachFailure(buf: []u8, outcome: detached_client.Attempt, requested: []const u8) []const u8 { + return switch (outcome) { + // The caller took this one and never asks. + .greeted => unreachable, + .no_session => if (requested.len != 0) + std.fmt.bufPrint(buf, "Attach: no detached session called '{s}'", .{requested}) catch + "Attach: no detached session under that name" + else + "Attach: nothing is detached (start one with `pardes --detach`)", + .ambiguous => |n| std.fmt.bufPrint(buf, "Attach: {d} detached sessions; say which one", .{n}) catch + "Attach: several detached sessions; say which one", + .refused => |why| switch (why) { + .version => "Attach: that session speaks a different wire version — it is another build of pardes", + .full => "Attach: that session already has every frontend slot taken", + .quitting => "Attach: that session is ending", + }, + .silent => "Attach: that session took the connection and never greeted us", + .lost => |err| std.fmt.bufPrint(buf, "Attach: lost the connection ({t})", .{err}) catch + "Attach: lost the connection", + }; +} + +/// `--attach[=]`: this window is a frontend from its first frame. Split +/// from `attachedLoop` because the two arrive with different evidence — a +/// command line has a person at a terminal to tell when there is nothing to +/// attach to and a process exit status to carry it, while an `Attach` inside a +/// session has a pane's message row and a live editor to leave standing. Both +/// reach `attachedLoop` with a GREETED client and never with less. +fn attachRequested(gpa: std.mem.Allocator, g: *Gui, requested: []const u8) !void { + const geom = windowCells(g); + const outcome = detached_client.attempt(gpa, requested, geom.cols, geom.rows); + switch (outcome) { + .greeted => |greeted| { + var client = greeted; + return attachedLoop(gpa, g, &client); + }, + else => { + // The window exists but has drawn nothing, so stderr is still the + // only place a person is looking; the wording is the message row's, + // because it is the same set of outcomes. + var mbuf: [256]u8 = undefined; + log.err("{s}", .{attachFailure(&mbuf, outcome, requested)}); + // ...and the exit status keeps the distinction the sentence makes, + // for whatever launched this window. + return switch (outcome) { + .no_session => error.NoSession, + .ambiguous => error.AmbiguousSession, + .refused => error.Refused, + .silent => error.NoGreeting, + .lost => |err| err, + .greeted => unreachable, + }; + }, + } +} + +/// The whole of an attached window: input and screen, and nothing else. SDL +/// events become `pardes.Event`s on the socket through the same `dispatch` a +/// local session uses; frames come back and go through the same `renderFrame`. +/// It forks no shell, writes no file and watches no path — the session process +/// does all of that now — so the only effects still arriving here are the three +/// that need a human's own display. +fn attachedLoop(gpa: std.mem.Allocator, g: *Gui, client: *detached_client.Client) !void { + // The `bye` is a courtesy: the session survives a frontend that simply + // dies, but seven bytes turn "the peer vanished" into "the peer left" in + // its log. + defer client.detach(); + // The window may have arrived here from `localSession`, where this was + // already called; SDL_StartTextInput is idempotent, and calling it is what + // makes the `--attach`-from-startup path receive SDL_EVENT_TEXT_INPUT at + // all. + _ = c.SDL_StartTextInput(g.window); + + var in: Input = .{ .client = client }; + // A frame is the only thing that makes this window redraw. There is no + // animation clock and no core asking for a tick — the session spends both + // and sends the result — so a pass that saw nothing new presents nothing. + var dirty = false; + var geom = windowCells(g); + while (true) { + const link = client.wait(detached_client.poll_ms); + // DECODE BEFORE REACTING TO THE HANGUP. `wait` reports the close in the + // same call that read the last bytes, and the last bytes are the + // session's `quit`: `fill` appends every chunk and only then sees the + // zero-length read. client.zig prefers POLLIN over POLLHUP for exactly + // this reason, and honouring it is what makes an ordinary `Kill` close + // every attached window by the front door instead of leaving whichever + // one lost the race reporting a broken link. + while (true) { + const msg = (try client.next()) orelse break; + switch (msg) { + // A greeting cannot arrive twice and a refusal cannot follow + // one at all — `detached_client.attempt` consumed the welcome + // before this loop was entered, and the union is exhaustive, so + // these two arms exist to say that rather than to do anything. + // A session that sent either here is not speaking this protocol. + .welcome => {}, + .refuse => |why| { + log.err("session refused an already-greeted frontend: {t}", .{why}); + return error.Refused; + }, + // Applied too — `grid` and `cursor` are current by the time + // this lands, so all that is left is putting them on screen. + .frame => dirty = true, + // THE SESSION ENDED (`Kill`): every frontend goes with it. + .quit => return, + // ...and `Detach`: THIS frontend was asked to leave and the + // session is carrying on without it, panes and shells and undo + // history intact, with whatever other frontends are attached + // still looking at it. Leaving because a person asked is a + // SUCCESS — hence a plain return and not the `error.Refused` + // above — and the deferred `client.detach()` still sends the + // `bye`, so the session logs a peer that left rather than one + // that vanished. The window closes because `runNative` returns. + .detach => return, + .set_clipboard => |text| putClipboard(gpa, text), + // The answer is not a reply message: it is an ordinary paste + // event on the way back, which is the same asynchronous shape + // `pull_read_clipboard` already has in process. + .read_clipboard => if (takeClipboard()) |text| { + defer c.SDL_free(text.ptr); + in.post(.{ .paste = text }); + }, + .open_link => |url| look.openLink(url), + } + } + try link; + + var sev = std.mem.zeroes(c.SDL_Event); + while (c.SDL_PollEvent(&sev)) dispatch(g, &in, &sev); + pollGamepad(g, &in); + // One check for the whole burst rather than one per event: `Input.post` + // stops sending at the first failure, so this is where a dead link is + // reported and there is nothing left in flight to lose. + if (in.lost) |err| return err; + if (in.quit) return; + + // What this WINDOW can show, which is not a promise about the next + // frame: with several frontends attached the session grid is the + // smallest common one (client.zig GEOMETRY). + const now = windowCells(g); + if (now.cols != geom.cols or now.rows != geom.rows) { + geom = now; + try client.resize(now.cols, now.rows); + } + if (!dirty) continue; + dirty = false; + paintAttached(g, gpa, client); + } +} + +/// The frame the session sent, through the renderer this window already has. +/// The `Surface` is built OVER the client's grid rather than copied into one: +/// `renderFrame` reads cells and never writes them, and a full frame of a large +/// grid is 1.6 MiB. +/// +/// Three of a session's own surface fields are absent here and each absence is +/// load-bearing. No panel tracks: a pane transition is composed by the process +/// that owns the panes and what arrives is the composed result, so `makePaintPlan` +/// builds its single static batch. No pixel attachments: this wire carries no +/// images. No previous cells: `hasPanelDiff` is therefore false and the whole +/// old/new layer machinery stays out of the plan. +fn paintAttached(g: *Gui, gpa: std.mem.Allocator, client: *detached_client.Client) void { + var surface: pardes.Surface = .{ + .cols = client.cols, + .rows = client.rows, + .cells = client.grid.items, + .cursor = if (client.cursor) |cu| .{ .x = cu.x, .y = cu.y, .bar = cu.bar } else null, + }; + // Two of `renderFrame`'s arguments are chrome colours the session resolved + // off a theme that is not on the wire. Both want `chromeTheme().tag_bg`, + // and the frame carries it exactly: row zero IS a full-width band that + // pardes.zig fills with that colour unconditionally, which is what + // `frameChromeBg` reads. The topbar rule then wears the band's own colour, + // joining the two bands directly the way + // `config.gui_topbar_pane_border_px = 0` does — a rule whose colour we + // would have to invent is worse than no rule. + const chrome = frameChromeBg(&surface); + _ = renderFrame( + g, + gpa, + null, + &surface, + // No theme background either, so every cell the session left at its + // default wears this window's own ground — the same answer a terminal + // frontend gives by writing a default cell. + null, + chrome, + chrome, + // Crt/Ripple/Glitch are core settings and the core is elsewhere; so is + // Debug, which is what the touch overlay hangs off. + .{}, + false, + ) catch |err| blk: { + log.err("render: {t}", .{err}); + break :blk false; + }; +} + +/// The tagline background this frame was painted with, read off the frame. An +/// attached window has no core to ask for `chromeTheme().tag_bg`, and +/// `renderFrame` wants it twice: as the chrome band under the topbar's compact +/// cells, and as the sub-cell strip `buildOverlay` extends below a bottom +/// tagline band when the window is not a whole number of cells tall. +/// +/// ROW ZERO is where it is read, and that is not a guess: the topbar is filled +/// edge to edge with `chrome.tag_bg` at `font_role = .tagline` on every frame +/// (pardes.zig `renderTopbar`), so its first tagline cell IS the colour. The +/// last row was the wrong place to look and cost a visibly dark band — a +/// session whose bottom row is pane BODY has no tagline cell there at all, so +/// the scan fell through to `bg_default` and painted the topbar's remainder +/// and every tag-cell gap near-black. +fn frameChromeBg(surface: *const pardes.Surface) [3]u8 { + if (surface.rows == 0 or surface.cols == 0) return bg_default; + for (surface.cells[0..surface.cols]) |cell| { + if (cell.default or cell.style.font_role != .tagline) continue; + return switch (cell.style.bg) { + .default => bg_default, + .index => |i| palColor(i), + .rgb => |rgb| rgb, + }; + } + return bg_default; +} + /// PARDES_TEST_GRID=1: headless. No SDL at all — stdin escape sequences in, /// the rendered Surface out as text frames (same framing as the prototype). fn runGrid(init: std.process.Init, opts_in: pardes.Options) !void { @@ -2183,7 +2551,8 @@ fn runGrid(init: std.process.Init, opts_in: pardes.Options) !void { // The two halves of a pass's input, in the order the flat loop had // them: the scripted feed, then whatever the reader threads handed // over. `pump` has no `wait_input` to do it in — see `grid_vtable`. - const r = try shell.feed.pump(gpa, core, null); + var in: Input = .{ .core = core }; + const r = try shell.feed.pump(gpa, &in, null); if (r.eof) break; if (r.n_events != 0) shell.saw_event = true; shell.drainQueue(); @@ -2222,7 +2591,7 @@ const StdinFeed = struct { const Result = struct { eof: bool = false, n_events: usize = 0 }; /// Poll stdin briefly and translate what arrived. `g` is null in grid mode. - fn pump(f: *StdinFeed, gpa: std.mem.Allocator, core: *pardes.Pardes, g: ?*Gui) !Result { + fn pump(f: *StdinFeed, gpa: std.mem.Allocator, in: *Input, g: ?*Gui) !Result { var out: Result = .{}; // a pty stdin also carries the winsize; poll it in place of SIGWINCH var ws: posix.winsize = std.mem.zeroes(posix.winsize); @@ -2232,7 +2601,7 @@ const StdinFeed = struct { { f.last_cols = ws.col; f.last_rows = ws.row; - f.applyResize(core, g, ws.col, ws.row); + f.applyResize(in, g, ws.col, ws.row); out.n_events += 1; } @@ -2273,14 +2642,14 @@ const StdinFeed = struct { if (mapScenePoint(gp, finger.x * win_w, finger.y * win_h, win_w, win_h)) |m| { finger.x = m.x / win_w; finger.y = m.y / win_h; - handleFinger(&gp.touch, core, finger, win_w, win_h, @floatFromInt(gp.cell_w), @floatFromInt(gp.cell_h), @floatFromInt(gp.tagline_width)); + handleFinger(&gp.touch, in, finger, win_w, win_h, @floatFromInt(gp.cell_w), @floatFromInt(gp.cell_h), @floatFromInt(gp.tagline_width)); } else { finger.kind = .cancel; - handleFinger(&gp.touch, core, finger, win_w, win_h, @floatFromInt(gp.cell_w), @floatFromInt(gp.cell_h), @floatFromInt(gp.tagline_width)); - core.update(.pointer_leave); + handleFinger(&gp.touch, in, finger, win_w, win_h, @floatFromInt(gp.cell_w), @floatFromInt(gp.cell_h), @floatFromInt(gp.tagline_width)); + in.post(.pointer_leave); } - } else { - handleFinger(&grid_touch, core, finger, @floatFromInt(core.screen_w), @floatFromInt(core.screen_h), 1, 1, 1); + } else if (in.core) |core| { + handleFinger(&grid_touch, in, finger, @floatFromInt(core.screen_w), @floatFromInt(core.screen_h), 1, 1, 1); } out.n_events += 1; } @@ -2305,7 +2674,7 @@ const StdinFeed = struct { .key_press => |key| { var text: []const u8 = ""; if (key.text) |t| text = f.cache.put(t); - core.update(.{ .key = .{ + in.post(.{ .key = .{ .cp = mapKey(effCp(key)), .text = text, .ctrl = key.mods.ctrl, @@ -2327,7 +2696,7 @@ const StdinFeed = struct { else => null, }; if (button) |b| { - core.update(.{ .mouse = .{ + in.post(.{ .mouse = .{ .button = b, .kind = switch (m.type) { .press => .press, @@ -2343,11 +2712,11 @@ const StdinFeed = struct { } }, .winsize => |vw| { - f.applyResize(core, g, @intCast(vw.cols), @intCast(vw.rows)); + f.applyResize(in, g, @intCast(vw.cols), @intCast(vw.rows)); out.n_events += 1; }, .paste => |text| { - core.update(.{ .paste = text }); + in.post(.{ .paste = text }); gpa.free(@constCast(text)); out.n_events += 1; }, @@ -2357,14 +2726,14 @@ const StdinFeed = struct { return out; } - fn applyResize(f: *StdinFeed, core: *pardes.Pardes, g: ?*Gui, cols: u16, rows: u16) void { + fn applyResize(f: *StdinFeed, in: *Input, g: ?*Gui, cols: u16, rows: u16) void { _ = f; if (g) |gp| { // capture mode: resize the window; the frame loop resizes the core _ = c.SDL_SetWindowSize(gp.window, @intCast(cols * gp.cell_w), @intCast(rows * gp.cell_h)); _ = c.SDL_SyncWindow(gp.window); } else { - core.update(.{ .resize = .{ .cols = cols, .rows = rows } }); + in.post(.{ .resize = .{ .cols = cols, .rows = rows } }); } } }; @@ -2459,20 +2828,71 @@ fn dumpGrid(gpa: std.mem.Allocator, surface: *pardes.Surface) !void { @memcpy(frame[at..][0..footer.len], footer); at += footer.len; std.debug.assert(at == frame.len); - writeFd(1, frame); + host_io.writeFd(1, frame); } // ===================================================================== // SDL event dispatch // ===================================================================== -fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { +/// Where a translated SDL event goes, and the only thing the input path knows +/// about the session it belongs to. The local shell hands events to the +/// `Pardes` in this process; an attached window puts them on a socket, because +/// the core is in the detached one. Everything between an SDL_Event and a +/// `pardes.Event` — the keycode table, the pointer/cell mapping, the touch +/// machine, the Steam Deck mapping — is ONE translation serving both, and this +/// is what keeps it from becoming two. +const Input = struct { + /// Null in an attached window, and this is also the flag the renderer- and + /// pointer-side functions test: no core means no pane rects and no theme, + /// and each of those has a documented body-grid fallback. + core: ?*pardes.Pardes = null, + /// Null in a local session. Exactly one of the two is ever set. + client: ?*detached_client.Client = null, + /// Attached only: the window was closed. A local session says the same + /// thing by writing `core.quit`, which the core owns and this must not + /// shadow. + quit: bool = false, + /// Attached only: a send failed, which means this window has lost its + /// session. Recorded rather than returned because `dispatch` is called from + /// inside an SDL drain with no error path, and a dead link does not need + /// reporting once per event in the burst. + lost: ?anyerror = null, + + /// One translated event on its way to the core, wherever the core is. + fn post(in: *Input, ev: pardes.Event) void { + if (in.core) |core| return core.update(ev); + const client = in.client orelse return; + // Nothing more goes out after the first failure: the rest of this + // burst would each fail the same way, and the loop is about to leave. + if (in.lost != null) return; + client.send(.{ .event = ev }) catch |err| switch (err) { + // A message this protocol cannot carry is not a link that has + // died. The one event here that can reach `wire.max_payload` is a + // paste of a 16 MiB clipboard, and dropping it beats ending a + // session over it. + error.Overlong, error.NoSpace => {}, + else => in.lost = err, + }; + } + + /// The window asked to close. In a session that ends the session; in an + /// attached window it ends this frontend and nothing else — the panes, the + /// shells and the undo history are in the other process and outlive it, + /// which is the whole point of `--detach`. + fn close(in: *Input) void { + if (in.core) |core| core.quit = true; + in.quit = true; + } +}; + +fn dispatch(g: *Gui, in: *Input, sev: *const c.SDL_Event) void { switch (sev.type) { - c.SDL_EVENT_QUIT, c.SDL_EVENT_WINDOW_CLOSE_REQUESTED => core.quit = true, + c.SDL_EVENT_QUIT, c.SDL_EVENT_WINDOW_CLOSE_REQUESTED => in.close(), c.SDL_EVENT_WINDOW_MOUSE_LEAVE => { g.pointer_present = false; g.pointer_mapped = false; - core.update(.pointer_leave); + in.post(.pointer_leave); }, // window resizes are picked up by the per-frame grid check c.SDL_EVENT_KEY_DOWN => { @@ -2480,8 +2900,9 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { g.live_alt = (sev.key.mod & c.SDL_KMOD_ALT) != 0; // Ctrl+ / Ctrl-: the font size. Here rather than in keyDown // because it is the shell's business and not the core's — the - // core has no font — and because this is the only side of the - // wall where `g` is in scope anyway. + // core has no font, and an attached window has no core at all yet + // still resizes its own text — and because this is the only side + // of the wall where `g` is in scope anyway. // // SIX keycodes for two keys, and every one of them is a key // somebody actually presses: @@ -2520,12 +2941,15 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { // the size already on screen if (want != g.px) { g.px = want; - refitFont(g, core); - observeGuiFont(g, core); + refitFont(g, in.core); + // Attached, the new grid reaches the session as the + // ordinary window-geometry check on the next pass, and + // there is no local Font state to observe either. + if (in.core) |core| observeGuiFont(g, core); } return; } - keyDown(core, sev.key.key, sev.key.mod); + keyDown(in, sev.key.key, sev.key.mod); }, c.SDL_EVENT_KEY_UP => { g.live_ctrl = (sev.key.mod & c.SDL_KMOD_CTRL) != 0; @@ -2548,7 +2972,7 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { var it = view.iterator(); var at: usize = 0; while (it.nextCodepoint()) |cp| : (at = it.i) { - core.update(.{ .key = .{ .cp = cp, .text = text[at..it.i], .ctrl = g.live_ctrl, .alt = g.live_alt } }); + in.post(.{ .key = .{ .cp = cp, .text = text[at..it.i], .ctrl = g.live_ctrl, .alt = g.live_alt } }); } }, c.SDL_EVENT_MOUSE_BUTTON_DOWN, c.SDL_EVENT_MOUSE_BUTTON_UP => { @@ -2562,22 +2986,22 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { g.pad_x = b.x; g.pad_y = b.y; g.pointer_present = true; - const mc = mouseCell(g, core, b.x, b.y) orelse { + const mc = mouseCell(g, in.core, b.x, b.y) orelse { g.pointer_mapped = false; // A release outside the visible CRT tube still ends a drag at // its last real cell; a press on black margin is inert. - if (!b.down) if (g.pointer_cell) |last| core.update(.{ .mouse = .{ + if (!b.down) if (g.pointer_cell) |last| in.post(.{ .mouse = .{ .button = button, .kind = .release, .col = last.col, .row = last.row, } }); - core.update(.pointer_leave); + in.post(.pointer_leave); return; }; g.pointer_mapped = true; g.pointer_cell = mc; - core.update(.{ + in.post(.{ .mouse = .{ .button = button, .kind = if (b.down) .press else .release, @@ -2605,14 +3029,14 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { .right else null; - const mc = mouseCell(g, core, m.x, m.y) orelse { + const mc = mouseCell(g, in.core, m.x, m.y) orelse { g.pointer_mapped = false; - core.update(.pointer_leave); + in.post(.pointer_leave); return; }; g.pointer_mapped = true; g.pointer_cell = mc; - core.update(.{ .mouse = .{ + in.post(.{ .mouse = .{ .button = held orelse .none, .kind = if (held != null) .drag else .motion, .col = mc.col, @@ -2625,53 +3049,68 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { g.pad_x = w.mouse_x; g.pad_y = w.mouse_y; g.pointer_present = true; - const mc = mouseCell(g, core, w.mouse_x, w.mouse_y) orelse { + const mc = mouseCell(g, in.core, w.mouse_x, w.mouse_y) orelse { g.pointer_mapped = false; - core.update(.pointer_leave); + in.post(.pointer_leave); return; }; g.pointer_mapped = true; g.pointer_cell = mc; if (w.y != 0 and std.math.isFinite(w.y)) { - // Preserve SDL's floating-point distance. Input events drained - // in this loop naturally form one render batch; stepScroll - // applies their exact sum and tells the core only about whole - // row boundaries. One accumulator belongs to one pane, so a - // wheel event over another pane first retires the old offset. - const hit: ?usize = for (core.panes, 0..) |slot, i| { - if (slot == null) continue; - const r = core.rects[i]; - if (mc.col >= r.x and mc.col < r.x + r.w and mc.row >= r.y and mc.row < r.y + r.h) break i; - } else null; - if (g.scroll_pane) |old| if (hit == null or hit.? != old) { - // There is one fractional overlay, not one per pane. Retire - // the old one at its already boundary-rounded core state; - // carrying its lag into `hit` would move the wrong pane. - resetScroll(g); - }; - if (hit) |id| { - const pdf_target = if (comptime pardes.pdf_enabled) - core.native_images and core.panes[id].?.pdfPage() != null - else - false; - if (pdf_target) { - // PDF placements live in physical document space, so - // preserve SDL's raw magnitude directly instead of - // quantizing through synthetic wheel buttons/rows. + if (in.core) |core| { + // Preserve SDL's floating-point distance. Input events drained + // in this loop naturally form one render batch; stepScroll + // applies their exact sum and tells the core only about whole + // row boundaries. One accumulator belongs to one pane, so a + // wheel event over another pane first retires the old offset. + const hit: ?usize = for (core.panes, 0..) |slot, i| { + if (slot == null) continue; + const r = core.rects[i]; + if (mc.col >= r.x and mc.col < r.x + r.w and mc.row >= r.y and mc.row < r.y + r.h) break i; + } else null; + if (g.scroll_pane) |old| if (hit == null or hit.? != old) { + // There is one fractional overlay, not one per pane. Retire + // the old one at its already boundary-rounded core state; + // carrying its lag into `hit` would move the wrong pane. resetScroll(g); - core.update(.{ .pdf_scroll = .{ - .pane = @intCast(id), - .delta_pixels = -w.y * @as(f32, @floatFromInt(g.cell_h)), - } }); - } else { - g.scroll_pane = id; - g.scroll_col = mc.col; - g.scroll_row = mc.row; - g.scroll_delta = accumulateWheelDelta(g.scroll_delta, w.y); + }; + if (hit) |id| { + const pdf_target = if (comptime pardes.pdf_enabled) + core.native_images and core.panes[id].?.pdfPage() != null + else + false; + if (pdf_target) { + // PDF placements live in physical document space, so + // preserve SDL's raw magnitude directly instead of + // quantizing through synthetic wheel buttons/rows. + resetScroll(g); + in.post(.{ .pdf_scroll = .{ + .pane = @intCast(id), + .delta_pixels = -w.y * @as(f32, @floatFromInt(g.cell_h)), + } }); + } else { + g.scroll_pane = id; + g.scroll_col = mc.col; + g.scroll_row = mc.row; + g.scroll_delta = accumulateWheelDelta(g.scroll_delta, w.y); + } } + } else { + // ATTACHED: no pane rect ever reaches this window, so there + // is nothing to slide a fractional row against — the + // session owns the panes and composes what is painted here. + // Accumulate SDL's exact distance (a precision touchpad + // sends fractions of a row) in the same field `stepScroll` + // would have drained, and hand the session the whole rows, + // which is all `Event.mouse` has ever been able to say. + g.scroll_delta = accumulateWheelDelta(g.scroll_delta, w.y); + while (g.scroll_delta >= 1) : (g.scroll_delta -= 1) + in.post(.{ .mouse = .{ .button = .wheel_down, .kind = .press, .col = mc.col, .row = mc.row } }); + while (g.scroll_delta <= -1) : (g.scroll_delta += 1) + in.post(.{ .mouse = .{ .button = .wheel_up, .kind = .press, .col = mc.col, .row = mc.row } }); } } - if (w.x != 0) core.update(.{ .mouse = .{ + if (w.x != 0) in.post(.{ .mouse = .{ .button = if (w.x > 0) .wheel_right else .wheel_left, .kind = .press, .col = mc.col, @@ -2702,14 +3141,14 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { if (mapScenePoint(g, finger.x * win_w, finger.y * win_h, win_w, win_h)) |m| { finger.x = m.x / win_w; finger.y = m.y / win_h; - handleFinger(&g.touch, core, finger, win_w, win_h, fcw, fch, @floatFromInt(g.tagline_width)); + handleFinger(&g.touch, in, finger, win_w, win_h, fcw, fch, @floatFromInt(g.tagline_width)); } else { finger.kind = .cancel; - handleFinger(&g.touch, core, finger, win_w, win_h, fcw, fch, @floatFromInt(g.tagline_width)); - core.update(.pointer_leave); + handleFinger(&g.touch, in, finger, win_w, win_h, fcw, fch, @floatFromInt(g.tagline_width)); + in.post(.pointer_leave); } }, - c.SDL_EVENT_PINCH_BEGIN, c.SDL_EVENT_PINCH_UPDATE => core.update(.{ .pinch = sev.pinch.scale }), + c.SDL_EVENT_PINCH_BEGIN, c.SDL_EVENT_PINCH_UPDATE => in.post(.{ .pinch = sev.pinch.scale }), // ---- steamdeck: first gamepad drives a virtual mouse (buttons here, // axes polled per frame in pollGamepad) ---- c.SDL_EVENT_GAMEPAD_ADDED => { @@ -2730,7 +3169,9 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { c.SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION, c.SDL_EVENT_GAMEPAD_TOUCHPAD_UP, => { - const in: deck.Input = switch (sev.type) { + // `pad_input` and not `in`: this file's `Input` is the event sink + // above, and deck.Input is a controller reading. + const pad_input: deck.Input = switch (sev.type) { c.SDL_EVENT_GAMEPAD_BUTTON_DOWN, c.SDL_EVENT_GAMEPAD_BUTTON_UP => .{ .button = .{ .idx = sev.gbutton.button, .down = sev.gbutton.down, @@ -2752,10 +3193,10 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { } }, }; var acts: [3]deck.Action = undefined; - for (acts[0..g.deck.feed(in, &acts)]) |act| switch (act) { + for (acts[0..g.deck.feed(pad_input, &acts)]) |act| switch (act) { .move => |mv| { const geometry = windowGeometry(g.window); - const old = mouseCellWithGeometry(g, core, g.pad_x, g.pad_y, geometry); + const old = mouseCellWithGeometry(g, in.core, g.pad_x, g.pad_y, geometry); // deck.Action.move is in physical screen pixels. Keep the // stored/warped cursor in SDL window coordinates. const dx = mv.dx * geometry.window_w / geometry.pixel_w; @@ -2766,9 +3207,9 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { // cursor and a hardware mouse are one visible pointer c.SDL_WarpMouseInWindow(g.window, g.pad_x, g.pad_y); g.pointer_present = true; - const mc = mouseCellWithGeometry(g, core, g.pad_x, g.pad_y, geometry) orelse { + const mc = mouseCellWithGeometry(g, in.core, g.pad_x, g.pad_y, geometry) orelse { g.pointer_mapped = false; - core.update(.pointer_leave); + in.post(.pointer_leave); continue; }; g.pointer_mapped = true; @@ -2778,7 +3219,7 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { // moving with a click held drags, so selections stretch // (a firm right-pad press drags-selects like a laptop pad) const held = heldPointerButton(g); - core.update(.{ .mouse = .{ + in.post(.{ .mouse = .{ .button = held orelse .none, .kind = if (held != null) .drag else .motion, .col = mc.col, @@ -2792,23 +3233,23 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { .look => .right, }; g.pointer_present = true; - const mc = mouseCell(g, core, g.pad_x, g.pad_y) orelse { + const mc = mouseCell(g, in.core, g.pad_x, g.pad_y) orelse { g.pointer_mapped = false; // Mirror hardware mouse releases: black CRT margins // are inert for presses, but cannot strand a drag whose // button was pressed over the visible tube. - if (!ck.down) if (g.pointer_cell) |last| core.update(.{ .mouse = .{ + if (!ck.down) if (g.pointer_cell) |last| in.post(.{ .mouse = .{ .button = button, .kind = .release, .col = last.col, .row = last.row, } }); - core.update(.pointer_leave); + in.post(.pointer_leave); continue; }; g.pointer_mapped = true; g.pointer_cell = mc; - core.update(.{ .mouse = .{ + in.post(.{ .mouse = .{ .button = button, .kind = if (ck.down) .press else .release, .col = mc.col, @@ -2817,9 +3258,9 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { }, .wheel => |ticks| { g.pointer_present = true; - const mc = mouseCell(g, core, g.pad_x, g.pad_y) orelse { + const mc = mouseCell(g, in.core, g.pad_x, g.pad_y) orelse { g.pointer_mapped = false; - core.update(.pointer_leave); + in.post(.pointer_leave); continue; }; g.pointer_mapped = true; @@ -2827,7 +3268,7 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { var left = ticks; while (left != 0) { left += if (ticks > 0) -1 else 1; - core.update(.{ .mouse = .{ + in.post(.{ .mouse = .{ .button = if (ticks > 0) .wheel_down else .wheel_up, .kind = .press, .col = mc.col, @@ -2835,14 +3276,23 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { } }); } }, - .key => |k| core.update(.{ + .key => |k| in.post(.{ .key = switch (k) { .n => .{ .cp = 'n', .text = "n" }, .cap_n => .{ .cp = 'N', .text = "N" }, .enter => .{ .cp = pardes.Key.enter }, .tab => .{ .cp = pardes.Key.tab }, - // back paddle: Ctrl- flips tty mode - .tty_toggle => .{ .cp = core.opts.tty_toggle, .ctrl = true }, + // back paddle: flip tty mode. `--tty-toggle` moves the + // ctrl chord and is the SESSION's option, so an + // attached window — which cannot know it and has no + // core to ask — sends the spelling that cannot be + // reconfigured instead: `config.tty_toggle_alt`, which + // pardes.zig honours beside the ctrl chord for exactly + // the hosts that can express it. + .tty_toggle => if (in.core) |core| + .{ .cp = core.opts.tty_toggle, .ctrl = true } + else + .{ .cp = config.tty_toggle_alt[0].cp, .shift = true }, }, }), // a brief gentle ack for execute/look, not a buzz @@ -2857,7 +3307,7 @@ fn dispatch(g: *Gui, core: *pardes.Pardes, sev: *const c.SDL_Event) void { /// Special keys + ctrl/alt shortcuts. Plain printable keys arrive as /// SDL_EVENT_TEXT_INPUT instead (so shift/layout map correctly). -fn keyDown(core: *pardes.Pardes, sym: c.SDL_Keycode, mod: c.SDL_Keymod) void { +fn keyDown(in: *Input, sym: c.SDL_Keycode, mod: c.SDL_Keymod) void { const ctrl = (mod & c.SDL_KMOD_CTRL) != 0; const alt = (mod & c.SDL_KMOD_ALT) != 0; const shift = (mod & c.SDL_KMOD_SHIFT) != 0; @@ -2885,7 +3335,7 @@ fn keyDown(core: *pardes.Pardes, sym: c.SDL_Keycode, mod: c.SDL_Keymod) void { }, }; if (cp == 0) return; - core.update(.{ .key = .{ .cp = cp, .ctrl = ctrl, .alt = alt, .shift = shift } }); + in.post(.{ .key = .{ .cp = cp, .ctrl = ctrl, .alt = alt, .shift = shift } }); } fn pixelCell(px: f32, cell: u32) u16 { @@ -2893,8 +3343,13 @@ fn pixelCell(px: f32, cell: u32) u16 { return @intFromFloat(@min(idx, 10_000)); } +/// Which grid cell a physical point is in. `core` is null in an attached +/// window, and then the pane loop is skipped and the body grid answers — the +/// same fallback `taglineLayoutForCell` takes for the same missing fact, which +/// is what makes a click on an attached tagline land on the glyph it was aimed +/// at. fn gridCellAtDimensions( - core: *const pardes.Pardes, + core: ?*const pardes.Pardes, x: f32, y: f32, body_w: f32, @@ -2908,21 +3363,21 @@ fn gridCellAtDimensions( if (row < pardes.TOPBAR_H) return .{ .col = @intFromFloat(@min(@floor(@max(x, 0) / tag_w), 10_000)), .row = row }; - for (core.panes, 0..) |slot, id| { + if (core) |p| for (p.panes, 0..) |slot, id| { if (slot == null) continue; - const r = core.rects[id]; - const tag_y = if (core.settings.tag_bottom) r.y + r.h -| pardes.BOX_H else r.y; + const r = p.rects[id]; + const tag_y = if (p.settings.tag_bottom) r.y + r.h -| pardes.BOX_H else r.y; if (row != tag_y or r.w == 0) continue; const left = @as(f32, @floatFromInt(r.x)) * safe_body_w; const right = @as(f32, @floatFromInt(r.x + r.w)) * safe_body_w; if (x < left or x >= right) continue; const within: u16 = @intFromFloat(@min(@floor(@max(0, x - left) / tag_w), @as(f32, @floatFromInt(r.w - 1)))); return .{ .col = r.x + within, .row = row }; - } + }; return .{ .col = @intFromFloat(@min(@floor(@max(x, 0) / safe_body_w), 10_000)), .row = row }; } -fn gridCellAtPixels(g: *const Gui, core: *const pardes.Pardes, x: f32, y: f32) MouseCell { +fn gridCellAtPixels(g: *const Gui, core: ?*const pardes.Pardes, x: f32, y: f32) MouseCell { return gridCellAtDimensions( core, x, @@ -2961,7 +3416,7 @@ fn mapScenePoint(g: *const Gui, x: f32, y: f32, w: f32, h: f32) ?crt.Point { return crt.mapScene(x, y, w, h, g.presented_scene.effects, g.presented_scene.time_seconds); } -fn mouseCellWithGeometry(g: *const Gui, core: *const pardes.Pardes, x: f32, y: f32, geometry: WindowGeometry) ?MouseCell { +fn mouseCellWithGeometry(g: *const Gui, core: ?*const pardes.Pardes, x: f32, y: f32, geometry: WindowGeometry) ?MouseCell { const physical = windowPointToPixels(geometry, x, y); const mapped = mapScenePoint(g, physical.x, physical.y, geometry.pixel_w, geometry.pixel_h) orelse return null; return gridCellAtPixels(g, core, mapped.x, mapped.y); @@ -2969,7 +3424,7 @@ fn mouseCellWithGeometry(g: *const Gui, core: *const pardes.Pardes, x: f32, y: f /// SDL window coords → the scene cell displayed at that physical point. /// Mouse, touch and the Deck pointer all share the same CRT/ripple/glitch map. -fn mouseCell(g: *const Gui, core: *const pardes.Pardes, x: f32, y: f32) ?MouseCell { +fn mouseCell(g: *const Gui, core: ?*const pardes.Pardes, x: f32, y: f32) ?MouseCell { return mouseCellWithGeometry(g, core, x, y, windowGeometry(g.window)); } @@ -3009,14 +3464,14 @@ fn refreshPresentedPointer(g: *Gui, core: *pardes.Pardes) void { /// RIGHT stick moves the virtual cursor at ~cell granularity (emits /// button-less motion so hover works), LEFT stick accumulates into wheel /// ticks (both axes: vertical + horizontal) at the cursor position. -fn pollGamepad(g: *Gui, core: *pardes.Pardes) void { +fn pollGamepad(g: *Gui, in: *Input) void { const pad = g.gamepad orelse return; const geometry = windowGeometry(g.window); const deadzone: f32 = 8000; // right stick = pointer const ax: f32 = @floatFromInt(c.SDL_GetGamepadAxis(pad, c.SDL_GAMEPAD_AXIS_RIGHTX)); const ay: f32 = @floatFromInt(c.SDL_GetGamepadAxis(pad, c.SDL_GAMEPAD_AXIS_RIGHTY)); - const old = mouseCellWithGeometry(g, core, g.pad_x, g.pad_y, geometry); + const old = mouseCellWithGeometry(g, in.core, g.pad_x, g.pad_y, geometry); // full tilt ≈ 0.4 cell-heights per frame: a gentle, aimable glide (the // mouse-move sensitivity knob — raise for a faster pointer) const speed: f32 = @as(f32, @floatFromInt(g.cell_h)) * 0.4; @@ -3031,8 +3486,8 @@ fn pollGamepad(g: *Gui, core: *pardes.Pardes) void { g.pointer_present = true; c.SDL_WarpMouseInWindow(g.window, g.pad_x, g.pad_y); } - const mc = mouseCellWithGeometry(g, core, g.pad_x, g.pad_y, geometry) orelse { - if (g.pointer_present and g.pointer_mapped) core.update(.pointer_leave); + const mc = mouseCellWithGeometry(g, in.core, g.pad_x, g.pad_y, geometry) orelse { + if (g.pointer_present and g.pointer_mapped) in.post(.pointer_leave); if (g.pointer_present) g.pointer_mapped = false; return; }; @@ -3041,7 +3496,7 @@ fn pollGamepad(g: *Gui, core: *pardes.Pardes) void { g.pointer_cell = mc; if (old == null or mc.col != old.?.col or mc.row != old.?.row) { const held = heldPointerButton(g); - core.update(.{ .mouse = .{ + in.post(.{ .mouse = .{ .button = held orelse .none, .kind = if (held != null) .drag else .motion, .col = mc.col, @@ -3057,12 +3512,12 @@ fn pollGamepad(g: *Gui, core: *pardes.Pardes) void { while (@abs(g.pad_scroll) >= 1.0) { const button: pardes.Mouse.Button = if (g.pad_scroll < 0) .wheel_up else .wheel_down; g.pad_scroll += if (g.pad_scroll < 0) 1.0 else -1.0; - core.update(.{ .mouse = .{ .button = button, .kind = .press, .col = mc.col, .row = mc.row } }); + in.post(.{ .mouse = .{ .button = button, .kind = .press, .col = mc.col, .row = mc.row } }); } while (@abs(g.pad_scroll_h) >= 1.0) { const button: pardes.Mouse.Button = if (g.pad_scroll_h < 0) .wheel_left else .wheel_right; g.pad_scroll_h += if (g.pad_scroll_h < 0) 1.0 else -1.0; - core.update(.{ .mouse = .{ .button = button, .kind = .press, .col = mc.col, .row = mc.row } }); + in.post(.{ .mouse = .{ .button = button, .kind = .press, .col = mc.col, .row = mc.row } }); } } @@ -3237,19 +3692,23 @@ fn shellOf(ctx: ?*anyopaque) *Shell { fn waitInput(ctx: ?*anyopaque, timeout_ms: u32) void { const s = shellOf(ctx); const core = s.core; + // The one place a local session builds the sink: everything downstream of + // here — `dispatch`, the scripted feed, the sticks — is the same code an + // attached window runs with `client` set instead. + var in: Input = .{ .core = core }; if (s.gui) |g| { var sev = std.mem.zeroes(c.SDL_Event); const ms: c_int = if (timeout_ms != 0) @intCast(timeout_ms) else 16; if (c.SDL_WaitEventTimeout(&sev, ms)) { - dispatch(g, core, &sev); - while (c.SDL_PollEvent(&sev)) dispatch(g, core, &sev); + dispatch(g, &in, &sev); + while (c.SDL_PollEvent(&sev)) dispatch(g, &in, &sev); } } if (s.test_mode) { // A dead scripted feed ends the session HERE, before the inbox, the // sticks and the frame: the pre-pump loop broke at this line, and a // capture written after EOF is a frame no script asked for. - const r = s.feed.pump(s.gpa, core, s.gui) catch { + const r = s.feed.pump(s.gpa, &in, s.gui) catch { core.quit = true; return; }; @@ -3259,7 +3718,7 @@ fn waitInput(ctx: ?*anyopaque, timeout_ms: u32) void { } } s.drainQueue(); - if (s.gui) |g| pollGamepad(g, core); + if (s.gui) |g| pollGamepad(g, &in); } /// Per-frame host bookkeeping with no event of its own, in the order the flat @@ -3307,12 +3766,8 @@ fn pollFrame(ctx: ?*anyopaque) void { pollCwds(core, s.ptys); // Off g.cell_w/h, not the startup metrics: a font change moves them, and // this is the line that would go on dividing by the old cell. - var pw: c_int = 0; - var ph: c_int = 0; - _ = c.SDL_GetWindowSizeInPixels(g.window, &pw, &ph); - const cols: u16 = @intCast(@max(1, @divTrunc(@as(u32, @intCast(@max(pw, 1))), g.cell_w))); - const rows: u16 = @intCast(@max(1, @divTrunc(@as(u32, @intCast(@max(ph, 1))), g.cell_h))); - if (updateCoreResize(core, cols, rows, g.cell_w, g.cell_h)) resetScroll(g); + const geom = windowCells(g); + if (updateCoreResize(core, geom.cols, geom.rows, g.cell_w, g.cell_h)) resetScroll(g); stepScroll(g, core, s.gpa); } @@ -3435,7 +3890,12 @@ fn spawnPane(ctx: ?*anyopaque, pane: u8, cwd: []const u8) void { cwd_buf[cwd.len] = 0; cwd_z = @ptrCast(&cwd_buf); } - const pt = forkShell(s.core, pane, s.prompt_rcs, s.core.shellBin(), cwd_z, s.core.screen_h, s.core.screen_w, s.fs); + // The machine-local half is host_io.zig's, not this file's: the same fork + // the tty shell and the detached daemon do, including the CLOEXEC on the + // master that this copy used to be missing (a master a later shell inherits + // is never closed, so a deleted pane's shell never hangs up). + const child = host_io.forkShell(s.core, pane, s.prompt_rcs, s.core.shellBin(), cwd_z, s.core.screen_h, s.core.screen_w, s.fs); + const pt: Pty = .{ .fd = child.file.handle, .pid = child.pid }; s.ptys[pane] = pt; // report the pane's starting directory back to the core (tags); the slot // needs no occupancy reset, nothing about it is remembered @@ -3446,7 +3906,7 @@ fn spawnPane(ctx: ?*anyopaque, pane: u8, cwd: []const u8) void { fn ptyWrite(ctx: ?*anyopaque, pane: u8, bytes: []const u8) void { const s = shellOf(ctx); - if (s.ptys[pane]) |pt| writeFd(pt.fd, bytes); + if (s.ptys[pane]) |pt| host_io.writeFd(pt.fd, bytes); } fn ptyResize(ctx: ?*anyopaque, pane: u8, cols: u16, rows: u16) void { @@ -3467,21 +3927,9 @@ fn ttyTaken(ctx: ?*anyopaque, pane: u8) bool { return look.ttyTaken(pt.pid, pt.fd); } -fn writeWholeFile(path: []const u8, bytes: []const u8) bool { - var pathbuf: [4096:0]u8 = undefined; - if (path.len >= pathbuf.len) return false; - @memcpy(pathbuf[0..path.len], path); - pathbuf[path.len] = 0; - const fd = libc.open(pathbuf[0..path.len :0], .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, @as(libc.mode_t, 0o644)); - if (fd < 0) return false; - writeFd(fd, bytes); - _ = libc.close(fd); - return true; -} - fn writeFile(ctx: ?*anyopaque, pane: u8, path: []const u8, bytes: []const u8) void { const s = shellOf(ctx); - if (!writeWholeFile(path, bytes)) return; + if (!host_io.writeFileBytes(path, bytes)) return; // our own write is about to come back as a watch event: restamp from the // bytes we just put there so it reads as "no change". Only for the pane's // OWN file — a `Put` elsewhere is a change like any other. @@ -3502,7 +3950,7 @@ fn writeDump(ctx: ?*anyopaque, bytes: []const u8) void { const s = shellOf(ctx); var pbuf: [1024:0]u8 = undefined; const path = pardes.dump.outPath(&pbuf) orelse return; - if (!writeWholeFile(path, bytes)) return; + if (!host_io.writeFileBytes(path, bytes)) return; s.core.setLastDump(path); } @@ -3538,12 +3986,35 @@ fn dumpThemes(ctx: ?*anyopaque, pane: u8) void { s.core.setMessage(pane, message.stamp(&mbuf, "dumped themes", out_dir)); } +/// Put `text` on THIS display's clipboard. The one place that copy happens: +/// SDL wants a sentinel-terminated string and a run of core cells is not one. +/// Shared, because the in-process host and an attached window answering a +/// `set_clipboard` off the wire are the same desktop action. +fn putClipboard(gpa: std.mem.Allocator, text: []const u8) void { + const z = gpa.dupeZ(u8, text) catch return; + defer gpa.free(z); + _ = c.SDL_SetClipboardText(z.ptr); +} + +/// THIS display's clipboard, or null when it holds nothing. SDL3 hands over an +/// OWNED copy that is the caller's to `SDL_free`, and reports "no text" as an +/// EMPTY string rather than null — so the length check is what actually +/// rejects a miss. Shared with the attached loop for the `putClipboard` +/// reason, turned round. +fn takeClipboard() ?[:0]u8 { + const raw = c.SDL_GetClipboardText() orelse return null; + const text = std.mem.span(raw); + if (text.len == 0) { + c.SDL_free(raw); + return null; + } + return text; +} + fn setClipboard(ctx: ?*anyopaque, text: []const u8) void { const s = shellOf(ctx); if (s.gui == null) return; - const z = s.gpa.dupeZ(u8, text) catch return; - defer s.gpa.free(z); - _ = c.SDL_SetClipboardText(z.ptr); + putClipboard(s.gpa, text); } fn readClipboard(ctx: ?*anyopaque) void { @@ -3551,13 +4022,8 @@ fn readClipboard(ctx: ?*anyopaque) void { if (s.gui == null) return; // SDL answers synchronously, so the paste the core is waiting on lands // inside this same drain — nothing to remember, no reply path to plumb. - // SDL3 hands over an OWNED copy that is ours to SDL_free, and reports "no - // text" as an EMPTY string rather than null, so the length check is what - // actually rejects a miss. - const raw = c.SDL_GetClipboardText() orelse return; - defer c.SDL_free(raw); - const text = std.mem.span(raw); - if (text.len == 0) return; + const text = takeClipboard() orelse return; + defer c.SDL_free(text.ptr); s.core.update(.{ .paste = text }); } @@ -3576,27 +4042,6 @@ fn pipe(ctx: ?*anyopaque, id: u32) void { if (s.threads_ok) spawnPipe(s.core, s.io, s.gpa, s.queue, s.pipe_tasks, id); } -fn forkShell(core: *pardes.Pardes, pane: usize, prompt_rcs: *const shell_bin.PromptRcs, bin: []const u8, cwd: ?[*:0]const u8, rows: u16, cols: u16, fs: ?*const fuse.Fs) Pty { - var master: c_int = undefined; - // resolved BEFORE the fork, into this frame, which the child inherits: - // nothing between fork and exec may allocate, and a PATH search would - var path_buf: [std.fs.max_path_bytes]u8 = undefined; - const spawn = shell_bin.resolve(bin, &path_buf, prompt_rcs); - // ...and so is the pane's own address on the control filesystem: acme puts - // `winid` in the child, which is safe there only because rfork(RFENVG) has - // just given it a private environment group. See fs_service.exportPaneEnv. - fs_service.exportPaneEnv(fs, if (core.panes[pane]) |pn| pn.serial else 0); - const ws = posix.winsize{ .row = rows, .col = cols, .xpixel = 0, .ypixel = 0 }; - const pid = forkpty(&master, null, null, &ws); - if (pid == 0) { - if (cwd) |cd| _ = chdir(cd); - _ = execv(spawn.path, &spawn.argv); - _exit(127); - } - if (pid > 0) core.acknowledgeShell(pane, std.mem.span(spawn.path), spawn.argv[1] != null); - return .{ .fd = master, .pid = pid }; -} - /// Live cwd for tags/look: a cheap per-pane process lookup, polled every frame /// because a tagline draws it. Whether a pane's tty still belongs to the prompt /// pardes forked is deliberately NOT polled with it — see `ttyTaken`. @@ -3817,7 +4262,7 @@ fn emitScrollRows(g: *Gui, instances: [*]CellInstance, base: u32, surface: *pard var col = x0; while (col < x0 + bw) : (col += 1) { const sidx: u32 = @as(u32, row) * surface.cols + col; - emitInstance(g, instances, base + n, col, row, shifted, win_w, win_h, null, surface.at(col, row), false, sidx == cursor_idx and !cursor_bar, page); + emitInstance(g, instances, base + n, col, row, shifted, win_w, win_h, null, cellFontRole(surface.at(col, row)), surface.at(col, row), false, sidx == cursor_idx and !cursor_bar, page); n += 1; } } @@ -3827,7 +4272,7 @@ fn emitScrollRows(g: *Gui, instances: [*]CellInstance, base: u32, surface: *pard const erow: u16 = if (g.scroll_lag > 0) y0 + bh else y0 - 1; var i: u16 = 0; while (i < bw) : (i += 1) { - emitInstance(g, instances, base + n, x0 + i, erow, shifted, win_w, win_h, null, &g.scroll_edge[i], false, false, page); + emitInstance(g, instances, base + n, x0 + i, erow, shifted, win_w, win_h, null, cellFontRole(&g.scroll_edge[i]), &g.scroll_edge[i], false, false, page); n += 1; } } @@ -4318,7 +4763,7 @@ fn drawNativeImagesGpu( fn renderFrame( g: *Gui, gpa: std.mem.Allocator, - core: *pardes.Pardes, + core: ?*const pardes.Pardes, surface: *pardes.Surface, theme_bg: ?[3]u8, topbar_pane_border_rgb: [3]u8, @@ -4458,9 +4903,9 @@ fn renderFrame( const data_diff = data_effect and surface.panelCellChanged(col, row); const logical_idx: u32 = @as(u32, row) * surface.cols + col; const instance_count = if (data_diff) - cellInstanceCount(&surface.previous_cells[logical_idx]) + cellInstanceCount(surface.at(col, row)) + cellInstanceCount(core, &surface.previous_cells[logical_idx], row) + cellInstanceCount(core, surface.at(col, row), row) else - cellInstanceCount(surface.at(col, row)); + cellInstanceCount(core, surface.at(col, row), row); paint_plan.batches[batch_index].cell_count = std.math.add( u32, paint_plan.batches[batch_index].cell_count, @@ -4485,7 +4930,7 @@ fn renderFrame( destination.cell_count = std.math.add( u32, destination.cell_count, - cellInstanceCount(&surface.previous_cells[logical_idx]), + cellInstanceCount(core, &surface.previous_cells[logical_idx], row), ) catch return error.GpuCapacity; } } @@ -4779,7 +5224,7 @@ test "tagline bands face the topbar rule and Tagbottom faces the window edge" { try std.testing.expectEqual(@as(u32, 4), taglineBandOffset(9, canvas_h, cell_h, tagline_h)); } -fn resolveCell(g: *Gui, cell: *const pardes.Cell, is_cursor: bool, page: [3]u8) ResolvedCell { +fn resolveCell(g: *Gui, cell: *const pardes.Cell, role: pardes.FontRole, is_cursor: bool, page: [3]u8) ResolvedCell { var fg = fg_default; var bg = page; var reverse = is_cursor; @@ -4803,10 +5248,6 @@ fn resolveCell(g: *Gui, cell: *const pardes.Cell, is_cursor: bool, page: [3]u8) } if (reverse) std.mem.swap([3]u8, &fg, &bg); - const role: pardes.FontRole = if (cell.default) - .body - else - cell.style.font_role; const cp = cellCodepoint(cell); return .{ .slot = if (cp == ' ') g.space_slot else ensureGlyph(g, cp, role), @@ -4816,12 +5257,43 @@ fn resolveCell(g: *Gui, cell: *const pardes.Cell, is_cursor: bool, page: [3]u8) }; } +/// What the CORE said this cell's face is. fn cellFontRole(cell: *const pardes.Cell) pardes.FontRole { return if (cell.default) .body else cell.style.font_role; } -fn cellInstanceCount(cell: *const pardes.Cell) u32 { - return if (cellFontRole(cell) == .tagline) 2 else 1; +/// ...and the face it is actually DRAWN in, which differs in exactly one case +/// and that case is the whole of what an attached window renders differently. +/// +/// A compact tagline band is anchored at its pane's LEFT EDGE — that is what +/// `compactTaglineLayout`'s `origin_col` is — and a pane's left edge is a pane +/// RECT, which this wire does not carry (it carries cells, not the layout that +/// placed them). Anchoring per cell instead is not a near-miss, it is a picket +/// fence: `x_off = col * (body_w - tag_w)` puts every cell back on BODY pitch +/// while the quad stays `tag_w` wide, so the chrome band shows through between +/// every pair of cells and the text tracks visibly loose. Widening the quad +/// does not close it either — `emitInstance` samples exactly `tagline_width` +/// atlas texels for a tagline cell, so a wider quad stretches the glyph. +/// +/// So a pane tag row with no core to ask goes on the BODY grid, face and all: +/// one quad per cell at body pitch and body size, tiling exactly and tracking +/// exactly. The visible difference from a local window is that those rows wear +/// the body face rather than the 82% one, and that is the price of the pane +/// rects not being on the wire. It is also the grid `gridCellAtDimensions` +/// already hit-tests an attached tag row against, so a click still lands on the +/// glyph it was aimed at. +/// +/// ROW ZERO is exempt, and that exemption is why the topbar was never striped: +/// its origin is not a pane rect but column zero, always, so its compact band +/// is right with or without a core. +fn drawnFontRole(core: ?*const pardes.Pardes, cell: *const pardes.Cell, row: u16) pardes.FontRole { + const role = cellFontRole(cell); + if (role != .tagline or core != null or row < pardes.TOPBAR_H) return role; + return .body; +} + +fn cellInstanceCount(core: ?*const pardes.Pardes, cell: *const pardes.Cell, row: u16) u32 { + return if (drawnFontRole(core, cell, row) == .tagline) 2 else 1; } /// A tagline cell has two quads. The first preserves the pane-wide chrome @@ -4830,7 +5302,7 @@ fn cellInstanceCount(cell: *const pardes.Cell) u32 { /// the compact cell never reaches the next body's cell origin. fn emitSurfaceCell( g: *Gui, - core: *const pardes.Pardes, + core: ?*const pardes.Pardes, instances: [*]CellInstance, next: *u32, col: u16, @@ -4845,15 +5317,16 @@ fn emitSurfaceCell( is_cursor: bool, page: [3]u8, ) void { - if (cellFontRole(cell) == .tagline) { - emitInstance(g, instances, next.*, col, row, body_layout, win_w, win_h, track, tagline_base, old_layer, false, page); + const role = drawnFontRole(core, cell, row); + if (role == .tagline) { + emitInstance(g, instances, next.*, col, row, body_layout, win_w, win_h, track, .tagline, tagline_base, old_layer, false, page); next.* += 1; const tag_layout = taglineLayoutForCell(g, core, col, row, track); - emitInstance(g, instances, next.*, col, row, tag_layout, win_w, win_h, track, cell, old_layer, is_cursor, page); + emitInstance(g, instances, next.*, col, row, tag_layout, win_w, win_h, track, .tagline, cell, old_layer, is_cursor, page); next.* += 1; return; } - emitInstance(g, instances, next.*, col, row, body_layout, win_w, win_h, track, cell, old_layer, is_cursor, page); + emitInstance(g, instances, next.*, col, row, body_layout, win_w, win_h, track, role, cell, old_layer, is_cursor, page); next.* += 1; } @@ -4867,6 +5340,11 @@ fn emitInstance( win_w: f32, win_h: f32, track: ?pardes.panel_animation.Track, + /// The face this quad draws in, decided once per cell by `drawnFontRole` + /// rather than re-derived here: an attached window demotes a pane tag row + /// to the body face, and the quad geometry, the atlas slot and the uv span + /// all have to agree about that in one place. + role: pardes.FontRole, cell: *const pardes.Cell, /// Old and new data layers carry their own quad geometry. The shader /// discards exactly one at every reveal state, so a body/tagline role @@ -4876,7 +5354,7 @@ fn emitInstance( /// the ground this frame: what a default background resolves to page: [3]u8, ) void { - const resolved = resolveCell(g, cell, is_cursor, page); + const resolved = resolveCell(g, cell, role, is_cursor, page); // Cell pixel rect (top-left origin) → NDC (y up). const px0 = layout.x_off + @as(f32, @floatFromInt(col)) * layout.w; @@ -5001,7 +5479,7 @@ fn firstCp(s: []const u8) u32 { /// the new cell size is a different grid over the same 2048², and a leftover /// bitmap no slot points at any more would still be sampled by whatever new /// slot overlaps it. -fn refitFont(g: *Gui, core: *pardes.Pardes) void { +fn refitFont(g: *Gui, core: ?*pardes.Pardes) void { g.scale = c.ui_font_scale_for_height(g.font, g.px); var cw: c_int = 10; var chh: c_int = 20; @@ -5022,13 +5500,13 @@ fn refitFont(g: *Gui, core: *pardes.Pardes) void { // shells re-derive this every frame anyway, so this is only the frame the // change happens on — but it is the frame the surface is about to be // rendered for, and a stale screen_w here is a row of cells drawn off the - // right edge of the window. - var pw: c_int = 0; - var ph: c_int = 0; - _ = c.SDL_GetWindowSizeInPixels(g.window, &pw, &ph); - const cols: u16 = @intCast(@max(1, @divTrunc(@as(u32, @intCast(@max(pw, 1))), g.cell_w))); - const rows: u16 = @intCast(@max(1, @divTrunc(@as(u32, @intCast(@max(ph, 1))), g.cell_h))); - _ = updateCoreResize(core, cols, rows, g.cell_w, g.cell_h); + // right edge of the window. An attached window has no core to tell: its + // loop compares `windowCells` against the last geometry it sent and puts a + // resize on the wire from there. + if (core) |p| { + const geom = windowCells(g); + _ = updateCoreResize(p, geom.cols, geom.rows, g.cell_w, g.cell_h); + } // ...and a fractional scroll is measured in the OLD grid: scroll_rect // is a rect of the pane the last frame drew, and scroll_edge is a saved row @@ -5485,7 +5963,7 @@ fn writeCapturePpm(g: *Gui, gpa: std.mem.Allocator, pixels: []const u8, width: u var header: [64]u8 = undefined; const hdr = std.fmt.bufPrint(&header, "P6\n{d} {d}\n255\n", .{ width, height }) catch return error.CaptureWriteFailed; - writeFd(fd, hdr); + host_io.writeFd(fd, hdr); const row_rgb = try gpa.alloc(u8, @as(usize, width) * 3); defer gpa.free(row_rgb); @@ -5498,7 +5976,7 @@ fn writeCapturePpm(g: *Gui, gpa: std.mem.Allocator, pixels: []const u8, width: u row_rgb[di + 1] = src[si + 1]; row_rgb[di + 2] = src[si + if (bgr) @as(usize, 0) else 2]; } - writeFd(fd, row_rgb); + host_io.writeFd(fd, row_rgb); } if (libc.rename(tmp_path, final_path) != 0) return error.CaptureWriteFailed; } @@ -5537,7 +6015,7 @@ fn addCursorBar( fn buildOverlay( g: *Gui, - core: *const pardes.Pardes, + core: ?*const pardes.Pardes, surface: *const pardes.Surface, layout: CellLayout, sw: u32, @@ -5578,7 +6056,7 @@ fn buildOverlay( if (surface.cursor) |cursor| { if (cursor.x < surface.cols and cursor.y < surface.rows) { const cell = surface.cells[@as(usize, cursor.y) * surface.cols + cursor.x]; - const role = cell.style.font_role; + const role = drawnFontRole(core, &cell, cursor.y); const visual_height: f32 = if (role == .tagline) @floatFromInt(g.tagline_height) else @@ -5868,15 +6346,3 @@ fn envU16(env: *std.process.Environ.Map, name: []const u8) ?u16 { const raw = env.get(name) orelse return null; return std.fmt.parseInt(u16, raw, 10) catch null; } - -fn writeFd(fd: c_int, data: []const u8) void { - var off: usize = 0; - while (off < data.len) { - const n = libc.write(fd, data[off..].ptr, data.len - off); - if (n < 0) { - if (libc.errno(n) == .INTR) continue; - return; - } - off += @intCast(n); - } -} diff --git a/src/host.zig b/src/host.zig index 9ca882b1..b6ccff34 100644 --- a/src/host.zig +++ b/src/host.zig @@ -66,6 +66,16 @@ pub const Host = struct { /// capability handshake landing, gamepad state. push_poll_frame: ?*const fn (ctx: ?*anyopaque) void = null, + // ---- this frontend's own membership ---- + /// `Detach` — leave the session, which carries on for everybody else. + /// Only a DETACHED core's host implements it, and the null case is the + /// point rather than an oversight: a local tty or SDL shell has no + /// session to leave, so the core reports that on the pane's row (see + /// `perform`) instead of quietly quitting something. Argumentless like + /// the two frame pushes above, because the host serving it already + /// knows whose keystroke arrived — it is the one that delivered it. + push_detach: ?*const fn (ctx: ?*anyopaque) void = null, + // ---- pseudo-terminals ---- push_spawn: ?*const fn (ctx: ?*anyopaque, pane: u8, cwd: []const u8) void = null, push_pty_write: ?*const fn (ctx: ?*anyopaque, pane: u8, bytes: []const u8) void = null, diff --git a/src/host_io.zig b/src/host_io.zig new file mode 100644 index 00000000..001446b1 --- /dev/null +++ b/src/host_io.zig @@ -0,0 +1,178 @@ +//! THE MACHINE-LOCAL HALF OF A HOST: fork a pane's shell, put bytes on a disk. +//! +//! `host.zig` is the seam — the struct of function pointers the core asks +//! through. This file is the part of the answer that is the same on every host +//! that has an operating system under it, and it is now the ONLY copy of it: +//! tty.zig, detached/server.zig, gui/gui.zig and macos.zig all fork and write +//! through here. They did not always. Each of the four grew its own `forkShell` +//! and its own `writeFd`, and what those four copies were for is best said by +//! what they had in common: ALL FOUR were missing FD_CLOEXEC on the pty master, +//! so in every shell pardes has ever shipped a program in one pane could read +//! and write another pane's terminal, and closing a master did not reliably hang +//! its shell up. One line below fixes that for all four at once (see `forkShell`) +//! — which is a better argument for this file existing than "it is shared" is. +//! +//! Why the daemon and not the frontend does this work: a unix socket means the +//! core and its frontends are on the SAME machine, so there is no question of +//! whose disk or whose process table is meant. Given that, the pane shells +//! belong to the long-lived process, because the whole promise of a detached +//! session is that it outlives the frontend attached to it — a shell forked by +//! a frontend dies with that frontend, and then the session has a pane with no +//! shell in it. The frontend keeps exactly what needs the human's screen: the +//! grid, the keyboard, the clipboard and a link to open. +//! +//! So `forkShell` takes the core it is forking on behalf of and nothing about +//! terminals: no vaxis, no `Loop`, no reader thread. Who drains the master fd +//! is the caller's business, and the callers answer differently on purpose. The +//! tty, gui and macOS shells hand it to a worker that posts into their event +//! loop; the daemon adds it to the one `poll(2)` it already runs over its +//! clients, and makes its own copy non-blocking in order to. That last is why +//! `Child.file.flags` is left saying what it says: the flag describes the +//! descriptor `forkpty` handed back, for the three callers that stream it, and +//! the one that polls it keeps only the handle. +const std = @import("std"); +const posix = std.posix; +const libc = std.c; +const pardes = @import("pardes.zig"); +const shell_bin = @import("shell_bin.zig"); +const fs_service = @import("fs_service.zig"); +const fuse = @import("fuse.zig"); + +/// `setCloexec` and nothing else. Imported rather than copied a fourth time — +/// fuse.zig and nested.zig each grew a private two-line version of it — because +/// the descriptor this file has to protect is the one every OTHER file in the +/// tree already protects, and one predicate is how the reasoning stays in one +/// place. nested.zig is a leaf (std, builtin, libc), so this costs no +/// dependency worth the name. +const nested = @import("nested.zig"); + +extern "c" fn forkpty(amaster: *c_int, name: ?[*:0]u8, termp: ?*const anyopaque, winp: ?*const posix.winsize) c_int; +extern "c" fn execv(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8) c_int; +extern "c" fn chdir(path: [*:0]const u8) c_int; +extern "c" fn _exit(status: c_int) noreturn; + +/// A forked pane shell: the pty master to read and write, and the pid to reap. +/// Named rather than anonymous because four files now hold one of these. +pub const Child = struct { + file: std.Io.File, + pid: posix.pid_t, +}; + +/// Fork a shell onto a fresh pty for `pane`, sized `rows`x`cols`. +/// +/// `core` is optional because a host may fork before it has one, and a core +/// that is absent simply does not name its shell. +pub fn forkShell( + core: ?*pardes.Pardes, + pane: usize, + prompt_rcs: *const shell_bin.PromptRcs, + bin: []const u8, + cwd: ?[*:0]const u8, + rows: u16, + cols: u16, + fs: ?*const fuse.Fs, +) Child { + var master: c_int = undefined; + // resolved BEFORE the fork, into this frame, which the child inherits: + // nothing between fork and exec may allocate, and a PATH search would + var path_buf: [std.fs.max_path_bytes]u8 = undefined; + const spawn = shell_bin.resolve(bin, &path_buf, prompt_rcs); + // ...and so is the pane's own address on the control filesystem, for a + // second reason on top of that one: acme puts `winid` in the child, which + // is safe there only because rfork(RFENVG) has just given it a private + // environment group. See fs_service.exportPaneEnv. + fs_service.exportPaneEnv(fs, if (core) |c| (if (c.panes[pane]) |pn| pn.serial else 0) else 0); + const ws = posix.winsize{ .row = rows, .col = cols, .xpixel = 0, .ypixel = 0 }; + const pid = forkpty(&master, null, null, &ws); + if (pid == 0) { + // the blocked-SIGWINCH mask survives fork AND exec — unblock it or + // bash/vim in the pane would never see resizes (sigprocmask is + // async-signal-safe) + var set = posix.sigemptyset(); + posix.sigaddset(&set, posix.SIG.WINCH); + posix.sigprocmask(posix.SIG.UNBLOCK, &set, null); + if (cwd) |c| _ = chdir(c); + _ = execv(spawn.path, &spawn.argv); + _exit(127); + } + if (pid > 0) { + // CLOEXEC ON THE MASTER, and it belongs here rather than at either + // caller because `forkpty` is what opens it: /dev/ptmx is opened with no + // O_CLOEXEC and there is no flag argument to ask for one. Without this, + // every pane shell forked AFTER this one inherits this master and keeps + // it across `execv`, which is two bugs at once. + // + // The loud one: a program running in pane 3 can read pane 0's output and + // write bytes into pane 0's screen. + // + // The silent one, and the reason it compounds: closing a master is the + // only thing that hangs its shell up, and a master a later shell still + // holds open is not closed. detached/server.zig `closePty` and tty.zig + // `spawn` both depend on that hangup, so a pane delete or a respawn left + // an orphaned shell that never exits — never reaped, eventually blocked + // writing into a pty nobody reads — and each orphan pinned every earlier + // pane's master in turn. The startup drain forks pane 0 and then pane 1, + // so the arrangement existed from boot, and it existed in all four + // copies of this function before they became this one. nested.zig and + // fuse.zig say the same thing about their own descriptors ("pane shells + // are forked with forkpty and inherit everything open"); the master was + // the one descriptor in the tree that nobody had said it to. + // + // THE WINDOW THIS LEAVES, stated rather than papered over: fcntl after + // fork is not atomic, so a thread that forks and execs between these two + // syscalls inherits the master anyway. In the detached daemon there is no + // such thread — it is single-threaded by construction, which is what + // putting the pty masters in its own `poll(2)` bought. The shells with + // worker threads that can exec — tty.zig's pipe tasks above all — have a + // window two syscalls wide, and closing it means replacing `forkpty` with + // our own `posix_openpt(O_CLOEXEC)` / `grantpt` / `unlockpt` / fork / + // `setsid`, which is a different change to a different file. + nested.setCloexec(master); + if (core) |c| c.acknowledgeShell(pane, std.mem.span(spawn.path), spawn.argv[1] != null); + } + return .{ .file = .{ .handle = master, .flags = .{ .nonblocking = false } }, .pid = pid }; +} + +/// Truncate-or-create `path` and put `bytes` there. False on any failure, and +/// the caller reports it: a save that did not happen must not be announced as +/// one. +pub fn writeFileBytes(path: []const u8, bytes: []const u8) bool { + var pathbuf: [4096:0]u8 = undefined; + if (path.len >= pathbuf.len) return false; + @memcpy(pathbuf[0..path.len], path); + pathbuf[path.len] = 0; + const fd = libc.open(pathbuf[0..path.len :0], .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, @as(libc.mode_t, 0o644)); + if (fd < 0) return false; + writeFd(fd, bytes); + _ = libc.close(fd); + return true; +} + +/// A whole-buffer write that finishes short writes, retries EINTR, and refuses +/// to loop on no progress. +/// +/// The zero guard is not bookkeeping: without it a `write(2)` that returns 0 for +/// a nonzero count is an infinite SPIN, because 0 is neither an error nor +/// progress and `off` never moves. macos.zig's copy carried the guard and its +/// reason all along — "a zero-byte write makes no progress; looping on it would +/// spin the main thread forever" — and the tty copy this file was extracted +/// from did not, so the extraction briefly promoted the weakest of the three to +/// being the shared one. All three are now this one: gui.zig and macos.zig were +/// migrated onto it, so the guard is no longer missing anywhere. +/// +/// A spin is strictly worse than the block it replaces, which is why this +/// matters more now that detached/server.zig reaches this file from a +/// single-threaded poll loop: a blocked `write` is one syscall a signal can +/// interrupt, and a spin is 100% of a core with the whole session behind it. +pub fn writeFd(fd: c_int, data: []const u8) void { + var off: usize = 0; + while (off < data.len) { + const n = libc.write(fd, data[off..].ptr, data.len - off); + if (n < 0) { + if (libc.errno(n) == .INTR) continue; + return; + } + if (n == 0) return; + off += @intCast(n); + } +} diff --git a/src/limits.zig b/src/limits.zig index 965f4d0a..75bf5262 100644 --- a/src/limits.zig +++ b/src/limits.zig @@ -77,6 +77,21 @@ pub const board_heap_bytes = 384 * KiB; /// `emitWrite`, i.e. only if a pty ever appears on this platform. pub const effect_cap = if (board) 128 else 4096; +/// Bytes of a pane's pty write that may WAIT in the core when `effect_cap` +/// chunks are already queued. `emitWrite` splits a burst into fixed 64-byte +/// effects, so without this a paste larger than `effect_cap * 64` (256 KiB on +/// a desktop) lost its tail silently — the ring refuses rather than evicts, +/// which keeps queued bytes in order but cut the new ones off. The remainder +/// parks here instead and `nextEffect` refills the ring as the host drains it, +/// so a large paste is DELAYED rather than truncated. +/// +/// 4 MiB matches `tty.max_paste_bytes`, the largest burst a host can hand the +/// core in one event, so the bound is the one the producer already enforces. +/// Zero on the board: no ptys means no `emitWrite`, and the allocation this +/// would justify cannot live in 384 KiB anyway. A zero cap parks nothing and +/// restores the old refusal exactly. +pub const pending_write_cap: usize = if (board) 0 else 4 << 20; + /// Rows the per-pane soft-wrap map covers. `wrapWidth` refuses to wrap a pane /// taller than this (it reads the array's own length), so shrinking it cannot /// truncate a map — a taller pane renders unwrapped, exactly as documented on diff --git a/src/macos.zig b/src/macos.zig index e54e013c..2b04237e 100644 --- a/src/macos.zig +++ b/src/macos.zig @@ -35,11 +35,8 @@ const file_watch = @import("file_watch.zig"); /// attachments, so nothing here is analysed. const image = if (pardes.pdf_enabled) @import("image.zig") else struct {}; const user_config = @import("user_config.zig"); +const host_io = @import("host_io.zig"); -extern "c" fn forkpty(amaster: *c_int, name: ?[*:0]u8, termp: ?*const anyopaque, winp: ?*const posix.winsize) c_int; -extern "c" fn execv(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8) c_int; -extern "c" fn chdir(path: [*:0]const u8) c_int; -extern "c" fn _exit(status: c_int) noreturn; extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int; /// Implemented by FileWatcher.swift in the app and e2e host. Zig-only unit @@ -1807,7 +1804,7 @@ fn spawnShell(ctx: ?*anyopaque, pane: u8, cwd: []const u8) void { cwd_buf[cwd.len] = 0; cwd_z = @ptrCast(&cwd_buf); } - const child = forkShell(core, pane, &st.prompt_rcs, core.shellBin(), cwd_z, core.screen_h, core.screen_w); + const child = host_io.forkShell(core, pane, &st.prompt_rcs, core.shellBin(), cwd_z, core.screen_h, core.screen_w, null); st.ptys[pane] = .{ .file = child.file, .pid = child.pid, @@ -1823,7 +1820,7 @@ fn spawnShell(ctx: ?*anyopaque, pane: u8, cwd: []const u8) void { fn ptyWrite(ctx: ?*anyopaque, pane: u8, bytes: []const u8) void { const st = hostState(ctx); - if (st.ptys[pane]) |pt| writeFd(pt.file.handle, bytes); + if (st.ptys[pane]) |pt| host_io.writeFd(pt.file.handle, bytes); } fn ptyResize(ctx: ?*anyopaque, pane: u8, cols: u16, rows: u16) void { @@ -1848,14 +1845,7 @@ fn ttyTaken(ctx: ?*anyopaque, pane: u8) bool { /// resolved which path and which bytes. fn writeFile(ctx: ?*anyopaque, pane: u8, path: []const u8, bytes: []const u8) void { const st = hostState(ctx); - var pathbuf: [4096:0]u8 = undefined; - if (path.len >= pathbuf.len) return; - @memcpy(pathbuf[0..path.len], path); - pathbuf[path.len] = 0; - const fd = libc.open(pathbuf[0..path.len :0], .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, @as(libc.mode_t, 0o644)); - if (fd < 0) return; - writeFd(fd, bytes); - _ = libc.close(fd); + if (!host_io.writeFileBytes(path, bytes)) return; // The directory source will observe our own close. Move its baseline first // so that notification is a hash no-op instead of manufacturing an external // reload and undo boundary. @@ -1870,10 +1860,7 @@ fn writeDump(ctx: ?*anyopaque, bytes: []const u8) void { const st = hostState(ctx); var pbuf: [1024:0]u8 = undefined; const path = pardes.dump.outPath(&pbuf) orelse return; - const fd = libc.open(path, .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, @as(libc.mode_t, 0o644)); - if (fd < 0) return; - writeFd(fd, bytes); - _ = libc.close(fd); + if (!host_io.writeFileBytes(path, bytes)) return; st.core.setLastDump(path); } @@ -1988,40 +1975,6 @@ fn wake(st: *State) void { // ---------------------------------------------------------------- helpers -fn forkShell(core: *pardes.Pardes, pane: usize, prompt_rcs: *const shell_bin.PromptRcs, bin: []const u8, cwd: ?[*:0]const u8, rows: u16, cols: u16) struct { file: std.Io.File, pid: posix.pid_t } { - var master: c_int = undefined; - // Resolved BEFORE the fork, into this frame, which the child inherits: - // nothing between fork and exec may allocate, so a PATH search cannot - // happen there. - var path_buf: [std.fs.max_path_bytes]u8 = undefined; - const spawn = shell_bin.resolve(bin, &path_buf, prompt_rcs); - const ws = posix.winsize{ .row = rows, .col = cols, .xpixel = 0, .ypixel = 0 }; - const pid = forkpty(&master, null, null, &ws); - if (pid == 0) { - if (cwd) |c| _ = chdir(c); - _ = execv(spawn.path, &spawn.argv); - _exit(127); - } - if (pid > 0) core.acknowledgeShell(pane, std.mem.span(spawn.path), spawn.argv[1] != null); - return .{ .file = .{ .handle = master, .flags = .{ .nonblocking = false } }, .pid = pid }; -} - -fn writeFd(fd: c_int, data: []const u8) void { - var off: usize = 0; - while (off < data.len) { - const n = libc.write(fd, data[off..].ptr, data.len - off); - if (n < 0) { - if (libc.errno(n) == .INTR) continue; - return; - } - // A zero-byte write makes no progress; looping on it would spin the - // main thread forever, which here means a beachball rather than the - // tty shell's hung terminal. - if (n == 0) return; - off += @intCast(n); - } -} - /// Rebuild the process environment as a Map, because a library never sees the /// std.process.Init that main() gets one from. Only the config-path lookup /// reads it, and the arena owns the copies for the life of the process. diff --git a/src/main.zig b/src/main.zig index 08beae86..b8cb6b2d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -93,15 +93,30 @@ const help_text = \\ a frontend can say which session it wants. One \\ path component: no '/' and nothing empty \\ --attach become a frontend of the one detached session - \\ that is running: draw its screen, send it input, - \\ fork its pane shells. Several frontends may be - \\ attached at once and all see the same screen + \\ that is running: draw its screen and send it + \\ input, and nothing else — the session owns its + \\ pane shells, its files and its watches. Works in + \\ both the terminal and the SDL window build. + \\ Several frontends may be attached at once and all + \\ see the same screen \\ --attach= ...of the session called , which is what to \\ use when more than one is running \\ -h, --help show this help and exit + \\ --version print the version and exit \\ ; +/// Built at COMPTIME, because both halves are: `version` comes out of +/// `build.zig.zon` through the options module and `commit` out of `git` at +/// configure time, so there is nothing here to format at runtime and no buffer +/// to size. The commit is in parentheses when there is one and absent +/// otherwise — a build from a tarball says `pardes 0.0.1` and is not lying +/// about a revision it never had. See `pardes.version`/`pardes.commit`. +const version_text = if (pardes.commit) |c| + "pardes " ++ pardes.version ++ " (" ++ c ++ ")\n" +else + "pardes " ++ pardes.version ++ "\n"; + const nested_text = \\pardes: this shell is already inside pardes, and a pardes inside a pardes \\is spicy. Name a file or a directory and the outer session opens it, or @@ -129,9 +144,10 @@ fn emscriptenMain(argc: c_int, argv: [*]?[*:0]u8) callconv(.c) c_int { return 0; } -// no argv in the browser: options stay default, state comes from the dump +// no argv in the browser: options stay default, state comes from the dump, and +// there is no session to attach to — a browser tab has no unix socket. fn webMain() !void { - try @import("gui/gui.zig").run(.{}); + try @import("gui/gui.zig").run(.{}, .{}, null); } fn nativeMain(init: std.process.Init) !void { @@ -159,8 +175,8 @@ fn nativeMain(init: std.process.Init) !void { // free to mean "the default name" the way opts.fs uses it for a directory. var detach: ?[]const u8 = null; // ...and `--attach[=]`, the same shape: null when it was not given, - // and the empty string means "the one session there is" (tty.zig - // `sessionName`) rather than a session with no name. + // and the empty string means "the one session there is" + // (detached_client.resolve) rather than a session with no name. var attach: ?[]const u8 = null; // Kept RAW until every flag is parsed: classifying it means chdir'ing into // a directory and recording nothing, and the nested client below still @@ -216,6 +232,9 @@ fn nativeMain(init: std.process.Init) !void { } else if (std.mem.eql(u8, a, "-h") or std.mem.eql(u8, a, "--help")) { try std.Io.File.stdout().writeStreamingAll(init.io, help_text); return; + } else if (std.mem.eql(u8, a, "--version")) { + try std.Io.File.stdout().writeStreamingAll(init.io, version_text); + return; } else if (a.len > 0 and a[0] != '-' and positional == null) { positional = a; } else { @@ -285,6 +304,19 @@ fn nativeMain(init: std.process.Init) !void { // reading. Refused rather than resolved by declaration order, which would // silently drop whichever flag lost. if (detach != null and attach != null) return error.BadArgs; + // `--fs` mounts the acme control filesystem, and only a LOCAL session has + // one: `fs_service.start` is called inside tty.zig's `localSession` and + // gui.zig's equivalent, both of which an `--attach` skips entirely, and + // `detached/server.zig` never reads `opts.fs` at all. So `--fs` with either + // of these was parsed, stored, and then served by nobody. + // + // Refused for the same reason as the line above, and it is the stronger + // case: a contradiction is at least visible, whereas a silently dropped + // mount is invisible until someone waits for a directory that will never + // appear. Serving it instead would mean mounting FUSE in the detached core, + // which is a feature rather than a fix — `push_fs_reply` is one of the five + // host methods that core deliberately leaves null (docs/detached.md). + if (opts.fs != null and (detach != null or attach != null)) return error.BadArgs; // `--detach` replaces the frontend rather than choosing among them: the // core runs here, with no terminal, and the frontends are elsewhere on a // socket (src/detached/). It is checked before `platform` because it is not @@ -300,15 +332,18 @@ fn nativeMain(init: std.process.Init) !void { try std.fmt.allocPrint(arena, "{d}", .{@as(u32, @intCast(std.c.getpid()))}); return @import("detached/server.zig").run(init, opts, named); } - // A frontend is a SHELL, and only the tty one knows how to be one today. - // Comptime-folded, so a tty build carries none of this. - if (attach != null and pardes.platform != .tty) { - try std.Io.File.stderr().writeStreamingAll(init.io, "pardes: --attach needs the tty shell\n"); + // A frontend is a SHELL, and the two native ones — a terminal and an SDL + // window — both know how to be one. The browser has no unix socket to + // reach a session over and the AppKit shell is entered by its own host + // rather than through this file, so neither is wired for it; the check is + // comptime-folded, so a tty or gui build carries none of it. + if (attach != null and pardes.platform != .tty and pardes.platform != .gui) { + try std.Io.File.stderr().writeStreamingAll(init.io, "pardes: --attach needs the tty or gui shell\n"); std.process.exit(1); } switch (pardes.platform) { .tty => try @import("tty/tty.zig").run(init, opts, attach), - .gui => try @import("gui/gui.zig").run(init, opts), + .gui => try @import("gui/gui.zig").run(init, opts, attach), // Every other shell is entered by its host and never links this file // at all: the browser through src/web.zig, the macOS app through // src/macos.zig, and the ESP32-P4 firmware through src/esp32p4/app.zig, diff --git a/src/nested.zig b/src/nested.zig index 887d0703..1997d8e4 100644 --- a/src/nested.zig +++ b/src/nested.zig @@ -82,11 +82,15 @@ extern "c" fn proc_pidinfo(pid: c_int, flavor: c_int, arg: u64, buffer: *anyopaq /// The gap is a race only against a fork on another thread, and every caller /// is past that: `listen` runs before the first pane exists, `acceptLine` runs /// on a thread of its own long after spawning has settled, and the detached -/// session forks nothing at all (its ptys live in its frontends). +/// session — which forks EVERY pane shell in the session, because the daemon +/// owns them now (`host_io.zig`) — has no other thread to race with, since it +/// services its pane ptys from the same `poll(2)` that accepts its frontends. /// -/// CLOEXEC still matters for both: a `--detach` session is long-lived, and an -/// inherited listener would keep its socket bound long after it ended — the -/// same shape as the inherited lock fd that once held a flock forever. +/// CLOEXEC matters MORE for that last one than it did when a frontend forked +/// the shells: a pane shell is long-lived and arbitrary, and an inherited +/// listener would keep the session's socket bound long after the session +/// ended — the same shape as the inherited lock fd that once held a flock +/// forever. pub fn setCloexec(fd: c_int) void { const FD_CLOEXEC: c_int = 1; _ = libc.fcntl(fd, libc.F.SETFD, FD_CLOEXEC); diff --git a/src/pardes.zig b/src/pardes.zig index db20ff32..55461c5d 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -82,6 +82,22 @@ pub const platform: Platform = @field(Platform, @tagName(@import("pardes_config" /// target - see `build.zig`. pub const theme_animation = @import("pardes_config").theme_animation; +/// WHICH BUILD THIS IS, for `--version` and for any bug report that follows it. +/// +/// `version` is `build.zig.zon`'s `.version`, read from the manifest by +/// `build.zig` rather than copied beside it, so there is exactly one place to +/// bump. `commit` is the git revision it was built from, and it is OPTIONAL +/// because a source drop is not a repository: a tarball, a container with no +/// `git`, or any checkout outside version control all yield null, and a +/// frontend must say the version happily without one. +/// +/// Both are strings the build baked in, never questions asked at runtime. A +/// binary that shelled out to `git` would describe whatever tree it was +/// standing in rather than the one it came from — and on the board there is +/// neither a `git` nor a process to run it with. +pub const version = @import("pardes_config").version; +pub const commit: ?[]const u8 = @import("pardes_config").commit; + /// A build with no host but its display: the embedded source filesystem, the /// in-process clipboard, silent ptys. Comptime, and its own option module /// rather than a `pardes_config` field, because it is the one setting that @@ -100,6 +116,22 @@ pub const font_picker = platform == .gui or platform == .macos; /// reads this instead so a third such platform cannot forget one of them. pub const hosted = platform == .tty or platform == .gui or platform == .macos; +/// Builds whose frontend can hand its screen to a detached core — which is +/// narrower than `hosted`, and the gap is a bug this predicate exists to close. +/// +/// macOS is hosted, has a unix socket, and compiles `detached/`; what it does +/// not do is POLL. `takeAttach` is a poll rather than a host method precisely +/// because attaching replaces the core the call is running inside (see +/// `Effect.attach`), and `src/macos.zig` never calls it. Gated on `hosted`, the +/// `Attach` word therefore parsed, queued an effect, stored a request in +/// `attach_buf` — and did nothing at all, for ever, silently. That is the +/// failure this codebase refuses everywhere else, so the word does not exist +/// on a frontend that cannot serve it. +/// +/// The two here are exactly the two `main.zig` accepts `--attach` for, which is +/// the same question asked at the command line instead of in a tag. +pub const can_attach = platform == .tty or platform == .gui; + /// Builds that HAVE terminal panes: a pane whose content is a live ghostty-vt /// emulator being fed pty bytes. The P4 firmware has no processes, no ptys and /// nothing that could produce a VT byte, so there the emulator is ~400 KiB of @@ -288,6 +320,67 @@ fn drainForSavePath(p: *Pardes, buf: []u8) ?[]const u8 { return if (len) |n| buf[0..n] else null; } +/// Drain the queue through the in-process host — what a shell's pump does with +/// it — and report the Attach among those effects. Going through `perform` is +/// the point: what a frontend acts on is what `takeAttach` hands back AFTER the +/// drain, not the effect value, which dies in the loop that read it. +fn drainForAttach(p: *Pardes) ?AttachRequest { + while (p.nextEffect()) |effect| p.perform(effect); + return p.takeAttach(); +} + +test "Attach asks for a session and tears nothing down" { + if (comptime !hosted) return; // no unix socket on this platform, so no word + const gpa = std.testing.allocator; + const p = try Pardes.init(gpa, .{ .tty_only = true }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + + // Bare means whichever session is there, and that is an EMPTY name rather + // than an absent request: the frontend still has to go and look. + try std.testing.expect(p.executeBuiltinLine(0, "Attach")); + const bare = drainForAttach(p) orelse return error.NoAttachAsked; + try std.testing.expectEqualStrings("", bare.name); + try std.testing.expectEqual(@as(u8, 0), bare.pane); + + // ...and the named form carries its tail, the way `Theme ` does. + try std.testing.expect(p.executeBuiltinLine(0, "Attach work")); + const named = drainForAttach(p) orelse return error.NoAttachAsked; + try std.testing.expectEqualStrings("work", named.name); + try std.testing.expect(p.takeAttach() == null); // taken once, then gone + + // The word only ASKS. A core that tore itself down here could not be handed + // back intact when the connect fails, and that is the entire guarantee. + try std.testing.expect(!p.quit); + try std.testing.expect(p.panes[0] != null); +} + +test "Detach asks the frontend to leave, and says so when there is nothing to leave" { + if (comptime !hosted) return; // no unix socket on this platform, so no word + const gpa = std.testing.allocator; + const p = try Pardes.init(gpa, .{ .tty_only = true }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + + // Whole-word only, like Kill: the effect names the pane that ran it and + // carries nothing else, because the daemon that serves it already knows + // which frontend's keystroke arrived. + try std.testing.expect(p.executeBuiltinLine(0, "Detach")); + const asked = while (p.nextEffect()) |effect| switch (effect) { + .detach => |d| break d, + else => {}, + } else return error.NoDetachAsked; + try std.testing.expectEqual(@as(u8, 0), asked.pane); + + // ...and this core is a LOCAL shell — a bare `Host{}` fills in no + // `push_detach` — so performing it reports on that pane instead of + // dismissing a session this process is not part of. + p.perform(.{ .detach = asked }); + const pane = p.panes[0].?; + try std.testing.expectEqualStrings("detach: NotAttached", pane.msg[0..pane.msg_len]); + try std.testing.expect(!p.quit); +} + test "selection pipe prompt submits exact request and Escape cancels" { const gpa = std.testing.allocator; const p = try Pardes.init(gpa, .{ .tty_only = true }); @@ -3697,6 +3790,25 @@ pub const Event = union(enum) { fs_req: acmefs.Req, }; +/// The longest session name `Effect.attach` can carry. A name is ONE path +/// component under the runtime socket directory (detached/server.zig +/// `socketPath`), so `sun_path`'s 108 bytes cap a usable one far below this; +/// 256 is the width `spawn`'s cwd and `open_link` already reserve, and reusing +/// it is why the new arm costs the effect ring nothing — `save_text`'s +/// {pane, serial, Buf(256)} is still the widest thing in the union. +pub const attach_name_max = 256; + +/// What `takeAttach` hands the shell: the session to reach for (empty means +/// "whichever one is there") and the pane whose message row a failed connect +/// is reported on, the way `Effect.dump_themes` carries the pane that receives +/// the native host's answer. +pub const AttachRequest = struct { pane: u8, name: []const u8 }; + +/// A pane's pty bytes waiting for room in the effect ring. Core-owned (the +/// `Event.paste` slice they came from is borrowed for one `update` only), and +/// freed the moment `off` reaches the end or the pane goes away. +pub const PendingWrite = struct { bytes: []u8, off: usize = 0 }; + /// IO the core wants done. Payloads are inline (fixed buffers): effects are /// queued values with no lifetime ties back into the core. pub const Effect = union(enum) { @@ -3758,6 +3870,27 @@ pub const Effect = union(enum) { /// blocking `event` read, with the waiting left where the kernel's /// request already is. fs_reply: acmefs.Reply, + /// `Attach [name]` — hand this frontend's screen to a detached core, the + /// one `pardes --detach [name]` left running; an empty name means "the + /// session that is there". `pane` is where a failed connect is reported. + /// + /// CONNECT FIRST, SWAP SECOND is what the shell owes this, and it is the + /// whole point of the word: the session's socket must be open before + /// anything local is torn down, so an attach that fails leaves this + /// instance running with every pane and every undo intact instead of half + /// dead. Which is also why no host method performs it — see `perform`. + attach: struct { pane: u8, name: Buf(attach_name_max) }, + /// `Detach` — this frontend leaves; the session and every other frontend + /// carry on. tmux's detach-client, and deliberately NOT the inverse of + /// `attach`: turning a live LOCAL session into a daemon needs setsid and a + /// fork, or closing the terminal takes the session with it. + /// + /// No name travels because there is nobody to name. The word is typed in a + /// frontend that has no core of its own, reaches the daemon as an ordinary + /// `Event.command`, and the daemon routes the effect back to the frontend + /// whose keystroke caused it. `pane` is only for the report a local shell + /// gets instead — `perform`'s null-method arm. + detach: struct { pane: u8 }, quit, fn Buf(comptime n: usize) type { @@ -5834,6 +5967,19 @@ pub const Pardes = struct { effects_head: usize = 0, effects_len: usize = 0, + /// Pty bytes that did not fit the ring, per pane, kept because refusing a + /// `write` TRUNCATES a byte stream rather than merely delaying it: a 1 MiB + /// paste used to reach a program as its first 256 KiB, silently. `emitWrite` + /// parks the tail here and `nextEffect` refills the ring from it as the host + /// drains, so the stream is delayed and never cut. See `limits.pending_write_cap`. + /// + /// Per pane because two ptys are independent streams: only order WITHIN one + /// matters, so a pane whose tail is waiting never delays another's writes. + pending_write: [MAX_PANES]?PendingWrite = @splat(null), + /// Total bytes parked above, so the drain path costs one comparison when + /// nothing is waiting — which is every frame that is not a large paste. + pending_write_bytes: usize = 0, + /// WHO SERVES THIS CORE. Every method optional; a null one is answered by /// `fallback` below, so a `Host{}` is a complete in-process pardes. host: Host = .{}, @@ -5864,6 +6010,14 @@ pub const Pardes = struct { /// shell consumes it via takeRestore each frame (restore contents stay host-fed) restore_req: ?[]const u8 = null, restore_buf: [1024]u8 = undefined, + /// An Attach builtin wants this frontend's screen handed to a detached + /// core; the shell consumes it via takeAttach from its OUTER loop, beside + /// takeRestore and for the same reason — both END this core, and nothing + /// running inside `pump` may destroy the core it is running in. `name` + /// points into `attach_buf`, which the effect's inline copy is unpacked + /// into: the effect is a value the drain loop owns and dies with it. + attach_req: ?AttachRequest = null, + attach_buf: [attach_name_max]u8 = undefined, surface: Surface = .{}, /// per-update scratch (paneCursorLines, selection text); reset each update @@ -5987,6 +6141,7 @@ pub const Pardes = struct { pub fn deinit(p: *Pardes) void { p.cancelLookHover(); + for (0..MAX_PANES) |id| p.dropPendingWrite(id); for (&p.panes) |*slot| if (slot.*) |pane| { p.teardownPane(pane); slot.* = null; @@ -6033,6 +6188,14 @@ pub const Pardes = struct { return r; } + /// the shell polls this each frame beside takeRestore: a pending Attach's + /// session and reporting pane, or null + pub fn takeAttach(p: *Pardes) ?AttachRequest { + const a = p.attach_req; + p.attach_req = null; + return a; + } + /// the topbar line: the fixed builtins, plus `Restore ` once a dump /// exists — render and click dispatch must agree on this exact string fn topbar(p: *Pardes, buf: []u8) []const u8 { @@ -6056,6 +6219,8 @@ pub const Pardes = struct { // `event` file open leaving the editor suppressing button actions // for whatever pane lands in this slot next. p.fs.forget(p.gpa, id); + // ...and so do bytes still queued for the pty it no longer has. + p.dropPendingWrite(id); }; if (p.lookHoverPane()) |h| if (h < p.panes.len and p.panes[h] == pane) p.cancelLookHover(); p.pane_alloc.doom(pane); @@ -6221,14 +6386,47 @@ pub const Pardes = struct { p.look_walk_owner = pane.serial; } - /// chunk arbitrary-length bytes into fixed-size write effects (order kept) + /// Chunk arbitrary-length bytes into fixed-size write effects, order kept. + /// + /// The ring refuses when full rather than evicting, which is right for every + /// other effect and WRONG for a byte stream: the tail of a large paste was + /// dropped where the program needed it whole (and, under bracketed paste, the + /// closing marker went with it, leaving the program in paste mode). What does + /// not fit parks in `pending_write` and `nextEffect` feeds it back as the host + /// drains, so this never truncates while `pending_write_cap` has room. pub fn emitWrite(p: *Pardes, id: usize, bytes: []const u8) void { var off: usize = 0; - while (off < bytes.len) { - const n = @min(bytes.len - off, 64); - p.emit(.{ .write = .{ .pane = @intCast(id), .bytes = .from(bytes[off .. off + n]) } }); - off += n; + // Anything already parked for this pane owns the stream's position, so + // new bytes queue BEHIND it — emitting them now would reorder the pty. + if (p.pending_write[id] == null) { + while (off < bytes.len and p.effects_len < p.effects.len) { + const n = @min(bytes.len - off, 64); + p.emit(.{ .write = .{ .pane = @intCast(id), .bytes = .from(bytes[off .. off + n]) } }); + off += n; + } } + if (off < bytes.len) p.parkPendingWrite(id, bytes[off..]); + } + + /// Take ownership of bytes the ring had no room for. A failed allocation or + /// an exhausted cap degrades to the old behaviour — dropping the tail — because + /// the alternative on a full heap is refusing to run at all. + fn parkPendingWrite(p: *Pardes, id: usize, bytes: []const u8) void { + if (comptime limits.pending_write_cap == 0) return; + if (p.pending_write_bytes + bytes.len > limits.pending_write_cap) return; + if (p.pending_write[id]) |*pw| { + // Compact what the drain already sent before growing: a burst that + // arrives in pieces must not keep re-copying bytes nobody wants. + const keep = pw.bytes.len - pw.off; + const grown = p.gpa.alloc(u8, keep + bytes.len) catch return; + @memcpy(grown[0..keep], pw.bytes[pw.off..]); + @memcpy(grown[keep..], bytes); + p.gpa.free(pw.bytes); + pw.* = .{ .bytes = grown }; + } else { + p.pending_write[id] = .{ .bytes = p.gpa.dupe(u8, bytes) catch return }; + } + p.pending_write_bytes += bytes.len; } /// The shell reports the spawned pane's working directory (and later cwd @@ -6533,6 +6731,10 @@ pub const Pardes = struct { } pub fn nextEffect(p: *Pardes) ?Effect { + // Before the emptiness test, not after: a pane whose tail is parked + // must not read as "no effects left" while the host's drain loop is + // still asking. This is what turns a truncated paste into a delayed one. + p.refillPendingWrites(); if (p.effects_len == 0) { p.effects_head = 0; return null; @@ -6543,6 +6745,38 @@ pub const Pardes = struct { return e; } + /// Move parked pty bytes into whatever room the ring now has, oldest pane + /// slot first. Costs one comparison when nothing is parked. + fn refillPendingWrites(p: *Pardes) void { + if (p.pending_write_bytes == 0) return; + for (&p.pending_write, 0..) |*slot, id| { + while (p.effects_len < p.effects.len) { + // `|*pw|` points INTO the slot: capturing by value would advance + // a copy's cursor and re-send the same chunk for ever. + const pw = if (slot.*) |*live| live else break; + const n = @min(pw.bytes.len - pw.off, 64); + p.emit(.{ .write = .{ .pane = @intCast(id), .bytes = .from(pw.bytes[pw.off..][0..n]) } }); + pw.off += n; + p.pending_write_bytes -= n; + if (pw.off == pw.bytes.len) { + p.gpa.free(pw.bytes); + slot.* = null; + break; + } + } + if (p.effects_len == p.effects.len) return; + } + } + + /// Drop a pane's parked bytes: its pty is gone, and the slot it occupied + /// may be handed to a different pane next frame. + fn dropPendingWrite(p: *Pardes, id: usize) void { + const pw = p.pending_write[id] orelse return; + p.pending_write_bytes -= pw.bytes.len - pw.off; + p.gpa.free(pw.bytes); + p.pending_write[id] = null; + } + /// Queue input for the next `pump`. Single-threaded, and a VALUE queue: an /// event that carries a borrowed slice cannot survive the trip, so this /// asserts rather than documents it. Hand those to `update` directly inside @@ -6683,6 +6917,33 @@ pub const Pardes = struct { // this is the borrow window. A host with no filesystem serving // cannot have asked, so a null method is not a dropped answer. .fs_reply => |r| if (v.push_fs_reply) |f| f(p.host.ctx, &r, p.fsPayload(r)), + // THE ONE EFFECT NO HOST METHOD CAN SERVE: attaching REPLACES the + // core this call is running inside — `pump` is two frames up the + // stack — so all it may do here is record the request where the + // shell's outer loop finds it, which is Restore's shape exactly. + // Reaching it through the ring rather than straight from the + // builtin is what ORDERS it: a `Save` queued by the same update is + // performed first, so nothing you typed is still unwritten when + // the screen changes owners. A shell that never polls (the + // browser, the board) simply cannot attach, which is the truth + // about a machine with no unix socket to attach to. + .attach => |a| { + const name = a.name.slice(); + @memcpy(p.attach_buf[0..name.len], name); + p.attach_req = .{ .pane = a.pane, .name = p.attach_buf[0..name.len] }; + }, + // ...and its counterpart, which a host CAN serve and usually does + // not. Only a detached core's host fills `push_detach` in; a local + // tty or SDL shell leaves it null, and the honest answer there is + // a message row rather than a frontend that quits or a word that + // silently does nothing. A null-method fallback and not a comptime + // gate, because whether there is a session to leave is a fact + // about this RUN — the same binary attaches one minute and does + // not the next. + .detach => |d| if (v.push_detach) |f| + f(p.host.ctx) + else + p.reportError(d.pane, "detach", error.NotAttached), // the loop's own condition; a host tears down after its own loop .quit => p.quit = true, } @@ -13423,15 +13684,37 @@ pub const Pardes = struct { // ---- the ONE dispatcher: look (right/Enter) and execute (middle/Tab) ---- /// Focus pane `id` and, for a nonzero 1-based `at.line`, put its modal - /// cursor there (`at.col` likewise, 0 = line start): files recenter the - /// view on it, terminals ride their scrollback to it. Both look targets + /// cursor there (`at.col` likewise, 0 = line start). `landing` says how far + /// the view may MOVE to show it: `.center` recenters a file on the line and + /// reveals a PDF's page — a look target, a `:NN`, a search hit, where the + /// context around the destination is the whole point of going there — while + /// `.keep` leaves the view alone and lets `ensureCursorVisible` do the least + /// that shows the cursor, usually nothing at all. A terminal has no recenter + /// to skip, so `landing` does not gate it — but it is not therefore free of + /// movement: a modal cursor PINNED high in the scrollback still pulls the + /// view up to it, which is `ensureCursorVisible` keeping its promise and the + /// reason `Last` records `line = 0` for an unpinned shell. Both look targets /// that name a live pane land here — a path a pane already holds, and /// `@pN:LINE:COL`. A RANGED spot selects (selectSpan below). - pub fn focusPaneLine(p: *Pardes, id: usize, at: look.Spot) void { + pub fn focusPaneLine(p: *Pardes, id: usize, at: look.Spot, landing: enum { center, keep }) void { if (id >= MAX_PANES) return; const pane = p.panes[id] orelse return; p.active = id; if (hasPdf(pane)) { + // A page reveal IS this pane's view, so `.keep` is simply not doing + // it. Not a formality: a reveal of the page you are already on still + // sets `document_scroll_y` to `page_starts[page]`, so returning to a + // PDF threw away the offset WITHIN the page you were reading. + // + // Read that literally — under `.keep` a PDF's page is not restored + // AT ALL, and the spot's line is a page. That only shows when + // something moved the pane while you were away, and something can: + // the wheel scrolls the pane under the POINTER, not the active one. + // Then Esc leaves the PDF on the page the wheel reached rather than + // the one the jumplist recorded, which is the answer a RETURN wants + // and not the answer a jump wants — so Ctrl-o and Ctrl-i, which + // centre, are still how you reach the recorded page. + if (landing == .keep) return; if (comptime pdf_enabled) { const pv = &pane.pdf.?; if (pv.focusLocation(p.pdf_gpa, at.line, at.col)) pdf_pane.resetPageChrome(pane); @@ -13440,16 +13723,23 @@ pub const Pardes = struct { } if (at.line == 0) return; if (pane.file) |*f| { + // The clamp holds either way: a stale jump naming a line past the + // end must not land the cursor there just because the view is not + // moving. if (at.line > file_pane.nlines(p.gpa, f)) return; - const next = (at.line - 1) -| pane.rows / 2; // center, clamp at top - if (next != f.scroll) { - f.scroll = next; - f.syntax_dirty = true; + if (landing == .center) { + const next = (at.line - 1) -| pane.rows / 2; // center, clamp at top + if (next != f.scroll) { + f.scroll = next; + f.syntax_dirty = true; + } } } - // land the modal cursor on the target line (and keep - // ensureCursorVisible agreeing with the recenter — a stale cursor - // would yank the view right back) + // Land the modal cursor on the target line. Under `.center` that keeps + // ensureCursorVisible agreeing with the recenter — a stale cursor would + // yank the view right back. Under `.keep` it IS the whole policy: no + // recenter ran, so the nudge below is the only thing that can move the + // view, and it moves it only far enough to show the cursor. pane.cur_row = @intCast(at.line - 1); pane.cur_col = if (at.col > 0) @intCast(at.col - 1) else 0; pane.cur_pinned = true; @@ -13495,12 +13785,12 @@ pub const Pardes = struct { return true; }; if (comptime pdf_enabled) if (tt.pdf) |pv| if (std.mem.eql(u8, pv.path, path)) { - p.focusPaneLine(i, at); + p.focusPaneLine(i, at, .center); return true; }; const ff = if (tt.file) |*f| f else continue; if (!std.mem.eql(u8, ff.path, path)) continue; - p.focusPaneLine(i, at); + p.focusPaneLine(i, at, .center); return true; } return false; @@ -13606,7 +13896,7 @@ pub const Pardes = struct { // `@p7:10:5`: pane 7, line 10, column 5 — how a search result // points at a terminal or an output buffer, neither of which // has a path. - .pane => |t| p.focusPaneLine(t.id, t.at), + .pane => |t| p.focusPaneLine(t.id, t.at, .center), .url => |u| if (u.len <= 256) p.emit(.{ .open_link = .from(u) }), .dir => |dir| { // focus an existing terminal on this dir, else fork one below. @@ -14303,12 +14593,20 @@ pub const Pardes = struct { /// the stack and go to what it names. Nothing is pushed and nothing is /// dropped — walking history is not making it — and trackJump agrees, /// because after the move the live spot IS `jumps[jcur]` again. + /// + /// `.center` where `Last` keeps the view, and the asymmetry is structural + /// rather than arbitrary: `Last` only ever CROSSES panes, so the pane it + /// lands on already holds the view you left it with. This may land in the + /// SAME pane, where there is no such view to keep — a long in-file jump + /// would arrive on the very top or bottom row with `scroll_off` lines of + /// context on one side. Helix splits the same pair the same way: its + /// jumplist centres, its buffer switch does not. pub fn jumpBy(p: *Pardes, delta: i32) void { const next = @as(i64, @intCast(p.jcur)) + delta; if (p.njumps == 0 or next < 0 or next >= p.njumps) return; p.jcur = @intCast(next); const j = p.jumps[p.jcur]; - p.focusPaneLine(j.pane, .{ .line = j.line, .col = j.col }); + p.focusPaneLine(j.pane, .{ .line = j.line, .col = j.col }, .center); } /// Recompute geometry, push grid-size changes to each emulator + pty, fire @@ -15418,7 +15716,16 @@ pub const Pardes = struct { // this same choice named. Order is load-bearing — gutter, recolor, then // wrap markers; the selection/cursor passes below win over all three. switch (pane.colorAlgo()) { - .tty => if (p.settings.colors and pane.mode == .tty) term_pane.recolorAnsi(p, pane, r, tx, tw, body_h), + // Every mode, not just `.tty`: `recolorAnsi` translates a row's + // colour anchor through the same slide the edit buffer applied to + // its text, so leaving a shell for normal mode no longer drains + // the screen of colour. A row the user typed has no ANSI and is + // skipped there, which is why this needs no mode test. + // `body` is the very text printed above: `recolorAnsi` pairs its + // graphemes with the cells that spelled them, which is the only way + // to stay on the right glyph when the emulator and this surface + // disagree about how many columns a cluster is worth. + .tty => if (p.settings.colors) term_pane.recolorAnsi(p, pane, r, tx, tw, body_h, body), .source, .diff => { const f = &pane.file.?; file_pane.drawGutter(p, pane, r, tx, tw, body_h, active); @@ -15792,6 +16099,50 @@ test "Esc back into a tty leaves its view at the prompt" { try std.testing.expectEqual(live, sp.vt.screens.active.pages.scrollbar().offset); } +test "Esc back into a file leaves its view where it was" { + if (platform == .web) return; + const gpa = std.testing.allocator; + const p = try Pardes.init(gpa, .{ .cols = 80, .rows = 24, .file = "src/allocators.zig" }); + defer p.deinit(); + p.update(.{ .resize = .{ .cols = 80, .rows = 24 } }); + p.update(.{ .key = .{ .cp = 'n', .alt = true } }); // a shell under the doc + const shell = p.active; + p.update(.{ .key = .{ .cp = Key.escape } }); // back onto the doc + p.sync(); + const doc = p.active; + try std.testing.expect(doc != shell); + const dp = p.panes[doc].?; + try std.testing.expect(dp.file != null); + + // Ride the cursor down until the view has scrolled: it now sits in the + // bottom band, which is exactly where a recenter is visible. + for (0..20) |_| p.update(.{ .key = .{ .cp = 'j' } }); + p.sync(); + const view = dp.file.?.scroll; + const row = dp.cur_row; + try std.testing.expect(view > 0); + + p.update(.{ .key = .{ .cp = Key.escape } }); // out to the shell + p.sync(); + try std.testing.expectEqual(shell, p.active); + p.update(.{ .key = .{ .cp = Key.escape } }); // ...and back + p.sync(); + try std.testing.expectEqual(doc, p.active); + + // Nothing moved. The old landing recentred the line under the cursor, so + // coming back repainted the whole screen to show what was already on it. + try std.testing.expectEqual(view, dp.file.?.scroll); + try std.testing.expectEqual(row, dp.cur_row); + // ...and the cursor is still on screen, which is all `.nearest` promises. + try std.testing.expect(dp.cur_row >= @as(i32, @intCast(view))); + try std.testing.expect(dp.cur_row < @as(i32, @intCast(view)) + @as(i32, dp.rows)); + + // The other arm still centres: a look target, a `:NN`, a search hit. + p.focusPaneLine(doc, .{ .line = @intCast(row + 1), .col = 1 }, .center); + try std.testing.expect(dp.file.?.scroll != view); + try std.testing.expectEqual(@as(usize, @intCast(row)) -| @as(usize, dp.rows) / 2, dp.file.?.scroll); +} + test "Shift-Esc in tty hops to the doc and leaves the shell in tty" { if (platform == .web) return; const gpa = std.testing.allocator; @@ -16017,6 +16368,79 @@ test "an unasked desktop paste reaches a tty pane's program, not its buffer" { try std.testing.expect(pane.ovl == null); } +test "a paste larger than the effect ring reaches the program whole and in order" { + if (platform == .web) return; + const gpa = std.testing.allocator; + const p = try Pardes.init(gpa, .{ .tty_only = true, .cols = 80, .rows = 24 }); + defer p.deinit(); + p.update(.{ .resize = .{ .cols = 80, .rows = 24 } }); + const buf = try gpa.alloc(u8, 1 << 20); + defer gpa.free(buf); + _ = drainWrites(p, buf); + term_pane.enterTty(p, 0); + + // Bigger than `effect_cap * 64` (256 KiB), which is where the ring stops + // taking chunks: the tail used to be refused and the program saw 256 KiB of + // a 300 KiB paste with nothing said. Position-dependent bytes, so a + // reordered or duplicated chunk fails as loudly as a missing one. + const text = try gpa.alloc(u8, 300 * 1024); + defer gpa.free(text); + for (text, 0..) |*c, i| c.* = 'a' + @as(u8, @intCast(i % 26)); + p.update(.{ .paste = text }); + try std.testing.expectEqualSlices(u8, text, drainWrites(p, buf)); + // ...and the core is not still holding a copy afterwards + try std.testing.expectEqual(@as(usize, 0), p.pending_write_bytes); +} + +test "a bracketed paste larger than the ring still closes its bracket" { + if (platform == .web) return; + if (comptime !term_pane.enabled) return; + const gpa = std.testing.allocator; + const p = try Pardes.init(gpa, .{ .tty_only = true, .cols = 80, .rows = 24 }); + defer p.deinit(); + p.update(.{ .resize = .{ .cols = 80, .rows = 24 } }); + const buf = try gpa.alloc(u8, 1 << 20); + defer gpa.free(buf); + _ = drainWrites(p, buf); + term_pane.enterTty(p, 0); + + // The program asks for brackets, so `typeToTty` emits marker, text, marker. + // The CLOSING one is queued last and was therefore the first casualty of a + // full ring: the program stayed in paste mode and read every later + // keystroke as pasted text. Worse than losing the bytes. + p.update(.{ .output = .{ .pane = 0, .bytes = "\x1b[?2004h" } }); + try std.testing.expect(term_pane.bracketedPaste(p.panes[0].?)); + const text = try gpa.alloc(u8, 300 * 1024); + defer gpa.free(text); + @memset(text, 'z'); + p.update(.{ .paste = text }); + const got = drainWrites(p, buf); + try std.testing.expect(std.mem.startsWith(u8, got, "\x1b[200~")); + try std.testing.expect(std.mem.endsWith(u8, got, "\x1b[201~")); + try std.testing.expectEqualSlices(u8, text, got["\x1b[200~".len .. got.len - "\x1b[201~".len]); +} + +test "pasted bytes still queued at shutdown are freed, not leaked" { + if (platform == .web) return; + const gpa = std.testing.allocator; + const p = try Pardes.init(gpa, .{ .tty_only = true, .cols = 80, .rows = 24 }); + defer p.deinit(); + p.update(.{ .resize = .{ .cols = 80, .rows = 24 } }); + const buf = try gpa.alloc(u8, 1 << 20); + defer gpa.free(buf); + _ = drainWrites(p, buf); + term_pane.enterTty(p, 0); + + const text = try gpa.alloc(u8, 300 * 1024); + defer gpa.free(text); + @memset(text, 'q'); + p.update(.{ .paste = text }); + // Parked and deliberately NOT drained — a session killed mid-paste. The + // copy is the core's, so `std.testing.allocator` fails this test through + // the `deinit` above if shutdown forgets it. + try std.testing.expect(p.pending_write_bytes > 0); +} + test "leaving tty hides the prompt and keeps the command typed at it" { if (platform == .web) return; const gpa = std.testing.allocator; diff --git a/src/pdf_pane_integration_test.zig b/src/pdf_pane_integration_test.zig index 2e671f49..f0a8be8f 100644 --- a/src/pdf_pane_integration_test.zig +++ b/src/pdf_pane_integration_test.zig @@ -1775,3 +1775,45 @@ test "PDF native mouse selection, Look, and highlights share page geometry" { try std.testing.expectEqual(@as(usize, 0), pv.selection_text.len); try std.testing.expectEqualStrings(saved_query, pv.search_query); } + +test "Esc back into a PDF keeps the offset within its page" { + if (!pdf_enabled or platform == .web) return; + const gpa = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + const fixture = try pdf_impl.makeOutlineTestPdf(gpa); + defer gpa.free(fixture); + try tmp.dir.writeFile(std.testing.io, .{ .sub_path = "outline.pdf", .data = fixture }); + var path_buf: [256]u8 = undefined; + const path = try std.fmt.bufPrint(&path_buf, ".zig-cache/tmp/{s}/outline.pdf", .{tmp.sub_path}); + + const p = try Pardes.init(gpa, .{ .file = path, .cols = 80, .rows = 28 }); + defer p.deinit(); + p.update(.{ .resize = .{ .cols = 80, .rows = 28 } }); + const doc = p.active; + const dp = p.panes[doc].?; + try std.testing.expect(dp.pdf != null); + pardes.pdf_test.sync(p); + + // Read a little way DOWN the page you are on, then step away. + const pv = &dp.pdf.?; + pv.document_scroll_y += 137; + const mid = pv.document_scroll_y; + pv.scroll_to_page_pending = false; + p.update(.{ .key = .{ .cp = 'n', .alt = true } }); // a shell under the doc + pardes.pdf_test.sync(p); + const shell = p.active; + try std.testing.expect(shell != doc); + + p.update(.{ .key = .{ .cp = Key.escape } }); // Esc: back into the PDF + pardes.pdf_test.sync(p); + try std.testing.expectEqual(doc, p.active); + + // The jumps stack records a PDF as its PAGE, so returning revealed the page + // you were already on — and a reveal sets `document_scroll_y` to that page's + // start, throwing away where you had read to inside it. + try std.testing.expectEqual(mid, pv.document_scroll_y); + // Same reveal, the other half: with no valid layout yet it only ARMS the + // snap, so a test that watched the offset alone would not see it coming. + try std.testing.expect(!pv.scroll_to_page_pending); +} diff --git a/src/term_pane.zig b/src/term_pane.zig index 624a68a1..75229e4e 100644 --- a/src/term_pane.zig +++ b/src/term_pane.zig @@ -787,15 +787,45 @@ fn promptRow(pin: ghostty_vt.Pin, raw: []const u8) []const u8 { var col: usize = 0; while (col < cols and at < raw.len) { const cell = &cells[col]; - var cps: usize = 1; - if (pin.grapheme(cell)) |extra| cps += extra.len; - for (0..cps) |_| at = modal.nextGrapheme(raw, at); + // Step the dump by exactly what THIS CELL contributed to it. The + // tempting walk — one `modal.nextGrapheme` per cell — assumes the two + // sides agree on where a cluster ends, and they do not: ghostty keeps a + // ZWJ family emoji in three cells and spells each one separately, while + // pardes' iterator joins the whole sequence into one grapheme. That walk + // then consumed three graphemes for one cell's worth of bytes and ate + // the first characters of what was typed at the prompt. + at = @min(raw.len, at + dumpedBytes(pin, cell)); // the tail cell of a wide glyph spells nothing of its own col += if (cell.wide == .wide) @as(usize, 2) else 1; } return std.mem.trimEnd(u8, raw[at..], " \t"); } +/// How many bytes `cell` contributed to `pin`'s dumped row. +/// +/// `ScreenFormatter` writes a cell's codepoint followed by the grapheme +/// codepoints stored with it, and writes NOTHING for either spacer, so this is +/// the dump's own arithmetic rather than a guess about clustering. +fn dumpedBytes(pin: ghostty_vt.Pin, cell: *const ghostty_vt.Cell) usize { + switch (cell.wide) { + .spacer_head, .spacer_tail => return 0, + .narrow, .wide => {}, + } + var n: usize = switch (cell.content_tag) { + .codepoint, .codepoint_grapheme => std.unicode.utf8CodepointSequenceLength( + cell.codepoint(), + ) catch 1, + // A cell carrying only a colour still spells one blank in the dump. + else => 1, + }; + if (cell.content_tag == .codepoint_grapheme) { + if (pin.grapheme(cell)) |extra| for (extra) |cp| { + n += std.unicode.utf8CodepointSequenceLength(cp) catch 1; + }; + } + return n; +} + /// A terminal's shell rows as the surface sees them: the WHOLE /// history+active grid, prompt rows blanked (OSC 133), absolute grid rows /// from 0. The raw material the motion surface is composed from — the @@ -1086,7 +1116,25 @@ pub fn bodyText(arena: std.mem.Allocator, pane: *Pane) ![]const u8 { // buffer's and is shared. With no emulator the viewport is simply empty, // and `fillBody` renders the overlay against blank rows. const vp: []const []const u8 = if (comptime !enabled) &.{} else vp: { - const raw = try pane.vt.plainString(arena); + const screen = pane.vt.screens.active; + // The dump has to start at COLUMN ZERO of the viewport's first row. + // `Terminal.plainString` cannot: it goes through `getTopLeft(.viewport)`, + // which hands back the viewport pin verbatim, x and all, while + // `PageList.pin` — how the colour pass finds that same row — forces x to + // 0. Reflow can leave a tracked viewport pin in the MIDDLE of a row + // (narrow the pane until a line wraps, scroll back onto the + // continuation, widen it again): from then on this pass dumped row 0 + // from that column while the colour pass paired the fragment with the + // row's first cells, so the row lost its left half and wore the wrong + // colours — every frame, until the pane snapped back to live output. + // Ghostty's own renderer walks rows and ignores that x, so column zero + // is also what the terminal itself draws. + var tl = screen.pages.getTopLeft(.viewport); + tl.x = 0; + const br = screen.pages.getBottomRight(.viewport) orelse return error.UnknownPoint; + var rows_out: std.Io.Writer.Allocating = .init(arena); + try screen.dumpString(&rows_out.writer, .{ .tl = tl, .br = br, .unwrap = false }); + const raw = try rows_out.toOwnedSlice(); var prompts = pane.vt.screens.active.pages.rowIterator(.right_down, .{ .viewport = .{} }, null); const vp = try arena.alloc([]const u8, std.mem.count(u8, raw, "\n") + 1); var lines = std.mem.splitScalar(u8, raw, '\n'); @@ -1112,56 +1160,317 @@ pub fn bodyText(arena: std.mem.Allocator, pane: *Pane) ![]const u8 { return out; } +/// Where ONE body row's content comes from. The text pass copies bytes for it +/// and the colour pass projects the emulator's styles onto it, so handing both +/// the same answer is what keeps a colour on the row its text landed on. +pub const BodyRow = union(enum) { + /// A shell row, as a VIEWPORT index. Out-of-range values are yielded rather + /// than filtered: each consumer knows its own bound (the text pass has the + /// dumped rows, the colour pass has the live viewport) and a row nobody can + /// source is a blank row, not a skipped one. + grid: i32, + /// One line of the edit buffer, and WHICH line it is. A line the user never + /// changed still stands over the shell row it was seeded from, so the index + /// is what lets the colour pass find that row again (see `EditAnchors`). + edit: struct { line: []const u8, idx: usize }, +}; + +/// THE body row walk, shared. Both passes stepping the same iterator is what +/// makes them agree by CONSTRUCTION rather than by two copies of the same +/// arithmetic agreeing: `Pane.gridRow` and this walk disagree whenever +/// `modal.lineCount` and `splitScalar` disagree about how many rows a buffer +/// occupies (they do, for empty text: 0 against 1), and re-deriving a row's +/// anchor from `gridRow` per row instead of stepping it here put colours one +/// row off below an emptied edit buffer. +const BodyWalk = struct { + pane: *Pane, + goff: i32, + g: i32, + /// the buffer can start above the viewport: drop the lines scrolled past + skip: usize, + n: usize = 0, + lines: ?std.mem.SplitIterator(u8, .scalar) = null, + covered: i32 = 0, + line_idx: usize = 0, + + fn init(pane: *Pane) BodyWalk { + const off = pane.scroll(); + const goff = gridOffset(pane); + return .{ + .pane = pane, + .goff = goff, + // tty mode does not apply the edit buffer, so it must not be moved + // by one either. `Pane.gridRow` and `Pane.surfRow` are NOT inverses + // for a row strictly inside the buffer's covered span (surfRow + // clamps to the buffer's last line, gridRow collapses the whole + // span onto its first shell row), so a buffer left behind by + // `enterTty` — which clears every other modal remnant but not this + // one — straddling the viewport top used to start this walk ABOVE + // the viewport and slide the entire body down. + .g = if (pane.mode == .tty) goff else pane.gridRow(off), + .skip = if (pane.ovl) |o| @intCast(@max(0, off - pane.surfRow(o.row))) else 0, + }; + } + + fn next(w: *BodyWalk) ?BodyRow { + while (w.n < w.pane.rows) { + if (w.lines) |*it| { + if (it.next()) |line| { + const idx = w.line_idx; + w.line_idx += 1; + // Lines scrolled off the top still count: the index names a + // line of the BUFFER, not of the visible body. + if (w.skip > 0) { + w.skip -= 1; + continue; + } + w.n += 1; + return .{ .edit = .{ .line = line, .idx = idx } }; + } + // The buffer stands in for `rows` shell rows however many lines + // it actually spelled, which is the whole slide. + w.g += w.covered; + w.skip = 0; + w.lines = null; + continue; + } + if (w.pane.mode != .tty) if (w.pane.ovl) |o| if (w.g == o.row) { + w.lines = std.mem.splitScalar(u8, o.text, '\n'); + w.covered = o.rows; + w.line_idx = 0; + continue; + }; + const vi = w.g - w.goff; + w.g += 1; + w.n += 1; + return .{ .grid = vi }; + } + return null; + } +}; + /// Run the terminal body row walk. A null destination counts bytes; a slice /// fills the exact allocation made from that count. fn fillBody(dst: ?[]u8, pane: *Pane, viewport: []const []const u8) usize { - const goff: i32 = gridOffset(pane); - const off = pane.scroll(); - var g: i32 = pane.gridRow(off); - // the buffer can start above the viewport: drop the lines scrolled past - var skip: usize = if (pane.ovl) |o| @intCast(@max(0, off - pane.surfRow(o.row))) else 0; + var walk: BodyWalk = .init(pane); var written: usize = 0; - var n: usize = 0; - while (n < pane.rows) { - if (pane.mode != .tty) if (pane.ovl) |o| if (g == o.row) { - var bit = std.mem.splitScalar(u8, o.text, '\n'); - var k: usize = 0; - while (bit.next()) |ln| : (k += 1) { - if (k < skip) continue; - if (n >= pane.rows) break; - if (n > 0) { - if (dst) |out| out[written] = '\n'; - written += 1; - } - if (dst) |out| @memcpy(out[written..][0..ln.len], ln); - written += ln.len; - n += 1; - } - skip = 0; - g += o.rows; - continue; - }; - if (n > 0) { + var first = true; + while (walk.next()) |row| { + if (!first) { if (dst) |out| out[written] = '\n'; written += 1; } - const vi = g - goff; - if (vi >= 0 and @as(usize, @intCast(vi)) < viewport.len) { - const line = viewport[@intCast(vi)]; - if (dst) |out| @memcpy(out[written..][0..line.len], line); - written += line.len; - } - n += 1; - g += 1; + first = false; + const bytes = switch (row) { + .edit => |e| e.line, + .grid => |vi| if (vi >= 0 and @as(usize, @intCast(vi)) < viewport.len) + viewport[@intCast(vi)] + else + "", + }; + if (dst) |out| @memcpy(out[written..][0..bytes.len], bytes); + written += bytes.len; } return written; } +/// WHICH edit-buffer lines still stand over a shell row. +/// +/// The buffer only ever GROWS: it starts at the row first typed on and stretches +/// to cover every row an edit since has touched, so after a few edits it spans +/// rows the user never altered. Those lines are still byte-identical to the +/// shell rows they were seeded from, and their anchor is therefore still known — +/// so they keep their colours, and only lines that actually differ go plain. +/// +/// The buffer's text is DERIVED from the rows it covers, so the untouched lines +/// appear in the same ORDER as the rows they came from. The answer is therefore +/// a MONOTONE MATCHING, and that is what this streams: one shell-row cursor +/// which only ever moves forward, advanced once per buffer line. A line claims +/// the first row at or after the cursor that its bytes equal; matching bytes is +/// the whole proof. A line that matches nothing was typed by the user, so it +/// claims no row and leaves the rows beneath it to the lines below. +/// +/// Two ALIGNED guesses — the Nth line over the Nth covered row, and the same +/// counted from the bottom — are not enough, and the counterexample is one +/// keystroke. Join two rows (backspace at column 0): the buffer loses a line +/// and gains covered rows, the two counts cancel at `lines == covered`, and both +/// guesses resolve to the SAME row, one short of where the lines below actually +/// live. Every untouched row under the join went plain. Nor is a leading and a +/// trailing RUN enough: a run stops at the first divergence, so two separate +/// edits drained the colour of every untouched line BETWEEN them. +/// +/// Cost is linear in the buffer, which the quadratic version this replaced was +/// not (walking to the Nth line per line: 35 ms a frame at a few thousand +/// lines). Every successful claim moves the cursor, so all of them together +/// scan the covered span once; only a typed line can scan without moving it, +/// and `budget` is what stops a buffer full of typed lines from paying that +/// scan per line. Exhausting it costs colour on rows further down, never +/// correctness. +const EditAnchors = struct { + /// the buffer's own text, walked in order: a line the VIEWPORT skipped still + /// consumes the row it came from, so the lines below it stay aligned + text: []const u8 = &.{}, + at: usize = 0, + shell: []const []const u8 = &.{}, + /// the covered span, absolute grid rows, as `[first, end)` + first: usize = 0, + end: usize = 0, + lines: usize = 0, + /// the line `at` names, and the first row still unclaimed + idx: usize = 0, + cursor: usize = 0, + budget: usize = 0, + active: bool = false, + + fn init(p: *Pardes, pane: *Pane, o: EditBuffer) EditAnchors { + if (o.rows <= 0 or o.row < 0) return .{}; + const shell = shellRows(p, pane) catch return .{}; + const first: usize = @intCast(o.row); + if (first >= shell.len) return .{}; + const covered: usize = @intCast(o.rows); + const lines = std.mem.count(u8, o.text, "\n") + 1; + return .{ + .text = o.text, + .shell = shell, + .first = first, + .end = @min(first + covered, shell.len), + .lines = lines, + .cursor = first, + .budget = covered + 4 * lines, + .active = true, + }; + } + + /// Where buffer line `idx` still stands over the grid, if anywhere. `idx` + /// only ever grows — both passes step `BodyWalk` from the top — so catching + /// up to it is amortised O(1) per visible row. + fn shellRow(a: *EditAnchors, idx: usize) ?Anchor { + if (!a.active or idx >= a.lines) return null; + var found: ?Anchor = null; + while (a.idx <= idx) : (a.idx += 1) found = a.claim(a.nextLine() orelse return null); + return found; + } + + fn nextLine(a: *EditAnchors) ?[]const u8 { + if (a.at > a.text.len) return null; + const rest = a.text[a.at..]; + if (std.mem.indexOfScalar(u8, rest, '\n')) |n| { + a.at += n + 1; + return rest[0..n]; + } + // The last line has no terminator; one past the end ends the walk. + a.at = a.text.len + 1; + return rest; + } + + /// Where this line still stands over the grid, if anywhere. + fn claim(a: *EditAnchors, line: []const u8) ?Anchor { + // An EXACT row is the best evidence there is, so look for one first and + // look anywhere ahead: a line that merely RESEMBLES the row alignment + // offers is often the row two below, unchanged and unedited. + // + // Scanning past the cursor crosses rows that were deleted or joined + // away, and the line's bytes are what justify the crossing — so an + // EMPTY line may not do it. Empty is not evidence: it equals every + // blank row in the span, and splitting a row makes exactly that. Two + // keystrokes (Home, Enter) used to hand the blank row below the last + // output to the new empty line and take every coloured row in between + // out of reach of the lines that owned them. + const end = if (line.len == 0) @min(a.cursor + 1, a.end) else a.end; + var k = a.cursor; + while (k < end) : (k += 1) { + if (a.budget == 0) return null; + a.budget -= 1; + if (!std.mem.eql(u8, line, a.shell[k])) continue; + a.cursor = k + 1; + return .{ .row = @intCast(k) }; + } + // No row spells this line, so it is either the row the alignment offers + // WITH AN EDIT IN IT, or text typed from nothing. The bytes shared at + // the two ends decide which — and, when it is an edit, exactly how much + // of the row's colour the line still has a right to. + if (a.cursor >= a.end) return null; + const shell = a.shell[a.cursor]; + var p: usize = 0; + while (p < line.len and p < shell.len and line[p] == shell[p]) p += 1; + var s: usize = 0; + const room = @min(line.len, shell.len) - p; + while (s < room and line[line.len - 1 - s] == shell[shell.len - 1 - s]) s += 1; + if (p + s == 0) return null; + // Accept when the row accounts for the whole LINE (nothing was typed; + // the line is a piece of the row, which is the top half of a split), or + // when most of the ROW survived in it (an ordinary edit). Otherwise this + // is new text that happens to share an edge with its neighbour, and + // colouring it would hand it a colour that was never its own. + if (line.len != p + s and shell.len - (p + s) > shell.len / 2) return null; + const row = a.cursor; + // A line that stopped short of the row's END leaves the rest of that row + // to the NEXT line. Splitting a row in two is exactly that, and it is + // why the bottom half can still find its colours: they are in the tail + // of the row the top half only partly covered. + if (s > 0 or p >= shell.len) a.cursor += 1; + return .{ .row = @intCast(row), .prefix = p, .suffix = s, .shell_len = shell.len }; + } +}; + +/// WHERE a body row's colours come from, and HOW MUCH of the row they cover. +/// +/// A row whose text is the grid's own takes the grid's colours end to end. A +/// row the user has EDITED still holds the row's own bytes at its two ends — +/// they are the same bytes, provably — and those keep their colours; only what +/// was typed between them has no cell under it and so takes none. Dropping the +/// whole row instead was the loudest colour bug in the editor: one keystroke +/// that changed one character's case turned every column of a coloured row +/// grey. +const Anchor = struct { + /// the row, absolute while it comes from `EditAnchors`, viewport once + /// `recolorAnsi` has subtracted the walk's offset + row: i32, + /// bytes at the START of the line that are still the row's own, and bytes at + /// its END. The default says ALL of it: an exact match, or a `.grid` row, + /// which is the grid's text by construction. + prefix: usize = std.math.maxInt(usize), + suffix: usize = 0, + /// the row's own dumped length — what the suffix is measured from on the + /// GRID side, where the edit may have changed the byte count + shell_len: usize = 0, + + fn whole(an: Anchor) bool { + return an.prefix == std.math.maxInt(usize); + } +}; + /// tty colors: recolor each visible body cell from the emulator's own style so -/// raw output keeps its ansi colors. Runs ONLY in tty mode (the caller gates -/// it) and reads the live viewport row for row: normal/insert editing shows -/// plain text, so nothing an edit does can move a shell row's colour. -pub fn recolorAnsi(p: *Pardes, pane: *Pane, r: pardes.Rect, tx: u16, tw: u16, body_h: u16) void { +/// raw output keeps its ansi colors — in EVERY mode, not just `.tty`, because a +/// body row's colour has the same origin its text does and `BodyWalk` already +/// knows it. +/// +/// Editing moves shell rows around: the edit buffer's lines stand in for the +/// rows it covers, so everything below slides, and `promptRow` left-hugs a +/// prompt row so what was typed starts at column 0. A colour therefore needs +/// exactly two translations, and takes each from the pass that made it: +/// +/// * ROW — step `BodyWalk`, the same iterator `fillBody` steps. A `.grid` row +/// names the viewport row whose bytes were drawn; an `.edit` row is the +/// user's own text with no shell row underneath, so it keeps the body style. +/// Sharing the walk is load-bearing: deriving the anchor independently (from +/// `Pane.gridRow`) put colours one row off wherever that arithmetic and this +/// walk disagreed about a buffer's height. +/// * COLUMN — pair the PRINTED graphemes with the grid cells that spelled them, +/// starting at the cell `promptCut` says the hug dropped to. Not `cut + c`: +/// the two sides disagree about how many columns a cluster is worth (ghostty +/// splits `👨�👩�👧` across three wide cells and spells it once; this surface +/// prints that one grapheme two columns wide), so column arithmetic walks off +/// the glyph it means and every cell after it wears a neighbour's colour. +/// `body` is the very text the caller just printed, which is what makes the +/// pairing exact rather than a second guess at clustering. +/// +/// In tty mode the buffer is not applied and no prompt is hugged, so the row +/// anchor collapses to the viewport row. That is not quite "as it always did": +/// the walk starts at `Pane.gridRow(pane.scroll())` like `fillBody`, so where a +/// stale buffer skews that start, the colours now follow the text instead of +/// silently disagreeing with it. +pub fn recolorAnsi(p: *Pardes, pane: *Pane, r: pardes.Rect, tx: u16, tw: u16, body_h: u16, body: []const u8) void { // No emulator, no ANSI cells: the whole pass — and the 256-colour theme // projection behind it — is compiled out. if (comptime !enabled) return; @@ -1172,29 +1481,215 @@ pub fn recolorAnsi(p: *Pardes, pane: *Pane, r: pardes.Rect, tx: u16, tw: u16, bo filtered_storage = FilteredColors.init(p, pane); break :blk &filtered_storage; } else null; + // DECSCNM, read once: the filtered palette folds it in itself, the raw + // path needs it per cell. + const scnm = pane.vt.modes.get(.reverse_colors); + const pages = &pane.vt.screens.active.pages; + // The text pass bounds its rows by the dump it was handed; this one has the + // live viewport, so it bounds by the viewport's own height. Both bounds + // exist for the same reason and NEITHER is `pin`: `PageList.pin` resolves a + // viewport row by walking DOWN the pagelist, so a viewport scrolled back + // answers happily for rows below its bottom edge — which painted the + // scrollback's colours onto rows the text pass had left blank. + const vp_rows: i32 = @intCast(scrollbar(pane).len); + // Which buffer lines the user has not actually changed, so a row swallowed + // by a growing buffer keeps the colour it still stands over. + var anchors: EditAnchors = if (pane.mode != .tty) + if (pane.ovl) |o| .init(p, pane, o) else .{} + else + .{}; + var walk: BodyWalk = .init(pane); + // The printed body, one line per body row, stepped ONCE per row alongside + // the walk. Asking for the Nth line per row instead re-scanned the whole + // body every time, which made a tall pane's render superlinear. + var lines = std.mem.splitScalar(u8, body, '\n'); var vr: u16 = 0; - while (vr < body_h and vr < pane.rows) : (vr += 1) { - var c: u16 = 0; - while (c < tw) : (c += 1) { - const ci = pane.vt.screens.active.pages.getCell(.{ .viewport = .{ .x = @intCast(c), .y = @intCast(vr) } }) orelse continue; - // Ghostty gives a wide glyph's spacer tail the head's style id; - // ordinary projection leaves it, a filter must repaint it too. - if (ci.cell.wide == .spacer_tail and filtered == null) continue; - const cell = s.at(tx + c, body_y + vr); - // sparse projection: bodyText already painted every glyph, so only - // a filter (which theme-keys blank/default cells too) touches these. - if (cell.default and filtered == null) continue; - cell.default = false; - cell.style = cellStyle(p, ci, filtered); + while (walk.next()) |row| : (vr += 1) { + if (vr >= body_h) break; + // Before any early exit below, or the lines fall out of step with rows. + const text = lines.next() orelse ""; + const anchor: Anchor = switch (row) { + // A line the user typed from nothing has no cell under it; one they + // only had swallowed, or edited a piece of, still names the row its + // bytes came from and how much of it is still that row's. + .edit => |e| blk: { + var an = anchors.shellRow(e.idx) orelse continue; + an.row -= walk.goff; + break :blk an; + }, + .grid => |v| .{ .row = v }, + }; + const vi = anchor.row; + if (vi < 0 or vi >= vp_rows) continue; + const row_pin = pages.pin(.{ .viewport = .{ .y = @intCast(vi) } }) orelse continue; + // The prompt the text pass dropped, added back as a starting CELL. + // Gated on the ROW FLAG first, exactly as `bodyText` gates `promptRow`: + // `promptCut` answers for the whole row under + // `config.tty_blank == .prompt_and_input`, so asking it about a row the + // text pass never asked about would blank colours nobody hid. + const cut: u16 = if (pane.mode == .tty) 0 else cut: { + if (row_pin.rowAndCell().row.semantic_prompt == .none) break :cut 0; + break :cut switch (promptCut(row_pin)) { + .keep => 0, + // Blanked end to end: the row shows nothing of the grid, so + // projecting the prompt's own colours onto it would be a lie. + .blank => continue, + .cut => |n| std.math.cast(u16, n) orelse continue, + }; + }; + // The text this row printed is walked grapheme by grapheme alongside the + // cells that spelled it. Both walks are driven by real data — the + // printed bytes and the cells' own dumped byte counts — so neither has + // to guess how many columns the other gives a cluster. + // The hug can empty a row outright: a prompt whose command did not fit + // leaves ghostty a styled spacer and nothing printable. The row DRAWS + // nothing, so nothing on it may take the grid's colour — the same + // reasoning as `.blank` above, reached by a different route. + if (cut > 0 and text.len == 0) continue; + var at: usize = 0; + var sc: u16 = 0; + var gc: u16 = cut; + // Shell bytes crossed so far, which is how the row's TAIL is found again + // after an edit: the printed text and the grid agree byte for byte over + // `prefix` and over `suffix`, and nowhere in between. + var sb: usize = 0; + const mine_from = @min(anchor.prefix, text.len); + const mine_to = text.len - @min(anchor.suffix, text.len); + var crossed = false; + while (at < text.len and sc < tw) { + const stop = modal.nextGrapheme(text, at); + if (stop <= at) break; + // What the glyph occupies HERE: `print` leaves an empty cell under a + // double-width one, and `fill` writes a space, so a zero-length cell + // is a spacer and nothing else. It is a property of the SURFACE, so + // it is known before any cell is consumed — which is what lets the + // user's own text spend its columns without spending the row's. + // + // This rule assumes the printed text holds no `\t` and no `\r`: + // `Surface.print` expands a tab into `config.tab_width` cells and + // draws nothing at all for a carriage return, either of which would + // slide every later colour on the row. The assumption is ghostty's, + // not ours — its row dump expands tabs to real spaces and replaces + // undecodable bytes with U+FFFD — so it holds for anything sourced + // from the grid, and an anchor only ever covers bytes that ARE such + // a row's. Feed this text from anywhere else and the span rule is + // the thing that breaks first. + const span: u16 = if (sc + 1 < tw and s.at(tx + sc + 1, body_y + vr).len == 0) 2 else 1; + // Between the row's own two ends lie the bytes the user typed. No + // cell spelled them, so they take no colour and spend no grid + // column: the row's tail then still lines up with the line's tail. + if (at >= mine_from and at < mine_to) { + sc += span; + at = stop; + continue; + } + // Crossing back into the row's own bytes: step over the cells whose + // bytes the edit replaced. `shell_len - suffix` is where the row's + // own tail starts on the GRID side, which is not where it starts in + // the line whenever the edit changed the byte count. + if (at >= mine_to and !crossed) { + crossed = true; + const upto = anchor.shell_len - @min(anchor.suffix, anchor.shell_len); + while (sb < upto) { + const ci = pages.getCell(.{ .viewport = .{ .x = gc, .y = @intCast(vi) } }) orelse break; + sb += dumpedBytes(row_pin, ci.cell); + gc = std.math.add(u16, gc, 1) catch break; + } + } + const want = stop - at; + // Consume every cell that contributed to this grapheme. A cluster + // ghostty split across several cells is still ONE printed glyph. + var covered: usize = 0; + var style: ?pardes.CellStyle = null; + while (covered < want) { + const ci = pages.getCell(.{ .viewport = .{ .x = gc, .y = @intCast(vi) } }) orelse break; + if (style == null and ci.cell.wide != .spacer_tail and ci.cell.wide != .spacer_head) + style = cellStyle(p, ci, filtered, scnm); + covered += dumpedBytes(row_pin, ci.cell); + gc = std.math.add(u16, gc, 1) catch break; + // A spacer contributes no bytes; without this the loop would + // spin on a row that ends in one. + if (covered == 0 and gc >= pane.cols) break; + } + // A wide cell's tail contributes NO bytes, so the loop above stops + // on it rather than past it. Step over any tail now: leaving `gc` on + // one pairs the next surface column with the cell before it, which + // left an unpainted hole beside a row-final CJK glyph and pushed + // every colour after it one column right. + while (pages.getCell(.{ .viewport = .{ .x = gc, .y = @intCast(vi) } })) |t| { + if (t.cell.wide != .spacer_tail) break; + gc = std.math.add(u16, gc, 1) catch break; + } + if (style) |st| for (0..span) |k| { + const cell = s.at(tx + sc + @as(u16, @intCast(k)), body_y + vr); + // sparse projection: bodyText already painted every glyph, so + // only a filter (which theme-keys blank/default cells too) + // touches these. + if (cell.default and filtered == null) continue; + cell.default = false; + cell.style = st; + }; + sb += covered; + sc += span; + at = stop; + } + // Past the text: the row's remaining cells carry colour but no glyph + // (an erase-to-end-of-line under a background). One cell, one column + // from here, with two exceptions on the grid side. + // + // Only a row that ENDS in the row's own bytes may ask what lies past + // them. Where the user's own text runs to the end of the line, the next + // cells still spell bytes the edit removed, and painting the line's + // margin from those would dress it in the colours of text that is no + // longer there. + var tail: ?pardes.CellStyle = null; + if (anchor.whole() or anchor.suffix > 0) { + while (sc < tw) { + const ci = pages.getCell(.{ .viewport = .{ .x = gc, .y = @intCast(vi) } }) orelse break; + gc = std.math.add(u16, gc, 1) catch break; + // A TAIL spells nothing and owns no column of its own, so it + // moves the grid on without spending a surface column. A HEAD + // does own its column — it is the gap ghostty leaves where a + // wide glyph would not fit, and it carries the row's background + // — so it is painted like any other cell. Skipping it left the + // last column of a coloured row bare, because a head is by + // construction that row's final cell. + if (ci.cell.wide == .spacer_tail) continue; + const cell = s.at(tx + sc, body_y + vr); + sc += 1; + const style = cellStyle(p, ci, filtered, scnm); + tail = style; + if (cell.default and filtered == null) continue; + cell.default = false; + cell.style = style; + } + // The grid can run out before the surface does: a cluster ghostty + // spends four cells on may print in two columns here, so a row + // ending in one has columns with no cell left to ask. The row's + // background does reach its edge on the grid, so carry the last + // cell's answer across rather than leaving a notch of pane colour at + // the margin. + if (tail) |style| while (sc < tw) : (sc += 1) { + const cell = s.at(tx + sc, body_y + vr); + if (cell.default and filtered == null) continue; + cell.default = false; + cell.style = style; + }; } } } -fn cellStyle(p: *Pardes, ci: ghostty_vt.PageList.Cell, filtered: ?*FilteredColors) pardes.CellStyle { +/// `scnm` is DECSCNM (`\x1b[?5h`), which swaps only the terminal's DEFAULT +/// colour roles — explicit SGR colours stay explicit. `FilteredColors` applies +/// it by swapping the theme's two defaults; the raw path resolves a `.none` +/// colour through `ghostColor`, whose `is_bg` argument chooses which default it +/// means, so flipping that argument is the same swap. Without it reverse video +/// simply vanished whenever `tty_filter` was off. +fn cellStyle(p: *Pardes, ci: ghostty_vt.PageList.Cell, filtered: ?*FilteredColors, scnm: bool) pardes.CellStyle { const style = ci.style(); var cs: pardes.CellStyle = .{ - .fg = if (filtered) |colors| colors.fg(style) else ghostColor(p, style.fg_color, false), - .bg = if (filtered) |colors| colors.bg(style, ci.cell) else ghostColor(p, style.bg_color, true), + .fg = if (filtered) |colors| colors.fg(style) else ghostColor(p, style.fg_color, scnm), + .bg = if (filtered) |colors| colors.bg(style, ci.cell) else ghostColor(p, style.bg_color, !scnm), .bold = style.flags.bold, .dim = style.flags.faint, .italic = style.flags.italic, @@ -1534,7 +2029,7 @@ test "terminal Filter preserves exact palette-null light theme default roles" { try testing.expectEqual(pardes.Color{ .rgb = light.bg.? }, reversed.at(tx, body_y).style.fg); try testing.expectEqual(pardes.Color{ .rgb = light.fg.? }, reversed.at(tx, body_y).style.bg); } -test "tty ansi colors render only in tty mode, never in normal mode" { +test "tty ansi colors follow the prompt hug into normal mode" { const testing = std.testing; const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 18, .rows = 6 }); defer p.deinit(); @@ -1559,15 +2054,777 @@ test "tty ansi colors render only in tty mode, never in normal mode" { try testing.expectEqual(red, tty.at(tx + 2, body_y).style.fg); try testing.expectEqual(blue, tty.at(tx + 3, body_y).style.fg); - // normal mode is a plain editing view: no ansi projection at all, so an - // edit made here cannot change what tty mode renders. + // Normal mode hugs the prompt away, so `R` starts at column 0 — and its + // colour comes with it. The two cells the prompt occupied are the COLUMN + // anchor `promptCut` hands back, which is the only reason the red lands on + // the R the user can see instead of two cells to the right of it. pane.mode = .normal; p.shell_rows.stale = true; _ = frame.reset(.retain_capacity); const norm = try p.render(frame.allocator()); try testing.expectEqualStrings("R", norm.at(tx, body_y).grapheme()); - try testing.expect(!std.meta.eql(red, norm.at(tx, body_y).style.fg)); - try testing.expect(!std.meta.eql(blue, norm.at(tx + 1, body_y).style.fg)); + try testing.expectEqualStrings("B", norm.at(tx + 1, body_y).grapheme()); + try testing.expectEqual(red, norm.at(tx, body_y).style.fg); + try testing.expectEqual(blue, norm.at(tx + 1, body_y).style.fg); +} + +test "an edit buffer slides shell rows and their colors together" { + const testing = std.testing; + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 18, .rows = 12 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = false; + pane.mode = .normal; + + p.update(.{ .output = .{ .pane = 0, .bytes = "\x1b[31mAAA\x1b[0m\r\n\x1b[32mBBB\x1b[0m\r\n\x1b[34mCCC\x1b[0m" } }); + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + const red: pardes.Color = .{ .index = 1 }; + const green: pardes.Color = .{ .index = 2 }; + const blue: pardes.Color = .{ .index = 4 }; + + p.shell_rows.stale = true; + const before = try p.render(frame.allocator()); + try testing.expectEqual(red, before.at(tx, body_y).style.fg); + try testing.expectEqual(green, before.at(tx, body_y + 1).style.fg); + try testing.expectEqual(blue, before.at(tx, body_y + 2).style.fg); + + // Four lines of typed text standing in for the ONE shell row `AAA` was: + // every row below slides down by three, and `surfRow` is the arithmetic + // that says so. The colours have to take the same three rows, or `BBB` + // would be painted green three rows above where it is now drawn. + pane.ovl = .{ .row = 0, .rows = 1, .text = try p.gpa.dupe(u8, "e\nd\ni\nt") }; + p.shell_rows.stale = true; + _ = frame.reset(.retain_capacity); + const after = try p.render(frame.allocator()); + + try testing.expectEqualStrings("B", after.at(tx, body_y + 4).grapheme()); + try testing.expectEqualStrings("C", after.at(tx, body_y + 5).grapheme()); + try testing.expectEqual(green, after.at(tx, body_y + 4).style.fg); + try testing.expectEqual(blue, after.at(tx, body_y + 5).style.fg); + + // ...and the rows the user typed are the user's own text: no shell row + // sits under them, so nothing projects a colour onto them. + for (0..4) |i| { + const cell = after.at(tx, body_y + @as(u16, @intCast(i))); + try testing.expect(!std.meta.eql(red, cell.style.fg)); + try testing.expect(!std.meta.eql(green, cell.style.fg)); + try testing.expect(!std.meta.eql(blue, cell.style.fg)); + } +} + +test "a combining mark in the prompt keeps the command and its colors aligned" { + const testing = std.testing; + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 18, .rows = 6 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = false; + pane.mode = .normal; + + // A ONE-cell prompt carrying a combining mark — an NFD `e` — then `ABC` + // typed at it. The cell walk that finds the prompt's end must step ONE + // grapheme for that cell, not one per stored codepoint: stepping twice ate + // the `A`, and left every colour a cell to the left of its glyph with the + // last one stranded on a blank. + p.update(.{ .output = .{ .pane = 0, .bytes = "\x1b]133;A\x1b\\\x1b[32me\u{301}\x1b]133;B\x1b\\\x1b[31mA\x1b[34mB\x1b[35mC" } }); + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + + p.shell_rows.stale = true; + const s = try p.render(frame.allocator()); + try testing.expectEqualStrings("A", s.at(tx, body_y).grapheme()); + try testing.expectEqualStrings("B", s.at(tx + 1, body_y).grapheme()); + try testing.expectEqualStrings("C", s.at(tx + 2, body_y).grapheme()); + try testing.expectEqual(pardes.Color{ .index = 1 }, s.at(tx, body_y).style.fg); + try testing.expectEqual(pardes.Color{ .index = 4 }, s.at(tx + 1, body_y).style.fg); + try testing.expectEqual(pardes.Color{ .index = 5 }, s.at(tx + 2, body_y).style.fg); + // ...and no colour past the end of what the row actually says + try testing.expect(!std.meta.eql(pardes.Color{ .index = 5 }, s.at(tx + 3, body_y).style.fg)); +} + +test "colors are never taken from shell rows below the viewport" { + const testing = std.testing; + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 18, .rows = 14 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + // Raw palette, so a leaked background reads back as `.index` — the theme + // filter would repaint every blank cell and hide the evidence. + pane.tty_filter = false; + pane.mode = .normal; + + // Sixty rows, each a distinct background, so a leaked colour names the row + // it leaked from. + for (0..60) |i| { + var buf: [32]u8 = undefined; + const bytes = std.fmt.bufPrint(&buf, "\x1b[4{d}mL{d:0>2}\x1b[0m\r\n", .{ (i % 6) + 1, i }) catch unreachable; + p.update(.{ .output = .{ .pane = 0, .bytes = bytes } }); + } + p.shell_rows.stale = true; + scrollGrid(pane, -20); + + // ONE buffer line standing in for SIX shell rows: everything below slides + // UP five, so the last rows of the body resolve past the viewport's bottom + // edge. `PageList.pin` answers for those rows anyway — it walks down the + // pagelist, not the viewport — so without a bound of its own this pass + // painted the scrollback's colours onto rows the text pass left blank. + const anchor = gridOffset(pane); + pane.ovl = .{ .row = anchor, .rows = 6, .text = try p.gpa.dupe(u8, "one") }; + p.shell_rows.stale = true; + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + const body_h = r.h - pardes.BOX_H; + + p.shell_rows.stale = true; + const s = try p.render(frame.allocator()); + // A body row the text pass left blank has no shell row under it, so no + // ANSI background may have reached it. Every colour in the payload above is + // an indexed one, so a leak is exactly an `.index` background on a blank row. + var vr: u16 = 0; + while (vr < body_h) : (vr += 1) { + var blank = true; + var c: u16 = 0; + while (c < r.w -| config.GUTTER) : (c += 1) { + if (!std.mem.eql(u8, " ", s.at(tx + c, body_y + vr).grapheme())) blank = false; + } + if (!blank) continue; + c = 0; + while (c < r.w -| config.GUTTER) : (c += 1) { + const bg = s.at(tx + c, body_y + vr).style.bg; + try testing.expect(std.meta.activeTag(bg) != .index); + } + } +} + +test "a row the edit buffer only swallowed keeps its color" { + const testing = std.testing; + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 18, .rows = 12 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = false; + pane.mode = .normal; + + p.update(.{ .output = .{ .pane = 0, .bytes = "\x1b[31mAAA\x1b[0m\r\n\x1b[32mBBB\x1b[0m\r\n\x1b[34mCCC\x1b[0m" } }); + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + const red: pardes.Color = .{ .index = 1 }; + const green: pardes.Color = .{ .index = 2 }; + const blue: pardes.Color = .{ .index = 4 }; + + // The buffer only ever grows, so after a few edits it covers rows nobody + // touched. Here it spans all three and only the MIDDLE line differs: the + // first and last are still byte-identical to the shell rows they were + // seeded from, so they still stand over them and keep their colours. + pane.ovl = .{ .row = 0, .rows = 3, .text = try p.gpa.dupe(u8, "AAA\nXXX\nCCC") }; + p.shell_rows.stale = true; + const s = try p.render(frame.allocator()); + + try testing.expectEqualStrings("A", s.at(tx, body_y).grapheme()); + try testing.expectEqualStrings("X", s.at(tx, body_y + 1).grapheme()); + try testing.expectEqualStrings("C", s.at(tx, body_y + 2).grapheme()); + try testing.expectEqual(red, s.at(tx, body_y).style.fg); + try testing.expectEqual(blue, s.at(tx, body_y + 2).style.fg); + // ...and the line that actually changed is the user's own text now + try testing.expect(!std.meta.eql(green, s.at(tx, body_y + 1).style.fg)); +} + +test "an edit buffer reaching past the dumped rows colors nothing from row zero" { + const testing = std.testing; + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 18, .rows = 12 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = false; + pane.mode = .normal; + + p.update(.{ .output = .{ .pane = 0, .bytes = "\x1b[31mAAA\x1b[0m\r\n\x1b[32mBBB\x1b[0m\r\n\x1b[34mCCC\x1b[0m" } }); + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + const red: pardes.Color = .{ .index = 1 }; + + // Covers far more rows than the grid was ever dumped for, so the anchor + // table cannot be built and answers "no shell row" for every line. The + // zeroed table must not read as "the last line sits on the buffer's first + // row", which claimed row zero's colour and underflowed on every line after. + pane.ovl = .{ .row = 1, .rows = 50, .text = try p.gpa.dupe(u8, "p\nq\nr") }; + p.shell_rows.stale = true; + const s = try p.render(frame.allocator()); + + try testing.expectEqualStrings("p", s.at(tx, body_y + 1).grapheme()); + var i: u16 = 1; + while (i <= 3) : (i += 1) { + try testing.expect(!std.meta.eql(red, s.at(tx, body_y + i).style.fg)); + } +} + +/// TTY MODE IS THE ORACLE. It paints the viewport row for row and column for +/// column, so whatever it shows on a glyph is what that glyph's colour IS. +/// Normal mode may move a glyph LEFT (the prompt hug) but must never change its +/// colour, so the comparison aligns by glyph rather than by column: for each +/// row the shift is recovered by finding where normal mode's glyph run sits in +/// tty mode's, without asking the code under test what it did. +/// +/// Returns the number of cells whose style disagrees; `note` labels the report. +fn modeStyleDiffs(p: *Pardes, pane: *Pane, gpa: std.mem.Allocator, note: []const u8) !usize { + var frame = std.heap.ArenaAllocator.init(gpa); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + const rows: usize = r.h - pardes.BOX_H; + const cols: usize = r.w -| config.GUTTER; + + const Snap = struct { text: [][7]u8, len: []u8, style: []pardes.CellStyle }; + const glyphAt = struct { + fn f(sn: Snap, i: usize) []const u8 { + return sn.text[i][0..sn.len[i]]; + } + }.f; + var shot: [2]Snap = undefined; + for (&shot) |*sn| { + sn.text = try gpa.alloc([7]u8, rows * cols); + sn.len = try gpa.alloc(u8, rows * cols); + sn.style = try gpa.alloc(pardes.CellStyle, rows * cols); + } + defer for (&shot) |*sn| { + gpa.free(sn.text); + gpa.free(sn.len); + gpa.free(sn.style); + }; + + for ([_]pardes.Mode{ .tty, .normal }, 0..) |mode, i| { + pane.mode = mode; + p.shell_rows.stale = true; + _ = frame.reset(.retain_capacity); + const s = try p.render(frame.allocator()); + for (0..rows) |row| for (0..cols) |col| { + const cell = s.at(tx + @as(u16, @intCast(col)), body_y + @as(u16, @intCast(row))); + shot[i].text[row * cols + col] = cell.text; + shot[i].len[row * cols + col] = cell.len; + shot[i].style[row * cols + col] = cell.style; + }; + } + + var diffs: usize = 0; + for (0..rows) |row| { + const base = row * cols; + // The glyph run normal mode shows, and where it ends. + var last: ?usize = null; + for (0..cols) |col| { + if (!std.mem.eql(u8, glyphAt(shot[1], base + col), " ")) last = col; + } + const end = last orelse continue; // blank row: nothing to align + + // Recover the shift: the first offset at which tty mode spells the same + // run. Zero for every row no prompt was hugged out of. + const shift = shift: { + var s: usize = 0; + while (s + end < cols) : (s += 1) { + var all = true; + for (0..end + 1) |col| { + if (!std.mem.eql(u8, glyphAt(shot[1], base + col), glyphAt(shot[0], base + col + s))) { + all = false; + break; + } + } + if (all) break :shift s; + } + var tty_row: [256]u8 = undefined; + var nrm_row: [256]u8 = undefined; + var tn: usize = 0; + var nn: usize = 0; + for (0..cols) |col| { + const tg = glyphAt(shot[0], base + col); + const ng = glyphAt(shot[1], base + col); + if (tn + tg.len < tty_row.len) { + @memcpy(tty_row[tn..][0..tg.len], tg); + tn += tg.len; + } + if (nn + ng.len < nrm_row.len) { + @memcpy(nrm_row[nn..][0..ng.len], ng); + nn += ng.len; + } + } + std.debug.print("\n[{s}] row {d} unalignable\n tty: '{s}'\nnormal: '{s}'\n", .{ note, row, tty_row[0..tn], nrm_row[0..nn] }); + diffs += 1; + break :shift null; + } orelse continue; + + // Every column the shift can reach, not just the ones holding a glyph: + // a cell with a background and no text (`\x1b[41m\x1b[K`, a padded + // table cell) carries colour too, and is exactly what a shell paints + // most of. + for (0..cols - shift) |col| { + const want = shot[0].style[base + col + shift]; + const got = shot[1].style[base + col]; + if (std.meta.eql(want, got)) continue; + if (diffs < 6) std.debug.print( + "\n[{s}] row {d} col {d} (shift {d}) glyph '{s}': tty fg={any} bg={any} rev={} ul={any} | normal fg={any} bg={any} rev={} ul={any}", + .{ note, row, col, shift, glyphAt(shot[1], base + col), want.fg, want.bg, want.reverse, want.ul, got.fg, got.bg, got.reverse, got.ul }, + ); + diffs += 1; + } + } + if (diffs > 0) std.debug.print("\n[{s}] {d} style mismatches\n", .{ note, diffs }); + return diffs; +} + +test "a prompted session keeps every glyph's color in normal mode" { + const testing = std.testing; + const payload = + "\x1b]133;A\x1b\\\x1b[32muser\x1b[34m@host\x1b[35m ~/dir\x1b[0m$ \x1b]133;B\x1b\\\x1b[36mls \x1b[33m-la\x1b[0m\r\n" ++ + "\x1b[34mdir1\x1b[0m \x1b[32mexec\x1b[0m plain.txt\r\n" ++ + "\x1b[31merror: nope\x1b[0m\r\n" ++ + "\x1b]133;A\x1b\\\x1b[32muser\x1b[34m@host\x1b[35m ~/dir\x1b[0m$ \x1b]133;B\x1b\\\x1b[36mecho \x1b[1;37mhi\x1b[0m\r\n" ++ + "\x1b[38;5;208mhi\x1b[0m\r\n"; + + for ([_]bool{ false, true }) |filter| { + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 44, .rows = 12 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = filter; + p.update(.{ .output = .{ .pane = 0, .bytes = payload } }); + const diffs = try modeStyleDiffs(p, pane, testing.allocator, if (filter) "session filter=on" else "session filter=off"); + try testing.expectEqual(@as(usize, 0), diffs); + } +} + +test "an emoji prompt neither eats the command nor slides its colors" { + const testing = std.testing; + // ABSOLUTE assertions, not a tty/normal comparison: ghostty and this + // surface can BOTH be wrong about a cluster's width, and then a differential + // agrees with itself while the user sees the wrong thing. What is typed at + // the prompt is what must appear, each character wearing its own colour. + // + // Ghostty splits these clusters across cells and spells each one in the row + // dump, so the cell walk and the byte walk only agree if the byte walk is + // driven by what each CELL contributed. `👨�💻` is two wide cells, `👨�👩�👧` + // three, `🇺🇸` two, `��` two, while all of them print as one glyph here. + const prompts = [_][]const u8{ + "plain", + "\u{1F468}\u{200D}\u{1F4BB}", // technologist + "\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}", // family + "\u{1F1FA}\u{1F1F8}", // flag + "\u{1F44D}\u{1F3FD}", // thumbs up, skin tone + "\u{2764}\u{FE0F}", // heart, VS16 + "\u{0031}\u{FE0F}\u{20E3}", // keycap + "\u{754C}", // CJK wide + "e\u{301}", // NFD + }; + for (prompts) |prompt| { + for ([_]bool{ false, true }) |filter| { + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 24, .rows = 6 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = filter; + pane.mode = .normal; + + var buf: [256]u8 = undefined; + const bytes = try std.fmt.bufPrint( + &buf, + "\x1b]133;A\x1b\\\x1b[32m{s}$ \x1b]133;B\x1b\\\x1b[31mab\x1b[34mcd\x1b[0m", + .{prompt}, + ); + p.update(.{ .output = .{ .pane = 0, .bytes = bytes } }); + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + p.shell_rows.stale = true; + const s = try p.render(frame.allocator()); + + for ([_][]const u8{ "a", "b", "c", "d" }, 0..) |want, i| { + const cell = s.at(tx + @as(u16, @intCast(i)), body_y); + testing.expectEqualStrings(want, cell.grapheme()) catch |err| { + std.debug.print("\nprompt '{s}' filter={}: col {d}\n", .{ prompt, filter, i }); + return err; + }; + } + // `ab` was printed red and `cd` blue, so whatever the theme does + // with those two runs, the pair boundary has to fall between `b` + // and `c`. A prompt that cost the row a character shows up here as + // the boundary sliding onto the wrong glyph. + const fg = [_]pardes.Color{ + s.at(tx, body_y).style.fg, + s.at(tx + 1, body_y).style.fg, + s.at(tx + 2, body_y).style.fg, + s.at(tx + 3, body_y).style.fg, + }; + errdefer std.debug.print("\nprompt '{s}' filter={}: fg {any}\n", .{ prompt, filter, fg }); + try testing.expect(std.meta.eql(fg[0], fg[1])); + try testing.expect(std.meta.eql(fg[2], fg[3])); + try testing.expect(!std.meta.eql(fg[1], fg[2])); + if (!filter) { + try testing.expectEqual(pardes.Color{ .index = 1 }, fg[0]); + try testing.expectEqual(pardes.Color{ .index = 4 }, fg[2]); + } + } + } +} + +test "background-only cells keep their color through the prompt hug" { + const testing = std.testing; + // Backgrounds with no glyph under them are most of what a shell paints: + // erase-to-end-of-line after a colour is set, padded table cells, and a + // selected row. They have no text to align on, so they are the cells a + // column translation is most likely to lose. + const payload = + "\x1b]133;A\x1b\\\x1b[32mp\x1b[0m$ \x1b]133;B\x1b\\cmd\x1b[41m\x1b[K\r\n" ++ + "\x1b[44mblue-bg\x1b[K\x1b[0m\r\n" ++ + "a\x1b[42m \x1b[0mb\r\n" ++ + "\x1b[100;97mbright-on-grey\x1b[0m\r\n" ++ + "\x1b]133;A\x1b\\\x1b[35m>>\x1b[0m \x1b]133;B\x1b\\\x1b[48;5;19mrun\x1b[K\x1b[0m\r\n" ++ + "\x1b[48;2;90;10;10mtruecolor-bg\x1b[K\x1b[0m\r\n"; + + for ([_]bool{ false, true }) |filter| { + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 30, .rows = 12 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = filter; + p.update(.{ .output = .{ .pane = 0, .bytes = payload } }); + const diffs = try modeStyleDiffs(p, pane, testing.allocator, if (filter) "bg filter=on" else "bg filter=off"); + try testing.expectEqual(@as(usize, 0), diffs); + } +} + +test "a leftover edit buffer does not move what tty mode shows" { + const testing = std.testing; + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 40, .rows = 14 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = false; + pane.mode = .tty; + + for (0..60) |i| { + var buf: [40]u8 = undefined; + const bytes = std.fmt.bufPrint(&buf, "\x1b[3{d}mL{d:0>2}\x1b[0m\r\n", .{ (i % 6) + 1, i }) catch unreachable; + p.update(.{ .output = .{ .pane = 0, .bytes = bytes } }); + } + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + const rows: usize = r.h - pardes.BOX_H; + const cols: usize = r.w -| config.GUTTER; + + // What tty mode shows with nothing left behind: the reference. + p.shell_rows.stale = true; + const clean = try p.render(frame.allocator()); + const want_text = try testing.allocator.alloc([7]u8, rows * cols); + defer testing.allocator.free(want_text); + const want_fg = try testing.allocator.alloc(pardes.Color, rows * cols); + defer testing.allocator.free(want_fg); + for (0..rows) |row| for (0..cols) |col| { + const cell = clean.at(tx + @as(u16, @intCast(col)), body_y + @as(u16, @intCast(row))); + want_text[row * cols + col] = cell.text; + want_fg[row * cols + col] = cell.style.fg; + }; + + // `enterTty` clears every other modal remnant but leaves the edit buffer, so + // a buffer whose covered span STRADDLES the viewport top is an ordinary + // state. tty mode does not apply the buffer, so it must not be moved by one + // either — and `surfRow`/`gridRow` are not inverses across that span. + const anchor = gridOffset(pane); + pane.ovl = .{ .row = anchor - 1, .rows = 4, .text = try p.gpa.dupe(u8, "one\ntwo") }; + p.shell_rows.stale = true; + _ = frame.reset(.retain_capacity); + const after = try p.render(frame.allocator()); + + for (0..rows) |row| for (0..cols) |col| { + const cell = after.at(tx + @as(u16, @intCast(col)), body_y + @as(u16, @intCast(row))); + try testing.expectEqualStrings( + std.mem.sliceTo(&want_text[row * cols + col], 0), + std.mem.sliceTo(&cell.text, 0), + ); + try testing.expectEqual(want_fg[row * cols + col], cell.style.fg); + }; +} + +test "a background after a row-final wide glyph lands on the right columns" { + const testing = std.testing; + // A CJK glyph then a coloured erase-to-end-of-line, with a second colour + // partway. The glyph's grid tail spells no bytes, so the pairing walk used + // to stop ON it and pair every later column with the cell before it: an + // unpainted hole beside the glyph and every boundary one column right. + // + // ABSOLUTE assertions: both modes were wrong identically here, so a + // tty/normal differential says nothing. + for ([_]bool{ false, true }) |filter| { + for ([_]pardes.Mode{ .tty, .normal }) |mode| { + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 12, .rows = 8 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = filter; + pane.mode = mode; + p.update(.{ .output = .{ .pane = 0, .bytes = "\x1b[32m\u{754C}\x1b[41m\x1b[K\x1b[7G\x1b[44m\x1b[K\r\n" } }); + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + p.shell_rows.stale = true; + const s = try p.render(frame.allocator()); + + try testing.expectEqualStrings("\u{754C}", s.at(tx, body_y).grapheme()); + // The glyph covers columns 0-1; red runs from 2 up to the second + // erase at column 6 (1-based 7), blue from there to the edge. + const red = s.at(tx + 3, body_y).style.bg; + const blue = s.at(tx + 9, body_y).style.bg; + errdefer std.debug.print("\nmode={any} filter={}: red={any} blue={any} col2={any}\n", .{ mode, filter, red, blue, s.at(tx + 2, body_y).style.bg }); + try testing.expect(!std.meta.eql(red, blue)); + for (2..6) |c| try testing.expectEqual(red, s.at(tx + @as(u16, @intCast(c)), body_y).style.bg); + for (6..10) |c| try testing.expectEqual(blue, s.at(tx + @as(u16, @intCast(c)), body_y).style.bg); + } + } +} + +test "a colored row reaches its last column when a wide glyph did not fit" { + const testing = std.testing; + // Thirteen cells of red background, then a wide glyph with one column left: + // ghostty leaves a `spacer_head` in that last column, carrying the row's + // background, and wraps the glyph to the next row. A head OWNS its column, + // so skipping it the way a tail is skipped left the row's final column bare. + for ([_]pardes.Mode{ .tty, .normal }) |mode| { + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 16, .rows = 8 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = false; + pane.mode = mode; + p.update(.{ .output = .{ .pane = 0, .bytes = "\x1b[41mzzzzzzzzzzzzz\u{754C}\x1b[0m\r\n" } }); + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + p.shell_rows.stale = true; + const s = try p.render(frame.allocator()); + + const red: pardes.Color = .{ .index = 1 }; + var c: u16 = 0; + while (c < r.w -| config.GUTTER) : (c += 1) { + errdefer std.debug.print("\nmode={any} col {d} bg={any}\n", .{ mode, c, s.at(tx + c, body_y).style.bg }); + try testing.expectEqual(red, s.at(tx + c, body_y).style.bg); + } + } +} + +test "tty colours survive a scrollback deeper than the pane" { + const testing = std.testing; + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 30, .rows = 12 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = false; + pane.mode = .tty; + for (0..40) |i| { + var buf: [64]u8 = undefined; + const bytes = std.fmt.bufPrint(&buf, "\x1b[38;5;{d}mline-{d:0>2}\x1b[0m\r\n", .{ 20 + i, i }) catch unreachable; + p.update(.{ .output = .{ .pane = 0, .bytes = bytes } }); + } + p.shell_rows.stale = true; + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const s = try p.render(frame.allocator()); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + + var bad: usize = 0; + for (0..r.h -| pardes.BOX_H) |vr| { + var buf: [16]u8 = undefined; + var n: usize = 0; + for (0..10) |c| { + const g = s.at(tx + @as(u16, @intCast(c)), body_y + @as(u16, @intCast(vr))).grapheme(); + if (g.len != 1) break; + buf[n] = g[0]; + n += 1; + } + const txt = buf[0..n]; + if (!std.mem.startsWith(u8, txt, "line-")) continue; + const num = std.fmt.parseInt(usize, std.mem.trim(u8, txt[5..], " "), 10) catch continue; + const want = pardes.Color{ .index = @intCast(20 + num) }; + const got = s.at(tx, body_y + @as(u16, @intCast(vr))).style.fg; + if (!std.meta.eql(want, got)) { + bad += 1; + std.debug.print("row {d}: text {s} want {any} got {any}\n", .{ vr, txt, want, got }); + } + } + try testing.expectEqual(@as(usize, 0), bad); +} + +test "reverse video swaps the default colors with the filter off too" { + const testing = std.testing; + // DECSCNM is a property of the terminal, not of a cell's SGR, so it has to + // be honoured on BOTH colour paths. The theme filter folds it into its own + // palette; the raw path resolves a `.none` colour by role, and simply + // dropped reverse video altogether. + for ([_]bool{ false, true }) |filter| { + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 20, .rows = 6 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = filter; + pane.mode = .normal; + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + + p.update(.{ .output = .{ .pane = 0, .bytes = "plain text\r\n" } }); + p.shell_rows.stale = true; + const before = try p.render(frame.allocator()); + const plain = before.at(tx, body_y).style; + + p.update(.{ .output = .{ .pane = 0, .bytes = "\x1b[?5h" } }); + p.shell_rows.stale = true; + _ = frame.reset(.retain_capacity); + const after = try p.render(frame.allocator()); + const reversed = after.at(tx, body_y).style; + + errdefer std.debug.print("\nfilter={}: plain fg={any} bg={any} | reversed fg={any} bg={any}\n", .{ filter, plain.fg, plain.bg, reversed.fg, reversed.bg }); + try testing.expectEqual(plain.fg, reversed.bg); + try testing.expectEqual(plain.bg, reversed.fg); + } +} + +test "untouched lines between two edits keep their colors" { + const testing = std.testing; + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 24, .rows = 14 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = false; + pane.mode = .normal; + + for (0..6) |i| { + var buf: [40]u8 = undefined; + const bytes = std.fmt.bufPrint(&buf, "\x1b[38;5;{d}mrow-{d:0>2}\x1b[0m\r\n", .{ 16 + i, i }) catch unreachable; + p.update(.{ .output = .{ .pane = 0, .bytes = bytes } }); + } + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + + // The state two ordinary edits reach: one at the bottom, one that split a + // line further up. The buffer now spans rows 2..6 and diverges at BOTH + // ends, with three untouched lines in the middle. Matching a leading and a + // trailing run stops at the first divergence and drains exactly those three; + // each line carries its own evidence, so each is anchored on its own. + pane.ovl = .{ .row = 2, .rows = 5, .text = try p.gpa.dupe(u8, "r\now-02\nrow-03\nrow-04\nrow-05\nZ") }; + p.shell_rows.stale = true; + const s = try p.render(frame.allocator()); + + // body row 2+k shows buffer line k; lines 2..4 are `row-03`..`row-05` + for (0..3) |k| { + const vr = @as(u16, @intCast(4 + k)); + var buf: [8]u8 = undefined; + const want_text = std.fmt.bufPrint(&buf, "row-{d:0>2}", .{3 + k}) catch unreachable; + const cell = s.at(tx, body_y + vr); + errdefer std.debug.print("\nbody row {d}: glyph '{s}' fg {any}\n", .{ vr, cell.grapheme(), cell.style.fg }); + try testing.expectEqualStrings(want_text[0..1], cell.grapheme()); + try testing.expectEqual(pardes.Color{ .index = @intCast(19 + k) }, cell.style.fg); + } +} + +test "a prompt row hidden end to end paints nothing at all" { + const testing = std.testing; + // The command's first glyph is wide with one column left, so ghostty leaves + // a spacer_head carrying the command's background and wraps the glyph to + // the next row. `promptRow` renders this row EMPTY, so no cell of it may + // take a colour — a spacer owns no column of its own. + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 12, .rows = 8 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = false; + pane.mode = .normal; + p.update(.{ .output = .{ .pane = 0, .bytes = "\x1b]133;A\x1b\\\x1b[32maaaaaaaaa\x1b]133;B\x1b\\\x1b[41;36m\u{754C}\x1b[0m\r\n" } }); + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + p.shell_rows.stale = true; + const s = try p.render(frame.allocator()); + + try testing.expectEqualStrings(" ", s.at(tx, body_y).grapheme()); + try testing.expect(!std.meta.eql(pardes.Color{ .index = 1 }, s.at(tx, body_y).style.bg)); +} + +test "an emptied edit buffer does not shift the colors below it" { + const testing = std.testing; + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 18, .rows = 12 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = false; + pane.mode = .normal; + + p.update(.{ .output = .{ .pane = 0, .bytes = "\x1b[31m000\x1b[0m\r\n\x1b[32m111\x1b[0m\r\n\r\n\x1b[34m333\x1b[0m\r\n\x1b[35m444\x1b[0m" } }); + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + + // The state three keystrokes reach on any blank shell row: type a character + // and delete it, and the buffer holds NO text while still standing in for + // the row. `modal.lineCount("")` is 0 while `splitScalar("")` yields one + // line, so anything deriving the slide from the former puts every colour + // below here one row too far down — and drops the bottom row's entirely. + pane.ovl = .{ .row = 2, .rows = 1, .text = try p.gpa.dupe(u8, "") }; + p.shell_rows.stale = true; + const s = try p.render(frame.allocator()); + + try testing.expectEqualStrings("3", s.at(tx, body_y + 3).grapheme()); + try testing.expectEqualStrings("4", s.at(tx, body_y + 4).grapheme()); + try testing.expectEqual(pardes.Color{ .index = 4 }, s.at(tx, body_y + 3).style.fg); + try testing.expectEqual(pardes.Color{ .index = 5 }, s.at(tx, body_y + 4).style.fg); + // ...and the user's own empty line takes no colour from the row beneath it + try testing.expect(!std.meta.eql(pardes.Color{ .index = 4 }, s.at(tx, body_y + 2).style.fg)); } test "an edit overlay never changes tty-mode ansi colors" { @@ -1716,3 +2973,252 @@ const DeviceAttrs = @typeInfo(@typeInfo(@typeInfo( pub fn ptyDeviceAttrs(_: *ghostty_vt.TerminalStream.Handler) DeviceAttrs { return .{}; } + +test "an edited row keeps the colours of the bytes the edit did not touch" { + const testing = std.testing; + // The loudest colour bug this editor had: one keystroke anywhere in a + // coloured row turned EVERY column of it grey, because an anchor was all or + // nothing. The row's own bytes survive at both ends of what was typed, and + // being the same bytes they keep the same colours; only the typed character + // has no cell under it and so takes none. + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 30, .rows = 12 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = false; + pane.mode = .normal; + for (0..6) |i| { + var buf: [64]u8 = undefined; + const bytes = std.fmt.bufPrint(&buf, "\x1b[38;5;{d}mrow-{d}-abcdefgh\x1b[0m\r\n", .{ 30 + i, i }) catch unreachable; + p.update(.{ .output = .{ .pane = 0, .bytes = bytes } }); + } + // One `Z` typed into the middle of row 3's own text. + pane.ovl = .{ .row = 3, .rows = 1, .text = try p.gpa.dupe(u8, "row-3-abcZdefgh") }; + p.shell_rows.stale = true; + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + + const s = try p.render(frame.allocator()); + const want = pardes.Color{ .index = 33 }; + var seen = false; + for (0..@as(usize, r.h -| pardes.BOX_H)) |vr| { + var buf: [15]u8 = undefined; + for (0..15) |c| { + const g = s.at(tx + @as(u16, @intCast(c)), body_y + @as(u16, @intCast(vr))).grapheme(); + buf[c] = if (g.len == 1) g[0] else '?'; + } + if (!std.mem.eql(u8, &buf, "row-3-abcZdefgh")) continue; + seen = true; + for (0..15) |c| { + const got = s.at(tx + @as(u16, @intCast(c)), body_y + @as(u16, @intCast(vr))).style.fg; + errdefer std.debug.print("\nedited row col {d} ('{c}') fg={any}\n", .{ c, buf[c], got }); + // Column 9 is the typed `Z`; every other column is row 3's own. + if (c == 9) try testing.expect(!std.meta.eql(want, got)) else try testing.expectEqual(want, got); + } + } + try testing.expect(seen); +} + +test "joining two rows leaves the rows below them their colours" { + const testing = std.testing; + // A join removes a buffer line while the buffer's covered span GROWS, so the + // two counts cancel at `lines == covered`. Anchoring that only counts down + // from the buffer's top and up from its bottom then resolves both ways to + // the SAME row, one short of where the lines below live, and every untouched + // row under the join went plain. This is the state four keystrokes reach + // (Enter, then a backspace two rows up), taken from the fuzzer that found it. + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 34, .rows = 14 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = false; + pane.mode = .normal; + for (0..26) |i| { + var buf: [64]u8 = undefined; + const bytes = std.fmt.bufPrint(&buf, "\x1b[38;5;{d}mrow-{d:0>2}-xyzzy\x1b[0m\r\n", .{ 20 + i, i }) catch unreachable; + p.update(.{ .output = .{ .pane = 0, .bytes = bytes } }); + } + pane.ovl = .{ + .row = 23, + .rows = 4, + .text = try p.gpa.dupe(u8, "row-23-xyzzyrow-24-xyzzy\nrow-25-xyzzy\n\n"), + }; + p.shell_rows.stale = true; + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + + const s = try p.render(frame.allocator()); + var seen = false; + for (0..@as(usize, r.h -| pardes.BOX_H)) |vr| { + var buf: [12]u8 = undefined; + for (0..12) |c| { + const g = s.at(tx + @as(u16, @intCast(c)), body_y + @as(u16, @intCast(vr))).grapheme(); + buf[c] = if (g.len == 1) g[0] else '?'; + } + if (!std.mem.eql(u8, &buf, "row-25-xyzzy")) continue; + seen = true; + // The join is above it and its own text is untouched, so every column + // still carries row 25's own colour. + for (0..12) |c| { + const got = s.at(tx + @as(u16, @intCast(c)), body_y + @as(u16, @intCast(vr))).style.fg; + errdefer std.debug.print("\nrow-25 col {d} fg={any}\n", .{ c, got }); + try testing.expectEqual(pardes.Color{ .index = 45 }, got); + } + } + try testing.expect(seen); +} + +test "an untouched row always carries the colour its own text names" { + const testing = std.testing; + // Random editing, absolute oracle: every row's own text names the colour it + // must have, so no sequence of keystrokes may leave an UNTOUCHED row wearing + // anything else. This is what found the join above, and the empty line that + // claimed a blank row far below it and took every coloured row in between + // out of reach of the lines that owned them. + var seed: u64 = 0; + while (seed < 40) : (seed += 1) { + var prng = std.Random.DefaultPrng.init(seed); + const rand = prng.random(); + + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 34, .rows = 14 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + // The raw palette, so a row's text names its exact colour instead of one + // this test would have to re-derive from the theme. + pane.tty_filter = false; + pane.mode = .normal; + for (0..26) |i| { + var buf: [64]u8 = undefined; + const bytes = std.fmt.bufPrint(&buf, "\x1b[38;5;{d}mrow-{d:0>2}-xyzzy\x1b[0m\r\n", .{ 20 + i, i }) catch unreachable; + p.update(.{ .output = .{ .pane = 0, .bytes = bytes } }); + } + p.shell_rows.stale = true; + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + const body_h = r.h -| pardes.BOX_H; + + var step: usize = 0; + while (step < 12) : (step += 1) { + _ = frame.reset(.retain_capacity); + const s = try p.render(frame.allocator()); + for (0..body_h) |vr| { + var buf: [24]u8 = undefined; + for (0..24) |c| { + const g = s.at(tx + @as(u16, @intCast(c)), body_y + @as(u16, @intCast(vr))).grapheme(); + buf[c] = if (g.len == 1) g[0] else '?'; + } + const txt = std.mem.trimEnd(u8, buf[0..24], " "); + if (txt.len != 12) continue; + if (!std.mem.startsWith(u8, txt, "row-") or !std.mem.endsWith(u8, txt, "-xyzzy")) continue; + const num = std.fmt.parseInt(usize, txt[4..6], 10) catch continue; + const want = pardes.Color{ .index = @intCast(20 + num) }; + for (0..txt.len) |c| { + const got = s.at(tx + @as(u16, @intCast(c)), body_y + @as(u16, @intCast(vr))).style.fg; + errdefer std.debug.print("\nseed {d} step {d}: untouched '{s}' col {d} fg={any}\n", .{ seed, step, txt, c, got }); + try testing.expectEqual(want, got); + } + } + + switch (rand.intRangeAtMost(u8, 0, 10)) { + 0 => p.update(.{ .key = .{ .cp = pardes.Key.up } }), + 1 => p.update(.{ .key = .{ .cp = pardes.Key.down } }), + 2 => p.update(.{ .key = .{ .cp = pardes.Key.left } }), + 3 => p.update(.{ .key = .{ .cp = pardes.Key.right } }), + 4 => { + p.update(.{ .key = .{ .cp = 'i', .text = "i" } }); + p.update(.{ .key = .{ .cp = 'Q', .text = "Q" } }); + p.update(.{ .key = .{ .cp = pardes.Key.escape } }); + }, + 5 => { + p.update(.{ .key = .{ .cp = 'i', .text = "i" } }); + p.update(.{ .key = .{ .cp = pardes.Key.enter } }); + p.update(.{ .key = .{ .cp = pardes.Key.escape } }); + }, + 6 => { + p.update(.{ .key = .{ .cp = 'i', .text = "i" } }); + p.update(.{ .key = .{ .cp = pardes.Key.backspace } }); + p.update(.{ .key = .{ .cp = pardes.Key.escape } }); + }, + 7 => { + p.update(.{ .key = .{ .cp = 'i', .text = "i" } }); + p.update(.{ .key = .{ .cp = 'W', .text = "W" } }); + p.update(.{ .key = .{ .cp = 'W', .text = "W" } }); + p.update(.{ .key = .{ .cp = pardes.Key.escape } }); + }, + 8 => p.update(.{ .key = .{ .cp = pardes.Key.home } }), + 9 => p.update(.{ .key = .{ .cp = pardes.Key.end } }), + else => { + p.update(.{ .key = .{ .cp = 'i', .text = "i" } }); + p.update(.{ .key = .{ .cp = pardes.Key.delete } }); + p.update(.{ .key = .{ .cp = pardes.Key.escape } }); + }, + } + while (p.nextEffect()) |_| {} + } + } +} + +test "a new empty line does not take the colours of the rows below it" { + const testing = std.testing; + // Splitting a row makes an EMPTY buffer line, and empty equals every blank + // row in the buffer's span - including the one under the last output. Left + // free to look ahead for a row spelling the same bytes, that line claimed + // the blank row far below and put every coloured row in between out of + // reach of the lines that owned them. Two keystrokes (Home, Enter) got here. + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 34, .rows = 14 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const pane = p.panes[0].?; + pane.tty_filter = false; + pane.mode = .normal; + for (0..26) |i| { + var buf: [64]u8 = undefined; + const bytes = std.fmt.bufPrint(&buf, "\x1b[38;5;{d}mrow-{d:0>2}-xyzzy\x1b[0m\r\n", .{ 20 + i, i }) catch unreachable; + p.update(.{ .output = .{ .pane = 0, .bytes = bytes } }); + } + // A newline typed at column 0 of row 24, and `WW` typed on the blank row + // below the output: the span covers rows 24, 25 and that blank row. + pane.ovl = .{ + .row = 24, + .rows = 3, + .text = try p.gpa.dupe(u8, "\nrow-24-xyzzy\nrow-25-xyzzy\nWW"), + }; + p.shell_rows.stale = true; + + var frame = std.heap.ArenaAllocator.init(testing.allocator); + defer frame.deinit(); + const r = p.rects[0]; + const tx = r.x + config.GUTTER; + const body_y = if (p.settings.tag_bottom) r.y else r.y + pardes.BOX_H; + + const s = try p.render(frame.allocator()); + var seen: usize = 0; + for (0..@as(usize, r.h -| pardes.BOX_H)) |vr| { + var buf: [12]u8 = undefined; + for (0..12) |c| { + const g = s.at(tx + @as(u16, @intCast(c)), body_y + @as(u16, @intCast(vr))).grapheme(); + buf[c] = if (g.len == 1) g[0] else '?'; + } + if (!std.mem.startsWith(u8, &buf, "row-2")) continue; + const num = std.fmt.parseInt(usize, buf[4..6], 10) catch continue; + if (num != 24 and num != 25) continue; + seen += 1; + const got = s.at(tx, body_y + @as(u16, @intCast(vr))).style.fg; + errdefer std.debug.print("\nrow-{d} fg={any}\n", .{ num, got }); + try testing.expectEqual(pardes.Color{ .index = @intCast(20 + num) }, got); + } + try testing.expectEqual(@as(usize, 2), seen); +} diff --git a/src/tty/tty.zig b/src/tty/tty.zig index f8042d71..d7de3ae2 100644 --- a/src/tty/tty.zig +++ b/src/tty/tty.zig @@ -2,6 +2,11 @@ //! events into core events, performs the core's effects (fork ptys, write //! them, resize them), and hands the core's Surface to vaxis cell-for-cell — //! the canonical interface rendered with no interpretation. +//! +//! It also holds the OTHER loop a terminal can run: an attached frontend, +//! which has a socket where its core would be and performs no machine-local +//! effect whatsoever (see `Attach`). The `Attach` builtin turns the first into +//! the second in place, without giving up the terminal. const std = @import("std"); const builtin = @import("builtin"); const posix = std.posix; @@ -21,18 +26,14 @@ const fuse = @import("../fuse.zig"); const fs_service = @import("../fs_service.zig"); const panel_compositor = @import("panel_compositor.zig"); const host_api = @import("../host.zig"); -const config = @import("../config.zig"); -// The other half of `--detach`, and the reason this file has an `--attach` -// branch at all: the frontend side of a detached session is a terminal and a -// socket, and this file is already the one that owns a terminal. +// The other half of `--detach`, and the reason this file has an attached loop +// at all: the frontend side of a detached session is a terminal and a socket, +// and this file is already the one that owns a terminal. const detached_client = @import("../detached/client.zig"); const detached_server = @import("../detached/server.zig"); const wire = @import("../detached/wire.zig"); +const host_io = @import("../host_io.zig"); -extern "c" fn forkpty(amaster: *c_int, name: ?[*:0]u8, termp: ?*const anyopaque, winp: ?*const posix.winsize) c_int; -extern "c" fn execv(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8) c_int; -extern "c" fn chdir(path: [*:0]const u8) c_int; -extern "c" fn _exit(status: c_int) noreturn; extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int; // TIOCSWINSZ: absent from std.c.T on darwin — _IOW('t', 103, winsize) @@ -453,8 +454,9 @@ fn nativePdfWheelTarget(core: *const pardes.Pardes, mouse: vaxis.Mouse) ?PdfWhee } /// `attach` is `--attach[=]`: empty means "the session there is" (see -/// `sessionName`). It is a parameter rather than an `Options` field because it -/// says nothing to the core — this process does not have one when it is set. +/// `detached_client.resolve`). It is a parameter rather than an `Options` field +/// because it says nothing to the core — this process does not have one when +/// it is set. pub fn run(init: std.process.Init, opts: pardes.Options, attach: ?[]const u8) !void { const io = init.io; const gpa = init.gpa; @@ -470,19 +472,27 @@ pub fn run(init: std.process.Init, opts: pardes.Options, attach: ?[]const u8) !v // ...and resolved before the terminal too, for the same reason turned the // other way: "no session called work" is a launch that never started, and // flashing the alt screen up and straight back down to say so is worse - // than never entering it. + // than never entering it. Only the QUESTION is asked here, and asked + // through client.zig because the SDL frontend asks the identical one: + // `detached_client.attempt` asks it again with the socket in hand, and a + // session that ends between the two answers is a `.no_session` from there + // rather than a disagreement between two spellings of the same scan. var name_buf: [detached_server.path_max]u8 = undefined; var attach_name: []const u8 = &.{}; - if (attach) |requested| attach_name = sessionName(&name_buf, requested, &attach_end) orelse return; - - const allocs = pardes.allocators.init(gpa); - defer pardes.allocators.deinit(); - var options = opts; - options.image_allocator = allocs.image; - options.pdf_allocator = allocs.pdf; - options.tree_sitter_allocator = allocs.tree_sitter; - // the 16 MiB static buffer behind every per-frame Surface - options.frame_allocator = allocs.frame; + if (attach) |requested| switch (detached_client.resolve(&name_buf, requested)) { + .name => |resolved| attach_name = resolved, + // Two ends for the union's one arm, because the advice differs: a name + // that resolved to nothing is a typo to correct, and no name at all is + // a session to start. + .none => { + attach_end = if (requested.len != 0) .{ .no_session = requested } else .nothing_detached; + return; + }, + .ambiguous => |found| { + attach_end = .{ .ambiguous = found }; + return; + }, + }; // SIGWINCH must never run vaxis's signal handler: it posts the winsize // event through std.Io.Mutex/Condition, and when the signal lands on a @@ -529,10 +539,105 @@ pub fn run(init: std.process.Init, opts: pardes.Options, attach: ?[]const u8) !v // ordinary exit does, and sharing the deferred teardown is the only way to // guarantee that instead of asserting it. if (attach != null) { - attach_end = attachSession(init, opts, attach_name, &tty, &vx); + attach_end = attachSession(init, attach_name, &tty, &vx); return; } + // Everything below is the TERMINAL's, shared by the two loops that can draw + // on it: the local session's and, after an `Attach`, an attached one's. The + // core and everything that only a core needs is `localSession`'s. + var kitty_handles = std.AutoHashMap(pardes.ImageCacheKey, vaxis.Image).init(gpa); + defer { + clearNativeImages(&kitty_handles, &vx, &tty); + kitty_handles.deinit(); + } + var paste_buf: std.Io.Writer.Allocating = .init(gpa); + defer paste_buf.deinit(); + var loop: Loop = .init(io, &tty, &vx); + + // How a connected client leaves `localSession`, and the whole of the + // handover: it is set only once `detached_client.attempt` has come back + // GREETED, so every way of failing to attach leaves the local session + // running with this still null. By the time `localSession` returns non-null + // its scope has ended, which means every pane shell, watch, worker and + // mount of the local session is already away — the teardown is a scope + // exit rather than a second copy of the same defers. + var attached: ?detached_client.Client = null; + // The loop is STARTED inside `localSession`, because the initial forkpty + // has to happen before any thread of ours exists, and stopped by whichever + // loop was the last to use it: `localSession` itself when it is exiting for + // good, and this defer when it handed the terminal on. Registered before + // the call so LIFO puts the drain after `loop.stop()` — vaxis's reader must + // be joined before the queue is emptied, or a late post lands in a queue + // nobody drains again and its bytes leak. + defer if (attached != null) { + loop.stop(); + drainAttachedQueue(&loop, gpa); + }; + try localSession(init, opts, &tty, &vx, &loop, &kitty_handles, &paste_buf, &attached); + if (attached) |*client| { + // `detach` and not `deinit`: seven bytes that turn "the peer vanished" + // into "the peer left" in the session's log. + defer client.detach(); + var a: Attach = .{ + .gpa = gpa, + .client = client, + .loop = &loop, + .vx = &vx, + .tty = &tty, + // The `Shell`'s own paste buffer. Nothing is in flight in it: a + // bracketed burst cannot span the switch, because the builtin that + // caused the switch was a keystroke, and every `paste_start` clears + // it before it fills. + .paste_buf = &paste_buf, + // `caps_pending` deliberately keeps its default rather than + // inheriting the local session's: `enableDetectedFeatures` is + // idempotent mode-setting, and the `queueRefresh` it pairs with is + // wanted anyway on a screen that just changed which core draws it. + // `session` keeps its empty default for a reason worth stating: the + // name the `Attach` word carried lived in the core's own + // `attach_buf`, and that core is deinited by the time this runs. A + // later `Detach` therefore says "that session" rather than naming + // it, which is also all a bare `Attach` ever said. + }; + attach_end = attachLoop(&a); + } +} + +/// The session that lives in THIS process: the core, its pane shells, its +/// watches, its acme filesystem, its workers and the loop that pumps them. A +/// function of its own rather than the tail of `run` because that makes its +/// teardown a SCOPE EXIT instead of a second copy of the same nine defers — +/// and the `Attach` builtin needs exactly that teardown, in exactly that LIFO +/// order, before an attached loop may draw on the same terminal. The hand-copy +/// it replaces had 29 lines identical to these defers and stated its ordering +/// contract in prose, so nothing but a reader could enforce it. +/// +/// The terminal itself is NOT here: `tty`, `vx`, the alt screen, the `Loop`, +/// the paste buffer and the kitty placements outlive this scope because the +/// attached loop keeps drawing on them. +fn localSession( + init: std.process.Init, + opts: pardes.Options, + tty: *vaxis.Tty, + vx: *vaxis.Vaxis, + loop: *Loop, + kitty_handles: *std.AutoHashMap(pardes.ImageCacheKey, vaxis.Image), + paste_buf: *std.Io.Writer.Allocating, + attached: *?detached_client.Client, +) !void { + const io = init.io; + const gpa = init.gpa; + + const allocs = pardes.allocators.init(gpa); + defer pardes.allocators.deinit(); + var options = opts; + options.image_allocator = allocs.image; + options.pdf_allocator = allocs.pdf; + options.tree_sitter_allocator = allocs.tree_sitter; + // the 16 MiB static buffer behind every per-frame Surface + options.frame_allocator = allocs.frame; + pardes.image.start(io, allocs.image); if (comptime pardes.pdf_enabled) pardes.pdf.start(allocs.pdf); pardes.syntax.start(allocs.tree_sitter); @@ -557,17 +662,8 @@ pub fn run(init: std.process.Init, opts: pardes.Options, attach: ?[]const u8) !v // pane unless this is in the env BEFORE bash starts (the rc is too late) if (comptime builtin.os.tag.isDarwin()) _ = setenv("BASH_SILENCE_DEPRECATION_WARNING", "1", 1); - var kitty_handles = std.AutoHashMap(pardes.ImageCacheKey, vaxis.Image).init(gpa); - defer { - var iterator = kitty_handles.valueIterator(); - while (iterator.next()) |handle| vx.freeImage(tty.writer(), handle.id); - kitty_handles.deinit(); - } var frame_arena: std.heap.ArenaAllocator = .init(allocs.frame); defer frame_arena.deinit(); - var paste_buf: std.Io.Writer.Allocating = .init(gpa); - defer paste_buf.deinit(); - var loop: Loop = .init(io, &tty, &vx); // `--fs`: mount before the initial spawns, because those shells are the // ones that need PARDES_FS in their environment, and before the first @@ -583,8 +679,8 @@ pub fn run(init: std.process.Init, opts: pardes.Options, attach: ?[]const u8) !v // like nested.zig's socket directory: another session may be living in it, // and rmdir of a shared directory is not ours to attempt. var fs = fs_service.start(gpa, core); - // Covers the error paths only: the ordinary exit unmounts at the END OF - // THE LOOP instead, see there. + // Covers the error paths and the handover; the ordinary exit unmounts at + // the END OF THE LOOP instead, see there. defer if (fs) |f| f.deinit(); var sh: Shell = .{ @@ -593,12 +689,12 @@ pub fn run(init: std.process.Init, opts: pardes.Options, attach: ?[]const u8) !v .lsp_gpa = allocs.lsp, .core = core, .prompt_rcs = &prompt_rcs, - .loop = &loop, - .vx = &vx, - .tty = &tty, - .kitty = &kitty_handles, + .loop = loop, + .vx = vx, + .tty = tty, + .kitty = kitty_handles, .frame = &frame_arena, - .paste_buf = &paste_buf, + .paste_buf = paste_buf, // One inotify instance for every watched pane, opened here — before // any thread exists — so the pre-loop effect drain below can already // mark the file a positional path argument opened. -1 off linux: @@ -614,6 +710,22 @@ pub fn run(init: std.process.Init, opts: pardes.Options, attach: ?[]const u8) !v for (&sh.ptys) |*slot| if (slot.*) |*pt| { pt.reader.cancel(io) catch {}; _ = libc.close(pt.file.handle); + // THE ONE DELTA BETWEEN THE TWO WAYS OUT OF THIS SCOPE, and the + // reason it is a condition rather than a comment: an exit leaves + // the closed master's SIGHUP to kill the shell and the kernel to + // collect it, which server.zig's `harvest` calls "the one + // bookkeeping cost a long-lived process pays that a frontend, + // which exits, never did". A handover does not exit — this process + // goes on drawing somebody else's session for hours — so a skipped + // `waitpid` is a zombie per pane held for all of it. SIGKILL and + // not the hangup alone because the wait has to be BOUNDED: the + // master is gone, so there is nothing left for the shell to print + // and no graceful exit left to give it, and SIGHUP is a signal it + // may decline while SIGKILL is not. + if (attached.* != null) { + _ = libc.kill(pt.pid, posix.SIG.KILL); + _ = libc.waitpid(pt.pid, null, 0); + } slot.* = null; }; // join the query worker BEFORE the drain below, or its late post @@ -663,19 +775,22 @@ pub fn run(init: std.process.Init, opts: pardes.Options, attach: ?[]const u8) !v while (core.nextEffect()) |effect| core.perform(effect); try loop.start(); - defer loop.stop(); + // ...and stopped here only when this scope is the last user of the + // terminal. A handover leaves vaxis's reader running for the attached loop, + // which is drawing on the same tty a moment later; `run` stops it then. + defer if (attached.* == null) loop.stop(); // resize watcher: plain detached thread (not io.concurrent — teardown // joins those, and sigwait never returns); dies with the process - (try std.Thread.spawn(.{}, winchWatch, .{ &loop, &vx, &tty })).detach(); + (try std.Thread.spawn(.{}, winchWatch, .{ loop, vx, tty })).detach(); // ...and the nested-instance listener, detached for the same reason: a // blocking accept(2) never returns either, so an io.concurrent task would // hang the teardown that joins it. - if (sock_fd >= 0) (try std.Thread.spawn(.{}, lookServer, .{ gpa, sock_fd, &loop })).detach(); + if (sock_fd >= 0) (try std.Thread.spawn(.{}, lookServer, .{ gpa, sock_fd, loop })).detach(); // ...and the /dev/fuse poller, which is the same kind of thread again: it // waits for POLLIN and posts, never touching the core or the descriptor's // data. Joined by `Fs.deinit` rather than detached, because unlike accept4 // it CAN be woken — fuse.zig gives it a control pipe for exactly that. - fs_service.wake(fs, &loop, wakeFs); + fs_service.wake(fs, loop, wakeFs); // Capability handshake — SEND the probes, do not wait on them. This was // queryTerminal(2ms), which blocks on a futex until DA1 comes back. The // number has to beat one terminal round trip: a local terminal answers in @@ -720,18 +835,18 @@ pub fn run(init: std.process.Init, opts: pardes.Options, attach: ?[]const u8) !v // now threads are fine: start a reader task per pty sh.threads_ok = true; for (&sh.ptys, 0..) |*slot, id| if (slot.*) |*pt| { - pt.reader = try io.concurrent(readPty, .{ io, gpa, pt.file, id, sh.gens[id], &loop }); + pt.reader = try io.concurrent(readPty, .{ io, gpa, pt.file, id, sh.gens[id], loop }); }; // ...and the one file watcher. Started here rather than lazily on the // first watched pane because the fd already exists and an unwatched // inotify instance just parks in read(2) — one thread for the process, // however many panes come and go. - if (sh.inotify_fd >= 0) sh.watch_task = io.concurrent(watchFiles, .{ io, sh.inotify_fd, &loop }) catch null; + if (sh.inotify_fd >= 0) sh.watch_task = io.concurrent(watchFiles, .{ io, sh.inotify_fd, loop }) catch null; // The core owns the loop ORDER (see Pardes.pump); the outer `while` stays // here rather than being `core.run` for one reason: Restore swaps the // whole core, and a core cannot replace itself from inside its own frame. - while (!core.quit) { + frames: while (!core.quit) { try core.pump(host); // Restore builtin: swap in a core rebuilt from the dump; the live // shells die with their masters (readers canceled, gens bumped so @@ -761,9 +876,7 @@ pub fn run(init: std.process.Init, opts: pardes.Options, attach: ?[]const u8) !v .{ .text = 0 }, ); _ = file_watch.applyThemeEffect(core, gpa, sh.inotify_fd, &sh.watches, 0, false, false); - var image_iterator = kitty_handles.valueIterator(); - while (image_iterator.next()) |handle| vx.freeImage(tty.writer(), handle.id); - kitty_handles.clearRetainingCapacity(); + clearNativeImages(kitty_handles, vx, tty); nc.native_images = vx.caps.kitty_graphics; core.deinit(); core = nc; @@ -776,6 +889,35 @@ pub fn run(init: std.process.Init, opts: pardes.Options, attach: ?[]const u8) !v updateCoreTerminalSize(core, vx.screen.width, vx.screen.height, vx.screen.width_pix, vx.screen.height_pix); } } + // `Attach [name]`: hand this terminal to a detached session and stop + // being a session at all. CONNECTING IS NOT BEING ATTACHED, which is + // why this asks `detached_client.attempt` for a GREETED client and not + // for a socket: `Client.open` writes a hello and returns, and every way + // a session says no — `refuse .version` for a session built from other + // bytes, `.full`, `.quitting`, or a plain `quit` from one that ended in + // the same round — arrives after a successful `connect(2)`. A swap that + // trusted the connect would already have SIGKILLed every pane shell, + // unmounted the filesystem and freed every undo history by the time it + // decoded the refusal. + // + // So `attached` is set only with the welcome in hand, and until it is, + // NOTHING here has been touched: a failed `Attach` costs one message + // row and leaves every pane, every shell and every undo history where + // it was. The teardown that follows is this function's own defers, + // reached by leaving its scope. + if (core.takeAttach()) |req| { + var attempt = detached_client.attempt(gpa, req.name, core.screen_w, core.screen_h); + switch (attempt) { + .greeted => |client| { + attached.* = client; + break :frames; + }, + else => { + var mbuf: [256]u8 = undefined; + core.setMessage(req.pane, attemptEnd(&attempt, req.name).row(&mbuf)); + }, + } + } } // THE FILESYSTEM GOES FIRST, ahead of every deferred teardown below. // `loop.stop()` joins a reader parked in `read(2)` on the tty, so it does @@ -783,7 +925,8 @@ pub fn run(init: std.process.Init, opts: pardes.Options, attach: ?[]const u8) !v // exit must not spend that wait holding a mount nobody is serving. A // client blocked on `/event` when the last pane is deleted through // `ctl` then gets ENOTCONN at once instead of hanging until somebody - // touches the keyboard. + // touches the keyboard. A handover skips this and lets the deferred + // unmount do it, because it does not stop the loop and so never waits. if (fs) |f| { f.deinit(); fs = null; @@ -791,6 +934,35 @@ pub fn run(init: std.process.Init, opts: pardes.Options, attach: ?[]const u8) !v } } +/// Free every kitty placement this session put on the terminal and forget them. +/// THREE callers and one reason: the pixels live in the TERMINAL, not in the +/// core, so a core that is replaced (`Restore`), handed away (`Attach`) or +/// simply gone (the exit) leaves placements the next frames know nothing about +/// and would paint text around. The exit's own caller follows it with `deinit`; +/// the two mid-session ones keep the map's capacity for the frames after. +fn clearNativeImages( + kitty_handles: *std.AutoHashMap(pardes.ImageCacheKey, vaxis.Image), + vx: *vaxis.Vaxis, + tty: *vaxis.Tty, +) void { + var iterator = kitty_handles.valueIterator(); + while (iterator.next()) |handle| vx.freeImage(tty.writer(), handle.id); + kitty_handles.clearRetainingCapacity(); +} + +/// Empty the event queue an attached loop leaves behind, AFTER `loop.stop()` +/// has joined vaxis's reader. Two of its events own gpa bytes — a decoded paste +/// (a bracketed burst, or an OSC 52 reply to the session's `read_clipboard`) +/// and a nested-instance command line — and the thread that posts the second +/// cannot be joined at all, so the drain is not optional on either path out. +fn drainAttachedQueue(loop: *Loop, gpa: std.mem.Allocator) void { + while (loop.tryEvent() catch null) |ev| switch (ev) { + .paste => |b| gpa.free(@constCast(b)), + .command => |line| gpa.free(line), + else => {}, + }; +} + /// Everything the terminal shell owns and the core cannot: the ptys, the /// inotify table, the worker futures, and the one thread allowed to touch /// `tty.writer()`. This struct IS the `Host.ctx`. @@ -1194,7 +1366,7 @@ const Shell = struct { cwd_buf[cwd.len] = 0; cwd_z = @ptrCast(&cwd_buf); } - const child = forkShell(s.core, pane, s.prompt_rcs, s.core.shellBin(), cwd_z, s.core.screen_h, s.core.screen_w, s.fs); + const child = host_io.forkShell(s.core, pane, s.prompt_rcs, s.core.shellBin(), cwd_z, s.core.screen_h, s.core.screen_w, s.fs); s.ptys[pane] = .{ .file = child.file, .pid = child.pid, .reader = .{ .any_future = null, .result = {} } }; // report the pane's starting directory back to the core (tags). The // slot needs no occupancy reset: nothing is remembered, and the next @@ -1210,7 +1382,7 @@ const Shell = struct { fn ptyWrite(ctx: ?*anyopaque, pane: u8, bytes: []const u8) void { const s = of(ctx); - if (s.ptys[pane]) |pt| writeFd(pt.file.handle, bytes); + if (s.ptys[pane]) |pt| host_io.writeFd(pt.file.handle, bytes); } fn ptyResize(ctx: ?*anyopaque, pane: u8, cols: u16, rows: u16) void { @@ -1236,7 +1408,7 @@ const Shell = struct { fn writeFile(ctx: ?*anyopaque, pane: u8, path: []const u8, bytes: []const u8) void { const s = of(ctx); - if (!writeFileBytes(path, bytes)) return; + if (!host_io.writeFileBytes(path, bytes)) return; // our own write is about to come back as a watch event: restamp from // the bytes we just put there so it reads as "no change". Only when // this IS the pane's watched file — a `Save ` must not @@ -1258,7 +1430,7 @@ const Shell = struct { const s = of(ctx); var pbuf: [1024:0]u8 = undefined; const path = pardes.dump.outPath(&pbuf) orelse return; - if (!writeFileBytes(path, bytes)) return; + if (!host_io.writeFileBytes(path, bytes)) return; s.core.setLastDump(path); } @@ -1298,31 +1470,27 @@ const Shell = struct { } // ---- the desktop -------------------------------------------------------- + // + // The three effects a detached session still puts on the wire + // (`wire.ServerTag` 0x10..), because each of them needs the display a + // human is actually looking at rather than the machine the core runs on. + // These are the `Host.ctx` shims and nothing else: the terminal work is + // `copyToClipboard`/`requestClipboard` below this struct, so an attached + // frontend serving `set_clipboard`/`read_clipboard` writes the same escape + // sequences from the same lines. - /// mirror the core's yank register out via OSC 52 fn setClipboard(ctx: ?*anyopaque, text: []const u8) void { const s = of(ctx); - if (text.len == 0) return; - s.vx.copyToSystemClipboard(s.tty.writer(), text, s.gpa) catch {}; + copyToClipboard(s.vx, s.tty, s.gpa, text); } - /// ...and the other direction, OSC 52 read. The answer arrives on vaxis's - /// reader thread as an ordinary `.paste` event and reaches the core - /// through the same path an outer bracketed paste does — this request is - /// the only wiring it needs. "The answer arrives" is the optimistic - /// reading: a clipboard READ is an exfiltration primitive and terminals - /// treat it as one (ghostty prompts by default, xterm ships it off, a - /// multiplexer or ssh link may eat it), and a refusal looks exactly like - /// silence. So the core's pending request is dropped by the next keystroke - /// rather than pasting minutes late, and `SPC p` in a locked-down terminal - /// honestly does nothing. fn readClipboard(ctx: ?*anyopaque) void { const s = of(ctx); - s.vx.requestSystemClipboard(s.tty.writer()) catch {}; + requestClipboard(s.vx, s.tty); } fn openLink(_: ?*anyopaque, url: []const u8) void { - look.openLink(url); // desktop browser + look.openLink(url); // desktop browser; no terminal in it, so Attach calls this one directly } // ---- work that must leave the loop -------------------------------------- @@ -1395,6 +1563,31 @@ const Shell = struct { } }; +/// Mirror a yank register out via OSC 52. Free functions over the terminal +/// they write to rather than `Shell` methods, because the clipboard is the one +/// piece of host work that survived the move into the daemon and BOTH loops in +/// this file perform it: `Shell` for its own core's `Effect.set_clipboard`, and +/// `Attach` for a detached session's `wire.ServerMsg.set_clipboard`. One +/// escape sequence written in two places is one that drifts. +fn copyToClipboard(vx: *vaxis.Vaxis, tty: *vaxis.Tty, gpa: std.mem.Allocator, text: []const u8) void { + if (text.len == 0) return; + vx.copyToSystemClipboard(tty.writer(), text, gpa) catch {}; +} + +/// ...and the other direction, OSC 52 read. The answer arrives on vaxis's +/// reader thread as an ordinary `.paste` event and reaches whoever asked — +/// the local core through `Shell`, the session through `ClientTag.event` — +/// by the same path an outer bracketed paste takes; this request is the only +/// wiring it needs. "The answer arrives" is the optimistic reading: a +/// clipboard READ is an exfiltration primitive and terminals treat it as one +/// (ghostty prompts by default, xterm ships it off, a multiplexer or ssh link +/// may eat it), and a refusal looks exactly like silence. So the core's +/// pending request is dropped by the next keystroke rather than pasting +/// minutes late, and `SPC p` in a locked-down terminal honestly does nothing. +fn requestClipboard(vx: *vaxis.Vaxis, tty: *vaxis.Tty) void { + vx.requestSystemClipboard(tty.writer()) catch {}; +} + /// Answer a language query off the event loop and post the rows back. This is /// the whole async execution model: the same shape as readPty — do the slow /// thing on a worker, hand the result to the loop as an event, let the core @@ -1503,42 +1696,6 @@ fn wakeFs(ctx: ?*anyopaque) void { _ = loop.tryPostEvent(.fs_ready) catch {}; } -/// `core` is null in an `--attach` frontend, which forks the pane shells for a -/// core that is in another process entirely. The two things it is used for are -/// both messages BACK to that core — which pane serial the child's environment -/// should name, and which binary was actually executed — and neither has a -/// place on the detached wire (see wire.zig): a detached session's `--fs` -/// stays in the session, and `acknowledgeShell` has no `ClientTag`. So both -/// are skipped rather than faked, and the pane tag in a detached session -/// simply does not name its shell. -fn forkShell(core: ?*pardes.Pardes, pane: usize, prompt_rcs: *const shell_bin.PromptRcs, bin: []const u8, cwd: ?[*:0]const u8, rows: u16, cols: u16, fs: ?*const fuse.Fs) struct { file: std.Io.File, pid: posix.pid_t } { - var master: c_int = undefined; - // resolved BEFORE the fork, into this frame, which the child inherits: - // nothing between fork and exec may allocate, and a PATH search would - var path_buf: [std.fs.max_path_bytes]u8 = undefined; - const spawn = shell_bin.resolve(bin, &path_buf, prompt_rcs); - // ...and so is the pane's own address on the control filesystem, for a - // second reason on top of that one: acme puts `winid` in the child, which - // is safe there only because rfork(RFENVG) has just given it a private - // environment group. See fs_service.exportPaneEnv. - fs_service.exportPaneEnv(fs, if (core) |c| (if (c.panes[pane]) |pn| pn.serial else 0) else 0); - const ws = posix.winsize{ .row = rows, .col = cols, .xpixel = 0, .ypixel = 0 }; - const pid = forkpty(&master, null, null, &ws); - if (pid == 0) { - // the blocked-SIGWINCH mask survives fork AND exec — unblock it or - // bash/vim in the pane would never see resizes (sigprocmask is - // async-signal-safe) - var set = posix.sigemptyset(); - posix.sigaddset(&set, posix.SIG.WINCH); - posix.sigprocmask(posix.SIG.UNBLOCK, &set, null); - if (cwd) |c| _ = chdir(c); - _ = execv(spawn.path, &spawn.argv); - _exit(127); - } - if (pid > 0) if (core) |c| c.acknowledgeShell(pane, std.mem.span(spawn.path), spawn.argv[1] != null); - return .{ .file = .{ .handle = master, .flags = .{ .nonblocking = false } }, .pid = pid }; -} - fn readPty(io: std.Io, gpa: std.mem.Allocator, pty: std.Io.File, id: usize, gen: u32, loop: *Loop) anyerror!void { var read_buf: [0x10000]u8 = undefined; var reader = pty.readerStreaming(io, &read_buf); @@ -1688,25 +1845,6 @@ fn paintCursor(win: vaxis.Window, x: u16, y: u16, bar: bool) void { win.setCursorShape(if (bar) .beam else .default); } -/// Create-or-truncate `path` and put `bytes` there. The half of `write_file` -/// that is nothing but the filesystem, so that the frontend which has a disk -/// and no core and the host which has both write a file the same way. -/// Everything else in `Shell.writeFile` — the watch restamp, the "saved" -/// message — is the CORE's memory of the write and stays with whoever owns -/// one. False is a save that did not happen, which no caller may report as -/// one. -fn writeFileBytes(path: []const u8, bytes: []const u8) bool { - var pathbuf: [4096:0]u8 = undefined; - if (path.len >= pathbuf.len) return false; - @memcpy(pathbuf[0..path.len], path); - pathbuf[path.len] = 0; - const fd = libc.open(pathbuf[0..path.len :0], .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, @as(libc.mode_t, 0o644)); - if (fd < 0) return false; - writeFd(fd, bytes); - _ = libc.close(fd); - return true; -} - fn vaxisStyle(s: pardes.CellStyle) vaxis.Style { return .{ .fg = vaxisColor(s.fg), @@ -1737,20 +1875,16 @@ fn vaxisColor(c: pardes.Color) vaxis.Color { }; } -fn writeFd(fd: c_int, data: []const u8) void { - var off: usize = 0; - while (off < data.len) { - const n = libc.write(fd, data[off..].ptr, data.len - off); - if (n < 0) { - if (libc.errno(n) == .INTR) continue; - return; - } - off += @intCast(n); - } -} - // --------------------------------------------------------------------------- -// --attach: a terminal, a socket, and no core +// Attached: a terminal, a socket, and no core +// +// Reached two ways — `--attach[=]` on the command line, and the `Attach` +// builtin handing a running local session's terminal to a detached one — and +// identical past the connect. NOTHING below this line forks a shell, writes a +// file or watches a path: the daemon owns every machine-local effect now +// (src/detached/server.zig), and the three that are still on the wire +// (`wire.ServerTag` 0x10..) are there because the clipboard and the browser +// are the human's, not the machine's. // --------------------------------------------------------------------------- /// Why an `--attach` frontend stopped, and where its exit status comes from. @@ -1772,17 +1906,40 @@ const AttachEnd = union(enum) { refused: wire.Refusal, /// the link died, or the stream stopped making sense lost: anyerror, - /// the connect landed and the session hung up before the hello was - /// answered: a refusal whose reason raced the close (see `attachSession`) + /// the connect landed and the session hung up before its reason arrived: a + /// refusal whose six bytes raced the close (server.zig `refuseFd`) rejected, + /// ...and the other silence: it accepted, kept the slot, and never greeted + /// us at all inside client.zig's `attempt` deadline + silent, + /// `Detach` in an attached frontend: THIS frontend leaves and the session + /// does not, which is the whole difference between it and `.none`. The name + /// is what to come back to, and empty when this frontend never knew it (an + /// `Attach` builtin's bare form: the core that held the word is gone by the + /// time the attached loop runs). + detached: []const u8, /// The nonzero exit lives HERE for the same reason the message does: every /// teardown this process owes is a defer registered after this one, so by /// the time this runs they have all run and there is nothing left to skip. fn report(e: AttachEnd, io: std.Io) void { var buf: [512]u8 = undefined; + // Set by the one ending that is not a failure. `Detach` is a word the + // user typed, so leaving is success: the line goes to STDOUT and the + // exit status is untouched, because there is still a session there to + // come back to. tmux's `[detached]` line is the same sentence for the + // same reason. + var ok = false; const text: []const u8 = switch (e) { .none => return, + .detached => |name| blk: { + ok = true; + break :blk if (name.len != 0) std.fmt.bufPrint( + &buf, + "pardes: detached from '{s}', which is still running — come back with `pardes --attach={s}`\n", + .{ name, name }, + ) catch "pardes: detached; that session is still running\n" else "pardes: detached; that session is still running — come back with `pardes --attach`\n"; + }, .no_session => |name| std.fmt.bufPrint( &buf, "pardes: no detached session called '{s}' (start one with `pardes --detach={s}`)\n", @@ -1802,130 +1959,83 @@ const AttachEnd = union(enum) { .lost => |err| std.fmt.bufPrint(&buf, "pardes: detached session lost: {t}\n", .{err}) catch "pardes: detached session lost\n", .rejected => "pardes: that session hung up on the connect — every frontend slot is taken (32), or it is shutting down. `PARDES_LOG=1` on the session names which\n", + .silent => "pardes: that session accepted the connection and then never greeted it — it is wedged inside its own loop, or something else is listening at that path. `PARDES_LOG=1` on the session says which\n", }; - std.Io.File.stderr().writeStreamingAll(io, text) catch {}; - std.process.exit(1); + const out = if (ok) std.Io.File.stdout() else std.Io.File.stderr(); + out.writeStreamingAll(io, text) catch {}; + if (!ok) std.process.exit(1); } -}; -/// The session `--attach` meant. `--attach=` is the name it says; bare -/// `--attach` is THE session, because bare `--detach` names itself by its own -/// pid and nobody can be expected to read a pid out of `$XDG_RUNTIME_DIR`. -/// With exactly one session listening that is the one meant; with none or -/// several this says which case it is instead of picking one. Null means "do -/// not open a terminal", with `end` already saying why. -/// -/// Both the directory and the filename convention come off ONE probe through -/// `server.sessionPath`, rather than being re-derived here, for the reason that -/// function exists at all: the side that binds and the side that looks must not -/// be able to disagree about where a session lives. -fn sessionName(buf: *[detached_server.path_max]u8, requested: []const u8, end: *AttachEnd) ?[]const u8 { - if (requested.len != 0) { - // A name is taken at its word — the connect in `attachSession` is the - // authority on whether anything is listening — except for the one case - // that is worth catching before a terminal is opened at all: a typo, - // where there is no socket file of that name whatsoever. Getting that - // wrong is the common failure, and the alternative is a full-screen - // alt-screen flash on the way to a one-line message. - var one_buf: [detached_server.path_max]u8 = undefined; - const one = detached_server.sessionPath(&one_buf, requested) orelse { - end.* = .{ .no_session = requested }; - return null; + /// The same reason on ONE pane message row, for the `Attach` builtin. It + /// exists because that path does not exit: `report` writes to a cooked main + /// screen on the way out of the process and can spend a clause on advice, + /// while this shares a row with a filename in a session that goes on + /// running. Same vocabulary, no `pardes:` prefix and no newline. + fn row(e: AttachEnd, buf: []u8) []const u8 { + return switch (e) { + // `report` reads `.none` as "exit 0, say nothing", and a message + // row only ever shows a failure — but a session that says `quit` + // before it greets is the one way this arm could be reached, and + // that is what it says. + .none => "attach: that session ended", + .no_session => |name| std.fmt.bufPrint(buf, "attach: no session '{s}'", .{name}) catch + "attach: no session under that name", + .nothing_detached => "attach: no detached session is running", + .ambiguous => |n| std.fmt.bufPrint(buf, "attach: {d} sessions running, name one", .{n}) catch + "attach: several sessions running, name one", + .refused => |why| switch (why) { + .version => "attach: that session is another build of pardes", + .full => "attach: that session has every frontend slot taken", + .quitting => "attach: that session is ending", + }, + .lost => |err| std.fmt.bufPrint(buf, "attach: {t}", .{err}) catch "attach: link lost", + .rejected => "attach: that session hung up on the connect", + .silent => "attach: that session accepted and never greeted", + // Never asked for: `attemptEnd` is the only caller and a connect + // that has not happened yet cannot have been detached from. Worded + // rather than left to an `else`, so the arm somebody adds next + // still has to be thought about. + .detached => "attach: detached from that session", }; - const F_OK: c_int = 0; - if (libc.access(one, F_OK) != 0) { - end.* = .{ .no_session = requested }; - return null; - } - return requested; - } - const probe_name = "0"; - var probe_buf: [detached_server.path_max]u8 = undefined; - const probe = detached_server.sessionPath(&probe_buf, probe_name) orelse { - end.* = .nothing_detached; - return null; - }; - const base = std.fs.path.basename(probe); - const cut = std.mem.lastIndexOf(u8, base, probe_name).?; - const prefix = base[0..cut]; - const suffix = base[cut + probe_name.len ..]; - var dir_buf: [detached_server.path_max:0]u8 = undefined; - const dir = std.fs.path.dirname(probe) orelse ""; - if (dir.len == 0 or dir.len >= dir_buf.len) { - end.* = .nothing_detached; - return null; } - @memcpy(dir_buf[0..dir.len], dir); - dir_buf[dir.len] = 0; - const d = libc.opendir(dir_buf[0..dir.len :0]) orelse { - end.* = .nothing_detached; - return null; - }; - defer _ = libc.closedir(d); - var found: usize = 0; - var len: usize = 0; - while (libc.readdir(d)) |ent| { - const entry = std.mem.sliceTo(&ent.name, 0); - if (entry.len <= prefix.len + suffix.len) continue; - if (!std.mem.startsWith(u8, entry, prefix) or !std.mem.endsWith(u8, entry, suffix)) continue; - const name = entry[prefix.len .. entry.len - suffix.len]; - if (name.len > buf.len) continue; - found += 1; - @memcpy(buf[0..name.len], name); - len = name.len; - } - // A socket file whose session is gone still counts here: the sweep that - // unlinks corpses runs when the NEXT session binds (server.zig `sweep`), - // and probing every candidate with a connect would put a phantom frontend - // into a live session's slot table just to count it. One stale file - // therefore fails at `open` with "no such session", which is the truth. - if (found != 1) { - end.* = if (found == 0) .nothing_detached else .{ .ambiguous = found }; - return null; - } - return buf[0..len]; -} +}; -/// `--attach[=]`: the frontend half of a detached session. This process -/// owns a terminal and a socket, and the `Pardes` is in the session process -/// (src/detached/). The whole job is client.zig's two sentences — send the -/// input it collects, draw the frames it is sent — plus the real host work a -/// daemon has no way to do and asks a frontend for: fork a shell on a real tty, -/// put bytes on a real disk, reach a real clipboard. +/// `--attach[=]` and the `Attach` builtin: the frontend half of a +/// detached session. This process owns a terminal and a socket; the `Pardes` +/// is in the session process (src/detached/). The whole job is client.zig's +/// two sentences — send the input it collects, draw the frames it is sent — +/// and since the daemon took its own IO back there is nothing else in it. +/// +/// THAT DELETION IS THE POINT. A frontend used to serve `spawn`, `pty_write`, +/// `pty_resize`, `write_file`, `write_dump`, `watch_file` and `dump_themes` +/// off the wire, which put every pane's shell in whichever frontend happened +/// to fork it and stopped that pane's output the moment that frontend left — +/// a daemon whose whole promise is outliving frontends killed your shells. A +/// unix socket means the two ends share a machine, so the daemon forks and +/// writes and watches for itself (src/host_io.zig, src/file_watch.zig) and +/// `wire.ServerTag` keeps exactly three effects, 0x10..: the clipboard both +/// ways and the browser, because each of those needs the display a human is +/// actually looking at. /// /// It is NOT a `Shell`, and the difference is not size. `Shell` IS the /// `Host.ctx` of a core in THIS process, and its methods reach into that core -/// on nearly every line — a pane's message row, a watch generation taken off -/// the pane's live text, `acknowledgeShell`, `setCwd`. With no core those are -/// not cheaper versions of the same work, they are absent, and each one is -/// named below where it goes missing. What the two do genuinely share is -/// shared: the cell walk, the key and mouse vocabularies, the paste ceiling, -/// `forkShell`, `readPty`, `writeFileBytes`. +/// on nearly every line — a pane's message row, `acknowledgeShell`, `setCwd`, +/// a watch generation taken off the pane's live text. With no core those are +/// not cheaper versions of the same work, they are absent. What the two +/// genuinely share is shared: the cell walk, the key and mouse vocabularies, +/// the paste ceiling, `copyToClipboard`, `requestClipboard`. const Attach = struct { - io: std.Io, gpa: std.mem.Allocator, client: *detached_client.Client, loop: *Loop, vx: *vaxis.Vaxis, tty: *vaxis.Tty, - prompt_rcs: *const shell_bin.PromptRcs, paste_buf: *std.Io.Writer.Allocating, - /// Where the config directory came from is main.zig's answer, carried in - /// `Options` — the only field of it this frontend reads, since every other - /// one describes a core that is elsewhere. - config_dir: ?[]const u8, - ptys: [pardes.MAX_PANES]?Pty = @splat(null), - gens: [pardes.MAX_PANES]u32 = @splat(0), - inotify_fd: c_int, - watches: file_watch.Table = @splat(null), - /// What each watched slot is watching. `file_watch.Table` remembers the - /// directory mark and the accepted generation but not the pathname — the - /// in-process host reads that back off the pane, and this one has no panes, - /// so the path the `watch_file` message carried is kept here. - watch_paths: [pardes.MAX_PANES]?[]u8 = @splat(null), - watch_task: ?std.Io.Future(anyerror!void) = null, + /// The session this frontend asked for, borrowed for the loop's lifetime + /// and only so `Detach` can name what to come back to. Empty when it is not + /// knowable here — see `AttachEnd.detached`. + session: []const u8 = &.{}, caps_pending: bool = true, - check_files: bool = false, in_paste: bool = false, /// A frame landed. Painted once at the end of the round rather than where /// it arrives: several can be decoded out of one poll and only the last of @@ -1936,21 +2046,22 @@ const Attach = struct { fn send(a: *Attach, ev: pardes.Event) ?AttachEnd { a.client.send(.{ .event = ev }) catch |err| switch (err) { // A message this protocol cannot carry, which is not a link that - // has died: `putSlice32` refuses past `wire.max_payload` (16 MiB), - // and the one event that can reach it is a `file_changed` for a - // watched file between that and `look.readFile`'s own 256 MiB cap. - // Dropping it costs one reload; treating it as a hangup would cost - // the session. + // has died: `putSlice32` refuses past `wire.max_payload` (16 MiB). + // One event still reaches it now that the watched files are the + // daemon's — an OSC 52 clipboard reply, whose size is whatever the + // terminal handed vaxis and which nothing in this file bounds + // (`max_paste_bytes` bounds the bracketed-paste assembly, not a + // decoded reply). Dropping it costs one paste; treating it as a + // hangup would cost the session. error.Overlong, error.NoSpace => return null, else => return .{ .lost = err }, }; return null; } - /// One decoded message from the session. Every arm of `wire.ServerMsg` is - /// named and none of them is a catch-all: a session goes on asking for what - /// it is not given, and the two this frontend genuinely cannot do say so - /// where they are handled rather than vanishing into an `else`. + /// One decoded message from the session. `wire.ServerMsg` has eight arms + /// and so has this switch — no catch-all, so a protocol that grows a ninth + /// stops compiling here rather than quietly ignoring it. fn handle(a: *Attach, msg: wire.ServerMsg) ?AttachEnd { switch (msg) { // Already applied to the client's slot and geometry; the full frame @@ -1961,41 +2072,22 @@ const Attach = struct { // returns, so all that is left is to say the screen moved. .frame => a.dirty = true, .quit => return .none, - - .spawn => |v| a.spawn(v.pane, v.cwd), - .pty_write => |v| if (a.ptys[v.pane]) |pt| writeFd(pt.file.handle, v.bytes), - .pty_resize => |v| if (a.ptys[v.pane]) |pt| { - const ws: posix.winsize = .{ .row = v.rows, .col = v.cols, .xpixel = 0, .ypixel = 0 }; - _ = posix.system.ioctl(pt.file.handle, TIOCSWINSZ, @intFromPtr(&ws)); - }, - .write_file => |v| a.writeFile(v.pane, v.path, v.bytes), - // The dump lands on this frontend's disk. WHERE it landed is the - // core's own memory of it (`setLastDump`), and there is no - // `ClientTag` to carry a path back, so a detached `Dump` writes the - // file and the session cannot then name it. - .write_dump => |bytes| { - var pbuf: [1024:0]u8 = undefined; - const path = pardes.dump.outPath(&pbuf) orelse return null; - _ = writeFileBytes(path, bytes); - }, - .watch_file => |v| a.watchFile(v.pane, v.path, v.on), - // NOT DOABLE FROM HERE, and it is the wire's shape rather than an - // omission: this message carries `{generation, on}`, the theme - // file's PATH lives in the core (`themeFileRequest`), and there is - // no message that would carry the reloaded bytes back. So a - // detached session does not live-reload a theme file. Named - // anyway, because the alternative is an `else` that would also - // swallow the next arm somebody adds to the protocol. - .watch_theme => {}, - .dump_themes => a.dumpThemes(), - .set_clipboard => |text| if (text.len != 0) { - a.vx.copyToSystemClipboard(a.tty.writer(), text, a.gpa) catch {}; - }, + // ...and its sibling, which is the same exit for the opposite + // reason: `quit` is the session ending under every frontend, and + // `Detach` is THIS frontend leaving one that carries on. The + // session keeps its panes, its shells and its other frontends, so + // there is nothing to report as a failure and something to come + // back to — see `AttachEnd.detached`. + .detach => return .{ .detached = a.session }, + // The three that are left, served by the same lines the local + // `Shell` runs for its own core's effects: this terminal's OSC 52 + // pair and this desktop's browser. + .set_clipboard => |text| copyToClipboard(a.vx, a.tty, a.gpa, text), // The answer is not a reply message: it comes back as an ordinary - // `Event.paste`, which is the same asynchronous shape the - // in-process host has (see `Shell.readClipboard`), and it arrives - // through the `.paste` arm of `apply` like any other. - .read_clipboard => a.vx.requestSystemClipboard(a.tty.writer()) catch {}, + // `Event.paste` through the `.paste` arm of `apply`, like any other + // input, which is the same asynchronous shape `pull_read_clipboard` + // has in-process. + .read_clipboard => requestClipboard(a.vx, a.tty), .open_link => |url| look.openLink(url), } return null; @@ -2008,10 +2100,16 @@ const Attach = struct { // is `Shell.waitInput`'s animation clock, and an animation runs // where the core is — the session sleeps on its own frame interval // (server.zig `nap`) and the frames simply arrive. `fs_ready` - // belongs to `--fs`, which wire.zig keeps in the detached process. - // `lsp_done`/`pipe_done` answer work the core dispatches, and it - // dispatches it there. - .nop, .tick, .fs_ready, .lsp_done, .pipe_done => {}, + // belongs to `--fs`, which lives with the core. `lsp_done` and + // `pipe_done` answer work the core dispatches, and it dispatches it + // there. `pty_read`, `pty_eof` and `files_changed` are the ones + // that MOVED: the daemon forks the pane shells and holds the + // inotify instance now, so the only descriptors this process reads + // are its terminal and one socket. An in-place switch (`Attach` in + // a local session) cancels its readers and its watcher and drains + // this queue before the attached loop starts, so not even a late + // post from the session it just left arrives here. + .nop, .tick, .fs_ready, .lsp_done, .pipe_done, .pty_read, .pty_eof, .files_changed => {}, .quit => return .none, .focus_in => {}, .focus_out => return a.send(.pointer_leave), @@ -2024,18 +2122,6 @@ const Attach = struct { a.dirty = true; a.client.resize(ws.cols, ws.rows) catch |err| return .{ .lost = err }; }, - .pty_read => |pr| { - defer a.gpa.free(pr.bytes); - return a.send(.{ .output = .{ .pane = @intCast(pr.id), .bytes = pr.bytes } }); - }, - .pty_eof => |e| if (a.gens[e.id] == e.gen) { - if (a.ptys[e.id]) |*pt| { - pt.reader.await(a.io) catch {}; // reader just finished; join it or its future leaks - _ = libc.close(pt.file.handle); - a.ptys[e.id] = null; - } - return a.send(.{ .eof = .{ .pane = @intCast(e.id) } }); - }, .key_press => |key| if (a.in_paste) { const bytes = pasteBytes(key); const room = max_paste_bytes -| a.paste_buf.written().len; @@ -2058,126 +2144,23 @@ const Attach = struct { const pasted = a.paste_buf.written(); if (pasted.len > 0) return a.send(.{ .paste = pasted }); }, + // A pardes launched inside a pane shell hands its file to the + // nearest pardes ANCESTOR (nested.zig `outer`), and now that the + // daemon forks those shells that ancestor is the daemon — which is + // why `attachSession` binds no listener at all. The one line that + // still reaches this arm is a switch racing itself: a local session + // whose own child wrote to `localSession`'s listener in the moment + // before `Attach` gave the terminal away, on a thread that is + // detached and so cannot be joined ahead of the queue drain. It + // goes over the wire, which is what `ClientTag.command` is for. .command => |line| { defer a.gpa.free(line); return a.send(.{ .command = line }); }, - .files_changed => a.check_files = true, - } - return null; - } - - fn spawn(a: *Attach, pane: u8, cwd: []const u8) void { - // The in-process host's reaping rule, and its reason: the core reuses - // pane ids and there is no close effect, so a deleted pane's shell - // lives in its slot until a respawn lands here. - if (a.ptys[pane]) |*old| { - old.reader.cancel(a.io) catch {}; - _ = libc.close(old.file.handle); - a.ptys[pane] = null; - } - a.gens[pane] +%= 1; - var cwd_buf: [256:0]u8 = undefined; - var cwd_z: ?[*:0]const u8 = null; - if (cwd.len > 0 and cwd.len < cwd_buf.len) { - @memcpy(cwd_buf[0..cwd.len], cwd); - cwd_buf[cwd.len] = 0; - cwd_z = @ptrCast(&cwd_buf); - } - // The SESSION's grid, which is what its panes are laid out against; the - // pane's own size follows immediately as a `pty_resize`. - // - // `config.default_shell` and not the session's configured one: `Shell - // ` is a core setting, no message carries it, and inventing a - // second place that decides which shell runs would be worse than one - // that is occasionally the default. `shell_bin.resolve` falls back from - // there exactly as it does for a whole session. - const child = forkShell(null, pane, a.prompt_rcs, config.default_shell, cwd_z, a.client.rows, a.client.cols, null); - a.ptys[pane] = .{ .file = child.file, .pid = child.pid, .reader = .{ .any_future = null, .result = {} } }; - // Unconditional, unlike `Shell.spawn`'s `threads_ok`: every spawn here - // arrives over a socket this loop is already running, so there is no - // pre-loop drain to be in. - if (a.ptys[pane]) |*pt| { - pt.reader = a.io.concurrent(readPty, .{ a.io, a.gpa, pt.file, @as(usize, pane), a.gens[pane], a.loop }) catch pt.reader; - } - } - - fn writeFile(a: *Attach, pane: u8, path: []const u8, bytes: []const u8) void { - if (!writeFileBytes(path, bytes)) return; - // Our own write is about to come back as a watch event: restamp from - // the bytes we just put there so it reads as "no change". Only when - // this IS the path this slot is watching — a `Save ` must - // not silence a real change to the file the pane has open. Same rule as - // `Shell.writeFile`; the comparison is against the path the session - // asked us to watch, because there is no pane here to ask. - // - // The "saved " message that host also writes is a pane's message - // row, which belongs to the core: a detached save is silent. - const watched = a.watch_paths[pane] orelse return; - if (!std.mem.eql(u8, watched, path)) return; - if (a.watches[pane]) |*w| w.generation = .{ .text = std.hash.Wyhash.hash(0, bytes) }; - } - - fn watchFile(a: *Attach, pane: u8, path: []const u8, on: bool) void { - if (a.watch_paths[pane]) |old| a.gpa.free(old); - a.watch_paths[pane] = null; - if (!on or path.len == 0) return file_watch.watchPane(a.inotify_fd, &a.watches, pane, null, 0, .{ .text = 0 }); - const owned = a.gpa.dupe(u8, path) catch return; - // Seeded from what is on disk RIGHT NOW, so the first `file_changed` - // this sends is the first edit that is not already in the core. The - // in-process host takes the same hash off the pane's live text; that - // text is a socket away, and the file it came from is not. - var hash: u64 = 0; - if (look.readFile(a.gpa, owned)) |bytes| { - hash = std.hash.Wyhash.hash(0, bytes); - a.gpa.free(bytes); - } else |_| {} - a.watch_paths[pane] = owned; - file_watch.watchPane(a.inotify_fd, &a.watches, pane, owned, 0, .{ .text = hash }); - } - - /// A coalesced inotify wake: re-read every watched path and hand the - /// session the ones that really changed. It sends BYTES rather than - /// reloading anything, because the text belongs to the core — which is - /// exactly why `ClientTag` has a `file_changed` at all. - fn reloadWatched(a: *Attach) ?AttachEnd { - if (!a.check_files) return null; - a.check_files = false; - for (a.watch_paths, 0..) |slot, pane| { - const path = slot orelse continue; - const w = if (a.watches[pane]) |*entry| entry else continue; - const bytes = look.readFile(a.gpa, path) catch continue; - defer a.gpa.free(bytes); - const hash = std.hash.Wyhash.hash(0, bytes); - switch (w.generation) { - .text => |accepted| if (accepted == hash) continue, - // Never stored by this frontend: it cannot tell a PDF pane from - // a text one (the message carries a path and nothing else), so - // every slot is hashed and the core decides what the bytes mean - // — `applyWatchedFileChanged` reopens the path for a PDF pane - // and ignores them. - .pdf => {}, - } - // Committed here rather than after an acknowledgement, because - // there is none: `file_changed` is a one-way event like every other - // input on this wire. A snapshot the core rejects is therefore not - // retried until the file changes again — the same bound the - // in-process host lives with whenever a reload fails. - w.generation = .{ .text = hash }; - if (a.send(.{ .file_changed = .{ .pane = @intCast(pane), .bytes = bytes } })) |end| return end; } return null; } - /// The themes land on this frontend's disk. Where they went is reported on - /// a pane's message row by the in-process host, and that row is the core's, - /// so a detached dump is silent — the same shape as `write_dump` above. - fn dumpThemes(a: *Attach) void { - const dir = a.config_dir orelse return; - const out = user_config.dumpThemes(a.io, a.gpa, dir, pardes.themes) catch return; - a.gpa.free(out); - } - /// The capability handshake, resolved on the loop exactly as /// `Shell.pollFrame` resolves it and for its reason: the replies land on /// vaxis's reader thread, and this is the only thread allowed to write to @@ -2205,32 +2188,83 @@ const Attach = struct { } }; -/// How long the frontend may sleep on the socket before it looks at the -/// terminal. This loop has TWO event sources and can block on only one of -/// them: vaxis delivers the terminal's events on its reader thread into a -/// mutex/condvar queue, which has no descriptor to hand `poll(2)` alongside -/// the socket — client.zig's `wait` takes a timeout for exactly that reason -/// ("the terminal it draws on is polled by whoever owns that"), and this is -/// whoever. +/// One `detached_client.Attempt` that did NOT come back with a client, in this +/// file's own vocabulary. `requested` is what the user actually typed, because +/// the union has a single `no_session` where this file has two ends for it: a +/// name that resolved to nothing is a typo to correct, and no name at all is a +/// session to start. +fn attemptEnd(a: *const detached_client.Attempt, requested: []const u8) AttachEnd { + return switch (a.*) { + // Both callers take the client out of the `.greeted` arm themselves, so + // this is only ever asked about a failure; `.none` is what "nothing to + // report" is spelled as everywhere else in this union. + .greeted => .none, + .no_session => if (requested.len != 0) .{ .no_session = requested } else .nothing_detached, + .ambiguous => |found| .{ .ambiguous = found }, + .refused => |why| .{ .refused = why }, + .silent => .silent, + // A hangup with no reason decoded is what `rejected` was written for: + // server.zig's `refuseFd` writes six bytes and closes in the same pass, + // so the close can beat the reason onto the socket. client.zig's `give` + // already prefers a refusal it did decode, so an `error.Closed` that + // reaches here is that race and nothing else. + .lost => |err| if (err == error.Closed) .rejected else .{ .lost = err }, + }; +} + +/// The attached loop: two event sources, one screen, no core. A function of its +/// own because there are two ways to become attached and only one loop — +/// `--attach` on the command line (`attachSession`, which opens the terminal +/// for it) and the `Attach` builtin (see `localSession`, whose terminal already +/// had one) — and past the connect the two are indistinguishable. Every thread +/// it needs (vaxis's reader, the SIGWINCH sigwait) is the caller's to have +/// started, which is the whole difference between the two entries. +fn attachLoop(a: *Attach) AttachEnd { + while (true) { + const link = a.client.wait(detached_client.poll_ms); + // DECODE BEFORE REACTING TO THE HANGUP. `wait` reports the close in + // the same call that read the last bytes, and the last bytes are the + // session's `quit`: `fill` appends every chunk and only then sees the + // zero-length read. client.zig prefers POLLIN over POLLHUP for exactly + // this reason, and honouring that means draining what arrived before + // deciding the link is what ended us — otherwise an ordinary `Kill` + // exits one frontend 0 (it got the quit alone) and whichever frontend + // was in the same poll round nonzero, which is what the first run of + // this loop actually did. + while (true) { + const msg = (a.client.next() catch |err| return .{ .lost = err }) orelse break; + if (a.handle(msg)) |end| return end; + } + link catch |err| return .{ .lost = err }; + // The terminal, drained the way `Shell.waitInput` drains it and for its + // reason: a wheel flick is one batch rather than fifty round trips, and + // a paste in flight keeps draining without a message per character. + var batch: usize = 0; + while (a.in_paste or batch < 64) { + // Propagated rather than swallowed, unlike `Shell.waitInput`'s + // identical drain: there the blocking `nextEvent` above it is what + // notices a dead event source, and here there is no blocking read + // to notice with. + const ev = (a.loop.tryEvent() catch |err| return .{ .lost = err }) orelse break; + batch += 1; + if (a.apply(ev)) |end| return end; + } + a.enableCaps(); + if (a.dirty) a.paint(); + } +} + +/// The whole `--attach` run: connect, then `attachLoop` until the session, the +/// link or the terminal ends it. The terminal is already raw, on the alt screen +/// and reporting the mouse — `run` did that, and `run`'s defers undo it, which +/// is what makes an attach leave a terminal in exactly the state an ordinary +/// exit does. /// -/// 8 ms is half a 60 Hz frame: a keystroke waits at most one of those before it -/// is on the wire (4 ms on average), and the frame it causes needs no wait at -/// all — it lands in the poll the moment the session writes it. The price of -/// the ceiling is 125 poll rounds a second on a frontend nobody is touching, -/// and it is measurably below the noise of what an idle pardes already costs: -/// on this machine (i7-11700, 100 Hz jiffies) an idle attached frontend used -/// 0.16% of one core over 60 s and 0.18% over 120 s, against 0.11% and 0.31% -/// for an idle in-process session on the same screen over the same windows. -/// Reach for an eventfd and a waker thread — fuse.zig's `pollLoop` is the -/// pattern — only if that ever stops being true. -const attach_poll_ms = 8; - -/// The whole `--attach` session: connect, then one loop over the two event -/// sources until the session, the link or the terminal ends it. The terminal is -/// already raw, on the alt screen and reporting the mouse — `run` did that, and -/// `run`'s defers undo it, which is what makes an attach leave a terminal in -/// exactly the state an ordinary exit does. -fn attachSession(init: std.process.Init, opts: pardes.Options, name: []const u8, tty: *vaxis.Tty, vx: *vaxis.Vaxis) AttachEnd { +/// No `Options` reaches here any more. Every field of it describes a core, and +/// the last two this frontend read went with the work that read them: the +/// config directory served a `dump_themes` the daemon now does itself, and +/// `nested` gated a listener for children this process no longer has. +fn attachSession(init: std.process.Init, name: []const u8, tty: *vaxis.Tty, vx: *vaxis.Vaxis) AttachEnd { const io = init.io; const gpa = init.gpa; @@ -2244,16 +2278,18 @@ fn attachSession(init: std.process.Init, opts: pardes.Options, name: []const u8, if (ws.cols == 0 or ws.rows == 0) return .{ .lost = error.NoWinsize }; vx.resize(gpa, tty.writer(), ws) catch |err| return .{ .lost = err }; - var client = detached_client.Client.open(gpa, name, ws.cols, ws.rows) catch |err| return switch (err) { - error.NoSession, error.NoSessionPath => .{ .no_session = name }, - // The connect landed and the session hung up during the hello. That is - // what a refusal AT ACCEPT TIME looks like from here: server.zig's - // `refuseFd` writes six bytes and closes in the same pass, so the close - // can beat our hello onto the socket and `open` never gets far enough - // to read the reason. A refusal we do read arrives as `.refused` with - // the reason in it. - error.Closed => .rejected, - else => .{ .lost = err }, + // The SAME three steps the `Attach` builtin takes, through the same + // function, because the two entries drifted apart the last time they were + // written separately: `--attach` resolved a bare name and the builtin did + // not, so the documented `SPC s a` answered `NoSessionPath` at a session + // that was listening. `attempt` resolves, connects, and waits to be + // GREETED. This path could afford to meet a refusal inside the loop below + // — it has no local session to lose — but there is no second sequence to + // maintain, so it does not have its own. + var attempt = detached_client.attempt(gpa, name, ws.cols, ws.rows); + var client = switch (attempt) { + .greeted => |c| c, + else => return attemptEnd(&attempt, name), }; // `detach` and not `deinit`: seven bytes that turn "the peer vanished" into // "the peer left" in the session's log. @@ -2262,105 +2298,35 @@ fn attachSession(init: std.process.Init, opts: pardes.Options, name: []const u8, var loop: Loop = .init(io, tty, vx); var paste_buf: std.Io.Writer.Allocating = .init(gpa); defer paste_buf.deinit(); - // Private and complete before any fork, exactly as in `run`: the pane - // shells this frontend is asked to spawn borrow these stable path buffers. - var prompt_rcs = shell_bin.PromptRcs.init(); - defer prompt_rcs.deinit(); var a: Attach = .{ - .io = io, .gpa = gpa, .client = &client, .loop = &loop, .vx = vx, .tty = tty, - .prompt_rcs = &prompt_rcs, .paste_buf = &paste_buf, - .config_dir = opts.config_dir, - .inotify_fd = if (builtin.os.tag == .linux) libc.inotify_init1(linux.IN.CLOEXEC) else -1, + // Concrete here, unlike the builtin's path: `run` resolved a bare + // `--attach` to one name before it opened the terminal, and it outlives + // this call. So a `Detach` from a `--attach` frontend can say what to + // come back to. + .session = name, }; - // Registered BEFORE `loop.stop()` below so LIFO runs it after: the reader - // has to be joined before the queue is emptied, or a late post lands in a - // queue nobody drains again and its bytes leak. `run`'s teardown has the - // same shape and the same order, minus the workers this frontend never - // starts. - defer { - for (&a.ptys) |*slot| if (slot.*) |*pt| { - pt.reader.cancel(io) catch {}; - _ = libc.close(pt.file.handle); - slot.* = null; - }; - if (a.watch_task) |*t| { - t.cancel(io) catch {}; - a.watch_task = null; - } - if (a.inotify_fd >= 0) { - _ = libc.close(a.inotify_fd); - a.inotify_fd = -1; - } - for (&a.watch_paths) |*slot| if (slot.*) |p| { - gpa.free(p); - slot.* = null; - }; - while (loop.tryEvent() catch null) |ev| switch (ev) { - .pty_read => |pr| gpa.free(pr.bytes), - .command => |line| gpa.free(line), - .paste => |b| gpa.free(@constCast(b)), - else => {}, - }; - } - - // A pardes started inside one of THIS frontend's pane shells finds this - // process as its outer instance — the shells are our children — so the - // nested-instance listener belongs here and not in the session, which has - // no children at all. The command line it accepts goes over the wire as an - // `Event.command`, which is what `ClientTag.command` is for. - const sock_fd: c_int = if (opts.nested) -1 else nested.listen(); - defer nested.unlisten(sock_fd); + // The whole teardown this frontend owes, which is now one queue drain: no + // ptys, no watches, no workers, nothing forked. Registered BEFORE + // `loop.stop()` below so LIFO runs it after — vaxis's reader has to be + // joined before the queue is emptied, or a late post lands in a queue + // nobody drains again and its bytes leak. + defer drainAttachedQueue(&loop, gpa); loop.start() catch |err| return .{ .lost = err }; defer loop.stop(); - // Both detached threads, for `run`'s reasons: sigwait and accept4 never - // return, so an `io.concurrent` task around either would hang the teardown - // that joins it. + // Detached rather than an `io.concurrent` task, for `run`'s reason: sigwait + // never returns, so a task around it would hang the teardown that joins it. (std.Thread.spawn(.{}, winchWatch, .{ &loop, vx, tty }) catch |err| return .{ .lost = err }).detach(); - if (sock_fd >= 0) (std.Thread.spawn(.{}, lookServer, .{ gpa, sock_fd, &loop }) catch |err| return .{ .lost = err }).detach(); - if (a.inotify_fd >= 0) a.watch_task = io.concurrent(watchFiles, .{ io, a.inotify_fd, &loop }) catch null; // Send the capability probes and do not wait on them; `Attach.enableCaps` // resolves them on the loop. run() has the long version of why. vx.queryTerminalSend(tty.writer()) catch {}; - while (true) { - const link = a.client.wait(attach_poll_ms); - // DECODE BEFORE REACTING TO THE HANGUP. `wait` reports the close in - // the same call that read the last bytes, and the last bytes are the - // session's `quit`: `fill` appends every chunk and only then sees the - // zero-length read. client.zig prefers POLLIN over POLLHUP for exactly - // this reason, and honouring that means draining what arrived before - // deciding the link is what ended us — otherwise an ordinary `Kill` - // exits one frontend 0 (it got the quit alone) and whichever frontend - // was in the same poll round nonzero, which is what the first run of - // this loop actually did. - while (true) { - const msg = (a.client.next() catch |err| return .{ .lost = err }) orelse break; - if (a.handle(msg)) |end| return end; - } - link catch |err| return .{ .lost = err }; - // The terminal, drained the way `Shell.waitInput` drains it and for its - // reason: a wheel flick is one batch rather than fifty round trips, and - // a paste in flight keeps draining without a message per character. - var batch: usize = 0; - while (a.in_paste or batch < 64) { - // Propagated rather than swallowed, unlike `Shell.waitInput`'s - // identical drain: there the blocking `nextEvent` above it is what - // notices a dead event source, and here there is no blocking read - // to notice with. - const ev = (loop.tryEvent() catch |err| return .{ .lost = err }) orelse break; - batch += 1; - if (a.apply(ev)) |end| return end; - } - if (a.reloadWatched()) |end| return end; - a.enableCaps(); - if (a.dirty) a.paint(); - } + return attachLoop(&a); } diff --git a/test/snapshots/boot.golden b/test/snapshots/boot.golden index afb20590..12f20b9e 100644 --- a/test/snapshots/boot.golden +++ b/test/snapshots/boot.golden @@ -6,31 +6,31 @@ == style boot grid=100x30 |0: 0-74 #949494,#121212, 75-99 d,#121212, |1: 0-1 #ffffff,#626262, 2-79 #949494,#121212, 80-99 d,#121212, -|2: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-99 d,#080808, -|3: 0-0 d,#444444, 1-1 d,#080808, 2-10 #bdbdbd,#080808, 11-99 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|3: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, |4: 0-0 d,#444444, 1-99 d,#080808, -|5: 0-0 d,#444444, 1-99 d,#080808, -|6: 0-0 d,#444444, 1-99 d,#080808, -|7: 0-0 d,#444444, 1-99 d,#080808, -|8: 0-0 d,#444444, 1-99 d,#080808, -|9: 0-0 d,#444444, 1-99 d,#080808, -|10: 0-0 d,#444444, 1-99 d,#080808, -|11: 0-0 d,#444444, 1-99 d,#080808, -|12: 0-0 d,#444444, 1-99 d,#080808, -|13: 0-0 d,#444444, 1-99 d,#080808, -|14: 0-0 d,#444444, 1-99 d,#080808, -|15: 0-0 d,#444444, 1-99 d,#080808, -|16: 0-0 d,#444444, 1-99 d,#080808, -|17: 0-0 d,#444444, 1-99 d,#080808, -|18: 0-0 d,#444444, 1-99 d,#080808, -|19: 0-0 d,#444444, 1-99 d,#080808, -|20: 0-0 d,#444444, 1-99 d,#080808, -|21: 0-0 d,#444444, 1-99 d,#080808, -|22: 0-0 d,#444444, 1-99 d,#080808, -|23: 0-0 d,#444444, 1-99 d,#080808, -|24: 0-0 d,#444444, 1-99 d,#080808, -|25: 0-0 d,#444444, 1-99 d,#080808, -|26: 0-0 d,#444444, 1-99 d,#080808, -|27: 0-0 d,#444444, 1-99 d,#080808, -|28: 0-0 d,#444444, 1-99 d,#080808, -|29: 0-0 d,#444444, 1-99 d,#080808, +|5: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|6: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|7: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|8: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|9: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|10: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|11: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|12: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|13: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|14: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|15: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|16: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-99 #bdbdbd,#080808, diff --git a/test/snapshots/builtins.golden b/test/snapshots/builtins.golden index 74db25e8..d37ff516 100644 --- a/test/snapshots/builtins.golden +++ b/test/snapshots/builtins.golden @@ -42,37 +42,37 @@ |64: 30 SPC h t Tutor topbar |65: 31 SPC j i Forward C-i == snap index-tail grid=120x66 cursor=7,64 -|35: 46 SPC n t Newtty -|36: 47 SPC p ClipPaste -|37: 48 SPC s d Dump topbar -|38: 49 SPC s r Restore -|39: 50 SPC t a Ascii -|40: 51 SPC t b Tagbottom -|41: 52 SPC t c Colors -|42: 53 SPC t d Debug topbar -|43: 54 SPC t i PdfTint -|44: 55 SPC t l Palette -|45: 56 SPC t n NextColor topbar -|46: 57 SPC t p Petscii -|47: 58 SPC t s PdfSections -|48: 59 SPC t t ThemeSel -|49: 60 SPC t w Wrap -|50: 61 SPC t z PdfFit -|51: 62 SPC w h Left C-w h, C-w left -|52: 63 SPC w j Down C-w j, C-w down -|53: 64 SPC w k Up C-w k, C-w up -|54: 65 SPC w l Right C-w l, C-w right -|55: 66 SPC y ClipYank -|56: 67 Look enter, right-click -|57: 68 Exec tab, middle-click -|58: 69 Msg -|59: 70 ThemeFile -|60: 71 DumpThemes -|61: 72 Filter -|62: 73 EffectCode -|63: 74 Theme -|64: 75 Shell -|65: 76 +|35: 48 SPC s D Detach +|36: 49 SPC s a Attach +|37: 50 SPC s d Dump topbar +|38: 51 SPC s r Restore +|39: 52 SPC t a Ascii +|40: 53 SPC t b Tagbottom +|41: 54 SPC t c Colors +|42: 55 SPC t d Debug topbar +|43: 56 SPC t i PdfTint +|44: 57 SPC t l Palette +|45: 58 SPC t n NextColor topbar +|46: 59 SPC t p Petscii +|47: 60 SPC t s PdfSections +|48: 61 SPC t t ThemeSel +|49: 62 SPC t w Wrap +|50: 63 SPC t z PdfFit +|51: 64 SPC w h Left C-w h, C-w left +|52: 65 SPC w j Down C-w j, C-w down +|53: 66 SPC w k Up C-w k, C-w up +|54: 67 SPC w l Right C-w l, C-w right +|55: 68 SPC y ClipYank +|56: 69 Look enter, right-click +|57: 70 Exec tab, middle-click +|58: 71 Msg +|59: 72 ThemeFile +|60: 73 DumpThemes +|61: 74 Filter +|62: 75 EffectCode +|63: 76 Theme +|64: 77 Shell +|65: 78 == snap not-a-picker grid=120x66 cursor=7,64 == snap window-group grid=120x66 cursor=7,35 |35: 1 pardes builtins under SPC w diff --git a/test/snapshots/chordcut.golden b/test/snapshots/chordcut.golden index 49b3d7bb..07628820 100644 --- a/test/snapshots/chordcut.golden +++ b/test/snapshots/chordcut.golden @@ -46,37 +46,37 @@ == style chordsel grid=120x30 |0: 0-74 #949494,#121212, 75-119 d,#121212, |1: 0-1 #ffffff,#262626, 2-58 #949494,#121212, 59-59 d,#121212, 60-61 #ffffff,#262626, 62-117 #949494,#121212, 118-119 d,#121212, -|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-63 #bdbdbd,#080808, 64-119 d,#080808, -|3: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-24 #bdbdbd,#080808, 25-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-73 #bdbdbd,#080808, 74-119 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|3: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-24 #bdbdbd,#080808, 25-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, |4: 0-1 #ffffff,#626262, 2-58 #949494,#121212, 59-59 d,#121212, 60-60 d,#444444, 61-119 d,#080808, -|5: 0-0 d,#444444, 1-1 d,#080808, 2-6 #949494,#121212, 7-10 #080808,#80a0ff, 11-12 #bdbdbd,#080808, 13-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|6: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|7: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|8: 0-1 #ffffff,#262626, 2-58 #949494,#121212, 59-59 d,#121212, 60-60 d,#444444, 61-119 d,#080808, -|9: 0-0 d,#444444, 1-1 d,#080808, 2-13 #bdbdbd,#080808, 14-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|10: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|11: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|12: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|13: 0-0 d,#444444, 1-1 d,#080808, 2-8 #bdbdbd,#080808, 9-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|14: 0-0 d,#444444, 1-1 d,#080808, 2-4 #bdbdbd,#080808, 5-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|15: 0-0 d,#121212, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|16: 0-1 #ffffff,#262626, 2-58 #949494,#121212, 59-59 d,#121212, 60-60 d,#444444, 61-119 d,#080808, -|17: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|18: 0-0 d,#444444, 1-1 d,#080808, 2-13 #bdbdbd,#080808, 14-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|19: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|20: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|21: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|22: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|23: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|24: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|25: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|26: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|27: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|28: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|29: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, +|5: 0-0 d,#444444, 1-1 d,#080808, 2-6 #949494,#121212, 7-10 #080808,#80a0ff, 11-12 #bdbdbd,#080808, 13-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|6: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|7: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|8: 0-1 #ffffff,#262626, 2-58 #949494,#121212, 59-59 d,#121212, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|9: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|10: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|11: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|12: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|13: 0-0 d,#444444, 1-1 d,#080808, 2-8 #bdbdbd,#080808, 9-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|14: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|15: 0-0 d,#121212, 1-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|16: 0-1 #ffffff,#262626, 2-58 #949494,#121212, 59-59 d,#121212, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, == style clickdismiss grid=120x30 -|5: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|6: 0-0 d,#444444, 1-1 d,#080808, 2-6 #949494,#121212, 7-12 #bdbdbd,#080808, 13-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, +|5: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|6: 0-0 d,#444444, 1-1 d,#080808, 2-6 #949494,#121212, 7-12 #bdbdbd,#080808, 13-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, == snap ttyprobe grid=120x30 cursor=42,15 |3: 2 2echo B''OO g.txt f.txt g.txt |4: /tmp/pardes-snap/chordcut/cwd/g.txt * Save New Newtty Del diff --git a/test/snapshots/cursorline.golden b/test/snapshots/cursorline.golden index 1d59d727..9f503745 100644 --- a/test/snapshots/cursorline.golden +++ b/test/snapshots/cursorline.golden @@ -21,35 +21,35 @@ == style top grid=150x30 |0: 0-74 #949494,#121212, 75-149 d,#121212, |1: 0-1 #ffffff,#626262, 2-60 #949494,#121212, 61-74 d,#121212, 75-76 #ffffff,#262626, 77-134 #949494,#121212, 135-149 d,#121212, -|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #949494,#121212, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-78 #bdbdbd,#080808, 79-149 d,#080808, -|3: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-83 #bdbdbd,#080808, 84-149 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #949494,#121212, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|3: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, |4: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|5: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|6: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|7: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|8: 0-1 #ffffff,#262626, 2-60 #949494,#121212, 61-74 d,#121212, 75-75 d,#444444, 76-149 d,#080808, -|9: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|10: 0-0 d,#444444, 1-1 d,#080808, 2-8 #bdbdbd,#080808, 9-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|11: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|12: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|13: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|14: 0-0 d,#444444, 1-1 d,#080808, 2-10 #bdbdbd,#080808, 11-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|15: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|16: 0-1 #ffffff,#262626, 2-60 #949494,#121212, 61-74 d,#121212, 75-75 d,#444444, 76-149 d,#080808, -|17: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|18: 0-0 d,#444444, 1-1 d,#080808, 2-8 #bdbdbd,#080808, 9-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|19: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|20: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|21: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|22: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|23: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|24: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|25: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|26: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|27: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|28: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|29: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, +|5: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|6: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|7: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|8: 0-1 #ffffff,#262626, 2-60 #949494,#121212, 61-74 d,#121212, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|9: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|10: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|11: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|12: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|13: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|14: 0-0 d,#444444, 1-1 d,#080808, 2-10 #bdbdbd,#080808, 11-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|15: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|16: 0-1 #ffffff,#262626, 2-60 #949494,#121212, 61-74 d,#121212, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, == snap down3 grid=150x30 cursor=7,5 == style down3 grid=150x30 -|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-78 #bdbdbd,#080808, 79-149 d,#080808, -|5: 0-0 d,#121212, 1-1 d,#080808, 2-6 #949494,#121212, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|5: 0-0 d,#121212, 1-1 d,#080808, 2-6 #949494,#121212, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, diff --git a/test/snapshots/edit.golden b/test/snapshots/edit.golden index 9295ccb7..c37c9aef 100644 --- a/test/snapshots/edit.golden +++ b/test/snapshots/edit.golden @@ -21,31 +21,31 @@ == style inserted grid=100x30 |0: 0-74 #949494,#121212, 75-99 d,#121212, |1: 0-1 #ffffff,#626262, 2-49 #949494,#121212, 50-51 #ffffff,#262626, 52-99 #949494,#121212, -|2: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-49 d,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-53 #bdbdbd,#080808, 54-99 d,#080808, -|3: 0-0 d,#444444, 1-1 d,#080808, 2-6 #bdbdbd,#080808, 7-49 d,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-56 #bdbdbd,#080808, 57-99 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|3: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, |4: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|5: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|6: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|7: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|8: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|9: 0-0 d,#444444, 1-1 d,#080808, 2-15 #bdbdbd,#080808, 16-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|10: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|11: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|12: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|13: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|14: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|15: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|16: 0-1 #ffffff,#262626, 2-49 #949494,#121212, 50-50 d,#444444, 51-99 d,#080808, -|17: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|18: 0-0 d,#444444, 1-1 d,#080808, 2-6 #bdbdbd,#080808, 7-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|19: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|20: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|21: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|22: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|23: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|24: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|25: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|26: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|27: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|28: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|29: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, +|5: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|6: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|7: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|8: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|9: 0-0 d,#444444, 1-1 d,#080808, 2-15 #bdbdbd,#080808, 16-49 d,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|10: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|11: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|12: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|13: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|14: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|15: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|16: 0-1 #ffffff,#262626, 2-49 #949494,#121212, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, diff --git a/test/snapshots/fsearch.golden b/test/snapshots/fsearch.golden index a96657c5..4b7832e6 100644 --- a/test/snapshots/fsearch.golden +++ b/test/snapshots/fsearch.golden @@ -26,34 +26,34 @@ == style results grid=180x30 |0: 0-74 #949494,#121212, 75-179 d,#121212, |1: 0-1 #ffffff,#626262, 2-69 #949494,#121212, 70-89 d,#121212, 90-91 #ffffff,#262626, 92-159 #949494,#121212, 160-179 d,#121212, -|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-8 #bdbdbd,#080808, 9-89 d,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-93 #bdbdbd,#080808, 94-179 d,#080808, -|3: 0-0 d,#121212, 1-1 d,#080808, 2-6 #949494,#121212, 7-10 #080808,#80a0ff, 11-12 #bdbdbd,#080808, 13-89 d,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-96 #bdbdbd,#080808, 97-179 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-8 #bdbdbd,#080808, 9-89 d,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|3: 0-0 d,#121212, 1-1 d,#080808, 2-6 #949494,#121212, 7-10 #080808,#80a0ff, 11-12 #bdbdbd,#080808, 13-89 d,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, |4: 0-1 #ffffff,#262626, 2-69 #949494,#121212, 70-89 d,#121212, 90-90 d,#444444, 91-179 d,#080808, -|5: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-24 #bdbdbd,#080808, 25-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|6: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-24 #bdbdbd,#080808, 25-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|7: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-25 #bdbdbd,#080808, 26-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|8: 0-1 #ffffff,#262626, 2-69 #949494,#121212, 70-89 d,#121212, 90-90 d,#444444, 91-179 d,#080808, -|9: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|10: 0-0 d,#444444, 1-1 d,#080808, 2-6 #bdbdbd,#080808, 7-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|11: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|12: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|13: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|14: 0-0 d,#444444, 1-1 d,#080808, 2-8 #bdbdbd,#080808, 9-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|15: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|16: 0-1 #ffffff,#262626, 2-69 #949494,#121212, 70-89 d,#121212, 90-90 d,#444444, 91-179 d,#080808, -|17: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|18: 0-0 d,#444444, 1-1 d,#080808, 2-6 #bdbdbd,#080808, 7-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|19: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|20: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|21: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|22: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|23: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|24: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|25: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|26: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|27: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|28: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, -|29: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-179 d,#080808, +|5: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-24 #bdbdbd,#080808, 25-89 d,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|6: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-24 #bdbdbd,#080808, 25-89 d,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|7: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-25 #bdbdbd,#080808, 26-89 d,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|8: 0-1 #ffffff,#262626, 2-69 #949494,#121212, 70-89 d,#121212, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|9: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|10: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|11: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|12: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|13: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|14: 0-0 d,#444444, 1-1 d,#080808, 2-8 #bdbdbd,#080808, 9-89 d,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|15: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|16: 0-1 #ffffff,#262626, 2-69 #949494,#121212, 70-89 d,#121212, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-89 d,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-89 #bdbdbd,#080808, 90-90 d,#444444, 91-91 d,#080808, 92-179 #bdbdbd,#080808, == snap second grid=180x30 cursor=10,3 |2: 5 l5 ls |3: 6 MARK b g.txt diff --git a/test/snapshots/images.golden b/test/snapshots/images.golden index e73734dc..a3e8c7c9 100644 --- a/test/snapshots/images.golden +++ b/test/snapshots/images.golden @@ -21,41 +21,41 @@ == style img-open grid=150x30 |0: 0-74 #949494,#121212, 75-149 d,#121212, |1: 0-1 #ffffff,#626262, 2-74 #949494,#121212, 75-76 #ffffff,#262626, 77-130 #949494,#121212, 131-149 d,#121212, -|2: 0-0 d,#121212, 1-31 d,#080808, 32-33 #959595,#6c6c6c, 34-34 #6c6c6c,#959595, 35-35 #959595,#6c6c6c, 36-36 #6c6c6c,#959595, 37-37 #959595,#6c6c6c, 38-38 #6c6c6c,#959595, 39-41 #959595,#6c6c6c, 42-42 #6c6c6c,#6c5eb5, 43-43 #352879,#6c6c6c, 44-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-78 #bdbdbd,#080808, 79-149 d,#080808, -|3: 0-0 d,#121212, 1-31 d,#080808, 32-34 #6c6c6c,#959595, 35-37 #959595,#6c6c6c, 38-39 #6c6c6c,#959595, 40-40 #6c6c6c,#6c5eb5, 41-41 #352879,#6c6c6c, 42-43 #352879,#352879, 44-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-83 #bdbdbd,#080808, 84-149 d,#080808, +|2: 0-0 d,#121212, 1-31 d,#080808, 32-33 #959595,#6c6c6c, 34-34 #6c6c6c,#959595, 35-35 #959595,#6c6c6c, 36-36 #6c6c6c,#959595, 37-37 #959595,#6c6c6c, 38-38 #6c6c6c,#959595, 39-41 #959595,#6c6c6c, 42-42 #6c6c6c,#6c5eb5, 43-43 #352879,#6c6c6c, 44-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|3: 0-0 d,#121212, 1-31 d,#080808, 32-34 #6c6c6c,#959595, 35-37 #959595,#6c6c6c, 38-39 #6c6c6c,#959595, 40-40 #6c6c6c,#6c5eb5, 41-41 #352879,#6c6c6c, 42-43 #352879,#352879, 44-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, |4: 0-0 d,#121212, 1-31 d,#080808, 32-33 #959595,#6c6c6c, 34-35 #6c6c6c,#959595, 36-36 #959595,#6c6c6c, 37-37 #6c6c6c,#959595, 38-38 #6c6c6c,#6c5eb5, 39-39 #352879,#6c6c6c, 40-43 #352879,#352879, 44-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|5: 0-0 d,#121212, 1-31 d,#080808, 32-32 #6c6c6c,#959595, 33-34 #959595,#6c6c6c, 35-35 #6c6c6c,#959595, 36-36 #6c6c6c,#6c5eb5, 37-37 #352879,#959595, 38-43 #352879,#352879, 44-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|6: 0-0 d,#121212, 1-31 d,#080808, 32-33 #959595,#6c6c6c, 34-34 #959595,#6c5eb5, 35-35 #352879,#6c6c6c, 36-43 #352879,#352879, 44-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|7: 0-0 d,#121212, 1-31 d,#080808, 32-32 #6c6c6c,#6c5eb5, 33-33 #352879,#6c6c6c, 34-43 #352879,#352879, 44-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|8: 0-1 #ffffff,#262626, 2-74 #949494,#121212, 75-75 d,#444444, 76-149 d,#080808, -|9: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|10: 0-0 d,#444444, 1-1 d,#080808, 2-8 #bdbdbd,#080808, 9-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|11: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|12: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|13: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|14: 0-0 d,#444444, 1-1 d,#080808, 2-10 #bdbdbd,#080808, 11-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|15: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|16: 0-1 #ffffff,#262626, 2-74 #949494,#121212, 75-75 d,#444444, 76-149 d,#080808, -|17: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|18: 0-0 d,#444444, 1-1 d,#080808, 2-8 #bdbdbd,#080808, 9-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|19: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|20: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|21: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|22: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|23: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|24: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|25: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|26: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|27: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|28: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|29: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, +|5: 0-0 d,#121212, 1-31 d,#080808, 32-32 #6c6c6c,#959595, 33-34 #959595,#6c6c6c, 35-35 #6c6c6c,#959595, 36-36 #6c6c6c,#6c5eb5, 37-37 #352879,#959595, 38-43 #352879,#352879, 44-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|6: 0-0 d,#121212, 1-31 d,#080808, 32-33 #959595,#6c6c6c, 34-34 #959595,#6c5eb5, 35-35 #352879,#6c6c6c, 36-43 #352879,#352879, 44-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|7: 0-0 d,#121212, 1-31 d,#080808, 32-32 #6c6c6c,#6c5eb5, 33-33 #352879,#6c6c6c, 34-43 #352879,#352879, 44-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|8: 0-1 #ffffff,#262626, 2-74 #949494,#121212, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|9: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|10: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|11: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|12: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|13: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|14: 0-0 d,#444444, 1-1 d,#080808, 2-10 #bdbdbd,#080808, 11-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|15: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|16: 0-1 #ffffff,#262626, 2-74 #949494,#121212, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, == style term-palette grid=150x30 -|2: 0-0 d,#121212, 1-31 d,#080808, 32-41 p8,p4, 42-42 p8,p12, 43-43 p12,p8, 44-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-78 #bdbdbd,#080808, 79-149 d,#080808, -|3: 0-0 d,#121212, 1-31 d,#080808, 32-39 p8,p4, 40-40 p8,p12, 41-41 p12,p8, 42-43 p12,p12, 44-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-83 #bdbdbd,#080808, 84-149 d,#080808, +|2: 0-0 d,#121212, 1-31 d,#080808, 32-41 p8,p4, 42-42 p8,p12, 43-43 p12,p8, 44-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|3: 0-0 d,#121212, 1-31 d,#080808, 32-39 p8,p4, 40-40 p8,p12, 41-41 p12,p8, 42-43 p12,p12, 44-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, |4: 0-0 d,#121212, 1-31 d,#080808, 32-32 p8,p4, 33-33 p4,p8, 34-37 p8,p4, 38-38 p8,p12, 39-40 p12,p8, 41-43 p12,p12, 44-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|5: 0-0 d,#121212, 1-31 d,#080808, 32-35 p8,p4, 36-36 p8,p12, 37-37 p12,p8, 38-43 p12,p12, 44-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|6: 0-0 d,#121212, 1-31 d,#080808, 32-32 p4,p8, 33-33 p8,p4, 34-34 p8,p12, 35-36 p12,p8, 37-43 p12,p12, 44-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|7: 0-0 d,#121212, 1-31 d,#080808, 32-32 p8,p12, 33-34 p12,p8, 35-43 p12,p12, 44-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, +|5: 0-0 d,#121212, 1-31 d,#080808, 32-35 p8,p4, 36-36 p8,p12, 37-37 p12,p8, 38-43 p12,p12, 44-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|6: 0-0 d,#121212, 1-31 d,#080808, 32-32 p4,p8, 33-33 p8,p4, 34-34 p8,p12, 35-36 p12,p8, 37-43 p12,p12, 44-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|7: 0-0 d,#121212, 1-31 d,#080808, 32-32 p8,p12, 33-34 p12,p8, 35-43 p12,p12, 44-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, == snap no-ascii grid=150x30 cursor=11,7 |1: img petscii:off palette:terminal ascii:off /tmp/pardes-snap/images/cwd/pi /tmp/pardes-snap/images/cwd Save New Newtty Del Filter |2: █████🬻████�▄ ls diff --git a/test/snapshots/jumps.golden b/test/snapshots/jumps.golden index 9dfafcf3..44af4b2d 100644 --- a/test/snapshots/jumps.golden +++ b/test/snapshots/jumps.golden @@ -135,34 +135,34 @@ == style step1-shell grid=150x30 |0: 0-74 #949494,#121212, 75-149 d,#121212, |1: 0-1 #ffffff,#262626, 2-55 #949494,#121212, 56-74 d,#121212, 75-76 #ffffff,#626262, 77-129 #949494,#121212, 130-149 d,#121212, -|2: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-78 #bdbdbd,#080808, 79-149 d,#080808, -|3: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-83 #bdbdbd,#080808, 84-149 d,#080808, +|2: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|3: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, |4: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|5: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|6: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|7: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|8: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|9: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|10: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|11: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|12: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|13: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|14: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|15: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|16: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|17: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|18: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|19: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|20: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|21: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|22: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|23: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|24: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|25: 0-1 #ffffff,#262626, 2-55 #949494,#121212, 56-74 d,#121212, 75-75 d,#444444, 76-149 d,#080808, -|26: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-40 #bdbdbd,#080808, 41-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|27: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-51 #bdbdbd,#080808, 52-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|28: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-40 #bdbdbd,#080808, 41-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|29: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-55 #bdbdbd,#080808, 56-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, +|5: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|6: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|7: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|8: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|9: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|10: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|11: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|12: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|13: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|14: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|15: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|16: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|17: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|18: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|19: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|20: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|21: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|22: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|23: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|24: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|25: 0-1 #ffffff,#262626, 2-55 #949494,#121212, 56-74 d,#121212, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-40 #bdbdbd,#080808, 41-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-51 #bdbdbd,#080808, 52-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-40 #bdbdbd,#080808, 41-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|29: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-55 #bdbdbd,#080808, 56-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, == snap step2-file grid=150x30 cursor=7,2 |2: 1 line 1 ls |3: 2 line 2 big.txt diff --git a/test/snapshots/load.golden b/test/snapshots/load.golden index a8f80900..16a07805 100644 --- a/test/snapshots/load.golden +++ b/test/snapshots/load.golden @@ -21,31 +21,31 @@ == style loaded grid=100x30 |0: 0-74 #949494,#121212, 75-99 d,#121212, |1: 0-1 #ffffff,#626262, 2-49 #949494,#121212, 50-51 #ffffff,#262626, 52-99 #949494,#121212, -|2: 0-0 d,#444444, 1-1 d,#080808, 2-26 #bdbdbd,#080808, 27-49 d,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-53 #bdbdbd,#080808, 54-99 d,#080808, -|3: 0-0 d,#444444, 1-1 d,#080808, 2-9 #bdbdbd,#080808, 10-49 d,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-56 #bdbdbd,#080808, 57-99 d,#080808, -|4: 0-0 d,#444444, 1-1 d,#080808, 2-2 #bdbdbd,#080808, 3-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|5: 0-0 d,#444444, 1-1 d,#080808, 2-2 #bdbdbd,#080808, 3-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|6: 0-0 d,#121212, 1-1 d,#080808, 2-2 #bdbdbd,#080808, 3-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|7: 0-0 d,#121212, 1-1 d,#080808, 2-2 #bdbdbd,#080808, 3-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|8: 0-0 d,#121212, 1-1 d,#080808, 2-2 #bdbdbd,#080808, 3-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|9: 0-0 d,#121212, 1-1 d,#080808, 2-2 #bdbdbd,#080808, 3-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|10: 0-0 d,#121212, 1-1 d,#080808, 2-2 #bdbdbd,#080808, 3-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|11: 0-0 d,#121212, 1-1 d,#080808, 2-2 #bdbdbd,#080808, 3-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|12: 0-0 d,#121212, 1-1 d,#080808, 2-2 #bdbdbd,#080808, 3-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|13: 0-0 d,#121212, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|14: 0-0 d,#121212, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|15: 0-0 d,#121212, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|16: 0-1 #ffffff,#262626, 2-49 #949494,#121212, 50-50 d,#444444, 51-99 d,#080808, -|17: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|18: 0-0 d,#444444, 1-1 d,#080808, 2-6 #bdbdbd,#080808, 7-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|19: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|20: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|21: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|22: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|23: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|24: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|25: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|26: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|27: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|28: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|29: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|3: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|4: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|5: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|6: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|7: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|8: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|9: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|10: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|11: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|12: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|13: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|14: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|15: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|16: 0-1 #ffffff,#262626, 2-49 #949494,#121212, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, diff --git a/test/snapshots/look-file.golden b/test/snapshots/look-file.golden index 7880b8f8..80750476 100644 --- a/test/snapshots/look-file.golden +++ b/test/snapshots/look-file.golden @@ -21,31 +21,31 @@ == style file-open grid=150x30 |0: 0-74 #949494,#121212, 75-149 d,#121212, |1: 0-1 #ffffff,#626262, 2-59 #949494,#121212, 60-74 d,#121212, 75-76 #ffffff,#262626, 77-133 #949494,#121212, 134-149 d,#121212, -|2: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-78 #bdbdbd,#080808, 79-149 d,#080808, -|3: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-83 #bdbdbd,#080808, 84-149 d,#080808, +|2: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|3: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, |4: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|5: 0-0 d,#444444, 1-1 d,#080808, 2-6 #949494,#121212, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|6: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|7: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|8: 0-1 #ffffff,#262626, 2-59 #949494,#121212, 60-74 d,#121212, 75-75 d,#444444, 76-149 d,#080808, -|9: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|10: 0-0 d,#444444, 1-1 d,#080808, 2-8 #bdbdbd,#080808, 9-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|11: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|12: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|13: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|14: 0-0 d,#444444, 1-1 d,#080808, 2-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|15: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|16: 0-1 #ffffff,#262626, 2-59 #949494,#121212, 60-74 d,#121212, 75-75 d,#444444, 76-149 d,#080808, -|17: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|18: 0-0 d,#444444, 1-1 d,#080808, 2-8 #bdbdbd,#080808, 9-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|19: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|20: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|21: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|22: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|23: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|24: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|25: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|26: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|27: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|28: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|29: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, +|5: 0-0 d,#444444, 1-1 d,#080808, 2-6 #949494,#121212, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|6: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|7: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|8: 0-1 #ffffff,#262626, 2-59 #949494,#121212, 60-74 d,#121212, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|9: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|10: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|11: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|12: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|13: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|14: 0-0 d,#444444, 1-1 d,#080808, 2-13 #bdbdbd,#080808, 14-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|15: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|16: 0-1 #ffffff,#262626, 2-59 #949494,#121212, 60-74 d,#121212, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, diff --git a/test/snapshots/lookpanes.golden b/test/snapshots/lookpanes.golden index 1416c342..a9ea5b25 100644 --- a/test/snapshots/lookpanes.golden +++ b/test/snapshots/lookpanes.golden @@ -22,51 +22,51 @@ == style mru-beta grid=130x50 |0: 0-74 #949494,#121212, 75-129 d,#121212, |1: 0-1 #ffffff,#262626, 2-64 #949494,#121212, 65-66 #ffffff,#262626, 67-129 #949494,#121212, -|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-68 #bdbdbd,#080808, 69-129 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, |3: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-121 #bdbdbd,#080808, 122-129 d,#080808, |4: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, |5: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 #ffffff,#262626, 67-129 #949494,#121212, -|6: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-68 #bdbdbd,#080808, 69-129 d,#080808, -|7: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-117 #bdbdbd,#080808, 118-129 d,#080808, +|6: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|7: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, |8: 0-1 #ffffff,#626262, 2-64 #949494,#121212, 65-65 d,#444444, 66-129 d,#080808, -|9: 0-0 d,#444444, 1-1 d,#080808, 2-6 #949494,#121212, 7-20 #bdbdbd,#080808, 21-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|10: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|11: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|12: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|13: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|14: 0-1 #ffffff,#262626, 2-64 #949494,#121212, 65-65 d,#444444, 66-129 d,#080808, -|15: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-20 #bdbdbd,#080808, 21-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|16: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|17: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|18: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|19: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|20: 0-1 #ffffff,#262626, 2-64 #949494,#121212, 65-65 d,#444444, 66-129 d,#080808, -|21: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-21 #bdbdbd,#080808, 22-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|22: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|23: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|24: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|25: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|26: 0-1 #ffffff,#262626, 2-64 #949494,#121212, 65-65 d,#444444, 66-129 d,#080808, -|27: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-19 #bdbdbd,#080808, 20-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|28: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|29: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|30: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|31: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|32: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|33: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|34: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|35: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|36: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|37: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|38: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|39: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|40: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|41: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|42: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|43: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|44: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|45: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|46: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|47: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|48: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, -|49: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-129 d,#080808, +|9: 0-0 d,#444444, 1-1 d,#080808, 2-6 #949494,#121212, 7-20 #bdbdbd,#080808, 21-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|10: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|11: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|12: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|13: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|14: 0-1 #ffffff,#262626, 2-64 #949494,#121212, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|15: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-20 #bdbdbd,#080808, 21-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|16: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|20: 0-1 #ffffff,#262626, 2-64 #949494,#121212, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-21 #bdbdbd,#080808, 22-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|26: 0-1 #ffffff,#262626, 2-64 #949494,#121212, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-19 #bdbdbd,#080808, 20-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|30: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|31: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|32: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|33: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|34: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|35: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|36: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|37: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|38: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|39: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|40: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|41: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|42: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|43: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|44: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|45: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|46: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|47: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|48: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, +|49: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-64 d,#080808, 65-65 d,#444444, 66-66 d,#080808, 67-129 #bdbdbd,#080808, diff --git a/test/snapshots/modal.golden b/test/snapshots/modal.golden index f09badc5..545238f1 100644 --- a/test/snapshots/modal.golden +++ b/test/snapshots/modal.golden @@ -23,41 +23,41 @@ == style xsel grid=100x30 |0: 0-74 #949494,#121212, 75-99 d,#121212, |1: 0-1 #ffffff,#626262, 2-49 #949494,#121212, 50-51 #ffffff,#262626, 52-99 #949494,#121212, -|2: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-49 d,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-53 #bdbdbd,#080808, 54-99 d,#080808, -|3: 0-0 d,#444444, 1-1 d,#080808, 2-10 #bdbdbd,#080808, 11-49 d,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-60 #bdbdbd,#080808, 61-99 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|3: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, |4: 0-0 d,#444444, 1-1 d,#080808, 2-48 #080808,#80a0ff, 49-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|5: 0-0 d,#444444, 1-1 d,#080808, 2-12 #bdbdbd,#080808, 13-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|6: 0-0 d,#444444, 1-1 d,#080808, 2-14 #bdbdbd,#080808, 15-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|7: 0-0 d,#444444, 1-1 d,#080808, 2-14 #bdbdbd,#080808, 15-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|8: 0-0 d,#444444, 1-1 d,#080808, 2-17 #bdbdbd,#080808, 18-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|9: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|10: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|11: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|12: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|13: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|14: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|15: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|16: 0-1 #ffffff,#262626, 2-49 #949494,#121212, 50-50 d,#444444, 51-99 d,#080808, -|17: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|18: 0-0 d,#444444, 1-1 d,#080808, 2-10 #bdbdbd,#080808, 11-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|19: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|20: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|21: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|22: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|23: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|24: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|25: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|26: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|27: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|28: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|29: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, +|5: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|6: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|7: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|8: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|9: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|10: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|11: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|12: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|13: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|14: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|15: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|16: 0-1 #ffffff,#262626, 2-49 #949494,#121212, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, == snap xsel2 grid=100x30 cursor=13,5 == snap yanked grid=100x30 cursor=13,5 == snap vsel grid=100x30 cursor=14,6 == style vsel grid=100x30 |4: 0-0 d,#444444, 1-1 d,#080808, 2-49 #080808,#80a0ff, 50-50 d,#444444, 51-99 d,#080808, -|5: 0-0 d,#444444, 1-1 d,#080808, 2-49 #080808,#80a0ff, 50-50 d,#444444, 51-99 d,#080808, -|6: 0-0 d,#444444, 1-1 d,#080808, 2-14 #080808,#80a0ff, 15-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, +|5: 0-0 d,#444444, 1-1 d,#080808, 2-49 #080808,#80a0ff, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|6: 0-0 d,#444444, 1-1 d,#080808, 2-14 #080808,#80a0ff, 15-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, == snap typed grid=100x30 cursor=7,4 |4: TYPEDprintf 'one two three\nfour five six\nseven == snap undone grid=100x30 cursor=14,6 diff --git a/test/snapshots/multicursor.golden b/test/snapshots/multicursor.golden index 2bc7c1c6..f27009a1 100644 --- a/test/snapshots/multicursor.golden +++ b/test/snapshots/multicursor.golden @@ -21,34 +21,34 @@ == style three-cursors grid=150x30 |0: 0-74 #949494,#121212, 75-149 d,#121212, |1: 0-1 #ffffff,#626262, 2-60 #949494,#121212, 61-74 d,#121212, 75-76 #ffffff,#262626, 77-135 #949494,#121212, 136-149 d,#121212, -|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-7 #80a0ff,#080808, 8-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-78 #bdbdbd,#080808, 79-149 d,#080808, -|3: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-7 #80a0ff,#080808, 8-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-81 #bdbdbd,#080808, 82-149 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-7 #80a0ff,#080808, 8-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|3: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-7 #80a0ff,#080808, 8-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, |4: 0-0 d,#121212, 1-1 d,#080808, 2-6 #949494,#121212, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|5: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|6: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|7: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|8: 0-1 #ffffff,#262626, 2-60 #949494,#121212, 61-74 d,#121212, 75-75 d,#444444, 76-149 d,#080808, -|9: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|10: 0-0 d,#444444, 1-1 d,#080808, 2-6 #bdbdbd,#080808, 7-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|11: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|12: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|13: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|14: 0-0 d,#444444, 1-1 d,#080808, 2-8 #bdbdbd,#080808, 9-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|15: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|16: 0-1 #ffffff,#262626, 2-60 #949494,#121212, 61-74 d,#121212, 75-75 d,#444444, 76-149 d,#080808, -|17: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|18: 0-0 d,#444444, 1-1 d,#080808, 2-6 #bdbdbd,#080808, 7-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|19: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|20: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|21: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|22: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|23: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|24: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|25: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|26: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|27: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|28: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|29: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, +|5: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|6: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|7: 0-0 d,#121212, 1-1 d,#080808, 2-6 #626262,#080808, 7-12 #bdbdbd,#080808, 13-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|8: 0-1 #ffffff,#262626, 2-60 #949494,#121212, 61-74 d,#121212, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|9: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|10: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|11: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|12: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|13: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|14: 0-0 d,#444444, 1-1 d,#080808, 2-8 #bdbdbd,#080808, 9-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|15: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|16: 0-1 #ffffff,#262626, 2-60 #949494,#121212, 61-74 d,#121212, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, == snap edited-at-three grid=150x30 cursor=9,4 |1: /tmp/pardes-snap/multicursor/cwd/f.txt * Save New Newtty Del /tmp/pardes-snap/multicursor/cwd Save New Newtty Del Filter |2: 1 >>line 1 ls @@ -59,12 +59,12 @@ == snap three-selections grid=150x30 cursor=8,4 == style three-selections grid=150x30 |1: 0-1 #ffffff,#626262, 2-61 #949494,#121212, 62-74 d,#121212, 75-76 #ffffff,#262626, 77-135 #949494,#121212, 136-149 d,#121212, -|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-7 #080808,#445483, 8-8 #80a0ff,#080808, 9-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-78 #bdbdbd,#080808, 79-149 d,#080808, -|3: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-7 #080808,#445483, 8-8 #80a0ff,#080808, 9-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-81 #bdbdbd,#080808, 82-149 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-7 #080808,#445483, 8-8 #80a0ff,#080808, 9-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|3: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-7 #080808,#445483, 8-8 #80a0ff,#080808, 9-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, |4: 0-0 d,#121212, 1-1 d,#080808, 2-6 #949494,#121212, 7-8 #080808,#80a0ff, 9-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -|8: 0-1 #ffffff,#262626, 2-61 #949494,#121212, 62-74 d,#121212, 75-75 d,#444444, 76-149 d,#080808, -|16: 0-1 #ffffff,#262626, 2-61 #949494,#121212, 62-74 d,#121212, 75-75 d,#444444, 76-149 d,#080808, +|8: 0-1 #ffffff,#262626, 2-61 #949494,#121212, 62-74 d,#121212, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|16: 0-1 #ffffff,#262626, 2-61 #949494,#121212, 62-74 d,#121212, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, == snap keep-primary grid=150x30 cursor=8,4 == style keep-primary grid=150x30 -|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-78 #bdbdbd,#080808, 79-149 d,#080808, -|3: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-81 #bdbdbd,#080808, 82-149 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|3: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, diff --git a/test/snapshots/psearch.golden b/test/snapshots/psearch.golden index 785bd9ed..8dfcd2b3 100644 --- a/test/snapshots/psearch.golden +++ b/test/snapshots/psearch.golden @@ -17,30 +17,30 @@ == style results grid=140x30 |0: 0-74 #949494,#121212, 75-139 d,#121212, |1: 0-1 #ffffff,#626262, 2-119 #949494,#121212, 120-139 d,#121212, -|2: 0-0 d,#444444, 1-1 d,#080808, 2-54 #bdbdbd,#080808, 55-139 d,#080808, -|3: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-7 #080808,#80a0ff, 8-9 #bdbdbd,#080808, 10-139 d,#080808, -|4: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-139 d,#080808, -|5: 0-0 d,#444444, 1-1 d,#080808, 2-9 #bdbdbd,#080808, 10-139 d,#080808, -|6: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-139 d,#080808, -|7: 0-0 d,#444444, 1-1 d,#080808, 2-9 #bdbdbd,#080808, 10-139 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|3: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-7 #080808,#80a0ff, 8-139 #bdbdbd,#080808, +|4: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|5: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|6: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|7: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, |8: 0-0 d,#444444, 1-139 d,#080808, -|9: 0-0 d,#444444, 1-139 d,#080808, -|10: 0-0 d,#444444, 1-139 d,#080808, -|11: 0-0 d,#444444, 1-139 d,#080808, -|12: 0-0 d,#444444, 1-139 d,#080808, -|13: 0-0 d,#444444, 1-139 d,#080808, -|14: 0-0 d,#444444, 1-139 d,#080808, -|15: 0-0 d,#444444, 1-139 d,#080808, -|16: 0-0 d,#444444, 1-139 d,#080808, -|17: 0-0 d,#444444, 1-139 d,#080808, -|18: 0-0 d,#444444, 1-139 d,#080808, -|19: 0-0 d,#444444, 1-139 d,#080808, -|20: 0-0 d,#444444, 1-139 d,#080808, -|21: 0-0 d,#444444, 1-139 d,#080808, -|22: 0-0 d,#444444, 1-139 d,#080808, -|23: 0-0 d,#444444, 1-139 d,#080808, -|24: 0-0 d,#444444, 1-139 d,#080808, -|25: 0-0 d,#444444, 1-139 d,#080808, +|9: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|10: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|11: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|12: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|13: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|14: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|15: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|16: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-139 #bdbdbd,#080808, |26: 0-1 #ffffff,#262626, 2-119 #949494,#121212, 120-139 d,#121212, |27: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-24 #bdbdbd,#080808, 25-139 d,#080808, |28: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-24 #bdbdbd,#080808, 25-139 d,#080808, diff --git a/test/snapshots/scroll.golden b/test/snapshots/scroll.golden index 978e3781..eb35bcba 100644 --- a/test/snapshots/scroll.golden +++ b/test/snapshots/scroll.golden @@ -81,31 +81,31 @@ == style wheel-down grid=100x30 |0: 0-74 #949494,#121212, 75-99 d,#121212, |1: 0-1 #ffffff,#626262, 2-49 #949494,#121212, 50-51 #ffffff,#262626, 52-99 #949494,#121212, -|2: 0-0 d,#121212, 1-1 d,#080808, 2-4 #bdbdbd,#080808, 5-49 d,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-53 #bdbdbd,#080808, 54-99 d,#080808, -|3: 0-0 d,#121212, 1-1 d,#080808, 2-4 #bdbdbd,#080808, 5-49 d,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-56 #bdbdbd,#080808, 57-99 d,#080808, -|4: 0-0 d,#121212, 1-1 d,#080808, 2-4 #bdbdbd,#080808, 5-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|5: 0-0 d,#121212, 1-1 d,#080808, 2-4 #bdbdbd,#080808, 5-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|6: 0-0 d,#121212, 1-1 d,#080808, 2-4 #bdbdbd,#080808, 5-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|7: 0-0 d,#121212, 1-1 d,#080808, 2-4 #bdbdbd,#080808, 5-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|8: 0-0 d,#121212, 1-1 d,#080808, 2-4 #bdbdbd,#080808, 5-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|9: 0-0 d,#121212, 1-1 d,#080808, 2-4 #bdbdbd,#080808, 5-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|10: 0-0 d,#121212, 1-1 d,#080808, 2-4 #bdbdbd,#080808, 5-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|11: 0-0 d,#121212, 1-1 d,#080808, 2-4 #bdbdbd,#080808, 5-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|12: 0-0 d,#121212, 1-1 d,#080808, 2-4 #bdbdbd,#080808, 5-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|13: 0-0 d,#121212, 1-1 d,#080808, 2-4 #bdbdbd,#080808, 5-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|14: 0-0 d,#444444, 1-1 d,#080808, 2-4 #bdbdbd,#080808, 5-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|15: 0-0 d,#121212, 1-1 d,#080808, 2-4 #bdbdbd,#080808, 5-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|16: 0-1 #ffffff,#262626, 2-49 #949494,#121212, 50-50 d,#444444, 51-99 d,#080808, -|17: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|18: 0-0 d,#444444, 1-1 d,#080808, 2-6 #bdbdbd,#080808, 7-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|19: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|20: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|21: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|22: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|23: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|24: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|25: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|26: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|27: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|28: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|29: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, +|2: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|3: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|4: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-99 d,#080808, +|5: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|6: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|7: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|8: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|9: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|10: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|11: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|12: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|13: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|14: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|15: 0-0 d,#121212, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|16: 0-1 #ffffff,#262626, 2-49 #949494,#121212, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, diff --git a/test/snapshots/syntax.golden b/test/snapshots/syntax.golden index 9dc22e83..31b212f9 100644 --- a/test/snapshots/syntax.golden +++ b/test/snapshots/syntax.golden @@ -21,9 +21,38 @@ == style zig-open grid=150x30 |0: 0-74 #949494,#121212, 75-149 d,#121212, |1: 0-1 #ffffff,#626262, 2-57 #949494,#121212, 58-74 d,#121212, 75-76 #ffffff,#262626, 77-130 #949494,#121212, 131-149 d,#121212, -|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #949494,#121212, 7-18 #bdbdbd,#080808, 19-25 #cf87e8,#080808,b 26-26 #bdbdbd,#080808, 27-31 #e3c78a,#080808, 32-34 #bdbdbd,#080808, 35-42 #ffffff,#080808,b 43-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-78 #bdbdbd,#080808, 79-149 d,#080808, -|3: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-10 #bdbdbd,#080808, 11-12 #cf87e8,#080808,b 13-26 #bdbdbd,#080808, 27-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-84 #bdbdbd,#080808, 85-149 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #949494,#121212, 7-18 #bdbdbd,#080808, 19-25 #cf87e8,#080808,b 26-26 #bdbdbd,#080808, 27-31 #e3c78a,#080808, 32-34 #bdbdbd,#080808, 35-42 #ffffff,#080808,b 43-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|3: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-10 #bdbdbd,#080808, 11-12 #cf87e8,#080808,b 13-26 #bdbdbd,#080808, 27-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, |4: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-23 #bdbdbd,#080808, 24-25 #e3c78a,#080808, 26-26 #bdbdbd,#080808, 27-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, +|5: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-16 #bdbdbd,#080808, 17-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|6: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-7 #bdbdbd,#080808, 8-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|7: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|8: 0-1 #ffffff,#262626, 2-57 #949494,#121212, 58-74 d,#121212, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|9: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|10: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|11: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|12: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|13: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|14: 0-0 d,#444444, 1-1 d,#080808, 2-11 #bdbdbd,#080808, 12-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|15: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|16: 0-1 #ffffff,#262626, 2-57 #949494,#121212, 58-74 d,#121212, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +== style colors-off grid=150x30 +|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #949494,#121212, 7-42 #bdbdbd,#080808, 43-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-78 #bdbdbd,#080808, 79-149 d,#080808, +|3: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-26 #bdbdbd,#080808, 27-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-84 #bdbdbd,#080808, 85-149 d,#080808, +|4: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-26 #bdbdbd,#080808, 27-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, |5: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-16 #bdbdbd,#080808, 17-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, |6: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-7 #bdbdbd,#080808, 8-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, |7: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, @@ -49,11 +78,32 @@ |27: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, |28: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, |29: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, -== style colors-off grid=150x30 -|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #949494,#121212, 7-42 #bdbdbd,#080808, 43-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-78 #bdbdbd,#080808, 79-149 d,#080808, -|3: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-26 #bdbdbd,#080808, 27-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-84 #bdbdbd,#080808, 85-149 d,#080808, -|4: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-26 #bdbdbd,#080808, 27-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, == style colors-on grid=150x30 -|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #949494,#121212, 7-18 #bdbdbd,#080808, 19-25 #cf87e8,#080808,b 26-26 #bdbdbd,#080808, 27-31 #e3c78a,#080808, 32-34 #bdbdbd,#080808, 35-42 #ffffff,#080808,b 43-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-78 #bdbdbd,#080808, 79-149 d,#080808, -|3: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-10 #bdbdbd,#080808, 11-12 #cf87e8,#080808,b 13-26 #bdbdbd,#080808, 27-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-84 #bdbdbd,#080808, 85-149 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-6 #949494,#121212, 7-18 #bdbdbd,#080808, 19-25 #cf87e8,#080808,b 26-26 #bdbdbd,#080808, 27-31 #e3c78a,#080808, 32-34 #bdbdbd,#080808, 35-42 #ffffff,#080808,b 43-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|3: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-10 #bdbdbd,#080808, 11-12 #cf87e8,#080808,b 13-26 #bdbdbd,#080808, 27-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, |4: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-23 #bdbdbd,#080808, 24-25 #e3c78a,#080808, 26-26 #bdbdbd,#080808, 27-74 d,#080808, 75-75 d,#444444, 76-149 d,#080808, +|5: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-16 #bdbdbd,#080808, 17-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|6: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-7 #bdbdbd,#080808, 8-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|7: 0-0 d,#444444, 1-1 d,#080808, 2-6 #626262,#080808, 7-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|8: 0-1 #ffffff,#262626, 2-57 #949494,#121212, 58-74 d,#121212, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|9: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|10: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|11: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|12: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|13: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|14: 0-0 d,#444444, 1-1 d,#080808, 2-11 #bdbdbd,#080808, 12-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|15: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|16: 0-1 #ffffff,#262626, 2-57 #949494,#121212, 58-74 d,#121212, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-74 #bdbdbd,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808, diff --git a/test/snapshots/tagbottomimage.golden b/test/snapshots/tagbottomimage.golden index fd518bb3..01a48012 100644 --- a/test/snapshots/tagbottomimage.golden +++ b/test/snapshots/tagbottomimage.golden @@ -58,20 +58,20 @@ |12: 0-0 d,#121212, 1-62 d,#080808, 63-63 #959595,#6c6c6c, 64-64 #6c6c6c,#959595, 65-65 #6c6c6c,#6c5eb5, 66-66 #352879,#6c5eb5, 67-88 #352879,#352879, 89-149 d,#080808, |13: 0-0 d,#121212, 1-62 d,#080808, 63-63 #6c6c6c,#352879, 64-64 #352879,#6c6c6c, 65-65 #352879,#6c5eb5, 66-88 #352879,#352879, 89-149 d,#080808, |14: 0-1 #ffffff,#626262, 2-129 #949494,#121212, 130-149 d,#121212, -|15: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-149 d,#080808, -|16: 0-0 d,#444444, 1-1 d,#080808, 2-8 #bdbdbd,#080808, 9-149 d,#080808, +|15: 0-0 d,#444444, 1-1 d,#080808, 2-149 #bdbdbd,#080808, +|16: 0-0 d,#444444, 1-1 d,#080808, 2-149 #bdbdbd,#080808, |17: 0-0 d,#444444, 1-149 d,#080808, -|18: 0-0 d,#444444, 1-149 d,#080808, -|19: 0-0 d,#444444, 1-149 d,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-149 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-1 d,#080808, 2-149 #bdbdbd,#080808, |20: 0-0 d,#444444, 1-1 d,#080808, 2-10 #bdbdbd,#080808, 11-149 d,#080808, -|21: 0-0 d,#444444, 1-149 d,#080808, -|22: 0-0 d,#444444, 1-149 d,#080808, -|23: 0-0 d,#444444, 1-149 d,#080808, -|24: 0-0 d,#444444, 1-149 d,#080808, -|25: 0-0 d,#444444, 1-149 d,#080808, -|26: 0-0 d,#444444, 1-149 d,#080808, -|27: 0-0 d,#444444, 1-149 d,#080808, -|28: 0-0 d,#444444, 1-149 d,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-149 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-149 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-149 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-149 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-149 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-149 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-149 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-149 #bdbdbd,#080808, |29: 0-1 #ffffff,#262626, 2-129 #949494,#121212, 130-149 d,#121212, == snap search-prompt grid=150x30 cursor=6,13 |13: /ZZ diff --git a/test/snapshots/theme.golden b/test/snapshots/theme.golden index 6d720f74..4ec5990a 100644 --- a/test/snapshots/theme.golden +++ b/test/snapshots/theme.golden @@ -21,73 +21,102 @@ == style helix grid=100x30 |0: 0-74 #949494,#121212, 75-99 d,#121212, |1: 0-1 #ffffff,#626262, 2-49 #949494,#121212, 50-51 #ffffff,#262626, 52-99 #949494,#121212, -|2: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-49 d,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-53 #bdbdbd,#080808, 54-99 d,#080808, -|3: 0-0 d,#444444, 1-1 d,#080808, 2-6 #bdbdbd,#080808, 7-49 d,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-56 #bdbdbd,#080808, 57-99 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|3: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, |4: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|5: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|6: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|7: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|8: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|9: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|10: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|11: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|12: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|13: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|14: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|15: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|16: 0-1 #ffffff,#262626, 2-49 #949494,#121212, 50-50 d,#444444, 51-99 d,#080808, -|17: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|18: 0-0 d,#444444, 1-1 d,#080808, 2-6 #bdbdbd,#080808, 7-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|19: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|20: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|21: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|22: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|23: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|24: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|25: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|26: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|27: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|28: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, -|29: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-99 d,#080808, +|5: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|6: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|7: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|8: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|9: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|10: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|11: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|12: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|13: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|14: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|15: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|16: 0-1 #ffffff,#262626, 2-49 #949494,#121212, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-49 d,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-49 #bdbdbd,#080808, 50-50 d,#444444, 51-51 d,#080808, 52-99 #bdbdbd,#080808, == snap dark grid=100x30 cursor=4,4 == style dark grid=100x30 |0: 0-54 #969696,#2c2a3e, 55-63 #d8c8a8,#4a3a1e, 64-74 #969696,#2c2a3e, 75-99 d,#2c2a3e, |1: 0-1 #ffffff,#6e4fd0, 2-49 #969696,#2c2a3e, 50-51 #ffffff,#383c54, 52-99 #969696,#2c2a3e, -|2: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|3: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, +|2: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|3: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, |4: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|5: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|6: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|7: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|8: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|9: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|10: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|11: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|12: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|13: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|14: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|15: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|16: 0-1 #ffffff,#383c54, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-99 d,d, -|17: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|18: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|19: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|20: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|21: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|22: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|23: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|24: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|25: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|26: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|27: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|28: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, -|29: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-99 d,d, +|5: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|6: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|7: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|8: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|9: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|10: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|11: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|12: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|13: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|14: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|15: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|16: 0-1 #ffffff,#383c54, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|17: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|18: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|19: 0-0 d,#525252, 1-49 d,d, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|20: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|21: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|22: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|23: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|24: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|25: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|26: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|27: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|28: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, +|29: 0-0 d,#525252, 1-1 d,d, 2-49 #969696,#2c2a3e, 50-50 d,#525252, 51-51 d,d, 52-99 #969696,#2c2a3e, == snap acme grid=100x30 cursor=4,4 == style acme grid=100x30 |0: 0-54 #000000,#eaffff, 55-63 #000000,#eeee9e, 64-74 #000000,#eaffff, 75-99 d,#eaffff, |1: 0-1 #ffffff,#6e4fd0, 2-49 #000000,#eaffff, 50-51 #ffffff,#383c54, 52-99 #000000,#eaffff, +|2: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|3: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|4: 0-99 d,#ffffea, +|5: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|6: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|7: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|8: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|9: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|10: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|11: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|12: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|13: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|14: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|15: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|16: 0-1 #ffffff,#383c54, 2-49 #000000,#eaffff, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|17: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|18: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|19: 0-51 d,#ffffea, 52-99 #000000,#ffffea, +|20: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|21: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|22: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|23: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|24: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|25: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|26: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|27: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|28: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +|29: 0-1 d,#ffffea, 2-49 #000000,#ffffea, 50-51 d,#ffffea, 52-99 #000000,#ffffea, +== snap colors-off grid=100x30 cursor=4,4 +== style colors-off grid=100x30 |2: 0-1 d,#ffffea, 2-3 #000000,#ffffea, 4-51 d,#ffffea, 52-53 #000000,#ffffea, 54-99 d,#ffffea, |3: 0-1 d,#ffffea, 2-6 #000000,#ffffea, 7-51 d,#ffffea, 52-56 #000000,#ffffea, 57-99 d,#ffffea, -|4: 0-99 d,#ffffea, |5: 0-99 d,#ffffea, |6: 0-99 d,#ffffea, |7: 0-99 d,#ffffea, @@ -113,37 +142,35 @@ |27: 0-99 d,#ffffea, |28: 0-99 d,#ffffea, |29: 0-99 d,#ffffea, -== snap colors-off grid=100x30 cursor=4,4 -== style colors-off grid=100x30 == snap generated grid=100x30 cursor=4,4 == style generated grid=100x30 |0: 0-54 #e0e0e0,#1e262e, 55-63 #e0e0e0,#303840, 64-74 #e0e0e0,#1e262e, 75-99 d,#1e262e, |1: 0-1 #000000,#ffa060, 2-49 #e0e0e0,#1e262e, 50-51 #ffffff,#303840, 52-99 #e0e0e0,#1e262e, -|2: 0-0 d,#303840, 1-1 d,#161e26, 2-3 #e0e0e0,#161e26, 4-49 d,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-53 #e0e0e0,#161e26, 54-99 d,#161e26, -|3: 0-0 d,#303840, 1-1 d,#161e26, 2-6 #e0e0e0,#161e26, 7-49 d,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-56 #e0e0e0,#161e26, 57-99 d,#161e26, +|2: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|3: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, |4: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|5: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|6: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|7: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|8: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|9: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|10: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|11: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|12: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|13: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|14: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|15: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|16: 0-1 #ffffff,#303840, 2-49 #e0e0e0,#1e262e, 50-50 d,#303840, 51-99 d,#161e26, -|17: 0-0 d,#303840, 1-1 d,#161e26, 2-3 #e0e0e0,#161e26, 4-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|18: 0-0 d,#303840, 1-1 d,#161e26, 2-6 #e0e0e0,#161e26, 7-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|19: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|20: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|21: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|22: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|23: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|24: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|25: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|26: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|27: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|28: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, -|29: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-99 d,#161e26, +|5: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|6: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|7: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|8: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|9: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|10: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|11: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|12: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|13: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|14: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|15: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|16: 0-1 #ffffff,#303840, 2-49 #e0e0e0,#1e262e, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|17: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|18: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|19: 0-0 d,#303840, 1-49 d,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|20: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|21: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|22: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|23: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|24: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|25: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|26: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|27: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|28: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, +|29: 0-0 d,#303840, 1-1 d,#161e26, 2-49 #e0e0e0,#161e26, 50-50 d,#303840, 51-51 d,#161e26, 52-99 #e0e0e0,#161e26, diff --git a/test/snapshots/ttylook.golden b/test/snapshots/ttylook.golden index a0b6cbf2..6a01c892 100644 --- a/test/snapshots/ttylook.golden +++ b/test/snapshots/ttylook.golden @@ -21,34 +21,34 @@ == style tok1 grid=120x30 |0: 0-74 #949494,#121212, 75-119 d,#121212, |1: 0-1 #ffffff,#626262, 2-56 #949494,#121212, 57-59 d,#121212, 60-61 #ffffff,#262626, 62-116 #949494,#121212, 117-119 d,#121212, -|2: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-63 #bdbdbd,#080808, 64-119 d,#080808, -|3: 0-0 d,#444444, 1-1 d,#080808, 2-8 #080808,#80a0ff, 9-24 #bdbdbd,#080808, 25-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-84 #bdbdbd,#080808, 85-119 d,#080808, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|3: 0-0 d,#444444, 1-1 d,#080808, 2-8 #080808,#80a0ff, 9-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, |4: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|5: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|6: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|7: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|8: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|9: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|10: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|11: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|12: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|13: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|14: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|15: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|16: 0-1 #ffffff,#262626, 2-56 #949494,#121212, 57-59 d,#121212, 60-60 d,#444444, 61-119 d,#080808, -|17: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|18: 0-0 d,#444444, 1-1 d,#080808, 2-24 #bdbdbd,#080808, 25-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|19: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|20: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|21: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|22: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|23: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|24: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|25: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|26: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|27: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|28: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, -|29: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-119 d,#080808, +|5: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|6: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|7: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|8: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|9: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|10: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|11: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|12: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|13: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|14: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|15: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|16: 0-1 #ffffff,#262626, 2-56 #949494,#121212, 57-59 d,#121212, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-59 d,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-59 #bdbdbd,#080808, 60-60 d,#444444, 61-61 d,#080808, 62-119 #bdbdbd,#080808, == snap tok2 grid=120x30 cursor=11,3 == snap tokback grid=120x30 cursor=2,3 == snap insert grid=120x30 cursor=4,3 diff --git a/test/snapshots/windowops.golden b/test/snapshots/windowops.golden index a9f5c506..7c294d9b 100644 --- a/test/snapshots/windowops.golden +++ b/test/snapshots/windowops.golden @@ -146,34 +146,34 @@ == style debug-on grid=100x30 |0: 0-64 #949494,#121212, 65-69 #080808,#80a0ff, 70-74 #949494,#121212, 75-99 d,#121212, |1: 0-1 #ffffff,#262626, 2-39 #949494,#121212, 40-41 #ffffff,#626262, 42-61 #949494,#121212, 62-99 d,d, -|2: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-39 d,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-43 #bdbdbd,#080808, 44-61 d,#080808, 62-62 d,d, 63-79 #949494,d, 80-98 d,#121212, 99-99 d,d, -|3: 0-0 d,#444444, 1-1 d,#080808, 2-6 #bdbdbd,#080808, 7-39 d,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-46 #bdbdbd,#080808, 47-61 d,#080808, 62-62 d,d, 63-74 #949494,d, 75-98 d,#121212, 99-99 d,d, +|2: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-61 #bdbdbd,#080808, 62-62 d,d, 63-79 #949494,d, 80-98 d,#121212, 99-99 d,d, +|3: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-61 #bdbdbd,#080808, 62-62 d,d, 63-74 #949494,d, 75-98 d,#121212, 99-99 d,d, |4: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-61 d,#080808, 62-62 d,d, 63-76 #949494,d, 77-98 d,#121212, 99-99 d,d, -|5: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-61 d,#080808, 62-62 d,d, 63-77 #949494,d, 78-98 d,#121212, 99-99 d,d, -|6: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-61 d,#080808, 62-62 d,d, 63-78 #949494,d, 79-98 d,#121212, 99-99 d,d, -|7: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-61 d,#080808, 62-62 d,d, 63-73 #949494,d, 74-98 d,#121212, 99-99 d,d, -|8: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-61 d,#080808, 62-62 d,d, 63-75 #949494,d, 76-98 d,#121212, 99-99 d,d, -|9: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-61 d,#080808, 62-62 d,d, 63-77 #949494,d, 78-98 d,#121212, 99-99 d,d, -|10: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-61 d,#080808, 62-99 d,d, -|11: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, -|12: 0-1 #ffffff,#262626, 2-39 #949494,#121212, 40-40 d,#444444, 41-99 d,#080808, -|13: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, -|14: 0-0 d,#444444, 1-1 d,#080808, 2-6 #bdbdbd,#080808, 7-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, -|15: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, -|16: 0-1 #ffffff,#262626, 2-39 #949494,#121212, 40-40 d,#444444, 41-99 d,#080808, -|17: 0-0 d,#444444, 1-1 d,#080808, 2-3 #bdbdbd,#080808, 4-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, -|18: 0-0 d,#444444, 1-1 d,#080808, 2-6 #bdbdbd,#080808, 7-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, -|19: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, -|20: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, -|21: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, -|22: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, -|23: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, -|24: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, -|25: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, -|26: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, -|27: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, -|28: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, -|29: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-99 d,#080808, +|5: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-61 #bdbdbd,#080808, 62-62 d,d, 63-77 #949494,d, 78-98 d,#121212, 99-99 d,d, +|6: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-61 #bdbdbd,#080808, 62-62 d,d, 63-78 #949494,d, 79-98 d,#121212, 99-99 d,d, +|7: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-61 #bdbdbd,#080808, 62-62 d,d, 63-73 #949494,d, 74-98 d,#121212, 99-99 d,d, +|8: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-61 #bdbdbd,#080808, 62-62 d,d, 63-75 #949494,d, 76-98 d,#121212, 99-99 d,d, +|9: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-61 #bdbdbd,#080808, 62-62 d,d, 63-77 #949494,d, 78-98 d,#121212, 99-99 d,d, +|10: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-61 #bdbdbd,#080808, 62-99 d,d, +|11: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|12: 0-1 #ffffff,#262626, 2-39 #949494,#121212, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|13: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|14: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|15: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|16: 0-1 #ffffff,#262626, 2-39 #949494,#121212, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|17: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|18: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|19: 0-0 d,#444444, 1-39 d,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|20: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|21: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|22: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|23: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|24: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|25: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|26: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|27: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|28: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, +|29: 0-0 d,#444444, 1-1 d,#080808, 2-39 #bdbdbd,#080808, 40-40 d,#444444, 41-41 d,#080808, 42-99 #bdbdbd,#080808, == snap debug-off grid=100x30 cursor=44,4 |1: /tmp/pardes-snap/windowops/cwd Save Ne /tmp/pardes-snap/windowops/cwd Save New Newtty Del Filter |2: ls ls diff --git a/transactions.txt b/transactions.txt index b1233d3d..6c2e7646 100644 --- a/transactions.txt +++ b/transactions.txt @@ -1,6 +1,14 @@ Selection anchors through insert-mode edits, and the transaction model ======================================================================= +STATUS: still open. `wiX-edit-drops-sel` is line 1 of test/hxcases/waivers.jsonl, +so the gap below describes the code as it stands rather than something already +fixed. One correction to the sentence that follows: it is not quite "the one +place". Line 3 of the same file, `msel-append`, says in its own words "Same +family as wiX-edit-drops-sel: pardes does not map selections through insert-mode +edits" — so the gap has two waivers, and what is singular is the CAUSE below, +not the number of divergences it produces. + The one place the helix differential suite shows a real structural gap (waiver "wiX-edit-drops-sel" in test/hxcases/waivers.jsonl): helix keeps a selection ALIVE across insert mode, pardes drops it on the first edit. -- cgit v1.3