From 8c81b87311c9814377dc04fcd58d39c41cc822a3 Mon Sep 17 00:00:00 2001 From: GIGAHERTZ00 Date: Sat, 19 Oct 2024 23:16:21 +0900 Subject: [PATCH 1/7] =?UTF-8?q?=E4=BB=BB=E6=84=8F=E3=81=AE=E5=95=8F?= =?UTF-8?q?=E9=A1=8C=E3=81=AE=E5=87=BA=E5=8A=9B=E3=81=AB=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mock-server/src/index.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/mock-server/src/index.ts b/apps/mock-server/src/index.ts index 54ff045..4bae12e 100644 --- a/apps/mock-server/src/index.ts +++ b/apps/mock-server/src/index.ts @@ -1,4 +1,5 @@ import type { Answer } from "@data-maki/schemas"; +import type { Problem } from "@data-maki/schemas"; import type { Serve } from "bun"; import { Hono } from "hono"; import { cors } from "hono/cors"; @@ -18,6 +19,8 @@ const validateAnswer = typia.createValidate(); const validTokens = new Set(config.teams); +let customProblem: Problem; + const generationSettings: GenerationSettings = { widthRandom: true, heightRandom: true, @@ -57,6 +60,14 @@ app.use(async (c, next) => { let lockedDown = true; +let customProblemFlg: boolean = false; + +app.post("/customProblem", async (c) => { + customProblem = await c.req.json(); + + return new Response(null, { status: 204 }); +}); + app.get("/problem", (c) => { if (lockedDown) return new Response('"AccessTimeError"', { status: 403 }); @@ -64,7 +75,7 @@ app.get("/problem", (c) => { c.header("X-Data-Maki-Problem-ID", id.toString()); - return c.json(problem); + return c.json(customProblemFlg ? customProblem : problem); }); app.post("/answer", async (c) => { From 8555afe3df38b17a934494bfa608fe1a44d702f8 Mon Sep 17 00:00:00 2001 From: GIGAHERTZ00 Date: Sat, 19 Oct 2024 23:17:13 +0900 Subject: [PATCH 2/7] =?UTF-8?q?=E6=80=92=E3=82=89=E3=82=8C=E3=82=92?= =?UTF-8?q?=E8=A7=A3=E6=B6=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mock-server/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/mock-server/src/index.ts b/apps/mock-server/src/index.ts index 4bae12e..3bba0e6 100644 --- a/apps/mock-server/src/index.ts +++ b/apps/mock-server/src/index.ts @@ -60,7 +60,7 @@ app.use(async (c, next) => { let lockedDown = true; -let customProblemFlg: boolean = false; +const customProblemFlg: boolean = false; app.post("/customProblem", async (c) => { customProblem = await c.req.json(); From e14e1f87f6d75b4e42e72938333f816dad248fc5 Mon Sep 17 00:00:00 2001 From: GIGAHERTZ00 Date: Sun, 20 Oct 2024 03:36:17 +0900 Subject: [PATCH 3/7] =?UTF-8?q?=E5=95=8F=E9=A1=8C=E7=94=9F=E6=88=90?= =?UTF-8?q?=E3=81=AE=E6=89=8B=E6=B3=95=E3=82=92=E5=A2=97=E3=82=84=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mock-server/src/gen.ts | 170 ++++++++++++++++++++++++++- apps/mock-server/src/utils/number.ts | 2 +- 2 files changed, 167 insertions(+), 5 deletions(-) diff --git a/apps/mock-server/src/gen.ts b/apps/mock-server/src/gen.ts index c339dce..f58383a 100644 --- a/apps/mock-server/src/gen.ts +++ b/apps/mock-server/src/gen.ts @@ -3,7 +3,14 @@ import type { FixedLengthArray } from "type-fest"; import { shuffle } from "./utils/array"; import { getRandomInt } from "./utils/number"; -type GenerationKindStart = "all-random" | "gen-random" | "column-seq" | "column-seq-reverse" | "column-group-shuffle"; +type GenerationKindStart = + | "all-random" + | "gen-random" + | "column-seq" + | "column-seq-reverse" + | "column-group-shuffle" + | "random-rectangle-area" + | "random-eclipse-area"; const generationKindStarts: GenerationKindStart[] = [ "all-random", @@ -11,6 +18,8 @@ const generationKindStarts: GenerationKindStart[] = [ "column-seq", "column-seq-reverse", "column-group-shuffle", + "random-rectangle-area", + "random-eclipse-area", ] as const; type GenerationKindGoal = @@ -21,7 +30,9 @@ type GenerationKindGoal = | "column-seq" | "column-seq-reverse" | "partial-single-swap" - | "partial-block-swap"; + | "partial-block-swap" + | "random-area-reverse" + | "block-swap"; const generationKindGoals: GenerationKindGoal[] = [ "all-random", @@ -32,6 +43,8 @@ const generationKindGoals: GenerationKindGoal[] = [ "column-seq-reverse", "partial-single-swap", "partial-block-swap", + "random-area-reverse", + "block-swap", ] as const; export type GenerationSettings = { @@ -63,11 +76,11 @@ export const generateProblem = (generationSettings: GenerationSettings): [id: nu const genKindStart = generationSettings.genKindStart === "all-random" - ? generationKindStarts[getRandomInt(1, 4)] + ? generationKindStarts[getRandomInt(1, 9)] : generationSettings.genKindStart; const genKindGoal = generationSettings.genKindGoal === "all-random" - ? generationKindGoals[getRandomInt(1, 7)] + ? generationKindGoals[getRandomInt(1, 8)] : generationSettings.genKindGoal; const numbers: number[] = Array(width * height).fill(0); @@ -118,6 +131,51 @@ export const generateProblem = (generationSettings: GenerationSettings): [id: nu [numbers[idx1], numbers[idx2]] = [numbers[idx2] as number, numbers[idx1] as number]; } break; + case "random-rectangle-area": { + const areaCnt: number = getRandomInt(30, 40); + for (let i = 0; i < areaCnt; i++) { + let x1: number = getRandomInt(-width / (i + 1), width); + x1 = x1 < 0 ? 0 : x1; + let x2: number = getRandomInt(x1, width + width / (i + 1)); + x2 = width < x2 ? width - 1 : x2; + let y1: number = getRandomInt(-height / (i + 1), height); + y1 = y1 < 0 ? 0 : y1; + let y2: number = getRandomInt(y1, height + height / (i + 1)); + y2 = height < y2 ? height - 1 : y2; + const num: number = getRandomInt(0, 4); + for (let y = y1; y < y2; ++y) { + for (let x = x1; x < x2; ++x) { + numbers[y * width + x] = num; + } + } + } + break; + } + case "random-eclipse-area": { + const areaCnt: number = getRandomInt(30, 50); + for (let i = 0; i < areaCnt; i++) { + let x1: number = getRandomInt(-width / (i + 1), width); + x1 = x1 < 0 ? 0 : x1; + let x2: number = getRandomInt(x1, width + width / (i + 1)); + x2 = width < x2 ? width - 1 : x2; + let y1: number = getRandomInt(-height / (i + 1), height); + y1 = y1 < 0 ? 0 : y1; + let y2: number = getRandomInt(y1, height + height / (i + 1)); + y2 = height < y2 ? height - 1 : y2; + const num: number = getRandomInt(0, 4); + for (let y = y1; y < y2; ++y) { + for (let x = x1; x < x2; ++x) { + if ( + (x - (x1 + x2) / 2) ** 2 / ((x1 - x2) / 2) ** 2 + (y - (y1 + y2) / 2) ** 2 / ((y1 - y2) / 2) ** 2 <= + 1 + ) { + numbers[y * width + x] = num; + } + } + } + } + break; + } default: throw new Error("Invalid generation kind"); } @@ -198,6 +256,110 @@ export const generateProblem = (generationSettings: GenerationSettings): [id: nu } goal = getBoardArrayFromNumbers(goalNumbers, width); break; + case "random-area-reverse": { + // random area reverse(30-50) + goalNumbers = numbers; + const areaCnt: number = getRandomInt(30, 50); + for (let i = 0; i < areaCnt; ++i) { + let x1: number = getRandomInt(-width / (i + 1), width); + x1 = x1 < 0 ? 0 : x1; + let x2: number = getRandomInt(x1, width + width / (i + 1)); + x2 = width < x2 ? width - 1 : x2; + let y1: number = getRandomInt(-height / (i + 1), height); + y1 = y1 < 0 ? 0 : y1; + let y2: number = getRandomInt(y1, height + height / (i + 1)); + y2 = height < y2 ? height - 1 : y2; + + if (getRandomInt(0, 2)) { + /* + 0 1 2 3 + 4 5 6 7 + | + V + 3 2 1 0 + 7 6 5 4 + */ + for (let y = y1; y < y2; ++y) { + for (let j = 0; j < (x2 - x1 + 1) / 2; ++j) { + const leftIndex = y * width + x1 + j; + const rightIndex = y * width + x2 - j; + + if ( + leftIndex >= 0 && + leftIndex < goalNumbers.length && + rightIndex >= 0 && + rightIndex < goalNumbers.length + ) { + [goalNumbers[leftIndex], goalNumbers[rightIndex]] = [ + goalNumbers[rightIndex] as number, + goalNumbers[leftIndex] as number, + ]; + } + } + } + } + if (getRandomInt(0, 2)) { + /* + 0 1 2 3 + 4 5 6 7 + | + V + 4 5 6 7 + 0 1 2 3 + */ + for (let x = x1; x < x2; ++x) { + for (let j = 0; j < (y2 - y1 + 1) / 2; ++j) { + const topIndex = (y1 + j) * width + x; + const bottomIndex = (y2 - j) * width + x; + + if ( + topIndex >= 0 && + topIndex < goalNumbers.length && + bottomIndex >= 0 && + bottomIndex < goalNumbers.length + ) { + [goalNumbers[topIndex], goalNumbers[bottomIndex]] = [ + goalNumbers[bottomIndex] as number, + goalNumbers[topIndex] as number, + ]; + } + } + } + } + } + goal = getBoardArrayFromNumbers(goalNumbers, width); + break; + } + case "block-swap": { + goalNumbers = numbers; + const blockCnt: number = getRandomInt(30, 50); + for (let i = 0; i < blockCnt; ++i) { + let x1: number = getRandomInt(-width / (i + 1), width - 8); + x1 = x1 < 0 ? 0 : x1; + let x2: number = getRandomInt(x1, x1 + 8); + x2 = width < x2 ? width - 1 : x2; + let y1: number = getRandomInt(-height / (i + 1), height - 8); + y1 = y1 < 0 ? 0 : y1; + let y2: number = getRandomInt(y1, y1 + 8); + y2 = height < y2 ? height - 1 : y2; + + let x3: number = getRandomInt(-width / (i + 1), width - (x2 - x1)); + x3 = x3 < 0 ? 0 : x3; + let y3: number = getRandomInt(-height / (i + 1), height - (y2 - y1)); + y3 = y3 < 0 ? 0 : y3; + + for (let j = 0; j < x2 - x1; ++j) { + for (let k = 0; k < y2 - y1; ++k) { + [goalNumbers[(y1 + k) * width + x1 + j], goalNumbers[(y3 + k) * width + x3 + j]] = [ + goalNumbers[(y3 + k) * width + x3 + j] as number, + goalNumbers[(y1 + k) * width + x1 + j] as number, + ]; + } + } + } + goal = getBoardArrayFromNumbers(goalNumbers, width); + break; + } default: throw new Error("Invalid generation kind"); } diff --git a/apps/mock-server/src/utils/number.ts b/apps/mock-server/src/utils/number.ts index db85e8e..a8d0e52 100644 --- a/apps/mock-server/src/utils/number.ts +++ b/apps/mock-server/src/utils/number.ts @@ -1,4 +1,4 @@ import type { IntRange } from "type-fest"; export const getRandomInt = (min: TMin, max: TMax): IntRange => - Math.floor(Math.random() * (max - min) + min) as unknown as IntRange; // [min,max] + Math.floor(Math.random() * (max - min) + min) as unknown as IntRange; // min <= n < max From 75ee993f8c8267340bfdded059ec5d2f3686894c Mon Sep 17 00:00:00 2001 From: Mido Date: Sun, 20 Oct 2024 06:04:31 +0900 Subject: [PATCH 4/7] brain power Co-authored-by: Hayatann --- .editorconfig | 4 +- .github/workflows/ci.yml | 2 +- .vscode/settings.json | 4 +- apps/mock-server/src/index.ts | 6 +- apps/solver/scripts/build.ts | 4 +- bun.lockb | Bin 505048 -> 505424 bytes packages/algorithm/package.json | 1 + packages/algorithm/src/index.ts | 4 +- packages/algorithm/src/models/pattern.ts | 2 +- packages/algorithm/src/utils/arrays.ts | 6 +- packages/algorithm/src/v3.test.ts | 21 + packages/algorithm/src/v3/Cargo.lock | 199 +++++ packages/algorithm/src/v3/Cargo.toml | 18 + packages/algorithm/src/v3/index.ts | 1 + .../src/v3/pkg/algorithm_v3_wasm.d.ts | 55 ++ .../algorithm/src/v3/pkg/algorithm_v3_wasm.js | 4 + .../src/v3/pkg/algorithm_v3_wasm_bg.js | 799 ++++++++++++++++++ .../src/v3/pkg/algorithm_v3_wasm_bg.wasm | Bin 0 -> 64812 bytes .../src/v3/pkg/algorithm_v3_wasm_bg.wasm.d.ts | 62 ++ packages/algorithm/src/v3/pkg/package.json | 9 + packages/algorithm/src/v3/src/arrays.rs | 188 +++++ packages/algorithm/src/v3/src/evaluation.rs | 46 + packages/algorithm/src/v3/src/katanuki.rs | 172 ++++ packages/algorithm/src/v3/src/lib.rs | 389 +++++++++ packages/algorithm/src/v3/src/schemas.rs | 247 ++++++ packages/algorithm/src/v3/src/types.rs | 290 +++++++ packages/algorithm/src/v3/src/utils.rs | 132 +++ packages/algorithm/src/workers/v3.master.ts | 47 ++ packages/algorithm/src/workers/v3.worker.ts | 18 + packages/algorithm/tsup.worker.ts | 2 + 30 files changed, 2717 insertions(+), 15 deletions(-) create mode 100644 packages/algorithm/src/v3.test.ts create mode 100644 packages/algorithm/src/v3/Cargo.lock create mode 100644 packages/algorithm/src/v3/Cargo.toml create mode 100644 packages/algorithm/src/v3/index.ts create mode 100644 packages/algorithm/src/v3/pkg/algorithm_v3_wasm.d.ts create mode 100644 packages/algorithm/src/v3/pkg/algorithm_v3_wasm.js create mode 100644 packages/algorithm/src/v3/pkg/algorithm_v3_wasm_bg.js create mode 100644 packages/algorithm/src/v3/pkg/algorithm_v3_wasm_bg.wasm create mode 100644 packages/algorithm/src/v3/pkg/algorithm_v3_wasm_bg.wasm.d.ts create mode 100644 packages/algorithm/src/v3/pkg/package.json create mode 100644 packages/algorithm/src/v3/src/arrays.rs create mode 100644 packages/algorithm/src/v3/src/evaluation.rs create mode 100644 packages/algorithm/src/v3/src/katanuki.rs create mode 100644 packages/algorithm/src/v3/src/lib.rs create mode 100644 packages/algorithm/src/v3/src/schemas.rs create mode 100644 packages/algorithm/src/v3/src/types.rs create mode 100644 packages/algorithm/src/v3/src/utils.rs create mode 100644 packages/algorithm/src/workers/v3.master.ts create mode 100644 packages/algorithm/src/workers/v3.worker.ts diff --git a/.editorconfig b/.editorconfig index 164db48..2c2060e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,9 +7,11 @@ insert_final_newline = true max_line_length = 120 trim_trailing_whitespace = true +[*.rs] +indent_size = 4 + [*.md] trim_trailing_whitespace = false [*.json] insert_final_newline = false - diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b11501..618579f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,7 +54,7 @@ jobs: - name: Build solver (standalone) working-directory: apps/solver - run: bun build --minify --sourcemap --compile --bytecode --target=bun-${{ matrix.target }} --outfile=dist/solver-${{ matrix.outfile }} dist/index.js $(find dist -name '*.js' -not -name 'index.js') + run: bun build --minify --sourcemap --compile --bytecode --target=bun-${{ matrix.target }} --outfile=dist/solver-${{ matrix.outfile }} dist/index.js $(find dist -name '*.js' -not -name 'index.js') $(find dist -name '*.wasm') - name: Build mock server working-directory: apps/mock-server diff --git a/.vscode/settings.json b/.vscode/settings.json index 0092792..ecd0707 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,7 @@ { "editor.rulers": [120], "npm.packageManager": "bun", - "typescript.tsdk": "node_modules/typescript/lib" + "typescript.tsdk": "node_modules/typescript/lib", + "rust-analyzer.linkedProjects": ["${workspaceFolder}/packages/algorithm/src/v3/Cargo.toml"], + "rust-analyzer.cargo.target": "wasm32-unknown-unknown" } diff --git a/apps/mock-server/src/index.ts b/apps/mock-server/src/index.ts index 3bba0e6..36d5b67 100644 --- a/apps/mock-server/src/index.ts +++ b/apps/mock-server/src/index.ts @@ -19,7 +19,7 @@ const validateAnswer = typia.createValidate(); const validTokens = new Set(config.teams); -let customProblem: Problem; +let customProblem: Problem | null = null; const generationSettings: GenerationSettings = { widthRandom: true, @@ -60,8 +60,6 @@ app.use(async (c, next) => { let lockedDown = true; -const customProblemFlg: boolean = false; - app.post("/customProblem", async (c) => { customProblem = await c.req.json(); @@ -75,7 +73,7 @@ app.get("/problem", (c) => { c.header("X-Data-Maki-Problem-ID", id.toString()); - return c.json(customProblemFlg ? customProblem : problem); + return c.json(customProblem ?? problem); }); app.post("/answer", async (c) => { diff --git a/apps/solver/scripts/build.ts b/apps/solver/scripts/build.ts index a9e4134..712fd64 100644 --- a/apps/solver/scripts/build.ts +++ b/apps/solver/scripts/build.ts @@ -54,8 +54,10 @@ if (!results.success) { console.log("Copying files..."); -const files = await Array.fromAsync(new Glob("../../packages/algorithm/dist/workers/*.worker.js").scan()); +const files = await Array.fromAsync(new Glob("../../packages/algorithm/dist/workers/*.worker.js*").scan()); +const wasmFiles = await Array.fromAsync(new Glob("../../packages/algorithm/dist/workers/*.wasm").scan()); await mkdirp("./dist/workers"); await copyFiles(files, "./dist/workers"); +await copyFiles(wasmFiles, "./dist"); console.timeEnd("Finished building solver"); diff --git a/bun.lockb b/bun.lockb index b88fbd7f0473c0c7db844cd79d9e246d2ab806da..054f9afa669a76ab431eca298e90b46eb50fd8da 100755 GIT binary patch delta 68790 zcmeFad0bUh`~Q6oaFng$kfu@&shOf-X~O{#h_aFtr%WY7MMObGoCTG@tdzQ~>|{$T zn+M6L)Jm}^y_+OcvoiHAOB*G#c@T?w-k-e}>izxoyw53C*RiOgbVa>}g=(GHvs}^)QXT&q5Z{9ZUiZ64^``*!U z^Yit6`h31@MEkJ!cdspYutCgg6MepBK3_=%+7O+SJ1cwUET8X!TrZxLJGF4;v_fCE zP)fsA{w<;OhI=yGvMq|VALaObr=!oJ+ILF+>}lELAI0{j#P`A*p?{%`(StL+{eH0V z4y#!?ne5^7eMY%WNVhk%tWhiHMYc94>J?hssD0`aL=Mr+S>t1~r_S*`PJOlYF7z}s zFUH$9tH9@LM?8D#v?;SE=1#q^(C2GKoSBtBV=86*7-|;P-v4>1XH+X^@qDk|3qnJp zS`{x?;6;DV^KyKjoYaafs7k7<{e9%pl9-HL{$#P&${E?2Sv0`ct;CCOMpfgLY*!E8 zfhr!j(9&d5y&xK#{53hA@ z*|E7ZdlKu2EDEJGZZ&TBZC*(?a1dHR`w}bno~T;%)9qf13p4YkP!V6&9bWo$v;o@( zoaytmMYCtj$e$6LUGVZUFMV@VE$V+~Xj9{hi&x$0^K~RlV>e1Fd9}BvJMZ!i_vqzb zwtd*H-L9eXjnR&Gd)1gutZHS=oH?tI0hDF8zsYu`??82%qAgGjyQ52ezBcHT6>d4j zCEIb7!8(*Gmt>$CM-!*ykIS4g(|2H{7r%!pgI7^yIB90~%q-g7iC$BM>UwX+yy6(2S@~1)XY?wb?0bq0?b$F5)!-Yn$SZK%{a(cuu!CAMoLD_{ zn@x8$styjKYSEcix0ZSh`3n{Q%<6e)H1V}Izo&`MV2G65B?ldTvv;Js+~gJf&SI|x zgCFs_I{2t}1XrPIaTioQaT=;|^UP!3p?civ@9d|uXS8k5<2L`vH+l`KCI4b2xZi?} zBvgj2iKX*w#WHQp`&n&i_0J_fUu)9sEAtATHFHYtI1b5|mf!cJSI}>$^4nqg3sxUp z>??MU(ft;5M^&N4R;O7_x4Oyp$fY)JVf80^Oy@+r?YY}*x=pslL1OjLx6gPzcfIAQ zH`4z)wufxXKVyRue#s81NP*30oYetVJEBS#W%a1t@6z>NOP@it|6^za^j@nwY`Qyb z1tt|+XW8@KvA)#CJ*=K#wVu@jwx!#xZnFA>)w`@Nw0b(Kvn$_fhSmO7iz9H<13%d@ z@Tt{ZRySLH#_Ce5aaQ-*o{J>bF+OVJtyarWozC}Iz18XhPm4?PEV#sKFI1!ZY^wt~ zMC!_KUiNw@iC6`__KJ5p_22BBw!Kj`WIbFOORV$W5bL!4kcmx)A}c?Ca&ETI_XxZx zv!ro_sx9vY<`YuP~we-8c7qE+VGJG>5k7S;T<3~i6j zLN(tFL(f8IPaU6|mFx2zd&jG0VgBr_9QvnVmsia#sB({d*XxJOnX$7n$4$w8Ae4K0 z`{FI{d50%ICv&FuI!v!NS1B{I@~2M7nyl%6Ms|U37hIjOVz-xmeEux0AbhXEb$Azj z;FXetx|a`BoisB$Z%+0MF3>acr(Q(<6SJqzES#F<>wBA9n=?wD+vD}Za#YQmu+Ph2 zR;WYER;jsUuTeAdLvPe^t;w8{IX=5Tv5F0%I>_Bor5p4wuhSAyoo{2vRfFg8(889j zitn+x`DjSC_{>X}Ib%ldghHS1Avo!;F3HQB7MnkAlCK{?$K+0LL&=S3W8yhh z+kD|=T$jGNjdR(qW@n(P$k0zhlUlXwSy%tMdewD$*jL_AnUFhmeC+ILzJFV*h2Ck^ z`m{-3dl|ieszMoGhmN&6r#R*tFPxA&C0i|<%c0lE%yf^P&sR!EYG_}8s@@&H^Z7cX z@1n}43RU`Wv^`q+y*GNk`qt~@-As1RL60l)dEx* z=Y+PjZtw3FI@r3E|KC@h*>f_d`1maO&1>0qiqyb;9@Rzi$)nyvYhI1d*Nb>C+6_H+ z%&Xe_sHTzC=(%VwwzorXV85>DDx1$@v?XzMt-HNp$qt+|0WCfY-4orecqZG`r2lzr z>c&j%U)~9l;`h5_ZEjAkd)l5)+>HH#VScyeUC}1QAED9cv>EwXu~YIVo<%y1wdSaX z#Ek4&GYV-p>&)1!{PCgS8O=ImQU~?k?5VjEac-`p0lzzZUJm7+(Z0AxeZM=Q zbU>BUxXfI)2XjcJgR?Hu@3v$Ws(I^vJaY?f%zdMtyL{;4bjr_izXn+Lj!Ygcbe8+w0PEj2+aMpP{PR*5+RS zTxJWn&}wI^4Nmtm$eZC^jOwP=hu|u(sHL~Plvpk9(aOtj9;yb-p5^s$urC{8Nwehgp`3M_YIqaniY6eTY~+_7SS|br+#l^pNr! zca~S72E^*oKSR}R&h!5fif-G!cwi@Q+ZI&&rgZS~%$*rKabE5;UyxXnNLN&yGA(m* zzB=QlBiT-JpsQ!)t2F?8ha_K#-E4MrZY-jWCvZFIs;VD zzsN`x`6;xkZLoMsH@~kFQ5LE`{qkI|ua=|A?rj^dK|2s%kG4XyQJtVqpXUzilBKA& zPq*>4sM1|#^&C{!@pSf6>3w>+WGR{_fK?hCwxr(fuIXQD??%@DbylNw@W zuz+-$=`vAGL>FG{cZa*3p-PhBy2ZR0Rl}Yqe;unqabEu<0_NN8x7!}coH=Vo?$n9CdBhqC)r=ff@IdHb z`!3FHmwCl53N<^cRdJ8Yy)xROs(jtZZbGa^)~))&h&9@N?d$D5GkdCgihgb5mrxC= z?CjVHxie{cQSKnX_hduRK26JGCG8wuhB036-6d9J7Q_2jyc_2a`uHxq1%t zu8rUHl9${YYSy9sRfh(6g-xHS6T{~lo#yTJB&td;BefcQ=|C^nc&mBYvvTss&-7(x zWo1vBHPhF9P-tF<9q9{)du`9lotir>bLPzH#5%jjp*jN}9^n;t*y=F2ib+M)=44dG zO^ap0LUr1td;3ifJ=C#PSnBZ5rjF;FmN?2wvZ}-y>h!xDRh`o* zhc*_Mq~WMOpJjQSkYeL=P#y5tY%haf$9tXfrPVi4btt^v9!AxY+opIK zEbk0~i|e|qu1o6V_ZJ)r|J)HyRn~^yA~klmqfwO>hw%Anlp z*9Bg|QwzOmE(KM^vZv0xjGdPESg2W- z_A%yWZbSw*h|Ci#{zFfylk zepou-`5rHCyJh{+3rg~x-4lJjt4QS!`KJe+_N>{GhytN4gM)#IL>Go?$0P(6<@$UV z%X2E^ks>^_aA+{_2T^Zve@dw4(PaNEq4>v=ogJLNm%4c_%m_N|CX}omR)Pj(cTzecg0Nj(a^BjUJnQ+^eYboLW3auln}tVBowN z>Z_s^eIjsZ3Z1zgPu=YgZ5bYPejw`Qc9fELVt&8G?b@tRVR>@kG2%Y%Uf<%Wd&5Ki zA;G};vn&s2Mee0ojdwmNIE;gW&W}V_5QXWWI&rhTGKttWhbYcX$>DUKCep!W+toqm zbE1K6&Ra2Kc3yCgqqlF8D{@+7|>yn*o3w^%fFkfiPbwPhcs0Q{8Of~a| z7;a(b%nQw1pX3am=kxV&ValMO|He?w`ebJltS^jip%P8!dsV9MMpxLVo+|C!No3Ch zQods&QgWCYFYp>$&)bhk9TA}@u$U+*R5>Ui@H*arQ03JLfo4S-bCn|#oOC=jFQD<@ ze>qh1!Yj@T+ykV*8if4U1)blBE+9HBv~YaTxwxdR19WcQPNZhKB{@|@-g&D!HDBoS zCDiQ~Hez9@cSVwOC(w?k!l3`PP(?+u^P6R4QW6(=gV&M>_mE~14ImG1n3NN#G;f%E zMU+V757C=W%UgZEWTJ+ll$(Oimqgm;MH&C_`8b|Rd2?JOfsR&qXv?r*ShuC2-Y+FN zGv+pLj-Xn@gZ@w`zB1X_2~!Qc!QkBP^9?5QTAxm&x>Cy8 zVBj$#W|JWa&YyTXZuK>={8K^|FDE;z?l`&M_7L?UMZj#C;g5@4=JSms3UmAWW+HDG z`(JW5DJ!bUsH%TwsQ2ciz^FSp^+Ji86a24)DmEuOKM;E(%3l}^Toht|9djoSPdhV9 zW&{Ikh%OD)<|Q~s@Kic&8XFA6+{GQVa&vCS(@^J>X7qhzBd@J(me!Q1TzCmy*zAot8NTgJpn!|$5UZRV=a>g=}jW=r8HX<07YC^|e zOLA@n>frj+wa!~a@m@447zkhC^IaP%`n^x6=JjOf6Cyic82a5;`g|i*fxsfXOI+_e zJUZm1gg}>j>evE2W~tW`0vqw%&UC`>tsAMDTP`B91L~zmMo_pAa_azEJO~Bt}hPRkHIF*c+)^MhBhqS9$FXbEnD)M598LSqXd(TliLT z*ctbSHocYP%(~xe2M3F(Vsj{dTe9POz-zZVr~~mtw7V)H5W*YjdWZ3byI#tJ@)EZt z1PtCVdH%zpnzxg~E_o=_dwWvg?uQt=!p@g?$uxobEDDBoemHb&dy;={D1Jw>Q}wXV zce&fT6dnLIek8PMM^Yf`5nU=M*eS=;l*ve)5Ol^o>WweP5%belqP||o(0z>_W4>`C z9r0mAL$#kt8OwMl(mCapANWy`DeCU;<9|GK?42a%hR2Wh@N+?bWvFmha#;8ip-sDz z0+&8PCxsHXC-`p*#lM>z*g?#A+nx~CXmx1QyGhPftG$9aB{@3x5e*8}4o(Oh#v80A zge8=PHoceRzcp0zUb6FPnO76$+o$3ppY)DD)nea)L<8OKD#z0#$NX_y(5b(su3=mc z!UnAg&D)*iUlpp@og6sKCI-pigs=;r3ibYgmORDia2vXCL@=Q!hOks;ImpV=*Sm^YKSX|lR~xo5(2;DvEfkzyxE}&;tJx+LX~?H0)Mz((TIew5%$)(}C3Sq!?u4)_Ukc6pJSlLK3usE< zRlGi-$~_5zzdVmb36-IFUnB+QR@QA=?|CHu?l|lIvgTYJg>2VTzdR7C`7${W_KJo} z?UxBo8lGl+u9q(bo&7|E>!Qfq&EBCh3sb|+4}Cu{(s>1Oj2 zCL*r}3-<;CjjO1EyF5z5V}793x8Z4d#4NBiE|NfVnztTn{g&6e%y)C*B5gp+b_Jcg zi2AstP=UZZib9nmxZ-S+$MwO_`uY20|N2nH_sLGe+dkh2H#O&p|ISeS56R8}n2KUe z$;J2V?OuM7A?oRjCDMfHEkf?G5&gzx{e7aiP$HN6Ry*p3;Fj-$&eud^+-Cc z7}%zSp~`~^&Kd7|moX~G+Gj$j;+N#Wde{(+T&L-K-rB}pVLAPXE+j{W!{{L2qE!5v z9Q_6CT3DFx-#ggDOw)A0s|Du*RFJMgq#qF!`dz1`lmmOMG!eTc48 zZtg9?V?>t{vGU~#^%apyk9615XMW&qb8lyyr9?WM5uuc8f=(6DXd8R1;7EMpnT?ytwiqXC{S-7L)u-OEX4B`Ec@`r zyKASse|d-7yK#Dg=wkBV@-rwH*iV!aD!L^hF!e)*vR3)dHaxEfSht3Se-!Fnn-q4% zN1=JONq!TmsO4n(h!(iz_1UjERkgoCo?AD~SX+t4dspF)L&yG13gmpOuGX!?J$Oua z3lf~q@zh|y+0uc__$S`^>$r{kfM}Sh9PRHDdB8hg=}K-UJ|G(ERiC@ED-U|h?r>e7 zojZtZ6$(j7bcvUen~9d6dfOu0gLNHIZ?<{2J9iVQOSCkNizMjh2L5$%k)L^acxTF3 zBDcQogM_We(oJ>B8nYAl{RHC<@Z(8j|-d*TGB9+K0{%Bkzfp(+OoKP9i+HZi>D&g^oWt5c4&qx`Sg5 z-X-#!&3HOzyk7l-NNx8@>v!1aJCBH-I1qH^67?i%5K0*pbXF6o?F<*rkxz-P3?)uU za4z`9%Z~|ha4;ZSvSszr^N4pjYk z-ckM}=g4=*+eVjk`rb=Jw{zo^LFAp3d+=CGb4wKagLfOlDb5IO{v%&+lBYYm0tG~F zdS@@*V7DwfI;`tYW?mD@s{DzjxT%>ooQR*jsemQKu((JYxU-~lACam`?~e}#J|JSL z_I85P?w8}kcXTjtog%lV@4(a1qEa+sE0Okg7j#b8uU-Rb5}godqk!>`#p7EQP$}~GC!d)i>MncXH)vix)?!wb4>dmh26KQr0n3NcQT*OgM zbd%WF-$$N%M;Umk#^<})op*X2^ZDkuUd!Kg8>3saX?Rmz?_<1qt~c_Jx|!+)ycsU* zT1(EZHxJL9^8)q%bmyx+OgDI=-S%r9t0MBoW2?Wsp717+G$OT@;lKs@CL-^`|2!Vg zMFuB0kv_kBc6n#SbwpQ?p1$H)!^1=gM4YYfvaTY!n8?d{xZm%tqL>vp8cz|~A<23D zvE9aHd}v%GU!dz^;4LPW6KQlbFbktOG(^`DQ8?8b6z;dT@Qd;K!M&4eCy`Dnw-HXW z2*3N7h7*LvUSA@&fhOff@*^5ZO7Ee{TSVu(`)SR8jEILqG0jUPKGyIqspUkf zyLXxXj>y)CXK(Ez{R|jg%HF`!ndHsiEgipm?eL-;qLJ=4P1Ku-RF-!@e@|XEi79iJsp-T--LQ#wM7)bZI*~3cTn9_z zA_?qR<1iHSO94q6MyF9zZ$A#& z5+Zdgxo}0M( zxznuEj&JKP_j1`xjfm9U-r)V3NIm9tz{M@S;qM*VWkjkU+inX64icqlE(-K+MLl$T z>fDBxN(%RZkbjRU?B-7n>(|gG=hY-vpe-NSNPwCy~zr8j2*kw%$YfxuEC9)R7D z;2gwrr%aR5hE)6xgB!WmyK9Ja9{J3cE11-XxP$&Q!TI1sDmwe3Grgn9q{~v^86uv? z3{8lv&+j#;k;iYc0#`%0vlx-!Jc8$qO#kp;WV5z@ck=X7KKq*!2~gkBuM30zb*82V z7urBOw*z>TdpX`^Zl^4%^ZeUP&3O#1PVJA67{*T~kvC=NcBzc$_>AqZHZ|w7L%XxQ z(dP|^$wVwN^b9K``0?8Fk%>3<`UPCIvE3A{kW1Ba0bful-P5`gBvdpy!5M+4gXK+bi-~klm^?-Y!?$<@f?;)fH#`8NIXK+!!24BIkS7+ zVbR@a8j(_RHDMc%*6L z$SD{3eet29g$d3)yzAT)%I5>3(Ml0E>|)b9g;HO-*vpS0#EnPL@0;Q#)*a(Nh%|aR z06hIXK-8DW8`)X>5{kx!-z@CHt;S*(o;3^=?aWYVf=hPPZUKlf7Zj8buciB8@2T zqWLqCI>K99_r2ug)8#HATQ~Omk;vPR?;4#eQtFPJM%2ATI+2++*Yb3TNGBvEuwSQ3 zkDuf_f=)V-4vKfn6(Z6+?{=d9Unc%4PO;N2^B~8K-LE2wCy6%?+(V?MyB(w*`m;lH zUvCHZq8m1(ui4a}k&$wFU8_^LI^0e~*K>gO;n}H~JAw{Zc!!H=mUd(lY5J$94g>>F z5?!jE^?zdu)40^dUTHR^k@e^+>*i)X{JEcqhh`)CMBwlp2kV~}{k$oXYBI`46WOVp z)zJM!UYi-w&Id%ACES#Oda24vwd{+he)5|Xu7Ht5UjM3=&k3ubwa^--!c2O&*m`HVWZz%n1P0iKR@h6~mq^QW14!`N?9 zT3zqx4s9cmcL84bQP8O+@=hN=4^{dPH1n=ukDCU1RrZDaGvgu&?9iKWa=>NsqCwu? zq#GA>rW1Kx!iB;Akf|BYrF}1Ops5}1PY9edm=oP|XPj_fKH)YPay-Fs+^f8_590Ro z+(Rea^M@X%1t;9iC)~)Zk0%&>!d-E~J%D?Kt=X{S?vxYmx)bhE+`eAgKGz&~OHR12 z9e16`;oj{=GhK_r?;OrG{f0#6BdEp%hig|b&|(CY(^4vM9UhNsc(PEAr(@@>O@FnK zcSXN&B!#-udjXzOdAA!c5NQl?W#vr%nn**!%^|#dIwcQDbQYw0doccZ>OtT=l+ai> zOr)zTH+3wcqOYw>s0nuj5f1>l3AkMzshBP}*WM&jk=|+W`-zAOTsf+4G;tP1-cF>m z^7t|(uoFTvMkfTCUq>$PdhtrUi%8Wpl(H!3EFe;ixRsCB!=Ee$^&XfesD|zg(lo>G zODA$W#y`*0D9Lj`O|qPEoG=li&Ajm}m`9HxsVN%I*xP_R#&cVa_4~3sw-7hiRA&2= z{h1!QEYt5B<+=Cc+H^ctsM~uiZibhj5_g2>cFm&e+-YS3-lf#U`>lf2M7pCU;&JoO zre-3QjUVr|hl92`=$~N>jW?QiD;xEvqksylfCPaw`~>CKsO}|JSTjL*CFoc30zL(e*fXzhu4=>5oXIm zau8SKK7neN=WXM$1T*+JB8?-4HNS`Qu&J0ze|-kz&`qVk226DiRK9x@UD7mP$IEr| zPq;g9Gd$XTnhvv@;AY%(&;0~XM9D;q>3u;bIO}*{aBDo9 zNGVTu`(h_if1*Zip=ZoC^JXz$=gjt2Bku6$H;cTIIEpOGBIkJLE8Dm==tGq3el35S zshCYaZUOK_Y*d2t8=fuy@2E z>Ph;J;p5!s9Tj((5teeJIku1iw+O1C?JYh&GKDvALIiK}77CoFjQ2buoipBbVm*=0 zb`~Z)n)!{02a~MAy4>tlgy&`Jf`QRQ@us#1N9ZA37ULw@kEf}@d-8bZ;<`(WdyyjI zH*`p~3eT$%&vGL7i?fe63ZEVAcOsY6wL_EAnB$S33C0lV6l)M#I5#eWAkqD%v14i7 zT3toWAmZE3kqQ2%H^eXYZ);HO_u4i%7aESfZ?~VU zt>A|({|{PBvi~aK|4-?>^8aH^)xvLSi5l~rZPCf9?LXS>KcNBQqc*NV!-#)J)pLK@ z?Gb`}B8uyrzfqxjmjBs%+1-+oko0d!b!SIlD8x@D+vKER{Q&AF5R*s_o+ht;!v5 zo`+v4v zstWvK<6pwKAjQ;sto@`mGCcZw5nJerG6#)9~x%bR{Gn!4T`YI0yddc^=n|` z|3+1EBhqV^CU(D*wLZM1{e7Q{)eDdrN4t%dKaoqh94^60aW`v zifa368<(Rh_!+BfQRQ2_&VuJq{`ofWLp#2RD#1&FR^`58`DV+da$n_#&ebYZ`@My# zB0Ev#_a3SW?6$fGrNYI&Pb@fy^3V4tecFn}sU$Ht8n@R4f&W|p@^ zRiQRE?tp4?>wzj=Pggnq7up2zc0(Uj$L2B{UxBJ?2cl}hV9T#Y)zV?8Dwb}ykFxPt z8;?U(ksQnOQ2zO*^CME{Um=b%o^Ll4quSvntG8NRhN>bfQMKScR6X-JstT;J`Yfva z*4cQ2)t68;>{V3xY(tCHC3`J6fU4jxQ58^)YKQMp&2zt7{wJyo>QO4LuK}vA}0vgxI&*JM;{r@1yRK$UJ@ zU5DYg3Fo8woUBS%1lOh;?DqdgRq!I4UaF3}8C8ButlkoEFBm@GEdcFsE2_`msdik- zcJ=r&n@*~DIja5dwp^<8D{OqSYN$PEw?ABrqYNHLo1o9z9sZLl;|-)!(wF$5_+=Zv zimD>7X`@x;zm-@$u+8#1Ev|F^ld6F2HiLI;1}Ce~Zp)=Q)(24)`~|B0zp`45>SMLo zb(G*R8&vaetRArmrK7F zRmH+I_n7kg?J_T*kk84gWcA@{cck5ZvWh!!6&GdGHAb~xQ=49@bj_@uX1P@RwXDZC z4%|}t23s)9CXgzl;Z{dlF4Z)ejVj{_c6*N9E^TBg-|(Loqs?s?u+;u~gYDLWN4L-i|8WGSt0Vpla4CRO#=x+aI*?Lw5UPmOo+RHT8VvkvIL( z?vlG4sL#o&E5ma(@kUgcSJ-q?6~4vte_DMNRb}3^@m3qZjq39^TI{xFhfQ#@Dx-HT zmn!}MRmJvO{@Jv|0ZKRsRgVs}@ztn4Qf(h*^%_(a9bw~hX% zu5(ajyuilNX2eS^zYSGJccMCi_n^vX6{?R^@q;#&D!+%UK5V&E=^jIMi7H-;ql}+N zwZjXjKL1ITZUgC5!L4@xx9$Ght?oe81MgUU7u7w$r>H(sDl**n1&%6EZS{!NAG`#< zpHUU~3#yM)8UKc=C;mcp%0v<W+1* zy-{`06{w2qXZ0#npOaN+sO2Z=rS6Wy?1sNn6`0O;9g*u$RV2fvmo_o09=GRcp2a7t zl25f7+Wi+iWaVo!*XQ zVp76)>;|b4zH8&ZQ*D3WZa-OD!oRlszgIgk{r5Xi%KueC|I2<6)VPsJEc179)FZ#E z0Vc1EyCsEx5vsj@oq1Np!-zG1Mxu%xROuSpbSJC&w+UP)K?}S6zff=S(2^ZAT%%DH za3-n(+gRQn)%|NXRG~jK-G{On?6TR?ppvBpy~m|+OY&x#y8k_k==f?jTfWp>VMdHDXK%T4AtjkRX!oh zrE1738yD9(|8GU)(iB{sX?_qR76z3*@RzyEl<+#hK^;zzo657%!0i>lZNwyU2aQB|t} zD&Ek>dhevRH$(N2hMC0O{#MPD>~xEz;U*6pD;#a{$*Nr1T7FzNEPUF3@2M}O=o&u% z;VU!uq&W43)L9HsjnY$JNVx;%@4leY?0M=7Deo$B>ISIUil6#I$~{v~eIcd6 zaq0^xo=DlJ@~6I#a_5axUr3$$Lh95PQtlJ0Q(s7Ni8%Fz)c@-*vQB*=rM1T2e35nP z3n}|G$*C`-PX1y_U$dP0LhAqO3#pa6{e6}U*!5iR-$xD0Kl{!H+I;n8{GK+OzZ*B} z=V_mOF!#oFp{>d59*peVFJbEETW`&KsxtPSoomj0b7$d2$)S?=|m#Ibz?k?Jl#K_B%noa08fAwK@IRuzLK|TsCjed&c+5Bx02?Q;OWp)_dBRi(D1D-dflL2v407nEinD{AxLjtR&04hwiz=}LTS{~p}wwSVfz{qKUsA+&#P5LxIBy1Z)@BW};^Twh9!@1Z+1|0tK@Gv9ka>P2ns+*V%ym0=rDiY`|WD zrLzI=nSBCF<^U4s0Ct0*(rl7Xm&pH3F;W0W#(R4w|xgfRXb7QS$+xne_R9$OV8(fiH}+0I*RYZvo&d zQz4L31ZZ0Xs5ZGpfY#RowhJ6K(bogE3KU%rIAW><3W@=-#enZjVKJa<31Gj#4<@Dr zuvcJd3E(HQPhiPHK;lBcFQ#-MAnpdh5rN-K{0)FZ0;_HS)R=046^j6AivYiym5TuV zZUoc{)SA>A0Y?SOZv^~hY6Mo_1jx7v;5TJA0Y=^oh`JdNZqjcCL@owY3IvR^7_dwezyZ^1=^a_+W|)f%5Mj>H#GvQ?*L@n0q9`L?f{Hj28db)=w#BD0V3}NR0?!9 z&Yggb0(o}=x|j-qoDiUG2oPg(Lx9$I0k#WtH_>+iwh9#81&B3O0tL$fvC9GHnZo6O zu6G0W3-mNGcLVkcEWI0Wf!Qap!~hZv;389M0C6h-M+AZ{c(WKr3I4V$n4OR0_CjCA@P*M|W61%{ZIhX8v8mOccy+Uyfp@-QIrVZb$}^kG2UBY-0UBTW1wfI|YS9s#7A zYJnAx0@5A@j4~@91@wCiP%DsOQXc~x6)1lUFvipftbQDj@i-vUlsyg@`2-;92|$)f ze*zG>8c->aZJgDBjRJYA0TWGyKu#H;Z5bff7Y0fz)u zJq;){)dDM?0i-PqSPNKa%GLr#t^-7^ z11vJ>>j07K0hIzb8D~9Uqd?wzz+zJ&knhOB(Ul=K!vFmSn)a_?RCJ5X65UEes2J31u9MI8-Sw%W>Dg|CQP8DFIKwcH#O;aI|^A@1(TYxH)`xc<}Ho$g) zZ6mUHo$gMB~b7-Aogv*PE+_cpzC(Pet}&kW;>) z=?*~LPQVd?Jtlr9;E=$ooq&C&T42RHfV6i2ADWf#0Q&6$)C%l3sk;D21!0dd;-`gkoO6ozNrw%IRI#T z0N|M11Ax{C0ow%{n&^XotpY^{0a2z(px{$L?5BVxrtnih*Utd^1)7(+sS+qS0*E~VIL{Ow0d)NquwS63iTM_= zS77P4fD6n%fhFGo62AjnWJj}W`~XP%0gzx;{s8Fr zBcN6w(WL$eI4V&7BOuw-2(11Ikns~B#gzR782K|G>Sw@ZCjDnXh3^IjB z0bOeV`vr!Wm>R%dfu%KotIa-vCC31X#{k!u(qn+Q-vLJiMws~D0fz)u{SHVs)dDO2 z0Hpl^7-d%e0q9o?s1?XCskMNk0_C-UF{Vaf^`C%@KLMGh>`%bRzW`Bx0kTZ`UtuGb zM~2fKinEtH;UkuB43Ao#7tWGnc||x~;b&`GKXI2*7@U=_V!uuvcJd1YoAwC$J;{NDKgGo6-Ovt{&isz+4kw4{%6e zRXsqVsTNpKACOibFyE}K59p_M)Cv@t)JVWlf$~T|v8fSQ?Eo?yz(P|-Q_aW*fT#w5 zMJBxgAhIE#Qs5@zGz4rE$ZH5#Y$^nD8Ufli0+gEEMu65)fb9aen&>FNR)L}@z*18s zP|z3<+Zb@WDQpbr+61s)V3~<&0@y3CveSc;xs_oX@Gmp%F_V-ngePDR+-f1fTIHC%>fUX8iCa<02wU+51FzSfRU#I zqD}`qV$x3sM79J}3Or_=RgWCLr-l!1Jc`Oh8;4z!8BB zCcX{ekie=ofC^JBu%ay>tu5e1v$8FqUpqjpK&44-2RJHF-VX4JsS#M+9+1%XIT{{Ez3+ys6odJ6VmUafbXZ8s!IR}t<4q&$_JqHli z1#m=QkBRRBI3%#D3t*qA7Ff|0kk%FOp;_4#&@Tp1E3n_B#sH2Al*a%*F*O3Ky8$w~ z0S=n7Zh(>90a4unpPBUTfXH(Jl>%QF=Ul)>fxL48UzrMloLE5HSU|POjRmys0oX2Z z*hKdLY!xW#0XSl+1Paar#GVKE&J>;p=z2b2zrYVB=6t|jfu-jIelq(6mh=Q9_5}Q5 zN_zt0dI63I{AS{N0S*bQ>IJAV)dDLn0Hj?2_}#3$0MPG3K&?QnNxcwoRG|Dqz+a|D zVD&|SjEew%Q+5$xM+91#_(Z@VfmMlsXj3h)A_IOak;v2Gk0)HL1yfqXOm0fcB3Jn`ktvm2Yz|6- zCcYmMXO>CgO*LXxq*A%GR4SKXR;B{_T?MEWNHnQe0geikUj;}uH3F;q12XypQcPKY zz{mlBr~!b>O!@#oWE!AS;Bw=n0X7Qcr2(!q6#_W}0c{5YQcdnaK=jr#1aP(4C$MBFAaN+*8dEwH5O+1; zh`J@@(kzptn`+6mCS@2h%B+-JXMT`mnAB^K(dHq^7*it|YlaL*GEJFeobiu9 zvP?Q+B1ck4

JNHqJ=EMuEJMfQhC;ASWHrHXV>_a?=5=uLW!um~5i21#A^4x)zXU zsssu~0b)l1@=f6=K-cR4`vs<(nCk$0uM2N%Zo4l0DlH}imSm73F@qGdO=$)oZZzPC zz+4kQ8gNKp)o4JWsTNo<29P!eFyE{k1L!vvP%BVmQpW<03Y3op6q_1>)tP{dOu#}@ zmPs$%!2ex>EHdem8_inDO~%P0@y09?=Vg(2v8e#~mzZYbky4W@xy5Xe+-jn;NxU_i z#6{U8UTUfY3MK$zCjf3Yg%bc>Cj#~hEHg0^0ec0OP6UL^K7l1UfW#cYa#NZEh|2{W z5ilk`7jQ^mRW4wqsTNo<36M4kaIaZ83D9pcpjKd&Nu3NhDo{Qd@PMfiSUm-hF$M6D zDVqWqnFol<13Y5V^8k@k0hI!e8D}bBqd?wNz!SU%6p)h-XqyixGr9SI*3$sn1=g78 zX@IQ)MbiM~rb?h-Iv{pB;2Bdm9nf_KV86gx6Eg#_S77N3za44I2;F=di}8h-)u zs!5l;X4XnxH%=k)hRKw?X(}XJO|yAOmB~d+>-ki5`#h?;%|y=!Y!xV)57=(11PT@a zViy2*n!*Kuu0??T0=rC15n!*t(jvfnW}m>4>j8<^19qFz>j81afFlBXOnfomkie>9 zz&=wgu%ZNzRs#6YtSkZaTL`EX*l$u70*(rlF9dvIY6Mo_0LZujaL|<902sLl5VZ*K z*@pB*;eVYG$jISO>CCRu@b3Iiei7}u<+^_ySn?gp-$9q?H;w1y&dS!8`WH_!J4(ZQ zD4qG}{_x1Krjs{(d4G7faE?W@hrp4}YVldbD@*zoU#ij=|hKE(kk$r~lJeHqU9l4R<{k zzR@4m`E{=qPi#}6WO}=s={GNsX`>xJU&9TV2f`cr8|2NNIV&?OCp+Rj(|BX}oWLvZ z>#x~K|AaZeBD_r?e6Q`CCtBuS@BSisN!FCy9+PHzzk)sTL(_Ou_`f>bW4F1@v608}Ogm^Ix>{XaB#L>oL-ri2Cc+@a-yfL!;NiAFb#AbHm!V!h`k0??~~Rou%Q| zXp=K-M)oZJlld9jHl)29Uf*eO`Dnjeu@+eyMt>OIGCbgR-SI9`^D`rG)DsZ7tK=49>H^d(sZ3q8np9E$eL4>77;=T6T_QdKE@% z`^)8BJXY+!Poty#&GW86y*c1CtdmWc1XDBgIwquQ zr^syC!%@B7Zt4VzaX0Wc9?B{2T^I%6X&yV0gQ2R6zwy~0n$cfmfl>}Jcl z!un$REQZm2#crXg9`lvjgx!c6+nsN*tUIjkx8(GGK%E-rVn3?Ud~UPpV)1{r>~_m~ zz<#mp4$Jt@{Cq|3+X!*=qCsVNK2}UZ4Xe9sLSCuwE44c>w~ROO`|hypZp$u!mDvMh zU>Y2}OrQ6t^Mj9GVyK?sh4{WK%kHV`>WhKnExgwz48q!5rZ*pIXWqK(d)AK6RhGrW z$}GFzGQG{K?rmKUSjNlhebw1k>Av>T z75jz&>jO3L58IuG;y=u|)8PHava9i5f@z!{v1}N=-U_2}`mJTx;McuZ_dCmm!?w`j z8oPSurMh4QR#^AuryndF3DjGkG){knsVDXJgw1y7{A|-*3%kX#Uo0C1D}iZD{%RTj z+kpEg>Kbal+59r_8`^zq>Q0Q&z(zogzhidiG5C5%pT^(smW{>#)efCMY=)Vz&tR9L zf7*28@b%KkzUW_;W#Q|+m+l8u?oz_&^R!aF$tTaH-G6kGyH zUxPmuZEE+Oihm4WpVMr*eEh|hHMi-e!7?p7Jz!6a>A-5$fKN-~|2U#~dMlg9Oi(9! zKpoZEvRU}&*iO?sQRU9Y8q-19=oyyH!Pk2<)OlxGHrFg9`(iC8|DZ(mOK~;A7b1ST9rl3ICDN73P;uA}$Mzq=@rP{DFwW z{(7eK!H8t;Y;zAroXO4UO$Q@7g;n^?lLsR@rd`aiXh|upFs*vd#M)qOv3A&5SO-iC zoi12cEC%a_or}d{JuoeLdYb4@BZB@*%#cqbI`z5=qE$*mtPvK4HN{$B-6{24Oz#1G z9eV?NGhiP7G@{$(F>t-c{yj{qmtAbrise1*eQYay6?#8rusg93)(;ziX@N4xoc>uv z`_o3^>W_=NW9MRB0voRQEaC?L>90|TH?gf)6?QE)dc(fYBf`Uq{~~`4d_NY3g=2b| zd;}W6^g{W~nBJapCe{W^q0W7=E3s7UDr^9jh7H7qU_&vjXtaV!$F9Y+a?z`1KE@7U ztXk^%zQ9Q^nO{Y;ifV}42-AY(Yg77FL>C?P^o4r_z9#k52?3+rfh9g1jQ9EID4&3ZrNd)WJ!-XZx6rj^55Y#pYzUarQp za?qPFZ^UlK7GoDtBds3Nu?$QLgmG9FHXa*{4Z+UhP-vl`x8&;;`mMs~I;|3Ro7vS7 zog7{Cmtwb@hpHo*U%L=@DmERPiOs@hW4h$)5}%7r#PlxW>#z%G#zj~>ri+PQdOjM} z!ra01rfw|*&JH(?zm90#DsL(~FHZU=>&^21)>{hv^DF36_t| z#Aac$u{qdWY#ufr(-nL*ruPd~qq+#|D&7n~0@IcIPmXa7rdOmrioF-fRp%`ny&7#7 z_99k^ZN{F(^nN#8jbEmn8?cR7K5Ps&7R$uOndOH$qLXn)VS2USuh=8lGE8s&+>7nQ z{)K&n{e~UIKEd=BMZHn65SxJM^_HEnbFeO0H>^8$Ce|9ekQ3?~_S2g{zreo4zN*KS zs1oO8>=kS?wgGzzE5&rN{|B}Z(|cTVuv}~sHU-PWref2u>DWxn!E~VyVD&Ivw|~Tb z!hXi|KHnR$o3I-KT;+9f)(hkdvH92nOgCM+$k z5y{2RbC6!dUcz(Fy|AO?Rf8SFe#id6YOyDDIbV%ahCPX`!JfjN#q`QbgRQ_;Vso)vY!a4^>1Cwf z(@8&IUt@1$+p*WM*D<|4`Ah6h{1A2*R)p2Zj>2{6zd67vQ`dW4-xt$4OR!?B+N6BT zHTM-7Scx^EWAx&)J21Va>r%?T0!zY@vEDQ?8B4+XVqLKqtUJ~c(+#NJZ0W-`V6R|X zuvam?Npl_c9M%In58Fl^zt&B_SJdlzf+pAx9JE`AvoO7;M;G*OvGXiQ2u|GCI zMuBPAAZ#!;1k;r5c_Ag9t%zPf}iS@!RsLu-OLY!g>cmD$lI;J^2 zF_cV(!!N>uSOnX{*)|kE1=F*L6znuA*Botu?WEGWjlUbc3LA{|HEX|%XnxM6)ctbo z3alU29~*=X#y-LHYP$hG^X+#L!+Td#wC+Ri!k#0Gb``Z?VY7uEccH!@1-x!*vgu%RW;u-Lw>9uVSMy-JIy& zL-!TBfoV>x7Z(OF-H+&9hexqrv4=3-k35d8#dIH{`;IM?_s@Xu4Bs?@=~Q0#4x_LN zD!m3PF;zcAjJhfxwO5gh~f;qNpne~cJa?B@tNgpru4R%cpoze<*@v3--VDcBZ@H<+^3GW%?-DRC6$ z!?dLS9XpIE%@}kn)`8-+1n!J!iK`{;9?Iige8uj6RicG#Eml{M{#zKWC-s8xKd~Q) zm!PHCIP5#(^TJrK4My;BCrpG4{ z?V^T~#}MpM?2!#^e~!os3*3%>+lHHdjc6DVw$#7jwqp?w`->j}cEz4!*9TF9@xN~Q z`0vJiA*?T)a(uYHJNS`~-H9#3^l0;L^e#;4#q0K4j{g9*0$YjQi`|3Whpocy$NYY> ze%P%2gPBN`dzCW&iLJ+`QK%YlGky;3J4UQD6Y;aL@z_S#NRzy6b`)mYuO zHTYtRrS`$^Uzrpz78`^80N09K|BX)urvKJw6n1SG=cktE>DX}WJWMwcBd})J5q7)= z?Sa+eYdNo_@G!IqcD1Rkjc65p6|Pp!U9mK50M;K%HL-t2v?|tsxeU7$OTjL|I0C*T zEEY?|bhFS6>w}$->A|!fSYL#7$1cP!!1Ny#X%cFO>8uIEdSZ<+4c4EynbWDKVD`=|Cd8bx0xIT& zrUBt z;_qPOl&zfec$D~Fm9vn41FU>z%%j9*Y{;(xTUwrNJUB`KHHwN?avCWsengs_Df865 z5{PFGn;M&wG8;w1rlaU=J7Igxb4A^&SsAIOBhNc7WXZXQ+&gB-^PL-EI__y2-1B5% zTGe=Z+&clRS`$)7fK|*iyz5me=X<;BNPU~1)~3QT5GiJgiRu~8Zf2tH)iwJbTOnCM zp2@0Qb%K43$`kaF)(l0YG@%+%z2u7OT9t7gHjnzuS7q+F_XO0vSzhzZXL`0HRXaZ{4dhD|lUt2ocYpxT?u9`F1HS_qg?`22JOn7T- z>(}0++}`k1PuM=#2^Ry{v9kKvS8{#ZPB0xiXqgwOH_lF!Gm0Zs3>6Xg!d7a&=5e$i zCbkXs`1V@kdnL53ri4Lb{JFo$3WM+uUTU@ z!bhgsTUEifCp=A=xlQGNt!qD2_B@%-QXYR>gK4X0ifPgf*PVc_Ku4fGzzY&jA!gPE z_niTzi2#@;9OwWr9oOePrsX>7x~DAffwVpQ|6Vp3y>Yz|XpRb{fl$ozSlk!E^;o2% zfEZvTFaj73L<3R4FkmRq4{!iI3$Yt%f7~;#0Z0b{gMoqaI&uguxR3=IgL-<3>(Kx| zmRKP3d2=)i*95RYQ-I09Bw!*i0T>622Ogr{eSqr}1lU>4K>l=K8t^+X6`09E^?Y2! z0e=8+I(!SyC!BcK9MMYjQY+_Uva*8yt*6`9W0`QaH$a?s95;sSI6TmUxIPj;u=JXVB8aN9i0{rxq8#xbL1FiyB zfCoSla0|E(+yQtmaU1D9fIDy(DYx|qc*x`b7cL$HPk@)ebKnK=8u$PdMWuI0-vECD zZE*h<>3bjy*Nw1q<{x(C`%eJ>*kgTwA2oAb69jkw@G#Xwei7tx&-rDQ(vBGaytsIa z^N3c+YzjhMwYYx?yZ|PnVr!JK!92tLQ-Ftt$2AVu?NG)WkV&{^JDG@-e+-nzJRE5U zpgq99Jj!;}5%t(l{DgbF#E1X{;6tK3Kn|ci3iTgg#r0QQe+I-Hu>Nm{yf45%0MCDC zFrEhd4t&Es3+IOVoW~Z=z2<4oKlJJX@WUCV3E|^sJ{;te8UFEE&QuJpqXIw4$pCQF z%D*m~4oC}J0z*EoHUX>+{w3NAbi-LQN4nw?L+g+kX%-+Wz^;TJ{;-0XJx>DeZ*IUH z;AZ(0oPS@I%kll*x=5U_CJ&eA1o$bD8DJVNix z1WEu*z;7gQWglFZ0{BG%zE=$~L;e-qARrJZ4LGvaal*8=zlaLWKyfl5F{paM{y zO{yGFmT%;B6{MBrHJ9-<|6*1I7YlfKfn4fG1Z69((-7;C>{~73d6b{%kPpgzE^P3(yVd0dxn-qHF}x z;XpLd6JTu*0{AgcAD|ciP;qZu^ac6@d>q{mDd+JMxPeHA0;-JZ1_Q(F@|f8WfN7aV z)#3aofE(`xaO0eEXSfsHSPi2&1HECV$M4ik!2Mic4lo<|1DF9!24aCJ0M38)9!|&g zG~jn&D!?k{>zM#+fG9DumNk;A%>uY7E@D>mfFrDowvvUw3Y&Y-MLH})#!_Gjun1stTMSeOxDqpAhMY1uZxz6d zRXLkwBVZHI5U2-i1egcU2^MNKumM;PtOM2nYk8EJC;{L=U^~)n01L4NhzFP<3&ae! z0xaZaKr!dWnK{#|Jib@goXDua!6863$Yaios{lsF&jq!)nm^1gw*zJ`=-iz#U*2z+U|>pz1%yb!T`I z=EZfnjmYP?_#1cwyq4f=cZ5@pKt|vT?)lfe`K5=n0LS{dk>&#U&2DEP4d4QpfvkWA z7=Ul6TL$iiUmxe!%K3HkugLoc_?*Upe$B3Bi(~?>%$1lKH&_VLDFHGdPc`m@Yi`U0 z*fzpU9gxR$*uQ z+H#SRp<+N;+|hfw*X9&2Wc=61Ox&BfWISk%Ax%nSsm9Ls{D+!3UC?M z#lWL7t}6i*fpS1u;3uFAa2g}QX?dW6%;S0-g;d3THGrdW&Z~v%L%8NT{Mt9axxjBQ zaD5<`UQib|b%43d1Zfb``9LVubJwyKw6&DRDCS5#48^>^QP05II?^b2t!QL>lykV* zf!QEULfQl90;~o)1D$~GKsO))=n8PTDq}~@_l(H^*Y^f57v%Fj2l=;<^3LHVFax*& z{K12I9T!)DNx&6=nO_FR15v{k@B`Z4(VKA9>C@Efdv4!!+JdhDBWUA zB9ryB6a_pW+@0k}2P5Te+CrciunKuAfn~r7fN5;Y`JQPwWtx+~3E((z46q%OwYXmc zbmRB4xnKjZ4p&I%XGIs;&j%jnEu5 zkhPwUhiV=)tcKx%yJomf8_+{_XH|hkmXL29%@ZFN3}Hz`aT*^VU9xB?E?Me= zM5s#1tal{bd>L;ZXMi*%JJo0uJ;vptt}`ERA1uK1tS)-tOJ4P~aC{_lY&|W+e13qw zbVTLLKfjW3Oy5Z$hveRPE#@A*s+TMP50S<2nkwzn+^9i)ZGpufZwKRDs;H)h&AVzI zm|6xEOL+%+`@*{qB-aMoABuW!11-1VE8S`UO$?zD4Z(9XRd1-@O>YQ+Zqu=bS~YxL zHd7;Qk*70WKlT9MaeKGBUb}ubrm(>QInKzT=EXICOF?e9z6K{i=@Sg|@$qSb60Da|V|Fe*>)t=HxGE{- z5J7VrL%^OC-x!khr6;&Ej|F*Vkf#k_NlUEr6qfiBQgzwoP#(R=c;4Rc2v@}b0x zl5#aJzL;C()IMuT`A91No91o~1qExr&YOhwzfI|%Bd?()I#$X%*r7f3{0$=YrSY5$ zqb0v-TMSdESrbS$3$?f-XEH|R|2$@^18Tuixy6+ESZ8jE=@c4G#hYsG#-dU5b5qUN@?%aR!)O{P z0`rd$Q}{r}>do4ZXwk}OV8ipn=FnG`%yfDa{!`4(I-d;k&e9&IKQ)V8g_@-AG#B4c!VJNpnDHtT~cO4M*B^B%Q%M zz5t+PU$iuY!p0z(NI6>|nMZN+kgTJ6e3w9-k(d+F29NadFJ41qGhZpFx4{F){Uo~M3n3h1MAeChGj;t~`}_%ONwyez-U8wjilMKAk(Q33gjq;y z#ZcLnFv=FFnFWousu-Ii)3{v^MNJ;J?$o`d<`EDL0$v(2bes}%qTl(ELMk7a*ECtO z?Zl4YO@YygvMK~#M!Qkfyh9SSfBLuUi{TUQh^ibDo}^bGFeFiiR!H8^f|)Q!(o1R(!q*!F4xMNpw>6AjO@(MvC0)*DV;0p~T(rGtgH^edGPMTX4$3_qbf-Ya^L=UB zEuLBYv*}%cWq|H3wE$h<2TAZKeBTZ4y(fRQGIAa*s?K~G+vq{rk>^DTYc}s_THP9B z5Hy;SrXj736!!sbP$N4U`?@mCsNowc#K{^^^#ECt|DWoHOaUd+bBT{`8j~n`3dCJZ zJBR5~hlL?IL%E?)^8?gkhtno&nO?s%53J6$0@*P*e4*GuuzR;Lg1~oVFaNYB=k#I% ze{X*f6dOY~qaa%4G4wSIdTk;pn;!U7WkN3ZOhN#kK*ZGK(*{kBrdmkM^FZg0CeyV& z_Df!KO*M5vx0ULR1l=iGhk0OrAnBUsSoYlW)$gv%uar0I|0|sYBTM$NVo5pN#kI-0 zF+I`>(b()tjwP4g5Us{odOi;mER5<51>-?fr!CBN8tSnl_lPU|Iq=KZYl5*qEMToH z`Mf>hqq)$V`l5t~@+i$kRr6gCSWvZ4`=7FWJL)(=R>gpQqeKuG&Ex24TOPM@^mPo< z2IDACJ4n!jYIR5bN#n4mMg0!Fi<<98zqE+@zNo*Ny0(Le$Ei|pEICssu?NVXf}EGg zfcZxYL|<^J?`kMt&IdjYdrx`+a*NY=;UFs|gv{*T!#mtc&}clB>WD?XVz}mrZz;*$ z6U=*3r4C3&P|x;AX3}_0*3ya&nxEw(n&QaooY$e^{Xdpj;A*%E-n`D*ax&(dKrS87 zpVT;7d?pBU9uR!vx##9=FhQ*0mZpR@PRUiLs=;}yG^1}2efP&bm29L zT8D#Lm)FfpU&(wX2_G|Ob~E>M2fSi0T? zNt3 zvz_l8D0?09um{n#d#fu1@tHuMx?=of1^T(0=8p+5xEt(yCTuV}G;rYdgc)5Y`kA*8Nf?azl zhwt5)Dp4}a$WZpu8bXQTGS*#0q5WXgVs)daewwFJ2)^s=bU$c0%VK)m54|b0m~!=J zPraDRPte@m?6(zehRUEAsJW-ByckDpFv7VM(_iyTg>aQ1Osfd#jxKucyH&npe(9g!L+-Hpr-0gt_=yEDk+Y+tjr8_v%!BSH}JHGA0$8>|?2M4ED*-*NIin zqoZTst^uD5LwH0AKG?{skyOJjb`gS`G?E{v*4^LRy8Zd_j%o?Lz{W3d9SC^u zl>Xz47q^=&E2|UudSlWT!ivG}AZWK!{7y@qBeE*|%l}+$l6|@ejYDHFYvpmxJ@rjk~s%Z`I z{)Z`u106MUm3`Vzk(483?P$vU{?tX7u37Z9sQ5ygnIbtD zp3?M%TCs8}F#q1Es*>@0#c@RQ-y26gZq?#4N1(p?a~7g63$c%11Oi?|%504(arb2p z9C#>#P2{r(`#n6CSc1cb;3YV0m_m_@@bE`gEs-FOjh>WGh$}Q~`%^4G{0s+MZc)|p z6A1WVVa<30oKi;&Bsgm>xspEz@6bIfzI8@8d$n%VYXvE52a>jy5sm`r*|V3R#J0#&kl2J6^lo z9@cqKfzQW+#X}@ll54Y7^dQ$H#p4Is}q&-VL zR>Q#K&Wa}p54iA2mwVVda%nM>Lv!`v>Y1G<}IMxmj z=Z+37E|Tk7>j|14B0q69X5ve6HkUn35KhIy)`4M1id~Cj1awR%Ry;a{p5 zTV0|d>oC+Q&+AI_d0n4NI4ecp{fA7P`FhC4o9Kf)>YI)dR&J9)i+8`xc5|(@WHWx5 z3Q{B=;sra1qrOzm5_32P^WkdLdaWwH;`h*ctqh*V*c=OrV_Y|rez@3y>frQIo`9xc zruXtFdXUO#rLo*qdb$B~w9ZvAvQJjEUo!7(93I1|u0&AojaqnMv1`J@{ie1Zv~TWw zOwL@$;luGDr2H)CNtv*)W4@v3^pOnk_6>HZb&YmyM8o(t*^3*s+F8{%&32!8I@7Q* zwaL}6KR#fSR>5GYaZA|CvA}N&yxX-GvA%xpT>lnL*^J}k7NFo6RxbQZyCI`Hmd3gJ7nI;#2A*^M=*DKagOMO`0l{za1E0Lk`n)7p6(%&5GH-#> z7Em!H=JlXsk6(7FL*n{}KgSvkm}JbyA%R+L(L#(xlW6T0D64dmm>{!XW?gmQ`YdPJ zA|~V(y3Gvt(Pt!ShZ+F2V8x=p3F!gK|pQQp#!tvI-KyGtjw!h_ei zH6tEL*8AkospUR(jK|i)_>j)T<0Lx)((=*l=LRi3uG}2Q#^>veb8|jVC&z8DnKe{% z8=NDg?y~J)r5-|Y+aUF5+U;fXpt^^&taN{yR>s)kG0iub-0^}&ne8awN{zN_r;SCP zQo$WsZG2sM?;To5RzEzJXYC$u_3cVXNbaMqhFS6Fc4(NVf79Jva0#&4-KhN+)w`u- zr?A~xPPWLX-RS-snzb8GTl5K`1(u?^Oik#vIR`9gJ(nfX*Jdgea3hg(?2K6 z9&|s)bJ1+MtNY*2aa}M1ggDgZ?h7+_4=ex*UUrgVvQ>Q+wJlPARYkWce_2v-ewSk& zAG*BN8_}PWRC!MGz};M5Qe=#A{yo~a^juJ2{bug^alKk{&8aTr`br;0x%X;*=H6h$ zlcUDQ{T)4PRm4L~T}+tDXzIEbYMD!3hjILmBY7Cl0?rXDw)rG#@>8;}?FR-Gjb4dE zFFX~|&G8|<0G%bx3*lqdT*-5G|I|UeV8HvpvP7q)zp?z(>CozhxBz52K-_JUjwVK~xB`jw^p>q+i9ijrHnS zI56<~dXPO$X@`{Z8O5WWD69Y*4nV;J==cFxT{L~;yWh#$U$^T?E2k}K9gwp zLA-1;gmNANk#%bLBP-?*DvY7+oQS6W#eeDp?18W7FLM~0UiksfmLdJaqc>;tYUIU- zYI4bt$_()RC_M45(uN~v+vC@ok}2w<1dm^$T+$^q$n|_@3u}oG0?$|X9f7dAvXVz9 zHMjpSjV%9!kIXpO{nZ0l&4o+YYCk6*JD}~cE*u^LMcVDBCxM%T4-Nj^pu8YLC6Q% zm_P}%=}$!3aKe`3SPDxgQQ&dy4@*|OvdxRunQ~1ob#C5g1}exIm&ZU)L1`%kip-!$ z95uABR{ls2Py`DXgs&*ia{|vpaj2;`TDJJ`jqf~5j%MjFLoi2zjI{6s!c^HRW^cKZ z+7{!_8dbavA?j=7atiJj^WhA3FcnW}!}0q9nNP!Zj?k+l^vCV4aKrAoDmTj;U1SSb zOYfS3&gkVtt}npR+lgAA21ij{CNkH9jxgsKy3NU4^0|j(BlUb}e@AW3U`Ho~G!IH| z(AT#z%`SXiv!_-D?j273;ev(OicqzAkG`QZfv-_5E7X?#<8Zg!6e!&^@wKXp@FUaOg01*rDZ5 z7P;V6Bt_AOUY&zedr8;M!!XL9$CKbV>U|ztTajgs%WTlkTo118Upb4RDm!mp7_mP0 zp-dOB+8?DB7r@~<#a@6i9?>c!mJe~PuwbVUgg4UN>C`1?F$feqL4RA_{Iyq?`gnN=6#7G~-^p+p!mXf6*AVge zT-JgDHiDQPM1!_27mLkl886vEZ%!WKM+Kbb#Gc4E;=w{$f?aPEEx4k&ne_K>Pg73= zVzV=sVVRfd3D_H*JjirK^9`8jDHvq9^7r)Lwr1!l0^ty}{;#izPd(uisaqvRCmsF`)*(P_)ox=Jvi5cUF|m zpl?4GKs0LpF@bFCG8(YFw;1$LXy<^(^)CPXrGm8|LGI*o4d%0vn!yf)#@V^>o%zQ` zB+SO*okNWN)Ard-LE#3xP{IR<2MiAGx#`SJc!KNKv{3xiP=&lEMDt!^2opxGJo@cX zPrQZ)Bj&ixA;^nbUx)cs@}gBB3<3`6N_&QOO3YlCV=3te&yW{fyx}U9;p71|O2T}7LtfADBFp5Lnx>q& zbcepeS^43oP$u4n*+~}`a2TJa%Yp{N+qkwl@o>;i?v>b96~G63xM`0}(_3cjG>-RM zvgwp8l)p8WEJW9_E?Fw0BAebnZl8-68uj^!wNs4?k?S28XBSYILD9AIpjZ7i{d$}m z#TcO5(h1+E7I)AqvC1e}rQ25RDKD5E$jEf=DIzgJhB9!l!gh!|rKwl(bEXQ{a90ruG!~RLkxN zZ#9f=|AnfuZ`0`oGg?K7_mRluMs`><5^$opm?;N?Ylh}BJmBC=hBB#YPB)kbl$jez zUYHl45xIQ%;wPN+Hj@iqJ%h0%-|1_(B#|q+lvUhW3YIkJ$4{ZZR4N)3di)QoJXK2K zBoQ-LbkW=tg#0Y8;Nwx2_&g@D+Uw%=q-Kxobr3KSH)inuLd!=Pe|eHC*xLOtlB(vP@$(fBO0ogPBqTx=-;_ zF(MAfyR-4gplK29er9WRtb$}x=?4WVy?d(_r+Lq{w3s})rUa*C>Xfl$$}#zi*m)ZL z18F@3r1a16_Q%Z^nB6jRmGeH>+dp?U<3#D3NZwPB5K%1visw7_3hWsic%X z6^@)e7}k!6*HE@yp-`ymyZIYhcR}l8ALa3^+WhZxXkA8%f_H#3zKMdxdoXE2yFV%q zaDoUD`6j(5GDfv)SaFuL@apDqXlGA1F9-tK_>X-%Hu{})2$_90tBuG0WYl! zhF#BFeVC)GmEgz5sZ20I)~&OB-e%7W4I@xj9)_K#lJ7Crk3qn}ZMymWx_z57{3Qqw zLvy$-S9Tim9#gJD1)BLD>zn8s-FuJSh3u!0Qs^KANA$0}Om4L9gQnq``f0>FDj5;~ z*9Wb$0f#Rqzalr5x_^Sr%&saT;NpK?c)I3HR(LLi;dt|zKPk}SAJA=}bzhL|BEvVV z?k_*-+q(>(QS~gU^0BhH^_$p{&9Y;MKa8Cog1Vbj0R(}MC4uRY*NKqy88_$zh;#5= zH?m~*kmB0?1MhF^(WCwZ(O8WpeMUSJil;(sMH%{qM_#z~wV2Vs0VBU4pQ#$v{tAcr zh_0a#OU`P-AVaVA`_!$+o}$c3m*2s=8oB-hoyiU_jYofSa{mW&hf?dF;W6F)f)fYP zgs-uF4T}1T*ivy28rc;ORn#-2)QsWm^wFkp!-nZEOn#O}wS`jKG+7qC{=sCdB>XNp zWOry8LgfuO78d&(M{J2iux=1Ry=7&Hm^{@6R{mq>zG{3tj$ZTmYUxl)Kuu%iP`YX` z`5KGWiFYxYUK))R8qd&7?g2F$i#DG;qiLPrcQK--j2s)HgjG4KaISMZb8q(6dFr31 zYeONL$-~?e1g;=>kT2-wqFtvKNdjy_qA6N4g_{@tDtI?sI%sXKA%z!%Ksvi8l*VKV zH(Hud7o2umdNvV+X~$M^y0vIwIS|SjF3!U@8*4VE#!jXvW65T8*$Le!*NifzHPzwU#s~ z8}wMWB_*YU-ojf7XB+8QKi`MgF^P^m0rba?(I{cZFn5?kk@QvlIpb!*d#p#NyIQL7louLBZR=_8qT0 z9((WRc`F4znszU}$rr!eoFfA?vWjYD;FjBoA#GSVPjtlm&;8iI^bZ_59HGRFX!#;l z%7o+rt#L*2jwa*IVrnlmmG;t5)0nFHv0|!PIoi`dIiZvi?dePwnCBnVit7nx<_Bnv z?Zf%cZAT(IPu&BZ%8G(9&B<7QELhyuxKMae0!wrgARmWFJY8uGWz4e=iAIZ#2k5JbZ|?CkUjJwAcl$ zN6=o*8coMDqw4Rd%C4#QyI0+=PwsbsTh)&j9OB5C6_c8p%xh4K`*}F*dfh8;+GE{S z`VsvM*ewZm{rdW3ySCxsR*_Glgpba0Z%;gzH{>lGnyQ*aH_)KcEV;Ux{QsqAQt>IJ zOfic1xbkg`jWM_*{o`sKGG?5GDrPp7Rf8ur(vvnoBT6xsIts&SvMS$W=a_!G_{|Lc zVTCfBlx^L|Pt(J2KA;vK=-eA`YVd5@w*5u}CW!9Oq!c*mn$3jH5yFlit$r~!7&ZRr%cS8{XGuU%SEOHc4DLcISJ}Qv|UR_T-Xj%?T)D^TY2Q(sG z(%W8QB;r>Td^9m}zc@}6pPKJYUvt2|GxR2RcT-WbTW-B6b-}b2;M?JwdU#Y=7ZMMmrUm7qV#MGQGYJf8ufs<@Q87a>CB0uAU%| zgB#$bwtkz(IZy2F#X-OH1IZ3&C+Jf8W%J_!!Y{8`P&F`YXwppfRtN#n$Vtj)hTT*e zNH;BT%la!g>IDdzZ#Frm_P!3UxR`SLXs_Ai5m0-eU|##DeVu0e&kM0KZy`%wZs^~* zRhDCGtR>yZ*<#bIc;Vu|wWnmY{J34!8@&}z%A6D4OdCwab0QpY8BDFQ5Gnth!W#uz z1`GdOF;jf=BYju=rbmrDmJY>H!VaqSqKzdw4(VD|bt&u80cXp^rOwG9BXa5OEF~f_?nNyl_LY^foW_>pYf<=0m65$5P9DrrI)fPhYrz z34OxDn!NP C+l2!F delta 69380 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 qPDk diff --git a/packages/algorithm/src/models/pattern.ts b/packages/algorithm/src/models/pattern.ts index d6127f1..101369b 100644 --- a/packages/algorithm/src/models/pattern.ts +++ b/packages/algorithm/src/models/pattern.ts @@ -1,4 +1,4 @@ -import { type General, type Pattern, fixedPatterns as fixedPatterns_ } from "@data-maki/schemas"; +import { type Pattern, fixedPatterns as fixedPatterns_ } from "@data-maki/schemas"; import { TwoDimensionalCells } from "../utils/arrays"; export interface InternalPattern { diff --git a/packages/algorithm/src/utils/arrays.ts b/packages/algorithm/src/utils/arrays.ts index 1e056d6..345a03f 100644 --- a/packages/algorithm/src/utils/arrays.ts +++ b/packages/algorithm/src/utils/arrays.ts @@ -32,10 +32,6 @@ export class TwoDimensionalCells { return this.#inner.length; } - [Symbol.iterator]() { - return this.#inner[Symbol.iterator](); - } - equals(other: TwoDimensionalCells) { for (let i = 0; i < this.#inner.length; i++) { if (this.#inner[i] !== other.#inner[i]) { @@ -159,7 +155,7 @@ export class TwoDimensionalCells { transposeInPlace() { this.#inner = this.#transposeInner(); - const tmp = this.width; + const tmp = this.height; this.height = this.width; this.width = tmp; diff --git a/packages/algorithm/src/v3.test.ts b/packages/algorithm/src/v3.test.ts new file mode 100644 index 0000000..3e773d3 --- /dev/null +++ b/packages/algorithm/src/v3.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, test } from "bun:test"; +import type { Answer, Problem } from "@data-maki/schemas"; +import typia from "typia"; +import dataExample from "../examples/input.json"; +import { solve } from "./workers/v3.master"; + +describe("algorithm v3 tests", () => { + let problem: Problem; + let answer: Answer; + + test("example data correctly solves", async () => { + problem = typia.assert(dataExample); + + const expected = problem.board.goal; + const [actualAnswer, actual] = await solve(structuredClone(problem)); + + answer = actualAnswer; + + expect(actual).toStrictEqual(expected); + }); +}); diff --git a/packages/algorithm/src/v3/Cargo.lock b/packages/algorithm/src/v3/Cargo.lock new file mode 100644 index 0000000..8fea06f --- /dev/null +++ b/packages/algorithm/src/v3/Cargo.lock @@ -0,0 +1,199 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "algorithm-v3-wasm" +version = "0.1.0" +dependencies = [ + "js-sys", + "once_cell", + "serde", + "ts-bindgen-rt", + "wasm-bindgen", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "proc-macro2" +version = "1.0.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.131" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67d42a0bd4ac281beff598909bb56a86acaf979b84483e1c79c10dcaf98f8cf3" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "ts-bindgen-rt" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b0008093d128668fd19214424857b8d70c39340e3779ad3ffcc682b3437a58" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" diff --git a/packages/algorithm/src/v3/Cargo.toml b/packages/algorithm/src/v3/Cargo.toml new file mode 100644 index 0000000..9e5a16f --- /dev/null +++ b/packages/algorithm/src/v3/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "algorithm-v3-wasm" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib", "rlib"] + +[profile.release] +lto = "thin" +opt-level = 3 + +[dependencies] +js-sys = "0.3.72" +once_cell = "1.20.2" +serde = { version = "1.0", features = ["derive"] } +ts-bindgen-rt = "0.5.0" +wasm-bindgen = "0.2.95" diff --git a/packages/algorithm/src/v3/index.ts b/packages/algorithm/src/v3/index.ts new file mode 100644 index 0000000..d59eb1d --- /dev/null +++ b/packages/algorithm/src/v3/index.ts @@ -0,0 +1 @@ +export { solve } from "./pkg"; diff --git a/packages/algorithm/src/v3/pkg/algorithm_v3_wasm.d.ts b/packages/algorithm/src/v3/pkg/algorithm_v3_wasm.d.ts new file mode 100644 index 0000000..f45efca --- /dev/null +++ b/packages/algorithm/src/v3/pkg/algorithm_v3_wasm.d.ts @@ -0,0 +1,55 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @param {Problem} problem + * @param {ReverseOperationPatterns} rv_op + * @returns {AnswerSet} + */ +export function solve(problem: Problem, rv_op: ReverseOperationPatterns): AnswerSet; +export class Answer { + free(): void; + n: number; + ops: Op[]; +} +export class AnswerSet { + free(): void; + answer: Answer; + board: string[]; +} +export class Board { + free(): void; + goal: string[]; + height: number; + start: string[]; + width: number; +} +export class General { + free(): void; + n: number; + patterns: Pattern[]; +} +export class Op { + free(): void; + p: number; + s: number; + x: number; + y: number; +} +export class Pattern { + free(): void; + cells: string[]; + height: number; + p: number; + width: number; +} +export class Problem { + free(): void; + board: Board; + general: General; +} +export class ReverseOperationPatterns { + free(): void; + has_reverse90: boolean; + has_reverse_left_right: boolean; + has_reverse_up_down: boolean; +} diff --git a/packages/algorithm/src/v3/pkg/algorithm_v3_wasm.js b/packages/algorithm/src/v3/pkg/algorithm_v3_wasm.js new file mode 100644 index 0000000..31102ad --- /dev/null +++ b/packages/algorithm/src/v3/pkg/algorithm_v3_wasm.js @@ -0,0 +1,4 @@ +import * as wasm from "./algorithm_v3_wasm_bg.wasm"; +export * from "./algorithm_v3_wasm_bg.js"; +import { __wbg_set_wasm } from "./algorithm_v3_wasm_bg.js"; +__wbg_set_wasm(wasm); diff --git a/packages/algorithm/src/v3/pkg/algorithm_v3_wasm_bg.js b/packages/algorithm/src/v3/pkg/algorithm_v3_wasm_bg.js new file mode 100644 index 0000000..d9ea208 --- /dev/null +++ b/packages/algorithm/src/v3/pkg/algorithm_v3_wasm_bg.js @@ -0,0 +1,799 @@ +let wasm; +export function __wbg_set_wasm(val) { + wasm = val; +} + +const heap = new Array(128).fill(undefined); + +heap.push(undefined, null, true, false); + +function getObject(idx) { + return heap[idx]; +} + +let heap_next = heap.length; + +function dropObject(idx) { + if (idx < 132) return; + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} + +const lTextDecoder = typeof TextDecoder === "undefined" ? (0, module.require)("util").TextDecoder : TextDecoder; + +let cachedTextDecoder = new lTextDecoder("utf-8", { ignoreBOM: true, fatal: true }); + +cachedTextDecoder.decode(); + +let cachedUint8ArrayMemory0 = null; + +function getUint8ArrayMemory0() { + if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { + cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8ArrayMemory0; +} + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); +} + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + +let WASM_VECTOR_LEN = 0; + +const lTextEncoder = typeof TextEncoder === "undefined" ? (0, module.require)("util").TextEncoder : TextEncoder; + +let cachedTextEncoder = new lTextEncoder("utf-8"); + +const encodeString = + typeof cachedTextEncoder.encodeInto === "function" + ? function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); + } + : function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length, + }; + }; + +function passStringToWasm0(arg, malloc, realloc) { + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8ArrayMemory0() + .subarray(ptr, ptr + buf.length) + .set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8ArrayMemory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7f) break; + mem[ptr + offset] = code; + } + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, (len = offset + arg.length * 3), 1) >>> 0; + const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); + const ret = encodeString(arg, view); + + offset += ret.written; + ptr = realloc(ptr, len, offset, 1) >>> 0; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +function isLikeNone(x) { + return x === undefined || x === null; +} + +let cachedDataViewMemory0 = null; + +function getDataViewMemory0() { + if ( + cachedDataViewMemory0 === null || + cachedDataViewMemory0.buffer.detached === true || + (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer) + ) { + cachedDataViewMemory0 = new DataView(wasm.memory.buffer); + } + return cachedDataViewMemory0; +} + +function getArrayJsValueFromWasm0(ptr, len) { + ptr = ptr >>> 0; + const mem = getDataViewMemory0(); + const result = []; + for (let i = ptr; i < ptr + 4 * len; i += 4) { + result.push(takeObject(mem.getUint32(i, true))); + } + return result; +} + +function passArrayJsValueToWasm0(array, malloc) { + const ptr = malloc(array.length * 4, 4) >>> 0; + const mem = getDataViewMemory0(); + for (let i = 0; i < array.length; i++) { + mem.setUint32(ptr + 4 * i, addHeapObject(array[i]), true); + } + WASM_VECTOR_LEN = array.length; + return ptr; +} + +function _assertClass(instance, klass) { + if (!(instance instanceof klass)) { + throw new Error(`expected instance of ${klass.name}`); + } + return instance.ptr; +} +/** + * @param {Problem} problem + * @param {ReverseOperationPatterns} rv_op + * @returns {AnswerSet} + */ +export function solve(problem, rv_op) { + _assertClass(problem, Problem); + var ptr0 = problem.__destroy_into_raw(); + _assertClass(rv_op, ReverseOperationPatterns); + var ptr1 = rv_op.__destroy_into_raw(); + const ret = wasm.solve(ptr0, ptr1); + return AnswerSet.__wrap(ret); +} + +const AnswerFinalization = + typeof FinalizationRegistry === "undefined" + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry((ptr) => wasm.__wbg_answer_free(ptr >>> 0, 1)); + +export class Answer { + static __wrap(ptr) { + ptr = ptr >>> 0; + const obj = Object.create(Answer.prototype); + obj.__wbg_ptr = ptr; + AnswerFinalization.register(obj, obj.__wbg_ptr, obj); + return obj; + } + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + AnswerFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_answer_free(ptr, 0); + } + /** + * @returns {(Op)[]} + */ + get ops() { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.__wbg_get_answer_ops(retptr, this.__wbg_ptr); + var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); + var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); + var v1 = getArrayJsValueFromWasm0(r0, r1).slice(); + wasm.__wbindgen_free(r0, r1 * 4, 4); + return v1; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } + } + /** + * @param {(Op)[]} arg0 + */ + set ops(arg0) { + const ptr0 = passArrayJsValueToWasm0(arg0, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + wasm.__wbg_set_answer_ops(this.__wbg_ptr, ptr0, len0); + } + /** + * @returns {number} + */ + get n() { + const ret = wasm.__wbg_get_answer_n(this.__wbg_ptr); + return ret >>> 0; + } + /** + * @param {number} arg0 + */ + set n(arg0) { + wasm.__wbg_set_answer_n(this.__wbg_ptr, arg0); + } +} + +const AnswerSetFinalization = + typeof FinalizationRegistry === "undefined" + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry((ptr) => wasm.__wbg_answerset_free(ptr >>> 0, 1)); + +export class AnswerSet { + static __wrap(ptr) { + ptr = ptr >>> 0; + const obj = Object.create(AnswerSet.prototype); + obj.__wbg_ptr = ptr; + AnswerSetFinalization.register(obj, obj.__wbg_ptr, obj); + return obj; + } + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + AnswerSetFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_answerset_free(ptr, 0); + } + /** + * @returns {Answer} + */ + get answer() { + const ret = wasm.__wbg_get_answerset_answer(this.__wbg_ptr); + return Answer.__wrap(ret); + } + /** + * @param {Answer} arg0 + */ + set answer(arg0) { + _assertClass(arg0, Answer); + var ptr0 = arg0.__destroy_into_raw(); + wasm.__wbg_set_answerset_answer(this.__wbg_ptr, ptr0); + } + /** + * @returns {(string)[]} + */ + get board() { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.__wbg_get_answerset_board(retptr, this.__wbg_ptr); + var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); + var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); + var v1 = getArrayJsValueFromWasm0(r0, r1).slice(); + wasm.__wbindgen_free(r0, r1 * 4, 4); + return v1; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } + } + /** + * @param {(string)[]} arg0 + */ + set board(arg0) { + const ptr0 = passArrayJsValueToWasm0(arg0, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + wasm.__wbg_set_answerset_board(this.__wbg_ptr, ptr0, len0); + } +} + +const BoardFinalization = + typeof FinalizationRegistry === "undefined" + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry((ptr) => wasm.__wbg_board_free(ptr >>> 0, 1)); + +export class Board { + static __wrap(ptr) { + ptr = ptr >>> 0; + const obj = Object.create(Board.prototype); + obj.__wbg_ptr = ptr; + BoardFinalization.register(obj, obj.__wbg_ptr, obj); + return obj; + } + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + BoardFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_board_free(ptr, 0); + } + /** + * @returns {(string)[]} + */ + get goal() { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.__wbg_get_board_goal(retptr, this.__wbg_ptr); + var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); + var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); + var v1 = getArrayJsValueFromWasm0(r0, r1).slice(); + wasm.__wbindgen_free(r0, r1 * 4, 4); + return v1; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } + } + /** + * @param {(string)[]} arg0 + */ + set goal(arg0) { + const ptr0 = passArrayJsValueToWasm0(arg0, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + wasm.__wbg_set_board_goal(this.__wbg_ptr, ptr0, len0); + } + /** + * @returns {(string)[]} + */ + get start() { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.__wbg_get_board_start(retptr, this.__wbg_ptr); + var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); + var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); + var v1 = getArrayJsValueFromWasm0(r0, r1).slice(); + wasm.__wbindgen_free(r0, r1 * 4, 4); + return v1; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } + } + /** + * @param {(string)[]} arg0 + */ + set start(arg0) { + const ptr0 = passArrayJsValueToWasm0(arg0, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + wasm.__wbg_set_board_start(this.__wbg_ptr, ptr0, len0); + } + /** + * @returns {number} + */ + get width() { + const ret = wasm.__wbg_get_board_width(this.__wbg_ptr); + return ret >>> 0; + } + /** + * @param {number} arg0 + */ + set width(arg0) { + wasm.__wbg_set_board_width(this.__wbg_ptr, arg0); + } + /** + * @returns {number} + */ + get height() { + const ret = wasm.__wbg_get_board_height(this.__wbg_ptr); + return ret >>> 0; + } + /** + * @param {number} arg0 + */ + set height(arg0) { + wasm.__wbg_set_board_height(this.__wbg_ptr, arg0); + } +} + +const GeneralFinalization = + typeof FinalizationRegistry === "undefined" + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry((ptr) => wasm.__wbg_general_free(ptr >>> 0, 1)); + +export class General { + static __wrap(ptr) { + ptr = ptr >>> 0; + const obj = Object.create(General.prototype); + obj.__wbg_ptr = ptr; + GeneralFinalization.register(obj, obj.__wbg_ptr, obj); + return obj; + } + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + GeneralFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_general_free(ptr, 0); + } + /** + * @returns {number} + */ + get n() { + const ret = wasm.__wbg_get_answer_n(this.__wbg_ptr); + return ret >>> 0; + } + /** + * @param {number} arg0 + */ + set n(arg0) { + wasm.__wbg_set_answer_n(this.__wbg_ptr, arg0); + } + /** + * @returns {(Pattern)[]} + */ + get patterns() { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.__wbg_get_general_patterns(retptr, this.__wbg_ptr); + var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); + var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); + var v1 = getArrayJsValueFromWasm0(r0, r1).slice(); + wasm.__wbindgen_free(r0, r1 * 4, 4); + return v1; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } + } + /** + * @param {(Pattern)[]} arg0 + */ + set patterns(arg0) { + const ptr0 = passArrayJsValueToWasm0(arg0, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + wasm.__wbg_set_general_patterns(this.__wbg_ptr, ptr0, len0); + } +} + +const OpFinalization = + typeof FinalizationRegistry === "undefined" + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry((ptr) => wasm.__wbg_op_free(ptr >>> 0, 1)); + +export class Op { + static __wrap(ptr) { + ptr = ptr >>> 0; + const obj = Object.create(Op.prototype); + obj.__wbg_ptr = ptr; + OpFinalization.register(obj, obj.__wbg_ptr, obj); + return obj; + } + + static __unwrap(jsValue) { + if (!(jsValue instanceof Op)) { + return 0; + } + return jsValue.__destroy_into_raw(); + } + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + OpFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_op_free(ptr, 0); + } + /** + * @returns {number} + */ + get p() { + const ret = wasm.__wbg_get_op_p(this.__wbg_ptr); + return ret >>> 0; + } + /** + * @param {number} arg0 + */ + set p(arg0) { + wasm.__wbg_set_op_p(this.__wbg_ptr, arg0); + } + /** + * @returns {number} + */ + get y() { + const ret = wasm.__wbg_get_op_y(this.__wbg_ptr); + return ret; + } + /** + * @param {number} arg0 + */ + set y(arg0) { + wasm.__wbg_set_op_y(this.__wbg_ptr, arg0); + } + /** + * @returns {number} + */ + get x() { + const ret = wasm.__wbg_get_op_x(this.__wbg_ptr); + return ret; + } + /** + * @param {number} arg0 + */ + set x(arg0) { + wasm.__wbg_set_op_x(this.__wbg_ptr, arg0); + } + /** + * @returns {number} + */ + get s() { + const ret = wasm.__wbg_get_op_s(this.__wbg_ptr); + return ret; + } + /** + * @param {number} arg0 + */ + set s(arg0) { + wasm.__wbg_set_op_s(this.__wbg_ptr, arg0); + } +} + +const PatternFinalization = + typeof FinalizationRegistry === "undefined" + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry((ptr) => wasm.__wbg_pattern_free(ptr >>> 0, 1)); + +export class Pattern { + static __wrap(ptr) { + ptr = ptr >>> 0; + const obj = Object.create(Pattern.prototype); + obj.__wbg_ptr = ptr; + PatternFinalization.register(obj, obj.__wbg_ptr, obj); + return obj; + } + + static __unwrap(jsValue) { + if (!(jsValue instanceof Pattern)) { + return 0; + } + return jsValue.__destroy_into_raw(); + } + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + PatternFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_pattern_free(ptr, 0); + } + /** + * @returns {number} + */ + get width() { + const ret = wasm.__wbg_get_answer_n(this.__wbg_ptr); + return ret >>> 0; + } + /** + * @param {number} arg0 + */ + set width(arg0) { + wasm.__wbg_set_answer_n(this.__wbg_ptr, arg0); + } + /** + * @returns {(string)[]} + */ + get cells() { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.__wbg_get_board_goal(retptr, this.__wbg_ptr); + var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); + var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); + var v1 = getArrayJsValueFromWasm0(r0, r1).slice(); + wasm.__wbindgen_free(r0, r1 * 4, 4); + return v1; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } + } + /** + * @param {(string)[]} arg0 + */ + set cells(arg0) { + const ptr0 = passArrayJsValueToWasm0(arg0, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + wasm.__wbg_set_board_goal(this.__wbg_ptr, ptr0, len0); + } + /** + * @returns {number} + */ + get height() { + const ret = wasm.__wbg_get_pattern_height(this.__wbg_ptr); + return ret >>> 0; + } + /** + * @param {number} arg0 + */ + set height(arg0) { + wasm.__wbg_set_pattern_height(this.__wbg_ptr, arg0); + } + /** + * @returns {number} + */ + get p() { + const ret = wasm.__wbg_get_pattern_p(this.__wbg_ptr); + return ret >>> 0; + } + /** + * @param {number} arg0 + */ + set p(arg0) { + wasm.__wbg_set_pattern_p(this.__wbg_ptr, arg0); + } +} + +const ProblemFinalization = + typeof FinalizationRegistry === "undefined" + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry((ptr) => wasm.__wbg_problem_free(ptr >>> 0, 1)); + +export class Problem { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + ProblemFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_problem_free(ptr, 0); + } + /** + * @returns {General} + */ + get general() { + const ret = wasm.__wbg_get_problem_general(this.__wbg_ptr); + return General.__wrap(ret); + } + /** + * @param {General} arg0 + */ + set general(arg0) { + _assertClass(arg0, General); + var ptr0 = arg0.__destroy_into_raw(); + wasm.__wbg_set_problem_general(this.__wbg_ptr, ptr0); + } + /** + * @returns {Board} + */ + get board() { + const ret = wasm.__wbg_get_problem_board(this.__wbg_ptr); + return Board.__wrap(ret); + } + /** + * @param {Board} arg0 + */ + set board(arg0) { + _assertClass(arg0, Board); + var ptr0 = arg0.__destroy_into_raw(); + wasm.__wbg_set_problem_board(this.__wbg_ptr, ptr0); + } +} + +const ReverseOperationPatternsFinalization = + typeof FinalizationRegistry === "undefined" + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry((ptr) => wasm.__wbg_reverseoperationpatterns_free(ptr >>> 0, 1)); + +export class ReverseOperationPatterns { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + ReverseOperationPatternsFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_reverseoperationpatterns_free(ptr, 0); + } + /** + * @returns {boolean} + */ + get has_reverse90() { + const ret = wasm.__wbg_get_reverseoperationpatterns_has_reverse90(this.__wbg_ptr); + return ret !== 0; + } + /** + * @param {boolean} arg0 + */ + set has_reverse90(arg0) { + wasm.__wbg_set_reverseoperationpatterns_has_reverse90(this.__wbg_ptr, arg0); + } + /** + * @returns {boolean} + */ + get has_reverse_up_down() { + const ret = wasm.__wbg_get_reverseoperationpatterns_has_reverse_up_down(this.__wbg_ptr); + return ret !== 0; + } + /** + * @param {boolean} arg0 + */ + set has_reverse_up_down(arg0) { + wasm.__wbg_set_reverseoperationpatterns_has_reverse_up_down(this.__wbg_ptr, arg0); + } + /** + * @returns {boolean} + */ + get has_reverse_left_right() { + const ret = wasm.__wbg_get_reverseoperationpatterns_has_reverse_left_right(this.__wbg_ptr); + return ret !== 0; + } + /** + * @param {boolean} arg0 + */ + set has_reverse_left_right(arg0) { + wasm.__wbg_set_reverseoperationpatterns_has_reverse_left_right(this.__wbg_ptr, arg0); + } +} + +export function __wbg_op_new(arg0) { + const ret = Op.__wrap(arg0); + return addHeapObject(ret); +} + +export function __wbg_pattern_new(arg0) { + const ret = Pattern.__wrap(arg0); + return addHeapObject(ret); +} + +export function __wbindgen_object_drop_ref(arg0) { + takeObject(arg0); +} + +export function __wbg_pattern_unwrap(arg0) { + const ret = Pattern.__unwrap(takeObject(arg0)); + return ret; +} + +export function __wbg_op_unwrap(arg0) { + const ret = Op.__unwrap(takeObject(arg0)); + return ret; +} + +export function __wbindgen_string_new(arg0, arg1) { + const ret = getStringFromWasm0(arg0, arg1); + return addHeapObject(ret); +} + +export function __wbindgen_string_get(arg0, arg1) { + const obj = getObject(arg1); + const ret = typeof obj === "string" ? obj : undefined; + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); +} + +export function __wbindgen_throw(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); +} diff --git a/packages/algorithm/src/v3/pkg/algorithm_v3_wasm_bg.wasm b/packages/algorithm/src/v3/pkg/algorithm_v3_wasm_bg.wasm new file mode 100644 index 0000000000000000000000000000000000000000..ba225a2137f4a90fdcabe126c64b1b14147582ea GIT binary patch literal 64812 zcmeFae~?{gncsPS+|VYX&Bk*e?c^`7^A-sgROzt8)gXz}E|aTG=Ip~*Xw z)2Ac-#Ha6!^^=|s=aHR8cSg~j4en8@rGrkIcBHVMgaTZL5~}7yR!tP2PW~ckUw+lq zi>vpntsmVua__-YZ#{TF035vQo~xEmb`KuB|E_xut{p#kZ0Y_eIe)<^FL-=$V`FLk z*o8{(Q0b$`4&SqM?BLp6%S(qg4jx{ok@cm!qsI9gnI3NBzGL^VFCKq6s3t4$FI@JL z8cZiQ){h>$$9fT;ADk`TxMyi2iq8K=uhz!Kk@dCvqbU8|_;=!@-H6j}qp5%CXu{Z! zG5k?962MIUM3hEJ8aGGdW~X5< zg`YU7{fpB^BkDBcG^S$y;y9+Bq}9?Hmr=^UsL_m%$8UH;GmWFE)+kh1oH=tg>WqB0 zdwuKPrF+-bA55Zcj%dbnX?=0^;N9y>OVQOf%J&=uC;a(?Kp!Wgw}*!(w|clQRCj!R z?XK0Od%e2LE-%~II9NUi)kZgkXR2-Uvo~CJ?t6EwEv_GqE)7rWZ~2qC5`aL?)}LK7 zwTi-nBTGl`IkFL*43Dg);YUZepZnJFXd~RMzi>QSHu|{dpaJ2%eq*3{A<=K&v$nVz zy*b?0`?cE(Mv{(lzkPCJaeX6tOSnHM?)Qsx%HMzV@WzqozH`d^{gq1xo$>ku0#1bo zg9gHb)gTxakDa`KY2EvDeXUR7{@U@A(Q9jcs@;B9MOzyO!|h|y{o$tCt8U&q?$9Af zY`|`r7(mOtdENMTRica?yr&Apr-q`XxVF82*`Hi2_c5Ex7B!d z@W{`it7<%Z&co|!>^XR7L=wHV#-5v>-OxO_wt8wQdd-zSSnErtmex-$tsO^@Y#d!X zR!R(q@4(){ke;{Xk;Ri}607Fzd!rAASJcOgeC6(audJ1W_Z>fYce*MB^>z|4rjjz1@@5k@` zrM*wYXX4Yp9e+6fTf6`3`0vD@jDIcu<-dz6d?VCtj7Obr+$o+9y-bRp^}cu^73`e@vw5>fPvz06JTB4`4M7war%zhfmUV9fME&q9 z^eS)g!t**)|G-&0Pj3dRY1Y`6bhAd5?n~NbTUk) z5-Q7L1|g#&VAY-ONCz~%;E>)no@xxUSa>YQD7tZ+o*x-PbnN)Efdfa0A(flGJOYKB zOxp=)o9T~Om5FfJ&7+$DJ}IWbh~YGvwT{K(aTqteZ!qD-ILsz}`gUGd{O=}@*l|UFcT|Lc4 z_9ZjIY%h<2%BX-5)sV~tUW{*q&g{W-`M}65EqW3eN8bX+n*G>f3H9vjeIK%|@EQy>Ffl7r;{C7bvW zxgpFC2FJkVMdRmgOZbV`UC_l4(THRPxPXX8Pvj6R)HcMdeF+0P*@0_#wIouI;zI90 zJ~G=M)$N`MP@PTp6A}F=0;YK&hvh`h2*_*%Sto){#NQptXdEi}kw>>tM)Lgj6D|iz z_K!svf&F7=P!kf)rOAN(ML#*L;p{IOGO7}v$D7z67>3zD8L)p+Ry@r9(4hdSu|HI{ znf>EB`={s<2F;}bU`i_1*k6@7`zP>p`M-vd_}!3Wvv@J6f=JZaEWUcCv5e35!4o+$ z?bJflkFs{rkm#qOHa$gP*mI=W>6`LoRHUAuyI=lZsXU2tU$VwJJ=FDV|ioU1| zsXbljSUq|8_qli>JkhZ#_Nbnm;YaW2SQYz>E*=R_bgYU!p^L}D6CJB!PwC?E@I=R| z*q3$jWO$-uRqX4!_(FK1V^!?w`jccPbe*NM(UFeRUJX^o`kuG#cPkvax_^~c@M^9eV#q;4p$Lh($;*S@? z6CJB!kLt-8epG>uRk6?L;*sz~$Ew&9x_B%+(XlG_lrA0*Pjsw`eOVVzh9^2!#lEhK zFN7yLR>i)li>JdA9jju`=;9mUiH=pVZ|mZ0c%oxf?0dTSR(PUgRqO}4_-=TjV^!=q zU3@=0(XlG_BV9Zjp6FN=6UQPICYNQ(V(+cw$V-Nz{(=M#+HW4!FJ@j@Mva&>kWdRs zF-9pC^N5(@F>&jeGiSuRQpFg%2Y4;HZC&bC5*!0UqMgI3taa0P>Jnfe2GmFvn^fU6 zUG6u6Fp-QwpqL~vi5tX7j1)2L*~OT*x@EDeZZu&jhZ3oDUO*DLdUt`eoR1u!m`NlI z^|;e9DK?A~vvLM#7!`yyG{|5yOPKqkTcCl4M~ZAk1_B5+nTa04gl`m*$_C=DBOxCu9^ku_v| ze+VmI#w>q86?#Bl{IxTQqbcg`F^g(o-qej629YLY%|!#F1}HXLa}20kbLfj9YmV01 zav)rUb6|+DwdjkqmC>b9B^uAooyOoMHiQ{MEBz+)8I%tM#YM0@Z^BwnP08UT6|p=A z>`Kvk+?xla<}$B}#&VVFLkD{7h>=m^qsrNG))ZRgTna6mfffv@=K0Yg|7??ydk$KR zp`@-2?ZI2o0g7vPn;ld7Y7K9I_N|X)M;TSDK(Q-mhzFpSW}zE5jlqw>M8vQ*x&yO#mts=<817GFa7(ZX z<RUK&PSc4wCg#6D3IeZ2 z(|}gdG%Q9n21`k(o8t+TFmk~hMI7z~($3d9@wU^1iO$3XS-8`pfGA)-xmG{d_jXD* z(CyZIlF_pwy49VBTk~yMs&gLfl#VJN+=2Dl&;x=*@d@-s;{YrX_eZ@VeorH`WB5c3 zH2Rz)pE+&kZSJ*;=$7$T@$bHLHrf*rY>NBi+>3jkh!v1RFpOp+S-bapaeqX9+^(d1 zL-vXN$p#ngeaUeSNat0*Izo5mm-+c2a|JVkwA;tqlMB_;VWv;~K@>0ogi)WG)p)bs zxb9iwV?-M|8GvRZYMg4a-oE4k4xN40I?bwe=vS?CrPi+>YW>#!cC~j4UE=W0epiQu z{zR7a8B}!$jGIekh;gKJKniSd3*n6#Qrg@rk&-%6Iwev7Xs?5?+rdL1(@Wam(Dh~l zB4!+->HfG6aWC)6M~M3q0;8*DlX|mb+_PpUZXAc8CqPG+W_QpI*uU0Wy(Zg6TS>WK*{}X&%pQzzODLQ>*!y?qX48V+bDw z7vpS9S_izZGNcXf`()STj)IBNd%#8w?Yd|EpF|lp`jgW9lf93`h%88>c=Y3EquzV) ztiJvUPCldClWJo*YiMxB0J@|8bZBfe+YV!J7+>vQX8l2CDmFEO?y-icyZs*YY@ChX zK0cW#QgIp2#5oD$@Z!g4<2ExsCYIq98C%5a0KQXvuWaAk#7l;2OuFNe1ZvFU1jZlP z6nuFp2%uX@HpcLSXK*L&1vTA(;d!RFKbg-O6cJk$=%(J!3w3*?-Z}1;{KnJ@1R={+ z@9G5^U~%s^yxd+Ip!huTK)uzW&xl^I?}HNx5({6>N3-6|2BQg+B8b!0uJnkEI14Ts zIR#3RHgs~@mCDSM{nIM;hI=KsL0!C`&Wq0GlMSa^&K}s8T$70$=aCk&L7;XYTe!_n z5C?vOP_mQaM~|Y@=8Gpk#bDgYU>L$+gaSbT`xP%T68k_~>xRaDAySxo`0r5o7b!dq zY9_Mj0c!BwgySz z?xEDMJ#Ki&hjWJ0ct6;$?jR(j@t`{O0PR=A*F|UwT}{h~lbW=vZjtm6Lj6f2-cHt8 z&0Eeu3N}JLjIx-Mk=JSOZxRq0ClH%TVm>Tg#qo@e#NR&XRX^ZY8@wH-1I{aj9l*;V z{W0PKD2te3`G<6gLk3GoBK`*V1UmdJ!lCf|a&AU!T#B2t$UN%!VpIwo4BK7F#{P6h zHZ6)@hpySQMR&RPb5w);q`~F}^-?yS5&6;7zGTi1ATpa2hNNM$hLax`M8BJX?l7u> z%dtq0pP(W!*W=+RjOcp95p$eFD#5rf`HY_IUG9$xE9mNHBOFF_b)%TU(hxTCX737Q zOJh$oBMzpZ%!JXBX(OHq3Jm~yS*q4-8^fV-CHIKuiZnQz2}B}zd8)xYt@~a5G*)1I zj?rvBqT`%?ro+wNERu$i7&$P@)PlW~L4yP;VR!xPeD`>~1EEO>n}96RbWe1-bf5Gk zqD6x>mL-fXq(^UUdZOStuiKacNIc6B@cL~WP4-vB51ozhq$G1_H*1S_A;5jIQAF2> z8C5>V&F?}*OF?hHfI0fJ{E zvsbf8W1)A7Ip!0T`@ZB>&N42MM}=qoHnAVT!iRfQu8u!mQm`jt>G z@N_NOj#e+Gisz-={~9myLTM`?7|{L^c!JQH=oFVZigK7A)pA{|UdbPagXA(EN{LoUG(i6UrLqt&Y@f@jQo1Qld6 zAc-Vc9F&aBkOURP(=AmH_q)(22aqM%$XS%PW>FcO&E-=%Oy{HFQ7<1yaE>6L#!yFN zuoRE8e48mGPUm$U6w*jPk=R7`?ul@I=;4?YbwhJlBQ8AYB=);2BP_O3dP?7!GD^-- zMrbluMqTx^n@v>8s7C``VcT?4TY${t83`U5=ODj~LtE^fwQlrD{J`Z(qyTSCak&^A zXEO|V({xM%cW2vflwoBrndP4@E_=pmx`pn{LIuf(sCLeK?9U(TV;>ITy zEbOm9w^ zK`t1tK)N6`8 zBKv-&sDdU#dk(8I0wU+AvdMxOfk~|BhvEaWr8oK=$v=Q4$fDZKyGmgRmtI*~JQ+L_ zde!?~yxl^gM<*bmrm@hzdph zn8FxwH|3+_k)J9@;)6`u@=$Rx9pDct#AeLX-O%R0IQ=fE@_4y+k<>5J>H>L&!2};0 zb<&1a(@(9&sxnHcI)WFNk@yW(7|{b^0TWp(GBaEBh8SRI6D1ATh}N4K_hJLeZYI^u zB;1t4n7|@*f{=NGwv#?6K<*c35&1kl8{cjzN#F#oOE&sM$bbf9dAz(`JU?;*=OByZ zGMS>5VRy0^IYs|kr`C&yKKyX|1hG3kYi#w*p5POl`<6ZFZutZlL|rH*><#g^616PH zLbw5G?Nh}|54|`-%YIxg9y-%lWlEj60It7K*Fh+QrQ*f-1U&C$K{PecBx~{w4kc8(L=_Tbx_jem#7pk->J0G z`Tb@*B3sW;#h|OB>AD$Lsfdx{%O>b~(lv)10V=mZ2f>{)O@JX|7AEl;5jp}cdXLbS zB9w@>-U%D(0+MML$HPSf%rWPKOC)KHV25FC+!;!db%Tm_7=vTAh$))RsE`S-gkBMz zGONxem}Kby6(SQ6oTW;^QejTGPq5dA+B!ofhNzv+^+;e+Z$dhupKF|2AO(I_uo?c~ zuW7}K?g{4@-J$;uPA+I&Sct7_7qxL~)W0fS7t0-;c)aoiJS;Bq2~V$3vxq&&0X0zR@dgyDSk(&oLT%2>8pqWPpk{Vf8)}*%Qc+2?J8`#;Cfv zOu`oUA@#=QKV9N;BQ3MYQbZZ^gkj_%nLUvHg^IR|Ho2-Q-|I6x#aJ6^(kFET}7j+M+@2VsAD&OiEUr;tM!rKJsGJtZUUPA|}_?kgiHKh83O5 zh+CORqd1aBg0YHurvWr85&Qsw*^3gh0>-=J;DStHsvm8F5LIl6p*3Rn4$JNj$c%tb z@RC3(O0`%?Y(v&k7)GR9HaJ|HVIpfCPyzm0RtsRLmd;-^<0k4$^Qu5vG*+a%G{u+W z-=j4W<4GlCAXZOU*wsp8JdQHt9Pd@r>5LuOZb+OH1&Sf|`Cz&0bip3@)VUh+sdJkO zG{~Wsc_XWUX%zDZJX3)O%iDqlNf^P^UFrk+>LqwmJ zEyqFhcZO7PIm;wDh#WlCO!;P@K-6W`N5qDeVW+A5wkm^TcVP zew~vNqU;UE&$JyaclunYN%+P>7huEYiFuiuAg@GuY$yj)MvpQiY(A($Ic!s+T)Kg{ zkmn$s7T!8PYG8=eB0)y*G=(jIZ6PxzPkkB-`;U1$6fZ3aN?ftHc*hAeWLjK-;^uHr zlUl8!wLyZ5l}yT#2_-STTN~?E{gY*k} zjk1_92o+-K@*~D%DO9d5^OZz&sErpA)-lM5$Vemt=bAOaAXkoULmoVuORhUMkjJCQ zm2AN4-gS5kQx35&Kcd7#!|~8CJh)j`#{*FZ;Q^WIcxVd`BkHe0R-vTfVZ`vD@`eW{ z!VM29=y+(KiwApWoen!5=%(Yr#;Y;F1w)L{APYby+G8Ifie(d+GJ1H12fgB$DPwI+ zkbx5>I}9n{3-<_IbcG35kl-o=zRYzT1ipq_h{TCdYZWUh<6_V%!)5N{f1$SjKU3S8 zzjSIFwD4c3?LT+f_S~obvSr(#g^QuKf5U2aX;Rb@bNRB;&NR@D643siTd`Sl4TLiW zh7MTJDN}Mtb7C+=HO-(xXXy@E#6A^?>6wutMN$nj+a|}E&g@-|0wQ=&uztwFLg&Q;j8Tr%c^+yg8jj&6H-8v<-Z8*fHu>z8vgKH zSpz$x&i~YiTLbzki3*b?31sCI=d>kk;f@*+Rb+L3d|@&$V#D|nK~3W&2dI2p@^%2K zqOd^d|8LT=L#x2X%hA=0yNX7MjqDt_1L4Y{d$l8d`NfR%Uul~9w~csgKvN(hy)N3# zAyaMzc7I?gBMtc-ncpOXNYA>OuGW8FNN97&FlSdw9WC{?-(dT?;@bujq6H98s2J@CM0hYX|+gjitgB};YiMMGO zS2#v-GpcVg1yZ&DjzRNnZ1r=Xz?-T;0c{MRAi{}pZAphEvw|d`m{Z=KA&!d~XJe8z z0aks)ZLpl5GK3okU->7__( z(mOlt{=$kVy;(dHGKun5@Aaa}J1pcO$QJVWb^;@uT?+wnxSpuDvXE{tHP_T3(=4UD zXUABf^kTVSLT)4AjD|qLt~7%ofM{1rx-6h%BjI&LfKvKO-Dp32+6~npL|^DlElu3= z&Gv=f?4Iwb`l&ix1ZmzqWkewK2dz(tyV(inx z{@yhUM#QMmyI2$syXa4+El&C4)S`CoLTaRo@5$4+O<^A@eeT14@$n$cNNQ;)sm1?i zgrrQQ;vYxvUPsdKe1;T_ zD$dYg)C0w}J;KBgg;~s0jWlF>ArYZd^U zSWCbJB1n#^U}EA*BQQD)6Vr*}0#s>Q%xt>M#*HAA`xcTfsCYeY!PFV2$=7 zU=-p?Jm{X%fH~|YI=I_gN9+QfcyFEqytk7BA%%8^vy$w*IqLJRV|N)utI?=0b{z znuyt2Z*JB+i?TKXI_*l#$jv#QmYIRdxtiA0EY!@b28XsZ3S8KiOa|a6S>Ocn0!@UF z^9oyKgj95TePD_)3)po?Bbr&oNVA9?G%B&(kJ1HI9OVz?wSjnWJF~Vf7Rhi4vOU#8- zjYwlB7gY9e30)z5IgY4OJ0h3HW8z#@;IhWPKgI)&3RVPzn5lCXn%JoiP zf+YD^HLfimg4PDg^EJ-P=sJpWlGPjMngmRL0Ed-by69TT;|wnk)e1K9xU?R8{TB2D z{sW2PH5#K8&z?CO74PUhE`IaHMx8%PlW6h-V4>>2^z<$@HXnucc5 z+PaTyA|j7sV7&$G`+KpHvGiZ{Xr5cg9>Ov?U^!1jU{Cd@7-3FUR{I?gz!H^J7A=he z&*_ush}D~wWW^w1&9zvh(k0EYV@B5mr6;m&VHQWNkN~tD%W|B2AneeIBvro>_&k^x zBxVXU^d=cyTBT+)+BdN|ic%Y-Q6NSVbckCg5rg<47$I4bqt) z{2qi4+%Ru9<75TlL5@-xT*V@j^fLo5lAMGQl(!}s%9WwmnS7EFauOQB9MEC{*a+z} z!@+{tBgHo#IcxHBMC%lK+;1-z&v4JyDFAr!Jzd8NHOTM${IUbgUa;329+xfDk)@S zg)xkxAZ~J%hCxlU?KY@kG&Ri)nIr--sSWCg59&zPS!Pg2v&nK$Swx^gRZ=Mjtybj_ z2GtgXf?6NciE2<=<)EVKHKh)GNsJ8f|#|;Mc$K{{`c=527l$3+|nQ#GNHf&Ho z*=B|lj+M#nivzJu7+g_0MMg_pO8T@td$>zKbjcKr?qQtvK5G&JF4Zk9=P?1Si)oAt zQ-@?Ey0pO{41@|@S91qMxRh~HMMk92xh}&n$%3pz>TsK1si?Bfs0zEEj9__>@9<=l zHsP~@5i(jCJ*biv`QA!|5#!kUz3Vw(S!hukhg&JYvs<|0@J=tH^*mg$&PS#Qi+s%T zw*^G6=K{-{B+gJH-MLnAIFK`6c6YbI7&LE$7odKI)g@Y4V0BbAFwt@iEnyzft~gxn zGSc%9csdZQYF;v?>*jS^4uxxQwIzMOWWfR?#OpJ>9xGjy z8i#J-w_77ts%iu=k%UZ{suh`x_O`*}73ys@iM_D-QQ9f{XAt4;7UK32gh3@mN;l|I z14*xQdrj8JMB0oa^Rn53R4IrlhKN-n%!Hn5v7>tH$f|eE>k2&qVg4#?z$?5jfVDPb zU-EvP-jZAI7L>5sgg|@mFvPGt#5_Ux@VclAE};U%mMByD2U?VNktPwvDiD{;*5d@- zFr*3OAH#_a4|!m|hNms}v``eRBQjy|Ldy08;{GzZL0q0=n$$BKxQFAaGBHKTaEQto zc=dpo$)*ek;En*;9+aB{U`H~et$ygKP)JQxii4G<*;kO>U=jKa;%zeQbDcRT+qO&T71LAxhA zqXfBtGQQCaErL=^TQKb8f{Wt`V*$^cg`iV^^xzt)CL>zWV4f4o_8^R40uL>2 zQh(|$5eBf5YozjI4q11lO;Yn16(3^m7vQxvm-f~8JNBDH%en#riR`OmOKOu z{McF41(-IKQrf;O>KmBz)QD$IMV#y$RNbsqUFxnas>VDoYu#C|6*3QN1#LB-&{ti9 zIiat*26G8YAzVkUs*<4l&9yzp_Wt8vO5Suj(LRvU;g@La{J(on=Fr0aVel?NbAW~2 z<=|W*4HgwU2iFX4G#K1VG`PZA7|ctgvgH1SYc^YH^!K=qjIv9zJwCXtm-lyBH6Jvh zEmt%G1Lp>#Vni(kO%*e`I+ipu!fCPf7Wp8e%$KJerc6TRmlW45_je+{W3clyL;z)H zm&m1`*I{>m7x&L_e}en0o={xS4twzpD3Ils?L!StWS8k`XP!YSCblQQLjO`;hFfrS zI0=iTPV@d=C@fcR@|KfQqaTZ+>Pxz8K`q0iG~Y=PckD~LDCT=4U~QCEl5;qVaLXlK z6oS$ekg|GrT4ce9-9wX#E=h2cTCS7!a<3;vvw8Uu)%2A0!tx_dwfu;KEkB}S(<|hE zY8)6AbHtTVD3_61>#&98N4(^gEKRYb&3zOOa4c_dvs`}Ea-~@|f=IXQGX@P-mLrH?Z&gOEKqX=7*;gK&v!V=BezLKP=^(HEfb;kl$ z%uQ`x0MsmqtL)MO=hj9TkH|xd*{C{QoOM{c#=EwHXHrY|S(GhO#IX*oT$+u9^*WbISd1`+U=TvkR!Fi4bNVT} zlHqP)z$!x&3t&=IFbcD_GC~F{Q!%r^Zo={0h@1|zs?h3TO_eMTxv`*O(_v6U}$!&NFB%4m8ATG;sL9zGd}+8DITR{kELPn1pGO6_Z=Z3ftd> z{WT;jriAcbHWg&WcqJ=r-R#B63X99YA;=1`ZNFW~im@SCF*cADqm`@}-6SjUe>6TC z8l?ry)Q4tBR$yF$- zgBT2&D4VpHyK&pK1UnJ>^C*TWOz{vm?6qDNx@WpXhpyfVf^@1TNT*y$qQ6Yl zxAQ5fl8P=HrO`E23Q}>DiwmpQj4s)xnkUY(ZJN?U*0Pd=S|pj5d+b-F8(5g7-h|4i zhP|lF=;D+#1l=*COO?r)XVObb51lQei>K&IgN?^-#t61xbn%i|2x^K@oQy8pXoG5j zgPRy#>|uo|f%-(rmW^->jdZ{hMum(nct>(&Tg~X23W9Wu(!v-+q4svvjV?^PAV|&f zvps%jKS0a9V03XdLzEbVmrtoYaifc8U2wsnkyqP~urV^$7Wd}z*XuByPl-`E>*ZIP zX781ny$4VCRGKBi7+qjTf)uFZ{+iilvKlsQB6mJl=Z~w@W_C8l7;L_0X>zyUV`)uI=?b|LqPM6Y%Iu&9g-wtLq-=> zfqB)a{VkydOj(xmLl0A;0oXf44b5(FyW0in<&?TyE30|C+rON3>)GYYS-|oh6ldksGBTnPuI+3*AgfIS z$gNRcMTAwW`LfB5Xy7$+X&M}e*wd)T%fJKyGoMu^nt}Abg9B2X5TLkYZC3ND);sr{)r7mOwaG!F{o zcw*&zFE2-B0AgsC@(LmOifqp3A5ajmD`S`=aH=WFJ~u8Mw5aE0i>2HNE%w!7-=Zm0 z1#jz33Bq#zIOE1;7gbfJs}ksj#l4Sm@*N{a{`VQyilBV31$q}l#t zQ%91k!M6Mx`#2*utomq^_PnbdLCgTrI$!J% zY!yxFD9A9F)QQ+&y^`al^+65{*=vcmeniVMA{rq2aoES@PCq=NG2!9EHcYl1nUzxS zb>D`Oqk2CWJCeE*+r&^@JNKu@)!+HDA>h@gnX16HzkfD!RmIl zI1GGN=Drbg8OokBVj}|3A)uNoGzmr+p2y?-s{JYW=~%pyn_SWYHHW0Sb86;^Ww^bP zxRY#uYl?l;Dos@>0ZC^2`*1-g@KpoALGZ(RU4vT$XE2wwO<|rYlBkCnT;1&RxCh9gy?c1rDG%3})MV0#2 zQWH=0wbb{9S&dSt1?WI#fNX-gntgWSv1|xU#iwLLVDpV{kpGagiH`=j&B{_}H`A!3 zMFj0I&>>}(v%Pssl10|dlOOeT1r<=*=&%q-7R021{R2dz1xtMeRm1H6N*F9K{^mLn z|F4?yx2Wq?GyWEJIpc4|^7tCvOU!T?>>vwjis!|Ew`m~Q2^F^Fa2&#PmpU>#i`^(~ zs*iB*X~hL12J0i5AGf_O$WmrJ`62ICo^H{=*|HsLx*9xoo6rzyzg0`*1a}zbMFT#? zo|ch-Ms$}ZIY6TPMZ0W1ueeq)hv?!ZIXz9;EYKA5Y5h9V)67xpf zy91bDpH$4vWiNk?&{CIpgtbJa6DIbaE*o>PR|&uI)oh{?e!WWg!EP@v{KS(qduzUn zU=V3;xfp(eh0N31Iu-PjuYoXX8hjL%*R)GJ{*l~9D_{p44}`vnePkKSs+rc81Vw;n zuep7^%K;cI9k8dw{y2NBK{hK1ifJJUszRI~L1W|tx+9wvqD)S+-;9#0TKcdWR?qlY zwqu;N)4U@qFCSNO1(o&&12D@VJzEY^5(?s~Bl_@0>6c^((AQ-M-L>Qn6Ecc=gC6_R zmO=nSDBs-wbU`wF(C<-zrQhn%`TOm8Vy~#*BlMf3h^pUBCCm)<8`ux9@p*CPV`Et_)Kx^E{6vQ2vVIqw zP{!RV!tdht3B6jxd?C%ZHvwsgct(MTyoAYhKnzM*AfZ(?C@C*rR@ZJt6e+O+ zfgvVB!oV}=U5WpQ?xSp?;>plwE~am{dz>6l($q8?pmvnS6V)k3V(o08m@T?aQX7yY zue|FDZK%JzKjH8H<@B0p5xsuCr1|fbvO;Lt!Ui5FKe3znm?i;Ju(HYaeHF^@oH~C3 z<+nv$uUh$SQP=sEpYmR{floG1ERJwiM_ZxG(j^7cK0M~DPqt%SLciPD5FM#$1PO^F zPqQ&Tl))3GmYMqG-U<@6q|=n0vLJCSe|$P9(=I+J5d)7~U7Gcav*7^)Bx_k!mj<&n zqoo^?&Hg`2oe@YTBjPAA&2bL!IbXvt*;Dl6}DOm;t zL~ACS>_th3eq?WI7Q%}8re``eb!nRqks1d;$ULUbSKJv9T%)V*v$78z5haw^o`|Vf z*L5~$bUUNY{5t!=;Kod1sAhNxG3S?QHd$5ihOdz@38r}_MjcTotwrg9)}n1X*(si^ zbvrH|(-qR2;3BnQKp5pnAkz5|21@OokRp9-J=r?CQ(ImXGqPzF-9eLFWLQTVJU%5( z8pNm04eM+Z3^-oZPz69=RlG`dohMrLQ|Qk{g{iDRKb0`mEw<5~=E=%16?0(v+H7km zOogRMuix2k!-0dZd7y=Tw|?#2X753}{Rl$0o%$d<(J-E$zAcC3AXtIL|y#~k`ajzq#6#Re#4s1-t$>+d2l(TEA?Z9JgwVeWm<@z#;X>%OI zYa~I!m5Z_zq~yHlP6G!SsbEcM{9J{_%UbYCx`&o4lM{%&E~8XtyTD*$S_f;pZVAC; zewlR?OOOn{ALFZ>i_{sCDfB{$n80*CS&SlFGs=SB(Q%IW9%5Um8B+&F%^=$W3M9?85G3$A`a$T+2Tbmn2xli z1fdpxSw0cN(ACsIS41QG4G(=K-)@3vr>JR4I6^H@2o*`CWqvy(5oS$NbHNxehO=wJ zf~bS=JDp8y)(TuV{Jf`(ng|?_+R#@P40&=S+f~Ys;tc|gHt4pY95>oI$wd-}W7+tz z92uN3_p*BPGRs2-<;XTb^N2Vmt7VS^euFmTv6VarQ&Mk4XThw1NtNe^YUnwsURFY+ zCiRCiwZlG>gQ;m98*Fk7Wda%~L@zUqU38Cq<%Z}(`5xc@Jlpem!kDl=Eilz7pR~Jz zkhl~ovz6W1rOWxHe5F-GVA8+%-G|Ob#oLM}K8(8fYY~;3zSU=1paYAE6HHRJ+oo9x z-Ln`!_5?atKJR4$cA9KOD1sFx-XbqwmXWBW0kJsgCFYatFW3mdnCAyd`X?pYk3kDh;xYZPFmE#tO0F?dq)7 zR&DbdE*Fz+!?v*1I(nN8@gYDlJ&-wj^s>vsp2W86GcYLr+5u*5c%h=!+j$PS|8pF^ z0R|OixJ)x^88>Fw9bQ<~XILytYyv=NTjV{W{0dvn%3{N?jiaUjBc`~;>LR-xEBTo5 z1zSdox~E*GPWg18k~IJXmi{hL)!vdcedfUun*nYc5h!iiIE46v+GY@Z2FNJ@2A9oa ztw5sAOfGJqUga>uVqU>uj%B(+!L$Z^9$qyROc&q8{u2tO9R<@u!86=56qqjPW>+xq z8S{;UX^XN2X^9FxF1FB{+?_A2gWV#iYVLz|CYCx)iEA%3`Db&ksd`=+K-ZMDUQ4ab zTCEG^gLe)!M>?;tKN38`*1&cZJu70xP=-m`>O?W1+Ujr@#FawHE)h2O7V|-;Xl+SU zenp3Y%P!faZ%Jr$nAo~x@GS|ZQFUE?OCmx%ay|H##3ep+%!}1B<>Fb0Caj(;e*CGy zd83&3b;@DEx@Q;%sUYi7neus9t{!8~nZ!rn5hT_#!tIUITq=bJ`$Rj=4W`jr@}HFw z+#h`MtJI;05t2vr4qxf8JRW^!f(9AUHl7f(I^Dh`1E0bp=C2Sm_It51dy+Ud;Jev+fjk%0+!L7;@s@ zmWhv#fuQrb72p=u$<#18q9q4?JTm|Z>#3QOm-++h5*H?}AiX&o*knN_1?yRTchKY% z&XmHK7MJlo&slNmBO!UK{YxDu$OFke!w>$`!?s=)VGYLBB4(ap)s%C6HGF}A%T(2lsMQT*uR68@y8>vLax{fJxb<*(SR zU+-aU9ilt)WSFtt60QcK#66j_EDmDTk47ljVw{tbX&^J_5upzV2Hhz6IL zF5$t_1S`J%@t}agw8G>#E1L3yC-o@}eS*qXYty(FB>=MtB=I&vMfC!pKzia!n?V`? zVDJn2LYrf(qzJyePNf61mVhn5#VuKTZz=j^O;pNP9#T+T~FS~t|zgE6^Sum zPg@S@(v)3K;1psmI2ldQn{nDvS+$=Jq$ zP)Z*!Hi%Z&oIqC2c(W;WkIHzJTyS3QgK66mpC@WskwG3$_%0`ifJs<{yOmi;{&Fx2 z!vZf&JA}h28vOr9!rJU)q4>^AzglR)ICclqZP7{=E8@l#aiq>N3lnwJ z7qOS=NH2W&4HX5kfk;F$c2?$#DdWD$qy)oU>w^_$<8^?S9}unBH?|Q@TtJ^IYESb7 zRG?WF1|rqW0F-LnjIdrW5?jd0Ep3E>wF|=R4$4;PlC&NQQH^$>9xBA0-mD2p6B4S~ z5u&2t#CVlDS=gD{zI*ohX(`9vvf%(Zf)uWSVY5{fP_nas^O~+`ctkh>G$e9w7J6645Fb*gX<68-(L@*^F`2_ikPo9nD zBTMpA6UC3Y-W5grBQRY2XmEi|AsIoHMk~6)t`dju=J2E67Kbz6D4}x=r5Ljybe@e6 z=b7Cq1^n~6rOgFJoytLkJxNtiW>$jC@3E=6Fks6ZEYeJn$#j)1?AjAS7ZgOssOca6 zX4y1~#G9T8O`nNr2Po-DvVs47<{zF7=}mKX^Qq64*A9%ntG*R5(wo9$&JSqe3~~4* zP>2`?4`Mq<(Xpo+k{j40EHf!g7MvQBd^&|mitm5vtZFI}^jPn&cZ%oy8e8UkPa4$_ zGX--yR1H;VA!>TNOhK??AA$rhLM-5MPWH9p?Pd*3_uC0?2%%xy3;Pm9Lo#Y$Sr_cm z2C0^bcyw7IXY;9j=F_71vJbu|?SC5`q`dVCXwu@DaJP8Ydvjr+hCwQ{j?SC2poM`q z*q~_>cu1jI@c|@JxkE)TGA*oRjH)?)R3wh62hyU9rM^U&yPYp4&QQ;@=)iWBkosae zQKU}pthjud121dGiYNb(=uLZCa6|L%C~C_#N8ap9ewq6}uo55O3#;k=2E_qF~+NSR91mva#6YfS7q7{ zid`u@-%crVZ5Jmdf(UD5_)@`S8XZi02vVM8p0I#-ZOMD`hM99EQ_o~Q8bG3)&X3m{ zn2{VAL5?Ut$TO1#he44~R4Hujd{U4w|BYuF0+JJ!U71bl0DDOr6GqV-F-wdzJvZjD z&&1*+EZ&#j|AR-J#foq1M3YlLOp=kx_#qB0MBg~H<{gO%d`jt8^bYo`mOa~=c6pm1 z2wuoW0hssAEoTI!USlDd*Ev&}SjLO_n_I7G$01~(G{hF7Oc)pWIMv=&O`gIRgz!bZ4`0tO&K*)CfzQU7-d?xrg3hL2`cyDLbdrjo=N*pZ`4K9Fo3^Sfr% zH8wNDo5C-4je!P}*uZa-NHOzNDMY-dWaC-?{t^BKzOinLHKh1h-OMvCReM}kjKIa& zm7Rfei`b9Uu+0&F|I6qx!-pKBlojR76QGB(=qC0a1OULrZ#DNXTmt5gzj`*ZAK)%N zWR=~2g0dCtyrud$co%3}yFWay_w?iMX+sHn&$p>C2`iseqiEfx3#wtpR3%Zm8%0}^ zt#VH6LFZ2sHk=k>6R1UwJB4Dfw0US+(coYX)i8jQ?ogmE+QAZ5MQgT$xV^H#yGWA5@R$QP}i)CxlwY7a8 zYOSenhCjVb`HBmmdnI^s+gR1uIf4|@(7O;vsfX)2U*7Vw7{f>w+evg7+?xBMg=xoi z4~v2;Wwr^L67!1Gsd+HpczMjTm0`fVL`K?=kO=C4?UGA}Od^!Hkk8(i4+Z91hIuU+ z*34mr1EZ@!Ew%whs3i>;4Ygo1G9p&XdAI6APeEm(Z*8I~umJ`gbYIs0EQQ^ES6VD)&s(B@7p*P-b|P zMDJzNK~Qk#ri|K{Bmpsfyn*GyLAhq+Z@zo5cLZtFCyOiibfj>sfQ-`$7{gdKhYPWW z)E5qkB*@o8JF6BcKhkRD@_`NxC`?$zeG6Tp3`5uf>XFZ#V0_1-6)KIpf8>W3J_ifBq|DQ0$2xB9{5!yShZv{$#qzan0fudjXhjA4qX z^Gss)0%oiMOJVJro7^5aTw1e&Z}Z4?JtBG9Z&u(O4XqV>GP+)Y2B$2b`>b1)~hjXD_6S3?d)U6pn9L&c= z@mU;JSNKapB7{Ad;XzdmZ5&+9(ul{5TmJ8X{0RZlgq{s6dpGohvhTV=XTt@7r_Z_D zY(S*fUS6-Wr&iGGXhG1}kO!&QqLBj#6`AxSo(Lai|CidnwExf5)4YQQ4ljd&O6y}0 zSYoh~9J#v~V~jvp5n7pJa?xC!hcIbw)MD1sW_8|lDl2#1b?*f}Axgp%G0Z?EOD>gm zTM4}!ECwD{En}{{hOG65IJ)!ghEFabn{V;)$py zeAWO^TmucMo-%Y)stNM+1#_i(bak%k;Vyc~=aN*Se0$v9uE|)b(0TzDBK3Lfw;zmV9A!aNUjyuOw{?UgwH;^u5%5| zz-n+cberd?6T8nGwX_+$L6=Z!@ERkFg%jPv*nFp@B&`0q-l6*y3zs19Yv%tw88x{sbh_^7mb{pjZ2ep{0m${3_%gq0G@$$JON!?NKc?gdm zo#EdDGBRKWsg{KOUoa(&nv)y!lsP6@I@ijqsv@0=eHs0RNGDp|&gLmOw_JStH_nO$ zG&8_bJVop*PK1`^w&u3@;=%q@u9d<)x=TJ}jZL88nc#TVpulfa9~6K<$OX+J78 zvqdR_2c4YPgEo~b<1i?-O6Ah`pmJp-NsJpRmtC^@BmvOKtQ#*$9 z(*MH5zOBjBG_N_2Y#J&fjRP|`mGh`eDrSwHI*Dg2G`E8D2~m13tDUbhLyWlvr+TW<(8r;OB`q z4Tsv=rS4Qij6Nn``@2y8ynhx!Tr0^mCZzfPIc)#WDkD|GLsqKrloL{7pw1?# zvNHI!8X%WU;RKt9CuYTqr8NsorJ&J6oxzN$1?H5lUA+v$S0=M9#U`IU1pI{;wV~E2 zR_z2svb8x$u3{p6?i`9pR#x!Ij58kq`400zMA9H6x0C^%ygJYj-WtVs$2(Cp0Z&jX!JU7RB5GZ zOeskWb;xKp9VCmECmNf?2)c+Bffu+6t)PyGq7-L8DgA`~s|iA;?_rDnulk5K;DIKoCMiNf0F>1Uckw{+VKYjtFd8 z;}jtx86ZN9IU{TTDIX;0!w_}j6 zn3o9$S?$tHy*_stECJ60qp(ZT1F=ul;hDXx(-5>y+M3kH;()r@#)icc_Y^)jibjF2d>mWpay0*(b~ zs`)}(A2XYj>C`84L9oPG+jt&u(LOk{MRF=<8op?)Du{|xE@UazK>rB&NyTuJQ#o6n zbZX~h>o^u6C%&;FiNL;p8p5WL2^Iim+D0kwG28WL#vztL8IxQw*9IrAdfc zh_1|igJu&b1|siRfG1M=1o6@k-AdUI{L5q>rIAmH-f;T8Q8tri+B@B3L0mlf>n;>ymj$Bq;sx&6 z!ULGpNWwd3oo7vEU8<5iZUt{7j!U;sO$?$jK z5h8IO!bV0Iw2?|*YyeJ^yAmXBRaBO_>~bg|h$zLF8E>EC!^n_Yk}|E{L;Gy$O9&z- zzQ7B03hBI{?p4C4_!c8(KcHT#VkI)^N(;M~-Fu#7O!nB`jCNXR+w9^AZrTs8_zT51 zsl_Ycwb(AH!b8A%R-E+$Sc~{a>f!WM#JFo`$k%Y-BNTHS=&|+@^R)P0`4a3;-rF-? zgTGleh$JWQYf)Q)TG$SgGO5r^_W2WQYlgN+b$%vx*Uni($n*%(kjYCwAk_{_@wHq*C(enXI@QlFVjTAOc6;P9em;x%oPl30z%9+okHg(s4h$BUt?FB&j~ z0wyd}iGbI6m_p&qX3nKN#2@Bt1oHfYi|K{+z~JXchImr1<$fVIY`iNmasn=_|>E@6ZGhB)9@D! zzNcl-57I3Mwzh#!HioMtgIm)xfG;2Orwe}Nu9KYjiowUaai2J)aiBWM$(Yh2^)C!OOB;2GnoEqUQpz<-1{3cBQ*dWt7vnTNLI@4iRAn8 z+I5j4Asny-$7SGR*CqgpSq`AogmlOGqo}qq?g4{o?k5x-jC#}Jd3YF=BGD&-$6Ov8 zwIOCv9_h^|s8ie`v)EbPz6fqJQ!F*Yud|K%8|4jKOI=EW%CuCSg{`=zep!knOvSdY zFE)G8eR`l*+q82VjnTG%11ilPgs7H2m+7B$B(86bbAuh3)e9_NF51>Oh_uEvQriUw_rvXCR{#xL-an#Etp_<3FJBdOnIs1VI%9k z>vSSx((|(9#AoFr10I$!{}og-Ez1N*)s^@81h;biveEa^11L$OZj_|Z8jF>1GSJ@I z^aRe*c$}7}?y8t-f+^o6)@nYe*GJlkLNQsxssrj&b@-f*i-aJDRb=!;wW9Q9s~^BBz3 zVfMC}yYqkm+Lg!j?C*#Q5%99EGj6ad_=nuH!VP6vu;|OY28Gs?YWp}NNY7lPxxxMj zxDPtG7-3Kl=D<@tYD0qf@H25l@onDjy)No}GcG4IkT;4BiCtx{Y?{zu`-MjE-7LUI zoKSA={H6&Fl7l7kgVP69+LqljX2OI9Hk<`gELsQs6TQ`h1|U#_GdT7hlBPp9 z&Y5x0&~?5R%y3Mi*yVl}X`Dq!VOkTV64;HqBqC6|wt?q|NQMzfb5j1wfGa;uF}e| zTMk&DZ0EBja1Nkx%@$dyM!xDQpG-Ce-eNYUaRHYMYhf$T3u(xU=cekF?pWuq7>u=r z;Tn3^TAjbwOvb9olo9w!bm>c_U9ki^MQAou`J;bSJ0^uV|6HBom^I_U5tSUO}qYwx- z5(jTN)3YrfvJm179}NQt3`voyxMwh#(yG#9YzC!k--r-XY??vA8>AV6Sbzy9@;ODW zHSwSY1jIUQB7$xyq)og7=E}OXuCmJiK-dCSA#ib6nng6 z31%xaU!u<>S~+E5pL9S-DDTU5@m83Pxds3l<=Fz>SqwONs(KGQ(| zG(H$})(@Ybkk$!nAx&&mC~mVO zaKEqm!HPlJBX%S!UEW(XU)L}+P4qr)roBx(bKT7q?kWX4r@&Pc^tPuzt3^;B)tWX8 zFQq+tKxoz5Ev?!th{`htX6(y&A%XrNw ze)jXOF^?p%Gat>#-ARQM84=cM>aC^4WBuTZ-J&YTJWYfIk0w`E>U zBGr6uL*7DYuWabsVRiDk4Tai6Tjy_RDRQxaqA#BRlDRe_1#ciKet;e4dWz+MT46wc zcXJ^7IV^8-eCbYY+4IB9zlW<*^Kcu) z7PfXSk@>`xPqe{poc-d)Azcy z$X%A0T__Rdp*$E<>T=j+pnA(drU7b3>$~~P8J@I9w>W1%0QVXadO*ei9fs3x!e!vU z8lYzwZnAXE`UY`@HPrdVij*tbQp&Oo0J6y)@GpE$BSIv>SNe;1O4HC*nrXyS>?ik< zwE{*1@e38?KW(7@w^lqhaFLN8T6AezqxgZHNsTm$D-0l=$=lQ(gEAM@5oHOKJAxrP z%0-*~*cDi^kS>&Gpe#L+r&^HPj7XdJueB+ItM?0GxhG;lM&3GmfQ8kcxey=Ul(+0l zA_!OkhiyZqxdd9Gfs^;!5<$Y!?5{;6h51X`%2L9~Jd=;06jN2j7>cgk88k%)4T@IY zFDO{tN3WSv-_-i3w{DHmy6IH`(F6x*Gpfb}OYj(gB3*Ff8`HD&@@Ra>>67j1kVt7E z_JtzGU3?Ul`&5tVCI}E2Y39y$Be!tE`^0z><6App4vpZV=8Hr?*=y8b^6FkAC(sZG zbKup0O2amtjc+mxa&mJP^~r(JzD|#Q(h5u^+$l@n!HmP0BLUcjxMDhxA?rc| zs-dO{1Wx374m*Z(_*7*SA#rP@KH@SjQieN^eAG-4$@-;9h^y{7z4(}zp$q)zamFcl zPS5|^S%y!(i=8FVY;i_YCU{Q8k9fy-(DblE=RL>;gAyP-C&=P564=ngz40D#X$tf%og<(%*zmDl`BUZyUy*ES>dov{P47J!-0=4zZZFKbqH$wg&`@LC$*n>oU)P#G zD9i0Ds7Yf1JT=paOgs_oz<4U+pNX|ngx+XHzME3g_f(>O!qm1pjY@(;tscsK4(1p| zH2w^eYT(N67sTBv2_?cLDX0?BVjX=c9(G_;jzWnyc1qQv8PN;cTlC{eJX*IyP@pEG z;^MoQ4=PTZn*2wT1H_)_HNBrzbj3A3q~`?m^_MApvp9#IoA{pKeK4M-dE$l)Xth2R#4qJHi6w_YBNz;5B0EDM{xTEfZJP~! zBXA`PS;U4NMbO4L=hh z9*US|40YPpfo+zYN}`Sh6J!^I)$SBgv~P&uH7F39ZbAk{&jP>DuN}GF`!sMujw0$a z3duQD^NQp;yU-LbSql3kRs&1UfNnpeUW^@>Rs1kQ3G@M{=pB%FKGm1uricZ*N&i;_2j2qwO9G_xk!Cy*=JVR%LwmFJ zyUz9Chw-rg=GGEE^WAmmf0cS)5xT_<$_;ir=RSl6w8Iqa$7WJuMz+LKXkZeYOI2hc zD3GT;JZ`Zb9|1MTyel<+FcKoTOfjSxr9Nq{7N zy@3@#0wf`C=20gycJ^eOoKe>iEQ+)QNEWYz1W4-9Jl3WJ$cf@hq?r0V>cwS1k^&O}|5F;~$Rd@n~C~p$; zP|z|3rp~!)q_N^(Vy%=}jqD5Pm5^%aiz1Y;sPc#py{%n|@%XNa5{#sp%t=?W*kumD zN;;sT-J+JH*;h##j9#fj5f@A+J&Zp1lDibO3=;$-Zq1w2N*S3&ZVk2|fQzZapyI<+ z*F}w2P`3FB%Cu?4#qR}fdNbEPz* z6hqGN7FXPtCnqr%zrwjqB17eG!ZfcDhP>}sZ%o(Y=35t;xp0xW3m3^QT%>p5B8>|d zVFw9|XdB^g-J|Y>ir{o!xX1-z*%LJ{TuZB7Hw-MQq&9xTae(6_EtRGm{$x89i znfC4M)u#%u>50l}Pri`3%6l%T^fAw0-QmJ_2Afjvp)@Fw9tR3Q~f)48=T556t4WpcW^(ET_k3)xGNe4u35XIdazsl1z#d zBz`U*!1_ucN9JqZJ*!!_-UJnBZs=2=J{uKR72o90yAyZd+kX9Gd3}NBTjKysYPexf z?GlSXr!bVN9S@*5#z;Qz=Oqv&S!$Va4B!`gbtg#SKgks z+p0h^L1i(3F4z1-?7YTi`o-dF#7X7j=04Nv^@fBMWP zzw^>p@7Cmgt`h&}AAaSZe&?g#`wxE}O1$vlzyC+S`EUQ#zx-#XRb(o(=~9TD?ZO=} zi?~B*aA!MZHo1o?WnkLt4W>z~|9UCh>DmqJrTNCpf*eG+ndp$np&)LHMz}t{LE!iZ zOXM)f>h*+7y9G_2GbCHkUwNe=0o5XD0u zez;BKikHoIl7U?cRJ|;2?8~y}1<4+BVTYU1CK!~AIzJD*$wDS~mFkF$u)&DZV92ji zY?9&9dKdxMrz(XynMPr_1!9w}V2~>EuScvB)KloP&BY9LnZTZP8CTmjnImvbE9yC- zl3XTLE}`CZ$RxNyKwAhHp$EVS67YEF8}*>`__ifjNyz~$W=@cSl&UD{JOEmNkrmy= zp<=AUF61mDGVuqlvnHf?Otso-E239_^p(QYy z{gi;W(V?%Qj{^j%kKyxAQAgBl&PAv5=Wl98m>p-M&UYGDuitlaeT#3|_tyP~?mGO|y>H!r*WtV0_V#P9z2?n_u3kNQ*ZSi6gI6C~ zTVJ~Rs?^bV!}r zP;b6RUv8lfzs=tmfA=0eOgps3`R=$EjdCo@`~l~e@~5|#)S*Se=P0>@vf|!J{@xhM z+_!Ob^(2osc-G~f>bjM`@_Ku?K6K>1V=I}umfb5{-L;e*TRXPrSC-b-q9=KGw)W1I zoD0sfj<<6@&Yxgn+zgoKct>U48%NPzj^*|HxxS)y{fk`B*RKC2*ZXSMS8@LO+V$0( zGl!uM=5{_eUeWMcdgxb?C{C!vfbI(6TBym`X?L{j$h*lc-8xNaW1MU>(jNU zt$eR6f8ZG82_?ft_uYHUNb9;R>;LS}W>NHFXk)6;i(ufYjXLG;<(a6pZ0FOQ@2Xw@ zCg<6vYeZ93{`Wbrmw%e;toB^jDleq1EN(0wyKm*FNcSn)6$dTx$Iisn^#zcjj zU9>UBz=@vqPh8*Nk6Tg7->r+k@*wieVNYEFzZ=Ew10QcJ=zwJx38_51K?fDyq8b z(E8%W(#fliuI<@(_nY@0e#=|#x@+;xZ`-@~&39kDMym%8Ev>HZdGl3o*?ZMn?5$F2 ze2ez(roAg53el*>rj%d{Etg=sx&J!;>erHbh-`*oaWz{)q_1C>E$JG4aNn`@rNu)> z7VkppA6h%Sq&hbqJPvs4CqEPF?DPIM{>tn3aW45Fx-akj8s{3n^7>PpUsk*R*PQZMx~h>W(=!@DAySEo z-SoWfs(MxL)oYzV5(NblA;PT0?&??dURUN-)tma5?uS_fKXBnDx-knEidh5{6;W}g zkl;dar7lE*+bmqT3W6Jd|65hvZ)Oxk@WJ6$-;Z<8z4x5+|KAIJQZ$Pgq=^h{HhC4z z5}T-vs+4KKL8<$1En@BZ$=4)`>;Jdkq}lSIs$;pnWo>L)FHw9SQLfk7^>sp4on480 z*JYH~tSu#=UuDc=)c>y`c*aiT(8je}Q{F)D^XUC3Mm%dh^Xj)jtFK?{cjggV6qS8s zKFYVOP}W(m7vjjX<(bO3eSdkTGRp@LWBFd3EUUeEkr`$&AgXppCa(G_tdfV6hRe(% z2vEFlFAds1eWOMF9Xm_&1LdS++`)=3Zkp`*21aY={?qVJo)3>s&xfCl|1smWzRnXl zto{A^n24mrOH0mUYuipM*hY;zS5>Mwjb`#{Iegpq#{F}qxG#5vqD+Uz(b(bPq`;`E~Pzs{=7Mu?QJq;OE= z;L8lxt5)o0W*4PhF*=&VIFmSSvbDBFQ9#>E7SU?c7B12Z(!Q1K&UfuUagQPP5kGOIXe_Dx-|_SN7HNr8;G9LH9|?)xs9t&5JX;j003ja?56Z zvLjyxMl{EowYQrBuS`y-NfIZqF`zCAdjUFvKvmI{xY1Elt&&qRbA6`5)Wz(L_HnA*9Gs?0kUzut$bIYqVk6M|f$v8V&@f|ZU7;%1X1|iBucO#b2ESJV- znmjpOwos7d?7++%O~(eaB;!~M;;(Gcw8^JL$-F4JAH;2yy;W4**S2B{(eLd?H~D{6 z%X)`Wb@xVQT5#!!(?WUj!ei~d;&I|gIjxJ!B_~u1e+bZhn|gt;GgBM?5z$y?z_QU0i|INz#~DS{6iQgKaAFZHtc6&ya^()2*gMLq`{C@9DW-z+{M!RkwF}h{9>J! zl&0VM-`dj;A-a3&wxk>`OjO?wBf3Z0$dkyqzgNw4N&=iUi*@|ew?y*`PkYSO8icFz zS$#`{!@^_r)xLy?Y>~a{t94vLq)&BfQ-A5y`Di_|S#|0ooR(hAp>OrmxAgxVOtE;m zyo!7T86wXiH<4qcLB5K-hP;ma1bGYjGx84dE^_hmrnQI6kynuKAU{BUg!~fuE%G+< zE^>jmyo!7b86clQZXz?}W#n7PcafhVzeawCd@fp%7(Ojpec$Ef4_DhahyMJ}-!{tV z%!IDVB7v z9roc1T!}h6%q8dU3*WxU7J4B94N2@|VzNiuks9>>h51zevzzzEjVEuAm8QqZ8aF!x zSZdoY)A@0I*ah1q8-_ls_w7qcYV(v`o16d zBY*6N{v>b$H}HaC;0Hl43dTVgOh(Sg9r27f@<+jFG#Zb>(PZq5-LW?wj{R{k9*xK2 za6Ac}&<(wC82Vulj>2&mhLZ^XQuxW)&v_)Y-h6-SSAU{>ihXIj3S?Uq8;`eV*SEI{+BP#4Yzh~U3o*$2VQn!+ znd50nWsi- zi3dlk_q|gBZs_hhFuDFM*v29JfioF6{`NyB1Zz`0#2v=lbuRNpn+=ZG9GLnw z-D@U+y)}%&IO5GX+_tV?;c(_jeK1QRFg&Rq+_?hsiDC`ri{glr+}Wtoc|J%^>wf|D C%#UIK literal 0 HcmV?d00001 diff --git a/packages/algorithm/src/v3/pkg/algorithm_v3_wasm_bg.wasm.d.ts b/packages/algorithm/src/v3/pkg/algorithm_v3_wasm_bg.wasm.d.ts new file mode 100644 index 0000000..847dfa5 --- /dev/null +++ b/packages/algorithm/src/v3/pkg/algorithm_v3_wasm_bg.wasm.d.ts @@ -0,0 +1,62 @@ +/* tslint:disable */ +/* eslint-disable */ +export const memory: WebAssembly.Memory; +export function __wbg_general_free(a: number, b: number): void; +export function __wbg_get_general_patterns(a: number, b: number): void; +export function __wbg_set_general_patterns(a: number, b: number, c: number): void; +export function __wbg_problem_free(a: number, b: number): void; +export function __wbg_get_problem_general(a: number): number; +export function __wbg_set_problem_general(a: number, b: number): void; +export function __wbg_get_problem_board(a: number): number; +export function __wbg_set_problem_board(a: number, b: number): void; +export function __wbg_pattern_free(a: number, b: number): void; +export function __wbg_get_pattern_height(a: number): number; +export function __wbg_set_pattern_height(a: number, b: number): void; +export function __wbg_get_pattern_p(a: number): number; +export function __wbg_set_pattern_p(a: number, b: number): void; +export function __wbg_board_free(a: number, b: number): void; +export function __wbg_get_board_goal(a: number, b: number): void; +export function __wbg_set_board_goal(a: number, b: number, c: number): void; +export function __wbg_get_board_start(a: number, b: number): void; +export function __wbg_set_board_start(a: number, b: number, c: number): void; +export function __wbg_get_board_width(a: number): number; +export function __wbg_set_board_width(a: number, b: number): void; +export function __wbg_get_board_height(a: number): number; +export function __wbg_set_board_height(a: number, b: number): void; +export function __wbg_answer_free(a: number, b: number): void; +export function __wbg_get_answer_ops(a: number, b: number): void; +export function __wbg_set_answer_ops(a: number, b: number, c: number): void; +export function __wbg_get_answer_n(a: number): number; +export function __wbg_set_answer_n(a: number, b: number): void; +export function __wbg_op_free(a: number, b: number): void; +export function __wbg_get_op_p(a: number): number; +export function __wbg_set_op_p(a: number, b: number): void; +export function __wbg_get_op_y(a: number): number; +export function __wbg_set_op_y(a: number, b: number): void; +export function __wbg_get_op_x(a: number): number; +export function __wbg_set_op_x(a: number, b: number): void; +export function __wbg_get_op_s(a: number): number; +export function __wbg_set_op_s(a: number, b: number): void; +export function __wbg_set_pattern_width(a: number, b: number): void; +export function __wbg_set_general_n(a: number, b: number): void; +export function __wbg_get_pattern_cells(a: number, b: number): void; +export function __wbg_get_pattern_width(a: number): number; +export function __wbg_get_general_n(a: number): number; +export function __wbg_set_pattern_cells(a: number, b: number, c: number): void; +export function __wbg_answerset_free(a: number, b: number): void; +export function __wbg_get_answerset_answer(a: number): number; +export function __wbg_set_answerset_answer(a: number, b: number): void; +export function __wbg_get_answerset_board(a: number, b: number): void; +export function __wbg_set_answerset_board(a: number, b: number, c: number): void; +export function solve(a: number, b: number): number; +export function __wbg_reverseoperationpatterns_free(a: number, b: number): void; +export function __wbg_get_reverseoperationpatterns_has_reverse90(a: number): number; +export function __wbg_set_reverseoperationpatterns_has_reverse90(a: number, b: number): void; +export function __wbg_get_reverseoperationpatterns_has_reverse_up_down(a: number): number; +export function __wbg_set_reverseoperationpatterns_has_reverse_up_down(a: number, b: number): void; +export function __wbg_get_reverseoperationpatterns_has_reverse_left_right(a: number): number; +export function __wbg_set_reverseoperationpatterns_has_reverse_left_right(a: number, b: number): void; +export function __wbindgen_malloc(a: number, b: number): number; +export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number; +export function __wbindgen_add_to_stack_pointer(a: number): number; +export function __wbindgen_free(a: number, b: number, c: number): void; diff --git a/packages/algorithm/src/v3/pkg/package.json b/packages/algorithm/src/v3/pkg/package.json new file mode 100644 index 0000000..a9330c5 --- /dev/null +++ b/packages/algorithm/src/v3/pkg/package.json @@ -0,0 +1,9 @@ +{ + "name": "algorithm-v3-wasm", + "type": "module", + "version": "0.1.0", + "files": ["algorithm_v3_wasm_bg.wasm", "algorithm_v3_wasm.js", "algorithm_v3_wasm_bg.js", "algorithm_v3_wasm.d.ts"], + "main": "algorithm_v3_wasm.js", + "types": "algorithm_v3_wasm.d.ts", + "sideEffects": ["./algorithm_v3_wasm.js", "./snippets/*"] +} diff --git a/packages/algorithm/src/v3/src/arrays.rs b/packages/algorithm/src/v3/src/arrays.rs new file mode 100644 index 0000000..d2f9354 --- /dev/null +++ b/packages/algorithm/src/v3/src/arrays.rs @@ -0,0 +1,188 @@ +use std::{cmp::Ordering, mem}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) struct TwoDimensionalCells { + inner: Vec, + pub(crate) width: usize, + pub(crate) height: usize, +} + +impl TwoDimensionalCells { + pub(crate) fn new(width: usize, height: usize) -> Self { + Self { + inner: vec![0; width * height], + width, + height, + } + } + + pub(crate) fn from_slice(width: usize, height: usize, slice: &[u8]) -> Self { + let mut inner = vec![0; width * height]; + + inner.copy_from_slice(slice); + + Self { + inner, + width, + height, + } + } + + pub(crate) fn len(&self) -> usize { + self.inner.len() + } + + pub(crate) fn from_vec(width: usize, height: usize, vec: Vec) -> Self { + Self { + inner: vec, + width, + height, + } + } + + pub(crate) fn iter(&self) -> impl Iterator + '_ { + self.inner.iter().copied() + } + + pub(crate) fn get(&self, row_index: usize, column_index: usize) -> Option { + if row_index >= self.height || column_index >= self.width { + return None; + } + + Some(self.inner[row_index * self.width + column_index]) + } + + pub(crate) fn get_multiple(&self, offset: usize, length: usize) -> Option<&[u8]> { + if offset >= self.inner.len() || offset + length > self.inner.len() { + return None; + } + + Some(&self.inner[offset..offset + length]) + } + + pub(crate) fn get_row(&self, idx: usize) -> Option<&[u8]> { + if idx >= self.height { + return None; + } + + self.get_multiple(idx * self.width, self.width) + } + + pub(crate) fn get_column(&self, idx: usize) -> Option> { + if idx >= self.width { + return None; + } + + let mut result = Vec::with_capacity(self.inner.len()); + + for (i, result_item) in result.iter_mut().enumerate().take(self.inner.len()) { + *result_item = self.inner[idx + i * self.width]; + } + + Some(result) + } + + pub(crate) fn set(&mut self, row_index: usize, column_index: usize, value: u8) { + if row_index >= self.height || column_index >= self.width { + eprintln!("Index out of bounds: {}, {}", row_index, column_index); + } + + self.inner[row_index * self.width + column_index] = value; + } + + pub(crate) fn set_multiple(&mut self, offset: usize, values: &[u8]) { + if offset >= self.inner.len() || offset + values.len() > self.inner.len() { + eprintln!("Index out of bounds: {}, {}", offset, values.len()); + } + + self.inner[offset..values.len()].copy_from_slice(values); + } + + pub(crate) fn set_row(&mut self, idx: usize, row: &[u8]) { + if idx >= self.height { + eprintln!("Index out of bounds: {}", idx); + } + + if row.len() != self.width { + eprintln!("Invalid column size: {} !== {}", row.len(), self.width); + } + + self.set_multiple(idx * self.width, row); + } + + fn transpose_inner(&self) -> Vec { + let mut new_inner = Vec::with_capacity(self.inner.len()); + + for i in 0..self.height { + for j in 0..self.width { + new_inner[j * self.height + i] = self.inner[i * self.width + j]; + } + } + + new_inner + } + + pub(crate) fn transpose_in_place(&mut self) { + self.inner = self.transpose_inner(); + + mem::swap(&mut self.width, &mut self.height); + } + + pub(crate) fn transpose(self) -> Self { + Self::from_vec(self.height, self.width, self.inner) + } + + pub(crate) fn reverse_row_wise_in_place(&mut self) { + for i in 0..(self.height / 2) { + for j in 0..self.width { + let v1 = i * self.width + j; + let v2 = (self.height - 1 - i) * self.width + j; + + let (low, high) = match v1.cmp(&v2) { + Ordering::Less => (v1, v2), + Ordering::Greater => (v2, v1), + Ordering::Equal => continue, + }; + + let (a, b) = self.inner.split_at_mut(high); + + mem::swap(&mut a[low], &mut b[0]); + } + } + } + + pub(crate) fn reverse_row_wise(self) -> Self { + let mut new_self = self.clone(); + + new_self.reverse_row_wise_in_place(); + + new_self + } + + pub(crate) fn reverse_column_wise_in_place(&mut self) { + for i in 0..self.height { + for j in 0..(self.width / 2) { + let v1 = i * self.width + j; + let v2 = i * self.width + self.width - 1 - j; + + let (low, high) = match v1.cmp(&v2) { + Ordering::Less => (v1, v2), + Ordering::Greater => (v2, v1), + Ordering::Equal => continue, + }; + + let (a, b) = self.inner.split_at_mut(high); + + mem::swap(&mut a[low], &mut b[0]); + } + } + } + + pub(crate) fn reverse_column_wise(self) -> Self { + let mut new_self = self.clone(); + + new_self.reverse_column_wise_in_place(); + + new_self + } +} diff --git a/packages/algorithm/src/v3/src/evaluation.rs b/packages/algorithm/src/v3/src/evaluation.rs new file mode 100644 index 0000000..4d780cd --- /dev/null +++ b/packages/algorithm/src/v3/src/evaluation.rs @@ -0,0 +1,46 @@ +use crate::{ + katanuki::katanuki_board, + types::{Context, Direction, Point}, + utils::{count_elements_column_wise, get_delta}, +}; + +pub(crate) fn evaluate_row( + c: &mut Context, + p: u32, + pp: Point, + goal_element_counts: [u32; 4], +) -> i32 { + let board = katanuki_board(c, p, pp.x, pp.y, Direction::Up); + let element_counts = count_elements_column_wise(&board); + let delta = get_delta(&element_counts[c.height - 1], &goal_element_counts); + + let mut value = 0; + + for i in delta { + if i >= 0 { + value += i; + } + } + + value +} + +pub(crate) fn evaluate_column_piece( + c: &mut Context, + p: u32, + pp: Point, + goal_column: Vec, +) -> i32 { + let board = katanuki_board(c, p, pp.x, pp.y, Direction::Left); + let column = board.get_column(c.width - 1).unwrap(); + + let mut value = 0; + + for i in 0..c.height { + if column[i] != goal_column[i] { + value += 1; + } + } + + value +} diff --git a/packages/algorithm/src/v3/src/katanuki.rs b/packages/algorithm/src/v3/src/katanuki.rs new file mode 100644 index 0000000..cc013c1 --- /dev/null +++ b/packages/algorithm/src/v3/src/katanuki.rs @@ -0,0 +1,172 @@ +use crate::{ + arrays::TwoDimensionalCells, + schemas::{Op, Problem}, + types::{get_pattern, Context, Direction, Point, DOWN, LEFT, UP}, + utils::{cells_to_board, count_elements_column_wise, reverse_cells, ReverseOperation}, +}; + +struct PatternData { + b: TwoDimensionalCells, + bx: usize, + by: usize, + pw: usize, + ph: usize, + pp: Point, + pattern: TwoDimensionalCells, +} + +fn generate_pattern_data( + dir: Direction, + b: &TwoDimensionalCells, + pattern: &TwoDimensionalCells, + w: usize, + h: usize, + x: i32, + y: i32, +) -> PatternData { + let dir = dir.to_u8(); + + if dir == UP || dir == DOWN { + PatternData { + b: reverse_cells(b.clone(), ReverseOperation::Reverse90), + bx: h, + by: w, + pw: pattern.height, + ph: pattern.width, + pp: Point { x: y, y: x }, + pattern: reverse_cells(pattern.clone(), ReverseOperation::Reverse90), + } + } else { + PatternData { + b: b.clone(), + bx: w, + by: h, + pw: pattern.width, + ph: pattern.height, + pp: Point { x, y }, + pattern: pattern.clone(), + } + } +} + +pub(crate) fn katanuki_board( + c: &mut Context, + p: u32, + x: i32, + y: i32, + s: Direction, +) -> TwoDimensionalCells { + let s = s.to_u8(); + let pattern = get_pattern(p as usize, &c.patterns); + + if x + pattern.width as i32 <= 0 + || x >= c.width as i32 + || y + pattern.height as i32 <= 0 + || y >= c.height as i32 + { + panic!("Pattern out of bounds"); + } + + let mut l = Point { x: 0, y: 0 }; + + let PatternData { + mut b, + bx, + by, + pw, + ph, + pp, + pattern, + } = generate_pattern_data( + Direction::from_u8(s), + &c.board, + &pattern.cells, + c.width, + c.height, + x, + y, + ); + + for i in 0..ph { + l.y = pp.y + i as i32; + + // Out of range + if l.y < 0 { + continue; + } + if l.y >= by as i32 { + break; + } + + let mut current_row = b.get_row(l.y as usize).unwrap().to_vec(); + let mut picked: Vec = vec![]; + + for j in (0..pw).rev() { + l.x = pp.x + j as i32; + + // Out of range + if l.x < 0 { + continue; + } + + if l.x >= bx as i32 { + break; + } + + if pattern.get(i, j).unwrap() == 0 { + continue; + } + + let pop_index = picked.iter().position(|&x| x == l.x as u8).unwrap(); + let removed_item = picked.remove(pop_index); + + picked.insert(0, removed_item); + } + + if s == UP || s == LEFT { + current_row.extend(picked); + } else { + let mut new_row = picked; + + new_row.extend(current_row); + + current_row = new_row; + } + + b.set_row(l.y as usize, ¤t_row); + } + + if s == UP || s == DOWN { + reverse_cells(b, ReverseOperation::Reverse90) + } else { + b + } +} + +pub(crate) fn easy_katanuki(problem: Problem, op: Op) -> Vec { + let mut c = Context::new(problem); + + cells_to_board(katanuki_board( + &mut c, + op.p, + op.x, + op.y, + Direction::from_u8(op.s), + )) +} + +pub(crate) fn katanuki(c: &mut Context, p: u32, x: i32, y: i32, s: Direction) { + let b = katanuki_board(c, p, x, y, s); + + if c.board != b { + c.board = b; + c.current_element_counts = count_elements_column_wise(&c.board); + + c.add_op(Op { + p, + x, + y, + s: s.to_u8(), + }); + } +} diff --git a/packages/algorithm/src/v3/src/lib.rs b/packages/algorithm/src/v3/src/lib.rs new file mode 100644 index 0000000..600f2dd --- /dev/null +++ b/packages/algorithm/src/v3/src/lib.rs @@ -0,0 +1,389 @@ +use wasm_bindgen::prelude::*; + +use std::cmp::max; + +use evaluation::evaluate_row; +use katanuki::katanuki; +use schemas::{Answer, Problem}; +use types::{Context, Direction, Point, ReverseOperationPatterns}; +use utils::{ + cells_to_board, count_elements_column_wise, get_delta, multi_dereverse_cells, + multi_reverse_cells, +}; + +mod arrays; +mod evaluation; +mod katanuki; +mod schemas; +mod types; +mod utils; + +#[derive(Clone, Debug)] +#[wasm_bindgen(getter_with_clone)] +pub struct AnswerSet { + pub answer: Answer, + pub board: Vec, +} + +fn solve_func(c: &mut Context) -> AnswerSet { + c.board = multi_reverse_cells(c.board.clone(), c.rv_op); + c.goal_board = multi_reverse_cells(c.goal_board.clone(), c.rv_op); + + c.current_element_counts = count_elements_column_wise(&c.board); + let goal_element_counts = count_elements_column_wise(&c.goal_board); + + let rv_ul = c.rv_op.has_reverse90; + let rv_ud = c.rv_op.has_reverse_up_down; + let rv_lr = c.rv_op.has_reverse_left_right; + + let mut delta; + let mut cnt_unmoved = 0; + + for i in (0..c.height).rev() { + let mut completed_rows = c.height - i - 1 - cnt_unmoved; + + delta = get_delta( + &c.current_element_counts[c.height - 1 - cnt_unmoved], + &goal_element_counts[i], + ); + + let mut unfilled: Vec = vec![]; + + for j in 0..c.width { + if delta == [0, 0, 0, 0] { + break; + } + + if cnt_unmoved > 0 { + katanuki( + c, + 22, + 0, + c.height as i32 - cnt_unmoved as i32, + Direction::Down, + ); + + completed_rows += cnt_unmoved; + cnt_unmoved = 0; + } + + let looking_cell = c.board.get(c.height - 1, j).unwrap(); + + if delta[looking_cell as usize] <= 0 { + continue; + } + + let mut is_filled = false; + + for k in (completed_rows..=c.height - 2).rev() { + let looking_cell = c.board.get(k, j).unwrap(); + + if delta[looking_cell as usize] > 0 { + let mut cnt = 0; + let mut value: [i32; 4] = [0, 0, 0, 256]; + + while k - (1 << cnt) + 1 >= completed_rows { + let x = j as i32; + let y = (k - (1 << cnt) + 1) as i32; + + let mut p; + let mut pp = Point { x: 0, y: 0 }; + + let mut evaluation; + + if cnt != 0 { + p = 3 * cnt - 1; + pp.x = if rv_ul && rv_ud { x - 1 } else { x }; + pp.y = if !rv_ul && !rv_ud { y + 1 } else { y }; + + evaluation = evaluate_row(c, p, pp, goal_element_counts[i]); + + if value[3] > evaluation { + value = [p as i32, pp.x, pp.y, evaluation]; + } + + p = 3 * cnt; + pp.x = if !rv_ul && rv_lr { x - 1 } else { x }; + pp.y = if rv_ul && !rv_lr { y + 1 } else { y }; + + evaluation = evaluate_row(c, p, pp, goal_element_counts[i]); + + if value[3] > evaluation { + value = [p as i32, pp.x, pp.y, evaluation]; + } + } else { + p = 0; + pp = Point { x, y }; + + evaluation = evaluate_row(c, p, pp, goal_element_counts[i]); + + if value[3] > evaluation { + value = [p as i32, pp.x, pp.y, evaluation]; + } + } + + cnt += 1; + } + + katanuki(c, value[0] as u32, value[1], value[2], Direction::Up); + + is_filled = true; + + delta = get_delta( + &c.current_element_counts[c.height - 1], + &goal_element_counts[i], + ); + + break; + } + } + + if !is_filled { + unfilled.push(j); + } + } + + // unfilled + for j in unfilled { + let mut is_filled = false; + + let looking_cell = c.board.get(c.height - 1, j).unwrap(); + + if delta[looking_cell as usize] <= 0 { + continue; + } + + for k in 1..=(max(j, c.width - j - 1)) { + // right side + let mut x = j + k; + + if x < c.width { + for m in (completed_rows..=(c.height - 2)).rev() { + let looking_cell = c.board.get(m, x).unwrap(); + + if delta[looking_cell as usize] < 0 { + let mut y = m; + let mut ln = k; + let mut irregular = false; + + if m % 2 == (c.height - 1) % 2 { + // Move lookingCell to the place which is confused and move the deepest cell to the place which is not confused + katanuki( + c, + if rv_ul { 2 } else { 3 }, + if (!rv_ul && !rv_lr) || (rv_ul && !rv_ud) { + x as i32 + } else { + x as i32 - 1 + }, + y as i32, + Direction::Up, + ); + + irregular = true; + y = c.height - 2; + } + + let mut cnt = 0; + + while ln > 0 { + if ln % 2 == 1 { + // border nukigata (else: 1 * 1) + katanuki( + c, + if cnt == 0 { + 0 + } else if rv_ul { + 3 * cnt + } else { + 3 * cnt - 1 + }, + (x - (1 << cnt)) as i32, + if (!rv_ud && !rv_ul) || (!rv_lr && rv_ul) || cnt == 0 { + y as i32 + } else { + y as i32 - 1 + }, + Direction::Left, + ); + + x -= 1 << cnt; + } + + ln >>= 1; + cnt += 1; + } + + katanuki(c, 0, x as i32, y as i32, Direction::Up); + + if irregular { + katanuki(c, 0, (j + k) as i32, c.height as i32 - 3, Direction::Up); + } + + delta = get_delta( + &c.current_element_counts[c.height - 1], + &goal_element_counts[i], + ); + + is_filled = true; + + break; + } + } + } + + if is_filled { + break; + } + + // left side + let mut x = j as i32 - k as i32; + + if x >= 0 { + for m in (completed_rows..=(c.height - 2)).rev() { + let looking_cell = c.board.get(m, x as usize).unwrap(); + + if delta[looking_cell as usize] < 0 { + let mut y = m; + let mut ln = k; + let mut irregular = false; + + if m % 2 == (c.height - 1) % 2 { + // Move lookingCell to the place which is confused and move the deepest cell to the place which is not confused + katanuki( + c, + if rv_ul { 2 } else { 3 }, + if (!rv_ul && !rv_lr) || (rv_ul && !rv_ud) { + x + } else { + x - 1 + }, + y as i32, + Direction::Up, + ); + + irregular = true; + y = c.height - 2; + } + + let mut cnt = 0; + + while ln > 0 { + if ln % 2 == 1 { + // border nukigata (else: 1 * 1) + katanuki( + c, + if cnt == 0 { + 0 + } else if rv_ul { + 3 * cnt + } else { + 3 * cnt - 1 + }, + x + 1, + if (!rv_ud && !rv_ul) || (!rv_lr && rv_ul) || cnt == 0 { + y as i32 + } else { + y as i32 - 1 + }, + Direction::Right, + ); + + x += 1 << cnt; + } + + ln >>= 1; + cnt += 1; + } + + katanuki(c, 0, x, y as i32, Direction::Up); + + if irregular { + katanuki(c, 0, (j - k) as i32, c.height as i32 - 3, Direction::Up); + } + + delta = get_delta( + &c.current_element_counts[c.height - 1], + &goal_element_counts[i], + ); + + is_filled = true; + + break; + } + } + } + + if is_filled { + break; + } + } + } + + cnt_unmoved += 1; + + if i == 0 { + katanuki( + c, + 22, + 0, + c.height as i32 - cnt_unmoved as i32, + Direction::Down, + ); + } + } + + for i in (0..c.height).rev() { + let goal_row = c.goal_board.get_row(i).unwrap().to_vec(); + + // Only stripe + if c.board.get_row(i).unwrap() == goal_row { + continue; + } + + for j in (0..c.width).rev() { + let completed_rows = c.width - j - 1; + let correct_cell = goal_row[j]; + + for k in (completed_rows..c.width).rev() { + let looking_cell = c.board.get(j, k).unwrap(); + + if looking_cell == correct_cell { + katanuki(c, 0, k as i32, i as i32, Direction::Right); + + break; + } + } + } + } + + c.board = multi_dereverse_cells(c.board.clone(), c.rv_op); + c.goal_board = multi_dereverse_cells(c.goal_board.clone(), c.rv_op); + + let answer = Answer { + n: c.ops.len() as u32, + ops: c.ops.clone(), + }; + + AnswerSet { + answer, + board: cells_to_board(c.board.clone()), + } +} + +#[wasm_bindgen] +pub fn solve(problem: Problem, rv_op: ReverseOperationPatterns) -> AnswerSet { + let height = problem.board.height as usize; + let width = problem.board.width as usize; + + let mut c = Context::new(problem); + + c.rv_op = rv_op; + + if c.rv_op.has_reverse90 { + c.width = height; + c.height = width; + } + + solve_func(&mut c) +} diff --git a/packages/algorithm/src/v3/src/schemas.rs b/packages/algorithm/src/v3/src/schemas.rs new file mode 100644 index 0000000..01b6669 --- /dev/null +++ b/packages/algorithm/src/v3/src/schemas.rs @@ -0,0 +1,247 @@ +#![allow( + clippy::let_and_return, + clippy::type_complexity, + clippy::unused_unit, + clippy::manual_non_exhaustive, + clippy::redundant_closure +)] + +#[allow(unused)] +use super::*; +#[allow(unused)] +use wasm_bindgen::prelude::*; +#[derive(Clone, serde::Serialize, serde::Deserialize)] +#[wasm_bindgen(getter_with_clone)] +pub struct General { + pub n: u32, + pub patterns: Vec, +} +#[allow(non_camel_case_types, non_snake_case)] +pub trait General_Trait { + fn set_n(&mut self, value: u32) -> std::result::Result<(), JsValue>; + fn n(&self) -> std::result::Result; + fn set_patterns(&mut self, value: Vec) -> std::result::Result<(), JsValue>; + fn patterns(&self) -> std::result::Result, JsValue>; +} +impl General_Trait for General { + fn set_n(&mut self, value: u32) -> std::result::Result<(), JsValue> { + self.n = value; + Ok(()) + } + fn n(&self) -> std::result::Result { + Ok(self.n) + } + fn set_patterns(&mut self, value: Vec) -> std::result::Result<(), JsValue> { + self.patterns = value; + Ok(()) + } + fn patterns(&self) -> std::result::Result, JsValue> { + Ok(self.patterns.clone()) + } +} +#[derive(Clone, serde :: Serialize, serde :: Deserialize)] +#[wasm_bindgen(getter_with_clone)] +pub struct Problem { + pub general: General, + pub board: Board, +} +#[allow(non_camel_case_types, non_snake_case)] +pub trait Problem_Trait { + fn set_general(&mut self, value: General) -> std::result::Result<(), JsValue>; + fn general(&self) -> std::result::Result; + fn set_board(&mut self, value: Board) -> std::result::Result<(), JsValue>; + fn board(&self) -> std::result::Result; +} +impl Problem_Trait for Problem { + fn set_general(&mut self, value: General) -> std::result::Result<(), JsValue> { + self.general = value; + Ok(()) + } + fn general(&self) -> std::result::Result { + Ok(self.general.clone()) + } + fn set_board(&mut self, value: Board) -> std::result::Result<(), JsValue> { + self.board = value; + Ok(()) + } + fn board(&self) -> std::result::Result { + Ok(self.board.clone()) + } +} +#[derive(Clone, serde :: Serialize, serde :: Deserialize)] +#[wasm_bindgen(getter_with_clone)] +pub struct Pattern { + pub width: u32, + pub cells: Vec, + pub height: u32, + pub p: u32, +} +#[allow(non_camel_case_types, non_snake_case)] +pub trait Pattern_Trait { + fn set_width(&mut self, value: u32) -> std::result::Result<(), JsValue>; + fn width(&self) -> std::result::Result; + fn set_p(&mut self, value: u32) -> std::result::Result<(), JsValue>; + fn p(&self) -> std::result::Result; + fn set_cells(&mut self, value: Vec) -> std::result::Result<(), JsValue>; + fn cells(&self) -> std::result::Result, JsValue>; + fn set_height(&mut self, value: u32) -> std::result::Result<(), JsValue>; + fn height(&self) -> std::result::Result; +} +impl Pattern_Trait for Pattern { + fn set_width(&mut self, value: u32) -> std::result::Result<(), JsValue> { + self.width = value; + Ok(()) + } + fn width(&self) -> std::result::Result { + Ok(self.width) + } + fn set_p(&mut self, value: u32) -> std::result::Result<(), JsValue> { + self.p = value; + Ok(()) + } + fn p(&self) -> std::result::Result { + Ok(self.p) + } + fn set_cells(&mut self, value: Vec) -> std::result::Result<(), JsValue> { + self.cells = value; + Ok(()) + } + fn cells(&self) -> std::result::Result, JsValue> { + Ok(self.cells.clone()) + } + fn set_height(&mut self, value: u32) -> std::result::Result<(), JsValue> { + self.height = value; + Ok(()) + } + fn height(&self) -> std::result::Result { + Ok(self.height) + } +} +#[derive(Clone, serde :: Serialize, serde :: Deserialize)] +#[wasm_bindgen(getter_with_clone)] +pub struct Board { + pub goal: Vec, + pub start: Vec, + pub width: u32, + pub height: u32, +} +#[allow(non_camel_case_types, non_snake_case)] +pub trait Board_Trait { + fn set_start(&mut self, value: Vec) -> std::result::Result<(), JsValue>; + fn start(&self) -> std::result::Result, JsValue>; + fn set_width(&mut self, value: u32) -> std::result::Result<(), JsValue>; + fn width(&self) -> std::result::Result; + fn set_height(&mut self, value: u32) -> std::result::Result<(), JsValue>; + fn height(&self) -> std::result::Result; + fn set_goal(&mut self, value: Vec) -> std::result::Result<(), JsValue>; + fn goal(&self) -> std::result::Result, JsValue>; +} +impl Board_Trait for Board { + fn set_start(&mut self, value: Vec) -> std::result::Result<(), JsValue> { + self.start = value; + Ok(()) + } + fn start(&self) -> std::result::Result, JsValue> { + Ok(self.start.clone()) + } + fn set_width(&mut self, value: u32) -> std::result::Result<(), JsValue> { + self.width = value; + Ok(()) + } + fn width(&self) -> std::result::Result { + Ok(self.width) + } + fn set_height(&mut self, value: u32) -> std::result::Result<(), JsValue> { + self.height = value; + Ok(()) + } + fn height(&self) -> std::result::Result { + Ok(self.height) + } + fn set_goal(&mut self, value: Vec) -> std::result::Result<(), JsValue> { + self.goal = value; + Ok(()) + } + fn goal(&self) -> std::result::Result, JsValue> { + Ok(self.goal.clone()) + } +} + +#[derive(Clone, Debug, serde :: Serialize, serde :: Deserialize)] +#[wasm_bindgen(getter_with_clone)] +pub struct Answer { + pub ops: Vec, + pub n: u32, +} +#[allow(non_camel_case_types, non_snake_case)] +pub trait Answer_Trait { + fn set_n(&mut self, value: u32) -> std::result::Result<(), JsValue>; + fn n(&self) -> std::result::Result; + fn set_ops(&mut self, value: Vec) -> std::result::Result<(), JsValue>; + fn ops(&self) -> std::result::Result, JsValue>; +} +impl Answer_Trait for Answer { + fn set_n(&mut self, value: u32) -> std::result::Result<(), JsValue> { + self.n = value; + Ok(()) + } + fn n(&self) -> std::result::Result { + Ok(self.n) + } + fn set_ops(&mut self, value: Vec) -> std::result::Result<(), JsValue> { + self.ops = value; + Ok(()) + } + fn ops(&self) -> std::result::Result, JsValue> { + Ok(self.ops.clone()) + } +} +#[derive(Clone, Debug, serde :: Serialize, serde :: Deserialize)] +#[wasm_bindgen] +pub struct Op { + pub p: u32, + pub y: i32, + pub x: i32, + pub s: u8, +} +#[allow(non_camel_case_types, non_snake_case)] +pub trait Op_Trait { + fn set_p(&mut self, value: u32) -> std::result::Result<(), JsValue>; + fn p(&self) -> std::result::Result; + fn set_s(&mut self, value: u8) -> std::result::Result<(), JsValue>; + fn s(&self) -> std::result::Result; + fn set_x(&mut self, value: i32) -> std::result::Result<(), JsValue>; + fn x(&self) -> std::result::Result; + fn set_y(&mut self, value: i32) -> std::result::Result<(), JsValue>; + fn y(&self) -> std::result::Result; +} +impl Op_Trait for Op { + fn set_p(&mut self, value: u32) -> std::result::Result<(), JsValue> { + self.p = value; + Ok(()) + } + fn p(&self) -> std::result::Result { + Ok(self.p) + } + fn set_s(&mut self, value: u8) -> std::result::Result<(), JsValue> { + self.s = value; + Ok(()) + } + fn s(&self) -> std::result::Result { + Ok(self.s) + } + fn set_x(&mut self, value: i32) -> std::result::Result<(), JsValue> { + self.x = value; + Ok(()) + } + fn x(&self) -> std::result::Result { + Ok(self.x) + } + fn set_y(&mut self, value: i32) -> std::result::Result<(), JsValue> { + self.y = value; + Ok(()) + } + fn y(&self) -> std::result::Result { + Ok(self.y) + } +} diff --git a/packages/algorithm/src/v3/src/types.rs b/packages/algorithm/src/v3/src/types.rs new file mode 100644 index 0000000..ff38f87 --- /dev/null +++ b/packages/algorithm/src/v3/src/types.rs @@ -0,0 +1,290 @@ +use wasm_bindgen::prelude::*; + +use std::mem; + +use once_cell::sync::Lazy; + +use crate::{ + arrays::TwoDimensionalCells, + schemas::{Op, Problem}, + utils::board_to_cells, +}; + +pub(crate) const UP: u8 = 0; +pub(crate) const DOWN: u8 = 1; +pub(crate) const LEFT: u8 = 2; +pub(crate) const RIGHT: u8 = 3; + +#[derive(Clone, Copy, Debug)] +pub(crate) enum Direction { + Up, + Down, + Left, + Right, +} + +impl Direction { + pub(crate) fn from_u8(value: u8) -> Self { + match value { + UP => Self::Up, + DOWN => Self::Down, + LEFT => Self::Left, + RIGHT => Self::Right, + _ => unreachable!(), + } + } + + pub(crate) fn to_u8(&self) -> u8 { + match self { + Self::Up => UP, + Self::Down => DOWN, + Self::Left => LEFT, + Self::Right => RIGHT, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct Point { + pub(crate) x: i32, + pub(crate) y: i32, +} + +#[derive(Clone, Debug)] +pub(crate) struct InternalPattern { + pub(crate) p: u32, + pub(crate) width: usize, + pub(crate) height: usize, + pub(crate) cells: TwoDimensionalCells, +} + +fn generate_type_i(p: u32, size: usize) -> InternalPattern { + let mut cells = TwoDimensionalCells::new(size, size); + + for i in 0..size { + cells.set_row(i, vec![1; size].as_slice()); + } + + InternalPattern { + p, + width: size, + height: size, + cells, + } +} + +fn generate_type_ii(p: u32, size: usize) -> InternalPattern { + let cells = TwoDimensionalCells::from_vec( + size, + size, + (0..size * size) + .flat_map(|i| { + if i / size % 2 == 0 { + vec![1; size] + } else { + vec![0; size] + } + }) + .collect(), + ); + + InternalPattern { + p, + width: size, + height: size, + cells, + } +} + +fn generate_type_iii(p: u32, size: usize) -> InternalPattern { + let cells = TwoDimensionalCells::from_vec( + size, + size, + (0..size * size) + .flat_map(|_| [1, 0].repeat(size / 2)) + .collect(), + ); + + InternalPattern { + p, + width: size, + height: size, + cells, + } +} + +static FIXED_PATTERNS: Lazy> = Lazy::new(|| { + let mut patterns = vec![InternalPattern { + p: 0, + width: 1, + height: 1, + cells: TwoDimensionalCells::from_vec(1, 1, vec![1]), + }]; + + let mut i = 1; + let mut j = 2; + + while j <= 256 { + patterns.push(generate_type_i(i, j)); + patterns.push(generate_type_ii(i + 1, j)); + patterns.push(generate_type_iii(i + 2, j)); + + i += 3; + j *= 2; + } + + patterns +}); + +pub(crate) fn get_pattern(idx: usize, general: &[InternalPattern]) -> InternalPattern { + if idx < FIXED_PATTERNS.len() { + FIXED_PATTERNS[idx].clone() + } else { + general[idx - FIXED_PATTERNS.len()].clone() + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[wasm_bindgen] +pub struct ReverseOperationPatterns { + pub has_reverse90: bool, + pub has_reverse_up_down: bool, + pub has_reverse_left_right: bool, +} + +impl ReverseOperationPatterns { + pub(crate) fn new() -> Self { + Self { + has_reverse90: false, + has_reverse_up_down: false, + has_reverse_left_right: false, + } + } +} + +pub(crate) struct Context { + pub(crate) board: TwoDimensionalCells, + pub(crate) goal_board: TwoDimensionalCells, + pub(crate) current_element_counts: Vec<[u32; 4]>, + pub(crate) patterns: Vec, + pub(crate) width: usize, + pub(crate) height: usize, + pub(crate) rv_op: ReverseOperationPatterns, + + // Answer + pub(crate) n: u32, + pub(crate) ops: Vec, +} + +impl Context { + pub(crate) fn new(problem: Problem) -> Self { + let (board, goal_board) = board_to_cells(&problem.board); + + Self { + board, + goal_board, + current_element_counts: vec![], + patterns: problem + .general + .patterns + .iter() + .map(|p| InternalPattern { + p: p.p, + width: p.width as usize, + height: p.height as usize, + cells: TwoDimensionalCells::from_vec( + p.width as usize, + p.height as usize, + p.cells + .iter() + .flat_map(|s| { + s.chars() + .map(|c| { + c.to_digit(10).and_then(|n| u8::try_from(n).ok()).unwrap() + }) + .collect::>() + }) + .collect(), + ), + }) + .collect(), + width: problem.board.width as usize, + height: problem.board.height as usize, + rv_op: ReverseOperationPatterns::new(), + n: 0, + ops: vec![], + } + } + + pub(crate) fn add_op(&mut self, op: Op) { + let Op { + p, + mut y, + mut x, + mut s, + } = op; + + let pattern = get_pattern(op.p as usize, &self.patterns); + + if self.rv_op.has_reverse90 { + mem::swap(&mut x, &mut y); + + if self.rv_op.has_reverse_up_down { + y = self.height as i32 - y - 1; + y -= pattern.height as i32 - 1; + + if s == LEFT { + s = RIGHT; + } else if s == RIGHT { + s = LEFT; + } + } + + if self.rv_op.has_reverse_left_right { + x = self.width as i32 - x - 1; + x -= pattern.width as i32 - 1; + + if s == UP { + s = DOWN; + } else if s == DOWN { + s = UP; + } + } + + if s == UP { + s = LEFT; + } else if s == DOWN { + s = RIGHT; + } else if s == LEFT { + s = UP; + } else if s == RIGHT { + s = DOWN; + } + } else { + if self.rv_op.has_reverse_up_down { + y = self.height as i32 - y - 1; + y -= pattern.height as i32 - 1; + + if s == UP { + s = DOWN; + } else if s == DOWN { + s = UP; + } + } + + if self.rv_op.has_reverse_left_right { + x = self.width as i32 - x - 1; + x -= pattern.width as i32 - 1; + + if s == LEFT { + s = RIGHT; + } else if s == RIGHT { + s = LEFT; + } + } + } + + self.n += 1; + self.ops.push(Op { p, y, x, s }); + } +} diff --git a/packages/algorithm/src/v3/src/utils.rs b/packages/algorithm/src/v3/src/utils.rs new file mode 100644 index 0000000..cfce46c --- /dev/null +++ b/packages/algorithm/src/v3/src/utils.rs @@ -0,0 +1,132 @@ +use crate::{arrays::TwoDimensionalCells, schemas::Board, types::ReverseOperationPatterns}; + +pub(crate) fn board_to_cells(b: &Board) -> (TwoDimensionalCells, TwoDimensionalCells) { + let start = b + .start + .iter() + .flat_map(|s| { + s.chars() + .map(|c| c.to_digit(10).and_then(|n| u8::try_from(n).ok()).unwrap()) + .collect::>() + }) + .collect::>(); + + let goal = b + .goal + .iter() + .flat_map(|s| { + s.chars() + .map(|c| c.to_digit(10).and_then(|n| u8::try_from(n).ok()).unwrap()) + .collect::>() + }) + .collect::>(); + + ( + TwoDimensionalCells::from_vec(b.width as usize, b.height as usize, start), + TwoDimensionalCells::from_vec(b.width as usize, b.height as usize, goal), + ) +} + +pub(crate) fn cells_to_board(cells: TwoDimensionalCells) -> Vec { + cells + .iter() + .map(|cell| cell.to_string()) + .collect::>() + .chunks(cells.width) + .map(|chunk| chunk.join("")) + .collect::>() +} + +pub(crate) fn count_elements_column_wise(cells: &TwoDimensionalCells) -> Vec<[u32; 4]> { + (0..cells.height) + .map(|i| { + (0..cells.width).fold([0; 4], |mut acc, j| { + acc[cells.get(i, j).unwrap() as usize] += 1; + + acc + }) + }) + .collect() +} + +pub(crate) fn get_delta(current_row_counts: &[u32; 4], target_row_counts: &[u32; 4]) -> [i32; 4] { + current_row_counts + .iter() + .zip(target_row_counts) + .fold([0; 4], |mut delta, (current, target)| { + delta[*target as usize] = *target as i32 - *current as i32; + + delta + }) +} + +pub(crate) enum ReverseOperation { + Reverse90, + ReverseUpDown, + ReverseLeftRight, +} + +pub(crate) fn reverse_cells( + cells: TwoDimensionalCells, + op: ReverseOperation, +) -> TwoDimensionalCells { + match op { + ReverseOperation::Reverse90 => cells.transpose().reverse_row_wise(), + ReverseOperation::ReverseUpDown => cells.reverse_row_wise(), + ReverseOperation::ReverseLeftRight => cells.reverse_column_wise(), + } +} + +pub(crate) fn multi_reverse_cells( + mut cells: TwoDimensionalCells, + rv_op: ReverseOperationPatterns, +) -> TwoDimensionalCells { + if rv_op.has_reverse90 { + cells = reverse_cells(cells, ReverseOperation::Reverse90); + + if rv_op.has_reverse_left_right { + cells = reverse_cells(cells, ReverseOperation::ReverseUpDown); + } + + if rv_op.has_reverse_up_down { + cells = reverse_cells(cells, ReverseOperation::ReverseLeftRight); + } + } else { + if rv_op.has_reverse_left_right { + cells = reverse_cells(cells, ReverseOperation::ReverseLeftRight); + } + + if rv_op.has_reverse_up_down { + cells = reverse_cells(cells, ReverseOperation::ReverseUpDown); + } + } + + cells +} + +pub(crate) fn multi_dereverse_cells( + mut cells: TwoDimensionalCells, + rv_op: ReverseOperationPatterns, +) -> TwoDimensionalCells { + if rv_op.has_reverse90 { + if rv_op.has_reverse_left_right { + cells = reverse_cells(cells, ReverseOperation::ReverseUpDown); + } + + if rv_op.has_reverse_up_down { + cells = reverse_cells(cells, ReverseOperation::ReverseLeftRight); + } + + cells = reverse_cells(cells, ReverseOperation::Reverse90); + } else { + if rv_op.has_reverse_left_right { + cells = reverse_cells(cells, ReverseOperation::ReverseLeftRight); + } + + if rv_op.has_reverse_up_down { + cells = reverse_cells(cells, ReverseOperation::ReverseUpDown); + } + } + + cells +} diff --git a/packages/algorithm/src/workers/v3.master.ts b/packages/algorithm/src/workers/v3.master.ts new file mode 100644 index 0000000..736d2d5 --- /dev/null +++ b/packages/algorithm/src/workers/v3.master.ts @@ -0,0 +1,47 @@ +import * as Comlink from "comlink"; +import type { FixedLengthArray } from "type-fest"; +import type { SolveFunc } from "../index"; +import { Barrier } from "../utils/concurrency/barrier"; +import { spawnWorker } from "../workers"; + +const TOTAL_WORKERS = 8; + +type Flags = FixedLengthArray, typeof TOTAL_WORKERS>; + +export const solve: SolveFunc = async (problem, onStartWorker, onWorkerFinish) => { + const barrier = new Barrier(TOTAL_WORKERS); + + onStartWorker?.(TOTAL_WORKERS); + + const flags = Array.from({ length: TOTAL_WORKERS }, () => [false, false, false]) as unknown as Flags; + + for (let i = 0; i < TOTAL_WORKERS; i++) { + for (let j = 0; j < 3; j++) { + if ((i >> j) & 1) { + flags[i][2 - j] = true; + } + } + } + + const workers = Array(TOTAL_WORKERS) + .fill(null) + .map(() => Comlink.wrap(spawnWorker("v2.worker.ts"))); + + const results = await Promise.all( + workers.map((worker, i) => + worker + .solve(problem, barrier, { + hasReverse90: flags[i][0], + hasReverseUpDown: flags[i][1], + hasReverseLeftRight: flags[i][2], + }) + .then((result) => { + onWorkerFinish?.(i, result[0]); + + return result; + }), + ), + ); + + return results.toSorted((a, b) => a[0].n - b[0].n)[0]; +}; diff --git a/packages/algorithm/src/workers/v3.worker.ts b/packages/algorithm/src/workers/v3.worker.ts new file mode 100644 index 0000000..9ed263b --- /dev/null +++ b/packages/algorithm/src/workers/v3.worker.ts @@ -0,0 +1,18 @@ +import type { Problem } from "@data-maki/schemas"; +import * as Comlink from "comlink"; +import type { ReverseOperationPatterns } from "../types"; +import { Barrier } from "../utils/concurrency/barrier"; +import { solve as solveFunc } from "../v3"; + +declare const self: Worker; + +export const solve = (problem: Problem, parentBarrier: Barrier, rvOp: ReverseOperationPatterns) => { + const barrier = Barrier.connect(parentBarrier); + + barrier.wait(); + + // @ts-expect-error + return solveFunc(problem, rvOp); +}; + +Comlink.expose({ solve }); diff --git a/packages/algorithm/tsup.worker.ts b/packages/algorithm/tsup.worker.ts index 937ed63..e80b58c 100644 --- a/packages/algorithm/tsup.worker.ts +++ b/packages/algorithm/tsup.worker.ts @@ -1,4 +1,5 @@ import { Glob } from "bun"; +import { wasmLoader } from "esbuild-plugin-wasm"; import { defineConfig } from "tsup"; const entry: string[] = []; @@ -17,4 +18,5 @@ export default defineConfig({ define: { "process.env.NODE_ENV": '"production"', }, + esbuildPlugins: [wasmLoader()], }); From 9c4c1df45eb262fc6638f2933df732e82cbd348c Mon Sep 17 00:00:00 2001 From: Mido Date: Sun, 20 Oct 2024 06:26:16 +0900 Subject: [PATCH 5/7] new mock configuration --- apps/frontend/src/app/routes/mock.tsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/app/routes/mock.tsx b/apps/frontend/src/app/routes/mock.tsx index 07b8584..d12a373 100644 --- a/apps/frontend/src/app/routes/mock.tsx +++ b/apps/frontend/src/app/routes/mock.tsx @@ -20,7 +20,14 @@ import { useState } from "react"; import { MockServerConnectionCard } from "../components/mock-server/MockServerConnectionCard"; // Code from apps/frontend/src/app/routes/mock.tsx -type GenerationKindStart = "all-random" | "gen-random" | "column-seq" | "column-seq-reverse" | "column-group-shuffle"; +type GenerationKindStart = + | "all-random" + | "gen-random" + | "column-seq" + | "column-seq-reverse" + | "column-group-shuffle" + | "random-area-reverse" + | "block-swap"; const generationKindStarts: GenerationKindStart[] = [ "all-random", @@ -28,6 +35,8 @@ const generationKindStarts: GenerationKindStart[] = [ "column-seq", "column-seq-reverse", "column-group-shuffle", + "random-area-reverse", + "block-swap", ] as const; type GenerationKindGoal = @@ -38,7 +47,9 @@ type GenerationKindGoal = | "column-seq" | "column-seq-reverse" | "partial-single-swap" - | "partial-block-swap"; + | "partial-block-swap" + | "random-area-reverse" + | "block-swap"; const generationKindGoals: GenerationKindGoal[] = [ "all-random", @@ -49,6 +60,8 @@ const generationKindGoals: GenerationKindGoal[] = [ "column-seq-reverse", "partial-single-swap", "partial-block-swap", + "random-area-reverse", + "block-swap", ] as const; export default function Page() { From 0d4a17ff5a1aafbf979ed79b77d583c402353019 Mon Sep 17 00:00:00 2001 From: Mido Date: Sun, 20 Oct 2024 06:49:39 +0900 Subject: [PATCH 6/7] canvas general viewing --- .../src/app/components/pattern/Pattern.tsx | 52 ++++++++++++++----- .../app/components/pattern/PatternList.tsx | 30 +++++------ 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/apps/frontend/src/app/components/pattern/Pattern.tsx b/apps/frontend/src/app/components/pattern/Pattern.tsx index 317bf32..db2c484 100644 --- a/apps/frontend/src/app/components/pattern/Pattern.tsx +++ b/apps/frontend/src/app/components/pattern/Pattern.tsx @@ -1,21 +1,45 @@ import type { Pattern as PatternSchema } from "@data-maki/schemas"; -import { Grid } from "@yamada-ui/react"; -import type { CSSProperties } from "react"; -import { BoardCell, isCell } from "../board/Cell"; +import { type CSSProperties, useEffect, useRef } from "react"; +import { isCell } from "../board/Cell"; type Props = Readonly<{ pattern: PatternSchema; + size?: number; style?: CSSProperties; }>; -export const Pattern = ({ pattern, style }: Props) => ( - - {pattern.cells.map((row, rowIndex) => - row.split("").map((cell, colIndex) => { - if (!isCell(cell)) throw new Error(`Invalid cell: ${cell}`); - - return ; - }), - )} - -); +export const Pattern = ({ pattern, size = 192, style }: Props) => { + const canvasRef = useRef(null); + + useEffect(() => { + if (!canvasRef.current) return; + + const canvas = canvasRef.current; + const ctx = canvas.getContext("2d"); + + if (!ctx) return; + + const { width, height, cells } = pattern; + + const cellHeight = size / height; + const cellWidth = size / width; + + for (let i = 0; i < height; i++) { + for (let j = 0; j < width; j++) { + const cell = cells[i][j]; + + if (isCell(cell)) { + if (cell === "1") { + ctx.fillStyle = "#000"; + } else { + ctx.fillStyle = "#fff"; + } + + ctx.fillRect(j * cellWidth, i * cellHeight, cellWidth, cellHeight); + } + } + } + }, [pattern, size]); + + return ; +}; diff --git a/apps/frontend/src/app/components/pattern/PatternList.tsx b/apps/frontend/src/app/components/pattern/PatternList.tsx index f8868af..375347c 100644 --- a/apps/frontend/src/app/components/pattern/PatternList.tsx +++ b/apps/frontend/src/app/components/pattern/PatternList.tsx @@ -1,16 +1,14 @@ import { type Pattern as PatternSchema, fixedPatterns } from "@data-maki/schemas"; import { useVirtualizer } from "@tanstack/react-virtual"; -import { Box, Card, CardBody, FormControl, HStack, Option, ScrollArea, Select } from "@yamada-ui/react"; +import { Box, Card, CardBody, FormControl, HStack, Option, ScrollArea, Select, Textarea } from "@yamada-ui/react"; import { Fragment, useRef, useState } from "react"; import { Pattern } from "./Pattern"; -type PatternType = "general" | "fixed"; - type Props = Readonly<{ patterns: PatternSchema[]; }>; -export const PatternListInner = ({ patterns }) => { +export const PatternListInner = ({ patterns }: Props) => { const scrollRef = useRef(null); const virtualizer = useVirtualizer({ @@ -18,13 +16,13 @@ export const PatternListInner = ({ patterns }) => { gap: 12, count: patterns.length, getScrollElement: () => scrollRef.current, - estimateSize: (i) => patterns[i].width * 32, + estimateSize: (i) => 192, paddingEnd: 12, overscan: 2, }); return ( - + {virtualizer.getVirtualItems().map((virtualItem) => { const pattern = patterns[virtualItem.index]; @@ -33,6 +31,7 @@ export const PatternListInner = ({ patterns }) => { { }; export const PatternList = ({ patterns }: Props) => { - const [patternType, setPatternType] = useState("general"); - return ( - - - - - - + <> + + - + +