diff --git a/build/darwin/icons.icns b/build/darwin/icons.icns index e69de29..b6e669a 100644 Binary files a/build/darwin/icons.icns and b/build/darwin/icons.icns differ diff --git a/build/windows/Taskfile.yml b/build/windows/Taskfile.yml index c9f96e0..d694f4f 100644 --- a/build/windows/Taskfile.yml +++ b/build/windows/Taskfile.yml @@ -39,7 +39,7 @@ tasks: summary: Generates Windows `.syso` file dir: build cmds: - - wails3 generate syso -arch {{.ARCH}} -icon windows/favicon_256x256.ico -manifest windows/wails.exe.manifest -info windows/info.json -out ../wails_windows_{{.ARCH}}.syso + - wails3 generate syso -arch {{.ARCH}} -icon windows/icon.ico -manifest windows/wails.exe.manifest -info windows/info.json -out ../wails_windows_{{.ARCH}}.syso vars: ARCH: '{{.ARCH | default ARCH}}' diff --git a/frontend/bindings/voidraft/internal/services/trayservice.ts b/frontend/bindings/voidraft/internal/services/trayservice.ts index 498add4..b05b8a9 100644 --- a/frontend/bindings/voidraft/internal/services/trayservice.ts +++ b/frontend/bindings/voidraft/internal/services/trayservice.ts @@ -10,6 +10,14 @@ // @ts-ignore: Unused imports import {Call as $Call, Create as $Create} from "@wailsio/runtime"; +/** + * AutoShowHide 自动显示/隐藏主窗口 + */ +export function AutoShowHide(): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(4044219428) as any; + return $resultPromise; +} + /** * HandleWindowClose 处理窗口关闭事件 */ diff --git a/frontend/package.json b/frontend/package.json index 868e783..626d879 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,10 +11,15 @@ "lint": "eslint", "lint:fix": "eslint --fix", "build:lang-parser": "node src/views/editor/extensions/codeblock/lang-parser/build-parser.js", + "build:mermaid-parser": "node src/views/editor/language/mermaid/build-parsers.js", "test": "vitest", "docs:dev": "vitepress dev docs", "docs:build": "vitepress build docs", - "docs:preview": "vitepress preview docs" + "docs:preview": "vitepress preview docs", + "app:dev": "cd .. &&wails3 dev", + "app:build": "cd .. && wails3 task build", + "app:package": "cd .. && wails3 package", + "app:generate": "cd .. && wails3 generate bindings -ts" }, "dependencies": { "@codemirror/autocomplete": "^6.19.1", diff --git a/frontend/src/views/editor/extensions/codeblock/lang-parser/codeblock.grammar b/frontend/src/views/editor/extensions/codeblock/lang-parser/codeblock.grammar index 64ed357..fd0a48b 100644 --- a/frontend/src/views/editor/extensions/codeblock/lang-parser/codeblock.grammar +++ b/frontend/src/views/editor/extensions/codeblock/lang-parser/codeblock.grammar @@ -18,7 +18,7 @@ BlockLanguage { "go" | "clj" | "ex" | "erl" | "js" | "ts" | "swift" | "kt" | "groovy" | "ps1" | "dart" | "scala" | "math" | "dockerfile" | "lua" | "vue" | "lezer" | "liquid" | "wast" | "sass" | "less" | "angular" | "svelte" | - "http" + "http" | "mermaid" } @tokens { diff --git a/frontend/src/views/editor/extensions/codeblock/lang-parser/languages.ts b/frontend/src/views/editor/extensions/codeblock/lang-parser/languages.ts index ba8b72f..f63fc01 100644 --- a/frontend/src/views/editor/extensions/codeblock/lang-parser/languages.ts +++ b/frontend/src/views/editor/extensions/codeblock/lang-parser/languages.ts @@ -24,7 +24,7 @@ import {lessLanguage} from "@codemirror/lang-less"; import {angularLanguage} from "@codemirror/lang-angular"; import { svelteLanguage } from "@replit/codemirror-lang-svelte"; import { httpLanguage } from "@/views/editor/extensions/httpclient/language/http-language"; - +import { mermaidLanguage } from '@/views/editor/language/mermaid'; import {StreamLanguage} from "@codemirror/language"; import {ruby} from "@codemirror/legacy-modes/mode/ruby"; import {shell} from "@codemirror/legacy-modes/mode/shell"; @@ -226,6 +226,7 @@ export const LANGUAGES: LanguageInfo[] = [ } }), new LanguageInfo("http", "Http", httpLanguage.parser, ["http"]), + new LanguageInfo("mermaid", "Mermaid", mermaidLanguage.parser, ["mermaid"]), ]; diff --git a/frontend/src/views/editor/extensions/codeblock/lang-parser/parser.ts b/frontend/src/views/editor/extensions/codeblock/lang-parser/parser.ts index 326be17..8e405c7 100644 --- a/frontend/src/views/editor/extensions/codeblock/lang-parser/parser.ts +++ b/frontend/src/views/editor/extensions/codeblock/lang-parser/parser.ts @@ -3,14 +3,14 @@ import {LRParser} from "@lezer/lr" import {blockContent} from "./external-tokens.js" export const parser = LRParser.deserialize({ version: 14, - states: "!jQQOQOOOVOQO'#C`O#xOPO'#C_OOOO'#Cc'#CcQQOQOOOOOO'#Ca'#CaO#}OSO,58zOOOO,58y,58yOOOO-E6a-E6aOOOP1G.f1G.fO$VOSO1G.fOOOP7+$Q7+$Q", - stateData: "$[~OXPO~OYTOZTO[TO]TO^TO_TO`TOaTObTOcTOdTOeTOfTOgTOhTOiTOjTOkTOlTOmTOnTOoTOpTOqTOrTOsTOtTOuTOvTOwTOxTOyTOzTO{TO|TO}TO!OTO!PTO!QTO!RTO!STO~OPVO~OUYO!TXO~O!TZO~O", + states: "!jQQOQOOOVOQO'#C`O#{OPO'#C_OOOO'#Cc'#CcQQOQOOOOOO'#Ca'#CaO$QOSO,58zOOOO,58y,58yOOOO-E6a-E6aOOOP1G.f1G.fO$YOSO1G.fOOOP7+$Q7+$Q", + stateData: "$_~OXPO~OYTOZTO[TO]TO^TO_TO`TOaTObTOcTOdTOeTOfTOgTOhTOiTOjTOkTOlTOmTOnTOoTOpTOqTOrTOsTOtTOuTOvTOwTOxTOyTOzTO{TO|TO}TO!OTO!PTO!QTO!RTO!STO!TTO~OPVO~OUYO!UXO~O!UZO~O", goto: "jWPPPX]aPdTROSTQOSRUPQSORWS", nodeNames: "⚠ BlockContent Document Block BlockDelimiter BlockLanguage Auto", - maxTerm: 51, + maxTerm: 52, skippedNodes: [0], repeatNodeCount: 1, - tokenData: "3u~RdYZ!a}!O!z#T#U#V#V#W$Q#W#X%R#X#Y&t#Z#['_#[#]([#^#_)R#_#`*Q#`#a*]#a#b,Y#d#e,y#f#g-r#g#h.V#h#i0|#j#k2R#k#l2d#l#m2{#m#n3^R!fP!TQ%&x%&y!iP!lP%&x%&y!oP!rP%&x%&y!uP!zOXP~!}P#T#U#Q~#VOU~~#YP#b#c#]~#`P#Z#[#c~#fP#i#j#i~#lP#`#a#o~#rP#T#U#u~#xP#f#g#{~$QO!Q~~$TR#`#a$^#d#e$i#g#h$t~$aP#^#_$d~$iOl~~$lP#d#e$o~$tOd~~$yPf~#g#h$|~%ROb~~%UQ#T#U%[#c#d%m~%_P#f#g%b~%eP#h#i%h~%mOu~~%pP#V#W%s~%vP#_#`%y~%|P#X#Y&P~&SP#f#g&V~&YP#Y#Z&]~&`P#]#^&c~&fP#`#a&i~&lP#X#Y&o~&tOx~~&wQ#f#g&}#l#m'Y~'QP#`#a'T~'YOn~~'_Om~~'bQ#c#d'h#f#g'm~'mOk~~'pP#c#d's~'vP#c#d'y~'|P#j#k(P~(SP#m#n(V~([Os~~(_P#h#i(b~(eQ#a#b(k#h#i(v~(nP#`#a(q~(vO]~~(yP#d#e(|~)RO!S~~)UQ#T#U)[#g#h)m~)_P#j#k)b~)eP#T#U)h~)mO`~~)rPo~#c#d)u~)xP#b#c){~*QOZ~~*TP#h#i*W~*]Or~~*`R#X#Y*i#]#^+`#i#j+}~*lQ#g#h*r#n#o*}~*uP#g#h*x~*}O!P~~+QP#X#Y+T~+WP#f#g+Z~+`O{~~+cP#e#f+f~+iP#i#j+l~+oP#]#^+r~+uP#W#X+x~+}O|~~,QP#T#U,T~,YOy~~,]Q#T#U,c#W#X,t~,fP#h#i,i~,lP#[#],o~,tOw~~,yO_~~,|R#[#]-V#g#h-b#m#n-m~-YP#d#e-]~-bOa~~-eP!R!S-h~-mOt~~-rO[~~-uQ#U#V-{#g#h.Q~.QOg~~.VOe~~.YU#T#U.l#V#W.}#[#]/f#e#f/k#j#k/v#k#l0e~.oP#g#h.r~.uP#g#h.x~.}O!O~~/QP#T#U/T~/WP#`#a/Z~/^P#T#U/a~/fOv~~/kOh~~/nP#`#a/q~/vO^~~/yP#X#Y/|~0PP#`#a0S~0VP#h#i0Y~0]P#X#Y0`~0eO!R~~0hP#]#^0k~0nP#Y#Z0q~0tP#h#i0w~0|Oq~~1PR#X#Y1Y#c#d1k#g#h1|~1]P#l#m1`~1cP#h#i1f~1kOY~~1nP#a#b1q~1tP#`#a1w~1|Oj~~2ROp~~2UP#i#j2X~2[P#X#Y2_~2dOz~~2gP#T#U2j~2mP#g#h2p~2sP#h#i2v~2{O}~~3OP#a#b3R~3UP#`#a3X~3^Oc~~3aP#T#U3d~3gP#a#b3j~3mP#`#a3p~3uOi~", + tokenData: "4m~RdYZ!a}!O!z#T#U#V#V#W$Q#W#X%R#X#Y&t#Z#['_#[#]([#^#_)R#_#`*Q#`#a*]#a#b,Y#d#e-q#f#g.j#g#h.}#h#i1t#j#k2y#k#l3[#l#m3s#m#n4UR!fP!UQ%&x%&y!iP!lP%&x%&y!oP!rP%&x%&y!uP!zOXP~!}P#T#U#Q~#VOU~~#YP#b#c#]~#`P#Z#[#c~#fP#i#j#i~#lP#`#a#o~#rP#T#U#u~#xP#f#g#{~$QO!Q~~$TR#`#a$^#d#e$i#g#h$t~$aP#^#_$d~$iOl~~$lP#d#e$o~$tOd~~$yPf~#g#h$|~%ROb~~%UQ#T#U%[#c#d%m~%_P#f#g%b~%eP#h#i%h~%mOu~~%pP#V#W%s~%vP#_#`%y~%|P#X#Y&P~&SP#f#g&V~&YP#Y#Z&]~&`P#]#^&c~&fP#`#a&i~&lP#X#Y&o~&tOx~~&wQ#f#g&}#l#m'Y~'QP#`#a'T~'YOn~~'_Om~~'bQ#c#d'h#f#g'm~'mOk~~'pP#c#d's~'vP#c#d'y~'|P#j#k(P~(SP#m#n(V~([Os~~(_P#h#i(b~(eQ#a#b(k#h#i(v~(nP#`#a(q~(vO]~~(yP#d#e(|~)RO!S~~)UQ#T#U)[#g#h)m~)_P#j#k)b~)eP#T#U)h~)mO`~~)rPo~#c#d)u~)xP#b#c){~*QOZ~~*TP#h#i*W~*]Or~~*`R#X#Y*i#]#^+`#i#j+}~*lQ#g#h*r#n#o*}~*uP#g#h*x~*}O!P~~+QP#X#Y+T~+WP#f#g+Z~+`O{~~+cP#e#f+f~+iP#i#j+l~+oP#]#^+r~+uP#W#X+x~+}O|~~,QP#T#U,T~,YOy~~,]R#T#U,f#W#X,w#X#Y,|~,iP#h#i,l~,oP#[#],r~,wOw~~,|O_~~-PP#f#g-S~-VP#a#b-Y~-]P#T#U-`~-cP#]#^-f~-iP#W#X-l~-qO!T~~-tR#[#]-}#g#h.Y#m#n.e~.QP#d#e.T~.YOa~~.]P!R!S.`~.eOt~~.jO[~~.mQ#U#V.s#g#h.x~.xOg~~.}Oe~~/QU#T#U/d#V#W/u#[#]0^#e#f0c#j#k0n#k#l1]~/gP#g#h/j~/mP#g#h/p~/uO!O~~/xP#T#U/{~0OP#`#a0R~0UP#T#U0X~0^Ov~~0cOh~~0fP#`#a0i~0nO^~~0qP#X#Y0t~0wP#`#a0z~0}P#h#i1Q~1TP#X#Y1W~1]O!R~~1`P#]#^1c~1fP#Y#Z1i~1lP#h#i1o~1tOq~~1wR#X#Y2Q#c#d2c#g#h2t~2TP#l#m2W~2ZP#h#i2^~2cOY~~2fP#a#b2i~2lP#`#a2o~2tOj~~2yOp~~2|P#i#j3P~3SP#X#Y3V~3[Oz~~3_P#T#U3b~3eP#g#h3h~3kP#h#i3n~3sO}~~3vP#a#b3y~3|P#`#a4P~4UOc~~4XP#T#U4[~4_P#a#b4b~4eP#`#a4h~4mOi~", tokenizers: [blockContent, 0, 1], topRules: {"Document":[0,2]}, tokenPrec: 0 diff --git a/frontend/src/views/editor/extensions/codeblock/types.ts b/frontend/src/views/editor/extensions/codeblock/types.ts index 562442f..47c4989 100644 --- a/frontend/src/views/editor/extensions/codeblock/types.ts +++ b/frontend/src/views/editor/extensions/codeblock/types.ts @@ -66,6 +66,7 @@ export type SupportedLanguage = | 'angular' | 'svelte' | 'http' // HTTP Client + | 'mermaid' /** * 创建块的选项 @@ -85,7 +86,6 @@ export interface EditorOptions { } - // 分隔符格式常量 export const DELIMITER_REGEX = /^\n∞∞∞([a-zA-Z0-9_-]+)(-a)?\n/gm; export const DELIMITER_PREFIX = '\n∞∞∞'; diff --git a/frontend/src/views/editor/language/mermaid/build-parsers.js b/frontend/src/views/editor/language/mermaid/build-parsers.js new file mode 100644 index 0000000..b44a860 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/build-parsers.js @@ -0,0 +1,57 @@ +import { buildParserFile } from '@lezer/generator'; +import { fileURLToPath } from 'url'; +import { dirname, join } from 'path'; +import { readFileSync, writeFileSync } from 'fs'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const parsersDir = join(__dirname, 'parsers'); + +const parserTypes = [ + 'mermaid', + 'mindmap', + 'pie', + 'flowchart', + 'sequence', + 'journey', + 'requirement', + 'gantt' +]; + +console.log('开始构建 Mermaid 语法解析器...\n'); + +for (const type of parserTypes) { + try { + const grammarPath = join(parsersDir, type, `${type}.grammar`); + const outputPath = join(parsersDir, type, `${type}.parser.grammar.ts`); + + console.log(`正在处理: ${type}`); + console.log(` 读取: ${grammarPath}`); + + const grammar = readFileSync(grammarPath, 'utf-8'); + const result = buildParserFile(grammar, { + fileName: `${type}.grammar`, + typeScript: true, + warn: (message) => console.warn(` 警告: ${message}`) + }); + + writeFileSync(outputPath, result.parser); + console.log(` ✓ 生成: ${outputPath}`); + + // 生成 terms 文件 + if (result.terms) { + const termsPath = join(parsersDir, type, `${type}.grammar.terms.ts`); + writeFileSync(termsPath, result.terms); + console.log(` ✓ 生成: ${termsPath}`); + } + + console.log(''); + } catch (error) { + console.error(` ✗ 错误: ${type} - ${error.message}\n`); + process.exit(1); + } +} + +console.log('✓ 所有解析器构建完成!'); + diff --git a/frontend/src/views/editor/language/mermaid/extensions/index.ts b/frontend/src/views/editor/language/mermaid/extensions/index.ts new file mode 100644 index 0000000..86f646a --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/extensions/index.ts @@ -0,0 +1,62 @@ +import { foldService } from '@codemirror/language'; + +const countLeadingSpaces = (str: string) => { + let count = 0; + + for (let i = 0; i < str.length; i++) { + if (str[i] === ' ') { + count++; + } else if (str[i] === '\t') { + count += 4; + } else { + break; + } + } + + return count; +}; + +const isEmptyLine = (text: string) => { + return /^[ \t]*$/.test(text); +}; + +export const foldByIndent = () => { + return foldService.of((state, lineStart, lineEnd) => { + const line = state.doc.lineAt(lineStart); + const lineCount = state.doc.lines; + + let indents = countLeadingSpaces(line.text); + let foldStart = lineStart; + let foldEnd = lineEnd; + let nextLine = line; + + while (nextLine.number < lineCount) { + nextLine = state.doc.line(nextLine.number + 1); + + if (nextLine.text === '' || isEmptyLine(nextLine.text)) continue; + + let nextIndents = countLeadingSpaces(nextLine.text); + + if (nextIndents > indents && !isEmptyLine(nextLine.text)) { + foldEnd = nextLine.to; + } else { + break; + } + } + + if ( + state.doc.lineAt(foldStart).number === state.doc.lineAt(foldEnd).number + ) { + return null; + } + + foldStart = line.to; + const lineAtFoldStart = state.doc.lineAt(foldStart); + + if (lineAtFoldStart.text === '' || isEmptyLine(lineAtFoldStart.text)) { + return null; + } + + return { from: foldStart, to: foldEnd }; + }); +}; diff --git a/frontend/src/views/editor/language/mermaid/index.ts b/frontend/src/views/editor/language/mermaid/index.ts new file mode 100644 index 0000000..648788e --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/index.ts @@ -0,0 +1,45 @@ +export { + mermaidLanguage, + mindmapLanguage, + pieLanguage, + flowchartLanguage, + sequenceLanguage, + journeyLanguage, + requirementLanguage, + ganttLanguage, +} from './language-definitions'; + +export { + mermaidLanguageDescription, + mindmapLanguageDescription, + pieLanguageDescription, + flowchartLanguageDescription, + sequenceLanguageDescription, + journeyLanguageDescription, + requirementLanguageDescription, + ganttLanguageDescription, +} from './language-descriptions'; + +export { + mermaid, + mindmap, + pie, + flowchart, + sequence, + journey, + requirement, + gantt, +} from './language-support'; + +export { + mermaidTags, + mindmapTags, + pieTags, + flowchartTags, + sequenceTags, + journeyTags, + requirementTags, + ganttTags, +} from './tags'; + +export { foldByIndent } from './extensions'; diff --git a/frontend/src/views/editor/language/mermaid/language-definitions/index.ts b/frontend/src/views/editor/language/mermaid/language-definitions/index.ts new file mode 100644 index 0000000..59ce47f --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/language-definitions/index.ts @@ -0,0 +1,74 @@ +import { LRLanguage } from '@codemirror/language'; +import { parseMixed } from '@lezer/common'; +import { + mermaidParser, + mindmapParser, + pieParser, + flowchartParser, + sequenceParser, + journeyParser, + requirementParser, + ganttParser, +} from '../parsers'; +import { DiagramType, MermaidLanguageType } from '../types'; + +export const mermaidLanguage = LRLanguage.define({ + name: MermaidLanguageType.Mermaid, + parser: mermaidParser.configure({ + wrap: parseMixed((node) => { + switch (node.name) { + case DiagramType.Mindmap: + return { parser: mindmapParser }; + case DiagramType.Pie: + return { parser: pieParser }; + case DiagramType.Flowchart: + return { parser: flowchartParser }; + case DiagramType.Sequence: + return { parser: sequenceParser }; + case DiagramType.Journey: + return { parser: journeyParser }; + case DiagramType.Requirement: + return { parser: requirementParser }; + case DiagramType.Gantt: + return { parser: ganttParser }; + default: + return null; + } + }), + }), +}); + +export const mindmapLanguage = LRLanguage.define({ + name: MermaidLanguageType.Mindmap, + parser: mindmapParser, +}); + +export const pieLanguage = LRLanguage.define({ + name: MermaidLanguageType.Pie, + parser: pieParser, +}); + +export const flowchartLanguage = LRLanguage.define({ + name: MermaidLanguageType.Flowchart, + parser: flowchartParser, +}); + +export const sequenceLanguage = LRLanguage.define({ + name: MermaidLanguageType.Sequence, + parser: sequenceParser, +}); + +export const journeyLanguage = LRLanguage.define({ + name: MermaidLanguageType.Journey, + parser: journeyParser, +}); + +export const requirementLanguage = LRLanguage.define({ + name: MermaidLanguageType.Requirement, + parser: requirementParser, +}); + +export const ganttLanguage = LRLanguage.define({ + name: MermaidLanguageType.Gantt, + parser: ganttParser, +}); diff --git a/frontend/src/views/editor/language/mermaid/language-descriptions/index.ts b/frontend/src/views/editor/language/mermaid/language-descriptions/index.ts new file mode 100644 index 0000000..a37357b --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/language-descriptions/index.ts @@ -0,0 +1,71 @@ +import { LanguageDescription } from '@codemirror/language'; +import { + mermaid, + mindmap, + pie, + flowchart, + sequence, + journey, + requirement, + gantt, +} from '../language-support'; +import { MermaidDescriptionName, MermaidAlias } from '../types'; + +export const mermaidLanguageDescription = LanguageDescription.of({ + name: MermaidDescriptionName.Mermaid, + load: async () => { + return mermaid(); + }, +}); + +export const mindmapLanguageDescription = LanguageDescription.of({ + name: MermaidDescriptionName.Mindmap, + load: async () => { + return mindmap(); + }, +}); + +export const pieLanguageDescription = LanguageDescription.of({ + name: MermaidDescriptionName.Pie, + load: async () => { + return pie(); + }, +}); + +export const flowchartLanguageDescription = LanguageDescription.of({ + name: MermaidDescriptionName.Flowchart, + alias: [MermaidAlias.Graph], + load: async () => { + return flowchart(); + }, +}); + +export const sequenceLanguageDescription = LanguageDescription.of({ + name: MermaidDescriptionName.Sequence, + alias: [MermaidAlias.Sequence], + load: async () => { + return sequence(); + }, +}); + +export const journeyLanguageDescription = LanguageDescription.of({ + name: MermaidDescriptionName.Journey, + load: async () => { + return journey(); + }, +}); + +export const requirementLanguageDescription = LanguageDescription.of({ + name: MermaidDescriptionName.Requirement, + alias: [MermaidAlias.Requirement], + load: async () => { + return requirement(); + }, +}); + +export const ganttLanguageDescription = LanguageDescription.of({ + name: MermaidDescriptionName.Gantt, + load: async () => { + return gantt(); + }, +}); diff --git a/frontend/src/views/editor/language/mermaid/language-support/index.ts b/frontend/src/views/editor/language/mermaid/language-support/index.ts new file mode 100644 index 0000000..ac0eb28 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/language-support/index.ts @@ -0,0 +1,43 @@ +import { LanguageSupport } from '@codemirror/language'; +import { + mermaidLanguage, + mindmapLanguage, + pieLanguage, + flowchartLanguage, + sequenceLanguage, + journeyLanguage, + requirementLanguage, + ganttLanguage, +} from '../language-definitions'; + +export function mermaid() { + return new LanguageSupport(mermaidLanguage); +} + +export function mindmap() { + return new LanguageSupport(mindmapLanguage); +} + +export function pie() { + return new LanguageSupport(pieLanguage); +} + +export function flowchart() { + return new LanguageSupport(flowchartLanguage); +} + +export function sequence() { + return new LanguageSupport(sequenceLanguage); +} + +export function journey() { + return new LanguageSupport(journeyLanguage); +} + +export function requirement() { + return new LanguageSupport(requirementLanguage); +} + +export function gantt() { + return new LanguageSupport(ganttLanguage); +} diff --git a/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.grammar b/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.grammar new file mode 100644 index 0000000..b2a6ca1 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.grammar @@ -0,0 +1,170 @@ +@top FlowchartDiagram { document+ } + +@skip { spaces | LineComment } + +@skip {} { + String { + singleQuote (stringContentSingle)* (singleQuote) | + doubleQuote (stringContentDouble)* (doubleQuote) | + backTick (stringContentBackTick)* (backTick) + } +} + +document { + ( + DiagramName | + DiagramName Orientation | + DiagramName Orientation newlines* subDocument (newlines* subDocument semicolon?)* + ) newlines* +} + + +subDocument { + NodeId | + Node | + Link | + NodeEdge | + ampersand | + Keyword | + emptyParentheses | + colon | + tripleColon | + String | + StyleKeyword NodeId StyleText +} + +NodeId { + identifier | Orientation +} + +text { + NodeText | String +} + +edgeText { + NodeEdgeText | String +} + +emptyParentheses { "()" } + +Node { + "(" text ")" | + "[" text "]" | + "|" text "|" | + "([" text "])" | + "[(" text "])" | + "[[" text "]]" | + "[(" text ")]" | + "((" text "))" | + ">" text "]" | + "{" text "}" | + "{{" text "}}" | + "(((" text ")))" +} + +NodeEdge { + (DoubleHyphen | DoubleEqual) edgeText Link +} + +Link { + linkType1 | + linkType2 | + linkType3 | + linkType4 | + linkType5 | + linkType6 +} + +DiagramName { + kw<"flowchart"> | + kw<"graph"> +} + +Orientation { + kw<"TB"> | + kw<"TD"> | + kw<"BT"> | + kw<"RL"> | + kw<"LR"> +} + +StyleKeyword { + kw<"style"> | + kw<"linkStyle"> | + kw<"class"> | + kw<"classDef"> +} + +Keyword { + kw<"subgraph"> | + kw<"end"> | + kw<"direction"> | + kw<"click"> | + kw<"call"> | + kw<"href"> | + kw<"_self"> | + kw<"_blank"> | + kw<"_parent"> | + kw<"_to"> +} + +kw { @specialize } + +@external tokens nodeEdgeText from "./tokens" { NodeEdgeText } +@external tokens nodeText from "./tokens" { NodeText } +@external tokens styleText from "./tokens" { StyleText } + +/* +Single character tokens will need to go inside the @tokens rule to specify precedence over "char". +Longer tokens always beat shorter tokens, which is why "flowchart" takes priority over multiple "char" tokens. +The @specialize rule also helps makes "DiagramName" have higher priority for "flowchart" even though it overlaps with "char" and "identifier" tokens +*/ +@tokens { + char { @asciiLetter | @digit | $[!"\#$%&'*+\.`?\\_\/\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC] } + identifier { char+ } + newlines { $[\n]+ } + spaces { @whitespace+ } + stringContentSingle { ![']+ } + stringContentDouble { !["]+ } + stringContentBackTick { ![`]+ } + LineComment { "%%" ![\n]* } + DoubleHyphen { "--" } + DoubleEqual { "==" } + linkType1 { ("<-" | "x-" | "o-") ("->" | "-x" | "-o")} + linkType2 { ("<=" | "x=" | "o=") ("=>" | "=x" | "=o")} + linkType3 { ("<-" | "x-" | "o-")? "-"+ ("->" | "-x" | "-o")?} + linkType4 { ("<=" | "x=" | "o=")? "="+ ("=>" | "=x" | "=o")?} + linkType5 { ("<-" | "x-" | "o-" | "-")? "."+ ("->" | "-x" | "-o" | "-")?} + linkType6 { "~~~" | "---" | "===" } + ampersand { "&" } + semicolon { ";" } + singleQuote { "'" } + doubleQuote { '"' } + backTick { "`" } + tripleColon[@name=":::"] { ":::" } + colon[@name=":"] { ":" } + + @precedence { + newlines, + spaces, + LineComment, + linkType1, + linkType2, + linkType3, + linkType4, + linkType5, + linkType6, + DoubleHyphen, + DoubleEqual, + ampersand, + semicolon, + singleQuote, + doubleQuote, + backTick, + tripleColon, + colon, + identifier + } +} + +@external propSource flowchartHighlighting from "./highlight" diff --git a/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.grammar.terms.d.ts b/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.grammar.terms.d.ts new file mode 100644 index 0000000..602751e --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.grammar.terms.d.ts @@ -0,0 +1,3 @@ +export declare const NodeText: number; +export declare const NodeEdgeText: number; +export declare const StyleText: number; diff --git a/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.grammar.terms.ts b/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.grammar.terms.ts new file mode 100644 index 0000000..0725867 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.grammar.terms.ts @@ -0,0 +1,20 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +export const + NodeEdgeText = 1, + NodeText = 2, + StyleText = 3, + LineComment = 4, + FlowchartDiagram = 5, + DiagramName = 6, + Orientation = 7, + NodeId = 8, + Node = 9, + String = 10, + Link = 11, + NodeEdge = 12, + DoubleHyphen = 13, + DoubleEqual = 14, + Keyword = 15, + colon = 16, + tripleColon = 17, + StyleKeyword = 18 diff --git a/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.grammar.test.ts b/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.grammar.test.ts new file mode 100644 index 0000000..1e4f306 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.grammar.test.ts @@ -0,0 +1,217 @@ +import { describe, it, expect } from 'vitest'; +import { parser } from './flowchart.parser.grammar'; + +/** + * Flowchart Grammar 测试 + * + * 测试目标:验证标准的 Mermaid Flowchart 语法是否能正确解析,不应该出现错误节点(⚠) + */ +describe('Flowchart Grammar 解析测试', () => { + + /** + * 辅助函数:解析代码并返回语法树 + */ + function parseCode(code: string) { + const tree = parser.parse(code); + return tree; + } + + /** + * 辅助函数:检查语法树中是否有错误节点 + */ + function hasErrorNodes(tree: any): { hasError: boolean; errors: Array<{ name: string; from: number; to: number; text: string }> } { + const errors: Array<{ name: string; from: number; to: number; text: string }> = []; + + tree.iterate({ + enter: (node: any) => { + if (node.name === '⚠') { + errors.push({ + name: node.name, + from: node.from, + to: node.to, + text: tree.toString().substring(node.from, node.to) + }); + } + } + }); + + return { + hasError: errors.length > 0, + errors + }; + } + + /** + * 辅助函数:打印语法树结构(用于调试) + */ + function printTree(tree: any, code: string, maxDepth = 5) { + const lines: string[] = []; + + tree.iterate({ + enter: (node: any) => { + const depth = getNodeDepth(tree, node); + if (depth > maxDepth) return false; // 限制深度 + + const indent = ' '.repeat(depth); + const text = code.substring(node.from, Math.min(node.to, node.from + 30)); + const displayText = text.length === 30 ? text + '...' : text; + + lines.push(`${indent}${node.name} [${node.from}-${node.to}]: "${displayText.replace(/\n/g, '\\n')}"`); + } + }); + + return lines.join('\n'); + } + + /** + * 获取节点深度 + */ + function getNodeDepth(tree: any, targetNode: any): number { + let depth = 0; + let current = targetNode; + while (current.parent) { + depth++; + current = current.parent; + } + return depth; + } + + it('应该正确解析基础的 flowchart 声明(TB 方向)', () => { + const code = `flowchart TB + A +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析基础的 graph 声明(LR 方向)', () => { + const code = `graph LR + A +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带方框节点的流程图', () => { + const code = `flowchart TD + A[Christmas] +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带箭头连接的节点', () => { + const code = `flowchart TD + A --> B +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带标签的连接线', () => { + const code = `flowchart TD + A -- text --> B +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析不同形状的节点(圆形)', () => { + const code = `flowchart TD + A((Circle)) +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析不同形状的节点(菱形)', () => { + const code = `flowchart TD + A{Diamond} +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析完整的流程图示例', () => { + const code = `flowchart TD + A[Start] --> B{Is it?} + B -->|Yes| C[OK] + B -->|No| D[End] +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); +}); + diff --git a/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.parser.grammar.d.ts b/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.parser.grammar.d.ts new file mode 100644 index 0000000..1d78640 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.parser.grammar.d.ts @@ -0,0 +1,3 @@ +import { LRParser } from '@lezer/lr'; + +export declare const parser: LRParser; diff --git a/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.parser.grammar.ts b/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.parser.grammar.ts new file mode 100644 index 0000000..44835d9 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/flowchart/flowchart.parser.grammar.ts @@ -0,0 +1,21 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +import {LRParser} from "@lezer/lr" +import {nodeEdgeText, nodeText, styleText} from "./tokens" +import {flowchartHighlighting} from "./highlight" +const spec_identifier = {__proto__:null,flowchart:60, graph:62, TB:64, TD:66, BT:68, RL:70, LR:72, subgraph:148, end:150, direction:152, click:154, call:156, href:158, _self:160, _blank:162, _parent:164, _to:166, style:170, linkStyle:172, class:174, classDef:176} +export const parser = LRParser.deserialize({ + version: 14, + states: "*hOYQWOOOvQWO'#CxOOQO'#Co'#CoQYQWOOOOQO'#Cb'#CbOOQO'#Cp'#CpO!TQWO,59dO!cQWO,59dOOQS'#Cc'#CcOOQO-E6m-E6mOOQO-E6n-E6nO%aQWO1G/OOOQS'#Cd'#CdO(_O`O'#CfO(gOpO'#CfO(oO!bO'#CfO(wQYO'#CeO(wQYO'#CeO(wQYO'#CeO(wQYO'#CeO(wQYO'#CeO(wQYO'#CeO(wQYO'#CeO(wQYO'#CeO(wQYO'#CeO(wQYO'#CeOOQO'#Cg'#CgO)VQXO'#ChOOQO'#DS'#DSO)eQWO'#DSO%aQWO1G/OOOQO'#Ck'#CkOOQO'#Cn'#CnO)yQWO7+$jOOOO'#Cq'#CqO,wO`O,59QOOQO,59Q,59QOOOO'#Cr'#CrO-POpO,59QOOOO'#Cs'#CsO-XO!bO,59QOOQO'#DU'#DUO-aQWO,59PO-fQWO,59PO-kQWO,59PO-pQ#tO,59PO-uQ#tO,59PO-}Q#tO,59PO.SQ#tO,59PO.XQWO,59PO.^Q#tO,59PO.cQ&jO,59POOQO'#Dv'#DvO.hQWO,59SO.|Q[O,59nO)yQWO7+$jO/RQWO'#CtO)yQWO7+$jO2dQWO<{~OkOSSOS~OnSOoSO~OpWOqWOrWOsWOtWOuTO~OilXnlXolX~PbOuTOilanlaola~O]kO^kO`lOalOm[Ow`Oy]O{^O}_O!QaO!SbO!TcO!VdO!WeO!ZfO!]aO!^gO!`hO!biO!djO!ejO!fjO!gjO!hjO!ijO!klO!loO!moO!noO!ooO!poO!qoO!roO!soO!toO!uoO!vlO!wpO!xpO!ypO!zpOilanlaola~PbO]kO^kO`lOalOm[Ow`Oy]O{^O}_O!QaO!SbO!TcO!VdO!WeO!ZfO!]aO!^gO!`hO!biO!djO!ejO!fjO!gjO!hjO!ijO!klO!loO!moO!noO!ooO!poO!qoO!roO!soO!toO!uoO!vlO!wpO!xpO!ypO!zpOilinlioli~PbOytOzrO~O{tO|uO~O}tO!OwO~OQyOy]O{^O}_O~OP!UOy]O{^O}_O~Om[OpWOqWOrWOsWOtWO~O]kO^kO`lOalOm[Ow`Oy]O{^O}_O!QaO!SbO!TcO!VdO!WeO!ZfO!]aO!^gO!`hO!biO!djO!ejO!fjO!gjO!hjO!ijO!klO!loO!moO!noO!ooO!poO!qoO!roO!soO!toO!uoO!vlO!wpO!xpO!ypO!zpOilqnlqolq~PbOy!_OzrO~O{!_O|uO~O}!_O!OwO~O!P!bO~O!R!bO~O!S!bO~O!U!bO~O!U!bO!Y!bO~O!X!bO~O![!bO~O!_!bO~O!a!bO~O!c!bO~O!djO!ejO!fjO!gjO!hjO!ijO~OR!dO~O!{!fO]hX^hX`hXahXihXmhXnhXohXphXqhXrhXshXthXuhXwhXyhX{hX}hX!QhX!ShX!ThX!VhX!WhX!ZhX!]hX!^hX!`hX!bhX!dhX!ehX!fhX!ghX!hhX!ihX!khX!lhX!mhX!nhX!ohX!phX!qhX!rhX!shX!thX!uhX!vhX!whX!xhX!yhX!zhX~O]kO^kO`lOalOm[Ow`Oy]O{^O}_O!QaO!SbO!TcO!VdO!WeO!ZfO!]aO!^gO!`hO!biO!djO!ejO!fjO!gjO!hjO!ijO!klO!loO!moO!noO!ooO!poO!qoO!roO!soO!toO!uoO!vlO!wpO!xpO!ypO!zpOilynlyoly~PbO!{!iO]ha^ha`haahaihamhanhaohaphaqharhashathauhawhayha{ha}ha!Qha!Sha!Tha!Vha!Wha!Zha!]ha!^ha!`ha!bha!dha!eha!fha!gha!hha!iha!kha!lha!mha!nha!oha!pha!qha!rha!sha!tha!uha!vha!wha!xha!yha!zha~O]kO^kO`lOalOm[Ow`Oy]O{^O}_O!QaO!SbO!TcO!VdO!WeO!ZfO!]aO!^gO!`hO!biO!djO!ejO!fjO!gjO!hjO!ijO!klO!loO!moO!noO!ooO!poO!qoO!roO!soO!toO!uoO!vlO!wpO!xpO!ypO!zpOil!Rnl!Rol!R~PbO]kO^kO`lOalOm[Ow`Oy]O{^O}_O!QaO!SbO!TcO!VdO!WeO!ZfO!]aO!^gO!`hO!biO!djO!ejO!fjO!gjO!hjO!ijO!klO!loO!moO!noO!ooO!poO!qoO!roO!soO!toO!uoO!vlO!wpO!xpO!ypO!zpO~PbOukS!d!e!f!g!h!i]^!k!{y{}a`m]~", + goto: "'[!kPPPPPP!l!p#Q#a#m$Y#aPP#aPP$i$u${%e%k%q%wPPP&RPPPPPPPPP&VP&iPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP'XTPORQVPg[VZmnq!X!Z![!]!h!jdlVZnq!X!Z![!]!h!jR!WmelVZnq!X!Z![!]!h!jdlVZnq!X!Z![!]!h!jdy`abcdefghiR!UkdlVZnq!X!Z![!]!h!jR!c!VemVZnq!X!Z![!]!h!jQRORXRQUP[YUZ!X![!h!jQZVQ!XnS![q!ZR!h!]Qs]R!^sQv^R!`vQx_R!axQ!ZnQ!]qT!g!Z!]TQORQnVQqZW!Ynq!Z!]X!e!X![!h!jQz`Q{aQ|bQ}cQ!OdQ!PeQ!QfQ!RgQ!ShR!TiR!Vk", + nodeNames: "⚠ NodeEdgeText NodeText StyleText LineComment FlowchartDiagram DiagramName Orientation NodeId Node String Link NodeEdge DoubleHyphen DoubleEqual Keyword : ::: StyleKeyword", + maxTerm: 89, + propSources: [flowchartHighlighting], + skippedNodes: [0,4], + repeatNodeCount: 6, + tokenData: "=3^!aR3ZOX!)tXY!.cYZ!0iZ^!.c^p!)tpq!.cqr!2wrs+ kst!2wtu!2wuv,(_vw6>_wx7ETxy8Kwyz9!^z{!2w{|!2w|}!)t}!O9&O!O!P9/Q!P!Q!2w!Q![!2w![!]:5y!]!^:8k!^!_:9c!_!`:Br!`!a:F`!a!b!2w!b!c!)t!c!}!2w!}#O:GW#O#P!2w#P#Q:Iw#Q#R!)t#R#S!2w#S#T:Lk#T#c!2w#c#d<%_#d#l!2w#l#m<%_#m#o!2w#o#p=,[#p#q=.Q#q#r=.x#r#s=0n#s#y!)t#y#z!.c#z$f!)t$f$g!.c$g$p!)t$p$q!2w$q${!)t${$|!2w$|%Q!)t%Q%R!2w%R%W!)t%W%o!2w%o%p!)t%p&a!2w&a&b!)t&b0`!2w0`0d!)t0d0p!2w0p1O!)t1O1T!2w1T1[!)t1[1]!2w1]1^!)t1^1_!2w1_4U!)t4U4Z!2w4Z4[!)t4[4]!2w4]4^!2w4^4`!)t4`4d!2w4d4l!)t4l4m!2w4m4n!)t4n4q!2w4q4r!)t4r4s!2w4s4t!)t4t5Y!2w5Y5Z!)t5Z7Q!2w7Q7R!)t7R:S!2w:S:[!)t:[=p!2w=p=y!)t=y>q!2w>q>s!)t>s>t!2w>t>{!)t>{?t!2w?tA`!)tA`A{!2wA{BQ!)tBQBT!2wBTCS!)tCSDP!2wDPDt!)tDtDu!2wDuDv!2wDvDw!)tDwGO!2wGOGP!)tGPGQ!2wGQGa!)tGaGb!2wGbGc!2wGcGj!)tGjGk!2wGkGl!2wGlGv!)tGvGy!2wGyG{!)tG{G|!2wG|H^!)tH^H_!2wH_H`!)tH`IO!2wIOIm!)tImKj!2wKjKu!)tKuKv!2wKvL`!)tL`MR!2wMRM[!)tM[M]!2wM]M^!2wM^Mb!)tMbMc!2wMcMh!)tMhNO!2wNONS!)tNSNT!2wNTN^!)tN^N_!2wN_Nb!)tNbNc!2wNcNz!)tNz! e!2w! e!#O!)t!#O!#P!2w!#P!#Q!)t!#Q!#]!2w!#]!%W!)t!%W!&`!2w!&`!&c!)t!&c!&d!2w!&d!&v!)t!&v!&w!2w!&w!'O!)t!'O!'Y!2w!'Y!'i!)t!'i!'p!2w!'p!'q!)t!'q!'x!2w!'x!'}!)t!'}!(V!2w!(V!(X!)t!(X!(Y!2w!(Y!(Z!2w!(Z!(]!)t!(]!(s!2w!(s!(t!)t!(t!({!2w!({!(|!)t!(|!(}!2w!(}!)Q!)t!)Q!)U!2w!)U!)X!)t!)X!)Y!2w!)Y!)j!)t!)j!)k!2w!)k!)x!)t!)x!)y!2w!)y!)z!2w!)z!){!)t!){!*O!2w!*O!*^!)t!*^!*_!2w!*_!*`!2w!*`!*s!)t!*s!*y!2w!*y!*}!)t!*}!+O!2w!+O!+P!2w!+P!+R!)t!+R!+i!2w!+i!+j!)t!+j!+q!2w!+q!+r!)t!+r!+s!2w!+s!+t!2w!+t!+u!)t!+u!+v!2w!+v!+w!2w!+w!+x!)t!+x!+y!2w!+y!+z!2w!+z!,k!)t!,k!,o!2w!,o!,p!)t!,p!,q!2w!,q!-U!)t!-U!-X!2w!-X!-i!)t!-i!-r!2w!-r!-s!)t!-s!-v!2w!-v!-w!)t!-w!._!2w!._!.`!)t!.`!.g!2w!.g!.h!)t!.h!.i!2w!.i!.j!2w!.j!.k!)t!.k!.p!2w!.p!.s!)t!.s!.t!2w!.t!/W!)t!/W!/X!2w!/X!/h!)t!/h!/i!2w!/i!/j!2w!/j!0_!)t!0_!0g!2w!0g!0i!)t!0i!0j!2w!0j!0k!2w!0k!0m!)t!0m!1T!2w!1T!1U!)t!1U!1]!2w!1]!1^!)t!1^!1_!2w!1_!1`!2w!1`!1a!)t!1a!1f!2w!1f!1i!)t!1i!1j!2w!1j!2Y!)t!2Y!2Z!2w!2Z!2[!2w!2[!2]!)t!2]!2`!2w!2`!2o!)t!2o!2p!2w!2p!3R!)t!3R!3S!2w!3S!3T!)t!3T!3Z!2w!3Z!3^!)t!3^!3a!2w!3a!3b!)t!3b!3f!2w!3f!3i!)t!3i!3j!2w!3j!3k!2w!3k!3l!)t!3l!3m!2w!3m!3n!)t!3n!3o!2w!3o!3p!2w!3p!3s!)t!3s!3t!2w!3t!3u!2w!3u!3x!)t!3x!3{!2w!3{!4O!)t!4O!4[!2w!4[!4r!)t!4r!4s!2w!4s!5y!)t!5y!6R!2w!6R!6S!)t!6S!6V!2w!6V!6W!)t!6W!6o!2w!6o!6p!)t!6p!6z!2w!6z!6{!)t!6{!7Q!2w!7Q!7T!)t!7T!7U!2w!7U!7p!)t!7p!7q!2w!7q!7r!2w!7r!7x!)t!7x!7y!2w!7y!7z!2w!7z!8o!)t!8o!8w!2w!8w!8x!)t!8x!8{!2w!8{!8|!)t!8|!9e!2w!9e!9f!)t!9f!9p!2w!9p!9q!)t!9q!9v!2w!9v!9y!)t!9y!9z!2w!9z!:l!)t!:l!:m!2w!:m!:n!)t!:n!:o!2w!:o!:p!2w!:p!;P!)t!;P!;Q!2w!;Q!;R!2w!;R!;e!)t!;e!;m!2w!;m!;n!)t!;n!;q!2w!;q!;r!)t!;r!O!)t!>O!>U!2w!>U!>Z!)t!>Z!>m!2w!>m!>p!)t!>p!?Y!2w!?Y!?Z!)t!?Z!?d!2w!?d!?e!)t!?e!?f!2w!?f!?h!)t!?h!?o!2w!?o!@{!)t!@{!A}!2w!A}!BO!)t!BO!BP!2w!BP!BQ!2w!BQ!B^!)t!B^!Be!2w!Be!Cq!)t!Cq!Cr!2w!Cr!Cs!2w!Cs!Ct!)t!Ct!Cu!2w!Cu!Cw!)t!Cw!Cx!2w!Cx!Cy!2w!Cy!Cz!)t!Cz!C{!2w!C{!C}!)t!C}!DO!2w!DO!DU!)t!DU!DY!2w!DY!DZ!)t!DZ!Db!2w!Db!Dc!)t!Dc!Df!2w!Df!Dg!)t!Dg!Dh!2w!Dh!Di!)t!Di!Dj!2w!Dj!Dl!)t!Dl!Dm!2w!Dm!Dn!2w!Dn!Do!)t!Do!Ds!2w!Ds!Dt!)t!Dt!Du!2w!Du!Dv!2w!Dv!EP!)t!EP!EQ!2w!EQ!ES!)t!ES!EX!2w!EX!EY!)t!EY!EZ!2w!EZ!Ep!)t!Ep!Et!2w!Et!Ff!)t!Ff!Fg!2w!Fg!Gx!)t!Gx!HQ!2w!HQ!HR!)t!HR!Hw!2w!Hw!Id!)t!Id!Ii!2w!Ii!LQ!)t!LQ!L}!2w!L}!Mc!)t!Mc!Md!2w!Md!Mt!)t!Mt!Mz!2w!Mz!NO!)t!NO!NS!2w!NS!NV!)t!NV!NW!2w!NW!NZ!)t!NZ!N[!2w!N[!N]!2w!N]!Nd!)t!Nd!Ng!2w!Ng!Nk!)t!Nk!Nx!2w!Nx# U!)t# U# V!2w# V# h!)t# h#!`!2w#!`#!a!)t#!a#!b!2w#!b#!g!)t#!g#!h!2w#!h#!j!)t#!j##g!2w##g##h!)t##h#*s!2w#*s#*t!)t#*t#*x!2w#*x#*z!)t#*z#+R!2w#+R#+S!)t#+S#+T!2w#+T#+U!)t#+U#+Y!2w#+Y#+[!)t#+[#,V!2w#,V#,W!)t#,W#,[!2w#,[#,^!)t#,^#-P!2w#-P#-Q!)t#-Q#-U!2w#-U#-W!)t#-W#-_!2w#-_#-`!)t#-`#-a!2w#-a#-b!)t#-b#-f!2w#-f#-h!)t#-h#-w!2w#-w#-x!)t#-x#/T!2w#/T#/U!)t#/U#/Y!2w#/Y#/[!)t#/[#0q!2w#0q#1h!)t#1h#1x!2w#1x#2Y!)t#2Y#4R!2w#4R#4_!)t#4_#Au!2w#Au#Aw!)t#Aw#BY!2w#BY#BZ!.c#BZ#Bu!2w#Bu#Bz!)t#Bz#Di!2w#Di#EO!)t#EO#E]!2w#E]#E^!)t#E^#Eb!2w#Eb#Ep!)t#Ep#FS!2w#FS#Fb!)t#Fb#Ft!2w#Ft#GS!)t#GS#Ga!2w#Ga#Gb!)t#Gb#Ge!2w#Ge#Gt!)t#Gt#Hz!2w#Hz#Io!)t#Io#Ip!2w#Ip#It!)t#It#Iu!2w#Iu#K[!)t#K[#MW!2w#MW#M`!)t#M`#NZ!2w#NZ#N[!)t#N[#N]!2w#N]#Nb!)t#Nb$ z!2w$ z$!U!)t$!U$!s!2w$!s$#x!)t$#x$$h!2w$$h$$j!)t$$j$$o!2w$$o$$z!)t$$z$%x!2w$%x$&_!)t$&_$&f!2w$&f$'p!)t$'p$(X!2w$(X$(b!)t$(b$)i!2w$)i$+_!)t$+_$+`!2w$+`$-a!)t$-a$.b!2w$.b$.s!)t$.s$.z!2w$.z$0T!)t$0T$0s!2w$0s$1Q!)t$1Q$1R!2w$1R$1S!2w$1S$1^!)t$1^$2[!2w$2[$2v!)t$2v$3l!2w$3l$4g!)t$4g$4j!2w$4j$4t!)t$4t$5j!2w$5j$7y!)t$7y$7}!2w$7}$8O!)t$8O$8S!2w$8S$8V!)t$8V$8W!2w$8W$8X!2w$8X$8b!)t$8b$z!2w5>z5>{!)t5>{5>|!2w5>|5?P!)t5?P5?Q!2w5?Q5?R!2w5?R5?T!)t5?T5?Y!2w5?Y5?[!)t5?[5?]!2w5?]5?^!)t5?^5?_!2w5?_5?w!)t5?w5?z!2w5?z5?|!)t5?|5@X!2w5@X5@`!)t5@`5@c!2w5@c5@o!)t5@o5@u!2w5@u5@w!)t5@w5@}!2w5@}5AP!)t5AP5AV!2w5AV5A`!)t5A`5Ag!2w5Ag5Ah!)t5Ah5Ao!2w5Ao5Dv!)t5Dv5Ek!2w5Ek5FY!)t5FY;%S!2w;%S;%`!)t;%`;%w!2w;%w;%{!)t;%{;'O!2w;'O;'S!)t;'S;=`!.]<%l?&r!)t?&r?.p!2w?.p?.r!)t?.r?1Q!2w?1Q?1x!)t?1x?2P!2w?2P?2]!)t?2]?2b!2w?2b?2g!)t?2g?2h!2w?2h?2i!)t?2i?2s!2w?2s?2t!)t?2t?3R!2w?3R?3S!)t?3S?3X!2w?3X?3Y!)t?3Y?3Z!2w?3Z?3[!)t?3[?3]!2w?3]?3^!2w?3^?3_!)t?3_?3`!2w?3`?3a!2w?3a?3b!)t?3b?5r!2w?5r?6e!)t?6e?>`!2w?>`?>r!)t?>r?@U!2w?@U?@W!)t?@W?A`!2w?A`?BY!)t?BY?Bf!2w?Bf?EO!)t?EO?ET!2w?ET?EU!)t?EU?HR!2w?HR?Hw!)t?Hw?Ic!2w?Ic?Ii!)t?Ii?JT!2w?JT?J`!)t?J`?L]!2w?L]?L`!)t?L`?Lf!2w?Lf?Lh!)t?Lh?Ln!2w?Ln?Lp!)t?Lp?Lv!2w?Lv?Lx!)t?Lx?L{!2w?L{O!)t^!)}X!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)tY!*qV!OWzQOw!*jwx!+Wx#S!*j#S#T!+o#T;'S!*j;'S;=`!,W<%lO!*jW!+]S!OWO#S!+W#T;'S!+W;'S;=`!+i<%lO!+WW!+lP;=`<%l!+WQ!+tSzQOw!+ox;'S!+o;'S;=`!,Q<%lO!+oQ!,TP;=`<%l!+oY!,ZP;=`<%l!*j[!,eV!OW|SOr!,^rs!+Ws#S!,^#S#T!,z#T;'S!,^;'S;=`!-c<%lO!,^S!-PS|SOr!,zs;'S!,z;'S;=`!-]<%lO!,zS!-`P;=`<%l!,z[!-fP;=`<%l!,^U!-pV|SzQOr!-irs!+osw!-iwx!,zx;'S!-i;'S;=`!.V<%lO!-iU!.YP;=`<%l!-i^!.`P;=`<%l!)t!a!.nm!OW|SzQk!ROX!)tX^!.c^p!)tpq!.cqr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T#y!)t#y#z!.c#z$f!)t$f$g!.c$g#BY!)t#BY#BZ!.c#BZ$IS!)t$IS$I_!.c$I_$I|!)t$I|$JO!.c$JO$JT!)t$JT$JU!.c$JU$KV!)t$KV$KW!.c$KW&FU!)t&FU&FV!.c&FV;'S!)t;'S;=`!.]<%lO!)t!a!0vo!OW|SzQuPk!ROX!)tXY!.cYZ!0iZ^!.c^p!)tpq!.cqr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T#y!)t#y#z!.c#z$f!)t$f$g!.c$g#BY!)t#BY#BZ!.c#BZ$IS!)t$IS$I_!.c$I_$I|!)t$I|$JO!.c$JO$JT!)t$JT$JU!.c$JU$KV!)t$KV$KW!.c$KW&FU!)t&FU&FV!.c&FV;'S!)t;'S;=`!.]<%lO!)t_!3S2g!OW|SzQmPOq!)tqr!2wrs#9kst!2wtu!2wuv!2wvw!2wwx';ixz!)tz{!2w{|!2w|!O!)t!O!P!2w!P!Q!2w!Q![!2w![!a!)t!a!b!2w!b!c!)t!c!}!2w!}#O!)t#O#P!2w#P#R!)t#R#S!2w#S#T)Hy#T#o!2w#o$p!)t$p$q!2w$q${!)t${$|!2w$|%Q!)t%Q%R!2w%R%W!)t%W%o!2w%o%p!)t%p&a!2w&a&b!)t&b0`!2w0`0d!)t0d0p!2w0p1O!)t1O1T!2w1T1[!)t1[1]!2w1]1^!)t1^1_!2w1_4U!)t4U4Z!2w4Z4[!)t4[4]!2w4]4^!2w4^4`!)t4`4d!2w4d4l!)t4l4m!2w4m4n!)t4n4q!2w4q4r!)t4r4s!2w4s4t!)t4t5Y!2w5Y5Z!)t5Z7Q!2w7Q7R!)t7R:S!2w:S:[!)t:[=p!2w=p=y!)t=y>q!2w>q>s!)t>s>t!2w>t>{!)t>{?t!2w?tA`!)tA`A{!2wA{BQ!)tBQBT!2wBTCS!)tCSDP!2wDPDt!)tDtDu!2wDuDv!2wDvDw!)tDwGO!2wGOGP!)tGPGQ!2wGQGa!)tGaGb!2wGbGc!2wGcGj!)tGjGk!2wGkGl!2wGlGv!)tGvGy!2wGyG{!)tG{G|!2wG|H^!)tH^H_!2wH_H`!)tH`IO!2wIOIm!)tImKj!2wKjKu!)tKuKv!2wKvL`!)tL`MR!2wMRM[!)tM[M]!2wM]M^!2wM^Mb!)tMbMc!2wMcMh!)tMhNO!2wNONS!)tNSNT!2wNTN^!)tN^N_!2wN_Nb!)tNbNc!2wNcNz!)tNz! e!2w! e!#O!)t!#O!#P!2w!#P!#Q!)t!#Q!#]!2w!#]!%W!)t!%W!&`!2w!&`!&c!)t!&c!&d!2w!&d!&v!)t!&v!&w!2w!&w!'O!)t!'O!'Y!2w!'Y!'i!)t!'i!'p!2w!'p!'q!)t!'q!'x!2w!'x!'}!)t!'}!(V!2w!(V!(X!)t!(X!(Y!2w!(Y!(Z!2w!(Z!(]!)t!(]!(s!2w!(s!(t!)t!(t!({!2w!({!(|!)t!(|!(}!2w!(}!)Q!)t!)Q!)U!2w!)U!)X!)t!)X!)Y!2w!)Y!)j!)t!)j!)k!2w!)k!)x!)t!)x!)y!2w!)y!)z!2w!)z!){!)t!){!*O!2w!*O!*^!)t!*^!*_!2w!*_!*`!2w!*`!*s!)t!*s!*y!2w!*y!*}!)t!*}!+O!2w!+O!+P!2w!+P!+R!)t!+R!+i!2w!+i!+j!)t!+j!+q!2w!+q!+r!)t!+r!+s!2w!+s!+t!2w!+t!+u!)t!+u!+v!2w!+v!+w!2w!+w!+x!)t!+x!+y!2w!+y!+z!2w!+z!,k!)t!,k!,o!2w!,o!,p!)t!,p!,q!2w!,q!-U!)t!-U!-X!2w!-X!-i!)t!-i!-r!2w!-r!-s!)t!-s!-v!2w!-v!-w!)t!-w!._!2w!._!.`!)t!.`!.g!2w!.g!.h!)t!.h!.i!2w!.i!.j!2w!.j!.k!)t!.k!.p!2w!.p!.s!)t!.s!.t!2w!.t!/W!)t!/W!/X!2w!/X!/h!)t!/h!/i!2w!/i!/j!2w!/j!0_!)t!0_!0g!2w!0g!0i!)t!0i!0j!2w!0j!0k!2w!0k!0m!)t!0m!1T!2w!1T!1U!)t!1U!1]!2w!1]!1^!)t!1^!1_!2w!1_!1`!2w!1`!1a!)t!1a!1f!2w!1f!1i!)t!1i!1j!2w!1j!2Y!)t!2Y!2Z!2w!2Z!2[!2w!2[!2]!)t!2]!2`!2w!2`!2o!)t!2o!2p!2w!2p!3R!)t!3R!3S!2w!3S!3T!)t!3T!3Z!2w!3Z!3^!)t!3^!3a!2w!3a!3b!)t!3b!3f!2w!3f!3i!)t!3i!3j!2w!3j!3k!2w!3k!3l!)t!3l!3m!2w!3m!3n!)t!3n!3o!2w!3o!3p!2w!3p!3s!)t!3s!3t!2w!3t!3u!2w!3u!3x!)t!3x!3{!2w!3{!4O!)t!4O!4[!2w!4[!4r!)t!4r!4s!2w!4s!5y!)t!5y!6R!2w!6R!6S!)t!6S!6V!2w!6V!6W!)t!6W!6o!2w!6o!6p!)t!6p!6z!2w!6z!6{!)t!6{!7Q!2w!7Q!7T!)t!7T!7U!2w!7U!7p!)t!7p!7q!2w!7q!7r!2w!7r!7x!)t!7x!7y!2w!7y!7z!2w!7z!8o!)t!8o!8w!2w!8w!8x!)t!8x!8{!2w!8{!8|!)t!8|!9e!2w!9e!9f!)t!9f!9p!2w!9p!9q!)t!9q!9v!2w!9v!9y!)t!9y!9z!2w!9z!:l!)t!:l!:m!2w!:m!:n!)t!:n!:o!2w!:o!:p!2w!:p!;P!)t!;P!;Q!2w!;Q!;R!2w!;R!;e!)t!;e!;m!2w!;m!;n!)t!;n!;q!2w!;q!;r!)t!;r!O!)t!>O!>U!2w!>U!>Z!)t!>Z!>m!2w!>m!>p!)t!>p!?Y!2w!?Y!?Z!)t!?Z!?d!2w!?d!?e!)t!?e!?f!2w!?f!?h!)t!?h!?o!2w!?o!@{!)t!@{!A}!2w!A}!BO!)t!BO!BP!2w!BP!BQ!2w!BQ!B^!)t!B^!Be!2w!Be!Cq!)t!Cq!Cr!2w!Cr!Cs!2w!Cs!Ct!)t!Ct!Cu!2w!Cu!Cw!)t!Cw!Cx!2w!Cx!Cy!2w!Cy!Cz!)t!Cz!C{!2w!C{!C}!)t!C}!DO!2w!DO!DU!)t!DU!DY!2w!DY!DZ!)t!DZ!Db!2w!Db!Dc!)t!Dc!Df!2w!Df!Dg!)t!Dg!Dh!2w!Dh!Di!)t!Di!Dj!2w!Dj!Dl!)t!Dl!Dm!2w!Dm!Dn!2w!Dn!Do!)t!Do!Ds!2w!Ds!Dt!)t!Dt!Du!2w!Du!Dv!2w!Dv!EP!)t!EP!EQ!2w!EQ!ES!)t!ES!EX!2w!EX!EY!)t!EY!EZ!2w!EZ!Ep!)t!Ep!Et!2w!Et!Ff!)t!Ff!Fg!2w!Fg!Gx!)t!Gx!HQ!2w!HQ!HR!)t!HR!Hw!2w!Hw!Id!)t!Id!Ii!2w!Ii!LQ!)t!LQ!L}!2w!L}!Mc!)t!Mc!Md!2w!Md!Mt!)t!Mt!Mz!2w!Mz!NO!)t!NO!NS!2w!NS!NV!)t!NV!NW!2w!NW!NZ!)t!NZ!N[!2w!N[!N]!2w!N]!Nd!)t!Nd!Ng!2w!Ng!Nk!)t!Nk!Nx!2w!Nx# U!)t# U# V!2w# V# h!)t# h#!`!2w#!`#!a!)t#!a#!b!2w#!b#!g!)t#!g#!h!2w#!h#!j!)t#!j##g!2w##g##h!)t##h#*s!2w#*s#*t!)t#*t#*x!2w#*x#*z!)t#*z#+R!2w#+R#+S!)t#+S#+T!2w#+T#+U!)t#+U#+Y!2w#+Y#+[!)t#+[#,V!2w#,V#,W!)t#,W#,[!2w#,[#,^!)t#,^#-P!2w#-P#-Q!)t#-Q#-U!2w#-U#-W!)t#-W#-_!2w#-_#-`!)t#-`#-a!2w#-a#-b!)t#-b#-f!2w#-f#-h!)t#-h#-w!2w#-w#-x!)t#-x#/T!2w#/T#/U!)t#/U#/Y!2w#/Y#/[!)t#/[#0q!2w#0q#1h!)t#1h#1x!2w#1x#2Y!)t#2Y#4R!2w#4R#4_!)t#4_#Au!2w#Au#Aw!)t#Aw#BY!2w#BY#BZ!)t#BZ#Bu!2w#Bu#Bz!)t#Bz#Di!2w#Di#EO!)t#EO#E]!2w#E]#E^!)t#E^#Eb!2w#Eb#Ep!)t#Ep#FS!2w#FS#Fb!)t#Fb#Ft!2w#Ft#GS!)t#GS#Ga!2w#Ga#Gb!)t#Gb#Ge!2w#Ge#Gt!)t#Gt#Hz!2w#Hz#Io!)t#Io#Ip!2w#Ip#It!)t#It#Iu!2w#Iu#K[!)t#K[#MW!2w#MW#M`!)t#M`#NZ!2w#NZ#N[!)t#N[#N]!2w#N]#Nb!)t#Nb$ z!2w$ z$!U!)t$!U$!s!2w$!s$#x!)t$#x$$h!2w$$h$$j!)t$$j$$o!2w$$o$$z!)t$$z$%x!2w$%x$&_!)t$&_$&f!2w$&f$'p!)t$'p$(X!2w$(X$(b!)t$(b$)i!2w$)i$+_!)t$+_$+`!2w$+`$-a!)t$-a$.b!2w$.b$.s!)t$.s$.z!2w$.z$0T!)t$0T$0s!2w$0s$1Q!)t$1Q$1R!2w$1R$1S!2w$1S$1^!)t$1^$2[!2w$2[$2v!)t$2v$3l!2w$3l$4g!)t$4g$4j!2w$4j$4t!)t$4t$5j!2w$5j$7y!)t$7y$7}!2w$7}$8O!)t$8O$8S!2w$8S$8V!)t$8V$8W!2w$8W$8X!2w$8X$8b!)t$8b$z!2w5>z5>{!)t5>{5>|!2w5>|5?P!)t5?P5?Q!2w5?Q5?R!2w5?R5?T!)t5?T5?Y!2w5?Y5?[!)t5?[5?]!2w5?]5?^!)t5?^5?_!2w5?_5?w!)t5?w5?z!2w5?z5?|!)t5?|5@X!2w5@X5@`!)t5@`5@c!2w5@c5@o!)t5@o5@u!2w5@u5@w!)t5@w5@}!2w5@}5AP!)t5AP5AV!2w5AV5A`!)t5A`5Ag!2w5Ag5Ah!)t5Ah5Ao!2w5Ao5Dv!)t5Dv5Ek!2w5Ek5FY!)t5FY;%S!2w;%S;%`!)t;%`;%w!2w;%w;%{!)t;%{;'O!2w;'O;'S!)t;'S;=`!.]<%l?&r!)t?&r?.p!2w?.p?.r!)t?.r?1Q!2w?1Q?1x!)t?1x?2P!2w?2P?2]!)t?2]?2b!2w?2b?2g!)t?2g?2h!2w?2h?2i!)t?2i?2s!2w?2s?2t!)t?2t?3R!2w?3R?3S!)t?3S?3X!2w?3X?3Y!)t?3Y?3Z!2w?3Z?3[!)t?3[?3]!2w?3]?3^!2w?3^?3_!)t?3_?3`!2w?3`?3a!2w?3a?3b!)t?3b?5r!2w?5r?6e!)t?6e?>`!2w?>`?>r!)t?>r?@U!2w?@U?@W!)t?@W?A`!2w?A`?BY!)t?BY?Bf!2w?Bf?EO!)t?EO?ET!2w?ET?EU!)t?EU?HR!2w?HR?Hw!)t?Hw?Ic!2w?Ic?Ii!)t?Ii?JT!2w?JT?J`!)t?J`?L]!2w?L]?L`!)t?L`?Lf!2w?Lf?Lh!)t?Lh?Ln!2w?Ln?Lp!)t?Lp?Lv!2w?Lv?Lx!)t?Lx?L{!2w?L{O!)tZ#9t2g!OWzQmPOq!*jqr#9krs#9kst#9ktu#9kuv#9kvw#9kwx$@]xz!*jz{#9k{|#9k|!O!*j!O!P#9k!P!Q#9k!Q![#9k![!a!*j!a!b#9k!b!c!*j!c!}#9k!}#O!*j#O#P#9k#P#R!*j#R#S#9k#S#T&4y#T#o#9k#o$p!*j$p$q#9k$q${!*j${$|#9k$|%Q!*j%Q%R#9k%R%W!*j%W%o#9k%o%p!*j%p&a#9k&a&b!*j&b0`#9k0`0d!*j0d0p#9k0p1O!*j1O1T#9k1T1[!*j1[1]#9k1]1^!*j1^1_#9k1_4U!*j4U4Z#9k4Z4[!*j4[4]#9k4]4^#9k4^4`!*j4`4d#9k4d4l!*j4l4m#9k4m4n!*j4n4q#9k4q4r!*j4r4s#9k4s4t!*j4t5Y#9k5Y5Z!*j5Z7Q#9k7Q7R!*j7R:S#9k:S:[!*j:[=p#9k=p=y!*j=y>q#9k>q>s!*j>s>t#9k>t>{!*j>{?t#9k?tA`!*jA`A{#9kA{BQ!*jBQBT#9kBTCS!*jCSDP#9kDPDt!*jDtDu#9kDuDv#9kDvDw!*jDwGO#9kGOGP!*jGPGQ#9kGQGa!*jGaGb#9kGbGc#9kGcGj!*jGjGk#9kGkGl#9kGlGv!*jGvGy#9kGyG{!*jG{G|#9kG|H^!*jH^H_#9kH_H`!*jH`IO#9kIOIm!*jImKj#9kKjKu!*jKuKv#9kKvL`!*jL`MR#9kMRM[!*jM[M]#9kM]M^#9kM^Mb!*jMbMc#9kMcMh!*jMhNO#9kNONS!*jNSNT#9kNTN^!*jN^N_#9kN_Nb!*jNbNc#9kNcNz!*jNz! e#9k! e!#O!*j!#O!#P#9k!#P!#Q!*j!#Q!#]#9k!#]!%W!*j!%W!&`#9k!&`!&c!*j!&c!&d#9k!&d!&v!*j!&v!&w#9k!&w!'O!*j!'O!'Y#9k!'Y!'i!*j!'i!'p#9k!'p!'q!*j!'q!'x#9k!'x!'}!*j!'}!(V#9k!(V!(X!*j!(X!(Y#9k!(Y!(Z#9k!(Z!(]!*j!(]!(s#9k!(s!(t!*j!(t!({#9k!({!(|!*j!(|!(}#9k!(}!)Q!*j!)Q!)U#9k!)U!)X!*j!)X!)Y#9k!)Y!)j!*j!)j!)k#9k!)k!)x!*j!)x!)y#9k!)y!)z#9k!)z!){!*j!){!*O#9k!*O!*^!*j!*^!*_#9k!*_!*`#9k!*`!*s!*j!*s!*y#9k!*y!*}!*j!*}!+O#9k!+O!+P#9k!+P!+R!*j!+R!+i#9k!+i!+j!*j!+j!+q#9k!+q!+r!*j!+r!+s#9k!+s!+t#9k!+t!+u!*j!+u!+v#9k!+v!+w#9k!+w!+x!*j!+x!+y#9k!+y!+z#9k!+z!,k!*j!,k!,o#9k!,o!,p!*j!,p!,q#9k!,q!-U!*j!-U!-X#9k!-X!-i!*j!-i!-r#9k!-r!-s!*j!-s!-v#9k!-v!-w!*j!-w!._#9k!._!.`!*j!.`!.g#9k!.g!.h!*j!.h!.i#9k!.i!.j#9k!.j!.k!*j!.k!.p#9k!.p!.s!*j!.s!.t#9k!.t!/W!*j!/W!/X#9k!/X!/h!*j!/h!/i#9k!/i!/j#9k!/j!0_!*j!0_!0g#9k!0g!0i!*j!0i!0j#9k!0j!0k#9k!0k!0m!*j!0m!1T#9k!1T!1U!*j!1U!1]#9k!1]!1^!*j!1^!1_#9k!1_!1`#9k!1`!1a!*j!1a!1f#9k!1f!1i!*j!1i!1j#9k!1j!2Y!*j!2Y!2Z#9k!2Z!2[#9k!2[!2]!*j!2]!2`#9k!2`!2o!*j!2o!2p#9k!2p!3R!*j!3R!3S#9k!3S!3T!*j!3T!3Z#9k!3Z!3^!*j!3^!3a#9k!3a!3b!*j!3b!3f#9k!3f!3i!*j!3i!3j#9k!3j!3k#9k!3k!3l!*j!3l!3m#9k!3m!3n!*j!3n!3o#9k!3o!3p#9k!3p!3s!*j!3s!3t#9k!3t!3u#9k!3u!3x!*j!3x!3{#9k!3{!4O!*j!4O!4[#9k!4[!4r!*j!4r!4s#9k!4s!5y!*j!5y!6R#9k!6R!6S!*j!6S!6V#9k!6V!6W!*j!6W!6o#9k!6o!6p!*j!6p!6z#9k!6z!6{!*j!6{!7Q#9k!7Q!7T!*j!7T!7U#9k!7U!7p!*j!7p!7q#9k!7q!7r#9k!7r!7x!*j!7x!7y#9k!7y!7z#9k!7z!8o!*j!8o!8w#9k!8w!8x!*j!8x!8{#9k!8{!8|!*j!8|!9e#9k!9e!9f!*j!9f!9p#9k!9p!9q!*j!9q!9v#9k!9v!9y!*j!9y!9z#9k!9z!:l!*j!:l!:m#9k!:m!:n!*j!:n!:o#9k!:o!:p#9k!:p!;P!*j!;P!;Q#9k!;Q!;R#9k!;R!;e!*j!;e!;m#9k!;m!;n!*j!;n!;q#9k!;q!;r!*j!;r!O!*j!>O!>U#9k!>U!>Z!*j!>Z!>m#9k!>m!>p!*j!>p!?Y#9k!?Y!?Z!*j!?Z!?d#9k!?d!?e!*j!?e!?f#9k!?f!?h!*j!?h!?o#9k!?o!@{!*j!@{!A}#9k!A}!BO!*j!BO!BP#9k!BP!BQ#9k!BQ!B^!*j!B^!Be#9k!Be!Cq!*j!Cq!Cr#9k!Cr!Cs#9k!Cs!Ct!*j!Ct!Cu#9k!Cu!Cw!*j!Cw!Cx#9k!Cx!Cy#9k!Cy!Cz!*j!Cz!C{#9k!C{!C}!*j!C}!DO#9k!DO!DU!*j!DU!DY#9k!DY!DZ!*j!DZ!Db#9k!Db!Dc!*j!Dc!Df#9k!Df!Dg!*j!Dg!Dh#9k!Dh!Di!*j!Di!Dj#9k!Dj!Dl!*j!Dl!Dm#9k!Dm!Dn#9k!Dn!Do!*j!Do!Ds#9k!Ds!Dt!*j!Dt!Du#9k!Du!Dv#9k!Dv!EP!*j!EP!EQ#9k!EQ!ES!*j!ES!EX#9k!EX!EY!*j!EY!EZ#9k!EZ!Ep!*j!Ep!Et#9k!Et!Ff!*j!Ff!Fg#9k!Fg!Gx!*j!Gx!HQ#9k!HQ!HR!*j!HR!Hw#9k!Hw!Id!*j!Id!Ii#9k!Ii!LQ!*j!LQ!L}#9k!L}!Mc!*j!Mc!Md#9k!Md!Mt!*j!Mt!Mz#9k!Mz!NO!*j!NO!NS#9k!NS!NV!*j!NV!NW#9k!NW!NZ!*j!NZ!N[#9k!N[!N]#9k!N]!Nd!*j!Nd!Ng#9k!Ng!Nk!*j!Nk!Nx#9k!Nx# U!*j# U# V#9k# V# h!*j# h#!`#9k#!`#!a!*j#!a#!b#9k#!b#!g!*j#!g#!h#9k#!h#!j!*j#!j##g#9k##g##h!*j##h#*s#9k#*s#*t!*j#*t#*x#9k#*x#*z!*j#*z#+R#9k#+R#+S!*j#+S#+T#9k#+T#+U!*j#+U#+Y#9k#+Y#+[!*j#+[#,V#9k#,V#,W!*j#,W#,[#9k#,[#,^!*j#,^#-P#9k#-P#-Q!*j#-Q#-U#9k#-U#-W!*j#-W#-_#9k#-_#-`!*j#-`#-a#9k#-a#-b!*j#-b#-f#9k#-f#-h!*j#-h#-w#9k#-w#-x!*j#-x#/T#9k#/T#/U!*j#/U#/Y#9k#/Y#/[!*j#/[#0q#9k#0q#1h!*j#1h#1x#9k#1x#2Y!*j#2Y#4R#9k#4R#4_!*j#4_#Au#9k#Au#Aw!*j#Aw#BY#9k#BY#BZ!*j#BZ#Bu#9k#Bu#Bz!*j#Bz#Di#9k#Di#EO!*j#EO#E]#9k#E]#E^!*j#E^#Eb#9k#Eb#Ep!*j#Ep#FS#9k#FS#Fb!*j#Fb#Ft#9k#Ft#GS!*j#GS#Ga#9k#Ga#Gb!*j#Gb#Ge#9k#Ge#Gt!*j#Gt#Hz#9k#Hz#Io!*j#Io#Ip#9k#Ip#It!*j#It#Iu#9k#Iu#K[!*j#K[#MW#9k#MW#M`!*j#M`#NZ#9k#NZ#N[!*j#N[#N]#9k#N]#Nb!*j#Nb$ z#9k$ z$!U!*j$!U$!s#9k$!s$#x!*j$#x$$h#9k$$h$$j!*j$$j$$o#9k$$o$$z!*j$$z$%x#9k$%x$&_!*j$&_$&f#9k$&f$'p!*j$'p$(X#9k$(X$(b!*j$(b$)i#9k$)i$+_!*j$+_$+`#9k$+`$-a!*j$-a$.b#9k$.b$.s!*j$.s$.z#9k$.z$0T!*j$0T$0s#9k$0s$1Q!*j$1Q$1R#9k$1R$1S#9k$1S$1^!*j$1^$2[#9k$2[$2v!*j$2v$3l#9k$3l$4g!*j$4g$4j#9k$4j$4t!*j$4t$5j#9k$5j$7y!*j$7y$7}#9k$7}$8O!*j$8O$8S#9k$8S$8V!*j$8V$8W#9k$8W$8X#9k$8X$8b!*j$8b$z#9k5>z5>{!*j5>{5>|#9k5>|5?P!*j5?P5?Q#9k5?Q5?R#9k5?R5?T!*j5?T5?Y#9k5?Y5?[!*j5?[5?]#9k5?]5?^!*j5?^5?_#9k5?_5?w!*j5?w5?z#9k5?z5?|!*j5?|5@X#9k5@X5@`!*j5@`5@c#9k5@c5@o!*j5@o5@u#9k5@u5@w!*j5@w5@}#9k5@}5AP!*j5AP5AV#9k5AV5A`!*j5A`5Ag#9k5Ag5Ah!*j5Ah5Ao#9k5Ao5Dv!*j5Dv5Ek#9k5Ek5FY!*j5FY;%S#9k;%S;%`!*j;%`;%w#9k;%w;%{!*j;%{;'O#9k;'O;'S!*j;'S;=`!,W<%l?&r!*j?&r?.p#9k?.p?.r!*j?.r?1Q#9k?1Q?1x!*j?1x?2P#9k?2P?2]!*j?2]?2b#9k?2b?2g!*j?2g?2h#9k?2h?2i!*j?2i?2s#9k?2s?2t!*j?2t?3R#9k?3R?3S!*j?3S?3X#9k?3X?3Y!*j?3Y?3Z#9k?3Z?3[!*j?3[?3]#9k?3]?3^#9k?3^?3_!*j?3_?3`#9k?3`?3a#9k?3a?3b!*j?3b?5r#9k?5r?6e!*j?6e?>`#9k?>`?>r!*j?>r?@U#9k?@U?@W!*j?@W?A`#9k?A`?BY!*j?BY?Bf#9k?Bf?EO!*j?EO?ET#9k?ET?EU!*j?EU?HR#9k?HR?Hw!*j?Hw?Ic#9k?Ic?Ii!*j?Ii?JT#9k?JT?J`!*j?J`?L]#9k?L]?L`!*j?L`?Lf#9k?Lf?Lh!*j?Lh?Ln#9k?Ln?Lp!*j?Lp?Lv#9k?Lv?Lx!*j?Lx?L{#9k?L{O!*jX$@d2g!OWmPOq!+Wqr$@]rs$@]st$@]tu$@]uv$@]vw$@]wx$@]xz!+Wz{$@]{|$@]|!O!+W!O!P$@]!P!Q$@]!Q![$@]![!a!+W!a!b$@]!b!c!+W!c!}$@]!}#O!+W#O#P$@]#P#R!+W#R#S$@]#S#T%F{#T#o$@]#o$p!+W$p$q$@]$q${!+W${$|$@]$|%Q!+W%Q%R$@]%R%W!+W%W%o$@]%o%p!+W%p&a$@]&a&b!+W&b0`$@]0`0d!+W0d0p$@]0p1O!+W1O1T$@]1T1[!+W1[1]$@]1]1^!+W1^1_$@]1_4U!+W4U4Z$@]4Z4[!+W4[4]$@]4]4^$@]4^4`!+W4`4d$@]4d4l!+W4l4m$@]4m4n!+W4n4q$@]4q4r!+W4r4s$@]4s4t!+W4t5Y$@]5Y5Z!+W5Z7Q$@]7Q7R!+W7R:S$@]:S:[!+W:[=p$@]=p=y!+W=y>q$@]>q>s!+W>s>t$@]>t>{!+W>{?t$@]?tA`!+WA`A{$@]A{BQ!+WBQBT$@]BTCS!+WCSDP$@]DPDt!+WDtDu$@]DuDv$@]DvDw!+WDwGO$@]GOGP!+WGPGQ$@]GQGa!+WGaGb$@]GbGc$@]GcGj!+WGjGk$@]GkGl$@]GlGv!+WGvGy$@]GyG{!+WG{G|$@]G|H^!+WH^H_$@]H_H`!+WH`IO$@]IOIm!+WImKj$@]KjKu!+WKuKv$@]KvL`!+WL`MR$@]MRM[!+WM[M]$@]M]M^$@]M^Mb!+WMbMc$@]McMh!+WMhNO$@]NONS!+WNSNT$@]NTN^!+WN^N_$@]N_Nb!+WNbNc$@]NcNz!+WNz! e$@]! e!#O!+W!#O!#P$@]!#P!#Q!+W!#Q!#]$@]!#]!%W!+W!%W!&`$@]!&`!&c!+W!&c!&d$@]!&d!&v!+W!&v!&w$@]!&w!'O!+W!'O!'Y$@]!'Y!'i!+W!'i!'p$@]!'p!'q!+W!'q!'x$@]!'x!'}!+W!'}!(V$@]!(V!(X!+W!(X!(Y$@]!(Y!(Z$@]!(Z!(]!+W!(]!(s$@]!(s!(t!+W!(t!({$@]!({!(|!+W!(|!(}$@]!(}!)Q!+W!)Q!)U$@]!)U!)X!+W!)X!)Y$@]!)Y!)j!+W!)j!)k$@]!)k!)x!+W!)x!)y$@]!)y!)z$@]!)z!){!+W!){!*O$@]!*O!*^!+W!*^!*_$@]!*_!*`$@]!*`!*s!+W!*s!*y$@]!*y!*}!+W!*}!+O$@]!+O!+P$@]!+P!+R!+W!+R!+i$@]!+i!+j!+W!+j!+q$@]!+q!+r!+W!+r!+s$@]!+s!+t$@]!+t!+u!+W!+u!+v$@]!+v!+w$@]!+w!+x!+W!+x!+y$@]!+y!+z$@]!+z!,k!+W!,k!,o$@]!,o!,p!+W!,p!,q$@]!,q!-U!+W!-U!-X$@]!-X!-i!+W!-i!-r$@]!-r!-s!+W!-s!-v$@]!-v!-w!+W!-w!._$@]!._!.`!+W!.`!.g$@]!.g!.h!+W!.h!.i$@]!.i!.j$@]!.j!.k!+W!.k!.p$@]!.p!.s!+W!.s!.t$@]!.t!/W!+W!/W!/X$@]!/X!/h!+W!/h!/i$@]!/i!/j$@]!/j!0_!+W!0_!0g$@]!0g!0i!+W!0i!0j$@]!0j!0k$@]!0k!0m!+W!0m!1T$@]!1T!1U!+W!1U!1]$@]!1]!1^!+W!1^!1_$@]!1_!1`$@]!1`!1a!+W!1a!1f$@]!1f!1i!+W!1i!1j$@]!1j!2Y!+W!2Y!2Z$@]!2Z!2[$@]!2[!2]!+W!2]!2`$@]!2`!2o!+W!2o!2p$@]!2p!3R!+W!3R!3S$@]!3S!3T!+W!3T!3Z$@]!3Z!3^!+W!3^!3a$@]!3a!3b!+W!3b!3f$@]!3f!3i!+W!3i!3j$@]!3j!3k$@]!3k!3l!+W!3l!3m$@]!3m!3n!+W!3n!3o$@]!3o!3p$@]!3p!3s!+W!3s!3t$@]!3t!3u$@]!3u!3x!+W!3x!3{$@]!3{!4O!+W!4O!4[$@]!4[!4r!+W!4r!4s$@]!4s!5y!+W!5y!6R$@]!6R!6S!+W!6S!6V$@]!6V!6W!+W!6W!6o$@]!6o!6p!+W!6p!6z$@]!6z!6{!+W!6{!7Q$@]!7Q!7T!+W!7T!7U$@]!7U!7p!+W!7p!7q$@]!7q!7r$@]!7r!7x!+W!7x!7y$@]!7y!7z$@]!7z!8o!+W!8o!8w$@]!8w!8x!+W!8x!8{$@]!8{!8|!+W!8|!9e$@]!9e!9f!+W!9f!9p$@]!9p!9q!+W!9q!9v$@]!9v!9y!+W!9y!9z$@]!9z!:l!+W!:l!:m$@]!:m!:n!+W!:n!:o$@]!:o!:p$@]!:p!;P!+W!;P!;Q$@]!;Q!;R$@]!;R!;e!+W!;e!;m$@]!;m!;n!+W!;n!;q$@]!;q!;r!+W!;r!O!+W!>O!>U$@]!>U!>Z!+W!>Z!>m$@]!>m!>p!+W!>p!?Y$@]!?Y!?Z!+W!?Z!?d$@]!?d!?e!+W!?e!?f$@]!?f!?h!+W!?h!?o$@]!?o!@{!+W!@{!A}$@]!A}!BO!+W!BO!BP$@]!BP!BQ$@]!BQ!B^!+W!B^!Be$@]!Be!Cq!+W!Cq!Cr$@]!Cr!Cs$@]!Cs!Ct!+W!Ct!Cu$@]!Cu!Cw!+W!Cw!Cx$@]!Cx!Cy$@]!Cy!Cz!+W!Cz!C{$@]!C{!C}!+W!C}!DO$@]!DO!DU!+W!DU!DY$@]!DY!DZ!+W!DZ!Db$@]!Db!Dc!+W!Dc!Df$@]!Df!Dg!+W!Dg!Dh$@]!Dh!Di!+W!Di!Dj$@]!Dj!Dl!+W!Dl!Dm$@]!Dm!Dn$@]!Dn!Do!+W!Do!Ds$@]!Ds!Dt!+W!Dt!Du$@]!Du!Dv$@]!Dv!EP!+W!EP!EQ$@]!EQ!ES!+W!ES!EX$@]!EX!EY!+W!EY!EZ$@]!EZ!Ep!+W!Ep!Et$@]!Et!Ff!+W!Ff!Fg$@]!Fg!Gx!+W!Gx!HQ$@]!HQ!HR!+W!HR!Hw$@]!Hw!Id!+W!Id!Ii$@]!Ii!LQ!+W!LQ!L}$@]!L}!Mc!+W!Mc!Md$@]!Md!Mt!+W!Mt!Mz$@]!Mz!NO!+W!NO!NS$@]!NS!NV!+W!NV!NW$@]!NW!NZ!+W!NZ!N[$@]!N[!N]$@]!N]!Nd!+W!Nd!Ng$@]!Ng!Nk!+W!Nk!Nx$@]!Nx# U!+W# U# V$@]# V# h!+W# h#!`$@]#!`#!a!+W#!a#!b$@]#!b#!g!+W#!g#!h$@]#!h#!j!+W#!j##g$@]##g##h!+W##h#*s$@]#*s#*t!+W#*t#*x$@]#*x#*z!+W#*z#+R$@]#+R#+S!+W#+S#+T$@]#+T#+U!+W#+U#+Y$@]#+Y#+[!+W#+[#,V$@]#,V#,W!+W#,W#,[$@]#,[#,^!+W#,^#-P$@]#-P#-Q!+W#-Q#-U$@]#-U#-W!+W#-W#-_$@]#-_#-`!+W#-`#-a$@]#-a#-b!+W#-b#-f$@]#-f#-h!+W#-h#-w$@]#-w#-x!+W#-x#/T$@]#/T#/U!+W#/U#/Y$@]#/Y#/[!+W#/[#0q$@]#0q#1h!+W#1h#1x$@]#1x#2Y!+W#2Y#4R$@]#4R#4_!+W#4_#Au$@]#Au#Aw!+W#Aw#BY$@]#BY#BZ!+W#BZ#Bu$@]#Bu#Bz!+W#Bz#Di$@]#Di#EO!+W#EO#E]$@]#E]#E^!+W#E^#Eb$@]#Eb#Ep!+W#Ep#FS$@]#FS#Fb!+W#Fb#Ft$@]#Ft#GS!+W#GS#Ga$@]#Ga#Gb!+W#Gb#Ge$@]#Ge#Gt!+W#Gt#Hz$@]#Hz#Io!+W#Io#Ip$@]#Ip#It!+W#It#Iu$@]#Iu#K[!+W#K[#MW$@]#MW#M`!+W#M`#NZ$@]#NZ#N[!+W#N[#N]$@]#N]#Nb!+W#Nb$ z$@]$ z$!U!+W$!U$!s$@]$!s$#x!+W$#x$$h$@]$$h$$j!+W$$j$$o$@]$$o$$z!+W$$z$%x$@]$%x$&_!+W$&_$&f$@]$&f$'p!+W$'p$(X$@]$(X$(b!+W$(b$)i$@]$)i$+_!+W$+_$+`$@]$+`$-a!+W$-a$.b$@]$.b$.s!+W$.s$.z$@]$.z$0T!+W$0T$0s$@]$0s$1Q!+W$1Q$1R$@]$1R$1S$@]$1S$1^!+W$1^$2[$@]$2[$2v!+W$2v$3l$@]$3l$4g!+W$4g$4j$@]$4j$4t!+W$4t$5j$@]$5j$7y!+W$7y$7}$@]$7}$8O!+W$8O$8S$@]$8S$8V!+W$8V$8W$@]$8W$8X$@]$8X$8b!+W$8b$z$@]5>z5>{!+W5>{5>|$@]5>|5?P!+W5?P5?Q$@]5?Q5?R$@]5?R5?T!+W5?T5?Y$@]5?Y5?[!+W5?[5?]$@]5?]5?^!+W5?^5?_$@]5?_5?w!+W5?w5?z$@]5?z5?|!+W5?|5@X$@]5@X5@`!+W5@`5@c$@]5@c5@o!+W5@o5@u$@]5@u5@w!+W5@w5@}$@]5@}5AP!+W5AP5AV$@]5AV5A`!+W5A`5Ag$@]5Ag5Ah!+W5Ah5Ao$@]5Ao5Dv!+W5Dv5Ek$@]5Ek5FY!+W5FY;%S$@];%S;%`!+W;%`;%w$@];%w;%{!+W;%{;'O$@];'O;'S!+W;'S;=`!+i<%l?&r!+W?&r?.p$@]?.p?.r!+W?.r?1Q$@]?1Q?1x!+W?1x?2P$@]?2P?2]!+W?2]?2b$@]?2b?2g!+W?2g?2h$@]?2h?2i!+W?2i?2s$@]?2s?2t!+W?2t?3R$@]?3R?3S!+W?3S?3X$@]?3X?3Y!+W?3Y?3Z$@]?3Z?3[!+W?3[?3]$@]?3]?3^$@]?3^?3_!+W?3_?3`$@]?3`?3a$@]?3a?3b!+W?3b?5r$@]?5r?6e!+W?6e?>`$@]?>`?>r!+W?>r?@U$@]?@U?@W!+W?@W?A`$@]?A`?BY!+W?BY?Bf$@]?Bf?EO!+W?EO?ET$@]?ET?EU!+W?EU?HR$@]?HR?Hw!+W?Hw?Ic$@]?Ic?Ii!+W?Ii?JT$@]?JT?J`!+W?J`?L]$@]?L]?L`!+W?L`?Lf$@]?Lf?Lh!+W?Lh?Ln$@]?Ln?Lp!+W?Lp?Lv$@]?Lv?Lx!+W?Lx?L{$@]?L{O!+WP%GQ*]mPqr%F{rs%F{st%F{tu%F{uv%F{vw%F{wx%F{z{%F{{|%F{!O!P%F{!P!Q%F{!Q![%F{!a!b%F{!c!}%F{#O#P%F{#R#S%F{#S#T%F{#T#o%F{$p$q%F{${$|%F{%Q%R%F{%W%o%F{%p&a%F{&b0`%F{0d0p%F{1O1T%F{1[1]%F{1^1_%F{4U4Z%F{4[4]%F{4]4^%F{4`4d%F{4l4m%F{4n4q%F{4r4s%F{4t5Y%F{5Z7Q%F{7R:S%F{:[=p%F{=y>q%F{>s>t%F{>{?t%F{A`A{%F{BQBT%F{CSDP%F{DtDu%F{DuDv%F{DwGO%F{GPGQ%F{GaGb%F{GbGc%F{GjGk%F{GkGl%F{GvGy%F{G{G|%F{H^H_%F{H`IO%F{ImKj%F{KuKv%F{L`MR%F{M[M]%F{M]M^%F{MbMc%F{MhNO%F{NSNT%F{N^N_%F{NbNc%F{Nz! e%F{!#O!#P%F{!#Q!#]%F{!%W!&`%F{!&c!&d%F{!&v!&w%F{!'O!'Y%F{!'i!'p%F{!'q!'x%F{!'}!(V%F{!(X!(Y%F{!(Y!(Z%F{!(]!(s%F{!(t!({%F{!(|!(}%F{!)Q!)U%F{!)X!)Y%F{!)j!)k%F{!)x!)y%F{!)y!)z%F{!){!*O%F{!*^!*_%F{!*_!*`%F{!*s!*y%F{!*}!+O%F{!+O!+P%F{!+R!+i%F{!+j!+q%F{!+r!+s%F{!+s!+t%F{!+u!+v%F{!+v!+w%F{!+x!+y%F{!+y!+z%F{!,k!,o%F{!,p!,q%F{!-U!-X%F{!-i!-r%F{!-s!-v%F{!-w!._%F{!.`!.g%F{!.h!.i%F{!.i!.j%F{!.k!.p%F{!.s!.t%F{!/W!/X%F{!/h!/i%F{!/i!/j%F{!0_!0g%F{!0i!0j%F{!0j!0k%F{!0m!1T%F{!1U!1]%F{!1^!1_%F{!1_!1`%F{!1a!1f%F{!1i!1j%F{!2Y!2Z%F{!2Z!2[%F{!2]!2`%F{!2o!2p%F{!3R!3S%F{!3T!3Z%F{!3^!3a%F{!3b!3f%F{!3i!3j%F{!3j!3k%F{!3l!3m%F{!3n!3o%F{!3o!3p%F{!3s!3t%F{!3t!3u%F{!3x!3{%F{!4O!4[%F{!4r!4s%F{!5y!6R%F{!6S!6V%F{!6W!6o%F{!6p!6z%F{!6{!7Q%F{!7T!7U%F{!7p!7q%F{!7q!7r%F{!7x!7y%F{!7y!7z%F{!8o!8w%F{!8x!8{%F{!8|!9e%F{!9f!9p%F{!9q!9v%F{!9y!9z%F{!:l!:m%F{!:n!:o%F{!:o!:p%F{!;P!;Q%F{!;Q!;R%F{!;e!;m%F{!;n!;q%F{!;r!O!>U%F{!>Z!>m%F{!>p!?Y%F{!?Z!?d%F{!?e!?f%F{!?h!?o%F{!@{!A}%F{!BO!BP%F{!BP!BQ%F{!B^!Be%F{!Cq!Cr%F{!Cr!Cs%F{!Ct!Cu%F{!Cw!Cx%F{!Cx!Cy%F{!Cz!C{%F{!C}!DO%F{!DU!DY%F{!DZ!Db%F{!Dc!Df%F{!Dg!Dh%F{!Di!Dj%F{!Dl!Dm%F{!Dm!Dn%F{!Do!Ds%F{!Dt!Du%F{!Du!Dv%F{!EP!EQ%F{!ES!EX%F{!EY!EZ%F{!Ep!Et%F{!Ff!Fg%F{!Gx!HQ%F{!HR!Hw%F{!Id!Ii%F{!LQ!L}%F{!Mc!Md%F{!Mt!Mz%F{!NO!NS%F{!NV!NW%F{!NZ!N[%F{!N[!N]%F{!Nd!Ng%F{!Nk!Nx%F{# U# V%F{# h#!`%F{#!a#!b%F{#!g#!h%F{#!j##g%F{##h#*s%F{#*t#*x%F{#*z#+R%F{#+S#+T%F{#+U#+Y%F{#+[#,V%F{#,W#,[%F{#,^#-P%F{#-Q#-U%F{#-W#-_%F{#-`#-a%F{#-b#-f%F{#-h#-w%F{#-x#/T%F{#/U#/Y%F{#/[#0q%F{#1h#1x%F{#2Y#4R%F{#4_#Au%F{#Aw#BY%F{#BZ#Bu%F{#Bz#Di%F{#EO#E]%F{#E^#Eb%F{#Ep#FS%F{#Fb#Ft%F{#GS#Ga%F{#Gb#Ge%F{#Gt#Hz%F{#Io#Ip%F{#It#Iu%F{#K[#MW%F{#M`#NZ%F{#N[#N]%F{#Nb$ z%F{$!U$!s%F{$#x$$h%F{$$j$$o%F{$$z$%x%F{$&_$&f%F{$'p$(X%F{$(b$)i%F{$+_$+`%F{$-a$.b%F{$.s$.z%F{$0T$0s%F{$1Q$1R%F{$1R$1S%F{$1^$2[%F{$2v$3l%F{$4g$4j%F{$4t$5j%F{$7y$7}%F{$8O$8S%F{$8V$8W%F{$8W$8X%F{$8b$z%F{5>{5>|%F{5?P5?Q%F{5?Q5?R%F{5?T5?Y%F{5?[5?]%F{5?^5?_%F{5?w5?z%F{5?|5@X%F{5@`5@c%F{5@o5@u%F{5@w5@}%F{5AP5AV%F{5A`5Ag%F{5Ah5Ao%F{5Dv5Ek%F{5FY;%S%F{;%`;%w%F{;%{;'O%F{?&r?.p%F{?.r?1Q%F{?1x?2P%F{?2]?2b%F{?2g?2h%F{?2i?2s%F{?2t?3R%F{?3S?3X%F{?3Y?3Z%F{?3[?3]%F{?3]?3^%F{?3_?3`%F{?3`?3a%F{?3b?5r%F{?6e?>`%F{?>r?@U%F{?@W?A`%F{?BY?Bf%F{?EO?ET%F{?EU?HR%F{?Hw?Ic%F{?Ii?JT%F{?J`?L]%F{?L`?Lf%F{?Lh?Ln%F{?Lp?Lv%F{?Lx?L{%F{R&5Q2gzQmPOq!+oqr&4yrs&4yst&4ytu&4yuv&4yvw&4ywx%F{xz!+oz{&4y{|&4y|!O!+o!O!P&4y!P!Q&4y!Q![&4y![!a!+o!a!b&4y!b!c!+o!c!}&4y!}#O!+o#O#P&4y#P#R!+o#R#S&4y#S#T&4y#T#o&4y#o$p!+o$p$q&4y$q${!+o${$|&4y$|%Q!+o%Q%R&4y%R%W!+o%W%o&4y%o%p!+o%p&a&4y&a&b!+o&b0`&4y0`0d!+o0d0p&4y0p1O!+o1O1T&4y1T1[!+o1[1]&4y1]1^!+o1^1_&4y1_4U!+o4U4Z&4y4Z4[!+o4[4]&4y4]4^&4y4^4`!+o4`4d&4y4d4l!+o4l4m&4y4m4n!+o4n4q&4y4q4r!+o4r4s&4y4s4t!+o4t5Y&4y5Y5Z!+o5Z7Q&4y7Q7R!+o7R:S&4y:S:[!+o:[=p&4y=p=y!+o=y>q&4y>q>s!+o>s>t&4y>t>{!+o>{?t&4y?tA`!+oA`A{&4yA{BQ!+oBQBT&4yBTCS!+oCSDP&4yDPDt!+oDtDu&4yDuDv&4yDvDw!+oDwGO&4yGOGP!+oGPGQ&4yGQGa!+oGaGb&4yGbGc&4yGcGj!+oGjGk&4yGkGl&4yGlGv!+oGvGy&4yGyG{!+oG{G|&4yG|H^!+oH^H_&4yH_H`!+oH`IO&4yIOIm!+oImKj&4yKjKu!+oKuKv&4yKvL`!+oL`MR&4yMRM[!+oM[M]&4yM]M^&4yM^Mb!+oMbMc&4yMcMh!+oMhNO&4yNONS!+oNSNT&4yNTN^!+oN^N_&4yN_Nb!+oNbNc&4yNcNz!+oNz! e&4y! e!#O!+o!#O!#P&4y!#P!#Q!+o!#Q!#]&4y!#]!%W!+o!%W!&`&4y!&`!&c!+o!&c!&d&4y!&d!&v!+o!&v!&w&4y!&w!'O!+o!'O!'Y&4y!'Y!'i!+o!'i!'p&4y!'p!'q!+o!'q!'x&4y!'x!'}!+o!'}!(V&4y!(V!(X!+o!(X!(Y&4y!(Y!(Z&4y!(Z!(]!+o!(]!(s&4y!(s!(t!+o!(t!({&4y!({!(|!+o!(|!(}&4y!(}!)Q!+o!)Q!)U&4y!)U!)X!+o!)X!)Y&4y!)Y!)j!+o!)j!)k&4y!)k!)x!+o!)x!)y&4y!)y!)z&4y!)z!){!+o!){!*O&4y!*O!*^!+o!*^!*_&4y!*_!*`&4y!*`!*s!+o!*s!*y&4y!*y!*}!+o!*}!+O&4y!+O!+P&4y!+P!+R!+o!+R!+i&4y!+i!+j!+o!+j!+q&4y!+q!+r!+o!+r!+s&4y!+s!+t&4y!+t!+u!+o!+u!+v&4y!+v!+w&4y!+w!+x!+o!+x!+y&4y!+y!+z&4y!+z!,k!+o!,k!,o&4y!,o!,p!+o!,p!,q&4y!,q!-U!+o!-U!-X&4y!-X!-i!+o!-i!-r&4y!-r!-s!+o!-s!-v&4y!-v!-w!+o!-w!._&4y!._!.`!+o!.`!.g&4y!.g!.h!+o!.h!.i&4y!.i!.j&4y!.j!.k!+o!.k!.p&4y!.p!.s!+o!.s!.t&4y!.t!/W!+o!/W!/X&4y!/X!/h!+o!/h!/i&4y!/i!/j&4y!/j!0_!+o!0_!0g&4y!0g!0i!+o!0i!0j&4y!0j!0k&4y!0k!0m!+o!0m!1T&4y!1T!1U!+o!1U!1]&4y!1]!1^!+o!1^!1_&4y!1_!1`&4y!1`!1a!+o!1a!1f&4y!1f!1i!+o!1i!1j&4y!1j!2Y!+o!2Y!2Z&4y!2Z!2[&4y!2[!2]!+o!2]!2`&4y!2`!2o!+o!2o!2p&4y!2p!3R!+o!3R!3S&4y!3S!3T!+o!3T!3Z&4y!3Z!3^!+o!3^!3a&4y!3a!3b!+o!3b!3f&4y!3f!3i!+o!3i!3j&4y!3j!3k&4y!3k!3l!+o!3l!3m&4y!3m!3n!+o!3n!3o&4y!3o!3p&4y!3p!3s!+o!3s!3t&4y!3t!3u&4y!3u!3x!+o!3x!3{&4y!3{!4O!+o!4O!4[&4y!4[!4r!+o!4r!4s&4y!4s!5y!+o!5y!6R&4y!6R!6S!+o!6S!6V&4y!6V!6W!+o!6W!6o&4y!6o!6p!+o!6p!6z&4y!6z!6{!+o!6{!7Q&4y!7Q!7T!+o!7T!7U&4y!7U!7p!+o!7p!7q&4y!7q!7r&4y!7r!7x!+o!7x!7y&4y!7y!7z&4y!7z!8o!+o!8o!8w&4y!8w!8x!+o!8x!8{&4y!8{!8|!+o!8|!9e&4y!9e!9f!+o!9f!9p&4y!9p!9q!+o!9q!9v&4y!9v!9y!+o!9y!9z&4y!9z!:l!+o!:l!:m&4y!:m!:n!+o!:n!:o&4y!:o!:p&4y!:p!;P!+o!;P!;Q&4y!;Q!;R&4y!;R!;e!+o!;e!;m&4y!;m!;n!+o!;n!;q&4y!;q!;r!+o!;r!O!+o!>O!>U&4y!>U!>Z!+o!>Z!>m&4y!>m!>p!+o!>p!?Y&4y!?Y!?Z!+o!?Z!?d&4y!?d!?e!+o!?e!?f&4y!?f!?h!+o!?h!?o&4y!?o!@{!+o!@{!A}&4y!A}!BO!+o!BO!BP&4y!BP!BQ&4y!BQ!B^!+o!B^!Be&4y!Be!Cq!+o!Cq!Cr&4y!Cr!Cs&4y!Cs!Ct!+o!Ct!Cu&4y!Cu!Cw!+o!Cw!Cx&4y!Cx!Cy&4y!Cy!Cz!+o!Cz!C{&4y!C{!C}!+o!C}!DO&4y!DO!DU!+o!DU!DY&4y!DY!DZ!+o!DZ!Db&4y!Db!Dc!+o!Dc!Df&4y!Df!Dg!+o!Dg!Dh&4y!Dh!Di!+o!Di!Dj&4y!Dj!Dl!+o!Dl!Dm&4y!Dm!Dn&4y!Dn!Do!+o!Do!Ds&4y!Ds!Dt!+o!Dt!Du&4y!Du!Dv&4y!Dv!EP!+o!EP!EQ&4y!EQ!ES!+o!ES!EX&4y!EX!EY!+o!EY!EZ&4y!EZ!Ep!+o!Ep!Et&4y!Et!Ff!+o!Ff!Fg&4y!Fg!Gx!+o!Gx!HQ&4y!HQ!HR!+o!HR!Hw&4y!Hw!Id!+o!Id!Ii&4y!Ii!LQ!+o!LQ!L}&4y!L}!Mc!+o!Mc!Md&4y!Md!Mt!+o!Mt!Mz&4y!Mz!NO!+o!NO!NS&4y!NS!NV!+o!NV!NW&4y!NW!NZ!+o!NZ!N[&4y!N[!N]&4y!N]!Nd!+o!Nd!Ng&4y!Ng!Nk!+o!Nk!Nx&4y!Nx# U!+o# U# V&4y# V# h!+o# h#!`&4y#!`#!a!+o#!a#!b&4y#!b#!g!+o#!g#!h&4y#!h#!j!+o#!j##g&4y##g##h!+o##h#*s&4y#*s#*t!+o#*t#*x&4y#*x#*z!+o#*z#+R&4y#+R#+S!+o#+S#+T&4y#+T#+U!+o#+U#+Y&4y#+Y#+[!+o#+[#,V&4y#,V#,W!+o#,W#,[&4y#,[#,^!+o#,^#-P&4y#-P#-Q!+o#-Q#-U&4y#-U#-W!+o#-W#-_&4y#-_#-`!+o#-`#-a&4y#-a#-b!+o#-b#-f&4y#-f#-h!+o#-h#-w&4y#-w#-x!+o#-x#/T&4y#/T#/U!+o#/U#/Y&4y#/Y#/[!+o#/[#0q&4y#0q#1h!+o#1h#1x&4y#1x#2Y!+o#2Y#4R&4y#4R#4_!+o#4_#Au&4y#Au#Aw!+o#Aw#BY&4y#BY#BZ!+o#BZ#Bu&4y#Bu#Bz!+o#Bz#Di&4y#Di#EO!+o#EO#E]&4y#E]#E^!+o#E^#Eb&4y#Eb#Ep!+o#Ep#FS&4y#FS#Fb!+o#Fb#Ft&4y#Ft#GS!+o#GS#Ga&4y#Ga#Gb!+o#Gb#Ge&4y#Ge#Gt!+o#Gt#Hz&4y#Hz#Io!+o#Io#Ip&4y#Ip#It!+o#It#Iu&4y#Iu#K[!+o#K[#MW&4y#MW#M`!+o#M`#NZ&4y#NZ#N[!+o#N[#N]&4y#N]#Nb!+o#Nb$ z&4y$ z$!U!+o$!U$!s&4y$!s$#x!+o$#x$$h&4y$$h$$j!+o$$j$$o&4y$$o$$z!+o$$z$%x&4y$%x$&_!+o$&_$&f&4y$&f$'p!+o$'p$(X&4y$(X$(b!+o$(b$)i&4y$)i$+_!+o$+_$+`&4y$+`$-a!+o$-a$.b&4y$.b$.s!+o$.s$.z&4y$.z$0T!+o$0T$0s&4y$0s$1Q!+o$1Q$1R&4y$1R$1S&4y$1S$1^!+o$1^$2[&4y$2[$2v!+o$2v$3l&4y$3l$4g!+o$4g$4j&4y$4j$4t!+o$4t$5j&4y$5j$7y!+o$7y$7}&4y$7}$8O!+o$8O$8S&4y$8S$8V!+o$8V$8W&4y$8W$8X&4y$8X$8b!+o$8b$z&4y5>z5>{!+o5>{5>|&4y5>|5?P!+o5?P5?Q&4y5?Q5?R&4y5?R5?T!+o5?T5?Y&4y5?Y5?[!+o5?[5?]&4y5?]5?^!+o5?^5?_&4y5?_5?w!+o5?w5?z&4y5?z5?|!+o5?|5@X&4y5@X5@`!+o5@`5@c&4y5@c5@o!+o5@o5@u&4y5@u5@w!+o5@w5@}&4y5@}5AP!+o5AP5AV&4y5AV5A`!+o5A`5Ag&4y5Ag5Ah!+o5Ah5Ao&4y5Ao5Dv!+o5Dv5Ek&4y5Ek5FY!+o5FY;%S&4y;%S;%`!+o;%`;%w&4y;%w;%{!+o;%{;'O&4y;'O;'S!+o;'S;=`!,Q<%l?&r!+o?&r?.p&4y?.p?.r!+o?.r?1Q&4y?1Q?1x!+o?1x?2P&4y?2P?2]!+o?2]?2b&4y?2b?2g!+o?2g?2h&4y?2h?2i!+o?2i?2s&4y?2s?2t!+o?2t?3R&4y?3R?3S!+o?3S?3X&4y?3X?3Y!+o?3Y?3Z&4y?3Z?3[!+o?3[?3]&4y?3]?3^&4y?3^?3_!+o?3_?3`&4y?3`?3a&4y?3a?3b!+o?3b?5r&4y?5r?6e!+o?6e?>`&4y?>`?>r!+o?>r?@U&4y?@U?@W!+o?@W?A`&4y?A`?BY!+o?BY?Bf&4y?Bf?EO!+o?EO?ET&4y?ET?EU!+o?EU?HR&4y?HR?Hw!+o?Hw?Ic&4y?Ic?Ii!+o?Ii?JT&4y?JT?J`!+o?J`?L]&4y?L]?L`!+o?L`?Lf&4y?Lf?Lh!+o?Lh?Ln&4y?Ln?Lp!+o?Lp?Lv&4y?Lv?Lx!+o?Lx?L{&4y?L{O!+o]';r2g!OW|SmPOq!,^qr';irs$@]st';itu';iuv';ivw';iwx';ixz!,^z{';i{|';i|!O!,^!O!P';i!P!Q';i!Q![';i![!a!,^!a!b';i!b!c!,^!c!}';i!}#O!,^#O#P';i#P#R!,^#R#S';i#S#T(BZ#T#o';i#o$p!,^$p$q';i$q${!,^${$|';i$|%Q!,^%Q%R';i%R%W!,^%W%o';i%o%p!,^%p&a';i&a&b!,^&b0`';i0`0d!,^0d0p';i0p1O!,^1O1T';i1T1[!,^1[1]';i1]1^!,^1^1_';i1_4U!,^4U4Z';i4Z4[!,^4[4]';i4]4^';i4^4`!,^4`4d';i4d4l!,^4l4m';i4m4n!,^4n4q';i4q4r!,^4r4s';i4s4t!,^4t5Y';i5Y5Z!,^5Z7Q';i7Q7R!,^7R:S';i:S:[!,^:[=p';i=p=y!,^=y>q';i>q>s!,^>s>t';i>t>{!,^>{?t';i?tA`!,^A`A{';iA{BQ!,^BQBT';iBTCS!,^CSDP';iDPDt!,^DtDu';iDuDv';iDvDw!,^DwGO';iGOGP!,^GPGQ';iGQGa!,^GaGb';iGbGc';iGcGj!,^GjGk';iGkGl';iGlGv!,^GvGy';iGyG{!,^G{G|';iG|H^!,^H^H_';iH_H`!,^H`IO';iIOIm!,^ImKj';iKjKu!,^KuKv';iKvL`!,^L`MR';iMRM[!,^M[M]';iM]M^';iM^Mb!,^MbMc';iMcMh!,^MhNO';iNONS!,^NSNT';iNTN^!,^N^N_';iN_Nb!,^NbNc';iNcNz!,^Nz! e';i! e!#O!,^!#O!#P';i!#P!#Q!,^!#Q!#]';i!#]!%W!,^!%W!&`';i!&`!&c!,^!&c!&d';i!&d!&v!,^!&v!&w';i!&w!'O!,^!'O!'Y';i!'Y!'i!,^!'i!'p';i!'p!'q!,^!'q!'x';i!'x!'}!,^!'}!(V';i!(V!(X!,^!(X!(Y';i!(Y!(Z';i!(Z!(]!,^!(]!(s';i!(s!(t!,^!(t!({';i!({!(|!,^!(|!(}';i!(}!)Q!,^!)Q!)U';i!)U!)X!,^!)X!)Y';i!)Y!)j!,^!)j!)k';i!)k!)x!,^!)x!)y';i!)y!)z';i!)z!){!,^!){!*O';i!*O!*^!,^!*^!*_';i!*_!*`';i!*`!*s!,^!*s!*y';i!*y!*}!,^!*}!+O';i!+O!+P';i!+P!+R!,^!+R!+i';i!+i!+j!,^!+j!+q';i!+q!+r!,^!+r!+s';i!+s!+t';i!+t!+u!,^!+u!+v';i!+v!+w';i!+w!+x!,^!+x!+y';i!+y!+z';i!+z!,k!,^!,k!,o';i!,o!,p!,^!,p!,q';i!,q!-U!,^!-U!-X';i!-X!-i!,^!-i!-r';i!-r!-s!,^!-s!-v';i!-v!-w!,^!-w!._';i!._!.`!,^!.`!.g';i!.g!.h!,^!.h!.i';i!.i!.j';i!.j!.k!,^!.k!.p';i!.p!.s!,^!.s!.t';i!.t!/W!,^!/W!/X';i!/X!/h!,^!/h!/i';i!/i!/j';i!/j!0_!,^!0_!0g';i!0g!0i!,^!0i!0j';i!0j!0k';i!0k!0m!,^!0m!1T';i!1T!1U!,^!1U!1]';i!1]!1^!,^!1^!1_';i!1_!1`';i!1`!1a!,^!1a!1f';i!1f!1i!,^!1i!1j';i!1j!2Y!,^!2Y!2Z';i!2Z!2[';i!2[!2]!,^!2]!2`';i!2`!2o!,^!2o!2p';i!2p!3R!,^!3R!3S';i!3S!3T!,^!3T!3Z';i!3Z!3^!,^!3^!3a';i!3a!3b!,^!3b!3f';i!3f!3i!,^!3i!3j';i!3j!3k';i!3k!3l!,^!3l!3m';i!3m!3n!,^!3n!3o';i!3o!3p';i!3p!3s!,^!3s!3t';i!3t!3u';i!3u!3x!,^!3x!3{';i!3{!4O!,^!4O!4[';i!4[!4r!,^!4r!4s';i!4s!5y!,^!5y!6R';i!6R!6S!,^!6S!6V';i!6V!6W!,^!6W!6o';i!6o!6p!,^!6p!6z';i!6z!6{!,^!6{!7Q';i!7Q!7T!,^!7T!7U';i!7U!7p!,^!7p!7q';i!7q!7r';i!7r!7x!,^!7x!7y';i!7y!7z';i!7z!8o!,^!8o!8w';i!8w!8x!,^!8x!8{';i!8{!8|!,^!8|!9e';i!9e!9f!,^!9f!9p';i!9p!9q!,^!9q!9v';i!9v!9y!,^!9y!9z';i!9z!:l!,^!:l!:m';i!:m!:n!,^!:n!:o';i!:o!:p';i!:p!;P!,^!;P!;Q';i!;Q!;R';i!;R!;e!,^!;e!;m';i!;m!;n!,^!;n!;q';i!;q!;r!,^!;r!O!,^!>O!>U';i!>U!>Z!,^!>Z!>m';i!>m!>p!,^!>p!?Y';i!?Y!?Z!,^!?Z!?d';i!?d!?e!,^!?e!?f';i!?f!?h!,^!?h!?o';i!?o!@{!,^!@{!A}';i!A}!BO!,^!BO!BP';i!BP!BQ';i!BQ!B^!,^!B^!Be';i!Be!Cq!,^!Cq!Cr';i!Cr!Cs';i!Cs!Ct!,^!Ct!Cu';i!Cu!Cw!,^!Cw!Cx';i!Cx!Cy';i!Cy!Cz!,^!Cz!C{';i!C{!C}!,^!C}!DO';i!DO!DU!,^!DU!DY';i!DY!DZ!,^!DZ!Db';i!Db!Dc!,^!Dc!Df';i!Df!Dg!,^!Dg!Dh';i!Dh!Di!,^!Di!Dj';i!Dj!Dl!,^!Dl!Dm';i!Dm!Dn';i!Dn!Do!,^!Do!Ds';i!Ds!Dt!,^!Dt!Du';i!Du!Dv';i!Dv!EP!,^!EP!EQ';i!EQ!ES!,^!ES!EX';i!EX!EY!,^!EY!EZ';i!EZ!Ep!,^!Ep!Et';i!Et!Ff!,^!Ff!Fg';i!Fg!Gx!,^!Gx!HQ';i!HQ!HR!,^!HR!Hw';i!Hw!Id!,^!Id!Ii';i!Ii!LQ!,^!LQ!L}';i!L}!Mc!,^!Mc!Md';i!Md!Mt!,^!Mt!Mz';i!Mz!NO!,^!NO!NS';i!NS!NV!,^!NV!NW';i!NW!NZ!,^!NZ!N[';i!N[!N]';i!N]!Nd!,^!Nd!Ng';i!Ng!Nk!,^!Nk!Nx';i!Nx# U!,^# U# V';i# V# h!,^# h#!`';i#!`#!a!,^#!a#!b';i#!b#!g!,^#!g#!h';i#!h#!j!,^#!j##g';i##g##h!,^##h#*s';i#*s#*t!,^#*t#*x';i#*x#*z!,^#*z#+R';i#+R#+S!,^#+S#+T';i#+T#+U!,^#+U#+Y';i#+Y#+[!,^#+[#,V';i#,V#,W!,^#,W#,[';i#,[#,^!,^#,^#-P';i#-P#-Q!,^#-Q#-U';i#-U#-W!,^#-W#-_';i#-_#-`!,^#-`#-a';i#-a#-b!,^#-b#-f';i#-f#-h!,^#-h#-w';i#-w#-x!,^#-x#/T';i#/T#/U!,^#/U#/Y';i#/Y#/[!,^#/[#0q';i#0q#1h!,^#1h#1x';i#1x#2Y!,^#2Y#4R';i#4R#4_!,^#4_#Au';i#Au#Aw!,^#Aw#BY';i#BY#BZ!,^#BZ#Bu';i#Bu#Bz!,^#Bz#Di';i#Di#EO!,^#EO#E]';i#E]#E^!,^#E^#Eb';i#Eb#Ep!,^#Ep#FS';i#FS#Fb!,^#Fb#Ft';i#Ft#GS!,^#GS#Ga';i#Ga#Gb!,^#Gb#Ge';i#Ge#Gt!,^#Gt#Hz';i#Hz#Io!,^#Io#Ip';i#Ip#It!,^#It#Iu';i#Iu#K[!,^#K[#MW';i#MW#M`!,^#M`#NZ';i#NZ#N[!,^#N[#N]';i#N]#Nb!,^#Nb$ z';i$ z$!U!,^$!U$!s';i$!s$#x!,^$#x$$h';i$$h$$j!,^$$j$$o';i$$o$$z!,^$$z$%x';i$%x$&_!,^$&_$&f';i$&f$'p!,^$'p$(X';i$(X$(b!,^$(b$)i';i$)i$+_!,^$+_$+`';i$+`$-a!,^$-a$.b';i$.b$.s!,^$.s$.z';i$.z$0T!,^$0T$0s';i$0s$1Q!,^$1Q$1R';i$1R$1S';i$1S$1^!,^$1^$2[';i$2[$2v!,^$2v$3l';i$3l$4g!,^$4g$4j';i$4j$4t!,^$4t$5j';i$5j$7y!,^$7y$7}';i$7}$8O!,^$8O$8S';i$8S$8V!,^$8V$8W';i$8W$8X';i$8X$8b!,^$8b$z';i5>z5>{!,^5>{5>|';i5>|5?P!,^5?P5?Q';i5?Q5?R';i5?R5?T!,^5?T5?Y';i5?Y5?[!,^5?[5?]';i5?]5?^!,^5?^5?_';i5?_5?w!,^5?w5?z';i5?z5?|!,^5?|5@X';i5@X5@`!,^5@`5@c';i5@c5@o!,^5@o5@u';i5@u5@w!,^5@w5@}';i5@}5AP!,^5AP5AV';i5AV5A`!,^5A`5Ag';i5Ag5Ah!,^5Ah5Ao';i5Ao5Dv!,^5Dv5Ek';i5Ek5FY!,^5FY;%S';i;%S;%`!,^;%`;%w';i;%w;%{!,^;%{;'O';i;'O;'S!,^;'S;=`!-c<%l?&r!,^?&r?.p';i?.p?.r!,^?.r?1Q';i?1Q?1x!,^?1x?2P';i?2P?2]!,^?2]?2b';i?2b?2g!,^?2g?2h';i?2h?2i!,^?2i?2s';i?2s?2t!,^?2t?3R';i?3R?3S!,^?3S?3X';i?3X?3Y!,^?3Y?3Z';i?3Z?3[!,^?3[?3]';i?3]?3^';i?3^?3_!,^?3_?3`';i?3`?3a';i?3a?3b!,^?3b?5r';i?5r?6e!,^?6e?>`';i?>`?>r!,^?>r?@U';i?@U?@W!,^?@W?A`';i?A`?BY!,^?BY?Bf';i?Bf?EO!,^?EO?ET';i?ET?EU!,^?EU?HR';i?HR?Hw!,^?Hw?Ic';i?Ic?Ii!,^?Ii?JT';i?JT?J`!,^?J`?L]';i?L]?L`!,^?L`?Lf';i?Lf?Lh!,^?Lh?Ln';i?Ln?Lp!,^?Lp?Lv';i?Lv?Lx!,^?Lx?L{';i?L{O!,^T(Bb2g|SmPOq!,zqr(BZrs%F{st(BZtu(BZuv(BZvw(BZwx(BZxz!,zz{(BZ{|(BZ|!O!,z!O!P(BZ!P!Q(BZ!Q![(BZ![!a!,z!a!b(BZ!b!c!,z!c!}(BZ!}#O!,z#O#P(BZ#P#R!,z#R#S(BZ#S#T(BZ#T#o(BZ#o$p!,z$p$q(BZ$q${!,z${$|(BZ$|%Q!,z%Q%R(BZ%R%W!,z%W%o(BZ%o%p!,z%p&a(BZ&a&b!,z&b0`(BZ0`0d!,z0d0p(BZ0p1O!,z1O1T(BZ1T1[!,z1[1](BZ1]1^!,z1^1_(BZ1_4U!,z4U4Z(BZ4Z4[!,z4[4](BZ4]4^(BZ4^4`!,z4`4d(BZ4d4l!,z4l4m(BZ4m4n!,z4n4q(BZ4q4r!,z4r4s(BZ4s4t!,z4t5Y(BZ5Y5Z!,z5Z7Q(BZ7Q7R!,z7R:S(BZ:S:[!,z:[=p(BZ=p=y!,z=y>q(BZ>q>s!,z>s>t(BZ>t>{!,z>{?t(BZ?tA`!,zA`A{(BZA{BQ!,zBQBT(BZBTCS!,zCSDP(BZDPDt!,zDtDu(BZDuDv(BZDvDw!,zDwGO(BZGOGP!,zGPGQ(BZGQGa!,zGaGb(BZGbGc(BZGcGj!,zGjGk(BZGkGl(BZGlGv!,zGvGy(BZGyG{!,zG{G|(BZG|H^!,zH^H_(BZH_H`!,zH`IO(BZIOIm!,zImKj(BZKjKu!,zKuKv(BZKvL`!,zL`MR(BZMRM[!,zM[M](BZM]M^(BZM^Mb!,zMbMc(BZMcMh!,zMhNO(BZNONS!,zNSNT(BZNTN^!,zN^N_(BZN_Nb!,zNbNc(BZNcNz!,zNz! e(BZ! e!#O!,z!#O!#P(BZ!#P!#Q!,z!#Q!#](BZ!#]!%W!,z!%W!&`(BZ!&`!&c!,z!&c!&d(BZ!&d!&v!,z!&v!&w(BZ!&w!'O!,z!'O!'Y(BZ!'Y!'i!,z!'i!'p(BZ!'p!'q!,z!'q!'x(BZ!'x!'}!,z!'}!(V(BZ!(V!(X!,z!(X!(Y(BZ!(Y!(Z(BZ!(Z!(]!,z!(]!(s(BZ!(s!(t!,z!(t!({(BZ!({!(|!,z!(|!(}(BZ!(}!)Q!,z!)Q!)U(BZ!)U!)X!,z!)X!)Y(BZ!)Y!)j!,z!)j!)k(BZ!)k!)x!,z!)x!)y(BZ!)y!)z(BZ!)z!){!,z!){!*O(BZ!*O!*^!,z!*^!*_(BZ!*_!*`(BZ!*`!*s!,z!*s!*y(BZ!*y!*}!,z!*}!+O(BZ!+O!+P(BZ!+P!+R!,z!+R!+i(BZ!+i!+j!,z!+j!+q(BZ!+q!+r!,z!+r!+s(BZ!+s!+t(BZ!+t!+u!,z!+u!+v(BZ!+v!+w(BZ!+w!+x!,z!+x!+y(BZ!+y!+z(BZ!+z!,k!,z!,k!,o(BZ!,o!,p!,z!,p!,q(BZ!,q!-U!,z!-U!-X(BZ!-X!-i!,z!-i!-r(BZ!-r!-s!,z!-s!-v(BZ!-v!-w!,z!-w!._(BZ!._!.`!,z!.`!.g(BZ!.g!.h!,z!.h!.i(BZ!.i!.j(BZ!.j!.k!,z!.k!.p(BZ!.p!.s!,z!.s!.t(BZ!.t!/W!,z!/W!/X(BZ!/X!/h!,z!/h!/i(BZ!/i!/j(BZ!/j!0_!,z!0_!0g(BZ!0g!0i!,z!0i!0j(BZ!0j!0k(BZ!0k!0m!,z!0m!1T(BZ!1T!1U!,z!1U!1](BZ!1]!1^!,z!1^!1_(BZ!1_!1`(BZ!1`!1a!,z!1a!1f(BZ!1f!1i!,z!1i!1j(BZ!1j!2Y!,z!2Y!2Z(BZ!2Z!2[(BZ!2[!2]!,z!2]!2`(BZ!2`!2o!,z!2o!2p(BZ!2p!3R!,z!3R!3S(BZ!3S!3T!,z!3T!3Z(BZ!3Z!3^!,z!3^!3a(BZ!3a!3b!,z!3b!3f(BZ!3f!3i!,z!3i!3j(BZ!3j!3k(BZ!3k!3l!,z!3l!3m(BZ!3m!3n!,z!3n!3o(BZ!3o!3p(BZ!3p!3s!,z!3s!3t(BZ!3t!3u(BZ!3u!3x!,z!3x!3{(BZ!3{!4O!,z!4O!4[(BZ!4[!4r!,z!4r!4s(BZ!4s!5y!,z!5y!6R(BZ!6R!6S!,z!6S!6V(BZ!6V!6W!,z!6W!6o(BZ!6o!6p!,z!6p!6z(BZ!6z!6{!,z!6{!7Q(BZ!7Q!7T!,z!7T!7U(BZ!7U!7p!,z!7p!7q(BZ!7q!7r(BZ!7r!7x!,z!7x!7y(BZ!7y!7z(BZ!7z!8o!,z!8o!8w(BZ!8w!8x!,z!8x!8{(BZ!8{!8|!,z!8|!9e(BZ!9e!9f!,z!9f!9p(BZ!9p!9q!,z!9q!9v(BZ!9v!9y!,z!9y!9z(BZ!9z!:l!,z!:l!:m(BZ!:m!:n!,z!:n!:o(BZ!:o!:p(BZ!:p!;P!,z!;P!;Q(BZ!;Q!;R(BZ!;R!;e!,z!;e!;m(BZ!;m!;n!,z!;n!;q(BZ!;q!;r!,z!;r!O!,z!>O!>U(BZ!>U!>Z!,z!>Z!>m(BZ!>m!>p!,z!>p!?Y(BZ!?Y!?Z!,z!?Z!?d(BZ!?d!?e!,z!?e!?f(BZ!?f!?h!,z!?h!?o(BZ!?o!@{!,z!@{!A}(BZ!A}!BO!,z!BO!BP(BZ!BP!BQ(BZ!BQ!B^!,z!B^!Be(BZ!Be!Cq!,z!Cq!Cr(BZ!Cr!Cs(BZ!Cs!Ct!,z!Ct!Cu(BZ!Cu!Cw!,z!Cw!Cx(BZ!Cx!Cy(BZ!Cy!Cz!,z!Cz!C{(BZ!C{!C}!,z!C}!DO(BZ!DO!DU!,z!DU!DY(BZ!DY!DZ!,z!DZ!Db(BZ!Db!Dc!,z!Dc!Df(BZ!Df!Dg!,z!Dg!Dh(BZ!Dh!Di!,z!Di!Dj(BZ!Dj!Dl!,z!Dl!Dm(BZ!Dm!Dn(BZ!Dn!Do!,z!Do!Ds(BZ!Ds!Dt!,z!Dt!Du(BZ!Du!Dv(BZ!Dv!EP!,z!EP!EQ(BZ!EQ!ES!,z!ES!EX(BZ!EX!EY!,z!EY!EZ(BZ!EZ!Ep!,z!Ep!Et(BZ!Et!Ff!,z!Ff!Fg(BZ!Fg!Gx!,z!Gx!HQ(BZ!HQ!HR!,z!HR!Hw(BZ!Hw!Id!,z!Id!Ii(BZ!Ii!LQ!,z!LQ!L}(BZ!L}!Mc!,z!Mc!Md(BZ!Md!Mt!,z!Mt!Mz(BZ!Mz!NO!,z!NO!NS(BZ!NS!NV!,z!NV!NW(BZ!NW!NZ!,z!NZ!N[(BZ!N[!N](BZ!N]!Nd!,z!Nd!Ng(BZ!Ng!Nk!,z!Nk!Nx(BZ!Nx# U!,z# U# V(BZ# V# h!,z# h#!`(BZ#!`#!a!,z#!a#!b(BZ#!b#!g!,z#!g#!h(BZ#!h#!j!,z#!j##g(BZ##g##h!,z##h#*s(BZ#*s#*t!,z#*t#*x(BZ#*x#*z!,z#*z#+R(BZ#+R#+S!,z#+S#+T(BZ#+T#+U!,z#+U#+Y(BZ#+Y#+[!,z#+[#,V(BZ#,V#,W!,z#,W#,[(BZ#,[#,^!,z#,^#-P(BZ#-P#-Q!,z#-Q#-U(BZ#-U#-W!,z#-W#-_(BZ#-_#-`!,z#-`#-a(BZ#-a#-b!,z#-b#-f(BZ#-f#-h!,z#-h#-w(BZ#-w#-x!,z#-x#/T(BZ#/T#/U!,z#/U#/Y(BZ#/Y#/[!,z#/[#0q(BZ#0q#1h!,z#1h#1x(BZ#1x#2Y!,z#2Y#4R(BZ#4R#4_!,z#4_#Au(BZ#Au#Aw!,z#Aw#BY(BZ#BY#BZ!,z#BZ#Bu(BZ#Bu#Bz!,z#Bz#Di(BZ#Di#EO!,z#EO#E](BZ#E]#E^!,z#E^#Eb(BZ#Eb#Ep!,z#Ep#FS(BZ#FS#Fb!,z#Fb#Ft(BZ#Ft#GS!,z#GS#Ga(BZ#Ga#Gb!,z#Gb#Ge(BZ#Ge#Gt!,z#Gt#Hz(BZ#Hz#Io!,z#Io#Ip(BZ#Ip#It!,z#It#Iu(BZ#Iu#K[!,z#K[#MW(BZ#MW#M`!,z#M`#NZ(BZ#NZ#N[!,z#N[#N](BZ#N]#Nb!,z#Nb$ z(BZ$ z$!U!,z$!U$!s(BZ$!s$#x!,z$#x$$h(BZ$$h$$j!,z$$j$$o(BZ$$o$$z!,z$$z$%x(BZ$%x$&_!,z$&_$&f(BZ$&f$'p!,z$'p$(X(BZ$(X$(b!,z$(b$)i(BZ$)i$+_!,z$+_$+`(BZ$+`$-a!,z$-a$.b(BZ$.b$.s!,z$.s$.z(BZ$.z$0T!,z$0T$0s(BZ$0s$1Q!,z$1Q$1R(BZ$1R$1S(BZ$1S$1^!,z$1^$2[(BZ$2[$2v!,z$2v$3l(BZ$3l$4g!,z$4g$4j(BZ$4j$4t!,z$4t$5j(BZ$5j$7y!,z$7y$7}(BZ$7}$8O!,z$8O$8S(BZ$8S$8V!,z$8V$8W(BZ$8W$8X(BZ$8X$8b!,z$8b$z(BZ5>z5>{!,z5>{5>|(BZ5>|5?P!,z5?P5?Q(BZ5?Q5?R(BZ5?R5?T!,z5?T5?Y(BZ5?Y5?[!,z5?[5?](BZ5?]5?^!,z5?^5?_(BZ5?_5?w!,z5?w5?z(BZ5?z5?|!,z5?|5@X(BZ5@X5@`!,z5@`5@c(BZ5@c5@o!,z5@o5@u(BZ5@u5@w!,z5@w5@}(BZ5@}5AP!,z5AP5AV(BZ5AV5A`!,z5A`5Ag(BZ5Ag5Ah!,z5Ah5Ao(BZ5Ao5Dv!,z5Dv5Ek(BZ5Ek5FY!,z5FY;%S(BZ;%S;%`!,z;%`;%w(BZ;%w;%{!,z;%{;'O(BZ;'O;'S!,z;'S;=`!-]<%l?&r!,z?&r?.p(BZ?.p?.r!,z?.r?1Q(BZ?1Q?1x!,z?1x?2P(BZ?2P?2]!,z?2]?2b(BZ?2b?2g!,z?2g?2h(BZ?2h?2i!,z?2i?2s(BZ?2s?2t!,z?2t?3R(BZ?3R?3S!,z?3S?3X(BZ?3X?3Y!,z?3Y?3Z(BZ?3Z?3[!,z?3[?3](BZ?3]?3^(BZ?3^?3_!,z?3_?3`(BZ?3`?3a(BZ?3a?3b!,z?3b?5r(BZ?5r?6e!,z?6e?>`(BZ?>`?>r!,z?>r?@U(BZ?@U?@W!,z?@W?A`(BZ?A`?BY!,z?BY?Bf(BZ?Bf?EO!,z?EO?ET(BZ?ET?EU!,z?EU?HR(BZ?HR?Hw!,z?Hw?Ic(BZ?Ic?Ii!,z?Ii?JT(BZ?JT?J`!,z?J`?L](BZ?L]?L`!,z?L`?Lf(BZ?Lf?Lh!,z?Lh?Ln(BZ?Ln?Lp!,z?Lp?Lv(BZ?Lv?Lx!,z?Lx?L{(BZ?L{O!,zV)IS2g|SzQmPOq!-iqr)Hyrs&4yst)Hytu)Hyuv)Hyvw)Hywx(BZxz!-iz{)Hy{|)Hy|!O!-i!O!P)Hy!P!Q)Hy!Q![)Hy![!a!-i!a!b)Hy!b!c!-i!c!})Hy!}#O!-i#O#P)Hy#P#R!-i#R#S)Hy#S#T)Hy#T#o)Hy#o$p!-i$p$q)Hy$q${!-i${$|)Hy$|%Q!-i%Q%R)Hy%R%W!-i%W%o)Hy%o%p!-i%p&a)Hy&a&b!-i&b0`)Hy0`0d!-i0d0p)Hy0p1O!-i1O1T)Hy1T1[!-i1[1])Hy1]1^!-i1^1_)Hy1_4U!-i4U4Z)Hy4Z4[!-i4[4])Hy4]4^)Hy4^4`!-i4`4d)Hy4d4l!-i4l4m)Hy4m4n!-i4n4q)Hy4q4r!-i4r4s)Hy4s4t!-i4t5Y)Hy5Y5Z!-i5Z7Q)Hy7Q7R!-i7R:S)Hy:S:[!-i:[=p)Hy=p=y!-i=y>q)Hy>q>s!-i>s>t)Hy>t>{!-i>{?t)Hy?tA`!-iA`A{)HyA{BQ!-iBQBT)HyBTCS!-iCSDP)HyDPDt!-iDtDu)HyDuDv)HyDvDw!-iDwGO)HyGOGP!-iGPGQ)HyGQGa!-iGaGb)HyGbGc)HyGcGj!-iGjGk)HyGkGl)HyGlGv!-iGvGy)HyGyG{!-iG{G|)HyG|H^!-iH^H_)HyH_H`!-iH`IO)HyIOIm!-iImKj)HyKjKu!-iKuKv)HyKvL`!-iL`MR)HyMRM[!-iM[M])HyM]M^)HyM^Mb!-iMbMc)HyMcMh!-iMhNO)HyNONS!-iNSNT)HyNTN^!-iN^N_)HyN_Nb!-iNbNc)HyNcNz!-iNz! e)Hy! e!#O!-i!#O!#P)Hy!#P!#Q!-i!#Q!#])Hy!#]!%W!-i!%W!&`)Hy!&`!&c!-i!&c!&d)Hy!&d!&v!-i!&v!&w)Hy!&w!'O!-i!'O!'Y)Hy!'Y!'i!-i!'i!'p)Hy!'p!'q!-i!'q!'x)Hy!'x!'}!-i!'}!(V)Hy!(V!(X!-i!(X!(Y)Hy!(Y!(Z)Hy!(Z!(]!-i!(]!(s)Hy!(s!(t!-i!(t!({)Hy!({!(|!-i!(|!(})Hy!(}!)Q!-i!)Q!)U)Hy!)U!)X!-i!)X!)Y)Hy!)Y!)j!-i!)j!)k)Hy!)k!)x!-i!)x!)y)Hy!)y!)z)Hy!)z!){!-i!){!*O)Hy!*O!*^!-i!*^!*_)Hy!*_!*`)Hy!*`!*s!-i!*s!*y)Hy!*y!*}!-i!*}!+O)Hy!+O!+P)Hy!+P!+R!-i!+R!+i)Hy!+i!+j!-i!+j!+q)Hy!+q!+r!-i!+r!+s)Hy!+s!+t)Hy!+t!+u!-i!+u!+v)Hy!+v!+w)Hy!+w!+x!-i!+x!+y)Hy!+y!+z)Hy!+z!,k!-i!,k!,o)Hy!,o!,p!-i!,p!,q)Hy!,q!-U!-i!-U!-X)Hy!-X!-i!-i!-i!-r)Hy!-r!-s!-i!-s!-v)Hy!-v!-w!-i!-w!._)Hy!._!.`!-i!.`!.g)Hy!.g!.h!-i!.h!.i)Hy!.i!.j)Hy!.j!.k!-i!.k!.p)Hy!.p!.s!-i!.s!.t)Hy!.t!/W!-i!/W!/X)Hy!/X!/h!-i!/h!/i)Hy!/i!/j)Hy!/j!0_!-i!0_!0g)Hy!0g!0i!-i!0i!0j)Hy!0j!0k)Hy!0k!0m!-i!0m!1T)Hy!1T!1U!-i!1U!1])Hy!1]!1^!-i!1^!1_)Hy!1_!1`)Hy!1`!1a!-i!1a!1f)Hy!1f!1i!-i!1i!1j)Hy!1j!2Y!-i!2Y!2Z)Hy!2Z!2[)Hy!2[!2]!-i!2]!2`)Hy!2`!2o!-i!2o!2p)Hy!2p!3R!-i!3R!3S)Hy!3S!3T!-i!3T!3Z)Hy!3Z!3^!-i!3^!3a)Hy!3a!3b!-i!3b!3f)Hy!3f!3i!-i!3i!3j)Hy!3j!3k)Hy!3k!3l!-i!3l!3m)Hy!3m!3n!-i!3n!3o)Hy!3o!3p)Hy!3p!3s!-i!3s!3t)Hy!3t!3u)Hy!3u!3x!-i!3x!3{)Hy!3{!4O!-i!4O!4[)Hy!4[!4r!-i!4r!4s)Hy!4s!5y!-i!5y!6R)Hy!6R!6S!-i!6S!6V)Hy!6V!6W!-i!6W!6o)Hy!6o!6p!-i!6p!6z)Hy!6z!6{!-i!6{!7Q)Hy!7Q!7T!-i!7T!7U)Hy!7U!7p!-i!7p!7q)Hy!7q!7r)Hy!7r!7x!-i!7x!7y)Hy!7y!7z)Hy!7z!8o!-i!8o!8w)Hy!8w!8x!-i!8x!8{)Hy!8{!8|!-i!8|!9e)Hy!9e!9f!-i!9f!9p)Hy!9p!9q!-i!9q!9v)Hy!9v!9y!-i!9y!9z)Hy!9z!:l!-i!:l!:m)Hy!:m!:n!-i!:n!:o)Hy!:o!:p)Hy!:p!;P!-i!;P!;Q)Hy!;Q!;R)Hy!;R!;e!-i!;e!;m)Hy!;m!;n!-i!;n!;q)Hy!;q!;r!-i!;r!O!-i!>O!>U)Hy!>U!>Z!-i!>Z!>m)Hy!>m!>p!-i!>p!?Y)Hy!?Y!?Z!-i!?Z!?d)Hy!?d!?e!-i!?e!?f)Hy!?f!?h!-i!?h!?o)Hy!?o!@{!-i!@{!A})Hy!A}!BO!-i!BO!BP)Hy!BP!BQ)Hy!BQ!B^!-i!B^!Be)Hy!Be!Cq!-i!Cq!Cr)Hy!Cr!Cs)Hy!Cs!Ct!-i!Ct!Cu)Hy!Cu!Cw!-i!Cw!Cx)Hy!Cx!Cy)Hy!Cy!Cz!-i!Cz!C{)Hy!C{!C}!-i!C}!DO)Hy!DO!DU!-i!DU!DY)Hy!DY!DZ!-i!DZ!Db)Hy!Db!Dc!-i!Dc!Df)Hy!Df!Dg!-i!Dg!Dh)Hy!Dh!Di!-i!Di!Dj)Hy!Dj!Dl!-i!Dl!Dm)Hy!Dm!Dn)Hy!Dn!Do!-i!Do!Ds)Hy!Ds!Dt!-i!Dt!Du)Hy!Du!Dv)Hy!Dv!EP!-i!EP!EQ)Hy!EQ!ES!-i!ES!EX)Hy!EX!EY!-i!EY!EZ)Hy!EZ!Ep!-i!Ep!Et)Hy!Et!Ff!-i!Ff!Fg)Hy!Fg!Gx!-i!Gx!HQ)Hy!HQ!HR!-i!HR!Hw)Hy!Hw!Id!-i!Id!Ii)Hy!Ii!LQ!-i!LQ!L})Hy!L}!Mc!-i!Mc!Md)Hy!Md!Mt!-i!Mt!Mz)Hy!Mz!NO!-i!NO!NS)Hy!NS!NV!-i!NV!NW)Hy!NW!NZ!-i!NZ!N[)Hy!N[!N])Hy!N]!Nd!-i!Nd!Ng)Hy!Ng!Nk!-i!Nk!Nx)Hy!Nx# U!-i# U# V)Hy# V# h!-i# h#!`)Hy#!`#!a!-i#!a#!b)Hy#!b#!g!-i#!g#!h)Hy#!h#!j!-i#!j##g)Hy##g##h!-i##h#*s)Hy#*s#*t!-i#*t#*x)Hy#*x#*z!-i#*z#+R)Hy#+R#+S!-i#+S#+T)Hy#+T#+U!-i#+U#+Y)Hy#+Y#+[!-i#+[#,V)Hy#,V#,W!-i#,W#,[)Hy#,[#,^!-i#,^#-P)Hy#-P#-Q!-i#-Q#-U)Hy#-U#-W!-i#-W#-_)Hy#-_#-`!-i#-`#-a)Hy#-a#-b!-i#-b#-f)Hy#-f#-h!-i#-h#-w)Hy#-w#-x!-i#-x#/T)Hy#/T#/U!-i#/U#/Y)Hy#/Y#/[!-i#/[#0q)Hy#0q#1h!-i#1h#1x)Hy#1x#2Y!-i#2Y#4R)Hy#4R#4_!-i#4_#Au)Hy#Au#Aw!-i#Aw#BY)Hy#BY#BZ!-i#BZ#Bu)Hy#Bu#Bz!-i#Bz#Di)Hy#Di#EO!-i#EO#E])Hy#E]#E^!-i#E^#Eb)Hy#Eb#Ep!-i#Ep#FS)Hy#FS#Fb!-i#Fb#Ft)Hy#Ft#GS!-i#GS#Ga)Hy#Ga#Gb!-i#Gb#Ge)Hy#Ge#Gt!-i#Gt#Hz)Hy#Hz#Io!-i#Io#Ip)Hy#Ip#It!-i#It#Iu)Hy#Iu#K[!-i#K[#MW)Hy#MW#M`!-i#M`#NZ)Hy#NZ#N[!-i#N[#N])Hy#N]#Nb!-i#Nb$ z)Hy$ z$!U!-i$!U$!s)Hy$!s$#x!-i$#x$$h)Hy$$h$$j!-i$$j$$o)Hy$$o$$z!-i$$z$%x)Hy$%x$&_!-i$&_$&f)Hy$&f$'p!-i$'p$(X)Hy$(X$(b!-i$(b$)i)Hy$)i$+_!-i$+_$+`)Hy$+`$-a!-i$-a$.b)Hy$.b$.s!-i$.s$.z)Hy$.z$0T!-i$0T$0s)Hy$0s$1Q!-i$1Q$1R)Hy$1R$1S)Hy$1S$1^!-i$1^$2[)Hy$2[$2v!-i$2v$3l)Hy$3l$4g!-i$4g$4j)Hy$4j$4t!-i$4t$5j)Hy$5j$7y!-i$7y$7})Hy$7}$8O!-i$8O$8S)Hy$8S$8V!-i$8V$8W)Hy$8W$8X)Hy$8X$8b!-i$8b$z)Hy5>z5>{!-i5>{5>|)Hy5>|5?P!-i5?P5?Q)Hy5?Q5?R)Hy5?R5?T!-i5?T5?Y)Hy5?Y5?[!-i5?[5?])Hy5?]5?^!-i5?^5?_)Hy5?_5?w!-i5?w5?z)Hy5?z5?|!-i5?|5@X)Hy5@X5@`!-i5@`5@c)Hy5@c5@o!-i5@o5@u)Hy5@u5@w!-i5@w5@})Hy5@}5AP!-i5AP5AV)Hy5AV5A`!-i5A`5Ag)Hy5Ag5Ah!-i5Ah5Ao)Hy5Ao5Dv!-i5Dv5Ek)Hy5Ek5FY!-i5FY;%S)Hy;%S;%`!-i;%`;%w)Hy;%w;%{!-i;%{;'O)Hy;'O;'S!-i;'S;=`!.V<%l?&r!-i?&r?.p)Hy?.p?.r!-i?.r?1Q)Hy?1Q?1x!-i?1x?2P)Hy?2P?2]!-i?2]?2b)Hy?2b?2g!-i?2g?2h)Hy?2h?2i!-i?2i?2s)Hy?2s?2t!-i?2t?3R)Hy?3R?3S!-i?3S?3X)Hy?3X?3Y!-i?3Y?3Z)Hy?3Z?3[!-i?3[?3])Hy?3]?3^)Hy?3^?3_!-i?3_?3`)Hy?3`?3a)Hy?3a?3b!-i?3b?5r)Hy?5r?6e!-i?6e?>`)Hy?>`?>r!-i?>r?@U)Hy?@U?@W!-i?@W?A`)Hy?A`?BY!-i?BY?Bf)Hy?Bf?EO!-i?EO?ET)Hy?ET?EU!-i?EU?HR)Hy?HR?Hw!-i?Hw?Ic)Hy?Ic?Ii!-i?Ii?JT)Hy?JT?J`!-i?J`?L])Hy?L]?L`!-i?L`?Lf)Hy?Lf?Lh!-i?Lh?Ln)Hy?Ln?Lp!-i?Lp?Lv)Hy?Lv?Lx!-i?Lx?L{)Hy?L{O!-i_+ v2g!OWzQ{TmPOq!*jqr#9krs#9kst#9ktu#9kuv#9kvw#9kwx$@]xz!*jz{#9k{|#9k|!O!*j!O!P#9k!P!Q#9k!Q![#9k![!a!*j!a!b#9k!b!c!*j!c!}#9k!}#O!*j#O#P#9k#P#R!*j#R#S#9k#S#T&4y#T#o#9k#o$p!*j$p$q#9k$q${!*j${$|#9k$|%Q!*j%Q%R#9k%R%W!*j%W%o#9k%o%p!*j%p&a#9k&a&b!*j&b0`#9k0`0d!*j0d0p#9k0p1O!*j1O1T#9k1T1[!*j1[1]#9k1]1^!*j1^1_#9k1_4U!*j4U4Z#9k4Z4[!*j4[4]#9k4]4^#9k4^4`!*j4`4d#9k4d4l!*j4l4m#9k4m4n!*j4n4q#9k4q4r!*j4r4s#9k4s4t!*j4t5Y#9k5Y5Z!*j5Z7Q#9k7Q7R!*j7R:S#9k:S:[!*j:[=p#9k=p=y!*j=y>q#9k>q>s!*j>s>t#9k>t>{!*j>{?t#9k?tA`!*jA`A{#9kA{BQ!*jBQBT#9kBTCS!*jCSDP#9kDPDt!*jDtDu#9kDuDv#9kDvDw!*jDwGO#9kGOGP!*jGPGQ#9kGQGa!*jGaGb#9kGbGc#9kGcGj!*jGjGk#9kGkGl#9kGlGv!*jGvGy#9kGyG{!*jG{G|#9kG|H^!*jH^H_#9kH_H`!*jH`IO#9kIOIm!*jImKj#9kKjKu!*jKuKv#9kKvL`!*jL`MR#9kMRM[!*jM[M]#9kM]M^#9kM^Mb!*jMbMc#9kMcMh!*jMhNO#9kNONS!*jNSNT#9kNTN^!*jN^N_#9kN_Nb!*jNbNc#9kNcNz!*jNz! e#9k! e!#O!*j!#O!#P#9k!#P!#Q!*j!#Q!#]#9k!#]!%W!*j!%W!&`#9k!&`!&c!*j!&c!&d#9k!&d!&v!*j!&v!&w#9k!&w!'O!*j!'O!'Y#9k!'Y!'i!*j!'i!'p#9k!'p!'q!*j!'q!'x#9k!'x!'}!*j!'}!(V#9k!(V!(X!*j!(X!(Y#9k!(Y!(Z#9k!(Z!(]!*j!(]!(s#9k!(s!(t!*j!(t!({#9k!({!(|!*j!(|!(}#9k!(}!)Q!*j!)Q!)U#9k!)U!)X!*j!)X!)Y#9k!)Y!)j!*j!)j!)k#9k!)k!)x!*j!)x!)y#9k!)y!)z#9k!)z!){!*j!){!*O#9k!*O!*^!*j!*^!*_#9k!*_!*`#9k!*`!*s!*j!*s!*y#9k!*y!*}!*j!*}!+O#9k!+O!+P#9k!+P!+R!*j!+R!+i#9k!+i!+j!*j!+j!+q#9k!+q!+r!*j!+r!+s#9k!+s!+t#9k!+t!+u!*j!+u!+v#9k!+v!+w#9k!+w!+x!*j!+x!+y#9k!+y!+z#9k!+z!,k!*j!,k!,o#9k!,o!,p!*j!,p!,q#9k!,q!-U!*j!-U!-X#9k!-X!-i!*j!-i!-r#9k!-r!-s!*j!-s!-v#9k!-v!-w!*j!-w!._#9k!._!.`!*j!.`!.g#9k!.g!.h!*j!.h!.i#9k!.i!.j#9k!.j!.k!*j!.k!.p#9k!.p!.s!*j!.s!.t#9k!.t!/W!*j!/W!/X#9k!/X!/h!*j!/h!/i#9k!/i!/j#9k!/j!0_!*j!0_!0g#9k!0g!0i!*j!0i!0j#9k!0j!0k#9k!0k!0m!*j!0m!1T#9k!1T!1U!*j!1U!1]#9k!1]!1^!*j!1^!1_#9k!1_!1`#9k!1`!1a!*j!1a!1f#9k!1f!1i!*j!1i!1j#9k!1j!2Y!*j!2Y!2Z#9k!2Z!2[#9k!2[!2]!*j!2]!2`#9k!2`!2o!*j!2o!2p#9k!2p!3R!*j!3R!3S#9k!3S!3T!*j!3T!3Z#9k!3Z!3^!*j!3^!3a#9k!3a!3b!*j!3b!3f#9k!3f!3i!*j!3i!3j#9k!3j!3k#9k!3k!3l!*j!3l!3m#9k!3m!3n!*j!3n!3o#9k!3o!3p#9k!3p!3s!*j!3s!3t#9k!3t!3u#9k!3u!3x!*j!3x!3{#9k!3{!4O!*j!4O!4[#9k!4[!4r!*j!4r!4s#9k!4s!5y!*j!5y!6R#9k!6R!6S!*j!6S!6V#9k!6V!6W!*j!6W!6o#9k!6o!6p!*j!6p!6z#9k!6z!6{!*j!6{!7Q#9k!7Q!7T!*j!7T!7U#9k!7U!7p!*j!7p!7q#9k!7q!7r#9k!7r!7x!*j!7x!7y#9k!7y!7z#9k!7z!8o!*j!8o!8w#9k!8w!8x!*j!8x!8{#9k!8{!8|!*j!8|!9e#9k!9e!9f!*j!9f!9p#9k!9p!9q!*j!9q!9v#9k!9v!9y!*j!9y!9z#9k!9z!:l!*j!:l!:m#9k!:m!:n!*j!:n!:o#9k!:o!:p#9k!:p!;P!*j!;P!;Q#9k!;Q!;R#9k!;R!;e!*j!;e!;m#9k!;m!;n!*j!;n!;q#9k!;q!;r!*j!;r!O!*j!>O!>U#9k!>U!>Z!*j!>Z!>m#9k!>m!>p!*j!>p!?Y#9k!?Y!?Z!*j!?Z!?d#9k!?d!?e!*j!?e!?f#9k!?f!?h!*j!?h!?o#9k!?o!@{!*j!@{!A}#9k!A}!BO!*j!BO!BP#9k!BP!BQ#9k!BQ!B^!*j!B^!Be#9k!Be!Cq!*j!Cq!Cr#9k!Cr!Cs#9k!Cs!Ct!*j!Ct!Cu#9k!Cu!Cw!*j!Cw!Cx#9k!Cx!Cy#9k!Cy!Cz!*j!Cz!C{#9k!C{!C}!*j!C}!DO#9k!DO!DU!*j!DU!DY#9k!DY!DZ!*j!DZ!Db#9k!Db!Dc!*j!Dc!Df#9k!Df!Dg!*j!Dg!Dh#9k!Dh!Di!*j!Di!Dj#9k!Dj!Dl!*j!Dl!Dm#9k!Dm!Dn#9k!Dn!Do!*j!Do!Ds#9k!Ds!Dt!*j!Dt!Du#9k!Du!Dv#9k!Dv!EP!*j!EP!EQ#9k!EQ!ES!*j!ES!EX#9k!EX!EY!*j!EY!EZ#9k!EZ!Ep!*j!Ep!Et#9k!Et!Ff!*j!Ff!Fg#9k!Fg!Gx!*j!Gx!HQ#9k!HQ!HR!*j!HR!Hw#9k!Hw!Id!*j!Id!Ii#9k!Ii!LQ!*j!LQ!L}#9k!L}!Mc!*j!Mc!Md#9k!Md!Mt!*j!Mt!Mz#9k!Mz!NO!*j!NO!NS#9k!NS!NV!*j!NV!NW#9k!NW!NZ!*j!NZ!N[#9k!N[!N]#9k!N]!Nd!*j!Nd!Ng#9k!Ng!Nk!*j!Nk!Nx#9k!Nx# U!*j# U# V#9k# V# h!*j# h#!`#9k#!`#!a!*j#!a#!b#9k#!b#!g!*j#!g#!h#9k#!h#!j!*j#!j##g#9k##g##h!*j##h#*s#9k#*s#*t!*j#*t#*x#9k#*x#*z!*j#*z#+R#9k#+R#+S!*j#+S#+T#9k#+T#+U!*j#+U#+Y#9k#+Y#+[!*j#+[#,V#9k#,V#,W!*j#,W#,[#9k#,[#,^!*j#,^#-P#9k#-P#-Q!*j#-Q#-U#9k#-U#-W!*j#-W#-_#9k#-_#-`!*j#-`#-a#9k#-a#-b!*j#-b#-f#9k#-f#-h!*j#-h#-w#9k#-w#-x!*j#-x#/T#9k#/T#/U!*j#/U#/Y#9k#/Y#/[!*j#/[#0q#9k#0q#1h!*j#1h#1x#9k#1x#2Y!*j#2Y#4R#9k#4R#4_!*j#4_#Au#9k#Au#Aw!*j#Aw#BY#9k#BY#BZ!*j#BZ#Bu#9k#Bu#Bz!*j#Bz#Di#9k#Di#EO!*j#EO#E]#9k#E]#E^!*j#E^#Eb#9k#Eb#Ep!*j#Ep#FS#9k#FS#Fb!*j#Fb#Ft#9k#Ft#GS!*j#GS#Ga#9k#Ga#Gb!*j#Gb#Ge#9k#Ge#Gt!*j#Gt#Hz#9k#Hz#Io!*j#Io#Ip#9k#Ip#It!*j#It#Iu#9k#Iu#K[!*j#K[#MW#9k#MW#M`!*j#M`#NZ#9k#NZ#N[!*j#N[#N]#9k#N]#Nb!*j#Nb$ z#9k$ z$!U!*j$!U$!s#9k$!s$#x!*j$#x$$h#9k$$h$$j!*j$$j$$o#9k$$o$$z!*j$$z$%x#9k$%x$&_!*j$&_$&f#9k$&f$'p!*j$'p$(X#9k$(X$(b!*j$(b$)i#9k$)i$+_!*j$+_$+`#9k$+`$-a!*j$-a$.b#9k$.b$.s!*j$.s$.z#9k$.z$0T!*j$0T$0s#9k$0s$1Q!*j$1Q$1R#9k$1R$1S#9k$1S$1^!*j$1^$2[#9k$2[$2v!*j$2v$3l#9k$3l$4g!*j$4g$4j#9k$4j$4t!*j$4t$5j#9k$5j$7y!*j$7y$7}#9k$7}$8O!*j$8O$8S#9k$8S$8V!*j$8V$8W#9k$8W$8X#9k$8X$8b!*j$8b$z#9k5>z5>{!*j5>{5>|#9k5>|5?P!*j5?P5?Q#9k5?Q5?R#9k5?R5?T!*j5?T5?Y#9k5?Y5?[!*j5?[5?]#9k5?]5?^!*j5?^5?_#9k5?_5?w!*j5?w5?z#9k5?z5?|!*j5?|5@X#9k5@X5@`!*j5@`5@c#9k5@c5@o!*j5@o5@u#9k5@u5@w!*j5@w5@}#9k5@}5AP!*j5AP5AV#9k5AV5A`!*j5A`5Ag#9k5Ag5Ah!*j5Ah5Ao#9k5Ao5Dv!*j5Dv5Ek#9k5Ek5FY!*j5FY;%S#9k;%S;%`!*j;%`;%w#9k;%w;%{!*j;%{;'O#9k;'O;'S!*j;'S;=`!,W<%l?&r!*j?&r?.p#9k?.p?.r!*j?.r?1Q#9k?1Q?1x!*j?1x?2P#9k?2P?2]!*j?2]?2b#9k?2b?2g!*j?2g?2h#9k?2h?2i!*j?2i?2s#9k?2s?2t!*j?2t?3R#9k?3R?3S!*j?3S?3X#9k?3X?3Y!*j?3Y?3Z#9k?3Z?3[!*j?3[?3]#9k?3]?3^#9k?3^?3_!*j?3_?3`#9k?3`?3a#9k?3a?3b!*j?3b?5r#9k?5r?6e!*j?6e?>`#9k?>`?>r!*j?>r?@U#9k?@U?@W!*j?@W?A`#9k?A`?BY!*j?BY?Bf#9k?Bf?EO!*j?EO?ET#9k?ET?EU!*j?EU?HR#9k?HR?Hw!*j?Hw?Ic#9k?Ic?Ii!*j?Ii?JT#9k?JT?J`!*j?J`?L]#9k?L]?L`!*j?L`?Lf#9k?Lf?Lh!*j?Lh?Ln#9k?Ln?Lp!*j?Lp?Lv#9k?Lv?Lx!*j?Lx?L{#9k?L{O!*j!a,(j2g!OW|SzQmPOq!)tqr!2wrs#9kst!2wtu!2wuv-/Rvw!2wwx';ixz!)tz{!2w{|!2w|!O!)t!O!P!2w!P!Q!2w!Q![!2w![!a!)t!a!b!2w!b!c!)t!c!}!2w!}#O!)t#O#P!2w#P#R!)t#R#S!2w#S#T)Hy#T#o!2w#o$p!)t$p$q!2w$q${!)t${$|!2w$|%Q!)t%Q%R!2w%R%W!)t%W%o!2w%o%p!)t%p&a!2w&a&b!)t&b0`!2w0`0d!)t0d0p!2w0p1O!)t1O1T!2w1T1[!)t1[1]!2w1]1^!)t1^1_!2w1_4U!)t4U4Z!2w4Z4[!)t4[4]!2w4]4^!2w4^4`!)t4`4d!2w4d4l!)t4l4m!2w4m4n!)t4n4q!2w4q4r!)t4r4s!2w4s4t!)t4t5Y!2w5Y5Z!)t5Z7Q!2w7Q7R!)t7R:S!2w:S:[!)t:[=p!2w=p=y!)t=y>q!2w>q>s!)t>s>t!2w>t>{!)t>{?t!2w?tA`!)tA`A{!2wA{BQ!)tBQBT!2wBTCS!)tCSDP!2wDPDt!)tDtDu!2wDuDv!2wDvDw!)tDwGO!2wGOGP!)tGPGQ!2wGQGa!)tGaGb!2wGbGc!2wGcGj!)tGjGk!2wGkGl!2wGlGv!)tGvGy!2wGyG{!)tG{G|!2wG|H^!)tH^H_!2wH_H`!)tH`IO!2wIOIm!)tImKj!2wKjKu!)tKuKv!2wKvL`!)tL`MR!2wMRM[!)tM[M]!2wM]M^!2wM^Mb!)tMbMc!2wMcMh!)tMhNO!2wNONS!)tNSNT!2wNTN^!)tN^N_!2wN_Nb!)tNbNc!2wNcNz!)tNz! e!2w! e!#O!)t!#O!#P!2w!#P!#Q!)t!#Q!#]!2w!#]!%W!)t!%W!&`!2w!&`!&c!)t!&c!&d!2w!&d!&v!)t!&v!&w!2w!&w!'O!)t!'O!'Y!2w!'Y!'i!)t!'i!'p!2w!'p!'q!)t!'q!'x!2w!'x!'}!)t!'}!(V!2w!(V!(X!)t!(X!(Y!2w!(Y!(Z!2w!(Z!(]!)t!(]!(s!2w!(s!(t!)t!(t!({!2w!({!(|!)t!(|!(}!2w!(}!)Q!)t!)Q!)U!2w!)U!)X!)t!)X!)Y!2w!)Y!)j!)t!)j!)k!2w!)k!)x!)t!)x!)y!2w!)y!)z!2w!)z!){!)t!){!*O!2w!*O!*^!)t!*^!*_!2w!*_!*`!2w!*`!*s!)t!*s!*y!2w!*y!*}!)t!*}!+O!2w!+O!+P!2w!+P!+R!)t!+R!+i!2w!+i!+j!)t!+j!+q!2w!+q!+r!)t!+r!+s!2w!+s!+t!2w!+t!+u!)t!+u!+v!2w!+v!+w!2w!+w!+x!)t!+x!+y!2w!+y!+z!2w!+z!,k!)t!,k!,o!2w!,o!,p!)t!,p!,q!2w!,q!-U!)t!-U!-X!2w!-X!-i!)t!-i!-r!2w!-r!-s!)t!-s!-v!2w!-v!-w!)t!-w!._!2w!._!.`!)t!.`!.g!2w!.g!.h!)t!.h!.i!2w!.i!.j!2w!.j!.k!)t!.k!.p!2w!.p!.s!)t!.s!.t!2w!.t!/W!)t!/W!/X!2w!/X!/h!)t!/h!/i!2w!/i!/j!2w!/j!0_!)t!0_!0g!2w!0g!0i!)t!0i!0j!2w!0j!0k!2w!0k!0m!)t!0m!1T!2w!1T!1U!)t!1U!1]!2w!1]!1^!)t!1^!1_!2w!1_!1`!2w!1`!1a!)t!1a!1f!2w!1f!1i!)t!1i!1j!2w!1j!2Y!)t!2Y!2Z!2w!2Z!2[!2w!2[!2]!)t!2]!2`!2w!2`!2o!)t!2o!2p!2w!2p!3R!)t!3R!3S!2w!3S!3T!)t!3T!3Z!2w!3Z!3^!)t!3^!3a!2w!3a!3b!)t!3b!3f!2w!3f!3i!)t!3i!3j!2w!3j!3k!2w!3k!3l!)t!3l!3m!2w!3m!3n!)t!3n!3o!2w!3o!3p!2w!3p!3s!)t!3s!3t!2w!3t!3u!2w!3u!3x!)t!3x!3{!2w!3{!4O!)t!4O!4[!2w!4[!4r!)t!4r!4s!2w!4s!5y!)t!5y!6R!2w!6R!6S!)t!6S!6V!2w!6V!6W!)t!6W!6o!2w!6o!6p!)t!6p!6z!2w!6z!6{!)t!6{!7Q!2w!7Q!7T!)t!7T!7U!2w!7U!7p!)t!7p!7q!2w!7q!7r!2w!7r!7x!)t!7x!7y!2w!7y!7z!2w!7z!8o!)t!8o!8w!2w!8w!8x!)t!8x!8{!2w!8{!8|!)t!8|!9e!2w!9e!9f!)t!9f!9p!2w!9p!9q!)t!9q!9v!2w!9v!9y!)t!9y!9z!2w!9z!:l!)t!:l!:m!2w!:m!:n!)t!:n!:o!2w!:o!:p!2w!:p!;P!)t!;P!;Q!2w!;Q!;R!2w!;R!;e!)t!;e!;m!2w!;m!;n!)t!;n!;q!2w!;q!;r!)t!;r!O!)t!>O!>U!2w!>U!>Z!)t!>Z!>m!2w!>m!>p!)t!>p!?Y!2w!?Y!?Z!)t!?Z!?d!2w!?d!?e!)t!?e!?f!2w!?f!?h!)t!?h!?o!2w!?o!@{!)t!@{!A}!2w!A}!BO!)t!BO!BP!2w!BP!BQ!2w!BQ!B^!)t!B^!Be!2w!Be!Cq!)t!Cq!Cr!2w!Cr!Cs!2w!Cs!Ct!)t!Ct!Cu!2w!Cu!Cw!)t!Cw!Cx!2w!Cx!Cy!2w!Cy!Cz!)t!Cz!C{!2w!C{!C}!)t!C}!DO!2w!DO!DU!)t!DU!DY!2w!DY!DZ!)t!DZ!Db!2w!Db!Dc!)t!Dc!Df!2w!Df!Dg!)t!Dg!Dh!2w!Dh!Di!)t!Di!Dj!2w!Dj!Dl!)t!Dl!Dm!2w!Dm!Dn!2w!Dn!Do!)t!Do!Ds!2w!Ds!Dt!)t!Dt!Du!2w!Du!Dv!2w!Dv!EP!)t!EP!EQ!2w!EQ!ES!)t!ES!EX!2w!EX!EY!)t!EY!EZ!2w!EZ!Ep!)t!Ep!Et!2w!Et!Ff!)t!Ff!Fg!2w!Fg!Gx!)t!Gx!HQ!2w!HQ!HR!)t!HR!Hw!2w!Hw!Id!)t!Id!Ii!2w!Ii!LQ!)t!LQ!L}!2w!L}!Mc!)t!Mc!Md!2w!Md!Mt!)t!Mt!Mz!2w!Mz!NO!)t!NO!NS!2w!NS!NV!)t!NV!NW!2w!NW!NZ!)t!NZ!N[!2w!N[!N]!2w!N]!Nd!)t!Nd!Ng!2w!Ng!Nk!)t!Nk!Nx!2w!Nx# U!)t# U# V!2w# V# h!)t# h#!`!2w#!`#!a!)t#!a#!b!2w#!b#!g!)t#!g#!h!2w#!h#!j!)t#!j##g!2w##g##h!)t##h#*s!2w#*s#*t!)t#*t#*x!2w#*x#*z!)t#*z#+R!2w#+R#+S!)t#+S#+T!2w#+T#+U!)t#+U#+Y!2w#+Y#+[!)t#+[#,V!2w#,V#,W!)t#,W#,[!2w#,[#,^!)t#,^#-P!2w#-P#-Q!)t#-Q#-U!2w#-U#-W!)t#-W#-_!2w#-_#-`!)t#-`#-a!2w#-a#-b!)t#-b#-f!2w#-f#-h!)t#-h#-w!2w#-w#-x!)t#-x#/T!2w#/T#/U!)t#/U#/Y!2w#/Y#/[!)t#/[#0q!2w#0q#1h!)t#1h#1x!2w#1x#2Y!)t#2Y#4R!2w#4R#4_!)t#4_#Au!2w#Au#Aw!)t#Aw#BY!2w#BY#BZ!)t#BZ#Bu!2w#Bu#Bz!)t#Bz#Di!2w#Di#EO!)t#EO#E]!2w#E]#E^!)t#E^#Eb!2w#Eb#Ep!)t#Ep#FS!2w#FS#Fb!)t#Fb#Ft!2w#Ft#GS!)t#GS#Ga!2w#Ga#Gb!)t#Gb#Ge!2w#Ge#Gt!)t#Gt#Hz!2w#Hz#Io!)t#Io#Ip!2w#Ip#It!)t#It#Iu!2w#Iu#K[!)t#K[#MW!2w#MW#M`!)t#M`#NZ!2w#NZ#N[!)t#N[#N]!2w#N]#Nb!)t#Nb$ z!2w$ z$!U!)t$!U$!s!2w$!s$#x!)t$#x$$h!2w$$h$$j!)t$$j$$o!2w$$o$$z!)t$$z$%x!2w$%x$&_!)t$&_$&f!2w$&f$'p!)t$'p$(X!2w$(X$(b!)t$(b$)i!2w$)i$+_!)t$+_$+`!2w$+`$-a!)t$-a$.b!2w$.b$.s!)t$.s$.z!2w$.z$0T!)t$0T$0s!2w$0s$1Q!)t$1Q$1R!2w$1R$1S!2w$1S$1^!)t$1^$2[!2w$2[$2v!)t$2v$3l!2w$3l$4g!)t$4g$4j!2w$4j$4t!)t$4t$5j!2w$5j$7y!)t$7y$7}!2w$7}$8O!)t$8O$8S!2w$8S$8V!)t$8V$8W!2w$8W$8X!2w$8X$8b!)t$8b$z!2w5>z5>{!)t5>{5>|!2w5>|5?P!)t5?P5?Q!2w5?Q5?R!2w5?R5?T!)t5?T5?Y!2w5?Y5?[!)t5?[5?]!2w5?]5?^!)t5?^5?_!2w5?_5?w!)t5?w5?z!2w5?z5?|!)t5?|5@X!2w5@X5@`!)t5@`5@c!2w5@c5@o!)t5@o5@u!2w5@u5@w!)t5@w5@}!2w5@}5AP!)t5AP5AV!2w5AV5A`!)t5A`5Ag!2w5Ag5Ah!)t5Ah5Ao!2w5Ao5Dv!)t5Dv5Ek!2w5Ek5FY!)t5FY;%S!2w;%S;%`!)t;%`;%w!2w;%w;%{!)t;%{;'O!2w;'O;'S!)t;'S;=`!.]<%l?&r!)t?&r?.p!2w?.p?.r!)t?.r?1Q!2w?1Q?1x!)t?1x?2P!2w?2P?2]!)t?2]?2b!2w?2b?2g!)t?2g?2h!2w?2h?2i!)t?2i?2s!2w?2s?2t!)t?2t?3R!2w?3R?3S!)t?3S?3X!2w?3X?3Y!)t?3Y?3Z!2w?3Z?3[!)t?3[?3]!2w?3]?3^!2w?3^?3_!)t?3_?3`!2w?3`?3a!2w?3a?3b!)t?3b?5r!2w?5r?6e!)t?6e?>`!2w?>`?>r!)t?>r?@U!2w?@U?@W!)t?@W?A`!2w?A`?BY!)t?BY?Bf!2w?Bf?EO!)t?EO?ET!2w?ET?EU!)t?EU?HR!2w?HR?Hw!)t?Hw?Ic!2w?Ic?Ii!)t?Ii?JT!2w?JT?J`!)t?J`?L]!2w?L]?L`!)t?L`?Lf!2w?Lf?Lh!)t?Lh?Ln!2w?Ln?Lp!)t?Lp?Lv!2w?Lv?Lx!)t?Lx?L{!2w?L{O!)t!a-/`2i!OW|SzQS!RmPOY.5}YZ!)tZq.5}qr-/Rrs.q-/R>q>s.5}>s>t-/R>t>{.5}>{?t-/R?tA`.5}A`A{-/RA{BQ.5}BQBT-/RBTCS.5}CSDP-/RDPDt.5}DtDu-/RDuDv-/RDvDw.5}DwGO-/RGOGP.5}GPGQ-/RGQGa.5}GaGb-/RGbGc-/RGcGj.5}GjGk-/RGkGl-/RGlGv.5}GvGy-/RGyG{.5}G{G|-/RG|H^.5}H^H_-/RH_H`.5}H`IO-/RIOIm.5}ImKj-/RKjKu.5}KuKv-/RKvL`.5}L`MR-/RMRM[.5}M[M]-/RM]M^-/RM^Mb.5}MbMc-/RMcMh.5}MhNO-/RNONS.5}NSNT-/RNTN^.5}N^N_-/RN_Nb.5}NbNc-/RNcNz.5}Nz! e-/R! e!#O.5}!#O!#P-/R!#P!#Q.5}!#Q!#]-/R!#]!%W.5}!%W!&`-/R!&`!&c.5}!&c!&d-/R!&d!&v.5}!&v!&w-/R!&w!'O.5}!'O!'Y-/R!'Y!'i.5}!'i!'p-/R!'p!'q.5}!'q!'x-/R!'x!'}.5}!'}!(V-/R!(V!(X.5}!(X!(Y-/R!(Y!(Z-/R!(Z!(].5}!(]!(s-/R!(s!(t.5}!(t!({-/R!({!(|.5}!(|!(}-/R!(}!)Q.5}!)Q!)U-/R!)U!)X.5}!)X!)Y-/R!)Y!)j.5}!)j!)k-/R!)k!)x.5}!)x!)y-/R!)y!)z-/R!)z!){.5}!){!*O-/R!*O!*^.5}!*^!*_-/R!*_!*`-/R!*`!*s.5}!*s!*y-/R!*y!*}.5}!*}!+O-/R!+O!+P-/R!+P!+R.5}!+R!+i-/R!+i!+j.5}!+j!+q-/R!+q!+r.5}!+r!+s-/R!+s!+t-/R!+t!+u.5}!+u!+v-/R!+v!+w-/R!+w!+x.5}!+x!+y-/R!+y!+z-/R!+z!,k.5}!,k!,o-/R!,o!,p.5}!,p!,q-/R!,q!-U.5}!-U!-X-/R!-X!-i.5}!-i!-r-/R!-r!-s.5}!-s!-v-/R!-v!-w.5}!-w!._-/R!._!.`.5}!.`!.g-/R!.g!.h.5}!.h!.i-/R!.i!.j-/R!.j!.k.5}!.k!.p-/R!.p!.s.5}!.s!.t-/R!.t!/W.5}!/W!/X-/R!/X!/h.5}!/h!/i-/R!/i!/j-/R!/j!0_.5}!0_!0g-/R!0g!0i.5}!0i!0j-/R!0j!0k-/R!0k!0m.5}!0m!1T-/R!1T!1U.5}!1U!1]-/R!1]!1^.5}!1^!1_-/R!1_!1`-/R!1`!1a.5}!1a!1f-/R!1f!1i.5}!1i!1j-/R!1j!2Y.5}!2Y!2Z-/R!2Z!2[-/R!2[!2].5}!2]!2`-/R!2`!2o.5}!2o!2p-/R!2p!3R.5}!3R!3S-/R!3S!3T.5}!3T!3Z-/R!3Z!3^.5}!3^!3a-/R!3a!3b.5}!3b!3f-/R!3f!3i.5}!3i!3j-/R!3j!3k-/R!3k!3l.5}!3l!3m-/R!3m!3n.5}!3n!3o-/R!3o!3p-/R!3p!3s.5}!3s!3t-/R!3t!3u-/R!3u!3x.5}!3x!3{-/R!3{!4O.5}!4O!4[-/R!4[!4r.5}!4r!4s-/R!4s!5y.5}!5y!6R-/R!6R!6S.5}!6S!6V-/R!6V!6W.5}!6W!6o-/R!6o!6p.5}!6p!6z-/R!6z!6{.5}!6{!7Q-/R!7Q!7T.5}!7T!7U-/R!7U!7p.5}!7p!7q-/R!7q!7r-/R!7r!7x.5}!7x!7y-/R!7y!7z-/R!7z!8o.5}!8o!8w-/R!8w!8x.5}!8x!8{-/R!8{!8|.5}!8|!9e-/R!9e!9f.5}!9f!9p-/R!9p!9q.5}!9q!9v-/R!9v!9y.5}!9y!9z-/R!9z!:l.5}!:l!:m-/R!:m!:n.5}!:n!:o-/R!:o!:p-/R!:p!;P.5}!;P!;Q-/R!;Q!;R-/R!;R!;e.5}!;e!;m-/R!;m!;n.5}!;n!;q-/R!;q!;r.5}!;r!O.5}!>O!>U-/R!>U!>Z.5}!>Z!>m-/R!>m!>p.5}!>p!?Y-/R!?Y!?Z.5}!?Z!?d-/R!?d!?e.5}!?e!?f-/R!?f!?h.5}!?h!?o-/R!?o!@{.5}!@{!A}-/R!A}!BO.5}!BO!BP-/R!BP!BQ-/R!BQ!B^.5}!B^!Be-/R!Be!Cq.5}!Cq!Cr-/R!Cr!Cs-/R!Cs!Ct.5}!Ct!Cu-/R!Cu!Cw.5}!Cw!Cx-/R!Cx!Cy-/R!Cy!Cz.5}!Cz!C{-/R!C{!C}.5}!C}!DO-/R!DO!DU.5}!DU!DY-/R!DY!DZ.5}!DZ!Db-/R!Db!Dc.5}!Dc!Df-/R!Df!Dg.5}!Dg!Dh-/R!Dh!Di.5}!Di!Dj-/R!Dj!Dl.5}!Dl!Dm-/R!Dm!Dn-/R!Dn!Do.5}!Do!Ds-/R!Ds!Dt.5}!Dt!Du-/R!Du!Dv-/R!Dv!EP.5}!EP!EQ-/R!EQ!ES.5}!ES!EX-/R!EX!EY.5}!EY!EZ-/R!EZ!Ep.5}!Ep!Et-/R!Et!Ff.5}!Ff!Fg-/R!Fg!Gx.5}!Gx!HQ-/R!HQ!HR.5}!HR!Hw-/R!Hw!Id.5}!Id!Ii-/R!Ii!LQ.5}!LQ!L}-/R!L}!Mc.5}!Mc!Md-/R!Md!Mt.5}!Mt!Mz-/R!Mz!NO.5}!NO!NS-/R!NS!NV.5}!NV!NW-/R!NW!NZ.5}!NZ!N[-/R!N[!N]-/R!N]!Nd.5}!Nd!Ng-/R!Ng!Nk.5}!Nk!Nx-/R!Nx# U.5}# U# V-/R# V# h.5}# h#!`-/R#!`#!a.5}#!a#!b-/R#!b#!g.5}#!g#!h-/R#!h#!j.5}#!j##g-/R##g##h.5}##h#*s-/R#*s#*t.5}#*t#*x-/R#*x#*z.5}#*z#+R-/R#+R#+S.5}#+S#+T-/R#+T#+U.5}#+U#+Y-/R#+Y#+[.5}#+[#,V-/R#,V#,W.5}#,W#,[-/R#,[#,^.5}#,^#-P-/R#-P#-Q.5}#-Q#-U-/R#-U#-W.5}#-W#-_-/R#-_#-`.5}#-`#-a-/R#-a#-b.5}#-b#-f-/R#-f#-h.5}#-h#-w-/R#-w#-x.5}#-x#/T-/R#/T#/U.5}#/U#/Y-/R#/Y#/[.5}#/[#0q-/R#0q#1h.5}#1h#1x-/R#1x#2Y.5}#2Y#4R-/R#4R#4_.5}#4_#Au-/R#Au#Aw.5}#Aw#BY-/R#BY#BZ.5}#BZ#Bu-/R#Bu#Bz.5}#Bz#Di-/R#Di#EO.5}#EO#E]-/R#E]#E^.5}#E^#Eb-/R#Eb#Ep.5}#Ep#FS-/R#FS#Fb.5}#Fb#Ft-/R#Ft#GS.5}#GS#Ga-/R#Ga#Gb.5}#Gb#Ge-/R#Ge#Gt.5}#Gt#Hz-/R#Hz#Io.5}#Io#Ip-/R#Ip#It.5}#It#Iu-/R#Iu#K[.5}#K[#MW-/R#MW#M`.5}#M`#NZ-/R#NZ#N[.5}#N[#N]-/R#N]#Nb.5}#Nb$ z-/R$ z$!U.5}$!U$!s-/R$!s$#x.5}$#x$$h-/R$$h$$j.5}$$j$$o-/R$$o$$z.5}$$z$%x-/R$%x$&_.5}$&_$&f-/R$&f$'p.5}$'p$(X-/R$(X$(b.5}$(b$)i-/R$)i$+_.5}$+_$+`-/R$+`$-a.5}$-a$.b-/R$.b$.s.5}$.s$.z-/R$.z$0T.5}$0T$0s-/R$0s$1Q.5}$1Q$1R-/R$1R$1S-/R$1S$1^.5}$1^$2[-/R$2[$2v.5}$2v$3l-/R$3l$4g.5}$4g$4j-/R$4j$4t.5}$4t$5j-/R$5j$7y.5}$7y$7}-/R$7}$8O.5}$8O$8S-/R$8S$8V.5}$8V$8W-/R$8W$8X-/R$8X$8b.5}$8b$z-/R5>z5>{.5}5>{5>|-/R5>|5?P.5}5?P5?Q-/R5?Q5?R-/R5?R5?T.5}5?T5?Y-/R5?Y5?[.5}5?[5?]-/R5?]5?^.5}5?^5?_-/R5?_5?w.5}5?w5?z-/R5?z5?|.5}5?|5@X-/R5@X5@`.5}5@`5@c-/R5@c5@o.5}5@o5@u-/R5@u5@w.5}5@w5@}-/R5@}5AP.5}5AP5AV-/R5AV5A`.5}5A`5Ag-/R5Ag5Ah.5}5Ah5Ao-/R5Ao5Dv.5}5Dv5Ek-/R5Ek5FY.5}5FY;%S-/R;%S;%`.5};%`;%w-/R;%w;%{.5};%{;'O-/R;'O;'S.5};'S;=`.`-/R?>`?>r.5}?>r?@U-/R?@U?@W.5}?@W?A`-/R?A`?BY.5}?BY?Bf-/R?Bf?EO.5}?EO?ET-/R?ET?EU.5}?EU?HR-/R?HR?Hw.5}?Hw?Ic-/R?Ic?Ii.5}?Ii?JT-/R?JT?J`.5}?J`?L]-/R?L]?L`.5}?L`?Lf-/R?Lf?Lh.5}?Lh?Ln-/R?Ln?Lp.5}?Lp?Lv-/R?Lv?Lx.5}?Lx?L{-/R?L{O.5}!a.6YZ!OW|SzQS!ROY.5}YZ!)tZr.5}rs.6{sw.5}wx.9vx#S.5}#S#T.;f#T;'S.5};'S;=`.q.q>s.6{>s>t.t>{.6{>{?t.O.6{!>O!>U.U!>Z.6{!>Z!>m.m!>p.6{!>p!?Y.z.
z5>{.6{5>{5>|.
|5?P.6{5?P5?Q.`.`?>r.6{?>r?@U.q/Cb>q>s.7q>s>t/Cb>t>{.7q>{?t/Cb?tA`.7qA`A{/CbA{BQ.7qBQBT/CbBTCS.7qCSDP/CbDPDt.7qDtDu/CbDuDv/CbDvDw.7qDwGO/CbGOGP.7qGPGQ/CbGQGa.7qGaGb/CbGbGc/CbGcGj.7qGjGk/CbGkGl/CbGlGv.7qGvGy/CbGyG{.7qG{G|/CbG|H^.7qH^H_/CbH_H`.7qH`IO/CbIOIm.7qImKj/CbKjKu.7qKuKv/CbKvL`.7qL`MR/CbMRM[.7qM[M]/CbM]M^/CbM^Mb.7qMbMc/CbMcMh.7qMhNO/CbNONS.7qNSNT/CbNTN^.7qN^N_/CbN_Nb.7qNbNc/CbNcNz.7qNz! e/Cb! e!#O.7q!#O!#P/Cb!#P!#Q.7q!#Q!#]/Cb!#]!%W.7q!%W!&`/Cb!&`!&c.7q!&c!&d/Cb!&d!&v.7q!&v!&w/Cb!&w!'O.7q!'O!'Y/Cb!'Y!'i.7q!'i!'p/Cb!'p!'q.7q!'q!'x/Cb!'x!'}.7q!'}!(V/Cb!(V!(X.7q!(X!(Y/Cb!(Y!(Z/Cb!(Z!(].7q!(]!(s/Cb!(s!(t.7q!(t!({/Cb!({!(|.7q!(|!(}/Cb!(}!)Q.7q!)Q!)U/Cb!)U!)X.7q!)X!)Y/Cb!)Y!)j.7q!)j!)k/Cb!)k!)x.7q!)x!)y/Cb!)y!)z/Cb!)z!){.7q!){!*O/Cb!*O!*^.7q!*^!*_/Cb!*_!*`/Cb!*`!*s.7q!*s!*y/Cb!*y!*}.7q!*}!+O/Cb!+O!+P/Cb!+P!+R.7q!+R!+i/Cb!+i!+j.7q!+j!+q/Cb!+q!+r.7q!+r!+s/Cb!+s!+t/Cb!+t!+u.7q!+u!+v/Cb!+v!+w/Cb!+w!+x.7q!+x!+y/Cb!+y!+z/Cb!+z!,k.7q!,k!,o/Cb!,o!,p.7q!,p!,q/Cb!,q!-U.7q!-U!-X/Cb!-X!-i.7q!-i!-r/Cb!-r!-s.7q!-s!-v/Cb!-v!-w.7q!-w!._/Cb!._!.`.7q!.`!.g/Cb!.g!.h.7q!.h!.i/Cb!.i!.j/Cb!.j!.k.7q!.k!.p/Cb!.p!.s.7q!.s!.t/Cb!.t!/W.7q!/W!/X/Cb!/X!/h.7q!/h!/i/Cb!/i!/j/Cb!/j!0_.7q!0_!0g/Cb!0g!0i.7q!0i!0j/Cb!0j!0k/Cb!0k!0m.7q!0m!1T/Cb!1T!1U.7q!1U!1]/Cb!1]!1^.7q!1^!1_/Cb!1_!1`/Cb!1`!1a.7q!1a!1f/Cb!1f!1i.7q!1i!1j/Cb!1j!2Y.7q!2Y!2Z/Cb!2Z!2[/Cb!2[!2].7q!2]!2`/Cb!2`!2o.7q!2o!2p/Cb!2p!3R.7q!3R!3S/Cb!3S!3T.7q!3T!3Z/Cb!3Z!3^.7q!3^!3a/Cb!3a!3b.7q!3b!3f/Cb!3f!3i.7q!3i!3j/Cb!3j!3k/Cb!3k!3l.7q!3l!3m/Cb!3m!3n.7q!3n!3o/Cb!3o!3p/Cb!3p!3s.7q!3s!3t/Cb!3t!3u/Cb!3u!3x.7q!3x!3{/Cb!3{!4O.7q!4O!4[/Cb!4[!4r.7q!4r!4s/Cb!4s!5y.7q!5y!6R/Cb!6R!6S.7q!6S!6V/Cb!6V!6W.7q!6W!6o/Cb!6o!6p.7q!6p!6z/Cb!6z!6{.7q!6{!7Q/Cb!7Q!7T.7q!7T!7U/Cb!7U!7p.7q!7p!7q/Cb!7q!7r/Cb!7r!7x.7q!7x!7y/Cb!7y!7z/Cb!7z!8o.7q!8o!8w/Cb!8w!8x.7q!8x!8{/Cb!8{!8|.7q!8|!9e/Cb!9e!9f.7q!9f!9p/Cb!9p!9q.7q!9q!9v/Cb!9v!9y.7q!9y!9z/Cb!9z!:l.7q!:l!:m/Cb!:m!:n.7q!:n!:o/Cb!:o!:p/Cb!:p!;P.7q!;P!;Q/Cb!;Q!;R/Cb!;R!;e.7q!;e!;m/Cb!;m!;n.7q!;n!;q/Cb!;q!;r.7q!;r!O.7q!>O!>U/Cb!>U!>Z.7q!>Z!>m/Cb!>m!>p.7q!>p!?Y/Cb!?Y!?Z.7q!?Z!?d/Cb!?d!?e.7q!?e!?f/Cb!?f!?h.7q!?h!?o/Cb!?o!@{.7q!@{!A}/Cb!A}!BO.7q!BO!BP/Cb!BP!BQ/Cb!BQ!B^.7q!B^!Be/Cb!Be!Cq.7q!Cq!Cr/Cb!Cr!Cs/Cb!Cs!Ct.7q!Ct!Cu/Cb!Cu!Cw.7q!Cw!Cx/Cb!Cx!Cy/Cb!Cy!Cz.7q!Cz!C{/Cb!C{!C}.7q!C}!DO/Cb!DO!DU.7q!DU!DY/Cb!DY!DZ.7q!DZ!Db/Cb!Db!Dc.7q!Dc!Df/Cb!Df!Dg.7q!Dg!Dh/Cb!Dh!Di.7q!Di!Dj/Cb!Dj!Dl.7q!Dl!Dm/Cb!Dm!Dn/Cb!Dn!Do.7q!Do!Ds/Cb!Ds!Dt.7q!Dt!Du/Cb!Du!Dv/Cb!Dv!EP.7q!EP!EQ/Cb!EQ!ES.7q!ES!EX/Cb!EX!EY.7q!EY!EZ/Cb!EZ!Ep.7q!Ep!Et/Cb!Et!Ff.7q!Ff!Fg/Cb!Fg!Gx.7q!Gx!HQ/Cb!HQ!HR.7q!HR!Hw/Cb!Hw!Id.7q!Id!Ii/Cb!Ii!LQ.7q!LQ!L}/Cb!L}!Mc.7q!Mc!Md/Cb!Md!Mt.7q!Mt!Mz/Cb!Mz!NO.7q!NO!NS/Cb!NS!NV.7q!NV!NW/Cb!NW!NZ.7q!NZ!N[/Cb!N[!N]/Cb!N]!Nd.7q!Nd!Ng/Cb!Ng!Nk.7q!Nk!Nx/Cb!Nx# U.7q# U# V/Cb# V# h.7q# h#!`/Cb#!`#!a.7q#!a#!b/Cb#!b#!g.7q#!g#!h/Cb#!h#!j.7q#!j##g/Cb##g##h.7q##h#*s/Cb#*s#*t.7q#*t#*x/Cb#*x#*z.7q#*z#+R/Cb#+R#+S.7q#+S#+T/Cb#+T#+U.7q#+U#+Y/Cb#+Y#+[.7q#+[#,V/Cb#,V#,W.7q#,W#,[/Cb#,[#,^.7q#,^#-P/Cb#-P#-Q.7q#-Q#-U/Cb#-U#-W.7q#-W#-_/Cb#-_#-`.7q#-`#-a/Cb#-a#-b.7q#-b#-f/Cb#-f#-h.7q#-h#-w/Cb#-w#-x.7q#-x#/T/Cb#/T#/U.7q#/U#/Y/Cb#/Y#/[.7q#/[#0q/Cb#0q#1h.7q#1h#1x/Cb#1x#2Y.7q#2Y#4R/Cb#4R#4_.7q#4_#Au/Cb#Au#Aw.7q#Aw#BY/Cb#BY#BZ.7q#BZ#Bu/Cb#Bu#Bz.7q#Bz#Di/Cb#Di#EO.7q#EO#E]/Cb#E]#E^.7q#E^#Eb/Cb#Eb#Ep.7q#Ep#FS/Cb#FS#Fb.7q#Fb#Ft/Cb#Ft#GS.7q#GS#Ga/Cb#Ga#Gb.7q#Gb#Ge/Cb#Ge#Gt.7q#Gt#Hz/Cb#Hz#Io.7q#Io#Ip/Cb#Ip#It.7q#It#Iu/Cb#Iu#K[.7q#K[#MW/Cb#MW#M`.7q#M`#NZ/Cb#NZ#N[.7q#N[#N]/Cb#N]#Nb.7q#Nb$ z/Cb$ z$!U.7q$!U$!s/Cb$!s$#x.7q$#x$$h/Cb$$h$$j.7q$$j$$o/Cb$$o$$z.7q$$z$%x/Cb$%x$&_.7q$&_$&f/Cb$&f$'p.7q$'p$(X/Cb$(X$(b.7q$(b$)i/Cb$)i$+_.7q$+_$+`/Cb$+`$-a.7q$-a$.b/Cb$.b$.s.7q$.s$.z/Cb$.z$0T.7q$0T$0s/Cb$0s$1Q.7q$1Q$1R/Cb$1R$1S/Cb$1S$1^.7q$1^$2[/Cb$2[$2v.7q$2v$3l/Cb$3l$4g.7q$4g$4j/Cb$4j$4t.7q$4t$5j/Cb$5j$7y.7q$7y$7}/Cb$7}$8O.7q$8O$8S/Cb$8S$8V.7q$8V$8W/Cb$8W$8X/Cb$8X$8b.7q$8b$z/Cb5>z5>{.7q5>{5>|/Cb5>|5?P.7q5?P5?Q/Cb5?Q5?R/Cb5?R5?T.7q5?T5?Y/Cb5?Y5?[.7q5?[5?]/Cb5?]5?^.7q5?^5?_/Cb5?_5?w.7q5?w5?z/Cb5?z5?|.7q5?|5@X/Cb5@X5@`.7q5@`5@c/Cb5@c5@o.7q5@o5@u/Cb5@u5@w.7q5@w5@}/Cb5@}5AP.7q5AP5AV/Cb5AV5A`.7q5A`5Ag/Cb5Ag5Ah.7q5Ah5Ao/Cb5Ao5Dv.7q5Dv5Ek/Cb5Ek5FY.7q5FY;%S/Cb;%S;%`.7q;%`;%w/Cb;%w;%{.7q;%{;'O/Cb;'O;'S.7q;'S;=`.8v<%l?&r.7q?&r?.p/Cb?.p?.r.7q?.r?1Q/Cb?1Q?1x.7q?1x?2P/Cb?2P?2].7q?2]?2b/Cb?2b?2g.7q?2g?2h/Cb?2h?2i.7q?2i?2s/Cb?2s?2t.7q?2t?3R/Cb?3R?3S.7q?3S?3X/Cb?3X?3Y.7q?3Y?3Z/Cb?3Z?3[.7q?3[?3]/Cb?3]?3^/Cb?3^?3_.7q?3_?3`/Cb?3`?3a/Cb?3a?3b.7q?3b?5r/Cb?5r?6e.7q?6e?>`/Cb?>`?>r.7q?>r?@U/Cb?@U?@W.7q?@W?A`/Cb?A`?BY.7q?BY?Bf/Cb?Bf?EO.7q?EO?ET/Cb?ET?EU.7q?EU?HR/Cb?HR?Hw.7q?Hw?Ic/Cb?Ic?Ii.7q?Ii?JT/Cb?JT?J`.7q?J`?L]/Cb?L]?L`.7q?L`?Lf/Cb?Lf?Lh.7q?Lh?Ln/Cb?Ln?Lp.7q?Lp?Lv/Cb?Lv?Lx.7q?Lx?L{/Cb?L{O.7q!R0Ja2hS!RmPOY.8_Zq.8_qr0JYrs0JYst0JYtu0JYuv0JYvw0JYwx0JYxz.8_z{0JY{|0JY|!O.8_!O!P0JY!P!Q0JY!Q![0JY![!a.8_!a!b0JY!b!c.8_!c!}0JY!}#O.8_#O#P0JY#P#R.8_#R#S0JY#S#T0JY#T#o0JY#o$p.8_$p$q0JY$q${.8_${$|0JY$|%Q.8_%Q%R0JY%R%W.8_%W%o0JY%o%p.8_%p&a0JY&a&b.8_&b0`0JY0`0d.8_0d0p0JY0p1O.8_1O1T0JY1T1[.8_1[1]0JY1]1^.8_1^1_0JY1_4U.8_4U4Z0JY4Z4[.8_4[4]0JY4]4^0JY4^4`.8_4`4d0JY4d4l.8_4l4m0JY4m4n.8_4n4q0JY4q4r.8_4r4s0JY4s4t.8_4t5Y0JY5Y5Z.8_5Z7Q0JY7Q7R.8_7R:S0JY:S:[.8_:[=p0JY=p=y.8_=y>q0JY>q>s.8_>s>t0JY>t>{.8_>{?t0JY?tA`.8_A`A{0JYA{BQ.8_BQBT0JYBTCS.8_CSDP0JYDPDt.8_DtDu0JYDuDv0JYDvDw.8_DwGO0JYGOGP.8_GPGQ0JYGQGa.8_GaGb0JYGbGc0JYGcGj.8_GjGk0JYGkGl0JYGlGv.8_GvGy0JYGyG{.8_G{G|0JYG|H^.8_H^H_0JYH_H`.8_H`IO0JYIOIm.8_ImKj0JYKjKu.8_KuKv0JYKvL`.8_L`MR0JYMRM[.8_M[M]0JYM]M^0JYM^Mb.8_MbMc0JYMcMh.8_MhNO0JYNONS.8_NSNT0JYNTN^.8_N^N_0JYN_Nb.8_NbNc0JYNcNz.8_Nz! e0JY! e!#O.8_!#O!#P0JY!#P!#Q.8_!#Q!#]0JY!#]!%W.8_!%W!&`0JY!&`!&c.8_!&c!&d0JY!&d!&v.8_!&v!&w0JY!&w!'O.8_!'O!'Y0JY!'Y!'i.8_!'i!'p0JY!'p!'q.8_!'q!'x0JY!'x!'}.8_!'}!(V0JY!(V!(X.8_!(X!(Y0JY!(Y!(Z0JY!(Z!(].8_!(]!(s0JY!(s!(t.8_!(t!({0JY!({!(|.8_!(|!(}0JY!(}!)Q.8_!)Q!)U0JY!)U!)X.8_!)X!)Y0JY!)Y!)j.8_!)j!)k0JY!)k!)x.8_!)x!)y0JY!)y!)z0JY!)z!){.8_!){!*O0JY!*O!*^.8_!*^!*_0JY!*_!*`0JY!*`!*s.8_!*s!*y0JY!*y!*}.8_!*}!+O0JY!+O!+P0JY!+P!+R.8_!+R!+i0JY!+i!+j.8_!+j!+q0JY!+q!+r.8_!+r!+s0JY!+s!+t0JY!+t!+u.8_!+u!+v0JY!+v!+w0JY!+w!+x.8_!+x!+y0JY!+y!+z0JY!+z!,k.8_!,k!,o0JY!,o!,p.8_!,p!,q0JY!,q!-U.8_!-U!-X0JY!-X!-i.8_!-i!-r0JY!-r!-s.8_!-s!-v0JY!-v!-w.8_!-w!._0JY!._!.`.8_!.`!.g0JY!.g!.h.8_!.h!.i0JY!.i!.j0JY!.j!.k.8_!.k!.p0JY!.p!.s.8_!.s!.t0JY!.t!/W.8_!/W!/X0JY!/X!/h.8_!/h!/i0JY!/i!/j0JY!/j!0_.8_!0_!0g0JY!0g!0i.8_!0i!0j0JY!0j!0k0JY!0k!0m.8_!0m!1T0JY!1T!1U.8_!1U!1]0JY!1]!1^.8_!1^!1_0JY!1_!1`0JY!1`!1a.8_!1a!1f0JY!1f!1i.8_!1i!1j0JY!1j!2Y.8_!2Y!2Z0JY!2Z!2[0JY!2[!2].8_!2]!2`0JY!2`!2o.8_!2o!2p0JY!2p!3R.8_!3R!3S0JY!3S!3T.8_!3T!3Z0JY!3Z!3^.8_!3^!3a0JY!3a!3b.8_!3b!3f0JY!3f!3i.8_!3i!3j0JY!3j!3k0JY!3k!3l.8_!3l!3m0JY!3m!3n.8_!3n!3o0JY!3o!3p0JY!3p!3s.8_!3s!3t0JY!3t!3u0JY!3u!3x.8_!3x!3{0JY!3{!4O.8_!4O!4[0JY!4[!4r.8_!4r!4s0JY!4s!5y.8_!5y!6R0JY!6R!6S.8_!6S!6V0JY!6V!6W.8_!6W!6o0JY!6o!6p.8_!6p!6z0JY!6z!6{.8_!6{!7Q0JY!7Q!7T.8_!7T!7U0JY!7U!7p.8_!7p!7q0JY!7q!7r0JY!7r!7x.8_!7x!7y0JY!7y!7z0JY!7z!8o.8_!8o!8w0JY!8w!8x.8_!8x!8{0JY!8{!8|.8_!8|!9e0JY!9e!9f.8_!9f!9p0JY!9p!9q.8_!9q!9v0JY!9v!9y.8_!9y!9z0JY!9z!:l.8_!:l!:m0JY!:m!:n.8_!:n!:o0JY!:o!:p0JY!:p!;P.8_!;P!;Q0JY!;Q!;R0JY!;R!;e.8_!;e!;m0JY!;m!;n.8_!;n!;q0JY!;q!;r.8_!;r!O.8_!>O!>U0JY!>U!>Z.8_!>Z!>m0JY!>m!>p.8_!>p!?Y0JY!?Y!?Z.8_!?Z!?d0JY!?d!?e.8_!?e!?f0JY!?f!?h.8_!?h!?o0JY!?o!@{.8_!@{!A}0JY!A}!BO.8_!BO!BP0JY!BP!BQ0JY!BQ!B^.8_!B^!Be0JY!Be!Cq.8_!Cq!Cr0JY!Cr!Cs0JY!Cs!Ct.8_!Ct!Cu0JY!Cu!Cw.8_!Cw!Cx0JY!Cx!Cy0JY!Cy!Cz.8_!Cz!C{0JY!C{!C}.8_!C}!DO0JY!DO!DU.8_!DU!DY0JY!DY!DZ.8_!DZ!Db0JY!Db!Dc.8_!Dc!Df0JY!Df!Dg.8_!Dg!Dh0JY!Dh!Di.8_!Di!Dj0JY!Dj!Dl.8_!Dl!Dm0JY!Dm!Dn0JY!Dn!Do.8_!Do!Ds0JY!Ds!Dt.8_!Dt!Du0JY!Du!Dv0JY!Dv!EP.8_!EP!EQ0JY!EQ!ES.8_!ES!EX0JY!EX!EY.8_!EY!EZ0JY!EZ!Ep.8_!Ep!Et0JY!Et!Ff.8_!Ff!Fg0JY!Fg!Gx.8_!Gx!HQ0JY!HQ!HR.8_!HR!Hw0JY!Hw!Id.8_!Id!Ii0JY!Ii!LQ.8_!LQ!L}0JY!L}!Mc.8_!Mc!Md0JY!Md!Mt.8_!Mt!Mz0JY!Mz!NO.8_!NO!NS0JY!NS!NV.8_!NV!NW0JY!NW!NZ.8_!NZ!N[0JY!N[!N]0JY!N]!Nd.8_!Nd!Ng0JY!Ng!Nk.8_!Nk!Nx0JY!Nx# U.8_# U# V0JY# V# h.8_# h#!`0JY#!`#!a.8_#!a#!b0JY#!b#!g.8_#!g#!h0JY#!h#!j.8_#!j##g0JY##g##h.8_##h#*s0JY#*s#*t.8_#*t#*x0JY#*x#*z.8_#*z#+R0JY#+R#+S.8_#+S#+T0JY#+T#+U.8_#+U#+Y0JY#+Y#+[.8_#+[#,V0JY#,V#,W.8_#,W#,[0JY#,[#,^.8_#,^#-P0JY#-P#-Q.8_#-Q#-U0JY#-U#-W.8_#-W#-_0JY#-_#-`.8_#-`#-a0JY#-a#-b.8_#-b#-f0JY#-f#-h.8_#-h#-w0JY#-w#-x.8_#-x#/T0JY#/T#/U.8_#/U#/Y0JY#/Y#/[.8_#/[#0q0JY#0q#1h.8_#1h#1x0JY#1x#2Y.8_#2Y#4R0JY#4R#4_.8_#4_#Au0JY#Au#Aw.8_#Aw#BY0JY#BY#BZ.8_#BZ#Bu0JY#Bu#Bz.8_#Bz#Di0JY#Di#EO.8_#EO#E]0JY#E]#E^.8_#E^#Eb0JY#Eb#Ep.8_#Ep#FS0JY#FS#Fb.8_#Fb#Ft0JY#Ft#GS.8_#GS#Ga0JY#Ga#Gb.8_#Gb#Ge0JY#Ge#Gt.8_#Gt#Hz0JY#Hz#Io.8_#Io#Ip0JY#Ip#It.8_#It#Iu0JY#Iu#K[.8_#K[#MW0JY#MW#M`.8_#M`#NZ0JY#NZ#N[.8_#N[#N]0JY#N]#Nb.8_#Nb$ z0JY$ z$!U.8_$!U$!s0JY$!s$#x.8_$#x$$h0JY$$h$$j.8_$$j$$o0JY$$o$$z.8_$$z$%x0JY$%x$&_.8_$&_$&f0JY$&f$'p.8_$'p$(X0JY$(X$(b.8_$(b$)i0JY$)i$+_.8_$+_$+`0JY$+`$-a.8_$-a$.b0JY$.b$.s.8_$.s$.z0JY$.z$0T.8_$0T$0s0JY$0s$1Q.8_$1Q$1R0JY$1R$1S0JY$1S$1^.8_$1^$2[0JY$2[$2v.8_$2v$3l0JY$3l$4g.8_$4g$4j0JY$4j$4t.8_$4t$5j0JY$5j$7y.8_$7y$7}0JY$7}$8O.8_$8O$8S0JY$8S$8V.8_$8V$8W0JY$8W$8X0JY$8X$8b.8_$8b$z0JY5>z5>{.8_5>{5>|0JY5>|5?P.8_5?P5?Q0JY5?Q5?R0JY5?R5?T.8_5?T5?Y0JY5?Y5?[.8_5?[5?]0JY5?]5?^.8_5?^5?_0JY5?_5?w.8_5?w5?z0JY5?z5?|.8_5?|5@X0JY5@X5@`.8_5@`5@c0JY5@c5@o.8_5@o5@u0JY5@u5@w.8_5@w5@}0JY5@}5AP.8_5AP5AV0JY5AV5A`.8_5A`5Ag0JY5Ag5Ah.8_5Ah5Ao0JY5Ao5Dv.8_5Dv5Ek0JY5Ek5FY.8_5FY;%S0JY;%S;%`.8_;%`;%w0JY;%w;%{.8_;%{;'O0JY;'O;'S.8_;'S;=`.8p<%l?&r.8_?&r?.p0JY?.p?.r.8_?.r?1Q0JY?1Q?1x.8_?1x?2P0JY?2P?2].8_?2]?2b0JY?2b?2g.8_?2g?2h0JY?2h?2i.8_?2i?2s0JY?2s?2t.8_?2t?3R0JY?3R?3S.8_?3S?3X0JY?3X?3Y.8_?3Y?3Z0JY?3Z?3[.8_?3[?3]0JY?3]?3^0JY?3^?3_.8_?3_?3`0JY?3`?3a0JY?3a?3b.8_?3b?5r0JY?5r?6e.8_?6e?>`0JY?>`?>r.8_?>r?@U0JY?@U?@W.8_?@W?A`0JY?A`?BY.8_?BY?Bf0JY?Bf?EO.8_?EO?ET0JY?ET?EU.8_?EU?HR0JY?HR?Hw.8_?Hw?Ic0JY?Ic?Ii.8_?Ii?JT0JY?JT?J`.8_?J`?L]0JY?L]?L`.8_?L`?Lf0JY?Lf?Lh.8_?Lh?Ln0JY?Ln?Lp.8_?Lp?Lv0JY?Lv?Lx.8_?Lx?L{0JY?L{O.8_!T2#U2izQS!RmPOY.8|YZ!+oZq.8|qr2!{rs2!{st2!{tu2!{uv2!{vw2!{wx0JYxz.8|z{2!{{|2!{|!O.8|!O!P2!{!P!Q2!{!Q![2!{![!a.8|!a!b2!{!b!c.8|!c!}2!{!}#O.8|#O#P2!{#P#R.8|#R#S2!{#S#T2!{#T#o2!{#o$p.8|$p$q2!{$q${.8|${$|2!{$|%Q.8|%Q%R2!{%R%W.8|%W%o2!{%o%p.8|%p&a2!{&a&b.8|&b0`2!{0`0d.8|0d0p2!{0p1O.8|1O1T2!{1T1[.8|1[1]2!{1]1^.8|1^1_2!{1_4U.8|4U4Z2!{4Z4[.8|4[4]2!{4]4^2!{4^4`.8|4`4d2!{4d4l.8|4l4m2!{4m4n.8|4n4q2!{4q4r.8|4r4s2!{4s4t.8|4t5Y2!{5Y5Z.8|5Z7Q2!{7Q7R.8|7R:S2!{:S:[.8|:[=p2!{=p=y.8|=y>q2!{>q>s.8|>s>t2!{>t>{.8|>{?t2!{?tA`.8|A`A{2!{A{BQ.8|BQBT2!{BTCS.8|CSDP2!{DPDt.8|DtDu2!{DuDv2!{DvDw.8|DwGO2!{GOGP.8|GPGQ2!{GQGa.8|GaGb2!{GbGc2!{GcGj.8|GjGk2!{GkGl2!{GlGv.8|GvGy2!{GyG{.8|G{G|2!{G|H^.8|H^H_2!{H_H`.8|H`IO2!{IOIm.8|ImKj2!{KjKu.8|KuKv2!{KvL`.8|L`MR2!{MRM[.8|M[M]2!{M]M^2!{M^Mb.8|MbMc2!{McMh.8|MhNO2!{NONS.8|NSNT2!{NTN^.8|N^N_2!{N_Nb.8|NbNc2!{NcNz.8|Nz! e2!{! e!#O.8|!#O!#P2!{!#P!#Q.8|!#Q!#]2!{!#]!%W.8|!%W!&`2!{!&`!&c.8|!&c!&d2!{!&d!&v.8|!&v!&w2!{!&w!'O.8|!'O!'Y2!{!'Y!'i.8|!'i!'p2!{!'p!'q.8|!'q!'x2!{!'x!'}.8|!'}!(V2!{!(V!(X.8|!(X!(Y2!{!(Y!(Z2!{!(Z!(].8|!(]!(s2!{!(s!(t.8|!(t!({2!{!({!(|.8|!(|!(}2!{!(}!)Q.8|!)Q!)U2!{!)U!)X.8|!)X!)Y2!{!)Y!)j.8|!)j!)k2!{!)k!)x.8|!)x!)y2!{!)y!)z2!{!)z!){.8|!){!*O2!{!*O!*^.8|!*^!*_2!{!*_!*`2!{!*`!*s.8|!*s!*y2!{!*y!*}.8|!*}!+O2!{!+O!+P2!{!+P!+R.8|!+R!+i2!{!+i!+j.8|!+j!+q2!{!+q!+r.8|!+r!+s2!{!+s!+t2!{!+t!+u.8|!+u!+v2!{!+v!+w2!{!+w!+x.8|!+x!+y2!{!+y!+z2!{!+z!,k.8|!,k!,o2!{!,o!,p.8|!,p!,q2!{!,q!-U.8|!-U!-X2!{!-X!-i.8|!-i!-r2!{!-r!-s.8|!-s!-v2!{!-v!-w.8|!-w!._2!{!._!.`.8|!.`!.g2!{!.g!.h.8|!.h!.i2!{!.i!.j2!{!.j!.k.8|!.k!.p2!{!.p!.s.8|!.s!.t2!{!.t!/W.8|!/W!/X2!{!/X!/h.8|!/h!/i2!{!/i!/j2!{!/j!0_.8|!0_!0g2!{!0g!0i.8|!0i!0j2!{!0j!0k2!{!0k!0m.8|!0m!1T2!{!1T!1U.8|!1U!1]2!{!1]!1^.8|!1^!1_2!{!1_!1`2!{!1`!1a.8|!1a!1f2!{!1f!1i.8|!1i!1j2!{!1j!2Y.8|!2Y!2Z2!{!2Z!2[2!{!2[!2].8|!2]!2`2!{!2`!2o.8|!2o!2p2!{!2p!3R.8|!3R!3S2!{!3S!3T.8|!3T!3Z2!{!3Z!3^.8|!3^!3a2!{!3a!3b.8|!3b!3f2!{!3f!3i.8|!3i!3j2!{!3j!3k2!{!3k!3l.8|!3l!3m2!{!3m!3n.8|!3n!3o2!{!3o!3p2!{!3p!3s.8|!3s!3t2!{!3t!3u2!{!3u!3x.8|!3x!3{2!{!3{!4O.8|!4O!4[2!{!4[!4r.8|!4r!4s2!{!4s!5y.8|!5y!6R2!{!6R!6S.8|!6S!6V2!{!6V!6W.8|!6W!6o2!{!6o!6p.8|!6p!6z2!{!6z!6{.8|!6{!7Q2!{!7Q!7T.8|!7T!7U2!{!7U!7p.8|!7p!7q2!{!7q!7r2!{!7r!7x.8|!7x!7y2!{!7y!7z2!{!7z!8o.8|!8o!8w2!{!8w!8x.8|!8x!8{2!{!8{!8|.8|!8|!9e2!{!9e!9f.8|!9f!9p2!{!9p!9q.8|!9q!9v2!{!9v!9y.8|!9y!9z2!{!9z!:l.8|!:l!:m2!{!:m!:n.8|!:n!:o2!{!:o!:p2!{!:p!;P.8|!;P!;Q2!{!;Q!;R2!{!;R!;e.8|!;e!;m2!{!;m!;n.8|!;n!;q2!{!;q!;r.8|!;r!O.8|!>O!>U2!{!>U!>Z.8|!>Z!>m2!{!>m!>p.8|!>p!?Y2!{!?Y!?Z.8|!?Z!?d2!{!?d!?e.8|!?e!?f2!{!?f!?h.8|!?h!?o2!{!?o!@{.8|!@{!A}2!{!A}!BO.8|!BO!BP2!{!BP!BQ2!{!BQ!B^.8|!B^!Be2!{!Be!Cq.8|!Cq!Cr2!{!Cr!Cs2!{!Cs!Ct.8|!Ct!Cu2!{!Cu!Cw.8|!Cw!Cx2!{!Cx!Cy2!{!Cy!Cz.8|!Cz!C{2!{!C{!C}.8|!C}!DO2!{!DO!DU.8|!DU!DY2!{!DY!DZ.8|!DZ!Db2!{!Db!Dc.8|!Dc!Df2!{!Df!Dg.8|!Dg!Dh2!{!Dh!Di.8|!Di!Dj2!{!Dj!Dl.8|!Dl!Dm2!{!Dm!Dn2!{!Dn!Do.8|!Do!Ds2!{!Ds!Dt.8|!Dt!Du2!{!Du!Dv2!{!Dv!EP.8|!EP!EQ2!{!EQ!ES.8|!ES!EX2!{!EX!EY.8|!EY!EZ2!{!EZ!Ep.8|!Ep!Et2!{!Et!Ff.8|!Ff!Fg2!{!Fg!Gx.8|!Gx!HQ2!{!HQ!HR.8|!HR!Hw2!{!Hw!Id.8|!Id!Ii2!{!Ii!LQ.8|!LQ!L}2!{!L}!Mc.8|!Mc!Md2!{!Md!Mt.8|!Mt!Mz2!{!Mz!NO.8|!NO!NS2!{!NS!NV.8|!NV!NW2!{!NW!NZ.8|!NZ!N[2!{!N[!N]2!{!N]!Nd.8|!Nd!Ng2!{!Ng!Nk.8|!Nk!Nx2!{!Nx# U.8|# U# V2!{# V# h.8|# h#!`2!{#!`#!a.8|#!a#!b2!{#!b#!g.8|#!g#!h2!{#!h#!j.8|#!j##g2!{##g##h.8|##h#*s2!{#*s#*t.8|#*t#*x2!{#*x#*z.8|#*z#+R2!{#+R#+S.8|#+S#+T2!{#+T#+U.8|#+U#+Y2!{#+Y#+[.8|#+[#,V2!{#,V#,W.8|#,W#,[2!{#,[#,^.8|#,^#-P2!{#-P#-Q.8|#-Q#-U2!{#-U#-W.8|#-W#-_2!{#-_#-`.8|#-`#-a2!{#-a#-b.8|#-b#-f2!{#-f#-h.8|#-h#-w2!{#-w#-x.8|#-x#/T2!{#/T#/U.8|#/U#/Y2!{#/Y#/[.8|#/[#0q2!{#0q#1h.8|#1h#1x2!{#1x#2Y.8|#2Y#4R2!{#4R#4_.8|#4_#Au2!{#Au#Aw.8|#Aw#BY2!{#BY#BZ.8|#BZ#Bu2!{#Bu#Bz.8|#Bz#Di2!{#Di#EO.8|#EO#E]2!{#E]#E^.8|#E^#Eb2!{#Eb#Ep.8|#Ep#FS2!{#FS#Fb.8|#Fb#Ft2!{#Ft#GS.8|#GS#Ga2!{#Ga#Gb.8|#Gb#Ge2!{#Ge#Gt.8|#Gt#Hz2!{#Hz#Io.8|#Io#Ip2!{#Ip#It.8|#It#Iu2!{#Iu#K[.8|#K[#MW2!{#MW#M`.8|#M`#NZ2!{#NZ#N[.8|#N[#N]2!{#N]#Nb.8|#Nb$ z2!{$ z$!U.8|$!U$!s2!{$!s$#x.8|$#x$$h2!{$$h$$j.8|$$j$$o2!{$$o$$z.8|$$z$%x2!{$%x$&_.8|$&_$&f2!{$&f$'p.8|$'p$(X2!{$(X$(b.8|$(b$)i2!{$)i$+_.8|$+_$+`2!{$+`$-a.8|$-a$.b2!{$.b$.s.8|$.s$.z2!{$.z$0T.8|$0T$0s2!{$0s$1Q.8|$1Q$1R2!{$1R$1S2!{$1S$1^.8|$1^$2[2!{$2[$2v.8|$2v$3l2!{$3l$4g.8|$4g$4j2!{$4j$4t.8|$4t$5j2!{$5j$7y.8|$7y$7}2!{$7}$8O.8|$8O$8S2!{$8S$8V.8|$8V$8W2!{$8W$8X2!{$8X$8b.8|$8b$z2!{5>z5>{.8|5>{5>|2!{5>|5?P.8|5?P5?Q2!{5?Q5?R2!{5?R5?T.8|5?T5?Y2!{5?Y5?[.8|5?[5?]2!{5?]5?^.8|5?^5?_2!{5?_5?w.8|5?w5?z2!{5?z5?|.8|5?|5@X2!{5@X5@`.8|5@`5@c2!{5@c5@o.8|5@o5@u2!{5@u5@w.8|5@w5@}2!{5@}5AP.8|5AP5AV2!{5AV5A`.8|5A`5Ag2!{5Ag5Ah.8|5Ah5Ao2!{5Ao5Dv.8|5Dv5Ek2!{5Ek5FY.8|5FY;%S2!{;%S;%`.8|;%`;%w2!{;%w;%{.8|;%{;'O2!{;'O;'S.8|;'S;=`.9j<%l?&r.8|?&r?.p2!{?.p?.r.8|?.r?1Q2!{?1Q?1x.8|?1x?2P2!{?2P?2].8|?2]?2b2!{?2b?2g.8|?2g?2h2!{?2h?2i.8|?2i?2s2!{?2s?2t.8|?2t?3R2!{?3R?3S.8|?3S?3X2!{?3X?3Y.8|?3Y?3Z2!{?3Z?3[.8|?3[?3]2!{?3]?3^2!{?3^?3_.8|?3_?3`2!{?3`?3a2!{?3a?3b.8|?3b?5r2!{?5r?6e.8|?6e?>`2!{?>`?>r.8|?>r?@U2!{?@U?@W.8|?@W?A`2!{?A`?BY.8|?BY?Bf2!{?Bf?EO.8|?EO?ET2!{?ET?EU.8|?EU?HR2!{?HR?Hw.8|?Hw?Ic2!{?Ic?Ii.8|?Ii?JT2!{?JT?J`.8|?J`?L]2!{?L]?L`.8|?L`?Lf2!{?Lf?Lh.8|?Lh?Ln2!{?Ln?Lp.8|?Lp?Lv2!{?Lv?Lx.8|?Lx?L{2!{?L{O.8|!_3*O2i!OW|SS!RmPOY.9vYZ!,^Zq.9vqr3)srs/Cbst3)stu3)suv3)svw3)swx3)sxz.9vz{3)s{|3)s|!O.9v!O!P3)s!P!Q3)s!Q![3)s![!a.9v!a!b3)s!b!c.9v!c!}3)s!}#O.9v#O#P3)s#P#R.9v#R#S3)s#S#T40m#T#o3)s#o$p.9v$p$q3)s$q${.9v${$|3)s$|%Q.9v%Q%R3)s%R%W.9v%W%o3)s%o%p.9v%p&a3)s&a&b.9v&b0`3)s0`0d.9v0d0p3)s0p1O.9v1O1T3)s1T1[.9v1[1]3)s1]1^.9v1^1_3)s1_4U.9v4U4Z3)s4Z4[.9v4[4]3)s4]4^3)s4^4`.9v4`4d3)s4d4l.9v4l4m3)s4m4n.9v4n4q3)s4q4r.9v4r4s3)s4s4t.9v4t5Y3)s5Y5Z.9v5Z7Q3)s7Q7R.9v7R:S3)s:S:[.9v:[=p3)s=p=y.9v=y>q3)s>q>s.9v>s>t3)s>t>{.9v>{?t3)s?tA`.9vA`A{3)sA{BQ.9vBQBT3)sBTCS.9vCSDP3)sDPDt.9vDtDu3)sDuDv3)sDvDw.9vDwGO3)sGOGP.9vGPGQ3)sGQGa.9vGaGb3)sGbGc3)sGcGj.9vGjGk3)sGkGl3)sGlGv.9vGvGy3)sGyG{.9vG{G|3)sG|H^.9vH^H_3)sH_H`.9vH`IO3)sIOIm.9vImKj3)sKjKu.9vKuKv3)sKvL`.9vL`MR3)sMRM[.9vM[M]3)sM]M^3)sM^Mb.9vMbMc3)sMcMh.9vMhNO3)sNONS.9vNSNT3)sNTN^.9vN^N_3)sN_Nb.9vNbNc3)sNcNz.9vNz! e3)s! e!#O.9v!#O!#P3)s!#P!#Q.9v!#Q!#]3)s!#]!%W.9v!%W!&`3)s!&`!&c.9v!&c!&d3)s!&d!&v.9v!&v!&w3)s!&w!'O.9v!'O!'Y3)s!'Y!'i.9v!'i!'p3)s!'p!'q.9v!'q!'x3)s!'x!'}.9v!'}!(V3)s!(V!(X.9v!(X!(Y3)s!(Y!(Z3)s!(Z!(].9v!(]!(s3)s!(s!(t.9v!(t!({3)s!({!(|.9v!(|!(}3)s!(}!)Q.9v!)Q!)U3)s!)U!)X.9v!)X!)Y3)s!)Y!)j.9v!)j!)k3)s!)k!)x.9v!)x!)y3)s!)y!)z3)s!)z!){.9v!){!*O3)s!*O!*^.9v!*^!*_3)s!*_!*`3)s!*`!*s.9v!*s!*y3)s!*y!*}.9v!*}!+O3)s!+O!+P3)s!+P!+R.9v!+R!+i3)s!+i!+j.9v!+j!+q3)s!+q!+r.9v!+r!+s3)s!+s!+t3)s!+t!+u.9v!+u!+v3)s!+v!+w3)s!+w!+x.9v!+x!+y3)s!+y!+z3)s!+z!,k.9v!,k!,o3)s!,o!,p.9v!,p!,q3)s!,q!-U.9v!-U!-X3)s!-X!-i.9v!-i!-r3)s!-r!-s.9v!-s!-v3)s!-v!-w.9v!-w!._3)s!._!.`.9v!.`!.g3)s!.g!.h.9v!.h!.i3)s!.i!.j3)s!.j!.k.9v!.k!.p3)s!.p!.s.9v!.s!.t3)s!.t!/W.9v!/W!/X3)s!/X!/h.9v!/h!/i3)s!/i!/j3)s!/j!0_.9v!0_!0g3)s!0g!0i.9v!0i!0j3)s!0j!0k3)s!0k!0m.9v!0m!1T3)s!1T!1U.9v!1U!1]3)s!1]!1^.9v!1^!1_3)s!1_!1`3)s!1`!1a.9v!1a!1f3)s!1f!1i.9v!1i!1j3)s!1j!2Y.9v!2Y!2Z3)s!2Z!2[3)s!2[!2].9v!2]!2`3)s!2`!2o.9v!2o!2p3)s!2p!3R.9v!3R!3S3)s!3S!3T.9v!3T!3Z3)s!3Z!3^.9v!3^!3a3)s!3a!3b.9v!3b!3f3)s!3f!3i.9v!3i!3j3)s!3j!3k3)s!3k!3l.9v!3l!3m3)s!3m!3n.9v!3n!3o3)s!3o!3p3)s!3p!3s.9v!3s!3t3)s!3t!3u3)s!3u!3x.9v!3x!3{3)s!3{!4O.9v!4O!4[3)s!4[!4r.9v!4r!4s3)s!4s!5y.9v!5y!6R3)s!6R!6S.9v!6S!6V3)s!6V!6W.9v!6W!6o3)s!6o!6p.9v!6p!6z3)s!6z!6{.9v!6{!7Q3)s!7Q!7T.9v!7T!7U3)s!7U!7p.9v!7p!7q3)s!7q!7r3)s!7r!7x.9v!7x!7y3)s!7y!7z3)s!7z!8o.9v!8o!8w3)s!8w!8x.9v!8x!8{3)s!8{!8|.9v!8|!9e3)s!9e!9f.9v!9f!9p3)s!9p!9q.9v!9q!9v3)s!9v!9y.9v!9y!9z3)s!9z!:l.9v!:l!:m3)s!:m!:n.9v!:n!:o3)s!:o!:p3)s!:p!;P.9v!;P!;Q3)s!;Q!;R3)s!;R!;e.9v!;e!;m3)s!;m!;n.9v!;n!;q3)s!;q!;r.9v!;r!O.9v!>O!>U3)s!>U!>Z.9v!>Z!>m3)s!>m!>p.9v!>p!?Y3)s!?Y!?Z.9v!?Z!?d3)s!?d!?e.9v!?e!?f3)s!?f!?h.9v!?h!?o3)s!?o!@{.9v!@{!A}3)s!A}!BO.9v!BO!BP3)s!BP!BQ3)s!BQ!B^.9v!B^!Be3)s!Be!Cq.9v!Cq!Cr3)s!Cr!Cs3)s!Cs!Ct.9v!Ct!Cu3)s!Cu!Cw.9v!Cw!Cx3)s!Cx!Cy3)s!Cy!Cz.9v!Cz!C{3)s!C{!C}.9v!C}!DO3)s!DO!DU.9v!DU!DY3)s!DY!DZ.9v!DZ!Db3)s!Db!Dc.9v!Dc!Df3)s!Df!Dg.9v!Dg!Dh3)s!Dh!Di.9v!Di!Dj3)s!Dj!Dl.9v!Dl!Dm3)s!Dm!Dn3)s!Dn!Do.9v!Do!Ds3)s!Ds!Dt.9v!Dt!Du3)s!Du!Dv3)s!Dv!EP.9v!EP!EQ3)s!EQ!ES.9v!ES!EX3)s!EX!EY.9v!EY!EZ3)s!EZ!Ep.9v!Ep!Et3)s!Et!Ff.9v!Ff!Fg3)s!Fg!Gx.9v!Gx!HQ3)s!HQ!HR.9v!HR!Hw3)s!Hw!Id.9v!Id!Ii3)s!Ii!LQ.9v!LQ!L}3)s!L}!Mc.9v!Mc!Md3)s!Md!Mt.9v!Mt!Mz3)s!Mz!NO.9v!NO!NS3)s!NS!NV.9v!NV!NW3)s!NW!NZ.9v!NZ!N[3)s!N[!N]3)s!N]!Nd.9v!Nd!Ng3)s!Ng!Nk.9v!Nk!Nx3)s!Nx# U.9v# U# V3)s# V# h.9v# h#!`3)s#!`#!a.9v#!a#!b3)s#!b#!g.9v#!g#!h3)s#!h#!j.9v#!j##g3)s##g##h.9v##h#*s3)s#*s#*t.9v#*t#*x3)s#*x#*z.9v#*z#+R3)s#+R#+S.9v#+S#+T3)s#+T#+U.9v#+U#+Y3)s#+Y#+[.9v#+[#,V3)s#,V#,W.9v#,W#,[3)s#,[#,^.9v#,^#-P3)s#-P#-Q.9v#-Q#-U3)s#-U#-W.9v#-W#-_3)s#-_#-`.9v#-`#-a3)s#-a#-b.9v#-b#-f3)s#-f#-h.9v#-h#-w3)s#-w#-x.9v#-x#/T3)s#/T#/U.9v#/U#/Y3)s#/Y#/[.9v#/[#0q3)s#0q#1h.9v#1h#1x3)s#1x#2Y.9v#2Y#4R3)s#4R#4_.9v#4_#Au3)s#Au#Aw.9v#Aw#BY3)s#BY#BZ.9v#BZ#Bu3)s#Bu#Bz.9v#Bz#Di3)s#Di#EO.9v#EO#E]3)s#E]#E^.9v#E^#Eb3)s#Eb#Ep.9v#Ep#FS3)s#FS#Fb.9v#Fb#Ft3)s#Ft#GS.9v#GS#Ga3)s#Ga#Gb.9v#Gb#Ge3)s#Ge#Gt.9v#Gt#Hz3)s#Hz#Io.9v#Io#Ip3)s#Ip#It.9v#It#Iu3)s#Iu#K[.9v#K[#MW3)s#MW#M`.9v#M`#NZ3)s#NZ#N[.9v#N[#N]3)s#N]#Nb.9v#Nb$ z3)s$ z$!U.9v$!U$!s3)s$!s$#x.9v$#x$$h3)s$$h$$j.9v$$j$$o3)s$$o$$z.9v$$z$%x3)s$%x$&_.9v$&_$&f3)s$&f$'p.9v$'p$(X3)s$(X$(b.9v$(b$)i3)s$)i$+_.9v$+_$+`3)s$+`$-a.9v$-a$.b3)s$.b$.s.9v$.s$.z3)s$.z$0T.9v$0T$0s3)s$0s$1Q.9v$1Q$1R3)s$1R$1S3)s$1S$1^.9v$1^$2[3)s$2[$2v.9v$2v$3l3)s$3l$4g.9v$4g$4j3)s$4j$4t.9v$4t$5j3)s$5j$7y.9v$7y$7}3)s$7}$8O.9v$8O$8S3)s$8S$8V.9v$8V$8W3)s$8W$8X3)s$8X$8b.9v$8b$z3)s5>z5>{.9v5>{5>|3)s5>|5?P.9v5?P5?Q3)s5?Q5?R3)s5?R5?T.9v5?T5?Y3)s5?Y5?[.9v5?[5?]3)s5?]5?^.9v5?^5?_3)s5?_5?w.9v5?w5?z3)s5?z5?|.9v5?|5@X3)s5@X5@`.9v5@`5@c3)s5@c5@o.9v5@o5@u3)s5@u5@w.9v5@w5@}3)s5@}5AP.9v5AP5AV3)s5AV5A`.9v5A`5Ag3)s5Ag5Ah.9v5Ah5Ao3)s5Ao5Dv.9v5Dv5Ek3)s5Ek5FY.9v5FY;%S3)s;%S;%`.9v;%`;%w3)s;%w;%{.9v;%{;'O3)s;'O;'S.9v;'S;=`.;`<%l?&r.9v?&r?.p3)s?.p?.r.9v?.r?1Q3)s?1Q?1x.9v?1x?2P3)s?2P?2].9v?2]?2b3)s?2b?2g.9v?2g?2h3)s?2h?2i.9v?2i?2s3)s?2s?2t.9v?2t?3R3)s?3R?3S.9v?3S?3X3)s?3X?3Y.9v?3Y?3Z3)s?3Z?3[.9v?3[?3]3)s?3]?3^3)s?3^?3_.9v?3_?3`3)s?3`?3a3)s?3a?3b.9v?3b?5r3)s?5r?6e.9v?6e?>`3)s?>`?>r.9v?>r?@U3)s?@U?@W.9v?@W?A`3)s?A`?BY.9v?BY?Bf3)s?Bf?EO.9v?EO?ET3)s?ET?EU.9v?EU?HR3)s?HR?Hw.9v?Hw?Ic3)s?Ic?Ii.9v?Ii?JT3)s?JT?J`.9v?J`?L]3)s?L]?L`.9v?L`?Lf3)s?Lf?Lh.9v?Lh?Ln3)s?Ln?Lp.9v?Lp?Lv3)s?Lv?Lx.9v?Lx?L{3)s?L{O.9v!V40v2i|SS!RmPOY.:lYZ!,zZq.:lqr40mrs0JYst40mtu40muv40mvw40mwx40mxz.:lz{40m{|40m|!O.:l!O!P40m!P!Q40m!Q![40m![!a.:l!a!b40m!b!c.:l!c!}40m!}#O.:l#O#P40m#P#R.:l#R#S40m#S#T40m#T#o40m#o$p.:l$p$q40m$q${.:l${$|40m$|%Q.:l%Q%R40m%R%W.:l%W%o40m%o%p.:l%p&a40m&a&b.:l&b0`40m0`0d.:l0d0p40m0p1O.:l1O1T40m1T1[.:l1[1]40m1]1^.:l1^1_40m1_4U.:l4U4Z40m4Z4[.:l4[4]40m4]4^40m4^4`.:l4`4d40m4d4l.:l4l4m40m4m4n.:l4n4q40m4q4r.:l4r4s40m4s4t.:l4t5Y40m5Y5Z.:l5Z7Q40m7Q7R.:l7R:S40m:S:[.:l:[=p40m=p=y.:l=y>q40m>q>s.:l>s>t40m>t>{.:l>{?t40m?tA`.:lA`A{40mA{BQ.:lBQBT40mBTCS.:lCSDP40mDPDt.:lDtDu40mDuDv40mDvDw.:lDwGO40mGOGP.:lGPGQ40mGQGa.:lGaGb40mGbGc40mGcGj.:lGjGk40mGkGl40mGlGv.:lGvGy40mGyG{.:lG{G|40mG|H^.:lH^H_40mH_H`.:lH`IO40mIOIm.:lImKj40mKjKu.:lKuKv40mKvL`.:lL`MR40mMRM[.:lM[M]40mM]M^40mM^Mb.:lMbMc40mMcMh.:lMhNO40mNONS.:lNSNT40mNTN^.:lN^N_40mN_Nb.:lNbNc40mNcNz.:lNz! e40m! e!#O.:l!#O!#P40m!#P!#Q.:l!#Q!#]40m!#]!%W.:l!%W!&`40m!&`!&c.:l!&c!&d40m!&d!&v.:l!&v!&w40m!&w!'O.:l!'O!'Y40m!'Y!'i.:l!'i!'p40m!'p!'q.:l!'q!'x40m!'x!'}.:l!'}!(V40m!(V!(X.:l!(X!(Y40m!(Y!(Z40m!(Z!(].:l!(]!(s40m!(s!(t.:l!(t!({40m!({!(|.:l!(|!(}40m!(}!)Q.:l!)Q!)U40m!)U!)X.:l!)X!)Y40m!)Y!)j.:l!)j!)k40m!)k!)x.:l!)x!)y40m!)y!)z40m!)z!){.:l!){!*O40m!*O!*^.:l!*^!*_40m!*_!*`40m!*`!*s.:l!*s!*y40m!*y!*}.:l!*}!+O40m!+O!+P40m!+P!+R.:l!+R!+i40m!+i!+j.:l!+j!+q40m!+q!+r.:l!+r!+s40m!+s!+t40m!+t!+u.:l!+u!+v40m!+v!+w40m!+w!+x.:l!+x!+y40m!+y!+z40m!+z!,k.:l!,k!,o40m!,o!,p.:l!,p!,q40m!,q!-U.:l!-U!-X40m!-X!-i.:l!-i!-r40m!-r!-s.:l!-s!-v40m!-v!-w.:l!-w!._40m!._!.`.:l!.`!.g40m!.g!.h.:l!.h!.i40m!.i!.j40m!.j!.k.:l!.k!.p40m!.p!.s.:l!.s!.t40m!.t!/W.:l!/W!/X40m!/X!/h.:l!/h!/i40m!/i!/j40m!/j!0_.:l!0_!0g40m!0g!0i.:l!0i!0j40m!0j!0k40m!0k!0m.:l!0m!1T40m!1T!1U.:l!1U!1]40m!1]!1^.:l!1^!1_40m!1_!1`40m!1`!1a.:l!1a!1f40m!1f!1i.:l!1i!1j40m!1j!2Y.:l!2Y!2Z40m!2Z!2[40m!2[!2].:l!2]!2`40m!2`!2o.:l!2o!2p40m!2p!3R.:l!3R!3S40m!3S!3T.:l!3T!3Z40m!3Z!3^.:l!3^!3a40m!3a!3b.:l!3b!3f40m!3f!3i.:l!3i!3j40m!3j!3k40m!3k!3l.:l!3l!3m40m!3m!3n.:l!3n!3o40m!3o!3p40m!3p!3s.:l!3s!3t40m!3t!3u40m!3u!3x.:l!3x!3{40m!3{!4O.:l!4O!4[40m!4[!4r.:l!4r!4s40m!4s!5y.:l!5y!6R40m!6R!6S.:l!6S!6V40m!6V!6W.:l!6W!6o40m!6o!6p.:l!6p!6z40m!6z!6{.:l!6{!7Q40m!7Q!7T.:l!7T!7U40m!7U!7p.:l!7p!7q40m!7q!7r40m!7r!7x.:l!7x!7y40m!7y!7z40m!7z!8o.:l!8o!8w40m!8w!8x.:l!8x!8{40m!8{!8|.:l!8|!9e40m!9e!9f.:l!9f!9p40m!9p!9q.:l!9q!9v40m!9v!9y.:l!9y!9z40m!9z!:l.:l!:l!:m40m!:m!:n.:l!:n!:o40m!:o!:p40m!:p!;P.:l!;P!;Q40m!;Q!;R40m!;R!;e.:l!;e!;m40m!;m!;n.:l!;n!;q40m!;q!;r.:l!;r!O.:l!>O!>U40m!>U!>Z.:l!>Z!>m40m!>m!>p.:l!>p!?Y40m!?Y!?Z.:l!?Z!?d40m!?d!?e.:l!?e!?f40m!?f!?h.:l!?h!?o40m!?o!@{.:l!@{!A}40m!A}!BO.:l!BO!BP40m!BP!BQ40m!BQ!B^.:l!B^!Be40m!Be!Cq.:l!Cq!Cr40m!Cr!Cs40m!Cs!Ct.:l!Ct!Cu40m!Cu!Cw.:l!Cw!Cx40m!Cx!Cy40m!Cy!Cz.:l!Cz!C{40m!C{!C}.:l!C}!DO40m!DO!DU.:l!DU!DY40m!DY!DZ.:l!DZ!Db40m!Db!Dc.:l!Dc!Df40m!Df!Dg.:l!Dg!Dh40m!Dh!Di.:l!Di!Dj40m!Dj!Dl.:l!Dl!Dm40m!Dm!Dn40m!Dn!Do.:l!Do!Ds40m!Ds!Dt.:l!Dt!Du40m!Du!Dv40m!Dv!EP.:l!EP!EQ40m!EQ!ES.:l!ES!EX40m!EX!EY.:l!EY!EZ40m!EZ!Ep.:l!Ep!Et40m!Et!Ff.:l!Ff!Fg40m!Fg!Gx.:l!Gx!HQ40m!HQ!HR.:l!HR!Hw40m!Hw!Id.:l!Id!Ii40m!Ii!LQ.:l!LQ!L}40m!L}!Mc.:l!Mc!Md40m!Md!Mt.:l!Mt!Mz40m!Mz!NO.:l!NO!NS40m!NS!NV.:l!NV!NW40m!NW!NZ.:l!NZ!N[40m!N[!N]40m!N]!Nd.:l!Nd!Ng40m!Ng!Nk.:l!Nk!Nx40m!Nx# U.:l# U# V40m# V# h.:l# h#!`40m#!`#!a.:l#!a#!b40m#!b#!g.:l#!g#!h40m#!h#!j.:l#!j##g40m##g##h.:l##h#*s40m#*s#*t.:l#*t#*x40m#*x#*z.:l#*z#+R40m#+R#+S.:l#+S#+T40m#+T#+U.:l#+U#+Y40m#+Y#+[.:l#+[#,V40m#,V#,W.:l#,W#,[40m#,[#,^.:l#,^#-P40m#-P#-Q.:l#-Q#-U40m#-U#-W.:l#-W#-_40m#-_#-`.:l#-`#-a40m#-a#-b.:l#-b#-f40m#-f#-h.:l#-h#-w40m#-w#-x.:l#-x#/T40m#/T#/U.:l#/U#/Y40m#/Y#/[.:l#/[#0q40m#0q#1h.:l#1h#1x40m#1x#2Y.:l#2Y#4R40m#4R#4_.:l#4_#Au40m#Au#Aw.:l#Aw#BY40m#BY#BZ.:l#BZ#Bu40m#Bu#Bz.:l#Bz#Di40m#Di#EO.:l#EO#E]40m#E]#E^.:l#E^#Eb40m#Eb#Ep.:l#Ep#FS40m#FS#Fb.:l#Fb#Ft40m#Ft#GS.:l#GS#Ga40m#Ga#Gb.:l#Gb#Ge40m#Ge#Gt.:l#Gt#Hz40m#Hz#Io.:l#Io#Ip40m#Ip#It.:l#It#Iu40m#Iu#K[.:l#K[#MW40m#MW#M`.:l#M`#NZ40m#NZ#N[.:l#N[#N]40m#N]#Nb.:l#Nb$ z40m$ z$!U.:l$!U$!s40m$!s$#x.:l$#x$$h40m$$h$$j.:l$$j$$o40m$$o$$z.:l$$z$%x40m$%x$&_.:l$&_$&f40m$&f$'p.:l$'p$(X40m$(X$(b.:l$(b$)i40m$)i$+_.:l$+_$+`40m$+`$-a.:l$-a$.b40m$.b$.s.:l$.s$.z40m$.z$0T.:l$0T$0s40m$0s$1Q.:l$1Q$1R40m$1R$1S40m$1S$1^.:l$1^$2[40m$2[$2v.:l$2v$3l40m$3l$4g.:l$4g$4j40m$4j$4t.:l$4t$5j40m$5j$7y.:l$7y$7}40m$7}$8O.:l$8O$8S40m$8S$8V.:l$8V$8W40m$8W$8X40m$8X$8b.:l$8b$z40m5>z5>{.:l5>{5>|40m5>|5?P.:l5?P5?Q40m5?Q5?R40m5?R5?T.:l5?T5?Y40m5?Y5?[.:l5?[5?]40m5?]5?^.:l5?^5?_40m5?_5?w.:l5?w5?z40m5?z5?|.:l5?|5@X40m5@X5@`.:l5@`5@c40m5@c5@o.:l5@o5@u40m5@u5@w.:l5@w5@}40m5@}5AP.:l5AP5AV40m5AV5A`.:l5A`5Ag40m5Ag5Ah.:l5Ah5Ao40m5Ao5Dv.:l5Dv5Ek40m5Ek5FY.:l5FY;%S40m;%S;%`.:l;%`;%w40m;%w;%{.:l;%{;'O40m;'O;'S.:l;'S;=`.;Y<%l?&r.:l?&r?.p40m?.p?.r.:l?.r?1Q40m?1Q?1x.:l?1x?2P40m?2P?2].:l?2]?2b40m?2b?2g.:l?2g?2h40m?2h?2i.:l?2i?2s40m?2s?2t.:l?2t?3R40m?3R?3S.:l?3S?3X40m?3X?3Y.:l?3Y?3Z40m?3Z?3[.:l?3[?3]40m?3]?3^40m?3^?3_.:l?3_?3`40m?3`?3a40m?3a?3b.:l?3b?5r40m?5r?6e.:l?6e?>`40m?>`?>r.:l?>r?@U40m?@U?@W.:l?@W?A`40m?A`?BY.:l?BY?Bf40m?Bf?EO.:l?EO?ET40m?ET?EU.:l?EU?HR40m?HR?Hw.:l?Hw?Ic40m?Ic?Ii.:l?Ii?JT40m?JT?J`.:l?J`?L]40m?L]?L`.:l?L`?Lf40m?Lf?Lh.:l?Lh?Ln40m?Ln?Lp.:l?Lp?Lv40m?Lv?Lx.:l?Lx?L{40m?L{O.:l!X57p2i|SzQS!RmPOY.;fYZ!-iZq.;fqr57ers2!{st57etu57euv57evw57ewx40mxz.;fz{57e{|57e|!O.;f!O!P57e!P!Q57e!Q![57e![!a.;f!a!b57e!b!c.;f!c!}57e!}#O.;f#O#P57e#P#R.;f#R#S57e#S#T57e#T#o57e#o$p.;f$p$q57e$q${.;f${$|57e$|%Q.;f%Q%R57e%R%W.;f%W%o57e%o%p.;f%p&a57e&a&b.;f&b0`57e0`0d.;f0d0p57e0p1O.;f1O1T57e1T1[.;f1[1]57e1]1^.;f1^1_57e1_4U.;f4U4Z57e4Z4[.;f4[4]57e4]4^57e4^4`.;f4`4d57e4d4l.;f4l4m57e4m4n.;f4n4q57e4q4r.;f4r4s57e4s4t.;f4t5Y57e5Y5Z.;f5Z7Q57e7Q7R.;f7R:S57e:S:[.;f:[=p57e=p=y.;f=y>q57e>q>s.;f>s>t57e>t>{.;f>{?t57e?tA`.;fA`A{57eA{BQ.;fBQBT57eBTCS.;fCSDP57eDPDt.;fDtDu57eDuDv57eDvDw.;fDwGO57eGOGP.;fGPGQ57eGQGa.;fGaGb57eGbGc57eGcGj.;fGjGk57eGkGl57eGlGv.;fGvGy57eGyG{.;fG{G|57eG|H^.;fH^H_57eH_H`.;fH`IO57eIOIm.;fImKj57eKjKu.;fKuKv57eKvL`.;fL`MR57eMRM[.;fM[M]57eM]M^57eM^Mb.;fMbMc57eMcMh.;fMhNO57eNONS.;fNSNT57eNTN^.;fN^N_57eN_Nb.;fNbNc57eNcNz.;fNz! e57e! e!#O.;f!#O!#P57e!#P!#Q.;f!#Q!#]57e!#]!%W.;f!%W!&`57e!&`!&c.;f!&c!&d57e!&d!&v.;f!&v!&w57e!&w!'O.;f!'O!'Y57e!'Y!'i.;f!'i!'p57e!'p!'q.;f!'q!'x57e!'x!'}.;f!'}!(V57e!(V!(X.;f!(X!(Y57e!(Y!(Z57e!(Z!(].;f!(]!(s57e!(s!(t.;f!(t!({57e!({!(|.;f!(|!(}57e!(}!)Q.;f!)Q!)U57e!)U!)X.;f!)X!)Y57e!)Y!)j.;f!)j!)k57e!)k!)x.;f!)x!)y57e!)y!)z57e!)z!){.;f!){!*O57e!*O!*^.;f!*^!*_57e!*_!*`57e!*`!*s.;f!*s!*y57e!*y!*}.;f!*}!+O57e!+O!+P57e!+P!+R.;f!+R!+i57e!+i!+j.;f!+j!+q57e!+q!+r.;f!+r!+s57e!+s!+t57e!+t!+u.;f!+u!+v57e!+v!+w57e!+w!+x.;f!+x!+y57e!+y!+z57e!+z!,k.;f!,k!,o57e!,o!,p.;f!,p!,q57e!,q!-U.;f!-U!-X57e!-X!-i.;f!-i!-r57e!-r!-s.;f!-s!-v57e!-v!-w.;f!-w!._57e!._!.`.;f!.`!.g57e!.g!.h.;f!.h!.i57e!.i!.j57e!.j!.k.;f!.k!.p57e!.p!.s.;f!.s!.t57e!.t!/W.;f!/W!/X57e!/X!/h.;f!/h!/i57e!/i!/j57e!/j!0_.;f!0_!0g57e!0g!0i.;f!0i!0j57e!0j!0k57e!0k!0m.;f!0m!1T57e!1T!1U.;f!1U!1]57e!1]!1^.;f!1^!1_57e!1_!1`57e!1`!1a.;f!1a!1f57e!1f!1i.;f!1i!1j57e!1j!2Y.;f!2Y!2Z57e!2Z!2[57e!2[!2].;f!2]!2`57e!2`!2o.;f!2o!2p57e!2p!3R.;f!3R!3S57e!3S!3T.;f!3T!3Z57e!3Z!3^.;f!3^!3a57e!3a!3b.;f!3b!3f57e!3f!3i.;f!3i!3j57e!3j!3k57e!3k!3l.;f!3l!3m57e!3m!3n.;f!3n!3o57e!3o!3p57e!3p!3s.;f!3s!3t57e!3t!3u57e!3u!3x.;f!3x!3{57e!3{!4O.;f!4O!4[57e!4[!4r.;f!4r!4s57e!4s!5y.;f!5y!6R57e!6R!6S.;f!6S!6V57e!6V!6W.;f!6W!6o57e!6o!6p.;f!6p!6z57e!6z!6{.;f!6{!7Q57e!7Q!7T.;f!7T!7U57e!7U!7p.;f!7p!7q57e!7q!7r57e!7r!7x.;f!7x!7y57e!7y!7z57e!7z!8o.;f!8o!8w57e!8w!8x.;f!8x!8{57e!8{!8|.;f!8|!9e57e!9e!9f.;f!9f!9p57e!9p!9q.;f!9q!9v57e!9v!9y.;f!9y!9z57e!9z!:l.;f!:l!:m57e!:m!:n.;f!:n!:o57e!:o!:p57e!:p!;P.;f!;P!;Q57e!;Q!;R57e!;R!;e.;f!;e!;m57e!;m!;n.;f!;n!;q57e!;q!;r.;f!;r!O.;f!>O!>U57e!>U!>Z.;f!>Z!>m57e!>m!>p.;f!>p!?Y57e!?Y!?Z.;f!?Z!?d57e!?d!?e.;f!?e!?f57e!?f!?h.;f!?h!?o57e!?o!@{.;f!@{!A}57e!A}!BO.;f!BO!BP57e!BP!BQ57e!BQ!B^.;f!B^!Be57e!Be!Cq.;f!Cq!Cr57e!Cr!Cs57e!Cs!Ct.;f!Ct!Cu57e!Cu!Cw.;f!Cw!Cx57e!Cx!Cy57e!Cy!Cz.;f!Cz!C{57e!C{!C}.;f!C}!DO57e!DO!DU.;f!DU!DY57e!DY!DZ.;f!DZ!Db57e!Db!Dc.;f!Dc!Df57e!Df!Dg.;f!Dg!Dh57e!Dh!Di.;f!Di!Dj57e!Dj!Dl.;f!Dl!Dm57e!Dm!Dn57e!Dn!Do.;f!Do!Ds57e!Ds!Dt.;f!Dt!Du57e!Du!Dv57e!Dv!EP.;f!EP!EQ57e!EQ!ES.;f!ES!EX57e!EX!EY.;f!EY!EZ57e!EZ!Ep.;f!Ep!Et57e!Et!Ff.;f!Ff!Fg57e!Fg!Gx.;f!Gx!HQ57e!HQ!HR.;f!HR!Hw57e!Hw!Id.;f!Id!Ii57e!Ii!LQ.;f!LQ!L}57e!L}!Mc.;f!Mc!Md57e!Md!Mt.;f!Mt!Mz57e!Mz!NO.;f!NO!NS57e!NS!NV.;f!NV!NW57e!NW!NZ.;f!NZ!N[57e!N[!N]57e!N]!Nd.;f!Nd!Ng57e!Ng!Nk.;f!Nk!Nx57e!Nx# U.;f# U# V57e# V# h.;f# h#!`57e#!`#!a.;f#!a#!b57e#!b#!g.;f#!g#!h57e#!h#!j.;f#!j##g57e##g##h.;f##h#*s57e#*s#*t.;f#*t#*x57e#*x#*z.;f#*z#+R57e#+R#+S.;f#+S#+T57e#+T#+U.;f#+U#+Y57e#+Y#+[.;f#+[#,V57e#,V#,W.;f#,W#,[57e#,[#,^.;f#,^#-P57e#-P#-Q.;f#-Q#-U57e#-U#-W.;f#-W#-_57e#-_#-`.;f#-`#-a57e#-a#-b.;f#-b#-f57e#-f#-h.;f#-h#-w57e#-w#-x.;f#-x#/T57e#/T#/U.;f#/U#/Y57e#/Y#/[.;f#/[#0q57e#0q#1h.;f#1h#1x57e#1x#2Y.;f#2Y#4R57e#4R#4_.;f#4_#Au57e#Au#Aw.;f#Aw#BY57e#BY#BZ.;f#BZ#Bu57e#Bu#Bz.;f#Bz#Di57e#Di#EO.;f#EO#E]57e#E]#E^.;f#E^#Eb57e#Eb#Ep.;f#Ep#FS57e#FS#Fb.;f#Fb#Ft57e#Ft#GS.;f#GS#Ga57e#Ga#Gb.;f#Gb#Ge57e#Ge#Gt.;f#Gt#Hz57e#Hz#Io.;f#Io#Ip57e#Ip#It.;f#It#Iu57e#Iu#K[.;f#K[#MW57e#MW#M`.;f#M`#NZ57e#NZ#N[.;f#N[#N]57e#N]#Nb.;f#Nb$ z57e$ z$!U.;f$!U$!s57e$!s$#x.;f$#x$$h57e$$h$$j.;f$$j$$o57e$$o$$z.;f$$z$%x57e$%x$&_.;f$&_$&f57e$&f$'p.;f$'p$(X57e$(X$(b.;f$(b$)i57e$)i$+_.;f$+_$+`57e$+`$-a.;f$-a$.b57e$.b$.s.;f$.s$.z57e$.z$0T.;f$0T$0s57e$0s$1Q.;f$1Q$1R57e$1R$1S57e$1S$1^.;f$1^$2[57e$2[$2v.;f$2v$3l57e$3l$4g.;f$4g$4j57e$4j$4t.;f$4t$5j57e$5j$7y.;f$7y$7}57e$7}$8O.;f$8O$8S57e$8S$8V.;f$8V$8W57e$8W$8X57e$8X$8b.;f$8b$z57e5>z5>{.;f5>{5>|57e5>|5?P.;f5?P5?Q57e5?Q5?R57e5?R5?T.;f5?T5?Y57e5?Y5?[.;f5?[5?]57e5?]5?^.;f5?^5?_57e5?_5?w.;f5?w5?z57e5?z5?|.;f5?|5@X57e5@X5@`.;f5@`5@c57e5@c5@o.;f5@o5@u57e5@u5@w.;f5@w5@}57e5@}5AP.;f5AP5AV57e5AV5A`.;f5A`5Ag57e5Ag5Ah.;f5Ah5Ao57e5Ao5Dv.;f5Dv5Ek57e5Ek5FY.;f5FY;%S57e;%S;%`.;f;%`;%w57e;%w;%{.;f;%{;'O57e;'O;'S.;f;'S;=`.<[<%l?&r.;f?&r?.p57e?.p?.r.;f?.r?1Q57e?1Q?1x.;f?1x?2P57e?2P?2].;f?2]?2b57e?2b?2g.;f?2g?2h57e?2h?2i.;f?2i?2s57e?2s?2t.;f?2t?3R57e?3R?3S.;f?3S?3X57e?3X?3Y.;f?3Y?3Z57e?3Z?3[.;f?3[?3]57e?3]?3^57e?3^?3_.;f?3_?3`57e?3`?3a57e?3a?3b.;f?3b?5r57e?5r?6e.;f?6e?>`57e?>`?>r.;f?>r?@U57e?@U?@W.;f?@W?A`57e?A`?BY.;f?BY?Bf57e?Bf?EO.;f?EO?ET57e?ET?EU.;f?EU?HR57e?HR?Hw.;f?Hw?Ic57e?Ic?Ii.;f?Ii?JT57e?JT?J`.;f?J`?L]57e?L]?L`.;f?L`?Lf57e?Lf?Lh.;f?Lh?Ln57e?Ln?Lp.;f?Lp?Lv57e?Lv?Lx.;f?Lx?L{57e?L{O.;f_6>l2g!OW|SzQ!kPmPOq!)tqr!2wrs#9kst!2wtu!2wuv!2wvw!2wwx';ixz!)tz{!2w{|!2w|!O!)t!O!P!2w!P!Q!2w!Q![!2w![!a!)t!a!b!2w!b!c!)t!c!}!2w!}#O!)t#O#P!2w#P#R!)t#R#S!2w#S#T)Hy#T#o!2w#o$p!)t$p$q!2w$q${!)t${$|!2w$|%Q!)t%Q%R!2w%R%W!)t%W%o!2w%o%p!)t%p&a!2w&a&b!)t&b0`!2w0`0d!)t0d0p!2w0p1O!)t1O1T!2w1T1[!)t1[1]!2w1]1^!)t1^1_!2w1_4U!)t4U4Z!2w4Z4[!)t4[4]!2w4]4^!2w4^4`!)t4`4d!2w4d4l!)t4l4m!2w4m4n!)t4n4q!2w4q4r!)t4r4s!2w4s4t!)t4t5Y!2w5Y5Z!)t5Z7Q!2w7Q7R!)t7R:S!2w:S:[!)t:[=p!2w=p=y!)t=y>q!2w>q>s!)t>s>t!2w>t>{!)t>{?t!2w?tA`!)tA`A{!2wA{BQ!)tBQBT!2wBTCS!)tCSDP!2wDPDt!)tDtDu!2wDuDv!2wDvDw!)tDwGO!2wGOGP!)tGPGQ!2wGQGa!)tGaGb!2wGbGc!2wGcGj!)tGjGk!2wGkGl!2wGlGv!)tGvGy!2wGyG{!)tG{G|!2wG|H^!)tH^H_!2wH_H`!)tH`IO!2wIOIm!)tImKj!2wKjKu!)tKuKv!2wKvL`!)tL`MR!2wMRM[!)tM[M]!2wM]M^!2wM^Mb!)tMbMc!2wMcMh!)tMhNO!2wNONS!)tNSNT!2wNTN^!)tN^N_!2wN_Nb!)tNbNc!2wNcNz!)tNz! e!2w! e!#O!)t!#O!#P!2w!#P!#Q!)t!#Q!#]!2w!#]!%W!)t!%W!&`!2w!&`!&c!)t!&c!&d!2w!&d!&v!)t!&v!&w!2w!&w!'O!)t!'O!'Y!2w!'Y!'i!)t!'i!'p!2w!'p!'q!)t!'q!'x!2w!'x!'}!)t!'}!(V!2w!(V!(X!)t!(X!(Y!2w!(Y!(Z!2w!(Z!(]!)t!(]!(s!2w!(s!(t!)t!(t!({!2w!({!(|!)t!(|!(}!2w!(}!)Q!)t!)Q!)U!2w!)U!)X!)t!)X!)Y!2w!)Y!)j!)t!)j!)k!2w!)k!)x!)t!)x!)y!2w!)y!)z!2w!)z!){!)t!){!*O!2w!*O!*^!)t!*^!*_!2w!*_!*`!2w!*`!*s!)t!*s!*y!2w!*y!*}!)t!*}!+O!2w!+O!+P!2w!+P!+R!)t!+R!+i!2w!+i!+j!)t!+j!+q!2w!+q!+r!)t!+r!+s!2w!+s!+t!2w!+t!+u!)t!+u!+v!2w!+v!+w!2w!+w!+x!)t!+x!+y!2w!+y!+z!2w!+z!,k!)t!,k!,o!2w!,o!,p!)t!,p!,q!2w!,q!-U!)t!-U!-X!2w!-X!-i!)t!-i!-r!2w!-r!-s!)t!-s!-v!2w!-v!-w!)t!-w!._!2w!._!.`!)t!.`!.g!2w!.g!.h!)t!.h!.i!2w!.i!.j!2w!.j!.k!)t!.k!.p!2w!.p!.s!)t!.s!.t!2w!.t!/W!)t!/W!/X!2w!/X!/h!)t!/h!/i!2w!/i!/j!2w!/j!0_!)t!0_!0g!2w!0g!0i!)t!0i!0j!2w!0j!0k!2w!0k!0m!)t!0m!1T!2w!1T!1U!)t!1U!1]!2w!1]!1^!)t!1^!1_!2w!1_!1`!2w!1`!1a!)t!1a!1f!2w!1f!1i!)t!1i!1j!2w!1j!2Y!)t!2Y!2Z!2w!2Z!2[!2w!2[!2]!)t!2]!2`!2w!2`!2o!)t!2o!2p!2w!2p!3R!)t!3R!3S!2w!3S!3T!)t!3T!3Z!2w!3Z!3^!)t!3^!3a!2w!3a!3b!)t!3b!3f!2w!3f!3i!)t!3i!3j!2w!3j!3k!2w!3k!3l!)t!3l!3m!2w!3m!3n!)t!3n!3o!2w!3o!3p!2w!3p!3s!)t!3s!3t!2w!3t!3u!2w!3u!3x!)t!3x!3{!2w!3{!4O!)t!4O!4[!2w!4[!4r!)t!4r!4s!2w!4s!5y!)t!5y!6R!2w!6R!6S!)t!6S!6V!2w!6V!6W!)t!6W!6o!2w!6o!6p!)t!6p!6z!2w!6z!6{!)t!6{!7Q!2w!7Q!7T!)t!7T!7U!2w!7U!7p!)t!7p!7q!2w!7q!7r!2w!7r!7x!)t!7x!7y!2w!7y!7z!2w!7z!8o!)t!8o!8w!2w!8w!8x!)t!8x!8{!2w!8{!8|!)t!8|!9e!2w!9e!9f!)t!9f!9p!2w!9p!9q!)t!9q!9v!2w!9v!9y!)t!9y!9z!2w!9z!:l!)t!:l!:m!2w!:m!:n!)t!:n!:o!2w!:o!:p!2w!:p!;P!)t!;P!;Q!2w!;Q!;R!2w!;R!;e!)t!;e!;m!2w!;m!;n!)t!;n!;q!2w!;q!;r!)t!;r!O!)t!>O!>U!2w!>U!>Z!)t!>Z!>m!2w!>m!>p!)t!>p!?Y!2w!?Y!?Z!)t!?Z!?d!2w!?d!?e!)t!?e!?f!2w!?f!?h!)t!?h!?o!2w!?o!@{!)t!@{!A}!2w!A}!BO!)t!BO!BP!2w!BP!BQ!2w!BQ!B^!)t!B^!Be!2w!Be!Cq!)t!Cq!Cr!2w!Cr!Cs!2w!Cs!Ct!)t!Ct!Cu!2w!Cu!Cw!)t!Cw!Cx!2w!Cx!Cy!2w!Cy!Cz!)t!Cz!C{!2w!C{!C}!)t!C}!DO!2w!DO!DU!)t!DU!DY!2w!DY!DZ!)t!DZ!Db!2w!Db!Dc!)t!Dc!Df!2w!Df!Dg!)t!Dg!Dh!2w!Dh!Di!)t!Di!Dj!2w!Dj!Dl!)t!Dl!Dm!2w!Dm!Dn!2w!Dn!Do!)t!Do!Ds!2w!Ds!Dt!)t!Dt!Du!2w!Du!Dv!2w!Dv!EP!)t!EP!EQ!2w!EQ!ES!)t!ES!EX!2w!EX!EY!)t!EY!EZ!2w!EZ!Ep!)t!Ep!Et!2w!Et!Ff!)t!Ff!Fg!2w!Fg!Gx!)t!Gx!HQ!2w!HQ!HR!)t!HR!Hw!2w!Hw!Id!)t!Id!Ii!2w!Ii!LQ!)t!LQ!L}!2w!L}!Mc!)t!Mc!Md!2w!Md!Mt!)t!Mt!Mz!2w!Mz!NO!)t!NO!NS!2w!NS!NV!)t!NV!NW!2w!NW!NZ!)t!NZ!N[!2w!N[!N]!2w!N]!Nd!)t!Nd!Ng!2w!Ng!Nk!)t!Nk!Nx!2w!Nx# U!)t# U# V!2w# V# h!)t# h#!`!2w#!`#!a!)t#!a#!b!2w#!b#!g!)t#!g#!h!2w#!h#!j!)t#!j##g!2w##g##h!)t##h#*s!2w#*s#*t!)t#*t#*x!2w#*x#*z!)t#*z#+R!2w#+R#+S!)t#+S#+T!2w#+T#+U!)t#+U#+Y!2w#+Y#+[!)t#+[#,V!2w#,V#,W!)t#,W#,[!2w#,[#,^!)t#,^#-P!2w#-P#-Q!)t#-Q#-U!2w#-U#-W!)t#-W#-_!2w#-_#-`!)t#-`#-a!2w#-a#-b!)t#-b#-f!2w#-f#-h!)t#-h#-w!2w#-w#-x!)t#-x#/T!2w#/T#/U!)t#/U#/Y!2w#/Y#/[!)t#/[#0q!2w#0q#1h!)t#1h#1x!2w#1x#2Y!)t#2Y#4R!2w#4R#4_!)t#4_#Au!2w#Au#Aw!)t#Aw#BY!2w#BY#BZ!)t#BZ#Bu!2w#Bu#Bz!)t#Bz#Di!2w#Di#EO!)t#EO#E]!2w#E]#E^!)t#E^#Eb!2w#Eb#Ep!)t#Ep#FS!2w#FS#Fb!)t#Fb#Ft!2w#Ft#GS!)t#GS#Ga!2w#Ga#Gb!)t#Gb#Ge!2w#Ge#Gt!)t#Gt#Hz!2w#Hz#Io!)t#Io#Ip!2w#Ip#It!)t#It#Iu!2w#Iu#K[!)t#K[#MW!2w#MW#M`!)t#M`#NZ!2w#NZ#N[!)t#N[#N]!2w#N]#Nb!)t#Nb$ z!2w$ z$!U!)t$!U$!s!2w$!s$#x!)t$#x$$h!2w$$h$$j!)t$$j$$o!2w$$o$$z!)t$$z$%x!2w$%x$&_!)t$&_$&f!2w$&f$'p!)t$'p$(X!2w$(X$(b!)t$(b$)i!2w$)i$+_!)t$+_$+`!2w$+`$-a!)t$-a$.b!2w$.b$.s!)t$.s$.z!2w$.z$0T!)t$0T$0s!2w$0s$1Q!)t$1Q$1R!2w$1R$1S!2w$1S$1^!)t$1^$2[!2w$2[$2v!)t$2v$3l!2w$3l$4g!)t$4g$4j!2w$4j$4t!)t$4t$5j!2w$5j$7y!)t$7y$7}!2w$7}$8O!)t$8O$8S!2w$8S$8V!)t$8V$8W!2w$8W$8X!2w$8X$8b!)t$8b$z!2w5>z5>{!)t5>{5>|!2w5>|5?P!)t5?P5?Q!2w5?Q5?R!2w5?R5?T!)t5?T5?Y!2w5?Y5?[!)t5?[5?]!2w5?]5?^!)t5?^5?_!2w5?_5?w!)t5?w5?z!2w5?z5?|!)t5?|5@X!2w5@X5@`!)t5@`5@c!2w5@c5@o!)t5@o5@u!2w5@u5@w!)t5@w5@}!2w5@}5AP!)t5AP5AV!2w5AV5A`!)t5A`5Ag!2w5Ag5Ah!)t5Ah5Ao!2w5Ao5Dv!)t5Dv5Ek!2w5Ek5FY!)t5FY;%S!2w;%S;%`!)t;%`;%w!2w;%w;%{!)t;%{;'O!2w;'O;'S!)t;'S;=`!.]<%l?&r!)t?&r?.p!2w?.p?.r!)t?.r?1Q!2w?1Q?1x!)t?1x?2P!2w?2P?2]!)t?2]?2b!2w?2b?2g!)t?2g?2h!2w?2h?2i!)t?2i?2s!2w?2s?2t!)t?2t?3R!2w?3R?3S!)t?3S?3X!2w?3X?3Y!)t?3Y?3Z!2w?3Z?3[!)t?3[?3]!2w?3]?3^!2w?3^?3_!)t?3_?3`!2w?3`?3a!2w?3a?3b!)t?3b?5r!2w?5r?6e!)t?6e?>`!2w?>`?>r!)t?>r?@U!2w?@U?@W!)t?@W?A`!2w?A`?BY!)t?BY?Bf!2w?Bf?EO!)t?EO?ET!2w?ET?EU!)t?EU?HR!2w?HR?Hw!)t?Hw?Ic!2w?Ic?Ii!)t?Ii?JT!2w?JT?J`!)t?J`?L]!2w?L]?L`!)t?L`?Lf!2w?Lf?Lh!)t?Lh?Ln!2w?Ln?Lp!)t?Lp?Lv!2w?Lv?Lx!)t?Lx?L{!2w?L{O!)t_7E`2g!OW|SyRmPOq!,^qr';irs$@]st';itu';iuv';ivw';iwx';ixz!,^z{';i{|';i|!O!,^!O!P';i!P!Q';i!Q![';i![!a!,^!a!b';i!b!c!,^!c!}';i!}#O!,^#O#P';i#P#R!,^#R#S';i#S#T(BZ#T#o';i#o$p!,^$p$q';i$q${!,^${$|';i$|%Q!,^%Q%R';i%R%W!,^%W%o';i%o%p!,^%p&a';i&a&b!,^&b0`';i0`0d!,^0d0p';i0p1O!,^1O1T';i1T1[!,^1[1]';i1]1^!,^1^1_';i1_4U!,^4U4Z';i4Z4[!,^4[4]';i4]4^';i4^4`!,^4`4d';i4d4l!,^4l4m';i4m4n!,^4n4q';i4q4r!,^4r4s';i4s4t!,^4t5Y';i5Y5Z!,^5Z7Q';i7Q7R!,^7R:S';i:S:[!,^:[=p';i=p=y!,^=y>q';i>q>s!,^>s>t';i>t>{!,^>{?t';i?tA`!,^A`A{';iA{BQ!,^BQBT';iBTCS!,^CSDP';iDPDt!,^DtDu';iDuDv';iDvDw!,^DwGO';iGOGP!,^GPGQ';iGQGa!,^GaGb';iGbGc';iGcGj!,^GjGk';iGkGl';iGlGv!,^GvGy';iGyG{!,^G{G|';iG|H^!,^H^H_';iH_H`!,^H`IO';iIOIm!,^ImKj';iKjKu!,^KuKv';iKvL`!,^L`MR';iMRM[!,^M[M]';iM]M^';iM^Mb!,^MbMc';iMcMh!,^MhNO';iNONS!,^NSNT';iNTN^!,^N^N_';iN_Nb!,^NbNc';iNcNz!,^Nz! e';i! e!#O!,^!#O!#P';i!#P!#Q!,^!#Q!#]';i!#]!%W!,^!%W!&`';i!&`!&c!,^!&c!&d';i!&d!&v!,^!&v!&w';i!&w!'O!,^!'O!'Y';i!'Y!'i!,^!'i!'p';i!'p!'q!,^!'q!'x';i!'x!'}!,^!'}!(V';i!(V!(X!,^!(X!(Y';i!(Y!(Z';i!(Z!(]!,^!(]!(s';i!(s!(t!,^!(t!({';i!({!(|!,^!(|!(}';i!(}!)Q!,^!)Q!)U';i!)U!)X!,^!)X!)Y';i!)Y!)j!,^!)j!)k';i!)k!)x!,^!)x!)y';i!)y!)z';i!)z!){!,^!){!*O';i!*O!*^!,^!*^!*_';i!*_!*`';i!*`!*s!,^!*s!*y';i!*y!*}!,^!*}!+O';i!+O!+P';i!+P!+R!,^!+R!+i';i!+i!+j!,^!+j!+q';i!+q!+r!,^!+r!+s';i!+s!+t';i!+t!+u!,^!+u!+v';i!+v!+w';i!+w!+x!,^!+x!+y';i!+y!+z';i!+z!,k!,^!,k!,o';i!,o!,p!,^!,p!,q';i!,q!-U!,^!-U!-X';i!-X!-i!,^!-i!-r';i!-r!-s!,^!-s!-v';i!-v!-w!,^!-w!._';i!._!.`!,^!.`!.g';i!.g!.h!,^!.h!.i';i!.i!.j';i!.j!.k!,^!.k!.p';i!.p!.s!,^!.s!.t';i!.t!/W!,^!/W!/X';i!/X!/h!,^!/h!/i';i!/i!/j';i!/j!0_!,^!0_!0g';i!0g!0i!,^!0i!0j';i!0j!0k';i!0k!0m!,^!0m!1T';i!1T!1U!,^!1U!1]';i!1]!1^!,^!1^!1_';i!1_!1`';i!1`!1a!,^!1a!1f';i!1f!1i!,^!1i!1j';i!1j!2Y!,^!2Y!2Z';i!2Z!2[';i!2[!2]!,^!2]!2`';i!2`!2o!,^!2o!2p';i!2p!3R!,^!3R!3S';i!3S!3T!,^!3T!3Z';i!3Z!3^!,^!3^!3a';i!3a!3b!,^!3b!3f';i!3f!3i!,^!3i!3j';i!3j!3k';i!3k!3l!,^!3l!3m';i!3m!3n!,^!3n!3o';i!3o!3p';i!3p!3s!,^!3s!3t';i!3t!3u';i!3u!3x!,^!3x!3{';i!3{!4O!,^!4O!4[';i!4[!4r!,^!4r!4s';i!4s!5y!,^!5y!6R';i!6R!6S!,^!6S!6V';i!6V!6W!,^!6W!6o';i!6o!6p!,^!6p!6z';i!6z!6{!,^!6{!7Q';i!7Q!7T!,^!7T!7U';i!7U!7p!,^!7p!7q';i!7q!7r';i!7r!7x!,^!7x!7y';i!7y!7z';i!7z!8o!,^!8o!8w';i!8w!8x!,^!8x!8{';i!8{!8|!,^!8|!9e';i!9e!9f!,^!9f!9p';i!9p!9q!,^!9q!9v';i!9v!9y!,^!9y!9z';i!9z!:l!,^!:l!:m';i!:m!:n!,^!:n!:o';i!:o!:p';i!:p!;P!,^!;P!;Q';i!;Q!;R';i!;R!;e!,^!;e!;m';i!;m!;n!,^!;n!;q';i!;q!;r!,^!;r!O!,^!>O!>U';i!>U!>Z!,^!>Z!>m';i!>m!>p!,^!>p!?Y';i!?Y!?Z!,^!?Z!?d';i!?d!?e!,^!?e!?f';i!?f!?h!,^!?h!?o';i!?o!@{!,^!@{!A}';i!A}!BO!,^!BO!BP';i!BP!BQ';i!BQ!B^!,^!B^!Be';i!Be!Cq!,^!Cq!Cr';i!Cr!Cs';i!Cs!Ct!,^!Ct!Cu';i!Cu!Cw!,^!Cw!Cx';i!Cx!Cy';i!Cy!Cz!,^!Cz!C{';i!C{!C}!,^!C}!DO';i!DO!DU!,^!DU!DY';i!DY!DZ!,^!DZ!Db';i!Db!Dc!,^!Dc!Df';i!Df!Dg!,^!Dg!Dh';i!Dh!Di!,^!Di!Dj';i!Dj!Dl!,^!Dl!Dm';i!Dm!Dn';i!Dn!Do!,^!Do!Ds';i!Ds!Dt!,^!Dt!Du';i!Du!Dv';i!Dv!EP!,^!EP!EQ';i!EQ!ES!,^!ES!EX';i!EX!EY!,^!EY!EZ';i!EZ!Ep!,^!Ep!Et';i!Et!Ff!,^!Ff!Fg';i!Fg!Gx!,^!Gx!HQ';i!HQ!HR!,^!HR!Hw';i!Hw!Id!,^!Id!Ii';i!Ii!LQ!,^!LQ!L}';i!L}!Mc!,^!Mc!Md';i!Md!Mt!,^!Mt!Mz';i!Mz!NO!,^!NO!NS';i!NS!NV!,^!NV!NW';i!NW!NZ!,^!NZ!N[';i!N[!N]';i!N]!Nd!,^!Nd!Ng';i!Ng!Nk!,^!Nk!Nx';i!Nx# U!,^# U# V';i# V# h!,^# h#!`';i#!`#!a!,^#!a#!b';i#!b#!g!,^#!g#!h';i#!h#!j!,^#!j##g';i##g##h!,^##h#*s';i#*s#*t!,^#*t#*x';i#*x#*z!,^#*z#+R';i#+R#+S!,^#+S#+T';i#+T#+U!,^#+U#+Y';i#+Y#+[!,^#+[#,V';i#,V#,W!,^#,W#,[';i#,[#,^!,^#,^#-P';i#-P#-Q!,^#-Q#-U';i#-U#-W!,^#-W#-_';i#-_#-`!,^#-`#-a';i#-a#-b!,^#-b#-f';i#-f#-h!,^#-h#-w';i#-w#-x!,^#-x#/T';i#/T#/U!,^#/U#/Y';i#/Y#/[!,^#/[#0q';i#0q#1h!,^#1h#1x';i#1x#2Y!,^#2Y#4R';i#4R#4_!,^#4_#Au';i#Au#Aw!,^#Aw#BY';i#BY#BZ!,^#BZ#Bu';i#Bu#Bz!,^#Bz#Di';i#Di#EO!,^#EO#E]';i#E]#E^!,^#E^#Eb';i#Eb#Ep!,^#Ep#FS';i#FS#Fb!,^#Fb#Ft';i#Ft#GS!,^#GS#Ga';i#Ga#Gb!,^#Gb#Ge';i#Ge#Gt!,^#Gt#Hz';i#Hz#Io!,^#Io#Ip';i#Ip#It!,^#It#Iu';i#Iu#K[!,^#K[#MW';i#MW#M`!,^#M`#NZ';i#NZ#N[!,^#N[#N]';i#N]#Nb!,^#Nb$ z';i$ z$!U!,^$!U$!s';i$!s$#x!,^$#x$$h';i$$h$$j!,^$$j$$o';i$$o$$z!,^$$z$%x';i$%x$&_!,^$&_$&f';i$&f$'p!,^$'p$(X';i$(X$(b!,^$(b$)i';i$)i$+_!,^$+_$+`';i$+`$-a!,^$-a$.b';i$.b$.s!,^$.s$.z';i$.z$0T!,^$0T$0s';i$0s$1Q!,^$1Q$1R';i$1R$1S';i$1S$1^!,^$1^$2[';i$2[$2v!,^$2v$3l';i$3l$4g!,^$4g$4j';i$4j$4t!,^$4t$5j';i$5j$7y!,^$7y$7}';i$7}$8O!,^$8O$8S';i$8S$8V!,^$8V$8W';i$8W$8X';i$8X$8b!,^$8b$z';i5>z5>{!,^5>{5>|';i5>|5?P!,^5?P5?Q';i5?Q5?R';i5?R5?T!,^5?T5?Y';i5?Y5?[!,^5?[5?]';i5?]5?^!,^5?^5?_';i5?_5?w!,^5?w5?z';i5?z5?|!,^5?|5@X';i5@X5@`!,^5@`5@c';i5@c5@o!,^5@o5@u';i5@u5@w!,^5@w5@}';i5@}5AP!,^5AP5AV';i5AV5A`!,^5A`5Ag';i5Ag5Ah!,^5Ah5Ao';i5Ao5Dv!,^5Dv5Ek';i5Ek5FY!,^5FY;%S';i;%S;%`!,^;%`;%w';i;%w;%{!,^;%{;'O';i;'O;'S!,^;'S;=`!-c<%l?&r!,^?&r?.p';i?.p?.r!,^?.r?1Q';i?1Q?1x!,^?1x?2P';i?2P?2]!,^?2]?2b';i?2b?2g!,^?2g?2h';i?2h?2i!,^?2i?2s';i?2s?2t!,^?2t?3R';i?3R?3S!,^?3S?3X';i?3X?3Y!,^?3Y?3Z';i?3Z?3[!,^?3[?3]';i?3]?3^';i?3^?3_!,^?3_?3`';i?3`?3a';i?3a?3b!,^?3b?5r';i?5r?6e!,^?6e?>`';i?>`?>r!,^?>r?@U';i?@U?@W!,^?@W?A`';i?A`?BY!,^?BY?Bf';i?Bf?EO!,^?EO?ET';i?ET?EU!,^?EU?HR';i?HR?Hw!,^?Hw?Ic';i?Ic?Ii!,^?Ii?JT';i?JT?J`!,^?J`?L]';i?L]?L`!,^?L`?Lf';i?Lf?Lh!,^?Lh?Ln';i?Ln?Lp!,^?Lp?Lv';i?Lv?Lx!,^?Lx?L{';i?L{O!,^_8LS]wP!OW|SzQOr!)trs!*jsw!)twx!,^xy8L{yz8Nnz!}!)t!}#O9 f#O#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_8MWY!ZP!OW|SzQOr!)trs!*jsw!)twx!,^xy8Mvy#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_8NRX!bP!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_8NyX!vP!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_9 qX!TP!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t!a9!i]!PP!OW|SzQOr!)trs!*jsw!)twx!,^xy!)tyz9#bz#P!)t#P#Q9%W#Q#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t!`9#mZ![`!OW|SzQOr!)trs!*jsw!)twx!,^xy!)tyz9$`z#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t!O9$kX!cp!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)tn9%cX!Y`!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_9&Z[!OW|SzQ!fPOr!)trs!*jsw!)twx!,^x}!)t}!O9'P!O!P9+}!P#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_9'^a!OW|SzQ!fP]POr!)trs!*jsw!)twx!,^x}!)t}!O9(c!O!`!)t!`!a9+V!a#S!)t#S#T!-i#T#c!)t#c#d9+V#d#l!)t#l#m9+V#m;'S!)t;'S;=`!.]<%lO!)t_9(pa!OW|SzQ!fP!iPOr!)trs!*jsw!)twx!,^x}!)t}!O9)u!O!`!)t!`!a9+V!a#S!)t#S#T!-i#T#c!)t#c#d9+V#d#l!)t#l#m9+V#m;'S!)t;'S;=`!.]<%lO!)t_9*Qa!OW|SzQ!fPOr!)trs!*jsw!)twx!,^x}!)t}!O9)u!O!`!)t!`!a9+V!a#S!)t#S#T!-i#T#c!)t#c#d9+V#d#l!)t#l#m9+V#m;'S!)t;'S;=`!.]<%lO!)t_9+bX!OW|SzQ!fPOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_9,Y[!OW|SzQ!hPOr!)trs!*jsw!)twx!,^x}!)t}!O9-O!O!P9+}!P#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_9-Z_!OW|SzQ!hPOr!)trs!*jsw!)twx!,^x!`!)t!`!a9.Y!a#S!)t#S#T!-i#T#c!)t#c#d9.Y#d#l!)t#l#m9.Y#m;'S!)t;'S;=`!.]<%lO!)t_9.eX!OW|SzQ!hPOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_9/_2h!OW|SzQ!hPmPOq!)tqr!2wrs#9kst!2wtu!2wuv!2wvw!2wwx';ixz!)tz{!2w{|!2w|}!)t}!O9-O!O!P9/Q!P!Q!2w!Q![!2w![!a!)t!a!b!2w!b!c!)t!c!}!2w!}#O!)t#O#P!2w#P#R!)t#R#S!2w#S#T)Hy#T#o!2w#o$p!)t$p$q!2w$q${!)t${$|!2w$|%Q!)t%Q%R!2w%R%W!)t%W%o!2w%o%p!)t%p&a!2w&a&b!)t&b0`!2w0`0d!)t0d0p!2w0p1O!)t1O1T!2w1T1[!)t1[1]!2w1]1^!)t1^1_!2w1_4U!)t4U4Z!2w4Z4[!)t4[4]!2w4]4^!2w4^4`!)t4`4d!2w4d4l!)t4l4m!2w4m4n!)t4n4q!2w4q4r!)t4r4s!2w4s4t!)t4t5Y!2w5Y5Z!)t5Z7Q!2w7Q7R!)t7R:S!2w:S:[!)t:[=p!2w=p=y!)t=y>q!2w>q>s!)t>s>t!2w>t>{!)t>{?t!2w?tA`!)tA`A{!2wA{BQ!)tBQBT!2wBTCS!)tCSDP!2wDPDt!)tDtDu!2wDuDv!2wDvDw!)tDwGO!2wGOGP!)tGPGQ!2wGQGa!)tGaGb!2wGbGc!2wGcGj!)tGjGk!2wGkGl!2wGlGv!)tGvGy!2wGyG{!)tG{G|!2wG|H^!)tH^H_!2wH_H`!)tH`IO!2wIOIm!)tImKj!2wKjKu!)tKuKv!2wKvL`!)tL`MR!2wMRM[!)tM[M]!2wM]M^!2wM^Mb!)tMbMc!2wMcMh!)tMhNO!2wNONS!)tNSNT!2wNTN^!)tN^N_!2wN_Nb!)tNbNc!2wNcNz!)tNz! e!2w! e!#O!)t!#O!#P!2w!#P!#Q!)t!#Q!#]!2w!#]!%W!)t!%W!&`!2w!&`!&c!)t!&c!&d!2w!&d!&v!)t!&v!&w!2w!&w!'O!)t!'O!'Y!2w!'Y!'i!)t!'i!'p!2w!'p!'q!)t!'q!'x!2w!'x!'}!)t!'}!(V!2w!(V!(X!)t!(X!(Y!2w!(Y!(Z!2w!(Z!(]!)t!(]!(s!2w!(s!(t!)t!(t!({!2w!({!(|!)t!(|!(}!2w!(}!)Q!)t!)Q!)U!2w!)U!)X!)t!)X!)Y!2w!)Y!)j!)t!)j!)k!2w!)k!)x!)t!)x!)y!2w!)y!)z!2w!)z!){!)t!){!*O!2w!*O!*^!)t!*^!*_!2w!*_!*`!2w!*`!*s!)t!*s!*y!2w!*y!*}!)t!*}!+O!2w!+O!+P!2w!+P!+R!)t!+R!+i!2w!+i!+j!)t!+j!+q!2w!+q!+r!)t!+r!+s!2w!+s!+t!2w!+t!+u!)t!+u!+v!2w!+v!+w!2w!+w!+x!)t!+x!+y!2w!+y!+z!2w!+z!,k!)t!,k!,o!2w!,o!,p!)t!,p!,q!2w!,q!-U!)t!-U!-X!2w!-X!-i!)t!-i!-r!2w!-r!-s!)t!-s!-v!2w!-v!-w!)t!-w!._!2w!._!.`!)t!.`!.g!2w!.g!.h!)t!.h!.i!2w!.i!.j!2w!.j!.k!)t!.k!.p!2w!.p!.s!)t!.s!.t!2w!.t!/W!)t!/W!/X!2w!/X!/h!)t!/h!/i!2w!/i!/j!2w!/j!0_!)t!0_!0g!2w!0g!0i!)t!0i!0j!2w!0j!0k!2w!0k!0m!)t!0m!1T!2w!1T!1U!)t!1U!1]!2w!1]!1^!)t!1^!1_!2w!1_!1`!2w!1`!1a!)t!1a!1f!2w!1f!1i!)t!1i!1j!2w!1j!2Y!)t!2Y!2Z!2w!2Z!2[!2w!2[!2]!)t!2]!2`!2w!2`!2o!)t!2o!2p!2w!2p!3R!)t!3R!3S!2w!3S!3T!)t!3T!3Z!2w!3Z!3^!)t!3^!3a!2w!3a!3b!)t!3b!3f!2w!3f!3i!)t!3i!3j!2w!3j!3k!2w!3k!3l!)t!3l!3m!2w!3m!3n!)t!3n!3o!2w!3o!3p!2w!3p!3s!)t!3s!3t!2w!3t!3u!2w!3u!3x!)t!3x!3{!2w!3{!4O!)t!4O!4[!2w!4[!4r!)t!4r!4s!2w!4s!5y!)t!5y!6R!2w!6R!6S!)t!6S!6V!2w!6V!6W!)t!6W!6o!2w!6o!6p!)t!6p!6z!2w!6z!6{!)t!6{!7Q!2w!7Q!7T!)t!7T!7U!2w!7U!7p!)t!7p!7q!2w!7q!7r!2w!7r!7x!)t!7x!7y!2w!7y!7z!2w!7z!8o!)t!8o!8w!2w!8w!8x!)t!8x!8{!2w!8{!8|!)t!8|!9e!2w!9e!9f!)t!9f!9p!2w!9p!9q!)t!9q!9v!2w!9v!9y!)t!9y!9z!2w!9z!:l!)t!:l!:m!2w!:m!:n!)t!:n!:o!2w!:o!:p!2w!:p!;P!)t!;P!;Q!2w!;Q!;R!2w!;R!;e!)t!;e!;m!2w!;m!;n!)t!;n!;q!2w!;q!;r!)t!;r!O!)t!>O!>U!2w!>U!>Z!)t!>Z!>m!2w!>m!>p!)t!>p!?Y!2w!?Y!?Z!)t!?Z!?d!2w!?d!?e!)t!?e!?f!2w!?f!?h!)t!?h!?o!2w!?o!@{!)t!@{!A}!2w!A}!BO!)t!BO!BP!2w!BP!BQ!2w!BQ!B^!)t!B^!Be!2w!Be!Cq!)t!Cq!Cr!2w!Cr!Cs!2w!Cs!Ct!)t!Ct!Cu!2w!Cu!Cw!)t!Cw!Cx!2w!Cx!Cy!2w!Cy!Cz!)t!Cz!C{!2w!C{!C}!)t!C}!DO!2w!DO!DU!)t!DU!DY!2w!DY!DZ!)t!DZ!Db!2w!Db!Dc!)t!Dc!Df!2w!Df!Dg!)t!Dg!Dh!2w!Dh!Di!)t!Di!Dj!2w!Dj!Dl!)t!Dl!Dm!2w!Dm!Dn!2w!Dn!Do!)t!Do!Ds!2w!Ds!Dt!)t!Dt!Du!2w!Du!Dv!2w!Dv!EP!)t!EP!EQ!2w!EQ!ES!)t!ES!EX!2w!EX!EY!)t!EY!EZ!2w!EZ!Ep!)t!Ep!Et!2w!Et!Ff!)t!Ff!Fg!2w!Fg!Gx!)t!Gx!HQ!2w!HQ!HR!)t!HR!Hw!2w!Hw!Id!)t!Id!Ii!2w!Ii!LQ!)t!LQ!L}!2w!L}!Mc!)t!Mc!Md!2w!Md!Mt!)t!Mt!Mz!2w!Mz!NO!)t!NO!NS!2w!NS!NV!)t!NV!NW!2w!NW!NZ!)t!NZ!N[!2w!N[!N]!2w!N]!Nd!)t!Nd!Ng!2w!Ng!Nk!)t!Nk!Nx!2w!Nx# U!)t# U# V!2w# V# h!)t# h#!`!2w#!`#!a!)t#!a#!b!2w#!b#!g!)t#!g#!h!2w#!h#!j!)t#!j##g!2w##g##h!)t##h#*s!2w#*s#*t!)t#*t#*x!2w#*x#*z!)t#*z#+R!2w#+R#+S!)t#+S#+T!2w#+T#+U!)t#+U#+Y!2w#+Y#+[!)t#+[#,V!2w#,V#,W!)t#,W#,[!2w#,[#,^!)t#,^#-P!2w#-P#-Q!)t#-Q#-U!2w#-U#-W!)t#-W#-_!2w#-_#-`!)t#-`#-a!2w#-a#-b!)t#-b#-f!2w#-f#-h!)t#-h#-w!2w#-w#-x!)t#-x#/T!2w#/T#/U!)t#/U#/Y!2w#/Y#/[!)t#/[#0q!2w#0q#1h!)t#1h#1x!2w#1x#2Y!)t#2Y#4R!2w#4R#4_!)t#4_#Au!2w#Au#Aw!)t#Aw#BY!2w#BY#BZ!)t#BZ#Bu!2w#Bu#Bz!)t#Bz#Di!2w#Di#EO!)t#EO#E]!2w#E]#E^!)t#E^#Eb!2w#Eb#Ep!)t#Ep#FS!2w#FS#Fb!)t#Fb#Ft!2w#Ft#GS!)t#GS#Ga!2w#Ga#Gb!)t#Gb#Ge!2w#Ge#Gt!)t#Gt#Hz!2w#Hz#Io!)t#Io#Ip!2w#Ip#It!)t#It#Iu!2w#Iu#K[!)t#K[#MW!2w#MW#M`!)t#M`#NZ!2w#NZ#N[!)t#N[#N]!2w#N]#Nb!)t#Nb$ z!2w$ z$!U!)t$!U$!s!2w$!s$#x!)t$#x$$h!2w$$h$$j!)t$$j$$o!2w$$o$$z!)t$$z$%x!2w$%x$&_!)t$&_$&f!2w$&f$'p!)t$'p$(X!2w$(X$(b!)t$(b$)i!2w$)i$+_!)t$+_$+`!2w$+`$-a!)t$-a$.b!2w$.b$.s!)t$.s$.z!2w$.z$0T!)t$0T$0s!2w$0s$1Q!)t$1Q$1R!2w$1R$1S!2w$1S$1^!)t$1^$2[!2w$2[$2v!)t$2v$3l!2w$3l$4g!)t$4g$4j!2w$4j$4t!)t$4t$5j!2w$5j$7y!)t$7y$7}!2w$7}$8O!)t$8O$8S!2w$8S$8V!)t$8V$8W!2w$8W$8X!2w$8X$8b!)t$8b$z!2w5>z5>{!)t5>{5>|!2w5>|5?P!)t5?P5?Q!2w5?Q5?R!2w5?R5?T!)t5?T5?Y!2w5?Y5?[!)t5?[5?]!2w5?]5?^!)t5?^5?_!2w5?_5?w!)t5?w5?z!2w5?z5?|!)t5?|5@X!2w5@X5@`!)t5@`5@c!2w5@c5@o!)t5@o5@u!2w5@u5@w!)t5@w5@}!2w5@}5AP!)t5AP5AV!2w5AV5A`!)t5A`5Ag!2w5Ag5Ah!)t5Ah5Ao!2w5Ao5Dv!)t5Dv5Ek!2w5Ek5FY!)t5FY;%S!2w;%S;%`!)t;%`;%w!2w;%w;%{!)t;%{;'O!2w;'O;'S!)t;'S;=`!.]<%l?&r!)t?&r?.p!2w?.p?.r!)t?.r?1Q!2w?1Q?1x!)t?1x?2P!2w?2P?2]!)t?2]?2b!2w?2b?2g!)t?2g?2h!2w?2h?2i!)t?2i?2s!2w?2s?2t!)t?2t?3R!2w?3R?3S!)t?3S?3X!2w?3X?3Y!)t?3Y?3Z!2w?3Z?3[!)t?3[?3]!2w?3]?3^!2w?3^?3_!)t?3_?3`!2w?3`?3a!2w?3a?3b!)t?3b?5r!2w?5r?6e!)t?6e?>`!2w?>`?>r!)t?>r?@U!2w?@U?@W!)t?@W?A`!2w?A`?BY!)t?BY?Bf!2w?Bf?EO!)t?EO?ET!2w?ET?EU!)t?EU?HR!2w?HR?Hw!)t?Hw?Ic!2w?Ic?Ii!)t?Ii?JT!2w?JT?J`!)t?J`?L]!2w?L]?L`!)t?L`?Lf!2w?Lf?Lh!)t?Lh?Ln!2w?Ln?Lp!)t?Lp?Lv!2w?Lv?Lx!)t?Lx?L{!2w?L{O!)t_:6UZ!OW|SzQ`POr!)trs!*jsw!)twx!,^x![!)t![!]:6w!]#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_:7QZ!OW|SzQOr!)trs!*jsw!)twx!,^x![!)t![!]:7s!]#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_:8OX!OW|SzQaPOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_:8vX!OW|SzQ!{POr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_:9l]!OW|SzQOr!)trs!*jsw!)twx!,^x}!)t}!O::e!O!_!)t!_!`:=l!`#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_::n[!OW|SzQOr!)trs!*jsw!)twx!,^x}!)t}!O:;d!O!P9+}!P#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_:;oa!OW|SzQ!fPOr!)trs!*jsw!)twx!,^x}!)t}!O9)u!O!`!)t!`!a:h!`#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_:>s`!OW|SzQ!gPOr!)trs!*jsw!)twx!,^x!_!)t!_!`:?u!`!a:Az!a#S!)t#S#T!-i#T#c!)t#c#d:Az#d#l!)t#l#m:Az#m;'S!)t;'S;=`!.]<%lO!)t_:@Q`!OW|SzQ!gPOr!)trs!*jsw!)twx!,^x!_!)t!_!`:?u!`!a:AS!a#S!)t#S#T!-i#T#c!)t#c#d:AS#d#l!)t#l#m:AS#m;'S!)t;'S;=`!.]<%lO!)t_:A_X!OW|SzQ!gPOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_:BVX!OW|SzQ!ePOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_:B}Z!OW|SzQ!gPOr!)trs!*jsw!)twx!,^x!_!)t!_!`:Cp!`#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_:C}`!OW|SzQ!gP^POr!)trs!*jsw!)twx!,^x!_!)t!_!`:EP!`!a:AS!a#S!)t#S#T!-i#T#c!)t#c#d:AS#d#l!)t#l#m:AS#m;'S!)t;'S;=`!.]<%lO!)t_:E^`!OW|SzQ!gP!iPOr!)trs!*jsw!)twx!,^x!_!)t!_!`:?u!`!a:AS!a#S!)t#S#T!-i#T#c!)t#c#d:AS#d#l!)t#l#m:AS#m;'S!)t;'S;=`!.]<%lO!)t_:FkX!]P!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_:Gc[!QP!OW|SzQOr!)trs!*jsw!)twx!,^xy:HXy!}!)t!}#O:IP#O#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_:HdX!VP!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_:I[X!WP!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)to:JS]!RP!OW|SzQOr!)trs!*jsw!)twx!,^xy!)tyz:J{z#P!)t#P#Q:Ks#Q#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)tn:KWX!U`!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)tn:LOX!X`!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_:Lv2g|SzQ}XmPOq!-iqr)Hyrs&4yst)Hytu)Hyuv)Hyvw)Hywx(BZxz!-iz{)Hy{|)Hy|!O!-i!O!P)Hy!P!Q)Hy!Q![)Hy![!a!-i!a!b)Hy!b!c!-i!c!})Hy!}#O!-i#O#P)Hy#P#R!-i#R#S)Hy#S#T)Hy#T#o)Hy#o$p!-i$p$q)Hy$q${!-i${$|)Hy$|%Q!-i%Q%R)Hy%R%W!-i%W%o)Hy%o%p!-i%p&a)Hy&a&b!-i&b0`)Hy0`0d!-i0d0p)Hy0p1O!-i1O1T)Hy1T1[!-i1[1])Hy1]1^!-i1^1_)Hy1_4U!-i4U4Z)Hy4Z4[!-i4[4])Hy4]4^)Hy4^4`!-i4`4d)Hy4d4l!-i4l4m)Hy4m4n!-i4n4q)Hy4q4r!-i4r4s)Hy4s4t!-i4t5Y)Hy5Y5Z!-i5Z7Q)Hy7Q7R!-i7R:S)Hy:S:[!-i:[=p)Hy=p=y!-i=y>q)Hy>q>s!-i>s>t)Hy>t>{!-i>{?t)Hy?tA`!-iA`A{)HyA{BQ!-iBQBT)HyBTCS!-iCSDP)HyDPDt!-iDtDu)HyDuDv)HyDvDw!-iDwGO)HyGOGP!-iGPGQ)HyGQGa!-iGaGb)HyGbGc)HyGcGj!-iGjGk)HyGkGl)HyGlGv!-iGvGy)HyGyG{!-iG{G|)HyG|H^!-iH^H_)HyH_H`!-iH`IO)HyIOIm!-iImKj)HyKjKu!-iKuKv)HyKvL`!-iL`MR)HyMRM[!-iM[M])HyM]M^)HyM^Mb!-iMbMc)HyMcMh!-iMhNO)HyNONS!-iNSNT)HyNTN^!-iN^N_)HyN_Nb!-iNbNc)HyNcNz!-iNz! e)Hy! e!#O!-i!#O!#P)Hy!#P!#Q!-i!#Q!#])Hy!#]!%W!-i!%W!&`)Hy!&`!&c!-i!&c!&d)Hy!&d!&v!-i!&v!&w)Hy!&w!'O!-i!'O!'Y)Hy!'Y!'i!-i!'i!'p)Hy!'p!'q!-i!'q!'x)Hy!'x!'}!-i!'}!(V)Hy!(V!(X!-i!(X!(Y)Hy!(Y!(Z)Hy!(Z!(]!-i!(]!(s)Hy!(s!(t!-i!(t!({)Hy!({!(|!-i!(|!(})Hy!(}!)Q!-i!)Q!)U)Hy!)U!)X!-i!)X!)Y)Hy!)Y!)j!-i!)j!)k)Hy!)k!)x!-i!)x!)y)Hy!)y!)z)Hy!)z!){!-i!){!*O)Hy!*O!*^!-i!*^!*_)Hy!*_!*`)Hy!*`!*s!-i!*s!*y)Hy!*y!*}!-i!*}!+O)Hy!+O!+P)Hy!+P!+R!-i!+R!+i)Hy!+i!+j!-i!+j!+q)Hy!+q!+r!-i!+r!+s)Hy!+s!+t)Hy!+t!+u!-i!+u!+v)Hy!+v!+w)Hy!+w!+x!-i!+x!+y)Hy!+y!+z)Hy!+z!,k!-i!,k!,o)Hy!,o!,p!-i!,p!,q)Hy!,q!-U!-i!-U!-X)Hy!-X!-i!-i!-i!-r)Hy!-r!-s!-i!-s!-v)Hy!-v!-w!-i!-w!._)Hy!._!.`!-i!.`!.g)Hy!.g!.h!-i!.h!.i)Hy!.i!.j)Hy!.j!.k!-i!.k!.p)Hy!.p!.s!-i!.s!.t)Hy!.t!/W!-i!/W!/X)Hy!/X!/h!-i!/h!/i)Hy!/i!/j)Hy!/j!0_!-i!0_!0g)Hy!0g!0i!-i!0i!0j)Hy!0j!0k)Hy!0k!0m!-i!0m!1T)Hy!1T!1U!-i!1U!1])Hy!1]!1^!-i!1^!1_)Hy!1_!1`)Hy!1`!1a!-i!1a!1f)Hy!1f!1i!-i!1i!1j)Hy!1j!2Y!-i!2Y!2Z)Hy!2Z!2[)Hy!2[!2]!-i!2]!2`)Hy!2`!2o!-i!2o!2p)Hy!2p!3R!-i!3R!3S)Hy!3S!3T!-i!3T!3Z)Hy!3Z!3^!-i!3^!3a)Hy!3a!3b!-i!3b!3f)Hy!3f!3i!-i!3i!3j)Hy!3j!3k)Hy!3k!3l!-i!3l!3m)Hy!3m!3n!-i!3n!3o)Hy!3o!3p)Hy!3p!3s!-i!3s!3t)Hy!3t!3u)Hy!3u!3x!-i!3x!3{)Hy!3{!4O!-i!4O!4[)Hy!4[!4r!-i!4r!4s)Hy!4s!5y!-i!5y!6R)Hy!6R!6S!-i!6S!6V)Hy!6V!6W!-i!6W!6o)Hy!6o!6p!-i!6p!6z)Hy!6z!6{!-i!6{!7Q)Hy!7Q!7T!-i!7T!7U)Hy!7U!7p!-i!7p!7q)Hy!7q!7r)Hy!7r!7x!-i!7x!7y)Hy!7y!7z)Hy!7z!8o!-i!8o!8w)Hy!8w!8x!-i!8x!8{)Hy!8{!8|!-i!8|!9e)Hy!9e!9f!-i!9f!9p)Hy!9p!9q!-i!9q!9v)Hy!9v!9y!-i!9y!9z)Hy!9z!:l!-i!:l!:m)Hy!:m!:n!-i!:n!:o)Hy!:o!:p)Hy!:p!;P!-i!;P!;Q)Hy!;Q!;R)Hy!;R!;e!-i!;e!;m)Hy!;m!;n!-i!;n!;q)Hy!;q!;r!-i!;r!O!-i!>O!>U)Hy!>U!>Z!-i!>Z!>m)Hy!>m!>p!-i!>p!?Y)Hy!?Y!?Z!-i!?Z!?d)Hy!?d!?e!-i!?e!?f)Hy!?f!?h!-i!?h!?o)Hy!?o!@{!-i!@{!A})Hy!A}!BO!-i!BO!BP)Hy!BP!BQ)Hy!BQ!B^!-i!B^!Be)Hy!Be!Cq!-i!Cq!Cr)Hy!Cr!Cs)Hy!Cs!Ct!-i!Ct!Cu)Hy!Cu!Cw!-i!Cw!Cx)Hy!Cx!Cy)Hy!Cy!Cz!-i!Cz!C{)Hy!C{!C}!-i!C}!DO)Hy!DO!DU!-i!DU!DY)Hy!DY!DZ!-i!DZ!Db)Hy!Db!Dc!-i!Dc!Df)Hy!Df!Dg!-i!Dg!Dh)Hy!Dh!Di!-i!Di!Dj)Hy!Dj!Dl!-i!Dl!Dm)Hy!Dm!Dn)Hy!Dn!Do!-i!Do!Ds)Hy!Ds!Dt!-i!Dt!Du)Hy!Du!Dv)Hy!Dv!EP!-i!EP!EQ)Hy!EQ!ES!-i!ES!EX)Hy!EX!EY!-i!EY!EZ)Hy!EZ!Ep!-i!Ep!Et)Hy!Et!Ff!-i!Ff!Fg)Hy!Fg!Gx!-i!Gx!HQ)Hy!HQ!HR!-i!HR!Hw)Hy!Hw!Id!-i!Id!Ii)Hy!Ii!LQ!-i!LQ!L})Hy!L}!Mc!-i!Mc!Md)Hy!Md!Mt!-i!Mt!Mz)Hy!Mz!NO!-i!NO!NS)Hy!NS!NV!-i!NV!NW)Hy!NW!NZ!-i!NZ!N[)Hy!N[!N])Hy!N]!Nd!-i!Nd!Ng)Hy!Ng!Nk!-i!Nk!Nx)Hy!Nx# U!-i# U# V)Hy# V# h!-i# h#!`)Hy#!`#!a!-i#!a#!b)Hy#!b#!g!-i#!g#!h)Hy#!h#!j!-i#!j##g)Hy##g##h!-i##h#*s)Hy#*s#*t!-i#*t#*x)Hy#*x#*z!-i#*z#+R)Hy#+R#+S!-i#+S#+T)Hy#+T#+U!-i#+U#+Y)Hy#+Y#+[!-i#+[#,V)Hy#,V#,W!-i#,W#,[)Hy#,[#,^!-i#,^#-P)Hy#-P#-Q!-i#-Q#-U)Hy#-U#-W!-i#-W#-_)Hy#-_#-`!-i#-`#-a)Hy#-a#-b!-i#-b#-f)Hy#-f#-h!-i#-h#-w)Hy#-w#-x!-i#-x#/T)Hy#/T#/U!-i#/U#/Y)Hy#/Y#/[!-i#/[#0q)Hy#0q#1h!-i#1h#1x)Hy#1x#2Y!-i#2Y#4R)Hy#4R#4_!-i#4_#Au)Hy#Au#Aw!-i#Aw#BY)Hy#BY#BZ!-i#BZ#Bu)Hy#Bu#Bz!-i#Bz#Di)Hy#Di#EO!-i#EO#E])Hy#E]#E^!-i#E^#Eb)Hy#Eb#Ep!-i#Ep#FS)Hy#FS#Fb!-i#Fb#Ft)Hy#Ft#GS!-i#GS#Ga)Hy#Ga#Gb!-i#Gb#Ge)Hy#Ge#Gt!-i#Gt#Hz)Hy#Hz#Io!-i#Io#Ip)Hy#Ip#It!-i#It#Iu)Hy#Iu#K[!-i#K[#MW)Hy#MW#M`!-i#M`#NZ)Hy#NZ#N[!-i#N[#N])Hy#N]#Nb!-i#Nb$ z)Hy$ z$!U!-i$!U$!s)Hy$!s$#x!-i$#x$$h)Hy$$h$$j!-i$$j$$o)Hy$$o$$z!-i$$z$%x)Hy$%x$&_!-i$&_$&f)Hy$&f$'p!-i$'p$(X)Hy$(X$(b!-i$(b$)i)Hy$)i$+_!-i$+_$+`)Hy$+`$-a!-i$-a$.b)Hy$.b$.s!-i$.s$.z)Hy$.z$0T!-i$0T$0s)Hy$0s$1Q!-i$1Q$1R)Hy$1R$1S)Hy$1S$1^!-i$1^$2[)Hy$2[$2v!-i$2v$3l)Hy$3l$4g!-i$4g$4j)Hy$4j$4t!-i$4t$5j)Hy$5j$7y!-i$7y$7})Hy$7}$8O!-i$8O$8S)Hy$8S$8V!-i$8V$8W)Hy$8W$8X)Hy$8X$8b!-i$8b$z)Hy5>z5>{!-i5>{5>|)Hy5>|5?P!-i5?P5?Q)Hy5?Q5?R)Hy5?R5?T!-i5?T5?Y)Hy5?Y5?[!-i5?[5?])Hy5?]5?^!-i5?^5?_)Hy5?_5?w!-i5?w5?z)Hy5?z5?|!-i5?|5@X)Hy5@X5@`!-i5@`5@c)Hy5@c5@o!-i5@o5@u)Hy5@u5@w!-i5@w5@})Hy5@}5AP!-i5AP5AV)Hy5AV5A`!-i5A`5Ag)Hy5Ag5Ah!-i5Ah5Ao)Hy5Ao5Dv!-i5Dv5Ek)Hy5Ek5FY!-i5FY;%S)Hy;%S;%`!-i;%`;%w)Hy;%w;%{!-i;%{;'O)Hy;'O;'S!-i;'S;=`!.V<%l?&r!-i?&r?.p)Hy?.p?.r!-i?.r?1Q)Hy?1Q?1x!-i?1x?2P)Hy?2P?2]!-i?2]?2b)Hy?2b?2g!-i?2g?2h)Hy?2h?2i!-i?2i?2s)Hy?2s?2t!-i?2t?3R)Hy?3R?3S!-i?3S?3X)Hy?3X?3Y!-i?3Y?3Z)Hy?3Z?3[!-i?3[?3])Hy?3]?3^)Hy?3^?3_!-i?3_?3`)Hy?3`?3a)Hy?3a?3b!-i?3b?5r)Hy?5r?6e!-i?6e?>`)Hy?>`?>r!-i?>r?@U)Hy?@U?@W!-i?@W?A`)Hy?A`?BY!-i?BY?Bf)Hy?Bf?EO!-i?EO?ET)Hy?ET?EU!-i?EU?HR)Hy?HR?Hw!-i?Hw?Ic)Hy?Ic?Ii!-i?Ii?JT)Hy?JT?J`!-i?J`?L])Hy?L]?L`!-i?L`?Lf)Hy?Lf?Lh!-i?Lh?Ln)Hy?Ln?Lp!-i?Lp?Lv)Hy?Lv?Lx!-i?Lx?L{)Hy?L{O!-i_<%j2j!OW|SzQmPOq!)tqr!2wrs#9kst!2wtu!2wuv!2wvw!2wwx';ixz!)tz{!2w{|!2w|}!)t}!O::e!O!P!2w!P!Q!2w!Q![!2w![!_!)t!_!`:=l!`!a!)t!a!b!2w!b!c!)t!c!}!2w!}#O!)t#O#P!2w#P#R!)t#R#S!2w#S#T)Hy#T#o!2w#o$p!)t$p$q!2w$q${!)t${$|!2w$|%Q!)t%Q%R!2w%R%W!)t%W%o!2w%o%p!)t%p&a!2w&a&b!)t&b0`!2w0`0d!)t0d0p!2w0p1O!)t1O1T!2w1T1[!)t1[1]!2w1]1^!)t1^1_!2w1_4U!)t4U4Z!2w4Z4[!)t4[4]!2w4]4^!2w4^4`!)t4`4d!2w4d4l!)t4l4m!2w4m4n!)t4n4q!2w4q4r!)t4r4s!2w4s4t!)t4t5Y!2w5Y5Z!)t5Z7Q!2w7Q7R!)t7R:S!2w:S:[!)t:[=p!2w=p=y!)t=y>q!2w>q>s!)t>s>t!2w>t>{!)t>{?t!2w?tA`!)tA`A{!2wA{BQ!)tBQBT!2wBTCS!)tCSDP!2wDPDt!)tDtDu!2wDuDv!2wDvDw!)tDwGO!2wGOGP!)tGPGQ!2wGQGa!)tGaGb!2wGbGc!2wGcGj!)tGjGk!2wGkGl!2wGlGv!)tGvGy!2wGyG{!)tG{G|!2wG|H^!)tH^H_!2wH_H`!)tH`IO!2wIOIm!)tImKj!2wKjKu!)tKuKv!2wKvL`!)tL`MR!2wMRM[!)tM[M]!2wM]M^!2wM^Mb!)tMbMc!2wMcMh!)tMhNO!2wNONS!)tNSNT!2wNTN^!)tN^N_!2wN_Nb!)tNbNc!2wNcNz!)tNz! e!2w! e!#O!)t!#O!#P!2w!#P!#Q!)t!#Q!#]!2w!#]!%W!)t!%W!&`!2w!&`!&c!)t!&c!&d!2w!&d!&v!)t!&v!&w!2w!&w!'O!)t!'O!'Y!2w!'Y!'i!)t!'i!'p!2w!'p!'q!)t!'q!'x!2w!'x!'}!)t!'}!(V!2w!(V!(X!)t!(X!(Y!2w!(Y!(Z!2w!(Z!(]!)t!(]!(s!2w!(s!(t!)t!(t!({!2w!({!(|!)t!(|!(}!2w!(}!)Q!)t!)Q!)U!2w!)U!)X!)t!)X!)Y!2w!)Y!)j!)t!)j!)k!2w!)k!)x!)t!)x!)y!2w!)y!)z!2w!)z!){!)t!){!*O!2w!*O!*^!)t!*^!*_!2w!*_!*`!2w!*`!*s!)t!*s!*y!2w!*y!*}!)t!*}!+O!2w!+O!+P!2w!+P!+R!)t!+R!+i!2w!+i!+j!)t!+j!+q!2w!+q!+r!)t!+r!+s!2w!+s!+t!2w!+t!+u!)t!+u!+v!2w!+v!+w!2w!+w!+x!)t!+x!+y!2w!+y!+z!2w!+z!,k!)t!,k!,o!2w!,o!,p!)t!,p!,q!2w!,q!-U!)t!-U!-X!2w!-X!-i!)t!-i!-r!2w!-r!-s!)t!-s!-v!2w!-v!-w!)t!-w!._!2w!._!.`!)t!.`!.g!2w!.g!.h!)t!.h!.i!2w!.i!.j!2w!.j!.k!)t!.k!.p!2w!.p!.s!)t!.s!.t!2w!.t!/W!)t!/W!/X!2w!/X!/h!)t!/h!/i!2w!/i!/j!2w!/j!0_!)t!0_!0g!2w!0g!0i!)t!0i!0j!2w!0j!0k!2w!0k!0m!)t!0m!1T!2w!1T!1U!)t!1U!1]!2w!1]!1^!)t!1^!1_!2w!1_!1`!2w!1`!1a!)t!1a!1f!2w!1f!1i!)t!1i!1j!2w!1j!2Y!)t!2Y!2Z!2w!2Z!2[!2w!2[!2]!)t!2]!2`!2w!2`!2o!)t!2o!2p!2w!2p!3R!)t!3R!3S!2w!3S!3T!)t!3T!3Z!2w!3Z!3^!)t!3^!3a!2w!3a!3b!)t!3b!3f!2w!3f!3i!)t!3i!3j!2w!3j!3k!2w!3k!3l!)t!3l!3m!2w!3m!3n!)t!3n!3o!2w!3o!3p!2w!3p!3s!)t!3s!3t!2w!3t!3u!2w!3u!3x!)t!3x!3{!2w!3{!4O!)t!4O!4[!2w!4[!4r!)t!4r!4s!2w!4s!5y!)t!5y!6R!2w!6R!6S!)t!6S!6V!2w!6V!6W!)t!6W!6o!2w!6o!6p!)t!6p!6z!2w!6z!6{!)t!6{!7Q!2w!7Q!7T!)t!7T!7U!2w!7U!7p!)t!7p!7q!2w!7q!7r!2w!7r!7x!)t!7x!7y!2w!7y!7z!2w!7z!8o!)t!8o!8w!2w!8w!8x!)t!8x!8{!2w!8{!8|!)t!8|!9e!2w!9e!9f!)t!9f!9p!2w!9p!9q!)t!9q!9v!2w!9v!9y!)t!9y!9z!2w!9z!:l!)t!:l!:m!2w!:m!:n!)t!:n!:o!2w!:o!:p!2w!:p!;P!)t!;P!;Q!2w!;Q!;R!2w!;R!;e!)t!;e!;m!2w!;m!;n!)t!;n!;q!2w!;q!;r!)t!;r!O!)t!>O!>U!2w!>U!>Z!)t!>Z!>m!2w!>m!>p!)t!>p!?Y!2w!?Y!?Z!)t!?Z!?d!2w!?d!?e!)t!?e!?f!2w!?f!?h!)t!?h!?o!2w!?o!@{!)t!@{!A}!2w!A}!BO!)t!BO!BP!2w!BP!BQ!2w!BQ!B^!)t!B^!Be!2w!Be!Cq!)t!Cq!Cr!2w!Cr!Cs!2w!Cs!Ct!)t!Ct!Cu!2w!Cu!Cw!)t!Cw!Cx!2w!Cx!Cy!2w!Cy!Cz!)t!Cz!C{!2w!C{!C}!)t!C}!DO!2w!DO!DU!)t!DU!DY!2w!DY!DZ!)t!DZ!Db!2w!Db!Dc!)t!Dc!Df!2w!Df!Dg!)t!Dg!Dh!2w!Dh!Di!)t!Di!Dj!2w!Dj!Dl!)t!Dl!Dm!2w!Dm!Dn!2w!Dn!Do!)t!Do!Ds!2w!Ds!Dt!)t!Dt!Du!2w!Du!Dv!2w!Dv!EP!)t!EP!EQ!2w!EQ!ES!)t!ES!EX!2w!EX!EY!)t!EY!EZ!2w!EZ!Ep!)t!Ep!Et!2w!Et!Ff!)t!Ff!Fg!2w!Fg!Gx!)t!Gx!HQ!2w!HQ!HR!)t!HR!Hw!2w!Hw!Id!)t!Id!Ii!2w!Ii!LQ!)t!LQ!L}!2w!L}!Mc!)t!Mc!Md!2w!Md!Mt!)t!Mt!Mz!2w!Mz!NO!)t!NO!NS!2w!NS!NV!)t!NV!NW!2w!NW!NZ!)t!NZ!N[!2w!N[!N]!2w!N]!Nd!)t!Nd!Ng!2w!Ng!Nk!)t!Nk!Nx!2w!Nx# U!)t# U# V!2w# V# h!)t# h#!`!2w#!`#!a!)t#!a#!b!2w#!b#!g!)t#!g#!h!2w#!h#!j!)t#!j##g!2w##g##h!)t##h#*s!2w#*s#*t!)t#*t#*x!2w#*x#*z!)t#*z#+R!2w#+R#+S!)t#+S#+T!2w#+T#+U!)t#+U#+Y!2w#+Y#+[!)t#+[#,V!2w#,V#,W!)t#,W#,[!2w#,[#,^!)t#,^#-P!2w#-P#-Q!)t#-Q#-U!2w#-U#-W!)t#-W#-_!2w#-_#-`!)t#-`#-a!2w#-a#-b!)t#-b#-f!2w#-f#-h!)t#-h#-w!2w#-w#-x!)t#-x#/T!2w#/T#/U!)t#/U#/Y!2w#/Y#/[!)t#/[#0q!2w#0q#1h!)t#1h#1x!2w#1x#2Y!)t#2Y#4R!2w#4R#4_!)t#4_#Au!2w#Au#Aw!)t#Aw#BY!2w#BY#BZ!)t#BZ#Bu!2w#Bu#Bz!)t#Bz#Di!2w#Di#EO!)t#EO#E]!2w#E]#E^!)t#E^#Eb!2w#Eb#Ep!)t#Ep#FS!2w#FS#Fb!)t#Fb#Ft!2w#Ft#GS!)t#GS#Ga!2w#Ga#Gb!)t#Gb#Ge!2w#Ge#Gt!)t#Gt#Hz!2w#Hz#Io!)t#Io#Ip!2w#Ip#It!)t#It#Iu!2w#Iu#K[!)t#K[#MW!2w#MW#M`!)t#M`#NZ!2w#NZ#N[!)t#N[#N]!2w#N]#Nb!)t#Nb$ z!2w$ z$!U!)t$!U$!s!2w$!s$#x!)t$#x$$h!2w$$h$$j!)t$$j$$o!2w$$o$$z!)t$$z$%x!2w$%x$&_!)t$&_$&f!2w$&f$'p!)t$'p$(X!2w$(X$(b!)t$(b$)i!2w$)i$+_!)t$+_$+`!2w$+`$-a!)t$-a$.b!2w$.b$.s!)t$.s$.z!2w$.z$0T!)t$0T$0s!2w$0s$1Q!)t$1Q$1R!2w$1R$1S!2w$1S$1^!)t$1^$2[!2w$2[$2v!)t$2v$3l!2w$3l$4g!)t$4g$4j!2w$4j$4t!)t$4t$5j!2w$5j$7y!)t$7y$7}!2w$7}$8O!)t$8O$8S!2w$8S$8V!)t$8V$8W!2w$8W$8X!2w$8X$8b!)t$8b$z!2w5>z5>{!)t5>{5>|!2w5>|5?P!)t5?P5?Q!2w5?Q5?R!2w5?R5?T!)t5?T5?Y!2w5?Y5?[!)t5?[5?]!2w5?]5?^!)t5?^5?_!2w5?_5?w!)t5?w5?z!2w5?z5?|!)t5?|5@X!2w5@X5@`!)t5@`5@c!2w5@c5@o!)t5@o5@u!2w5@u5@w!)t5@w5@}!2w5@}5AP!)t5AP5AV!2w5AV5A`!)t5A`5Ag!2w5Ag5Ah!)t5Ah5Ao!2w5Ao5Dv!)t5Dv5Ek!2w5Ek5FY!)t5FY;%S!2w;%S;%`!)t;%`;%w!2w;%w;%{!)t;%{;'O!2w;'O;'S!)t;'S;=`!.]<%l?&r!)t?&r?.p!2w?.p?.r!)t?.r?1Q!2w?1Q?1x!)t?1x?2P!2w?2P?2]!)t?2]?2b!2w?2b?2g!)t?2g?2h!2w?2h?2i!)t?2i?2s!2w?2s?2t!)t?2t?3R!2w?3R?3S!)t?3S?3X!2w?3X?3Y!)t?3Y?3Z!2w?3Z?3[!)t?3[?3]!2w?3]?3^!2w?3^?3_!)t?3_?3`!2w?3`?3a!2w?3a?3b!)t?3b?5r!2w?5r?6e!)t?6e?>`!2w?>`?>r!)t?>r?@U!2w?@U?@W!)t?@W?A`!2w?A`?BY!)t?BY?Bf!2w?Bf?EO!)t?EO?ET!2w?ET?EU!)t?EU?HR!2w?HR?Hw!)t?Hw?Ic!2w?Ic?Ii!)t?Ii?JT!2w?JT?J`!)t?J`?L]!2w?L]?L`!)t?L`?Lf!2w?Lf?Lh!)t?Lh?Ln!2w?Ln?Lp!)t?Lp?Lv!2w?Lv?Lx!)t?Lx?L{!2w?L{O!)t_=,gZ!^P!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T#o!)t#o#p=-Y#p;'S!)t;'S;=`!.]<%lO!)t_=-eX!`P!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_=.]X!SP!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)to=/TZ!_P!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T#q!)t#q#r=/v#r;'S!)t;'S;=`!.]<%lO!)tn=0RX!a`!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t_=0wZ!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T#r!)t#r#s=1j#s;'S!)t;'S;=`!.]<%lO!)t_=1sZ!OW|SzQOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T#r!)t#r#s=2f#s;'S!)t;'S;=`!.]<%lO!)t_=2qX!OW|SzQ!iPOr!)trs!*jsw!)twx!,^x#S!)t#S#T!-i#T;'S!)t;'S;=`!.]<%lO!)t", + tokenizers: [nodeEdgeText, nodeText, styleText, 0, 1, 2, 3, 4, 5], + topRules: {"FlowchartDiagram":[0,5]}, + specialized: [{term: 29, get: (value: keyof typeof spec_identifier) => spec_identifier[value] || -1}], + tokenPrec: 1356 +}) diff --git a/frontend/src/views/editor/language/mermaid/parsers/flowchart/highlight.ts b/frontend/src/views/editor/language/mermaid/parsers/flowchart/highlight.ts new file mode 100644 index 0000000..eea7f8a --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/flowchart/highlight.ts @@ -0,0 +1,22 @@ +import { styleTags, tags as t } from '@lezer/highlight'; +import { flowchartTags } from '../../tags'; + +export const flowchartHighlighting = styleTags({ + '( )': t.paren, + '[ ]': t.squareBracket, + '{ }': t.brace, + '<': t.angleBracket, + DiagramName: flowchartTags.diagramName, + DoubleEqual: flowchartTags.link, + DoubleHyphen: flowchartTags.link, + Keyword: flowchartTags.keyword, + LineComment: flowchartTags.lineComment, + Link: flowchartTags.link, + NodeEdge: flowchartTags.nodeEdge, + NodeEdgeText: flowchartTags.nodeEdgeText, + NodeId: flowchartTags.nodeId, + NodeText: flowchartTags.nodeText, + Number: flowchartTags.number, + Orientation: flowchartTags.orientation, + String: flowchartTags.string, +}); diff --git a/frontend/src/views/editor/language/mermaid/parsers/flowchart/tokens.ts b/frontend/src/views/editor/language/mermaid/parsers/flowchart/tokens.ts new file mode 100644 index 0000000..10f8455 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/flowchart/tokens.ts @@ -0,0 +1,55 @@ +import { ExternalTokenizer } from '@lezer/lr'; +import { NodeText, NodeEdgeText, StyleText } from './flowchart.grammar.terms'; + +const skipCodePoints = [-1, 9, 13, 32, 34, 39, 96]; +const startBracketCodePoints = [40, 62, 91, 123, 124]; +const endBracketCodePoints = [41, 93, 124, 125]; +const hyphen = 45; +const equal = 61; +const dot = 46; + +export const nodeText = new ExternalTokenizer((input) => { + if ( + skipCodePoints.includes(input.next) || + startBracketCodePoints.includes(input.next) + ) + return; + + while (!endBracketCodePoints.includes(input.next) && input.next !== -1) { + input.advance(); + } + + input.acceptToken(NodeText); +}); + +export const nodeEdgeText = new ExternalTokenizer((input) => { + if ( + skipCodePoints.includes(input.next) || + startBracketCodePoints.includes(input.next) || + input.next === hyphen || + input.next === equal || + input.next === dot + ) + return; + + while ( + input.next !== hyphen && + input.next !== equal && + input.next !== dot && + input.next !== -1 + ) { + input.advance(); + } + + input.acceptToken(NodeEdgeText); +}); + +export const styleText = new ExternalTokenizer((input) => { + if (input.next === 10 || input.next === -1) return; + + while (input.next !== 10 && input.next !== -1) { + input.advance(); + } + + input.acceptToken(StyleText); +}); diff --git a/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.grammar b/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.grammar new file mode 100644 index 0000000..aadb85c --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.grammar @@ -0,0 +1,60 @@ +@top GanttDiagram { + document +} + +@skip { spaces } + +document { + DiagramName newlines? | + DiagramName newlines (subDocument newlines?)+ +} + +subDocument { + Title ImportantText | + Section ImportantText | + DateFormat Text | + AxisFormat Text | + Excludes Text | + TickInterval Text | + TodayMarker Text | + Weekday Text | + Text | + InclusiveEndDates | + LineComment +} + +ImportantText { + text +} + +Text { + text +} + +DiagramName { kw<"gantt"> } + +kw { @specialize } + +@external tokens textToken from "./tokens" { + AxisFormat[group=Keyword], + DateFormat[group=Keyword], + Excludes[group=Keyword], + InclusiveEndDates[group=Keyword], + TickInterval[group=Keyword], + Title[group=Keyword], + TodayMarker[group=Keyword], + Weekday[group=Keyword], + Section, + text +} + +@tokens { + identifier { @asciiLetter+ } + spaces { @whitespace+ } + newlines { $[\n]+ } + LineComment { "%%" ![\n]* } + + @precedence { newlines, spaces } +} + +@external propSource ganttHighlighting from "./highlight" diff --git a/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.grammar.terms.d.ts b/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.grammar.terms.d.ts new file mode 100644 index 0000000..b5f9c13 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.grammar.terms.d.ts @@ -0,0 +1,10 @@ +export declare const AxisFormat: number; +export declare const DateFormat: number; +export declare const Excludes: number; +export declare const InclusiveEndDates: number; +export declare const Section: number; +export declare const TickInterval: number; +export declare const Title: number; +export declare const TodayMarker: number; +export declare const Weekday: number; +export declare const text: number; diff --git a/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.grammar.terms.ts b/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.grammar.terms.ts new file mode 100644 index 0000000..dda0ee3 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.grammar.terms.ts @@ -0,0 +1,17 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +export const + AxisFormat = 1, + DateFormat = 2, + Excludes = 3, + InclusiveEndDates = 4, + TickInterval = 5, + Title = 6, + TodayMarker = 7, + Weekday = 8, + Section = 9, + text = 17, + GanttDiagram = 10, + DiagramName = 11, + ImportantText = 12, + Text = 13, + LineComment = 14 diff --git a/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.grammar.test.ts b/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.grammar.test.ts new file mode 100644 index 0000000..fc7d811 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.grammar.test.ts @@ -0,0 +1,273 @@ +import { describe, it, expect } from 'vitest'; +import { parser } from './gantt.parser.grammar'; + +/** + * Gantt Diagram Grammar 测试 + * + * 测试目标:验证标准的 Mermaid Gantt Diagram 语法是否能正确解析,不应该出现错误节点(⚠) + */ +describe('Gantt Diagram Grammar 解析测试', () => { + + /** + * 辅助函数:解析代码并返回语法树 + */ + function parseCode(code: string) { + const tree = parser.parse(code); + return tree; + } + + /** + * 辅助函数:检查语法树中是否有错误节点 + */ + function hasErrorNodes(tree: any): { hasError: boolean; errors: Array<{ name: string; from: number; to: number; text: string }> } { + const errors: Array<{ name: string; from: number; to: number; text: string }> = []; + + tree.iterate({ + enter: (node: any) => { + if (node.name === '⚠') { + errors.push({ + name: node.name, + from: node.from, + to: node.to, + text: tree.toString().substring(node.from, node.to) + }); + } + } + }); + + return { + hasError: errors.length > 0, + errors + }; + } + + /** + * 辅助函数:打印语法树结构(用于调试) + */ + function printTree(tree: any, code: string, maxDepth = 5) { + const lines: string[] = []; + + tree.iterate({ + enter: (node: any) => { + const depth = getNodeDepth(tree, node); + if (depth > maxDepth) return false; // 限制深度 + + const indent = ' '.repeat(depth); + const text = code.substring(node.from, Math.min(node.to, node.from + 30)); + const displayText = text.length === 30 ? text + '...' : text; + + lines.push(`${indent}${node.name} [${node.from}-${node.to}]: "${displayText.replace(/\n/g, '\\n')}"`); + } + }); + + return lines.join('\n'); + } + + /** + * 获取节点深度 + */ + function getNodeDepth(tree: any, targetNode: any): number { + let depth = 0; + let current = targetNode; + while (current.parent) { + depth++; + current = current.parent; + } + return depth; + } + + it('应该正确解析基础的 gantt 声明', () => { + const code = `gantt +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带标题的 gantt 图', () => { + const code = `gantt + title A Gantt Diagram +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带日期格式的 gantt 图', () => { + const code = `gantt + dateFormat YYYY-MM-DD +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带章节的 gantt 图', () => { + const code = `gantt + dateFormat YYYY-MM-DD + section Section +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带任务的 gantt 图', () => { + const code = `gantt + dateFormat YYYY-MM-DD + title Adding GANTT diagram functionality to mermaid + section A section + Completed task :done, des1, 2014-01-06,2014-01-08 +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带活动任务的 gantt 图', () => { + const code = `gantt + dateFormat YYYY-MM-DD + section A section + Active task :active, des2, 2014-01-09, 3d +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带 axisFormat 的 gantt 图', () => { + const code = `gantt + dateFormat YYYY-MM-DD + axisFormat %m-%d +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带 excludes 的 gantt 图', () => { + const code = `gantt + dateFormat YYYY-MM-DD + excludes weekends +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带 todayMarker 的 gantt 图', () => { + const code = `gantt + dateFormat YYYY-MM-DD + todayMarker off +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析完整的 gantt 图示例', () => { + const code = `gantt + dateFormat YYYY-MM-DD + title Adding GANTT diagram functionality to mermaid + excludes weekends + + section A section + Completed task :done, des1, 2014-01-06,2014-01-08 + Active task :active, des2, 2014-01-09, 3d + Future task : des3, after des2, 5d + Future task2 : des4, after des3, 5d + + section Critical tasks + Completed task in the critical line :crit, done, 2014-01-06,24h + Implement parser and jison :crit, done, after des1, 2d + Create tests for parser :crit, active, 3d + Future task in critical line :crit, 5d + Create tests for renderer :2d + Add to mermaid :1d +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); +}); + diff --git a/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.parser.grammar.d.ts b/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.parser.grammar.d.ts new file mode 100644 index 0000000..1d78640 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.parser.grammar.d.ts @@ -0,0 +1,3 @@ +import { LRParser } from '@lezer/lr'; + +export declare const parser: LRParser; diff --git a/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.parser.grammar.ts b/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.parser.grammar.ts new file mode 100644 index 0000000..554531b --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/gantt/gantt.parser.grammar.ts @@ -0,0 +1,24 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +import {LRParser} from "@lezer/lr" +import {textToken} from "./tokens" +import {ganttHighlighting} from "./highlight" +const spec_identifier = {__proto__:null,gantt:44} +export const parser = LRParser.deserialize({ + version: 14, + states: "!|OVQQOOO[QQO'#CpQOQQOOOOQO'#Cg'#CgO!XQRO,59[OOQP'#Ci'#CiO!`QRO'#CtO!SQRO'#CtOOQP'#Ct'#CtO!eQRO'#CkO#`QRO1G.vOOQP'#Ch'#ChOOQP,59`,59`OOQP,59V,59VOOQP-E6i-E6i", + stateData: "#j~OcOS~OfRO~OgSO`dX~OPVOQVORVOSWOTVOUUOVVOWVOXUO^WOaTO~O`da~PdOaZO~Og]OP_XQ_XR_XS_XT_XU_XV_XW_XX_X^_X`_Xa_X~O`di~PdOgc~", + goto: "!UiPPPPPPPPPPPjmpPwPPPP}PPP!QRPOR[USWSYR[VQYSR^YRQOTXSY", + nodeNames: "⚠ AxisFormat DateFormat Excludes InclusiveEndDates TickInterval Title TodayMarker Weekday Section GanttDiagram DiagramName ImportantText Text LineComment", + maxTerm: 24, + nodeProps: [ + ["group", -8,1,2,3,4,5,6,7,8,"Keyword"] + ], + propSources: [ganttHighlighting], + skippedNodes: [0], + repeatNodeCount: 1, + tokenData: "$l~R_XY!QYZ!uZ^!Qpq!Quv#r!c!}$a#T#o$a#y#z!Q$f$g!Q#BY#BZ!Q$IS$I_!Q$I|$JO!Q$JT$JU!Q$KV$KW!Q&FU&FV!Q~!VYc~X^!Qpq!Q#y#z!Q$f$g!Q#BY#BZ!Q$IS$I_!Q$I|$JO!Q$JT$JU!Q$KV$KW!Q&FU&FV!Q~!|[g~c~XY!QYZ!uZ^!Qpq!Q#y#z!Q$f$g!Q#BY#BZ!Q$IS$I_!Q$I|$JO!Q$JT$JU!Q$KV$KW!Q&FU&FV!Q~#uPuv#x~#}S^~OY#xZ;'S#x;'S;=`$Z<%lO#x~$^P;=`<%l#x~$fQe~!c!}$a#T#o$a", + tokenizers: [textToken, 0], + topRules: {"GanttDiagram":[0,10]}, + specialized: [{term: 21, get: (value: keyof typeof spec_identifier) => spec_identifier[value] || -1}], + tokenPrec: 115 +}) diff --git a/frontend/src/views/editor/language/mermaid/parsers/gantt/highlight.ts b/frontend/src/views/editor/language/mermaid/parsers/gantt/highlight.ts new file mode 100644 index 0000000..c60cd88 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/gantt/highlight.ts @@ -0,0 +1,9 @@ +import { styleTags } from '@lezer/highlight'; +import { ganttTags } from '../../tags'; + +export const ganttHighlighting = styleTags({ + 'DiagramName Section': ganttTags.diagramName, + Keyword: ganttTags.keyword, + ImportantText: ganttTags.string, + LineComment: ganttTags.lineComment, +}); diff --git a/frontend/src/views/editor/language/mermaid/parsers/gantt/tokens.ts b/frontend/src/views/editor/language/mermaid/parsers/gantt/tokens.ts new file mode 100644 index 0000000..3b8323a --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/gantt/tokens.ts @@ -0,0 +1,59 @@ +import { ExternalTokenizer } from '@lezer/lr'; +import { + AxisFormat, + DateFormat, + Excludes, + InclusiveEndDates, + Section, + TickInterval, + Title, + TodayMarker, + Weekday, + text, +} from './gantt.grammar.terms'; + +const keywordMap: { [key: string]: number } = { + axisFormat: AxisFormat, + dateFormat: DateFormat, + excludes: Excludes, + inclusiveEndDates: InclusiveEndDates, + section: Section, + tickInterval: TickInterval, + title: Title, + todayMarker: TodayMarker, + weekday: Weekday, +}; + +const keywords = Object.keys(keywordMap); + +export const textToken = new ExternalTokenizer((input) => { + if (input.next === 32 || input.next === 10 || input.next === -1) return; + + if (input.next === 37 && input.peek(1) === 37) { + return; + } + + let tokens = ''; + + while (input.next !== 10 && input.next !== -1) { + tokens += String.fromCodePoint(input.next); + input.advance(); + } + + const activeKeyword = keywords.filter((keyword) => { + if (keyword === tokens) { + return tokens.startsWith(keyword); + } + return tokens.startsWith(keyword + ' '); + }); + + if (activeKeyword.length > 0) { + input.acceptToken( + keywordMap[activeKeyword[0]], + activeKeyword[0].length - tokens.length + ); + return; + } + + input.acceptToken(text); +}); diff --git a/frontend/src/views/editor/language/mermaid/parsers/index.ts b/frontend/src/views/editor/language/mermaid/parsers/index.ts new file mode 100644 index 0000000..d0feecb --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/index.ts @@ -0,0 +1,8 @@ +export { parser as mermaidParser } from './mermaid/mermaid.parser.grammar'; +export { parser as mindmapParser } from './mindmap/mindmap.parser.grammar'; +export { parser as pieParser } from './pie/pie.parser.grammar'; +export { parser as flowchartParser } from './flowchart/flowchart.parser.grammar'; +export { parser as sequenceParser } from './sequence/sequence.parser.grammar'; +export { parser as journeyParser } from './journey/journey.parser.grammar'; +export { parser as requirementParser } from './requirement/requirement.parser.grammar'; +export { parser as ganttParser } from './gantt/gantt.parser.grammar'; diff --git a/frontend/src/views/editor/language/mermaid/parsers/journey/highlight.ts b/frontend/src/views/editor/language/mermaid/parsers/journey/highlight.ts new file mode 100644 index 0000000..160279d --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/journey/highlight.ts @@ -0,0 +1,11 @@ +import { styleTags } from '@lezer/highlight'; +import { journeyTags } from '../../tags'; + +export const journeyHighlighting = styleTags({ + DiagramName: journeyTags.diagramName, + 'Text TaskName': journeyTags.text, + Actor: journeyTags.actor, + Keyword: journeyTags.keyword, + LineComment: journeyTags.lineComment, + Score: journeyTags.score, +}); diff --git a/frontend/src/views/editor/language/mermaid/parsers/journey/journey.grammar b/frontend/src/views/editor/language/mermaid/parsers/journey/journey.grammar new file mode 100644 index 0000000..b1f2844 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/journey/journey.grammar @@ -0,0 +1,59 @@ +@top JourneyDiagram { + document +} + +@skip { spaces } + +document { + DiagramName newlines* ( + () | + subDocument newlines* | + subDocument (newlines+ subDocument)+ newlines* + ) +} + +subDocument { + LineComment | + Keyword Text | + Task +} + +Task { + TaskName ":" Score (":" Actor ("," Actor)*)? +} + +Text { + text1 +} + +TaskName { + text2 +} + +Score { + text2 +} + +Actor { + text3 +} + +DiagramName { kw<"journey"> } + +kw { @specialize } + +@external tokens keywordTokens from "./tokens" { Keyword } +@external tokens textTokens1 from "./tokens" { text1 } +@external tokens textTokens2 from "./tokens" { text2 } +@external tokens textTokens3 from "./tokens" { text3 } + +@tokens { + spaces { @whitespace+ } + newlines { $[\n]+ } + LineComment { "%%" ![\n]* } + identifier { @asciiLetter+ } + + @precedence { newlines, spaces } +} + +@external propSource journeyHighlighting from "./highlight" diff --git a/frontend/src/views/editor/language/mermaid/parsers/journey/journey.grammar.terms.d.ts b/frontend/src/views/editor/language/mermaid/parsers/journey/journey.grammar.terms.d.ts new file mode 100644 index 0000000..d55df61 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/journey/journey.grammar.terms.d.ts @@ -0,0 +1,5 @@ +export declare const Keyword: number; +export declare const text1: number; +export declare const text2: number; +export declare const text3: number; + diff --git a/frontend/src/views/editor/language/mermaid/parsers/journey/journey.grammar.terms.ts b/frontend/src/views/editor/language/mermaid/parsers/journey/journey.grammar.terms.ts new file mode 100644 index 0000000..25a16b7 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/journey/journey.grammar.terms.ts @@ -0,0 +1,14 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +export const + Keyword = 1, + text1 = 14, + text2 = 15, + text3 = 16, + JourneyDiagram = 2, + DiagramName = 3, + LineComment = 4, + Text = 5, + Task = 6, + TaskName = 7, + Score = 8, + Actor = 9 diff --git a/frontend/src/views/editor/language/mermaid/parsers/journey/journey.grammar.test.ts b/frontend/src/views/editor/language/mermaid/parsers/journey/journey.grammar.test.ts new file mode 100644 index 0000000..9e3dafc --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/journey/journey.grammar.test.ts @@ -0,0 +1,234 @@ +import { describe, it, expect } from 'vitest'; +import { parser } from './journey.parser.grammar'; + +/** + * Journey Diagram Grammar 测试 + * + * 测试目标:验证标准的 Mermaid Journey Diagram 语法是否能正确解析,不应该出现错误节点(⚠) + */ +describe('Journey Diagram Grammar 解析测试', () => { + + /** + * 辅助函数:解析代码并返回语法树 + */ + function parseCode(code: string) { + const tree = parser.parse(code); + return tree; + } + + /** + * 辅助函数:检查语法树中是否有错误节点 + */ + function hasErrorNodes(tree: any): { hasError: boolean; errors: Array<{ name: string; from: number; to: number; text: string }> } { + const errors: Array<{ name: string; from: number; to: number; text: string }> = []; + + tree.iterate({ + enter: (node: any) => { + if (node.name === '⚠') { + errors.push({ + name: node.name, + from: node.from, + to: node.to, + text: tree.toString().substring(node.from, node.to) + }); + } + } + }); + + return { + hasError: errors.length > 0, + errors + }; + } + + /** + * 辅助函数:打印语法树结构(用于调试) + */ + function printTree(tree: any, code: string, maxDepth = 5) { + const lines: string[] = []; + + tree.iterate({ + enter: (node: any) => { + const depth = getNodeDepth(tree, node); + if (depth > maxDepth) return false; // 限制深度 + + const indent = ' '.repeat(depth); + const text = code.substring(node.from, Math.min(node.to, node.from + 30)); + const displayText = text.length === 30 ? text + '...' : text; + + lines.push(`${indent}${node.name} [${node.from}-${node.to}]: "${displayText.replace(/\n/g, '\\n')}"`); + } + }); + + return lines.join('\n'); + } + + /** + * 获取节点深度 + */ + function getNodeDepth(tree: any, targetNode: any): number { + let depth = 0; + let current = targetNode; + while (current.parent) { + depth++; + current = current.parent; + } + return depth; + } + + it('应该正确解析基础的 journey 声明', () => { + const code = `journey +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带标题的 journey 图', () => { + const code = `journey + title My working day +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带章节的 journey 图', () => { + const code = `journey + title My working day + section Go to work +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带任务和分数的 journey 图', () => { + const code = `journey + title My working day + section Go to work + Make tea: 5 +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带任务、分数和参与者的 journey 图', () => { + const code = `journey + title My working day + section Go to work + Make tea: 5: Me + Go upstairs: 3: Me +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带多个参与者的任务', () => { + const code = `journey + title My working day + section Go to work + Make tea: 5: Me, Cat +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析多个章节的 journey 图', () => { + const code = `journey + title My working day + section Go to work + Make tea: 5: Me + Go upstairs: 3: Me + section Work + Do work: 1: Me, Cat +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析完整的 journey 图示例', () => { + const code = `journey + title My working day + section Go to work + Make tea: 5: Me + Go upstairs: 3: Me + Do work: 1: Me, Cat + section Go home + Go downstairs: 5: Me + Sit down: 5: Me +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); +}); + diff --git a/frontend/src/views/editor/language/mermaid/parsers/journey/journey.parser.grammar.d.ts b/frontend/src/views/editor/language/mermaid/parsers/journey/journey.parser.grammar.d.ts new file mode 100644 index 0000000..1d78640 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/journey/journey.parser.grammar.d.ts @@ -0,0 +1,3 @@ +import { LRParser } from '@lezer/lr'; + +export declare const parser: LRParser; diff --git a/frontend/src/views/editor/language/mermaid/parsers/journey/journey.parser.grammar.ts b/frontend/src/views/editor/language/mermaid/parsers/journey/journey.parser.grammar.ts new file mode 100644 index 0000000..db08729 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/journey/journey.parser.grammar.ts @@ -0,0 +1,21 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +import {LRParser} from "@lezer/lr" +import {keywordTokens, textTokens1, textTokens2, textTokens3} from "./tokens" +import {journeyHighlighting} from "./highlight" +const spec_identifier = {__proto__:null,journey:42} +export const parser = LRParser.deserialize({ + version: 14, + states: "%^OVQ`OOO[QeO'#CoQOQ`OOOOQT'#C_'#C_OOQT'#Cf'#CfOmQeO,59ZOOQO'#Cc'#CcO!OQ`O'#CbOOQO'#Cs'#CsO!TQbO'#CsOvQ`O,59ZOOQT-E6d-E6dO!YQ`O1G.uO!bQdO,58|OOQO'#Ca'#CaOOQO,59_,59_O!gQeO1G.uO!YQ`O1G.uO!xQeO7+$aO#RQ`O7+$aOOQO'#Cd'#CdO#ZQ`O1G.hOOQO,59S,59SOOQO-E6f-E6fO#fQeO< spec_identifier[value] || -1}], + tokenPrec: 172 +}) diff --git a/frontend/src/views/editor/language/mermaid/parsers/journey/tokens.ts b/frontend/src/views/editor/language/mermaid/parsers/journey/tokens.ts new file mode 100644 index 0000000..36b64a6 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/journey/tokens.ts @@ -0,0 +1,69 @@ +import { ExternalTokenizer } from '@lezer/lr'; +import { Keyword, text1, text2, text3 } from './journey.grammar.terms'; + +import type { InputStream } from '@lezer/lr'; + +const skipCodePoints = [-1, 9, 10, 13, 32]; + +const keywords = ['title', 'section']; + +const isComment = (input: InputStream) => { + return input.peek(0) === 37 && input.peek(1) === 37; +}; + +const shouldSkip = (input: InputStream) => { + return skipCodePoints.includes(input.next) || isComment(input); +}; + +export const keywordTokens = new ExternalTokenizer((input) => { + if (shouldSkip(input)) return; + + let tokens = ''; + + while (!skipCodePoints.includes(input.next)) { + tokens += String.fromCodePoint(input.next); + input.advance(); + } + + const activeKeyword = keywords.filter((keyword) => { + if (keyword === tokens) { + return tokens.toLowerCase().startsWith(keyword); + } + return tokens.toLowerCase().startsWith(keyword + ' '); // ensure the keyword isn't used as a token unless there's a space at the end e.g. titleStuff + }); + + if (activeKeyword.length > 0) { + input.acceptToken(Keyword, activeKeyword[0].length - tokens.length); + return; + } +}); + +export const textTokens1 = new ExternalTokenizer((input) => { + if (shouldSkip(input)) return; + + while (input.next !== 10 && input.next !== -1) { + input.advance(); + } + + input.acceptToken(text1); +}); + +export const textTokens2 = new ExternalTokenizer((input) => { + if (shouldSkip(input)) return; + + while (input.next !== 58 && input.next !== 10 && input.next !== -1) { + input.advance(); + } + + input.acceptToken(text2); +}); + +export const textTokens3 = new ExternalTokenizer((input) => { + if (shouldSkip(input)) return; + + while (input.next !== 44 && input.next !== 10 && input.next !== -1) { + input.advance(); + } + + input.acceptToken(text3); +}); diff --git a/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.grammar b/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.grammar new file mode 100644 index 0000000..610d16f --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.grammar @@ -0,0 +1,29 @@ +// See this link for entire Pie chart syntax in Mermaid: https://mermaid.js.org/syntax/pie.html#syntax +@top MermaidDiagram { + preDiagramLine* ( + PieDiagram | + MindmapDiagram | + FlowchartDiagram | + SequenceDiagram | + JourneyDiagram | + RequirementDiagram | + GanttDiagram + ) +} + +@skip { space } + +@tokens { + space { $[ \t\r]+ } +} + +@external tokens diagramText from "./tokens" { + preDiagramLine, + PieDiagram, + MindmapDiagram, + FlowchartDiagram, + SequenceDiagram, + JourneyDiagram, + RequirementDiagram, + GanttDiagram +} diff --git a/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.grammar.terms.d.ts b/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.grammar.terms.d.ts new file mode 100644 index 0000000..3e84fd5 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.grammar.terms.d.ts @@ -0,0 +1,8 @@ +export declare const preDiagramLine: number; +export declare const MindmapDiagram: number; +export declare const PieDiagram: number; +export declare const FlowchartDiagram: number; +export declare const SequenceDiagram: number; +export declare const JourneyDiagram: number; +export declare const RequirementDiagram: number; +export declare const GanttDiagram: number; diff --git a/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.grammar.terms.ts b/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.grammar.terms.ts new file mode 100644 index 0000000..0f860fc --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.grammar.terms.ts @@ -0,0 +1,11 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +export const + preDiagramLine = 11, + PieDiagram = 1, + MindmapDiagram = 2, + FlowchartDiagram = 3, + SequenceDiagram = 4, + JourneyDiagram = 5, + RequirementDiagram = 6, + GanttDiagram = 7, + MermaidDiagram = 8 diff --git a/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.grammar.test.ts b/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.grammar.test.ts new file mode 100644 index 0000000..17a6e96 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.grammar.test.ts @@ -0,0 +1,283 @@ +import { describe, it, expect } from 'vitest'; +import { parser } from './mermaid.parser.grammar'; + +/** + * Mermaid Grammar 测试 + * + * 测试目标:验证标准的 Mermaid 综合语法是否能正确解析,不应该出现错误节点(⚠) + * 这个测试涵盖所有类型的 Mermaid 图表 + */ +describe('Mermaid Grammar 解析测试', () => { + + /** + * 辅助函数:解析代码并返回语法树 + */ + function parseCode(code: string) { + const tree = parser.parse(code); + return tree; + } + + /** + * 辅助函数:检查语法树中是否有错误节点 + */ + function hasErrorNodes(tree: any): { hasError: boolean; errors: Array<{ name: string; from: number; to: number; text: string }> } { + const errors: Array<{ name: string; from: number; to: number; text: string }> = []; + + tree.iterate({ + enter: (node: any) => { + if (node.name === '⚠') { + errors.push({ + name: node.name, + from: node.from, + to: node.to, + text: tree.toString().substring(node.from, node.to) + }); + } + } + }); + + return { + hasError: errors.length > 0, + errors + }; + } + + /** + * 辅助函数:打印语法树结构(用于调试) + */ + function printTree(tree: any, code: string, maxDepth = 5) { + const lines: string[] = []; + + tree.iterate({ + enter: (node: any) => { + const depth = getNodeDepth(tree, node); + if (depth > maxDepth) return false; // 限制深度 + + const indent = ' '.repeat(depth); + const text = code.substring(node.from, Math.min(node.to, node.from + 30)); + const displayText = text.length === 30 ? text + '...' : text; + + lines.push(`${indent}${node.name} [${node.from}-${node.to}]: "${displayText.replace(/\n/g, '\\n')}"`); + } + }); + + return lines.join('\n'); + } + + /** + * 获取节点深度 + */ + function getNodeDepth(tree: any, targetNode: any): number { + let depth = 0; + let current = targetNode; + while (current.parent) { + depth++; + current = current.parent; + } + return depth; + } + + it('应该正确解析 Pie 图', () => { + const code = `pie title Pets + "Dogs" : 386 + "Cats" : 85 +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析 Mindmap 图', () => { + const code = `mindmap + root((mindmap)) + Origins + Long history + Research + On effectiveness +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析 Flowchart 图', () => { + const code = `flowchart TD + A[Start] --> B{Is it?} + B -->|Yes| C[OK] + B -->|No| D[End] +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析 Sequence 图', () => { + const code = `sequenceDiagram + Alice->>John: Hello John + John-->>Alice: Great! +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析 Journey 图', () => { + const code = `journey + title My working day + section Go to work + Make tea: 5: Me + Go upstairs: 3: Me +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析 Requirement 图', () => { + const code = `requirementDiagram + + requirement test_req { + id: 1 + text: the test text + risk: high + } + + element test_entity { + type: simulation + } + + test_entity - satisfies -> test_req +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析 Gantt 图', () => { + const code = `gantt + dateFormat YYYY-MM-DD + title Adding GANTT diagram + section A section + Completed task :done, des1, 2014-01-06,2014-01-08 + Active task :active, des2, 2014-01-09, 3d +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带注释的 flowchart 图', () => { + const code = `%% This is a comment +flowchart TD + A[Start] --> B[End] +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带空行的 sequence 图', () => { + const code = ` +sequenceDiagram + participant Alice + + Alice->>John: Hello +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析 graph 类型的流程图', () => { + const code = `graph LR + A[Square Rect] -- Link text --> B((Circle)) + A --> C(Round Rect) + B --> D{Rhombus} + C --> D +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); +}); + diff --git a/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.parser.grammar.d.ts b/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.parser.grammar.d.ts new file mode 100644 index 0000000..1d78640 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.parser.grammar.d.ts @@ -0,0 +1,3 @@ +import { LRParser } from '@lezer/lr'; + +export declare const parser: LRParser; diff --git a/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.parser.grammar.ts b/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.parser.grammar.ts new file mode 100644 index 0000000..275fca9 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/mermaid/mermaid.parser.grammar.ts @@ -0,0 +1,17 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +import {LRParser} from "@lezer/lr" +import {diagramText} from "./tokens" +export const parser = LRParser.deserialize({ + version: 14, + states: "nOVQROOOOQQ'#Ce'#CeOVQROOQOQPOOOOQQ-E6c-E6c", + stateData: "q~O]OS~OPROQRORROSROTROUROVROZPO~O", + goto: "aYPPPPPPPPPZQQORSQ", + nodeNames: "⚠ PieDiagram MindmapDiagram FlowchartDiagram SequenceDiagram JourneyDiagram RequirementDiagram GanttDiagram MermaidDiagram", + maxTerm: 13, + skippedNodes: [0], + repeatNodeCount: 1, + tokenData: "j~RRXY[]^[pq[~aR]~XY[]^[pq[", + tokenizers: [0, diagramText], + topRules: {"MermaidDiagram":[0,8]}, + tokenPrec: 0 +}) diff --git a/frontend/src/views/editor/language/mermaid/parsers/mermaid/tokens.ts b/frontend/src/views/editor/language/mermaid/parsers/mermaid/tokens.ts new file mode 100644 index 0000000..e91e622 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/mermaid/tokens.ts @@ -0,0 +1,52 @@ +import { ExternalTokenizer } from '@lezer/lr'; +import { + preDiagramLine, + MindmapDiagram, + PieDiagram, + FlowchartDiagram, + SequenceDiagram, + JourneyDiagram, + RequirementDiagram, + GanttDiagram, +} from './mermaid.grammar.terms'; + +const skipCodePoints = [-1, 9, 13, 32]; + +const diagramMap: Record = { + mindmap: MindmapDiagram, + pie: PieDiagram, + flowchart: FlowchartDiagram, + graph: FlowchartDiagram, + sequenceDiagram: SequenceDiagram, + journey: JourneyDiagram, + requirementDiagram: RequirementDiagram, + gantt: GanttDiagram, +}; + +const diagrams = Object.keys(diagramMap); + +export const diagramText = new ExternalTokenizer((input) => { + if (skipCodePoints.includes(input.next)) return; + + let tokens = ''; + + while (input.next != 10 && input.next !== -1) { + tokens += String.fromCodePoint(input.next); + input.advance(); + } + + input.advance(); + + const activeDiagram = diagrams.filter((diagram) => { + return tokens.startsWith(diagram); + }); + + if (activeDiagram.length > 0) { + while (input.next !== -1) { + input.advance(); + } + input.acceptToken(diagramMap[activeDiagram[0]]); + } else { + input.acceptToken(preDiagramLine); + } +}); diff --git a/frontend/src/views/editor/language/mermaid/parsers/mindmap/highlight.ts b/frontend/src/views/editor/language/mermaid/parsers/mindmap/highlight.ts new file mode 100644 index 0000000..2ea3036 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/mindmap/highlight.ts @@ -0,0 +1,11 @@ +import { styleTags } from '@lezer/highlight'; +import { mindmapTags } from '../../tags'; + +export const mindmapHighlighting = styleTags({ + DiagramName: mindmapTags.diagramName, + LineText1: mindmapTags.lineText1, + LineText2: mindmapTags.lineText2, + LineText3: mindmapTags.lineText3, + LineText4: mindmapTags.lineText4, + LineText5: mindmapTags.lineText5, +}); diff --git a/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.grammar b/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.grammar new file mode 100644 index 0000000..46eb10a --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.grammar @@ -0,0 +1,92 @@ +@top MindmapDiagram { + newline+ | + DiagramName Line* +} + +@skip { spaces | newlineEmpty } + +lineText { + LineText1 | + LineText2 | + LineText3 | + LineText4 | + LineText5 +} + +ShapedText { + square | + roundedSquare | + circle | + bang | + cloud | + hexagon +} + +square { + "[" lineText "]" +} + +roundedSquare { + "(" lineText ")" +} + +circle { + "((" lineText "))" +} + +bang { + "))" lineText "((" +} + +cloud { + ")" lineText "(" +} + +hexagon { + "{{" lineText "}}" +} + +IconLine { + "::" Icon "(" lineText ")" +} + +ClassLine { + ":::" lineText +} + +Line { + newline | + newline indent ( + lineText | + IconLine | + ClassLine | + ShapedText | + lineText ShapedText + ) +} + +DiagramName { kw<"mindmap"> } + +Icon { kw<"icon"> } + +kw { @specialize } + +@context trackIndent from "./tokens.js" + +@external tokens indentation from "./tokens" { indent } +@external tokens lineTextType from "./tokens" { + LineText1, + LineText2, + LineText3, + LineText4, + LineText5 +} + +@tokens { + spaces { ($[ \t\f] | "\\" $[\n\r])+ } + word { @asciiLetter+ } +} + +@external tokens newlines from "./tokens" { newline, newlineEmpty } + +@external propSource mindmapHighlighting from "./highlight" diff --git a/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.grammar.terms.d.ts b/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.grammar.terms.d.ts new file mode 100644 index 0000000..9210fa7 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.grammar.terms.d.ts @@ -0,0 +1,8 @@ +export declare const newline: number; +export declare const newlineEmpty: number; +export declare const indent: number; +export declare const LineText1: number; +export declare const LineText2: number; +export declare const LineText3: number; +export declare const LineText4: number; +export declare const LineText5: number; diff --git a/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.grammar.terms.ts b/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.grammar.terms.ts new file mode 100644 index 0000000..fd5b71f --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.grammar.terms.ts @@ -0,0 +1,17 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +export const + indent = 16, + LineText1 = 1, + LineText2 = 2, + LineText3 = 3, + LineText4 = 4, + LineText5 = 5, + newline = 17, + newlineEmpty = 18, + MindmapDiagram = 6, + DiagramName = 7, + Line = 8, + IconLine = 9, + Icon = 10, + ClassLine = 11, + ShapedText = 12 diff --git a/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.grammar.test.ts b/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.grammar.test.ts new file mode 100644 index 0000000..c07f322 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.grammar.test.ts @@ -0,0 +1,239 @@ +import { describe, it, expect } from 'vitest'; +import { parser } from './mindmap.parser.grammar'; + +/** + * Mindmap Grammar 测试 + * + * 测试目标:验证标准的 Mermaid Mindmap 语法是否能正确解析,不应该出现错误节点(⚠) + */ +describe('Mindmap Grammar 解析测试', () => { + + /** + * 辅助函数:解析代码并返回语法树 + */ + function parseCode(code: string) { + const tree = parser.parse(code); + return tree; + } + + /** + * 辅助函数:检查语法树中是否有错误节点 + */ + function hasErrorNodes(tree: any): { hasError: boolean; errors: Array<{ name: string; from: number; to: number; text: string }> } { + const errors: Array<{ name: string; from: number; to: number; text: string }> = []; + + tree.iterate({ + enter: (node: any) => { + if (node.name === '⚠') { + errors.push({ + name: node.name, + from: node.from, + to: node.to, + text: tree.toString().substring(node.from, node.to) + }); + } + } + }); + + return { + hasError: errors.length > 0, + errors + }; + } + + /** + * 辅助函数:打印语法树结构(用于调试) + */ + function printTree(tree: any, code: string, maxDepth = 5) { + const lines: string[] = []; + + tree.iterate({ + enter: (node: any) => { + const depth = getNodeDepth(tree, node); + if (depth > maxDepth) return false; // 限制深度 + + const indent = ' '.repeat(depth); + const text = code.substring(node.from, Math.min(node.to, node.from + 30)); + const displayText = text.length === 30 ? text + '...' : text; + + lines.push(`${indent}${node.name} [${node.from}-${node.to}]: "${displayText.replace(/\n/g, '\\n')}"`); + } + }); + + return lines.join('\n'); + } + + /** + * 获取节点深度 + */ + function getNodeDepth(tree: any, targetNode: any): number { + let depth = 0; + let current = targetNode; + while (current.parent) { + depth++; + current = current.parent; + } + return depth; + } + + it('应该正确解析基础的 mindmap 声明', () => { + const code = `mindmap + Root +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带子节点的 mindmap', () => { + const code = `mindmap + Root + A + B +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析多层级的 mindmap', () => { + const code = `mindmap + Root + A + A1 + A2 + B + B1 + B2 +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带方括号形状的节点', () => { + const code = `mindmap + Root + [Square node] +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带圆括号形状的节点', () => { + const code = `mindmap + Root + (Rounded node) +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带双圆括号形状的节点(圆形)', () => { + const code = `mindmap + Root + ((Circle node)) +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带花括号形状的节点(六边形)', () => { + const code = `mindmap + Root + {{Hexagon node}} +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析完整的 mindmap 示例', () => { + const code = `mindmap + root((mindmap)) + Origins + Long history + ::icon(fa fa-book) + Popularisation + British popular psychology author Tony Buzan + Research + On effectiveness
and features + On Automatic creation + Uses + Creative techniques + Strategic planning + Argument mapping +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); +}); + diff --git a/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.parser.grammar.d.ts b/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.parser.grammar.d.ts new file mode 100644 index 0000000..1d78640 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.parser.grammar.d.ts @@ -0,0 +1,3 @@ +import { LRParser } from '@lezer/lr'; + +export declare const parser: LRParser; diff --git a/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.parser.grammar.ts b/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.parser.grammar.ts new file mode 100644 index 0000000..ed5553b --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/mindmap/mindmap.parser.grammar.ts @@ -0,0 +1,23 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +import {LRParser} from "@lezer/lr" +import {indentation, lineTextType, newlines} from "./tokens" +import {trackIndent} from "./tokens.js" +import {mindmapHighlighting} from "./highlight" +const spec_word = {__proto__:null,mindmap:44, icon:50} +export const parser = LRParser.deserialize({ + version: 14, + states: "&fOYQ[OOOOQW'#Ci'#CiQbQ[OOQgQ[OOOOQW'#Cc'#CcOOQW-E6g-E6gOlQ]O'#CdOOQW'#Cj'#CjQgQ[OOO!]Q^O,59OOOQW-E6h-E6hOOQW'#Cs'#CsO!vQ[O'#CeO!{Q^O'#CgO!{Q^O'#CyO!{Q^O'#C|O!{Q^O'#C}O!{Q^O'#DQO!{Q^O'#DRO!{Q^O'#DSOOQW'#Ch'#ChO#^Q[O1G.jOOQW1G.j1G.jO#hQ[O,59POOQW'#Cf'#CfOOQW,59R,59RO#mQ[O,59eO#rQ[O,59hO#wQ[O,59iO#|Q[O,59lO$RQ[O,59mO$WQ[O,59nOOQW7+$U7+$UO!{Q^O1G.kOOQW1G/P1G/POOQW1G/S1G/SOOQW1G/T1G/TOOQW1G/W1G/WOOQW1G/X1G/XOOQW1G/Y1G/YO$]Q[O7+$VOOQW< spec_word[value] || -1}], + tokenPrec: 0 +}) diff --git a/frontend/src/views/editor/language/mermaid/parsers/mindmap/tokens.ts b/frontend/src/views/editor/language/mermaid/parsers/mindmap/tokens.ts new file mode 100644 index 0000000..ce25bcb --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/mindmap/tokens.ts @@ -0,0 +1,140 @@ +import { ExternalTokenizer, ContextTracker } from '@lezer/lr'; +import { + newline as newlineToken, + newlineEmpty, + indent, + LineText1, + LineText2, + LineText3, + LineText4, + LineText5, +} from './mindmap.grammar.terms'; + +import type { InputStream } from '@lezer/lr'; + +type InputStreamWithRead = InputStream & { + read: (inputPosition: number, stackPosition: number) => string; +}; + +const LineTextTokens = [LineText1, LineText2, LineText3, LineText4, LineText5]; + +const newline = 10, + carriageReturn = 13, + space = 32, + tab = 9, + hash = 35, + colon = 58, + parenL = 40, + parenR = 41, + bracketL = 91, + bracketR = 93, + braceL = 123, + braceR = 125; + +export const newlines = new ExternalTokenizer( + (input, _stack) => { + if (input.next < 0) return; + else { + input.advance(); + let spaces = 0; + while ((input.next as number) == space || (input.next as number) == tab) { + input.advance(); + spaces++; + } + let empty = + input.next == newline || + input.next == carriageReturn || + input.next == hash; + input.acceptToken(empty ? newlineEmpty : newlineToken, -spaces); + } + }, + { contextual: true, fallback: true } +); + +export const lineTextType = new ExternalTokenizer((input, stack) => { + let chars = 0; + + while (input.next > -1 && input.next !== newline) { + if (input.next === colon) return; + + if ( + input.next === parenL || + input.next === bracketL || + input.next === braceL + ) { + if (chars > 0) { + input.acceptToken(stack.context.lineType); + return; + } else return; + } + + if ( + (input.next === parenR || + input.next === bracketR || + input.next === braceR) && + chars > 0 + ) { + input.acceptToken(stack.context.lineType); + return; + } + + input.advance(); + chars++; + } + + input.acceptToken(stack.context.lineType); +}); + +const tabDepth = (depth: number) => { + return 4 - (depth % 4); +}; + +export const indentation = new ExternalTokenizer((input, _stack) => { + let prev = input.peek(-1); + if (prev == newline || prev == carriageReturn) { + let depth = 0; + let chars = 0; + + while (true) { + if (input.next == space) depth++; + else if (input.next == tab) depth += tabDepth(depth); + else break; + input.advance(); + chars++; + } + + if ( + input.next != newline && + input.next != carriageReturn && + input.next != hash + ) { + input.acceptToken(indent); + } + } +}); + +const indentTracker = { + lineType: LineText1, +}; + +const countIndent = (space: string) => { + let depth = 0; + for (let i = 0; i < space.length; i++) + depth += space.charCodeAt(i) == tab ? tabDepth(depth) : 1; + return depth; +}; + +const getLineType = (depth: number) => { + return LineTextTokens[depth % 5]; +}; + +export const trackIndent = new ContextTracker({ + start: indentTracker, + shift(context, term, stack, input: InputStreamWithRead) { + if (term === indent) { + const depth = countIndent(input.read(input.pos, stack.pos)); + context.lineType = getLineType(depth); + } + return context; + }, +}); diff --git a/frontend/src/views/editor/language/mermaid/parsers/pie/highlight.ts b/frontend/src/views/editor/language/mermaid/parsers/pie/highlight.ts new file mode 100644 index 0000000..216f44c --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/pie/highlight.ts @@ -0,0 +1,12 @@ +import { styleTags } from '@lezer/highlight'; +import { pieTags } from '../../tags'; + +export const pieHighlighting = styleTags({ + DiagramName: pieTags.diagramName, + LineComment: pieTags.lineComment, + Number: pieTags.number, + ShowData: pieTags.showData, + String: pieTags.string, + Title: pieTags.title, + TitleText: pieTags.titleText, +}); diff --git a/frontend/src/views/editor/language/mermaid/parsers/pie/pie.grammar b/frontend/src/views/editor/language/mermaid/parsers/pie/pie.grammar new file mode 100644 index 0000000..db27165 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/pie/pie.grammar @@ -0,0 +1,44 @@ +@top PieDiagram { document+ } + +@skip { spaces | LineComment } + +document { + DiagramName ShowData? ( + () | + Title | + Title TitleText | + Title TitleText kvPair+ | + Title kvPair+ | + kvPair+ + ) +} + +@skip {} { + String { + '"' (stringContentDouble)* '"' + } +} + +kvPair { + String ":" Number +} + +DiagramName { kw<"pie"> } + +ShowData { kw<"showData"> } + +Title { kw<"title"> } + +kw { @specialize } + +@external tokens titleText from "./tokens" { TitleText } + +@tokens { + identifier { @asciiLetter+ } + stringContentDouble { !["]+ } + spaces { @whitespace+ } + Number { @digit+ ("." @digit+)? } + LineComment { "%%" ![\n]* } +} + +@external propSource pieHighlighting from "./highlight" diff --git a/frontend/src/views/editor/language/mermaid/parsers/pie/pie.grammar.terms.d.ts b/frontend/src/views/editor/language/mermaid/parsers/pie/pie.grammar.terms.d.ts new file mode 100644 index 0000000..f134b0d --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/pie/pie.grammar.terms.d.ts @@ -0,0 +1,2 @@ +export declare const LineComment: number; +export declare const TitleText: number; diff --git a/frontend/src/views/editor/language/mermaid/parsers/pie/pie.grammar.terms.ts b/frontend/src/views/editor/language/mermaid/parsers/pie/pie.grammar.terms.ts new file mode 100644 index 0000000..0f1cb21 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/pie/pie.grammar.terms.ts @@ -0,0 +1,10 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +export const + TitleText = 1, + LineComment = 2, + PieDiagram = 3, + DiagramName = 4, + ShowData = 5, + Title = 6, + String = 7, + Number = 8 diff --git a/frontend/src/views/editor/language/mermaid/parsers/pie/pie.grammar.test.ts b/frontend/src/views/editor/language/mermaid/parsers/pie/pie.grammar.test.ts new file mode 100644 index 0000000..7003693 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/pie/pie.grammar.test.ts @@ -0,0 +1,204 @@ +import { describe, it, expect } from 'vitest'; +import { parser } from './pie.parser.grammar'; + +/** + * Pie Chart Grammar 测试 + * + * 测试目标:验证标准的 Mermaid Pie Chart 语法是否能正确解析,不应该出现错误节点(⚠) + */ +describe('Pie Chart Grammar 解析测试', () => { + + /** + * 辅助函数:解析代码并返回语法树 + */ + function parseCode(code: string) { + const tree = parser.parse(code); + return tree; + } + + /** + * 辅助函数:检查语法树中是否有错误节点 + */ + function hasErrorNodes(tree: any): { hasError: boolean; errors: Array<{ name: string; from: number; to: number; text: string }> } { + const errors: Array<{ name: string; from: number; to: number; text: string }> = []; + + tree.iterate({ + enter: (node: any) => { + if (node.name === '⚠') { + errors.push({ + name: node.name, + from: node.from, + to: node.to, + text: tree.toString().substring(node.from, node.to) + }); + } + } + }); + + return { + hasError: errors.length > 0, + errors + }; + } + + /** + * 辅助函数:打印语法树结构(用于调试) + */ + function printTree(tree: any, code: string, maxDepth = 5) { + const lines: string[] = []; + + tree.iterate({ + enter: (node: any) => { + const depth = getNodeDepth(tree, node); + if (depth > maxDepth) return false; // 限制深度 + + const indent = ' '.repeat(depth); + const text = code.substring(node.from, Math.min(node.to, node.from + 30)); + const displayText = text.length === 30 ? text + '...' : text; + + lines.push(`${indent}${node.name} [${node.from}-${node.to}]: "${displayText.replace(/\n/g, '\\n')}"`); + } + }); + + return lines.join('\n'); + } + + /** + * 获取节点深度 + */ + function getNodeDepth(tree: any, targetNode: any): number { + let depth = 0; + let current = targetNode; + while (current.parent) { + depth++; + current = current.parent; + } + return depth; + } + + it('应该正确解析基础的 pie 声明', () => { + const code = `pie +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带 showData 的 pie 图', () => { + const code = `pie showData +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带标题的 pie 图', () => { + const code = `pie title My Pie Chart +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带数据的 pie 图', () => { + const code = `pie + "Dogs" : 386 + "Cats" : 85 +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带标题和数据的完整 pie 图', () => { + const code = `pie title Pets adopted by volunteers + "Dogs" : 386 + "Cats" : 85 + "Rats" : 15 +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带小数值的数据', () => { + const code = `pie + "A" : 10.5 + "B" : 20.75 + "C" : 30.25 +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带 showData 和完整数据的 pie 图', () => { + const code = `pie showData + title Key elements in Product X + "Calcium" : 42.96 + "Potassium" : 50.05 + "Magnesium" : 10.01 + "Iron" : 5 +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); +}); + diff --git a/frontend/src/views/editor/language/mermaid/parsers/pie/pie.parser.grammar.d.ts b/frontend/src/views/editor/language/mermaid/parsers/pie/pie.parser.grammar.d.ts new file mode 100644 index 0000000..1d78640 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/pie/pie.parser.grammar.d.ts @@ -0,0 +1,3 @@ +import { LRParser } from '@lezer/lr'; + +export declare const parser: LRParser; diff --git a/frontend/src/views/editor/language/mermaid/parsers/pie/pie.parser.grammar.ts b/frontend/src/views/editor/language/mermaid/parsers/pie/pie.parser.grammar.ts new file mode 100644 index 0000000..7866dcd --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/pie/pie.parser.grammar.ts @@ -0,0 +1,21 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +import {LRParser} from "@lezer/lr" +import {titleText} from "./tokens" +import {pieHighlighting} from "./highlight" +const spec_identifier = {__proto__:null,pie:34, showData:36, title:38} +export const parser = LRParser.deserialize({ + version: 14, + states: "$nOYQQOOO_QQO'#CkOOQO'#Ce'#CeQYQQOOOOQO'#C`'#C`OpOSO'#CcOxQQO'#CpOOQO'#Cf'#CfO}QQO,59VO!YQRO,59VO!hQQO,59VOOQO'#Ca'#CaOOQP'#Cb'#CbOOQO-E6c-E6cOOOO'#Cg'#CgO!vOSO,58}OOQO,58},58}O#OQQO,59[OOQO-E6d-E6dO#TQQO1G.qO#TQQO1G.qO#`QRO1G.qOOOO-E6e-E6eOOQO1G.i1G.iOOQO1G.v1G.vO#nQQO7+$]O#nQQO7+$]O#yQQO< spec_identifier[value] || -1}], + tokenPrec: 0 +}) diff --git a/frontend/src/views/editor/language/mermaid/parsers/pie/tokens.ts b/frontend/src/views/editor/language/mermaid/parsers/pie/tokens.ts new file mode 100644 index 0000000..91fbe84 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/pie/tokens.ts @@ -0,0 +1,17 @@ +import { ExternalTokenizer } from '@lezer/lr'; +import { TitleText } from './pie.grammar.terms'; + +export const titleText = new ExternalTokenizer((input) => { + if (input.next === 10) { + input.acceptToken(TitleText); + return; + } + + if (input.next === -1) return; + + while (input.next !== 10 && input.next !== -1) { + input.advance(); + } + + input.acceptToken(TitleText); +}); diff --git a/frontend/src/views/editor/language/mermaid/parsers/requirement/highlight.ts b/frontend/src/views/editor/language/mermaid/parsers/requirement/highlight.ts new file mode 100644 index 0000000..0343b6d --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/requirement/highlight.ts @@ -0,0 +1,13 @@ +import { styleTags } from '@lezer/highlight'; +import { requirementTags } from '../../tags'; + +export const requirementHighlighting = styleTags({ + 'DiagramName SubDiagramType': requirementTags.diagramName, + LineComment: requirementTags.lineComment, + IDNumber: requirementTags.number, + 'UnquotedString RelationshipStart': requirementTags.unquotedString, + QuotedString: requirementTags.quotedString, + PropKeyword: requirementTags.unquotedString, + Keyword: requirementTags.keyword, + 'ForwardArrow BackArrow Hyphen': requirementTags.arrow, +}); diff --git a/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.grammar b/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.grammar new file mode 100644 index 0000000..a06c767 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.grammar @@ -0,0 +1,151 @@ +@top RequirementDiagram { + document +} + +@skip { spaces | LineComment } + +@skip {} { + UnquotedString { unquotedString } + + QuotedString { + '"' (stringContent)* '"' + } +} + +document { + DiagramName newlines? | + DiagramName newlines ((subDiagram | RelationshipLine ) newlines?)+ +} + +subDiagram { + SubDiagramType subDiagramName "{" newlines "}" | + SubDiagramType subDiagramName "{" newlines subDiagramLine+ "}" +} + +subDiagramName { + UnquotedString | QuotedString +} + +subDiagramLine { + ( + idLine | + textLine | + riskLine | + verifyMethodLine | + typeLine | + docRefLine + ) newlines? +} + +idLine { + ID ":" IDNumber +} + +textLine { + Text ":" textContent +} + +riskLine { + Risk ":" RiskType +} + +verifyMethodLine { + VerifyMethod ":" VerifyMethodType +} + +typeLine { + Type ":" textContent +} + +docRefLine { + DocRef ":" textContent +} + +textContent { + UnquotedString | QuotedString +} + +RelationshipLine { + relationshipStart Hyphen RelationshipType ForwardArrow relationshipEnd | + relationshipStart BackArrow RelationshipType Hyphen relationshipEnd +} + +relationshipStart { + RelationshipStart | QuotedString +} + +relationshipEnd { + UnquotedString | QuotedString +} + +DiagramName { diagramKw<"requirementDiagram"> } + +SubDiagramType { + diagramKw<"requirement"> | diagramKw<"Requirement"> | + diagramKw<"functionalRequirement"> | diagramKw<"FunctionalRequirement"> | + diagramKw<"performanceRequirement"> | diagramKw<"PerformanceRequirement"> | + diagramKw<"interfaceRequirement"> | diagramKw<"InterfaceRequirement"> | + diagramKw<"physicalRequirement"> | diagramKw<"PhysicalRequirement"> | + diagramKw<"designConstraint"> | diagramKw<"DesignConstraint"> | + diagramKw<"element"> | diagramKw<"Element"> +} + +ID { propKw<"id"> | propKw<"Id"> | propKw<"ID"> } + +Text { propKw<"text"> | propKw<"Text"> } + +Risk { propKw<"risk"> | propKw<"Risk"> } + +VerifyMethod { propKw<"verifymethod"> | propKw<"verifyMethod"> | propKw<"VerifyMethod"> } + +Type { propKw<"type"> | propKw<"Type"> } + +DocRef { propKw<"docRef"> | propKw<"DocRef"> } + +RiskType { + kw<"low"> | kw<"Low"> | + kw<"medium"> | kw<"Medium"> | + kw<"high"> | kw<"High"> +} + +VerifyMethodType { + kw<"analysis"> | kw<"Analysis"> | + kw<"demonstration"> | kw<"Demonstration"> | + kw<"inspection"> | kw<"Inspection"> | + kw<"test"> | kw<"Test"> +} + +RelationshipType { + kw<"contains"> | kw<"Contains"> | + kw<"copies"> | kw<"Copies"> | + kw<"derives"> | kw<"Derives"> | + kw<"satisfies"> | kw<"Satisfies"> | + kw<"verifies"> | kw<"Verifies"> | + kw<"refines"> | kw<"Refines"> | + kw<"traces"> | kw<"Traces"> +} + +diagramKw { @specialize } +propKw { @specialize[@name=PropKeyword] } +kw { @specialize[@name=Keyword] } + +@external tokens relationshipStart from "./tokens" { RelationshipStart } + +@tokens { + word { @asciiLetter+ } + spaces { @whitespace+ } + newlines { $[\n]+ } + LineComment { "%%" ![\n]* } + unquotedString { word ![\r\n\{\<\>\-\=]* } + IDNumber { @digit+ ("." @digit+)* } + stringContent { !["]+ } + ForwardArrow { "->"} + BackArrow { "<-"} + Hyphen { "-" } + + @precedence { newlines, spaces } + @precedence { ForwardArrow, Hyphen } + @precedence { BackArrow, Hyphen } +} + +@external propSource requirementHighlighting from "./highlight" diff --git a/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.grammar.terms.d.ts b/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.grammar.terms.d.ts new file mode 100644 index 0000000..c2c4b50 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.grammar.terms.d.ts @@ -0,0 +1 @@ +export declare const RelationshipStart: number; diff --git a/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.grammar.terms.ts b/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.grammar.terms.ts new file mode 100644 index 0000000..509c4cd --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.grammar.terms.ts @@ -0,0 +1,25 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +export const + RelationshipStart = 1, + LineComment = 2, + RequirementDiagram = 3, + DiagramName = 4, + SubDiagramType = 5, + UnquotedString = 6, + QuotedString = 7, + ID = 8, + PropKeyword = 44, + IDNumber = 12, + Text = 13, + Risk = 16, + RiskType = 19, + Keyword = 61, + VerifyMethod = 26, + VerifyMethodType = 30, + Type = 39, + DocRef = 42, + RelationshipLine = 45, + Hyphen = 46, + RelationshipType = 47, + ForwardArrow = 62, + BackArrow = 63 diff --git a/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.grammar.test.ts b/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.grammar.test.ts new file mode 100644 index 0000000..e4f7b7c --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.grammar.test.ts @@ -0,0 +1,330 @@ +import { describe, it, expect } from 'vitest'; +import { parser } from './requirement.parser.grammar'; + +/** + * Requirement Diagram Grammar 测试 + * + * 测试目标:验证标准的 Mermaid Requirement Diagram 语法是否能正确解析,不应该出现错误节点(⚠) + */ +describe('Requirement Diagram Grammar 解析测试', () => { + + /** + * 辅助函数:解析代码并返回语法树 + */ + function parseCode(code: string) { + const tree = parser.parse(code); + return tree; + } + + /** + * 辅助函数:检查语法树中是否有错误节点 + */ + function hasErrorNodes(tree: any): { hasError: boolean; errors: Array<{ name: string; from: number; to: number; text: string }> } { + const errors: Array<{ name: string; from: number; to: number; text: string }> = []; + + tree.iterate({ + enter: (node: any) => { + if (node.name === '⚠') { + errors.push({ + name: node.name, + from: node.from, + to: node.to, + text: tree.toString().substring(node.from, node.to) + }); + } + } + }); + + return { + hasError: errors.length > 0, + errors + }; + } + + /** + * 辅助函数:打印语法树结构(用于调试) + */ + function printTree(tree: any, code: string, maxDepth = 5) { + const lines: string[] = []; + + tree.iterate({ + enter: (node: any) => { + const depth = getNodeDepth(tree, node); + if (depth > maxDepth) return false; // 限制深度 + + const indent = ' '.repeat(depth); + const text = code.substring(node.from, Math.min(node.to, node.from + 30)); + const displayText = text.length === 30 ? text + '...' : text; + + lines.push(`${indent}${node.name} [${node.from}-${node.to}]: "${displayText.replace(/\n/g, '\\n')}"`); + } + }); + + return lines.join('\n'); + } + + /** + * 获取节点深度 + */ + function getNodeDepth(tree: any, targetNode: any): number { + let depth = 0; + let current = targetNode; + while (current.parent) { + depth++; + current = current.parent; + } + return depth; + } + + it('应该正确解析基础的 requirementDiagram 声明', () => { + const code = `requirementDiagram +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析空的需求定义', () => { + const code = `requirementDiagram + + requirement test_req { + } +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带 ID 的需求', () => { + const code = `requirementDiagram + + requirement test_req { + id: 1 + } +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带文本的需求', () => { + const code = `requirementDiagram + + requirement test_req { + id: 1 + text: the test text + } +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带风险级别的需求', () => { + const code = `requirementDiagram + + requirement test_req { + id: 1 + text: the test text + risk: high + } +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带验证方法的需求', () => { + const code = `requirementDiagram + + requirement test_req { + id: 1 + text: the test text + risk: high + verifymethod: test + } +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析不同类型的需求(functionalRequirement)', () => { + const code = `requirementDiagram + + functionalRequirement test_req { + id: 1.1 + text: the test text + } +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析元素定义', () => { + const code = `requirementDiagram + + element test_entity { + type: simulation + } +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析关系(contains)', () => { + const code = `requirementDiagram + + requirement test_req { + id: 1 + } + + element test_entity { + type: simulation + } + + test_entity - contains -> test_req +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析反向关系', () => { + const code = `requirementDiagram + + requirement test_req { + id: 1 + } + + element test_entity { + type: simulation + } + + test_req <- satisfies - test_entity +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析完整的需求图示例', () => { + const code = `requirementDiagram + + requirement test_req { + id: 1 + text: the test text. + risk: high + verifymethod: test + } + + functionalRequirement test_req2 { + id: 1.1 + text: the second test text. + risk: low + verifymethod: inspection + } + + element test_entity { + type: simulation + } + + test_entity - satisfies -> test_req2 + test_req - traces -> test_req2 +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); +}); + diff --git a/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.parser.grammar.d.ts b/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.parser.grammar.d.ts new file mode 100644 index 0000000..1d78640 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.parser.grammar.d.ts @@ -0,0 +1,3 @@ +import { LRParser } from '@lezer/lr'; + +export declare const parser: LRParser; diff --git a/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.parser.grammar.ts b/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.parser.grammar.ts new file mode 100644 index 0000000..88d3d76 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/requirement/requirement.parser.grammar.ts @@ -0,0 +1,21 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +import {LRParser} from "@lezer/lr" +import {relationshipStart} from "./tokens" +import {requirementHighlighting} from "./highlight" +const spec_word = {__proto__:null,requirementDiagram:144, requirement:150, Requirement:152, functionalRequirement:154, FunctionalRequirement:156, performanceRequirement:158, PerformanceRequirement:160, interfaceRequirement:162, InterfaceRequirement:164, physicalRequirement:166, PhysicalRequirement:168, designConstraint:170, DesignConstraint:172, element:174, Element:176, id:18, Id:20, ID:22, text:28, Text:30, risk:34, Risk:36, low:40, Low:42, medium:44, Medium:46, high:48, High:50, verifymethod:54, verifyMethod:56, VerifyMethod:58, analysis:62, Analysis:64, demonstration:66, Demonstration:68, inspection:70, Inspection:72, test:74, Test:76, type:80, Type:82, docRef:86, DocRef:88, contains:96, Contains:98, copies:100, Copies:102, derives:104, Derives:106, satisfies:108, Satisfies:110, verifies:112, Verifies:114, refines:116, Refines:118, traces:120, Traces:122} +export const parser = LRParser.deserialize({ + version: 14, + states: ")`OYQQOOO_QQO'#DtQOQQOOOOQO'#C`'#C`O!kQRO,5:`O!rOSO'#CcOOQO'#Ef'#EfO!zQQO'#DZO#SQRO'#DnO$^QRO1G/zOOQO'#Ca'#CaO$eQWO'#DxOOOO'#Do'#DoO$mOSO,58}OOQP,58},58}O$uQQO,59uO$uQQO,59uOOQP,5:Y,5:YOOQP-E7l-E7lOOQP'#Cb'#CbOOQP'#Eg'#EgO%sQQO,5:dOOOO-E7m-E7mOOQP1G.i1G.iO%xQQO1G/aOOQO'#D]'#D]O%}QQO1G/aO&SQQO1G0OO$eQWO7+${O'VQQO7+%jOOQP<pAN>pO$eQWO,5:{O$eQWO,5;OO$eQWO,5;POOQO1G0e1G0eOOQO1G0h1G0hOOQO'#Co'#CoOOQO1G0i1G0iOOQO'#Cz'#CzOOQO1G0g1G0gOOQO1G0j1G0jOOQO1G0k1G0k", + stateData: "*e~O!gOSQOS~O!jRO~O!kSO!e!hX~OPUO!mYO!nYO!oYO!pYO!qYO!rYO!sYO!tYO!uYO!vYO!wYO!xYO!yYO!zYO!|TO~O!e!ha~PgO!|^O!}[O~O!O_O!a`O~O!kaOP!bX!e!bX!m!bX!n!bX!o!bX!p!bX!q!bX!r!bX!s!bX!t!bX!u!bX!v!bX!w!bX!x!bX!y!bX!z!bX!|!bX~O!e!hi~PgO!{cO!|TO~O!|gO!}[O~O!QiO!RiO!SiO!TiO!UiO!ViO!WiO!XiO!YiO!ZiO![iO!]iO!^iO!_iO~O#OkO~O!`lO~O!OlO~O!kmO~OXuOYuOZuO^vO_vOawObwOkxOlxOmxOxyOyyO{zO|zO~O#P!OO~P&XO#S!PO~O#S!QO~O#S!RO~O!k!SOX#QXY#QXZ#QX^#QX_#QXa#QXb#QXk#QXl#QXm#QXx#QXy#QX{#QX|#QX#P#QX~O#P!UO~P&XO#S!VO~O#S!WO~O#S!XO~O[!YO~Od![Oe![Of![Og![Oh![Oi![O~Oo!^Op!^Oq!^Or!^Os!^Ot!^Ou!^Ov!^O~O!k!a!g!`!O!`~", + goto: "%r#[PPPP#]#`#d#k#vPPPP#zPP$OPP$SPPPPPP$VPPP$ZPPPPPPPP$^PP$bPP$fP$jPPPPPPPPPPPPPPPP$p$v$|PPP%SPPP$fPPPPPPPPPPPPPPPPPPP%V%ZP%Z%Z%Z%Z%Z%_%cRPOTZSXZdZl!V!W!XSUSXZdZl!V!W!XTomtT{mtTpmtR!Z!QTqmtR!]!RT|mtT}mtTWSXQh_Rj`QXSRbXQ]TRf]QtmR!TtRQOTsmtTrmtTVSXQeZQnlQ!_!VQ!`!WR!a!X", + nodeNames: "⚠ RelationshipStart LineComment RequirementDiagram DiagramName SubDiagramType UnquotedString QuotedString ID PropKeyword PropKeyword PropKeyword IDNumber Text PropKeyword PropKeyword Risk PropKeyword PropKeyword RiskType Keyword Keyword Keyword Keyword Keyword Keyword VerifyMethod PropKeyword PropKeyword PropKeyword VerifyMethodType Keyword Keyword Keyword Keyword Keyword Keyword Keyword Keyword Type PropKeyword PropKeyword DocRef PropKeyword PropKeyword RelationshipLine Hyphen RelationshipType Keyword Keyword Keyword Keyword Keyword Keyword Keyword Keyword Keyword Keyword Keyword Keyword Keyword Keyword ForwardArrow BackArrow", + maxTerm: 103, + propSources: [requirementHighlighting], + skippedNodes: [0,2], + repeatNodeCount: 3, + tokenData: "1g~R{OX#xXY$aYZ&SZ^$a^p#xpq$aqr#xrs'}su#xuv(Sv}#x}!O)v!O!Q#x!Q![*t![!]+|!]!^#x!^!_,a!_!c#x!c!}-]!}#T#x#T#o-]#o#p0o#p#q#x#q#r1S#r#y#x#y#z$a#z$f#x$f$g$a$g#BY#x#BY#BZ$a#BZ$IS#x$IS$I_$a$I_$I|#x$I|$JO$a$JO$JT#x$JT$JU$a$JU$KV#x$KV$KW$a$KW&FU#x&FU&FV$a&FV;'S#x;'S;=`$Z<%lO#xQ#}S!}QOr#xs;'S#x;'S;=`$Z<%lO#xQ$^P;=`<%l#xV$hh!}Q!gTOX#xX^$a^p#xpq$aqr#xs#y#x#y#z$a#z$f#x$f$g$a$g#BY#x#BY#BZ$a#BZ$IS#x$IS$I_$a$I_$I|#x$I|$JO$a$JO$JT#x$JT$JU$a$JU$KV#x$KV$KW$a$KW&FU#x&FU&FV$a&FV;'S#x;'S;=`$Z<%lO#xV&]j!}Q!kP!gTOX#xXY$aYZ&SZ^$a^p#xpq$aqr#xs#y#x#y#z$a#z$f#x$f$g$a$g#BY#x#BY#BZ$a#BZ$IS#x$IS$I_$a$I_$I|#x$I|$JO$a$JO$JT#x$JT$JU$a$JU$KV#x$KV$KW$a$KW&FU#x&FU&FV$a&FV;'S#x;'S;=`$Z<%lO#x~(SO!|~V(XU!}QOr#xsu#xuv(kv;'S#x;'S;=`$Z<%lO#xV(rVQT!}QOY(kYZ#xZr(krs)Xs;'S(k;'S;=`)p<%lO(kT)^SQTOY)XZ;'S)X;'S;=`)j<%lO)XT)mP;=`<%l)XV)sP;=`<%l(kR)}U!}Q!OPOr#xs!`#x!`!a*a!a;'S#x;'S;=`$Z<%lO#xR*hS!}Q!`POr#xs;'S#x;'S;=`$Z<%lO#xR*{W[P!}QOr#xs!O#x!O!P+e!P!Q#x!Q![*t![;'S#x;'S;=`$Z<%lO#xR+jU!}QOr#xs!Q#x!Q![*t![;'S#x;'S;=`$Z<%lO#xR,TS#SP!}QOr#xs;'S#x;'S;=`$Z<%lO#xR,fU!}QOr#xs}#x}!O,x!O;'S#x;'S;=`$Z<%lO#xR-PS!}Q!aPOr#xs;'S#x;'S;=`$Z<%lO#xV-fb!}Q!{S!iPOY.nYZ#xZ].n]^#x^r.nrs/ts}.n}!O#x!O!^.n!^!a#x!a!c.n!c!}-]!}#T.n#T#o-]#o#p#x#p;'S.n;'S;=`0i<%lO.nU.u_!}Q!{SOY.nYZ#xZ].n]^#x^r.nrs/ts}.n}!O#x!O!^.n!^!a#x!a#o.n#o#p#x#p;'S.n;'S;=`0i<%lO.nS/yW!{SOY/tZ]/t^}/t!O!^/t!a#o/t#p;'S/t;'S;=`0c<%lO/tS0fP;=`<%l/tU0lP;=`<%l.nR0vS#OP!}QOr#xs;'S#x;'S;=`$Z<%lO#xR1ZS#PP!}QOr#xs;'S#x;'S;=`$Z<%lO#x", + tokenizers: [relationshipStart, 0, 1, 2], + topRules: {"RequirementDiagram":[0,3]}, + specialized: [{term: 71, get: (value: keyof typeof spec_word) => spec_word[value] || -1}], + tokenPrec: 428 +}) diff --git a/frontend/src/views/editor/language/mermaid/parsers/requirement/tokens.ts b/frontend/src/views/editor/language/mermaid/parsers/requirement/tokens.ts new file mode 100644 index 0000000..bbdbb23 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/requirement/tokens.ts @@ -0,0 +1,24 @@ +import { ExternalTokenizer } from '@lezer/lr'; +import { RelationshipStart } from './requirement.grammar.terms'; + +const notAllowedCodePoints = [-1, 45, 60, 62, 10, 13, 123, 61]; + +export const relationshipStart = new ExternalTokenizer((input) => { + if (notAllowedCodePoints.includes(input.next) || input.next === 32) return; + + let peek; + let tokens = ''; + let count = 0; + + do { + peek = input.peek(count); + if (peek === -1) return; + tokens += String.fromCodePoint(peek); + count++; + } while (!notAllowedCodePoints.includes(peek)); + + if (peek === 45 || peek === 60) { + tokens = tokens.slice(0, -1).trim(); + input.acceptToken(RelationshipStart, tokens.length); + } +}); diff --git a/frontend/src/views/editor/language/mermaid/parsers/sequence/highlight.ts b/frontend/src/views/editor/language/mermaid/parsers/sequence/highlight.ts new file mode 100644 index 0000000..7ac16f5 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/sequence/highlight.ts @@ -0,0 +1,14 @@ +import { styleTags } from '@lezer/highlight'; +import { sequenceTags } from '../../tags'; + +export const sequenceHighlighting = styleTags({ + DiagramName: sequenceTags.diagramName, + NodeText: sequenceTags.nodeText, + Keyword1: sequenceTags.keyword1, + Keyword2: sequenceTags.keyword2, + LineComment: sequenceTags.lineComment, + 'Arrow ArrowSuffix': sequenceTags.arrow, + Position: sequenceTags.position, + MessageText1: sequenceTags.messageText1, + MessageText2: sequenceTags.messageText2, +}); diff --git a/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.grammar b/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.grammar new file mode 100644 index 0000000..b575057 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.grammar @@ -0,0 +1,114 @@ +@top SequenceDiagram { + document +} + +@skip { spaces } + +document { + DiagramName newlines? | + DiagramName newlines subDocument newlines? | + DiagramName newlines subDocument (newlines subDocument)+ newlines? +} + +subDocument { + LineComment | + NodeText Arrow ArrowSuffix? newlines? NodeText ":" MessageText1 | + (Create | Destroy)? (Participant | Actor)? NodeText (As NodeText)? | + (Activate | Deactivate) NodeText | + Note Position NodeText ("," NodeText)? ":" MessageText1 | + Keyword MessageText2 | + Autonumber | + End | + Link NodeText ":" MessageText1 +} + +MessageText1 { + messageText +} + +MessageText2 { + messageText +} + +ArrowSuffix { + "+" | "-" +} + +Link[group=Keyword1] { + link | links +} + +Keyword[group=Keyword1] { + alt | + and | + box | + break | + critical | + else | + loop | + opt | + option | + par | + rect +} + +DiagramName { kw<"sequenceDiagram"> } + +kw { @specialize } + +@external tokens messageTextToken from "./tokens" { messageText } +@external tokens textTokens from "./tokens" { + Activate[group=Keyword1], + Autonumber[group=Keyword1], + Create[group=Keyword1], + Deactivate[group=Keyword1], + Destroy[group=Keyword1], + End[group=Keyword1], + Note[group=Keyword1], + Actor[group=Keyword2], + As[group=Keyword2], + Participant[group=Keyword2], + NodeText, + Position, + alt, + and, + box, + break, + critical, + else, + link, + links + loop, + opt, + option, + par, + rect +} + +@tokens { + spaces { @whitespace+ } + newlines { $[\n]+ } + LineComment { "%%" ![\n]* } + identifierChar { @asciiLetter | $[$\u{a1}-\u{10ffff}] } + word { identifierChar (identifierChar | @digit)* } + identifier { word } + Arrow { + "->" | + "-->" | + "->>" | + "-->>" | + "-x" | + "--x" | + "-)" | + "--)" + } + + @precedence { + newlines, + spaces, + Arrow, + identifier + } +} + +@external propSource sequenceHighlighting from "./highlight" diff --git a/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.grammar.terms.d.ts b/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.grammar.terms.d.ts new file mode 100644 index 0000000..0b9306b --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.grammar.terms.d.ts @@ -0,0 +1,26 @@ +export declare const _break: number; +export declare const _else: number; +export declare const Activate: number; +export declare const Actor: number; +export declare const alt: number; +export declare const and: number; +export declare const As: number; +export declare const Autonumber: number; +export declare const box: number; +export declare const Create: number; +export declare const critical: number; +export declare const Deactivate: number; +export declare const Destroy: number; +export declare const End: number; +export declare const link: number; +export declare const links: number; +export declare const loop: number; +export declare const messageText: number; +export declare const NodeText: number; +export declare const Note: number; +export declare const opt: number; +export declare const option: number; +export declare const par: number; +export declare const Participant: number; +export declare const Position: number; +export declare const rect: number; diff --git a/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.grammar.terms.ts b/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.grammar.terms.ts new file mode 100644 index 0000000..97e6326 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.grammar.terms.ts @@ -0,0 +1,37 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +export const + messageText = 24, + Activate = 1, + Autonumber = 2, + Create = 3, + Deactivate = 4, + Destroy = 5, + End = 6, + Note = 7, + Actor = 8, + As = 9, + Participant = 10, + NodeText = 11, + Position = 12, + alt = 25, + and = 26, + box = 27, + _break = 28, + critical = 29, + _else = 30, + link = 31, + links = 32, + loop = 33, + opt = 34, + option = 35, + par = 36, + rect = 37, + SequenceDiagram = 13, + DiagramName = 14, + LineComment = 15, + Arrow = 16, + ArrowSuffix = 17, + MessageText1 = 18, + Keyword = 19, + MessageText2 = 20, + Link = 21 diff --git a/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.grammar.test.ts b/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.grammar.test.ts new file mode 100644 index 0000000..01f8e21 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.grammar.test.ts @@ -0,0 +1,270 @@ +import { describe, it, expect } from 'vitest'; +import { parser } from './sequence.parser.grammar'; + +/** + * Sequence Diagram Grammar 测试 + * + * 测试目标:验证标准的 Mermaid Sequence Diagram 语法是否能正确解析,不应该出现错误节点(⚠) + */ +describe('Sequence Diagram Grammar 解析测试', () => { + + /** + * 辅助函数:解析代码并返回语法树 + */ + function parseCode(code: string) { + const tree = parser.parse(code); + return tree; + } + + /** + * 辅助函数:检查语法树中是否有错误节点 + */ + function hasErrorNodes(tree: any): { hasError: boolean; errors: Array<{ name: string; from: number; to: number; text: string }> } { + const errors: Array<{ name: string; from: number; to: number; text: string }> = []; + + tree.iterate({ + enter: (node: any) => { + if (node.name === '⚠') { + errors.push({ + name: node.name, + from: node.from, + to: node.to, + text: tree.toString().substring(node.from, node.to) + }); + } + } + }); + + return { + hasError: errors.length > 0, + errors + }; + } + + /** + * 辅助函数:打印语法树结构(用于调试) + */ + function printTree(tree: any, code: string, maxDepth = 5) { + const lines: string[] = []; + + tree.iterate({ + enter: (node: any) => { + const depth = getNodeDepth(tree, node); + if (depth > maxDepth) return false; // 限制深度 + + const indent = ' '.repeat(depth); + const text = code.substring(node.from, Math.min(node.to, node.from + 30)); + const displayText = text.length === 30 ? text + '...' : text; + + lines.push(`${indent}${node.name} [${node.from}-${node.to}]: "${displayText.replace(/\n/g, '\\n')}"`); + } + }); + + return lines.join('\n'); + } + + /** + * 获取节点深度 + */ + function getNodeDepth(tree: any, targetNode: any): number { + let depth = 0; + let current = targetNode; + while (current.parent) { + depth++; + current = current.parent; + } + return depth; + } + + it('应该正确解析基础的 sequenceDiagram 声明', () => { + const code = `sequenceDiagram +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带 participant 的序列图', () => { + const code = `sequenceDiagram + participant Alice + participant Bob +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析简单的消息传递', () => { + const code = `sequenceDiagram + Alice->John: Hello John, how are you? +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带虚线箭头的消息', () => { + const code = `sequenceDiagram + Alice-->John: Hello +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带异步箭头的消息', () => { + const code = `sequenceDiagram + Alice->>John: Hello +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带激活/停用的序列图', () => { + const code = `sequenceDiagram + Alice->>John: Hello + activate John + John-->>Alice: Hi there! + deactivate John +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带 note 的序列图', () => { + const code = `sequenceDiagram + Alice->John: Hello John + Note over Alice,John: A typical interaction +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带 alt/else 的序列图', () => { + const code = `sequenceDiagram + Alice->John: Hello John + alt is sick + John-->Alice: Not so good + else is well + John-->Alice: Feeling fresh + end +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析带 loop 的序列图', () => { + const code = `sequenceDiagram + Alice->John: Hello John + loop Every minute + John-->Alice: Great! + end +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); + + it('应该正确解析完整的序列图示例', () => { + const code = `sequenceDiagram + participant Alice + participant Bob + Alice->>John: Hello John, how are you? + loop HealthCheck + John->>John: Fight against hypochondria + end + Note right of John: Rational thoughts! + John-->>Alice: Great! + John->>Bob: How about you? + Bob-->>John: Jolly good! +`; + + const tree = parseCode(code); + const result = hasErrorNodes(tree); + + if (result.hasError) { + console.log('语法树:'); + console.log(printTree(tree, code)); + console.log('错误节点:', result.errors); + } + + expect(result.hasError).toBe(false); + }); +}); + diff --git a/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.parser.grammar.d.ts b/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.parser.grammar.d.ts new file mode 100644 index 0000000..1d78640 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.parser.grammar.d.ts @@ -0,0 +1,3 @@ +import { LRParser } from '@lezer/lr'; + +export declare const parser: LRParser; diff --git a/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.parser.grammar.ts b/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.parser.grammar.ts new file mode 100644 index 0000000..33cc33d --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/sequence/sequence.parser.grammar.ts @@ -0,0 +1,24 @@ +// This file was generated by lezer-generator. You probably shouldn't edit it. +import {LRParser} from "@lezer/lr" +import {messageTextToken, textTokens} from "./tokens" +import {sequenceHighlighting} from "./highlight" +const spec_identifier = {__proto__:null,sequenceDiagram:84} +export const parser = LRParser.deserialize({ + version: 14, + states: "'nOVQSOOO[QSO'#DUQOQSOOOOQO'#Cj'#CjO#QQUO,59pOOQP'#Co'#CoOOQQ'#Cq'#CqOOQO'#DY'#DYO#XQUO'#DYO#gQUO'#DYO#lQUO'#DYO#wQUO'#DYO#|QUO'#DYO$RQTO'#DYO$WQUO'#DYO$]QSO1G/[O$eQYO,59tO$sQUO,59tO$xQUO,59tO%TQUO,59tOOQO,59t,59tO%YQUO,59tOOQO'#Cp'#CpO%_QSO,59tO%dQUO7+$vO%kQSO7+$vOOQQ'#Cm'#CmO%sQSO1G/`O%xQUO1G/`O%}QUO1G/`OOQO1G/`1G/`O&VQUO1G/`O&[QUO1G/`O&gQSO1G/`O&oQTO1G/`OOQO,59^,59^O&tQUO<QAN>QO&oQTOAN>QOOQOG23lG23l", + stateData: "'g~OwOS~OzRO~O{SOgxX~OPZOQVORYOSZOTYOUVOV[OWXOYXOZWO_VOiTOjTOkTOlTOmTOnTOoUOpUOqTOrTOsTOtTOuTO~Ogxa~PdOXaO``Og|X{|X~OZbO~OWcOYcOZbO~OZdO~O[eO~OhfO~OZgO~O{hOgxi~OZkO{lO}jO!OjO~OZnO~OXoOg|a{|a~OZpO~OZqO~O!PrO~Ogxq~PdO{tOgxq~O!PvO~OZwO~OZwO{xO~OZyO~OXzOg|i{|i~O!PvO!QxO~Oh{O~Ogxy~PdO!P!OO~OZ!PO~OZ}O~O!P!RO~O{w`y`~", + goto: "#S}PPPPPPPPPPPPPP!OPP!R!U!b!h!k!qPPPPPPPPPPPPPPPPP!wPPP!zRPORm`QyrQ}vQ!Q!OR!S!RX]Sht|Rd]X^Sht|Qi_RuiRQOQ_SVsht|", + nodeNames: "⚠ Activate Autonumber Create Deactivate Destroy End Note Actor As Participant NodeText Position SequenceDiagram DiagramName LineComment Arrow ArrowSuffix MessageText1 Keyword MessageText2 Link", + maxTerm: 48, + nodeProps: [ + ["group", -9,1,2,3,4,5,6,7,19,21,"Keyword1",-3,8,9,10,"Keyword2"] + ], + propSources: [sequenceHighlighting], + skippedNodes: [0], + repeatNodeCount: 1, + tokenData: "(x~RmXY!|YZ#qZ^!|pq!|tu$nuv%`{|%}|}&S}!O&X![!]'T!c!}$n#T#o$n#y#z!|$f$g!|$g#BY$n#BY#BZ'Y#BZ$IS$n$IS$I_'Y$I_$I|$n$I|$JO'Y$JO$JT$n$JT$JU'Y$JU$KV$n$KV$KW'Y$KW&FU$n&FU&FV'Y&FV;'S$n;'S;=`%Y<%lO$n~#RYw~X^!|pq!|#y#z!|$f$g!|#BY#BZ!|$IS$I_!|$I|$JO!|$JT$JU!|$KV$KW!|&FU&FV!|~#x[{~w~XY!|YZ#qZ^!|pq!|#y#z!|$f$g!|#BY#BZ!|$IS$I_!|$I|$JO!|$JT$JU!|$KV$KW!|&FU&FV!|~$sVy~tu$n!Q![$n!c!}$n#T#o$n$g;'S$n;'S;=`%Y<%lO$n~%]P;=`<%l$n~%cPuv%f~%kS_~OY%fZ;'S%f;'S;=`%w<%lO%f~%zP;=`<%l%f~&SO}~~&XO!Q~R&^S!OQyz&j}!O&o!`!a&{#l#m&jP&oO`PP&rRyz&j!`!a&{#l#m&jP'QP`P!`!a&j~'YO!P~~'agw~y~X^!|pq!|tu$n!Q![$n!c!}$n#T#o$n#y#z!|$f$g!|$g#BY$n#BY#BZ'Y#BZ$IS$n$IS$I_'Y$I_$I|$n$I|$JO'Y$JO$JT$n$JT$JU'Y$JU$KV$n$KV$KW'Y$KW&FU$n&FU&FV'Y&FV;'S$n;'S;=`%Y<%lO$n", + tokenizers: [messageTextToken, textTokens, 0, 1], + topRules: {"SequenceDiagram":[0,13]}, + specialized: [{term: 41, get: (value: keyof typeof spec_identifier) => spec_identifier[value] || -1}], + tokenPrec: 293 +}) diff --git a/frontend/src/views/editor/language/mermaid/parsers/sequence/tokens.ts b/frontend/src/views/editor/language/mermaid/parsers/sequence/tokens.ts new file mode 100644 index 0000000..4b65789 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/parsers/sequence/tokens.ts @@ -0,0 +1,129 @@ +import { ExternalTokenizer } from '@lezer/lr'; +import { + _break, + _else, + Activate, + Actor, + alt, + and, + As, + Autonumber, + box, + Create, + critical, + Deactivate, + Destroy, + End, + link, + links, + loop, + messageText, + NodeText, + Note, + opt, + option, + par, + Participant, + Position, + rect, +} from './sequence.grammar.terms'; + +const skipCodePoints = [-1, 9, 10, 13, 32, 37]; +const arrowSuffixCodePoints = [43, 45]; +const notAllowedCodePoints = [44, 58, 62]; +const notAllowed2Chars = ['->', '-x', '-)', ' -', ' ']; +const notAllowed3Chars = ['-->', '->>', '--x', '--)', ' as']; + +const keywordMap: { [key: string]: number } = { + 'left of': Position, + 'right of': Position, + activate: Activate, + actor: Actor, + alt: alt, + and: and, + as: As, + autonumber: Autonumber, + box: box, + break: _break, + create: Create, + critical: critical, + deactivate: Deactivate, + destroy: Destroy, + else: _else, + end: End, + link: link, + links: links, + loop: loop, + note: Note, + opt: opt, + option: option, + over: Position, + par: par, + participant: Participant, + rect: rect, +}; + +const keywords = Object.keys(keywordMap); + +export const messageTextToken = new ExternalTokenizer((input) => { + if (skipCodePoints.includes(input.next)) return; + + while (input.next !== 10 && input.next !== -1) { + input.advance(); + } + + input.acceptToken(messageText); +}); + +export const textTokens = new ExternalTokenizer((input) => { + if ( + skipCodePoints.includes(input.next) || + arrowSuffixCodePoints.includes(input.next) + ) + return; + + const isArrowNext = () => { + if (input.peek(0) === -1 || input.peek(1) === -1 || input.peek(2) === -1) + return false; + + let result = + String.fromCodePoint(input.peek(0)) + String.fromCodePoint(input.peek(1)); + + if (notAllowed2Chars.includes(result)) return true; + + result += String.fromCodePoint(input.peek(2)); + + if (notAllowed3Chars.includes(result)) return true; + + return false; + }; + + let tokens = ''; + + while ( + !notAllowedCodePoints.includes(input.next) && + !isArrowNext() && + input.next !== 10 && + input.next !== -1 + ) { + tokens += String.fromCodePoint(input.next); + input.advance(); + } + + const activeKeyword = keywords.filter((keyword) => { + if (keyword === tokens) { + return tokens.toLowerCase().startsWith(keyword); + } + return tokens.toLowerCase().startsWith(keyword + ' '); + }); + + if (activeKeyword.length > 0) { + input.acceptToken( + keywordMap[activeKeyword[0]], + activeKeyword[0].length - tokens.length + ); + return; + } + + input.acceptToken(NodeText); +}); diff --git a/frontend/src/views/editor/language/mermaid/tags/index.ts b/frontend/src/views/editor/language/mermaid/tags/index.ts new file mode 100644 index 0000000..f83140a --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/tags/index.ts @@ -0,0 +1,76 @@ +import { Tag, tags as t } from '@lezer/highlight'; + +export const mermaidTags = { + diagramName: Tag.define(t.typeName), +}; + +export const mindmapTags = { + diagramName: Tag.define(mermaidTags.diagramName), + lineText1: Tag.define(), + lineText2: Tag.define(), + lineText3: Tag.define(), + lineText4: Tag.define(), + lineText5: Tag.define(), +}; + +export const pieTags = { + diagramName: Tag.define(mermaidTags.diagramName), + lineComment: Tag.define(t.lineComment), + number: Tag.define(t.number), + showData: Tag.define(t.keyword), + string: Tag.define(t.string), + title: Tag.define(t.keyword), + titleText: Tag.define(t.string), +}; + +export const flowchartTags = { + diagramName: Tag.define(mermaidTags.diagramName), + keyword: Tag.define(t.keyword), + lineComment: Tag.define(t.lineComment), + link: Tag.define(t.contentSeparator), + nodeEdge: Tag.define(t.contentSeparator), + nodeEdgeText: Tag.define(t.string), + nodeId: Tag.define(t.variableName), + nodeText: Tag.define(t.string), + number: Tag.define(t.number), + orientation: Tag.define(t.modifier), + string: Tag.define(t.string), +}; + +export const sequenceTags = { + diagramName: Tag.define(mermaidTags.diagramName), + arrow: Tag.define(t.contentSeparator), + keyword1: Tag.define(t.keyword), + keyword2: Tag.define(t.controlKeyword), + lineComment: Tag.define(t.lineComment), + messageText1: Tag.define(t.string), + messageText2: Tag.define(t.content), + nodeText: Tag.define(t.variableName), + position: Tag.define(t.modifier), +}; + +export const journeyTags = { + diagramName: Tag.define(mermaidTags.diagramName), + actor: Tag.define(t.variableName), + keyword: Tag.define(t.keyword), + lineComment: Tag.define(t.lineComment), + score: Tag.define(t.number), + text: Tag.define(t.string), +}; + +export const requirementTags = { + diagramName: Tag.define(mermaidTags.diagramName), + arrow: Tag.define(t.contentSeparator), + keyword: Tag.define(t.keyword), + lineComment: Tag.define(t.lineComment), + number: Tag.define(t.number), + quotedString: Tag.define(t.string), + unquotedString: Tag.define(t.content), +}; + +export const ganttTags = { + diagramName: Tag.define(mermaidTags.diagramName), + keyword: Tag.define(t.keyword), + lineComment: Tag.define(t.lineComment), + string: Tag.define(t.string), +}; diff --git a/frontend/src/views/editor/language/mermaid/types/index.ts b/frontend/src/views/editor/language/mermaid/types/index.ts new file mode 100644 index 0000000..9fe5ba6 --- /dev/null +++ b/frontend/src/views/editor/language/mermaid/types/index.ts @@ -0,0 +1,38 @@ +export enum DiagramType { + Mermaid = 'MermaidDiagram', + Mindmap = 'MindmapDiagram', + Pie = 'PieDiagram', + Flowchart = 'FlowchartDiagram', + Sequence = 'SequenceDiagram', + Journey = 'JourneyDiagram', + Requirement = 'RequirementDiagram', + Gantt = 'GanttDiagram', +} + +export enum MermaidDescriptionName { + Mermaid = 'mermaid', + Mindmap = 'mindmap', + Pie = 'pie', + Flowchart = 'flowchart', + Sequence = 'sequenceDiagram', + Journey = 'journey', + Requirement = 'requirementDiagram', + Gantt = 'gantt', +} + +export enum MermaidLanguageType { + Mermaid = 'mermaid', + Mindmap = 'mindmap', + Pie = 'pie', + Flowchart = 'flowchart', + Sequence = 'sequence', + Journey = 'journey', + Requirement = 'requirement', + Gantt = 'gantt', +} + +export enum MermaidAlias { + Graph = 'graph', + Sequence = 'sequence', + Requirement = 'requirement', +} diff --git a/go.mod b/go.mod index aa8198a..5da59b4 100644 --- a/go.mod +++ b/go.mod @@ -10,11 +10,11 @@ require ( github.com/knadh/koanf/providers/structs v1.0.0 github.com/knadh/koanf/v2 v2.3.0 github.com/stretchr/testify v1.11.1 - github.com/wailsapp/wails/v3 v3.0.0-alpha.36 + github.com/wailsapp/wails/v3 v3.0.0-alpha.38 golang.org/x/net v0.46.0 golang.org/x/sys v0.37.0 golang.org/x/text v0.30.0 - modernc.org/sqlite v1.39.1 + modernc.org/sqlite v1.40.0 resty.dev/v3 v3.0.0-beta.3 ) @@ -29,11 +29,11 @@ require ( github.com/adrg/xdg v0.5.3 // indirect github.com/bep/debounce v1.2.1 // indirect github.com/cloudflare/circl v1.6.1 // indirect - github.com/cyphar/filepath-securejoin v0.5.1 // indirect + github.com/cyphar/filepath-securejoin v0.6.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/davidmz/go-pageant v1.0.2 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/ebitengine/purego v0.9.0 // indirect + github.com/ebitengine/purego v0.9.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect diff --git a/go.sum b/go.sum index c4404fd..66625b5 100644 --- a/go.sum +++ b/go.sum @@ -25,8 +25,8 @@ github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/creativeprojects/go-selfupdate v1.5.1 h1:fuyEGFFfqcC8SxDGolcEPYPLXGQ9Mcrc5uRyRG2Mqnk= github.com/creativeprojects/go-selfupdate v1.5.1/go.mod h1:2uY75rP8z/D/PBuDn6mlBnzu+ysEmwOJfcgF8np0JIM= -github.com/cyphar/filepath-securejoin v0.5.1 h1:eYgfMq5yryL4fbWfkLpFFy2ukSELzaJOTaUTuh+oF48= -github.com/cyphar/filepath-securejoin v0.5.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is= +github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -34,8 +34,8 @@ github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454Wv github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/ebitengine/purego v0.9.0 h1:mh0zpKBIXDceC63hpvPuGLiJ8ZAa3DfrFTudmfi8A4k= -github.com/ebitengine/purego v0.9.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A= +github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -166,8 +166,8 @@ github.com/wailsapp/go-webview2 v1.0.22 h1:YT61F5lj+GGaat5OB96Aa3b4QA+mybD0Ggq6N github.com/wailsapp/go-webview2 v1.0.22/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= -github.com/wailsapp/wails/v3 v3.0.0-alpha.36 h1:GQ8vSrFgafITwMd/p4k+WBjG9K/anma9Pk2eJ/5CLsI= -github.com/wailsapp/wails/v3 v3.0.0-alpha.36/go.mod h1:7i8tSuA74q97zZ5qEJlcVZdnO+IR7LT2KU8UpzYMPsw= +github.com/wailsapp/wails/v3 v3.0.0-alpha.38 h1:pknuf+fecyZtP7hLCWTILttj6xB/VXRiXoy4T/7iorQ= +github.com/wailsapp/wails/v3 v3.0.0-alpha.38/go.mod h1:7i8tSuA74q97zZ5qEJlcVZdnO+IR7LT2KU8UpzYMPsw= github.com/xanzy/go-gitlab v0.115.0 h1:6DmtItNcVe+At/liXSgfE/DZNZrGfalQmBRmOcJjOn8= github.com/xanzy/go-gitlab v0.115.0/go.mod h1:5XCDtM7AM6WMKmfDdOiEpyRWUqui2iS9ILfvCZ2gJ5M= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= @@ -256,8 +256,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.39.1 h1:H+/wGFzuSCIEVCvXYVHX5RQglwhMOvtHSv+VtidL2r4= -modernc.org/sqlite v1.39.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE= +modernc.org/sqlite v1.40.0 h1:bNWEDlYhNPAUdUdBzjAvn8icAs/2gaKlj4vM+tQ6KdQ= +modernc.org/sqlite v1.40.0/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/internal/events/tray_events.go b/internal/events/tray_events.go index 0243c6c..863761c 100644 --- a/internal/events/tray_events.go +++ b/internal/events/tray_events.go @@ -9,7 +9,7 @@ import ( ) // RegisterTrayEvents 注册与系统托盘相关的所有事件 -func RegisterTrayEvents(app *application.App, systray *application.SystemTray, mainWindow *application.WebviewWindow, trayService *services.TrayService) { +func RegisterTrayEvents(systray *application.SystemTray, mainWindow *application.WebviewWindow, trayService *services.TrayService) { // 不附加窗口到系统托盘,避免失去焦点自动缩小 // systray.AttachWindow(mainWindow) @@ -18,7 +18,7 @@ func RegisterTrayEvents(app *application.App, systray *application.SystemTray, m // 设置点击托盘图标显示主窗口 systray.OnClick(func() { - trayService.ShowWindow() + trayService.AutoShowHide() }) // 处理窗口关闭事件 - 根据配置决定是隐藏到托盘还是直接退出 diff --git a/internal/services/tray_service.go b/internal/services/tray_service.go index 0668b27..5cac2a1 100644 --- a/internal/services/tray_service.go +++ b/internal/services/tray_service.go @@ -59,3 +59,8 @@ func (ts *TrayService) ShowWindow() { func (ts *TrayService) MinimizeButtonClicked() { ts.windowHelper.MinimiseMainWindow() } + +// AutoShowHide 自动显示/隐藏主窗口 +func (ts *TrayService) AutoShowHide() { + ts.windowHelper.AutoShowMainWindow() +} diff --git a/internal/services/window_helper.go b/internal/services/window_helper.go index 0d8be14..c119d90 100644 --- a/internal/services/window_helper.go +++ b/internal/services/window_helper.go @@ -67,3 +67,13 @@ func (wh *WindowHelper) FocusMainWindow() bool { } return false } + +// AutoShowMainWindow 自动显示主窗口 +func (wh *WindowHelper) AutoShowMainWindow() { + window := wh.MustGetMainWindow() + if window.IsVisible() { + window.Hide() + } else { + window.Show() + } +} diff --git a/internal/systray/systray.go b/internal/systray/systray.go index b86a267..2234b18 100644 --- a/internal/systray/systray.go +++ b/internal/systray/systray.go @@ -3,6 +3,9 @@ package systray import ( "embed" "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/icons" + "runtime" + "time" "voidraft/internal/events" "voidraft/internal/services" "voidraft/internal/version" @@ -18,8 +21,19 @@ func SetupSystemTray(mainWindow *application.WebviewWindow, assets embed.FS, tra // 设置标签 systray.SetLabel("voidraft") // 设置图标 - iconBytes, _ := assets.ReadFile("appicon.png") + iconBytes, err := assets.ReadFile("frontend/dist/appicon.png") + if err != nil { + panic(err) + } systray.SetIcon(iconBytes) + systray.SetDarkModeIcon(iconBytes) + + // Use the template icon on macOS so the clock respects light/dark modes. + if runtime.GOOS == "darwin" { + systray.SetTemplateIcon(icons.SystrayMacTemplate) + } + + systray.WindowDebounce(300 * time.Millisecond) // 创建托盘菜单 menu := app.NewMenu() @@ -30,5 +44,5 @@ func SetupSystemTray(mainWindow *application.WebviewWindow, assets embed.FS, tra systray.SetMenu(menu) // 注册托盘相关事件 - events.RegisterTrayEvents(app, systray, mainWindow, trayService) + events.RegisterTrayEvents(systray, mainWindow, trayService) } diff --git a/main.go b/main.go index c48b23c..b391ed6 100644 --- a/main.go +++ b/main.go @@ -3,7 +3,6 @@ package main import ( "embed" _ "embed" - "log" "log/slog" "time" "voidraft/internal/common/constant" @@ -102,6 +101,6 @@ func main() { // If an error occurred while running the application, log it and exit. if err != nil { - log.Fatal(err) + panic(err) } }