diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-01 22:44:19 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-10 09:17:07 -0300 |
| commit | d59397d4146ffa23adbbe6dc99c64ebb22a93b2d (patch) | |
| tree | a06984bf172a6c894c1586d58aad02ec302b6c2d | |
| parent | cbcdf01764e2e144a821643c3a8e9a5ad39eda2e (diff) | |
| download | pardes-d59397d4146ffa23adbbe6dc99c64ebb22a93b2d.tar.gz pardes-d59397d4146ffa23adbbe6dc99c64ebb22a93b2d.zip | |
add vanilla DOM web backend
| -rw-r--r-- | build.zig | 306 | ||||
| -rw-r--r-- | build/snap.zig | 12 | ||||
| -rw-r--r-- | docs/design.pdf | 1556 | ||||
| -rw-r--r-- | docs/design.typ | 34 | ||||
| -rw-r--r-- | docs/ideas.typ | 2 | ||||
| -rw-r--r-- | docs/web.md | 49 | ||||
| -rw-r--r-- | src/dump.zig | 6 | ||||
| -rw-r--r-- | src/web.zig | 481 | ||||
| -rw-r--r-- | src/web/app.mjs | 436 | ||||
| -rw-r--r-- | src/web/index.html | 17 | ||||
| -rw-r--r-- | src/web/libc.c | 152 | ||||
| -rw-r--r-- | src/web/libc/include/assert.h | 14 | ||||
| -rw-r--r-- | src/web/libc/include/ctype.h | 7 | ||||
| -rw-r--r-- | src/web/libc/include/endian.h | 21 | ||||
| -rw-r--r-- | src/web/libc/include/inttypes.h | 8 | ||||
| -rw-r--r-- | src/web/libc/include/limits.h | 9 | ||||
| -rw-r--r-- | src/web/libc/include/stdio.h | 19 | ||||
| -rw-r--r-- | src/web/libc/include/stdlib.h | 16 | ||||
| -rw-r--r-- | src/web/libc/include/string.h | 17 | ||||
| -rw-r--r-- | src/web/libc/include/wchar.h | 9 | ||||
| -rw-r--r-- | src/web/libc/include/wctype.h | 18 | ||||
| -rw-r--r-- | src/web/pardes.css | 91 | ||||
| -rw-r--r-- | src/web/zstbi.zig | 30 | ||||
| -rw-r--r-- | test/web-snapshots/README.md | 7 | ||||
| -rw-r--r-- | test/web-snapshots/source-list.golden | 306 | ||||
| -rw-r--r-- | test/web_harness.mjs | 136 | ||||
| -rw-r--r-- | test/web_snapshot.mjs | 41 |
27 files changed, 2633 insertions, 1167 deletions
@@ -9,7 +9,6 @@ pub const Platform = enum { tty, gui, web }; /// which fails on a fetched package that has no .git) and to `SPC l i`. const zls_version = "0.16.1-dev+3e0d0820"; pub const TreeSitterGrammars = enum { disabled, zig, minimal, full }; -const GhosttyArtifact = enum { ghostty, lib }; pub fn build(b: *std.Build) void { // Default target is the Steam Deck (deckcap's trick): x86_64 linux-gnu @@ -27,10 +26,10 @@ pub fn build(b: *std.Build) void { const platform = b.option(Platform, "platform", "which shell to build (tty, gui, web)") orelse .tty; const static = b.option(bool, "static", "statically link") orelse false; const dump_path = b.option([]const u8, "dump", "dump .zon embedded into the web shell (-Dplatform=web)"); - const emcc_path = b.option([]const u8, "emcc", "emcc for the web shell's final link") orelse "/usr/lib/emscripten/emcc"; - const is_emscripten = target.result.os.tag == .emscripten; + const is_web = platform == .web; + const is_web_target = target.result.cpu.arch == .wasm32 and target.result.os.tag == .freestanding; // wasm: size is the budget - const optimize = if (is_emscripten) .ReleaseSmall else requested_optimize; + const optimize = if (is_web) .ReleaseSmall else requested_optimize; // The vendored C is never what we are debugging, and at -O0 it dominates // the app: 90% of a Debug startup is tree-sitter's query analyser // (perf: ts_query__perform_analysis + ts_lookahead_iterator__next), and @@ -38,12 +37,16 @@ pub fn build(b: *std.Build) void { // mode takes a Debug boot from ~710ms to ~210ms — the same treatment // ghostty's simdutf/highway already get here. Zig code keeps its mode. const c_optimize: std.builtin.OptimizeMode = if (optimize == .Debug) .ReleaseFast else optimize; - const default_grammars: TreeSitterGrammars = if (is_emscripten) .zig else .full; + // The browser keeps its useful default grammar without acquiring a host + // libc contract: Tree-sitter and the generated Zig parser are linked into + // the freestanding module against src/web/libc's tiny in-module shim. + const default_grammars: TreeSitterGrammars = if (is_web) .zig else .full; const tree_sitter_grammars = b.option(TreeSitterGrammars, "tree-sitter", "tree-sitter grammar set: disabled, zig, minimal (c/c++/zig), full") orelse default_grammars; const tracy = b.option([]const u8, "tracy", "enable Tracy profiling; supply the path to a Tracy source checkout"); - // the browser shell: zig object + emcc link + html, installed to zig-out/web - const web_step = b.step("web", "build the browser shell into zig-out/web (-Dplatform=web -Dtarget=wasm32-emscripten -Ddump=<dump.zon>)"); + // The browser shell is a freestanding wasm core plus ordinary web files. + // JavaScript owns the loop and IO; HTML/CSS own rendering. + const web_step = b.step("web", "build the DOM browser shell into zig-out/web (-Dplatform=web -Dtarget=wasm32-freestanding -Ddump=<dump.zon>)"); // Web LOOK has a tiny read-only source filesystem. Generate one Zig module // on the host from every tracked .zig path + its current working-tree @@ -68,30 +71,9 @@ pub fn build(b: *std.Build) void { snap_build.addWeb(b); - const emscripten_sysroot = if (is_emscripten) detectEmscriptenSysroot(b) else null; - if (platform == .web and !is_emscripten) return failBuild(b, web_step, "-Dplatform=web requires -Dtarget=wasm32-emscripten"); - if (platform != .web and is_emscripten) return failBuild(b, web_step, "-Dtarget=wasm32-emscripten requires -Dplatform=web"); + if (is_web and !is_web_target) return failBuild(b, web_step, "-Dplatform=web requires -Dtarget=wasm32-freestanding"); + if (!is_web and (is_web_target or target.result.os.tag == .emscripten)) return failBuild(b, web_step, "wasm browser targets require -Dplatform=web"); if (platform == .web and dump_path == null) return failBuild(b, web_step, "-Dplatform=web requires -Ddump=<dump.zon> (the browser has no ptys; state replays from an embedded dump)"); - if (platform == .web and emscripten_sysroot == null) return failBuild(b, web_step, "no emscripten sysroot found; pass --sysroot <emscripten-cache>/sysroot or set EM_CACHE/EMSDK"); - - // emscripten "libc": point zig at the sysroot headers/crt via a libc.txt - // (the prototype's WriteFiles trick) — applied to EVERY compile step. - const libc_file: ?std.Build.LazyPath = if (emscripten_sysroot) |sysroot| blk: { - const files = b.addWriteFiles(); - break :blk files.add("emscripten-libc.txt", b.fmt( - \\include_dir={s}/include - \\sys_include_dir={s}/include - \\crt_dir={s}/lib/wasm32-emscripten - \\msvc_lib_dir= - \\kernel32_lib_dir= - \\gcc_dir= - \\ - , .{ sysroot, sysroot, sysroot })); - } else null; - const emscripten_include: ?std.Build.LazyPath = if (emscripten_sysroot) |sysroot| - .{ .cwd_relative = b.pathJoin(&.{ sysroot, "include" }) } - else - null; // build-time IO: slurps the grammars' highlights.scm queries, and reads // vendor/themes to find the theme sources @@ -102,8 +84,8 @@ pub fn build(b: *std.Build) void { const root_mod = b.createModule(.{ .target = target, .optimize = optimize, - .root_source_file = b.path("src/main.zig"), - .link_libc = true, + .root_source_file = b.path(if (is_web) "src/web.zig" else "src/main.zig"), + .link_libc = !is_web, }); // helix differential harness (test/hxdiff.zig): a second compilation of // the sans-IO core, driven headlessly. Mirrors root_mod's wiring for @@ -150,19 +132,16 @@ pub fn build(b: *std.Build) void { root_mod.addImport("generated_themes", themes_mod); hx_core_mod.addImport("generated_themes", themes_mod); - // static archives the emcc link needs beyond the zig object (grammars, SDL) - var wasm_libs: std.ArrayList(std.Build.LazyPath) = .empty; - // forkpty: in libc proper on glibc>=2.34 and darwin; the BSDs keep it in libutil if (target.result.os.tag == .freebsd or target.result.os.tag == .netbsd or target.result.os.tag == .openbsd) root_mod.linkSystemLibrary("util", .{}); // The language backend (src/lsp/lsp_zls.zig) links ZLS as a MODULE — no zls // binary, no JSON-RPC, no protocol. Everything except the browser gets it; - // emscripten does not, because the web shell has no threads and no-ops the + // the web shell does not, because it has no threads and no-ops the // lsp effect anyway, so paying to compile an analyser it can never call // would be pure wasm. - const zls_backend = !is_emscripten; + const zls_backend = !is_web; const opts = b.addOptions(); opts.addOption(Platform, "platform", platform); @@ -236,16 +215,26 @@ pub fn build(b: *std.Build) void { // ts_queries options module (codegen consumed by comptime in syntax.zig). if (tree_sitter_grammars != .disabled) { const tree_sitter_mod = b.dependency("tree_sitter", .{ .target = target, .optimize = c_optimize }).module("tree_sitter"); - // the C runtime rides the module as a static lib; give it the wasm - // libc and hand its archive to the emcc link - if (is_emscripten) { + if (is_web) { + // zig-tree-sitter carries its C runtime as a linked static library. + // Retarget that library away from libc and put the compatibility + // headers before any unavailable freestanding system headers. + const web_libc_include: std.Build.LazyPath = .{ + .cwd_relative = b.path("src/web/libc/include").getPath(b), + }; for (tree_sitter_mod.link_objects.items) |link_object| switch (link_object) { .other_step => |lib| { - lib.setLibCFile(libc_file.?); - wasm_libs.append(b.allocator, lib.getEmittedBin()) catch @panic("OOM"); + lib.root_module.link_libc = false; + lib.root_module.addIncludePath(web_libc_include); + lib.root_module.addCMacro("NDEBUG", "1"); }, else => {}, }; + root_mod.addIncludePath(b.path("src/web/libc/include")); + root_mod.addCSourceFile(.{ + .file = b.path("src/web/libc.c"), + .flags = &.{ "-std=c11", "-DNDEBUG=1" }, + }); } root_mod.addImport("tree-sitter", tree_sitter_mod); hx_core_mod.addImport("tree-sitter", tree_sitter_mod); @@ -299,10 +288,9 @@ pub fn build(b: *std.Build) void { const lib = b.addLibrary(.{ .name = "tree-sitter-" ++ g.name, - .root_module = b.createModule(.{ .target = target, .optimize = c_optimize, .link_libc = true }), + .root_module = b.createModule(.{ .target = target, .optimize = c_optimize, .link_libc = !is_web }), .linkage = .static, }); - if (libc_file) |f| lib.setLibCFile(f); // grammars declare external_scanner_create() with EMPTY PARENS // (a K&R non-prototype, not (void)): under clang's // -fsanitize=function the callee's type hash differs from the @@ -315,19 +303,26 @@ pub fn build(b: *std.Build) void { lib.root_module.addCSourceFile(.{ .file = dep.path(g.src ++ "/parser.c"), .flags = &ts_cflags }); if (g.scanner) lib.root_module.addCSourceFile(.{ .file = dep.path(g.src ++ "/scanner.c"), .flags = &ts_cflags }); lib.root_module.addIncludePath(dep.path(g.src)); + if (is_web) lib.root_module.addIncludePath(b.path("src/web/libc/include")); root_mod.linkLibrary(lib); hx_core_mod.linkLibrary(lib); - if (is_emscripten) wasm_libs.append(b.allocator, lib.getEmittedBin()) catch @panic("OOM"); } } root_mod.addOptions("ts_queries", ts_queries); hx_core_mod.addOptions("ts_queries", ts_queries); } - // zstbi (C stb_image): image pane decode. zigimg crashes the 0.16 compiler. - const zstbi_dep = b.dependency("zstbi", .{ .target = target, .optimize = c_optimize }); - root_mod.addImport("zstbi", zstbi_dep.module("root")); - hx_core_mod.addImport("zstbi", zstbi_dep.module("root")); + // zstbi (C stb_image): image pane decode. The freestanding core has no C + // allocator ABI, so its module is a compatible no-decode shim; browser IO + // can grow native image decoding independently of the core. + const zstbi_dep = if (!is_web) b.dependency("zstbi", .{ .target = target, .optimize = c_optimize }) else null; + const zstbi_mod = if (zstbi_dep) |dep| dep.module("root") else b.createModule(.{ + .target = target, + .optimize = optimize, + .root_source_file = b.path("src/web/zstbi.zig"), + }); + root_mod.addImport("zstbi", zstbi_mod); + hx_core_mod.addImport("zstbi", zstbi_mod); // mvzr: the regex engine behind `s` / `S` (helix select/split on a regex). // A bytecode VM that compiles a RUNTIME pattern into a fixed-size struct @@ -353,11 +348,11 @@ pub fn build(b: *std.Build) void { .tables_path = uucode_tables, }).module("uucode"); - const vaxis_mod = b.dependency("vaxis", .{ + const vaxis_mod: ?*std.Build.Module = if (!is_web) b.dependency("vaxis", .{ .target = target, .optimize = optimize, .external_uucode = true, - }).module("vaxis"); + }).module("vaxis") else null; var ghostty_vt_for_snap: ?*std.Build.Module = null; // ghostty's simd libs (simdutf/highway, C++) locate the Apple SDK via @@ -379,7 +374,7 @@ pub fn build(b: *std.Build) void { // without it); gate that block on `b.graph.host.result.os.tag == .linux`. // Ghostty's pinned translate-c tarball also 404s now (codeberg dropped // the archive endpoint) — seed zig-pkg/ from a machine that has it. - const ghostty_simd = !target.result.os.tag.isDarwin() or b.graph.host.result.os.tag.isDarwin(); + const ghostty_simd = !is_web and (!target.result.os.tag.isDarwin() or b.graph.host.result.os.tag.isDarwin()); // app-runtime none + emit-xcframework off: only the ghostty-vt module is // consumed, and the defaults otherwise drag ghostty's app graph into the // build — gtk4 header translation via host pkg-config for linux targets, @@ -394,39 +389,19 @@ pub fn build(b: *std.Build) void { // own safety checks come from OUR optimize mode and are unaffected, since // ghostty-vt is a module compiled into this binary. See reflow.snap. const ghostty_optimize: std.builtin.OptimizeMode = if (optimize == .Debug) .ReleaseSafe else optimize; - const ghostty_dep = if (is_emscripten) - b.lazyDependency("ghostty", .{ .simd = false }) - else - b.lazyDependency("ghostty", .{ .target = target, .optimize = ghostty_optimize, .simd = ghostty_simd, .@"app-runtime" = .none, .@"emit-xcframework" = false }); + const ghostty_dep = b.lazyDependency("ghostty", .{ .target = target, .optimize = ghostty_optimize, .simd = ghostty_simd, .@"app-runtime" = .none, .@"emit-xcframework" = false }); if (ghostty_dep) |dep| { const ghostty_vt = dep.module("ghostty-vt"); ghostty_vt_for_snap = ghostty_vt; ghostty_vt.addImport("uucode", uucode_mod); - // emscripten: hand ghostty-vt the terminal_options module its build() - // would otherwise provide (copied verbatim from the prototype build) - if (is_emscripten) { - const term_opts = b.addOptions(); - term_opts.addOption(GhosttyArtifact, "artifact", .lib); - term_opts.addOption(bool, "c_abi", false); - term_opts.addOption(bool, "oniguruma", false); - term_opts.addOption(bool, "simd", false); - term_opts.addOption(bool, "slow_runtime_safety", false); - term_opts.addOption(bool, "kitty_graphics", false); - term_opts.addOption(bool, "tmux_control_mode", false); - term_opts.addOption([]const u8, "version_string", "0.0.0"); - term_opts.addOption(usize, "version_major", 0); - term_opts.addOption(usize, "version_minor", 0); - term_opts.addOption(usize, "version_patch", 0); - term_opts.addOption(?[]const u8, "version_pre", null); - term_opts.addOption(?[]const u8, "version_build", null); - ghostty_vt.addOptions("terminal_options", term_opts); - } root_mod.addImport("ghostty-vt", ghostty_vt); hx_core_mod.addImport("ghostty-vt", ghostty_vt); } // override uucode on vaxis LAST so it wins over ghostty's build() wiring. - vaxis_mod.addImport("uucode", uucode_mod); - root_mod.addImport("vaxis", vaxis_mod); + if (vaxis_mod) |vaxis| { + vaxis.addImport("uucode", uucode_mod); + root_mod.addImport("vaxis", vaxis); + } root_mod.addImport("uucode", uucode_mod); hx_core_mod.addImport("uucode", uucode_mod); @@ -467,29 +442,22 @@ pub fn build(b: *std.Build) void { root_mod.addOptions("pardes_config", opts); hx_core_mod.addOptions("pardes_config", opts); - // SDL3 shell wiring (-Dplatform=gui and web). The sdl package builds SDL3 + // SDL3 shell wiring (-Dplatform=gui). The sdl package builds SDL3 // from source as a static lib + emits the SDL3/ headers; gui.zig @cImports // those, plus a small stb_truetype C shim for the glyph atlas, and embeds - // the SPIR-V compiled at build time from shaders/*.glsl — or, on web, the - // GL ES shader sources compiled at runtime by WebGL. - if (platform == .gui or platform == .web) { + // the SPIR-V compiled at build time from shaders/*.glsl. + if (platform == .gui) { // sanitize_c MUST stay off: zig cc's UBSan (on for C in Debug AND // ReleaseSafe) traps hidapi's mismatched fn-pointer calls the moment // a HID gamepad is enumerated — SDL_Init SIGILLs on the deck itself // (ud2 in SDL_hid_set_nonblocking_REAL); headless boxes never see it. // SDL3 always statically linked (like deckcap): the gui binary must // only need system libc/libm — never a shared libSDL3. - const sdl_dep = if (emscripten_include) |include| - b.lazyDependency("sdl", .{ .target = target, .optimize = optimize, .sanitize_c = .off, .preferred_linkage = .static, .system_include_path = include }) - else - b.lazyDependency("sdl", .{ .target = target, .optimize = optimize, .sanitize_c = .off, .preferred_linkage = .static }); + const sdl_dep = b.lazyDependency("sdl", .{ .target = target, .optimize = optimize, .sanitize_c = .off, .preferred_linkage = .static }); if (sdl_dep) |dep| { const sdl_lib = dep.artifact("SDL3"); - if (libc_file) |f| sdl_lib.setLibCFile(f); root_mod.linkLibrary(sdl_lib); root_mod.addIncludePath(dep.path("include")); - if (emscripten_include) |include| root_mod.addSystemIncludePath(include); - if (is_emscripten) wasm_libs.append(b.allocator, sdl_lib.getEmittedBin()) catch @panic("OOM"); } // darwin: SDL_GPU only speaks SPIRV here (shaders/*.glsl -> glslc), so // it must pick its vulkan backend via the Vulkan SDK's loader + @@ -505,71 +473,56 @@ pub fn build(b: *std.Build) void { root_mod.addAnonymousImport("AdwaitaMono-Regular.ttf", .{ .root_source_file = b.path("assets/AdwaitaMono-Regular.ttf"), }); - if (platform == .web) { - root_mod.addAnonymousImport("ui.vert.es.glsl", .{ .root_source_file = b.path("shaders/ui.vert.es.glsl") }); - root_mod.addAnonymousImport("ui.frag.es.glsl", .{ .root_source_file = b.path("shaders/ui.frag.es.glsl") }); - root_mod.addAnonymousImport("overlay.vert.es.glsl", .{ .root_source_file = b.path("shaders/overlay.vert.es.glsl") }); - root_mod.addAnonymousImport("overlay.frag.es.glsl", .{ .root_source_file = b.path("shaders/overlay.frag.es.glsl") }); - const dp = dump_path.?; - root_mod.addAnonymousImport("embedded.dump.zon", .{ - .root_source_file = if (std.fs.path.isAbsolute(dp)) .{ .cwd_relative = dp } else b.path(dp), - }); - root_mod.addAnonymousImport("embedded_sources", .{ .root_source_file = embedded_sources }); - } else { - root_mod.addAnonymousImport("ui.vert.spv", .{ - .root_source_file = compileGlsl(b, "shaders/ui.vert.glsl", "vertex", "ui.vert.spv"), - }); - root_mod.addAnonymousImport("ui.frag.spv", .{ - .root_source_file = compileGlsl(b, "shaders/ui.frag.glsl", "fragment", "ui.frag.spv"), - }); - root_mod.addAnonymousImport("overlay.vert.spv", .{ - .root_source_file = compileGlsl(b, "shaders/overlay.vert.glsl", "vertex", "overlay.vert.spv"), - }); - root_mod.addAnonymousImport("overlay.frag.spv", .{ - .root_source_file = compileGlsl(b, "shaders/overlay.frag.glsl", "fragment", "overlay.frag.spv"), - }); - root_mod.addAnonymousImport("crt.vert.spv", .{ - .root_source_file = compileGlsl(b, "shaders/crt.vert.glsl", "vertex", "crt.vert.spv"), - }); - root_mod.addAnonymousImport("crt.frag.spv", .{ - .root_source_file = compileGlsl(b, "shaders/crt.frag.glsl", "fragment", "crt.frag.spv"), - }); - } + root_mod.addAnonymousImport("ui.vert.spv", .{ + .root_source_file = compileGlsl(b, "shaders/ui.vert.glsl", "vertex", "ui.vert.spv"), + }); + root_mod.addAnonymousImport("ui.frag.spv", .{ + .root_source_file = compileGlsl(b, "shaders/ui.frag.glsl", "fragment", "ui.frag.spv"), + }); + root_mod.addAnonymousImport("overlay.vert.spv", .{ + .root_source_file = compileGlsl(b, "shaders/overlay.vert.glsl", "vertex", "overlay.vert.spv"), + }); + root_mod.addAnonymousImport("overlay.frag.spv", .{ + .root_source_file = compileGlsl(b, "shaders/overlay.frag.glsl", "fragment", "overlay.frag.spv"), + }); + root_mod.addAnonymousImport("crt.vert.spv", .{ + .root_source_file = compileGlsl(b, "shaders/crt.vert.glsl", "vertex", "crt.vert.spv"), + }); + root_mod.addAnonymousImport("crt.frag.spv", .{ + .root_source_file = compileGlsl(b, "shaders/crt.frag.glsl", "fragment", "crt.frag.spv"), + }); + } + + if (is_web) { + const dp = dump_path.?; + root_mod.addAnonymousImport("embedded.dump.zon", .{ + .root_source_file = if (std.fs.path.isAbsolute(dp)) .{ .cwd_relative = dp } else b.path(dp), + }); + root_mod.addAnonymousImport("embedded_sources", .{ .root_source_file = embedded_sources }); } if (platform == .web) { - // zig emits an object; emcc does the final link (wasm + js glue), - // compiling the C shims itself and pulling in every static archive. - const obj = b.addObject(.{ .name = "pardes", .root_module = root_mod }); - obj.setLibCFile(libc_file.?); + const wasm = b.addExecutable(.{ .name = "pardes", .root_module = root_mod }); + wasm.entry = .disabled; + wasm.rdynamic = true; + wasm.export_memory = true; + // The full grammar tier carries ~48 MiB of generated parse tables. + // Keep the compact default at 32 MiB, but leave enough static address + // space for an explicitly requested all-language build to link. + wasm.initial_memory = @as(u64, if (tree_sitter_grammars == .full) 64 else 32) * 1024 * 1024; + wasm.max_memory = 512 * 1024 * 1024; + web_step.dependOn(&b.addInstallFileWithDir(wasm.getEmittedBin(), .{ .custom = "web" }, "pardes.wasm").step); + inline for (.{ + .{ "src/web/index.html", "index.html" }, + .{ "src/web/app.mjs", "app.mjs" }, + .{ "src/web/pardes.css", "pardes.css" }, + .{ "assets/AdwaitaMono-Regular.ttf", "AdwaitaMono-Regular.ttf" }, + }) |file| web_step.dependOn(&b.addInstallFileWithDir(b.path(file[0]), .{ .custom = "web" }, file[1]).step); - const emcc = b.addSystemCommand(&.{emcc_path}); - emcc.addFileArg(obj.getEmittedBinDirectory().path(b, "pardes_zcu.o")); - emcc.addFileArg(obj.getEmittedBin()); - emcc.addFileArg(b.path("vendor/stb/stb_truetype_impl.c")); - emcc.addFileArg(b.path("vendor/stb/font.c")); - emcc.addFileArg(zstbi_dep.path("src/zstbi.c")); - for (wasm_libs.items) |lib| emcc.addFileArg(lib); - emcc.addPrefixedDirectoryArg("-I", b.path("vendor/stb")); - emcc.addPrefixedDirectoryArg("-I", zstbi_dep.path("libs/stbi")); - emcc.addArgs(&.{ - "-std=c99", - "-fno-sanitize=undefined", - "-Oz", - "-sINITIAL_MEMORY=1073741824", // the 512 MiB web heap + code/stack - "-sEXIT_RUNTIME=0", // run() returns; the rAF loop keeps living - "-sFULL_ES3=1", - "-sMIN_WEBGL_VERSION=2", - "-sMAX_WEBGL_VERSION=2", - "-sINCOMING_MODULE_JS_API=canvas", - "-sEXPORTED_FUNCTIONS=['_main','_pardes_web_test_cols','_pardes_web_test_rows','_pardes_web_test_cursor_x','_pardes_web_test_cursor_y','_pardes_web_test_cell','_pardes_web_test_cell_fg','_pardes_web_test_cell_bg','_pardes_web_test_cell_attrs','_pardes_web_test_cell_x','_pardes_web_test_cell_y']", - "-o", - }); - const js = emcc.addOutputFileArg("pardes.js"); - web_step.dependOn(&b.addInstallFileWithDir(js, .{ .custom = "web" }, "pardes.js").step); - web_step.dependOn(&b.addInstallFileWithDir(js.dirname().path(b, "pardes.wasm"), .{ .custom = "web" }, "pardes.wasm").step); - const html = b.addWriteFiles().add("index.html", html_shell); - web_step.dependOn(&b.addInstallFileWithDir(html, .{ .custom = "web" }, "index.html").step); + const run_web_harness = b.addSystemCommand(&.{ "node", "test/web_harness.mjs" }); + run_web_harness.addArg(b.getInstallPath(.{ .custom = "web" }, "")); + run_web_harness.step.dependOn(web_step); + b.step("web-harness", "run the dependency-free JS/WASM DOM harness").dependOn(&run_web_harness.step); b.getInstallStep().dependOn(web_step); } else { // binary name: the native-shaped linux-x86_64 tty build stays `pardes` @@ -587,7 +540,7 @@ pub fn build(b: *std.Build) void { b.installArtifact(exe); const run = b.addRunArtifact(exe); b.step("run", "descend to the pardes").dependOn(&run.step); - web_step.dependOn(&b.addFail("web needs -Dplatform=web -Dtarget=wasm32-emscripten -Ddump=<dump.zon>").step); + web_step.dependOn(&b.addFail("web needs -Dplatform=web -Dtarget=wasm32-freestanding -Ddump=<dump.zon>").step); // snapshot parity suite: scripted input traces in a pty, captured grids // diffed against the goldens in snapshots/ — the frozen record of parity @@ -736,45 +689,6 @@ pub fn build(b: *std.Build) void { } } -// the html shell: a full-viewport canvas wired into Module (from the prototype) -const html_shell = - \\<!doctype html> - \\<html> - \\<head> - \\ <meta charset="utf-8"> - \\ <meta name="viewport" content="width=device-width, initial-scale=1"> - \\ <title>pardes</title> - \\ <style> - \\ html, body { margin: 0; width: 100%; height: 100%; background: #121212; } - \\ body { overflow: hidden; } - \\ canvas { display: block; width: 100vw; height: 100vh; outline: none; - \\ image-rendering: crisp-edges; image-rendering: pixelated; - \\ -ms-interpolation-mode: nearest-neighbor; - \\ transform: translateZ(0); backface-visibility: hidden; - \\ touch-action: none; } - \\ </style> - \\</head> - \\<body> - \\ <canvas id="canvas" tabindex="0"></canvas> - \\ <script> - \\ const canvas = document.getElementById('canvas'); - \\ canvas.addEventListener('contextmenu', (event) => event.preventDefault()); - \\ canvas.addEventListener('pointerdown', (event) => { - \\ canvas.focus({ preventScroll: true }); - \\ canvas.setPointerCapture?.(event.pointerId); - \\ }); - \\ const releasePointer = (event) => { - \\ if (canvas.hasPointerCapture?.(event.pointerId)) canvas.releasePointerCapture(event.pointerId); - \\ }; - \\ canvas.addEventListener('pointerup', releasePointer); - \\ canvas.addEventListener('pointercancel', releasePointer); - \\ window.Module = { canvas }; - \\ </script> - \\ <script src="pardes.js"></script> - \\</body> - \\</html> -; - // a bad -D combination: fail both `zig build` and `zig build web` with the why fn failBuild(b: *std.Build, web_step: *std.Build.Step, msg: []const u8) void { const fail = &b.addFail(msg).step; @@ -812,17 +726,3 @@ fn vendoredThemes(b: *std.Build, io: std.Io) []const []const u8 { }.lt); return names.items; } - -fn detectEmscriptenSysroot(b: *std.Build) ?[]const u8 { - if (b.option([]const u8, "emscripten-sysroot", "path to the Emscripten sysroot")) |path| return path; - if (b.sysroot) |path| return path; - if (envPath(b, "EMSCRIPTEN_SYSROOT")) |path| return path; - if (envPath(b, "EM_CACHE")) |cache| return b.pathJoin(&.{ cache, "sysroot" }); - if (envPath(b, "EMSDK")) |emsdk| return b.pathJoin(&.{ emsdk, "upstream", "emscripten", "cache", "sysroot" }); - if (envPath(b, "HOME")) |home| return b.pathJoin(&.{ home, ".cache", "emscripten", "sysroot" }); - return null; -} - -fn envPath(b: *std.Build, name: []const u8) ?[]const u8 { - return b.graph.environ_map.get(name); -} diff --git a/build/snap.zig b/build/snap.zig index a721f483..2c135fef 100644 --- a/build/snap.zig +++ b/build/snap.zig @@ -39,18 +39,17 @@ pub fn addTty(b: *std.Build, opts: struct { b.step("snap", "run the snapshot parity suite (-- --update regenerates goldens)").dependOn(&run_snap.step); } -/// Browser regression suite: make an isolated web build from the real TTY dump -/// captured after running `git ls-files '*.zig'`, with the Zig-only tree-sitter -/// tier. The dependency-free CDP harness then drives its .snap script through -/// real Chromium touch events. This recursive build selects `web`, not -/// `web-snap`, so it bottoms out after one level. +/// Browser regression suite: make an isolated freestanding web build from the +/// real TTY dump captured after running `git ls-files '*.zig'`. The +/// dependency-free Chrome/CDP driver then exercises real DOM pointer events. +/// This recursive build selects `web`, not `web-snap`, so it bottoms out. pub fn addWeb(b: *std.Build) void { const build_web_snap = b.addSystemCommand(&.{ b.graph.zig_exe, "build", "web", "-Dplatform=web", - "-Dtarget=wasm32-emscripten", + "-Dtarget=wasm32-freestanding", "-Dtree-sitter=zig", "-Ddump=test/web-snapshots/source-list.dump.zon", "-p", @@ -67,4 +66,5 @@ pub fn addWeb(b: *std.Build) void { run_web_snap.step.dependOn(&build_web_snap.step); if (b.args) |args| run_web_snap.addArgs(args); b.step("web-snap", "run browser touch/LOOK snapshots (-- --update regenerates goldens)").dependOn(&run_web_snap.step); + b.step("web-e2e", "run the Chrome-driven DOM end-to-end suite").dependOn(&run_web_snap.step); } diff --git a/docs/design.pdf b/docs/design.pdf index 0022972b..83f97dc2 100644 --- a/docs/design.pdf +++ b/docs/design.pdf @@ -2,340 +2,340 @@ %���� 1 0 obj -<</Type/Pages/Count 4/Kids[245 0 R 246 0 R 247 0 R 248 0 R]>> +<</Type/Pages/Count 5/Kids[253 0 R 254 0 R 255 0 R 256 0 R 257 0 R]>> endobj 2 0 obj -<</Parent 11 0 R/Next 3 0 R/Title(What it is)/Dest 217 0 R>> +<</Parent 10 0 R/Next 3 0 R/Title(What it is)/Dest 228 0 R>> endobj 3 0 obj -<</Parent 11 0 R/Next 4 0 R/Prev 2 0 R/Title(The shape)/Dest 218 0 R>> +<</Parent 10 0 R/Next 4 0 R/Prev 2 0 R/Title(The shape)/Dest 229 0 R>> endobj 4 0 obj -<</Parent 11 0 R/Next 5 0 R/Prev 3 0 R/Title(Data structures)/Dest 219 0 R>> +<</Parent 10 0 R/Next 5 0 R/Prev 3 0 R/Title(Data structures)/Dest 230 0 R>> endobj 5 0 obj -<</Parent 11 0 R/Next 6 0 R/Prev 4 0 R/Title(Style)/Dest 220 0 R>> +<</Parent 10 0 R/Next 6 0 R/Prev 4 0 R/Title(Style)/Dest 231 0 R>> endobj 6 0 obj -<</Parent 11 0 R/Next 7 0 R/Prev 5 0 R/Title(Testing: the old program is the oracle)/Dest 221 0 R>> +<</Parent 10 0 R/Next 7 0 R/Prev 5 0 R/Title(Testing: the old program is the oracle)/Dest 232 0 R>> endobj 7 0 obj -<</Parent 11 0 R/Next 8 0 R/Prev 6 0 R/Title(Build)/Dest 222 0 R>> +<</Parent 10 0 R/Next 8 0 R/Prev 6 0 R/Title(Build)/Dest 233 0 R>> endobj 8 0 obj -<</Parent 11 0 R/Next 9 0 R/Prev 7 0 R/Title(What is deliberately absent)/Dest 223 0 R>> +<</Parent 10 0 R/Next 9 0 R/Prev 7 0 R/Title(What is deliberately absent)/Dest 234 0 R>> endobj 9 0 obj -<</Parent 11 0 R/Next 10 0 R/Prev 8 0 R/Title(Appendix A: feature parity checklist)/Dest 224 0 R>> +<</Parent 10 0 R/Prev 8 0 R/Title(Appendix A: feature parity checklist)/Dest 235 0 R>> endobj 10 0 obj -<</Parent 11 0 R/Prev 9 0 R/Title(middle click, pinch, touch debug overlay, PARDES_TEST PPM capture and)/Dest 225 0 R>> +<</Type/Outlines/First 2 0 R/Last 9 0 R/Count 8>> endobj 11 0 obj -<</Type/Outlines/First 2 0 R/Last 10 0 R/Count 9>> +<</Type/StructTreeRoot/RoleMap<</Datetime/Span/Terms/Part/Title/P/Strong/Span/Em/Span>>/K[225 0 R]/ParentTree<</Nums[0 12 0 R 1 13 0 R 2 14 0 R 3 15 0 R 4 16 0 R]>>/ParentTreeNextKey 5>> endobj 12 0 obj -<</Type/StructTreeRoot/RoleMap<</Datetime/Span/Terms/Part/Title/P/Strong/Span/Em/Span>>/K[214 0 R]/ParentTree<</Nums[0 13 0 R 1 14 0 R 2 15 0 R 3 16 0 R]>>/ParentTreeNextKey 4>> +[17 0 R 19 0 R 20 0 R 21 0 R 21 0 R 21 0 R 21 0 R 22 0 R 22 0 R 22 0 R 22 0 R 23 0 R 25 0 R 24 0 R 25 0 R 25 0 R 25 0 R 26 0 R 30 0 R 28 0 R 30 0 R 29 0 R 30 0 R 32 0 R 34 0 R 34 0 R 34 0 R 40 0 R 37 0 R 40 0 R 40 0 R 40 0 R 38 0 R 40 0 R 40 0 R 40 0 R 39 0 R 40 0 R 41 0 R 42 0 R 50 0 R 43 0 R 50 0 R 44 0 R 50 0 R 45 0 R 50 0 R 46 0 R 50 0 R 47 0 R 50 0 R 48 0 R 50 0 R 49 0 R 50 0 R 50 0 R 50 0 R 50 0 R 51 0 R 53 0 R 53 0 R 52 0 R 53 0 R 53 0 R 53 0 R 53 0 R 53 0 R 54 0 R 64 0 R 55 0 R 64 0 R 56 0 R 64 0 R 57 0 R 64 0 R 58 0 R 64 0 R 59 0 R 64 0 R 60 0 R 64 0 R 61 0 R 64 0 R 62 0 R 64 0 R 64 0 R 63 0 R 64 0 R 64 0 R 66 0 R 65 0 R 66 0 R] endobj 13 0 obj -[17 0 R 19 0 R 20 0 R 21 0 R 21 0 R 21 0 R 21 0 R 22 0 R 22 0 R 22 0 R 23 0 R 25 0 R 24 0 R 25 0 R 25 0 R 25 0 R 26 0 R 30 0 R 28 0 R 30 0 R 29 0 R 30 0 R 32 0 R 34 0 R 34 0 R 34 0 R 40 0 R 37 0 R 40 0 R 40 0 R 40 0 R 38 0 R 40 0 R 40 0 R 40 0 R 39 0 R 40 0 R 41 0 R 42 0 R 50 0 R 43 0 R 50 0 R 44 0 R 50 0 R 45 0 R 50 0 R 46 0 R 50 0 R 47 0 R 50 0 R 48 0 R 50 0 R 49 0 R 50 0 R 50 0 R 50 0 R 51 0 R 53 0 R 53 0 R 52 0 R 53 0 R 53 0 R 53 0 R 53 0 R 54 0 R 64 0 R 55 0 R 64 0 R 56 0 R 64 0 R 57 0 R 64 0 R 58 0 R 64 0 R 59 0 R 64 0 R 60 0 R 64 0 R 61 0 R 64 0 R 62 0 R 64 0 R 64 0 R 63 0 R 64 0 R 64 0 R 66 0 R 65 0 R 66 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 68 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R] +[67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 67 0 R 68 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 69 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 70 0 R 76 0 R 72 0 R 76 0 R 73 0 R 76 0 R 76 0 R 74 0 R 76 0 R 75 0 R 76 0 R 76 0 R 77 0 R 78 0 R 79 0 R 80 0 R 80 0 R 80 0 R 80 0 R 80 0 R 80 0 R 80 0 R 81 0 R 81 0 R 81 0 R 81 0 R 81 0 R 81 0 R 81 0 R 82 0 R 82 0 R 82 0 R 82 0 R 82 0 R 83 0 R 83 0 R 83 0 R 83 0 R 83 0 R 83 0 R 83 0 R 84 0 R 84 0 R 84 0 R 84 0 R 84 0 R 84 0 R 84 0 R 85 0 R 85 0 R 85 0 R 85 0 R 85 0 R 86 0 R 86 0 R 86 0 R 86 0 R 86 0 R 86 0 R 86 0 R 87 0 R 87 0 R 87 0 R 87 0 R 87 0 R 87 0 R 87 0 R 87 0 R 89 0 R 90 0 R 90 0 R 90 0 R 90 0 R 90 0 R 90 0 R 90 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 92 0 R 92 0 R 92 0 R 92 0 R 92 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 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 94 0 R 94 0 R 94 0 R 94 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 96 0 R 96 0 R 96 0 R 96 0 R 96 0 R 96 0 R 96 0 R 97 0 R 97 0 R 97 0 R 97 0 R 97 0 R 97 0 R 97 0 R 97 0 R 97 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 99 0 R 99 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 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 105 0 R 103 0 R 105 0 R 104 0 R 105 0 R 105 0 R 105 0 R 106 0 R 109 0 R 107 0 R 109 0 R 109 0 R 108 0 R 108 0 R 109 0 R 109 0 R 110 0 R 112 0 R 116 0 R 117 0 R 118 0 R 118 0 R 118 0 R 120 0 R 121 0 R 122 0 R 124 0 R 125 0 R 126 0 R 128 0 R 129 0 R 130 0 R 130 0 R] endobj 14 0 obj -[76 0 R 72 0 R 76 0 R 73 0 R 76 0 R 76 0 R 74 0 R 76 0 R 75 0 R 76 0 R 76 0 R 77 0 R 78 0 R 79 0 R 80 0 R 80 0 R 80 0 R 80 0 R 80 0 R 80 0 R 80 0 R 81 0 R 81 0 R 81 0 R 81 0 R 81 0 R 81 0 R 81 0 R 82 0 R 82 0 R 82 0 R 82 0 R 82 0 R 83 0 R 83 0 R 83 0 R 83 0 R 83 0 R 83 0 R 83 0 R 84 0 R 84 0 R 84 0 R 84 0 R 84 0 R 84 0 R 84 0 R 85 0 R 85 0 R 85 0 R 85 0 R 85 0 R 86 0 R 86 0 R 86 0 R 86 0 R 86 0 R 86 0 R 86 0 R 87 0 R 87 0 R 87 0 R 87 0 R 87 0 R 87 0 R 87 0 R 87 0 R 89 0 R 90 0 R 90 0 R 90 0 R 90 0 R 90 0 R 90 0 R 90 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 91 0 R 92 0 R 92 0 R 92 0 R 92 0 R 92 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 0 R 93 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 94 0 R 94 0 R 94 0 R 94 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 95 0 R 96 0 R 96 0 R 96 0 R 96 0 R 96 0 R 96 0 R 96 0 R 97 0 R 97 0 R 97 0 R 97 0 R 97 0 R 97 0 R 97 0 R 97 0 R 97 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 99 0 R 99 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 0 R 100 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 105 0 R 103 0 R 105 0 R 104 0 R 105 0 R 105 0 R 105 0 R 106 0 R 109 0 R 107 0 R 109 0 R 109 0 R 108 0 R 108 0 R 109 0 R 109 0 R 110 0 R 112 0 R 116 0 R 117 0 R 118 0 R 120 0 R 121 0 R 122 0 R 124 0 R 125 0 R 126 0 R 128 0 R 129 0 R 130 0 R 132 0 R 134 0 R 136 0 R 136 0 R 138 0 R 139 0 R 140 0 R] +[132 0 R 134 0 R 136 0 R 136 0 R 136 0 R 138 0 R 139 0 R 140 0 R 143 0 R 152 0 R 144 0 R 152 0 R 145 0 R 152 0 R 152 0 R 146 0 R 152 0 R 152 0 R 152 0 R 147 0 R 152 0 R 152 0 R 148 0 R 152 0 R 152 0 R 152 0 R 149 0 R 152 0 R 150 0 R 152 0 R 151 0 R 152 0 R 154 0 R 153 0 R 154 0 R 154 0 R 154 0 R 154 0 R 154 0 R 155 0 R 155 0 R 156 0 R 157 0 R 164 0 R 158 0 R 164 0 R 159 0 R 164 0 R 160 0 R 164 0 R 164 0 R 164 0 R 161 0 R 161 0 R 164 0 R 162 0 R 164 0 R 163 0 R 164 0 R 164 0 R 164 0 R 165 0 R 169 0 R 166 0 R 169 0 R 169 0 R 169 0 R 169 0 R 167 0 R 169 0 R 168 0 R 169 0 R 170 0 R 172 0 R 171 0 R 171 0 R 172 0 R 172 0 R 173 0 R 178 0 R 178 0 R 178 0 R 178 0 R 178 0 R 178 0 R 174 0 R 178 0 R 178 0 R 178 0 R 175 0 R 178 0 R 176 0 R 178 0 R 178 0 R 177 0 R 178 0 R] endobj 15 0 obj -[143 0 R 152 0 R 144 0 R 152 0 R 145 0 R 152 0 R 152 0 R 146 0 R 152 0 R 152 0 R 152 0 R 147 0 R 152 0 R 152 0 R 148 0 R 152 0 R 152 0 R 152 0 R 149 0 R 152 0 R 150 0 R 152 0 R 151 0 R 152 0 R 154 0 R 153 0 R 154 0 R 154 0 R 154 0 R 154 0 R 154 0 R 155 0 R 155 0 R 156 0 R 157 0 R 164 0 R 158 0 R 164 0 R 159 0 R 164 0 R 160 0 R 164 0 R 164 0 R 164 0 R 161 0 R 164 0 R 162 0 R 164 0 R 164 0 R 163 0 R 164 0 R 164 0 R 164 0 R 165 0 R 169 0 R 166 0 R 169 0 R 169 0 R 169 0 R 169 0 R 167 0 R 169 0 R 168 0 R 169 0 R 170 0 R 172 0 R 171 0 R 171 0 R 172 0 R 172 0 R 173 0 R 178 0 R 178 0 R 178 0 R 178 0 R 178 0 R 178 0 R 174 0 R 178 0 R 178 0 R 178 0 R 175 0 R 178 0 R 176 0 R 178 0 R 178 0 R 177 0 R 178 0 R 179 0 R 184 0 R 184 0 R 184 0 R 180 0 R 184 0 R 184 0 R 181 0 R 184 0 R 182 0 R 184 0 R 184 0 R 183 0 R 184 0 R 184 0 R] +[179 0 R 186 0 R 186 0 R 186 0 R 180 0 R 186 0 R 186 0 R 181 0 R 186 0 R 182 0 R 186 0 R 186 0 R 186 0 R 186 0 R 183 0 R 186 0 R 186 0 R 186 0 R 184 0 R 186 0 R 185 0 R 186 0 R 186 0 R 186 0 R 186 0 R 187 0 R 197 0 R 188 0 R 197 0 R 189 0 R 197 0 R 190 0 R 197 0 R 197 0 R 191 0 R 197 0 R 192 0 R 197 0 R 193 0 R 197 0 R 194 0 R 197 0 R 197 0 R 197 0 R 195 0 R 197 0 R 197 0 R 196 0 R 197 0 R 197 0 R 198 0 R 204 0 R 199 0 R 204 0 R 200 0 R 204 0 R 201 0 R 204 0 R 202 0 R 202 0 R 204 0 R 203 0 R 204 0 R 204 0 R 205 0 R 207 0 R 207 0 R 206 0 R 207 0 R 207 0 R 207 0 R 207 0 R 207 0 R 208 0 R 218 0 R 209 0 R 218 0 R 218 0 R 218 0 R 210 0 R 218 0 R 218 0 R 211 0 R 218 0 R 212 0 R 218 0 R 213 0 R 218 0 R 218 0 R 218 0 R 218 0 R 214 0 R 218 0 R 215 0 R 215 0 R 218 0 R 216 0 R 218 0 R 217 0 R 218 0 R 218 0 R 218 0 R 219 0 R 224 0 R 224 0 R 224 0 R 224 0 R 224 0 R 220 0 R 224 0 R 224 0 R 221 0 R 224 0 R 224 0 R 224 0 R 224 0 R 224 0 R] endobj 16 0 obj -[184 0 R 184 0 R 185 0 R 195 0 R 186 0 R 195 0 R 187 0 R 195 0 R 188 0 R 195 0 R 195 0 R 189 0 R 195 0 R 190 0 R 195 0 R 191 0 R 195 0 R 192 0 R 195 0 R 195 0 R 195 0 R 193 0 R 195 0 R 195 0 R 194 0 R 195 0 R 195 0 R 196 0 R 201 0 R 197 0 R 201 0 R 198 0 R 201 0 R 199 0 R 201 0 R 200 0 R 200 0 R 200 0 R 201 0 R 202 0 R 204 0 R 204 0 R 203 0 R 204 0 R 204 0 R 204 0 R 204 0 R 204 0 R 205 0 R 206 0 R 206 0 R 207 0 R 208 0 R 208 0 R 208 0 R 208 0 R 210 0 R 209 0 R 210 0 R 210 0 R 210 0 R 211 0 R 213 0 R 212 0 R 213 0 R 213 0 R] +[224 0 R 224 0 R 224 0 R 224 0 R 222 0 R 224 0 R 224 0 R 223 0 R 224 0 R 224 0 R] endobj 17 0 obj -<</Type/StructElem/S/Strong/P 18 0 R/K[0]/Pg 245 0 R>> +<</Type/StructElem/S/Strong/P 18 0 R/K[0]/Pg 253 0 R>> endobj 18 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[17 0 R]>> +<</Type/StructElem/S/P/P 225 0 R/K[17 0 R]>> endobj 19 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[1]/Pg 245 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[1]/Pg 253 0 R>> endobj 20 0 obj -<</Type/StructElem/S/H1/P 214 0 R/T(What it is)/K[2]/Pg 245 0 R>> +<</Type/StructElem/S/H1/P 225 0 R/T(What it is)/K[2]/Pg 253 0 R>> endobj 21 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[3 4 5 6]/Pg 245 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[3 4 5 6]/Pg 253 0 R>> endobj 22 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[7 8 9]/Pg 245 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[7 8 9 10]/Pg 253 0 R>> endobj 23 0 obj -<</Type/StructElem/S/H1/P 214 0 R/T(The shape)/K[10]/Pg 245 0 R>> +<</Type/StructElem/S/H1/P 225 0 R/T(The shape)/K[11]/Pg 253 0 R>> endobj 24 0 obj -<</Type/StructElem/S/Strong/P 25 0 R/K[12]/Pg 245 0 R>> +<</Type/StructElem/S/Strong/P 25 0 R/K[13]/Pg 253 0 R>> endobj 25 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[11 24 0 R 13 14 15]/Pg 245 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[12 24 0 R 14 15 16]/Pg 253 0 R>> endobj 26 0 obj -<</Type/StructElem/S/Strong/P 27 0 R/K[16]/Pg 245 0 R>> +<</Type/StructElem/S/Strong/P 27 0 R/K[17]/Pg 253 0 R>> endobj 27 0 obj <</Type/StructElem/S/TD/P 31 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[26 0 R]>> endobj 28 0 obj -<</Type/StructElem/S/Code/P 30 0 R/K[18]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 30 0 R/K[19]/Pg 253 0 R>> endobj 29 0 obj -<</Type/StructElem/S/Code/P 30 0 R/K[20]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 30 0 R/K[21]/Pg 253 0 R>> endobj 30 0 obj -<</Type/StructElem/S/TD/P 31 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[17 28 0 R 19 29 0 R 21]/Pg 245 0 R>> +<</Type/StructElem/S/TD/P 31 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[18 28 0 R 20 29 0 R 22]/Pg 253 0 R>> endobj 31 0 obj <</Type/StructElem/S/TR/P 36 0 R/K[27 0 R 30 0 R]>> endobj 32 0 obj -<</Type/StructElem/S/Strong/P 33 0 R/K[22]/Pg 245 0 R>> +<</Type/StructElem/S/Strong/P 33 0 R/K[23]/Pg 253 0 R>> endobj 33 0 obj <</Type/StructElem/S/TD/P 35 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[32 0 R]>> endobj 34 0 obj -<</Type/StructElem/S/TD/P 35 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[23 24 25]/Pg 245 0 R>> +<</Type/StructElem/S/TD/P 35 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[24 25 26]/Pg 253 0 R>> endobj 35 0 obj <</Type/StructElem/S/TR/P 36 0 R/K[33 0 R 34 0 R]>> endobj 36 0 obj -<</Type/StructElem/S/Table/P 214 0 R/A[<</O/Layout/BorderColor[0 0 0]/BorderThickness 0.4>>]/K[31 0 R 35 0 R]>> +<</Type/StructElem/S/Table/P 225 0 R/A[<</O/Layout/BorderColor[0 0 0]/BorderThickness 0.4>>]/K[31 0 R 35 0 R]>> endobj 37 0 obj -<</Type/StructElem/S/Strong/P 40 0 R/K[27]/Pg 245 0 R>> +<</Type/StructElem/S/Strong/P 40 0 R/K[28]/Pg 253 0 R>> endobj 38 0 obj -<</Type/StructElem/S/Code/P 40 0 R/K[31]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 40 0 R/K[32]/Pg 253 0 R>> endobj 39 0 obj -<</Type/StructElem/S/Code/P 40 0 R/K[35]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 40 0 R/K[36]/Pg 253 0 R>> endobj 40 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[26 37 0 R 28 29 30 38 0 R 32 33 34 39 0 R 36]/Pg 245 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[27 37 0 R 29 30 31 38 0 R 33 34 35 39 0 R 37]/Pg 253 0 R>> endobj 41 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[37]/Pg 245 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[38]/Pg 253 0 R>> endobj 42 0 obj -<</Type/StructElem/S/Strong/P 50 0 R/K[38]/Pg 245 0 R>> +<</Type/StructElem/S/Strong/P 50 0 R/K[39]/Pg 253 0 R>> endobj 43 0 obj -<</Type/StructElem/S/Code/P 50 0 R/K[40]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 50 0 R/K[41]/Pg 253 0 R>> endobj 44 0 obj -<</Type/StructElem/S/Code/P 50 0 R/K[42]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 50 0 R/K[43]/Pg 253 0 R>> endobj 45 0 obj -<</Type/StructElem/S/Code/P 50 0 R/K[44]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 50 0 R/K[45]/Pg 253 0 R>> endobj 46 0 obj -<</Type/StructElem/S/Code/P 50 0 R/K[46]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 50 0 R/K[47]/Pg 253 0 R>> endobj 47 0 obj -<</Type/StructElem/S/Code/P 50 0 R/K[48]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 50 0 R/K[49]/Pg 253 0 R>> endobj 48 0 obj -<</Type/StructElem/S/Code/P 50 0 R/K[50]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 50 0 R/K[51]/Pg 253 0 R>> endobj 49 0 obj -<</Type/StructElem/S/Code/P 50 0 R/K[52]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 50 0 R/K[53]/Pg 253 0 R>> endobj 50 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[42 0 R 39 43 0 R 41 44 0 R 43 45 0 R 45 46 0 R 47 47 0 R 49 48 0 R 51 49 0 R 53 54 55]/Pg 245 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[42 0 R 40 43 0 R 42 44 0 R 44 45 0 R 46 46 0 R 48 47 0 R 50 48 0 R 52 49 0 R 54 55 56 57]/Pg 253 0 R>> endobj 51 0 obj -<</Type/StructElem/S/Strong/P 53 0 R/K[56]/Pg 245 0 R>> +<</Type/StructElem/S/Strong/P 53 0 R/K[58]/Pg 253 0 R>> endobj 52 0 obj -<</Type/StructElem/S/Strong/P 53 0 R/K[59]/Pg 245 0 R>> +<</Type/StructElem/S/Strong/P 53 0 R/K[61]/Pg 253 0 R>> endobj 53 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[51 0 R 57 58 52 0 R 60 61 62 63]/Pg 245 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[51 0 R 59 60 52 0 R 62 63 64 65 66]/Pg 253 0 R>> endobj 54 0 obj -<</Type/StructElem/S/Strong/P 64 0 R/K[64]/Pg 245 0 R>> +<</Type/StructElem/S/Strong/P 64 0 R/K[67]/Pg 253 0 R>> endobj 55 0 obj -<</Type/StructElem/S/Code/P 64 0 R/K[66]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 64 0 R/K[69]/Pg 253 0 R>> endobj 56 0 obj -<</Type/StructElem/S/Code/P 64 0 R/K[68]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 64 0 R/K[71]/Pg 253 0 R>> endobj 57 0 obj -<</Type/StructElem/S/Code/P 64 0 R/K[70]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 64 0 R/K[73]/Pg 253 0 R>> endobj 58 0 obj -<</Type/StructElem/S/Code/P 64 0 R/K[72]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 64 0 R/K[75]/Pg 253 0 R>> endobj 59 0 obj -<</Type/StructElem/S/Code/P 64 0 R/K[74]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 64 0 R/K[77]/Pg 253 0 R>> endobj 60 0 obj -<</Type/StructElem/S/Code/P 64 0 R/K[76]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 64 0 R/K[79]/Pg 253 0 R>> endobj 61 0 obj -<</Type/StructElem/S/Code/P 64 0 R/K[78]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 64 0 R/K[81]/Pg 253 0 R>> endobj 62 0 obj -<</Type/StructElem/S/Code/P 64 0 R/K[80]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 64 0 R/K[83]/Pg 253 0 R>> endobj 63 0 obj -<</Type/StructElem/S/Code/P 64 0 R/K[83]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 64 0 R/K[86]/Pg 253 0 R>> endobj 64 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[54 0 R 65 55 0 R 67 56 0 R 69 57 0 R 71 58 0 R 73 59 0 R 75 60 0 R 77 61 0 R 79 62 0 R 81 82 63 0 R 84 85]/Pg 245 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[54 0 R 68 55 0 R 70 56 0 R 72 57 0 R 74 58 0 R 76 59 0 R 78 60 0 R 80 61 0 R 82 62 0 R 84 85 63 0 R 87 88]/Pg 253 0 R>> endobj 65 0 obj -<</Type/StructElem/S/Code/P 66 0 R/K[87]/Pg 245 0 R>> +<</Type/StructElem/S/Code/P 66 0 R/K[90]/Pg 253 0 R>> endobj 66 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[86 65 0 R 88]/Pg 245 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[89 65 0 R 91]/Pg 253 0 R>> endobj 67 0 obj -<</Type/StructElem/S/P/P 71 0 R/K[89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110]/Pg 245 0 R>> +<</Type/StructElem/S/P/P 71 0 R/K[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21]/Pg 254 0 R>> endobj 68 0 obj -<</Type/StructElem/S/P/P 71 0 R/K[111]/Pg 245 0 R>> +<</Type/StructElem/S/P/P 71 0 R/K[22]/Pg 254 0 R>> endobj 69 0 obj -<</Type/StructElem/S/P/P 71 0 R/K[112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128]/Pg 245 0 R>> +<</Type/StructElem/S/P/P 71 0 R/K[23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39]/Pg 254 0 R>> endobj 70 0 obj -<</Type/StructElem/S/P/P 71 0 R/K[129 130 131 132 133 134 135 136 137 138 139 140 141 142 143]/Pg 245 0 R>> +<</Type/StructElem/S/P/P 71 0 R/K[40 41 42 43 44 45 46 47 48 49 50 51 52 53 54]/Pg 254 0 R>> endobj 71 0 obj -<</Type/StructElem/S/Code/P 214 0 R/A[<</O/Layout/Placement/Block>>]/K[67 0 R 68 0 R 69 0 R 70 0 R]>> +<</Type/StructElem/S/Code/P 225 0 R/A[<</O/Layout/Placement/Block>>]/K[67 0 R 68 0 R 69 0 R 70 0 R]>> endobj 72 0 obj -<</Type/StructElem/S/Code/P 76 0 R/K[1]/Pg 246 0 R>> +<</Type/StructElem/S/Code/P 76 0 R/K[56]/Pg 254 0 R>> endobj 73 0 obj -<</Type/StructElem/S/Strong/P 76 0 R/K[3]/Pg 246 0 R>> +<</Type/StructElem/S/Strong/P 76 0 R/K[58]/Pg 254 0 R>> endobj 74 0 obj -<</Type/StructElem/S/Code/P 76 0 R/K[6]/Pg 246 0 R>> +<</Type/StructElem/S/Code/P 76 0 R/K[61]/Pg 254 0 R>> endobj 75 0 obj -<</Type/StructElem/S/Code/P 76 0 R/K[8]/Pg 246 0 R>> +<</Type/StructElem/S/Code/P 76 0 R/K[63]/Pg 254 0 R>> endobj 76 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[0 72 0 R 2 73 0 R 4 5 74 0 R 7 75 0 R 9 10]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[55 72 0 R 57 73 0 R 59 60 74 0 R 62 75 0 R 64 65]/Pg 254 0 R>> endobj 77 0 obj -<</Type/StructElem/S/H1/P 214 0 R/T(Data structures)/K[11]/Pg 246 0 R>> +<</Type/StructElem/S/H1/P 225 0 R/T(Data structures)/K[66]/Pg 254 0 R>> endobj 78 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[12]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[67]/Pg 254 0 R>> endobj 79 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[13]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[68]/Pg 254 0 R>> endobj 80 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[14 15 16 17 18 19 20]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[69 70 71 72 73 74 75]/Pg 254 0 R>> endobj 81 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[21 22 23 24 25 26 27]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[76 77 78 79 80 81 82]/Pg 254 0 R>> endobj 82 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[28 29 30 31 32]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[83 84 85 86 87]/Pg 254 0 R>> endobj 83 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[33 34 35 36 37 38 39]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[88 89 90 91 92 93 94]/Pg 254 0 R>> endobj 84 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[40 41 42 43 44 45 46]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[95 96 97 98 99 100 101]/Pg 254 0 R>> endobj 85 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[47 48 49 50 51]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[102 103 104 105 106]/Pg 254 0 R>> endobj 86 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[52 53 54 55 56 57 58]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[107 108 109 110 111 112 113]/Pg 254 0 R>> endobj 87 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[59 60 61 62 63 64 65 66]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[114 115 116 117 118 119 120 121]/Pg 254 0 R>> endobj 88 0 obj <</Type/StructElem/S/P/P 102 0 R/K[]>> endobj 89 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[67]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[122]/Pg 254 0 R>> endobj 90 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[68 69 70 71 72 73 74]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[123 124 125 126 127 128 129]/Pg 254 0 R>> endobj 91 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154]/Pg 254 0 R>> endobj 92 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[100 101 102 103 104]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[155 156 157 158 159]/Pg 254 0 R>> endobj 93 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178]/Pg 254 0 R>> endobj 94 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[124 125 126 127 128 129 130 131 132 133 134]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[179 180 181 182 183 184 185 186 187 188 189]/Pg 254 0 R>> endobj 95 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208]/Pg 254 0 R>> endobj 96 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[154 155 156 157 158 159 160]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[209 210 211 212 213 214 215]/Pg 254 0 R>> endobj 97 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[161 162 163 164 165 166 167 168 169]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[216 217 218 219 220 221 222 223 224]/Pg 254 0 R>> endobj 98 0 obj <</Type/StructElem/S/P/P 102 0 R/K[]>> endobj 99 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251]/Pg 254 0 R>> endobj 100 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280]/Pg 254 0 R>> endobj 101 0 obj -<</Type/StructElem/S/P/P 102 0 R/K[226 227 228 229 230 231 232 233 234 235 236 237 238 239 240]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 102 0 R/K[281 282 283 284 285 286 287 288 289 290 291 292 293 294 295]/Pg 254 0 R>> endobj 102 0 obj -<</Type/StructElem/S/Code/P 214 0 R/A[<</O/Layout/Placement/Block>>]/K[79 0 R 80 0 R 81 0 R 82 0 R 83 0 R 84 0 R 85 0 R 86 0 R 87 0 R 88 0 R 89 0 R 90 0 R 91 0 R 92 0 R 93 0 R 94 0 R 95 0 R 96 0 R 97 0 R 98 0 R 99 0 R 100 0 R 101 0 R]>> +<</Type/StructElem/S/Code/P 225 0 R/A[<</O/Layout/Placement/Block>>]/K[79 0 R 80 0 R 81 0 R 82 0 R 83 0 R 84 0 R 85 0 R 86 0 R 87 0 R 88 0 R 89 0 R 90 0 R 91 0 R 92 0 R 93 0 R 94 0 R 95 0 R 96 0 R 97 0 R 98 0 R 99 0 R 100 0 R 101 0 R]>> endobj 103 0 obj -<</Type/StructElem/S/Code/P 105 0 R/K[242]/Pg 246 0 R>> +<</Type/StructElem/S/Code/P 105 0 R/K[297]/Pg 254 0 R>> endobj 104 0 obj -<</Type/StructElem/S/Code/P 105 0 R/K[244]/Pg 246 0 R>> +<</Type/StructElem/S/Code/P 105 0 R/K[299]/Pg 254 0 R>> endobj 105 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[241 103 0 R 243 104 0 R 245 246 247]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[296 103 0 R 298 104 0 R 300 301 302]/Pg 254 0 R>> endobj 106 0 obj -<</Type/StructElem/S/H1/P 214 0 R/T(Style)/K[248]/Pg 246 0 R>> +<</Type/StructElem/S/H1/P 225 0 R/T(Style)/K[303]/Pg 254 0 R>> endobj 107 0 obj -<</Type/StructElem/S/Code/P 109 0 R/K[250]/Pg 246 0 R>> +<</Type/StructElem/S/Code/P 109 0 R/K[305]/Pg 254 0 R>> endobj 108 0 obj -<</Type/StructElem/S/Code/P 109 0 R/K[253 254]/Pg 246 0 R>> +<</Type/StructElem/S/Code/P 109 0 R/K[308 309]/Pg 254 0 R>> endobj 109 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[249 107 0 R 251 252 108 0 R 255 256]/Pg 246 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[304 107 0 R 306 307 108 0 R 310 311]/Pg 254 0 R>> endobj 110 0 obj -<</Type/StructElem/S/Strong/P 111 0 R/K[257]/Pg 246 0 R>> +<</Type/StructElem/S/Strong/P 111 0 R/K[312]/Pg 254 0 R>> endobj 111 0 obj <</Type/StructElem/S/TD/P 115 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[110 0 R]>> endobj 112 0 obj -<</Type/StructElem/S/Strong/P 113 0 R/K[258]/Pg 246 0 R>> +<</Type/StructElem/S/Strong/P 113 0 R/K[313]/Pg 254 0 R>> endobj 113 0 obj <</Type/StructElem/S/TD/P 115 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[112 0 R]>> @@ -347,565 +347,614 @@ endobj <</Type/StructElem/S/TR/P 142 0 R/K[111 0 R 113 0 R 114 0 R]>> endobj 116 0 obj -<</Type/StructElem/S/TD/P 119 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[259]/Pg 246 0 R>> +<</Type/StructElem/S/TD/P 119 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[314]/Pg 254 0 R>> endobj 117 0 obj -<</Type/StructElem/S/TD/P 119 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[260]/Pg 246 0 R>> +<</Type/StructElem/S/TD/P 119 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[315]/Pg 254 0 R>> endobj 118 0 obj -<</Type/StructElem/S/TD/P 119 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[261]/Pg 246 0 R>> +<</Type/StructElem/S/TD/P 119 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[316 317 318]/Pg 254 0 R>> endobj 119 0 obj <</Type/StructElem/S/TR/P 142 0 R/K[116 0 R 117 0 R 118 0 R]>> endobj 120 0 obj -<</Type/StructElem/S/TD/P 123 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[262]/Pg 246 0 R>> +<</Type/StructElem/S/TD/P 123 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[319]/Pg 254 0 R>> endobj 121 0 obj -<</Type/StructElem/S/TD/P 123 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[263]/Pg 246 0 R>> +<</Type/StructElem/S/TD/P 123 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[320]/Pg 254 0 R>> endobj 122 0 obj -<</Type/StructElem/S/TD/P 123 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[264]/Pg 246 0 R>> +<</Type/StructElem/S/TD/P 123 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[321]/Pg 254 0 R>> endobj 123 0 obj <</Type/StructElem/S/TR/P 142 0 R/K[120 0 R 121 0 R 122 0 R]>> endobj 124 0 obj -<</Type/StructElem/S/TD/P 127 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[265]/Pg 246 0 R>> +<</Type/StructElem/S/TD/P 127 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[322]/Pg 254 0 R>> endobj 125 0 obj -<</Type/StructElem/S/TD/P 127 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[266]/Pg 246 0 R>> +<</Type/StructElem/S/TD/P 127 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[323]/Pg 254 0 R>> endobj 126 0 obj -<</Type/StructElem/S/TD/P 127 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[267]/Pg 246 0 R>> +<</Type/StructElem/S/TD/P 127 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[324]/Pg 254 0 R>> endobj 127 0 obj <</Type/StructElem/S/TR/P 142 0 R/K[124 0 R 125 0 R 126 0 R]>> endobj 128 0 obj -<</Type/StructElem/S/TD/P 131 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[268]/Pg 246 0 R>> +<</Type/StructElem/S/TD/P 131 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[325]/Pg 254 0 R>> endobj 129 0 obj -<</Type/StructElem/S/TD/P 131 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[269]/Pg 246 0 R>> +<</Type/StructElem/S/TD/P 131 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[326]/Pg 254 0 R>> endobj 130 0 obj -<</Type/StructElem/S/TD/P 131 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[270]/Pg 246 0 R>> +<</Type/StructElem/S/TD/P 131 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[327 328]/Pg 254 0 R>> endobj 131 0 obj <</Type/StructElem/S/TR/P 142 0 R/K[128 0 R 129 0 R 130 0 R]>> endobj 132 0 obj -<</Type/StructElem/S/Strong/P 133 0 R/K[271]/Pg 246 0 R>> +<</Type/StructElem/S/Strong/P 133 0 R/K[0]/Pg 255 0 R>> endobj 133 0 obj <</Type/StructElem/S/TD/P 137 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[132 0 R]>> endobj 134 0 obj -<</Type/StructElem/S/Strong/P 135 0 R/K[272]/Pg 246 0 R>> +<</Type/StructElem/S/Strong/P 135 0 R/K[1]/Pg 255 0 R>> endobj 135 0 obj <</Type/StructElem/S/TD/P 137 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[134 0 R]>> endobj 136 0 obj -<</Type/StructElem/S/TD/P 137 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[273 274]/Pg 246 0 R>> +<</Type/StructElem/S/TD/P 137 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[2 3 4]/Pg 255 0 R>> endobj 137 0 obj <</Type/StructElem/S/TR/P 142 0 R/K[133 0 R 135 0 R 136 0 R]>> endobj 138 0 obj -<</Type/StructElem/S/TD/P 141 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[275]/Pg 246 0 R>> +<</Type/StructElem/S/TD/P 141 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[5]/Pg 255 0 R>> endobj 139 0 obj -<</Type/StructElem/S/TD/P 141 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[276]/Pg 246 0 R>> +<</Type/StructElem/S/TD/P 141 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[6]/Pg 255 0 R>> endobj 140 0 obj -<</Type/StructElem/S/TD/P 141 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[277]/Pg 246 0 R>> +<</Type/StructElem/S/TD/P 141 0 R/A[<</O/Table/Headers[]>><</O/Layout/BorderStyle/Solid>>]/K[7]/Pg 255 0 R>> endobj 141 0 obj <</Type/StructElem/S/TR/P 142 0 R/K[138 0 R 139 0 R 140 0 R]>> endobj 142 0 obj -<</Type/StructElem/S/Table/P 214 0 R/A[<</O/Layout/BorderColor[0 0 0]/BorderThickness 0.4>>]/K[115 0 R 119 0 R 123 0 R 127 0 R 131 0 R 137 0 R 141 0 R]>> +<</Type/StructElem/S/Table/P 225 0 R/A[<</O/Layout/BorderColor[0 0 0]/BorderThickness 0.4>>]/K[115 0 R 119 0 R 123 0 R 127 0 R 131 0 R 137 0 R 141 0 R]>> endobj 143 0 obj -<</Type/StructElem/S/H1/P 214 0 R/T(Testing: the old program is the oracle)/K[0]/Pg 247 0 R>> +<</Type/StructElem/S/H1/P 225 0 R/T(Testing: the old program is the oracle)/K[8]/Pg 255 0 R>> endobj 144 0 obj -<</Type/StructElem/S/Code/P 152 0 R/K[2]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 152 0 R/K[10]/Pg 255 0 R>> endobj 145 0 obj -<</Type/StructElem/S/Code/P 152 0 R/K[4]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 152 0 R/K[12]/Pg 255 0 R>> endobj 146 0 obj -<</Type/StructElem/S/Strong/P 152 0 R/K[7]/Pg 247 0 R>> +<</Type/StructElem/S/Strong/P 152 0 R/K[15]/Pg 255 0 R>> endobj 147 0 obj -<</Type/StructElem/S/Code/P 152 0 R/K[11]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 152 0 R/K[19]/Pg 255 0 R>> endobj 148 0 obj -<</Type/StructElem/S/Code/P 152 0 R/K[14]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 152 0 R/K[22]/Pg 255 0 R>> endobj 149 0 obj -<</Type/StructElem/S/Code/P 152 0 R/K[18]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 152 0 R/K[26]/Pg 255 0 R>> endobj 150 0 obj -<</Type/StructElem/S/Code/P 152 0 R/K[20]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 152 0 R/K[28]/Pg 255 0 R>> endobj 151 0 obj -<</Type/StructElem/S/Code/P 152 0 R/K[22]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 152 0 R/K[30]/Pg 255 0 R>> endobj 152 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[1 144 0 R 3 145 0 R 5 6 146 0 R 8 9 10 147 0 R 12 13 148 0 R 15 16 17 149 0 R 19 150 0 R 21 151 0 R 23]/Pg 247 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[9 144 0 R 11 145 0 R 13 14 146 0 R 16 17 18 147 0 R 20 21 148 0 R 23 24 25 149 0 R 27 150 0 R 29 151 0 R 31]/Pg 255 0 R>> endobj 153 0 obj -<</Type/StructElem/S/Code/P 154 0 R/K[25]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 154 0 R/K[33]/Pg 255 0 R>> endobj 154 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[24 153 0 R 26 27 28 29 30]/Pg 247 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[32 153 0 R 34 35 36 37 38]/Pg 255 0 R>> endobj 155 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[31 32]/Pg 247 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[39 40]/Pg 255 0 R>> endobj 156 0 obj -<</Type/StructElem/S/H1/P 214 0 R/T(Build)/K[33]/Pg 247 0 R>> +<</Type/StructElem/S/H1/P 225 0 R/T(Build)/K[41]/Pg 255 0 R>> endobj 157 0 obj -<</Type/StructElem/S/Code/P 164 0 R/K[34]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 164 0 R/K[42]/Pg 255 0 R>> endobj 158 0 obj -<</Type/StructElem/S/Code/P 164 0 R/K[36]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 164 0 R/K[44]/Pg 255 0 R>> endobj 159 0 obj -<</Type/StructElem/S/Code/P 164 0 R/K[38]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 164 0 R/K[46]/Pg 255 0 R>> endobj 160 0 obj -<</Type/StructElem/S/Code/P 164 0 R/K[40]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 164 0 R/K[48]/Pg 255 0 R>> endobj 161 0 obj -<</Type/StructElem/S/Code/P 164 0 R/K[44]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 164 0 R/K[52 53]/Pg 255 0 R>> endobj 162 0 obj -<</Type/StructElem/S/Code/P 164 0 R/K[46]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 164 0 R/K[55]/Pg 255 0 R>> endobj 163 0 obj -<</Type/StructElem/S/Code/P 164 0 R/K[49]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 164 0 R/K[57]/Pg 255 0 R>> endobj 164 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[157 0 R 35 158 0 R 37 159 0 R 39 160 0 R 41 42 43 161 0 R 45 162 0 R 47 48 163 0 R 50 51 52]/Pg 247 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[157 0 R 43 158 0 R 45 159 0 R 47 160 0 R 49 50 51 161 0 R 54 162 0 R 56 163 0 R 58 59 60]/Pg 255 0 R>> endobj 165 0 obj -<</Type/StructElem/S/H1/P 214 0 R/T(What is deliberately absent)/K[53]/Pg 247 0 R>> +<</Type/StructElem/S/H1/P 225 0 R/T(What is deliberately absent)/K[61]/Pg 255 0 R>> endobj 166 0 obj -<</Type/StructElem/S/Strong/P 169 0 R/K[55]/Pg 247 0 R>> +<</Type/StructElem/S/Strong/P 169 0 R/K[63]/Pg 255 0 R>> endobj 167 0 obj -<</Type/StructElem/S/Code/P 169 0 R/K[60]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 169 0 R/K[68]/Pg 255 0 R>> endobj 168 0 obj -<</Type/StructElem/S/Code/P 169 0 R/K[62]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 169 0 R/K[70]/Pg 255 0 R>> endobj 169 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[54 166 0 R 56 57 58 59 167 0 R 61 168 0 R 63]/Pg 247 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[62 166 0 R 64 65 66 67 167 0 R 69 168 0 R 71]/Pg 255 0 R>> endobj 170 0 obj -<</Type/StructElem/S/H1/P 214 0 R/T(Appendix A: feature parity checklist)/K[64]/Pg 247 0 R>> +<</Type/StructElem/S/H1/P 225 0 R/T(Appendix A: feature parity checklist)/K[72]/Pg 255 0 R>> endobj 171 0 obj -<</Type/StructElem/S/Code/P 172 0 R/K[66 67]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 172 0 R/K[74 75]/Pg 255 0 R>> endobj 172 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[65 171 0 R 68 69]/Pg 247 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[73 171 0 R 76 77]/Pg 255 0 R>> endobj 173 0 obj -<</Type/StructElem/S/Strong/P 178 0 R/K[70]/Pg 247 0 R>> +<</Type/StructElem/S/Strong/P 178 0 R/K[78]/Pg 255 0 R>> endobj 174 0 obj -<</Type/StructElem/S/Code/P 178 0 R/K[77]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 178 0 R/K[85]/Pg 255 0 R>> endobj 175 0 obj -<</Type/StructElem/S/Code/P 178 0 R/K[81]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 178 0 R/K[89]/Pg 255 0 R>> endobj 176 0 obj -<</Type/StructElem/S/Code/P 178 0 R/K[83]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 178 0 R/K[91]/Pg 255 0 R>> endobj 177 0 obj -<</Type/StructElem/S/Code/P 178 0 R/K[86]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 178 0 R/K[94]/Pg 255 0 R>> endobj 178 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[173 0 R 71 72 73 74 75 76 174 0 R 78 79 80 175 0 R 82 176 0 R 84 85 177 0 R 87]/Pg 247 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[173 0 R 79 80 81 82 83 84 174 0 R 86 87 88 175 0 R 90 176 0 R 92 93 177 0 R 95]/Pg 255 0 R>> endobj 179 0 obj -<</Type/StructElem/S/Strong/P 184 0 R/K[88]/Pg 247 0 R>> +<</Type/StructElem/S/Strong/P 186 0 R/K[0]/Pg 256 0 R>> endobj 180 0 obj -<</Type/StructElem/S/Code/P 184 0 R/K[92]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 186 0 R/K[4]/Pg 256 0 R>> endobj 181 0 obj -<</Type/StructElem/S/Code/P 184 0 R/K[95]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 186 0 R/K[7]/Pg 256 0 R>> endobj 182 0 obj -<</Type/StructElem/S/Code/P 184 0 R/K[97]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 186 0 R/K[9]/Pg 256 0 R>> endobj 183 0 obj -<</Type/StructElem/S/Code/P 184 0 R/K[100]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 186 0 R/K[14]/Pg 256 0 R>> endobj 184 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[179 0 R 89 90 91 180 0 R 93 94 181 0 R 96 182 0 R 98 99 183 0 R 101 102<</Type/MCR/MCID 0/Pg 248 0 R>><</Type/MCR/MCID 1/Pg 248 0 R>>]/Pg 247 0 R>> +<</Type/StructElem/S/Code/P 186 0 R/K[18]/Pg 256 0 R>> endobj 185 0 obj -<</Type/StructElem/S/Strong/P 195 0 R/K[2]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 186 0 R/K[20]/Pg 256 0 R>> endobj 186 0 obj -<</Type/StructElem/S/Code/P 195 0 R/K[4]/Pg 248 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[179 0 R 1 2 3 180 0 R 5 6 181 0 R 8 182 0 R 10 11 12 13 183 0 R 15 16 17 184 0 R 19 185 0 R 21 22 23 24]/Pg 256 0 R>> endobj 187 0 obj -<</Type/StructElem/S/Code/P 195 0 R/K[6]/Pg 248 0 R>> +<</Type/StructElem/S/Strong/P 197 0 R/K[25]/Pg 256 0 R>> endobj 188 0 obj -<</Type/StructElem/S/Code/P 195 0 R/K[8]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 197 0 R/K[27]/Pg 256 0 R>> endobj 189 0 obj -<</Type/StructElem/S/Code/P 195 0 R/K[11]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 197 0 R/K[29]/Pg 256 0 R>> endobj 190 0 obj -<</Type/StructElem/S/Code/P 195 0 R/K[13]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 197 0 R/K[31]/Pg 256 0 R>> endobj 191 0 obj -<</Type/StructElem/S/Code/P 195 0 R/K[15]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 197 0 R/K[34]/Pg 256 0 R>> endobj 192 0 obj -<</Type/StructElem/S/Code/P 195 0 R/K[17]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 197 0 R/K[36]/Pg 256 0 R>> endobj 193 0 obj -<</Type/StructElem/S/Code/P 195 0 R/K[21]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 197 0 R/K[38]/Pg 256 0 R>> endobj 194 0 obj -<</Type/StructElem/S/Code/P 195 0 R/K[24]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 197 0 R/K[40]/Pg 256 0 R>> endobj 195 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[185 0 R 3 186 0 R 5 187 0 R 7 188 0 R 9 10 189 0 R 12 190 0 R 14 191 0 R 16 192 0 R 18 19 20 193 0 R 22 23 194 0 R 25 26]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 197 0 R/K[44]/Pg 256 0 R>> endobj 196 0 obj -<</Type/StructElem/S/Strong/P 201 0 R/K[27]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 197 0 R/K[47]/Pg 256 0 R>> endobj 197 0 obj -<</Type/StructElem/S/Code/P 201 0 R/K[29]/Pg 248 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[187 0 R 26 188 0 R 28 189 0 R 30 190 0 R 32 33 191 0 R 35 192 0 R 37 193 0 R 39 194 0 R 41 42 43 195 0 R 45 46 196 0 R 48 49]/Pg 256 0 R>> endobj 198 0 obj -<</Type/StructElem/S/Code/P 201 0 R/K[31]/Pg 248 0 R>> +<</Type/StructElem/S/Strong/P 204 0 R/K[50]/Pg 256 0 R>> endobj 199 0 obj -<</Type/StructElem/S/Code/P 201 0 R/K[33]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 204 0 R/K[52]/Pg 256 0 R>> endobj 200 0 obj -<</Type/StructElem/S/Code/P 201 0 R/K[35 36 37]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 204 0 R/K[54]/Pg 256 0 R>> endobj 201 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[196 0 R 28 197 0 R 30 198 0 R 32 199 0 R 34 200 0 R 38]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 204 0 R/K[56]/Pg 256 0 R>> endobj 202 0 obj -<</Type/StructElem/S/Strong/P 204 0 R/K[39]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 204 0 R/K[58 59]/Pg 256 0 R>> endobj 203 0 obj -<</Type/StructElem/S/Code/P 204 0 R/K[42]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 204 0 R/K[61]/Pg 256 0 R>> endobj 204 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[202 0 R 40 41 203 0 R 43 44 45 46 47]/Pg 248 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[198 0 R 51 199 0 R 53 200 0 R 55 201 0 R 57 202 0 R 60 203 0 R 62 63]/Pg 256 0 R>> endobj 205 0 obj -<</Type/StructElem/S/Strong/P 206 0 R/K[48]/Pg 248 0 R>> +<</Type/StructElem/S/Strong/P 207 0 R/K[64]/Pg 256 0 R>> endobj 206 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[205 0 R 49 50]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 207 0 R/K[67]/Pg 256 0 R>> endobj 207 0 obj -<</Type/StructElem/S/Strong/P 208 0 R/K[51]/Pg 248 0 R>> +<</Type/StructElem/S/P/P 225 0 R/K[205 0 R 65 66 206 0 R 68 69 70 71 72]/Pg 256 0 R>> endobj 208 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[207 0 R 52 53 54 55]/Pg 248 0 R>> +<</Type/StructElem/S/Strong/P 218 0 R/K[73]/Pg 256 0 R>> endobj 209 0 obj -<</Type/StructElem/S/Code/P 210 0 R/K[57]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 218 0 R/K[75]/Pg 256 0 R>> endobj 210 0 obj -<</Type/StructElem/S/H1/P 214 0 R/T(middle click, pinch, touch debug overlay, PARDES_TEST PPM capture and)/K[56 209 0 R 58 59 60]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 218 0 R/K[79]/Pg 256 0 R>> endobj 211 0 obj -<</Type/StructElem/S/Code/P 213 0 R/K[61]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 218 0 R/K[82]/Pg 256 0 R>> endobj 212 0 obj -<</Type/StructElem/S/Strong/P 213 0 R/K[63]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 218 0 R/K[84]/Pg 256 0 R>> endobj 213 0 obj -<</Type/StructElem/S/P/P 214 0 R/K[211 0 R 62 212 0 R 64 65]/Pg 248 0 R>> +<</Type/StructElem/S/Code/P 218 0 R/K[86]/Pg 256 0 R>> endobj 214 0 obj -<</Type/StructElem/S/Document/P 12 0 R/K[18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R 25 0 R 36 0 R 40 0 R 41 0 R 50 0 R 53 0 R 64 0 R 66 0 R 71 0 R 76 0 R 77 0 R 78 0 R 102 0 R 105 0 R 106 0 R 109 0 R 142 0 R 143 0 R 152 0 R 154 0 R 155 0 R 156 0 R 164 0 R 165 0 R 169 0 R 170 0 R 172 0 R 178 0 R 184 0 R 195 0 R 201 0 R 204 0 R 206 0 R 208 0 R 210 0 R 213 0 R]>> +<</Type/StructElem/S/Code/P 218 0 R/K[91]/Pg 256 0 R>> endobj 215 0 obj -[/ICCBased 268 0 R] +<</Type/StructElem/S/Code/P 218 0 R/K[93 94]/Pg 256 0 R>> endobj 216 0 obj -[/ICCBased 269 0 R] +<</Type/StructElem/S/Code/P 218 0 R/K[96]/Pg 256 0 R>> endobj 217 0 obj -[245 0 R/XYZ 62.362206 729.37256 0] +<</Type/StructElem/S/Code/P 218 0 R/K[98]/Pg 256 0 R>> endobj 218 0 obj -[245 0 R/XYZ 62.362206 590.0229 0] +<</Type/StructElem/S/P/P 225 0 R/K[208 0 R 74 209 0 R 76 77 78 210 0 R 80 81 211 0 R 83 212 0 R 85 213 0 R 87 88 89 90 214 0 R 92 215 0 R 95 216 0 R 97 217 0 R 99 100 101]/Pg 256 0 R>> endobj 219 0 obj -[246 0 R/XYZ 62.362206 733.7516 0] +<</Type/StructElem/S/Strong/P 224 0 R/K[102]/Pg 256 0 R>> endobj 220 0 obj -[246 0 R/XYZ 62.362206 342.88815 0] +<</Type/StructElem/S/Code/P 224 0 R/K[108]/Pg 256 0 R>> endobj 221 0 obj -[247 0 R/XYZ 62.362206 789.5276 0] +<</Type/StructElem/S/Code/P 224 0 R/K[111]/Pg 256 0 R>> endobj 222 0 obj -[247 0 R/XYZ 62.362206 546.3959 0] +<</Type/StructElem/S/Code/P 224 0 R/K[4]/Pg 257 0 R>> endobj 223 0 obj -[247 0 R/XYZ 62.362206 426.81766 0] +<</Type/StructElem/S/Strong/P 224 0 R/K[7]/Pg 257 0 R>> endobj 224 0 obj -[247 0 R/XYZ 62.362206 335.20096 0] +<</Type/StructElem/S/P/P 225 0 R/K[219 0 R 103 104 105 106 107 220 0 R 109 110 221 0 R 112 113 114 115 116<</Type/MCR/MCID 0/Pg 257 0 R>><</Type/MCR/MCID 1/Pg 257 0 R>><</Type/MCR/MCID 2/Pg 257 0 R>><</Type/MCR/MCID 3/Pg 257 0 R>>222 0 R<</Type/MCR/MCID 5/Pg 257 0 R>><</Type/MCR/MCID 6/Pg 257 0 R>>223 0 R<</Type/MCR/MCID 8/Pg 257 0 R>><</Type/MCR/MCID 9/Pg 257 0 R>>]/Pg 256 0 R>> endobj 225 0 obj -[248 0 R/XYZ 62.362206 411.61353 0] +<</Type/StructElem/S/Document/P 11 0 R/K[18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R 25 0 R 36 0 R 40 0 R 41 0 R 50 0 R 53 0 R 64 0 R 66 0 R 71 0 R 76 0 R 77 0 R 78 0 R 102 0 R 105 0 R 106 0 R 109 0 R 142 0 R 143 0 R 152 0 R 154 0 R 155 0 R 156 0 R 164 0 R 165 0 R 169 0 R 170 0 R 172 0 R 178 0 R 186 0 R 197 0 R 204 0 R 207 0 R 218 0 R 224 0 R]>> endobj 226 0 obj -<</ProcSet[/PDF/Text/ImageC/ImageB]/ColorSpace<</c0 215 0 R/c1 216 0 R>>/Font<</f0 230 0 R/f1 233 0 R/f2 236 0 R>>>> +[/ICCBased 275 0 R] endobj 227 0 obj -<</ProcSet[/PDF/Text/ImageC/ImageB]/ColorSpace<</c0 215 0 R/c1 216 0 R>>/Font<</f0 233 0 R/f1 236 0 R/f2 230 0 R>>>> +[/ICCBased 276 0 R] endobj 228 0 obj -<</ProcSet[/PDF/Text/ImageC/ImageB]/ColorSpace<</c0 215 0 R>>/Font<</f0 230 0 R/f1 233 0 R/f2 236 0 R/f3 239 0 R>>>> +[253 0 R/XYZ 62.362206 729.37256 0] endobj 229 0 obj -<</ProcSet[/PDF/Text/ImageC/ImageB]/ColorSpace<</c0 215 0 R>>/Font<</f0 233 0 R/f1 230 0 R/f2 236 0 R/f3 242 0 R>>>> +[253 0 R/XYZ 62.362206 576.02637 0] endobj 230 0 obj -<</Type/Font/Subtype/Type0/BaseFont/PNVZNM+NewCM10-Bold-Identity-H/Encoding/Identity-H/DescendantFonts[231 0 R]/ToUnicode 250 0 R>> +[254 0 R/XYZ 62.362206 679.2434 0] endobj 231 0 obj -<</Type/Font/Subtype/CIDFontType0/BaseFont/PNVZNM+NewCM10-Bold/CIDSystemInfo<</Registry(Adobe)/Ordering(Identity)/Supplement 0>>/FontDescriptor 232 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 37 575 38 38 818 39 39 692 40 40 319 41 41 1092 42 42 831 43 44 447 45 45 383]>> +[254 0 R/XYZ 62.362206 288.38 0] endobj 232 0 obj -<</Type/FontDescriptor/FontName/PNVZNM+NewCM10-Bold/Flags 131076/FontBBox[13 -249 1164 750]/ItalicAngle 0/Ascent 806/Descent -194/CapHeight 686/StemV 168.6/CIDSet 249 0 R/FontFile3 251 0 R>> +[255 0 R/XYZ 62.362206 706.6116 0] endobj 233 0 obj -<</Type/Font/Subtype/Type0/BaseFont/IZZBXO+NewCM10-Regular-Identity-H/Encoding/Identity-H/DescendantFonts[234 0 R]/ToUnicode 253 0 R>> +[255 0 R/XYZ 62.362206 463.47986 0] endobj 234 0 obj -<</Type/Font/Subtype/CIDFontType0/BaseFont/IZZBXO+NewCM10-Regular/CIDSystemInfo<</Registry(Adobe)/Ordering(Identity)/Supplement 0>>/FontDescriptor 235 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 278 37 37 722 38 38 686 39 39 528 40 40 681 41 41 528 42 42 361 43 44 389 45 45 556 46 46 764 47 47 625 48 48 500 49 49 778 50 50 444 51 52 750 53 53 778 54 54 278 55 55 583 56 56 500 57 57 750 58 58 500 59 59 750 60 60 306 61 61 736 62 62 785 63 63 708 64 64 917 65 65 556 66 66 500 67 67 611 68 69 500 70 70 778 71 71 750 72 72 722 73 73 1000 74 74 653 75 75 1028 76 76 514]>> +[255 0 R/XYZ 62.362206 343.90167 0] endobj 235 0 obj -<</Type/FontDescriptor/FontName/IZZBXO+NewCM10-Regular/Flags 131076/FontBBox[-41 -260 1009 760]/ItalicAngle 0/Ascent 806/Descent -194/CapHeight 683/StemV 95.4/CIDSet 252 0 R/FontFile3 254 0 R>> +[255 0 R/XYZ 62.362206 252.28497 0] endobj 236 0 obj -<</Type/Font/Subtype/Type0/BaseFont/EFHJNG+DejaVuSansMono/Encoding/Identity-H/DescendantFonts[237 0 R]/ToUnicode 256 0 R>> +<</ProcSet[/PDF/Text/ImageC/ImageB]/ColorSpace<</c0 226 0 R>>/Font<</f0 241 0 R/f1 244 0 R/f2 247 0 R>>>> endobj 237 0 obj -<</Type/Font/Subtype/CIDFontType2/BaseFont/EFHJNG+DejaVuSansMono/CIDSystemInfo<</Registry(Adobe)/Ordering(Identity)/Supplement 0>>/FontDescriptor 238 0 R/DW 0/CIDToGIDMap/Identity/W[0 78 602.0508]>> +<</ProcSet[/PDF/Text/ImageC/ImageB]/ColorSpace<</c0 227 0 R/c1 226 0 R>>/Font<</f0 247 0 R/f1 244 0 R/f2 241 0 R>>>> endobj 238 0 obj -<</Type/FontDescriptor/FontName/EFHJNG+DejaVuSansMono/Flags 131077/FontBBox[0 -235.83984 602.0508 844.2383]/ItalicAngle 0/Ascent 759.7656/Descent -240.23438/CapHeight 759.7656/StemV 95.4/CIDSet 255 0 R/FontFile2 257 0 R>> +<</ProcSet[/PDF/Text/ImageC/ImageB]/ColorSpace<</c0 226 0 R>>/Font<</f0 241 0 R/f1 244 0 R/f2 247 0 R/f3 250 0 R>>>> endobj 239 0 obj -<</Type/Font/Subtype/Type0/BaseFont/HCGNIB+LibertinusSerif-Regular-Identity-H/Encoding/Identity-H/DescendantFonts[240 0 R]/ToUnicode 259 0 R>> +<</ProcSet[/PDF/Text/ImageC/ImageB]/ColorSpace<</c0 226 0 R>>/Font<</f0 241 0 R/f1 244 0 R/f2 247 0 R>>>> endobj 240 0 obj -<</Type/Font/Subtype/CIDFontType0/BaseFont/HCGNIB+LibertinusSerif-Regular/CIDSystemInfo<</Registry(Adobe)/Ordering(Identity)/Supplement 0>>/FontDescriptor 241 0 R/DW 0/W[0 0 500 1 1 527]>> +<</ProcSet[/PDF/Text/ImageC/ImageB]/ColorSpace<</c0 226 0 R>>/Font<</f0 244 0 R/f1 247 0 R/f2 241 0 R>>>> endobj 241 0 obj -<</Type/FontDescriptor/FontName/HCGNIB+LibertinusSerif-Regular/Flags 131078/FontBBox[0 0 500 698]/ItalicAngle 0/Ascent 894/Descent -246/CapHeight 658/StemV 95.4/CIDSet 258 0 R/FontFile3 260 0 R>> +<</Type/Font/Subtype/Type0/BaseFont/ISLOEJ+NewCM10-Bold-Identity-H/Encoding/Identity-H/DescendantFonts[242 0 R]/ToUnicode 259 0 R>> endobj 242 0 obj -<</Type/Font/Subtype/Type0/BaseFont/EFDVLV+DejaVuSansMono-Bold/Encoding/Identity-H/DescendantFonts[243 0 R]/ToUnicode 262 0 R>> +<</Type/Font/Subtype/CIDFontType0/BaseFont/ISLOEJ+NewCM10-Bold/CIDSystemInfo<</Registry(Adobe)/Ordering(Identity)/Supplement 0>>/FontDescriptor 243 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 37 575 38 38 818 39 39 692 40 40 319 41 41 1092 42 42 831 43 44 447]>> endobj 243 0 obj -<</Type/Font/Subtype/CIDFontType2/BaseFont/EFDVLV+DejaVuSansMono-Bold/CIDSystemInfo<</Registry(Adobe)/Ordering(Identity)/Supplement 0>>/FontDescriptor 244 0 R/DW 0/CIDToGIDMap/Identity/W[0 8 602.0508]>> +<</Type/FontDescriptor/FontName/ISLOEJ+NewCM10-Bold/Flags 131076/FontBBox[21 -249 1164 750]/ItalicAngle 0/Ascent 806/Descent -194/CapHeight 686/StemV 168.6/CIDSet 258 0 R/FontFile3 260 0 R>> endobj 244 0 obj -<</Type/FontDescriptor/FontName/EFDVLV+DejaVuSansMono-Bold/Flags 131077/FontBBox[0 -235.83984 602.0508 742.1875]/ItalicAngle 0/Ascent 759.7656/Descent -240.23438/CapHeight 759.7656/StemV 168.6/CIDSet 261 0 R/FontFile2 263 0 R>> +<</Type/Font/Subtype/Type0/BaseFont/VAXLSP+NewCM10-Regular-Identity-H/Encoding/Identity-H/DescendantFonts[245 0 R]/ToUnicode 262 0 R>> endobj 245 0 obj -<</Type/Page/Resources 226 0 R/MediaBox[0 0 595.2756 841.8898]/StructParents 0/Parent 1 0 R/Contents 264 0 R>> +<</Type/Font/Subtype/CIDFontType0/BaseFont/VAXLSP+NewCM10-Regular/CIDSystemInfo<</Registry(Adobe)/Ordering(Identity)/Supplement 0>>/FontDescriptor 246 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 278 37 37 722 38 38 686 39 39 528 40 40 681 41 41 528 42 42 361 43 44 389 45 45 556 46 46 764 47 47 625 48 48 500 49 49 514 50 50 750 51 51 917 52 52 500 53 53 722 54 54 444 55 56 750 57 57 778 58 58 278 59 59 583 60 60 500 61 61 750 62 62 778 63 63 750 64 64 306 65 65 736 66 66 785 67 67 708 68 68 556 69 69 500 70 70 611 71 71 778 72 73 500 74 74 778 75 75 750 76 76 1000 77 77 653 78 78 1028 79 80 750]>> endobj 246 0 obj -<</Type/Page/Resources 227 0 R/MediaBox[0 0 595.2756 841.8898]/StructParents 1/Parent 1 0 R/Contents 265 0 R>> +<</Type/FontDescriptor/FontName/VAXLSP+NewCM10-Regular/Flags 131076/FontBBox[-41 -260 1009 760]/ItalicAngle 0/Ascent 806/Descent -194/CapHeight 683/StemV 95.4/CIDSet 261 0 R/FontFile3 263 0 R>> endobj 247 0 obj -<</Type/Page/Resources 228 0 R/MediaBox[0 0 595.2756 841.8898]/StructParents 2/Parent 1 0 R/Contents 266 0 R>> +<</Type/Font/Subtype/Type0/BaseFont/RIONKM+DejaVuSansMono/Encoding/Identity-H/DescendantFonts[248 0 R]/ToUnicode 265 0 R>> endobj 248 0 obj -<</Type/Page/Resources 229 0 R/MediaBox[0 0 595.2756 841.8898]/StructParents 3/Parent 1 0 R/Contents 267 0 R>> +<</Type/Font/Subtype/CIDFontType2/BaseFont/RIONKM+DejaVuSansMono/CIDSystemInfo<</Registry(Adobe)/Ordering(Identity)/Supplement 0>>/FontDescriptor 249 0 R/DW 0/CIDToGIDMap/Identity/W[0 79 602.0508]>> endobj 249 0 obj +<</Type/FontDescriptor/FontName/RIONKM+DejaVuSansMono/Flags 131077/FontBBox[0 -235.83984 602.0508 844.2383]/ItalicAngle 0/Ascent 759.7656/Descent -240.23438/CapHeight 759.7656/StemV 95.4/CIDSet 264 0 R/FontFile2 266 0 R>> +endobj +250 0 obj +<</Type/Font/Subtype/Type0/BaseFont/HCGNIB+LibertinusSerif-Regular-Identity-H/Encoding/Identity-H/DescendantFonts[251 0 R]/ToUnicode 268 0 R>> +endobj +251 0 obj +<</Type/Font/Subtype/CIDFontType0/BaseFont/HCGNIB+LibertinusSerif-Regular/CIDSystemInfo<</Registry(Adobe)/Ordering(Identity)/Supplement 0>>/FontDescriptor 252 0 R/DW 0/W[0 0 500 1 1 527]>> +endobj +252 0 obj +<</Type/FontDescriptor/FontName/HCGNIB+LibertinusSerif-Regular/Flags 131078/FontBBox[0 0 500 698]/ItalicAngle 0/Ascent 894/Descent -246/CapHeight 658/StemV 95.4/CIDSet 267 0 R/FontFile3 269 0 R>> +endobj +253 0 obj +<</Type/Page/Resources 236 0 R/MediaBox[0 0 595.2756 841.8898]/StructParents 0/Parent 1 0 R/Contents 270 0 R>> +endobj +254 0 obj +<</Type/Page/Resources 237 0 R/MediaBox[0 0 595.2756 841.8898]/StructParents 1/Parent 1 0 R/Contents 271 0 R>> +endobj +255 0 obj +<</Type/Page/Resources 238 0 R/MediaBox[0 0 595.2756 841.8898]/StructParents 2/Parent 1 0 R/Contents 272 0 R>> +endobj +256 0 obj +<</Type/Page/Resources 239 0 R/MediaBox[0 0 595.2756 841.8898]/StructParents 3/Parent 1 0 R/Contents 273 0 R>> +endobj +257 0 obj +<</Type/Page/Resources 240 0 R/MediaBox[0 0 595.2756 841.8898]/StructParents 4/Parent 1 0 R/Contents 274 0 R>> +endobj +258 0 obj <</Length 14/Filter/FlateDecode>> stream -x������� +x�������? endstream endobj -250 0 obj -<</Length 554/Type/CMap/Filter/FlateDecode/WMode 0>> +259 0 obj +<</Length 549/Type/CMap/Filter/FlateDecode/WMode 0>> stream -x�m�ˎ�0E�|E�)�� -�!*W��2�����*��跒=D#P)0*tQ�Q�7 -��=�r�C:����(Ǯc3ثh��t�+��;�6�4�>�gp��L�=���B� +x�m�O��@��|���sp�q4�DQ�'�d��t�HC8��7�+��fj^WU��Uw��x?N7�9�4��w�`K�f/E����)��M�ʬYߣݒZ۔��vS���Ly4�s����Ke �eC�7����U�%M�@�':h6}��(|�|�ۮj̒"���FgM=6�y�xP�n���=�+��8�i��"E�*{Q��o]��������V2����T]oo4q�=��3"oV��̅&�f��C�^yl�B��F��`�-j�@��V������e� ���軈�b�h�ڢd[�{�0�5��<�ף�?�$A��\�)�K�ִ +�Y�vJ9�FP�Ss� �@͠fP)2c��S���)%�q��]��b�g�Jb;ԉ��{���S�8�0LP�Ks(��A�o6�>�G����|i�s��7?�D��8�o&1�Q�T�/}��1�:���`6 +�1\zU`���c�Ε��ԁ1��E��Vr���c*F�)*0����r��k9���wPֲ���s�~���W�6�X�>����IF����s?� endstream endobj -251 0 obj -<</Length 4378/Filter/FlateDecode/Subtype/CIDFontType0C>> +260 0 obj +<</Length 4357/Filter/FlateDecode/Subtype/CIDFontType0C>> stream -x��XiX������F�lL�T�ApBp�I�FE�!jb4N4 +x��XiX������F�lL�T�ApBp�I���&F��(((eZ����4�$3�A@ZQJ�c4�!1כ�I�C5&!<�v�C�}_��r�}�{��>��9{�Zk��[���3 +��iah��c}�gDm�
�˃�!�(�/+�~���ӑ���ރC��!��ؗF��l{Ƹ��0����Pۗ����Ŏ��B��꧇D� ��4|��q�c_��;�/n�#c�"�c�b�6Dm�
������!48&4�-.2$4�-6,�m�{����"c�懯
�� u��vs�
u���4��'6n�����>�"cc|6<^�c�������ϝ�p�ߘ��X�uQ�n!����b�0��`F� +T�R�Cx�~��ɖØ׆���p6���'TNѠ���Ib�D2�w(��-?A;��O�l$e0�x�"�+:h���,x��� ������3��w֍�]����}��&���x��M�v�..4C1����̺HG�KU�ƯnJ�W�PuH���e��r�E@�h +��
�i����:Fa����+�ڪ!~��x��,�=Z��F�F�l�^����ฒc�����%EYՏ���t@��(/��5'ޫ[*����� � +�Tб�s!D+����:��.��H� 8���U�@br䚆�:����U���%-+��H�KP7� +� �."gJ��y� ���PD��4Gf���ӫ����W|�'���!����r��F�c -AA�A�Vh�f7͌�L��eTJ�c4�!��M��$�!�LO�]�{��8�\s��ޟ��9}��Zg�V0}�0 -��q~h��yc|�|�օ���A�+8�� -�/�x�t�];�sȕ�v���}�a�N�g���9�} pp�},t�( -NU3-$jU������ؤaӇ�����X��q��(&j]������n���c���#��cܢCׅDŽ���E��F�ņ����`�"7���X���C#cBݼ���bBC��bc7L����[;:*z�������uO�x�~�����E^sgO����otlb�ۚ�h������u1�ƎQ0����O��H~� -����PU#��3.���S��q+����V��p �Ԙ�Qn�y�/냧h�yA�a�yۀ���/�����-D�_��%�*v�-
D^#H#�S����?,28�䘅����0vJQV�cd(Ӂ�����r͉�/�`��ع�e��tb���y���y�j� - 58�C���}�e�Q�D%n��4�a�AAaaAA�a�����"m��1MUa6H�!)�?w�Fe�թ�Q�=-=�Ps�Ϭa�1m -�x���N�!#�x�����+���c,���� �����a
��9���
I�"a:�4I�m��5�h�}�S�l��7�����Kǝ����Y@%����K����A�[��#*=H�%)�o\�<��h�.lqIn@@�6�[@ǖ�ӎ��0m�N^��|�D������>t.@=��ĵ -[�@Ϸ��u��UU��D$�C�r]��P'S�w��>h�G���7.%��M(���@�$ -X}�� -�)g���=��b(�R(�V��2�[��w�
���~F�7�CU����"͓=y����{�Wzz�UYX����ߘ �`�U�d����J�Wʍ���ޝ�u���P� -�w���I�;�I���4�2T� s[�#�q���C-���-�v�U��" *��b��!��� ������]K��EX�lc`�=oK(���WD�ԙa��,z�,����w'������$��?/��$)\�����o�3��P�k-D��_O�Ԟ��O^��Ć� B`�Y���<���G��4���`s7eo�{!�0wA_y1��,~at�|��-5g�X*�*a$��)�!�k�n� -���&hg����������ְ�[����_�G���w�������V���� ��Pn6���fS����@.��Oս-�w{3%����E���_�)+'[����ii#�E���>*�²Ș����C�=����lWy~Z�w����m�\�� -u���1��7�.�}۸��(��az�v3�g�� -m7�d^E+,��K�
V�#s���l�[Ӳ���� ���1U�%�����B^���<�}U�Ẃ�3m֧%B" �O�>P[�,�N���6~�A�tRv��ݚ�5�'�a� C�>�~!��*�Z(���|~����*C��]��;��0�%���#�]v��ٙ��/�A�4k��
�a
$�ŗ���t�C��K
?�5��P�����%O�@�.�"%�A`�N�E�d\�.�lIQA�e ��
�P��*���]@��� -��$6t|�����ϭ(!�T���J -3�� -K{Ŭ��������^�����b��K���2�N���z�5@��#V��i��3��w �Ml��_�?FUPa���k��TIBg�C>����q�@=s�d�gg� -�X�Q͓�-���8;�(�&���\R\b|R�H�g:t�;
����KbG-G�˕g� z�,������a���J��h>?� p]X��}���PQj��QŜ��I��dk�����n�VT�[�ˊ��GQ���솮�#� -�5��q�:�q����r��l�۹G:i��࠱*��ڌ�
���WE56�n8����}s�aR�[��Ҕ�b� -��m&L�Й~3
��lJ*� �$��xU
�⨎+V|�s�U]{k*G��Tf��%tUʧ������@q3�+È�@��#�rPwX4�`UŪ�X"��� -3|�Dxt6g/o�H1���\���Tԡߍ�WD8��٬�?zF�M�2�D��re��ʠ0{gn~[�Ǧ���p�\)�n��*���Ĕa�]�T-V����҈Q�0k�(<Џ��G���;w`� -�p��=���!P갼���gP"�=W�,�ӪP�b��FO�'q��Sx�m���i�e�Rdd�/������_� b��s��p8�;��oj��7B�>X�s�βI�Z��>��&h�.ҹxs �� RAOT�1���ᜤ���;��'ӱt&w�.���F���< �B��~��~[�x��ݣJ�|J�a'���Y���E��|�n��@�hB�{�i�\����{u3�v�9�23��1FD����r��C��H�|$���"-�f�Md���ޞ�lA�"�����kej��kk2��{��pߦ�*�K�o��z�i'mX���ME�9YL�m���NR��0^߳�Sݙ�/�{8mA�K/�.:6�3�`�n*Xͦ* ��j�p#�ܮ��dJK'��c��U6͢^t8
���E�c��E��ת�d:����jr�9 F��i0������ಏqڡr�N�r�h�%
�!xMR2�>�;�΅�@�먝��%��0�H~���wcC�@���C��};y�ˉ�[���0�nB5���t�R:��Q%ټ932@_ḣRh9TV�>�]�s�1Q�T��P�'J������^��9�g� + +j +koojji����T4E�Y ������U�����צ@2D��Դ$Bͽ?����ԩ@�u:X�8
�D�ak����V�9�9@�6[�C;��mh2z
�44��zV��,���␄i8S%e�I��ģ���O
����@ �z�Sow҂J8g�p�FRo���<�oP��� a����v�f�t2�Dte�Krr�����:��LEi�`ڵ���=�V +���! +��"�r����F��J�X�?|��KK�ݼ� +X{�� +�)g�����lQt1CX +�NB��)�ʽ +5�2T� sK�#�aف����:��RV������Eހ#'4����Bw/k�}a��́ w},�0�]aSg�.�������:��Nx�Y���IV%Z�3XR�7��ߔfZ�� +�:�Q��*$)��W>!r�i�3"�)����*`�s~��=v%�2b�,�&s�%?���Hc̞��ĐeҚs�5����3#�@6����o7C��ܪ�Y�袔��AS�
v�I�aJ߾y���e@�Ы�4�+j�MPf,6&m1��N��aݤ$g�=��З��1���HrE�?V�����bM3b���%�ߙ�ׂ�h�0��4�r�xږgd�!8s�����U�� +B�ճk������� +���:WHCE���2cu<��w3������T��+SqDF9d�=��{�|����%�w
6��~ٮ'��O�����t6�>5�+4ҷ��.�__�dƛƽ'Ea�禷�n7�~k��q�L�U��r�%�dU�2w�Y)ϕ{4���2�2��=�龠����Kԩ�G�/���ِ
�����DH$AM�����DՉ>�`��Ưu�|��ʞ�r��}��)b,J�mnZq@���J�i����lA�'�Ѝ� +w=u8����o]�d>�Y�K ���ѵ�-)�ϽD�7�a3�ۀ]c^*����Y����тNOغ�����E�l~Ji��$�.�������@�p 〉���O���+y z){>��4���D
]!��x�a�39\�#���R/��"eIĪ���oYh��;��]�o;����͆1DT/�C���)E��Ex ��yQC��X�^�۲�2�MD��0��J�|Y\��Y�sʀ�.).1>)n��IN�NCN��}I����h����lSA��B&4�9�>=E��zͧ�gnZ�/�C�*�,�T���$>ɐn4���(ܑ���MԊ� };v[qE��(��]�p��P��`
�y^�Nb,����sw�NZ?�r8`��%��>=il$�5Q--{j��b�s_�H�E���F��4�˘+@P���8H+<�C25�0�S\ +�ZRK�4�U('�rR:%އi^ZU��� �Ϡ������r Fkt�1a�[���i��⤖��G�Ѕܷ$�O��oԝ:y/��ѭ{Z���p<�tkVݞ�B+܄��0
�Cg��"4���S� $I��h�G?�b��X�
7�rL'nBe�M��Bw�|Jv��$7�[Ag\F�������@= ` +����5
�D�Ufx���l�Z���8#�e�������"���O�X���0 +lz�n&¯U�s��PY�s�:b>4��S�� +Hэ[K��Ve��)ݴ$�,�V�-�أ��5�`�xQ�����˷oÜUZ�� d{�4�Cޣ��qEGǹO�DD�YX�U�$<b�2I�^8O�Pʣ��ǀ5�����|"��T\<�����^�g< �pҷ���xC���#E�e8��*���_iR��"��Gp W "����D�7#?��Iʞ�}�C�2Gg���`�[m� +X��B-���W�g践����=���'�~B�]��Y]%��w���f>���&4����ɵ�~��O7�mG�� .##�c4A�h��.g��qh3)�O�dsRf�%��D��<�����O(J/�J��O�A�f +w1����L��42�v��9峁�L�MG�`��tfj�md�y��NE�RV�M�1��}�>������\�!�E;T��m6C6B2�KJ&ԗ�����ԥ��I�]���s*��X +�66<P��h� endstream endobj -252 0 obj -<</Length 13/Filter/FlateDecode>> +261 0 obj +<</Length 12/Filter/FlateDecode>> stream -x��� -~ +x��� +w endstream endobj -253 0 obj -<</Length 692/Type/CMap/Filter/FlateDecode/WMode 0>> +262 0 obj +<</Length 705/Type/CMap/Filter/FlateDecode/WMode 0>> stream -x�m��n�@E������d��_��!�%y(D3kb��ж�X����[�h4�nW��*SI�no�u��7���7ک��f���4]��td?>3�\���=u}[
<�j���fL�t�ϩ�s��R�����P�V�0��$Mߛ��� -=Ѧf?6��u������ߓN�����
�Lj��o�-��o|�K%�um�n�QT�[w]|x{F>n��%��z�$��h�Ƈf�]Ec�T�������:���N]���$�xʾ�߳ -Lŀ�>@�/�/S3`4��Ft�c~��g.�����g��p:�N��p��3CG8�����9"ӂ���g�NZpfpf���h�����h�*����Y0Z8�`�$F#7�с��,ŏ0bBV�YaD́��mq�[~�'�D'����}E�8-�fi�^pfp�d���F߉�#�38w`t˸Xd��6�e�US߳�"�,l���e#wm��������z)��~�� +x�m�Io�@���=K�!Co�BH,��!�B4s&ږ��~�� +�FsH��*W���鏷���5��`~J�N}3v=���m����O�"G��g�횪�A������$M���]r����c�o ��X��М�4���/��;<q&�u�z8y���/����3��4}�nݜ�p}2�b��5Վq��븓�}�����U�]��m|yw�:m��Ynl9S!&�t���;��8ؽpt@�s���(�.�~�ƶ��0���)y�N���Db��קL)���ǹ%.0��ܸ�P�j����n̥�r!�eY�������k���Ͼ��j!�R�v��ʠLT����M�2�*�z�*����j +UB-�AC��x�52�6�q����k�8��1�** �Aw>�� +|z>�� +|� +|Wa>��)�܁���r8��g�P̷�b>���,�;�425�r�j�؊�YA�/ϯ[�`�ؓfF���1�n@�3�jഘP���#����S�
N�1pfpD��r?pZp�#2
8-�3��`�i�aF|�-Wc�U����0�1�\%��ܨV7_�uF��0#ǘ.f�o�w�y,3l�2#>��9fo�-N�u��q]����[p�Y���I����Ք' +�z����� endstream endobj -254 0 obj -<</Length 10538/Filter/FlateDecode/Subtype/CIDFontType0C>> +263 0 obj +<</Length 11216/Filter/FlateDecode/Subtype/CIDFontType0C>> stream -x�}zwt���Ȳ��,D �� �� @0�w�c��
���c�]ҕ�^q�E�0� -�ߍ�:H�� uRnD�ж�:��w�;��]�T�QXZ��֝Al��p�EW���-��Q5�(�ѫW�LZ���sL`P���7��������<p����C=���������p�������?�#�G����#<`�O�G���DŽ�S?�3~�G�~�>>~aaA�_
�u@x�������>�>�V���z��[��2>0 l\��OP�`���X�а���MO��}ϱ�z�3{�Z���?�c��w�C<}=&L��O���l��������x��y�yx�� �����p��X�?��m��70 ,�?!���H�6
�t�ҘB��^��\��h���
Z4w��Z��Ւa��hq�yE�-ݚn���Z�T!�L�M��`�MՉ�l�tw2M��SrH�������]_��ΨǪwr��uh֧s����ۓ��Zx���2�U�G#Z����e��/�*���|�f����g��mg�}���v�j����'_v����q~�N��;]� W���Z�):���Ӭ._u���^7}�f�O���㗞#{�սח�N����d��}��4���a�<�<�V�*�4vUnܨ�&Qg�$N��PjpH�KiZ�{�-����5ta�Y�����i���9��0�����#J��Kd
�s�#q�"���Y�e1�M_F������}���$!W��PF���B��쎮��`�߲�����p��U,�Y"��|�(�5�+�N�[�g���6�?���QN�v���P��м�"���&_@ws���B�M�����֒ o�S��}O[ҍ��b"������YW���z�F��e�v���gˌ+�>+q�~hq��Ci�Icu��6�R�bM�9�O�b��իXYY�������x$�85��1��y�=$����g�H�� �����0���,��A��w�NA��k.� J7�#��G|�jb�eH�~�HZ+R�C�Oې(ɐlA{1��dvD|�X��E��]�j$��D�3x!���/�ۊ�cY��\�$$c��y8��Sp`MŊ|�{���� -�z�D=p�W9�}��x�v8 -����H��s���C�7�ӀD�*�C���9��B�Ѩ.@D��c����U���Bc(�O�S:m�cK��&�(88x�_HA`��a+���S��(�Jݷ��mf]�ێ��Oh�q�B�jX�k_�K�7/�^0�
+��-�6������gy�?N��G���A|>:�ʏ/�O��O�;�v@�4���=N$Ë����+��M�����YF?C��{�>رK�����m����p�܀�vّ�n���B�!���nH���� ����a#�1?6�$;������/b�!F�E���.���`,H�X�!6�E"@|:ɔ�H�/�H��dK~~q�6@2ye$ q+2"ֶ�/�����ԥ�M�x��4b�)�ڛ�S^�����EMپ��rw�r��\��c��%��a���9<�Ri������gⳝ�YDd%���<Q�sep�VE��1x7)��%I��
"��i��_<y��V�?����)X -�xm�3��y�I��c�}PWLyf���MBҼU$��5좆=iڋ4�LA]�?�SI�_��?�����f������%��b�=��f���E�=�UG�mw��#���
nU�q��W����}����L�c7Ɛ�]��٥���[�v[Im����.����~��3�V� �
����w �v�#����{�@XQ@��L�aJ��A�h����`�DG��x�Z�ֽ���f��}h3ڲ�)� -��dC���s������ne�i���찿I���Y��!�x��ېm�,�ݯ�A� ͼ)!>"��-ѥ�+�kj�T������Y<P�~��2�S�X�a1t�5,/�.�)�vq)��~R��NOQ��d�Ln4�#Ι�;�A��}��Y�Vpڳ_�����yhy�W�����e-��c]䇝��"�]9 7����x��>kWF}��#ܩ��������Q�k/�ŏ��G��� ^y���N�,�C������ ����om+ -�cd� qqmH4O��<���$$��F$�����H����"�<� ��H�0��&q��(*3߽_tY�s10��a�1L������D���oȘ���nҮ9��-�������k/$��C���"u�VH��J�ר�L���P!�;�Dl8D�z"�K��]@|�6�"p)bc�I��r���x⛂KH>ڀ��g -Q -[�Hss�"6���p^^��q�b��vGI5���~a��Qw�na��/�f�%sg�&�%�Bږ�r��t"��i/�KO粉l���e�M�V����k��u��������DC�� -J�R,) -[B��>��HZ�1!�'w�4;����O{7�TH\n}�(����s=bC˳��H��#q9�@��1�����K��پ�H�u�D7q�nwo����4��A4L���!}�l�� �qi��J���w/�.,O��S�v��ԝ�\�ޗ��Km:joJ��)��!^w�o@|˪��<�f+�mp�X�c�А�K�3�Ν�xоf���9oC��^��>��X&� "M�^��1LKV̊;������^�xxٔY����/Y�ѸT�Nڊ�;8�4%7)�ˉ��g. ���~%~��촻o�M�?�T��h�AG�wQuaz�-^L -�/�,:�����,����M���*SB D�A�U�⢪�#v�f��L������hGA_��?_+�I��ZP�m[y�6������kʝM�WyN{����[�a��e,�y�7,3�ȫ;�c�%J۸�í����.�5G��W�\���u��K�����Tݖu5�}����^�g�#�L8�d��S�K��I�3��Z[H{� -:�LI]�\]�u$ӿ;�$x҄�qg��n
���� ?#+�O�`D���K�77 �{�bcsO0�)��LA�y�ziH)��ʍpu���v��RK�d�����������PK -l���x3;c���U�� �,��#�jUOՔ<�mi۞O���x�OF?�z�/��ӏ]t�/��_��� ��D� N$n�=�~O$ng�P��0�%�?�d��`$�7M@2�T*�O_#���ܛ
`��s�Ұ�a�eޫ%v[ -w(Jn*���+Ke�V��f���y9� ��cL -SiɞU�:^jZ"6n�|
F�٪���=��+���&Z�����cϘ�B���+�˼�H������:0���K��Z�NS���>�5U -��>]d���V�RN�uvZ�f�:3k!,�)Ꝺ��m���a���)_�4���^uw�y��X���� z�7n/� U��t �<Ӫ�Ӷ��� G�;L�e?0�A��J=���i�%�4���I'�9 -� -yJI7|X��?�ܺ�"�,&/��,����e��zV�W���Ռ��~�ϲ��â����˶To��|�F�^�0����=�,=v^���q�Jי� -)�5�jJ�Th*�Y��s��-5��V� -� H�}� -C� i&y 2�i$.ܝ�
��XQC? @����^�����%5GG�v�:�Ne�l��F��Vu���/6�b����_�����D� g�Is����s�=��c�bQ���;ܧw�J�:�х�7���f���W����� -nwf�a�C��4�<f�T�����p��6�k9���B�^*Rk·^�Jڭ����+��rh=���E�hUHWuՇ/��8�>�1AcD"�h�D|{����'"4�1">���݅��o�z#q�
ѐbII5�V�n(����]c��.#����a����R�6��(��V�����㩒�Wר�Wg�`���.�
��ώ��<v�ص%�j�ʶ ��
�э{�j�RRэ:V˅g'ggg�� -���r�i������"����Ҹ�����p���=������J��R�2E����Pn��A:W�$�c�H\�mH�#��Zjɚ��5��9IC�ȡ_Л��lMN!�l2����S]_ֆ��#K���z�6���bRR�DM}�����|[��h��>��f�|,8�
0�k`�-TU�����3�u�6j��{�XA���a��S(p��M3�sa.�l2t>��-�_ll�1-ua�hg����!h�������۔R����)%%^��i�s�����\ʊ�l�\j -__�S0��]�YKX]���-}�M�dR��{]���:�k�$�.e�+���ցSB�L.ݴ>9zCXAl^�)����'omhU�^w8|?-�~D{����u?T�?7���m�7�x�w{8�eSB�����4��/�N�<��˴� -������� 켾Z}2�?tv�qt�m��"��{ͻM2�=Ř�M4Ӭ��R �t n�o�2[,X,�a -Xm�x푒δUJ1���#֓|�)-�`L�]��G���2�������[����v�/�/ѡ:��.��WF%�����3i��Rf�8 �y5�(ݕY˼�r����'��vw䮧I�����K��k���>m���!T��>��߯G|[8 Qg��R���M�|�g6��F�o�B:�
���?�ڶ�ymtV�ޥy���Y�E:��f-�%TU��ƨ�mo�����K��!�*C�)��~��pꊡ������yꤋv��~g�r��5���6�Zy�!k*�m��V\��'hNB5=[JwW�S�6���z�$-�
�'%�N�5�a���S��֫/��Z��4��1>��Mz�RM#`ۨ���m����#n1u�bi�./'�T�=��~�:r�؉�E�O�~r���ܝF9}t?��SoL-5t��XEMj��l���E�x�[�`!���gS4��J��|noZ�-���ƫ��hL�ȳ�Ɠl�tXg7YC�g_���׳�|��Ln��g��4F���;��s����>���&�����%d��C:���2�Cz8��,���`�9�uI+g�k�R����s��qT��x^{3>ec����]�ij����;���d��� �[�,�aC�1��wVoD�?0���$�>e�B�C�����Z,`."S���:�����I��7y�~'�k��.�� -��R�V�7���E�'��D���W������K�\c�A�W�Z�������B��^�ĭ��H��|����Y��r�'����I124����\)<���4\7�^�5��j�E�4�B)U�]��l�Ȳ�ȧ4���'A�yHRW���+$*�&����lz�.�g�g�b}���Dֲ �KKd�� B"���ȾIFd������O��ᠢ#a���m_-��PУW��)��56cFN������]��=�U\^J�A����5���$⛴و�K��ىF_��b��T�����z��N�>xq�׃ۥRW�{���r�W�BRH�t]=���v8{�B�����_���]�:��LCM:��.��1�iou��xv,9�o�XVg��*X�a�3�U�eeK��";���|��FG�m��l����'{h�=�c�^qə�<�����-�Tkz�5���RғSm���jCJR��l�+�e$f����[Zjj:�ϙҒS�m&����eu|Jb2xM=슆x&����9�/��
}�c/�� -��y��v�K9�Ȅ�\H��Y����ϖ�^ԓΡ�i/:���K��mu����b -vî&��E�6-����'��Xcr,l��]�b$-zNEd v$-���l�ldc�ʜ��� +x�}zw\���]`wF�5��%���5��k�k,� �JP�����]z�����b�
5j�-�Ʈ�E��z/z��`~��|�����q���ms��|��qr" +��i�!c���ϕ�:����R{�4�� +��� +s�}ֲ#!�;��4�"�;Ӭ+qt +�"j�0j��r�I+<}�|��z����yz�t�r��!}��差�|��|}]g�������su�����v��O��ZO��{�� +�`��k]��=]'̘�U���\��u
��t� +�ڿHHH������֮�����
���_���=��w�]��~�A�B=<��|�|]�����Y�����;�������+\}|]gyz4�0����u´9}�
�s����:��:�^>:O� o� W�5���!>Aޮ�A��+��~^�����
+�G�,�钡U}Gmp�4&��f$�g��~͚$7s�fM��Ef��b1�X,͚]lZZcM�$[�֤f���Bf�G��`���#!d�|�.d���䀢����
� N�8�R�W�V�UUq���oһ s�����{�y4��ܷ��φ�$-u-�[�wY�q��m�E��V�3ڿZ�l��MU�^mO�smw��oۗvp�0�cǎg���.�p��z���N�����X�%��t)��mP�v�����ٽ��^_�Z���ޞ�����gt�����2��&T<Hk:l-u�*��6l�J�hm�$N�O_��K�hR�K�m��� +As �H�9�%�v�W�}����)�w�� ��5�y��/�0x5s��Q]!u+V�ݡ�����Ӯ�?":�!0+8�}[�Q�����&�5�r��b*��ƕ5g»~���?QEa�!9Z�����H���g�uY���h`Q)\*m�{�Ny�Ԩb����R�-�����4V�8n�1!*Ҙ����)��^�B�啡��H�I�_>�FҖS!Q����k YH���E�yA<��̂����g�|6��!�=;��F���(�؋�N������
w� q��F���X����xʊđJ4S�Cj�O�8� ��~H]�����w�\����kħ�6#qI�#��#I��kp���$�[U�,�A]$��\�}-^"Q
X�^�k���_��A��˴솲��
�R$�{ݬH� ��''��EuvIY�B;�v��f�9ͯ[[۹_r���nH��i%��O���*�v� +u��"��g�6lٸ��sp�m��!i!����L� is�+���$H6�ц�h��oȎ�.H��$ q9�5��H�g�л�3�������X��G"�2G|8��GH����P���0;;?s �~y( q�3 V��/�u�5j�C��vѴ�L1p�����V�+����1߉�~���#J\��E�yQ�WZL�j�~��_?.������](Ϙ_T��g�p�z�DA��Yk ��-C�vnR$s<�n��E:��쭻x�ܾ͈�*���.X�xm`m$���n��9�G-{ ��L�=����i��H���&��E5{֨�4�XA�?��H�_��[TegMh����;��bAH0D���J\�c�<�N������M`�p�uv�[aX ��T%�u�ær#o�ȍѧl�Q6�_�l���q��l�tX\v@oO�g�LꮲJ$�!�|��6$]Ot@�n��`���-�Iu��<LN����.Y>x@��V�߲�e/�`-��uoڄ6�{N5���7���������4{���]e� �8�m��)C�,�^�tH���H7���nײ�! ��d� !�axѦ����Ue˅��1/t&�A>?���0dW��at��,+�&�*�pyuim)��T����j���(Ļ�jg nk�h�q�ogZ�i�~[��Ӻ��9_��le�i�՜��u���yy�5��O@20jM���'=k[L�*�C\�������4KQ�SO-�EO��G�8 ^y���O�,�C�3�� +H4K���H�u}R�Hz��J��܍���'��?��R`�8�_e)�>.�,�i
��0�$��[b�nQ����S&6����K�:3(�<F2��@���I;� �տJ]*�W���e�v�?}��)?�E�?@�Ǻ!~Kb��}@|�:��o1bC�I�����x%⻜KH>\��Գ?�I�C�Q&�bȁ�p�Z����#���j��Y�Y�G���U��$�\G|����n�����R(�����x�;��Ț\B|��Z�DBnD!��0$�+��?T�u�� �Q"Q�R Qn���<�#Q��H����H�=ُ�W� +$��/#�jb�\ �Ao@��O=ءĒc�u�2@|�pC�s�⛏������Y�L����ҵ��o�!~�~�(K.�
�^+A��G�P��*����5M+R�4���E�Uj���}Ugw9��.}`Ӽ������� + +�� +0s�t�ҦN��b�������i�C�;�VRj�<�,zK�f�i��OӒ��1b4�_=�b�z�� �"��E$Ƨ)�hNJMɨ>qʡ<$wU�W�j� +��>=��Q�J/ji�WHf��E���uf�K�Qdc�}������PɹG�6�T�p�z�������w�)U"�����iY{�YU�i���W�i���JTG��²G{�].?��/N?u�T�Ar�۴P�P���k�G��gZ1Z��9/oK��B��|�*��]6T\α�qS��D�,���[���C7M9|���+��k���#C²� ��i��~���>Hz��E���� +��+ͼ$��]����/�K�l���6w�!��9Hڸ:#�o[���"��k����Y�x�f)��� ��ۄ_2S9�{0��">�����_�: >�6z�Q���e%AB��8$$����75����:Ćz����I�u��)�����(^"Q4ق�8���+������t�7ׯ q�?�|�E i���j��HZx�C��$-w}�_��5W"q�
�l�B$]}�o"����x#>�x�>b"8N�qep�Z�a��H�R? +��j���i���_Tq�]r�X�˅*.Q��>REk�E��y�O +>6)!)��
�o1�6�;�? J@Ҳ� ��j�R��Od�LVux�����y:���ۛ[۹��^n�N�u�·�B2ee-���ܖ�Z�_&@��j��m{Y�j>�EҺ��"�'
v�!2Gځ�o>�X��78p�R���R�rXK�4�.�Z����۷1�G��bn*և[�j�$踀���rX,�`���Dj��@Կ�]bv�6M}@Sm����Z�[�K�.6o�X���jQ*�*�IG�B�
�٬o��@!��@�MdV����j�}Ć2�3:�5BU���ҍ2A��\�ӹ���\�� �ӳ�-�Hy՟�����9�� a���R�%��d��ֹ㾝�I`�L�\��a� +�?K� �a�E<�{ ��# �����ܝ��#��O�����1��yb�S"��AH�H���r +�w7x�)6RT�OPA;�)��9n�ku���#i[�
G���[6Fd#��X��Vj��s�q_ +���ߨ���Ei?g�H�oT��s���}#sgzQ����һ6G�:�ЮUm`��Ng���Zm�%J�ԴG��j�q�i&̄)f���N�m\'6Ts��Q��@���Ro�Ji��ם*u�������n�ZqDSO�B���8x!� +*��:��������e-��z��/�q�~���_y�.�F|���Ӏt�sB����v@��E�onۛu��S\ +� +WM4Y� +����rkP��wH�ʯ������F�E�:�S�(=yn�:ֻ���cW�+��+�"�F�,Ԭ}p���Q +�P�P'��c
u�fsɶ����W�V�� *$N�k�u�B��poċ7��L��I�Б3"�m�� ��D�����>��]��x��ݱ��������O1z�"4IB�����;�zx= V� �k6":�����:��SvZi�� =��5�^2�f9�J�~��N����l>�ษ�({��>@�@S�[�Y7�|�*��˴f�4����nZ#}D������?��]�S���M,�F;��t��\�Y�p��c.�l����4�ZC9��pnh���(es��Ro9v6^C���5�`����(��<�N�����)6�rs�F.�S���ݰ��l����=�-��&��VZcR�!�������{��Mw:>cAZ���0%�_*�4o��j�,$-�����X�K%�V�٠Y����Ƴ��)�#���c�c��x����}슣���RG�G�-�ξa_�ẽb�4:�mK�gArd�D���z�o#� F*��t=͢ݯQ�H�`@�f��l�7��P�Q\.��H��H~�(�W������`�Z+jl�t���,-$#qr_�Du��S��j��VH�YSP�&;;n�2�7�&(7Y�cDH0M�>r�^ۇ�⩫Y��\z"��/�
l:{ʈ��4Q]l�o�Ɍ�/w��i����v�V=��za�U0�(�����{��Ǻv9`r�]t� }�m���r�*�t��3
L6w��#6�昆:�&�m��ud��d�ʥ�[�z�Wm�QncBB� 'E�� $�~:Ć_�"q�����Tf�a�3/y��Y��2X� q���s�(�p�D7!�Ng�RbS#�f��D��嶸��;g���Fղ��/�jZ��Tl�\�8�JZ��Qj���i:�zP6�`��x6�
`̃d�x��ԍ�zB3i�>����"X&kկ��7��>Dq���V��W(����K�
��<�}B#�!�Sb� ��22����'�<������5�z��;ߜ�+�9��@n]s��o�߷uE�[e�.��d6O����5�c�dD��D�r_ȁ��CD������KH�~�_��g۹�|��V���+�̙ |�.S����rK���$%?��FD<)R���R/v��4L��Ҵ̉�P�{t{�I���6������`�1�6�16(u�9���� >��HOя�#���M��nGt6�� +P���ުܬ�\ +s�T]���6z�;�[��@K��"�GGGG�Ʊ���`�MF����ɩ����|71z��P�-�I���[N���8��"¹�MH�����{J�HGq��m�]Z��R�c�r�v�L�^����:�7�"��3�ַQ"6luC�$��ͬZ$��nq�o8���N$��Ax��@�a��|�N���8a,��17��T$M,؏$�����B����!I_�����y�Ǻ͈�w|)�Ց�w��?y��p�ʷ�e�L-VUS��*�S�J�O�~�N�j���K��!����o&����˾�ZLE��*8L��檂R�S"�'b�_O�~4b%+D���I��z$ݺ�!ik�E�r�"$�r�H:>�D������OJj�\��H;�2��#>�&��<�__�-��4Y>!~k���+6������&;=\L�]�p�n{�̦��&�'�+�/�����<`}v^W�:��o.'�0�Ӊ�b�s9�L;�r.g�!{�h�i�A�+$@�q_���%�H0���d�X7�C�h��|8
�,'�LcR��������������O���cs����/��Z��&��K��_�Щ�3�3h��\lI9�y�(ݗ)���r���A�x5�l�\G��R7�����7W�k�Q?G+�
{?���}�$Di���;R9���T��m��F��ڟ
�s�[pmK�ϼ&<-l��,��,�"��m��l�+TLmP�2��x�y�Oܥ�ȵ� +A�1����`ʲ��{�u��yZKn;.R-��Zd2gK$��!
�`���DHښ�-�\�5JP�� +z���p�����1uIZB�� +.��k��~��{�S]���b�).aCt��ut����zȷRe��;�7��f��E�2mVFZ�@����<e���������Dw���GF1mT_��UlH,�O���&��i�&��\�42#��:�^��uN�<M�2��knF'l�� +҇5�r�m"�~���⁈�GF�5��dw엋x��2�}Y���AdJ�+]��-�)�����r²[��85إ��3! +����4����2��ɱh7���������Ӧ���q��$��Z�̋)d-ǽ����D��s�/�p��|C'����4J��ȩE���C�����+�V���*�՞P)}�P\�ISJ� +Z�-U�X:���eܺ8�!�B�"W��A��n"�X3���Bz�}J�<FDz�H/X�QFzq1"�!b�D6:���D���>o�@�������Rho���
;�r�������N�OOOO*h5�抒����ӃEVM�UEQIQQQ����{�GEE%E�j��vҺ=|ő�J�
M�����Z�zn�1}�x�{wi����"�_%".9��.i�ˢ�<;��:����9J:J��������?�S$u���<�^�Mм��m�EߍaJ���5Ӯ������.�3W'N��<SS�V�sϮ�c�)/^���8���c�������UyNIq�"���P�N��!�v{C���h6[)�B��N�o��(��BTJ|j<O/��9��ʜhIN�$��\Br|b����\�O���(��u����t_�\�bMJLL�9cR|b��h6 +]�!�}�nr���NUA������I��ɼ:��';��%9�F��9ҷ�WX&�I�F�-��-G&� ����ę��@B��>r����>�ׂ̂2��AGY/�G���Ҏp�w&>sm,/`�<�����77�AB�
G�^1 ���?�%�Vn��&lS��2�����Z$9;*n���k��ܼ��C;�H>?�x;�I��刧�r��+�S�r���A�nܸ�g��)"
�Q�ዐ4�1��ؐ4�ZóA�
yJSB\BD��-��yԫ����)�lpذ��1K���ɩ�Ӄ� �s�b]���?N��EB6MGl��D1� endstream endobj -255 0 obj -<</Length 13/Filter/FlateDecode>> +264 0 obj +<</Length 11/Filter/FlateDecode>> stream -x��� -� +x��� endstream endobj -256 0 obj -<</Length 681/Type/CMap/Filter/FlateDecode/WMode 0>> +265 0 obj +<</Length 687/Type/CMap/Filter/FlateDecode/WMode 0>> stream -x�m�MO�0����C$8�Ml'�*іH=�!�v�%q��h������)�� -�9]��m*|R�\b��rN�$'|Z��o.Y�˙��/�O-�ǭh�r���e�E�gȩ�3T��i� �a.�L��e�>�=h��8��j�,�E�>�>r�,�n3#|�4�Yr�,�eR>˔|Vb�Y�i�ˤ:|�����|�Y��t��X�,����<-|��>M=��tf�g=�΄��g�n�}�S�D���n��IL���>ح�Ad�˰dd��u���~+��w��jXf~sՍ���]��S�'�������xB�x +x�m�MO�0����C$8�Ml'�*��H=�!�v�%q��h������)�� +=�M嚱O*�����뇺mnU��CS�ڣon�f�f/}[nݨ�uS��o�jU��(*�.��.ޞ��7;U��j�$S)�f��PcR��KU�=��r}��Ź�o���u�7����5U�;��O��S3��*�*���v���~l��Hi�l+7t�����ࢻ$I���+��Xx�������?�>��u�$�l�F%(TnQ��22%��*��5��uC�jI�
�L�Z�4j�9��̂*t��$�^���W(�4�)|�%J� +���Q�w��oNש�A� +��×K�\� �ԄOK��ͥ +|9���e�����
_N����h�55|w
�������$�L��g�
��L�,�>�{������g�3�����`����2|��*�e��ef>+1�c��L����|��e�3L����3����g��>�k�3�[�4~�����g=�΄��gq��>%S�D��䦭�IL���>q��r�V��a��n������mWN}�1�ذ����������OX���^=g�U endstream endobj -257 0 obj -<</Length 12338/Filter/FlateDecode>> +266 0 obj +<</Length 12464/Filter/FlateDecode>> stream -x��|xT���گs�<2���d�~2 1�AB��@�@���;��A ��?&-Hx�FjS*1�!�������ۢ�6 �ds����>�������w&��9����o��^�( -h4��*�-��"��2r.����?P&y
�>�z��1i��p�4@9i (����% -�p9��1P��� -)�����b���� -@7O��B� �ҶÈN���i(V`�����mN�)W�Z�m���� -Į@��b��,�l�6�";vWl��a�E�֭]���z��j�~�����<���ЩO?A�̀����i��,OqQF�a&+� ��� �d -#1�y����%�:ll�je_�V��a_M�9�L�XTP�F4Jvc<MH7�t[x�A�����4�8��N��? -LUd� ��M�E�4,��v���=�=0zD�Y�n�Z��̳?��g7��q� -zC�q���T��e��[L��̀U�+"2"7<"�h���N�@'���y"��F+�
�]Í��PF�*�|Uq��u��46{��ck37+�q�z��%�����͕�ʿc�S���"�ӡ����gD �Rp2���3=-��;�f���G^�qS�c���>6n�7��g��ۓ�������64��{�ޣ��
3'��-�[V=w Zb8����`�Q� -���[��y@P@�J$�A�'� -�l3r�9��0ш'M��d4hz�jR!�隳[�m.�=��U+;�Z5߇��s��(�u��@M���q ��,�j1E�� %�8��$��Z7s&��2�L5]�6�i��A����Z�a�i���!Et�k�eZ�W�Ǵ������8�D�`i�4&�D��c��!Ɖƹx+6��Ud9]EW��W�&�ɶ�P$��X��;U��=����/:H�y������N
.jݩ��f -S]��.�5���\ -�S6�0!'a�Q�%�)�@�3[[���l:��W\�F8�+��PԄ�i���1 -ִ@Ă�܃���"\��2��hZ0q� ���AZWϺ�}Hڋe����$_��LT�9t�2G]Nc˕�j�d��nC��T[ls��)��g|����-G����A8w
��|<]C��j<y~.��0%6���6��V����f�����,�\k�X��)��d����gTA�k�|K^�H % -V��gf�Ś�Y��=:���j]jŚM�����,q��m��|�D?�d�����2����IɃg�$?��֒�G�B���S���8��5r��)�t�w+���y8 -6�ڽ94x�/om*��r���EUK����U�%�.jK�d/!�l ���J����]�`�2+JH��F�^B�^F�G�3֖N�m��Z/w�U��?��-ܑ\_�S|��ؘ~�Ϳ�'��$Z\��������NyǗ:�3��;)�d�h���|���$�鞰v��w�� ->.9v{��NH��L����qe�p��u�6n\�n=I�1����]�#���(�o����'�ND��
�� c�����f�j��B�x����b�4TFV���.
����| >�`�n��^@��kԛ ֦k�܋s�w�J}�l�I��\�a���k��w쌚�l趽��/�oK�Ǚ�>��ܹ?�/y�7,�.�۞���)��,�"
e������i -9_v�TmC/��ڽͶ�k��ʹtw��1�]�^р��q��n[U�kZ�QJaQ;� -����Z�V��ʊKA���E��Z92�jo�3s?8�oy�\������eO����d|��n��u��,�
���+M�&%}�?pc�¥Q�)
5o��x@p��&0@O�RM����kuWI�0J�ܨۑ�Z���b����G��]gYS-R��K�^�����<�>zZ5��1�$�n�IJ���vF�&K[��ͭ��q�2AyPm�e�A - -]̓��D�T{��%�4>�b�K�jf�D�=��Y�Ҝ�.��,>�-dM��j�s�Wҋ]��0ؓ -��d��AU�'�F� �H�eBMbqq�忿�Μ_�zc�y�J�K��:^.�i& -�2�2M��h*�j�jz�E��gɳ�Y��������Y㳦p -m�>��_��(�)P��Ժv5!��)VW -��酆o� ��Wɽ��t�k��4�����K2ġ�%z�L�S�m����������h�E���Ȃ0���(����x�5�j���D�����3 � @�&0 -��s�
*T�X%�@,2Y/���WV\lNuG�X�[�yȂ��Kh�ۨ���ZZ6�~���D�c%��A����Щ���{^bM���2�G�O�z�uY��x/���m8�����'y{g�ݯ�<ϥϐ@}l@F�3=#ڙ��/G���������u��-�]�����l�A�̶�.]�'f��)]͠$: �/��N4'%gX���m���F�\T���6^��E���c�|��Վ�NE��N=��ad_H�����\Y��I-���[�fՕO�y�
�F?����7�%4�{��L�)���d��>G�ٵk��v�����رo�������#�U���\�>@�� �����)��.'f�8���?XhM�jLa`���D�7�bs�.o��������N�K�E��5��@ӈ��6���،¡f�Ƀ6�K}z�itP����{F2c���V�ٚJ<a*�+��+��a�)H�6�]�A�����F�oe�bb�!�:΅����V'�B<W-�+Tc���A(_��f�bE�@� ��-��u�.p�k��N߽�H߽�^��Uq�ؠ�'�2Vj��X-��I�ݤa����g���{�Gv��&&��
� -� E�A%������NZf���E�u��!�4��!�kj��*xN4�ܽL��&��˟"Cu�+��a�# 5���o��^�s��I4�W�5U2�r���|��Ǣ��ogʭ�;�v@^�K�{�%���dg� ~d>��-����>YY}����f[�]D�FW#����RT�iu�6�wL��ŨJ -�t���{��Y���f�^�c��"^����z��y+�|��yĉ7Rk�髖K��
�E��[��5[��yb�v�5w<����p��_��=�V�M���_{���~���J��ľ���� �����ĤV3D6��U��],V�W,ᮈ��t��kP4I �]���>����7ϴ�g��/ɻ����nTx�-6��������}h���
g8��oF��z�-pܓ⊦kc���K�cqZO<?�'h�B��-��
LU(X��Y7%Q�ӲH>�V&X�R�N���1�u���Ƴx�W�G���SD�b���W�M����zi� -��ՠ������C6�F�Q�FСt}�~ʞP��B��.U_R��
ъ����n�����cO�4� -�nc��SS�i���z�������^��,������������z̖m+���k�����%v�/�2ୀ/��J���4��(�a(�� -;D�M@6`@�[�ODE�&�_��DA������N�i�sA�w߹a�}�Ġ߹ �P���/ -��[�ر@),����l(�%]a&$@�A*�B:D�XQ�s` ,�%�f�t(�n�� -ƴ�Z,�͂�0�R���F�`.L��PQ0�a:,��rdL���`,�(Xe0��� QP�P���錕T�P -�r�Ja���/�9�,ҡ�=Ϸ=��콴��U]�%>酄K`!dA��5|�B$�b(�2X3a�|v��.�,X�:Qk��
��,� Ũ0�C),����$�N�?8���t�����[����b�� K��72��(t.���<��R(����FKz%�Z�
괋�Y>�f�Yʂ?A�Hޝ�>��aݚ��%P*9\ �_�s}�R�K|��>Yf��n��Drq��O��r\ ,�Qo� F��4K�݂c:YI�Ԝx�P��0��t�|�
΄2(�T�K��)�90�g�]�y�A�w��X�s1c&��BX�Pe��n -�Bcs`�I~:p��u��L�e����2i�r�/�!#�}�Dm�;,|q;�e�n��#�.��X���X^�!9��˙"�N��������j���nCN�V�3]�{��C�e���h���P$}������Ԅ�IJ%7�%sa̔��1��x��K�iHH.$�Ӯ�Ő%W�8�S�a�J�С�ξ���{/]ae��۶V������J�m]S���kk:�'����u!t_"���?��X.�-�^@�N���zV`���1��\��6�&xv����Ω��MJ���V���,:^e0_~�H<+����B]��0_JS컶����,n��~|�[�:���{,LH���4'��w?.?ģ��9���ǽ��%�W�Wrݶ+��-�m��Ef����c�eR�B�|��Řv��B�o��1��M_;��3=6��*�a~'M,�R���͂�$κ�N�������Ѧ����y��#�(��b�b���]����n�u/�1?���ܣ���5+|����ݱ��V�� ��'�_�(�� �`�#(�qQX�����ǥ�3-E�ڑyr�Q0��yF� �?����a,D�h�`<��@ȃ�R/��q?F�� �/)���$-���/iO�(I[d�#�a�#a�|6 -�y0VRc$�0�C��S�1G� -<N:��'���}W<&��B_\[����9{��J;��:��Ι��˱%��븪�g=fu�<�s��\mUr���߶�C��zm�9�-�y��.n�J��Qڞ���_�ڪQ
��ro��XΫK���}Zz~)��-�4*B�_! -^F��2y.zIz�0]�c���Uź���d�w����e�����'�}t��g���V�}Z�>A-�{y��`v'�E}��"�s�����<�)�G��c�,����r_F)�%���?�$_g�ޟQ�3=�@�L03��� ���F�
&��#�xF�0S��A��8*;X`�p��t�x� -N� �zA -B< -7;��ۜ�YI���MN����N?���)NNr�:''6�� 9n&
8�8�ñ��G�V�cG���dr�C���k��F���W9�����n�Wv��Ü�\H��[�A;9��K��J~��~N~k'������^L#�,䅽6�BW��F~�|��J�|�5'{8y���g�YM!y�WV�l0�����Hvs�k����N?�c{2����dR]՟Uo#۞9ʶq����왣��t�Sn�u2��[8�'O?�fO%O�IU��U�'�7��f�d"�nVYH*��� -7y�F6r�����<��ƞ�d���'k9yܖ�K�p��1���+�*N~���� ?��BVp������-1�2RV���]b&KN��v��Cq�('9)]0��n#J��cIIW2��yid.'s�H�m2�()�d'��̜�fr2�lF���4N�r2e��M��Ʌ䑷�ÓL�a�d"9)p� �����`�P��XN�p��J2��Q2��(���d�Q2�+�Ć�"��,?��bC8<���AyAl�Q�D����Ȁ\`'��c����Y����c�h����'��� ��zr��c!�z��c�9f�1��z���~��EI��m��I��$��L[.�,$������H/Nz&9XON2���!��0����9I��4N�'9X��BR�,%�$Y�Q��-�%9HR=�v��X� +x�ݼ TTW�?��tסּ�nQE1C�dDQJ�Q4qF�1**�Fm�QJ�&��H|i��v��!m4�IԤ�Q����i�ۄ_��D���{�A3t��}����A��u�}���y�2� +�,��S2c�"v�� +i�Mp�4Ai"(���% +�q4�P
�� +>��P 7`� +��0���8X�5P܂ո��EP ��v��Z` d��� + +@f��;��Ń� +��'�����D����
��hNmm��
��w{�p�:;4'�u{v,���w���:���?}ۀ̄�ّ�y����>{���}�}�OI ^����^j�*�!*Ć��� +��A�3 +?�%����QG�6}��x�I%�S&�e6������qJ��!$OG��$�m^�=�=Cg��~\SO�Z��t�2��Y'�x�*T�E^5������}����ڵ��+6�W������|�j�On��ݭ/�E>�����>jy��]�=�_�s~%"�IB~ +V.��aS`J@���(�jLk�����|�&��@���8w-��|]K��
�<y~N��0%6�����6��� +�M��Y����_�5�,�*��j9�Z���2�6Ŗo����U��33�s͏�I�=:}l�j]fŊ���~��X�>j��o��g�S�$cقP�rB�429iȬ�'y���G��ѧ�4�*.;ο�5j��f:��4Y��y +�u0�R��_�����P�/}��2��g��w�Y9#f�kC�.�Oۖ�_/���m5^{}�x�ӧg�n���Zn��>����a�Oև꿨%{�Z��� +�'56�d�0�[���o�W���q�ň�pz�� +�W�B�b���z��*
ܴ_ +��" �@c�u�=��f +!�P�$�\��;�Z{
�ZV#F�g��t�z���'N��� +����ןFI�W֎^�%w��=S��i����@`���t�&��d�q��6N��H���Dl�{1%���G�C�#4�D��"S���Vo�o�,���Ǭ�D���)��gO�G1��1���{��-p�Ϭ߸�~v�S߾{�ۧ������~w��5�+�(_�O�>���ڪM�ƻ�>t�¡Շ]��v��r�L�4s�c��X�F��dž�����������;22"W��!����ժ�e��uרۻG�ø��LKO������C�{�q�;�wھA��&�C,�!�U!#���7��]�s��H`�M��n<rdj�ޒg�w��{i��w��ח=���c��� Ml�;}��?:�8&��{��|���A��M��E'7ս��q��* +�T�)��eԒL���k���\%�m���m;ϱ�z>��-C�R�Q�/�{}��Z�G#b�d�Y'�Ѽ����:M����[����������"� +abN�jO�1��Nȣ��P�'I��Zm�ⷳ)n5G g@IB�Q��D�(S����=��C#+f�~��B��m�~�Ԗ�'���ҽ��??�mk��{Oœڮ����2îC8d{\�!HBj��ɋ��f��*�G��,L��������_ȐjL���y��2~�7# +��̃��N�>�����E;*��D��T$����G������o�t������{裾��<1�Pk4��6�Z�#USpd��X"YjXOD�R\zA�Y���4kѶ
u�H��3D�#��7k����W�Y��i>������m������O*6�f.���z�p��lz�x���y����Y`0�6� +᧙ +LS�s�bs9YF�
�L1S�c�J���tu���է?=~��q�rg��~L�n'��^������ ��c����K����S�����V�^aJ@:r�t��d,y��O�˧������Gh5�b��B~k��F�k�j"�wsD�J.���pEvQ�R��Z��ҵ��ɐ��� +j��b�c|�K��n8��#{�3:�����++7o�����͛�|s���1fLF�Q#�.�.?���wQ +�2PJ_���_�h#z�F����/�ɒ$��d(�d*ݻ�D��d�3 +�x%+W*L�HX7űR��d�|2D"?L&��@���'+� +�y�.�sx��mG�<x��T�x[�s����F
��xM`�A�Q�V?P�A��*�Y���5"K�M1�AR "�����'���&�H�?�� �33�!Y|a��%����n��w��L~p ?�����L4f C?=yj�9����y��s��o8����6�������oB�>b�������;�kR� +T�p +=-h������U8'�
�P#�N�!�m�O�u +�V(BW�y4*� +���� +��wL���]���}���+�B|�&�Bپc��X��wl�b�.���a1̅9PK! +��,��(H�H�4���PQ�sa),���f�(���fAD� +�v�Z�}�
K`6,�e0 +! �0f�<���`�Xs�;�`�F? +��B��EP3a̅Y�P +%0C�v?�qAa$��B(�\(�R�����\���, � ��۟n�^�E�Y]��>酄KadA��|�/�2H�%P +e�f�l��ŚtI�f�R܅Z�����eqM (� �a��r��0�I��O��#7�����Vc���ůX���%F����P:g��f�a>DA)�K^�dc4z%�N�ik�f�䚣�"P� :E�����֭I\_ +������\_�f�R���k�O�Y>��i.ո���g�,��X��NAܭ�[�l�kt��b%њ�ij�ڻ��f�\��O��YP%���|�`.,��q�;W�.�_ +�}v.V��D�Y�� +�L㳓�BM���0�4~:q��u��,X +�J��~fI�e����Yd�/� �;WZ�IU�=�y1�C�����g��.֦�Έ���E�M�� +kX�Eˠ��b�a���n�3`,�e1��"۴?�U�:Ͽ�1B��x_��Q��K��K�S1\\-����_�W�����S�1vAG����v���{"/���~��Y0fh��z^V���������R镖��ȋEH
�<m��0 +�k댆�0&� +_��U�b�R��������#�1@��!0H�[&��&�C��ſ��Ѡ��a<�#`*��tȃ\ô V� ��b�= ��B�7�CwȀd�tx +9�G��5N�Ԑ���UN�����~�0'/�C��r�Fp����?p�����H='/S�>��^���;٫�߾��~����H���=�<�I��`VWHv?ge���sV���d�?���~d��$���mI��z +iN��D��6��ow��I���2I��!��p��I�D;��I�p�+%��N�RBX'�j.K�g���!)!$9�Β�H�!�%%�=X��$6b�l��z�������%p�!�=�&�8��sҝ�8Nb��;0���H7�I��?�����\�HT"�N"�\�I8'a��,��P���`�I0'A�8s�s0 t$��\�[�#�ح$ endstream endobj -258 0 obj +267 0 obj <</Length 9/Filter/FlateDecode>> stream x�; endstream endobj -259 0 obj +268 0 obj <</Length 343/Type/CMap/Filter/FlateDecode/WMode 0>> stream x�mQ=k�0��+�� R�I�B�uj���Ύtv�$$y�/�夔��w����ѧS�x�ꊋ�s @@ -914,7 +963,7 @@ g��7��քҝb}��9�)kנ�b�宔�JK�n=lj��[!�yo����n���@�%G� �J?�X� �� endstream endobj -260 0 obj +269 0 obj <</Length 380/Filter/FlateDecode/Subtype/CIDFontType0C>> stream x�cd`aa`dd���LJ-*��+-N-�L�
JM/�I,ɹ���!��#�C��A��A��B��<��=EFB��OVY�����\�����(��>W<�̌�l|ގ)�I��)�y%�%����E��% @@ -924,152 +973,128 @@ x�cd`aa`dd���LJ-*��+-N-�L�
JM/�I,ɹ���!��#�C��A��A��B��<��=EFB��OVY����� b��W}��c�3��w�������Od�����`�.��?L��}� ���w�3{V�N��#�[����zJ����J3'v���)�G�����]��� endstream endobj -261 0 obj -<</Length 10/Filter/FlateDecode>> -stream -x��� -endstream -endobj -262 0 obj -<</Length 380/Type/CMap/Filter/FlateDecode/WMode 0>> -stream -x�m��n�0��y���h~Z�R��-��={���m����MhU��D��83k����'i�8��sأ7�8Zok�1�1�mP�WD����Xg�� -endstream -endobj -263 0 obj -<</Length 4371/Filter/FlateDecode>> +270 0 obj +<</Length 5285/Filter/FlateDecode>> stream -x��Yp\�y�����]�,iw����]=,?�������-�ɒ,�%bKڕ%���Zɶ0;���%IJ��1���!J�x���2��P���R�dF���iE��s�>$�$����{�=�|������@2�Pֶ��s��� -X�8��$z��.�K��Z|���p����� -�
����,�g8F����`=nIe����e\@p���j�:iEǐVP�����foqѤ[���B�Pʀg�ڵ�-�,�<�gQ�mH/����?,.�i��z���D��QY\TӴeH+w��^oqQU�F�x�/��1�i��<�x$o�#���b@C�1�w� X1�?M?�9f㭚����.�����G�ҝ^g�����1�Y������k�
�}?�
�(�g��ᤀ˦9��ޔ9N�%�v;�G}������E(�"P�s�o��%s� -�}�[��9������7v�?gKY���;��E��������w���U*
^@�#�`�+����s]㺕��uf';4ٓ�n��� -m�u�-����c�vVK݆m���˼�%���(�4�`����ŭF���E�o�>E�����1��a@��G�F6����I6��l!���S�EG�a��pj`�N��δ[�����L�k��䴔���NWyf�S�'��*�����3��9��L楹�s2����dLw[b!�¢�R�j�~ƖR�k�q��b��^��сΎ{�7�*�_�w6������}��k���+�.cw��e�81�?]$;�)����\�c|4�0�W�|q:��1�N�36�p�;�9ma+Y7[e�3�e������2�sf7�o��9��>�d,�g��=��a�AX�s�x�=MJ��E(�h�ʨ�r�|���w�o��Wk�]}S��G�q�1n��е_��g(D�k�t-�]���4���Sҁtw!t{Ρ�ox��PROJ��=�w^a����y��sٔ;�1~~t|�#�Ky���d�ߗ��f�RE�̯�\V.�_���t���\�tw����~ʝ[��a*b����i��j���:�cWoӃ��9e���M�{~���P��x��w>--1�u��M
��_T���'�탥Kآ
�mͫ��BG���i���L�V�3Y(��t�R���K�K�Ή=�p'���#��X�� -dU/D�C�;^?�J,�0 ���* �K&(u��B��a]h.�r�� �"O{cOS�i�K�Ĭ��_ҫ]�.�X+��if��w'��7�ބ*�SL"1���|�O�5.0!Ä�S3�1���&�2G�b��$<�⪮�$��Bp�O"�̌�ɧHЬw�q��^�U@�ϝ�\̍�=y��=us�M��I��E�MQ�"�"�=�B� �I�U�� ��y���/N����+�7�1"�bL|GL���Ey7U
��r�D�soد�N���g#f��������(�A��'�%u�LFkڰ-r�@V���'��닽R��RQ���1�nG��S�:l�v��h�c�h�
hD=6���B��K��r7nF�D��F��0Q!���آS��w5�F*��*l�6��$Q��(�ס���6U�Y�ը�FT�뵲U��P/���u��b�� V'��M1f�P�F����hV�Z� ���y]��dZ!5�s56�Vމ�шԣI�)�+M�u҇5h4}��T$�ըG�匵�$ai�9S�o���Ȭ�Vk�SŬތ������ -1&Tz��f6&DEĴB�M0�'B��)=��M��T�� �D܄R�rvP��I=Q�X-}]mj�0Uޫ�Ps'�:���h�Px��P;D��{�"Pa��:A�x����#�Yd�����'��Y"�]T�c"J���/�F4����I�F�Qt�W� -+j{b��B'�PU�����jW�ɺ6ϵ�u'wb��J�Ϣ�Z�� �*�V�
M���:���<�=�T'W�-�hR��>T�V�F��o@���ĺu~��:����!��"���2�}/"�*ϔ��T)�)k�)Լ� 5�
Qp ��Z���:�9G����V�|��1���e���y��^�S*,���7�~�D(�~� -M�z<�ڊ��P������N���6���k�R�]�D��(��C����(��Q��!�����
x�oŀ�ew1����eX����A�9�Ξ������я��W����ҫk錏~4H?��+��O��w��t��t��A�^J���z����������f��c��A���s��f:�ן�K'����O��]˟3��k���g���Ϯ�g*��|��]����g�_�ѓc�=���`}۠o��Afб��;@�<���̠��9�Q7=�G�s��>zdy�,?b��m�����}��?�����(�����3��4z`��nС��������� -��=�whg{�������P�A��j5�� -�xA
����(�M���<�G^�<pq��3���̤�Yn�cP�l7���l7͚��g�i��I��N>3�f*N3li|��fN��,7e�P\<c��/'�rJg9<}�\>r:|�9F���Gi�[x�J���n�[Oɡi�i|Z%��.n�N�4�)�;�N#��0I��$n��e����<��0���i)�rHK!g9�a8|�-���?������� +x��=˒�qw~E�;w�;�6������#˛�Ò�q��mKT;�{E�t� +���w.�$��D"_���~:���Ç������?���������W�̤'==��$��nJ1�C���?���_&=������'=}����'���O��ӏ�?����Z�Aks���w{��.?��g����������������n�gXO�Q_.�����������y���@_`<����y��������~��&�A���(닛�+�Y���|�Iwh~�l����vE�C��e>�YIX�
H^�mZRϿ�����"�G�\�V�)�l�N\�$ѭʹ�H�f,���:�;�X�r!If� +�������"'O���X�l)e=�r�M� +��mŏ\p�).�;� sX�u�yg��f`�M����Y%�P�0�͓;�ww��2�]�� +�0v�X�%��rQ��U�L�3��_/����Ǜ��5�k�s��v` :������ ��^��)Rgp��,TI�H��Q� +�k{��mϯ���0���3�WC\�Џ�a&���@�'��Ƭ��N%<ف�A;��k��`߈�H V��gP�� ���L�����n�����Y8�щ���2�iy @�x�DZ`�L��R:�����w��Y�rX�5�"y�Ё@ _I�h�J8���+2`��)萙�Ъ=�-���}+�4Z���!�8���*y�zT��0p�:�������Y����M����]a�L��̸��z](,�xb%�X6�P�fE�
>UA��u��?�C��N�^_pHCtN��i�چo3t��H����<D +m�۷X� +V�=�/#�;�8�4�*�_:c���cp�e귔Wu��-gڛ����v�m'����j"�+p��;�D<@���1��B�*���u-ǖGo\s�H���!W��{�2c$������oQ�dk�\�њU8�6�?,!� /�W����b^8Hi�JYk!F F�R�2!L8m9���uw�3G4��9�p���*1{欽��#7��4�K[�pً�Q��A��K0G��m �<� ������
�ו00��(8���}����Qb+=�?!�ض�8a��a��b���-�d)��U%۸��
��·pV�Cp�]]�����纻`Md5���h?�3B$;�{���)�?E:,����$����������0@��9�j���6Rco��.�V��I�Y�M�a ��<�+��ze��<`O�ÄR�0���}�a�մ� ���|9���O����?~�����������w��KT!G3��|�q�ӏ�u��O�?���'=����a��W?^�Z�J'cux���I.��/�X�ʜ��5��I��|ҪcҤw���&��zV+\���k�}�21U��^���7�ej��MzP�H�Ѧ +p���\�R�n�-����x�ն!'�#��0L��u`����E%D!dOƻу d��S'}"��8$1?���vxd�1�;���J��,2a�5����仮��/��O�Q����)+�*��߭�x&���.51�l����'��6�Y��� ����Aկ8e]��̩���8�hͣl$;'m�*�nI{VW!P*��O?s#e��5��AL&9mx�1o���6<v����ݺ!�'8�M�c� �:H�B͏��<!����<x���!g)��C�i�6�������#��d�~{.�� ++�`V +MJ�9���-�����*�sI�a;^���$LD +ϭ����K0\�ٿOKKg��[#`����kS����"���t6�ސ���9v])��n[^8b�����t0���TvGî �Bߣ�:I\
������B/)m��l�Bm��s���G&����0�P��_z�� +L�Z +m1j:���'\ٻR�5���<�ł�nb���u�#M�q?)^x��tR�;o�x�GCE,�|}&K��-�8VC��:ڜ +���</��|Ė�y��*��sR�g�� m���SѤP��f��`Pd�;�r �q�T�����[�K K���ѥ��������5�z��/�H� +��LN�w�ry�
�}�Vٺ�pn|5�T??%����~�/ʛ3L�^���ߣ�#�Sϧv)�܂��e$�{��|Wә���s�@��ja;aX�q�w�{0���Tt�ԑ���G���B�L�D:�U��i����MM��n���p��Iz�Z��b���V�݉W>�T?* +�@3�2=��C + +`��UΩ6���8�� �@~PǦ��¾8mإqҝ�o�I�ڝ�f��q���ۚ>�%f����P���
�?m��⻞�u�C�B����HDHR:}3`o�!%Rl���X��!��M������D!7a7��Nm���G��ќ:ye��#�b0�}�BSg����V�� +V0#��>\��2.��c�-#k��M2v�c8y|�m+O�N���Gf>���H�;;i��<�e��N������KeN��r8���|�k;mοnL0�\��(�̑�K�{9�Lt.��6��9;'��d��O]p����X�2C�n�j�S�#�� J;��љh<i�Ү�t)^����`�X�'��:��U&��U�.�a3q%�R����( endstream endobj -264 0 obj -<</Length 5745/Filter/FlateDecode>> +271 0 obj +<</Length 6496/Filter/FlateDecode>> stream -x��]ݓ�m�_Ѻ[[��[���R�쇤|�r���<�Tޔ]����T��}�G��&�9�=뗝�� �� ��ǿ�tz�����׃~�۟N���O����_x�?����3����1D���n����z���A�|z����>>�{ԃ���ǹ����������6���p���Ow����p����O�ϋ�����/m=.J=]~ s��K�tM����߀Z��x�������7_���o~��n�H���Q���*{,��`�Ⱦݬ;U���ۍUYX0�T�x��k�#�������`��w<�a| -�`��V��"���,2�Z�hOw$!û�v_� M���aH;/�O�R%7���Z���*�1/:��f�Y�$�㒲!����q㡙�6&n\dƆ�������gV��I��7+�o�U�`rR����-���u
����T|�TQ[��|��#��!�.��Hl[|K)�ay8��X۔��Rs�5j�!�:�B$�S)gD�v�?j��n������؍�������$�~�!d:�G�7ײ���%�UIR�)�������n'��}�A+k�qi15O
>5/E�Ue����ńъ����ʪ���Q_�) �� ��F��XѶ�8�7�yY�!���_��deU<W����S)�x���T;3ò&.> -k=Kk]k4�̣iE�-�Y1����0Cby$k`C��z�k�^��0�/�e{X��-Ol�L����-�1����k����廙ǚ
l��&|�*��f��ĶS�$tZ�h}���=�^ �H�{�r��}B�ULZ.!T��6 8�������ꈧ�h��$-��:��H`H��%�ǰ<p%x��*W�\h�$���e��@&C��N���ig�$�: -m �-NvU;�;0�;��(8�68n��Wc��O8<`@8�Q��*o뻟�h��A����d$���A��慌��L,���w�_����`�L���$�������9�<F�,�>���Oq�v�l�*I��<��0%�[�T�sR�#[õe�&�(��c],��嵊�&� -�j�n�Jƛ�1�u�{�Y����;�]|�z�Q�� -���)���8!�j�Z�'H`>W|R��x$�Y]Mx�(� -�?k�\!���Nx�L3ɤ �
�0�l��ӆ�n�W���̞�6f���`�(�`E-8������?8���������'��6 -j�{2 �y��찂����[�]سi q�D?�%��\x��L?�amK>��$��!��G8,�exv��x�JS۹8�ab��H����hF�]L�w���eeō.HG��T��R3N��Q�'�(w����3�5���ť�8�հ92P2��Xgg��feU����W��؋�,�g�<s�����n��]V[���k� -?[�7��Jٛ�Y[�����`�ڳ?8�B���(+�������������*��I�M�[>q� -)�� -��x����{����j/]�k� ��Kq�Nk^�r����u��j��1\�b08�o�m�'�v�Q��| ^���wL;߶��R��"iH]���Z]�7���ԅ:��B��O�$�5o�*�f;�����\�,<8�.�ֆ)�M����5u2+� #�C�c~�5د���C�!�)(�ΤG�d�6���~P�m����;��J�!;���T���H4��e�^]ꓸuX�� -\�z��f˶R* -k�r)�͎way/���6�d�p�K��Qx��y?�a�x �^�זɀ�v�H�������\���I4 �0eV��$���`� -)��O�^�"��U�[m(fo�Bu='�c��.�����)sD�.v-@a -sU��P�ؾ��En���m�U��=Xe����(�ՙx�<�Ҙ�D����+���m����Z�^�� ]8B�"0Gcu,8]$�Yȇ,�.�H��v*d
����69���t��|�*�4�kd��n�٪��5�3�~�72��J�[98e!�I�eQT.&ߓ������QZ�`��������@!a$����`'f��+ps��Bo�=�� ����e�����n߀�@"�_[3������R�Ue�q�8|ɗʚ��ƅ#�#s��P�NJ�0��l%�M!G�*d�ǃQE6�S2_bW�_�-V*]��k�ט4N�+S�5�"�P��Z��`�s���-FԻU�x����RP������k �$F}�1J�)B�><QQ�U�5�J<�{���C�H� r4�S�a��5�_�u�ZfH�Nl,��y�p�U�a]��?d=4v -��I3���\q%v}q=���~b����Y���*D��um�1��G��R(�����вA���T -y�s�ؖ�E�q���1`@�hc\�:�N���-m�UN"��~���G���}A�S�d��
���4�1���6)��U���G����P6����؏�x-7���[7ޡ�����H��M�����UI�H��qc���{��Ѻ�z.���y[{M����Ѱ!�n��R���,�gQc걔�+�-�sB"���u�����b ]�E5y�,Gm�X -��˫�Êa��������?����,uΖ����1KD�<Z��`���bZA.A����dtf���.�ϒW�^��߮��R��oh#�� -� �D�,�!�EA�͙_m��� -�8k���s=���l�� -7�;K����J�gJש� -o���q�k8*J�TU��4���� - -L����}|L��G�x
�M&��!Qq�*�ow�|����<wD]��j"�q4E�v�1���j�t���(!��2e^�}��N@��X���
z�>���k���R���9�Y��M�|R�gӱHo�� �v�KVY�G�� -a�I�!lLV�@ؑ\�\Mܐ�W�0� $�m�6`'�;lҙ|Mr�e[�m����6nJ��<(?�{����0��n�}bG$�ai���ӞsY�c �18�e�'�+���$8>�S�i���A{���L�R�\���V{�ٜ{V�Yc#�A�{�����5����I�M?�G�=ƨ��)!B���i�,�r{�KcZ
c����S�/��L\��Q��ݖ_4hF?�s�f��!3��Z���8h�O�a�����%�A� �/z�lȷ� ��'�ܘ>�f"����"�wgV�&��q%$��/��*���)Il�g�RQ���vKF/�e���f�ֱ����-:�@�
Gl4�RW[���~G�M����!ԭ����X �\h5.�S&���)�[ 0B��v/���P��|�O����K�1� ����<�n7e>���Nyƈ���_�#�(��+#d�t��eB&�b�l���gC<��J -��b��$?�8��)��
�f���O�a=C��3�L�i� -tm�� -2z[v$�/={
"����f�d +x��]ݓ�6r�_1Vl�[X���(��d;�+;U)�-��^%u����Ur�}��$H4MA�\��i�C4���Fw���?��������$���~��'u�'yz�N�v +* +��[��u�� +`���خ�/� +���V�?Gf������]W�� & +���x��a�Ю�0)���x�. +/CW�������H�]}���G +i�7�x0)����:�f9�72���9�L�v�|f����;*�PԔ������0�&<�`D�y?��g����©h���č���/$��x��a + h�U�n�1��Fm��ӷ�d>!����:%T�L�Фڐ8H�;��l��C0]Te��4�I8S�|bozҤ1*Ѩ��Jϒ�uL{H����ZWn�F�R��b��y<�l�@����l�z����$�6�_T��y.�t���/��#���f+7�WL�y������(Ʈu�V�v���)1d;KQ�l� 3FX�M3{_p�Q�rТR[}����îRF�d��)�8��bɶ��O�V�� ))V�������
kR��ё��+�6�F��#-��5�_+��5u-�v�lm�*ዴ��B͙!唞�d�_��|c|�\�����c�Y~O;#�F��� +���EV�З�o@F�\w0��E:f%u��F6 +e#��N�_գ��&76��=�$�,�����b�Z��sh�Ԗ?4������" O&��g�rnԣ��8&FH�0�Rt�D������,��g���8��`�ٌI�j���b[H�&���/���B���N1A 0�w��^�&�M��P9�eF���-'F^2e:���sp��ė�@����~?A�^\��.���w��b��y��
�]?��H=X��K�!�0� +i+z�����s���>�����hT�X��?��"�Ե���OσӺ�z���s���^X��'� ���vVd��k�C�~���8$� H��i�� �К��Տؐ8 ��y�;�Ak��QH.��V�İ��j��!��)��� +N�TU�Ǟ����o���z"\� +��)H�*'9�B^�>�b�^W�.8���@�\ '�e^V���j`����4�Y���κ9]o&a4�V�S�(��~bV;*�o8P�=�����//�� ���^����p�9���T3��/���&��7�n�����|%r�����G��4K:�CN#P�,�:R74OnR)�Am+ɍ��I�e�A�����n`������#�͂J��ܝ�J�8A))�����\%���G�~_k��WW=릈�.;ѸM?���2-v�I��0�V�K}!QI�ab3 �$}�9=�a*֟N|���6^Ĩ�~�W�RI�tp���������=��Q�tK�^I�4�� �U��:pj�LR˃�Jv��M"��%sM��15��@�l�5�fV�%)�U� 3p�&��<`������?���?j�K�D^�7GO(w�� 3�"]������⚇ %��`Q�̉�p( +NXM�ܔ"�-`�����tj�1�+65���U ��(�-��`��S�aV�L��"�-�Tm�bG�(`÷�\�[T��1inIm�Y�]�S��9a`ͯ��R�c�L�"ݹ�I����i�{V�L�"�/� ���5D$f��#%�E��rZ�ܬ��\&��0n�e�}�]8y�'�L�q<I�t�<��2�^�E/=���,U�C�'�8��|��'���y�jє�W���W� .��7��d�r}�ιќ\'��Q(�����H�*7�p�| EL���r7Ak�L̎J����`9���x/�����9Gb���Z(�uh��#FI�ұ慹���,�������)j��0�PS��t�았����}E@�{R>烁�fj.5�)M�i7n + ��v#��1sR�Rw4W���W���vV��6{��M��k%���UFԔ�iPv�N������YPC��1�;� +1�IK)� ��դ�JF����p���2*Vk)t�p���Y��)㼀5��؉IF��w�qd����d�ZT`� +ю=M��e��s�2q��)��b]��9_�]wМc���X�����_�@�����=5��zu�x�C�sie�jj�,�Q����|�]����1h�ޅɦ���k�$%z�;m;
E71G0��IY�S���9�u����86p�>�)
��&kORH�#(��I +�t�.`>m�sֶM8?�0�:�}�d�ѝ6�4�K=52��Z#�z��s�{w>g����{��6�vk�|�7]ˋ�[W }�� 8g��� +���E)LW��&v��b=i�q�-M���:��_0��c�g^�ԗlފ2�y+4����Ňc�<{��ȍk�S4�_��2'�6�r2������.���J^��*������Gy�O7ӰV��Ѵ<��&mſ��x�+k[�7[�cx�.�V��<����w�lh���vv*��^�ܣqÄI���S��/���Y�;�&��l�(�"4�^���ߝ-��#}k�ps��B��Lg�� �Ӑ�5�V��` ����n�V�52x~QP}�3¶����q4q�9���7D����]"yԉ���or)Ӛ�Y-ȋx�@]ڱY�ɋ�"��*�Պ����^���w�h*�n?AW�:��6��g�q�=4�WNo�-� \U�FiU���͝=@4����g���f_ߓ(^]��@t��k7W� +���eQ��t�9S!�Y�YoG�p~���n��X`��#,�5*I��G-2s�8���Z)�<�m�� l��۩Y���M�H�H��� ��(@�8d�����!{e{�BPyj:f�a_�#;ժ�{��������ɗ��I_ǡI�����]�S��8nfT�{H�e���+�S�̘�ղ��}��\tuZ}�|U���:9��ˋ�l�%�S1�},�D�
K4�����H���
&��<�5�WP�̹��Q�����-�����^�o�R�K)~T_��5�rԞ����������a��Y�VZ�2,����+��KS$�
�A +P��~�� ©�,9��j�hy����j��*U� �n �[Mk�����rD���|�g�3Z��z +]��K�Dt`�NϦ��]�A�7u}m�$r�n�2�5J��W,M���mS���W��whtc,���4�,i-%?���hr]���T�n)�8C�ul�0�N ��.���Mz�f!g-�0��u�_[��+����I�;��o����Qc~A-�Ւu��7�@�t���[��稑��#�m�cN���[�ц� endstream endobj -265 0 obj -<</Length 6017/Filter/FlateDecode>> +272 0 obj +<</Length 5389/Filter/FlateDecode>> stream -x��]ݏ7r�_1�eK[\��}Qw�� � �ޢ<h
�!A����!��>��nv�X��i6��E���&���Ǫb���?~zx����w���$�۷��=��I��I�^����t'�Ah0���/�?��O>�ӟzx���I�>|���'%�=}���>>���>J)���x�WJ8>d����8)@{�η|�Ҍ��r����[iA��n ��hB�|9^h��O�8_T�Vh��a��m�z~����/Ͼ���ߞ}�ûۥ�T!)�Ahp�%�NA���>J �>Ga|�xz�|^�JjM>������R5��&)lா����^��pS�0Ѩu�a�n=�1װ'��n\����_��B��7Z�ct��K��!&y+G�~������n$��ll)]����Q^���P����d'�<qLꉹ�)HS�hD��]��%g�X�9!�c��j]�-�v -��6�R^�p���M��@,FX�M3s_�<y�,�NS,irQ��>�vJ�a�(�t2����D���r�dW�؍I3N�����w�zBKC�J5����HU�J�M�Q��Hed��k�\�����.�1Bke�^-|�V�Y�97��HX�yC�kzB7���z��&`��Ȃ��
�5 -}-��f��~�� Om��P�Qܕ+F����Pts���G��#��s^�����G'¡MlF�=z�I�j���LOn��<��Fxm�92�����A*"�lA�V�ܨH�8FX�P��t�} +P� -ou�g�Tg��R��?�?I�O�.q}�̞ -�^r&9�
G��"*v;�j�~n�+#�x�9���[5(0�xR���������Yz�����#��|�
ÞҕW:�Rc���ÒdH,={e������>��Z���":c-�ecM�*:�gp������^@`������E@&=��x�RN~�O#$�'Az�*%S�R�\�i4K&㪿�����\t�f8g�Y�E�?��I����fVd��+h�vӵ��P�sJ� ���J�Z�2К��Տؐ:"Yf�0&Zu5v�V�A+��؊R�v\-}<�8?%��Z����/ȋi�QK����8#��}.r�JA -h�a���I$�F�d1g/�+H�˥GI/��p -���-!�&fM��7(�E���g$�~��L|Dg���J{UV���-��{.��N�/�^�&��7�I���VnɃ:st��p�]��,iO91L� -{u%ųn��1\�PI#�T�o����I����5�i!8�+C3��U^Ĩ|�+ f�H��&Dh��,��Ċ��l��c�2��%un�]VA�j�ՌV�S0�r?�s%�g�bk�0{7�kzL,9 -D���#��AH�Q쌒�#y���P�ia��'�We�U��x�U��R������e&`���q����� ���a�CI)��W�3R�|%����$���<��I��2|r\�r7�+�逃�ㄳAH���%%�gn�� - ��fè63��D�8DV���0"qn�6B˖�k���8�BG��U��]��)!l6�#���eD¥wF8��� --�Y�e8`�Hn}�2�|��}�hD���~�Q���R��]�)����JF������۽�)�Tk)t�p����RɕYP"Hot�f8�5�pwQ�G&���M�Z���\�mZ֚�4���|����+`W���d#yET���|+ �t
G��hUM�p�:�`��% =��|�u�~M���~X��x�&NMX�'f�yE�f0'z>0��nTμ�9��yZ�m�%��qv�<Ʝ"���vO���͠��=Q�y:��-��8�w=�A��* -��� �z0BEml�8�^bĩ�9����{y8�� -�
WZ9�
Չ��o�3*PF�bikk>"��#e%��Q�T�z�(n<]�_+�.:�7�wf��A(���� wj�4��e������縶�����Ds��x6N�Z��
�%�7a�,�4���D+�ܢz�S�~;��34&nq6Eu.'�/7����t,��l�o��TDK����[?9Z
���^�t��0��\�r5�A%ƨ��q�H�]��r�1L�$��Lp�9Ѻ�CF�}/V�v�\�_���O�ښJ�k�֑B�>_ql��R�Q4��K֞��RGP�œ�i�!\�&&d$:�S��Q8o�i�[F2��V�-�k�tS+sإ�p�&�=G�"τ}�P�U���W��n͗���v�9��WB_3|�3؎z������趝�[��w�E;/֓��\�IW�X��_0}�1��_Y#�v���M��2�y+tr�}����y���4����kG���̠v��n��H4
/�io�ѱs��B�p�[IX+e筇9�i7ek�5��H�5M�b��K�-'��7�S��>��>�ٟ<N�{}r�?s'�Rb��('_���+�"���+�ЉP�<�D��4)��-�A�/�Ձ�q�Y��+�a�@�fr���*Ӿ��N�9y���5T%��g(��(,�����tMܩ����Ax�s9@"y��O���{R�3Y�\ x�.�Y+��,��*��H� ��^���lwZh*�n%AW����2��g��j��3+�7}�h�˽X|tQi��˼�KG�a�T�TNi7h&:��5���a����7�S�wse0]����M����E�HD�����ڐ7`���r�:�m�|�%t{jN��9��#�X@�\b�#�����S��� ��x�*��
�Vz6/偈��EJ�'�Q�hP"��{�gѡ<����oX߉�e�Q�h�Ȝ��dF� ���i����&��ʹ�G��){��Y��r�U\%�j�BkC�'':�SC�A�A�5.C�*kc�u�ьh:�V -��{@�tKE4+koET�Ʌ�Q%r� Et~(gi5����O��M�7%���VCGu#o��)�yyՂ�+���S���^-�s`��-�%���Ȗ��B[ ����v7_��f�el�,���[܋�������Ͼ�Q�7��l�3�]��e����]��y>�/S<s�ѯ�$���ʏȥ�����z9�z��tEO^�ʋ|E��� �ͼٻ�D31��e.D�ܹٗ�i���M��c��,��U����HZgf2@�3O'"��i#ň��E��t�6�T�s:�� -�r�Z;���)G��0h�S��F�"w�&o���T)|�Ҥ - -�6��]�q��|�F�����&5�,i-%?�-�;r]���Tu�s�!�:6X�u����o^�\��&�v���Ӗ�8��u�_[��+�V��n�����1����jɺ��՛�� -p���R��,�9j$4��g[x�ɑ{'�Aٻ�ᤌ���7:!�DLd]��Zj<��j�]C�4����z%�+SC.3zi�B��W���{�)���zz:���q��a�A��8�jA\SE��(�/EC�����~ʥ(\A�r���Q\�{j��Ց���<Ut�{�'�� -����U-����6˔ʄ�9ְ�z�c��G�<��<������~�%j���Z��.��n�9���������]P���L�-��|��\ƹ��5"#�� -AV\+D') -�O�yD�W�����r��?�B�R�Lf�|�H�xȕ�������O�o��~������_��/��
/D -���w?����w�:h���dN?����$�7/����ُ�M��e-\i�s
��י�j�e�ufs � 8{��DZu:㥈v���M�iJ��7�2"�`��SC�v����RXg��e���S�t�kͧA -��{��z! �s�T�W��#țщ��������Ïg]l���{r�΅W�q$�V.��B�V�c$β��M� -a�n�G�e,����� -�e��/9�4c -V ->����j+k�A1>��>K��q��6Ypm�{�\�G�|�5�{���<^��8��� ���~�����.������V��(z?�g���(��?�}����3L��?�g�Lώ�x�r$pn/4�զm�j�1]����3F!P#�~��5������ +x��=�r\�����!��f(��_��dcY�fc�*e���`�M�LVa*��O��9�os�H/���K����������?�~���ݿ����o��_����_|�͛��3;�ӻ;��V�h�����J6�{���{�{��N����V~�ϝ�}����NO���������^������d.fU�K�b.�����O1WpvZ��sE��D���,����I��I;�i:畋N�O5�I��ry���ǧׯ�{��ov�������?=���Re����ӽ��N��������vz���������������O����Z��o��x�=��?hg����W{�j��6����o����x��X���;nƍ+��~\O?>�~������j�q�N��hXH�
�����;���e�o����t� +�t�{83W�uC�r<*�����n�g�������J��gZ�$>{u2��M�Yz��u�#�_r�~�Qn9���3-߲�z��=�)F�8��c|k��o[r�A-�S{�����?.u.v�A�X{)f���k�.��C�"�pij���:�vh���f�x� w3���aY�Y# 8cTH٥���1�7YY���^4sR����T�2�+�=L
?�%��C#F�; � �%��r�nU��l�ʇ`� +�'��/�t��Y?����'�J��P� +��
�<h�F���ΌS�6�L�hw$lxJ*@��n�����N���/{ +бh���X��f����<A�j*N&�) (�҈!v��A��s4.���g +=�C@L +,�����[o�Z]�0�\d��ACZ����}��`��?� +@hI�$�t(��'��,z����;cE��/i7L�9Y����o��C�� +�kE&�敨��|LqC.tW>�(�6�BlN�J�D~�=w���Q����KV�A��}ɫ��w�1��9��w @}��h D'�.9�80�f(;���\-��nm);���w/�P{�̻��˒���JT>�-��1�[7A�*�lk��ƍ���(}=<��;����H�9�Oa1d&"̻�:�N(j�P�1S��D-+��N&G��X\4SP�(Ay SV��K��@0��]U�O��8aA�1\���'�2B5��(�fl�����i`3��oyp�$\�U>��Z����k��y�a�)'}v~ +<�g�@��c +1:�U~�o5w�n_7�6Ko�t�����\O�dEs�Ȁ���A�Ao�c�]7�y7%4:�Pk1ʖ)�o������ +�9��b߸w��߀I aKP(�%S +)��#pL���쏧�Xz�37ΨRJ�5�
�ڈ&� X�����$%X㔍Ů~�廚�Ėj�Fk�_�o[^2���]�%�a�Ʊ�L��Gf�/�M(h�M�l�xT,�H]cW+gHQ}�w~Ȗ�`>������9�B;=$��)9�v��Hg��$얜nAG��YL
FQ�P�[�Krw�R�M6�O ��r��=U�u���}�~��k�ʙ�X��ڤ�<�kJ��鯰�60�_�J1=SlWF�,@9��(t��ֿ�؞G� +I ֱ���2�a���.ܛ�sJ |w�d��yRV +�qE�.����N��lrP6E��0~�'����~f*�=F�v'�|�̏� +(?�E�}�+<@SN��M�4����� +��MB;�#�����!�E �k1`�ߏ�TN�f�C�FF���������r!�p�z;\*�օ�w�.e=ėi/܀������J�Y�l\R>������Br*�����ְ���G�ԝ~:��.�Hz^�ٱ�A�+DQ�%�
endstream endobj -266 0 obj -<</Length 5845/Filter/FlateDecode>> +273 0 obj +<</Length 7164/Filter/FlateDecode>> stream -x��]Y�9~�����=�G�},���.�<���o0D��� ��K-�U���y��[G*�Je~��^���{���|��w��ͷ߽}���9�><�C��Eku<�X�����O�z��>��߃>����g߾;�û���x��/է_�����?j�|�+������;?����'�Gm��~y.����w}���;m����Q�}��ܥkK����K�U��+H��:Hb����?��ݳ?=���o_��a��Zc�g'��V���_vt���x���b��\V����3��P�o�C�����s������Ӭ�-���F2���Rr�]����X�f�r�p�Dn�,;}�d�*7��Y[:g���>���q��E�W�F����r -g��>��W����Jf4J��E�+�������SV��z�>1�`Z#VE7|ϠQ��_͛@�|�#a_�=5�2$F�Ifɍ�r�^��oO�x���;$6�K��ђ@�Ey?�I�����i���=�J�1��G2o�Նҝm�ȏ3cuk�4����;���N��M�#��P��ݹ����͓��`��w��yE�Y�~��~����,`)WE�.'h3�v�p�a�Y�)���V��\�!6�e�<#�)��ol�Ĕí,^槪�n�m��S0�9@���q>p��- -�!���Cu��=+�N>�4��� -N����FE����=�^��l��ڪ�<�P�H=\ mgq����i�g���f�&x�pw�8��a@�9A���/~��VI��ʚ�L����H�Ff�O�j@��@\����e�����葾�(n�=P���V%큑"��!X�<�K��3�fz���yI8��$�z�J�jUg@U����s
�_��C�
��E���������A�svб�WIdWK��zc�O�>�� �Y+_R�;P�s�Q;��ͺ�Z����KZt�� -uI���4`�['��������9�D7z�՛��{����>��XeC�6*��5�������f]}�BB@��@��� d�#�����9��ķ�2jXTFPx��$��:��]�4Ht�*�h|"����*AKq�L5&��]0��w�75:���ǯ�B��`��x��-�]����@��6�}��r.��mGep48��J�m����)AY�i7}+Љ=@�����t
���#�}#k�ʡ���WǙ�Y�&J��^�L
�76D�̵�� -�T��!�Md��BV�K�:e�����?���Zg����lf8(H+R:����$t���� ��1c�V^��lQH cQO $Z8�2d ����F�VIk����,�2Dц~����^� 28�8�wR��
,'0��d�S�A�Zd����e0K�� ����,��vô0�,�D64(2�#�C&z#~r�(a��O�H�d�� ��l},&��������5� ��6��5�1�v�&�C�e�9�L9�(��[m<K -�{δ�o��30��l�4���7�@���t�ͳ��V#�N:���.$F�魅�+p>n�n�n!g�L8"!��5��/"
"b�r%mG�6�3#^A��u<�~�u5C�-��I�����.��ؗ�Sn$����#�����\V�$�@ -��"t.'������~;=�=���p���O��LIv�G}�^2I�o�A[�4p���u��$���N�Q���i���j����-Ey��~Y�7�WU|ML��ª[��Z♐̈́@� ZJǠ�ܚ -���=3���$�KN���f��ér1���nLpQ�z(WZ�X��7$��9,� -=,H�qI���\�N�Y�h�HL[m ���O"ۍa�pf���FNxo=�#���Y -��� -�{�
{m��L��E5�ڥswMz�˙�)���+> -�7�ȼ�@Ln~�N�� L�-Q���;J~_S�؛ϭQƚ�asX�Yb�m��,��ɭЎ�u0<2y|�l�D��:�ۣ��=R'���ʩTT]�����k�d����5�B���e�J-�ȶ���r��MvD�,fW����[�K�l��䜶g�{���D=uu�t���i��!�ת��=��y�S��1%���TYXi�/T�����VhF�{@ɄB+tH�E�41^��V����n��<����*��}J %|u`dצy�^�N��]�c�J��e�2��=�w0 ���뙹��Q˝���<�
p�Ͱ�������.@H�6��r��͡���u����k�[{ -��6��^�<38�Ɏ+&.C����0�bޘїQ��$�3c�W�X5?h9=[V -�m}Ċ&��۬� -bn?l�2w��{�d ��o̎�'Z!�/���x%��+B�ސ -�;Nh��1 CY�bB�撆�2-ە���n� �)��6Vd�J��r��A5�*��u�lJ�~��fX�Iu��T-l-7�Ί�N���oT��@�G���
���Qvn�rC^�����U9٘Ĉ��t|Ǻm���KRŖ�Gv��~����~7�
i�Wq������ʺd�5�))�/Zö�&A�L�˸(qm"a8��qc�� -����]�;��̲t�
�[���f)����v;`����Wd�@j�a)w�kj~���u�#G�86,7���գ��4����k����C掂$��H���,��L^c�2��;2K{^�r����z��M��E���e��U��<�����g_y�_B@\��f����1mO
������D�rR�g�(N��zayq���ݪ!�r ^��U�NN��qGF� +�xU&�n�ZY�x�2�c�*��ѡ����[η�:�4q�F���}�����5��N��\0UD�C��R{1vj�s�O����hUI�Xz9��3�
�3!�p�#����y������qs�$l�r���"����V���H����s��s�[�� -����[��%�����Oe
��=*����}�E/��6�u E�XJ~�6\���9�n�s�� -��b�-��xU��E��uS���� -��c�hg�JB�aB� -�&7 ��N�m���jA5�o���@ �(椴��j�^%0ٱz�� ~b�g��C�߫�s��ިc"~8 -�2>@���g�������{�.�
��>��DR�iB*/��%ܮѕ�ag�1��PQa��n?�<����΅� +x��=k�7r��+FkY�z%��ǝ��K�Yg; �oq>��(@�\t����t�XUdw���6�b�Xo��������W�~������=����:���w?|����A��9D�\�V�CJV9�������]~��A����������W��h�>\{O����?��������S[s��������������{�ٛw���go~��U
�i�3Z+k�վ�逧u��2F�O{u�`���.`;��oZ��C����/�
;���� +Y9��v6p�q٨�W��sʚ`����h���O��@p
�OM߹M
�#\����$��co�s��9�#��0i<����d3�B77�P�����<��� ���lg!� +13������ae8�y�.�Y��$��r���=8��3�LcۭIQy��A������טCC���R�uw0�v9.s����0n�9��rx�?p�|���wx��QȜVa�4�t>�lr4hYyAP��a����_����?������6:��g��U�D#ζp
t52Z��p_ �����j�l�]`����+^|����ݳ(�5��s��P~ +4 +��2�{�<��@[�=��+�T{�O�B�2�cm~k�> +���Oذ/N���ְ+ {��G��ύ�#B�{��@h�
!�����[�л^`�o X�Z��46[�(�3�B��r~��od"���m@�����K����δ�4�"lwh� +m�Q�NhȪ
���]�D����bj�a6GU\ȡOL]c�e�F�#Z{�-I��~j�n�)c#�d�\ˉb!x m����p��ȏ��vc��+zx���!m�(�WΗ�HZ������?�Ʒ�>6@m|B+d�@� +�^f�{J�S�j=���Fq�F��:Yũ���BI�BQ+�vwN���$������5�▍Kt��0��`#���Px�_��Q�Yz�Q�9y� ��+g�$�t B�N�f_����N�1��ږ��)�H����?L�PkPv�O��x�|���Z��sZ���z�Uۜ"D�i�z���^l|��r1K�n��#�5�i�t��TG�㈤!|�ۺ��J��r!��w�ޗ�d�]0��ݼ(��(�9��� +�@w�d�*�X}ӂ�?���ik���n�y +�rg^�a�$��J��ƍ)�g�A��L{5�,i@嵢i�Y�B�eMAB ���������W-w�I�)�oUT��EC��W.I��AVB= +��T��t�MV.��ŕ�ܩ>b���^sR��\@�|�� +X
#yW�d��7H���,��g����+�����qȊn&U̥+{�xAQ�p�4�`17���c!�,?h��:�2�֞wda���{�xLƧӷ�܇�ĩoF��o�Y���l�}�}(��}��������]eDd�J���m�]�Y�����SA�|�c:b~�D��J'��n����H�Qu��G�g>��$͙�R y�U����/4��0�sR��4C�q:�+���m��R��"ج�C�H�� N%�%d�Bt{.1.��#H�v�d�w�ᰗ��.�фkǵ����ɍ�{�T䒍*ELjUV����?�C-���ӷº�H +���4��� +q��<��ʦ����4�� �2 �~V�j���ŴʚwY�
N/���� +����z3�jKſ��D%o�ƣ�@��~:��8i{�~��M�Y�n�3)L�8.hdHǯUւN��n�e?�"���B}x%����"�(?gg�j_�^�T����F�W�$�S�s���ϐU�G��x��%er)���+�c���z֧���X�O����1����ck��.����ߋ��n �Em
�c`Ǡ��vi�ђ�Z'��� Ay �p�� +�^:p�9'����{�,:����p�@8DI3��/�S��`�1N7���Fq�Xo����D��;Q��
N���q�"���)�ڏX������_[�IZ��k���l�k�%b������݇qH�t^E���U�m���phm�Tyl��s��̯��8��l +��h��O��
a��G~J�l0*��QD\K�����s�c5���� ��0�
��p�e$����G���p@��)����8Tv�㠒'��-1n��(Cz��i���{w��K� +!*��k*�MGQ\��-1D\Ѫ��+��v���mF�mP]����K�חcSP��Nƣ͙��Ch*�8*,U���4�����@!0"�͍�=4^I��FÑ�4����6|����r,Gf$`��:�G�S��ȟ�bo�tڥt8��*��Rj8��>r�8���Ǝ��Y +h
���I��f�<��-I(���|"J �Rp��92�v�ty�e�AC�E<�����@hB"b��T"z?���'K��}'�CH�����fw�sd=�k�j"��{ue������6��8� +&Cc�` +�C� +�R��| �eĆI/`�E�ۚa�����իȟ����NY�w��Z�Јa�!J��i7IS +#64�|�b9���j��G�L��:p83B��d�!�� �?�JZ7�܊��3 �g+�:ek�$
�J���@t�MV�1������[{E�����o���m��5��G����>��.VLtY3�h�³���7����OrW~���(:����4�g��Id������:���1DZWNTۥ���L<���*!܆n�
��B-�G��f(���Nx1(5N/+ }�<>����G�hI��>�ʮ@ �u�e���=��`kP�Y�0��pT§y1j Y
��84S�O��
Pw��U��`۸�5<$���V�~�����恢�ZUܔ4�i���5@'��/- ����� +֤����^(h��~��V��KN[ec��� +iY��~����POA�'4"!ݪ��l�>dCl/+]�\���K�N�Qj'l �iKI�B���ʕ���-"1Do�7�5��V9��?ɮ +oJ❟� +��HwIyO��yI|ɝ�!��H�)Iy�C�^�˺�`<|����K���Sa8���`an�*Ih{-v1T�cU���T5Ǡ��f���e���d(�[���L(��|H�����5�e<�MR\;�I�w|�KJO��H��B��np�x~�C�]+Η�I��Ta���֎ݱ��v�ђMf*��n��t���v��O�dMJK%|I��m����W'>���l���������|�[����5|X=� +��h����5Sc~՞�X�F�E1n`�/��|�8l +�FnSi���8�O�����&�}��Ex����h�qN�BM�pPQ���8`�`�H��ӑ��8b&Asla�p�/���Yʳ�t����D az<q�nd����:�c�Ŧ���a-�����~��2hG����.�S3 7mD��R����9S�J��N�g%M҇l�:�t��ޙ�/!��y�?,�2U�I�y�ȱMxY���nn0&[�;`5*rM���S�+w� jF��.8W%����^ +��*_Ʃ +^T{P��Z����n8��#�K�P��q��1_s@�%>+O�P���؎�Z42�)�j���z?�F���J F8�D�b�|�l�_U��ud\r�$�|�{����d�Jg���ٔ�c�P�|�f�`c�\h�lp����.ή�`����]ف���+��;l[[� endstream endobj -267 0 obj -<</Length 3869/Filter/FlateDecode>> +274 0 obj +<</Length 954/Filter/FlateDecode>> stream -x��\Y��~ׯ�${ǫ��fS�C�!_��-ʃW��A���G�3M6Y�9�����f���"��*����m��Ww����ۍ|��ͷo��w�6r#7�j�0^k�7!ha�W�����(7����wO����ǻ�QR������������ �� �?|��[�o=?_� �[��1�QӾ�7��|���ؒ -��g���^�~�~��@���yn������FG�'
^5����B�u� �i��oo�,����>��:������@����;����n����خ&f�7ѯS{`��П�Yۃ�uh�e6w����m�h�ʯ�H-w����OO�{��/O����]���j�&a\t��Z>H����~9�;�_ �s^<}��o�^�c�m�i&���T1��j{kt��� �5ϫA,*)��J�9�N�^�����+���0YH^9(ͮ�9������ۧ������S��T+-�t>�Uo&a[ʝ�尽�AHg�h��~��M=S�g����z��?��{0qXy������=���ӖPW����
uc�tH��2�I9]�D���c�2k��紩�Ҩ�0iYa�e���c�i� -�R�H����nS�ˤD��{0D��K��Ȓ��o�k,���^��5�U�.��R��9cCs�}�/�[�ːN��Y���}�w��#���z�۶�sx�v�w!����E,�������e�[��QLZy}sN�6�<o��_4�H1�~n���I�'1E.1�����fW�j���R�O+�����9�/���\�MA,�a�Y�v%�Cj,�b�w -�z -�F��Z� -��S��
�Ѵ��I��'u�(�++��ݐO���>�9g�S!���Dc�Bay�:ݥZ -7����-�9ƾ���I�`�( -9���-��gZ��5��j�x���B6fBC!�Y�^��[��hJ�WDC�=ͺ��� - ��e��@%[tU�w�!�v`�]}���1p�4|��{;��<=���{�K�ksMNH%���X��B;eԡ2�h3��m�ָ�����V7��?�m[ �H� P�����,7O���^�u���CᇢVlg���0��vD�j�Hv������{:v�i�
�9�Ŵ^�4NG�"u*�pZO#�P;����jT�`�i+|��Y{^��ƴ -¼�"��CS�2���C����J")��Llw�������`��uSQ�ڢ���D�����1�o"�ey.9rCdhm�{�v=�ѯ�ꥅ�B�ۗ��2�:��#I��7�:A#�zo�x��������.0wkz,���Wk�H2��5S���� �s�F�U�'�XP����m��FP�pI-�KYv�T]�Əc3B�jF��y�_Pz S6b s���5�-!e.(y���H�R�.���.*��؟k�K����^�P -�:�
o��ւ��t��r���J� -�H`�y�ByH -Ւ5{d�C�B����L(��^���_��d�E�t"N�݄xU�V� - &����&p;��H2��(�C5�`���ݐw�,� -�֑^,�Z�c�u.@�d�R_�:Z.<_> -J��gF��;w�#�\�� -<"-1i~��v�"�z�S��}����mp�ZF�\q`ȇ68)���j�Hq}?^Ӆ��_�-h��JP�O@�'��n�1����T��g� aV�A�����K{p3l���u��:)����[�my�]�cדJ[ �ѡ�CĬ�����B6���S[S<��s�Ø�I�L~E���X�c�t�δ�!�&t8ű(�6'�����+I -�� c -�$������ +x��XKo1��W�R* �k{�J}x�ȍrh#©+h��������3qR.�f;��7��;���;?��^}�f�����j��I&�`Sɬ��*%,sNqm�d���R��3�yٍ.L���l%�ܰ�j�{�,�� !�n������N�wk/b����U��T +ŭ�FY�G�~�6��o���lE7��Q��ސV��iV�ZR +�E�@�pm�h������[�\����[g��J�ؐ�c���P�0%;��X[��T��&4��i��Z������y�~ǎ�d��#�1���=�j�ávC�G�U8]`��`�� +!C�\�,>����:�<0� +r� endstream endobj -268 0 obj +275 0 obj <</Length 258/N 1/Range[0 1]/Filter/FlateDecode>> stream x�u��J�PFO��U�v�D�D@�`]\� @@ -1078,7 +1103,7 @@ F��Mk�I���R�B|��&إ�n�ऋ��(�H�r�Tų��9�`��ըa�&�ʺ���:�l ̵�8+�/<����a�Rkp��ش�A�mW�Q�Ih��
�%��l��E5VW~��d���X��)�ϴ�:��b���k#�&�_¤3�0��
�I� endstream endobj -269 0 obj +276 0 obj <</Length 314/N 3/Range[0 1 0 1 0 1]/Filter/FlateDecode>> stream x�}��Kq�?�U�X
94%MQKS��N�#Ԧ��Q�v��B�ˡ�h�Fk ������! @@ -1086,295 +1111,302 @@ x�}��Kq�?�U�X
94%MQKS��N�#Ԧ��Q�v��B�ˡ�h�Fk ������! ��0l+��ߣ�y7���;����;Q�?V.�_�tF��3L�E�K�)y ��z�80e�IP���k��T�/%[�p endstream endobj -270 0 obj -<</Creator(Typst 0.15.0)/ModDate(D:20260706083532-03'00)/CreationDate(D:20260706083532-03'00)>> +277 0 obj +<</Creator(Typst 0.15.1)/ModDate(D:20260802002740-03'00)/CreationDate(D:20260802002740-03'00)>> endobj -271 0 obj +278 0 obj <</Length 996/Type/Metadata/Subtype/XML>> stream -<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?><x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="xmp-writer"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/" xmlns:pdf="http://ns.adobe.com/pdf/1.3/" ><xmp:CreatorTool>Typst 0.15.0</xmp:CreatorTool><dc:language><rdf:Bag><rdf:li>en</rdf:li></rdf:Bag></dc:language><xmp:ModifyDate>2026-07-06T08:35:32-03:00</xmp:ModifyDate><xmp:CreateDate>2026-07-06T08:35:32-03:00</xmp:CreateDate><xmpTPg:NPages>4</xmpTPg:NPages><dc:format>application/pdf</dc:format><xmpMM:InstanceID>ryCwVs21NRYeVZORaVGmEw==</xmpMM:InstanceID><xmpMM:DocumentID>ryCwVs21NRYeVZORaVGmEw==</xmpMM:DocumentID><xmpMM:RenditionClass>proof</xmpMM:RenditionClass><pdf:PDFVersion>1.7</pdf:PDFVersion></rdf:Description></rdf:RDF></x:xmpmeta><?xpacket end="r"?> +<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?><x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="xmp-writer"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/" xmlns:pdf="http://ns.adobe.com/pdf/1.3/" ><xmp:CreatorTool>Typst 0.15.1</xmp:CreatorTool><dc:language><rdf:Bag><rdf:li>en</rdf:li></rdf:Bag></dc:language><xmp:ModifyDate>2026-08-02T00:27:40-03:00</xmp:ModifyDate><xmp:CreateDate>2026-08-02T00:27:40-03:00</xmp:CreateDate><xmpTPg:NPages>5</xmpTPg:NPages><dc:format>application/pdf</dc:format><xmpMM:InstanceID>od6Ce8g5ofbkk30tkgkhhg==</xmpMM:InstanceID><xmpMM:DocumentID>od6Ce8g5ofbkk30tkgkhhg==</xmpMM:DocumentID><xmpMM:RenditionClass>proof</xmpMM:RenditionClass><pdf:PDFVersion>1.7</pdf:PDFVersion></rdf:Description></rdf:RDF></x:xmpmeta><?xpacket end="r"?> endstream endobj -272 0 obj -<</Type/Catalog/Pages 1 0 R/Metadata 271 0 R/Lang(en)/StructTreeRoot 12 0 R/MarkInfo<</Marked true/Suspects false>>/ViewerPreferences<</Direction/L2R>>/Outlines 11 0 R>> +279 0 obj +<</Type/Catalog/Pages 1 0 R/Metadata 278 0 R/Lang(en)/StructTreeRoot 11 0 R/MarkInfo<</Marked true/Suspects false>>/ViewerPreferences<</Direction/L2R>>/Outlines 10 0 R>> endobj xref -0 273 +0 280 0000000000 65535 f
0000000016 00000 n
-0000000093 00000 n
-0000000169 00000 n
-0000000255 00000 n
-0000000347 00000 n
-0000000429 00000 n
-0000000544 00000 n
-0000000626 00000 n
-0000000730 00000 n
-0000000844 00000 n
-0000000980 00000 n
-0000001047 00000 n
-0000001241 00000 n
-0000002267 00000 n
-0000004312 00000 n
-0000005154 00000 n
-0000005700 00000 n
-0000005771 00000 n
-0000005832 00000 n
-0000005899 00000 n
-0000005981 00000 n
-0000006054 00000 n
-0000006125 00000 n
-0000006207 00000 n
-0000006279 00000 n
-0000006363 00000 n
+0000000101 00000 n
+0000000177 00000 n
+0000000263 00000 n
+0000000355 00000 n
+0000000437 00000 n
+0000000552 00000 n
+0000000634 00000 n
+0000000738 00000 n
+0000000840 00000 n
+0000000906 00000 n
+0000001109 00000 n
+0000001771 00000 n
+0000004169 00000 n
+0000004955 00000 n
+0000005909 00000 n
+0000006007 00000 n
+0000006078 00000 n
+0000006139 00000 n
+0000006206 00000 n
+0000006288 00000 n
+0000006361 00000 n
0000006435 00000 n
-0000006553 00000 n
-0000006623 00000 n
-0000006693 00000 n
-0000006838 00000 n
-0000006906 00000 n
-0000006978 00000 n
-0000007096 00000 n
-0000007227 00000 n
-0000007295 00000 n
-0000007423 00000 n
-0000007495 00000 n
-0000007565 00000 n
-0000007635 00000 n
-0000007745 00000 n
-0000007813 00000 n
-0000007885 00000 n
-0000007955 00000 n
-0000008025 00000 n
-0000008095 00000 n
-0000008165 00000 n
-0000008235 00000 n
-0000008305 00000 n
-0000008375 00000 n
-0000008526 00000 n
-0000008598 00000 n
-0000008670 00000 n
-0000008767 00000 n
+0000006517 00000 n
+0000006589 00000 n
+0000006673 00000 n
+0000006745 00000 n
+0000006863 00000 n
+0000006933 00000 n
+0000007003 00000 n
+0000007148 00000 n
+0000007216 00000 n
+0000007288 00000 n
+0000007406 00000 n
+0000007537 00000 n
+0000007605 00000 n
+0000007733 00000 n
+0000007805 00000 n
+0000007875 00000 n
+0000007945 00000 n
+0000008055 00000 n
+0000008123 00000 n
+0000008195 00000 n
+0000008265 00000 n
+0000008335 00000 n
+0000008405 00000 n
+0000008475 00000 n
+0000008545 00000 n
+0000008615 00000 n
+0000008685 00000 n
0000008839 00000 n
-0000008909 00000 n
-0000008979 00000 n
-0000009049 00000 n
-0000009119 00000 n
-0000009189 00000 n
-0000009259 00000 n
-0000009329 00000 n
-0000009399 00000 n
-0000009469 00000 n
-0000009640 00000 n
-0000009710 00000 n
-0000009788 00000 n
-0000009929 00000 n
-0000009997 00000 n
-0000010129 00000 n
-0000010253 00000 n
-0000010371 00000 n
-0000010440 00000 n
-0000010511 00000 n
-0000010580 00000 n
-0000010649 00000 n
-0000010757 00000 n
+0000008911 00000 n
+0000008983 00000 n
+0000009083 00000 n
+0000009155 00000 n
+0000009225 00000 n
+0000009295 00000 n
+0000009365 00000 n
+0000009435 00000 n
+0000009505 00000 n
+0000009575 00000 n
+0000009645 00000 n
+0000009715 00000 n
+0000009785 00000 n
+0000009956 00000 n
+0000010026 00000 n
+0000010104 00000 n
+0000010224 00000 n
+0000010291 00000 n
+0000010406 00000 n
+0000010515 00000 n
+0000010633 00000 n
+0000010703 00000 n
+0000010775 00000 n
0000010845 00000 n
-0000010913 00000 n
-0000010981 00000 n
-0000011067 00000 n
-0000011153 00000 n
-0000011233 00000 n
-0000011319 00000 n
-0000011405 00000 n
-0000011485 00000 n
-0000011571 00000 n
-0000011660 00000 n
-0000011715 00000 n
-0000011783 00000 n
-0000011869 00000 n
+0000010915 00000 n
+0000011029 00000 n
+0000011117 00000 n
+0000011185 00000 n
+0000011253 00000 n
+0000011339 00000 n
+0000011425 00000 n
+0000011505 00000 n
+0000011591 00000 n
+0000011679 00000 n
+0000011764 00000 n
+0000011857 00000 n
+0000011954 00000 n
0000012009 00000 n
-0000012094 00000 n
-0000012235 00000 n
-0000012344 00000 n
-0000012485 00000 n
-0000012578 00000 n
-0000012679 00000 n
-0000012734 00000 n
-0000012907 00000 n
-0000013089 00000 n
-0000013215 00000 n
-0000013469 00000 n
+0000012078 00000 n
+0000012171 00000 n
+0000012336 00000 n
+0000012421 00000 n
+0000012562 00000 n
+0000012671 00000 n
+0000012812 00000 n
+0000012905 00000 n
+0000013006 00000 n
+0000013061 00000 n
+0000013234 00000 n
+0000013416 00000 n
0000013542 00000 n
-0000013615 00000 n
-0000013717 00000 n
-0000013797 00000 n
-0000013870 00000 n
-0000013947 00000 n
-0000014049 00000 n
+0000013796 00000 n
+0000013869 00000 n
+0000013942 00000 n
+0000014044 00000 n
0000014124 00000 n
-0000014245 00000 n
-0000014320 00000 n
-0000014441 00000 n
-0000014555 00000 n
-0000014635 00000 n
-0000014763 00000 n
-0000014891 00000 n
-0000015019 00000 n
-0000015099 00000 n
-0000015227 00000 n
-0000015355 00000 n
-0000015483 00000 n
-0000015563 00000 n
-0000015691 00000 n
-0000015819 00000 n
-0000015947 00000 n
-0000016027 00000 n
-0000016155 00000 n
-0000016283 00000 n
-0000016411 00000 n
-0000016491 00000 n
-0000016566 00000 n
-0000016687 00000 n
-0000016762 00000 n
-0000016883 00000 n
-0000017015 00000 n
-0000017095 00000 n
-0000017223 00000 n
-0000017351 00000 n
-0000017479 00000 n
-0000017559 00000 n
-0000017730 00000 n
-0000017841 00000 n
-0000017912 00000 n
-0000017983 00000 n
-0000018056 00000 n
-0000018128 00000 n
-0000018200 00000 n
-0000018272 00000 n
-0000018344 00000 n
-0000018416 00000 n
-0000018585 00000 n
-0000018657 00000 n
-0000018749 00000 n
-0000018821 00000 n
-0000018900 00000 n
-0000018972 00000 n
-0000019044 00000 n
-0000019116 00000 n
-0000019188 00000 n
-0000019260 00000 n
-0000019332 00000 n
-0000019404 00000 n
-0000019562 00000 n
-0000019663 00000 n
-0000019737 00000 n
-0000019809 00000 n
-0000019881 00000 n
-0000019992 00000 n
-0000020102 00000 n
-0000020177 00000 n
-0000020260 00000 n
-0000020334 00000 n
-0000020406 00000 n
-0000020478 00000 n
-0000020550 00000 n
-0000020622 00000 n
-0000020767 00000 n
-0000020841 00000 n
-0000020913 00000 n
-0000020985 00000 n
-0000021057 00000 n
-0000021130 00000 n
-0000021330 00000 n
-0000021403 00000 n
-0000021474 00000 n
-0000021545 00000 n
-0000021616 00000 n
-0000021688 00000 n
-0000021760 00000 n
-0000021832 00000 n
-0000021904 00000 n
-0000021976 00000 n
-0000022048 00000 n
-0000022235 00000 n
-0000022309 00000 n
-0000022381 00000 n
-0000022453 00000 n
-0000022525 00000 n
-0000022603 00000 n
-0000022724 00000 n
-0000022798 00000 n
-0000022870 00000 n
-0000022973 00000 n
-0000023047 00000 n
-0000023127 00000 n
-0000023201 00000 n
-0000023287 00000 n
-0000023359 00000 n
-0000023519 00000 n
-0000023591 00000 n
-0000023665 00000 n
-0000023756 00000 n
-0000024135 00000 n
-0000024172 00000 n
-0000024209 00000 n
-0000024262 00000 n
-0000024314 00000 n
-0000024366 00000 n
-0000024419 00000 n
-0000024471 00000 n
-0000024523 00000 n
-0000024576 00000 n
-0000024629 00000 n
-0000024682 00000 n
-0000024816 00000 n
-0000024950 00000 n
-0000025084 00000 n
-0000025218 00000 n
-0000025367 00000 n
-0000025944 00000 n
-0000026152 00000 n
-0000026304 00000 n
-0000027149 00000 n
-0000027360 00000 n
-0000027500 00000 n
-0000027716 00000 n
-0000027955 00000 n
-0000028115 00000 n
-0000028321 00000 n
-0000028534 00000 n
-0000028679 00000 n
-0000028899 00000 n
-0000029144 00000 n
-0000029272 00000 n
-0000029400 00000 n
-0000029528 00000 n
-0000029656 00000 n
-0000029739 00000 n
-0000030381 00000 n
-0000034852 00000 n
-0000034934 00000 n
-0000035714 00000 n
-0000046346 00000 n
-0000046428 00000 n
-0000047197 00000 n
-0000059607 00000 n
-0000059684 00000 n
-0000060115 00000 n
-0000060587 00000 n
-0000060666 00000 n
-0000061134 00000 n
-0000065576 00000 n
-0000071392 00000 n
-0000077480 00000 n
-0000083396 00000 n
-0000087336 00000 n
-0000087679 00000 n
-0000088086 00000 n
-0000088199 00000 n
-0000089272 00000 n
+0000014197 00000 n
+0000014274 00000 n
+0000014376 00000 n
+0000014451 00000 n
+0000014572 00000 n
+0000014647 00000 n
+0000014768 00000 n
+0000014882 00000 n
+0000014962 00000 n
+0000015090 00000 n
+0000015218 00000 n
+0000015354 00000 n
+0000015434 00000 n
+0000015562 00000 n
+0000015690 00000 n
+0000015818 00000 n
+0000015898 00000 n
+0000016026 00000 n
+0000016154 00000 n
+0000016282 00000 n
+0000016362 00000 n
+0000016490 00000 n
+0000016618 00000 n
+0000016750 00000 n
+0000016830 00000 n
+0000016903 00000 n
+0000017024 00000 n
+0000017097 00000 n
+0000017218 00000 n
+0000017348 00000 n
+0000017428 00000 n
+0000017554 00000 n
+0000017680 00000 n
+0000017806 00000 n
+0000017886 00000 n
+0000018057 00000 n
+0000018168 00000 n
+0000018240 00000 n
+0000018312 00000 n
+0000018386 00000 n
+0000018458 00000 n
+0000018530 00000 n
+0000018602 00000 n
+0000018674 00000 n
+0000018746 00000 n
+0000018920 00000 n
+0000018992 00000 n
+0000019084 00000 n
+0000019156 00000 n
+0000019235 00000 n
+0000019307 00000 n
+0000019379 00000 n
+0000019451 00000 n
+0000019523 00000 n
+0000019598 00000 n
+0000019670 00000 n
+0000019742 00000 n
+0000019897 00000 n
+0000019998 00000 n
+0000020072 00000 n
+0000020144 00000 n
+0000020216 00000 n
+0000020327 00000 n
+0000020437 00000 n
+0000020512 00000 n
+0000020595 00000 n
+0000020669 00000 n
+0000020741 00000 n
+0000020813 00000 n
+0000020885 00000 n
+0000020957 00000 n
+0000021102 00000 n
+0000021175 00000 n
+0000021246 00000 n
+0000021317 00000 n
+0000021388 00000 n
+0000021460 00000 n
+0000021532 00000 n
+0000021604 00000 n
+0000021774 00000 n
+0000021848 00000 n
+0000021920 00000 n
+0000021992 00000 n
+0000022064 00000 n
+0000022136 00000 n
+0000022208 00000 n
+0000022280 00000 n
+0000022352 00000 n
+0000022424 00000 n
+0000022496 00000 n
+0000022687 00000 n
+0000022761 00000 n
+0000022833 00000 n
+0000022905 00000 n
+0000022977 00000 n
+0000023052 00000 n
+0000023124 00000 n
+0000023259 00000 n
+0000023333 00000 n
+0000023405 00000 n
+0000023508 00000 n
+0000023582 00000 n
+0000023654 00000 n
+0000023726 00000 n
+0000023798 00000 n
+0000023870 00000 n
+0000023942 00000 n
+0000024014 00000 n
+0000024089 00000 n
+0000024161 00000 n
+0000024233 00000 n
+0000024435 00000 n
+0000024510 00000 n
+0000024583 00000 n
+0000024656 00000 n
+0000024727 00000 n
+0000024800 00000 n
+0000025200 00000 n
+0000025563 00000 n
+0000025600 00000 n
+0000025637 00000 n
+0000025690 00000 n
+0000025743 00000 n
+0000025795 00000 n
+0000025845 00000 n
+0000025897 00000 n
+0000025950 00000 n
+0000026003 00000 n
+0000026056 00000 n
+0000026179 00000 n
+0000026313 00000 n
+0000026447 00000 n
+0000026570 00000 n
+0000026693 00000 n
+0000026842 00000 n
+0000027409 00000 n
+0000027617 00000 n
+0000027769 00000 n
+0000028644 00000 n
+0000028855 00000 n
+0000028995 00000 n
+0000029211 00000 n
+0000029450 00000 n
+0000029610 00000 n
+0000029816 00000 n
+0000030029 00000 n
+0000030157 00000 n
+0000030285 00000 n
+0000030413 00000 n
+0000030541 00000 n
+0000030669 00000 n
+0000030752 00000 n
+0000031389 00000 n
+0000035839 00000 n
+0000035920 00000 n
+0000036713 00000 n
+0000048023 00000 n
+0000048103 00000 n
+0000048878 00000 n
+0000061414 00000 n
+0000061491 00000 n
+0000061922 00000 n
+0000062394 00000 n
+0000067750 00000 n
+0000074317 00000 n
+0000079777 00000 n
+0000087012 00000 n
+0000088036 00000 n
+0000088379 00000 n
+0000088786 00000 n
+0000088899 00000 n
+0000089972 00000 n
trailer -<</Size 273/Root 272 0 R/Info 270 0 R/ID[(ryCwVs21NRYeVZORaVGmEw==)(ryCwVs21NRYeVZORaVGmEw==)]>> +<</Size 280/Root 279 0 R/Info 277 0 R/ID[(od6Ce8g5ofbkk30tkgkhhg==)(od6Ce8g5ofbkk30tkgkhhg==)]>> startxref -89459 +90159 %%EOF
\ No newline at end of file diff --git a/docs/design.typ b/docs/design.typ index 763dcde4..9cc05cab 100644 --- a/docs/design.typ +++ b/docs/design.typ @@ -18,7 +18,8 @@ is text and all text is equally alive, whether the shell printed it or the user typed it. It runs in three places: the terminal (libvaxis), a native SDL3 window (the -steamdeck), and the browser (wasm + SDL3 + emscripten). The rewrite exists because +steamdeck), and the browser (a freestanding wasm core driven by vanilla +JavaScript and rendered as HTML/CSS). The rewrite exists because the prototype grew three parallel implementations of one program. The rewrite has exactly one program and three thin shells. @@ -59,8 +60,9 @@ input: one event vocabulary, three dialects translated at the door. *Surface* (out): a grid of cells — grapheme, fg, bg, attrs — plus a cursor and per-pane rectangles. This is the *canonical interface*: it is literally what the -tty shell hands to vaxis, cell by cell. The SDL shells rasterize the same grid -through a glyph atlas. If it cannot be expressed in the surface, it does not +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 an attachment per pane: the SDL shells blit RGBA, the tty shell falls back to the petscii matcher, kitty graphics when available.) @@ -136,7 +138,7 @@ only goes down. As built, against the prototype's 12,072 lines of Zig: [*module*], [*lines*], [], [core (pardes.zig + look.zig + syntax + image + dump)], [4,289], [all semantics, all platforms], [modal.zig + petscii.zig (pure, unit-testable)], [1,410], [carried over, unchanged], - [shells (tty.zig; gui.zig = sdl native + web)], [2,768], [translate + rasterize only], + [shells (tty.zig; gui.zig = SDL native; web.zig + web/ = browser)], [2,768], [translate + render only], [main.zig + build.zig], [484], [three targets, codegen for queries], [*application total*], [*7,626*], [vs 12,072 — three full backends instead of one and a half], [snapshot harness (snapshot.zig + e2e_harness.zig)], [831], [the parity oracle, kept], @@ -170,8 +172,8 @@ test. = Build `zig build` produces three statically linked artifacts: `pardes` (vaxis), -`pardes-gui` (SDL3), `pardes.wasm` + shell page (emscripten). Same dependency -set as the prototype — ghostty, vaxis, uucode (shared config), zstbi, SDL +`pardes-gui` (SDL3), `pardes.wasm` + a vanilla DOM shell (wasm32-freestanding). +Same native dependency set as the prototype — ghostty, vaxis, uucode (shared config), zstbi, SDL (castholm, lazy), zig-tree-sitter + grammars — all via `zig fetch`, wired in `build.zig`. Codegen during build, consumed by comptime: tree-sitter `highlights.scm` → options module; tutor text → embedded pane content. Debug @@ -260,23 +262,23 @@ instead of looking it and stepping the list wears the themes. Colors toggles all recolor passes; Debug stats overlay; Dump writes state ZON. *Web (replay viewer parity).* Embedded dump; focus, border/move drags, wheel, -scrollbar, Colors/NextColor, pinch, render scale, and link-LOOK opening a new +scrollbar, Colors/NextColor, and link-LOOK opening a new tab (new — the prototype has no web link handling). One-finger touch is deliberately complete by itself: tap = LOOK; drag past a small slop = natural scroll, with no LOOK on release. A build-generated read-only archive lets LOOK open the current contents of Git-tracked Pardes `.zig` files despite the web shell having no host filesystem. The published launcher dump is captured from a running Pardes TTY after `git ls-files '*.zig'`, so its complete terminal -listing is the set the user can open; the published web build carries only the -Zig tree-sitter grammar and colors opened source panes without shipping unused -C/C++ parsers. A body gesture retains tap-LOOK/drag-scroll, but finger-down on +listing is the set the user can open. The freestanding module carries no host +libc, SDL, or WebGL; Tree-sitter's C runtime and the selected parsers link into +the module through a tiny local ABI shim (Zig is the compact web default). A body gesture retains +tap-LOOK/drag-scroll, but finger-down on a tagline or a one-cell-tolerant pane separator latches to a left-mouse gesture for its lifetime, keeping layout drags out of the scroll heuristic. Touch -circles, trails, and click flashes render only while Debug is enabled. SDL -shells use an -stb_truetype atlas over the SDL GPU API (SPIR-V on native, GLES3 on web), -letterboxed web scaling, pinch, and a touch debug overlay. `PARDES_TEST` -provides PPM and text-grid capture; the browser `.snap` harness drives real -Chromium touch input and reads both text and per-cell styles from the rendered +input translation and scroll/tap state live entirely in the JavaScript shell. +The native SDL shell uses an stb_truetype atlas over the SDL GPU API (SPIR-V). +The browser exposes each cell as selectable, inspectable text and applies the +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. diff --git a/docs/ideas.typ b/docs/ideas.typ index c954dbbd..3493f758 100644 --- a/docs/ideas.typ +++ b/docs/ideas.typ @@ -7,5 +7,3 @@ - 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?) - -|- web: native web client (DOM-rendered, not canvas/WASM). Use WASM only for text logic (parsing, grep, treesitter analysis, look internals) — rendering stays in native DOM/CSS for accessibility, performance, and browser-native text selection. Send structured display commands (layout tree, styled spans, cursor, scroll pos) from WASM to JS via a flat buffer or JSON. diff --git a/docs/web.md b/docs/web.md new file mode 100644 index 00000000..339509d2 --- /dev/null +++ b/docs/web.md @@ -0,0 +1,49 @@ +# DOM web backend + +The web backend keeps the existing Pardes core and replaces the old +Emscripten/SDL/WebGL shell. Zig produces a `wasm32-freestanding` module with no +imports. Vanilla JavaScript owns `requestAnimationFrame`, browser input, window +sizing, and effect/IO dispatch; HTML and CSS render selectable, accessible text. +There is no canvas. + +The default web build links Tree-sitter's C runtime and the Zig grammar inside +that same import-free module. A small freestanding compatibility layer supplies +the C ABI locally, so source panes retain the core's Tree-sitter highlighting +without Emscripten, WASI, or browser libc imports. Other grammar tiers remain +selectable with `-Dtree-sitter=minimal` or `-Dtree-sitter=full`. + +`src/web.zig` is the narrow WASM ABI. Events enter through exported functions, +and `pardes_frame` writes the canonical `Surface` into a packed 20-byte cell +array. `src/web/app.mjs` reads that array in one linear pass and patches stable +DOM nodes. Core effects become browser operations (links, downloads, clipboard) +or `pardes-io` custom events for a process-capable embedding host. + +Build a replay from a dump: + +```sh +zig build web \ + -Dplatform=web \ + -Dtarget=wasm32-freestanding \ + -Dtree-sitter=zig \ + -Ddump=test/web-snapshots/source-list.dump.zon +``` + +The output is in `zig-out/web`. Serve that directory over HTTP; browsers do not +allow a useful WASM module load from `file:` URLs. + +Two dependency-free test layers cover the boundary: + +```sh +# Real WASM in Node plus a small DOM double. +zig build web-harness \ + -Dplatform=web \ + -Dtarget=wasm32-freestanding \ + -Dtree-sitter=zig \ + -Ddump=test/web-snapshots/source-list.dump.zon + +# Real headless Chrome, DOM rendering, and browser touch input. +zig build web-e2e +``` + +Set `PARDES_CHROME` when Chrome/Chromium is not on `PATH` or in a normal +Playwright/Puppeteer cache. diff --git a/src/dump.zig b/src/dump.zig index 0f8cb810..44f61cf4 100644 --- a/src/dump.zig +++ b/src/dump.zig @@ -31,8 +31,8 @@ pub fn outPath(buf: *[1024:0]u8) ?[:0]const u8 { const md = yd.calculateMonthDay(); const ds = es.getDaySeconds(); return std.fmt.bufPrintSentinel(buf, "{s}/pardes-{d:0>4}{d:0>2}{d:0>2}-{d:0>2}{d:0>2}{d:0>2}.zon", .{ - dir, yd.year, md.month.numeric(), - @as(u8, md.day_index) + 1, ds.getHoursIntoDay(), ds.getMinutesIntoHour(), + dir, yd.year, md.month.numeric(), + @as(u8, md.day_index) + 1, ds.getHoursIntoDay(), ds.getMinutesIntoHour(), ds.getSecondsIntoMinute(), }, 0) catch null; } @@ -156,7 +156,7 @@ pub fn readZon(gpa: std.mem.Allocator, bytes: []const u8, label: []const u8) !St } pub fn readZonZ(gpa: std.mem.Allocator, source: [:0]const u8, label: []const u8) !State { - if (builtin.os.tag == .emscripten) { + if (builtin.os.tag == .emscripten or builtin.os.tag == .freestanding) { const state = std.zon.parse.fromSliceAlloc(State, gpa, source, null, .{}) catch |err| { log.err("parse dump {s}: {s}", .{ label, @errorName(err) }); return err; diff --git a/src/web.zig b/src/web.zig new file mode 100644 index 00000000..cebbeebf --- /dev/null +++ b/src/web.zig @@ -0,0 +1,481 @@ +//! Freestanding WebAssembly boundary for the DOM backend. +//! +//! This file deliberately contains no browser imports. JavaScript owns the +//! clock and every IO operation, feeds events through this ABI, and reads one +//! packed cell buffer after `pardes_frame`. Keeping that boundary flat makes +//! the same module directly instantiable by the dependency-free Node harness. + +const std = @import("std"); +const pardes = @import("pardes.zig"); + +const gpa = std.heap.wasm_allocator; +const embedded_dump = @embedFile("embedded.dump.zon"); +const input_capacity = 64 * 1024; +const c_alloc_header_size = 16; + +comptime { + if (c_alloc_header_size < @sizeOf(usize)) @compileError("C allocation header is too small"); +} + +/// Tree-sitter's C runtime is linked into this module. Its conventional +/// allocation symbols share the same wasm allocator as the Zig core and keep +/// the original allocation size in a 16-byte, max-aligned prefix. +export fn malloc(size_arg: usize) callconv(.c) ?*anyopaque { + const size = @max(size_arg, 1); + const total = std.math.add(usize, c_alloc_header_size, size) catch return null; + const bytes = gpa.alignedAlloc(u8, .@"16", total) catch return null; + const header: *align(16) usize = @ptrCast(bytes.ptr); + header.* = total; + return @ptrCast(bytes.ptr + c_alloc_header_size); +} + +export fn calloc(count: usize, size: usize) callconv(.c) ?*anyopaque { + const len = std.math.mul(usize, count, size) catch return null; + const ptr = malloc(len) orelse return null; + if (len != 0) { + const bytes: [*]u8 = @ptrCast(ptr); + @memset(bytes[0..len], 0); + } + return ptr; +} + +export fn free(ptr: ?*anyopaque) callconv(.c) void { + const data = ptr orelse return; + const data_bytes: [*]u8 = @ptrCast(data); + const base: [*]align(16) u8 = @ptrCast(@alignCast(data_bytes - c_alloc_header_size)); + const header: *align(16) usize = @ptrCast(base); + gpa.free(base[0..header.*]); +} + +export fn realloc(ptr: ?*anyopaque, size: usize) callconv(.c) ?*anyopaque { + if (ptr == null) return malloc(size); + if (size == 0) { + free(ptr); + return null; + } + const data_bytes: [*]u8 = @ptrCast(ptr.?); + const base: [*]align(16) u8 = @ptrCast(@alignCast(data_bytes - c_alloc_header_size)); + const header: *align(16) usize = @ptrCast(base); + const old_size = header.* - c_alloc_header_size; + const replacement = malloc(size) orelse return null; + const replacement_bytes: [*]u8 = @ptrCast(replacement); + @memcpy(replacement_bytes[0..@min(old_size, size)], data_bytes[0..@min(old_size, size)]); + free(ptr); + return replacement; +} + +pub const std_options: std.Options = .{ .logFn = logFn }; +pub const panic = std.debug.FullPanic(panicImpl); + +fn logFn( + comptime _: std.log.Level, + comptime _: @EnumLiteral(), + comptime _: []const u8, + _: anytype, +) void {} + +fn panicImpl(_: []const u8, _: ?usize) noreturn { + @trap(); +} + +const WebCell = extern struct { + // UTF-8 graphemes in the core are at most seven bytes. The eighth byte is + // always zero, which also makes a debugger's memory view pleasant. + text: [8]u8, + fg: u32, + bg: u32, + attrs: u16, + len: u8, + flags: u8, +}; + +comptime { + if (@sizeOf(WebCell) != 20) @compileError("the JS frame ABI changed"); +} + +const State = struct { + core: *pardes.Pardes, + arena: std.heap.ArenaAllocator, + cells: std.ArrayList(WebCell) = .empty, + effect_data: std.ArrayList(u8) = .empty, + + fn deinit(s: *State) void { + s.core.deinit(); + s.arena.deinit(); + s.cells.deinit(gpa); + s.effect_data.deinit(gpa); + } +}; + +var state: ?State = null; +var input: [input_capacity]u8 = undefined; +var last_error: u32 = 0; +var effect_aux0: u32 = 0; +var effect_aux1: u32 = 0; + +export fn pardes_init(cols_arg: u32, rows_arg: u32) u32 { + if (state) |*old| old.deinit(); + state = null; + last_error = 0; + + const cols: u16 = @intCast(std.math.clamp(cols_arg, 1, std.math.maxInt(u16))); + const rows: u16 = @intCast(std.math.clamp(rows_arg, 1, std.math.maxInt(u16))); + const core = pardes.Pardes.initFromDump(gpa, .{ .cols = cols, .rows = rows }, embedded_dump) catch |err| { + last_error = @intFromError(err); + return last_error; + }; + state = .{ .core = core, .arena = .init(gpa) }; + core.update(.{ .resize = .{ .cols = cols, .rows = rows } }); + return 0; +} + +export fn pardes_deinit() void { + if (state) |*s| s.deinit(); + state = null; +} + +export fn pardes_last_error() u32 { + return last_error; +} + +export fn pardes_input_ptr() u32 { + return @intCast(@intFromPtr(&input)); +} + +export fn pardes_input_capacity() u32 { + return input_capacity; +} + +export fn pardes_resize(cols_arg: u32, rows_arg: u32) void { + const s = &(state orelse return); + const cols: u16 = @intCast(std.math.clamp(cols_arg, 1, std.math.maxInt(u16))); + const rows: u16 = @intCast(std.math.clamp(rows_arg, 1, std.math.maxInt(u16))); + if (s.core.screen_w == cols and s.core.screen_h == rows) return; + s.core.update(.{ .resize = .{ .cols = cols, .rows = rows } }); +} + +/// `mods`: bit 0 ctrl, bit 1 alt, bit 2 shift. `len` bytes at input are the +/// browser's composed text for printable keys and zero for functional keys. +export fn pardes_key(cp_arg: u32, len_arg: u32, mods: u32) void { + const s = &(state orelse return); + if (cp_arg > std.math.maxInt(u21)) return; + const len: usize = @min(len_arg, input.len); + s.core.update(.{ .key = .{ + .cp = @intCast(cp_arg), + .text = input[0..len], + .ctrl = mods & 1 != 0, + .alt = mods & 2 != 0, + .shift = mods & 4 != 0, + } }); +} + +export fn pardes_paste(len_arg: u32) void { + const s = &(state orelse return); + const len: usize = @min(len_arg, input.len); + s.core.update(.{ .paste = input[0..len] }); +} + +export fn pardes_output(pane: u32, len_arg: u32) void { + const s = &(state orelse return); + if (pane >= pardes.MAX_PANES) return; + const len: usize = @min(len_arg, input.len); + s.core.update(.{ .output = .{ .pane = @intCast(pane), .bytes = input[0..len] } }); +} + +export fn pardes_lsp_response(id: u32, len_arg: u32) void { + const s = &(state orelse return); + const len: usize = @min(len_arg, input.len); + s.core.update(.{ .lsp_resp = .{ .id = id, .rows = input[0..len] } }); +} + +export fn pardes_file_changed(pane: u32, len_arg: u32) void { + const s = &(state orelse return); + if (pane >= pardes.MAX_PANES) return; + const len: usize = @min(len_arg, input.len); + s.core.update(.{ .file_changed = .{ .pane = @intCast(pane), .bytes = input[0..len] } }); +} + +export fn pardes_set_cwd(pane: u32, len_arg: u32) void { + const s = &(state orelse return); + if (pane >= pardes.MAX_PANES) return; + const len: usize = @min(len_arg, input.len); + s.core.setCwd(@intCast(pane), input[0..len]); +} + +export fn pardes_eof(pane: u32) void { + const s = &(state orelse return); + if (pane < pardes.MAX_PANES) s.core.update(.{ .eof = .{ .pane = @intCast(pane) } }); +} + +/// Buttons and kinds are their boundary enum ordinals. Invalid values are +/// ignored rather than reaching an unchecked enum cast inside wasm. +export fn pardes_mouse(button_arg: u32, kind_arg: u32, col_arg: u32, row_arg: u32, mods: u32) void { + const s = &(state orelse return); + const button: pardes.Mouse.Button = switch (button_arg) { + 0 => .left, + 1 => .middle, + 2 => .right, + 3 => .wheel_up, + 4 => .wheel_down, + 5 => .wheel_left, + 6 => .wheel_right, + 7 => .none, + else => return, + }; + const kind: pardes.Mouse.Kind = switch (kind_arg) { + 0 => .press, + 1 => .release, + 2 => .motion, + 3 => .drag, + else => return, + }; + s.core.update(.{ .mouse = .{ + .button = button, + .kind = kind, + .col = @intCast(@min(col_arg, std.math.maxInt(u16))), + .row = @intCast(@min(row_arg, std.math.maxInt(u16))), + .ctrl = mods & 1 != 0, + } }); +} + +export fn pardes_touch_scroll(delta: f32) void { + const s = &(state orelse return); + s.core.update(.{ .touch_scroll = delta }); +} + +export fn pardes_tick() void { + const s = &(state orelse return); + s.core.update(.tick); +} + +/// Render the canonical Surface into a stable, packed JS-facing buffer. +/// Returns the number of cells, or zero if initialization/rendering failed. +export fn pardes_frame() u32 { + const s = &(state orelse return 0); + _ = s.arena.reset(.retain_capacity); + const surface = s.core.render(s.arena.allocator()) catch |err| { + last_error = @intFromError(err); + return 0; + }; + const count: usize = @as(usize, surface.cols) * surface.rows; + s.cells.resize(gpa, count) catch |err| { + last_error = @intFromError(err); + return 0; + }; + for (surface.cells, s.cells.items) |cell, *out| { + out.* = .{ + .text = @splat(0), + .fg = encodeColor(cell.style.fg), + .bg = encodeColor(cell.style.bg), + .attrs = encodeAttrs(cell.style), + .len = if (cell.default) 1 else cell.len, + .flags = @intFromBool(cell.default), + }; + if (cell.default) out.text[0] = ' ' else @memcpy(out.text[0..cell.len], cell.grapheme()); + } + return @intCast(count); +} + +export fn pardes_frame_ptr() u32 { + const s = &(state orelse return 0); + return if (s.cells.items.len == 0) 0 else @intCast(@intFromPtr(s.cells.items.ptr)); +} + +export fn pardes_frame_cols() u32 { + const s = &(state orelse return 0); + return s.core.screen_w; +} + +export fn pardes_frame_rows() u32 { + const s = &(state orelse return 0); + return s.core.screen_h; +} + +export fn pardes_cursor_x() i32 { + const s = &(state orelse return -1); + return if (s.core.surface.cursor) |cursor| cursor.x else -1; +} + +export fn pardes_cursor_y() i32 { + const s = &(state orelse return -1); + return if (s.core.surface.cursor) |cursor| cursor.y else -1; +} + +export fn pardes_cursor_bar() u32 { + const s = &(state orelse return 0); + return @intFromBool(if (s.core.surface.cursor) |cursor| cursor.bar else false); +} + +/// The browser's touch policy asks whether a gesture began on layout chrome. +/// A hit returns row:col packed into u32, including snapping near a separator; +/// 0xffffffff means body text and therefore one-finger scroll/tap-as-LOOK. +export fn pardes_chrome_target(col_arg: u32, row_arg: u32) u32 { + const s = &(state orelse return std.math.maxInt(u32)); + const col: u16 = @intCast(@min(col_arg, std.math.maxInt(u16))); + const row: u16 = @intCast(@min(row_arg, std.math.maxInt(u16))); + const target = chromeTarget(s.core, col, row) orelse return std.math.maxInt(u32); + return (@as(u32, target.row) << 16) | target.col; +} + +// Effect codes consumed by app.mjs. Unknown/host-specific effects are still +// surfaced as `pardes-io` CustomEvents, so embedding the static shell in a +// process-capable host does not require another wasm boundary. +export fn pardes_effect_next() u32 { + const s = &(state orelse return 0); + s.effect_data.clearRetainingCapacity(); + effect_aux0 = 0; + effect_aux1 = 0; + const effect = s.core.nextEffect() orelse return 0; + return switch (effect) { + .spawn => |e| blk: { + effect_aux0 = e.pane; + putEffect(s, e.cwd.slice()); + break :blk 1; + }, + .write => |e| blk: { + effect_aux0 = e.pane; + putEffect(s, e.bytes.slice()); + break :blk 2; + }, + .resize_pty => |e| blk: { + effect_aux0 = e.pane; + effect_aux1 = (@as(u32, e.cols) << 16) | e.rows; + break :blk 3; + }, + .open_link => |e| blk: { + putEffect(s, e.slice()); + break :blk 4; + }, + .save_file => |e| blk: { + effect_aux0 = e.pane; + if (s.core.panes[e.pane]) |pane| if (pane.file) |file| { + putU32(s, @intCast(file.path.len)); + appendEffect(s, file.path); + appendEffect(s, file.content); + }; + break :blk 5; + }, + .write_dump => blk: { + if (s.core.dump_out) |bytes| putEffect(s, bytes); + break :blk 6; + }, + .set_clipboard => blk: { + if (s.core.yank) |bytes| putEffect(s, bytes); + break :blk 7; + }, + .lsp => |e| blk: { + effect_aux0 = e.id; + effect_aux1 = e.pane; + putEffect(s, e.arg.slice()); + break :blk 8; + }, + .watch => |e| blk: { + effect_aux0 = e.pane; + effect_aux1 = @intFromBool(e.on); + if (e.on) if (s.core.panes[e.pane]) |pane| if (pane.file) |file| putEffect(s, file.path); + break :blk 9; + }, + .quit => 10, + }; +} + +export fn pardes_effect_ptr() u32 { + const s = &(state orelse return 0); + return if (s.effect_data.items.len == 0) 0 else @intCast(@intFromPtr(s.effect_data.items.ptr)); +} + +export fn pardes_effect_len() u32 { + const s = &(state orelse return 0); + return @intCast(s.effect_data.items.len); +} + +export fn pardes_effect_aux0() u32 { + return effect_aux0; +} + +export fn pardes_effect_aux1() u32 { + return effect_aux1; +} + +fn encodeColor(color: pardes.Color) u32 { + return switch (color) { + .default => 0x01000000, + .index => |index| 0x02000000 | @as(u32, index), + .rgb => |rgb| (@as(u32, rgb[0]) << 16) | (@as(u32, rgb[1]) << 8) | rgb[2], + }; +} + +fn encodeAttrs(style: pardes.CellStyle) u16 { + var attrs: u16 = 0; + attrs |= @as(u16, @intFromBool(style.bold)) << 0; + attrs |= @as(u16, @intFromBool(style.dim)) << 1; + attrs |= @as(u16, @intFromBool(style.italic)) << 2; + attrs |= @as(u16, @intFromBool(style.blink)) << 3; + attrs |= @as(u16, @intFromBool(style.reverse)) << 4; + attrs |= @as(u16, @intFromBool(style.invisible)) << 5; + attrs |= @as(u16, @intFromBool(style.strikethrough)) << 6; + attrs |= @as(u16, @intFromEnum(style.ul)) << 8; + return attrs; +} + +fn putEffect(s: *State, bytes: []const u8) void { + s.effect_data.clearRetainingCapacity(); + appendEffect(s, bytes); +} + +fn appendEffect(s: *State, bytes: []const u8) void { + s.effect_data.appendSlice(gpa, bytes) catch {}; +} + +fn putU32(s: *State, value: u32) void { + var bytes: [4]u8 = undefined; + std.mem.writeInt(u32, &bytes, value, .little); + appendEffect(s, &bytes); +} + +const Target = struct { col: u16, row: u16 }; + +fn chromeTarget(core: *const pardes.Pardes, col: u16, row: u16) ?Target { + if (row < pardes.TOPBAR_H) return .{ .col = col, .row = row }; + for (core.panes, 0..) |slot, id| { + if (slot == null) continue; + const rect = core.rects[id]; + if (row == rect.y and col >= rect.x and col < rect.x + rect.w and col < rect.x + pardes.config.GUTTER) + return .{ .col = col, .row = rect.y }; + } + for (0..core.ncol -| 1) |column| { + const handle = core.col_x[column] + core.col_w[column] -| 1; + if (col == handle) return .{ .col = handle, .row = row }; + } + for (0..core.ncol) |column| { + if (col < core.col_x[column] or col >= core.col_x[column] + core.col_w[column]) continue; + for (0..core.col_n[column] -| 1) |index| { + const rect = core.rects[core.col_terms[column][index]]; + const handle = rect.y + rect.h -| 1; + if (row == handle) return .{ .col = col, .row = handle }; + } + } + for (core.panes, 0..) |slot, id| { + if (slot == null) continue; + const rect = core.rects[id]; + if (row == rect.y and col >= rect.x and col < rect.x + rect.w) + return .{ .col = col, .row = rect.y }; + } + for (0..core.ncol -| 1) |column| { + const handle = core.col_x[column] + core.col_w[column] -| 1; + if (distance(col, handle) == 1) return .{ .col = handle, .row = row }; + } + for (0..core.ncol) |column| { + if (col < core.col_x[column] or col >= core.col_x[column] + core.col_w[column]) continue; + for (0..core.col_n[column] -| 1) |index| { + const rect = core.rects[core.col_terms[column][index]]; + const handle = rect.y + rect.h -| 1; + if (distance(row, handle) == 1) return .{ .col = col, .row = handle }; + } + } + return null; +} + +fn distance(a: u16, b: u16) u16 { + return if (a > b) a - b else b - a; +} diff --git a/src/web/app.mjs b/src/web/app.mjs new file mode 100644 index 00000000..3d32a16b --- /dev/null +++ b/src/web/app.mjs @@ -0,0 +1,436 @@ +const CELL_SIZE = 20; +const DEFAULT_FG = "#cccccc"; +const DEFAULT_BG = "#121212"; +const decoder = new TextDecoder(); +const encoder = new TextEncoder(); + +const BUTTON = Object.freeze({ left: 0, middle: 1, right: 2, wheelUp: 3, wheelDown: 4, wheelLeft: 5, wheelRight: 6, none: 7 }); +const MOUSE = Object.freeze({ press: 0, release: 1, motion: 2, drag: 3 }); +const SPECIAL_KEYS = Object.freeze({ + Enter: 0x0d, + Backspace: 0x7f, + Tab: 0x09, + Escape: 0x1b, + ArrowUp: 0xf0001, + ArrowDown: 0xf0002, + ArrowLeft: 0xf0003, + ArrowRight: 0xf0004, + Home: 0xf0005, + End: 0xf0006, + PageUp: 0xf0007, + PageDown: 0xf0008, + Delete: 0xf0009, +}); + +export class DomRenderer { + constructor(root, wasm) { + this.root = root; + this.wasm = wasm; + this.rows = []; + this.cells = []; + this.cols = 0; + this.rowCount = 0; + this.snapshot = null; + } + + render() { + const count = this.wasm.pardes_frame(); + const cols = this.wasm.pardes_frame_cols(); + const rows = this.wasm.pardes_frame_rows(); + if (count !== cols * rows) throw new Error(`bad frame: ${count} cells for ${cols}x${rows}`); + this.ensureGrid(cols, rows); + + const ptr = this.wasm.pardes_frame_ptr(); + const view = new DataView(this.wasm.memory.buffer, ptr, count * CELL_SIZE); + const bytes = new Uint8Array(this.wasm.memory.buffer); + const cursorX = this.wasm.pardes_cursor_x(); + const cursorY = this.wasm.pardes_cursor_y(); + const cursorBar = this.wasm.pardes_cursor_bar() !== 0; + const raw = new Array(count); + + for (let index = 0; index < count; index += 1) { + const off = index * CELL_SIZE; + const len = view.getUint8(off + 18); + const fg = view.getUint32(off + 8, true); + const bg = view.getUint32(off + 12, true); + const attrs = view.getUint16(off + 16, true); + const text = decoder.decode(bytes.subarray(ptr + off, ptr + off + len)) || " "; + const x = index % cols; + const y = Math.floor(index / cols); + const cursor = x === cursorX && y === cursorY; + const cell = this.cells[index]; + if (cell.textContent !== text) cell.textContent = text; + const styleKey = `${fg}:${bg}:${attrs}:${cursor ? 1 : 0}:${cursorBar ? 1 : 0}`; + if (cell.pardesStyle !== styleKey) { + applyStyle(cell, fg, bg, attrs, cursor, cursorBar); + cell.pardesStyle = styleKey; + } + raw[index] = { text, fg, bg, attrs }; + } + this.snapshot = { cols, rows, cells: raw, cursorX, cursorY, cursorBar }; + return this.snapshot; + } + + ensureGrid(cols, rows) { + if (cols === this.cols && rows === this.rowCount) return; + const document = this.root.ownerDocument; + const rowNodes = []; + const cells = []; + for (let y = 0; y < rows; y += 1) { + const row = document.createElement("div"); + row.className = "pardes-row"; + row.setAttribute("role", "row"); + row.dataset.row = String(y); + const rowCells = []; + for (let x = 0; x < cols; x += 1) { + const cell = document.createElement("span"); + cell.className = "pardes-cell"; + cell.setAttribute("role", "gridcell"); + cell.dataset.col = String(x); + cell.dataset.row = String(y); + cell.textContent = " "; + rowCells.push(cell); + cells.push(cell); + } + row.replaceChildren(...rowCells); + rowNodes.push(row); + } + this.root.replaceChildren(...rowNodes); + this.root.setAttribute("aria-colcount", String(cols)); + this.root.setAttribute("aria-rowcount", String(rows)); + this.rows = rowNodes; + this.cells = cells; + this.cols = cols; + this.rowCount = rows; + } +} + +export class PardesRuntime { + constructor(root, wasm) { + this.root = root; + this.wasm = wasm; + this.renderer = new DomRenderer(root, wasm); + this.inputPtr = wasm.pardes_input_ptr(); + this.inputCapacity = wasm.pardes_input_capacity(); + this.cellWidth = 1; + this.cellHeight = 1; + this.running = false; + this.pointer = null; + this.frameHandle = 0; + this.bindEvents(); + } + + start() { + this.measure(); + const { cols, rows } = this.gridSize(); + const error = this.wasm.pardes_init(cols, rows); + if (error) throw new Error(`Pardes wasm init failed (${error})`); + this.inputPtr = this.wasm.pardes_input_ptr(); + this.resize(); + this.renderer.render(); + this.drainEffects(); + this.running = true; + this.frameHandle = requestAnimationFrame(() => this.frame()); + this.root.focus({ preventScroll: true }); + } + + stop() { + this.running = false; + if (this.frameHandle) cancelAnimationFrame(this.frameHandle); + this.wasm.pardes_deinit(); + } + + frame() { + if (!this.running) return; + this.wasm.pardes_tick(); + this.renderer.render(); + this.drainEffects(); + this.frameHandle = requestAnimationFrame(() => this.frame()); + } + + measure() { + const probe = this.root.ownerDocument.createElement("span"); + probe.className = "pardes-probe"; + probe.textContent = "M"; + this.root.ownerDocument.body.append(probe); + const rect = probe.getBoundingClientRect(); + probe.remove(); + this.cellWidth = Math.max(1, rect.width); + this.cellHeight = Math.max(1, rect.height); + } + + gridSize() { + return { + cols: Math.max(1, Math.floor(this.root.clientWidth / this.cellWidth)), + rows: Math.max(1, Math.floor(this.root.clientHeight / this.cellHeight)), + }; + } + + resize() { + this.measure(); + const { cols, rows } = this.gridSize(); + this.wasm.pardes_resize(cols, rows); + } + + writeInput(text) { + const data = encoder.encode(text); + if (data.length > this.inputCapacity) throw new Error("browser input exceeds wasm transfer buffer"); + new Uint8Array(this.wasm.memory.buffer, this.inputPtr, data.length).set(data); + return data.length; + } + + key(event) { + const mods = modifiers(event); + let cp = SPECIAL_KEYS[event.key]; + let text = ""; + if (cp === undefined && event.key.length > 0 && [...event.key].length === 1) { + text = mods & 3 ? "" : event.key; + const shortcut = event.code?.match(/^Key([A-Z])$/); + cp = shortcut && (mods & 3) ? shortcut[1].toLowerCase().codePointAt(0) : event.key.codePointAt(0); + } + if (cp === undefined) return; + event.preventDefault(); + this.wasm.pardes_key(cp, this.writeInput(text), mods); + } + + point(event) { + const rect = this.root.getBoundingClientRect(); + return { + col: clamp(Math.floor((event.clientX - rect.left) / this.cellWidth), 0, Math.max(0, this.wasm.pardes_frame_cols() - 1)), + row: clamp(Math.floor((event.clientY - rect.top) / this.cellHeight), 0, Math.max(0, this.wasm.pardes_frame_rows() - 1)), + }; + } + + cellPoint(col, row) { + const rect = this.root.getBoundingClientRect(); + return { x: rect.left + (col + 0.5) * this.cellWidth, y: rect.top + (row + 0.5) * this.cellHeight }; + } + + pointerDown(event) { + event.preventDefault(); + this.root.focus({ preventScroll: true }); + this.root.setPointerCapture?.(event.pointerId); + const point = this.point(event); + if (event.pointerType === "touch") { + const packed = this.wasm.pardes_chrome_target(point.col, point.row) >>> 0; + const chrome = packed !== 0xffffffff; + const snapped = chrome ? { col: packed & 0xffff, row: packed >>> 16 } : point; + this.pointer = { id: event.pointerId, touch: true, chrome, startX: event.clientX, startY: event.clientY, lastX: event.clientX, lastY: event.clientY, point: snapped, scrolling: false, accum: 0 }; + if (chrome) this.wasm.pardes_mouse(BUTTON.left, MOUSE.press, snapped.col, snapped.row, modifiers(event)); + return; + } + const button = pointerButton(event.button); + this.pointer = { id: event.pointerId, touch: false, button }; + this.wasm.pardes_mouse(button, MOUSE.press, point.col, point.row, modifiers(event)); + } + + pointerMove(event) { + const point = this.point(event); + const active = this.pointer?.id === event.pointerId ? this.pointer : null; + if (!active) { + if (event.pointerType !== "touch") this.wasm.pardes_mouse(BUTTON.none, MOUSE.motion, point.col, point.row, modifiers(event)); + return; + } + event.preventDefault(); + if (!active.touch) { + this.wasm.pardes_mouse(active.button, MOUSE.drag, point.col, point.row, modifiers(event)); + return; + } + if (active.chrome) { + this.wasm.pardes_mouse(BUTTON.left, MOUSE.drag, point.col, point.row, modifiers(event)); + return; + } + const travel = Math.hypot(event.clientX - active.startX, event.clientY - active.startY); + if (travel > 8) active.scrolling = true; + if (active.scrolling) { + const dy = event.clientY - active.lastY; + active.accum += dy / this.cellHeight; + while (active.accum <= -1) { + this.wasm.pardes_mouse(BUTTON.wheelDown, MOUSE.press, point.col, point.row, 0); + active.accum += 1; + } + while (active.accum >= 1) { + this.wasm.pardes_mouse(BUTTON.wheelUp, MOUSE.press, point.col, point.row, 0); + active.accum -= 1; + } + this.wasm.pardes_touch_scroll(dy / this.cellHeight); + } + active.lastX = event.clientX; + active.lastY = event.clientY; + } + + pointerUp(event) { + const active = this.pointer?.id === event.pointerId ? this.pointer : null; + if (!active) return; + event.preventDefault(); + const point = this.point(event); + if (active.touch) { + if (active.chrome) this.wasm.pardes_mouse(BUTTON.left, MOUSE.release, point.col, point.row, modifiers(event)); + else if (!active.scrolling) { + this.wasm.pardes_mouse(BUTTON.right, MOUSE.press, point.col, point.row, 0); + this.wasm.pardes_mouse(BUTTON.right, MOUSE.release, point.col, point.row, 0); + } + } else { + this.wasm.pardes_mouse(active.button, MOUSE.release, point.col, point.row, modifiers(event)); + } + if (this.root.hasPointerCapture?.(event.pointerId)) this.root.releasePointerCapture(event.pointerId); + this.pointer = null; + } + + wheel(event) { + event.preventDefault(); + const point = this.point(event); + if (event.deltaY) this.wasm.pardes_mouse(event.deltaY < 0 ? BUTTON.wheelUp : BUTTON.wheelDown, MOUSE.press, point.col, point.row, modifiers(event)); + if (event.deltaX) this.wasm.pardes_mouse(event.deltaX < 0 ? BUTTON.wheelLeft : BUTTON.wheelRight, MOUSE.press, point.col, point.row, modifiers(event)); + } + + paste(event) { + const text = event.clipboardData?.getData("text/plain"); + if (text === undefined) return; + event.preventDefault(); + this.wasm.pardes_paste(this.writeInput(text)); + } + + bindEvents() { + this.root.addEventListener("keydown", (event) => this.key(event)); + this.root.addEventListener("paste", (event) => this.paste(event)); + this.root.addEventListener("contextmenu", (event) => event.preventDefault()); + this.root.addEventListener("pointerdown", (event) => this.pointerDown(event)); + this.root.addEventListener("pointermove", (event) => this.pointerMove(event)); + this.root.addEventListener("pointerup", (event) => this.pointerUp(event)); + this.root.addEventListener("pointercancel", (event) => this.pointerUp(event)); + this.root.addEventListener("wheel", (event) => this.wheel(event), { passive: false }); + globalThis.addEventListener?.("resize", () => this.resize()); + } + + drainEffects() { + for (;;) { + const kind = this.wasm.pardes_effect_next(); + if (!kind) return; + const ptr = this.wasm.pardes_effect_ptr(); + const len = this.wasm.pardes_effect_len(); + const data = new Uint8Array(len); + if (len) data.set(new Uint8Array(this.wasm.memory.buffer, ptr, len)); + const aux0 = this.wasm.pardes_effect_aux0(); + const aux1 = this.wasm.pardes_effect_aux1(); + this.handleEffect(kind, data, aux0, aux1); + } + } + + handleEffect(kind, data, aux0, aux1) { + if (kind === 4) { + globalThis.open?.(decoder.decode(data), "_blank", "noopener,noreferrer"); + } else if (kind === 5) { + const view = new DataView(data.buffer, data.byteOffset, data.byteLength); + const pathLength = data.length >= 4 ? view.getUint32(0, true) : 0; + const path = decoder.decode(data.subarray(4, 4 + pathLength)); + download(data.subarray(4 + pathLength), path.split("/").pop() || "pardes.txt", "text/plain;charset=utf-8"); + } else if (kind === 6) { + download(data, "pardes-dump.zon", "text/plain;charset=utf-8"); + } else if (kind === 7) { + navigator.clipboard?.writeText(decoder.decode(data)).catch(() => {}); + } else if (kind === 10) { + this.running = false; + } else { + this.root.dispatchEvent(new CustomEvent("pardes-io", { detail: { kind, data, aux0, aux1 } })); + } + } +} + +export async function instantiate(url = "./pardes.wasm") { + const response = await fetch(url); + if (!response.ok) throw new Error(`fetch ${url}: HTTP ${response.status}`); + if (WebAssembly.instantiateStreaming) { + try { + return (await WebAssembly.instantiateStreaming(response.clone(), {})).instance.exports; + } catch (error) { + if (!/mime|content-type/i.test(String(error))) throw error; + } + } + return (await WebAssembly.instantiate(await response.arrayBuffer(), {})).instance.exports; +} + +export async function boot(root, url = "./pardes.wasm") { + if (!root) throw new Error("Pardes DOM root is missing"); + const wasm = await instantiate(url); + const runtime = new PardesRuntime(root, wasm); + runtime.start(); + const test = { + grid: () => runtime.renderer.snapshot, + cellPoint: (col, row) => runtime.cellPoint(col, row), + }; + globalThis.pardes = { runtime, wasm, test }; + return runtime; +} + +function applyStyle(cell, fgCode, bgCode, attrs, cursor, cursorBar) { + let fg = color(fgCode, DEFAULT_FG); + let bg = color(bgCode, DEFAULT_BG); + if (attrs & (1 << 4)) [fg, bg] = [bg, fg]; + if (attrs & (1 << 5)) fg = bg; + if (attrs & (1 << 1)) fg = dim(fg); + cell.style.color = fg; + cell.style.backgroundColor = bg; + cell.style.fontWeight = attrs & 1 ? "700" : "400"; + cell.style.fontStyle = attrs & (1 << 2) ? "italic" : "normal"; + const underline = (attrs >>> 8) & 7; + cell.style.textDecorationLine = `${underline ? "underline " : ""}${attrs & (1 << 6) ? "line-through" : ""}`.trim() || "none"; + cell.style.textDecorationStyle = ["solid", "solid", "double", "wavy", "dotted", "dashed"][underline] || "solid"; + cell.style.setProperty("--cell-fg", fg); + cell.style.setProperty("--cell-bg", bg); + cell.classList.toggle("is-cursor", cursor); + cell.classList.toggle("is-bar", cursor && cursorBar); + cell.classList.toggle("is-blinking", Boolean(attrs & (1 << 3))); +} + +function color(encoded, fallback) { + if (encoded === 0x01000000) return fallback; + if ((encoded & 0xff000000) === 0x02000000) return indexedColor(encoded & 0xff); + return `#${(encoded & 0xffffff).toString(16).padStart(6, "0")}`; +} + +function indexedColor(index) { + const ansi = [ + "#000000", "#cc0000", "#4e9a06", "#c4a000", "#3465a4", "#75507b", "#06989a", "#d3d7cf", + "#555753", "#ef2929", "#8ae234", "#fce94f", "#729fcf", "#ad7fa8", "#34e2e2", "#eeeeec", + ]; + if (index < 16) return ansi[index]; + if (index >= 232) { + const level = 8 + (index - 232) * 10; + return rgb(level, level, level); + } + const n = index - 16; + const level = (part) => part === 0 ? 0 : 55 + part * 40; + return rgb(level(Math.floor(n / 36)), level(Math.floor(n / 6) % 6), level(n % 6)); +} + +function dim(value) { + const n = Number.parseInt(value.slice(1), 16); + return rgb(((n >>> 16) & 0xff) * 0.6, ((n >>> 8) & 0xff) * 0.6, (n & 0xff) * 0.6); +} + +function rgb(r, g, b) { + return `#${[r, g, b].map((part) => Math.round(part).toString(16).padStart(2, "0")).join("")}`; +} + +function modifiers(event) { + return (event.ctrlKey ? 1 : 0) | (event.altKey ? 2 : 0) | (event.shiftKey ? 4 : 0); +} + +function pointerButton(button) { + return button === 1 ? BUTTON.middle : button === 2 ? BUTTON.right : BUTTON.left; +} + +function clamp(value, low, high) { + return Math.max(low, Math.min(high, value)); +} + +function download(bytes, name, type) { + if (typeof document === "undefined") return; + const url = URL.createObjectURL(new Blob([bytes], { type })); + const anchor = document.createElement("a"); + anchor.href = url; + anchor.download = name; + anchor.click(); + setTimeout(() => URL.revokeObjectURL(url), 0); +} diff --git a/src/web/index.html b/src/web/index.html new file mode 100644 index 00000000..4012c313 --- /dev/null +++ b/src/web/index.html @@ -0,0 +1,17 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Pardes</title> + <link rel="stylesheet" href="pardes.css"> +</head> +<body> + <main id="pardes" class="pardes" role="grid" aria-label="Pardes text environment" tabindex="0"></main> + <noscript>Pardes needs JavaScript to drive its WebAssembly core.</noscript> + <script type="module"> + import { boot } from "./app.mjs"; + boot(document.getElementById("pardes")); + </script> +</body> +</html> diff --git a/src/web/libc.c b/src/web/libc.c new file mode 100644 index 00000000..c49fa03e --- /dev/null +++ b/src/web/libc.c @@ -0,0 +1,152 @@ +/* Minimal libc-shaped surface used by Tree-sitter inside the freestanding + * browser module. Allocation lives in web.zig so it shares Zig's wasm heap; + * these routines neither import browser functions nor perform IO. */ +#include <ctype.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <wctype.h> + +_Noreturn void abort(void) { + __builtin_trap(); +} + +size_t strlen(const char *str) { + const char *end = str; + while (*end) end++; + return (size_t)(end - str); +} + +char *strchr(const char *str, int character) { + const unsigned char needle = (unsigned char)character; + do { + if ((unsigned char)*str == needle) return (char *)str; + } while (*str++); + return (char *)0; +} + +void *memchr(const void *ptr, int value, size_t count) { + const unsigned char *bytes = (const unsigned char *)ptr; + const unsigned char needle = (unsigned char)value; + for (size_t index = 0; index < count; index++) { + if (bytes[index] == needle) return (void *)(bytes + index); + } + return (void *)0; +} + +int strcmp(const char *lhs, const char *rhs) { + while (*lhs && (unsigned char)*lhs == (unsigned char)*rhs) { + lhs++; + rhs++; + } + return (int)(unsigned char)*lhs - (int)(unsigned char)*rhs; +} + +int strncmp(const char *lhs, const char *rhs, size_t count) { + while (count && *lhs && (unsigned char)*lhs == (unsigned char)*rhs) { + lhs++; + rhs++; + count--; + } + return count ? (int)(unsigned char)*lhs - (int)(unsigned char)*rhs : 0; +} + +char *strncpy(char *restrict dest, const char *restrict src, size_t count) { + char *result = dest; + while (count && *src) { + *dest++ = *src++; + count--; + } + while (count--) *dest++ = '\0'; + return result; +} + +int isprint(int character) { + return character >= 0x20 && character <= 0x7e; +} + +int isdigit(int character) { + return character >= '0' && character <= '9'; +} + +int iswspace(wint_t character) { + return character == ' ' || (character >= '\t' && character <= '\r'); +} + +int iswalnum(wint_t character) { + return iswdigit(character) || iswalpha(character); +} + +int iswalpha(wint_t character) { + return (character >= 'A' && character <= 'Z') || + (character >= 'a' && character <= 'z') || character >= 0x80; +} + +int iswblank(wint_t character) { + return character == ' ' || character == '\t'; +} + +int iswdigit(wint_t character) { + return character >= '0' && character <= '9'; +} + +int iswlower(wint_t character) { + return character >= 'a' && character <= 'z'; +} + +int iswupper(wint_t character) { + return character >= 'A' && character <= 'Z'; +} + +int iswxdigit(wint_t character) { + return iswdigit(character) || (character >= 'A' && character <= 'F') || + (character >= 'a' && character <= 'f'); +} + +wint_t towupper(wint_t character) { + return iswlower(character) ? character - ('a' - 'A') : character; +} + +int vsnprintf(char *restrict buffer, size_t size, const char *restrict format, va_list args) { + (void)format; + (void)args; + if (size) buffer[0] = '\0'; + return 0; +} + +int snprintf(char *restrict buffer, size_t size, const char *restrict format, ...) { + va_list args; + va_start(args, format); + int result = vsnprintf(buffer, size, format, args); + va_end(args); + return result; +} + +int fprintf(FILE *stream, const char *format, ...) { + (void)stream; + (void)format; + return 0; +} + +int fclose(FILE *stream) { + (void)stream; + return 0; +} + +FILE *fdopen(int descriptor, const char *mode) { + (void)descriptor; + (void)mode; + return (FILE *)0; +} + +int fputc(int character, FILE *stream) { + (void)stream; + return character; +} + +int fputs(const char *string, FILE *stream) { + (void)stream; + return (int)strlen(string); +} diff --git a/src/web/libc/include/assert.h b/src/web/libc/include/assert.h new file mode 100644 index 00000000..fd018947 --- /dev/null +++ b/src/web/libc/include/assert.h @@ -0,0 +1,14 @@ +#ifndef PARDES_WEB_ASSERT_H +#define PARDES_WEB_ASSERT_H + +#ifndef static_assert +#define static_assert _Static_assert +#endif + +#ifdef NDEBUG +#define assert(expression) ((void)0) +#else +#define assert(expression) ((expression) ? (void)0 : __builtin_trap()) +#endif + +#endif diff --git a/src/web/libc/include/ctype.h b/src/web/libc/include/ctype.h new file mode 100644 index 00000000..985c8759 --- /dev/null +++ b/src/web/libc/include/ctype.h @@ -0,0 +1,7 @@ +#ifndef PARDES_WEB_CTYPE_H +#define PARDES_WEB_CTYPE_H + +int isprint(int character); +int isdigit(int character); + +#endif diff --git a/src/web/libc/include/endian.h b/src/web/libc/include/endian.h new file mode 100644 index 00000000..a047c88f --- /dev/null +++ b/src/web/libc/include/endian.h @@ -0,0 +1,21 @@ +#ifndef PARDES_WEB_ENDIAN_H +#define PARDES_WEB_ENDIAN_H + +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#define __BYTE_ORDER __LITTLE_ENDIAN + +#define htole16(value) (value) +#define le16toh(value) (value) +#define htole32(value) (value) +#define le32toh(value) (value) +#define htole64(value) (value) +#define le64toh(value) (value) +#define htobe16(value) __builtin_bswap16(value) +#define be16toh(value) __builtin_bswap16(value) +#define htobe32(value) __builtin_bswap32(value) +#define be32toh(value) __builtin_bswap32(value) +#define htobe64(value) __builtin_bswap64(value) +#define be64toh(value) __builtin_bswap64(value) + +#endif diff --git a/src/web/libc/include/inttypes.h b/src/web/libc/include/inttypes.h new file mode 100644 index 00000000..7e795746 --- /dev/null +++ b/src/web/libc/include/inttypes.h @@ -0,0 +1,8 @@ +#ifndef PARDES_WEB_INTTYPES_H +#define PARDES_WEB_INTTYPES_H + +#include <stdint.h> + +#define PRId32 "d" + +#endif diff --git a/src/web/libc/include/limits.h b/src/web/libc/include/limits.h new file mode 100644 index 00000000..6ba31375 --- /dev/null +++ b/src/web/libc/include/limits.h @@ -0,0 +1,9 @@ +#ifndef PARDES_WEB_LIMITS_H +#define PARDES_WEB_LIMITS_H + +#define CHAR_BIT __CHAR_BIT__ +#define INT_MAX __INT_MAX__ +#define UINT_MAX (__INT_MAX__ * 2U + 1U) +#define USHRT_MAX (__SHRT_MAX__ * 2U + 1U) + +#endif diff --git a/src/web/libc/include/stdio.h b/src/web/libc/include/stdio.h new file mode 100644 index 00000000..92895054 --- /dev/null +++ b/src/web/libc/include/stdio.h @@ -0,0 +1,19 @@ +#ifndef PARDES_WEB_STDIO_H +#define PARDES_WEB_STDIO_H + +#include <stdarg.h> +#include <stddef.h> + +typedef struct PardesWebFile FILE; + +#define stderr ((FILE *)0) + +int fprintf(FILE *stream, const char *format, ...); +int fclose(FILE *stream); +FILE *fdopen(int descriptor, const char *mode); +int fputc(int character, FILE *stream); +int fputs(const char *string, FILE *stream); +int snprintf(char *restrict buffer, size_t size, const char *restrict format, ...); +int vsnprintf(char *restrict buffer, size_t size, const char *restrict format, va_list args); + +#endif diff --git a/src/web/libc/include/stdlib.h b/src/web/libc/include/stdlib.h new file mode 100644 index 00000000..cb280904 --- /dev/null +++ b/src/web/libc/include/stdlib.h @@ -0,0 +1,16 @@ +#ifndef PARDES_WEB_STDLIB_H +#define PARDES_WEB_STDLIB_H + +#include <stddef.h> + +#ifndef NULL +#define NULL ((void *)0) +#endif + +void *malloc(size_t size); +void *calloc(size_t count, size_t size); +void *realloc(void *ptr, size_t size); +void free(void *ptr); +_Noreturn void abort(void); + +#endif diff --git a/src/web/libc/include/string.h b/src/web/libc/include/string.h new file mode 100644 index 00000000..7e5b8783 --- /dev/null +++ b/src/web/libc/include/string.h @@ -0,0 +1,17 @@ +#ifndef PARDES_WEB_STRING_H +#define PARDES_WEB_STRING_H + +#include <stddef.h> + +void *memchr(const void *ptr, int value, size_t count); +int memcmp(const void *lhs, const void *rhs, size_t count); +void *memcpy(void *restrict dest, const void *restrict src, size_t count); +void *memmove(void *dest, const void *src, size_t count); +void *memset(void *dest, int value, size_t count); +size_t strlen(const char *str); +char *strchr(const char *str, int character); +int strcmp(const char *lhs, const char *rhs); +int strncmp(const char *lhs, const char *rhs, size_t count); +char *strncpy(char *restrict dest, const char *restrict src, size_t count); + +#endif diff --git a/src/web/libc/include/wchar.h b/src/web/libc/include/wchar.h new file mode 100644 index 00000000..4888631e --- /dev/null +++ b/src/web/libc/include/wchar.h @@ -0,0 +1,9 @@ +#ifndef PARDES_WEB_WCHAR_H +#define PARDES_WEB_WCHAR_H + +#include <stddef.h> +#include <wctype.h> + +typedef __WCHAR_TYPE__ wchar_t; + +#endif diff --git a/src/web/libc/include/wctype.h b/src/web/libc/include/wctype.h new file mode 100644 index 00000000..77bbabe9 --- /dev/null +++ b/src/web/libc/include/wctype.h @@ -0,0 +1,18 @@ +#ifndef PARDES_WEB_WCTYPE_H +#define PARDES_WEB_WCTYPE_H + +#include <stddef.h> + +typedef unsigned int wint_t; + +int iswalnum(wint_t character); +int iswalpha(wint_t character); +int iswblank(wint_t character); +int iswdigit(wint_t character); +int iswlower(wint_t character); +int iswspace(wint_t character); +int iswupper(wint_t character); +int iswxdigit(wint_t character); +wint_t towupper(wint_t character); + +#endif diff --git a/src/web/pardes.css b/src/web/pardes.css new file mode 100644 index 00000000..a959e6c3 --- /dev/null +++ b/src/web/pardes.css @@ -0,0 +1,91 @@ +@font-face { + font-family: "Pardes Mono"; + src: url("./AdwaitaMono-Regular.ttf") format("truetype"); + font-display: swap; +} + +:root { + color-scheme: dark; + --pardes-bg: #121212; + --pardes-fg: #cccccc; + --pardes-font-size: 18px; + --pardes-cell-width: 9.333333px; + --pardes-cell-height: 19.459459px; +} + +html, +body { + width: 100%; + height: 100%; + margin: 0; + overflow: hidden; + background: var(--pardes-bg); +} + +.pardes { + box-sizing: border-box; + width: 100vw; + height: 100vh; + overflow: hidden; + color: var(--pardes-fg); + background: var(--pardes-bg); + font-family: "Pardes Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: var(--pardes-font-size); + font-variant-ligatures: none; + line-height: 1; + outline: none; + touch-action: none; + user-select: text; + cursor: text; +} + +.pardes-row { + display: flex; + width: max-content; + height: var(--pardes-cell-height); + line-height: var(--pardes-cell-height); + white-space: pre; +} + +.pardes-cell { + box-sizing: border-box; + display: inline-block; + flex: 0 0 var(--pardes-cell-width); + width: var(--pardes-cell-width); + height: var(--pardes-cell-height); + overflow: hidden; + text-align: left; + white-space: pre; +} + +.pardes-cell.is-cursor { + color: var(--cell-bg) !important; + background: var(--cell-fg) !important; +} + +.pardes-cell.is-cursor.is-bar { + color: var(--cell-fg) !important; + background: var(--cell-bg) !important; + box-shadow: inset 2px 0 var(--cell-fg); +} + +.pardes-cell.is-blinking { + animation: pardes-blink 1s step-end infinite; +} + +.pardes-probe { + position: fixed; + left: -10000px; + top: -10000px; + width: var(--pardes-cell-width); + height: var(--pardes-cell-height); + visibility: hidden; +} + +@keyframes pardes-blink { + 50% { opacity: 0; } +} + +@media (prefers-reduced-motion: reduce) { + .pardes-cell.is-blinking { animation: none; } +} diff --git a/src/web/zstbi.zig b/src/web/zstbi.zig new file mode 100644 index 00000000..76b144f7 --- /dev/null +++ b/src/web/zstbi.zig @@ -0,0 +1,30 @@ +//! Freestanding image-decoder shim. +//! +//! The browser core has no libc or C allocator ABI. Image bytes still survive +//! dumps, but decoding belongs in the JavaScript IO layer rather than pulling +//! stb_image and an Emscripten runtime into the state machine. + +const std = @import("std"); + +pub fn init(_: std.Io, _: std.mem.Allocator) void {} +pub fn deinit() void {} + +pub const Image = struct { + data: []u8 = &.{}, + width: u32 = 0, + height: u32 = 0, + num_components: u32 = 4, + bytes_per_component: u32 = 1, + bytes_per_row: u32 = 0, + is_hdr: bool = false, + + pub fn loadFromMemory(_: []const u8, _: u32) !Image { + return error.ImageDecodeUnavailable; + } + + pub fn resize(_: *const Image, _: u32, _: u32) Image { + return .{}; + } + + pub fn deinit(_: *Image) void {} +}; diff --git a/test/web-snapshots/README.md b/test/web-snapshots/README.md index d2ea84f8..c6564ba0 100644 --- a/test/web-snapshots/README.md +++ b/test/web-snapshots/README.md @@ -13,6 +13,7 @@ To recapture it: The visible `.body` must exactly match the command plus every path reported by Git (and the final prompt). The base64 PTY stream must contain the command -itself. `source-list.snap` then drives that dump through Chromium: it checks the -full listing, taps `build.zig`, snapshots tree-sitter style runs, resizes the -pane separator, and finally verifies body scrolling. +itself. `source-list.snap` then drives the freestanding WASM + DOM build through +Chromium: it checks the full listing, taps `build.zig`, snapshots packed cell +styles (including Tree-sitter's Zig highlighting), resizes the pane separator, +and finally verifies body scrolling. diff --git a/test/web-snapshots/source-list.golden b/test/web-snapshots/source-list.golden index d4321fbb..35f86de6 100644 --- a/test/web-snapshots/source-list.golden +++ b/test/web-snapshots/source-list.golden @@ -1,6 +1,6 @@ == snap launcher grid=120x37 cursor=4,23 -|Kill Newcol Tutor Debug Colors NextColor Crt Dump -| TTY /home/goblin/00-projects/0x4200.cafe/02-pardes-code Del +|Kill Newcol Tutor Debug NextColor Dump Find Grep Help +|$ /home/goblin/00-projects/0x4200.cafe/02-pardes-code Del | $ git ls-files '*.zig' | build.zig | src/crt.zig @@ -36,155 +36,155 @@ | | | -== snap source-open grid=120x37 cursor=7,25 -|Kill Newcol Tutor Debug Colors NextColor Crt Dump -| TTY /home/goblin/00-projects/0x4200.cafe/02-pardes-code Del -| $ git ls-files '*.zig' -| build.zig -| src/crt.zig -| src/deck.zig -| src/dump.zig -| src/gui.zig -| src/image.zig -| src/look.zig -| src/main.zig -| src/modal.zig -| src/pardes.zig -| src/petscii.zig -| src/syntax.zig -| src/tracy.zig -| src/tty.zig -| test/deck/replay.zig -| test/deck/schema.zig -| test/e2e_harness.zig -| test/hxdiff.zig -| test/snapshot.zig -| uucode_config.zig -| -| NOR build.zig Save Del -| 1 const std = @import("std"); -| 2 -| 3 pub const Platform = enum { tty, gui, web }; -| 4 pub const TreeSitterGrammars = enum { disabled, zig, minimal, full }; -| 5 const GhosttyArtifact = enum { ghostty, lib }; -| 6 -| 7 pub fn build(b: *std.Build) void { -| 8 // Default target is the Steam Deck (deckcap's trick): x86_64 linux-gnu -| 9 // with the glibc version pinned low, so a binary built on a rolling- -| 10 // release host runs on SteamOS — a native build references the host's -| 11 // newer versioned libm/libc symbols and dies with "GLIBC_2.4x not found" -| 12 // on the deck. Override with -Dtarget= as usual. +== snap source-open grid=120x37 cursor=7,2 +|Kill Newcol Tutor Debug NextColor Dump Find Grep Help +| build.zig Save Del $ /home/goblin/00-projects/0x4200.cafe/02-pardes-code Del +| 1 const std = @import("std"); $ git ls-files '*.zig' +| 2 const snap_build = @import("build/snap.zig"); build.zig +| 3 src/crt.zig +| 4 pub const Platform = enum { tty, gui, web }; src/deck.zig +| 5 src/dump.zig +| 6 /// The ZLS the language backend links, spelled once. src/gui.zig +| 7 /// build.zig.zon pins (0.16.x branch) and is passed src/image.zig +| 8 /// `-Dversion-string` (its build.zig otherwise shell src/look.zig +| 9 /// which fails on a fetched package that has no .git src/main.zig +| 10 const zls_version = "0.16.1-dev+3e0d0820"; src/modal.zig +| 11 pub const TreeSitterGrammars = enum { disabled, zig, src/pardes.zig +| 12 src/petscii.zig +| 13 pub fn build(b: *std.Build) void { src/syntax.zig +| 14 // Default target is the Steam Deck (deckcap's tr src/tracy.zig +| 15 // with the glibc version pinned low, so a binary src/tty.zig +| 16 // release host runs on SteamOS — a native build test/deck/replay.zig +| 17 // newer versioned libm/libc symbols and dies wit test/deck/schema.zig +| 18 // on the deck. Override with -Dtarget= as usual. test/e2e_harness.zig +| 19 const target = b.standardTargetOptions(.{ .defaul test/hxdiff.zig +| 20 .cpu_arch = .x86_64, test/snapshot.zig +| 21 .os_tag = .linux, uucode_config.zig +| 22 .abi = .gnu, +| 23 .glibc_version = .{ .major = 2, .minor = 38, +| 24 } }); +| 25 const requested_optimize = b.standardOptimizeOpti +| 26 const platform = b.option(Platform, "platform", " +| 27 const static = b.option(bool, "static", "statical +| 28 const dump_path = b.option([]const u8, "dump", "d +| 29 const is_web = platform == .web; +| 30 const is_web_target = target.result.cpu.arch == . +| 31 // wasm: size is the budget +| 32 const optimize = if (is_web) .ReleaseSmall else r +| 33 // The vendored C is never what we are debugging, +| 34 // the app: 90% of a Debug startup is tree-sitter +| 35 // (perf: ts_query__perform_analysis + ts_lookahe == style source-open -|0: 0-48 #969696,#2c2a3e, 49-119 d,#2c2a3e, -|1: 0-1 d,#383c54, 2-60 #969696,#2c2a3e, 61-119 d,#2c2a3e, -|2: 0-1 d,#525252, 2-119 d,d, -|3: 0-1 d,#525252, 2-119 d,d, -|4: 0-1 d,#525252, 2-119 d,d, -|5: 0-1 d,#525252, 2-119 d,d, -|6: 0-1 d,#525252, 2-119 d,d, -|7: 0-1 d,#525252, 2-119 d,d, -|8: 0-1 d,#525252, 2-119 d,d, -|9: 0-1 d,#525252, 2-119 d,d, -|10: 0-1 d,#525252, 2-119 d,d, -|11: 0-1 d,#525252, 2-119 d,d, -|12: 0-1 d,#525252, 2-119 d,d, -|13: 0-1 d,#525252, 2-119 d,d, -|14: 0-1 d,#525252, 2-119 d,d, -|15: 0-1 d,#525252, 2-119 d,d, -|16: 0-1 d,#525252, 2-119 d,d, -|17: 0-1 d,#525252, 2-119 d,d, -|18: 0-1 d,#525252, 2-119 d,d, -|19: 0-1 d,#525252, 2-119 d,d, -|20: 0-1 d,#525252, 2-119 d,d, -|21: 0-1 d,#525252, 2-119 d,d, -|22: 0-1 d,#525252, 2-119 d,d, -|23: 0-1 d,#525252, 2-119 d,d, -|24: 0-1 d,#6e4fd0, 2-23 #969696,#2c2a3e, 24-119 d,#2c2a3e, -|25: 0-1 d,#525252, 2-6 #969696,#2c2a3e, 7-18 d,d, 19-25 #cc70d8,d,b 26-26 d,d, 27-31 #8ab87a,d, 32-119 d,d, -|26: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-119 d,d, -|27: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-27 d,d, 28-31 #cc70d8,d,b 32-119 d,d, -|28: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-37 d,d, 38-41 #cc70d8,d,b 42-119 d,d, -|29: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-30 d,d, 31-34 #cc70d8,d,b 35-119 d,d, -|30: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-119 d,d, -|31: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-10 d,d, 11-12 #cc70d8,d,b 13-119 d,d, -|32: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-10 d,d, 11-81 #f0f0f0,d,b 82-119 d,d, -|33: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-10 d,d, 11-79 #f0f0f0,d,b 80-119 d,d, -|34: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-10 d,d, 11-80 #f0f0f0,d,b 81-119 d,d, -|35: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-10 d,d, 11-83 #f0f0f0,d,b 84-119 d,d, -|36: 0-1 d,#2a2a2a, 2-6 #5a5a5a,d, 7-10 d,d, 11-59 #f0f0f0,d,b 60-119 d,d, -== snap separator-resized grid=120x37 cursor=7,21 -|Kill Newcol Tutor Debug Colors NextColor Crt Dump -| TTY /home/goblin/00-projects/0x4200.cafe/02-pardes-code Del -| src/dump.zig -| src/gui.zig -| src/image.zig -| src/look.zig -| src/main.zig -| src/modal.zig -| src/pardes.zig -| src/petscii.zig -| src/syntax.zig -| src/tracy.zig -| src/tty.zig -| test/deck/replay.zig -| test/deck/schema.zig -| test/e2e_harness.zig -| test/hxdiff.zig -| test/snapshot.zig -| uucode_config.zig -|╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ -| NOR build.zig Save Del -| 1 const std = @import("std"); -| 2 -| 3 pub const Platform = enum { tty, gui, web }; -| 4 pub const TreeSitterGrammars = enum { disabled, zig, minimal, full }; -| 5 const GhosttyArtifact = enum { ghostty, lib }; -| 6 -| 7 pub fn build(b: *std.Build) void { -| 8 // Default target is the Steam Deck (deckcap's trick): x86_64 linux-gnu -| 9 // with the glibc version pinned low, so a binary built on a rolling- -| 10 // release host runs on SteamOS — a native build references the host's -| 11 // newer versioned libm/libc symbols and dies with "GLIBC_2.4x not found" -| 12 // on the deck. Override with -Dtarget= as usual. -| 13 const target = b.standardTargetOptions(.{ .default_target = .{ -| 14 .cpu_arch = .x86_64, -| 15 .os_tag = .linux, -| 16 .abi = .gnu, -== snap source-scrolled grid=120x37 cursor=0,0 -|Kill Newcol Tutor Debug Colors NextColor Crt Dump -| TTY /home/goblin/00-projects/0x4200.cafe/02-pardes-code Del -| src/dump.zig -| src/gui.zig -| src/image.zig -| src/look.zig -| src/main.zig -| src/modal.zig -| src/pardes.zig -| src/petscii.zig -| src/syntax.zig -| src/tracy.zig -| src/tty.zig -| test/deck/replay.zig -| test/deck/schema.zig -| test/e2e_harness.zig -| test/hxdiff.zig -| test/snapshot.zig -| uucode_config.zig -| -| NOR build.zig Save Del -| 10 // release host runs on SteamOS — a native build references the host's -| 11 // newer versioned libm/libc symbols and dies with "GLIBC_2.4x not found" -| 12 // on the deck. Override with -Dtarget= as usual. -| 13 const target = b.standardTargetOptions(.{ .default_target = .{ -| 14 .cpu_arch = .x86_64, -| 15 .os_tag = .linux, -| 16 .abi = .gnu, -| 17 .glibc_version = .{ .major = 2, .minor = 38, .patch = 0 }, -| 18 } }); -| 19 const requested_optimize = b.standardOptimizeOption(.{}); -| 20 const platform = b.option(Platform, "platform", "which shell to build (tty, gui, web)") orelse .tty; -| 21 const static = b.option(bool, "static", "statically link") orelse false; -| 22 const dump_path = b.option([]const u8, "dump", "dump .zon embedded into the web shell (-Dplatform=web)"); -| 23 const emcc_path = b.option([]const u8, "emcc", "emcc for the web shell's final link") orelse "/usr/lib/emscri -| 24 const is_emscripten = target.result.os.tag == .emscripten; -| 25 // wasm: size is the budget +|0: 0-52 #969696,#2c2a3e, 53-119 d,#2c2a3e, +|1: 0-1 #ffffff,#6e4fd0, 2-19 #969696,#2c2a3e, 20-59 d,#2c2a3e, 60-61 #ffffff,#383c54, 62-116 #969696,#2c2a3e, 117-119 d,#2c2a3e, +|2: 0-0 d,#525252, 1-1 d,d, 2-6 #969696,#2c2a3e, 7-18 d,d, 19-25 #cc70d8,d,b 26-26 d,d, 27-31 #8ab87a,d, 32-59 d,d, 60-60 d,#525252, 61-119 d,d, +|3: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-25 d,d, 26-32 #cc70d8,d,b 33-33 d,d, 34-49 #8ab87a,d, 50-59 d,d, 60-60 d,#525252, 61-119 d,d, +|4: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-59 d,d, 60-60 d,#525252, 61-119 d,d, +|5: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-27 d,d, 28-31 #cc70d8,d,b 32-59 d,d, 60-60 d,#525252, 61-119 d,d, +|6: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-59 d,d, 60-60 d,#525252, 61-119 d,d, +|7: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-59 #f0f0f0,d,b 60-60 d,#525252, 61-119 d,d, +|8: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-59 #f0f0f0,d,b 60-60 d,#525252, 61-119 d,d, +|9: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-59 #f0f0f0,d,b 60-60 d,#525252, 61-119 d,d, +|10: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-59 #f0f0f0,d,b 60-60 d,#525252, 61-119 d,d, +|11: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-26 d,d, 27-47 #8ab87a,d, 48-59 d,d, 60-60 d,#525252, 61-119 d,d, +|12: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-37 d,d, 38-41 #cc70d8,d,b 42-59 d,d, 60-60 d,#525252, 61-119 d,d, +|13: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-59 d,d, 60-60 d,#525252, 61-119 d,d, +|14: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-10 d,d, 11-12 #cc70d8,d,b 13-59 d,d, 60-60 d,#525252, 61-119 d,d, +|15: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-10 d,d, 11-59 #f0f0f0,d,b 60-60 d,#525252, 61-119 d,d, +|16: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-10 d,d, 11-59 #f0f0f0,d,b 60-60 d,#525252, 61-119 d,d, +|17: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-10 d,d, 11-59 #f0f0f0,d,b 60-60 d,#525252, 61-119 d,d, +|18: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-10 d,d, 11-59 #f0f0f0,d,b 60-60 d,#525252, 61-119 d,d, +|19: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-10 d,d, 11-59 #f0f0f0,d,b 60-60 d,#525252, 61-119 d,d, +|20: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-59 d,d, 60-60 d,#525252, 61-119 d,d, +|21: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-59 d,d, 60-60 d,#525252, 61-119 d,d, +|22: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-59 d,d, 60-60 d,#525252, 61-119 d,d, +|23: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-59 d,d, 60-60 d,#525252, 61-119 d,d, +|24: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-43 d,d, 44-44 #d0c070,d, 45-55 d,d, 56-57 #d0c070,d, 58-59 d,d, 60-60 d,#525252, 61-119 d,d, +|25: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-59 d,d, 60-60 d,#525252, 61-119 d,d, +|26: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-59 d,d, 60-60 d,#525252, 61-119 d,d, +|27: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-46 d,d, 47-56 #8ab87a,d, 57-58 d,d, 59-59 #8ab87a,d, 60-60 d,#525252, 61-119 d,d, +|28: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-40 d,d, 41-48 #8ab87a,d, 49-50 d,d, 51-59 #8ab87a,d, 60-60 d,#525252, 61-119 d,d, +|29: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-49 d,d, 50-55 #8ab87a,d, 56-57 d,d, 58-59 #8ab87a,d, 60-60 d,#525252, 61-119 d,d, +|30: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-59 d,d, 60-60 d,#525252, 61-119 d,d, +|31: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-59 d,d, 60-60 d,#525252, 61-119 d,d, +|32: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-10 d,d, 11-37 #f0f0f0,d,b 38-59 d,d, 60-60 d,#525252, 61-119 d,d, +|33: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-27 d,d, 28-29 #cc70d8,d,b 30-53 d,d, 54-57 #cc70d8,d,b 58-59 d,d, 60-60 d,#525252, 61-119 d,d, +|34: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-10 d,d, 11-59 #f0f0f0,d,b 60-60 d,#525252, 61-119 d,d, +|35: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-10 d,d, 11-59 #f0f0f0,d,b 60-60 d,#525252, 61-119 d,d, +|36: 0-0 d,#2a2a2a, 1-1 d,d, 2-6 #5a5a5a,d, 7-10 d,d, 11-59 #f0f0f0,d,b 60-60 d,#525252, 61-119 d,d, +== snap separator-resized grid=120x37 cursor=7,2 +|Kill Newcol Tutor Debug NextColor Dump Find Grep Help +| build.zig Save Del ╎$ /home/goblin/00-projects/0x4200.cafe/02-pardes-code Del +| 1 const std = @import("std"); ╎ $ git ls-files '*.zig' +| 2 const snap_build = @import("build/snap.zig"); ╎ build.zig +| 3 ╎ src/crt.zig +| 4 pub const Platform = enum { tty, gui, web }; ╎ src/deck.zig +| 5 ╎ src/dump.zig +| 6 /// The ZLS the language backend links, spelled once.╎ src/gui.zig +| 7 /// build.zig.zon pins (0.16.x branch) and is passed ╎ src/image.zig +| 8 /// `-Dversion-string` (its build.zig otherwise shell╎ src/look.zig +| 9 /// which fails on a fetched package that has no .git╎ src/main.zig +| 10 const zls_version = "0.16.1-dev+3e0d0820"; ╎ src/modal.zig +| 11 pub const TreeSitterGrammars = enum { disabled, zig, ╎ src/pardes.zig +| 12 ╎ src/petscii.zig +| 13 pub fn build(b: *std.Build) void { ╎ src/syntax.zig +| 14 // Default target is the Steam Deck (deckcap's tr╎ src/tracy.zig +| 15 // with the glibc version pinned low, so a binary╎ src/tty.zig +| 16 // release host runs on SteamOS — a native build ╎ test/deck/replay.zig +| 17 // newer versioned libm/libc symbols and dies wit╎ test/deck/schema.zig +| 18 // on the deck. Override with -Dtarget= as usual.╎ test/e2e_harness.zig +| 19 const target = b.standardTargetOptions(.{ .defaul╎ test/hxdiff.zig +| 20 .cpu_arch = .x86_64, ╎ test/snapshot.zig +| 21 .os_tag = .linux, ╎ uucode_config.zig +| 22 .abi = .gnu, ╎ +| 23 .glibc_version = .{ .major = 2, .minor = 38, ╎ +| 24 } }); ╎ +| 25 const requested_optimize = b.standardOptimizeOpti╎ +| 26 const platform = b.option(Platform, "platform", "╎ +| 27 const static = b.option(bool, "static", "statical╎ +| 28 const dump_path = b.option([]const u8, "dump", "d╎ +| 29 const is_web = platform == .web; ╎ +| 30 const is_web_target = target.result.cpu.arch == .╎ +| 31 // wasm: size is the budget ╎ +| 32 const optimize = if (is_web) .ReleaseSmall else r╎ +| 33 // The vendored C is never what we are debugging,╎ +| 34 // the app: 90% of a Debug startup is tree-sitter╎ +| 35 // (perf: ts_query__perform_analysis + ts_lookahe╎ +== snap source-scrolled grid=120x37 cursor=7,2 +|Kill Newcol Tutor Debug NextColor Dump Find Grep Help +| build.zig Save Del ╎$ /home/goblin/00-projects/0x4200.cafe/02-pardes-code Del +| 1 const std = @import("std"); ╎ $ git ls-files '*.zig' +| 2 const snap_build = @import("build/snap.zig"); ╎ build.zig +| 3 ╎ src/crt.zig +| 4 pub const Platform = enum { tty, gui, web }; ╎ src/deck.zig +| 5 ╎ src/dump.zig +| 6 /// The ZLS the language backend links, spelled once.╎ src/gui.zig +| 7 /// build.zig.zon pins (0.16.x branch) and is passed ╎ src/image.zig +| 8 /// `-Dversion-string` (its build.zig otherwise shell╎ src/look.zig +| 9 /// which fails on a fetched package that has no .git╎ src/main.zig +| 10 const zls_version = "0.16.1-dev+3e0d0820"; ╎ src/modal.zig +| 11 pub const TreeSitterGrammars = enum { disabled, zig, ╎ src/pardes.zig +| 12 ╎ src/petscii.zig +| 13 pub fn build(b: *std.Build) void { ╎ src/syntax.zig +| 14 // Default target is the Steam Deck (deckcap's tr╎ src/tracy.zig +| 15 // with the glibc version pinned low, so a binary╎ src/tty.zig +| 16 // release host runs on SteamOS — a native build ╎ test/deck/replay.zig +| 17 // newer versioned libm/libc symbols and dies wit╎ test/deck/schema.zig +| 18 // on the deck. Override with -Dtarget= as usual.╎ test/e2e_harness.zig +| 19 const target = b.standardTargetOptions(.{ .defaul╎ test/hxdiff.zig +| 20 .cpu_arch = .x86_64, ╎ test/snapshot.zig +| 21 .os_tag = .linux, ╎ uucode_config.zig +| 22 .abi = .gnu, ╎ +| 23 .glibc_version = .{ .major = 2, .minor = 38, ╎ +| 24 } }); ╎ +| 25 const requested_optimize = b.standardOptimizeOpti╎ +| 26 const platform = b.option(Platform, "platform", "╎ +| 27 const static = b.option(bool, "static", "statical╎ +| 28 const dump_path = b.option([]const u8, "dump", "d╎ +| 29 const is_web = platform == .web; ╎ +| 30 const is_web_target = target.result.cpu.arch == .╎ +| 31 // wasm: size is the budget ╎ +| 32 const optimize = if (is_web) .ReleaseSmall else r╎ +| 33 // The vendored C is never what we are debugging,╎ +| 34 // the app: 90% of a Debug startup is tree-sitter╎ +| 35 // (perf: ts_query__perform_analysis + ts_lookahe╎ diff --git a/test/web_harness.mjs b/test/web_harness.mjs new file mode 100644 index 00000000..bcd45746 --- /dev/null +++ b/test/web_harness.mjs @@ -0,0 +1,136 @@ +#!/usr/bin/env node +// Dependency-free JS harness: instantiate the real freestanding core in Node, +// then render its packed frame through app.mjs against a deliberately tiny DOM +// double. Chrome is reserved for the separate browser/e2e snapshot harness. + +import assert from "node:assert/strict"; +import fs from "node:fs/promises"; +import path from "node:path"; +import { pathToFileURL } from "node:url"; + +if (process.argv.length !== 3) { + console.error("usage: node test/web_harness.mjs <zig-out/web>"); + process.exit(2); +} + +async function main() { + const webDir = path.resolve(process.argv[2]); + const wasmBytes = await fs.readFile(path.join(webDir, "pardes.wasm")); + const module = await WebAssembly.compile(wasmBytes); + assert.deepEqual(WebAssembly.Module.imports(module), [], "the freestanding core must have no JS or libc imports"); + const { exports: wasm } = await WebAssembly.instantiate(module, {}); + + for (const name of [ + "memory", "pardes_init", "pardes_frame", "pardes_frame_ptr", "pardes_key", + "pardes_mouse", "pardes_output", "pardes_lsp_response", "pardes_file_changed", + "pardes_effect_next", "pardes_deinit", + ]) assert.ok(wasm[name], `missing wasm export ${name}`); + + assert.equal(wasm.pardes_init(80, 24), 0, `wasm init error ${wasm.pardes_last_error()}`); + assert.equal(wasm.pardes_frame(), 80 * 24); + assert.ok(wasm.pardes_frame_ptr() > 0); + + const { DomRenderer } = await import(pathToFileURL(path.join(webDir, "app.mjs"))); + const document = new FakeDocument(); + const root = document.createElement("main"); + root.ownerDocument = document; + const renderer = new DomRenderer(root, wasm); + const first = renderer.render(); + + assert.equal(first.cols, 80); + assert.equal(first.rows, 24); + assert.equal(root.children.length, 24); + assert.equal(root.children[0].children.length, 80); + assert.ok(first.cells.some((cell) => cell.text !== " "), "dump rendered an empty frame"); + assert.ok(root.walk().every((node) => node.tagName !== "CANVAS"), "renderer created a canvas"); + + const firstCell = renderer.cells[0]; + const second = renderer.render(); + assert.equal(renderer.cells[0], firstCell, "stable frames should reuse DOM nodes"); + assert.equal(second.cells.length, 80 * 24); + + wasm.pardes_mouse(2, 0, 5, 3, 0); + wasm.pardes_mouse(2, 1, 5, 3, 0); + const looked = renderer.render(); + assert.match(looked.cells.map((cell) => cell.text).join(""), /build\.zig Save Del/, "right-click LOOK did not open the embedded source"); + const sourceRows = Array.from({ length: looked.rows }, (_, row) => + looked.cells.slice(row * looked.cols, (row + 1) * looked.cols)); + const codeRows = sourceRows.filter((row) => row.map((cell) => cell.text).join("").match(/^\s*\d+\s/)); + const codeColors = new Set(codeRows.flatMap((row) => row.slice(5).map((cell) => `${cell.fg}:${cell.attrs & 1}`))); + assert.ok(codeColors.size >= 3, "Tree-sitter did not apply distinct syntax styles to build.zig"); + let sawWatch = false; + for (let index = 0; index < 32; index += 1) { + const kind = wasm.pardes_effect_next(); + if (!kind) break; + if (kind === 9) sawWatch = true; + } + assert.ok(sawWatch, "JS effect drain did not receive the opened file watch request"); + + wasm.pardes_resize(41, 13); + const resized = renderer.render(); + assert.equal(resized.cols, 41); + assert.equal(resized.rows, 13); + assert.equal(root.children.length, 13); + assert.equal(root.children[0].children.length, 41); + + wasm.pardes_deinit(); + console.error("web harness ok: freestanding wasm + Tree-sitter + packed DOM renderer"); +} + +class FakeDocument { + constructor() { + this.body = new FakeElement("body", this); + } + + createElement(tagName) { + return new FakeElement(tagName, this); + } +} + +class FakeElement { + constructor(tagName, ownerDocument) { + this.tagName = tagName.toUpperCase(); + this.ownerDocument = ownerDocument; + this.children = []; + this.attributes = new Map(); + this.dataset = {}; + this.style = new FakeStyle(); + this.classList = new FakeClassList(this); + this.className = ""; + this.textContent = ""; + } + + setAttribute(name, value) { + this.attributes.set(name, String(value)); + } + + replaceChildren(...children) { + this.children = children; + } + + *walk() { + yield this; + for (const child of this.children) yield* child.walk(); + } +} + +class FakeStyle { + setProperty(name, value) { + this[name] = value; + } +} + +class FakeClassList { + constructor(element) { + this.element = element; + this.names = new Set(); + } + + toggle(name, force) { + if (force) this.names.add(name); + else this.names.delete(name); + this.element.className = [...this.names].join(" "); + } +} + +await main(); diff --git a/test/web_snapshot.mjs b/test/web_snapshot.mjs index 7c0c7462..4c50956e 100644 --- a/test/web_snapshot.mjs +++ b/test/web_snapshot.mjs @@ -1,5 +1,7 @@ #!/usr/bin/env node -// Drive the Emscripten build in a real headless Chromium using .snap scripts. +// Drive the freestanding WASM + DOM build in a real headless Chromium using +// .snap scripts. This is an end-to-end browser test: Chrome creates genuine +// touch events and the JS shell translates them into core events. // Shared commands retain test/snapshot.zig's spelling and output format: // wait <ms> <text> | stable <quiet-ms> <timeout-ms> // snap <label> | snapstyle <label> @@ -183,10 +185,7 @@ async function runScript(source) { } async function dispatchTouch(phase, id, col, row, active) { - const point = await evaluate(`({ - x: Module._pardes_web_test_cell_x(${JSON.stringify(col)}), - y: Module._pardes_web_test_cell_y(${JSON.stringify(row)}) - })`); + const point = await evaluate(`pardes.test.cellPoint(${JSON.stringify(col)}, ${JSON.stringify(row)})`); const touch = { x: point.x, y: point.y, id, radiusX: 1, radiusY: 1, force: 1 }; if (phase === "down") { if (active) throw new Error("only one finger is supported by this web test"); @@ -207,29 +206,24 @@ async function dispatchTouch(phase, id, col, row, active) { async function grid() { return evaluate(`(() => { - const cols = Module._pardes_web_test_cols(); - const rows = Module._pardes_web_test_rows(); + const frame = pardes.test.grid(); + const { cols, rows, cursorX, cursorY } = frame; const lines = []; const styleRows = []; for (let y = 0; y < rows; y++) { let line = ""; const styles = []; for (let x = 0; x < cols; x++) { - const cp = Module._pardes_web_test_cell(x, y) || 32; - line += String.fromCodePoint(cp); - styles.push([ - Module._pardes_web_test_cell_fg(x, y), - Module._pardes_web_test_cell_bg(x, y), - Module._pardes_web_test_cell_attrs(x, y), - ]); + const cell = frame.cells[y * cols + x]; + line += cell.text || " "; + styles.push([cell.fg, cell.bg, cell.attrs]); } lines.push(line.replace(/ +$/, "")); styleRows.push(styles); } return { cols, rows, lines, styleRows, - cursorX: Module._pardes_web_test_cursor_x(), - cursorY: Module._pardes_web_test_cursor_y(), + cursorX, cursorY, text: lines.join("\\n"), }; })()`); @@ -301,7 +295,14 @@ async function waitStable(quiet, timeout) { async function waitForRuntime(timeout) { await waitUntil(async () => { try { - return await evaluate(`typeof Module?._pardes_web_test_cols === "function" && Module._pardes_web_test_cols() > 0`); + return await evaluate(`(() => { + const app = globalThis.pardes; + if (!app?.test?.grid()?.cols || document.querySelector("canvas")) return false; + const want = app.runtime.gridSize(); + const frame = app.test.grid(); + return frame.cols === want.cols && frame.rows === want.rows && + document.querySelectorAll(".pardes-row").length === want.rows; + })()`); } catch { return false; } @@ -388,8 +389,10 @@ async function serveDirectory(root) { } const contents = await fsp.readFile(filename); const contentType = filename.endsWith(".wasm") ? "application/wasm" - : filename.endsWith(".js") ? "text/javascript; charset=utf-8" - : "text/html; charset=utf-8"; + : filename.endsWith(".js") || filename.endsWith(".mjs") ? "text/javascript; charset=utf-8" + : filename.endsWith(".css") ? "text/css; charset=utf-8" + : filename.endsWith(".ttf") ? "font/ttf" + : "text/html; charset=utf-8"; response.writeHead(200, { "content-type": contentType, "cache-control": "no-store" }); response.end(contents); } catch { |
